python-debian-0.1.21+nmu2ubuntu1/0000755000000000000000000000000012021626007013417 5ustar python-debian-0.1.21+nmu2ubuntu1/README.deb8220000644000000000000000000000547412015175026015301 0ustar deb822.py README ================ The Python deb822 aims to provide a dict-like interface to various rfc822-like Debian data formats, like Packages/Sources, .changes/.dsc, pdiff Index files, etc. The benefit is that deb822 knows about special fields that contain whitespace separated sub-fields, and provides named access to them. For example, the "Files" field in Source packages, which has three subfields, or "Files" in .changes files, which has five. These are known as "multifields". deb822 has no external dependencies, but can use python-apt if available to parse the data, which gives a very significant performance boost when iterating over big Packages files. Key lookup in Deb822 objects and their multifields is case-insensitive, but the original case is always preserved, e.g. when printing the object. Classes ======= Here is a list of the types deb822 knows about: * Deb822 (aliases: Packages) - base class with no multifields. * Dsc (aliases: Sources) - class to represent .dsc files / Sources paragraphs. - Multivalued fields: · Files: md5sum, size, name * Changes - class to represent a .changes file - Multivalued fields: · Files: md5sum, size, section, priority, name * PdiffIndex - class to represent a pdiff Index file - Multivalued fields: · SHA1-Current: SHA1, size · SHA1-History: SHA1, size, date · SHA1-Patches: SHA1, size, date Input ===== Deb822 objects are normally initialized from a file object, from which at most one paragraph is read, or a string. Alternatively, any sequence that returns one line of input at a time may be used, e.g. an array of strings. PGP signatures, if present, will be stripped. Iteration ========= All classes provide an "iter_paragraphs" class method to easily go over each stanza in a file with multiple entries, like a Packages.gz file. For example: with open('/mirror/debian/dists/sid/main/binary-i386/Sources') as f: for src in Sources.iter_paragraphs(f): print src['Package'], src['Version'] This method uses python-apt if available to parse the file, since it significantly boosts performance. The downside, though, is that yielded objects share storage, so they should never be kept accross iterations. To prevent this behavior, pass a "shared_storage=False" keyword-argument to the iter_paragraphs() function. Sample usage (TODO: Improve) ============ import deb822 with open('foo_1.1.dsc') as f: d = deb822.Dsc(f) source = d['Source'] version = d['Version'] for f in d['Files']: print 'Name:', f['name'] print 'Size:', f['size'] print 'MD5sum:', f['md5sum'] # If we like, we can change fields d['Standards-Version'] = '3.7.2' # And then dump the new contents new_dsc = open('foo_1.1.dsc2', 'w') d.dump(new_dsc) new_dsc.close() python-debian-0.1.21+nmu2ubuntu1/.bzr-builddeb/0000755000000000000000000000000011703667743016064 5ustar python-debian-0.1.21+nmu2ubuntu1/.bzr-builddeb/default.conf0000644000000000000000000000003211703667743020352 0ustar [BUILDDEB] native = True python-debian-0.1.21+nmu2ubuntu1/HISTORY.deb8220000644000000000000000000000375411703667743015523 0ustar Following is the changelog for deb822 before it was merged into python-debian. deb822 (0.3) unstable; urgency=low * deb822.py: - Allow Deb822 objects to be initialized with a dict containing the initial key-value pairs. - _multivalued class: + Make all the multivalued dicts Deb822Dict objects, so the keys are case-preserving, but case-insensitive - Add a Release class, which knows about Release-file multivalued fields. Thanks to Alexandre Fayolle. (Closes: 428540) - Deb822Dict no longer directly subclasses dict. All of the important methods were already implemented with userdict.DictMixin; the dict subclass was so that Python would see a Deb822Dict instance as a dict instance. Unfortunately, this causes confusion if you do something like d = dict(Deb822Dict({'foo': 'bar'}) The "Pythonic" way to check for a dictionary interface is to check for the 'items' attribute. * test_deb822.py: - Add a test case for deriving a Python dict from a Deb822Dict. * debian/control: - Add a XS-Vcs-Bzr field -- John Wright Tue, 12 Jun 2007 17:37:05 -0600 deb822 (0.2) unstable; urgency=low * debian/rules: - Tell setup.py not to compile the modules -- python-support does that at install time * README: - Fix a typo * debian/control: - Improve the description - Remove X[BS]-Python-Version fields, as per python-support README * deb822.py: - Add a get_pool_path() method to the Changes class which returns the path in the pool you would expect to find the files listed in the .changes file - Fix a typo in Deb822Dict.__init__ where I was referring to fields, not _fields * Remove debian/pycompat, as per python-support README -- John Wright Fri, 5 Jan 2007 17:40:54 -0700 deb822 (0.1) unstable; urgency=low * Initial packaging (Closes: 380173) -- John Wright Wed, 15 Nov 2006 17:00:32 -0700 python-debian-0.1.21+nmu2ubuntu1/README0000644000000000000000000000135711703667743014327 0ustar This is a collection of modules that are for dealing with Debian related data. Each module is documented with it's own README file, or some examples. Currently supported are * Debtags data with debtags.py * Debian changelogs with changelog.py * Packages files and the like with deb822.py * .deb files (and .ar files FWIW) via debfile.py Some of the modules will use python-apt for some of the functions. It should make no difference if it is installed, but if you find a bug installing it might help. If you have a module that is for manipulation or interrogation of Debian specific data then consider adding it to this package. You can discuss it with the maintainers by contacting pkg-python-debian-discuss@lists.alioth.debian.org python-debian-0.1.21+nmu2ubuntu1/debian/0000755000000000000000000000000012302376313014645 5ustar python-debian-0.1.21+nmu2ubuntu1/debian/docs0000644000000000000000000000012511703667743015534 0ustar README.changelog README.debtags README.deb822 README HISTORY.deb822 ACKNOWLEDGEMENTS python-debian-0.1.21+nmu2ubuntu1/debian/copyright0000644000000000000000000000272111703667743016620 0ustar This package was debianized by James Westby on Wed, 16 Aug 2006 19:17:30 +0100. The original source can always be found at: ftp://ftp.debian.org/dists/unstable/main/source/ Copyright: deb822.py is Copyright (C) 2005-2006 dann frazier 2006 John Wright changelog.py, setup.py, README.Changelog and the contents of examples/changelog/ are Copyright (C) 2006-7 James Westby Copyright (C) 2008 Canonical Ltd. debian_support.py is Copyright (C) 2005 Florian Weimer Debtags.py, and the contents of examples/debtags/ are Copyright (C) 2003--3006 Enrico Zini debfile.py, arfile.py, and examples/debfile/* are Copyright (C) 2007 Stefano Zacchiroli 2007 Filippo Giunchedi Licenses: The following modules are released under GPL v2 or above: changelog.py, debian_support.py, deb822.py The following modules are released under GPL v3 or above: debfile.py, arfile.py, debtags.py The Debian packaging is (C) Debian python-debian Maintainers (see debian/control for maintainer names), and is placed under GPL v2 or above. On Debian systems, the complete text of the GNU General Public License can be found in `/usr/share/common-licenses/GPL'. python-debian-0.1.21+nmu2ubuntu1/debian/changelog0000644000000000000000000006174512302376313016534 0ustar python-debian (0.1.21+nmu2ubuntu2) trusty; urgency=medium * Rebuild to drop files installed into /usr/share/pyshared. -- Matthias Klose Sun, 23 Feb 2014 13:51:07 +0000 python-debian (0.1.21+nmu2ubuntu1) quantal; urgency=low * Resynchronise with Debian. Remaining changes: - Add .lzma as a possible extension for data.tar. Note that this does not mean that reading from the .lzma part is supported. This is blocked on http://bugs.python.org/issue5689. -- Colin Watson Wed, 05 Sep 2012 11:39:48 +0100 python-debian (0.1.21+nmu2) unstable; urgency=low * Non-maintainer upload. * In Python 2, restore Deb822Dict.has_key method for compatibility. (Closes: #686731) -- Colin Watson Wed, 05 Sep 2012 11:28:58 +0100 python-debian (0.1.21+nmu1ubuntu1) quantal; urgency=low * Resynchronise with Debian (NMU awaiting processing in DELAYED/3). Remaining changes: - Add .lzma as a possible extension for data.tar. Note that this does not mean that reading from the .lzma part is supported. This is blocked on http://bugs.python.org/issue5689. -- Colin Watson Wed, 22 Aug 2012 18:15:01 +0100 python-debian (0.1.21+nmu1) unstable; urgency=low * Non-maintainer upload. * Port to Python 3. (Closes: #625509) -- Colin Watson Wed, 22 Aug 2012 16:27:05 +0100 python-debian (0.1.21ubuntu1) precise; urgency=low * Merge from Debian testing. Remaining changes: - Add .lzma as a possible extenstion for data.tar (LP: #407198) Note that this does not mean that reading from the .lzma part is supported. This is blocked on http://bugs.python.org/issue5689 -- Michael Vogt Wed, 25 Jan 2012 16:59:16 +0100 python-debian (0.1.21) unstable; urgency=low [ Tshepang Lekhonkhobe ] * test_changelog.py: Close open files. (Closes: #625672) [ John Wright ] * deb822: Allow ':' as the first character of a value. (Closes: #597249) * deb822: Avoid dumping unparseable data. (Closes: #597120) * Clean up deb822.GpgInfo implementation: - Change several @staticmethod decorated methods to @classmethod, since they call the class constructor. - from_sequence now can accept both sequences of newline-terminated strings and the output of str.splitlines(). - from_file now actually calls the from_sequence method. (Closes: #627058) - from_sequence now makes a copy of the initial args list before extending it. (Closes: #627060) - Keyrings are no longer checked for accessibility, since gpgv can accept keyring arguments that are under the GnuPG home directory, regardless of the current directory. (Closes: #627063) * deb822.Deb822.gpg_info takes an optional keyrings argument. * deb822: Don't interpret lines starting with '#'. (Closes: #632306) [ Colin Watson ] * Use dh_python2 instead of python-support. (Closes: #631392) -- John Wright Wed, 03 Aug 2011 23:07:17 -0700 python-debian (0.1.20ubuntu3) precise; urgency=low * Rebuild to drop python2.6 dependencies. -- Matthias Klose Sat, 31 Dec 2011 02:09:27 +0000 python-debian (0.1.20ubuntu2) oneiric; urgency=low * Switch to dh_python2. (LP: #788514 LP: #847514) -- Barry Warsaw Wed, 14 Sep 2011 14:23:19 -0400 python-debian (0.1.20ubuntu1) oneiric; urgency=low * Merge from debian unstable. Remaining changes: - Add .lzma as a possible extenstion for data.tar (LP: #407198) Note that this does not mean that reading from the .lzma part is supported. This is blocked on http://bugs.python.org/issue5689 - workaround incorrect permissions in upstream tarball (ftbfs otherwise) -- Michael Vogt Fri, 17 Jun 2011 10:33:50 +0200 python-debian (0.1.20) unstable; urgency=low * Fix FTBFS, caused by referring to apt_pkg in a test with no python-apt Build-Depends. (Closes: #623011) * changelog: Implement __unicode__ in ChangeBlock and Changelog. (Closes: #561805) -- John Wright Sun, 17 Apr 2011 17:37:44 -0700 python-debian (0.1.19) unstable; urgency=low * debian_support.BaseVersion: Disallow ':' in upstream version if there is no epoch. Original patch by Steve Kowalik. (Closes: #619997) -- John Wright Fri, 15 Apr 2011 02:05:45 -0700 python-debian (0.1.18+nmu1) unstable; urgency=low * Non-maintainer upload. * Migrate to new python-apt API (Closes: #604544) -- Julian Andres Klode Tue, 05 Apr 2011 11:02:08 +0200 python-debian (0.1.18ubuntu2) natty; urgency=low * debian/rules: - workaround incorrect permissions in upstream tarball (fixes ftbfs) -- Michael Vogt Tue, 07 Dec 2010 17:23:18 +0100 python-debian (0.1.18ubuntu1) natty; urgency=low * Merge from debian unstable. Remaining changes: - Add .lzma as a possible extenstion for data.tar (LP: #407198) Note that this does not mean that reading from the .lzma part is supported. This is blocked on http://bugs.python.org/issue5689 -- Michael Vogt Mon, 18 Oct 2010 15:43:11 +0200 python-debian (0.1.18) unstable; urgency=low * Support installation together with older versions of python-apt. Original patch by Jelmer Vernooij. (Closes: #590805) -- John Wright Wed, 04 Aug 2010 01:16:52 -0700 python-debian (0.1.17) unstable; urgency=low [ James Westby ] * Use os.path.normpath to hide python version differences [ John Wright ] * test_deb822.py: Make test_gpg_info more robust (Closes: #582878) * deb822: Use chardet to try to detect character encodings as necessary - This is only used when the specified encoding doesn't work. It's mainly useful for files containing multiple deb822 paragraphs with mixed encodings, like etch's Sources file. (Closes: #586021) -- John Wright Sun, 25 Jul 2010 02:07:11 -0700 python-debian (0.1.16ubuntu1) maverick; urgency=low [ Scott Howard ] * Merge from debian unstable (LP: #588714). Remaining changes: - Depend on python-apt as debian_support uses it unconditionally. - Add .lzma as a possible extenstion for data.tar (LP: #407198) Note that this does not mean that reading from the .lzma part is supported. This is blocked on http://bugs.python.org/issue5689 [ James Westby ] * Use os.path.normpath to ensure debfile tests work across python versions. -- Scott Howard Wed, 02 Jun 2010 10:04:40 -0400 python-debian (0.1.16) unstable; urgency=low * debian_support: Be more careful in case apt_pkg is not available - If apt_pkg is not available, AptPkgVersion now raises NotImplementedError on any initialization attempt - test_debian_support.py now doesn't try to test AptPkgVersion if apt_pkg is not available * test_deb822.py: Don't test gpg info if debian-keyring is not available (Closes: #573934) * Use the warnings system to report deprecation warnings (Closes: #573945) * Change examples and control file to refer to debian instead of debian_bundle -- John Wright Mon, 15 Mar 2010 20:02:17 -0600 python-debian (0.1.15) unstable; urgency=low [ John Wright ] * deb822: Don't ignore leading newlines on field data with apt_pkg (Closes: #466753) * changelog: Consistently parse different types of inputs. The previous implementation added extra newlines when given a file object as input, rather than the result of str.splitlines(). (Closes: #539316) * deb822: Faster non-shared storage by keeping a TagFile object around for each Deb822 object yielded [ Stefano Zacchiroli ] * make debian_support.Version hashable, patch from Piotr Ożarowski (Closes: #543223) [ Filippo Giunchedi ] * changelog: add iterator over changelog blocks (Closes: #539334) * changelog: split tests into tests/test_changelog.py for consistency [ John Wright ] * deb822: Use the apt_pkg.TagFile class instead of apt_pkg.ParseTagFile() (Closes: #552190) * test_deb822.py: Update to support new gpg "SIG_ID" algorithm * deb822: Better support for non-ascii values. Deb822 objects now always contain unicode objects instead of strings. Use the encoding argument to Deb822's initializer and its iter_paragraphs class method if the encoding of a file you are reading in is not utf-8. (The dump method also takes an encoding argument, if you wish the output not to be utf-8-encoded.) (Closes: #495272) * Use the apt_pkg.TagFile iterator interface instead of hacking its offset. As of python-apt version 0.7.94, that interface doesn't use shared storage. (Closes: #571470) * debian_support: Add a native python Version class, based on Raphael Hertzog's DpkgVersion class in the PTS (Closes: #562257, #573009) [ Jelmer Vernooij ] * Remove unused imports in the debfile and debtags modules [ John Wright ] * changelog: Use debian_support.Version directly * Deprecate the 'debian_bundle' package in favor of a new 'debian' package (Closes: #570210) * debian_support: split tests into tests/test_debian_support.py for consistency * Convert to "3.0 (native)" source format and update Standards-Version to 3.8.4 (no changes necessary) -- John Wright Sun, 14 Mar 2010 05:04:50 -0600 python-debian (0.1.14ubuntu2) lucid; urgency=low * Depend on python-apt as debian_support uses it unconditionally. -- James Westby Wed, 13 Jan 2010 15:58:18 +1300 python-debian (0.1.14ubuntu1) karmic; urgency=low * Add .lzma as a possible extenstion for data.tar (LP: #407198) Note that this does not mean that reading from the .lzma part is supported. This is blocked on http://bugs.python.org/issue5689 -- Michael Vogt Tue, 01 Sep 2009 11:08:07 +0200 python-debian (0.1.14) unstable; urgency=low [ Stefano Zacchiroli ] * setup.py.in: switch to setuptools, which are able to generate eggs; add matching build-dep on python-setuptools (Closes: #525694) * examples/deb822/: add new example render-dctrl, to render packages in a dctrl-tools pipeline (using Markdown as long description syntax) * deb822: use __new__ to sub-class strings for case-insensitiveness; former approach is deprecated with python 2.6. Thanks to Loïc Minier for the patch. (Closes: #524061) [ Filippo Giunchedi ] * deb822: parse also Binary as PkgRelation, thus add .binary attribute [ Muharem Hrnjadovic ] * fixed changelog parser exception raised when dealing with empty changelog files (LP: #400589). -- Stefano Zacchiroli Sat, 18 Jul 2009 21:26:40 +0200 python-debian (0.1.13) unstable; urgency=low [ John Wright ] * deb822: Correctly handle deletion of keys that only exist in a pre-parsed dictionary [ Stefano Zacchiroli ] * change naming convention to match PEP 8, with backward compatibility (and deprecation warnings) for all old names. Thanks to Ben Finney for the patch. (Closes: #489722) * Switch from "sha" module to "hashlib": fix a deprecation warning of Python 2.6. As a consequence, update pyversions to require a minimum of Python 2.5, which is available also in stable now. (Closes: #518427) * debian_support.PackageFile: allow for stanza separators made of tabs and spaces in addition to newlines (Closes: #501927) * bump Standards-Version to 3.8.1 (no changes needed) -- Stefano Zacchiroli Tue, 07 Apr 2009 10:21:04 +0200 python-debian (0.1.12) unstable; urgency=low [ Filippo Giunchedi ] * Add initial support to deb822 for gpg signature checking (Closes: #485829) * Suggest gpgv for signature checking [ Stefano Zacchiroli ] * deb822: add support for pretty printing structured inter-package relationships (and relevant tests) * debfile: add support for .tar.bz2 parts contained in .deb packages, and relative regression test. Thanks to Cameron Dale and Tom Parker. (Closes: #462859). [ John Wright ] * deb822: allow the use of unicode objects in __getitem__ * changelog: fix the "topline" regular expression to match the one in dpkg's Dpkg/Changelog/Debian.pm so that it allows '.' and '+' in the distribution field * deb822: Add a use_apt_pkg parameter to Deb822.iter_paragraphs. Now, callers can set use_apt_pkg=True and shared_storage=False (now the default) in order to take advantage of apt_pkg's speed and still be able to use values across iterations. (Closes: #504413) -- John Wright Wed, 05 Nov 2008 16:03:40 -0700 python-debian (0.1.11) unstable; urgency=low [ James Westby ] * Allow urgency=HIGH and other words made of capital letters there, see fuse's changelog. * Allow any number of spaces before "urgency" in the header line of a changelog, see lvm2's changelog in Ubuntu. * Overhaul the parsing code to follow that used by dpkg-parsechangelog.py, making it much more robust. - There is now a "strict" option to turn warnings in to errors. It is on by default. * The file parameter in the changelog can now be a file-like object, to save reading a file to a string first. Thanks to Tilman Koschnick. (Closes: #487797) [ John Wright ] * debian/control: - Fix a typo in the Description field (Closes: #483688) * debian_bundle/deb822.py: Checksums-Sha1 and Checksums-Sha256 don't have section or priority subfields (Closes: #487902) [ Stefano Zacchiroli ] * debian_bundle/deb822.py: add support for "parsed" dependencies and other inter-package relationships. See examples/deb822/depgraph for sample usage * examples/deb822/ add new example "depgraph" * tests/test_deb822.py: add tests for inter-package relationships * debian_bundle/debfile.py: add has_key (same implementation of __contains__) to better emulate dictionary containers * minor revamp of README pointing to deb822.py for Packages parsing and mentioning debfile.py * debian/control: - fix Vcs-* fields to point to the new git repository (bzr is gone) * bump Standards-Version to 3.8.0, no changes needed though profit of the possibility of wrapping Uploaders in debian/control [ Filippo Giunchedi ] * Add test case for changes file with checksums (see #487902) -- Stefano Zacchiroli Wed, 06 Aug 2008 23:48:38 -0300 python-debian (0.1.10) unstable; urgency=low * debian_bundle/deb822.py, tests/test_deb822.py: - Do not cache _CaseInsensitiveString objects, since it causes case preservation issues under certain circumstances (Closes: #473254) - Add a test case * debian_bundle/deb822.py: - Add support for fixed-length subfields in multivalued fields. I updated the Release and PdiffIndex classes to use this. The default behavior for Release is that of apt-ftparchive, just because it's simpler. Changing the behavior to resemble dak requires simply setting the size_field_behavior attribute to 'dak'. (Ideally, deb822 would detect which behavior to use if given an actual Release file as input, but this is not implemented yet.) (Closes: #473259) - Add support for Checksums-{Sha1,Sha256} multivalued fields in Dsc and Changes classes * debian/control: - "python" --> "Python" in the Description field - Change the section to "python" -- John Wright Wed, 30 Apr 2008 23:58:24 -0600 python-debian (0.1.9) unstable; urgency=low * Promote python-apt from Suggests to Recommends. (Closes: #462845) -- Adeodato Simó Wed, 06 Feb 2008 15:36:02 +0100 python-debian (0.1.8) unstable; urgency=low [ Filippo Giunchedi ] * Move tests to a separate subdirectory. [ Stefano Zacchiroli ] * Add examples dir for deb822 with a simple example. [ Adeodato Simó ] * Sync debian_support.py with the latest version from the secure-testing repository. Note that this removes support for version comparison without apt_pkg. (Our copy was modified in 0.1.2 to *accept* ~ in version strings, but comparison without apt_pkg was already broken by then.) Also, undo the change introduced for #431087, since we shouldn't be keeping incompatible versions of debian_support.py around, plus the desired functionality in that bug report is best provided by the deb822 module. (Closes: #457697, #457854) * While rewriting the grep-maintainer example to use deb822.Packages to accommodate the above change, make it cope with stanzas without a maintainer field as well. (Closes: #457855) * Fix dump() method of deb822._multivalued. (Closes: #457929) * Small improvements to the exception handling in the grep-maintainer example. * Bump Standards-Version to 3.7.3 (no changes needed). -- Adeodato Simó Fri, 28 Dec 2007 19:46:13 +0100 python-debian (0.1.7) unstable; urgency=low [ John Wright ] * debian_bundle/deb822.py, debian_bundle/test_deb822.py - Bug fix: Allow fields with blank (empty string) values. Previously, these were parsed fine, but if you tried to dump the resulting Deb822 object, you'd get an IndexError (because of how we handled fields whose values start with newlines). Added a test case. - Fix the copy method of Deb822Dict so that it returns an object of the same type, rather than explicitly instantiating Deb822Dict (so that subclasses like Deb822 can directly use the copy method and have it "just work"). Added a test case. [ Stefano Zacchiroli ] * debian/control - mention the debfile module in the long description - promote Vcs-Bzr to a real field, now that dpkg supports it -- John Wright Tue, 27 Nov 2007 16:25:33 -0700 python-debian (0.1.6) unstable; urgency=low [ Stefano Zacchiroli ] * debfile.py - bugfix: make md5sums top-level method acts as a proxy instead of returning a method - bugfix: use readlines() to iterate over the md5sums file since direct iteration on files is available only in python >= 2.5 (closes: #438804) - bugfix: support spaces in filenames when accessing md5sums; thanks to Romain Francoise for the patch (closes: #438909) [ John Wright ] * debian/pyversions: - Since there are some Python 2.4-isms in this package, support only Python >= 2.4, rather than all versions (Closes: #438920) -- Stefano Zacchiroli Sat, 08 Sep 2007 10:12:20 +0200 python-debian (0.1.5) unstable; urgency=low [ Stefano Zacchiroli ] * debfile.py - do not fail if extra ar members exist in a .deb, according to deb(5) extra ar members should be ignored (Closes: #438486). Also add the corresponding regression test to test_debfile.py - support both the (buggy) file naming scheme for TarFile of python << 2.5 (e.g. "control") and of python >= 2.5 (e.g. "./control"). In addition, enable users to specify file members in three equivalent formats: "file", "./file", and "/file" (Closes: #438603) * deb822.py - add a deprecation warning (printed on stderr) when accessing the module as a top-level one; it should be rather used as "debian_bundle.deb822" [ John Wright ] * examples/debfile/ar, examples/debfile/changelog_head, examples/debfile/dpkg-info, examples/debfile/extract_cron: - Fix ImportError caused by importing modules as if they were top-level modules, rather than modules in the debian_bundle package (Closes: #438428) -- Stefano Zacchiroli Sun, 19 Aug 2007 09:38:20 +0200 python-debian (0.1.4) unstable; urgency=low [ James Westby ] * changelog.py - Add allow_empty_author option to changelog to allow the author line to have no information. [ Stefano Zacchiroli ] * debian/control - joining the team: add myself as an uploader - add Vcs-Bzr field pointing to our Bzr repository on bzr.debian.org * arfile.py / debfile.py - new modules implementing (read-only) dpkg-deb like access to .deb packages; when possible, access to information contained in .deb is made available via other debian_bundle classes (e.g. Changelog, Deb822, ...) - code contributed by Stefano Zacchiroli and Filippo Giunchedi - corresponding examples in examples/debfile * debian_support.py - yield dictionaries instead of tuples in Packages parsing and normalize to lower case 822 field names (Closes: #431087) - no longer require a space after a ':' in RFC 822 fields; fix bogus parse errors of Packages-like file with PackageFile() class * examples/ - added grep-maintainer example to show the Packages parsing API [ John Wright ] * debian_bundle/deb822.py, debian_bundle/test_deb822.py, deb822.py: - Import latest version of deb822, and create a "dummy" top-level module for compatibility - For multiline fields that start with a newline (e.g. Files in a .dsc, MD5Sum in Release files), the line with the "Field:" should not end in trailing whitespace. Fixed and added test cases. * setup.py: - The version wasn't getting updated, so I've renamed it to setup.py.in, and added a __CHANGELOG_VERSION__ placeholder * debian/rules: - Generate generate setup.py from setup.py.in, filling in the version information from debian/changelog - No longer delete deb822 files * debian/control: - Add myself as an uploader - Add Provides, Conflicts, and Replaces fields for python-deb822 - When describing features, mention which modules they are implemented in * HISTORY.deb822, debian/docs: - Install old changelog for deb822 as /usr/share/doc/python-debian/HISTORY.deb822 * debian_bundle/changelog.py: - Accept numbers, '+', and '.' in the distribution field, to match dpkg's behavior -- John Wright Sun, 5 Aug 2007 00:41:24 -0600 python-debian (0.1.3) unstable; urgency=low [ James Westby ] * changelog.py - Add max_blocks parameter to Changelog constructor. This limits the number of blocks that will be parsed to that number. If you only need the latest entry then pass max_blocks=1 and you will only have one block. This is also useful to avoid trying to parse old changelog entries that don't follow the current format. (Closes: #429357) [ Reinhard Tartler ] * debian_bundle/changelog.py: don't fail if a changelog block is not parseable. Just return all changelog blocks we've been able to parse so far. (Closes: #429299) -- James Westby Sun, 17 Jun 2007 13:25:20 +0100 python-debian (0.1.2) unstable; urgency=low [ James Westby ] * debian_support.py - Support ~ in version numbers when python-apt is not installed. * Turn on the testsuites of changelog.py, and debtags.py in the build. * debtags.py - Rename with to with_ to avoid a warning on python2.5 as with will be a keyword in 2.6. (Closes: #409333) * Suggest python-apt and mention why in the README. [Reinhard Tartler] * Fix the regex matching the end line of a debian/changelog. Using improved regex contributed by Francois-Denis Gonthier, thanks! (Closes: #410880) * disable python_support.py testrun, it is missing in the branch. * add myself to uploaders. -- Reinhard Tartler Thu, 14 Jun 2007 19:54:13 +0100 python-debian (0.1.1) unstable; urgency=low [ John Wright ] * changelog.py: - Version class: + Subclass debian_support.Version, for rich comparison + Changing any attribute now automatically updates any other affected attribute (i.e. changing full_version will update all of the other attributes; changing debian_version will update full_version) - Changelog class: + Many "getter" and "setter" methods were replaced with properties (so they appear as regular object attributes). Most set_* methods remain for compatibility, and they're the actual set methods for the properties anyway. Please see README.changelog. + You can now assign a string to the version attribute, and it will be coerced into a Version object [ James Westby ] * changelog.py: - Added a method to write the changelog to an open file. - Allow single digit day with no extra space in the endline of a changelog. Hopefully this wont allow things to be parsed that will be rejected by the more important tools. It does allow more files to be parsed and used, and I'm not entirely sure what is right. - Allow uppercase characters in the version number when parsing changelogs. Thanks to Jelmer Vernooij for the report and the fix. * README.changelog: - Update the documentation to reflect the changes mentioned above * Add debian/NEWS to warn of the API changes. [ Enrico Zini ] * debtags.py: - Added various methods after the work on the inferrer of tag relationships - Added tagminer and pkgwalk examples -- James Westby Tue, 30 Jan 2007 20:56:44 +0000 python-debian (0.1.0) experimental; urgency=low * Initial release. (Closes: #381599) -- James Westby Tue, 15 Aug 2006 18:57:57 +0100 python-debian-0.1.21+nmu2ubuntu1/debian/NEWS0000644000000000000000000000172511703667743015367 0ustar python-debian (0.1.1) unstable; urgency=low This release includes an API incompatible change to changelog.py, sorry for this. The previous behaviour was broken though, and the change fixes that as well as being a cleaner API as well. The change is that most methods of Changelog (and Version) are now properties. If you are using them and you get an error message on update from a line something like version = changelog.full_version() or many of the other methods, then you can just delete the parentheses from the end of the line, and access the property instead. The other change is that Version objects are now immutable. If you were using the mutable behaviour then I apologise, but the code was broken. If you would like mutable objects let me know and I will code up the changes necessary. As always details of how to use the model are in README.changelog. -- James Westby Tue, 30 Jan 2007 07:43:41 +0000 python-debian-0.1.21+nmu2ubuntu1/debian/examples0000644000000000000000000000001311703667743016416 0ustar examples/* python-debian-0.1.21+nmu2ubuntu1/debian/compat0000644000000000000000000000000211703667743016061 0ustar 5 python-debian-0.1.21+nmu2ubuntu1/debian/control0000644000000000000000000000457612021625521016260 0ustar Source: python-debian Section: python Priority: optional Maintainer: Ubuntu Developers XSBC-Original-Maintainer: Debian python-debian Maintainers Uploaders: Adeodato Simó , Enrico Zini , James Westby , Reinhard Tartler , Stefano Zacchiroli , John Wright Build-Depends: debhelper (>= 5.0.37.2), python (>= 2.6.6-3~), python3 (>= 3.1.2-8~), python-setuptools, python3-setuptools, python-chardet, python3-chardet, python-six, python3-six Standards-Version: 3.8.4 Vcs-Browser: http://git.debian.org/?p=pkg-python-debian/python-debian.git Vcs-Git: git://git.debian.org/git/pkg-python-debian/python-debian.git X-Python-Version: >= 2.5 Package: python-debian Architecture: all Depends: ${python:Depends}, ${misc:Depends}, python-chardet, python-six Recommends: python-apt Suggests: gpgv Provides: python-deb822 Conflicts: python-deb822 Replaces: python-deb822 Description: Python modules to work with Debian-related data formats This package provides Python modules that abstract many formats of Debian related files. Currently handled are: * Debtags information (debian.debtags module) * debian/changelog (debian.changelog module) * Packages files, pdiffs (debian.debian_support module) * Control files of single or multiple RFC822-style paragraphs, e.g. debian/control, .changes, .dsc, Packages, Sources, Release, etc. (debian.deb822 module) * Raw .deb and .ar files, with (read-only) access to contained files and meta-information Package: python3-debian Architecture: all Depends: ${python3:Depends}, ${misc:Depends}, python3-chardet, python3-six Recommends: python3-apt Suggests: gpgv Description: Python 3 modules to work with Debian-related data formats This package provides Python 3 modules that abstract many formats of Debian related files. Currently handled are: * Debtags information (debian.debtags module) * debian/changelog (debian.changelog module) * Packages files, pdiffs (debian.debian_support module) * Control files of single or multiple RFC822-style paragraphs, e.g. debian/control, .changes, .dsc, Packages, Sources, Release, etc. (debian.deb822 module) * Raw .deb and .ar files, with (read-only) access to contained files and meta-information python-debian-0.1.21+nmu2ubuntu1/debian/rules0000755000000000000000000000432712015175027015733 0ustar #!/usr/bin/make -f # -*- makefile -*- # Uncomment this to turn on verbose mode. #export DH_VERBOSE=1 VERSION := $(shell dpkg-parsechangelog | grep '^Version: ' | awk '{print $$2}') %.py: %.py.in sed -e 's/__CHANGELOG_VERSION__/$(VERSION)/' < $< > $@ build: build-stamp build-stamp: setup.py dh_testdir # Add here commands to compile the package. python setup.py build python3 setup.py build # run the tests cd tests && ./test_deb822.py cd tests && ./test_debfile.py cd tests && ./test_debtags.py cd tests && ./test_changelog.py cd tests && ./test_debian_support.py cd tests && python3 ./test_deb822.py cd tests && python3 ./test_debfile.py cd tests && python3 ./test_debtags.py cd tests && python3 ./test_changelog.py cd tests && python3 ./test_debian_support.py lib/debian/doc-debtags > README.debtags touch $@ clean: setup.py dh_testdir dh_testroot rm -f build-stamp # Add here commands to clean up after the build process. python setup.py clean python3 setup.py clean rm -rf lib/python_debian.egg-info rm -rf build/ rm -f README.debtags rm -f setup.py find lib/debian -type f -name '*.pyc' -exec rm {} \; dh_clean install: build dh_testdir dh_testroot dh_clean dh_installdirs # Add here commands to install the package into debian/tmp python setup.py install --root="$(CURDIR)/debian/python-debian" --no-compile --install-layout=deb python3 setup.py install --root="$(CURDIR)/debian/python3-debian" --no-compile --install-layout=deb # Build architecture-independent files here. binary-indep: build install dh_testdir dh_testroot dh_installchangelogs dh_installdocs dh_installexamples # TODO: Enrico would like to keep wxssearch out of the package # but so far, he has no better place for it #rm "$(CURDIR)/debian/python-debian/usr/share/doc/python-debian/examples/wxssearch" rm "$(CURDIR)/debian/python-debian/usr/share/doc/python-debian/examples/debtags/wxssearch" # dh_install dh_compress -Xexamples dh_fixperms dh_python2 dh_installdeb dh_shlibdeps dh_gencontrol dh_md5sums dh_builddeb # Build architecture-dependent files here. binary-arch: build install # We have nothing to do by default. binary: binary-indep binary-arch .PHONY: build clean binary-indep binary-arch binary install python-debian-0.1.21+nmu2ubuntu1/debian/source/0000755000000000000000000000000011703667743016163 5ustar python-debian-0.1.21+nmu2ubuntu1/debian/source/format0000644000000000000000000000001511703667743017372 0ustar 3.0 (native) python-debian-0.1.21+nmu2ubuntu1/examples/0000755000000000000000000000000011703667743015257 5ustar python-debian-0.1.21+nmu2ubuntu1/examples/deb822/0000755000000000000000000000000012015175026016226 5ustar python-debian-0.1.21+nmu2ubuntu1/examples/deb822/depgraph0000755000000000000000000000367312015175026017757 0ustar #!/usr/bin/python # depgraph # Copyright (C) 2008 Stefano Zacchiroli # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. """Graph the dependencies of all packages contained in a given Packages file. Only consider Depends fields. Versioned dependencies are considered as they were not versioned. The graph is retourned in output as a (non normalized) graphviz graph suitable to be processed by dot (for an unstable/main Packages file, generating the final graph will take a while ...).""" from __future__ import print_function import sys from debian import deb822 __fresh_id = 0 def main(): if len(sys.argv) != 2: print("Usage: depgraph PACKAGES_FILE") sys.exit(2) def get_id(): global __fresh_id __fresh_id += 1 return ("NODE_%d" % __fresh_id) def emit_arc(node1, node2): print(' "%s" -> "%s" ;' % (node1, node2)) def emit_node(node, dsc): print(' "%s" [label="%s"] ;' % (node, dsc)) print("digraph depgraph {") for pkg in deb822.Packages.iter_paragraphs(open(sys.argv[1])): name = pkg['package'] rels = pkg.relations for deps in rels['depends']: if len(deps) == 1: emit_arc(name, deps[0]['name']) else: # output an OR node or_node = get_id() emit_arc(name, or_node) emit_node(or_node, 'OR') for dep in deps: emit_arc(or_node, dep['name'].lower()) # even though it is forbidden by policy, there are some # dependencies with upper case letter in the archive, # apparently apt-get turn them to lowercase ... print("}") if __name__ == '__main__': main() python-debian-0.1.21+nmu2ubuntu1/examples/deb822/render-dctrl0000755000000000000000000001253412015175026020546 0ustar #!/usr/bin/python # render-dctrl # Copyright (C) 2009 Stefano Zacchiroli # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # Requirements (Debian packages): python-debian python-markdown from __future__ import print_function usage = """Usage: render-dctrl [OPTION ...] [FILE ...] Render a 822-like listing of Debian packages (AKA "Packages" file) to XHTML, rendering (long) descriptions as Markdown text. Render text coming from FILEs, if given, or from standard input otherwise. Typical usage is within a dctrl-tools pipeline, example: grep-available -s Package,Depends,Description ocaml | render-dctrl > foo.html Warning: beware of #525525 and thus avoid using "-s Description" alone.""" import re import string import sys from debian import deb822 from markdown import markdown from optparse import OptionParser options = None # global, for cmdline options css = """ body { font-family: sans-serif; } dt { font-weight: bold; } dd { margin-bottom: 5pt; } div.package { border: solid 1pt; margin-top: 10pt; padding-left: 2pt; padding-right: 2pt; } .raw { font-family: monospace; background: #ddd; padding-left: 2pt; padding-right: 2pt; } .shortdesc { text-decoration: underline; margin-bottom: 5pt; display: block; } .longdesc { background: #eee; } span.package { font-family: monospace; font-size: 110%; } .uid { float: right; font-size: x-small; padding-right: 10pt; } """ html_header = """ """ % css html_trailer = """ """ mdwn_list_line = re.compile(r'^(\s*)[\*\+\-]') # Markdown list item line # mdwn_head_line = re.compile(r'^(\s*)#') # Markdown header padding = re.compile(r'^(\s*)') def get_indent(s): m = padding.match(s) if m: return len(m.group(1)) else: return 0 def render_longdesc(lines): print('
') lines = map(lambda s: s[1:], lines) # strip 822 heading space curpara, paragraphs = [], [] inlist, listindent = False, 0 store_para = lambda: paragraphs.append(string.join(curpara, '\n') + '\n') add_indent = lambda n, s: string.expandtabs('\t', n) + s for l in lines: # recognize Markdown paragraphs if l.rstrip() == '.': # RULE 1: split paragraphs at Debian's "." store_para() curpara, inlist, listindent = [], False, 0 else: if inlist: # currently in a list if get_indent(l) <= listindent: # RULE 3: leave list on underflow store_para() curpara, inlinst, linstindent = [l], False, 0 else: # the list goes on ... curpara.append(l) else: # currently not in a list if mdwn_list_line.match(l): # new list start if curpara: # RULE 2: handle list item *not* at para start store_para() curpara, inlist, listindent = [l], True, get_indent(l) elif get_indent(l) >= 1: # RULE 4: hande non-list verbatim if curpara and get_indent(curpara[-1]) < 4: store_para() curpara = [] curpara.append(add_indent(3, l)) else: curpara.append(l) if curpara: store_para() for p in paragraphs: # render paragraphs print(markdown(p)) print('
') def render_field(field, val): field = field.lower() print('
%s
' % field) print('
' % field) if field == 'description': lines = val.split('\n') print('%s' % lines[0]) render_longdesc(lines[1:]) elif field == 'package': print('id' % val) print('%s' % (val, val)) elif field in []: # fields not to be typeset as "raw" print('%s' % (field, val)) else: print('%s' % val) print('
') def render_file(f): global options, html_header, html_trailer if options.print_header: print(html_header) for pkg in deb822.Packages.iter_paragraphs(f): print('
') print('
') for (field, val) in pkg.items(): render_field(field, val) print('
') print('
\n') if options.print_header: print(html_trailer) def main(): global options, usage parser = OptionParser(usage=usage) parser.add_option("-n", "--no-headers", action="store_false", dest="print_header", default=True, help="suppress printing of HTML header/trailer") (options, args) = parser.parse_args() if len(args): for fname in args: render_file(open(fname)) else: render_file(sys.stdin) if __name__ == '__main__': main() python-debian-0.1.21+nmu2ubuntu1/examples/deb822/grep_native_packages.py0000755000000000000000000000127412015175026022750 0ustar #!/usr/bin/python # grep the names of all Debian native packages out of Sources files # Copyright (C) 2007 Stefano Zacchiroli # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. from __future__ import print_function import sys from debian import deb822 for fname in sys.argv[1:]: f = open(fname) for stanza in deb822.Sources.iter_paragraphs(f): pieces = stanza['version'].split('-') if len(pieces) < 2: print(stanza['package']) f.close() python-debian-0.1.21+nmu2ubuntu1/examples/deb822/grep-maintainer0000755000000000000000000000161012015175026021234 0ustar #!/usr/bin/python # grep-maintainer # Copyright (C) 2007 Stefano Zacchiroli # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. """Dumb maintainer-based grep for the dpkg status file.""" from __future__ import print_function import re import sys from debian import deb822 try: maint_RE = re.compile(sys.argv[1]) except IndexError: print("Usage: grep-maintainer REGEXP", file=sys.stderr) sys.exit(1) except re.error as e: print("Error in the regexp: %s" % (e,), file=sys.stderr) sys.exit(1) for pkg in deb822.Packages.iter_paragraphs(open('/var/lib/dpkg/status')): if 'Maintainer' in pkg and maint_RE.search(pkg['maintainer']): print(pkg['package']) python-debian-0.1.21+nmu2ubuntu1/examples/changelog/0000755000000000000000000000000012015175026017167 5ustar python-debian-0.1.21+nmu2ubuntu1/examples/changelog/simple_changelog0000755000000000000000000000262012015175026022415 0ustar #!/usr/bin/python # simple_changelog - A simple example of how to use the changelog.py module. # Copyright (C) 2006 James Westby # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA from __future__ import print_function from debian.changelog import Changelog, Version changelog = Changelog() changelog.new_block(package='python-debian', version=Version('0.1'), distributions='unstable', urgency='low', author='James Westby ', date='Thu, 3 Aug 2006 19:16:22 +0100', ) changelog.add_change(''); changelog.add_change(' * Welcome to changelog.py'); changelog.add_change(''); print(changelog) python-debian-0.1.21+nmu2ubuntu1/examples/changelog/changelog_to_file0000755000000000000000000000310512015175026022544 0ustar #!/usr/bin/python # changelog_to_file -- An example of outputting to changelog to a file. # Copyright (C) 2007 James Westby # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA from __future__ import print_function from debian.changelog import Changelog, Version import sys changelog = Changelog() changelog.new_block(package='python-debian', version=Version('0.1'), distributions='unstable', urgency='low', author='James Westby ', date='Thu, 3 Aug 2006 19:16:22 +0100', ) changelog.add_change(''); changelog.add_change(' * Welcome to changelog.py'); changelog.add_change(''); try: filename = sys.argv[1] except IndexError: print("Usage: "+sys.argv[0]+" filename") sys.exit(1) f = open(filename, 'w') try: changelog.write_to_open_file(f) finally: f.close() python-debian-0.1.21+nmu2ubuntu1/examples/debfile/0000755000000000000000000000000012015175026016632 5ustar python-debian-0.1.21+nmu2ubuntu1/examples/debfile/extract_cron0000755000000000000000000000236612015175026021262 0ustar #!/usr/bin/python # extract_cron - extract cron-related files from .deb s # Copyright (C) 2007 Stefano Zacchiroli # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. """Extracts all cron-related files from a (list of) .deb package(s).""" from __future__ import print_function import os import re import sys from debian import debfile def is_cron(fname): return re.match(r'^etc/cron\.(d|daily|hourly|monthly|weekly)\b', fname) if __name__ == '__main__': if not sys.argv[1:]: print("Usage: extract_cron DEB ...") sys.exit(1) for fname in sys.argv[1:]: deb = debfile.DebFile(fname) cron_files = filter(is_cron, list(deb.data)) for cron_file in cron_files: print('Extracting cron-related file %s ...' % cron_file) path = os.path.join('.', cron_file) dir = os.path.dirname(path) if not os.path.exists(dir): os.mkdir(dir) out = open(path, 'w') out.write(deb.data.get_content(cron_file)) out.close() python-debian-0.1.21+nmu2ubuntu1/examples/debfile/dpkg-info0000755000000000000000000000343412015175026020442 0ustar #!/usr/bin/python # dpkg-info - DebFile's implementation of "dpkg --info" # Copyright (C) 2007 Stefano Zacchiroli # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. """ (An approximation of) a 'dpkg --info' implementation relying on DebFile class. """ from __future__ import print_function import os import stat import sys from debian import debfile if __name__ == '__main__': if len(sys.argv) != 2: print("Usage: dpkg-info DEB") sys.exit(1) fname = sys.argv[1] deb = debfile.DebFile(fname) if deb.version == '2.0': print(' new debian package, version %s.' % deb.version) print(' size %d bytes: control archive= %d bytes.' % ( os.stat(fname)[stat.ST_SIZE], deb['control.tar.gz'].size)) for fname in deb.control: # print info about control part contents content = deb.control[fname] if not content: continue lines = content.split('\n') ftype = '' try: if lines[0].startswith('#!'): ftype = lines[0].split()[0] except IndexError: pass print(' %d bytes, %d lines, %s, %s' % (len(content), len(lines), fname, ftype)) for n, v in deb.debcontrol().items(): # print DEBIAN/control fields if n.lower() == 'description': # increase indentation of long dsc lines = v.split('\n') short_dsc = lines[0] long_dsc = '\n'.join(map(lambda l: ' ' + l, lines[1:])) print(' %s: %s\n%s' % (n, short_dsc, long_dsc)) else: print(' %s: %s' % (n, v)) python-debian-0.1.21+nmu2ubuntu1/examples/debfile/ar0000755000000000000000000000251112015175026017161 0ustar #!/usr/bin/python # ar.py: ar emulation using ArFile # Copyright (C) 2007 Filippo Giunchedi # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 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 . from __future__ import print_function import os import sys from debian import arfile if __name__ == '__main__': if len(sys.argv) < 3: print("usage: arfile.py [tp] ") sys.exit(1) if not os.path.exists(sys.argv[2]): print("please provide a file to operate on") sys.exit(1) a = arfile.ArFile(sys.argv[2]) if sys.argv[1] == 't': print("\n".join(a.getnames())) elif sys.argv[1] == 'p': for m in a.getmembers(): #print("".join(m.readlines())) sys.stdout.write("".join(m.readlines())) python-debian-0.1.21+nmu2ubuntu1/examples/debfile/changelog_head0000755000000000000000000000175412015175026021477 0ustar #!/usr/bin/python # changelog_head - head like tool for .deb changelog entries # Copyright (C) 2007 Stefano Zacchiroli # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. """Like "head" for changelog entries, return last n-th entries of the changelog shipped in a .deb file.""" from __future__ import print_function import sys from debian import debfile if __name__ == '__main__': if len(sys.argv) > 3 or len(sys.argv) < 2: print("Usage: changelog_head DEB [ENTRIES]") print(" ENTRIES defaults to 10") sys.exit(1) entries = 10 try: entries = int(sys.argv[2]) except IndexError: pass deb = debfile.DebFile(sys.argv[1]) chg = deb.changelog() entries = chg._blocks[:entries] print(''.join(map(str, entries))) python-debian-0.1.21+nmu2ubuntu1/examples/debtags/0000755000000000000000000000000012015175027016652 5ustar python-debian-0.1.21+nmu2ubuntu1/examples/debtags/tagsbyrelevance0000644000000000000000000000343212015175026021754 0ustar #!/usr/bin/python # debtags - Implement package tags support for Debian # # Copyright (C) 2003--2006 Enrico Zini # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA from __future__ import print_function import sys import re from debian import debtags if len(sys.argv) < 2: print("Usage: %s tagdb [packagelist]" % (sys.argv[0]), file=sys.stderr) sys.exit(1) full = debtags.DB() # Read full database tag_filter = re.compile(r"^special::.+$|^.+::TODO$") full.read(open(sys.argv[1], "r"), lambda x: not tag_filter.match(x)) # Read the package list and create the subcollection input = len(sys.argv) > 2 and open(sys.argv[2],"r") or sys.stdin pkgs = set() for pkg in input: # Tolerate apt-cache search output as well pkg, none = pkg.rstrip("\n").split(' - ', 1) pkgs.add(pkg) sub = full.choose_packages(pkgs) rel_index = debtags.relevance_index_function(full, sub) # Get all the tags sorted by increasing relevance tags = sorted(sub.iter_tags(), lambda a, b: cmp(rel_index(a), rel_index(b))) ## And finally print them for tag in tags: print(tag) #print(tag, sub.card(tag), full.card(tag), float(sub.card(tag)) / float(full.card(tag))) python-debian-0.1.21+nmu2ubuntu1/examples/debtags/reverse0000755000000000000000000000244012015175026020252 0ustar #!/usr/bin/python # debtags - Implement package tags support for Debian # # Copyright (C) 2003--2006 Enrico Zini # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA from __future__ import print_function import sys from debian import debtags input = sys.stdin if len(sys.argv) > 1: input = open(sys.argv[1],"r") #db = read_tag_database(input) #db = reverse(db) db = debtags.read_tag_database_reversed(input) #dummy, db = debtags.read_tag_database_both_ways(input) for pkg, tags in db.items(): # Using % here seems awkward to me, but if I use calls to # sys.stdout.write it becomes a bit slower print("%s:" % (pkg), ", ".join(tags)) python-debian-0.1.21+nmu2ubuntu1/examples/debtags/tagminer0000755000000000000000000001447612015175026020421 0ustar #!/usr/bin/python # # Copyright (C) 2007 Enrico Zini # # This program is free software; you can redistribute it and/or modify it under # the terms of the GNU General Public License as published by the Free Software # Foundation; either version 2 of the License, or (at your option) any later # version. # Given a file, search Debian packages that can somehow handle it from __future__ import print_function import sys # Requires python-extractor, python-magic and python-debtags import extractor import magic from debian import debtags import re from optparse import OptionParser import apt VERSION="0.1" mime_map = ( ( r'text/html\b', ("works-with::text","works-with-format::html") ), ( r'text/plain\b', ("works-with::text","works-with-format::plaintext") ), ( r'text/troff\b', ("works-with::text", "works-with-format::man") ), ( r'image/', ("works-with::image",) ), ( r'image/jpeg\b', ("works-with::image:raster","works-with-format::jpg") ), ( r'image/png\b', ("works-with::image:raster","works-with-format::png") ), ( r'application/pdf\b', ("works-with::text","works-with-format::pdf")), ( r'application/postscript\b', ("works-with::text","works-with-format::postscript")), ( r'application/x-iso9660\b', ('works-with-format::iso9660',)), ( r'application/zip\b', ('works-with::archive', 'works-with-format::zip')), ( r'application/x-tar\b', ('works-with::archive', 'works-with-format::tar')), ( r'audio/', ("works-with::audio",) ), ( r'audio/mpeg\b', ("works-with-format::mp3",) ), ( r'audio/x-wav\b', ("works-with-format::wav",) ), ( r'message/rfc822\b', ("works-with::mail",) ), ( r'video/', ("works-with::video",)), ( r'application/x-debian-package\b', ("works-with::software:package",)), ( r'application/vnd.oasis.opendocument.text\b', ("works-with::text",)), ( r'application/vnd.oasis.opendocument.graphics\b', ("works-with::image:vector",)), ( r'application/vnd.oasis.opendocument.spreadsheet\b', ("works-with::spreadsheet",)), ( r'application/vnd.sun.xml.base\b', ("works-with::db",)), ( r'application/rtf\b', ("works-with::text",)), ( r'application/x-dbm\b', ("works-with::db",)), # How do we tell these two apart? # ( 'application/ogg', ('works-with-format::oggvorbis',)), # ( 'application/ogg', ('works-with-format::oggtheora',)), # Missing tags # ( 'application/vnd.oasis.opendocument.presentation', ), # Still unhandled/unhandlable: # works-with::3dmodel - 3D Model # works-with::dictionary - Dictionaries # works-with::dtp - Desktop Publishing (DTP) # works-with::fax - Faxes # works-with::file - Files # works-with::font - Fonts # works-with::logfile - System Logs # works-with::music-notation - Music Notation # works-with::people - People # works-with::pim - Personal Information # works-with::software:source - Source code # works-with::spreadsheet - Spreadsheet # works-with::text - Text # works-with::unicode - Unicode # works-with-format::docbook - Docbook # works-with-format::info - Documentation in Info format # works-with-format::ldif - LDIF # works-with-format::sgml - SGML, Standard Generalized Markup Language # works-with-format::svg - SVG, Scalable Vector Graphics # works-with-format::tex - TeX, LaTeX and DVI # works-with-format::vrml - VRML 3D Model # works-with-format::xml - XML # works-with-format::xml:rss - RSS Rich Site Summary # works-with-format::xml:xslt - XSL Transformations (XSLT) ) extractor = extractor.Extractor() magic = magic.open(magic.MAGIC_MIME) magic.load() def mimetype(fname): keys = extractor.extract(fname) xkeys = {} for k, v in keys: if k in xkeys: xkeys[k].append(v) else: xkeys[k] = [v] namemagic = magic.file(fname) contentmagic = magic.buffer(open(fname, "r").read(4096)) return "mimetype" in xkeys and xkeys['mimetype'][0] or contentmagic or namemagic class Parser(OptionParser): def __init__(self, *args, **kwargs): OptionParser.__init__(self, *args, **kwargs) def error(self, msg): sys.stderr.write("%s: error: %s\n\n" % (self._get_prog_name(), msg)) self.print_help(sys.stderr) sys.exit(2) if __name__ == '__main__': parser = Parser(usage="usage: %prog [options] filename", version="%prog "+ VERSION, description="search Debian packages that can handle a given file") parser.add_option("--tagdb", default="/var/lib/debtags/package-tags", help="Tag database to use (default: %default)") parser.add_option("--action", default=None, help="Show the packages that allow the given action on the file (default: %default)") (options, args) = parser.parse_args() if len(args) == 0: parser.error("Please provide the name of a file to scan") # Read full database fullcoll = debtags.DB() tag_filter = re.compile(r"^special::.+$|^.+::TODO$") fullcoll.read(open(options.tagdb, "r"), lambda x: not tag_filter.match(x)) type = mimetype(args[0]) #print("Mime type:", type, file=sys.stderr) found = set() for match, tags in mime_map: match = re.compile(match) if match.match(type): for t in tags: found.add(t) if len(found) == 0: print("Unhandled mime type:", type, file=sys.stderr) else: if options.action != None: apt_cache = apt.Cache() query = found.copy() query.add("role::program") query.add("use::"+options.action) print("Debtags query:", " && ".join(query)) subcoll = fullcoll.filter_packages_tags(lambda pt: query.issubset(pt[1])) for i in subcoll.iter_packages(): aptpkg = apt_cache[i] desc = aptpkg.raw_description.split("\n")[0] print(i, "-", desc) else: print("Debtags query:", " && ".join(found)) query = found.copy() query.add("role::program") subcoll = fullcoll.filter_packages_tags(lambda pt: query.issubset(pt[1])) uses = map(lambda x:x[5:], filter(lambda x:x.startswith("use::"), subcoll.iter_tags())) print("Available actions:", ", ".join(uses)) # vim:set ts=4 sw=4: python-debian-0.1.21+nmu2ubuntu1/examples/debtags/smartsearch0000755000000000000000000001731512015175026021122 0ustar #!/usr/bin/python # debtags - Implement package tags support for Debian # # Copyright (C) 2003--2006 Enrico Zini # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA from __future__ import print_function import sys import re import subprocess from debian import debtags import apt class SmartSearcher: def __init__(self, fullcoll, query): self.apt_cache = apt.Cache() self.wanted = set() self.unwanted = set() self.ignored = set() self.interesting = [] self.tags_in_menu = [] self.fullcoll = fullcoll self.subcoll = fullcoll # Initialise the search self.compute_interesting(query) def compute_interesting(self, query): input = subprocess.Popen("apt-cache search " + query, shell=True, stdout = subprocess.PIPE, close_fds = True) # Read the package list and create the subcollection pkgs = [] for pkg in input.stdout: pkg, none = pkg.rstrip("\n").split(' - ', 1) #print(pkg) pkgs.append(pkg) subcoll = self.fullcoll.choose_packages(pkgs) rel_index = debtags.relevance_index_function(self.fullcoll, subcoll) # Get all the tags sorted by increasing relevance self.interesting = sorted(self.subcoll.iter_tags(), lambda b, a: cmp(rel_index(a), rel_index(b))) def tag_match(self, pkg): tags = self.fullcoll.tags_of_package(pkg) if len(self.wanted) > 0 and not self.wanted.issubset(tags): return False if len(self.unwanted) > 0 and len(tags.intersection(self.unwanted)) > 0: return False return True def refilter(self): # Regenerate subcoll self.subcoll = self.fullcoll.filter_packages(self.tag_match) def show_set(self, tags, type): for tag in tags: self.tags_in_menu.append(tag) print("%d) %s (%s)" % (len(self.tags_in_menu), tag, type)) def show_choice_sequence(self, seq, max = 7): for tag in seq: if tag in self.wanted or tag in self.unwanted or tag in self.ignored: continue self.tags_in_menu.append(tag) print("%d) %s (%d/%d)" % \ (len(self.tags_in_menu), tag, self.subcoll.card(tag), self.subcoll.package_count())) max = max - 1 if max == 0: break def show_tags(self): self.tags_in_menu = [] self.show_set(self.wanted, "wanted") self.show_set(self.unwanted, "unwanted") self.show_set(self.ignored, "ignored") print self.show_choice_sequence(self.interesting) print # Compute the most interesting tags by discriminance discr = sorted(self.subcoll.iter_tags(), \ lambda a, b: cmp(self.subcoll.discriminance(a), self.subcoll.discriminance(b))) self.show_choice_sequence(discr) def output_packages(self): for pkg in self.subcoll.iter_packages(): aptpkg = self.apt_cache[pkg] desc = aptpkg.raw_description.split("\n")[0] print(pkg, "-", desc) def interact(self): done = False while not done: print("Tag selection:") self.show_tags() print(self.subcoll.package_count(), " packages selected so far.") changed = False sys.stdout.write("Your choice (+#, -#, =#, K word, View, Done, Quit, ?): ") ans = sys.stdin.readline() if ans == None: break ans = ans.strip(" \t\n") # If we're setting a new keyword search, process now and skip # processing as a list if ans == "?": print("+ number select the tag with the given number as a tag you want") print("- number select the tag with the given number as a tag you do not want") print("= number select the tag with the given number as a tag you don't care about") print("K word recompute the set of interesting tags from a full-text search using the given word") print("V view the packages selected so far") print("D print the packages selected so far and exit") print("Q quit debtags smart search") print("? print this help information") elif ans[0] == 'k' or ans[0] == 'K': # Strip initial command and empty spaces ans = ans[1:].strip(); if len(ans) == 0: print("The 'k' command needs a keyword to use for finding new interesting tags.") else: self.compute_interesting(ans) ans = '' else: # Split the answer by spaces for cmd in ans.split(): if cmd[0] == '+' or cmd[0] == '-' or cmd[0] == '=': try: idx = int(cmd[1:]) except ValueError: print(cmd, "should have a number after +, - or =") continue if idx > len(self.tags_in_menu): print("Tag", idx, "was not on the menu.") else: tag = self.tags_in_menu[idx - 1] # cout << "Understood " << ans << " as " << ans[0] << tag.fullname() << endl; if cmd[0] == '+': self.wanted.add(tag) if tag in self.unwanted: self.unwanted.remove(tag) if tag in self.ignored: self.ignored.remove(tag) if cmd[0] == '-': if tag in self.wanted: self.wanted.remove(tag) self.unwanted.add(tag) if tag in self.ignored: self.ignored.remove(tag) if cmd[0] == '=': if tag in self.wanted: self.wanted.remove(tag) if tag in self.unwanted: self.unwanted.remove(tag) self.ignored.add(tag) changed = True elif cmd == "V" or cmd == "v": self.output_packages() elif cmd == "D" or cmd == "d": self.output_packages() done = True; elif cmd == "Q" or cmd == "q": done = True; else: print("Ignoring command \"%s\"" % (cmd)) if changed: self.refilter() if len(sys.argv) < 3: print("Usage: %s tagdb keywords..." % (sys.argv[0]), file=sys.stderr) sys.exit(1) # Main starts fullcoll = debtags.DB() # Read full database tag_filter = re.compile(r"^special::.+$|^.+::TODO$") fullcoll.read(open(sys.argv[1], "r"), lambda x: not tag_filter.match(x)) searcher = SmartSearcher(fullcoll, " ".join(sys.argv[2:])) searcher.interact() # vim:set ts=4 sw=4 expandtab: python-debian-0.1.21+nmu2ubuntu1/examples/debtags/pkgwalk0000755000000000000000000000704212015175027020243 0ustar #!/usr/bin/python # # Copyright (C) 2007 Enrico Zini # # This program is free software; you can redistribute it and/or modify it under # the terms of the GNU General Public License as published by the Free Software # Foundation; either version 2 of the License, or (at your option) any later # version. # Navigate among related Debian packages from __future__ import print_function import sys # Requires python-extractor, python-magic and python-debtags from debian import debtags import re from optparse import OptionParser import apt VERSION="0.1" class Parser(OptionParser): def __init__(self, *args, **kwargs): OptionParser.__init__(self, *args, **kwargs) def error(self, msg): sys.stderr.write("%s: error: %s\n\n" % (self.get_prog_name(), msg)) self.print_help(sys.stderr) sys.exit(2) if __name__ == '__main__': parser = Parser(usage="usage: %prog [options] pkgname", version="%prog "+ VERSION, description="walk through Debian packages") parser.add_option("--tagdb", default="/var/lib/debtags/package-tags", help="Tag database to use (default: %default)") (options, args) = parser.parse_args() if len(args) == 0: parser.error("Please provide the name of an initial package") # Read full database db = debtags.DB() tag_filter = re.compile(r"^special::.+$|^.+::TODO$") db.read(open(options.tagdb, "r"), lambda x: not tag_filter.match(x)) apt_cache = apt.Cache() # Maximum number of previous packages to remember maxlen = 5 # Initial package selection trail = [ args[0] ] # Loop until the user chooses to quit done = False while not done: # Compute a package weight according to how old it is in the # trail pkgweight = {} for idx, pkg in enumerate(trail): pkgweight[pkg] = 1.-(idx/maxlen) # For every tag, find the number of packages in trail that have the tag tagscores = {} for pkg in trail: for tag in db.tags_of_package(pkg): if tag in tagscores: tagscores[tag] += pkgweight[pkg] else: tagscores[tag] = pkgweight[pkg] # Divide every tag score by the number of packages in the trail, # obtaining a 'tag weight'. A package can be later scored by summing # the weight of all its tags. for tag in tagscores: tagscores[tag] = float(tagscores[tag]) / float(len(trail)) # Find the merged tagset of the packages in trail trailtags = set(tagscores.keys()) # Get the list of packages whose tagsets intersect the trail tagset nextpkgs = set() for pkg, tags in db.iter_packages_tags(): if trailtags & tags: nextpkgs.add(pkg) # Score every package by the sum of the weight of its tags def pkgscore(pkg): score = 0.0 for tag in db.tags_of_package(pkg): if tag in tagscores: score += tagscores[tag] return score # Show the first 20 packages in reverse score order #display = sorted(nextpkgs - set(trail), key=pkgscore, reverse=True)[:20] display = sorted(nextpkgs, key=pkgscore, reverse=True)[:20] for num, pkg in enumerate(display): aptpkg = apt_cache[pkg] desc = aptpkg.raw_description.split("\n")[0] print("%2d) %s - %s" % (num + 1, pkg, desc)) # Ask the user to choose a new package while True: if sys.version >= '3': ans = input("> ").strip() else: ans = raw_input("> ").strip() if ans[0] == 'q': done = True break elif ans.isdigit(): num = int(ans) - 1 if num < len(display): # TODO: on a different kind of interface, display the full # description of pkg trail = [display[num]] + trail[:maxlen] break else: print("The number is too high") # vim:set ts=4 sw=4: python-debian-0.1.21+nmu2ubuntu1/examples/debtags/wxssearch0000755000000000000000000004030712015175026020612 0ustar #!/usr/bin/python # debtags - Implement package tags support for Debian # # Copyright (C) 2003--2006 Enrico Zini # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA from __future__ import print_function import wx import wx.html import wx.lib.dialogs import sys import re import subprocess from debian import debtags import apt from optparse import OptionParser VERSION="0.1" class Model(wx.EvtHandler): class ModelEvent(wx.PyCommandEvent): def __init__(self, event_type): wx.PyCommandEvent.__init__(self, event_type) # Create custom events wxEVT_CHANGED = wx.NewEventType() EVT_CHANGED = wx.PyEventBinder(wxEVT_CHANGED, 0) def __init__(self, fullcoll): wx.EvtHandler.__init__(self) self.apt_cache = apt.Cache() self.wanted = set() self.unwanted = set() self.ignored = set() self.interesting = [] self.discriminant = [] self.uninteresting = [] self.fullcoll = fullcoll self.subcoll = fullcoll self.refilter() def tag_match(self, pkg): tags = self.fullcoll.tags_of_package(pkg) if len(self.wanted) > 0 and not self.wanted.issubset(tags): return False if len(self.unwanted) > 0 and len(tags.intersection(self.unwanted)) > 0: return False return True def refilter(self): # Regenerate subcoll if len(self.wanted) == 0 and len(self.unwanted) == 0: self.subcoll = self.fullcoll else: self.subcoll = self.fullcoll.filter_packages(self.tag_match) # Compute the most interesting tags by discriminance self.discriminant = sorted(self.subcoll.iter_tags(), \ lambda b, a: cmp(self.subcoll.discriminance(a), self.subcoll.discriminance(b))) #print("-----------------------------") #for d in self.discriminant: # print(d, self.subcoll.discriminance(d)) # Notify the change e = Model.ModelEvent(Model.wxEVT_CHANGED) self.ProcessEvent(e) def add_wanted(self, tag): self.wanted.add(tag) if tag in self.unwanted: self.unwanted.remove(tag) if tag in self.ignored: self.ignored.remove(tag) self.refilter() def add_unwanted(self, tag): self.unwanted.add(tag) if tag in self.wanted: self.wanted.remove(tag) if tag in self.ignored: self.ignored.remove(tag) self.refilter() def add_ignored(self, tag): self.ignored.add(tag) if tag in self.wanted: self.wanted.remove(tag) if tag in self.unwanted: self.unwanted.remove(tag) self.refilter() def remove_tag_from_filter(self, tag): if tag in self.wanted: self.wanted.remove(tag) if tag in self.unwanted: self.unwanted.remove(tag) if tag in self.ignored: self.ignored.remove(tag) self.refilter() def set_query(self, query): query = query.split() def match(pkg): for q in query: try: if pkg.name.find(q) == -1 and \ pkg.raw_description.find(q) == -1: return False except UnicodeDecodeError: desc = pkg.raw_description.decode("ascii", "replace") if pkg.name.find(q) == -1 and \ desc.find(q) == -1: return False return True subcoll = self.fullcoll.choose_packages(map(lambda x: x.name, filter(match, self.apt_cache))) rel_index = debtags.relevance_index_function(self.fullcoll, subcoll) # Get all the tags sorted by increasing relevance self.interesting = sorted(subcoll.iter_tags(), lambda b, a: cmp(rel_index(a), rel_index(b))) # Get the list of the uninteresting tags, sorted by cardinality (the # ones that you are less likely to want, and first the ones that will # weed out most results) self.uninteresting = set(self.fullcoll.iter_tags()) self.uninteresting -= set(subcoll.iter_tags()) if len(self.wanted) == 0: self.wanted = set(fullcoll.ideal_tagset(self.interesting)) self.refilter() # Notify the change e = Model.ModelEvent(Model.wxEVT_CHANGED) self.ProcessEvent(e) class TagList(wx.html.HtmlWindow): class TagListEvent(wx.PyCommandEvent): def __init__(self, event_type): wx.PyCommandEvent.__init__(self, event_type) self.action = None self.tag = None # Create custom events wxEVT_ACTION = wx.NewEventType() EVT_ACTION = wx.PyEventBinder(wxEVT_ACTION, 0) def __init__(self, parent, model): wx.html.HtmlWindow.__init__(self, parent) self.model = model self.model.Bind(Model.EVT_CHANGED, self.model_changed) self.SetBorders(0) self.SetFonts("", "", [4, 6, 8, 10, 11, 12, 13]) def OnLinkClicked(self, link_info): # Notify the change action, tag = link_info.GetHref().split(':',1) e = TagList.TagListEvent(TagList.wxEVT_ACTION) e.action, e.tag = action, tag self.ProcessEvent(e) def model_changed(self, event): #print("TLMC") self.SetPage("") first = True if len(self.model.wanted): if first: first = False; else: self.AppendToPage("
") self.AppendToPage("Wanted:
") for tag in self.model.wanted: self.AppendToPage("   %s
" % (tag, tag)) if len(self.model.unwanted): if first: first = False; else: self.AppendToPage("
") self.AppendToPage("Not wanted:
") for tag in self.model.unwanted: self.AppendToPage("   %s
" % (tag, tag)) def filter_candidate(tag): if self.model.subcoll.card(tag) == 0: return False if tag in self.model.wanted: return False if tag in self.model.unwanted: return False if tag in self.model.ignored: return False return True interesting = filter(filter_candidate, self.model.interesting)[:7] if len(interesting): if first: first = False; else: self.AppendToPage("
") self.AppendToPage("Candidate tags:
") for tag in interesting: self.AppendToPage("   %s [no] (%d pkgs)
" % (tag, tag, tag, self.model.subcoll.card(tag))) discr = filter(filter_candidate, self.model.discriminant)[:7] if len(discr): if first: first = False; else: self.AppendToPage("
") for tag in discr: self.AppendToPage("   %s [no] (%d pkgs)
" % (tag, tag, tag, self.model.subcoll.card(tag))) unint = filter(filter_candidate, sorted(self.model.uninteresting, lambda a,b: cmp(self.model.subcoll.card(b), self.model.subcoll.card(a))))[:7] if len(unint): if first: first = False; else: self.AppendToPage("
") self.AppendToPage("Candidate unwanted tags:
") for tag in unint: self.AppendToPage("   %s [yes] (%d pkgs)
" % (tag, tag, tag, self.model.subcoll.card(tag))) self.AppendToPage("") event.Skip() class Results(wx.ListCtrl): def __init__(self, parent, model): wx.ListCtrl.__init__(self, parent, style=wx.LC_REPORT|wx.LC_VIRTUAL) self.model = model self.model.Bind(Model.EVT_CHANGED, self.model_changed) self.packages = [] self.InsertColumn(0, "Name") self.InsertColumn(1, "Description") self.SetColumnWidth(0, wx.LIST_AUTOSIZE) self.SetColumnWidth(1, wx.LIST_AUTOSIZE) self.Bind(wx.EVT_SIZE, self.OnResize) def resize_columns(self): """ Ugly hack to have some decent size for the columns, since the ListCtrl appearently won't autosize itself. """ w, h = self.GetClientSizeTuple() self.SetColumnWidth(0, w * 0.3) # -20 to hope to account for the vertical scrollbar, when it's present self.SetColumnWidth(1, w * 0.7 - 20) def model_changed(self, event): self.packages = sorted(self.model.subcoll.iter_packages()) self.SetItemCount(len(self.packages)) self.resize_columns() event.Skip() def OnResize(self, event): self.resize_columns() event.Skip() def OnGetItemText(self, row, col): if col == 0: return self.packages[row] else: aptpkg = self.model.apt_cache[self.packages[row]] return aptpkg.raw_description.split("\n")[0] class SearchWindow(wx.Frame): ACTION_QUIT = wx.NewId() ACTION_CONTEXT_HELP = wx.NewId() ACTION_ABOUT = wx.NewId() def __init__(self, parent, model, title): wx.Frame.__init__(self, parent, -1, title, style=wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE) self.status_bar = self.CreateStatusBar() # Create menu bar self.menu_bar = wx.MenuBar() m_file = wx.Menu() m_file.Append(SearchWindow.ACTION_QUIT, "&Quit", "Quit wxdballe") self.menu_bar.Append(m_file, "&File") m_file = wx.Menu() m_file.Append(SearchWindow.ACTION_CONTEXT_HELP, "&What is...", "Show context information about interface elements") m_file.Append(SearchWindow.ACTION_ABOUT, "&About...", "Show information about this application") self.menu_bar.Append(m_file, "&Help") self.SetMenuBar(self.menu_bar) self.Bind(wx.EVT_MENU, self.on_action) self.model = model self.model.Bind(Model.EVT_CHANGED, self.model_changed) self.SetSizeHints(500, 500) query_panel = wx.Panel(self, style=wx.SUNKEN_BORDER) query_field = wx.Panel(query_panel) self.query = wx.TextCtrl(query_field) self.query.SetHelpText("Enter here some keyword about what you are looking for. They will lead you to a selection of categories that you can use to browse the packages") self.query.Bind(wx.EVT_CHAR, self.query_changed) # self.query.Bind(wx.EVT_TEXT, self.query_changed) self.query_button = wx.Button(query_field, -1, "Go", style=wx.BU_EXACTFIT) self.query_button.Bind(wx.EVT_BUTTON, self.go_button_pressed) self.query_button.SetHelpText("Look for keyword corresponding to the text entered in the Search field.") box = wx.BoxSizer(wx.HORIZONTAL) box.Add(wx.StaticText(query_field, -1, "Search: "), 0, wx.ALIGN_CENTER_VERTICAL) box.Add(self.query, 1, wx.EXPAND) box.Add(self.query_button, 0, wx.ALIGN_CENTER_VERTICAL) query_field.SetSizerAndFit(box) self.tag_list = TagList(query_panel, model) self.tag_list.Bind(TagList.EVT_ACTION, self.wanted_event) self.tag_list.SetHelpText("Tags used for searching. Candidate tags are up for selection: click on a tag to say that you want it, and click on 'no' next to it to say that you do not want it. To remove a tag from the 'wanted' or 'unwanted' list, just click on it") box = wx.BoxSizer(wx.VERTICAL) box.Add(query_field, 0, wx.EXPAND) box.Add(self.tag_list, 3, wx.EXPAND) query_panel.SetSizerAndFit(box) self.results = Results(self, model) self.results.SetHelpText("List of packages matching the current selecion of tags") box = wx.BoxSizer(wx.HORIZONTAL) box.Add(query_panel, 2, wx.EXPAND) box.Add(self.results, 3, wx.EXPAND) self.SetSizerAndFit(box) self.timed_updater = None self.Bind(wx.EVT_CHAR, self.on_char) self.query.SetFocus() def on_action(self, event): id = event.GetId() if id == SearchWindow.ACTION_QUIT: self.Destroy() elif id == SearchWindow.ACTION_ABOUT: msg = """ Prototype for a new concept of package search ============================================= Introduction ------------ Debtags Smart Search is an attempt to use the Debtags category data to create a powerful and intuitive search metaphore. It combines the ease of use of a keyword search text box with the unambiguiry and accuracy of categories. The keywords entered are not used to filter packages, but to create a selection of relevant tags for the user to choose from. The actual package filtering is done with the tags only. The way the user interact with tags is through simple "I want this"/"I don't want this" decisions. Tips ---- * you can be vague while giving keywords, since you can always refine later using the tags * you can change the search keywords in the middle of a search, to get a different selection of candidate tags. """ dia = wx.lib.dialogs.ScrolledMessageDialog(self, msg, "About Debtags Smart Search") dia.ShowModal() elif id == SearchWindow.ACTION_CONTEXT_HELP: context_help = wx.ContextHelp() context_help.BeginContextHelp() def on_char(self, event): c = event.GetKeyCode() # Quit on ^Q if c == 17: self.Destroy() def wanted_event(self, event): action, tag = event.action, event.tag if action == 'add': self.model.add_wanted(tag) elif action == 'addnot': print("wanted_event -> addnot") self.model.add_unwanted(tag) elif action == 'del': self.model.remove_tag_from_filter(tag) else: print("Unknown action", action) def go_button_pressed(self, event): self.model.set_query(self.query.GetValue()) def query_changed(self, event): "Delayed update of the filter from the value of the input fields" c = event.GetKeyCode() if c == 13: self.model.set_query(self.query.GetValue()) event.Skip() def model_changed(self, event): self.status_bar.SetStatusText("%d packages" % (self.model.subcoll.package_count())) event.Skip() ## ### Main ### class Parser(OptionParser): def __init__(self, *args, **kwargs): OptionParser.__init__(self, *args, **kwargs) def error(self, msg): sys.stderr.write("%s: error: %s\n\n" % (self._get_prog_name(), msg)) self.print_help(sys.stderr) sys.exit(2) if __name__ == "__main__": parser = Parser(usage="usage: %prog [options]", version="%prog "+ VERSION, description="smart search of Debian packages") parser.add_option("--tagdb", default="/var/lib/debtags/package-tags", help="Tag database to use (default: %default)") (options, args) = parser.parse_args() app = wx.PySimpleApp() provider = wx.SimpleHelpProvider() wx.HelpProvider_Set(provider) # Read full database fullcoll = debtags.DB() tag_filter = re.compile(r"^special::.+$|^.+::TODO$") fullcoll.read(open(options.tagdb, "r"), lambda x: not tag_filter.match(x)) model = Model(fullcoll) sw = SearchWindow(None, model, "Debtags Smart Search") sw.Show() app.MainLoop() # vim:set ts=4 sw=4 expandtab: python-debian-0.1.21+nmu2ubuntu1/README.changelog0000644000000000000000000001133111703667743016246 0ustar README for debian.changelog --------------------------- The aim of this module is to provide programmatic access to Debian changelogs to query and manipulate them. N.B. The API is not stable yet, and so you can expect it to change. Create a changelog object using the constuctor. Pass it the contents of the file if there are some entries, or None to create an empty changelog. See /usr/share/doc/python-debian/examples/changelog/ for examples of usage. If you have the full contents of a changelog, but are only interested in the most recent versions you can pass the max_blocks keyword parameter to the constuctor to limit the number of blocks of the changelog that will be parsed. If you are only interested in the most recent version of the package then pass max_blocks=1. The Changelog class provides the following interesting attributes and methods: * version A Version object representing the version of the last block. You may assign to it a Version object or a full version string. * full_version The full version number of the last version. * debian_version The debian part of the version number of the last version. * upstream_version The upstream part of the version number of the last version. * epoch The epoch number of the last revision, or None if no epoch was used. * versions A list of Version objects that the package went through. These version objects provide all attributes named the same as the above to get that same information. You cannot assign to this attribute. * package The name of the package in the last version. * distributions A string indicating the distributions that the package will be uploaded to in the most recent version. * urgency A string indicating the urgency with which the most recent version will be uploaded. * author The author of the most recent change. This should be a properly formatted name/email pair. * date The date associated with the current entry. Should be a properly formatted string with the date and timezone. * parse_changelog(file) If you create an Changelog object without sepcifying a changelog file, you can parse a changelog file with this method. If the changelog doesn't parse cleanly, a ChangelogParseError exception is thrown. The constructor will parse the changelog on a best efford basis. * add_change(change) Adds a change entry to the most recent version. The change entry should conform to the required format of the changelog (i.e. start with two spaces). No line wrapping or anything will be performed, so it is advisable to do this yourself if it is a long entry. The change will be appended to he current changes, no support is provided for per-maintainer changes. * new_block(package, version, distributions, urgency, changes, author, date) Start a new version of the package. The arguments (all optional) specify the values that can be provided to the set_* methods. If they are omitted the associated attributes must be assigned to before the changelog is created. * write_to_open_file(file) Write the changelog out to the filehandle passed. The file argument must be an open file object. To get the properly formatted changelog back out of the object merely call str() on it. The returned string should be a properly formatted changelog. There are a number of errors that may be thrown by the module. * ChangelogParseError Indicates that the changelog could not be parsed, i.e. there is a line that does not conform to the requirements, or a line was found out of its normal position. May be thrown when using the method parse_changelog. The constructor will not throw this exception. * ChangelogCreateError Some information required to create the changelog was not available. This can be thrown when str() is used on the object, and will occur if a required value is None. * VersionError The string used to create a Version object cannot be parsed as it doesn't conform to the specification of a version number. Can be thrown when creating a Changelog object from an existing changelog, or instantiating a Version object directly to assign to the version attribute of a Changelog object. If you have a changelog that may have no author information yet as it is still a work in progress, i.e. the author line is just -- rather than -- Author Thu, 12 Dec 2006 12:23:34 +0000 then you can pass allow_empty_author=True to the Changelog constructor. If you do this then the ``author`` and ``date`` attributes may be ``None``. This file is (C) 2006-7 James Westby, and licensed under the GPL, see changelog.py for details. python-debian-0.1.21+nmu2ubuntu1/lib/0000755000000000000000000000000011703670441014174 5ustar python-debian-0.1.21+nmu2ubuntu1/lib/debian_bundle/0000755000000000000000000000000011705315273016750 5ustar python-debian-0.1.21+nmu2ubuntu1/lib/debian_bundle/__init__.py0000644000000000000000000000042411703667743021073 0ustar import os import warnings warnings.warn("please use 'debian' instead of 'debian_bundle'", DeprecationWarning, stacklevel=2) # Support "from debian_bundle import foo" parent_dir = os.path.dirname(__path__[0]) __path__.append(os.path.join(parent_dir, "debian")) python-debian-0.1.21+nmu2ubuntu1/lib/debian/0000755000000000000000000000000012021625761015415 5ustar python-debian-0.1.21+nmu2ubuntu1/lib/debian/__init__.py0000644000000000000000000000000311703667743017533 0ustar python-debian-0.1.21+nmu2ubuntu1/lib/debian/debtags.py0000644000000000000000000003441112015175026017400 0ustar # debtags.py -- Access and manipulate Debtags information # Copyright (C) 2006-2007 Enrico Zini # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 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 . from __future__ import absolute_import, print_function import re try: import cPickle as pickle except ImportError: import pickle import six from debian.deprecation import function_deprecated_by def parse_tags(input): lre = re.compile(r"^(.+?)(?::?\s*|:\s+(.+?)\s*)$") for line in input: # Is there a way to remove the last character of a line that does not # make a copy of the entire line? m = lre.match(line) pkgs = set(m.group(1).split(', ')) if m.group(2): tags = set(m.group(2).split(', ')) else: tags = set() yield pkgs, tags parseTags = function_deprecated_by(parse_tags) def read_tag_database(input): "Read the tag database, returning a pkg->tags dictionary" db = {} for pkgs, tags in parse_tags(input): # Create the tag set using the native set for p in pkgs: db[p] = tags.copy() return db; readTagDatabase = function_deprecated_by(read_tag_database) def read_tag_database_reversed(input): "Read the tag database, returning a tag->pkgs dictionary" db = {} for pkgs, tags in parse_tags(input): # Create the tag set using the native set for tag in tags: if tag in db: db[tag] |= pkgs else: db[tag] = pkgs.copy() return db; readTagDatabaseReversed = function_deprecated_by(read_tag_database_reversed) def read_tag_database_both_ways(input, tag_filter = None): "Read the tag database, returning a pkg->tags and a tag->pkgs dictionary" db = {} dbr = {} for pkgs, tags in parse_tags(input): # Create the tag set using the native set if tag_filter == None: tags = set(tags) else: tags = set(filter(tag_filter, tags)) for pkg in pkgs: db[pkg] = tags.copy() for tag in tags: if tag in dbr: dbr[tag] |= pkgs else: dbr[tag] = pkgs.copy() return db, dbr; readTagDatabaseBothWays = function_deprecated_by(read_tag_database_both_ways) def reverse(db): "Reverse a tag database, from package -> tags to tag->packages" res = {} for pkg, tags in db.items(): for tag in tags: if tag not in res: res[tag] = set() res[tag].add(pkg) return res def output(db): "Write the tag database" for pkg, tags in db.items(): # Using % here seems awkward to me, but if I use calls to # sys.stdout.write it becomes a bit slower print("%s:" % (pkg), ", ".join(tags)) def relevance_index_function(full, sub): #return (float(sub.card(tag)) / float(sub.tag_count())) / \ # (float(full.card(tag)) / float(full.tag_count())) #return sub.card(tag) * full.card(tag) / sub.tag_count() # New cardinality divided by the old cardinality #return float(sub.card(tag)) / float(full.card(tag)) ## Same as before, but weighted by the relevance the tag had in the ## full collection, to downplay the importance of rare tags #return float(sub.card(tag) * full.card(tag)) / float(full.card(tag) * full.tag_count()) # Simplified version: #return float(sub.card(tag)) / float(full.tag_count()) # Weighted by the square root of the relevance, to downplay the very # common tags a bit #return lambda tag: float(sub.card(tag)) / float(full.card(tag)) * math.sqrt(full.card(tag) / float(full.tag_count())) #return lambda tag: float(sub.card(tag)) / float(full.card(tag)) * math.sqrt(full.card(tag) / float(full.package_count())) # One useless factor removed, and simplified further, thanks to Benjamin Mesing return lambda tag: float(sub.card(tag)**2) / float(full.card(tag)) # The difference between how many packages are in and how many packages are out # (problems: tags that mean many different things can be very much out # as well. In the case of 'image editor', for example, there will be # lots of editors not for images in the outside group. # It is very, very good for nonambiguous keywords like 'image'. #return lambda tag: 2 * sub.card(tag) - full.card(tag) # Same but it tries to downplay the 'how many are out' value in the # case of popular tags, to mitigate the 'there will always be popular # tags left out' cases. Does not seem to be much of an improvement. #return lambda tag: sub.card(tag) - float(full.card(tag) - sub.card(tag))/(math.sin(float(full.card(tag))*3.1415/full.package_count())/4 + 0.75) relevanceIndexFunction = function_deprecated_by(relevance_index_function) class DB: """ In-memory database mapping packages to tags and tags to packages. """ def __init__(self): self.db = {} self.rdb = {} def read(self, input, tag_filter=None): """ Read the database from a file. Example:: # Read the system Debtags database db.read(open("/var/lib/debtags/package-tags", "r")) """ self.db, self.rdb = read_tag_database_both_ways(input, tag_filter) def qwrite(self, file): "Quickly write the data to a pickled file" pickle.dump(self.db, file) pickle.dump(self.rdb, file) def qread(self, file): "Quickly read the data from a pickled file" self.db = pickle.load(file) self.rdb = pickle.load(file) def insert(self, pkg, tags): self.db[pkg] = tags.copy() for tag in tags: if tag in self.rdb: self.rdb[tag].add(pkg) else: self.rdb[tag] = set((pkg)) def dump(self): output(self.db) def dump_reverse(self): output(self.rdb) dumpReverse = function_deprecated_by(dump_reverse) def reverse(self): "Return the reverse collection, sharing tagsets with this one" res = DB() res.db = self.rdb res.rdb = self.db return res def facet_collection(self): """ Return a copy of this collection, but replaces the tag names with only their facets. """ fcoll = DB() tofacet = re.compile(r"^([^:]+).+") for pkg, tags in self.iter_packagesTags(): ftags = set([tofacet.sub(r"\1", t) for t in tags]) fcoll.insert(pkg, ftags) return fcoll facetCollection = function_deprecated_by(facet_collection) def copy(self): """ Return a copy of this collection, with the tagsets copied as well. """ res = DB() res.db = self.db.copy() res.rdb = self.rdb.copy() return res def reverse_copy(self): """ Return the reverse collection, with a copy of the tagsets of this one. """ res = DB() res.db = self.rdb.copy() res.rdb = self.db.copy() return res reverseCopy = function_deprecated_by(reverse_copy) def choose_packages(self, package_iter): """ Return a collection with only the packages in package_iter, sharing tagsets with this one """ res = DB() db = {} for pkg in package_iter: if pkg in self.db: db[pkg] = self.db[pkg] res.db = db res.rdb = reverse(db) return res choosePackages = function_deprecated_by(choose_packages) def choose_packages_copy(self, package_iter): """ Return a collection with only the packages in package_iter, with a copy of the tagsets of this one """ res = DB() db = {} for pkg in package_iter: db[pkg] = self.db[pkg] res.db = db res.rdb = reverse(db) return res choosePackagesCopy = function_deprecated_by(choose_packages_copy) def filter_packages(self, package_filter): """ Return a collection with only those packages that match a filter, sharing tagsets with this one. The filter will match on the package. """ res = DB() db = {} for pkg in filter(package_filter, six.iterkeys(self.db)): db[pkg] = self.db[pkg] res.db = db res.rdb = reverse(db) return res filterPackages = function_deprecated_by(filter_packages) def filter_packages_copy(self, filter): """ Return a collection with only those packages that match a filter, with a copy of the tagsets of this one. The filter will match on the package. """ res = DB() db = {} for pkg in filter(filter, six.iterkeys(self.db)): db[pkg] = self.db[pkg].copy() res.db = db res.rdb = reverse(db) return res filterPackagesCopy = function_deprecated_by(filter_packages_copy) def filter_packages_tags(self, package_tag_filter): """ Return a collection with only those packages that match a filter, sharing tagsets with this one. The filter will match on (package, tags). """ res = DB() db = {} for pkg, tags in filter(package_tag_filter, six.iteritems(self.db)): db[pkg] = self.db[pkg] res.db = db res.rdb = reverse(db) return res filterPackagesTags = function_deprecated_by(filter_packages_tags) def filter_packages_tags_copy(self, package_tag_filter): """ Return a collection with only those packages that match a filter, with a copy of the tagsets of this one. The filter will match on (package, tags). """ res = DB() db = {} for pkg, tags in filter(package_tag_filter, six.iteritems(self.db)): db[pkg] = self.db[pkg].copy() res.db = db res.rdb = reverse(db) return res filterPackagesTagsCopy = function_deprecated_by(filter_packages_tags_copy) def filter_tags(self, tag_filter): """ Return a collection with only those tags that match a filter, sharing package sets with this one. The filter will match on the tag. """ res = DB() rdb = {} for tag in filter(tag_filter, six.iterkeys(self.rdb)): rdb[tag] = self.rdb[tag] res.rdb = rdb res.db = reverse(rdb) return res filterTags = function_deprecated_by(filter_tags) def filter_tags_copy(self, tag_filter): """ Return a collection with only those tags that match a filter, with a copy of the package sets of this one. The filter will match on the tag. """ res = DB() rdb = {} for tag in filter(tag_filter, six.iterkeys(self.rdb)): rdb[tag] = self.rdb[tag].copy() res.rdb = rdb res.db = reverse(rdb) return res filterTagsCopy = function_deprecated_by(filter_tags_copy) def has_package(self, pkg): """Check if the collection contains the given package""" return pkg in self.db hasPackage = function_deprecated_by(has_package) def has_tag(self, tag): """Check if the collection contains packages tagged with tag""" return tag in self.rdb hasTag = function_deprecated_by(has_tag) def tags_of_package(self, pkg): """Return the tag set of a package""" return pkg in self.db and self.db[pkg] or set() tagsOfPackage = function_deprecated_by(tags_of_package) def packages_of_tag(self, tag): """Return the package set of a tag""" return tag in self.rdb and self.rdb[tag] or set() packagesOfTag = function_deprecated_by(packages_of_tag) def tags_of_packages(self, pkgs): """Return the set of tags that have all the packages in pkgs""" res = None for p in pkgs: if res == None: res = set(self.tags_of_package(p)) else: res &= self.tags_of_package(p) return res tagsOfPackages = function_deprecated_by(tags_of_packages) def packages_of_tags(self, tags): """Return the set of packages that have all the tags in tags""" res = None for t in tags: if res == None: res = set(self.packages_of_tag(t)) else: res &= self.packages_of_tag(t) return res packagesOfTags = function_deprecated_by(packages_of_tags) def card(self, tag): """ Return the cardinality of a tag """ return tag in self.rdb and len(self.rdb[tag]) or 0 def discriminance(self, tag): """ Return the discriminance index if the tag. Th discriminance index of the tag is defined as the minimum number of packages that would be eliminated by selecting only those tagged with this tag or only those not tagged with this tag. """ n = self.card(tag) tot = self.package_count() return min(n, tot - n) def iter_packages(self): """Iterate over the packages""" return six.iterkeys(self.db) iterPackages = function_deprecated_by(iter_packages) def iter_tags(self): """Iterate over the tags""" return six.iterkeys(self.rdb) iterTags = function_deprecated_by(iter_tags) def iter_packages_tags(self): """Iterate over 2-tuples of (pkg, tags)""" return six.iteritems(self.db) iterPackagesTags = function_deprecated_by(iter_packages_tags) def iter_tags_packages(self): """Iterate over 2-tuples of (tag, pkgs)""" return six.iteritems(self.rdb) iterTagsPackages = function_deprecated_by(iter_tags_packages) def package_count(self): """Return the number of packages""" return len(self.db) packageCount = function_deprecated_by(package_count) def tag_count(self): """Return the number of tags""" return len(self.rdb) tagCount = function_deprecated_by(tag_count) def ideal_tagset(self, tags): """ Return an ideal selection of the top tags in a list of tags. Return the tagset made of the highest number of tags taken in consecutive sequence from the beginning of the given vector, that would intersecate with the tagset of a comfortable amount of packages. Comfortable is defined in terms of how far it is from 7. """ # TODO: the scoring function is quite ok, but may need more # tuning. I also center it on 15 instead of 7 since we're # setting a starting point for the search, not a target point def score_fun(x): return float((x-15)*(x-15))/x hits = [] tagset = set() min_score = 3 for i in range(len(tags)): pkgs = self.packages_of_tags(tags[:i+1]) card = len(pkgs) if card == 0: break; score = score_fun(card) if score < min_score: min_score = score tagset = set(tags[:i+1]) # Return always at least the first tag if len(tagset) == 0: return set(tags[:1]) else: return tagset idealTagset = function_deprecated_by(ideal_tagset) def correlations(self): """ Generate the list of correlation as a tuple (hastag, hasalsotag, score). Every touple will indicate that the tag 'hastag' tends to also have 'hasalsotag' with a score of 'score'. """ for pivot in self.iter_tags(): with_ = self.filter_packages_tags(lambda pt: pivot in pt[1]) without = self.filter_packages_tags(lambda pt: pivot not in pt[1]) for tag in with_.iter_tags(): if tag == pivot: continue has = float(with_.card(tag)) / float(with_.package_count()) hasnt = float(without.card(tag)) / float(without.package_count()) yield pivot, tag, has - hasnt python-debian-0.1.21+nmu2ubuntu1/lib/debian/changelog.py0000644000000000000000000005730212015175027017723 0ustar # changelog.py -- Python module for Debian changelogs # Copyright (C) 2006-7 James Westby # Copyright (C) 2008 Canonical Ltd. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # The parsing code is based on that from dpkg which is: # Copyright 1996 Ian Jackson # Copyright 2005 Frank Lichtenheld # and licensed under the same license as above. """This module implements facilities to deal with Debian changelogs.""" from __future__ import absolute_import import os import pwd import re import socket import warnings import sys import six from debian import debian_support # Python 3 doesn't have StandardError, but let's avoid changing our # exception inheritance hierarchy for Python 2. try: _base_exception_class = StandardError except NameError: _base_exception_class = Exception class ChangelogParseError(_base_exception_class): """Indicates that the changelog could not be parsed""" is_user_error = True def __init__(self, line): self._line=line def __str__(self): return "Could not parse changelog: "+self._line class ChangelogCreateError(_base_exception_class): """Indicates that changelog could not be created, as all the information required was not given""" class VersionError(_base_exception_class): """Indicates that the version does not conform to the required format""" is_user_error = True def __init__(self, version): self._version=version def __str__(self): return "Could not parse version: "+self._version # TODO(jsw): Remove this in favor of using debian_support.Version directly. I # don't think we gain anything by using this empty subclass. class Version(debian_support.Version): """Represents a version of a Debian package.""" # debian_support.Version now has all the functionality we need class ChangeBlock(object): """Holds all the information about one block from the changelog.""" def __init__(self, package=None, version=None, distributions=None, urgency=None, urgency_comment=None, changes=None, author=None, date=None, other_pairs=None, encoding='utf-8'): self._raw_version = None self._set_version(version) self.package = package self.distributions = distributions self.urgency = urgency or "unknown" self.urgency_comment = urgency_comment or '' self._changes = changes self.author = author self.date = date self._trailing = [] self.other_pairs = other_pairs or {} self._encoding = encoding self._no_trailer = False self._trailer_separator = " " def _set_version(self, version): if version is not None: self._raw_version = str(version) def _get_version(self): return Version(self._raw_version) version = property(_get_version, _set_version) def other_keys_normalised(self): norm_dict = {} for (key, value) in other_pairs.items(): key = key[0].upper() + key[1:].lower() m = xbcs_re.match(key) if m is None: key = "XS-%s" % key norm_dict[key] = value return norm_dict def changes(self): return self._changes def add_trailing_line(self, line): self._trailing.append(line) def add_change(self, change): if self._changes is None: self._changes = [change] else: #Bit of trickery to keep the formatting nicer with a blank #line at the end if there is one changes = self._changes changes.reverse() added = False for i in range(len(changes)): m = blankline.match(changes[i]) if m is None: changes.insert(i, change) added = True break changes.reverse() if not added: changes.append(change) self._changes = changes def _format(self): # TODO(jsw): Switch to StringIO or a list to join at the end. block = "" if self.package is None: raise ChangelogCreateError("Package not specified") block += self.package + " " if self._raw_version is None: raise ChangelogCreateError("Version not specified") block += "(" + self._raw_version + ") " if self.distributions is None: raise ChangelogCreateError("Distribution not specified") block += self.distributions + "; " if self.urgency is None: raise ChangelogCreateError("Urgency not specified") block += "urgency=" + self.urgency + self.urgency_comment for (key, value) in self.other_pairs.items(): block += ", %s=%s" % (key, value) block += '\n' if self.changes() is None: raise ChangelogCreateError("Changes not specified") for change in self.changes(): block += change + "\n" if not self._no_trailer: if self.author is None: raise ChangelogCreateError("Author not specified") if self.date is None: raise ChangelogCreateError("Date not specified") block += " -- " + self.author + self._trailer_separator \ + self.date + "\n" for line in self._trailing: block += line + "\n" return block if sys.version >= '3': __str__ = _format def __bytes__(self): return str(self).encode(self._encoding) else: __unicode__ = _format def __str__(self): return unicode(self).encode(self._encoding) topline = re.compile(r'^(\w%(name_chars)s*) \(([^\(\) \t]+)\)' '((\s+%(name_chars)s+)+)\;' % {'name_chars': '[-+0-9a-z.]'}, re.IGNORECASE) blankline = re.compile('^\s*$') change = re.compile('^\s\s+.*$') endline = re.compile('^ -- (.*) <(.*)>( ?)((\w+\,\s*)?\d{1,2}\s+\w+\s+' '\d{4}\s+\d{1,2}:\d\d:\d\d\s+[-+]\d{4}(\s+\([^\\\(\)]\))?\s*)$') endline_nodetails = re.compile('^ --(?: (.*) <(.*)>( ?)((\w+\,\s*)?\d{1,2}' '\s+\w+\s+\d{4}\s+\d{1,2}:\d\d:\d\d\s+[-+]\d{4}' '(\s+\([^\\\(\)]\))?))?\s*$') keyvalue= re.compile('^([-0-9a-z]+)=\s*(.*\S)$', re.IGNORECASE) value_re = re.compile('^([-0-9a-z]+)((\s+.*)?)$', re.IGNORECASE) xbcs_re = re.compile('^X[BCS]+-', re.IGNORECASE) emacs_variables = re.compile('^(;;\s*)?Local variables:', re.IGNORECASE) vim_variables = re.compile('^vim:', re.IGNORECASE) cvs_keyword = re.compile('^\$\w+:.*\$') comments = re.compile('^\# ') more_comments = re.compile('^/\*.*\*/') old_format_re1 = re.compile('^(\w+\s+\w+\s+\d{1,2} \d{1,2}:\d{1,2}:\d{1,2}' '\s+[\w\s]*\d{4})\s+(.*)\s+(<|\()(.*)(\)|>)') old_format_re2 = re.compile('^(\w+\s+\w+\s+\d{1,2},?\s*\d{4})\s+(.*)' '\s+(<|\()(.*)(\)|>)') old_format_re3 = re.compile('^(\w[-+0-9a-z.]*) \(([^\(\) \t]+)\)\;?', re.IGNORECASE) old_format_re4 = re.compile('^([\w.+-]+)(-| )(\S+) Debian (\S+)', re.IGNORECASE) old_format_re5 = re.compile('^Changes from version (.*) to (.*):', re.IGNORECASE) old_format_re6 = re.compile('^Changes for [\w.+-]+-[\w.+-]+:?\s*$', re.IGNORECASE) old_format_re7 = re.compile('^Old Changelog:\s*$', re.IGNORECASE) old_format_re8 = re.compile('^(?:\d+:)?\w[\w.+~-]*:?\s*$') class Changelog(object): """Represents a debian/changelog file.""" # TODO(jsw): Avoid masking the 'file' built-in. def __init__(self, file=None, max_blocks=None, allow_empty_author=False, strict=True, encoding='utf-8'): """Initializer. Args: file: The contents of the changelog, either as a str, unicode object, or an iterator of lines (each of which is either a str or unicode) max_blocks: The maximum number of blocks to parse from the input. (Default: no limit) allow_empty_author: Whether to allow an empty author in the trailer line of a change block. (Default: False) strict: Whether to raise an exception if there are errors. (Default: use a warning) encoding: If the input is a str or iterator of str, the encoding to use when interpreting the input. """ self._encoding = encoding self._blocks = [] self.initial_blank_lines = [] if file is not None: try: self.parse_changelog(file, max_blocks=max_blocks, allow_empty_author=allow_empty_author, strict=strict) except ChangelogParseError: pass def _parse_error(self, message, strict): if strict: raise ChangelogParseError(message) else: warnings.warn(message) def parse_changelog(self, file, max_blocks=None, allow_empty_author=False, strict=True, encoding=None): first_heading = "first heading" next_heading_or_eof = "next heading of EOF" start_of_change_data = "start of change data" more_changes_or_trailer = "more change data or trailer" slurp_to_end = "slurp to end" encoding = encoding or self._encoding if file is None: self._parse_error('Empty changelog file.', strict) return self._blocks = [] self.initial_blank_lines = [] current_block = ChangeBlock(encoding=encoding) changes = [] state = first_heading old_state = None if isinstance(file, bytes): file = file.decode(encoding) if isinstance(file, six.string_types): # Make sure the changelog file is not empty. if len(file.strip()) == 0: self._parse_error('Empty changelog file.', strict) return file = file.splitlines() for line in file: if not isinstance(line, six.text_type): line = line.decode(encoding) # Support both lists of lines without the trailing newline and # those with trailing newlines (e.g. when given a file object # directly) line = line.rstrip('\n') if state == first_heading or state == next_heading_or_eof: top_match = topline.match(line) blank_match = blankline.match(line) if top_match is not None: if (max_blocks is not None and len(self._blocks) >= max_blocks): return current_block.package = top_match.group(1) current_block._raw_version = top_match.group(2) current_block.distributions = top_match.group(3).lstrip() pairs = line.split(";", 1)[1] all_keys = {} other_pairs = {} for pair in pairs.split(','): pair = pair.strip() kv_match = keyvalue.match(pair) if kv_match is None: self._parse_error("Invalid key-value " "pair after ';': %s" % pair, strict) continue key = kv_match.group(1) value = kv_match.group(2) if key.lower() in all_keys: self._parse_error("Repeated key-value: " "%s" % key.lower(), strict) all_keys[key.lower()] = value if key.lower() == "urgency": val_match = value_re.match(value) if val_match is None: self._parse_error("Badly formatted " "urgency value: %s" % value, strict) else: current_block.urgency = val_match.group(1) comment = val_match.group(2) if comment is not None: current_block.urgency_comment = comment else: other_pairs[key] = value current_block.other_pairs = other_pairs state = start_of_change_data elif blank_match is not None: if state == first_heading: self.initial_blank_lines.append(line) else: self._blocks[-1].add_trailing_line(line) else: emacs_match = emacs_variables.match(line) vim_match = vim_variables.match(line) cvs_match = cvs_keyword.match(line) comments_match = comments.match(line) more_comments_match = more_comments.match(line) if ((emacs_match is not None or vim_match is not None) and state != first_heading): self._blocks[-1].add_trailing_line(line) old_state = state state = slurp_to_end continue if (cvs_match is not None or comments_match is not None or more_comments_match is not None): if state == first_heading: self.initial_blank_lines.append(line) else: self._blocks[-1].add_trailing_line(line) continue if ((old_format_re1.match(line) is not None or old_format_re2.match(line) is not None or old_format_re3.match(line) is not None or old_format_re4.match(line) is not None or old_format_re5.match(line) is not None or old_format_re6.match(line) is not None or old_format_re7.match(line) is not None or old_format_re8.match(line) is not None) and state != first_heading): self._blocks[-1].add_trailing_line(line) old_state = state state = slurp_to_end continue self._parse_error("Unexpected line while looking " "for %s: %s" % (state, line), strict) if state == first_heading: self.initial_blank_lines.append(line) else: self._blocks[-1].add_trailing_line(line) elif (state == start_of_change_data or state == more_changes_or_trailer): change_match = change.match(line) end_match = endline.match(line) end_no_details_match = endline_nodetails.match(line) blank_match = blankline.match(line) if change_match is not None: changes.append(line) state = more_changes_or_trailer elif end_match is not None: if end_match.group(3) != ' ': self._parse_error("Badly formatted trailer " "line: %s" % line, strict) current_block._trailer_separator = end_match.group(3) current_block.author = "%s <%s>" \ % (end_match.group(1), end_match.group(2)) current_block.date = end_match.group(4) current_block._changes = changes self._blocks.append(current_block) changes = [] current_block = ChangeBlock(encoding=encoding) state = next_heading_or_eof elif end_no_details_match is not None: if not allow_empty_author: self._parse_error("Badly formatted trailer " "line: %s" % line, strict) continue current_block._changes = changes self._blocks.append(current_block) changes = [] current_block = ChangeBlock(encoding=encoding) state = next_heading_or_eof elif blank_match is not None: changes.append(line) else: cvs_match = cvs_keyword.match(line) comments_match = comments.match(line) more_comments_match = more_comments.match(line) if (cvs_match is not None or comments_match is not None or more_comments_match is not None): changes.append(line) continue self._parse_error("Unexpected line while looking " "for %s: %s" % (state, line), strict) changes.append(line) elif state == slurp_to_end: if old_state == next_heading_or_eof: self._blocks[-1].add_trailing_line(line) else: changes.append(line) else: assert False, "Unknown state: %s" % state if ((state != next_heading_or_eof and state != slurp_to_end) or (state == slurp_to_end and old_state != next_heading_or_eof)): self._parse_error("Found eof where expected %s" % state, strict) current_block._changes = changes current_block._no_trailer = True self._blocks.append(current_block) def get_version(self): """Return a Version object for the last version""" return self._blocks[0].version def set_version(self, version): """Set the version of the last changelog block version can be a full version string, or a Version object """ self._blocks[0].version = Version(version) version = property(get_version, set_version, doc="Version object for last changelog block""") ### For convenience, let's expose some of the version properties full_version = property(lambda self: self.version.full_version) epoch = property(lambda self: self.version.epoch) debian_version = property(lambda self: self.version.debian_revision) debian_revision = property(lambda self: self.version.debian_revision) upstream_version = property(lambda self: self.version.upstream_version) def get_package(self): """Returns the name of the package in the last version.""" return self._blocks[0].package def set_package(self, package): self._blocks[0].package = package package = property(get_package, set_package, doc="Name of the package in the last version") def get_versions(self): """Returns a list of version objects that the package went through.""" return [block.version for block in self._blocks] versions = property(get_versions, doc="List of version objects the package went through") def _raw_versions(self): return [block._raw_version for block in self._blocks] def _format(self): pieces = [] pieces.append(six.u('\n').join(self.initial_blank_lines)) for block in self._blocks: pieces.append(six.text_type(block)) return six.u('').join(pieces) if sys.version >= '3': __str__ = _format def __bytes__(self): return str(self).encode(self._encoding) else: __unicode__ = _format def __str__(self): return unicode(self).encode(self._encoding) def __iter__(self): return iter(self._blocks) def __len__(self): return len(self._blocks) def set_distributions(self, distributions): self._blocks[0].distributions = distributions distributions = property(lambda self: self._blocks[0].distributions, set_distributions) def set_urgency(self, urgency): self._blocks[0].urgency = urgency urgency = property(lambda self: self._blocks[0].urgency, set_urgency) def add_change(self, change): self._blocks[0].add_change(change) def set_author(self, author): self._blocks[0].author = author author = property(lambda self: self._blocks[0].author, set_author) def set_date(self, date): self._blocks[0].date = date date = property(lambda self: self._blocks[0].date, set_date) def new_block(self, **kwargs): kwargs.setdefault('encoding', self._encoding) block = ChangeBlock(**kwargs) block.add_trailing_line('') self._blocks.insert(0, block) def write_to_open_file(self, file): file.write(self.__str__()) def get_maintainer(): """Get the maintainer information in the same manner as dch. This function gets the information about the current user for the maintainer field using environment variables of gecos informations as approriate. It uses the same methods as dch to get the information, namely DEBEMAIL, DEBFULLNAME, EMAIL, NAME, /etc/mailname and gecos. :returns: a tuple of the full name, email pair as strings. Either of the pair may be None if that value couldn't be determined. """ env = os.environ regex = re.compile(r"^(.*)\s+<(.*)>$") # Split email and name if 'DEBEMAIL' in env: match_obj = regex.match(env['DEBEMAIL']) if match_obj: if not 'DEBFULLNAME' in env: env['DEBFULLNAME'] = match_obj.group(1) env['DEBEMAIL'] = match_obj.group(2) if 'DEBEMAIL' not in env or 'DEBFULLNAME' not in env: if 'EMAIL' in env: match_obj = regex.match(env['EMAIL']) if match_obj: if not 'DEBFULLNAME' in env: env['DEBFULLNAME'] = match_obj.group(1) env['EMAIL'] = match_obj.group(2) # Get maintainer's name if 'DEBFULLNAME' in env: maintainer = env['DEBFULLNAME'] elif 'NAME' in env: maintainer = env['NAME'] else: # Use password database if no data in environment variables try: maintainer = re.sub(r',.*', '', pwd.getpwuid(os.getuid()).pw_gecos) except (KeyError, AttributeError): maintainer = None # Get maintainer's mail address if 'DEBEMAIL' in env: email = env['DEBEMAIL'] elif 'EMAIL' in env: email = env['EMAIL'] else: addr = None if os.path.exists('/etc/mailname'): f = open('/etc/mailname') try: addr = f.readline().strip() finally: f.close() if not addr: addr = socket.getfqdn() if addr: user = pwd.getpwuid(os.getuid()).pw_name if not user: addr = None else: addr = "%s@%s" % (user, addr) if addr: email = addr else: email = None return (maintainer, email) python-debian-0.1.21+nmu2ubuntu1/lib/debian/doc-debtags0000755000000000000000000000515212015175002017511 0ustar #!/usr/bin/python from __future__ import absolute_import, print_function import sys import os import inspect sys.path.insert(0, os.path.join(sys.path[0], os.pardir)) from debian import debtags def print_indented (spaces, string): for line in string.split("\n"): for i in range(1,spaces): sys.stdout.write(" ") sys.stdout.write(line) sys.stdout.write("\n") def document (callable): if callable.__doc__ != None: print_indented(2, callable.__name__) print_indented(4, inspect.getdoc(callable)) print() print("""debtags.py README ================= The Debtags python module provides support for accessing and manipulating Debtags tag data. The module provides a single class, debtags.DB, which implements various kinds of tag operations on an in-memory tag database. The database can be queried both as a database of packages with associated tags and as a database of tags with associated packages. Performance are good in both ways: querying the tags of a package has the same peed as querying the packages having a tag. debtags.DB allows both simple queries and more complex algorithms to be implemented easily and efficiently. Have a look at the Sample usage section below for some examples. Classes ======= There is only one class: debtags.DB: """) document (debtags.DB) print(""" The methods of debtags.DB are: """) for m in dir(debtags.DB): if m[0:2] != '__' and callable(getattr(debtags.DB, m)): document(getattr(debtags.DB, m)) print("""Iteration ========= debtags.DB provides various iteration methods to iterate the collection either in a package-centered or in a tag-centered way: """) document(debtags.DB.iter_packages) document(debtags.DB.iter_packages_tags) document(debtags.DB.iter_tags) document(debtags.DB.iter_tags_packages) print("""Sample usage ============ This example reads the system debtags database and performs a simple tag search:: import debtags db = debtags.DB() db.read(open("/var/lib/debtags/package-tags", "r")) print(db.package_count(), "packages in the database") print("Image editors:") for pkg in db.packages_of_tags(set(("use::editing", "works-with::image:raster"))): print(" *", pkg) This example computes the set of tags that belong to all the packages in a list, then shows all the other packages that have those tags: import debtags db = debtags.DB() db.read(open("/var/lib/debtags/package-tags", "r")) tags = db.tags_of_packages(("gimp", "krita")) print("Common tags:") for tag in tags: print(" *", tag) print("Packages similar to gimp and krita:") for pkg in db.packages_of_tags(tags): print(" *", pkg) """) python-debian-0.1.21+nmu2ubuntu1/lib/debian/debian_support.py0000644000000000000000000005016712015175027021014 0ustar # debian_support.py -- Python module for Debian metadata # Copyright (C) 2005 Florian Weimer # Copyright (C) 2010 John Wright # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA """This module implements facilities to deal with Debian-specific metadata.""" from __future__ import absolute_import, print_function import os import re import hashlib import types from debian.deprecation import function_deprecated_by try: import apt_pkg apt_pkg.init() _have_apt_pkg = True except ImportError: _have_apt_pkg = False class ParseError(Exception): """An exception which is used to signal a parse failure. Attributes: filename - name of the file lineno - line number in the file msg - error message """ def __init__(self, filename, lineno, msg): assert type(lineno) == types.IntType self.filename = filename self.lineno = lineno self.msg = msg def __str__(self): return self.msg def __repr__(self): return "ParseError(%r, %d, %r)" % (self.filename, self.lineno, self.msg) def print_out(self, file): """Writes a machine-parsable error message to file.""" file.write("%s:%d: %s\n" % (self.filename, self.lineno, self.msg)) file.flush() printOut = function_deprecated_by(print_out) class BaseVersion(object): """Base class for classes representing Debian versions It doesn't implement any comparison, but it does check for valid versions according to Section 5.6.12 in the Debian Policy Manual. Since splitting the version into epoch, upstream_version, and debian_revision components is pretty much free with the validation, it sets those fields as properties of the object, and sets the raw version to the full_version property. A missing epoch or debian_revision results in the respective property set to None. Setting any of the properties results in the full_version being recomputed and the rest of the properties set from that. It also implements __str__, just returning the raw version given to the initializer. """ re_valid_version = re.compile( r"^((?P\d+):)?" "(?P[A-Za-z0-9.+:~-]+?)" "(-(?P[A-Za-z0-9+.~]+))?$") magic_attrs = ('full_version', 'epoch', 'upstream_version', 'debian_revision', 'debian_version') def __init__(self, version): self.full_version = version def _set_full_version(self, version): m = self.re_valid_version.match(version) if not m: raise ValueError("Invalid version string %r" % version) # If there no epoch ("1:..."), then the upstream version can not # contain a :. if (m.group("epoch") is None and ":" in m.group("upstream_version")): raise ValueError("Invalid version string %r" % version) self.__full_version = version self.__epoch = m.group("epoch") self.__upstream_version = m.group("upstream_version") self.__debian_revision = m.group("debian_revision") def __setattr__(self, attr, value): if attr not in self.magic_attrs: super(BaseVersion, self).__setattr__(attr, value) return # For compatibility with the old changelog.Version class if attr == "debian_version": attr = "debian_revision" if attr == "full_version": self._set_full_version(str(value)) else: if value is not None: value = str(value) private = "_BaseVersion__%s" % attr old_value = getattr(self, private) setattr(self, private, value) try: self._update_full_version() except ValueError: # Don't leave it in an invalid state setattr(self, private, old_value) self._update_full_version() raise ValueError("Setting %s to %r results in invalid version" % (attr, value)) def __getattr__(self, attr): if attr not in self.magic_attrs: return super(BaseVersion, self).__getattribute__(attr) # For compatibility with the old changelog.Version class if attr == "debian_version": attr = "debian_revision" private = "_BaseVersion__%s" % attr return getattr(self, private) def _update_full_version(self): version = "" if self.__epoch is not None: version += self.__epoch + ":" version += self.__upstream_version if self.__debian_revision: version += "-" + self.__debian_revision self.full_version = version def __str__(self): return self.full_version def __repr__(self): return "%s('%s')" % (self.__class__.__name__, self) def _compare(self, other): raise NotImplementedError # TODO: Once we support only Python >= 2.7, we can simplify this using # @functools.total_ordering. def __lt__(self, other): return self._compare(other) < 0 def __le__(self, other): return self._compare(other) <= 0 def __eq__(self, other): return self._compare(other) == 0 def __ne__(self, other): return self._compare(other) != 0 def __ge__(self, other): return self._compare(other) >= 0 def __gt__(self, other): return self._compare(other) > 0 def __hash__(self): return hash(str(self)) class AptPkgVersion(BaseVersion): """Represents a Debian package version, using apt_pkg.VersionCompare""" def __init__(self, version): if not _have_apt_pkg: raise NotImplementedError("apt_pkg not available; install the " "python-apt package") super(AptPkgVersion, self).__init__(version) def _compare(self, other): return apt_pkg.version_compare(str(self), str(other)) # NativeVersion based on the DpkgVersion class by Raphael Hertzog in # svn://svn.debian.org/qa/trunk/pts/www/bin/common.py r2361 class NativeVersion(BaseVersion): """Represents a Debian package version, with native Python comparison""" re_all_digits_or_not = re.compile("\d+|\D+") re_digits = re.compile("\d+") re_digit = re.compile("\d") re_alpha = re.compile("[A-Za-z]") def _compare(self, other): # Convert other into an instance of BaseVersion if it's not already. # (All we need is epoch, upstream_version, and debian_revision # attributes, which BaseVersion gives us.) Requires other's string # representation to be the raw version. if not isinstance(other, BaseVersion): try: other = BaseVersion(str(other)) except ValueError as e: raise ValueError("Couldn't convert %r to BaseVersion: %s" % (other, e)) lepoch = int(self.epoch or "0") repoch = int(other.epoch or "0") if lepoch < repoch: return -1 elif lepoch > repoch: return 1 res = self._version_cmp_part(self.upstream_version, other.upstream_version) if res != 0: return res return self._version_cmp_part(self.debian_revision or "0", other.debian_revision or "0") @classmethod def _order(cls, x): """Return an integer value for character x""" if x == '~': return -1 elif cls.re_digit.match(x): return int(x) + 1 elif cls.re_alpha.match(x): return ord(x) else: return ord(x) + 256 @classmethod def _version_cmp_string(cls, va, vb): la = [cls._order(x) for x in va] lb = [cls._order(x) for x in vb] while la or lb: a = 0 b = 0 if la: a = la.pop(0) if lb: b = lb.pop(0) if a < b: return -1 elif a > b: return 1 return 0 @classmethod def _version_cmp_part(cls, va, vb): la = cls.re_all_digits_or_not.findall(va) lb = cls.re_all_digits_or_not.findall(vb) while la or lb: a = "0" b = "0" if la: a = la.pop(0) if lb: b = lb.pop(0) if cls.re_digits.match(a) and cls.re_digits.match(b): a = int(a) b = int(b) if a < b: return -1 elif a > b: return 1 else: res = cls._version_cmp_string(a, b) if res != 0: return res return 0 if _have_apt_pkg: class Version(AptPkgVersion): pass else: class Version(NativeVersion): pass def version_compare(a, b): va = Version(a) vb = Version(b) if va < vb: return -1 elif va > vb: return 1 else: return 0 class PackageFile: """A Debian package file. Objects of this class can be used to read Debian's Source and Packages files.""" re_field = re.compile(r'^([A-Za-z][A-Za-z0-9-]+):(?:\s*(.*?))?\s*$') re_continuation = re.compile(r'^\s+(?:\.|(\S.*?)\s*)$') def __init__(self, name, file_obj=None): """Creates a new package file object. name - the name of the file the data comes from file_obj - an alternate data source; the default is to open the file with the indicated name. """ if file_obj is None: file_obj = open(name) self.name = name self.file = file_obj self.lineno = 0 def __iter__(self): line = self.file.readline() self.lineno += 1 pkg = [] while line: if line.strip(' \t') == '\n': if len(pkg) == 0: self.raise_syntax_error('expected package record') yield pkg pkg = [] line = self.file.readline() self.lineno += 1 continue match = self.re_field.match(line) if not match: self.raise_syntax_error("expected package field") (name, contents) = match.groups() contents = contents or '' while True: line = self.file.readline() self.lineno += 1 match = self.re_continuation.match(line) if match: (ncontents,) = match.groups() if ncontents is None: ncontents = "" contents = "%s\n%s" % (contents, ncontents) else: break pkg.append((name, contents)) if pkg: yield pkg def raise_syntax_error(self, msg, lineno=None): if lineno is None: lineno = self.lineno raise ParseError(self.name, lineno, msg) raiseSyntaxError = function_deprecated_by(raise_syntax_error) class PseudoEnum: """A base class for types which resemble enumeration types.""" def __init__(self, name, order): self._name = name self._order = order def __repr__(self): return '%s(%r)' % (self.__class__._name__, self._name) def __str__(self): return self._name # TODO: Once we support only Python >= 2.7, we can simplify this using # @functools.total_ordering. def __lt__(self, other): return self._order < other._order def __le__(self, other): return self._order <= other._order def __eq__(self, other): return self._order == other._order def __ne__(self, other): return self._order != other._order def __ge__(self, other): return self._order >= other._order def __gt__(self, other): return self._order > other._order def __hash__(self): return hash(self._order) class Release(PseudoEnum): pass def list_releases(): releases = {} rels = ("potato", "woody", "sarge", "etch", "lenny", "sid") for r in range(len(rels)): releases[rels[r]] = Release(rels[r], r) Release.releases = releases return releases listReleases = function_deprecated_by(list_releases) def intern_release(name, releases=list_releases()): return releases.get(name) internRelease = function_deprecated_by(intern_release) del listReleases del list_releases def read_lines_sha1(lines): m = hashlib.sha1() for l in lines: if isinstance(l, bytes): m.update(l) else: m.update(l.encode("UTF-8")) return m.hexdigest() readLinesSHA1 = function_deprecated_by(read_lines_sha1) def patches_from_ed_script(source, re_cmd=re.compile(r'^(\d+)(?:,(\d+))?([acd])$')): """Converts source to a stream of patches. Patches are triples of line indexes: - number of the first line to be replaced - one plus the number of the last line to be replaced - list of line replacements This is enough to model arbitrary additions, deletions and replacements. """ i = iter(source) for line in i: match = re_cmd.match(line) if match is None: raise ValueError("invalid patch command: %r" % line) (first, last, cmd) = match.groups() first = int(first) if last is not None: last = int(last) if cmd == 'd': first = first - 1 if last is None: last = first + 1 yield (first, last, []) continue if cmd == 'a': if last is not None: raise ValueError("invalid patch argument: %r" % line) last = first else: # cmd == c first = first - 1 if last is None: last = first + 1 lines = [] for l in i: if l == '': raise ValueError("end of stream in command: %r" % line) if l == '.\n' or l == '.': break lines.append(l) yield (first, last, lines) patchesFromEdScript = function_deprecated_by(patches_from_ed_script) def patch_lines(lines, patches): """Applies patches to lines. Updates lines in place.""" for (first, last, args) in patches: lines[first:last] = args patchLines = function_deprecated_by(patch_lines) def replace_file(lines, local): import os.path local_new = local + '.new' new_file = open(local_new, 'w+') try: for l in lines: new_file.write(l) new_file.close() os.rename(local_new, local) finally: if os.path.exists(local_new): os.unlink(local_new) replaceFile = function_deprecated_by(replace_file) def download_gunzip_lines(remote): """Downloads a file from a remote location and gunzips it. Returns the lines in the file.""" # The implementation is rather crude, but it seems that the gzip # module needs a real file for input. import gzip import tempfile import urllib (handle, fname) = tempfile.mkstemp() try: os.close(handle) (filename, headers) = urllib.urlretrieve(remote, fname) gfile = gzip.GzipFile(filename) lines = gfile.readlines() gfile.close() finally: os.unlink(fname) return lines downloadGunzipLines = function_deprecated_by(download_gunzip_lines) def download_file(remote, local): """Copies a gzipped remote file to the local system. remote - URL, without the .gz suffix local - name of the local file """ lines = download_gunzip_lines(remote + '.gz') replace_file(lines, local) return lines downloadFile = function_deprecated_by(download_file) def update_file(remote, local, verbose=None): """Updates the local file by downloading a remote patch. Returns a list of lines in the local file. """ try: local_file = open(local) except IOError: if verbose: print("update_file: no local copy, downloading full file") return download_file(remote, local) lines = local_file.readlines() local_file.close() local_hash = read_lines_sha1(lines) patches_to_apply = [] patch_hashes = {} import urllib index_name = remote + '.diff/Index' re_whitespace=re.compile('\s+') try: index_url = urllib.urlopen(index_name) index_fields = list(PackageFile(index_name, index_url)) except ParseError: # FIXME: urllib does not raise a proper exception, so we parse # the error message. if verbose: print("update_file: could not interpret patch index file") return download_file(remote, local) except IOError: if verbose: print("update_file: could not download patch index file") return download_file(remote, local) for fields in index_fields: for (field, value) in fields: if field == 'SHA1-Current': (remote_hash, remote_size) = re_whitespace.split(value) if local_hash == remote_hash: if verbose: print("update_file: local file is up-to-date") return lines continue if field =='SHA1-History': for entry in value.splitlines(): if entry == '': continue (hist_hash, hist_size, patch_name) \ = re_whitespace.split(entry) # After the first patch, we have to apply all # remaining patches. if patches_to_apply or hist_hash == local_hash: patches_to_apply.append(patch_name) continue if field == 'SHA1-Patches': for entry in value.splitlines(): if entry == '': continue (patch_hash, patch_size, patch_name) \ = re_whitespace.split(entry) patch_hashes[patch_name] = patch_hash continue if verbose: print("update_file: field %r ignored" % field) if not patches_to_apply: if verbose: print("update_file: could not find historic entry", local_hash) return download_file(remote, local) for patch_name in patches_to_apply: print("update_file: downloading patch %r" % patch_name) patch_contents = download_gunzip_lines(remote + '.diff/' + patch_name + '.gz') if read_lines_sha1(patch_contents ) != patch_hashes[patch_name]: raise ValueError("patch %r was garbled" % patch_name) patch_lines(lines, patches_from_ed_script(patch_contents)) new_hash = read_lines_sha1(lines) if new_hash != remote_hash: raise ValueError("patch failed, got %s instead of %s" % (new_hash, remote_hash)) replace_file(lines, local) return lines updateFile = function_deprecated_by(update_file) def merge_as_sets(*args): """Create an order set (represented as a list) of the objects in the sequences passed as arguments.""" s = {} for x in args: for y in x: s[y] = True return sorted(s) mergeAsSets = function_deprecated_by(merge_as_sets) python-debian-0.1.21+nmu2ubuntu1/lib/debian/deprecation.py0000644000000000000000000000266411703667743020310 0ustar # -*- coding: utf-8 -*- vim: fileencoding=utf-8 : # # debian/deprecation.py # Utility module to deprecate features # # Copyright © Ben Finney # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation, either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. """ Utility module to deprecate features """ import warnings def function_deprecated_by(func): """ Return a function that warns it is deprecated by another function. Returns a new function that warns it is deprecated by function ``func``, then acts as a pass-through wrapper for ``func``. """ func_name = func.__name__ warn_msg = "Use %(func_name)s instead" % vars() def deprecated_func(*args, **kwargs): warnings.warn(warn_msg, DeprecationWarning, stacklevel=2) return func(*args, **kwargs) return deprecated_func python-debian-0.1.21+nmu2ubuntu1/lib/debian/__pycache__/0000755000000000000000000000000012015167657017635 5ustar python-debian-0.1.21+nmu2ubuntu1/lib/debian/debfile.py0000644000000000000000000002720512021625521017361 0ustar # DebFile: a Python representation of Debian .deb binary packages. # Copyright (C) 2007-2008 Stefano Zacchiroli # Copyright (C) 2007 Filippo Giunchedi # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 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 . from __future__ import absolute_import, print_function import gzip import tarfile import sys from debian.arfile import ArFile, ArError from debian.changelog import Changelog from debian.deb822 import Deb822 DATA_PART = 'data.tar' # w/o extension CTRL_PART = 'control.tar' PART_EXTS = ['gz', 'bz2', 'lzma'] # possible extensions INFO_PART = 'debian-binary' MAINT_SCRIPTS = ['preinst', 'postinst', 'prerm', 'postrm', 'config'] CONTROL_FILE = 'control' CHANGELOG_NATIVE = 'usr/share/doc/%s/changelog.gz' # with package stem CHANGELOG_DEBIAN = 'usr/share/doc/%s/changelog.Debian.gz' MD5_FILE = 'md5sums' class DebError(ArError): pass class DebPart(object): """'Part' of a .deb binary package. A .deb package is considered as made of 2 parts: a 'data' part (corresponding to the 'data.tar.gz' archive embedded in a .deb) and a 'control' part (the 'control.tar.gz' archive). Each of them is represented by an instance of this class. Each archive should be a compressed tar archive; supported compression formats are: .tar.gz, .tar.bz2 . When referring to file members of the underlying .tar.gz archive, file names can be specified in one of 3 formats "file", "./file", "/file". In all cases the file is considered relative to the root of the archive. For the control part the preferred mechanism is the first one (as in deb.control.get_content('control') ); for the data part the preferred mechanism is the third one (as in deb.data.get_file('/etc/vim/vimrc') ). """ def __init__(self, member): self.__member = member # arfile.ArMember file member self.__tgz = None def tgz(self): """Return a TarFile object corresponding to this part of a .deb package. Despite the name, this method gives access to various kind of compressed tar archives, not only gzipped ones. """ if self.__tgz is None: name = self.__member.name if name.endswith('.gz'): gz = gzip.GzipFile(fileobj=self.__member, mode='r') self.__tgz = tarfile.TarFile(fileobj=gz, mode='r') elif name.endswith('.bz2'): # Tarfile's __init__ doesn't allow for r:bz2 modes, but the # open() classmethod does ... self.__tgz = tarfile.open(fileobj=self.__member, mode='r:bz2') else: raise DebError("part '%s' has unexpected extension" % name) return self.__tgz @staticmethod def __normalize_member(fname): """ try (not so hard) to obtain a member file name in a form relative to the .tar.gz root and with no heading '.' """ if fname.startswith('./'): fname = fname[2:] elif fname.startswith('/'): fname = fname[1:] return fname # XXX in some of the following methods, compatibility among >= 2.5 and << # 2.5 python versions had to be taken into account. TarFile << 2.5 indeed # was buggied and returned member file names with an heading './' only for # the *first* file member. TarFile >= 2.5 fixed this and has the heading # './' for all file members. def has_file(self, fname): """Check if this part contains a given file name.""" fname = DebPart.__normalize_member(fname) names = self.tgz().getnames() return (('./' + fname in names) \ or (fname in names)) # XXX python << 2.5 TarFile compatibility def get_file(self, fname, encoding=None, errors=None): """Return a file object corresponding to a given file name. If encoding is given, then the file object will return Unicode data; otherwise, it will return binary data. """ fname = DebPart.__normalize_member(fname) try: fobj = self.tgz().extractfile('./' + fname) except KeyError: # XXX python << 2.5 TarFile compatibility fobj = self.tgz().extractfile(fname) if encoding is not None: if sys.version >= '3': import io if not hasattr(fobj, 'flush'): # XXX http://bugs.python.org/issue13815 fobj.flush = lambda: None return io.TextIOWrapper(fobj, encoding=encoding, errors=errors) else: import codecs if errors is None: errors = 'strict' return codecs.EncodedFile(fobj, encoding, errors=errors) else: return fobj def get_content(self, fname, encoding=None, errors=None): """Return the string content of a given file, or None (e.g. for directories). If encoding is given, then the content will be a Unicode object; otherwise, it will contain binary data. """ f = self.get_file(fname, encoding=encoding, errors=errors) content = None if f: # can be None for non regular or link files content = f.read() f.close() return content # container emulation def __iter__(self): return iter(self.tgz().getnames()) def __contains__(self, fname): return self.has_file(fname) if sys.version < '3': def has_key(self, fname): return self.has_file(fname) def __getitem__(self, fname): return self.get_content(fname) def close(self): self.__member.close() class DebData(DebPart): pass class DebControl(DebPart): def scripts(self): """ Return a dictionary of maintainer scripts (postinst, prerm, ...) mapping script names to script text. """ scripts = {} for fname in MAINT_SCRIPTS: if self.has_file(fname): scripts[fname] = self.get_content(fname) return scripts def debcontrol(self): """ Return the debian/control as a Deb822 (a Debian-specific dict-like class) object. For a string representation of debian/control try .get_content('control') """ return Deb822(self.get_content(CONTROL_FILE)) def md5sums(self, encoding=None, errors=None): """ Return a dictionary mapping filenames (of the data part) to md5sums. Fails if the control part does not contain a 'md5sum' file. Keys of the returned dictionary are the left-hand side values of lines in the md5sums member of control.tar.gz, usually file names relative to the file system root (without heading '/' or './'). The returned keys are Unicode objects if an encoding is specified, otherwise binary. The returned values are always Unicode.""" if not self.has_file(MD5_FILE): raise DebError("'%s' file not found, can't list MD5 sums" % MD5_FILE) md5_file = self.get_file(MD5_FILE, encoding=encoding, errors=errors) sums = {} if encoding is None: newline = b'\r\n' else: newline = '\r\n' for line in md5_file.readlines(): # we need to support spaces in filenames, .split() is not enough md5, fname = line.rstrip(newline).split(None, 1) if sys.version >= '3' and isinstance(md5, bytes): sums[fname] = md5.decode() else: sums[fname] = md5 md5_file.close() return sums class DebFile(ArFile): """Representation of a .deb file (a Debian binary package) DebFile objects have the following (read-only) properties: - version debian .deb file format version (not related with the contained package version), 2.0 at the time of writing for all .deb packages in the Debian archive - data DebPart object corresponding to the data.tar.gz (or other compressed tar) archive contained in the .deb file - control DebPart object corresponding to the control.tar.gz (or other compressed tar) archive contained in the .deb file """ def __init__(self, filename=None, mode='r', fileobj=None): ArFile.__init__(self, filename, mode, fileobj) actual_names = set(self.getnames()) def compressed_part_name(basename): global PART_EXTS candidates = [ '%s.%s' % (basename, ext) for ext in PART_EXTS ] parts = actual_names.intersection(set(candidates)) if not parts: raise DebError("missing required part in given .deb" \ " (expected one of: %s)" % candidates) elif len(parts) > 1: raise DebError("too many parts in given .deb" \ " (was looking for only one of: %s)" % candidates) else: # singleton list return list(parts)[0] if not INFO_PART in actual_names: raise DebError("missing required part in given .deb" \ " (expected: '%s')" % INFO_PART) self.__parts = {} self.__parts[CTRL_PART] = DebControl(self.getmember( compressed_part_name(CTRL_PART))) self.__parts[DATA_PART] = DebData(self.getmember( compressed_part_name(DATA_PART))) self.__pkgname = None # updated lazily by __updatePkgName f = self.getmember(INFO_PART) self.__version = f.read().strip() f.close() def __updatePkgName(self): self.__pkgname = self.debcontrol()['package'] version = property(lambda self: self.__version) data = property(lambda self: self.__parts[DATA_PART]) control = property(lambda self: self.__parts[CTRL_PART]) # proxy methods for the appropriate parts def debcontrol(self): """ See .control.debcontrol() """ return self.control.debcontrol() def scripts(self): """ See .control.scripts() """ return self.control.scripts() def md5sums(self, encoding=None, errors=None): """ See .control.md5sums() """ return self.control.md5sums(encoding=encoding, errors=errors) def changelog(self): """ Return a Changelog object for the changelog.Debian.gz of the present .deb package. Return None if no changelog can be found. """ if self.__pkgname is None: self.__updatePkgName() for fname in [ CHANGELOG_DEBIAN % self.__pkgname, CHANGELOG_NATIVE % self.__pkgname ]: if self.data.has_file(fname): gz = gzip.GzipFile(fileobj=self.data.get_file(fname)) raw_changelog = gz.read() gz.close() return Changelog(raw_changelog) return None def close(self): self.control.close() self.data.close() if __name__ == '__main__': import sys deb = DebFile(filename=sys.argv[1]) tgz = deb.control.tgz() print(tgz.getmember('control')) python-debian-0.1.21+nmu2ubuntu1/lib/debian/deb822.py0000644000000000000000000013344512021623302016754 0ustar # vim: fileencoding=utf-8 # # A python interface for various rfc822-like formatted files used by Debian # (.changes, .dsc, Packages, Sources, etc) # # Copyright (C) 2005-2006 dann frazier # Copyright (C) 2006-2010 John Wright # Copyright (C) 2006 Adeodato Simó # Copyright (C) 2008 Stefano Zacchiroli # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation, either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. from __future__ import absolute_import, print_function from debian.deprecation import function_deprecated_by try: import apt_pkg # This module uses apt_pkg only for its TagFile interface. apt_pkg.TagFile _have_apt_pkg = True except (ImportError, AttributeError): _have_apt_pkg = False import chardet import os import re import subprocess import sys import warnings try: from StringIO import StringIO BytesIO = StringIO except ImportError: from io import BytesIO, StringIO try: from collections import Mapping, MutableMapping _mapping_mixin = Mapping _mutable_mapping_mixin = MutableMapping except ImportError: from UserDict import DictMixin _mapping_mixin = DictMixin _mutable_mapping_mixin = DictMixin import six if sys.version >= '3': import io def _is_real_file(f): if not isinstance(f, io.IOBase): return False try: f.fileno() return True except (AttributeError, io.UnsupportedOperation): return False else: def _is_real_file(f): return isinstance(f, file) and hasattr(f, 'fileno') GPGV_DEFAULT_KEYRINGS = frozenset(['/usr/share/keyrings/debian-keyring.gpg']) GPGV_EXECUTABLE = '/usr/bin/gpgv' class TagSectionWrapper(_mapping_mixin, object): """Wrap a TagSection object, using its find_raw method to get field values This allows us to pick which whitespace to strip off the beginning and end of the data, so we don't lose leading newlines. """ def __init__(self, section): self.__section = section def __iter__(self): for key in self.__section.keys(): if not key.startswith('#'): yield key def __len__(self): return len([key for key in self.__section.keys() if not key.startswith('#')]) def __getitem__(self, key): s = self.__section.find_raw(key) if s is None: raise KeyError(key) # Get just the stuff after the first ':' # Could use s.partition if we only supported python >= 2.5 data = s[s.find(b':')+1:] # Get rid of spaces and tabs after the ':', but not newlines, and strip # off any newline at the end of the data. return data.lstrip(b' \t').rstrip(b'\n') class OrderedSet(object): """A set-like object that preserves order when iterating over it We use this to keep track of keys in Deb822Dict, because it's much faster to look up if a key is in a set than in a list. """ def __init__(self, iterable=[]): self.__set = set() self.__order = [] for item in iterable: self.add(item) def add(self, item): if item not in self: # set.add will raise TypeError if something's unhashable, so we # don't have to handle that ourselves self.__set.add(item) self.__order.append(item) def remove(self, item): # set.remove will raise KeyError, so we don't need to handle that # ourselves self.__set.remove(item) self.__order.remove(item) def __iter__(self): # Return an iterator of items in the order they were added return iter(self.__order) def __len__(self): return len(self.__order) def __contains__(self, item): # This is what makes OrderedSet faster than using a list to keep track # of keys. Lookup in a set is O(1) instead of O(n) for a list. return item in self.__set ### list-like methods append = add def extend(self, iterable): for item in iterable: self.add(item) ### class Deb822Dict(_mutable_mapping_mixin, object): # Subclassing _mutable_mapping_mixin because we're overriding so much # dict functionality that subclassing dict requires overriding many more # than the methods that _mutable_mapping_mixin requires. """A dictionary-like object suitable for storing RFC822-like data. Deb822Dict behaves like a normal dict, except: - key lookup is case-insensitive - key order is preserved - if initialized with a _parsed parameter, it will pull values from that dictionary-like object as needed (rather than making a copy). The _parsed dict is expected to be able to handle case-insensitive keys. If _parsed is not None, an optional _fields parameter specifies which keys in the _parsed dictionary are exposed. """ # See the end of the file for the definition of _strI def __init__(self, _dict=None, _parsed=None, _fields=None, encoding="utf-8"): self.__dict = {} self.__keys = OrderedSet() self.__parsed = None self.encoding = encoding if _dict is not None: # _dict may be a dict or a list of two-sized tuples if hasattr(_dict, 'items'): items = _dict.items() else: items = list(_dict) try: for k, v in items: self[k] = v except ValueError: this = len(self.__keys) len_ = len(items[this]) raise ValueError('dictionary update sequence element #%d has ' 'length %d; 2 is required' % (this, len_)) if _parsed is not None: self.__parsed = _parsed if _fields is None: self.__keys.extend([ _strI(k) for k in self.__parsed ]) else: self.__keys.extend([ _strI(f) for f in _fields if f in self.__parsed ]) def _detect_encoding(self, value): """If value is not already Unicode, decode it intelligently.""" if isinstance(value, bytes): try: return value.decode(self.encoding) except UnicodeDecodeError as e: # Evidently, the value wasn't encoded with the encoding the # user specified. Try detecting it. warnings.warn('decoding from %s failed; attempting to detect ' 'the true encoding' % self.encoding, UnicodeWarning) result = chardet.detect(value) try: return value.decode(result['encoding']) except UnicodeDecodeError: raise e else: # Assume the rest of the paragraph is in this encoding as # well (there's no sense in repeating this exercise for # every field). self.encoding = result['encoding'] else: return value ### BEGIN _mutable_mapping_mixin methods def __iter__(self): for key in self.__keys: yield str(key) def __len__(self): return len(self.__keys) def __setitem__(self, key, value): key = _strI(key) self.__keys.add(key) self.__dict[key] = value def __getitem__(self, key): key = _strI(key) try: value = self.__dict[key] except KeyError: if self.__parsed is not None and key in self.__keys: value = self.__parsed[key] else: raise return self._detect_encoding(value) def __delitem__(self, key): key = _strI(key) self.__keys.remove(key) try: del self.__dict[key] except KeyError: # If we got this far, the key was in self.__keys, so it must have # only been in the self.__parsed dict. pass def __contains__(self, key): key = _strI(key) return key in self.__keys if sys.version < '3': has_key = __contains__ ### END _mutable_mapping_mixin methods def __repr__(self): return '{%s}' % ', '.join(['%r: %r' % (k, v) for k, v in self.items()]) def __eq__(self, other): mykeys = sorted(self) otherkeys = sorted(other) if not mykeys == otherkeys: return False for key in mykeys: if self[key] != other[key]: return False # If we got here, everything matched return True # Overriding __eq__ blocks inheritance of __hash__ in Python 3, and # instances of this class are not sensibly hashable anyway. __hash__ = None def copy(self): # Use self.__class__ so this works as expected for subclasses copy = self.__class__(self) return copy # TODO implement __str__() and make dump() use that? class Deb822(Deb822Dict): def __init__(self, sequence=None, fields=None, _parsed=None, encoding="utf-8"): """Create a new Deb822 instance. :param sequence: a string, or any any object that returns a line of input each time, normally a file. Alternately, sequence can be a dict that contains the initial key-value pairs. :param fields: if given, it is interpreted as a list of fields that should be parsed (the rest will be discarded). :param _parsed: internal parameter. :param encoding: When parsing strings, interpret them in this encoding. (All values are given back as unicode objects, so an encoding is necessary in order to properly interpret the strings.) """ if hasattr(sequence, 'items'): _dict = sequence sequence = None else: _dict = None Deb822Dict.__init__(self, _dict=_dict, _parsed=_parsed, _fields=fields, encoding=encoding) if sequence is not None: try: self._internal_parser(sequence, fields) except EOFError: pass self.gpg_info = None def iter_paragraphs(cls, sequence, fields=None, use_apt_pkg=True, shared_storage=False, encoding="utf-8"): """Generator that yields a Deb822 object for each paragraph in sequence. :param sequence: same as in __init__. :param fields: likewise. :param use_apt_pkg: if sequence is a file, apt_pkg will be used if available to parse the file, since it's much much faster. Set this parameter to False to disable using apt_pkg. :param shared_storage: not used, here for historical reasons. Deb822 objects never use shared storage anymore. :param encoding: Interpret the paragraphs in this encoding. (All values are given back as unicode objects, so an encoding is necessary in order to properly interpret the strings.) """ if _have_apt_pkg and use_apt_pkg and _is_real_file(sequence): kwargs = {} if sys.version >= '3': # bytes=True is supported for both Python 2 and 3, but we # only actually need it for Python 3, so this saves us from # having to require a newer version of python-apt for Python # 2 as well. This allows us to apply our own encoding # handling, which is more tolerant of mixed-encoding files. kwargs['bytes'] = True parser = apt_pkg.TagFile(sequence, **kwargs) for section in parser: paragraph = cls(fields=fields, _parsed=TagSectionWrapper(section), encoding=encoding) if paragraph: yield paragraph else: iterable = iter(sequence) x = cls(iterable, fields, encoding=encoding) while len(x) != 0: yield x x = cls(iterable, fields, encoding=encoding) iter_paragraphs = classmethod(iter_paragraphs) ### @staticmethod def _skip_useless_lines(sequence): """Yields only lines that do not begin with '#'. Also skips any blank lines at the beginning of the input. """ at_beginning = True for line in sequence: # The bytes/str polymorphism required here to support Python 3 # is unpleasant, but fortunately limited. We need this because # at this point we might have been given either bytes or # Unicode, and we haven't yet got to the point where we can try # to decode a whole paragraph and detect its encoding. if isinstance(line, bytes): if line.startswith(b'#'): continue else: if line.startswith('#'): continue if at_beginning: if isinstance(line, bytes): if not line.rstrip(b'\r\n'): continue else: if not line.rstrip('\r\n'): continue at_beginning = False yield line def _internal_parser(self, sequence, fields=None): # The key is non-whitespace, non-colon characters before any colon. key_part = r"^(?P[^: \t\n\r\f\v]+)\s*:\s*" single = re.compile(key_part + r"(?P\S.*?)\s*$") multi = re.compile(key_part + r"$") multidata = re.compile(r"^\s(?P.+?)\s*$") wanted_field = lambda f: fields is None or f in fields if isinstance(sequence, (six.string_types, bytes)): sequence = sequence.splitlines() curkey = None content = "" for line in self.gpg_stripped_paragraph( self._skip_useless_lines(sequence)): line = self._detect_encoding(line) m = single.match(line) if m: if curkey: self[curkey] = content if not wanted_field(m.group('key')): curkey = None continue curkey = m.group('key') content = m.group('data') continue m = multi.match(line) if m: if curkey: self[curkey] = content if not wanted_field(m.group('key')): curkey = None continue curkey = m.group('key') content = "" continue m = multidata.match(line) if m: content += '\n' + line # XXX not m.group('data')? continue if curkey: self[curkey] = content def __str__(self): return self.dump() def __unicode__(self): return self.dump() if sys.version >= '3': def __bytes__(self): return self.dump().encode(self.encoding) # __repr__ is handled by Deb822Dict def get_as_string(self, key): """Return the self[key] as a string (or unicode) The default implementation just returns unicode(self[key]); however, this can be overridden in subclasses (e.g. _multivalued) that can take special values. """ return six.text_type(self[key]) def dump(self, fd=None, encoding=None): """Dump the the contents in the original format If fd is None, return a unicode object. If fd is not None, attempt to encode the output to the encoding the object was initialized with, or the value of the encoding argument if it is not None. This will raise UnicodeEncodeError if the encoding can't support all the characters in the Deb822Dict values. """ if fd is None: fd = StringIO() return_string = True else: return_string = False if encoding is None: # Use the encoding we've been using to decode strings with if none # was explicitly specified encoding = self.encoding for key in self: value = self.get_as_string(key) if not value or value[0] == '\n': # Avoid trailing whitespace after "Field:" if it's on its own # line or the value is empty. We don't have to worry about the # case where value == '\n', since we ensure that is not the # case in __setitem__. entry = '%s:%s\n' % (key, value) else: entry = '%s: %s\n' % (key, value) if not return_string: fd.write(entry.encode(encoding)) else: fd.write(entry) if return_string: return fd.getvalue() ### def is_single_line(self, s): if s.count("\n"): return False else: return True isSingleLine = function_deprecated_by(is_single_line) def is_multi_line(self, s): return not self.is_single_line(s) isMultiLine = function_deprecated_by(is_multi_line) def _merge_fields(self, s1, s2): if not s2: return s1 if not s1: return s2 if self.is_single_line(s1) and self.is_single_line(s2): ## some fields are delimited by a single space, others ## a comma followed by a space. this heuristic assumes ## that there are multiple items in one of the string fields ## so that we can pick up on the delimiter being used delim = ' ' if (s1 + s2).count(', '): delim = ', ' L = sorted((s1 + delim + s2).split(delim)) prev = merged = L[0] for item in L[1:]: ## skip duplicate entries if item == prev: continue merged = merged + delim + item prev = item return merged if self.is_multi_line(s1) and self.is_multi_line(s2): for item in s2.splitlines(True): if item not in s1.splitlines(True): s1 = s1 + "\n" + item return s1 raise ValueError _mergeFields = function_deprecated_by(_merge_fields) def merge_fields(self, key, d1, d2=None): ## this method can work in two ways - abstract that away if d2 == None: x1 = self x2 = d1 else: x1 = d1 x2 = d2 ## we only have to do work if both objects contain our key ## otherwise, we just take the one that does, or raise an ## exception if neither does if key in x1 and key in x2: merged = self._mergeFields(x1[key], x2[key]) elif key in x1: merged = x1[key] elif key in x2: merged = x2[key] else: raise KeyError ## back to the two different ways - if this method was called ## upon an object, update that object in place. ## return nothing in this case, to make the author notice a ## problem if she assumes the object itself will not be modified if d2 == None: self[key] = merged return None return merged mergeFields = function_deprecated_by(merge_fields) def split_gpg_and_payload(sequence): """Return a (gpg_pre, payload, gpg_post) tuple Each element of the returned tuple is a list of lines (with trailing whitespace stripped). """ gpg_pre_lines = [] lines = [] gpg_post_lines = [] state = b'SAFE' gpgre = re.compile(br'^-----(?PBEGIN|END) PGP (?P[^-]+)-----$') blank_line = re.compile(b'^$') first_line = True for line in sequence: # Some consumers of this method require bytes (encoding # detection and signature checking). However, we might have # been given a file opened in text mode, in which case it's # simplest to encode to bytes. if sys.version >= '3' and isinstance(line, str): line = line.encode() line = line.strip(b'\r\n') # skip initial blank lines, if any if first_line: if blank_line.match(line): continue else: first_line = False m = gpgre.match(line) if not m: if state == b'SAFE': if not blank_line.match(line): lines.append(line) else: if not gpg_pre_lines: # There's no gpg signature, so we should stop at # this blank line break elif state == b'SIGNED MESSAGE': if blank_line.match(line): state = b'SAFE' else: gpg_pre_lines.append(line) elif state == b'SIGNATURE': gpg_post_lines.append(line) else: if m.group('action') == b'BEGIN': state = m.group('what') elif m.group('action') == b'END': gpg_post_lines.append(line) break if not blank_line.match(line): if not lines: gpg_pre_lines.append(line) else: gpg_post_lines.append(line) if len(lines): return (gpg_pre_lines, lines, gpg_post_lines) else: raise EOFError('only blank lines found in input') split_gpg_and_payload = staticmethod(split_gpg_and_payload) def gpg_stripped_paragraph(cls, sequence): return cls.split_gpg_and_payload(sequence)[1] gpg_stripped_paragraph = classmethod(gpg_stripped_paragraph) def get_gpg_info(self, keyrings=None): """Return a GpgInfo object with GPG signature information This method will raise ValueError if the signature is not available (e.g. the original text cannot be found). :param keyrings: list of keyrings to use (see GpgInfo.from_sequence) """ # raw_text is saved (as a string) only for Changes and Dsc (see # _gpg_multivalued.__init__) which is small compared to Packages or # Sources which contain no signature if not hasattr(self, 'raw_text'): raise ValueError("original text cannot be found") if self.gpg_info is None: self.gpg_info = GpgInfo.from_sequence(self.raw_text, keyrings=keyrings) return self.gpg_info def validate_input(self, key, value): """Raise ValueError if value is not a valid value for key Subclasses that do interesting things for different keys may wish to override this method. """ # The value cannot end in a newline (if it did, dumping the object # would result in multiple stanzas) if value.endswith('\n'): raise ValueError("value must not end in '\\n'") # Make sure there are no blank lines (actually, the first one is # allowed to be blank, but no others), and each subsequent line starts # with whitespace for line in value.splitlines()[1:]: if not line: raise ValueError("value must not have blank lines") if not line[0].isspace(): raise ValueError("each line must start with whitespace") def __setitem__(self, key, value): self.validate_input(key, value) Deb822Dict.__setitem__(self, key, value) # XXX check what happens if input contains more that one signature class GpgInfo(dict): """A wrapper around gnupg parsable output obtained via --status-fd This class is really a dictionary containing parsed output from gnupg plus some methods to make sense of the data. Keys are keywords and values are arguments suitably splitted. See /usr/share/doc/gnupg/DETAILS.gz""" # keys with format "key keyid uid" uidkeys = ('GOODSIG', 'EXPSIG', 'EXPKEYSIG', 'REVKEYSIG', 'BADSIG') def valid(self): """Is the signature valid?""" return 'GOODSIG' in self or 'VALIDSIG' in self # XXX implement as a property? # XXX handle utf-8 %-encoding def uid(self): """Return the primary ID of the signee key, None is not available""" pass @classmethod def from_output(cls, out, err=None): """Create a new GpgInfo object from gpg(v) --status-fd output (out) and optionally collect stderr as well (err). Both out and err can be lines in newline-terminated sequence or regular strings.""" n = cls() if isinstance(out, six.string_types): out = out.split('\n') if isinstance(err, six.string_types): err = err.split('\n') n.out = out n.err = err header = '[GNUPG:] ' for l in out: if not l.startswith(header): continue l = l[len(header):] l = l.strip('\n') # str.partition() would be better, 2.5 only though s = l.find(' ') key = l[:s] if key in cls.uidkeys: # value is "keyid UID", don't split UID value = l[s+1:].split(' ', 1) else: value = l[s+1:].split(' ') n[key] = value return n @classmethod def from_sequence(cls, sequence, keyrings=None, executable=None): """Create a new GpgInfo object from the given sequence. :param sequence: sequence of lines of bytes or a single byte string :param keyrings: list of keyrings to use (default: ['/usr/share/keyrings/debian-keyring.gpg']) :param executable: list of args for subprocess.Popen, the first element being the gpgv executable (default: ['/usr/bin/gpgv']) """ keyrings = keyrings or GPGV_DEFAULT_KEYRINGS executable = executable or [GPGV_EXECUTABLE] # XXX check for gpg as well and use --verify accordingly? args = list(executable) #args.extend(["--status-fd", "1", "--no-default-keyring"]) args.extend(["--status-fd", "1"]) for k in keyrings: args.extend(["--keyring", k]) if "--keyring" not in args: raise IOError("cannot access any of the given keyrings") p = subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=False) # XXX what to do with exit code? if isinstance(sequence, bytes): inp = sequence else: inp = cls._get_full_bytes(sequence) out, err = p.communicate(inp) return cls.from_output(out.decode('utf-8'), err.decode('utf-8')) @staticmethod def _get_full_bytes(sequence): """Return a byte string from a sequence of lines of bytes. This method detects if the sequence's lines are newline-terminated, and constructs the byte string appropriately. """ # Peek at the first line to see if it's newline-terminated. sequence_iter = iter(sequence) try: first_line = next(sequence_iter) except StopIteration: return b"" join_str = b'\n' if first_line.endswith(b'\n'): join_str = b'' return first_line + join_str + join_str.join(sequence_iter) @classmethod def from_file(cls, target, *args, **kwargs): """Create a new GpgInfo object from the given file. See GpgInfo.from_sequence. """ with open(target, 'rb') as target_file: return cls.from_sequence(target_file, *args, **kwargs) class PkgRelation(object): """Inter-package relationships Structured representation of the relationships of a package to another, i.e. of what can appear in a Deb882 field like Depends, Recommends, Suggests, ... (see Debian Policy 7.1). """ # XXX *NOT* a real dependency parser, and that is not even a goal here, we # just parse as much as we need to split the various parts composing a # dependency, checking their correctness wrt policy is out of scope __dep_RE = re.compile( \ r'^\s*(?P[a-zA-Z0-9.+\-]{2,})(\s*\(\s*(?P[>=<]+)\s*(?P[0-9a-zA-Z:\-+~.]+)\s*\))?(\s*\[(?P[\s!\w\-]+)\])?\s*$') __comma_sep_RE = re.compile(r'\s*,\s*') __pipe_sep_RE = re.compile(r'\s*\|\s*') __blank_sep_RE = re.compile(r'\s*') @classmethod def parse_relations(cls, raw): """Parse a package relationship string (i.e. the value of a field like Depends, Recommends, Build-Depends ...) """ def parse_archs(raw): # assumption: no space beween '!' and architecture name archs = [] for arch in cls.__blank_sep_RE.split(raw.strip()): if len(arch) and arch[0] == '!': archs.append((False, arch[1:])) else: archs.append((True, arch)) return archs def parse_rel(raw): match = cls.__dep_RE.match(raw) if match: parts = match.groupdict() d = { 'name': parts['name'] } if not (parts['relop'] is None or parts['version'] is None): d['version'] = (parts['relop'], parts['version']) else: d['version'] = None if parts['archs'] is None: d['arch'] = None else: d['arch'] = parse_archs(parts['archs']) return d else: print('deb822.py: WARNING: cannot parse package' \ ' relationship "%s", returning it raw' % raw, file=sys.stderr) return { 'name': raw, 'version': None, 'arch': None } tl_deps = cls.__comma_sep_RE.split(raw.strip()) # top-level deps cnf = map(cls.__pipe_sep_RE.split, tl_deps) return [[parse_rel(or_dep) for or_dep in or_deps] for or_deps in cnf] @staticmethod def str(rels): """Format to string structured inter-package relationships Perform the inverse operation of parse_relations, returning a string suitable to be written in a package stanza. """ def pp_arch(arch_spec): (excl, arch) = arch_spec if excl: return arch else: return '!' + arch def pp_atomic_dep(dep): s = dep['name'] if dep.get('version') is not None: s += ' (%s %s)' % dep['version'] if dep.get('arch') is not None: s += ' [%s]' % ' '.join(map(pp_arch, dep['arch'])) return s pp_or_dep = lambda deps: ' | '.join(map(pp_atomic_dep, deps)) return ', '.join(map(pp_or_dep, rels)) class _lowercase_dict(dict): """Dictionary wrapper which lowercase keys upon lookup.""" def __getitem__(self, key): return dict.__getitem__(self, key.lower()) class _PkgRelationMixin(object): """Package relationship mixin Inheriting from this mixin you can extend a Deb882 object with attributes letting you access inter-package relationship in a structured way, rather than as strings. For example, while you can usually use pkg['depends'] to obtain the Depends string of package pkg, mixing in with this class you gain pkg.depends to access Depends as a Pkgrel instance To use, subclass _PkgRelationMixin from a class with a _relationship_fields attribute. It should be a list of field names for which structured access is desired; for each of them a method wild be added to the inherited class. The method name will be the lowercase version of field name; '-' will be mangled as '_'. The method would return relationships in the same format of the PkgRelation' relations property. See Packages and Sources as examples. """ def __init__(self, *args, **kwargs): self.__relations = _lowercase_dict({}) self.__parsed_relations = False for name in self._relationship_fields: # To avoid reimplementing Deb822 key lookup logic we use a really # simple dict subclass which just lowercase keys upon lookup. Since # dictionary building happens only here, we ensure that all keys # are in fact lowercase. # With this trick we enable users to use the same key (i.e. field # name) of Deb822 objects on the dictionary returned by the # relations property. keyname = name.lower() if name in self: self.__relations[keyname] = None # lazy value # all lazy values will be expanded before setting # __parsed_relations to True else: self.__relations[keyname] = [] @property def relations(self): """Return a dictionary of inter-package relationships among the current and other packages. Dictionary keys depend on the package kind. Binary packages have keys like 'depends', 'recommends', ... while source packages have keys like 'build-depends', 'build-depends-indep' and so on. See the Debian policy for the comprehensive field list. Dictionary values are package relationships returned as lists of lists of dictionaries (see below for some examples). The encoding of package relationships is as follows: - the top-level lists corresponds to the comma-separated list of Deb822, their components form a conjuction, i.e. they have to be AND-ed together - the inner lists corresponds to the pipe-separated list of Deb822, their components form a disjunction, i.e. they have to be OR-ed together - member of the inner lists are dictionaries with the following keys: - name: package (or virtual package) name - version: A pair if the relationship is versioned, None otherwise. operator is one of "<<", "<=", "=", ">=", ">>"; version is the given version as a string. - arch: A list of pairs if the relationship is architecture specific, None otherwise. Polarity is a boolean (false if the architecture is negated with "!", true otherwise), architecture the Debian archtiecture name as a string. Examples: "emacs | emacsen, make, debianutils (>= 1.7)" becomes [ [ {'name': 'emacs'}, {'name': 'emacsen'} ], [ {'name': 'make'} ], [ {'name': 'debianutils', 'version': ('>=', '1.7')} ] ] "tcl8.4-dev, procps [!hurd-i386]" becomes [ [ {'name': 'tcl8.4-dev'} ], [ {'name': 'procps', 'arch': (false, 'hurd-i386')} ] ] """ if not self.__parsed_relations: lazy_rels = filter(lambda n: self.__relations[n] is None, self.__relations.keys()) for n in lazy_rels: self.__relations[n] = PkgRelation.parse_relations(self[n]) self.__parsed_relations = True return self.__relations class _multivalued(Deb822): """A class with (R/W) support for multivalued fields. To use, create a subclass with a _multivalued_fields attribute. It should be a dictionary with *lower-case* keys, with lists of human-readable identifiers of the fields as the values. Please see Dsc, Changes, and PdiffIndex as examples. """ def __init__(self, *args, **kwargs): Deb822.__init__(self, *args, **kwargs) for field, fields in self._multivalued_fields.items(): try: contents = self[field] except KeyError: continue if self.is_multi_line(contents): self[field] = [] updater_method = self[field].append else: self[field] = Deb822Dict() updater_method = self[field].update for line in filter(None, contents.splitlines()): updater_method(Deb822Dict(zip(fields, line.split()))) def validate_input(self, key, value): if key.lower() in self._multivalued_fields: # It's difficult to write a validator for multivalued fields, and # basically futile, since we allow mutable lists. In any case, # with sanity checking in get_as_string, we shouldn't ever output # unparseable data. pass else: Deb822.validate_input(self, key, value) def get_as_string(self, key): keyl = key.lower() if keyl in self._multivalued_fields: fd = StringIO() if hasattr(self[key], 'keys'): # single-line array = [ self[key] ] else: # multi-line fd.write("\n") array = self[key] order = self._multivalued_fields[keyl] try: field_lengths = self._fixed_field_lengths except AttributeError: field_lengths = {} for item in array: for x in order: raw_value = six.text_type(item[x]) try: length = field_lengths[keyl][x] except KeyError: value = raw_value else: value = (length - len(raw_value)) * " " + raw_value if "\n" in value: raise ValueError("'\\n' not allowed in component of " "multivalued field %s" % key) fd.write(" %s" % value) fd.write("\n") return fd.getvalue().rstrip("\n") else: return Deb822.get_as_string(self, key) class _gpg_multivalued(_multivalued): """A _multivalued class that can support gpg signed objects This class's feature is that it stores the raw text before parsing so that gpg can verify the signature. Use it just like you would use the _multivalued class. This class only stores raw text if it is given a raw string, or if it detects a gpg signature when given a file or sequence of lines (see Deb822.split_gpg_and_payload for details). """ def __init__(self, *args, **kwargs): try: sequence = args[0] except IndexError: sequence = kwargs.get("sequence", None) if sequence is not None: if isinstance(sequence, bytes): self.raw_text = sequence elif isinstance(sequence, six.string_types): # If the file is really in some other encoding, then this # probably won't verify correctly, but this is the best we # can reasonably manage. For accurate verification, the # file should be opened in binary mode. self.raw_text = sequence.encode('utf-8') elif hasattr(sequence, "items"): # sequence is actually a dict(-like) object, so we don't have # the raw text. pass else: try: gpg_pre_lines, lines, gpg_post_lines = \ self.split_gpg_and_payload(sequence) except EOFError: # Empty input gpg_pre_lines = lines = gpg_post_lines = [] if gpg_pre_lines and gpg_post_lines: raw_text = BytesIO() raw_text.write(b"\n".join(gpg_pre_lines)) raw_text.write(b"\n\n") raw_text.write(b"\n".join(lines)) raw_text.write(b"\n\n") raw_text.write(b"\n".join(gpg_post_lines)) self.raw_text = raw_text.getvalue() try: args = list(args) args[0] = lines except IndexError: kwargs["sequence"] = lines _multivalued.__init__(self, *args, **kwargs) class Dsc(_gpg_multivalued): _multivalued_fields = { "files": [ "md5sum", "size", "name" ], "checksums-sha1": ["sha1", "size", "name"], "checksums-sha256": ["sha256", "size", "name"], } class Changes(_gpg_multivalued): _multivalued_fields = { "files": [ "md5sum", "size", "section", "priority", "name" ], "checksums-sha1": ["sha1", "size", "name"], "checksums-sha256": ["sha256", "size", "name"], } def get_pool_path(self): """Return the path in the pool where the files would be installed""" # This is based on the section listed for the first file. While # it is possible, I think, for a package to provide files in multiple # sections, I haven't seen it in practice. In any case, this should # probably detect such a situation and complain, or return a list... s = self['files'][0]['section'] try: section, subsection = s.split('/') except ValueError: # main is implicit section = 'main' if self['source'].startswith('lib'): subdir = self['source'][:4] else: subdir = self['source'][0] return 'pool/%s/%s/%s' % (section, subdir, self['source']) class PdiffIndex(_multivalued): _multivalued_fields = { "sha1-current": [ "SHA1", "size" ], "sha1-history": [ "SHA1", "size", "date" ], "sha1-patches": [ "SHA1", "size", "date" ], } @property def _fixed_field_lengths(self): fixed_field_lengths = {} for key in self._multivalued_fields: if hasattr(self[key], 'keys'): # Not multi-line -- don't need to compute the field length for # this one continue length = self._get_size_field_length(key) fixed_field_lengths[key] = {"size": length} return fixed_field_lengths def _get_size_field_length(self, key): lengths = [len(str(item['size'])) for item in self[key]] return max(lengths) class Release(_multivalued): """Represents a Release file Set the size_field_behavior attribute to "dak" to make the size field length only as long as the longest actual value. The default, "apt-ftparchive" makes the field 16 characters long regardless. """ # FIXME: Add support for detecting the behavior of the input, if # constructed from actual 822 text. _multivalued_fields = { "md5sum": [ "md5sum", "size", "name" ], "sha1": [ "sha1", "size", "name" ], "sha256": [ "sha256", "size", "name" ], } __size_field_behavior = "apt-ftparchive" def set_size_field_behavior(self, value): if value not in ["apt-ftparchive", "dak"]: raise ValueError("size_field_behavior must be either " "'apt-ftparchive' or 'dak'") else: self.__size_field_behavior = value size_field_behavior = property(lambda self: self.__size_field_behavior, set_size_field_behavior) @property def _fixed_field_lengths(self): fixed_field_lengths = {} for key in self._multivalued_fields: length = self._get_size_field_length(key) fixed_field_lengths[key] = {"size": length} return fixed_field_lengths def _get_size_field_length(self, key): if self.size_field_behavior == "apt-ftparchive": return 16 elif self.size_field_behavior == "dak": lengths = [len(str(item['size'])) for item in self[key]] return max(lengths) class Sources(Dsc, _PkgRelationMixin): """Represent an APT source package list""" _relationship_fields = [ 'build-depends', 'build-depends-indep', 'build-conflicts', 'build-conflicts-indep', 'binary' ] def __init__(self, *args, **kwargs): Dsc.__init__(self, *args, **kwargs) _PkgRelationMixin.__init__(self, *args, **kwargs) class Packages(Deb822, _PkgRelationMixin): """Represent an APT binary package list""" _relationship_fields = [ 'depends', 'pre-depends', 'recommends', 'suggests', 'breaks', 'conflicts', 'provides', 'replaces', 'enhances' ] def __init__(self, *args, **kwargs): Deb822.__init__(self, *args, **kwargs) _PkgRelationMixin.__init__(self, *args, **kwargs) class _CaseInsensitiveString(str): """Case insensitive string. """ def __new__(cls, str_): s = str.__new__(cls, str_) s.str_lower = str_.lower() s.str_lower_hash = hash(s.str_lower) return s def __hash__(self): return self.str_lower_hash def __eq__(self, other): return self.str_lower == other.lower() def lower(self): return self.str_lower _strI = _CaseInsensitiveString python-debian-0.1.21+nmu2ubuntu1/lib/debian/arfile.py0000644000000000000000000002575012015175027017240 0ustar # ArFile: a Python representation of ar (as in "man 1 ar") archives. # Copyright (C) 2007 Stefano Zacchiroli # Copyright (C) 2007 Filippo Giunchedi # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 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 . from __future__ import print_function import sys GLOBAL_HEADER = b"!\n" GLOBAL_HEADER_LENGTH = len(GLOBAL_HEADER) FILE_HEADER_LENGTH = 60 FILE_MAGIC = b"`\n" class ArError(Exception): pass class ArFile(object): """ Representation of an ar archive, see man 1 ar. The interface of this class tries to mimick that of the TarFile module in the standard library. ArFile objects have the following (read-only) properties: - members same as getmembers() """ def __init__(self, filename=None, mode='r', fileobj=None, encoding=None, errors=None): """ Build an ar file representation starting from either a filename or an existing file object. The only supported mode is 'r'. In Python 3, the encoding and errors parameters control how member names are decoded into Unicode strings. Like tarfile, the default encoding is sys.getfilesystemencoding() and the default error handling scheme is 'surrogateescape' (>= 3.2) or 'strict' (< 3.2). """ self.__members = [] self.__members_dict = {} self.__fname = filename self.__fileobj = fileobj if encoding is None: encoding = sys.getfilesystemencoding() self.__encoding = encoding if errors is None: if sys.version >= '3.2': errors = 'surrogateescape' else: errors = 'strict' self.__errors = errors if mode == "r": self.__index_archive() pass # TODO write support def __index_archive(self): if self.__fname: fp = open(self.__fname, "rb") elif self.__fileobj: fp = self.__fileobj else: raise ArError("Unable to open valid file") if fp.read(GLOBAL_HEADER_LENGTH) != GLOBAL_HEADER: raise ArError("Unable to find global header") while True: newmember = ArMember.from_file(fp, self.__fname, encoding=self.__encoding, errors=self.__errors) if not newmember: break self.__members.append(newmember) self.__members_dict[newmember.name] = newmember if newmember.size % 2 == 0: # even, no padding fp.seek(newmember.size, 1) # skip to next header else: fp.seek(newmember.size + 1 , 1) # skip to next header if self.__fname: fp.close() def getmember(self, name): """ Return the (last occurrence of a) member in the archive whose name is 'name'. Raise KeyError if no member matches the given name. Note that in case of name collisions the only way to retrieve all members matching a given name is to use getmembers. """ return self.__members_dict[name] def getmembers(self): """ Return a list of all members contained in the archive. The list has the same order of members in the archive and can contain duplicate members (i.e. members with the same name) if they are duplicate in the archive itself. """ return self.__members members = property(getmembers) def getnames(self): """ Return a list of all member names in the archive. """ return [f.name for f in self.__members] def extractall(): """ Not (yet) implemented. """ raise NotImplementedError # TODO def extract(self, member, path): """ Not (yet) implemented. """ raise NotImplementedError # TODO def extractfile(self, member): """ Return a file object corresponding to the requested member. A member can be specified either as a string (its name) or as a ArMember instance. """ for m in self.__members: if isinstance(member, ArMember) and m.name == member.name: return m elif member == m.name: return m else: return None # container emulation def __iter__(self): """ Iterate over the members of the present ar archive. """ return iter(self.__members) def __getitem__(self, name): """ Same as .getmember(name). """ return self.getmember(name) class ArMember(object): """ Member of an ar archive. Implements most of a file object interface: read, readline, next, readlines, seek, tell, close. ArMember objects have the following (read-only) properties: - name member name in an ar archive - mtime modification time - owner owner user - group owner group - fmode file permissions - size size in bytes - fname file name""" def __init__(self): self.__name = None # member name (i.e. filename) in the archive self.__mtime = None # last modification time self.__owner = None # owner user self.__group = None # owner group self.__fmode = None # permissions self.__size = None # member size in bytes self.__fname = None # file name associated with this member self.__fp = None # file pointer self.__offset = None # start-of-data offset self.__end = None # end-of-data offset def from_file(fp, fname, encoding=None, errors=None): """fp is an open File object positioned on a valid file header inside an ar archive. Return a new ArMember on success, None otherwise. """ buf = fp.read(FILE_HEADER_LENGTH) if not buf: return None # sanity checks if len(buf) < FILE_HEADER_LENGTH: raise IOError("Incorrect header length") if buf[58:60] != FILE_MAGIC: raise IOError("Incorrect file magic") if sys.version >= '3': if encoding is None: encoding = sys.getfilesystemencoding() if errors is None: if sys.version >= '3.2': errors = 'surrogateescape' else: errors = 'strict' # http://en.wikipedia.org/wiki/Ar_(Unix) #from to Name Format #0 15 File name ASCII #16 27 File modification date Decimal #28 33 Owner ID Decimal #34 39 Group ID Decimal #40 47 File mode Octal #48 57 File size in bytes Decimal #58 59 File magic \140\012 # XXX struct.unpack can be used as well here f = ArMember() f.__name = buf[0:16].split(b"/")[0].strip() if sys.version >= '3': f.__name = f.__name.decode(encoding, errors) f.__mtime = int(buf[16:28]) f.__owner = int(buf[28:34]) f.__group = int(buf[34:40]) f.__fmode = buf[40:48] # XXX octal value f.__size = int(buf[48:58]) f.__fname = fname f.__offset = fp.tell() # start-of-data f.__end = f.__offset + f.__size return f from_file = staticmethod(from_file) # file interface # XXX this is not a sequence like file objects def read(self, size=0): if self.__fp is None: self.__fp = open(self.__fname, "rb") self.__fp.seek(self.__offset) cur = self.__fp.tell() if size > 0 and size <= self.__end - cur: # there's room return self.__fp.read(size) if cur >= self.__end or cur < self.__offset: return b'' return self.__fp.read(self.__end - cur) def readline(self, size=None): if self.__fp is None: self.__fp = open(self.__fname, "rb") self.__fp.seek(self.__offset) if size is not None: buf = self.__fp.readline(size) if self.__fp.tell() > self.__end: return b'' return buf buf = self.__fp.readline() if self.__fp.tell() > self.__end: return b'' else: return buf def readlines(self, sizehint=0): if self.__fp is None: self.__fp = open(self.__fname, "rb") self.__fp.seek(self.__offset) buf = None lines = [] while True: buf = self.readline() if not buf: break lines.append(buf) return lines def seek(self, offset, whence=0): if self.__fp is None: self.__fp = open(self.__fname, "rb") self.__fp.seek(self.__offset) if self.__fp.tell() < self.__offset: self.__fp.seek(self.__offset) if whence < 2 and offset + self.__fp.tell() < self.__offset: raise IOError("Can't seek at %d" % offset) if whence == 1: self.__fp.seek(offset, 1) elif whence == 0: self.__fp.seek(self.__offset + offset, 0) elif whence == 2: self.__fp.seek(self.__end + offset, 0) def tell(self): if self.__fp is None: self.__fp = open(self.__fname, "rb") self.__fp.seek(self.__offset) cur = self.__fp.tell() if cur < self.__offset: return 0 else: return cur - self.__offset def seekable(self): return True def close(self): if self.__fp is not None: self.__fp.close() def next(self): return self.readline() def __iter__(self): def nextline(): line = self.readline() if line: yield line return iter(nextline()) name = property(lambda self: self.__name) mtime = property(lambda self: self.__mtime) owner = property(lambda self: self.__owner) group = property(lambda self: self.__group) fmode = property(lambda self: self.__fmode) size = property(lambda self: self.__size) fname = property(lambda self: self.__fname) if __name__ == '__main__': # test # ar r test.ar .. a = ArFile("test.ar") print("\n".join(a.getnames())) python-debian-0.1.21+nmu2ubuntu1/lib/deb822.py0000644000000000000000000000022211703667743015543 0ustar import warnings warnings.warn("please use 'debian.deb822' instead", DeprecationWarning, stacklevel=2) from debian.deb822 import * python-debian-0.1.21+nmu2ubuntu1/setup.py.in0000644000000000000000000000236012015175026015542 0ustar #!/usr/bin/python # Copyright (C) 2006 James Westby # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA from setuptools import setup setup(name='python-debian', version='__CHANGELOG_VERSION__', description='Debian package related modules', url='http://packages.debian.org/sid/python-debian', package_dir={'': 'lib'}, packages=['debian', 'debian_bundle'], py_modules=['deb822'], maintainer='Debian python-debian Maintainers', maintainer_email='pkg-python-debian-maint@lists.alioth.debian.org', install_requires=['six'], ) python-debian-0.1.21+nmu2ubuntu1/TODO0000644000000000000000000000020711703667743014130 0ustar - Revamp the README files. In particular, note that debian_support.py comes from the secure-testing repository (also in copyright!!) python-debian-0.1.21+nmu2ubuntu1/ACKNOWLEDGEMENTS0000644000000000000000000000022511703667743015715 0ustar Stefano Zacchiroli thanks the Mancoosi project for having supported part of his work on python-debian. python-debian-0.1.21+nmu2ubuntu1/tests/0000755000000000000000000000000012015175027014565 5ustar python-debian-0.1.21+nmu2ubuntu1/tests/test_changelog_unicode0000644000000000000000000000056011703667743021223 0ustar haskell-src-exts (1.8.2-3) unstable; urgency=low * control: Use versioned Replaces: and Conflicts: -- Marco Túlio Gontijo e Silva Wed, 05 May 2010 18:01:53 -0300 haskell-src-exts (1.8.2-2) unstable; urgency=low * debian/control: Rename -doc package. -- Marco Túlio Gontijo e Silva Tue, 16 Mar 2010 10:59:48 -0300 python-debian-0.1.21+nmu2ubuntu1/tests/test_modify_changelog10000644000000000000000000000067411703667743021153 0ustar gnutls13 (1:1.4.1-1) unstable; urgency=low [ James Westby ] * New upstream release. * Remove the following patches as they are now included upstream: - 10_certtoolmanpage.diff - 15_fixcompilewarning.diff - 30_man_hyphen_*.patch * Link the API reference in /usr/share/gtk-doc/html as gnutls rather than gnutls-api so that devhelp can find it. -- Andreas Metzler Sat, 15 Jul 2006 11:11:08 -0200 python-debian-0.1.21+nmu2ubuntu1/tests/test_changelog_full_stops0000644000000000000000000000020111703667743021757 0ustar foo (1.2.3) hpde-3.0.x; urgency=low * Initial release. -- John Wright Wed, 15 Oct 2008 15:06:05 -0600 python-debian-0.1.21+nmu2ubuntu1/tests/test_debfile.py0000755000000000000000000001404212015175027017574 0ustar #! /usr/bin/python # Tests for ArFile/DebFile # Copyright (C) 2007 Stefano Zacchiroli # Copyright (C) 2007 Filippo Giunchedi # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 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 . from __future__ import absolute_import import unittest import os import re import stat import sys import tempfile import uu import six sys.path.insert(0, '../lib/') from debian import arfile from debian import debfile class TestArFile(unittest.TestCase): def setUp(self): os.system("ar r test.ar test_debfile.py test_changelog test_deb822.py >/dev/null 2>&1") assert os.path.exists("test.ar") with os.popen("ar t test.ar") as ar: self.testmembers = [x.strip() for x in ar.readlines()] self.a = arfile.ArFile("test.ar") def tearDown(self): if os.path.exists('test.ar'): os.unlink('test.ar') def test_getnames(self): """ test for file list equality """ self.assertEqual(self.a.getnames(), self.testmembers) def test_getmember(self): """ test for each member equality """ for member in self.testmembers: m = self.a.getmember(member) assert m self.assertEqual(m.name, member) mstat = os.stat(member) self.assertEqual(m.size, mstat[stat.ST_SIZE]) self.assertEqual(m.owner, mstat[stat.ST_UID]) self.assertEqual(m.group, mstat[stat.ST_GID]) def test_file_seek(self): """ test for faked seek """ m = self.a.getmember(self.testmembers[0]) for i in [10,100,10000,100000]: m.seek(i, 0) self.assertEqual(m.tell(), i, "failed tell()") m.seek(-i, 1) self.assertEqual(m.tell(), 0, "failed tell()") m.seek(0) self.assertRaises(IOError, m.seek, -1, 0) self.assertRaises(IOError, m.seek, -1, 1) m.seek(0) m.close() def test_file_read(self): """ test for faked read """ for m in self.a.getmembers(): f = open(m.name, 'rb') for i in [10, 100, 10000]: self.assertEqual(m.read(i), f.read(i)) m.close() f.close() def test_file_readlines(self): """ test for faked readlines """ for m in self.a.getmembers(): f = open(m.name, 'rb') self.assertEqual(m.readlines(), f.readlines()) m.close() f.close() class TestDebFile(unittest.TestCase): def setUp(self): def uudecode(infile, outfile): uu_deb = open(infile, 'rb') bin_deb = open(outfile, 'wb') uu.decode(uu_deb, bin_deb) uu_deb.close() bin_deb.close() self.debname = 'test.deb' self.broken_debname = 'test-broken.deb' self.bz2_debname = 'test-bz2.deb' uudecode('test.deb.uu', self.debname) uudecode('test-broken.deb.uu', self.broken_debname) uudecode('test-bz2.deb.uu', self.bz2_debname) self.debname = 'test.deb' uu_deb = open('test.deb.uu', 'rb') bin_deb = open(self.debname, 'wb') uu.decode(uu_deb, bin_deb) uu_deb.close() bin_deb.close() self.d = debfile.DebFile(self.debname) def tearDown(self): self.d.close() os.unlink(self.debname) os.unlink(self.broken_debname) os.unlink(self.bz2_debname) def test_missing_members(self): self.assertRaises(debfile.DebError, lambda _: debfile.DebFile(self.broken_debname), None) def test_tar_bz2(self): bz2_deb = debfile.DebFile(self.bz2_debname) # random test on the data part (which is bzipped), just to check if we # can access its content self.assertEqual(os.path.normpath(bz2_deb.data.tgz().getnames()[10]), os.path.normpath('./usr/share/locale/bg/')) bz2_deb.close() def test_data_names(self): """ test for file list equality """ tgz = self.d.data.tgz() with os.popen("dpkg-deb --fsys-tarfile %s | tar t" % self.debname) as tar: dpkg_names = [os.path.normpath(x.strip()) for x in tar.readlines()] debfile_names = [os.path.normpath(name) for name in tgz.getnames()] # skip the root self.assertEqual(debfile_names[1:], dpkg_names[1:]) def test_control(self): """ test for control equality """ with os.popen("dpkg-deb -f %s" % self.debname) as dpkg_deb: filecontrol = "".join(dpkg_deb.readlines()) self.assertEqual( self.d.control.get_content("control").decode("utf-8"), filecontrol) self.assertEqual( self.d.control.get_content("control", encoding="utf-8"), filecontrol) def test_md5sums(self): """test md5 extraction from .debs""" md5 = self.d.md5sums() self.assertEqual(md5[b'usr/bin/hello'], '9c1a72a78f82216a0305b6c90ab71058') self.assertEqual(md5[b'usr/share/locale/zh_TW/LC_MESSAGES/hello.mo'], 'a7356e05bd420872d03cd3f5369de42f') md5 = self.d.md5sums(encoding='UTF-8') self.assertEqual(md5[six.u('usr/bin/hello')], '9c1a72a78f82216a0305b6c90ab71058') self.assertEqual(md5[six.u('usr/share/locale/zh_TW/LC_MESSAGES/hello.mo')], 'a7356e05bd420872d03cd3f5369de42f') if __name__ == '__main__': unittest.main() python-debian-0.1.21+nmu2ubuntu1/tests/test_Sources.iso8859-10000644000000000000000000001005711703667743020500 0ustar Package: apache2-mpm-itk Binary: apache2-mpm-itk Version: 2.2.6-01-3 Priority: extra Section: net Maintainer: Steinar H. Gunderson Build-Depends: apache2-src (>= 2.2.9), libaprutil1-dev, libcap-dev [!kfreebsd-i386 !kfreebsd-amd64 !hurd-i386], autoconf, debhelper (>> 5.0.0) Architecture: any Standards-Version: 3.7.3 Format: 1.0 Directory: pool/main/a/apache2-mpm-itk Files: 24d02f8ecf0f37164cfcc2fb23bf1013 1135 apache2-mpm-itk_2.2.6-01-3.dsc f8041c58e43dae9814f903919d222c73 33269 apache2-mpm-itk_2.2.6-01.orig.tar.gz 71dcfe1d2349b376c062c964a87cd213 12824 apache2-mpm-itk_2.2.6-01-3.diff.gz Checksums-Sha1: c9ab75c5b2360d1f1ebee513cbf62963023196b9 33269 apache2-mpm-itk_2.2.6-01.orig.tar.gz 12b996db5c4a628388c1402e13bd30aa0db3cec4 12824 apache2-mpm-itk_2.2.6-01-3.diff.gz Checksums-Sha256: e789b7754072fa6a629c55b931411806432102be7a379f750ac6d328df7790c3 33269 apache2-mpm-itk_2.2.6-01.orig.tar.gz db631e44c83f7086b15e701bafcc7e6e1481f957d47b8075092ba80d863f83bb 12824 apache2-mpm-itk_2.2.6-01-3.diff.gz Package: binutils Binary: binutils, binutils-dev, binutils-multiarch, binutils-hppa64, binutils-spu, binutils-doc, binutils-source Version: 2.18.1~cvs20080103-6 Priority: optional Section: devel Maintainer: James Troup Build-Depends: dpkg-dev (>= 1.13.9), autoconf (>= 2.13), bash, bison, flex, gettext, texinfo, expect-tcl8.3 (>= 5.32.2) [hppa], dejagnu (>= 1.4.2-1.1), dpatch, file, bzip2, lsb-release Architecture: any Standards-Version: 3.7.3 Format: 1.0 Directory: pool/main/b/binutils Files: 04c6dfcfa9d7e2057fea9561610b009a 1331 binutils_2.18.1~cvs20080103-6.dsc 23b4c0bdda7c9974489bc9d3aaf7a754 19746003 binutils_2.18.1~cvs20080103.orig.tar.gz b57f7ed234c299f56a6545c4251e509e 64334 binutils_2.18.1~cvs20080103-6.diff.gz Uploaders: Matthias Klose Checksums-Sha1: ec4182b10960eb6ce927af37326feaa114a232cc 19746003 binutils_2.18.1~cvs20080103.orig.tar.gz 5589356c2c4cc9a94aeb9ba658c08a4ec3fae11b 64334 binutils_2.18.1~cvs20080103-6.diff.gz Checksums-Sha256: bf2e12578caf3b79be6182bb109b3a0215d8a3b89ae2049773f56f286eeb8ad3 19746003 binutils_2.18.1~cvs20080103.orig.tar.gz 44e346757ce336f6a16879264b6cc4ff4e2b4da517e4b5db6cc7f1d254f302dd 64334 binutils_2.18.1~cvs20080103-6.diff.gz Package: debian-archive-keyring Binary: debian-archive-keyring, debian-archive-keyring-udeb Version: 2009.01.31 Priority: important Section: misc Maintainer: Debian Release Team Architecture: all Standards-Version: 3.7.3 Format: 1.0 Directory: pool/main/d/debian-archive-keyring Files: 2675031b2286ca8dfc085e2a9c9d38ed 838 debian-archive-keyring_2009.01.31.dsc 5365c07ddcf639544933552e31a571ee 13627 debian-archive-keyring_2009.01.31.tar.gz Uploaders: Luk Claes Checksums-Sha1: dd987a5ee85d8c4fc3e115f60a6a021e78c626dd 13627 debian-archive-keyring_2009.01.31.tar.gz Checksums-Sha256: 9bdfe720407f5c134842eeaa8cfc9932be33150a5a3c041426216a90cce9e32d 13627 debian-archive-keyring_2009.01.31.tar.gz Package: python-debian Binary: python-debian Version: 0.1.14 Priority: optional Section: devel Maintainer: Debian python-debian Maintainers Build-Depends: debhelper (>= 5.0.37.2), python, python-setuptools Build-Depends-Indep: python-support (>= 0.3) Architecture: all Standards-Version: 3.8.1 Format: 1.0 Directory: pool/main/p/python-debian Files: b153648eaf2fbf2b20e07986f3f8fc37 1224 python-debian_0.1.14.dsc 9d05b7813dbbdb4fd8d41f4a2825de57 176558 python-debian_0.1.14.tar.gz Uploaders: Adeodato Simó , Enrico Zini , James Westby , Reinhard Tartler , Stefano Zacchiroli , John Wright Vcs-Browser: http://git.debian.org/?p=pkg-python-debian/python-debian.git Vcs-Git: git://git.debian.org/git/pkg-python-debian/python-debian.git Checksums-Sha1: 00521e603691403da83a517a8ff10cb4e0e05bcc 176558 python-debian_0.1.14.tar.gz Checksums-Sha256: 40be3da49191a26b3301f18d6824c2a3e7ef4ac52a99ed4484d9176ecc7685e8 176558 python-debian_0.1.14.tar.gz python-debian-0.1.21+nmu2ubuntu1/tests/test_modify_changelog20000644000000000000000000000073011703667743021145 0ustar gnutls14 (1:1.4.1-2) experimental; urgency=medium [ James Westby ] * New upstream release. * Remove the following patches as they are now included upstream: - 10_certtoolmanpage.diff - 15_fixcompilewarning.diff - 30_man_hyphen_*.patch * Link the API reference in /usr/share/gtk-doc/html as gnutls rather than gnutls-api so that devhelp can find it. * Add magic foo -- James Westby Sat, 16 Jul 2008 11:11:08 -0200 python-debian-0.1.21+nmu2ubuntu1/tests/test-bz2.deb.uu0000644000000000000000000016622011703667743017370 0ustar begin 644 hello-bz2.deb M(3QA73OS-PA-UYQ MA5U2BOWK>]:TC:28K9$'%2C*"THKD3.'=\X]]V,NKRX>W03,6[L\I;?BM\]/ M=B&%='9QW?MXN(X'VCJNHOWE-_^IW5?^OQ_U"ZO;HN=C[?S M(W['$F!GS%K\G5?V<_RMPSHIE3(7G7A$GS[;_WG\8Y;D%?E0@U+2D=#")I>C MH.2EL*'KCO-TE?K]U8Z'8=Q(43VE7`*QL9%*3=FX8*K.L<0<]6G]O*.)KX8Q MT\!7A:_^]MV;'U[?R=MRD[+&M!&U85!N*T(9\M$:%&/$MI@$U M'5>@1`J2*$!*HMA4!/DD@K'9D,F^M*!X;D-5GZQ(FI3.@EQP4DD9$K/U.1G% M+:@ZM:&\5-H8([E8841E2EQQU"JMTM+KW/)J:$-9"=9MR+;ZH(PU<*W&J"5S M]=ZZVH#:4AM*%O8F%5EE-@8@%`FX21:]+D0VHO`(52B618S3,.G,6NB1/ MQ>20BG&QMKR:[U>X,J!'U^J2#ZYF$D4Y:Z*(AJ1F:NF*YS?/_KZB!XH.:LC6 M!A\#I\#!AQP(D$*2;#'_?O?FNU=M--8FJ>*+J*%`\2[4Z"HBR$('R;7%V'XE MCCJYQ#'5K."6P^E4310J1,J)H-L&5%_:4-'+:`EY`KJ\B:Q\5F2\REXCG+%) M_HI099*E,O*1`_+7!1"<$?NDD09HE[UR),2G5J@_CBKJRJ4Y*(R(JA(KD')BJ M.+6,%#SY%E=OVU"*+7S@"(&F6KT6F2E7Q"$YKD+95@0/:U6+=76*!'+92.F4 M538I4M*B!)%L>?5VA?8HO*X"BB(I2\A0*2$A+9,*2'#B%M0*5\A6BO%VX\ZUT/J[H"K07AUS1'DVX!DJRHO.4HJ,.)$R+J[L5 M75DEHH2T?"6/'([0E1`Y$F,`A@B$EJBZ6Z0J)160FI-(ZX[QRQ@3&*=6E#9:H5+'H&%2=5<0A M"MGJ%VF[0I?4Z.T8;@O*:ERR.Z6(X4MI+@J9T(`ZK&50*9AK'.=L%3([JR`3 M)ZT85U6DD?3L*Z MPF2KKJHJ1#2;B'Z6`\;,WP\/9MV`G*6B,!QC,'*./ M"W1U+\SOMM[2?OF1'UF4RU?_@?O?)>C;'Z9Q>+0;YA?O_Q)%8[G_0]&8GYS[ M3;E77_`LN,$D%X'MWG.=[PO\W4W]"F[[NMO_PPT?>F>NF\V M+_>@>QBX/+WIWV.##6'S`_7[`WYXNNYN:'_H:3MV/_4#=7^::7^//_Y23HDY M3MMO`3_GJ;\[^?7CCKL\T#SWN=M.S(=^OWW2T;YTU&W'L73\*]W>#;SYL/+[ M5_\XG;.[F\;M1+?+LQPSSUA>Z;8?>IJ>='7J<8#AW6?$RZY[>=AT<'Q\F+O] MN'_ZWAT//'5S!E!FK!B'[F'7Y]VF>QB/0^G&PXZG MAQ[;$G?'/=T3CIF&96F'CVXO-QU>-PS"C_/P[@G>'(_;W36>_=SA1?M/1^O& MVNW&AV5G&>'%J8AU=Z?``N7E8=D`U$\?W9^"O&P\?&3E]33^@B!^-7?_/!'T M,$Y#^>H33YONZP_N+T#]8>:A_M:!.D[_#O3-Y1\I0F<[V]G.=K:SG>UL9SO; MVS3[%^2'WY0`*```"F1A=&$N=&%R+F)Z,B\@("`Q,C(P.3@Y M.3@Q("`Q,#`P("`Q,#`P("`Q,#`V-#0@(#0Q.#8X("`@("!@"D)::#DQ05DF M4UD?00X!`&0Y_____________________________________________^"5 M=]?7MQ[W7>T]G0```9.>>SZ$D4!2K8J4"$];Q:7M-QXM7QT^ZBB@^ M^[G=\7![@`ZM]=S[X'H/9J``"\'5J\W?>NV*.YA[MW8`<@'2S`"CMZ7GLYN[ M[ZKNN MSD?(]?7TW?=O9U[V/5MK6@M=[WO=P`=(SX4UH?3[L`4\G7WWOO2>SURH[V<% MY\X%&W<94+S>W>[VCKW7/7GO,H:]"5YW:4\=OO>]Y[#>S%MKO==O9]>]Z`'> MQNS5=;WF#T;W<[3'NN[V]Y]W@E\TECNSM];KI-J^YA[V-+;)G??.OO5\:Y[O M;WWS[:WW>]>L`Z"12E\C(1(50?0#0`!0JGT#1(H$@76T`U76;-!0`%5X^2`' MW?>>OO`#[WN^^P`/O?4=Z^]G5!:,44`````OK[SPTD0(``F``)@"83"8(Q,$ MT:8(8F1IH:--&$`,0:&0PC08FF0TT:&FFFC1B--!HFTT`!H--`T````09)!- M``!&C":-`:`$TQ&FAH#03-)IA,:(-$\AH-3TS03",FF4R:;)J>F1HTTRGHTT M3:#1,PIY)F@*>3)Z%3\-`-)H>@::"*"20`@$`F@)IH30T":)Z-&BI_HTCU'J M:3]3)3]4_)-D9"G^HPJ'LIZ*>HVD_2:>BAO5!^0D\1DGE/4SU33:9)ZGZ)/U M3U/4]JGA,A#,U3U,CU&T)Z@T\H&U``$A(0@@(3$:-)@"9,F#4T]"&4\J;R34 M],FT:C4_1,:4VD]'I3:8FFC333U&FC)DAM1M#31HFGE--HVJ;281IM"-D-(] M1C4-ZC32>C2>D>34,CU-/4"*)!`(FTT1,3U-/53>-$>JGBFC:"?J>IZ2?ZFJ M-Z&FIZF4?JGJ>IYH*;*'J9&U/T28>J/2/4-&TGDG@FD>*;4_35-ZH/4VIZAZ MF)ZGDAY)H\IZ)IZCR1Z@_5`T'J>DT>*/U-01*(0(R:$R:9!J89)Z)DR3T9"9 MJ3R9E3Q3>IJ?JIO(:IO)3\J>4_(TIZF-H,2-&:*/)IJ,U-ZD])XI^D]2?D4R M:#S2C>DR9#30#U$_*CTU/4]3RC93R-JFCU'IH:GJ?^58*D;(_7^'(G.WC))` MDDP5YS,%`I#Q44GEI]0J?66')5PFS:O3!0SXV0OZO_(^A9YA]3(8A<$,WOLN MY.Z\JF[>JBJ;[?<`(,VLFBF8$BI!!L`)T>F):O](!8DSG%.""]#>`!75P"EZ MK-"I=KFK:(V8\G++Y],@8%\'/U?<<7=8.O_.[S4WW&-_?^7XZG'DP?>7MT`X M.N4MR-!JO4YL]8U[N4""Y'ZXAD&!V2-5:J^;2K5%G2@BW>5Y>>(T'?D>D%F/ MHA"\BN:0O;9)3]NT)/;6W3L]8$#>W*>I[2L=9%V!')ST]FB@>_X^BAXT5:N? MA)7"(=QZ5)$0EWXE4N(*%TDD)=2H,K*$G)4I$"-WS(4@;_\FA^7[:BFVAE@",B`7$L&ZJ7![>IY,%7MO#Y^ M)"E35.U3WA3;P:-!`I7+M,Q,M[:D2@MYAU`ZHFM7MK55I&7EKN'.7P-\L"`8 MS+0L7*Z[@#+DK+`6R/6X"AK(UD)[J%`X$*J]AP+G,XK^2,>36YESV2\#&7(# M$28\J*[NF22!&ZVENOI=-P^!!NR7;BF5TQ MP*N'DKKZ5"T.=3\JMMN,[IUV]`';P">=@@QFS5K6UBT4$FU)L!;1I]= MP$0.YXAD'NK_)X]`7>/P$+-+`^KX>3_F=VR60A;-="IO[3A2:DQ"RPC"W;?, M\#:O#]?==?R;;A"XVO1X:7G>G"U@,.EOBE0(P0)W?)MH3NDD\VLC/NZP+=[] M+KOYV*-Q`;6UE7EA>(K\LN)#B"M;S*62,`3?P*YUC=?6OWU59@.9-_7`@DVM ML`\T%K03`+.@PY-^G@*I^"2`(POQ,_IJ&+CQ2+-.P+:%0$K@-26W`B3!:DT# M1/79Y=F"./$&Y7;8^RK7RA'YVMV-ENQSCGW6B/6".8<_2C4SEF"F,>?/=QIX M`-JNEG.\+.II!1-K21^,FJ7=Q72U_?K.)D&TG0[R,VK8M(3">X& MA@;)]>8+6PLL!;$T)-O04"SHC)LTE!"8?6.'H7=.UEZ6GF43H@@+0[194I@$ M%OAP&\60Q!?2)3:B%=2[JUD,$,I13]/"KDL49>SPIQ-FP(V.=\;:QV MTAV"0*JM4&J!FA6LV*R_K>3@(T>2U^R82R!*JI^P;4DQE_2+,"OLL``^S5CF72]3,HFSVMF4G(G3SF?T:ASU M!48-TW@DOXNJ=0(^_N5GF>?R&S_-JF7=QX<.LZ>7,]#71Z6ORNH?C9H.H=X MZO7;M+A8N`08$X#9SSB9N&A86/2WN$?1^6T`R$@*"KQ6*7J[;6P+=?#6;.3< M`.A31!7TP'F4/8ZZ))'3QWCF=0XRWQD_.K,16Q.T2;&A#!Z#XX>_0$&:!F< M*^6J8KFTL\$30DYM_XK:>S=@'G<-[W*SX(-9"RJ!UYWB5),:!Y=_M[C$2<7> M`@T(GX%JU.H&^>M8E_DM\P[JY/DQM%AH7`C]DU"E-^T5DA/X2??!^!GOB8P, MC3N7(L&0,1O7>%GFI=HOHP*F4^5A,'?HS&@OX=Y:/#L:K/LJX,)E<2P07!-V MEY?_(C2&^)H;.`3'1XB(F%;\_A%P=FZ%8[`%^LIRW=R28T2)%H=_'AV^`MX0 M)(`K:[!S*EBFA#&<"WAR-Y<`-H$AF(ZAYC)NB6*#D8B;@7CKGE8*S7J/?G4P M%HJFU6-JR8,-X""Z)=M4DBV)^*8T*VAJZKO/2MWSK$JEZ;O:7B39?BJ&)KKY M,OZ7.Y)?8F\/A%F49^+T;I8UJO;G27ND/[TKTP--D]`@30T*RU@0)5.W\1?H%]Q]M%CY:>Y]9[!AY3$S5J:. M`Z!!+4F")I.8`1=L!%;X?/5\!S2433R[2FD0E12L(EXS\QD+$6;`/948%9R>D\GD7RF3`NT):6U4DC6P2PFL,TREGL8]DB M0P!!BS(MR1S3>VXN99@S<UF5,OD1IM7X-*OJQJ M$*:[:[#MK"]#<2Q9C+[V:2K8ZR"-L8YH6NE=9,Q'+49XQ,L%(:M>SHX,LK2V M0JS2IM9TB>5FZV8O6D5GE62!&T=4[)9[75:2&X]YS@UWM$]-# M1.\M".S(ZC<>H*U,;6CT>>[$6JJPL\Y8-YWM>:DKB%-JF$4H(3,,I)XN6RX& M&6[E@U1:@>N)55,VY;*,Z$*:!B;C-CC39([!T3M;(P46#>5 MG@CN"M;@BI%H$!*[;5<6`]W*V-,LA6ADE@.HP0N$)G)GA+,0F`1:,WA:SZ,Z M$V"6JUGMSF6$#+1+@:6F,8A9`&3$(DB$H2*:6)2&T7KK\ZMK8)ZM>=KZ1)L6 M^A,EBX1ECD]!5@2&D"F8E:62. M^8%5$B;W%G0'HU,B=G(S9;&$;6461.CA)`<8.$8M.VJ<298E_';4`(1"7YCQ M6#P+**PO466!AI<"0KNZ75UQG2:R9!,:UY0.CL$$7A$)R`G(CKZ(E3./*HC. MIHB38+K&D68DF,6QG6`& M?`Q1>+$$[>*G&@`&?$PP$;HBUB8LY2@EL+*4:0`U<8:BZ5@*LT@NT54$"O:" M7-O#1HRZ&+I6@.@8+#01)!OP&\0#%%+(`M8IP(I;`LBAO(EN.@$@;$$2L420 M[2A1%8L1!NBA2(&&(@:T`/O?H\^MD0`N@"XXH&*Q#+H40K$+K1%`I`5)2@`[ M^#6*.I`00.A@"_RR$/YY2Z]4\*`!BB+^5%03U\`0/_L%PA$00X4139B"A+ZY MHY8F`T@38NU\OW4YK^WR9'9U?M6,;NY)CFR^)BYE=7O:,O+&4>#*\Q:6;;M, M7OBV*HPWC#>4ER\C;=K7RMANV*J]VVY)E53*JQC&#&5\LLI,K/0T8A=E"(0_ M;.!^\TA)>"DJ4]W!1/#B`%D154@*L%[B40.)%`#AU`I0&@`;^/6]A0_C@6^7 MS[ZB)^C$1^ND@=3V"H&AK66%[OAK:A/6;]0V;;9XV@J(/^;)0)L@9HHD()"( MJ4!$"**<")P(B%(GV+D2[9V;N@"S22&Q"*KJ.HHC$$.U@1#_F%=Q!:0,),DI$"_IRK.G%5 M!EA))D!U,9FX..KZC'*W!1@0NPH,_60\39V07L1C.X:15<24A%'_Z!&E7V51 MITP(DWTA`2`1:@I4I!A")$61H-&/VSCL[)A30(;(V`T+G;`H9V.2QIG90@W( MR$99B.W:)G^C204P[%@?0L1\ON"B'"S>CU)T=M>[H=/F*TD50.;"ZM"8(:M> M/$P'PKKHI%\MKQ'\'!*_R'$6$,PV`=."S^31F-AHP1-_ M7V=D@7A^3E,[-KA_)WMMRBR1'%P\%YN#D/SEE`4#L-;I[ M[Y42\C^/_`-.RDA'I^5F/663O.8.SM)#A'*RSB#^K$`RQ$.%[2B`FS)!.HT: M(%(LBEBD(FFP6D#OH15S87R(`P$(=M#T\(_K0H&_@K@$B`/`F4`\KJI0'G>D MD!KA-I,JFJ]MP>^RZ<]8.*([H.8TDN]:/4YW5`NND$(/@,BM M"(-/)>1KGG==%E$BAB2]Y(!JQ3IH@UB"=3+.-0%O05M*45*1?B]71!R$)*Z$ M$$?C'4#9I_M`O56/D;#LKOU@/:)S78PZH$'H;?#:F;P)'@S@KKN7W=+3*=\U M]&TA':5$(X*_9KM>V@]L`_T#/@>SN7H["1Z,!@2X56^#+':K,=V^5,!$/7V1@1/:;X)#`UG&ME#<2M MM:[24R9F1QOU1_=5)`@5<;P<'/TJ7Q56,<55>7=VE_>LUZNA#:;,*@ M.($I6+_@IN05=9I.,M)?Y/7O"A46"@A>6F!/DDB$5CSU+B00L$U\ES*QBF)9 M!+G'X",!`E(\@0QALN<<$V2"V']IX7X(JFZX0(%F0/WS(8$".]`NQRP0+&!? M>&$$"EI8*;`"!8:I!`EZ[G@0*S$D`O$4G^XRQG_#OP)0B@,6QM!<0T3$10/& M&,5/KNCB:><>S]^7K2M-_KDD8?J9%SS.1@$`&G'SW<=D8+WI"+_PAY:]PNI6NVNAN_<>"!-,U%_H"Z!`L MD"!5&'W%\!ED`%8P$7VD/""!/G[MOQ_K;``>@$4"``<2)^S%,/SZ"H_Y8ACB M+B,];IT[[RQS2DNN8@*8,_@_;B]TU@^VD_GG1_2PLC8-`B-.3T#2H:\!@B/P MG_]K[?UOM?I*\"_B/F>PG(?@?;[>RL:G3\O?9[?YM\"]I#[#\*WW;Z?SVVLC MX/P."M&LVTO+$/>,I"0^)84*R2@4$?JU/\*120/?;`>W]GK"/UIRIQ#;5.+^ M1(]U0;D`S6!"@C%6"*:?(!EHW'(J7'?4P`,>C1G?,PO3` M&R!P?^K"`P^5[P\*'OO_9L:JA&T]49_^/Q$AF$>J:!*7J71?IL&_GX#OYDZS MX_%E?DXG.>O]CC"&W];%39"4V>@4-'(',A)AAISYI%6(0X2(%YJ&=-A,_F?HV64I. MCNIT:*NIX\0FS,8W7:53W]%H8!5[?`]T^T$0(0RVI+' MC2VRXS_9=11`5:$`!0"$HF8]>V4@.$$H=3PMN3E"9G_P&KT?ZS/QZ#K`::$> M+:,QOM_1V+JI3\9=MPBM&R.'C.+%X9N5H':RD;@:/8Y+)I6@8.6N@P`_(G MU0-H3N@'9/P`5)/!TP&-)^^_D[\"Y)^W`V6\J8F\V,7Y_?715M9^AP8$_??3 M_GJC:Z)<0_+K^#6^P\VD7+/Y5(KZ M^[U4\N^9OX5ES=5J8GE_#_9BD>P^\S."<92?E;FXRPJ4L..K^"@K]P1A164I M$6@:(/T)&8;SS'#-,>!PI1,/#QZ/RM-K?R9M@;@0+<'``>#S8?V_C=7+Y^LYN?!26]-)[7,P M/)<6.:I>NBX.(5>IP7/U7NH>;]8R*IE.Y?U_\Q.%1*N6?N1;M.YZW$4%/\X7 M*]?GJ*LL:"!083.TO!ZZU[N5AVGD3844.MJD5WC8'#)SSZNKOO7`6D4^:*2> M&GWW7J5*C6LG[4/QZN*)O2X:TUV?TDS`R]?4:J%>,=Y:C9T>.PLS>L'.V MOXV_9D8A.=)3R9')0,QWJ'H8G:6-]EOEU_D^;\KZGY?^7$^=N>+UW[Z*D&]@ M1@['0H+38>12D,0(Q!/K4I!&1C[N+19RYY<6LD=X=-S?W>7\S['R?YO:8[Y\ M?W"KFCBWF'UVAS`/7;;&P]9<;&/ZGPMI#O[H7QP>M9ONYTI`6*(2`+A`1R3* M9G]36^GY(N8Q\Y+4'\8OSTX[@>OB)UG+=_"JCM#M%YRD_N(\XE>37F1*K;9^ M-5W/BQC#U>2_--0W)E[$R/#<*WOO\&Y+"UX9:B@(>F3L[^L0B<8?-H=AXOD_ M@BAC$_B#.0-&DB!'-.73APVPG:PG)1Q(>T]*2]"C\^9#I(0_!@^_@_;G;?T4 M#W_YV=5^M!#T<4/I_)^^JJ!S(>/\2&C+/E;)R_G1>G8&3+OB_HVIL*;Y5-NC MS=B(8U9IC"1D&U%`AAQA<:(F/)1WT=`&MF32+L])\F'5<>+VWY4V#8/1QB$: MV&QBY\2L+,RE"#"%8#Y$-`F6A1W@*;$.Y.'\[Q^]OF%HXLP\]A7B>&Z=?C?S M.OW'_FLX<>BW533Q.0[&DU/\S]Y^+C?9NT-ESTM!H@AIGH[W(/D95!!6`>P0(Y0, M2X89,SU:]*$4I/?I]K3_.1K.;1N>W8GEAT_\]>R?*!7G:NQ8J)QH%IVVT1C] MI,,T;#_7QRK.U_/G,<;UWT7CG]9X&I?FBSPBV"` MZ1B!_BB)?S?V=1*SZD''#8!>VV_'2U[K+DP>`R%#EI`K/]] M+)%@2L(1II&4R^S(1>VL')&G)&G'\/*5G)-ZX^$+FVJS0YAF[JU0E"$4(1J$(8))-`BU+30;66.=O?T<+O533M1 M9Z<:4\];"4_OM\]WT4'V@>N,&3+CS;&@0Z$(FS@&.&UW^`T]^N&)SU'R6QSE M/7)14?O^SK]VK6+2T/U=L.\;D_IZF?I1IQP)W_]+3]O0^_%#J0VSF\8'XVN0 M^0\?CR?53YUBNPM[YGZ;^3ZOF0FH`Z-]'(=@Q)5X^+?J+Q_9<_V90Q@TE_?@ MO::610#8/)B%JBBH;2C4=)6!S8C6,B;45"WH_'J@6Q74F.&K%Z+)0$J=/9V8 MTC'FK;6C5UFY6QQVKIZ5J^-7Y.)P=[$9]5.NGP(7IT/$I,W;U=[N,.M,5N&3 MYG?+\6!S+1]=["B=S//1,RN[:WY/WJ[ZO1>NIO2LL5A:D4]4,4R09V"L6'-V MTV(*EUG.W;.6(M9=MJ6`1YOI5FT3.M!]!I>M]5])X_K_:GD>?RX98Z=9`;^U MT#_7/-?GQ*#&A>L:';!`$"`(B.%*5M0>_`(`4+JJJ-_N>+\\L7ZN><7R7'H\ MYW[^_8VO.G:?(K]@ MF\BEPA@N!=*(UUK1$WXB;TR[SP^QO_:U;M4]S#$!4D1L;%NPP<._1&&DG^HT M[+$'M:3%WX__N=DUL:[;7)UG=7_U3#UES4J]GLJ81E5%'KM@!`#O^AS=@2DE M2NGP?S\57U=LD9!D,/-KK.2$QE7LJ#&I08&;R6.`F4[)(U^V5&[(1$&6-,"F MM8`#@0!3.CWU"_1M+#3?-\*=T\#OH/(R_-E+MDPRO-_61NY=RH3YJ?S+'@,) MGL9-;VVR_93X:&E_NKSG"3=E^8#0)V@P(EC`4)OO8;^/X]+H0`0+)3[!PZ/$ MH9W:);#SL%A<'`,<[J777Q'/I.=[-/>JSRQ`@0AS$"FJ+V>-WB[]O4_^/EE& M$.`#.:,!;QFB]8B`41`@ MMUC9C9\7^\/)=/.8?U.;%O5H#WYFGZ/S7\2J=O4&)8!C`(M`18KL+#^"NHZG MP]%A]_D_;K_P=?T?Q\NI?\,ORK:#"8O-:WJP!JT`O`0(Q$0*4,"`*/O-G.7] M2`8?UC+N*\?!\&46UG3?1H1Q'<^7$6[;JKO-7%T;@T4,IE\W`_/QNWJA03"K MZB:M/>_8ECT'S8KSE0%5BT+=97&(YL;<:#;.7I]W#3?%&M(#:#&,`1"OU#]` MY7?TS!FL-E?S9^>"TK3J9O]OM$KCZF`<=RN.H#O#;%X\LC,;?8XJ@_MIGV+O MN%)`R(Z?;\?$F3\#[/K^U:A]:P'#@SB#Q2&A:"!`/8!@49*;C5TZC?W"%-50 MZ86YO,4>-?+'749DC\L^*'LP0T\16]QUTK3>4_0M+QBI\OH]OENZ]QMX_WOO M_Q/G^CO[6?*ZVRD=5WT_`JU2WN;>NMDWCR?P/16\KWF*R'@,A,^3Y>FJGUFK#4ZK^N+YG0>Q^;(;D@XW%**@$)0E"0,&@''H+ M.XTGA8QL6Z$=ZG-2TT_WUEG\^''EY70_C,[3VL)>U(1!`@@(N MJ?G\1()T?*\#44B.$KGSFJ$/D+U@F[I98XS^'O7PA_'T8/\ZU^E=+A<]VWQ9 M!!K,"H5,XB#95_&[0`6C;&U:#0A3Z1^)P/Z[<=FE5%[LD\KK&=B/'[,%P5*L M5;/L97V-5?-*R60$R8B`93,QDS/9?3>9")[V/KI@.!(3@T/:Q;5W]8HP@#GH M_.K]+Y+ZVL"&F-5\7/M@7_IHF9+Y/]B,*XV9M]Y.!9,JL#)P`.`!$W3IW),+ MQ'UO1]FW7V[5NE2%Q^#V-DX>2#F?U%6ZXX)>"J3MC7:'`;-KW_)Z@[ M)<[]Q'*CL5V[WJJ]]?7')<2)L!$95.`R?N.((UDX2MM\O^9_7@M3WT;BLLJG M\\=+.C)_.CV8AXT"P1$549.C.`?/&\GI3N"]99BTEBG>/%^Y\Q:@0(@!EH)T MO#$-S/+$P[\DY+N##*T]U&_C5N/P7;"73"`=`##1&(``E[T;0^<,528#*:7U MR/#8L?)_Q@2YE2YWKV=TX,?CI4(B9)BW6-M%)&G923I<5YW>NRU8YY-X\/]8 MEKI>AB($`W&(6?D_.F4C;"7VW4W<[:7'(CVU? M+#$1B`#"8FK]>Y_E]#_;_R8Z+_WGI+E@T&T_"QSFGE4_^K:W#Z^>\>#35^!V M4BIP[5M$W3+U:>LX^?\&+=6PB($&UN.4C`57TW9%NS$!JZ.44N'78T2&(9^H M\5*G$9U?R>]KD775Y3VI\^)3SX9RZ&'OG-YZ7FW6A<7;40S0R\_SJ"XYUMYF7-SZ3^^2?HN'['Y'0;D>6& M51TJ@!8`P!@@G:0][C"070J;,H3#1I$9`A-!7A![Y7W4H?D/=(0D!0/^L.M8@TT_7]A)S?.53=F MX3Q'#\:=;^G;U'9_./\7YTK/SZ2_K9^%2C`E:*"8DZRD6F-.K!,`ZB,6PI`/ MB;N]=W4=>_R%0HH;N"`1BZ+U)JD>7[$K=E\G''#JL[7@A#M>WQ='8ON[.L;J MATO2_/C5=GRHA&KIM"' M4@=/GUX^QV5UK]I^I6[CLZSP=#3/0D1A%)^ ME?+^';35JH608PU^#Q(6`ECGM=K@\U0;$2J)?,YD)(ON$(OLFPU.MG1;.&&W M4+R!/$D!02=_SD/6&4",V&-?+I@,_\%2LN^6<(L6IFWH0O;T'^Q^_J^TFQ>; MH9M3_?$B_-NB%QI\&3M5>89SW:0F[I7@Y2]S558RTXF]6OF96YDLZTA&#/]#+''Y&A`=!>\IV[`8#(4ID-+;'F.UBS3J-=_Q2@O>.%]OFD+U?J^ZD#,GD?M//'[("0XQDSQ+$XF MOS7>Y$C7*;R#T:K>P^BY,)=+P2/%F5>=4NALMM7G!`FJ,JC/;'8[P_/]@];, MG%&W-7;+7@A/&=2%"DG18R[.C/]Z*)Z&@^\S.XA>+#D1IB:VZ'XK- MMFW(/\?\5FL>OR&;RJ9\=O/2;C9SL0B>DY_:S,+!$MP!#S;V.N2L6Q@K+.>P MKO,M[B_$Z4!%NF>YM<'6[CXI]%\W]O(W'O#S&Q"-45:YP.%WPD!(Q@=S!67" M3P:)3H3U_7T].XOY:I''C$GRQ2$_ETRNSXS"8^KQ",(`XS:C/T4B$I#YV=RO MU=?'VUKY4,$"'WWO'L@1)P#DYV%I3(NF/I@.'$:TO(WQ:[U_C]/:8A8G3>P: M..\IU`K)SW)%=/*L2A-YD<,`ZXP0]?834"+JJR6=5=15W.+D3RR0%,)65-VX MU&,1+!$B@D03!9R]#MRS--M#L>ONS\`"5`3Z%-#'=EQ83V&5";-`ETO@CL.M M2U,-,-C#3@389(0B`,\)*J4F(1MI1-6/DMPH.8,!H:>Q'*7WUL$:<01I.Q,9 MT=IK2C7K2,.?CG01IHF"&("WGZ/0Q$G!I[1"'DLP$.VZ%:.>R%T8YD2GWA2-B'&3L*M\<0CX M2QXX*>FW#PR=Y>D:98O\ZYUQ#1N.P"W(W_=XF"U@M[MPU$9X=X;61P. M/=69*3,NMY)HPDDC"!(PC($(R,D9J`) MGE^_FDA>)AT/JFK8;>[Q9BI3N^!^_GD,+O+9H5=KPNL,\0A[5-;/U;FW7#:1 MOQOLYSAL:03`(D).9(5'XRHU)>N?W.:RKF@0V='74'I+N)`CD8O0G/=0F=00 MKEJX7%&9,RH[3'$K&G+R&9@1<3=:$WKQ3<[2DV0\@7\F>(HFU,^+J"$8"6='D.42Q"N=JDM>NKX:KI@_NW7[!GY7EBY78ZWP MB[P>9#HU^9A8V>627)LPDMN0EDS*SX/6P5P]`CH@JQ#)F9(^X]$G"^K+B^$U MP+09PIC.R18FB9=,S9PY>7RB%8.1M/E;3CZU MQS('%%F.%DRU;@?%0`9/.1H^8W!C6VZ.J]66$+F/Y#?Y44I)K9E2G6ZEHWM( M@][B#!5I2>_E*PX6\QT"U&XS8-+0D3:8V)@V!3<(Q-@Q@0D"2`3+>HI& M,@:Z"<%!)!$H*K!$B()AQ\HK4E^$DDB)J_@;HDH>)J7)A;UF1A-L,I[ M825L>5SVBUR>^E(TVR=F>7U2P)#`[4G]J9'K<1+=YXA#T"&HXRO3F&WAG`N_ M:[>NNOESOOB13.88?H],.OAX;#V-OLX^/YT35K1/GZ8G9=N&&[.Y'-FT$MC; M'/G@NG5&#MO)5Q`18%.<$=.EWNOT_!U.U#-.TN%^-].63.AP>)PWZ7./0[D2JIG6OZD`7>5'9SWOG`\-0ISE>"^$,!8]<'B!"GC: M.W(\\XT>#NMW@WKTD.^Z[J:A[4OP>/N!H^YA:''*(B(B"&7+@K`A!\TR;CL' MJ>N:Z_R:[?]E#0`]JMGDR*_F(?8NT1Q7X31;S@SV4-P^]OF%28N]=H.IO<.G MXN"YJFPQWDU[ES)ZQ:P+[69WEOW\@B3&VFT]]HJ2B^#\G0R9U[GL*JB=7!L"#FA>XR(EO,;[))2VDCO;482C7$Z_H>".!?\:L5*'G*7"XE,MB MV]>X7#2P4$']PL&`R%S<':A61F$A[8_F/UP>NUHUQ:@=><`*,:T,B.VT2[*1 M;'JXW'37@NY4/",IOK_?B<:>9D-SWO06F+1 MA`=-?P+#VU8T3LJT[#?6MDZ4T%X?C+YWMZ9JGM_BWB;/X3MB[/D_SH.X?-R0 M6]R4_[..&>]!;9ER\^]2]Y_D\Z"/\?]CU/+YX]7X0_55R[)<=WF+0L`%B._? M8Q@#"O'R=;@[*-BL=E_2AD,[?AX->!`BOH4.$(:(`5!`"4BK+OHC,-"%%40A M*30KJZ9W.5>>:O=ZN9OW!C=!ZYNH=.K?,*QJ&R\T*QHO^A[;0**+-8%1>+WJ M>/QN4R&OZCNZ`/9P9^XX,>!5\3W\^Y_NL"V`.D@#"KGP#(:H9-V,+[)_K[%*L[]*=_H_2Z?#Z?9_/[#]+X_<>\_C_:U_;/4QR(CCC7&:G'A$RI%O" M(`VGUWNK\!@GNT+=/M3>)=;WZ:/2(TSE4TB'OT/3(/%0V:#/N"43YI!4#X;$ M*1_0@0?GAW<^G!*1%+92*I(K(A]STX>0,.:=V&V[J?<\KWU?0RSG95#*8PR1 M5)`JJFXC7G7-FQKO(X:-FY4[[F_ZWNEG>)I$*;:L'3[^IX%5H%QUZ1`F8`% M*SF!&%,/"$)`#":(`@`-P8/K_N:==\^?DQ8!!"*!^W*"2J@>7!*$``-M\UP? MYTB$Q@V)L$GZ'I`7Q=6?YNOFO"0U7A;H*]U(<5_W-@X88!`S$N:R"236?)N/ MIRU=Z#X>I+Y[)8+?=)P>MPM0B,0)A,'+?=8>]!WS`@!>:$X%3;B7A+)39M,8 M`>SA>D\@0+F&_C?X#]$86A@"'>P*NJBDZ@\,)Q^YL(SO(\WI_.=P$[,\/&8/ M\,[5FMI9')8``AP19NE]?1P?K'E%"<4'Z&+#\6%W-4?\FSX'?>2X/Q=\2/INLVZ-M-,$)@A----,8VVWX^_P?O'\W]W MJ/'.MDJI>EU5U_/C?(ZSX5]-?[ MZVJ$]/<::\K\G7]ZX\K>HY&/5SX<`([/\#_/Y?6=CM^^]+=_>_A>L]G^N@]R M*AB`^%EB&2O0#3*=T+A<`6) M'/H_[KJ:UG>VQX4=\YF&S7M4)I:"C.SE-8,1``!.4%2\KC82S*GJ>UR(7)$^"N# M_%MLM_M\N47S38"%Y;5_L_]W7M?O[?#2Y]#(JN.ZK+UJ"'Z9M.?^Y+1[/P^# M'3.G3"($7?LSJ4PZX,8$0`&W,1"+ZTB@H>M%1?E0/@#0$[B`)(2"I(`2(?O> M6'O1AY9ZL,`"!^J`@81'3B@F""(2(.&(-)6"H4YX!R9W'*[:RQN>AN7\&YB_ MT]+T3+X2$92PDU9MH!5*S$(F*)2PR]QF>K)'EC`!IIG?+4Z?@U;T@WZI@NT` MQ$)"$80S?L]P(G?B)V8(G9B)Z1WX<\1/!/1]R5$3U6:(GHA$[81+!5=6\=_K0! M>H4$+@^O=KKMCL+W1@931O>MPWTH&--A72[RG:M;"F1"EFXLD*(`O>7YC>-G!+OL7[4ML=A'5BZSWNF77T>GYR^Q<`*#^&_3[4^SAZ75LCJB1>[EK[V MO+)OO/;/E<.ZR.5&+CG="`(=-DX;>V43.M9-EZ06^A52,P542R[;4+\EADM? M%\+^A(!]<@'M/Z1D(:R'F_IV/]^'AP\*'60^##K(?XX?!A_\AV<-M#VD/J0_ MJALP^4SX#(STC(SVK.P9&?,9[UGW+.A9_(SR3/P6?OL[MDP9D'[$'IP;N#P8 M3;P9(.;@Z#KE$@3E)%95U8DV)-"JE]I5.DV)/OU(T$1Z^Z"][_EV@#EK7)XD M?TT>FQ&F[W)ZBY'Q:23@P]P^.0P%&APQA/L3/MPBZY:E(&!D.#/DRO^1Z#_4 MS]_OA[_WZ2'!KPZHXZFU*+0GP^%+W')N MQM^L]S\J];@WNOVG8\?.V<;@M^;7`@C.6.+2YHP*&4H)4RM)4"CFU-0M1K*8 MG(NQ8+-U,SF&@V6KG=0Y4C-[/D#:(:"ZP$W[O`<9R_``\/^$]+E2TER@)<10 M27)ERQ*=1Y4I2[$)%V):S=3,MAH-GZ:M1JU4E?JNSPE*,87SU9O?DTW/=&KJ M:IZH:BI+^=]C[3^Q>K"&PB-W*)_#`/,PA,#DPJDN9;L](1-),EB)M]C"J5%` M,!$-(CIB)`;]]?G];Z?I?U M9`!R`XOM30[N.[WU":I]Y]N63-9NZ553"J!KX14#[;U\HI]IAE%2*Q$E<$(( M7T/1W]=9@^C[#R=SN_C[.!\1Z,IM'&V('NFW"P"$@*A@^D:COW%%)EY'Q:E: MAPOSE99)"+!HV"$@+K*OC"W10"8&;;-;<#)9N)-)-@PP(Y6AA(0&6K0 MK+,;;:S,8Q8NF$N8E],+`766:QE;`9-&30"=)"`<)3G4I(KR)"`BS3K$(C"$ M38BY-'MLSXLXF\E55V*^XFI$=757JDDJ7-MUV/QES!^'4S.3 MY4!@*1<3+]]99&^`#GNF\=RZZN5UA3NW5O'575X[>ZX2J[0O)%_OWW7] M+Y7_;Z5"N>K,,X.UT>)>7X?YKZ39SMUG,`*X2:@A$S",PK#,S"L"0CORW/PV M[*RYG!4+>M40.'BPW#V2$!@'PI%]4@5QE<)6O$FJ^>)C4PN_HU3[6%ZV06NJ MA(H^CKAA('4)D+[R0.8SG,I4O$F**:2"0PI$$?W\KJ7S*#)`4!(K;3U:AT+! M@+#/&BA>DO^`P,;H:NU/[?;=I\[Q?,K*AN'UA9;>C]+)%\R,Q-K?T/:Y'Z0" MX%BZL&WGU/E'==X6)7CXBMVF8^<]JL"K`C-O7GCN9V/9=>6[/*UK6,^SU*1% MJ*5TQ)Y#.X?%_/Y7W"`;3MK-6[G:["S$#8'UGS+(&P!O<8B;DJG8S]A[#5>7 M)$P0680@G2U6$JD]7LJ!E"96Z*M:/\66VK.F[;-T&2K?<^$7!%H)"`I(R?2^ ME**;`'&('$><02C!.,>GF)`"2$],:B];OJ[=)U=K^>9K<$F'^7;?V]^.OEKT M\[QO.K=IX__G>-VWVW56154%4BA5*%14L5"%9$78"ZEPM5!HDA>GKC\5AZVP M:;`@ANTJK5O'`Z?I7ZS1`GM1A'0`ALV(AM"$0^6L_/9V]9NV]6:7OZC3/&C\K$IVF'F@"0XR6KDQ$`U3.8`)6GWULL%61UE-!9>3W5B>,M8*)^R@_-:XK)5R_Q\[4Z#7HJ8$V.Q]1L=I,4I-5^A. MSV46V"J5@C#0A#QD93,7::&?MO%]=9):/S_;Y;ECQ\TF>Q!D73%AJ] MWO4'7.US#`WV@64A1BUS3K<6+6$DC%J)P%1E($`R]8K(@*'W$?N^5T'GU MS%-7_;C[]Z9UQ^!_CAY^;AX#J-)`@3$Q,+`8CF-C.8S(CC'(C":B*665$T]1 MS^E>%:\U=X>%Q-OYMFS3`7)V/A]%7YV9-I`"4]NQULT((I"`\9Y(V/@);;5: M<:N*,&YS3LDD8TO@N@0%GE1?*U[F,0T:0#^XF8TA"NQG9X/TWVD_+=\;E;#E M\_X_;;;[\XQW7SOJ^2:&30F5'H%29,I&P9"RZZR?^<'P/N:@_9Z'A_M_W_]Z M_-C3-W?AGI(?A!38@T$A`,:V6MC1WWKTK-5;%6PXFS#C+Z&"@Q5G1EH))#9< MAANB,J7:;L+`+4>'T<;FX"ZL+GW\K^GE>;O>3G.=G[G\>K_;M^IE6-8J2A/9 MQZ^".Q/2^-Q1?>?-W_`V9;2S!;T,.W`+O/$ZFP%.D`?E5`-+Y_+ZWAZJ^(]_ MZ5H]+D^AWWTNV]W7U>3SMW;[>PV$)J0@XV=^)^)8GTSGK_)AKI&RJ0("0T@N MMNLH+A"A4AJ5B5`;5"V)+"(GS8GUNXAC6H@E&?]I07\1%"*NH,?\U/34<+JN MF_SE<7V_JYUH6L,*K$J_Q]E^P[KI6[:F(MU+YD76<0C?X7.VZ.*(DI)JZ"KL M_BC/"%"GF(CSDM\W5_Q7O(]]XPN$1,`(EX1,'@AG2,(0C`A(9<,+D>;DS+>V MQ"WP^-'"QA(A"+A"09!*!!C;8-.#8.<_2$V(1H(1+((%P+`$0,0B140D3#%" M022]SU?Q=8"\EV_J!_0?3=X[3N?B5"LNYS\"Q:VDQH[WF65K-==8$$8E=&E[ MVI9LJN-LI2VJ_S0W.+H\186LM;@?CQL5^>LLN7\5OYU*K53.8EI1(:W'Y23< MP"(-30%O,+<`RN<9VU`AXG?RR'HS_HRJ;#)G.K\="*T#&9C&.NX9.RH.KX>Y MBO+*E5FM_?Y8-B@``Y?EV.;W1K2.J0`@#G)@KK$R5YWN;;X^S:'F0#0COR*A M(*,@NC^UC\GVW;VG!]AK^P+WX'[_C\K26S\TL*>W>)P><[+:.+=BRI)FA8"( M!\RAB!!*/(B'1R_HR7B9(^UB<.BQZ=?&$QN)9URUP]JG[8[$WH-SDY?(RR9: M40RFH&]#.=[J"FZ@-/<9UNN6/( MZ53>X"/#W,K7A5*;6>!VG;[GY%PTSD?"Z8WP8W*2$6,")7!$`U'?#Q05SP\C MNO&D0M_877`*L3JYO3=!0@\=(_Y4F<\J,W^5)_U,^<1YV3Q]@R#^F(@7O-;; M[Z:2]PJ0<+4X>SW?'Y5=1_?4*?DU/NAUV>0UCBY:J0I?L@EOGTLM?W%87<+V M*SMY);M+6[S_>8`(SR'%X+&OP&2Z'C0E#])+D:.YCL(;(@/U+CCOQ_LOL_KY MS\DC$HD(_0>W]4_'O^#:?J%'97/O=E@H0C)^53W38,ORMDQK:JE;\6A>2QS\ M0PF7S)Q8J^_6?V9X]<^_]K'_W^=^!^?(I23YAZY[IZ=,W4AO+!];M<*I.X%[ M2ZO*9+Z'J5#`A1*X,+[C-Q\38]6RC?[OP$!>VN MHO<7F_8^3-2QNRI0B5UQX<#/!A:\)G_71Y2#`['/^!X6^N".:N)=O7$?/\QA M,>!\BAFUU(B(F%4$"E#4`@@87?ZH8M[=M$S?ICYWU=G[LH*U3F/7%WX.. M6VNU6B!"':AOW"_@BMXKA[L7O-35WS)NW::84Y3W&DG3;T$2KY%GUL+KH?&D MHB(/.(B,1@9O!`@:^ENL^<#IJ_JI5%NZC"G*!K'@[""0!K$>;TI`-<8/E747 M]SB\7#S?BH#ZC<=!\[8#K/7>9CR;X$3?\;I1;*P)5S1!!^P83A@>9/SCAGGJ MN1L[D=.\3R.^RS-_5?=*V_SPW)L(11*6I<8?\LI[=A#F,``W&`-]3@4K.QES M1RX($-+,%A[6FR.G[.`Z\:1:KY'G)T@`0295$[0M[JIN`![3FM#]3H[@^A', M")EV%'<1I0=[Z-?&X6=_E>ZG^>@R%15)^%4HU8*?IH5#`%`07V?S_]%+R?==!7 M#$!@L$@;13!Z3)?M?0"',OF#6`8FLQZEK6]!5]1B<.`W< ME7SVH,USR#L4?IKV][2@EPG\O<:FA%<-@HT!C!5-C=&S0-$A1O&44&#]Z&8H M$O-Z>/H]FHHAI_\G!3!FTVVVVT)#$(\RS^T_NK22'KG_%]1[KNL!Y_LOE_%Z MGQ69@RNR54PF,T*[8-5L9[G5J@O'V6&H=SD_,H@A--E_W,'/LREC,VSJKIG" M"9RXY&T?8[+>G[O-MV^X]B'[7T2?TP^6^])X?2A:>J'66DJV7!(%YELC>J/ M099)C(=\WE-^#J0\9!Z.$Y3,9P7_FKP'D9LBHD7FA3DQ&`:1.GSP]F`B MH]!W.9GB)E@B4A&(TA)V'^^SI-?RWXG$H`%XQ"VW3B>!,/BLA&"-KT_]'D<39Q?F>/4G1B)P^J!$YOL;K!$M\ MYP[`LRU)*D^'(`6046_U(2[2_ZP^I]C@U(,B=7"WY?B_#]28>5[7\:"N(5-D&#Y* M3=4S_G2=?L:9('3]SAX5)O#-09X=P($>P0&V(`9P$"$3-;-4C/9KE=6QKW1) MW((%QKW9((P+`NM0)8CPU`D9F!ZLW^>CEVE1Z:KV.4MCIN,8'VAF]3!G?O,_ M\9MS;4/Q MO;UJF%I20C)((..%,#R`!<,\N3E.-B,6C"J'9^XFFQ/Q1J<\B]LC_?1K[=NY M`QJ:1+QKO8+2IX5U$']QJ,DU/BFW)5%8<2`\#E"P[FZ[.#;%?G/].Q("H3`@ M6.93NW->%WP[T831^Y64'%&-..,8QM[D+NN0K8(C-TYXFG]VV]DW3+:?>II$ M4Y"K"\1'&AGT\CP`00`@0,8PIP0(+6'0D,#^GH\-S+5+/ZSL[_8TWT-L MJ^WG_.XLNC\]Z&_TV$F+>I\3R]]S%?=4-BHM9_-R$)[#5Z$1-C,T/J^EX^;Z M;8C)"*^[D68HKR9F<_;N5?,JKS9B-G752C-@OP^%@P&\$2.!ML:Z9@'XD0G( MJ;A+"$0[1M_C_06$(HP($$0`(:TQ'R2`$"&TG96Y_,_*)>C[#Z>G*;(.VLXO MD1",#'>BT;.2.Y3)^0@'U,;U8E@>NH?:^9!)&00#M[50)S]O;<[4/[/T.!0' M9^-]]ZSD[KE_-Y`-UA]7L,GTO^]N[@0&0`W\\0@]9YM`]^/G7=TQ=!]=#]3^ M[TOL=?N_M?9^9[[FS'Q>7SK=L>XK%[2PT]"T-YM6;?F'`Y$>S@$`0 M]<>BDBC%IL:@1&]U_)"0OI[-HR`2S-TVGQ&VR"/4W&#GN#@MDSZ'98?;4B=W M7;C99!QC.#8<([8TF-JO4S@0BAA);TS_N;```O878M3")=FIGNK_$:U-ZC+R M%[D>GBV.@[O:A?%:1UAO,-CN2FE*FNE(X"0J-UJ6>@Q/A/.($Y4AV?>-[JDK M:L>C2:=QV=U,.FC>DU[!'[FCY'\RC!.[S;,%=]7?OLCDM.J MCO.A>)1("^JI MN6]0#[M-W#^S[8K"8I:DVR867MGS&$7@JSN]E>[=R"T0Z.>]=Q\H[$8P@0E. M;SES1*\/5)NV-,U6O^:C]3MQ,<#*_+R\Z+E;*B86";UW:\6@@&KIV4GHZS'X M%:6%+WF!"PU9#1`Q1:3]=]'I)XQ@"<7`X"Z-I^;MXS_L%O;R)W'@>-:O=<=? M$T?FY7MNB[ZWW7`F8MMZZQ#Q"*_VF[M4S9J]%@*84I2S"ZR(`84'O2OCZ#DT M&*H[4`>>-1-=I8YS"L$$@(Q&&95!"F4&)7/EJ.(6ZJU7,D8A>9'Y\O].T7`K M7I,P,_._L%_O?VU50B%MN,4IJC'5V0\W^IM>05=+:$'DAQP,&?'LL"]<:Q`" M"-[?7;@SB'31--DWD\.P0,(TNJ?1C1MZQ'2;%;ZY]PFHRC!6Q;O1Q+_NI__O M&P4414(`VWB3V(?@$DAA"`28Z$@?2@)(`%]3K7<339!L-PX()`$[HOGYF*_7 M2=5S=(XOMH]MOSO<>,S\I\W)]=KZXU,*H^QR[(O;_IG_3F?0]9[O;TO`LFEEPZZD3\B_$L^YO&]>+PAA9%!KOVX/ MOOU.?KTU&5,L0GB]O8.^C:$Y-&$HO<]B#HS8ED,]\!G,/?>]IJN"H9E2@7FHG9&;4076D^ZA!1E9B%Q(*.I?0=.#_*U$! MXTM4O#Q-FLNRL6OQ++*($V;;;&G8,`4&X-PX+(J]9&#AUY9WFD%684?1[::: MBZ^Q2SSPW`?'&W2HSG0O?OC9?D7W3ULF9,RWIW+J):-H;X5KHS;6NRRQNI&, M8QC&,7QA'\/YE,\6I[_1U!=L.D#OB/N+:X/G-GMM0T`$B>6$^R5@D$3ZY^4H M_;*H`!&GA2^-6&7R#]>Q9CN<_-^[L#';FP#FO2_.67]J97??V)G\,D\Z&HHK MUM&P2,[@>EK===0UG2/-F<'GI^;_%^]WC?PBY5RW'@OUI+K[ZX@\\JA,_&B=SN>`J%YIPKVO_O,44:CN#=";N0&R\+@/'B$825X[ MD^@54S7MC6IVSM+DO^2?T'$EB2*U.MU5;Y(55QJ8[.]HJEZBY(M=++C+`. M4V)F+\[Z(22Q6?C/T6O3MX!^8P=@S'5S7U1B"9A"&.@9CM8`@3^"82B.QVQZ M)*C':VJ6G1FG5;&20AF'`8;G;MBM9!.@ M?JI"'T-;8V=U>"T(FR[6U'`\OLCKI-Y3."0C8F0DGIND;>S;W0\2>YW^;=%@ MP09D]G<[3W]EQK75K!.[W'OH.&1_#NIUC9H2`QY+C76RPL-.@,RZ'4?X3W\X MM>[P@'B.JO?YNI=CC6N8'[=T>&R<:,,\K#":@;QGLDG)G4G7,IW+ZF*2OE4Y1D[QSR;7= ML0BF^W#8<.?:8B%]EJ'>!7,.#ZW$W'(S8^NH$7^,"$..@PV>[X=7+D=V`A.` M0T)N:(%K$(S!72$5"%I&%_52D,$/3(9A`[J2.&#T1KRR,^]41T?GJ*1M)`GO M1N"RDMA)VUSB"B=1,5D*'QLK1_)Z%=1`(W378V$ARN`@K12/D,08?L=VSI=3 MU<@1A'B0M]X.>^2M[>\XF*?3P>#V+Q\))]+Y9U(W%AC.@3&]9CV!7C".7X\9 M"O@+C.06'E1A]1VT'`_*`[N%`J?F`]EY'U/JX^6X7_R\H2@BK*+!HJI[SE]8 M8JI?>21[8^&K_77QE[X?DDGL"@,7",ADD(=&`Z^QCD1%YRE[[;DA"0DC)&0D M@$8F0M#!:)GAG#HNP5T//5I(H5QH2L*(P2^ID35F0A&+$PJ.ERT!$!"GJIIPPT&_IY'6);-**=AW'!KY?7V-RHF;$GGI)+T'JT M46V>DW7(77Q+OQ2+I57)[EZVU0.@>@T;&Q7S#/[]IK?U M^5T\'[$@X,]5&>`@`"AP6`[WT_$4R,F=U8.'[+!YL,KI&&)8&!`8C4Y'D,@:E+N[]MI*NWC:T; MA"+@0C$F`;)#GFCXCQA-,>28RC]BQ_"\/X.^=BS8\-`5A"(C&3$?;FAN0`]+ M(!+"J#XV)6NB#)@V?(SB*]^G8C_'54L8T_?@Z-EL%DJ9:.'%LEUC7SN6TB'T M_^>+\A_7F!@D.>,^?M\G[G7U&?7=)*>N0J%>1Q2M:@"G>RA`!%AR-2R!$!"/ M(R%=[)!$-E)L`4]3X.>5?-_TO*)V`B7*RA?OXBC0YW8R!6/K,^9;OOZ/A;.V MWU@&P.*QM64"J<+$Z16]]S3(C`#DPUD".D/7$'@_S9>O4!"[M%EV_T,(#FI= M\;4Y[%&+,R9'`9PU7-]KL61HO2>K>BJTV.=AL:]0Q?!DRI#&;RN2N'*D79`8 M?[:I%(QOYCI4B%S=(!@O9"DS*X5XGZPLZ99)9O\3S8O+-H,Q)S"JA[5;(O38M5\V`^Q:N6?6 MX!JCBJP++OUU-@[KANM1`['TJKDNX4!'84Z^IWW6O&Y)V3YER:30;X4A-&ER MBIX.#5P@3D"%N3"$UN.K^GU2UVVWJ<;F+F>F0<;ZH?I4&XL&W2ZXR8RQ>^KL M<5J*).GD).P51+&&#I.@A6`G1/TNM`A_-@@1="CTD_`=!D^KC%95;[A56*H2 M:I8IBA03R<5,=9IZ1N+S'.BBO@,HO<-?.^&&.#Z=L9,78"6..3..U`NAC-Z% M5XF89ED#+)VZUTH$9CL'O'4R'.E/)GOP_"H)3ENE?977,(1DTY$,'-P:=6;O MN70JW5I5#H5-`Q-!8``(884'4Q;J]=1O`B2CD$2Y[Z.NR8 M"F;;0,K,S3B_V]SWMKB(O+>OF36PYU*N%2AC![T1+Y6Z7MRRYTMK`KV%EY1- M_Y1AWGBJF4/&R8CQ[QAL?7YTCJNE:9T54V!$O=_4H9^&*;K>XL5UJ-NR75,N MZ"$6X:XCK MA(.\;!*IN4>\X$)XQL+S.4#0@><_3%>_;:&J*[?F$0J+;U#,?T,PYPN*5/(C ML"PPQPP."V0)#;^:)#/,9C<[3LR''$YX2V;N5TW]'*244DIK"B M153>8RQQAF6@`-DD$LWB:10U<#3$@"9-.A-YDWJ,%W`4V>]M;?OFK?[R:2^X MR=/M8/W6'JE# M5U&IT3>F\3I`<;T6Q/\EA!(3*QFPL29OG7VY,FL+YW+?"\8C6$XP#I?^I`:X M2:>G145VX2J,@(D3?0:1W#>4L^)UZ2-HJ9"<]:ZM2A.C$C4Y_EJ+XG;$(EA2*ZQ-C`> MC$@]7"(WO$D?D&K7HD4)9))W`Y@[(2-;UM$J+MDM5"ELQEX7< MPYR4T&V1DCE2!$6/2_8AO\3)Y1Y%QV7L14Z4\X<4OEKE.!K;CJ.!:.YOF5)T M#P+=`ALZ5I.[[O$<$))3BP!SD'M?>#O`?7'2Z&0P#G"YZ+CTF_H5`,5WY0'I MA$@V2\:6T.5.$(PC MPUK9L]?M%B4-18A;'+N^.(0]288]:-4:9L9">;5!!.14+0$BB5%+=QI6V.D4 M-DR4L(982A0C/>/*Z/ROQ[ZR]>,,W1@B#$?$)L//U.KY52+.,>T0SPH+$2NL M5;H``+$"7`;BNTK!"NW<,4@VOIV&:IU&Q5(0BX]J!)#+EE$+MH$1+&IZN2]C M>FL9FX8VDDHMUFTL3@D@9K\+_WK:GE?EP.-PMW34-W)VVOTQ#XZM`KH2*@+ M2+I2+QE5,L@/`_BJ43,L\NB#8J_H2>NZCZ4/OHG7E_'$;!N4?O.?^D>6M%'R M)S`%]ZP6`>SB-]N:/'_KLXDL,&-)(L^*)-"V,\[T>H-1,H&9RHG'NI'2`B,1 M/R2/-O?S^Z=J&H9(`DD/O20ZV&="1'UPA'H4?$B+>JPa*<00B&\9(1[(! M,F*/*Z\BED4/:0TL.=9)%K6N2!VMA`GH-3%F%]03!DR!#O(\&+"JB7%Y+T$5 M!LH>E?R2A8!(88D6&4E'FYCT:'!3:KI4Z9BQ6!N5@JS-4_-=E?CLK-L+L<, M$#SU'&M2*LF5.#/L-&)(&9=$[O9M4UX["M*[4[T*N@0.B9BN3<$""@?T0#FD M$BZ*+/BT%]84,TUD4"M1$P\F9D3O@9>F;@,5.HM&PO*>LC(H$C>\\X@S MIIADOF4+*EU;Q("G_Z44FFRA`>042J`!*@$@4#K)D3!=9"9JO@=UU]ZWKM;Z M&MMKL1JF9@02"P@2S6*&#P^8Q"J\(LY!>,0,:W'2RY& M-<>==G9PR+6Y',D#),GWA-)'*DBUD_H/'K%@P\G)_X,/]!MK.,_5ZYS67RF8 M<0Y'4?8P'MZ(]6B&1F=2_T'Z;/349AMA!0<$A`H+HH.G;C@\CH9@7#]S2C$+ MJ%1SU`_?U.-=+C"V-)#`J?U["M#+,)O;?DW`M;+E=Y_*KKV7[\(6Z.WJ0<#` M5?F19>0B@B!$0K!?/'C+#PIM?[/^%2G1*+AXZ2;E?_MPMD.ACVT[GN1=JE&T MOK;]7T5RM<>(CY9VS\S!?Q/=::0U8Z\\`]'VO+/H^06_6\PW)<"*(SRA6?2W M"X);AI$1-WA`+@7V'H[$1M2U?? M1 M_/QOF:W7VV^JS;^?G@6PXZS(JD8K228EM&'G?-R\+:@C877W:_0ZW$"#!7J\ M\<[37S5;UK&ER\$C\-0=!9NS>V,4CGV M;25#1KO)[MYW>KQ2:AQ&+&+#P',/ID8NC^%T2&;LQJ^5N1@??[/D("$7G3XB MZ>W&[33Y1-[*L#&!@9TR[RA3I,P&Y!RT(),*)T\*6YCAOTQPHU0KHS*'<4:" M)1!K-`N,E5')5B<[T\:'(>J_+<_E0>R17H'NJXM(%<)(H/.T(CCD/SLT6R-P&]@]J;[]CB'$CIJ M?0UEDA$+S^,EF>;``1?A(Q&!Q9?A,5O26&>SSKKFV<)QN,0-,%NXLG,D2L9KRC5H5%PXCV3T](1GJP-M$ MB.S0R$G9F(U$^9F4YO6@]DN@S@:6]B[7Y;NLFI$\[E$.P6RX`4(1(?@$$5JF MZ1.>[6OZOW<9]'C9S"X_@288Z(60[28((K1)JZ.?S68\G):L-QFD#0LG)X?[ MN9-G,H`82``4Z`A$$UQ:$CH*[J\-6V905I0#P:ML5P)]5<+-_M=R\JW\'"(D M-%!`B($;XO1A-MHZG:JN85+;X]Y6DXW:\GC@1B421B"T#+3L%SB_?P[_-T2?9*>\[_J8% MSGWBTT'WV,9DDR<^>;*\3KT<@[@0AMB/H/,P[8U9M4:$)!CQ_TZN!VM&ZRP^ MS]SG80];BW-/X_*GK1M#,5:Y-JP2Y+(A-@\P M#$TN*!8F+&.>P%A+O!K1]OJUGVFR]>3%]GLNWWV^=;F+.H8QNQ3'S^*%D_O^ M^JCYS5M:[XD85WET-PWYVM9!:Y!?;XIW=]S@7%X>VG@W.KOAD-%>G>\UE:GA MOB2*M=Z6G:("D@1I09S)%/2I)#$\Z=EZ/)ZMAVGCTT_S'=7%29F^^,XO[60N M;SCI0#>IP[>Q&(@CH3J'$40GKW]WZ;TUQ"PP:?3F;;CE/IE:FF6A%+>^<["] MQH5#<.>H0B[JQ:<2K/?2#PZOF_"['T_'PMF7?:[,TR21F6HTJVEK6M:U6JWV MWY?59_/U?ETO3K-@BTTVX)S(;P^(6(DNQ9A_2W/D,;#"SPO"]!`'YFH MMH(17,^E"+^A')<@?`'.Q];;:D%S");@$L1L0$F*J*3VMM#Z-^LG M5C3'C=GQL# M99$OF*B"\@,,#!(X:U-(BM&_U&VXZ8GB@$A+EVU1:AT2+9T-%Y3P M7*&H31^;W7T07O'JP`],@$HAY3PFY`O?HN:2W.&0+Y=:4PZQG^[XW)K$"[S-U];R=S_<^/&Z=W-"-5X4(;Q1Q09%(;N[!%(%A0Q\"M%G=^L8P'I)IN+NF1#@.C0&3`R`M^H(C!)0N(1&W(C"*DKQZ@T$-S7!#E;&Q M<<;MFQP)1"["1QR(`>C9Z]G0^+_+JICKB?PS)K)%5<07@H/<[C5?!6L!DF@J M5'(1UD^">+%Z#?.5_#TFFM?"?).5!\F\8&$A"$*BI@@B*37&D7M&BX!=M@-EFD,Z"'6L+#/?0",=QXC0A&\I(Q M"&VQ"&T(0TQ"&T"&V%*U*>IBNT"&S)%F%X`A#:!$B8'Y[8Q44/E.>6)U(499 M^G)KL6Q:HHA:6E[EZ`1:2UCI64F%C/O\JN;CS;65J?LG!4=15/6D[8L4XTA$ M+WB*&7=SQ`6F+7IZ$\J?26T:Q?*%@L.S(S1V8&/Z-Q-J@"9+)9-.6.T;]LSYN96POS]&7Q-+B;&&1H$'..N^#68CVZ#UI>*%7FTW66\N(0RO4D.%TQ`*,N0 M0JAD#5#VHL=04@L*['B$8V#;>NK&"RH=Q5D*<56CWF1<:X#F5OL*RI+!WK5F M:.#/4ZT=]1,[TB0DX9R%ZJLD@6P(E\Y\7M!D@K`5.[O0)Z&80H$3`WU"D%$S MR(0C6/E3S;?6%0L1J.67+"%9^(T!'UY"8Q`7HVA`9Y2^HWPA433U.)@":ZVK MAN!8958'@/,26Q+KD*!,&(#A:R;CR:$/JG?6=Z2\JI.196.^"$=UR^1'+E`M M70G3JR[`SRI8NN?*V8@1,]-D1T$18>0!K=VD?2Q1$T&CHX2\#1.`[MM5E`8O M&#>"XF(S866PQJ!")FAL;R0#8A00APBA"*PWR6:X%%;9$E(8%!Z*6:Z@FEJB MF0(1%!@9`R-"4(XCUU@@!5XCF>^$(V62$@:14YH$GQ+`)2@X=[)@[T!BWT8; M(P`QH2[\'"?'V/[E](Y#=,UFE40=@'O]!WN_\S/EH1[3!M5 M/(O>=],(DX?#IM5^#9"&M8E,@A2)#+2]Q]'^>[0`MNDH0*()`$X6K9Q+"RON MM/D=?T18#T2,C\&UT(87]'YCK_DM]LWQ>WXA MRKJW!S[$NIZ0`6(FB-.YQ.0RMB7_HM(>(0^FOJMHVBJWT,9V8CJEI"05>Y!#L.=[V!;#7HI(H)\M@"J0" M.71A!"0J-0GERB[SS<"#2[R%2[#T7-W>@8MT M(FGDQ6<]0&"ZFYO6R./X&3=J`%]$0W1'B,+^?OCBW\)M^"<2Q53,A<1Y!A!O MIRZ=S;>,HO(%P=SR?W[ZTZ?%GXM@O[0A(#8]%<$_-G!R7>4-NS-7`0L""U=2 M\<052!%L:KB;<#$O75MUZH1UNBIP>F-D$$ M&`H%`%PY`4)GKK\'\NBQE&">[=F9`#<8C`7L[VFO-B"`++1^GKN7RLJEO@UP MO62[BN>TVGGU"H?)@1-CSG64*F'0R$Q2X`-)/$*'>V@].>JI9U(*9G48"R$0KK53'`6R>*:AUQ,]H^/V MTQPJ)LQU%#!L-4_8I(]%3]ZX0BN*`1@6QG3&90'\8(%05`@`0`!"S$CO*0+QH"MG9MP"!N8759+<5;/:Y?8E8 M'I<9LO[[7'<%L8KT$!/\UW4NTT6;E]M`8=` M-H5SB`(A@#M9=:9??E5YI-/0J;;F*_']G?_&ZJ/:\4^'6^I<2K6+\]I20\DW MD!]!,-[FP#KIISFVE\?;JI4J4)(#D9B2/8^MOI,V51^M_Y]_C_MC>-S>;T(?474 M3"@8$?$=T'07966[D4"1XU]]V<2GJ/;VUZ[Z8(.$2^AIVHT$@WBU"O1_?YF: M(D%D"")>+L#;[+<&=<984$,302J?!*11%"FR0@+-)VLCU5G]XD(C8]\"$.2*%G;]PA,):^UAP!(+\E7 MOX>S(*]A+WF$[')2;`R%2"Y%!,\IICA+]ECU6/(6VB"[BB4@GY@PDD@H'.7" MQA"4&XQ'1RH'S#(I+IK0'3;,*C`1E;#D+C\>NHP^+)">8=_5^BH8$Q:(K MB#G?PU!"L0J43&\#\>O%Y1LAO_4;6N8NPR'G,`",=3IOUW7)P)'L\LL"3M7IO&T@5N8ZP81X^)`B`A%@,]F+2ZLU8O'K()" MW<^VK1I*4Q_M81;;Z+L1B=!P=#CG8?Z&9>04'.$M`$9#FS)2W-IF`%KHJA0A!4SV"*H9C<410 MLH3%HE_D]-WEX;"URZE.,5J[>VHEJ8^.A1/9TKC)Q22^&R7:)QZ7>;A*.HJ[ M#,PGH[C-%5S7$6+C,(A3:`\=?BH*Q?&\^FA=4$X5^K) MK((10-=E6D`'6-;E\Z-QK>R"(IG.N[L$$(9!2^M6>2$@/?+&:A,Y8M,^)(H" M_P,.X`HC,,>OQ3U[HN<8VPS`W*HWJNF-N@VZJI",C7"D]U-HZ0UIVQ=99Z"L M[WB5HT]7AP_7_2_1[IZ?Z'[/F`'^XR`?WG``\J"*5\.FAF@R7H?C,-5'!=@` MS3GU$C0WW9=>SMFL9*()`#^GE%`T3PMMV54\<%O):6&W<;3M\?(3BH_@S2/* MJ,.(PE905/B91EM)0A1^PG;YLJHE4!['9&2B54V(C("(/E:!!ECNY]>7ZT?2 MXQ\J6/U/Z_XM,Q!J$"]KTYOY&`U;_<(228=2.]D]@X,"<*#+`PNA@/J]@M,0V1 MZ_^3D1!NW^AA^HEU?#0'P\G-LU0'%8R<[;]B(F(1-(1-$1((FF;@!&_?4,>C M6DFJ20?RXK M-GP'T32A%NT]5VW2G5FN5WW\%ZHA)>\LKL*U?4.NP]"0$W"]/-W\R*<,%XVC M8TP/I_A170VICO!1#E4S!6+NY?88R-D&YBX2:4L!$T@?7.7,!OBX;C!V7.NN M:>3)TG;'9]_UNXS5(EL!X!$)$C=D15`EN0G`A&`F3$]WN7%U$ZY<^G") M5/%;,=)DL6][;5-35MQTSS1L$37S<7=^%H&71IK$WN+MJRI_XB8,&-4KHW6JE_+ M1#@$,;(%'0#TF23H]-[M1/WTPW85B,!,5R<]-`U0$D0MU2W'@@G*N73&DZ*II+1+;T)*8HFA>*JKI"2$H M3`)P0(`@6@O$`01)$3.>>G`Q!@'-K4:`KSL3"EO)BUS4K;TVI]WUU-3F^/%D M5\E14"B@#_KH?=UBXF4`P:PI!1#.R@VR+7LOEFXMMPKC\][..ASV#0EO`2,! M5"`0O;,,\_G/0@<^-*V=.7@G)B5T8/;QK<,4#:\QZ7/7JYG0+UTQ*G%>!0)>PF^]##4,F9PKDH%/\V0U:!;'@!^/M)=["^F3-F.UD#;7S0$5",">-6IG0!$!)@.NU',BWOM8 MA2,+B(@PL"I]7V&'PF.0H?8=)@"$88#KO>L3M]8X>D"(E>2+:'ZK)7W4V"XL MM4A!'HSG298[*)`B,'..G+Z)"9):T*\J?60#+8WHJ![+ZZ*!OP=7#G@=X4RY MYH9]9"5-U),O"ES>%_3G4JO[FH.CS-W"9CEB?5/3%P]7W'`D%#8:R^_EDOX[+'?5OH-"\F>VV$/1UW:]X^G4$G!5@]]7C(]U^G M9*:5UC]9^]6I(A@H4&MGJB*`-0)"Z@9JE#88?][G*T_R7-E8V5GTS,5A"041MO($49U>2QSE*=2@DET**73CQTK]_ MDLNM,#U-UMT@9>ZP%1;,NL]X1TQ5?;($YJ_(Z/.<>JA3ER)A1(XZ:5,TV3[, M8O*,WA7ZDQQ,CZ)97M=4,>QDS7!^F2<3C]T\C+REBF;2`A%-Z)[+T.A\=EA, M<-]++B1@$8(C%,H@`<-8@&`TMW$F&YP*8OJF/HN;,73&NJL/0I_GMDJY5PQ& M1([KKUC+D#K5B1U_60N\OL#[=/5`8BZ5;(U#B?,_%0"22B/U*"?]^>0MC*L@ MWI)&HLQI=M19[@\K].L+\91P0"((%NO=319`+,"3/L:#VW\'@?"2S>7^EDJA M]"H/[UO-/IE.>`DL.9ZG2\13'S;X,M[6&!'P_ZJW=G=XM9R1 M;=:LM!+D`)Y^]O_V MCBPU"B,3KFG(FT(-#J'24P)`Q2\SZ3V3R1RU>4/D?N/"/E#A'Z4AZ6,/`YX/ M2++/U`.X>2;[,V7/P],8O\]&_:0R?=K8(UST[K+84VLB,!99J=5_HWK1\(VNQ[Z?GLJ1 M.B1%FX^(3$8U:*_\_5J`0`C`;M\TC4?$_9%M+;%76R2BJ%I\[^/DNP@M'YZ: M>K\\[C*>JZ2Y9!5FXD MU/T$E%L^TG_?,OCC[H7H9F-_MU*TE/?O:N2'NW[&'-T.]`?3B$E.4_=)-]81 M3&:ALBGN7G[BQ2C6<-I]N&D9"0RU[J%1@RGUXE,GY9=7%JF0CM/:$(AJO:"# MNZF6%B!WJ$D%[7-]=_?BW[X=O5`@$0K]W58``3>83E!-(>YYKWLZ`95<">K% MIS$].Q=TCKI`7:,+/,3FDPZI(93?;K#JG9O)SUMG^C825_[&`1_[.JH!02K[ M*'D0YB]]@;"9!G($!CC$0GJK\.8`#88+`MZ^N3E&E[%3`H>%$Z0'@*9`'!/` M/Y$L[6B,(B,"WSYW]!R$V"XS)NN7-)]RI1J!Z4_1CLMOF.-;G5[U*;EET$1" MOQ*@I?[05+*D1>;45`K4"$/ABLZSN/7<:_S-73\I0A976*HP>?7% M7K'$=Y3FPQ^>,<:(6O*"`T9@1`$7R0D$@LH1PM5TCLHY5=NJ+%N/67.U0?JU MZ7J,>C[L*I!(J+[0[X6-?/;+7P(32='$05)*+%`%$[U%%`DIGU_';>TG)NR1T7D#KVV;EY`'W:_R.2] M,7E1DM[%8%+V$#_T/KMX/(PCT"X&Q4!2@M;_3@?B]D`=\S!(I"`,']`MUE[K MY6&3R+O!)K.@OJWOD8F'E(Y4A`[[FL66"IJ!:-6WT"/T5J6P<31P)Q`$AWE>:TP39NZT` M)HCCK]`?EP6-%!KBB1`O.$@.(3G942`S';4)JXQ3"SNMC/TWMACDZ7=(MLQRWK7*!JA MYVN-SJQT?[HI;,436I!>(BP1@&8-)B"QB4?XX1*I7!N3!W./CG?[8/J_.@J) M?7_MR6!\%C#+AO=%A^NR\5F*I2(B($GD4&5+J[ M]%W]I(7L3UM'/:[4^4T==P_C7VEWYP\3M?,;FW9N?FZA>A:/#Q)DYYZCM/*0 M^K=PV/UFOE`>6BH2GE*SPT7J4_W M!9C+>S**@SJR\![%6[UJ%&:5-1.I>4H8W;UM7<,4?F["Q MJK=:[[$$04)AH0B$H$!C!.`%(44[+:):9U2:F(]J+WI"A\5AL!BL5BK]BD*4 M2S_(?Z<%NI<_JYOMI72=,2S^-.=KN[JIK6ON(R[N0[2JH9G1F%UF.E,VBBQ$ M^.8.N"X?],X=!'*@PROE]`IL(><4K0-6[KR)[:)[."SB![HFF1U^A_^SJAE MLW`;4*E0H43N';`A+\I8\5964_G#%`8;A\_^91W=+)XDL/,2CY(F5"ELJKN6 M%!]@E0501UW0R=1",5RDC\+Y\*G1!09Z!_(&7^RJ,*%$9?AJOH[QCJ)HVZN>7":?2" M"Y/752P)@2F8`-A@7J48/I*NR:01:)W@++^L6UGM.Z.-IT\!1Y)GP.D<7Z)7 M8-BO[TMCQ`Q`P,`1@`#$4O(@:>E+W9CCVV/97]TY21F2Y77Y#_9 M"$_3LN!C_"J\UD,:''86'=BY42%#2[%4K+!`D\I2@I$C+=%DEP%\HR\T<;YE M\R\79K\YX'WL=;N'`>KINT"VY33_(NM!B7YNN1_A$"(@_5!3QI7Q6GPE"A", M5OFWB":DZ_7]5^R`0`C^)?,N=9M,RV?T6F\Z<\_H8J[KKE"]M`3.;X]KJ.:: MX16SS;5-9E9MC$&+[77[K]=HE#4)B3;;('$!7O4]B+\A\F@H6/%Q',OP!-5_&:ZT_ZL0RX/LG0G-_AP&.0`XGS>5AX0/GF(! M1`0>_"])"TF`&'C$LW-RK4X)HR'`=.9O.-?P$]/+))0,92FZ).`-ZA(8$A&R M"QW'\EEW=/L/C]O5\.[@QW/\A,I7B2JB3^\CR\1OM?;;U$1+0)F>D^SK_S^GW.P];X M+!R7U0,EEHY`#`/SS^%GY`X?OVVPL>),MJPYZB\F];N MX=ZTK-E4[2X9"C9>Q9G96":%C?LY`N.Q21@.])$#@'429PM7ILS'QV2INWF' MM,8TYW=MR[&_CL8:4QNVA5-;+XRJ=V>7[H7I*4H6>?)F$;S-4REW_S!L_S_M+%W'8O[VL.WW@=W`S`V",2P8N7-H#ZZ6.&(:P.GI=X?-23YW7G@]-,B,8YPEW MFW&PN/*P"/^W/\V_77GFM6?CG+ET6BP->QPTUS?9W8MI'OQ<"J/ADZD>YOD] MM'-ME&B'MQ8$C"_3"\38O\/A/[BE&^D8T^\5B&Y7`%Y^J&/Y6;.D4FD;-\3N=;*_[E4"R\^]^["_YN+0)H9Y"HG9^, M9&)BSTX$LA'D"!BXQCA9,3(1$6RUN>%.]32=.L`4@F`RYF];OX3>Y-'T.$^3 M';O^NZ;.R)R2NZO68\LO;/Q9[C\SU/<-8\3XK#MHF\BY_HP!-`+?FU=:G*C5D_EX;=@IHZ09`/R^WO*OO]_V/\6I_2=61D)?>CC9O$Q() MN`F$;ZAAF,`!"*S.5F_,2>8B\\7+3_?VM,X$./F_9@V1755/M176@$1"OR/Z MQ4H3";:BFRVKEOCAV@='115_P27`GX/>; M;H^_V;[-Q=;G?^^5[#^F^F_90N1D;`86>@&A@12/Q6NC,,)`#+,Z,P9"2P"+ MKP9P^;OY3]YX'G.YS1:1'$^KT7OQ9M.>&J/4N5UMEKF;G]S@MZ23P$"`D_L4 MSD1".9-W%[G2\':M8I<6R`>*'O40(*]#S3P`@M+?P-#\6M5\E2PYJCO> MCZ01SK80JS#@>"BV.<5*S: M["AV`$$5].ZA+T'[\5]^MGJ>@G` MFZ<."U0`%5?=<2-"6?#^22Q\(H*\E_53P.@I;I@`("U^BA$**P,"]RTY_J-5 M`\B]?^^$("D0+/V)T4P?S'3*=N'0XYPLN]B&X&@->$?8Y4S:9I1+P(`#F*%Z)%BJV*BW97KL%R6OPREDD>V;+GD,FL@9E8Q;`T0?:N=XV]CQ)> M0KMTFLD;8I>,:TV#;[BEZ?8CWJ;6P$9CX0'#%_/HQ?[#&0XF-(5+X\$"B.EQ MZZI'0^^*QCC'5M?4F7_-W=J;SG+P)+ONM+_OTM6FQ&"NDL+$UG&WC7Y,+94C ML#Z_X;^`N5#!#0-,_/XY_F9SHI0E2()Z;,Q0N[@/+C'NY#TR#I6^QP;YW4:Z M.5`E.5[<^K@O-S#)07C&-GH\\$%+-F,/.Q2Z0(L/$Z6"^]/K(/BI5%9ZP\B( M+O4>7^F4":+2H7A>2BP5$%L00ZS!B4X"+,8QC<%G&4 M&T0E$S!79,)'']X_6ESJFAK?61880?*-W-:7+!ZSIH4I=0?J#*!;I=*^+XTG M\\8!$?W<3A,ZO)33IN&\E4>(YJ):*GO6LN,85?-W=:S3,OD)F@MO(G=_0^C^ MR&0[6NNZ)4%1)J4;CX8 MIEJW>7UW3\7G]JWKW1T#"F_L.R$!"0&]`;D;.Q#:=61_5N&S\\TY>3]:Q.0= MGHK_P/LL)V`\SZ)!`%X!FOCNI6('9W31TFVLH_R\.>)('[>'>'C6C.W2DWU% M./LS11\==I2UA?9_?*]7W:Y(`HDVDW6Y5UE%B^?ZM.(''TU3_.@I M.N%;2_B'SU.V]&+/UWM]/=5LLW$,NW#M8"\J.YJE'XMX(.!B30`N9.3/*:Y! MPS$>U3K;8M*3YY"6PD;24J9!ED]/S<&3_J=\75U+BK(S>QQX`!0H0WRC`[O8 M<3FZU""!`>(PES`=N#]=CMI`2>.4GT># M1%;<#//#\A']9]'L$3=(6J>VR.8N`*Y,U6`A'E)L%K",8(@03BAN8HEWN#5_ MFJ3NL,VE]!X0%H38GH-)FMU-"'`$4SCUWB^M92#/@S28\"?@SC\T;8_9)>#B MH0`/*8$!U3`*8NKSF>C/>GLIOYS,TX"''P^ZT&'?LFPCUY)N[;"R0`,Y]R@! MP_FQ/.WORCF4,LC+(^QO\/CZ%ZP#_Q!T_@7UATG=^S^#>Y>,ZKQ=$="T0])` M`?`F]H)Z`HL6M$"@P8L,;U^/M>'(#U:681P3JR;C]';Q#J?/"\)N&23"Q779 M+]GC80BU(\H;>(B`T;W"/:P!,&WC?9*X5G0 M$<.6G-O52WCM`A[WOM6>4V4DX^4TSZL0:&`/D@*()BC-IN'8 MK3D?-9N4\!$%-J5[!`76Z";8L3SOMXNZ*-))!N4TIR-%J=>\^L.C`1$0%]HT MV1C[=>3L2"#7P3&!B-TZ%OJT`[;J<7=O3AJS`F=VP:7_#;?*G$Q%F@<,348* MFFFMABIS!=#,=X.T)X%DJU3:5<(9]%IFT@#M*F0NVLR9!+SL.VV-#$L*W>#] M_U`#P<8XY32B3S%/Z[]K^6YZSH,6]?W.MMEC>OEX;-M^=TN(T/[FK#1.>MTC M!'$7`(A5]H/Q)COSJ#\GP.3I&4``#'->6KCL M98=2V4C'''NM;T>E[(++PF&A,,K^%UBOWEI1@XG9L*#T?7&=SH\%^\7@\JZ_ MK2T8F+S)DR9,F3)DR9,F3)DR9,F3)DR9,F3)DR9,F3*>HP*MNFYJ=,@9$X-I MRRP::_TB[(T2-W#3.Z:QO5;-6/06GFX;5)Z*]K-P(^YD@AMLO^^Z#RI>H2M/ MTNA0;WN?=*IVBC^5`OI^8PTHQ(+Q-QB,S&8;\]N!/G5R@GFX8M$+7*>)GRJG M69[)8`$"(*%^]?QZ#P[$^L;UIMP1!M^ZJ)KIO_'S. M:M-JAJ.NY9G#'29C&,#$*8SJG&_TN1(-!*P\HA+"^J]9P-U(L*?*XS"H'";] M,#VZ1YHJN'^(W@9273\X>1!2:G2ZJ-_8676A[Q:8"$U^F1I&>KY1(I-,-`P` MJ%QT0-6.0`B6XSJHK5"`[@'*!]3V@L@]!?9IK(-VSRVRV:7%\=,=K#<8^&=` M8./Q^+FOPEH!D#$0HC%EK9A36H(2)4@BT"%+&Y)9!`"@!EK'`N2X^R;BIW^. M@_BQK_95TM>H+2T`W0"EO#;>YC66R`1A^:7:DH-2TY:I!W\`UW_,Z'=:[59LU>U@`XR$9)"0'2`\E/AHL!8=3>4#VB!;`PGJLX:AS@U!S(%8(@VZ^+*<$>*,>WUP2=$7VP/AC M$1<:"[?P#@/#IOBK1K*/Q=DDJ?S<'J3UQ^1Z9U78^+J6R:/%\SA'U<\1!FOY M`BSCY_UCC2AP^D&RV7=J>E&[QML_:5HW*Z5-AOEG%?70C#H2(?1S($0']_>C-=G;AN?ZV:P`LF(P,`8&?OE*$HP'K?N=_BPG`3#]F@"(&)1"#H7\A?8Q/# MX^DSQ#H64RFZ21W5B:?(Y=4Y(%!(/Y+8JUQ4'^E!=B$$HODPJL:84!K^2]&9[LJ7W1#X2>5'T3N"O9#I M=373LIC_6_X]XBV\-PP9;[<9W\+?P",PD=;Z(V2S(#@A+ M'AS1PB;0)8$K'`T'^$BDE$._\'U]4;`M@4&XBAW7V/%J]5=V3NV MH<\7_?[$`:+.>WH+$X#KE/D$A`K.>B%ET![)`I"B!->I24:E=GG/GJWW?(HS\=I]QFE-@H(.OY96O=MZ3ZE4\G_MX74 M'QU/MT.MT8;@"IK40ZG%;UG5M"NJ*RW(\B"6JT^-0)1!,,$9?0'+&!JK\K92[:-&APO(.*"`UQ@':&.+QR_O@7&-\VIZ*5L;HZH\/4Z/V_F3PZ M7X7GE.[NR0(YXWO08?CU>6EI1$)A!.#P+EN%B%N-=L40O?JX6,%N(H^_&+8: MZ"X$8,@"?"8590.!Y`'(R?NEQ;QJF%OW/#Z.0@#N#$#AUCBY6(%1<""267OY MF_*M2?5,X50V"3_#0-J1@J\GQ9P[^FWV?P6VTR?W*+,WK-'YSMX'V:3#=FKU MT>U@,]*X;^0EUNV.PRWK:]3FYX\=LAK;,$&D(!LB%DV9-$1!"0SY& M";V7OA,CQS'$:>],1)1%#D"X8KPUM[.2];YC3SRZ&P M?!F`@[9P'[UP^GZ+MM5HT#&;UWQWYM5P*4`[-EM*WLUVF,=`PG+>S(@A,0,$ MXJ@,VLK7V7';Z]#OX2OBYF#`^#'=5U.T@OG$'?J1%GG!6$0)F$8"(8-#"NZY M1UZ;I%_(.((39>]4U'M=N-;+Q%I!?J),`PNEXN,TB'JB)SFP4@@7QZSL>1"_ MI4JW2R'84?-C7K.-8;84X'ZGM8Y!\#`@@6+/X43)T$Z9V&IH%$:CJ=R29[-= MH\'.:-/53EOAF4%PV_,^+D568V3V^%'RX"K%-E5R2J\G\>OW+Z!RLMGW'NF# M6!FE5%L4`?!=I$C3:Q#.UJ'YQ*UY]7.SX*Q-/W.?AJO`6/G(@I"-:LD MK+B[BEH.ACY2:O*$-XUXV(<3C@TM*$("2CP6T)I)T%I@[G$O+):L:SI*.[,E3-B8W-OZF)H"1O@V\WMQ!*S>$FY`7QQ9 MK#(BX5JNEEV)B!MRF)G2?VX1`T.;>2!N(E%O3K>V/ M#ZOT6FR:(9(P#8 M8XVMF==9_/<;KCI44E@:<:KGQ/(ZU`E)02!*!_?ECP$!@9X.$'RX*$^?ZGN' MG&'5.S=F\01#K8&ROQ*P6!$#TJGU=PE%.&!=A:.(,&V[5LO[*MX^DB>LHP=` ME.NJ@OH>IA.P*S/?:.K^=G^;6T#AMOMM^UX/;Y.!],]QL'G!`?:UI0Y!""&` M(@P$\EH!C;W9[-*@,!7_C@JV;95_'33<.OK$VJ(?7C!TH.Q*)^=ROJ3^#E.N MT8';#(2GH/0?':Y-6SW@-(B6(K=NSP,1%C6N+P(8G/HD(:/$,A`%2@@-7[VT M1\*[IYF0WO]O"7,_DVL]Y_K,EG,]H_H<`R"20\PU_\%<)V"F>5ZD/&BSYS01 M.KY^SVRP3*+S)+00`0@9D M+&97X!6=0N?H[P5U0>],C*V*_0^G'Z#_/[+#WL+E$9:,_,C:Z6UOPO MZ^,6RJ.U7P6&,>1&L#Q0\AY4VVQMMFH3CZ[W+JV_Y`/ZK>[9%F%@']YQK*&. M%R[=!_XE*ND]-$J(_"GH,*^^1`>:M6']?\KZS_CAT(UQQ'&L/WA9TX$,#GX+VL6@ M'/C"1*#;2NEN!(@*#^]A']R.]M4XM"!.E'C#N=:SQW/9)Z<)F`C.1P>;Y(GD._].># M-:W(]VJPS[I;")ZSEH]J4^E;.YI[]3KTR\88%6;F831I9^H:\JAA(#J3X]Q<>.Q.[.RBM2A0QY@()]3TM@>6?Z7X,0!BH%,&T^ M1>:=BE]%.^!S?\_QOI=?W''.I7DD82$>4=78=5TXG+MO%XO+>J'(#HQIM(Q MW2X#1=MSZ[_7O'>Z(;0XH![AA+(=%2-U/WR"2N\-`,CJEWU)Z+/*U>TS_$Q`PAA;MJ,$'\&0A*U7JWF[W'K[/4?'J;[FK>R>C&-W MK?85-(M/]9N_MW;/(XJ#].4B&%V\..CZXB%0]%J3'@A#QS3+XY(90&H%2>EH M14!`'^YKAN;IY5[OPJS*`8UOOG&5N.-AV3D>A.JD[W^;>0',B9!;4)!2WT]P M9K=0U;7VO4TT4@I\M:M:$@?JB#*VBQ(R`FBM7G"NIDK[)W)@]CK`FF#U]C^- M"]$]=-@X!DC#RX&5LCPCQ'SR=1L&7+/VK;-QV(I;;3/_UM8[2K($+DY^';6\ MG3`I"<0Y')::!\C`1PIHP3Y.B4@"4'T\B#[4LK0C),(N\]TFG7_E"PT[!3%J M]^'`CB*6&;S5>@HZ^LB3^Q(6V'][3U_?K>VTR>HZ;D7?:_!DVVKQQ+KZMD%^-?\ M#D_9;+Y8<#OG3%'WO!L'BTU>CU29W-IGLEJL7HJWGD0D0(M_,#!ZHFN+M76; MP6=3GK?X*4(72EA`H@P&`.(Q0!*"`<[C8.JH,C.MQ#2&STKY$\0]#6AV-3I?4KKS52]K<0RGY M`P$./U>(^_O+"$/PDGO6OS:,BJ2#B]@5IB:4O)E\)Z$@$.]_"7PYG@#"*2A4 M3>?V>$I\E`X#69+*NP=R!/!#(QKMLN_F@%:].SPK2#,#@FZ('%T+9-X?BFKJJ_"PX[IGV2Y4^3SL[P'_[6V4YRUG8;]OW):7G^1DU\_BBD@$- MBJ]2#5O'1ZGL)9R)(+*0`&5(Q97+^(I.513!$$TB;^(]!*?U1Q46SN*C?5I; M9ND&@QE:V)L3'>#-H'PB-&7!Z8;GZ7(>RO_D>XIMGV4*-)39/&JVG@V)\5T^ M4YB>H^UBB]SM\DT;':2,^HW"M_R;DF9E($!(P3+UDRM?F>:K'2C^_@\UPP?* MM>LJY]WVSL+"(=2;;L6^9B8N>5AE6AE(FI,.&,G/Y]K\"!#*<#K%V@8QC&!@ M8QC&!A]#8./N9OT\MD1:#'W3FEJ+UTE;4=D[(YN=B]L"^E2+R\IW\('>SMWR M*:,Q[#B%*R'=1P-VV+$;/5J=/IAPS)!WKC MU4I"'49TQWHP;S22$S@X[I\5=G=V1#`=XP<"40T&#$$6R,#Q^/=@@AYH^A)` M>27$;7B]F\_EJ.CRX)8T,[Z$OO#\)9&]9@8RKB+4(D8>P.RL!*2($0LC_8AZ MO5!U?`)GICBI[`[S_&$E^^V?JWA5=&I>8+2AIOH*1AK=RZF:GL]Q-;K(-IF8 MC6LG-&R9!:LC) MAGL4XUADHK&[.'"^(4;X]3FA7`B&[8:VKO$T.9&UT=S@"]##)B)6@LD8CZ&* MO/RW#SA]#*:J:EBA8B6P8Y!:9)D13 M5NLAY0HPQ`Q<'NO_CK^J_8_+_%NM7N-_\+(3OTP(\L*'<'`PIS$#Q#.OP![: M8(0]G$8X)MP]'D0?"&Q"@)XXQHZ!]`9='!7#9OVY9>H'Y>*=M*L??_OY&1Y6 MSR(OD&4+<+D&`TY`8PM@8CW+;^I,=`Q[Z)RE3$R'[U?4]61YGR:?Q8/;G!2` MX5R!PB/N%+?2?:-7Z.[+T'C+YDM8&9"I!*IO5#P/L_B56GF?7PA`0HXQ`MNY M^R`83W0UMMK`52(`<<-=O^9=R[H\Y[CR#+SE)@[< MM$^WZ3*P'W!@L^TOFT&L4S!?$H9,H6Q1!LA:<[[>:,Q<6-<#!_Q?DIF';5/] M4]%NVW+3P!BA!@D`40G<3#V$[-GC3 M$C1DYA-``,245H,K`-C.6QR=O@>#J27,#V5+>R:W#/&5U=(?[>V_1D=VJ375 MUJK:S)RVX$GK_5+?O2Y?A$.5B!:OL1X0P]'DXK-9M(G#-59V*^.=S#\/?!5/ M1Z[V0$"##"@0@SX.@B,8"IP(3L*I*FHN-SO+&-_DWEB1>8C,NZ'J;T\NG\/H M[HO]K95.G%WO14A8`V<^9^5Z/R%AOP]^B'[G[7AVX2\;-).V]I=;M\NS#Q2A!YUY\?Z\SBY%H0$:> M/7/EK,=.QJ,DN8[/ABFZ>C#=L=H1X=\NPG+94G4ZM5+NU=3#S/T,^(G+'ZHM:Y%+M6.\.P:H^A7>[T><+W1XB"7#V[',7USGG)D^P8+2!\^< MK)6L-+(N%!':.48V#!K5QE9SGD@C6*))6)FR,ACSBJWX4N]0+TTD9O2/VDY2 M0/&8VTDIM/CC?:D"UMW&<0`,X,KYQ:8#H3A MY':CAI0Y`Z@7PD,$(G>&C^]K@#QD,4U%@R,8"C0O92>2ZB`N*T+2>-8-F\.! M$,DE"\:[W<+C7(Z1$'X<#-PUQ06O:]F0N5MDFHI.WUE\+D?U)$&D=$C1CQ+] M]?96`!*84!":(*!$%`:V81=I`L)(@Q^73.(@E\ M8&1,205YJ=+UIROEK]1TK1([;N;EZ",FO_3&=%MA$`MP$0G@H`I`-\@+>T,V M`'ERS(I,X"Y-S>B`%#T_KPWWT9MXZZ97+1,'!=M%M%<8H2'D]VG6PUD8L61A MM!-$>`UA3.`VV\QKT8"[G_+L/*Z'JY88UZ8O'2YK41\U9%9ZD*9>A9\E:DF< M?P)1B1-;!3;>'+G=%4&V*2"10.)N#'V^;]^C,['Y+4F79<'QY='EV::25@O+ M!>6F'!%HO$W`VN+3#@9O]X($K&+F@#88SK8[?84D1<['B:G2/>[;(B3&MV;K M!+7GQ#A8#I;09;;YX+U])9];`^YI`TI8T]YK=@B\1K?P0TT05=_V]'K`TN#< MG3'.@T-=D5#S*`WOAUCC.E;*W):O+$+'!L;K^]O=K_9H+1"*$JTB"H%V\7:Q M<(@]$0I^5]CRCQ$9T8=I;`G\F6.!`L'HCD,.SJ?YQ]0TWL"NKRFDX"#7OJA* MW9WM5!1\E$($Q4I[%KUS$JV^.&I9K[,C7=K?8#*KGYO6\8\@3J:P5O/ULSA-U$;C1VE3,Z7"/M/T< MKE@'%"V?CKU:GV1DZI;@.;Q-QRY:-^^%LKO4A5*7^PUTP/,JN:,..*L:3W=& MIU#C4>E=Y4"0$+Q\C/3V$6[ZXA\W3KUJ9'L,?I$S-:GW<_7K6VU'3YF`6#RY MDH=NZ8XF6EUMRW26>AB*GF/RQ:N]>05-;M3@"(P,&919NG>=#9)F'>\ M!_FP_`Z>-9@U$;QA#)?]/LB#;(@VI,=NW"6T_#^.1&O>0<42(#"#3H MF`[6P.$NU][W+B$=WWMW3B(0>5MH,A?>K.4DC-1MW M4<1M(AH"DBD3QT]6[7E_O\<8:136UG4/;-X73`R0>)WC-WIWNB&9C=47O7&L M/'$O2(1XJIR!J/`]NU<\1O81$MHOA*\,_.>>QX9XRZIPYI]@H]=MG,;*F.X_ MB,8Q]+WS+W9@H>4)>%WY4Z#:>#\&[U>:8PU?W8 M\HT[2F@^GO.T[:JMH/H)WITEC]]5B%KTO.NR"2RSV:^N3LH;HM@V!6T63;A( M%=9HI5Q@6`#US`6Q_&KD_=(OS%.W'X6_N&[6XM4WI*X\NXYS9<4QP!JLP.(L M5?L;4=^@(-(=VMNS;MN(RCP<_=<#D;1FL;39V6BM:FKS;>LL.X'N#E4_N MQEH;TW57`^=%[,`!*OG<6?5&$!!($M[RG^X>%PM<86(.;-$>%CW5!% M?@8*#F6B[^JK=$&13E9'BTGM5D^[W*43/;!KTR".!@009\E\_9I4[ZFK!6&S MP^,MXXW;700(O7_CA"C([?0565\$I8-=KHO%4O_NG9+"+BJOU>W&V M;^.906\)F!K'KH\[2:2;=HB1=P0I3$0XAH^PY-:R[G M4;]4QS==9WKY;4+FQ:WLQP,",;I^59?97US/&[GJ=^;X/VVJ"\*>NH^\A'+L MF$>VAF!`XC?_W)T\0H_[$?:8^5NU3CY#?/[>O[+'([9GKS8O2?: MU\*CX(O-]3U0NAV4/[836+(EVZ68WI8E:ACR/]F0@PN*^^2Z/Z:;5FZN]U&( M3&Z=ZUYLO-P*6BK/'?8%/-O)R>V77:+=N2?[7\S$6/T^-=XMWU`(@B%]$JM]K4O),[7`TB4S:+=1+/ZI MC(7TU_RSM",8`AK#@3T&D=="K(65_QJ'\[&A6`Y;LD@=*GZK.'BX&=\J^R$3 MHN/%%I[0?=I==$\'&KE$5$C40E%%:]CJ($-E`K?A4-]!^3I2GB]KQJ*]S&E" M&2"(]]OY8N)8]](T9D$:!,EQVS2^@SV>6$-`"-4A!'Q>-R)*]4@5,1300^7^ MF_5V1^G;[0B.:Z[\:+O;R'RQRE@9/_9D[')FH=6$8:6M2MVMM8OP6NYC$M*6 M'O5OY+!I,MN=RV4O>4[ZV[PA#*]\0)B]9*\^'6^[8D,02&@,OX3W7R^7VG[O MRN__0]^@7E3,PG24/0;8;[UQ?*Z#1LFQ7C8V&2HV-C7W!QL;B%B%3_26['_B *[DBG"A(#Z"'`(``` ` end python-debian-0.1.21+nmu2ubuntu1/tests/test_Sources0000644000000000000000000001006011703667743017205 0ustar Package: apache2-mpm-itk Binary: apache2-mpm-itk Version: 2.2.6-01-3 Priority: extra Section: net Maintainer: Steinar H. Gunderson Build-Depends: apache2-src (>= 2.2.9), libaprutil1-dev, libcap-dev [!kfreebsd-i386 !kfreebsd-amd64 !hurd-i386], autoconf, debhelper (>> 5.0.0) Architecture: any Standards-Version: 3.7.3 Format: 1.0 Directory: pool/main/a/apache2-mpm-itk Files: 24d02f8ecf0f37164cfcc2fb23bf1013 1135 apache2-mpm-itk_2.2.6-01-3.dsc f8041c58e43dae9814f903919d222c73 33269 apache2-mpm-itk_2.2.6-01.orig.tar.gz 71dcfe1d2349b376c062c964a87cd213 12824 apache2-mpm-itk_2.2.6-01-3.diff.gz Checksums-Sha1: c9ab75c5b2360d1f1ebee513cbf62963023196b9 33269 apache2-mpm-itk_2.2.6-01.orig.tar.gz 12b996db5c4a628388c1402e13bd30aa0db3cec4 12824 apache2-mpm-itk_2.2.6-01-3.diff.gz Checksums-Sha256: e789b7754072fa6a629c55b931411806432102be7a379f750ac6d328df7790c3 33269 apache2-mpm-itk_2.2.6-01.orig.tar.gz db631e44c83f7086b15e701bafcc7e6e1481f957d47b8075092ba80d863f83bb 12824 apache2-mpm-itk_2.2.6-01-3.diff.gz Package: binutils Binary: binutils, binutils-dev, binutils-multiarch, binutils-hppa64, binutils-spu, binutils-doc, binutils-source Version: 2.18.1~cvs20080103-6 Priority: optional Section: devel Maintainer: James Troup Build-Depends: dpkg-dev (>= 1.13.9), autoconf (>= 2.13), bash, bison, flex, gettext, texinfo, expect-tcl8.3 (>= 5.32.2) [hppa], dejagnu (>= 1.4.2-1.1), dpatch, file, bzip2, lsb-release Architecture: any Standards-Version: 3.7.3 Format: 1.0 Directory: pool/main/b/binutils Files: 04c6dfcfa9d7e2057fea9561610b009a 1331 binutils_2.18.1~cvs20080103-6.dsc 23b4c0bdda7c9974489bc9d3aaf7a754 19746003 binutils_2.18.1~cvs20080103.orig.tar.gz b57f7ed234c299f56a6545c4251e509e 64334 binutils_2.18.1~cvs20080103-6.diff.gz Uploaders: Matthias Klose Checksums-Sha1: ec4182b10960eb6ce927af37326feaa114a232cc 19746003 binutils_2.18.1~cvs20080103.orig.tar.gz 5589356c2c4cc9a94aeb9ba658c08a4ec3fae11b 64334 binutils_2.18.1~cvs20080103-6.diff.gz Checksums-Sha256: bf2e12578caf3b79be6182bb109b3a0215d8a3b89ae2049773f56f286eeb8ad3 19746003 binutils_2.18.1~cvs20080103.orig.tar.gz 44e346757ce336f6a16879264b6cc4ff4e2b4da517e4b5db6cc7f1d254f302dd 64334 binutils_2.18.1~cvs20080103-6.diff.gz Package: debian-archive-keyring Binary: debian-archive-keyring, debian-archive-keyring-udeb Version: 2009.01.31 Priority: important Section: misc Maintainer: Debian Release Team Architecture: all Standards-Version: 3.7.3 Format: 1.0 Directory: pool/main/d/debian-archive-keyring Files: 2675031b2286ca8dfc085e2a9c9d38ed 838 debian-archive-keyring_2009.01.31.dsc 5365c07ddcf639544933552e31a571ee 13627 debian-archive-keyring_2009.01.31.tar.gz Uploaders: Luk Claes Checksums-Sha1: dd987a5ee85d8c4fc3e115f60a6a021e78c626dd 13627 debian-archive-keyring_2009.01.31.tar.gz Checksums-Sha256: 9bdfe720407f5c134842eeaa8cfc9932be33150a5a3c041426216a90cce9e32d 13627 debian-archive-keyring_2009.01.31.tar.gz Package: python-debian Binary: python-debian Version: 0.1.14 Priority: optional Section: devel Maintainer: Debian python-debian Maintainers Build-Depends: debhelper (>= 5.0.37.2), python, python-setuptools Build-Depends-Indep: python-support (>= 0.3) Architecture: all Standards-Version: 3.8.1 Format: 1.0 Directory: pool/main/p/python-debian Files: b153648eaf2fbf2b20e07986f3f8fc37 1224 python-debian_0.1.14.dsc 9d05b7813dbbdb4fd8d41f4a2825de57 176558 python-debian_0.1.14.tar.gz Uploaders: Adeodato Simó , Enrico Zini , James Westby , Reinhard Tartler , Stefano Zacchiroli , John Wright Vcs-Browser: http://git.debian.org/?p=pkg-python-debian/python-debian.git Vcs-Git: git://git.debian.org/git/pkg-python-debian/python-debian.git Checksums-Sha1: 00521e603691403da83a517a8ff10cb4e0e05bcc 176558 python-debian_0.1.14.tar.gz Checksums-Sha256: 40be3da49191a26b3301f18d6824c2a3e7ef4ac52a99ed4484d9176ecc7685e8 176558 python-debian_0.1.14.tar.gz python-debian-0.1.21+nmu2ubuntu1/tests/test_strange_changelog0000644000000000000000000000466311703667743021250 0ustar python-debian (0.1.2) unstable; urgency=low [ James Westby ] * debian_support.py - Support ~ in version numbers when python-apt is not installed. * Turn on the testsuites of changelog.py, and debtags.py in the build. * debtags.py - Rename with to with_ to avoid a warning on python2.5 as with will be a keyword in 2.6. (Closes: #409333) * Suggest python-apt and mention why in the README. [Reinhard Tartler] * Fix the regex matching the end line of a debian/changelog. Using improved regex contributed by Francois-Denis Gonthier, thanks! (Closes: #410880) * disable python_support.py testrun, it is missing in the branch. * add myself to uploaders. -- Reinhard Tartler Thu, 14 Jun 2007 19:54:13 +0100 python-debian (0.1.1) unstable; urgency=low [ John Wright ] * changelog.py: - Version class: + Subclass debian_support.Version, for rich comparison + Changing any attribute now automatically updates any other affected attribute (i.e. changing full_version will update all of the other attributes; changing debian_version will update full_version) - Changelog class: + Many "getter" and "setter" methods were replaced with properties (so they appear as regular object attributes). Most set_* methods remain for compatibility, and they're the actual set methods for the properties anyway. Please see README.changelog. + You can now assign a string to the version attribute, and it will be coerced into a Version object [ James Westby ] * changelog.py: - Added a method to write the changelog to an open file. - Allow single digit day with no extra space in the endline of a changelog. Hopefully this wont allow things to be parsed that will be rejected by the more important tools. It does allow more files to be parsed and used, and I'm not entirely sure what is right. - Allow uppercase characters in the version number when parsing changelogs. Thanks to Jelmer Vernooij for the report and the fix. * README.changelog: - Update the documentation to reflect the changes mentioned above * Add debian/NEWS to warn of the API changes. [ Enrico Zini ] * debtags.py: - Added various methods after the work on the inferrer of tag relationships - Added tagminer and pkgwalk examples -- James Westby Tue, 30 Jan 2007 20:56:44 +0000 python-debian-0.1.21+nmu2ubuntu1/tests/test_debian_support.py0000755000000000000000000002041312015175026021216 0ustar #!/usr/bin/python # Copyright (C) 2005 Florian Weimer # Copyright (C) 2006-2007 James Westby # Copyright (C) 2010 John Wright # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA from __future__ import absolute_import import sys import unittest sys.path.insert(0, '../lib/') from debian import debian_support from debian.debian_support import * class VersionTests(unittest.TestCase): """Tests for AptPkgVersion and NativeVersion classes in debian_support""" def setUp(self): if debian_support._have_apt_pkg: self.test_classes = [AptPkgVersion, NativeVersion] else: self.test_classes = [NativeVersion] def _test_version(self, full_version, epoch, upstream, debian): for cls in self.test_classes: v = cls(full_version) self.assertEqual(v.full_version, full_version, "%s: full_version broken" % cls) self.assertEqual(v.epoch, epoch, "%s: epoch broken" % cls) self.assertEqual(v.upstream_version, upstream, "%s: upstream_version broken" % cls) self.assertEqual(v.debian_revision, debian, "%s: debian_revision broken" % cls) def testversions(self): self._test_version('1:1.4.1-1', '1', '1.4.1', '1') self._test_version('7.1.ds-1', None, '7.1.ds', '1') self._test_version('10.11.1.3-2', None, '10.11.1.3', '2') self._test_version('4.0.1.3.dfsg.1-2', None, '4.0.1.3.dfsg.1', '2') self._test_version('0.4.23debian1', None, '0.4.23debian1', None) self._test_version('1.2.10+cvs20060429-1', None, '1.2.10+cvs20060429', '1') self._test_version('0.2.0-1+b1', None, '0.2.0', '1+b1') self._test_version('4.3.90.1svn-r21976-1', None, '4.3.90.1svn-r21976', '1') self._test_version('1.5+E-14', None, '1.5+E', '14') self._test_version('20060611-0.0', None, '20060611', '0.0') self._test_version('0.52.2-5.1', None, '0.52.2', '5.1') self._test_version('7.0-035+1', None, '7.0', '035+1') self._test_version('1.1.0+cvs20060620-1+2.6.15-8', None, '1.1.0+cvs20060620-1+2.6.15', '8') self._test_version('1.1.0+cvs20060620-1+1.0', None, '1.1.0+cvs20060620', '1+1.0') self._test_version('4.2.0a+stable-2sarge1', None, '4.2.0a+stable', '2sarge1') self._test_version('1.8RC4b', None, '1.8RC4b', None) self._test_version('0.9~rc1-1', None, '0.9~rc1', '1') self._test_version('2:1.0.4+svn26-1ubuntu1', '2', '1.0.4+svn26', '1ubuntu1') self._test_version('2:1.0.4~rc2-1', '2', '1.0.4~rc2', '1') for cls in self.test_classes: self.assertRaises( ValueError, cls, 'a1:1.8.8-070403-1~priv1') def test_version_updating(self): for cls in self.test_classes: v = cls('1:1.4.1-1') v.debian_version = '2' self.assertEqual(v.debian_version, '2') self.assertEqual(v.full_version, '1:1.4.1-2') v.upstream_version = '1.4.2' self.assertEqual(v.upstream_version, '1.4.2') self.assertEqual(v.full_version, '1:1.4.2-2') v.epoch = '2' self.assertEqual(v.epoch, '2') self.assertEqual(v.full_version, '2:1.4.2-2') self.assertEqual(str(v), v.full_version) v.full_version = '1:1.4.1-1' self.assertEqual(v.full_version, '1:1.4.1-1') self.assertEqual(v.epoch, '1') self.assertEqual(v.upstream_version, '1.4.1') self.assertEqual(v.debian_version, '1') @staticmethod def _get_truth_fn(cmp_oper): if cmp_oper == "<": return lambda a, b: a < b elif cmp_oper == "<=": return lambda a, b: a <= b elif cmp_oper == "==": return lambda a, b: a == b elif cmp_oper == ">=": return lambda a, b: a >= b elif cmp_oper == ">": return lambda a, b: a > b else: raise ValueError("invalid operator %s" % cmp_oper) def _test_comparison(self, v1_str, cmp_oper, v2_str): """Test comparison against all combinations of Version classes This is does the real work for test_comparisons. """ if debian_support._have_apt_pkg: test_class_tuples = [ (AptPkgVersion, AptPkgVersion), (AptPkgVersion, NativeVersion), (NativeVersion, AptPkgVersion), (NativeVersion, NativeVersion), (str, AptPkgVersion), (AptPkgVersion, str), (str, NativeVersion), (NativeVersion, str), ] else: test_class_tuples = [ (NativeVersion, NativeVersion), (str, NativeVersion), (NativeVersion, str), ] for (cls1, cls2) in test_class_tuples: v1 = cls1(v1_str) v2 = cls2(v2_str) truth_fn = self._get_truth_fn(cmp_oper) self.assertTrue(truth_fn(v1, v2) == True, "%r %s %r != True" % (v1, cmp_oper, v2)) def test_comparisons(self): """Test comparison against all combinations of Version classes""" self._test_comparison('0', '<', 'a') self._test_comparison('1.0', '<', '1.1') self._test_comparison('1.2', '<', '1.11') self._test_comparison('1.0-0.1', '<', '1.1') self._test_comparison('1.0-0.1', '<', '1.0-1') self._test_comparison('1.0', '==', '1.0') self._test_comparison('1.0-0.1', '==', '1.0-0.1') self._test_comparison('1:1.0-0.1', '==', '1:1.0-0.1') self._test_comparison('1:1.0', '==', '1:1.0') self._test_comparison('1.0-0.1', '<', '1.0-1') self._test_comparison('1.0final-5sarge1', '>', '1.0final-5') self._test_comparison('1.0final-5', '>', '1.0a7-2') self._test_comparison('0.9.2-5', '<', '0.9.2+cvs.1.0.dev.2004.07.28-1.5') self._test_comparison('1:500', '<', '1:5000') self._test_comparison('100:500', '>', '11:5000') self._test_comparison('1.0.4-2', '>', '1.0pre7-2') self._test_comparison('1.5~rc1', '<', '1.5') self._test_comparison('1.5~rc1', '<', '1.5+b1') self._test_comparison('1.5~rc1', '<', '1.5~rc2') self._test_comparison('1.5~rc1', '>', '1.5~dev0') class ReleaseTests(unittest.TestCase): """Tests for debian_support.Release""" def test_comparison(self): self.assertTrue(intern_release('sarge') < intern_release('etch')) class HelperRoutineTests(unittest.TestCase): """Tests for various debian_support helper routines""" def test_read_lines_sha1(self): self.assertEqual(read_lines_sha1([]), 'da39a3ee5e6b4b0d3255bfef95601890afd80709') self.assertEqual(read_lines_sha1(['1\n', '23\n']), '14293c9bd646a15dc656eaf8fba95124020dfada') def test_patch_lines(self): file_a = ["%d\n" % x for x in range(1, 18)] file_b = ['0\n', '1\n', '<2>\n', '<3>\n', '4\n', '5\n', '7\n', '8\n', '11\n', '12\n', '<13>\n', '14\n', '15\n', 'A\n', 'B\n', 'C\n', '16\n', '17\n',] patch = ['15a\n', 'A\n', 'B\n', 'C\n', '.\n', '13c\n', '<13>\n', '.\n', '9,10d\n', '6d\n', '2,3c\n', '<2>\n', '<3>\n', '.\n', '0a\n', '0\n', '.\n'] patch_lines(file_a, patches_from_ed_script(patch)) self.assertEqual(''.join(file_b), ''.join(file_a)) if __name__ == "__main__": unittest.main() python-debian-0.1.21+nmu2ubuntu1/tests/test_changelog0000644000000000000000000003026711703667743017524 0ustar gnutls13 (1:1.4.1-1) unstable; urgency=HIGH [ James Westby ] * New upstream release. * Remove the following patches as they are now included upstream: - 10_certtoolmanpage.diff - 15_fixcompilewarning.diff - 30_man_hyphen_*.patch * Link the API reference in /usr/share/gtk-doc/html as gnutls rather than gnutls-api so that devhelp can find it. -- Andreas Metzler Sat, 15 Jul 2006 11:11:08 +0200 gnutls13 (1.4aA.0-3) unstable; urgency=low [ Andreas Metzler ] * Strip "libgnutls-config --libs"' output to only list stuff required for dynamic linking. (Closes: #375815). Document this in "libgnutls-dev's README.Debian. * Pull patches/16_libs.private_gnutls.diff and debian/patches/16_libs.private_gnutls-extra.diff from upstream to make pkg-config usable for static linking. -- Andreas Metzler Sun, 2 Jul 2006 12:10:56 +0200 gnutls13 (1.4.0-2) unstable; urgency=low [ Andreas Metzler ] * Set maintainer to alioth mailinglist. * Drop code for updating config.guess/config.sub from debian/rules, as cdbs handles this. Build-Depend on autotools-dev. * Drop build-dependency on binutils (>= 2.14.90.0.7), even sarge has 2.15-6. * Use cdbs' simple-patchsys.mk. - add debian/README.source_and_patches - add patches/10_certtoolmanpage.diff patches/12_lessdeps.diff * Fix libgnutls-dev's Suggests to point to existing package. (gnutls-doc) * Also ship css-, devhelp- and sgml files in gnutls-doc. * patches/15_fixcompilewarning.diff correct order of funtion arguments. [ James Westby ] * This release allows the port to be specified as the name of the service when using gnutls-cli (closes: #342891) -- Andreas Metzler Sat, 17 Jun 2006 20:44:09 +0200 gnutls13 (1.4.0-1) experimental; urgency=low * New maintainer team. Thanks, Matthias for all the work you did. * Re-add gnutls-doc package, featuring api-reference as manual pages and html, and reference manual in html and pdf format. (closes: #368185,#368449) * Fix reference to gnutls0.4-doc package in debian/copyright. Update debian/copyright and include actual copyright statements. (closes: #369071) * Bump shlibs because of changes to extra.h * Drop debian/libgnutls13.dirs and debian/libgnutls-dev.dirs. dh_* will generate the necessary directories. * Drop debian/NEWS.Debian as it only talks about the move of the (since purged) gnutls-doc package to contrib a long time ago. (Thanks Simon Josefsson, for these suggestions.) * new upstream version. (closes: #368323) * clean packaging against upstream tarball. - Drop all patches, except for fixing error in certtool.1 and setting gnutls_libs=-lgnutls-extra in libgnutls-extra-config. - Add --enable-ld-version-script to DEB_CONFIGURE_EXTRA_FLAGS to force versioning of symbols, instead of patching ./configure.in. (closes: #367358) * Set DEB_MAKE_CHECK_TARGET = check to run included testsuite. * Build against external libtasn1-3. (closes: #363294) * Standards-Version: 3.7.2, no changes required. * debian/control and override file are in sync with respect to Priority and Section, everthing except libgnutls13-dbg already was. (closes: #366956) * acknowledge my own NMU. (closes: #367065) * libgnutls13-dbg is nonempty (closes: #367056) -- Andreas Metzler Sat, 20 May 2006 11:22:36 +0000 gnutls13 (1.3.5-1.1) unstable; urgency=low * NMU * Invoke ./configure with --with-included-libtasn1 to prevent accidental linking against the broken 0.3.1-1 upload of libtasn1-2-dev which contained libtasn1.so.3 and force gnutls13 to use the internal version of libtasn instead until libtasn1-3-dev is uploaded. Drop broken Build-Depency on libtasn1-2-dev (>= 0.3.1). (closes: #363294) * Make libgnutls13-dbg nonempty by using --dbg-package=libgnutls13 instead of --dbg-package=libgnutls12. (closes: #367056) -- Andreas Metzler Sat, 13 May 2006 07:45:32 +0000 gnutls13 (1.3.5-1) unstable; urgency=low * New Upstream version. - Security fix. - Yet another ABI change. * Depends on libgcrypt 1.2.2, thus should close:#330019,#355272 * Let -dev package depend on liblzo-dev (closes:#347438) * Fix certtool help output (closes:#338623) -- Matthias Urlichs Sat, 18 Mar 2006 22:46:25 +0100 gnutls12 (1.2.9-2) unstable; urgency=low * Install /usr/lib/pkgconfig/*.pc files. * Depend on texinfo (>= 4.8, for the @euro{} sign). -- Matthias Urlichs Tue, 15 Nov 2005 19:26:02 +0100 gnutls12 (1.2.9-1) unstable; urgency=low * New Upstream version. -- Matthias Urlichs Fri, 11 Nov 2005 18:51:28 +0100 gnutls12 (1.2.8-1) unstable; urgency=low * New Upstream version. - depends on libgcrypt11 1.2.2 * Bumped shlibs version, just to be on the safe side. -- Matthias Urlichs Wed, 19 Oct 2005 12:05:14 +0200 gnutls12 (1.2.6-1) unstable; urgency=low * New Upstream version. * Remove Provides: on libgnutls11-dev. Hopefully this will be temporary (pending discussion with Upstream). -- Matthias Urlichs Thu, 11 Aug 2005 12:21:36 +0200 gnutls12 (1.2.5-3) unstable; urgency=high * Updated libgnutls12.shlibs file. Thanks to Mike Paul . Closes: #319291: libgnutls12: Wrong soversion in shlibs file; breaks dependencies on this library -- Matthias Urlichs Thu, 21 Jul 2005 13:19:25 +0200 gnutls12 (1.2.5-2) unstable; urgency=medium * Did not depend on libgnutls12 -- not picked up by dh_shlibdeps. Added an explicit dependency as a stopgap fix. -- Matthias Urlichs Thu, 21 Jul 2005 08:27:22 +0200 gnutls12 (1.2.5-1) unstable; urgency=low * Merged with the latest stable release. * Renamed to gnutls12. - Changed the library version strings to GNUTLS_1_2. - Renamed the development package back to "libgnutls-dev". -- Matthias Urlichs Tue, 5 Jul 2005 10:35:56 +0200 gnutls11 (1.0.19-1) experimental; urgency=low * Merged with the latest stable release. -- Matthias Urlichs Sun, 26 Dec 2004 13:28:45 +0100 gnutls11 (1.0.16-13) unstable; urgency=high * Fixed an ASN.1 extraction error. Found by Pelle Johansson . -- Matthias Urlichs Mon, 29 Nov 2004 10:16:21 +0100 gnutls11 (1.0.16-12) unstable; urgency=high * Fixed a segfault in certtool. Closes: #278361. -- Matthias Urlichs Thu, 11 Nov 2004 09:40:02 +0100 gnutls11 (1.0.16-11) unstable; urgency=medium * Merged binary (non-UF8) string printing code from Upstream. * Password code in certtool was somewhat broken. -- Matthias Urlichs Sat, 6 Nov 2004 13:11:03 +0100 gnutls11 (1.0.16-10) unstable; urgency=high * Fixed one instance of uninitialized memory usage. -- Matthias Urlichs Thu, 21 Oct 2004 06:07:53 +0200 gnutls11 (1.0.16-9) unstable; urgency=high * Pulled from Upstream CVS: - Fix two memory leaks. - Fix NULL dereference. -- Matthias Urlichs Fri, 8 Oct 2004 10:43:20 +0200 gnutls11 (1.0.16-8) unstable; urgency=high * Pulled these changes from Upstream CVS: - Added default limits in the verification of certificate chains, to avoid denial of service attacks. - Added gnutls_certificate_set_verify_limits() to override them. - Added gnutls_certificate_verify_peers2(). -- Matthias Urlichs Sun, 12 Sep 2004 02:05:25 +0200 gnutls11 (1.0.16-7) unstable; urgency=low * Removed superfluous -lFOO entries from libgnutls{,-extra}-config output. Thanks to joeyh@debian.org for reporting this problem. -- Matthias Urlichs Sat, 14 Aug 2004 11:22:51 +0200 gnutls11 (1.0.16-6) unstable; urgency=medium * Memory leak, found by Modestas Vainius . - Closes: #264420 -- Matthias Urlichs Sun, 8 Aug 2004 22:21:01 +0200 gnutls11 (1.0.16-5) unstable; urgency=low * Depend on current libtasn1-2 (>= 0.2.10). - Closes: #264198. * Fixed maintainer email to point to Debian address. -- Matthias Urlichs Sat, 7 Aug 2004 19:44:38 +0200 gnutls11 (1.0.16-4) unstable; urgency=low * The OpenSSL compatibility library has been linked incorrectly (-ltasn1 was missing). * Need to build-depend on current opencdk8 and libtasn1-2 version. -- Matthias Urlichs Sat, 7 Aug 2004 19:29:32 +0200 gnutls11 (1.0.16-3) unstable; urgency=high * Documentation no longer includes LaTeX-produced output (the source contains latex2html-specific features, which is non-free). * Urgency: High because of pending base freeze. -- Matthias Urlichs Mon, 26 Jul 2004 11:18:20 +0200 gnutls11 (1.0.16-2) unstable; urgency=high * Actually *enable* debug symbols :-/ * Urgency: High for speedy inclusion in d-i -- Matthias Urlichs Fri, 23 Jul 2004 22:38:07 +0200 gnutls11 (1.0.16-1) experimental; urgency=low * Update to latest Upstream version. * now depends on libgcrypt11 * Include debugging package * Use hevea, not latex2html. -- Matthias Urlichs Wed, 21 Jul 2004 16:58:26 +0200 gnutls10 (1.0.4-4) unstable; urgency=low * New maintainer. * Run autotools at source package build time. - Closes: #257237: FTBFS (i386/sid): aclocal failed * Remove "package is still changed upstream" warning. * Build-Depend on debhelper 4.1 (cdbs), versioned libgcrypt7. -- Matthias Urlichs Fri, 16 Jul 2004 02:09:36 +0200 gnutls10 (1.0.4-3) unstable; urgency=low * control: Changed the build dependency and the dependency of libgnutls10-dev to be versioned on libopencdk8-dev >= 0.5.3; libopencdk8-dev 0.5.1 had an invalid dependency on libgcrypt-dev which could cause linking against two versions of libgcrypt. -- Ivo Timmermans Sat, 24 Jan 2004 15:32:22 +0100 gnutls10 (1.0.4-2) unstable; urgency=low * libgnutls-doc.doc-base: Removed HTML manual listing. * control: Removed Jordi Mallach from the list of Uploaders. Thanks, Jordi :) -- Ivo Timmermans Wed, 14 Jan 2004 13:35:42 +0100 gnutls10 (1.0.4-1) unstable; urgency=low * New upstream release (Closes: #227527) * The new documentation in libgnutls-doc fixes several typo's and style glitches: Closes: #215772: inconsistent auth method list in manual Closes: #215775: dangling footnote on page 14 of manual Closes: #215777: bad sentence on page 18 of manual Closes: #215780: incorrect info about ldaps/imaps in manual * rules: * Use --add-missing instead of --force in the call to automake. * Don't build gnutls.ps, use the upstream version. (Closes: #224846) * gnutls-bin.manpages: Use glob to find manpages. * patches/008_manpages.diff: Removed; included upstream. -- Ivo Timmermans Tue, 13 Jan 2004 23:57:16 +0100 gnutls10 (1.0.0-1) unstable; urgency=low * New upstream release. * Major soversion changed to 10. * control: Changed build dependencies of libtasn1-dev. * libgnutls10.shlibs: Added libgnutls-openssl to the list. -- Ivo Timmermans Mon, 29 Dec 2003 23:23:08 +0100 gnutls8 (0.9.99-1) experimental; urgency=low * New upstream release. * Included upstream GPG signature in .orig.tar.gz. -- Ivo Timmermans Wed, 3 Dec 2003 22:33:52 +0100 gnutls8 (0.9.98-1) experimental; urgency=low * New upstream release. * debian/control: libgnutls8-dev depends on libopencdk8-dev. * debian/libgnutls-doc.examples: Install src/*.[ch]. -- Ivo Timmermans Sun, 23 Nov 2003 15:44:38 +0100 gnutls8 (0.9.95-1) experimental; urgency=low * New upstream version. -- Ivo Timmermans Fri, 7 Nov 2003 19:50:22 +0100 gnutls8 (0.9.94-1) experimental; urgency=low * New upstream version; package based on gnutls7 0.8.12-2. * debian/control: * Build-depend on libgcrypt7-dev (>= 1.1.44-0). * debian/rules: Run auto* after the patches have been applied. -- Ivo Timmermans Fri, 31 Oct 2003 18:47:09 +0100 python-debian-0.1.21+nmu2ubuntu1/tests/test_tagdb0000644000000000000000000003061611703667743016654 0ustar 3ddesktop: game::toys, interface::3d, use::viewing, x11::applet aa3d: game::toys, interface::commandline, use::viewing achilles: field::biology, game::toys, interface::x11, uitoolkit::sdl, x11::application xbase-clients: accessibility::input, accessibility::screen-magnify, game::toys, interface::commandline, interface::text-mode, interface::x11, mail::notification, security::authentication, uitoolkit::athena, use::checking, use::monitor, use::timekeeping, use::viewing, works-with::font, x11::applet, x11::application amor: game::toys, interface::x11, suite::kde, uitoolkit::qt, use::gameplaying, x11::application kdetoys: game::toys, special::meta, suite::kde, uitoolkit::qt an: game::toys, interface::commandline, use::gameplaying, works-with::text animals: game::toys, interface::commandline, use::gameplaying asr-manpages: admin::user-management, devel::doc, game::toys funny-manpages: devel::doc, game::toys verse: game::toys, interface::commandline blast: game::toys, interface::x11, use::gameplaying, x11::applet brickos-doc: devel::doc, game::toys, hardware::embedded, made-of::data:html, use::driver, use::gameplaying bsdgames: game::adventure, game::board, game::puzzle, game::simulation, game::toys, interface::commandline, interface::text-mode, uitoolkit::ncurses, use::gameplaying cappuccino: game::toys, interface::x11, suite::gnome, uitoolkit::gtk, use::gameplaying, x11::application clc-intercal: devel::compiler, devel::interpreter, game::toys, interface::commandline, works-with::software:source cmatrix: game::toys, interface::text-mode, uitoolkit::ncurses, use::gameplaying cmatrix-xfont: game::toys, made-of::data:font, use::gameplaying, x11::font fortune-mod: game::toys, interface::commandline, use::gameplaying, works-with::text cowsay: game::toys, interface::commandline, works-with::text cw: game::toys, hardware::hamradio, interface::commandline, use::converting, use::gameplaying, works-with::audio, works-with::text cwcp: game::toys, hardware::hamradio, interface::text-mode, uitoolkit::ncurses, use::converting, use::gameplaying, works-with::audio, works-with::text dadadodo: game::toys, interface::commandline, use::gameplaying, works-with::text debroster: game::toys, suite::debian kstars: field::astronomy, game::toys, interface::x11, suite::kde, uitoolkit::qt, use::gameplaying, x11::application spacechart: field::astronomy, game::toys, interface::3d, suite::gnome, uitoolkit::gtk, use::gameplaying, use::viewing, x11::application starplot: field::astronomy, game::toys, interface::3d, uitoolkit::gtk, use::gameplaying, x11::application sunclock: game::toys, interface::x11, use::timekeeping, x11::application xplanet: field::astronomy, game::toys, interface::x11, use::viewing, x11::screensaver xplanet-images: field::astronomy, game::toys, interface::x11, use::viewing, x11::screensaver lightspeed: field::astronomy, game::toys, interface::3d, uitoolkit::gtk, use::gameplaying, works-with::3dmodel, x11::application xaos: field::mathematics, game::toys, interface::svga, interface::text-mode, interface::x11, uitoolkit::ncurses, use::gameplaying, use::viewing, x11::application ktuberling: game::toys, interface::x11, suite::kde, uitoolkit::qt, use::gameplaying, x11::application electricsheep: game::toys, interface::x11, uitoolkit::sdl, use::downloading, use::gameplaying, use::viewing, works-with::video, x11::screensaver eyesapplet: game::toys, suite::kde, uitoolkit::qt, use::gameplaying, x11::applet fcitx: accessibility::input, culture::chinese, game::toys, interface::x11, use::gameplaying, x11::application filters: game::toys, interface::commandline, use::converting, use::gameplaying, works-with::text fkiss: game::toys, interface::x11, use::gameplaying, use::viewing, works-with::image:raster, x11::application fortunes-min: game::toys, interface::commandline, use::gameplaying, works-with::text fortune-zh: culture::chinese, culture::taiwanese, game::toys, interface::commandline, use::gameplaying fortunes: game::toys, interface::commandline, use::gameplaying, works-with::text fortunes-bg: culture::bulgarian, game::toys, interface::commandline, use::gameplaying fortunes-bofh-excuses: game::toys, interface::commandline, use::gameplaying, works-with::text fortunes-br: culture::brazilian, game::toys, interface::commandline, use::gameplaying, works-with::text fortunes-cs: culture::czech, culture::slovak, game::toys, interface::commandline, use::gameplaying fortunes-de: culture::german, game::toys, interface::commandline, use::gameplaying, works-with::text fortunes-debian-hints: game::toys, interface::commandline, suite::debian, use::gameplaying, works-with::text fortunes-eo: culture::esperanto, game::toys, use::gameplaying fortunes-eo-ascii: culture::esperanto, game::toys, use::gameplaying, use::viewing, works-with::text fortunes-eo-iso3: culture::esperanto, game::toys, use::gameplaying, use::viewing, works-with::text fortunes-es: culture::spanish, game::toys, interface::commandline, use::gameplaying fortunes-es-off: culture::spanish, game::toys, use::gameplaying fortunes-fr: culture::french, game::toys, use::gameplaying, works-with::text fortunes-ga: culture::irish, game::toys, interface::commandline, use::gameplaying, works-with::text fortunes-it: culture::italian, game::toys, interface::commandline, use::gameplaying, works-with::text fortunes-it-off: culture::italian, game::toys, interface::commandline, use::gameplaying, works-with::text fortunes-mario: culture::brazilian, culture::portuguese, game::toys, interface::commandline, use::gameplaying, works-with::text fortunes-off: game::toys, interface::commandline, use::gameplaying, works-with::text fortunes-pl: culture::polish, game::toys, interface::commandline, use::gameplaying freej: game::toys, interface::x11, uitoolkit::gtk, uitoolkit::ncurses, uitoolkit::sdl, use::gameplaying, works-with::audio, works-with::video, x11::application gdesklets: game::toys, interface::daemon, suite::gnome, uitoolkit::gtk, use::gameplaying, x11::application geekcode: game::toys, interface::commandline, use::gameplaying glunarclock: game::toys, suite::gnome, uitoolkit::gtk, use::gameplaying, use::timekeeping, x11::applet gniall: game::toys, interface::x11, suite::gnome, uitoolkit::gtk, use::gameplaying, x11::application gnome-about: game::toys, interface::x11, suite::gnome, uitoolkit::gtk, use::viewing, works-with::people, x11::application gnome-applets: admin::filesystem, admin::power-management, game::toys, hardware::modem, hardware::storage:cd, interface::x11, mail::notification, suite::gnome, uitoolkit::gtk, use::downloading, use::monitor, use::playing, works-with::file, works-with::mail, works-with::pim, x11::applet gnomekiss: game::toys, suite::gnome, uitoolkit::gtk, use::gameplaying, use::viewing, works-with::image:raster, x11::application gpe-julia: field::mathematics, game::toys, hardware::embedded, interface::x11, uitoolkit::gtk, use::viewing, works-with::image, x11::application groach: game::toys, interface::x11, suite::gnome, uitoolkit::gtk, x11::application hubcot: game::toys, hardware::usb, interface::commandline, use::driver hubcot-source: game::toys, hardware::usb polygen: devel::interpreter, game::toys, interface::commandline, works-with::text nettoe: game::toys, interface::text-mode, use::gameplaying zivot: game::toys, interface::text-mode, use::gameplaying kworldclock: game::toys, interface::x11, suite::kde, uitoolkit::qt, use::timekeeping, x11::applet, x11::application snowflake: game::toys, interface::x11, uitoolkit::gtk, use::gameplaying, x11::application lavaps: game::toys, interface::x11, suite::gnome, uitoolkit::gtk, uitoolkit::tk, use::gameplaying, use::monitor, works-with::software:running, x11::application xcruise: game::toys, interface::3d, interface::x11, uitoolkit::athena, use::browsing, works-with::file, x11::application junior-toys: game::toys, interface::x11, junior::meta, special::meta, use::gameplaying, x11::application oneko: game::toys, interface::x11, use::gameplaying, x11::application xfireworks: game::toys, interface::x11, x11::screensaver xfishtank: game::toys, interface::x11, x11::screensaver xhangglider: game::toys, interface::x11, x11::screensaver xpenguins: game::toys, interface::x11, use::gameplaying, x11::screensaver xteddy: game::toys, interface::x11, use::viewing, x11::application kali: game::toys, interface::x11, use::gameplaying, x11::application kaquarium: game::toys, suite::kde, uitoolkit::qt, x11::applet, x11::application kmoon: game::toys, interface::x11, suite::kde, uitoolkit::qt, use::timekeeping, x11::applet, x11::application kodo: game::toys, interface::x11, suite::kde, uitoolkit::qt, use::gameplaying, x11::application kteatime: game::toys, interface::x11, suite::kde, uitoolkit::qt, use::gameplaying, use::timekeeping, x11::applet, x11::application ktux: game::toys, interface::x11, suite::kde, uitoolkit::qt, use::gameplaying, x11::application, x11::screensaver kweather: game::toys, interface::x11, suite::kde, uitoolkit::qt, use::gameplaying, x11::applet, x11::application kdetoys-doc-html: game::toys, made-of::data:html, suite::kde, uitoolkit::qt kfish: game::toys, interface::x11, suite::kde, uitoolkit::qt, x11::applet, x11::application kstars-data: field::astronomy, game::toys, interface::x11, suite::kde, uitoolkit::qt, use::gameplaying, x11::application pi: field::mathematics, game::toys libfortune-perl: devel::library, game::toys, interface::commandline, use::gameplaying, works-with::text lincredits: game::toys, use::printing linuxlogo: admin::boot, game::toys, interface::commandline matrem: game::toys, interface::x11, x11::application megahal: game::toys, interface::commandline, use::gameplaying pileup: game::toys, hardware::hamradio, interface::commandline, use::gameplaying, use::learning polygen-data: devel::interpreter, game::toys, interface::commandline, works-with::text purity: game::toys, interface::commandline, use::gameplaying purity-off: game::toys, interface::commandline, use::gameplaying pyching: game::toys, interface::x11, x11::application randtype: game::toys, interface::commandline, works-with::text razzle: game::toys, interface::svga, use::gameplaying, works-with::video rss-glx: game::toys, interface::x11, x11::library, x11::screensaver shermans-aquarium: admin::monitoring, game::toys, interface::x11, uitoolkit::gtk, uitoolkit::sdl, use::monitor, x11::applet, x11::screensaver sl: game::toys, interface::text-mode, uitoolkit::ncurses, use::gameplaying gliese: field::astronomy, game::toys, use::gameplaying ssystem: field::astronomy, game::toys, interface::3d, use::gameplaying, x11::application sunclock-maps: game::toys, interface::x11, use::timekeeping, x11::application sysvbanner: game::toys, use::viewing, works-with::text tama: game::toys, interface::daemon, use::gameplaying tdfsb: game::toys, interface::3d, uitoolkit::sdl, use::browsing, works-with::file, x11::application tuxeyes: game::toys, interface::x11, uitoolkit::qt, use::gameplaying, x11::application unlambda: devel::interpreter, game::toys, interface::commandline, works-with::software:source vigor: devel::editor, game::toys, uitoolkit::ncurses, use::editing, use::gameplaying welcome2l: game::toys, interface::text-mode whitespace: devel::interpreter, game::toys, interface::commandline wmbio: game::toys, interface::x11, suite::gnustep, use::monitor, use::timekeeping, x11::applet wmblob: game::toys, interface::x11, suite::gnustep, uitoolkit::gtk, use::viewing, x11::applet wmmand: field::mathematics, game::toys, interface::x11, use::viewing, works-with::image:raster, x11::applet wmmatrix: game::toys, interface::x11, suite::gnustep, use::viewing, x11::applet wordplay: game::toys, interface::commandline, use::gameplaying, works-with::text xearth: game::toys, use::gameplaying xball: game::toys, interface::x11, uitoolkit::motif, use::gameplaying, x11::application xeji: game::toys, interface::x11, use::gameplaying, x11::application xflip: game::toys, interface::commandline, use::gameplaying, x11::application xjokes: game::toys, interface::x11, use::gameplaying, x11::application xlaby: game::toys, hardware::input:mouse, interface::x11, use::gameplaying, x11::application xlife: game::toys, interface::text-mode, uitoolkit::ncurses, use::gameplaying xmountains: game::toys, interface::x11, use::gameplaying, x11::application xpenguins-applet: game::toys, suite::gnome, uitoolkit::gtk, use::gameplaying, x11::applet xphoon: game::toys, interface::x11, use::timekeeping, x11::screensaver xtartan: game::toys, interface::x11, use::viewing, works-with::image:raster, x11::application astrolog: game::toys, use::gameplaying xfractint: game::toys, use::gameplaying xsnow: game::toys, use::gameplaying empty1: empty2: empty3 python-debian-0.1.21+nmu2ubuntu1/tests/test.deb.uu0000644000000000000000000017046211703667743016700 0ustar begin 644 hello.deb M(3QA73OS-PA-UYQ MA5U2BOWK>]:TC:28K9$'%2C*"THKD3.'=\X]]V,NKRX>W03,6[L\I;?BM\]/ M=B&%='9QW?MXN(X'VCJNHOWE-_^IW5?^OQ_U"ZO;HN=C[?S M(W['$F!GS%K\G5?V<_RMPSHIE3(7G7A$GS[;_WG\8Y;D%?E0@U+2D=#")I>C MH.2EL*'KCO-TE?K]U8Z'8=Q(43VE7`*QL9%*3=FX8*K.L<0<]6G]O*.)KX8Q MT\!7A:_^]MV;'U[?R=MRD[+&M!&U85!N*T(9\M$:%&/$MI@$U M'5>@1`J2*$!*HMA4!/DD@K'9D,F^M*!X;D-5GZQ(FI3.@EQP4DD9$K/U.1G% M+:@ZM:&\5-H8([E8841E2EQQU"JMTM+KW/)J:$-9"=9MR+;ZH(PU<*W&J"5S M]=ZZVH#:4AM*%O8F%5EE-@8@%`FX21:]+D0VHO`(52B618S3,.G,6NB1/ MQ>20BG&QMKR:[U>X,J!'U^J2#ZYF$D4Y:Z*(AJ1F:NF*YS?/_KZB!XH.:LC6 M!A\#I\#!AQP(D$*2;#'_?O?FNU=M--8FJ>*+J*%`\2[4Z"HBR$('R;7%V'XE MCCJYQ#'5K."6P^E4310J1,J)H-L&5%_:4-'+:`EY`KJ\B:Q\5F2\REXCG+%) M_HI099*E,O*1`_+7!1"<$?NDD09HE[UR),2G5J@_CBKJRJ4Y*(R(JA(KD')BJ M.+6,%#SY%E=OVU"*+7S@"(&F6KT6F2E7Q"$YKD+95@0/:U6+=76*!'+92.F4 M538I4M*B!)%L>?5VA?8HO*X"BB(I2\A0*2$A+9,*2'#B%M0*5\A6BO%VX\ZUT/J[H"K07AUS1'DVX!DJRHO.4HJ,.)$R+J[L5 M75DEHH2T?"6/'([0E1`Y$F,`A@B$EJBZ6Z0J)160FI-(ZX[QRQ@3&*=6E#9:H5+'H&%2=5<0A M"MGJ%VF[0I?4Z.T8;@O*:ERR.Z6(X4MI+@J9T(`ZK&50*9AK'.=L%3([JR`3 M)ZT85U6DD?3L*Z MPF2KKJHJ1#2;B'Z6`\;,WP\/9MV`G*6B,!QC,'*./ M"W1U+\SOMM[2?OF1'UF4RU?_@?O?)>C;'Z9Q>+0;YA?O_Q)%8[G_0]&8GYS[ M3;E77_`LN,$D%X'MWG.=[PO\W4W]"F[[NMO_PPT?>F>NF\V M+_>@>QBX/+WIWV.##6'S`_7[`WYXNNYN:'_H:3MV/_4#=7^::7^//_Y23HDY M3MMO`3_GJ;\[^?7CCKL\T#SWN=M.S(=^OWW2T;YTU&W'L73\*]W>#;SYL/+[ M5_\XG;.[F\;M1+?+LQPSSUA>Z;8?>IJ>='7J<8#AW6?$RZY[>=AT<'Q\F+O] MN'_ZWAT//'5S!E!FK!B'[F'7Y]VF>QB/0^G&PXZG MAQ[;$G?'/=T3CIF&96F'CVXO-QU>-PS"C_/P[@G>'(_;W36>_=SA1?M/1^O& MVNW&AV5G&>'%J8AU=Z?``N7E8=D`U$\?W9^"O&P\?&3E]33^@B!^-7?_/!'T M,$Y#^>H33YONZP_N+T#]8>:A_M:!.D[_#O3-Y1\I0F<[V]G.=K:SG>UL9SO; MVS3[%^2'WY0`*```"F1A=&$N=&%R+F=Z+R`@("`Q,3@W-C`U M.#$P("`Q,#`P("`Q,#`P("`Q,#`V-#0@(#0R-S,Q("`@("!@"A^+"``````` M``/L?0=`$TOS>*03.BJ*6`X4!2%(J`I(46E*DZ9@#7[W_+_V8#!`+!0/^,/G332W-+"3R_W7R%X8Q!.B`/T?^="LS M*XG\?[7\67SF@#]#_F;FUA+[_UOD'X9R./P!OUO^YA;6$OW_C?+W_+?%O*_S;&O\>B'\/,D;,0._`O^G&@`J`9OB3.?YMB7];(2X"P(D?/U04 M`_HPXL(7\UAXR<:(.X_96#S&8R,,'@MA84*1``L10PR$'PJJ`.H/JVZ,Q&"B M,`14`T*^6(1P^2PL%&,2Q``92#X2%7`QD0BO(Z`7BW!1%B;F-N81\&,9'%$L M$BG@1V,L@`:;B-E0?4`$-"S&1'%6\*(;GB%M`2I$!=$H"S`=")H9`O%V!_QA-4`"`)8!,H3"1$1'V=;A`J)!RXC M`I-VRA#L22]%G-WX`Z496;R9*.2],4&2[FQ.+=&4HRH(U\-@BL M5Z@`_&'&""_$&(D431CB:XP(Q*"Z$<:(.`(VEQ?@Z[MLX'THPQADG^`$T-G@ MGQ'@3\`'V0"UN+`)0[U:,45'#`8!GGAH(T]#Q.Q0;!(J)&0`I!+)8<3BG2)$ MS`:=*Q*8!ERC6"S0:5M7TQ14TPSTK*9:.C4TN5`<"7-#]ITG`77"8)\#!L3+ MPP\QT/,"%0'=V@.8'#&##?2:P-8SA/C^S:J*RY$)JL:"?T![40[XXQOC#8>* MB(:##<@&Z6'@?1CX=Y@8ZC'&,D8P@!$.&P?DX$033,VIQ]4'C/0B?I!Q*%`=T70&O!!ZS39#J@H(EQ!B2[,X2#`X$2* M\4=@:X4-'8(-3`(N'"Z0#&Q#@WXT7C]CI!^-Q@/=FL9&>:@`;\1^>*NVE#54 M27P.`+-Q^QDB+#Z*6RF$P^='PK)0H-\(%HIX.KE[("@O&A/P>3A;T5`U0L`@ M@@D!*=S4(4)4U*)ST$W,&[L&'`%@UV@JDL>/P:V&L+G9P!L;J#@35`?8'9@0 M"D8AT$`8#Z\GP`?E-5@=GABWDR#3`-!,8KP%&EH"#`W@%>S&$QO-5#_$`.!- MI$6#NH),@-)$PIHUO`@#+X`9::7.A#+3VZD*D\'#JP,-,1(+S!5NB@EY$PQP M&PJ@P3>-A7Q7!M!-NA7BAT:*"/L/!T]8DG\8*H`M#;H;L$[�:8C@1B'88*7UC!E MY@L(QT.`XE1/(`A(1.8G+$P!R;#>V$5Q?E,7B`G)`)7!,@)U!O!IX9S@;`7(0O MACT&9AO0GW`50`$^1'_XSB7]&4?2LYD[2$P_29Y:>;<6QKA;"[W:YF)KE#7) M`G0\89%"LDA;.$_`QVG@XC0X0F`(%35,9G"'-)8*$IHF,F`FQ!4VJ";LXZ[X M](Z#^(A#.$#C/(!WR0-BA@H%4X1A>`^AXIK<3H5M$13#NUV#Y@.W&Q1OP!`1 M,QEBJF!(A5X/F(XW(;91PZ:*X"XS+#8,:##X!Z`&:A*#`6T/:7`J@1#%(NHH M=W\W[P!_Q,DK"!GEY.OKY.4?9-OH8@,;0-#!G5HXAV8(@&,@PJ<.GLZ^0]T` MOM,0=P]W_R#(MHN[OY>SGQ_BXNV+."$^3K[^[D,#/)Q\$9\`7Q]O/V=@N!IB M&:#]J.VT7RC>_J"9B"`'[$IP5B4$+(%I$I@DX;$3%(-3,`8^:_\)H<`J4?$F M;69ZA;%",-_X'@[W6C?%?*[HYG/]96UM*YG^_XM-K@8+"@L.HLY1TH\PIF6.KHBO/ M+O]*6Z_W,MLAOEN?WOO+MCT3Z52L6&"HW//$N,/L%]V-ATF%1^0$/Q=<>7VT MKEO'96LT#;68&2\'C>,6:6IK:]<6+E_6]5JXWS7QM1T:KV\K]Q"XGWJ5L'3` MDE>/0N[KI_0RF6;8:87W0^MN+H=>:@PUSYPY;7FI>4#.\DW@^>-/$Q$V[GTR>JSJDVU6# MOM[:2D>C[ZS`UIVS6'%X>5C8I`NO<[-SW88DSW4>7E-QSO/;-,%5[26VTW6\ MIU4KN]YVZA5JZ7DKOL!^9[B-\MX^)R\N-)SAU'UR\`P=^]ZG\S;<<[@4-_]Y M@KR0I_%U=9AYMV':=P,"'!:_/.J1YG1VJX-^S*8-`QU6#O";'J>1<27E44Z> MNN]\[LN$T2.NCBDR*^KN>V-Z6A]5S]&Z?*?UJZT6JB=KZ7.613K/<'UDGY]3 MN&;PAHYOL]TNQGJ<8INO6Q96Y>!:4"=;G.AY;*K#5ZNNBX-9R9.V/$@)'/9X M'KOPK*AX1,^=*8MWI+L8:QZYX*_@/I8N&#Q]DTKP%K9#"=LV..=@]+B2$UFV M7RXO*;ZYM?9P]+=/'PO7\%.?IRU^%[TD/F9U:=04VC:'FJRUZ54U?1:\BTY2 M^9R4<_)]Q.$;VMBAP]'FU!@A3"W,63[[K:"=/HSTN'TJE%DUS^J2W;X(?/N@\PK$T:Z%9) MG36+A^(4(SDSKVZ7K5;>W#-?:JK.5!E4<'3`PK[1WXU'49=96V MZ9BE\M(*/5,NK_NE,L8Y;SN>>:?UWEM8 M31/T,G:T-4Q?/LE3=A(_[,.HY=9Y=2ZC!-D"?17,)=;[B*>!SH@IUMUO>[;!S__HNHQS7GSQ.&IBZ;L>G9I MENO7Z]B$F#E)Q>\_4L=-T_ST7B%-;][%+H][^K`L.G7'$O9*/:O/>:#X?O%) M[J?)\@J*U>BFV<^*G[A5^/@N?90Q./O%XM.<\P7.5ZB[>TWMNZQ&[O+GA/<3 MW^IN)ZW2JVGZ@OMKTE_-];N[I#=E?WSR'8^NW:-ZFXY#5QB? M^$+O8GVEH'^&7E+ZIF=O3VU1VFW9[9;:/IND"_4[_&;L.RRNUWALNUFUS%F6 MH;+GJC+FI'C1(,T\WNR)[&U#J?6/+E/&&;@]6;.B:T5?-U'?_'0YJLS^FDN/ M#X[YRDU\NTWQ7E)X7P?O6ZX5"T*N1H5&=^V!:8CW+ML?-%WZ>ORI8?7K+YKJ M;:TH]GP7)XB=;IZ2N6_?W%0-J0VCS9$E2:SC_?5/9OD*%F]Y^'Z5HOFX_`7J M>>_'/SOF>/00>]`YY2W>'5:.K]27\N*MW"ANS\FGF&.* M>JEL]?J\=-$1S'_SIPO5+R(OS`K1#]\5%1>?_[S.>8<5YYEVQQ+'R5-/J6<\ M.>2G,3W@JN*JT(2$*9TU'@4$UAJ8N4[*O!RP/$]V_!O%OB,VG`M_;]/-94Y" MCXI@^[7W8H=Z##=@,GJFW.D=+W/D7GZ'9[=/!YS44!Y6P9D9T3N#?_'49(L[ M(9/"!*-RO7(V#-^O=FO6G8J^]SP3MHZ;.(6WT43WXD;3NT%.:XZ[V9DMK_BR M.OU:9E:.X;;=]X=`F^TQ.FIRSQ*JJX[`E,Z4,_9Z^7+YY_6JF94]C`U?&6HT]NUVX!FO%N[H7ZDQ4K<\:_X6F>AU*?*,QXP5 M(ZL'+>[4:56?H<&5#N7Q#V7V&)]ZNN((1O?.3YM3.MI-<:^SO(,R-O#\O+S) M[+XGS`HT&FZ3.7!U]]?5X?DEU/?]%A8+[8>.XNMSD MN*C9FM+OC%^D]:%2U28B=E._E>WL$[C]"G9=T\G;7L0\3=6L=-&V'?;#._$+OU\M'2=I\?+KG MXR,[HCM3=&,/7]YLL'!77%)]S8WQGT)O/ZCL./G]S;[:^U=5#8S06+I8=MF# M+RD/W`XE2G=:[4[?NM#]X-?UA>G[@E)VG'AHO'W5HNBXQ+>YP^YV_)RND&7!V> ML/J(7]=N9CY'=YXM']-5L:Q6-?G^^^4S%(63E6.1?6F!S^<.4)Y"G3_;/F!O M]/A5!5$6._38ZD<3$U.??)"ILPF_7!)I$"-)C(<$%\<>HOGU')Y3N6I52@YIST@6GI+N,]<^V<0BR6FT3KL*=-K?RQO@@ MM_KXJ$ZC!ITJ[S94+58W1+Q,$7F77,,=D7%DMM\2:<4>=^-CYSN>E75>9;8K M:&'5D!3W@X92UPPV=@WE+KZ1?TGN3?C#]*EO/3E[/!1^/8` MR]X+KKU'WO:69QPY%#G?C/;`+7AGR7%>UX_;TD8=#H]4K=7>$'6DLN.03W6; MW]\:/BCVXLCT/%>IC_:*_C'#[VV>^&W/GIK*]*-S;1^,77XJ:O^!J,NN9XP7 MZ4Q8<,MGR9REGT:_2EXM0WN]]\L8=DE.RJUHE]ZFM2,31!]K+HQGE[Q7"Q;;W3$0-FSJ'!XZ=NZ4YVD MRU8P%WL+9'Q+#YSI/KG;I^.GA*^R>X544[1T:>O-J0\_[',>LGF'IO[5;\7V M_AO5TYXOQSI_\GYS4&U6@G;JV!C?DJ<;EABYLG=-/>IZ<.W-,4=2*K1'6!ZJ M.Q_T;/S43\4O&$^G*8RX-/7Z]7OUE'/T*T<.7CN7;-JGC_W2>=RKFY,3JYX\ M><*A>SLL/3#X@L&HF)0[8V1OE/4[\K%BR&X'CWD[QYV-+16E;E]AQGM=+>]S MV0F=.F+/A@V89X7NYH^+/QM,B%XV)K(@NOSPY6LQAS:H7AE[O][\^A-WQQG? M-*<%^_8SO5=XH#ZX5D&ID]HK_V4K1I_J%)LX9/?[+K6:BY5*RQ\K5TP.OJ^J M.B-29HG,XYG#0TIU>AUQ=+++[AY:X!UC\_I+A'_O7:72@;I7^YQ6*5DZ>(9Y MT.ZY]^QWNM(H8>93RK>?/9W^:*_[L>HL9JPUJ[_\L^G=;_IE*^[.NSED2NZV M]\CX\0%)77?V^+AQ5\%BG\7W[GB\3)BX5SO_CNS`VS-GZHRT/UOAL7//^,\K ME\I/657#[C(O@LF/[V'^9?Y[U4B2@EB5]]I^FZ.JI]XJ?M2,E^GG)@QS'Q9S M8D+Z%F/NX.M!&KWS/;?>G#QW-/>&H,>'A;8?.Y?0CO9A7%MQZPC'?.A-O6L1 MD^DCGGB+/FVFNRWH'//4?(_JA;=H)9@4B'UWE"T1<,_LOT(I6+RTXW=O#63+YW)OPY\\;V+NB8>]JC>F4X7GO=\]"; M.NM"*S6&KT?0J3D%IW."KK[;ROQ87C'PX(/.[$6WUW^D7*Y';?8I MPMQ79>^VFLN:D>^H'GWR7QR\^,EL064J9A7AW]_@PRI%/:N3AP=%JE]ZM^>8 MX.LNMU+!#ZV4>RW;7M M]YMT"!I][-O9Z,BKKLKQ]Y)-G%?01./V!JB->[IWS6S#97NFQ0>^7%#Q]O2J M6)T1:Q9\_)1=UD6F^JW(CADC$IZ8]';J@UH?T=2;0F[AG/$.4P;?.%*F]9+J M=^'PC!=U0X[67CDH*\UU"I^Z-'%OS?[G%?$5,ZWN+?$T\LK0V+=QSPB=S?LT M%DZ).*Z^V>G-P<`OQV1#YG8NVV\TJ=8MRXA:K!(IOGW?J.BXS#K?3[DC@U=K M]-.KW"@62'V[8=K'@GLIB[GH\O(D9,WZ$UN3>J265L4FT_D>\C,23*7[.IMG MKJKWNSEK\,Q)EX/3Q0%5,UQ\K=+E8O)F<4>5E&W2%3`#E!U' M:+\[!,P.B]\]W6[I<4J&XI[WC&MZ'J(']5K3=;Z M*8_'%:M_2.=[Y'I:1DU^P[A?'E6P)P%#7CAZWLS-X7.3!V@5N(6O,]"YLZB[ MK_R8UUO8[IWZU0WZ6%$1?=AA3J?,P=W%IJY3XUVYW@-"@U5\=V[R=]R_J.[= M<'VE+7XS6*O>/+WOI#!ZCV&W28FGUM)[W4&NU05FE8>O.\Z<]"+9BE(=$5;G M)$JS'O,V3BG_U"FYY^<" MC2N8)BG[ M&PTS7:CVJO>TVN,%[H9SI\Z M=W[]UUO'EQ<_U%^"/77(ETX4[%8)+;7>;MQ_!)B>FL!7 M?ZM97),LT]WMH&"XHF8!U]+@G7/AXI5F_'"M)R8:![-W,PJV6LJ6> MGY4E^J+]8?6&UQI9E>SB#>=U7A]?ZRURG],M.RAE^DW91Q>DOHT?O:U'&$-S M@&/ETR?3.[PXS\91/\%CA&;]QPZ,3J M44%3KMJ5G!7D=LP?,UBAE\K',4;A?+:M_MA._:_<2_W@^8J]4OF5[-N+=<\' MK*&_=OZP\>[CQ91=)N=E>VQ6+?;<'1>]<:S5XV0'/UWQM(ROS!.#IWDCO533 M3R4O"JPYAEK\@`A:;OV=*CI?_V-PQHQ+,5CS)9>HS390,C`MZ^F]=G MM>V"FS,-CK.4%@[-?*;B9FP?:7S+A&)=][3CE9/A6(GV7#4WK9Y(O_\YP MU)B,V=VCI)>?^23(3^"%7U)2?)A@:""#Y"X^.V9?'I-*6YZP5%^ZY)R90^R! MGAZC"D9FS=2[>33QUK7"KETZ[.\ELR);=\"NC/#%LI5JSL(S]5V_;%AZK-NE MU\,/)NO8%9[=.ZV>T76![_+%1R(F[\W+]G5X;!GT>9SU8K:;_[;!N#FKL;^?L7WV\^9$1LLYCFM_"U/J0ZI[O^R&3-\KD M[U.(,#+HLK3#VWLKE>HO%MZF./"7%0>+!SQ\$J.W_.+#ZF]VEMB*!.>W7_K= MGU]]3_Q&;=B7.2LTJS8FNNR66W.W2CY?M:+DS!O>.I\2+RTJ9G97\13K:;W\ M(N[B?>_JJC-WS`S-&KIWPXQ=XP8/V)+BY^W^@E,[?WN[0@=UC= MYQY8K]1:O]PI_?JO]T@]H]!%]HB12U2RXQN=PM!U2K:C>[,_'CJ[O^_JW)>; M7^0I];G'4^KAL>MFYTWY8^GGGLEN&WTC]6[?U;MO'-PP[,2>\7'^@@]&@9;U MG?J[V80[!>7[673Z--UC'>?:Z$.W!JXL^'!]F-;5R(4/LON7&AV[X=*GCT>T ML7.0D?/>&F-NZ,F0U<\X"U(OUVSHCYB.V-1A5M2%BDM6G)+7LU13%F^2ET%Z MI55-B4[)OU*YZ,1F99?;>2LU^O?'E,_MZG2Q-^7!AE%/;N][\>)\?DY8Q>S` M3^OT5DYQX:AS-K MZ;6E4C:;S\\WOQ:XR'GHOO4S2[3`0&-4>N'FG#[77,=4F/>?_/":[-F;TKYG MC1Y_D#'>J:/W@--]AOZZ!8^FN:Q>-W+:G."I6[)?R)4Z:BENZ-VIU/25_5+= MG1%#^JP;K'5HF_[ZQ#?Q8P8]8;S=$CQNQ6N=E[739GT*N-G_VP&U`<$7'-;[ MJ3N\[.UE;B(U]TO!$NMW1OF,LQMJM984%`Z=E-!CW#EKD1VE,*1^LO3,0ULT M.YOQ[E+4.X3$^UCLGDFY+[__C6(2_>O(,[OS8Z.Z=`AC"JQFW\:T@IYLVIC! MVQ(P/-Y=_^;HSB')TK+&GH>M[<=C>BLW6/.224,CZO/U3NC6A[P_N*%A`RS M8^//3MT9N2@[+'Y%AEKWURDK^XRH9`9_>^D]/&7;R@Y5EYR[&7?JE*CEZVU: M.\7!('W[R2VE&U8<]!NL-Z3S\/2*;S8^NM11 MME]J7\70OJ`]+&OF#)]>NBC(X?:!D*J>6F/F+)"IS$M^<_/+6KU),T]N_O"Z M>E_>XZ%+`LIJ,],\]=Q>L-\_&.$[P]W/-]+0H#KJ?)6A2_F5M?<> MGN1&YVI/23,7*^<;C]CI.4BEQZ/>:Y75R\OM^^YYO4M-_H;ANDV?+GVSDNJ9 M9C!N?^^-G4MRO*[,?+U!]1[KADYT6<9:YZ7*GFJVZLMZS'#;O"PU`//IM7"J M$O6&=_VV/>CS_L;CNFB9+3Q7."FRGV;ZA9-=S9XK+1K4^Z3CH4W[M6S1JSYG MA]HO[A]8MM)B>?_7H8ESIY@&W+KT3>NYUJ-9PX-M=Z@;KMZYIQ!#CH[=H!!Q M\MH5NR#UH/NZE\8RHAY='?M^`=UPYHG!'_J8?S.Y9G6AW-=HZQ2&Q;OJO@:K M/E?9C#Q&/S=T&-LY,"EFC=K)CG4CN\^VCQ[..U9XN^Y>7,:=F",S=QEO/?>X MIWZ'5>5?\Q9'5&!=YVQZ^6E^XFJN;Z&%^H/^LRTL:B^K['8(69S M9A7X)^F4S(X_8_7:Z<"3^^[F'_6"F9&4-_TOGSU;OM_G1MA<'230.JG11E5MQ>;S]3?:NCSGC3:3V'6[H& M%D3&"3T.YO95-0\L7B5*7+`S8\IHWY$Y5CXGPB^^6M53E8NJ6RPWRJ@;4.9? MG;,L8-"WU2^4[N]80.WQ)M/>G#MBW]:C!U#I-P_''[,I35DU4//`X:?K9(*6 MJ$]YM=W(-U"GKL9G]X?5']X-<;MZ0.?([O(W1D,7>JU*_+@D_WKBB0',JP[K M^\D53'29J]8W*K( MU;Y3.;.RO-29<2=AG<..D164CD?/:CYU77W4H,X-W:T;'_ZIN"1!*^[$;)T3 M*EUV,)?S'PER#@8E4VEY87LG[UM-/7W)P7S_9QS5AUO+\:>O&;-.+%TF,S$(UT=+82O:\<,-D>:QR,!?\E3VX\&9K'] M0JA75YS76SA;;]#6,).TBB'G2S:-S-YGL%J_L]2-/09:27TN+EG0OW#K]+/G M-%;I4NN2SV=O=%\^MW#^>?4KAMB:B=>PY"WOIV8ICUKA\=;7?5H__H=-\]_K M1BL6&G6:J>FLJ[?TPCH]NQ*GC7NU%ITZYI&_9LN$Y19;1O6_^/+5F8B.BWLO M*)\^;UKG6HULDU1CZNN;-?99\DXY.D=Z3KU[^DEN[@?T&*(="ZK'U\>H*'("KJ,!I1\BMG^9$5";.=MCTMV.Z\YNRY8 MN]#7-''%^2E]J9H3.@:4G*SP[[])*J[JT-Q9C'=%Y;O.IJD[;YLQSBV@;^+; M1/KPUSNW8/E/YU*,1JP8_EI;:X*]](ZHQ,(PM^UZ(1OC'M;/BK:-M*64DS_8,\MUO06(>]>K/JA'TW[I[J.C!W"LEGS;O6&)=._OKCT M_HUVU/BSW:]5EW@S+D=%GZV1[3'@587M[B<.W9XD+$S"9CH.R!]S,E.P?-UP MQ9YWJ;Z=NP9>'OIB])ZEVVB7[-,W.GX139X_.H8_MK/'T+T=`I:?5ITZ9(W' M@`%HE([6@?GBTPHV>X;O[&6Z,S1S7;_BL0[>52LYDPT72TU7HH?<-")D/D9%W!KX:6U.6FQ?YJ[C$WI> M>KBDD+^0:N9Q_J&WDY*!S+N\B]>$*7DA']]@QEI:"WCW)G=(S%-:,'UCDD>X MV0+'5>HOAA=?DO\ZI6"LVX/I-;-\$)?YK$>EK_JM\=+]&NMN2-F33'%=*O^X M4U]WP^,AG18E%,GDV'T2[]'W8795\ND3(+<_YMPHK<>R/4\?GI]VHO,DK1== M8A=N"-F4ID?/OZ4^<=_5=U/'[/:>5W?MNKGZFX.M9Z58KWZ5,S6O8G0( MK=2E=M5;KD+:$(,$KT>%AMT,5RY07I.'Z.NI7MQ#[T]?H3'T6%KL66ZIPA>% MJ$VW+K$T5[Y<;;R#KE;AH14R>5V!E+1^24J?`Z:^GFL>NWE9.AM/]QN4,_VP MEF"<_YG"!QTSQ.;[!@V^=#B(OTN6U==Z7Y0W3!BW1B=MW$#C;5U:>,4--,3 M=B:>[.`UNV//Z_L7NNTURW@8>'N#_H*`91G3ACQ9)5G97C&]/G9"]:)'[`F=C2GCAWHN"/QH4+D[/+[PGZ#E!=W MMEIB9QWVCJ[ZPO)@]7M:S*NP`9MCPC3[GXT^3;MZ\81N<,>I]U2/'5^T=/-6 M-;W9.W+G;UVG'V)[-,8Y;?/!I//ZRNWA+@FON?'GZ\>[\Q;$APUTTBDHS MV?E/J^)G6FU^'COK?I'CU/Y<]L32LZ8CCKAL]9G#N3F]V\MS3"_KDWI%KFRO+*K3,_4L3T/CN]4 M!%UC;:W0?C-9A?&H^$K)OCW]UTQ-2IMJ'EUO65X?:_7BQH>]9?75RPR%G#,9 M04-3U/M>8)?NDK&\?W%TY(IHN8K/.G:KIP4SY&Z:/)V?77/BU+;S=YWS%M<= MN?WYR\/7E<)9@5JW4@)W+O*]PA67ED;ZIJ34RZF<\+6NGUUHI3)*:GW2S%-R M5^@)B6>*3FKFSO_@G=2Y<_%KL>^PRWT+5S_V'CPR474TNY/P/GKFY9&Y8_Q\ M:/<$:SH>LC]SU&S%R0HA__/M56=D%CB4?]+QEK[S:".?LRWOWMCLTILFUL.[ M/GYUKNJ94UA*?,5^[-'ETE/(.OF8CW/5CIRGS-KM,>52\2?K^[L?/^A4/&%Z M<9Z:B=]LMUT41[]1[3_/,Z+'_EO>PF*+[YB+UDI.;58=U]ZDH7-_OA'^Y M>\$9S\\O9WZ9S9*7KGRT=TXPQ^#ZCF/[9XSMG?[AZ$@UBXTO-@W=_.5BSZ1[ MB^.,6!45+*Z5S8@@IT&ZYH$NJBJN]9WC-IXMB5NK<.C4S(TGJU\\UG?M,LIS M[86*`5^[==LX>*YELD-1XO(@U2L:U;D'[J!;=672/.[YOSJBL3RQV-;&A.L2 ML+-*/NF+;FEQ'>:='RK8^43O82?9!/6%!NLZI]WIO&KB_L0:+9\QR/%W]U!V M677%KO&S"VFY3I-D<\_W_?RM>EABJ/OMGN/.VYI@O6PZ'C$UM;\UXA/#/6]_ MEP^!VC%+(AW#U+0NWK^FH:_*Z35QV:&%-CE=#,J5)G]%+-"NUU>GSJ@X.-MW MH>K"OC'77`Z]N*+A*[P4'YL8_\;G_&[.3$T==?&VXP^^]G[U<4:%TJUZKU=? M#45+2[;?/XW*=W7I>R?<=FS=M^.I%LK:%IVW?AT7T#'@3D)"_B1SYOP!]9\" MYY5O?EVR47_IB*W''G6+4E=P2#V[[_&S].PXXWPIVCN>W5=1[*[-9S+.C%M6 MOSA\Z@9%[9Q7<=YK^B;M>#=;L^OTBO(5)^IF>JW=6[Y8\[VWC6D^:U(&0R^W M<\S-):FF70L>;*O16&,?MXCYU+7FB@OE3;])@P9*Q\F>F.!:)Z-M:?985V8R=U3=FFHO3LY?#G4>>RR^*YT96A1E,VFXX+ MSN#+)[\<5%O[BL/[G+)[\I`%-(Y7UTO<#X=NSKJ]5'-!AN/CB`"S;X>3BZO5 M%#,GCU3;S$[;6%T=*Y+2-C<52>>I11Q6FE.O.V_?*T/%P\+D%Y4#MBRB+;KC M67#+2?;&@6QGTP?"*-N(VMS0&)E;C_4.9AW';M[18:93;6=DK8Y,=Q@[[BM/ M36K5'F?.FT5?XG@?IY159QTN.7.]M/JEV_3W,>4;)GP)/S"A+OG*>CW[)!\W MQQN965_WKCPW^GF%N?VB60]*7:?RO[D8ZKA^EA>DL?H/CSHB]VR'2Y5#L7"R M8\Y'EX\]8^)'IG^J4CN^YKA*["PI\V2QE7Y8;/[MS=?]9GN;%O=B?^PT7[>\ MN,.::4D^B<[=7#A#WI>5WM0)7R-DJ;N=Q`2(K5;7<8L=4M=MJ$L:'G'D@\S& MK9R=F1O+%AP?._U8W+?:AY5KU,-K>UFIOYGU0IV6*7WRJ[7&E)QYPV.6U:[= M,[O[U-M]KK`G\3OVBEC7Y?VL'K5O5:>/5JM/I:>FYET=UR5FQS=X[/]8.JXD"O'-(9T&F<_KFZJC"]SAW=6>>:UJRHY(?G! MDR??*G'N8/KFA>Y5[MC7(Q9[+K#M=<7+:\#E=;DV91'F[X]/F8V6UG%5*A?JC5(^ITX.LWANR]]G'B#[MDQ)'T% MZ_+BO,T1]GVY7Y7WKSQ]-3#)8MX]<3GGU=:*,P75%SSS4FHSYP6]=+_#N[RF M^J5K]R.QTV\.G%W8^^6FM^JB*S-YEV=?#,K8>6AJ3H&M?M8BG]UHWPOI;\?F MW$F\>_!I4J=/4N^_#>SP9<^H7NWY?QP^D\'Y%XZ!^0?G/Y@"(#G_X1?[_Z3\ M0]A_P/D?EA+Y_T;Y>PR=X.GLY^?DZNPWX'?(W]+,PDPB_S]#_GA$T(3+_]?6 M_YM:6-*_D[^5M:FY)/[W*SYW-&560MB=_/L,_O3!WZT.Q/M(*0J%`V`4@``%)E"#H&,D2Z%X`T`"<#B,!\`/8%\#R` M<$)22T(-60H%%DT'T`I`,"J%TA_` M"!*N`G`H@">I!%^R@(@=@'9*1'HL@,,`S`5P$(!2R@2T4R;J'4W"+0"")J*4 M`-@9E'<=0&T`GZL0[2BG1J&$`H@`"`4S%T`O`'L`YJ$^<-2)^J>J$W3.J!/Y M+@#8!\!'`/:$[:]!U!]^8+V[@3\94L;P`ZJ-MPO\:))0!_R!ZN/MUQ76`?RI MD>]@6P,14%0I1'GP`]M1D4*T.Z@&SD]'\IT&Y`'\R3?K:UW@%X+0PHP;#HQI M_+`:CQC!A$CC63+P+`@JR!!MW.Q8F989&E*;'3G4E!46)H)YFYUT0.;'CT)H MEMJX,YD@`TOEP9S?'9E!YOP^M8W<;)B[(7VPO_-H_\9R\0=R/][WQS50*?KP M&)=)@#<$[G0&=;%!](5$,L;AH&S`+'E6`XV&Z#/)-[QH!@=CM?&&3)FHS]07 M]L-/\^#U$\'C0^"Q#*"M!&PQ/+WC.V2`"L^6X(9@;#%?+&S]5H!&B3$!*FR? M!HWV-PJDC4+^/G:['+;%7/,V$?/@.5=L'A:'LEHVT`_>X_6A4OI1C&@M/T;4 M*8@;'"2-D1B^@,/21:906^-06FX3UA?^U7YCO(`Q`'$-Y+'BN$Q,,HGPN%H>?_M+0HT$^7_R@ M('AF$'Z.E)V^T!ZD^@MB81]"&HY9:=Q:V^+8,$H`+X+'C^&1^V$15"#@"R@! M4$>@0B!CO'W\W;V]QIF8`-R)E+!F+%(I7)0+SQ)!)X4Q`&LHBT+L;R5H^`CX MX2A31'-GT#*&(UG#*$5]@@SCQ1*#K!6,\?C2&1B-V<>2_'(E][9`+>VK# M84DT?Y3!M4$:#ZE"[%@84^08*F300M@$JJ>[IW-3I>DFIM2A?+B/'I0:&PG8 M@N=>#0!V%>/9PEW]`B$J&AS@[T(;V(0'N0M%!31G'I,/-]O;(`-#2%/;AETO M.E)TN.A`45;1H:+,H@RD>&91!D@X@A3/*,HKC@,PMGEUT'-`Z5I11/*?H2/$2G!0"D0"M&3`%%MMF&3\8(/"" M\HJ.%^7@!4+><88S`4%0#'B1"_Z#Q34K?R99-LQWN"B3J.$/QI+6A?P%+02\ M!B]R0=J!HN/$RZ-X_BS(35%6ZZ&G**UH95%*T::BM#:KU>PU%`5HJN/?E0DJ M.1.0SX&E`L$5SP/[+9X%6 M!JP6SVR7!D$!SW\`SWT,O)Y!MG7&7V2#[7$$+_'`WR@3#C+_*=_XZ/A_1^1O MU?\OJMU:OGA'R\7[70OI-LGP)[')EJ-2P!>E$.!%#!_:%?H%%!`B_$& MH`%V#D%!%B_&!9K[G;DT)B2035A2**Q#A`YE0+)9N,`AKP`;8$&V(`.;&LUJ M1G-9$B,`CGH0)W\,&ET$?\@"MA:7(JP5,;E2+EV#(V`]HYX`>E=$@>-AY M(-$LO'$R20JM9@M$Y5OW,-S(D4,];B(:QRG8">827;C5L`S(I;;28;S#$(([ M1DBGB?F,UN6#[DQ,_HK6%VT@I$],``%+E&8C5@LA@H+S\);+`HT^D^AKN$+- MQ4>H/*)?4)J73#0/.6'(@RS^+ZS_(^-_3,:?$/\WE<1_?Y_\?W_\WUSR^\\? M(O]_._YO:F%*_U[^5E;6II+X_Z^,_VN2L>$]%")^;T<&CS,Z$/'__1V(N&]/ M*0(.E"+B^KY2!/YT*2+>GTJFGY(BXMVOI(AX?T]I(J[N($W$^4-).$>:B*=O MDR;BT?#W`QAKKB;Q.\@0Z?!WA`C`@Q6`4>`Y39:(:7>2(_@9`&`_&,>7(]*C MY`A^%I+I!P`T`/"F',&OM#P1+]>5)YY=Y(GX-PM`\)'_+M9.(>/K"F0LGD+&XY7(V+DJ&;>G MD''[ACC\KXV?2X+.?T;0^1^&G%O&;?^C:&S+^.J/0JBF)I:FORB$:D8SM:31 MK<`_;4RM73W]6P=0A\.;#I!`!@?T4R<$BA9@)#Q6U"I\.98@8 M'!@\93(<67P62TA@_9/(J;N?-VW@0,M!-/I?AD]'TX@C(G&F1PQAA*`X%5_`.]:5XL`V%$B5$PC"&,<#&+@6"(4`P/V&];\?\Z'^B-H/\QL3J$ MQ\=_#4"%C);*3;Z+O@>T'[YJ^8;4^H;,\+325BI%X`&LJ@9U8K1ZQ4/AD>\8 MX*\]`H2V_V5!_4C5_6N.2$2T!5/D2ZC?J`#%)C7/W:KR+%3(Y/-0-FS0YMK= MUMMV-9O/81@CW#I>>VK=^)[4:51,:#6C0:F!?D8S6J@UO$N`"QJ;U&K`#%3J M!D*28SG_5S]MS/]9?X3_3Y?X?[]/_G^`_R^Y_^T/D?^_[_\#V;?V_ZTE_K_$ M_V_I__N1_C\&GN?+$O[Z&Q*JRQ'E&>!7QP#,:'1+A&YF0[=H9QG5")3#0OPP+A_ZO8A=!'AT9$6(Q6P35D2K`,`P M!@\3AB%V+)`_PC$"='`+-<^#4*W?[9$ M4EW_)GIS'K]#:0Z=NE"?^[ M5)J(`TB1^_YZD_O]AI,P7(:(`\PG_?V3,@1/5TC\1S+D/C-9XO=X77(_X$`` MPP!O+@`&4XA]?^70_Y8GZO.0A!T4"#YZ`J@+H`>`1@#RR/04\OFL`A$W>*-` M[/O3!DY\;Q@?(&$8@#%P78$BX??+@,D/7!RH0>X33*(2_!VG$OO=KE$)O!HJ M$2]HON].IEG,0(F,&31\8%R@(RD7^.E$Q@L4F\425,G8@R89']`@TV6;Q0XD M^^U^9K^=),8AB7'\O1UG?R\>0C<99/[KMI69TNC6"-W:QM2RO7A(RRMI843$ M40@TQ82%M@J'N**@@7B(G8C,#PL3@3[ MS(9A*%!S-XP3BH*>&8=BP%2T8\?(^U\;PR*-Z#^P7?X-*4!,H.^/`M8B@L\% M?5](]!YH%&.`CT\4VHX9:\I%9H(-7QF_WNO4%F:8$-Y$[-J(#A?5MZ#9CG$C M#!NG!4M&S>2EY`FP""((+!XS4U8T.C_1B'J!R54KER1>7*I3_] M_VIJY9*]1 MPF*'H'AK(GZXS0[%F:?XQ?)$C$EM&.VFBDIB5C_P_R8,<_Z_#`']??_?`OQ# MXO__5OG_'X:`_D'\QXHNB?_\.?+_#T-`?Q'_,;>VM/A._M9F5I+SGWYI_$># MC/]LH1!Q"V,R_I-(QG_FD>L_ZDFH3J[SZ$^N_QA%KO\0D^EKR?4?9\GU'_52 M1!RG"[GNPYZ$X\EU']/(=1\+R74?:63Z.0#=0)DW`!P+PQFR!)]329@B2Y1S M6):(ZSR2)>(X\N2Z#WA.%'QFD.M!%LD1<1JX'P3&OVF!!MW$W,3,ZEK`^'WG2737W0@_"X=TN]S)_V^<"G"WUM&POU2A']43,*[4L3O^#52!!^! M,@04RA!^V%+R/.$<&6+]_GTR78;T]P;($OO[Q\@2Z_1GRQ)^W1X2%LD2OM<; M$LJ2YP,KD'Y:PUFU2LU^6V_XR)%^GBSIMTF3OES#;_CX1^)2_8DNU<]N6\<] M(OHO\HCH-%-S>08FIE8VX!/"*;MEPBN%Q=B(Q&>6!BQP$30L3.SZ3IR5$0 M%L)CFC"8)N+6*]A=!2@:\2/\_V@MN_5/K&6'0GK^!+G]X-G;VB?(HU=5U2\? MW&DNX1^^;$HDM>+AHQKDX>UWC][UMZM>W[_S?W6J`#QT=T:@/*PJOYE M[OY]2_T%#JDBIQY5W4?J7KXU1IY]KGO^2)?RX%'5 MK68)DM_&_J=__T/Y?X#_9RTY_^TWRO\/\/\D_O\?(O]_W?^S`F)O[?]92/R_ M7^G_=2/]O^<4PL\Z1O[8Y4/>_S*2O/_E.+G^6T"N_UY(KO/>1J[_ODJN_ZXE MT[N0][X,(^]]$9#KN9/(==]'27A3AO#'/I+KP#_+$/>^F)/KO%_($K\/?I8E M\FN2][T,(N][\2+].92\[X5-WO4JF:U()_]*22K1A$`EAN!7ZB4NH MQ#TQ<%UX/O1#J<1O@TE*Q!TKFY4(?_68$E&?ATI$/3\H$?G5E`G\CLJ$7]Y; MF?!5K96;?H^$[R&MSJ0<&WZ?;-B7WK`N7)OT<6$Y6J0/V]S/E2%]X2[D,Y72 MM`Y=B?1[&_:OJY.^L-SW'4JRWERRWEQRD2.NONZ>'LY!B'.@DY@"ABX&?X\9N;'QZ*&^LVWA`)Y"$)/+3A M&$5AHQ5KH-^`B)\Z*`((D1PQ$!N/R6CU/@2-`ZW5+@GR,,2_+JOA-,2?QOP! M8ZUY:MD^$>!M8\LU/Q3A^U>D4:Z,3VUEE@G#[`=E`VTJ$"N+WY9IQHWS"'XD M)F"$`[WZ.>/L"W`Q-L;A_R/S7)`(U8)/MA`'"P&=L.'@#;X)4'*8[.7LY>Z$ MN#I!(^WN;0PZ6N%>V-L`$HN/<-""U$9)B!C`6GY74WC,`[!U8H2)Q36480MRB>M-:@CIV`>*HC$"E(Q(448(<`K*^!3))__W_Q_X9\0_[.0Q']^ MG_Q_?_S/2G+^YQ\B_W\__F=I9MDJ_F"@`7 MP#,*1/QKL2(14]NF2,348-P/]L>;BL3YDDI4@H_^5"+='T`Z@`E48A_"8?(\ MB'(J45\9)0+J*Q%\N2@1]7-3(NHW78F(U]DH$^M97`%T@_52)LHYIDRTZV-E MHIUD5`@Z?52(^O15(>*`9BI$C&Z(BN3^9\G]SY*PH21L*+G_^>\'&'_M'=!T M&&`TI[<38/1C\$08@\U'`C$.`QG&YS%1#F(G9/"BP;.C&)@B$U38*K[H%TD> M8XH*_Z-M,O\'1YARX24CP)P15XW$PKLO8I%0#%A=T&/:BQ^2>9JNJP`=@1\" M.@EH`QP5)(#A`O95'N.'AEZ$P7(0T&A$[^(C+!2/]+'X#>%&B/XCB_\C$MS[ M0AB`1%E\(;]MN^_=G`TB@1]+XRL.H^%MTZ4L#!87Z@(/L/>QR3SR6V=H=1%*J]>XG846 M1?PC0HVWM/QLR4W7M?S-'.VSVR:G;30B*!.:?AZ?B;5U@4LK!')0R&ES4-A$ M7M0B;B=@^4\'!2;&^$>C@C.PTK"-&@8$&+"$PX(7'PD#4RDO=R_7`*^&2.4E M)V.0"E66Q>>Q4')8P/6/@\8"FXO7CJP_Q2C(6C+ M:VX8@`FHDLT,!\!TAO0`-0X9`F60-_%`*?(IGGP6KM$H-Y*#-HN)7B5'E]WX M13E$5>[' M97FBG`IY@LX;,EU7=+D*'V^D0^D`;4[-V(BA#42$P M\6XFB"^?P\*O=H4)CF%B-E^`IY@`?]R$(6@51,%'0L3.`?_\ZW?`_IWP24/@ MX\?1$TZS``IXR6J*GO!;1$\:U_.T%U[`#02YUJEU>('?(KS`1"-;7MG*;XW< MOJ_._^G0`K]%:.$G2FT**X3]#?3V^?RIF`+_KV(*_/9B"D;M7@S;3CRAZ698 MPB0],#F*1'O=)?<)?B;W#W:G M$M"*O._%AUP'Y`O@<0"G4HG8@)T2L9_0E8212D3Y:\C]?QGD.J'32@3^626B M7E>5B/C`?27)_C_)_C])3$6RD.=_8?_?[[I'Q\P*WBML;F9D:MY68,F?S^<" M/?<#`$7LB"<3_,F1BS$%?`[&BS!!T38V_XGX/+A7S0X5_4>K")G3\5F4ROB,.88!.+D:BWXJPYK8F&F,S>&CK9/A,&F44P[,WFBJL M!0I``,P+44CF^Q>\MV)&2-OY"//[0](-H:H?X#0BM<7"=Z6WN.\9&+.FYFAN M6[][TZ95A1;5'UJJB+=8Q'?6%%A2)S%0(^PM,/S"G[:E(B%@4?B/C*D?(`U/ M#6:`-@*3_HAHAH!A@K@RX#Y<#$,BQ6*6.`2\!AX!&(1%H)T8+-@WV`RBNXC$ M+"02PP08"\47LC363)?BP^"(>:`[,41`R_&NPD*%8/ACL,#H)<0X:(-U]<"$ M#%Q9A`RX0@;T.[A?D`]RM%C(`H@W-*_P,V`!Y6(X5',8 M0@8;O,.`KX#&3N"A$2C/$14+(QB<-H_G&<(01HG!I!UX\.))-%3L"#QN'@,U M87`BP3]0D0DS[-_UV'_@L',8;#%/%,=`PAAB!/C$$6+@1"/0[<8$\!Y9E(U! MP&IO=4@(*H!>-T.``8D(Q'$1_`99QX'4'U,D7&24C<8!)YP7R0=Y&>((>.I) M\V4A?_&>-`O$BP@$X++$N,JP,.`.1@#?A2$0Q37<7#NQ"1>H%JYYC$96)C8G M1))`0AB@EZ.@P0%=$HVP"S];(JGJ_PB]-8\_8J]YLY#YT3@T#DJXY5*1UB]_ M8!I&,+!)?&+1AK@=T]`"A0)4E`8XA"8!%!*"L8!=QT^BP:`'VMP\T`1H!,9K MUF$0-AH&MZ>!&O)!EHAF="7K1?XKY_^AC#_`_[>4^'^_4?Z_W_^WD)S__8?( M_]]?_T$W;WW^LZGD_!>)__^=_\\C_7]X3LM666)_!X?"QA`TC,5@(<["4$98")]% MGKCK&((GM^G^^\!*P=_G6YW-&\KXM6?S_B`.@)1MKUA8/@QK?9)8GENUK MQT:`EPD0O7PNP*Q8",D>+$LOG_L/"\%[^7[S%!>=L!7G;9KK(=9?M_A)F_&[_<-02*L!F1T#LE0^?&R M#%AL>TP!`C`[R+8#-&P":&A0\5T_Q/V^J-FP.C#K/KPMVBT(OV+V/^&4N";W M_X+"7U3W+ZOX7*I3_W_VJ` M3ZU; M$Q=+)@(5$_2`?2:-I"F0#X+:]K+#N""SP=\N0`P@9L(B\'[=<,EP#ED4U+]= M97MQ\=_<63X'I]IV_26QG_]>_P_[$^Y_EL1_?J/\_X#[OR3QGS]$_O]Z_,>: M;D9O??^7)/[S2^,__XOG_XK)\W^A7#$%"N4DK)\"D7^C(L'G81*6D?=^?5`D MRNM-)=:/#"?/`1:0^WS6D<]GJ,2^GEHJ4:]N2@2T(>_U"@;P#(#C`;P$8%]E M(N;S1IF(3TD#IN=3B/-\(;U<%:)=GZ@0Y_3*JA)T=%6)?/JJ1#TMR.!J-?>O8OW0HQ@_>_&YF:M;W!B,O%D$`&QL,BP!?* M@[N,0)))LR1'+`(S"<5:Q3!=,!YQ`G#K&";VQ\0P>:>VQ(I$I[:1?$XTRFN+0AM;D-Q;4B#2FI4?(41! M!_UJU=033FP&N2)3HL` M$Y+,"R3_BDN4@D\LG0_`%]8-B5\W/$#D`M(K^HFF(O(AD``'[;?+DJBX;5:H M$IF%XT%JO(4N.^:B*> M-(E>97;_,+>,GQ-XIX6'M0A&U%I`)KF+YQ7_^$.(S09C3S`IT2XXK3@7EU>7 M)4V,VS?GB7USIES@_UF0[%$\>)4G<':>%VUV_@8.@B!XSH7G-E>]=JU?U*RE M"V:ORR5>W-I%.H0>U'+P/*$'-F),.(D;CSTEJ(+0ESF`@B$"\58W M'@Y$(A5'9@H`NGC.R>?*^$+/;:DZ1C?>;9/1ME8!$H`\+`%G"NJPR59>P;-@ M`9R&U"-P%0'<0AK#+-G.R2<>D47$C!R'+'<>-@"@B2)"-& M^Y+%Y0$<;.?L"`YM1'+,8ULN@%C3PV1`&$1!D7-Y-J5H638;CRC(R&TWY9(Q. MUQW<$-H7W2-Y;:>[HCEM[S)L52&>?+VOD_3>BHZ?]% MT/]+3NJ=W&(*@,G]$I/3N.1^_9.3$Y-[AS\$:(0BN;%RMJCP(N&SG.P]8_C8 MT4D3LO"Z>H&W)0T>&"I'IT1FM3B,%\`(%0),GD2`H M)0B!>(/#"/.[$(ZH^"A+*#!>4X_X#A--D=T*H8.R'68%O.WUWU@/:3B'S>WB M3#>IZ(NK%Y?#PPSN3T>5S??526'*2=(4RW[J_;^Y39S_T_C_5AS_-G#^3^/_ MV\CX__3WO_9W:> MMX*+],C#[#W_CMW[_L-I_->#V!_?S>-K/-UY&X?[$[G>?P/QW7T9E!G9V M__M?M?O?-?TO312CZ7]I^E]M6O^K-V?HW3^M7P3!U0A()7'CC+PD0E7FBD\4&^A0412C=EF M6X#[YUR"S/%H"MAEE$5G!+H?2"K(>*E5#B_#NTW@S8CK@=1-D/YL:`UWQM4` MY!(RR!:@1WDCH"5L`WDTCLQ+B=))5Q,K0*0,)#>?"-6!'(R6,$O`X&%T":`I MB==.R\RV^/)@9`SOY880#AS1)006`*=LMO"P^O!VBQ'2F'@1$5=%V50`V*FV M8^I(Z1A'4QU3!"S8:3Z)F"QRDDGD19.%DWB6@9H8CF_`*OK"D4A[LRW.8S*T MD>I`-?AS,@FQ^"<92 MW5TA<1'7#?7Z,43D.9-;A!D+KD6VP]A)$>5OC=>3YJAD#8:N.R/Q%VSA@ M%2\[LRVR&?H>=B9>1%983T8=LP$V.!LDA><&CN4\_#)H.94*?HTB:!PR! M[C7ZIRITL-,+N&ZR'0.R';;5N+X`66D0;2K4,B*%,(EG(%"4$M&$-DX&AV)! M?:YQ0`HXQ0T0HLMX#$A"MD7!A)#*+9I]R\X@Q%K%":-J0[15KT``!U%NW%[Q M,.1(67!OP>58H!9X?3PTFF"[?U2-EI-.@-'M05RFZ]'8T4.'#?0M1^%:%J/P!W$KC[8J@\;U`LE7LIFOZ/ M)G32]'_"B%!:5O+N,M]G3 MV^`?QMO@!6`@CD62ES!P_Y7>)R7.)6?#\#(5(/"S]/X;R4_BC>02E]?0^+KW M)B-Y7[3_*GA``Y,=Q6ZAUZRK@`'4&WS#>G"D`[!"="$WU40NOGO@FU6D_Q[X M"X&.5,>PU0O3::I>033_4L"[WH,A`HU00X2Y!KYT]'FN@5<#Z(:3H0?66W81 M`:@+[YMW.&6SD[?S_DOAPT#1&]IP[D/M``[2/`Q(+Z,*G$N2?8>0,H%W%;/Y MT,ODY6Q4!K(WH"@Z,/T@A7:C?)O>_UN$ML#_:_H?K3C^;4#_1[O_K8V,?PO< M_P5S0]/_T?C_\_'_(QG__S?P%W6@^BKMHVG^U[/[O3(8WR\S=RX+7P4P>Y`X>X`Z/-P=050%*$IH/3M&\614.@>W?J*9-]17KF0,'#AXZKN[Z$#C:@#!P/<.J M$MQP[.C!KPYP1X\Y\[`K#H:1#^^\WX+(0>^ M/!%2A,;L_TSV_T[M_N]?-/_GU.[_UL:_M;__:_=_:_R_+OS]7U-U]/XO/--R M4S1U![%S01.9_8^_L^_]BUCX1]&4?X]B]C]ZL7O`[F3?_:`SC^Z^)I?D,B:7ADL;_:_R_QO^W.O^?EIB:S!D,_7NG]\S*''_+D''CN1X3 MQ@_NF7)S.#,@3A=OXL;+)MDIY_%&;H!=H>^3^J1G%/`660ZK$3#8*4,%42/` M)EMAQA?R1C$7[P&3W`\G69R7J`F0N%%13##7U'8V*T40";7'_[VY]_K]WLO;] MOQ7'OPWP_YK\IXV,_T_/_Z<:4C3[GQK_?U[^W\[X?ROXRSK07'\_O\B\OFQ M5,XP+Y;R^^]I_+_&_VO\?QO0_T].1Q,*O?M&T/_/M`LN@1N!_+Z+&R"@#TJ7 M[4*2Q1VJ`."6S+PS_,W?%O>/:43ATME__`EVF]5Z%\[HHYW\RMT&-\0W06,[*.XPNV792/8^]SCRO5*`( M-D$*&^_G]?V1$K"L.;*9!ZIB\Z.&VRZSXAX,`1;,!5XG7E#Q?2!+-8PZ#TXT M0W5\UDO]['YS"P^P\I>8(K3.358WB(ZZ[()34-0=VC^(-(0':((V<.,*19Z[ M2[0UF&_@PM,&%81NH']*6,3L!A&-NUA/Y0D*3*UO..,Q.U['A..M6(!G-_5G MI&,@T@K3,:7!Y8+E@54,D,X%4ZK0B7,+TE@%S"WN`M`)"P3RD]?@S'5;4;K@ M/)G=8#-Q02<,$@4%HES0AEY<'C9"DR3\:/M_T:3I__^2^3\8?TW_7QO_EK/_ M&>[[?W*:QO^W)/__&\;_']51/KZ2\?]CF/W/.YG]SS6,_W/GO+39_24_SZKIW8_4YG=S^,= MJ%S@;`>:_E?,WF,]W)P MGV'W/6=UI.<)3!VI7L"4CC3_=]B]SS4L_'A'>H[@NCAJSS*+W?N,GS)^!^XL MYKX31^NSG=D%K8^C[8GI1.NWOA,MM[X3[9_#G6BZ*^/I6*3%T_2CXRG\G?%4 MSC`IGMK8M,4'['NB;`#M=5[)QE'72"YQ.7.O87((;->OF?RALVHNZ)D,XBJ5 M#`/E$U>4)I]SV;8]]1D*IHAS]8WY/DS,\^92,QS&EK, M/&=R>F)*7WCM;TCIF=PW[/7,@DF$&>,4"W@\]3\`FB).LOC\36B4#)=,LB2X MP@N:1%/+7\]\3^)02NV(U=%!?+9@@_P-AJ24R#8\[0[19D7KE=!8-X\D6N10 MQ<$JV-Q\9/N=OF1L.KE$2N/529N@ZV:WQ&-J.A4YMY%W\!*E]"X"UP1E#Y^6 M@D`%LGFGNPG*[DM-`EQ"-F_F1<`Q%P31G'R4"[*BJC+!U!N"%='$6SE*X!O' M,;I.00!!LF6;8,'R>)-?!R4('&DW+QG=C0,QK5MQD[1033=O"9><$O5FE\8( M]87"-ZY@Y+I%Z"N3:!5P\-5$/5RT1M,931_)`W$D6.DV`SKYB/I@N9&%#C?@ MK%6%@S8A6[1PN;S;0JXX=O$VWH*#)!+J'M3;NC&"'3`!B%T=1O8OH_C[+P-YG]CT^8O9!.3/^G%SL/ M=#=S_\KT?U`/",O].(;*U@ZQ\T+G6'B2IO^CR:HT_9\0_1]#4FI2:M\6M?]A MX`PI_0V1]'^R>*=1YDPW#5>`#QE@-[%C.Z(2*I118#83B8R#"6`"H*UCZ],N MN_`>J(?<@@L&C!?=\!(C+MZ(J'" MC2I73>/F_]/]?VZ;N/]7N_^C%<>_#>C_:/=_M)'Q;P'['WW"Z/]HYW\T_K\1 M_S^"\?]YR-=WH.F_8^XUS-Y'.CL/A/H_:+?39P?D'78.Z#_1]'[>SLRNIX&Y M]S)W*K@CP%T40_5DO@'W?N3G8^D]OSFQ5!_F18W_U_A_C?]7\?_X);35;(#V M(S*`])[)Z?W#"0&&#Q\RD+M7=HF\5>0&B,#*1K0!>@=^RLVKX>VS MLI414'M!/299<'C!R34GMIR,D)1,N=K=5$>*W MLMS>K'WS_8_"PU%\;VZI#(7+ZDKG76""2#7UE?S^1\L::,^J^V?3JY6+(=5; M95O*RL+TE3^_2'"L=03NM;=?>S/,OW?B7GNKJFS)PC)/Z3R8,R>AT]9!^UY_ M[:VXUQ:'35$>IPN70+>@?D%YV5<5;Y350'^L+?,2`@&#L.7DRA,X?*:LJ M6U965CH_;N/9K9]6'"W;7G%LVS=E=6I,"0^O4Q.8LDU0L*?L\S6?KOIWV18Z M16N6EOT;4OPW.%5H-3690POL_ZUM@?\'ED#C_UIM_-L`_]];&_^V,?X__?F? M<-__#=KY'XW_;\3_CV7\_]7@%@/CG0GA\X!Y_A.XGFA:SQ[`:-\![M/@S@!W M;PP-%X'1MH/[(;A9X`X!YOPQ<)_J2._[N`:V%OW!O2N.\OM/QM%V'`/W47!_ M`XRY`]O'SNMTCM?X?XW_U_C_4/L?AJ26.J*1BJQ_2F_.D$JN_TP+Q_F/(;!S?^(Z$-SPPN&@-5,%#`FZ2D5RX@/2-GD:&Q%UX@[9_KJ^B+OEK^R]\C MF-3?7MHN`'F=_'W!W^0'FRYJKC^'^<3_G*];GO9WBZKW2@GD6R20#L.L\Q9` M4CU+,IS;G*8Q('6S0MI$R7,+=2`CV&VEM$L?L1N-+3Q>I*JODY0L39/MP^$- MK&<7D3B",5M<[7P]\KR?=M`&EI+?%W_;/^1-#H5U7C\***H)&.8FLS2?Z<;WB?9)FH M5VO?R-%589ZJAL_2UM'FOQ/D_\%R_^RF/S/`OYI':@-FV,=Z/D#V!=<20_5\7F'A2V-H^MWL M_$^4IO^CR?\T^5]8_9]6.0.4DMP_+:5G!@"R^9\V69&UO@Y@88\YT% M[@R3D"WR4E@5H!'0?"+XDR]9`6C$V"8E?XE-B/ZJMG'+EJW]JF)UU6JN>M^* M]9OJRH]Q%JPY.0Z99U7\K3B_; MS*VMK=RDGIVKME=]7_6_33M"(A@.8W#5*JYJ>^6I\E)NTY:U!S;5K3O%K=GR M7E4@;T32I>NYBA.;Y[.O_9(9.#(E5.7 MI8Y5=T75]O=J-NWD*B'STY`3Z2DU!H>-;P*#UVQ>\$/J5IY:_!$T]4;7BNT]JBI6;+^96[-UQ3INW8<(!KBZ]!NN8B5T M"9L(JRI6K*OP!B'YIKKWMKQ7O;8JB1;@RUICZ37^'YR\MF#_0^/_6W'\6Y__ M[ZOQ_VUD_']R_C^E;Y]0_9\T3?^G1?G_SHS_?T)'^=ZS+'X8X_N',]<81?G@ MR5&4SWX]BO+]FUGX\2C*]_^6\?W#VU$^/K<=Y:.?9.[B=I3?W<'<_X+[9TA[ MC,6/U-/ZV,#M`6ZQGO+K[^HI7_\A"_].3^U\=&-V@4=VH/$N9A?D&>8N[4#Y MZ9W,W=^!\N^QC.^.8SQZ)]9NM5W<:,;C=V`\?7O&N_MX<_+36.6VP2H'\[P7 MRK:FI+8$VVI(3T]/3.Y+KJY-ZV_HR_5,3@E[;,56P',C!,4)/3E`!$^&V20D M*7((QSJ25_(:&Q-U9=CRDJ"?E4LP'CI\W&AFPB*U&7PLN?9$<=D.FYFE"$[B M\R#D"]ZM\$G!\2S2`>N.6-@XLAN.=3=.$ARB`(ES`S>HA``B&.23)PJ2)!<> MMQ\W\^%@'$[>Q4?.!N=.L\HC,_N"(,]7.6.DRDE"H2CML_.->B5B+&D#Q([+ M$T0KWPNZUL6[;<(-.E=P0-SYUG\INRWH_VOGOUMK_P?CW_K[_]Z:_<LS MFX6O9/=^-C#[`+',WE\/9M\OB[DYX*9B>V+HGG])#&UO%;,7N(>%=]"^_VE, MC?;]+\+Y_Y8[`Y"&G_\`+B6U?^_T2%>`BGDB3.GQO!EX&@LW0"!^);.3[/97,K!/OO"L5^3(C*7\_E84O9/J_.YE>\#DF M'^@60^\1&,9<$[CIX$Z)H3SX*G!Y<&L9_]\SEH;?I?'_&O^O\?]ASO^WN.V_ M%,Z0WC^E7P3F?WB>S(T7[<#TV"$0N/@\N2G=WR%NQ6CA!@!:X&T`DCE#LK%K M`"Y:#_A'X//-@I`#C'XA\/EN"`<6/UOX8NU[HH2\:7@$)TE(D)#H'TJA45(Z M?R6S8`.?0&9=\!4`3421`(;AL(;R"A89F/KPJLA","Q%'Y,[.UNP%8J2Y*]! M(!YJ+(@NA5-G%01$,;QY!3*4O2#@)BH8KF[!O94M6`7))`1U3H2X2,>="6KS M%-F@O/"X39`["$B7!7\!>=W8-%$";%0XAU,V.WD[?B1&W0#`]UOC[,%@)M(J M.M4I5(293P0*MYX![L?H'\L M#<_6^'^-_]?X_[9R_C>%,_3M;TB.(`,8D2O@A]'Q4)I3M'/#9+M=X`982:@% M#_E*P"HUK04@%4BRTV7U:0%(;5@+0`BO!I`77@V`,8E,&R"\!H`020-`:(8& M@&#%K^VY>+>=58[T`3_PP5V,J`:@.`5@4@7Q?%H`_@(C@3$U@&95+$@9(&S= M0JOUHZD#$#P/*`3PD0X)JR!4"@%L9!OI!=R.7A'G"T=FM,#YIHL5ICF*!P![ M*'P34]VG7Q"J6R!&5"[@-4'`SV+_[[!I_/\OF?]SV#3^7QO_%K3_G]8W]/M_ MFD'C_UN2_[^6/7CN%_GB3QG_[VA'^?^'P%V"\.VHW:VGVE,YP"+F5K>G_#7R M[R@/B-/3?)!OQ_!1>BH/^(N>R@,6Z2E?OD5/^?4OF7LY.Y=KZ$#Y\%1P5X&; MVX'RQS=&T_.^J.^/-H^'UQ M5#]@6AR5$[P#[@W@?L3@OF/N-9UHO=([43V#6\'=C=.P$^7Y[XC7Z9#DWAM/ M\RV.IW*-)?&TO?^-IW**;]C]`YTO8[*"RZCY+G"F MVO>[JL7E%Y!6P;2*DS>)&,?;6'JW"S:^JE"S4P!N$#;R-!LL5<*4$K!LB69! M$IPT;YJR<6B8U&9,[0N_;7SF/>/]Y1(/#TRX10BDM`LN%S">E#F`[)T\)SL@ M?\G4'S@031335D0Q-UVL,&:P["APBF:+PO48?#/4G[L=AIX;)^,M`DIV7-RHT=S=`\>.'3AJ_+V] M4!B$4Q$F'?833#J[J"C0\NP"SL;G)S66(XUQB@#&0^:B()EL!;TX(RRBLETL MY+-M@1D-Z2Y)XC1!LDIROL2Y"H#[M`,'[I2=>%^]64"$X.X?/6;\\-&C)B8E M`>R#C>13=@$R+(`667BHFF#2Y3M%16!YG/_P2GJ?%A)>4;@^*+Q*CB2\&LOG M\$NX+-Y5:)7S77A_I;/0GB$:[4F"R9WDL(4(K<;(P+!;@FP!)"H0D^BP$5;> ME>22W4ZC`)UM_C%L`Z1;$[E"WBI_:2S$ MB`@+0%!:"5#NV"AY$XL`AA08"W*E`D:X.8>SL"!?5'A)Y)M:`M86 M&*%4FL;-.626A(,)*^1R%-:8*X9;!T:,(PL!RX+X)W"Y4-]`-NR$/V^RB[Q3 MSB_PT[#`2@`-(TN!A*N$,9=74[>FXF@`6PRPY^SR6O@C"H<"]@>"(0$N5W`I M")PKF"2Y4.*-\*B`"$Q^@9TW\Q$RH4M!HIIUSHP&3^ MSGI$M0Z$B_>M`]W"J^[<#4.8R^'4A.&-I+IS44L!%!Y8"\A,Y[D+6!%DVE/Y MLDU"'20J&Y3S8;[!PC#0#:2FL(#TNXG/7BA:Y?"#L M`&SDC98XDUP(^22X!9S,92_YW,23G&57X>)`@Y>`62ZV,26$K=NG'4);P)8WT(R^(Z!=!WI MILM7MR).Y^#MXN>'H`Y`3)P.F`4ZDE$ACXW[_Y7_5]J`_,^@R?]:3>LS()I^?[^+^:>R^[U>8.%KV?F`SZ-I.W_-[/^EQM#O]/"^&$._??\;W#'(Q[#[OU#_/X'I"VCZ_]I'9TW_O_'Y_Y:S_9>2:$A&AC'% MT-\`<&%-J`^T"0_#?"9?56`BXX'G+$$2"@5D'G/L^`K,H]/))QEE>U*V,X1] M'.3D"T4;'@%@C*3@$K@!-I,C,=N9DB`!+ M&L?GNH$[$S@7+T8@`0R:C?/A;Z!+7'(V]!1/J`(P>C1UDZPL"Y?R&FS(#8;$ MJ%AL,Q^$VA[\-X6[/6Y#O*^,%P(:M5Y@J-=D#B.",;XY0]2"(GF%O M0A\DVX%Z\SYF.^QEYX#[C<`8]E,="!?:W><9!0B.@#'$K^)NK!<%"HLE2#C0 MC$(0Z2#P=EZ4D0JJIA4`9P?51I,*M.7]OU-N"_K_VO>?5AS_-O#]+T4;_[8Q M_BUP_C\E5/\_5;O_2^/_&_'_3L;_XS@L8G;_?A=-OQ^FL_/]$]CY_T>CZ?U@ M\UCXUFA:G]/L_/_O8ZA^_7!VO[>+N;/9^?\E[)[OSYE=P-/@IH#;-9:&#]7X M?XW_U_C_5N7_4_&#L2&=2T[OGY+<,SDUK.U_MUF0N&$R+QF!EQ'09\EP.[-Y M*='LY',>3G*&GOH?*]M1DU`*HT+ME%M!A;H)CI_/$5<)?.%^CC>B=J=="U)<(JV@7%Z0X!!E!HB<"0;'^X>$F`QHK*_B8S8CQU\TKU"00N"+J):H:M M8;B>`T"W)+N,;H`-_M`>)OY')09$W19&GF"EVRER-I[[(R4)VR6CX#1B7!!W M#],3&@'\/8<79PBJR=.8/&B_G_7^W]T6]'^U[_^M./ZMSO^G:O8?VLKXMP#_ M'_+]OT_?WMKW?XW_;\3_WZ6R_S^S`[-7P.P`7@-,=!JX*>S[OAE<`[C36/AR M<)&>'&'R@1L8/S\PAL(KS'T2W#^!^W8,Y;^/,SN!Y\#-`/?&6!J>H_'_&O^O M\?^M_/T?0%/2.$-J_Q1#S^2TOF3DY%B%1"-O M=[A=27;1H20YW:&"`+?+1>0`3O?SZNJWK]JS;_3'WZ>X/ MN%WU.]=]M/4#KI;[Z-];=M=&P&Y_DMKM&^MVOK^NNK:*J^,^VK!S5VVM/RF9 M==LWU-35?[!K=^W[_][";=U6]9EZRF[?`$FVAXD@?H;=-77;/MRPG:O=55^W M[>,]=1]!L;MV<.LW[.'6[=STP?L;MN]>Y\NMKF9[W9[MZS[S9_F@?_;[O1'R M,_ISJWM?G8(B?+/KP!)15+ZDFM,\5+G>:/1EN/[?YZ_]]@VU>S[:L&NWJBL" MI"`T,M)M`904G*F!2;%K]T5J$H*VO/]W6=O"]W_M^V\KCG\; ML/_75QO_MC'^+<#_IZ9H]_]I_/]Y^?][&/__$/C_V8%^EX]B^O[7L?O\^D93 MO0"T"XCZ_`^S<-\]`%71M+Z'F1Y`AQB:O@=S!X&+\^X!9O]_)K@EX#X70_4) M;F3V_T=I_+_&_VO\?^OS_P8#.3">TC.\_G\6[S0*-FZ\8+/RW``[\>%I<2N? MY+*&,/SC;'(>;^4&N*R):/RNGMZ`A>"O922N4L_$4PFI..:.@G3"[;.1X MSF65)8^X.@*"^]/0<6Q`:V,RHGNA**H24SM7,%QYLDV0&K@\>5EVL#4Q`=JC MA(NA`;[/^R2+9>X0L_L^*&*J"\!R>9?4$!*55["&-T5,S+[EGZ<(WT?\YH&% MKTQ(/8*[HE!JL/-!#0\;$Q9K$6<'6N3<7IPK3U!",1;P516M&^=P-N2A.2RC MI2"[P6CA"GF7*-CX7`4JSG.\"4B-FR`QUT.$0F$BVT2CM>#FI+@1,M:\`7#8 MRKE@1@N2R^JUNSF'4[#:H'D1LHHPZ0'YQUG/N!KK^D/N>2)O#$RO8P#(^QN@ ML?@_W_U_F[#_KWW_;\7Q;P/Z_]KW_S8R_C\]_Y^2%OK]/U7C_S7^7Q?^_/]P M'3W_?QWC_['>U[/S_6@'`,NSL//_Q2S\+78.X-\L/#J&\O]_8.?^LY@K,_W_ M)YG^_V9P_PGNATQ?((?I_S^G\?\:_Z_Q_ZW*_V-<8BKP_RG]D_M$X/_'.$6[ M7,B-$13!"2P--\!!`I(<+"`C6Y1SQ,(D>TZ26Q(3;;E)+C&L6$"(<"+`]:,: M59\P_O;$?AY>,'#$"C:N62-YB5/PF0QOU#M-0=#FA$?U^TQ.&-9(@@." MZD$@NC&R4ZZ9"=.`DV%&.'@K;^%@!$7*\"/F]^(<86#R_/("P8XL/LPE=YP; M(/EL2=Q0:H6%@,NS`[D4K3S)J3G8A'<%RBZKVX7%X]BR>4N&&#J@9J;3(0FY M&TH#LQ>2%*H:I`D36G3_[VP+^O\&C?]KO?%O`_Q_'VW\V\;X_^3\?]^^8;[_ MIVG\?XOR_[_$^__N8O?_S0;_':C7#_[/8JEI_?=>Q>OZQX*B>8#&XFN%/C:;F; MXJG>PL=0V9'@G@3W27`S.NMTH\'=V9G*'[HET/L%)R2P?!)HNJD)5*[Q8@(= MCZH$[?X_[?X_312CW?^GW?_7C/O_6O'NOY24_KW3$I-[1[!>:74!"8`=RAV" M39!0-R67OF0`Y1,+H:>(K*FQP$IP9A-Q%50U@_+Q%W]ZY?PRJ:8.KZS=,:6^ MHGY5?7G]\OH*KGY-_;OU:]!9O&-N??F.&3N>KU^&#X>1*P#JW1U3=LRHKXAT ML*51=N]"ZBF0KF+'W!U%(7DT0?\ABS68U8ZI.QZ%9#L>!4]Y_5((>`SR7U/_ M'OA6P_,>%`G>%?5+"<"[$$%:L1ABH1Y-K!.-2H"$V/:*\^<7O'+4SZM_NO[Y M^M+Z>6&JK8K$AH?DC7VR%E/L**I?M:.(4C<`64NZ#5I7OQ3[L`)ZJ[Q^56"A M@8AET!F8%PS7CN?`]QYF#RFQ?W#HU/24@$.=R.C01C4!K`[?-^4EI+O[IKQ( M,N%(H6MW%,'$>!3S*(<,ET#E*[$&.QZ-D`-)#SZ2'BL\%1)@?5;3BC>5#/MM MQW0H;1FD;&Z)N!9<6IW)$A:B]/!G_"W*-:_K)[''+0%)BG4KCS2C8KULZ#_'R4S?2I2!\X_%D)''L7J+8&@99#[*O004E!.4BP#JE7$=@%8/TJ6L+.*""5'.DX+QFF)'5?. M(67"LFG.H9DU6N!\-<*>+XLCR3IUN71BK"6X7WX)\I^\MB#_U>R_MIK\+Z\MV'_1 MSO^UD?%O@?-?R:FA\E_-_HNF_]5(_VLLT_]RHEX7._^%]E^P/EE:"HWW<#TPXXQ?V=F]Z4/<\8)28!+-86*S!3.%B?/ MY0@V#H>0H?,XJVBTXF$P^P&G58+V0R\X#G+8$-`,(0:!VFBZ6?^_[?^5-J'_I?%_K3C^;4#_2[/_ MTD;&_Z?7_TI-ZZW9?]7TOUI<_VLYT_]R@_]U8,0_1/:Z(]53^K8CK6="'&U/ M M71D^81,KB!GU\`2K"UH#D!M>KEELXV&0LL7J5P6[R%G=-ALO06#-8F<3JTF! M((FPG+C$YN42O*ID98ZO*1^%E5&G)7WA$CG9QCMY*\>`@G*B,BJ^T,:;@$1B MJ^V!5<:*/6^J68P98$36+"\,43A81 MEKSF99%BEA%B851\&=KX<-G1U4:5Y'SELS7DDE($5]K?&4W45MU1"@9* M=KX`AL"?5KT,A0?PKT/[YCRQ;\Z4"_\_*V[?G$>Y+,%IX;/Y7IP):$H!?P,' M@1`QYZ*RG!LW2E;ZQPUBB%@@`@*)A6;$2`YFLBB9!,0O)TSI`MGIPG85DFXR M"7'"PV21&#EPU-#;NBG.2>/')A&ZTBW.+8E`2(1)R*$I<2*YO5T2"D2)W#YM M$W.X8;S5"I-T_:)FK:=0KNNBEE-H%U:V>GDA#*J3*^`+R6C:`<5,[IJ7L2`) M0ET\8!R,&"<6BA+N3$TBSI+JY;;J5ZLKH:D6['/)3.>S&;!34LC%:Z(-%BS! M"@N5Z(2V-1X:W1#9I7!&F$PUBV%R]L**P$(D;'A9$2&MD"W"-"1YAB,#V5`O MS'5D=:62(T!O!*IOX1569>A$ MLYBN]>,RJ_^>.2ISA&^UMP?W4)PN&_O#RBG5E5;8DX@Z*`2ZP)>?]KL0^8^[ M3=C_3M/D?ZTW_FU`_JN=_VXCX__3ZW\E]];L?VGZ7^?7_[(P_:\'P?\RN_]+ MQ^Q_7]]8+7D"K"%50%PWS5?"W=NK]W"U:_;N>[]#;MW-KH#+'*D/XQA^O8- M7$W=GH_JMNTH\]]XM?"C$%@"N6=GW9X/WE^XK79+N/B/:M]?MVE=4[E05&]> MB0QW+Q`X8@T;5ZYQ?]74O:_NL."KO4*CFT#R$SMK/UJXNQ>W"_Y&0'(*0B'` M4P>#_7[MMJVUW!X((7=V;5_G0_:3$>_QJMFP>]VVC[P_\\3-?O?OV3^#\9?L_^MC7_+Z7^E&-)"^?\TC?]O2?[_-XS_ M/\KTORH9_S^&Z7_=R?2_UC#^W\GTOIY@>E\+F=[7KO8T_9_24GSZCI_SW63W5^TIE>E_'.U"Y`)[WPO2_8OI>Z4S?:Q33 M]Q*8OI>9Z7NYF;X7R@'>`("GP/T'^/\(C/JCZ-Z:'&=:'W3.M%X*W/+.E%YP'_`%W4/[0 M>%IO(U3R'G`?!O=6<'=<1OOOLL[T7%K/SC3]N,Y4I^KNSK2=ULY4KO"H2K\K M@>EK7K'9,SZ%1RALM\>EI,AA'+=+HZ,?G&KU0Z M91U5L@M=J\@G?J[Z79I,15/D:GU%KI^E>E9JRZEG)2<:^B&X[(95\Z5)P3E!=92PBP$L)]4K(0-3 MPZI2S`;L#:O?@#"HI;N@8?546N23DGD25$P*NT`T*S=+PZJU M',3\G5,:5K\@8F[!*T;-K-$-JUX9I*7'&AE4+[-SZ-R#S&XW<^M6A$%;:?>"9[^!L6"<"S44`AT@D MH1#G2VFTR)S#4KW(`76$SA$Y%_P-GY`DP\K;-Y9BB;.;`(.:OXK]+4)?*>?- M&LGV1=6)+#*7EK+I!D5L1]/];+1L+..MH"\T641/&M@WY3Z%ETN!1/Z8(^,+E?VG) M?33[_ZT[_C^B"/@BY+]]>FOR_[8S_IUCZ&Q_E??U)043?ZKZ7\% MZW_)3/]K%,)WH'+);YC;D=G]NHZ="[XUFIYGO9.%VZ.I'E=)-)65OA[-SBU' M4YGI?YA[.IK6^XH8*O\<&$/S&\GNB\R.H7+/Z3&:_I6I@\]]LA/1UN[>L M6;JU_(L/S[P[[_VM2TX13$>H($Q'J/)#B]?4;ZB;Q^!@NLU?M:.T[OB*32M> M*WV]]'4Z16G8]L_J7VT<5OHVSM/2=]Y=O7M+_;&EI]_=ON5_-!9B(!QA=M5O M/+SS8"`-=Q-BR^*/ZX]0^-+YZOQP7H?+C\4BJITWNG&)JK)4]=_^V8K7MBQ? M?&)9#:N5#^-"HQBR+2I?5.;_MS1NT<(M5>\=KMF_JGI^Z:*%<8LJ5+&5<;I` MI*[N*\2IQ:]5'%JRK?ZKS8OK_H.-7C)ORV[L)H9AI>_LW+WZRV75%6>7[\4Q M01!?'G%M9?\W_NY6YO\,*9K^1^N.?^OR?WV3-?O?;6?\?UK^+RTEI4\(_]?; MH/%_&O\7S/\]P?@_,^;?@:;OR<[_C(BF^CL6QN\]R^P\+V/A:/<)Z1B>^\'P MWC$T_5TQM-YYS'T1W/$Z>NX'ZW%5+,T_,9;>YX?W!"*_^:AV_D?C_S3^KU7Y M/XA+2TPA*AIH_CDL_W>W(":.=$O`V/'09."[Y'Q!M+FE#(?1SHLVFOG.@8J'WA7G>9THB4`6:Z'#QC$,OONE9\G=(%$@!D_2+RN5Z=R^.5/8)(?XP]-*#ST](T(D#=LW92Z],>HYS.R?[WBG M8Y8'*Y\^]'3%@??6'I[S6@@X`SXT]Q^'YZT*&^M]<8IGX52:26,`>M]4LTMC M=VI=#'R8"JHK%MI52Y[VO#7WX(P%_@X+=,YYH%BCXIHP`T,,O1R/@"\5? M5#YQL.S5@V\O/[C@O2\JIS9MZV5NG"YL*IWGC9*#LYXDE(,[^,)+!U]9['WB M'<_K9?NG/!I'(N<^ZUFT\.#+*SRE3Y"!\=^Z]-R!-8\=GOGNP7\^<^B%I0?+ MGJ>3BZ0++4H[9M0Z^W\[+]WR4Y1QX?Q?2EJR9O^A5<8?'L,MK3[^J2FI&O_? M>N-/^7U#DKGP1QO_)OA_0TI*X_,_O9.U^Y]:YG=]26RL>_JUM[=KSP9=YQ[W M7_F%Y"Z/?#OIH?WBSK&O;EUZV_B)R])N[#@V+O:UY9:CZZ+:/Q-_^_+AS__O MFUMCQ7E9LQ]5UGSZ[7_>^<+R>=4XW2.SJEPG#B2NF/7F>U<_\]G&R3%[[^V7 MW/Y&N2+GF^6C>GWG29-7])QZKO/%/VOW';._D/5!\] M=:K3&L-]?YB[XYH]MT3_K].$_^O7MW/IE[\Y^]V2$:,,WLNZWG?B]\\.[='[ M\%UI-RZ][^GG/QALJ_RF*/E4]J#K^2FS7A8;.O#[.CQRWY"U/18,_G3V;V_I M)CMFCRG9V.XOQICOJEEW^^LJ-_TCJDE6JO&^3SVW-?N?> M-W-6>MH_=_2Y_M4-6]S/[2P^,6CG`[/3SGZPL._^N$W/%RY<^[]SK_9\Q[O] M3L_WC^\?_>GUM<\M7A%U,/N-K:;20QO+GTIXX:%N?>/GU9R\^\3I=Q^_[E=? MWB5V6OWVD#F/_B7ML=(1W[QEZ_^Z\IUCC[=;[(HG5]QWDZETS_Q?"\(-DU]> M-#"GO,;4Y8WUOYM[J'S\Q-D)B5E5UI0[^G3;/[,AO_RK20]>*3WXI^]UWSZ] M[-A_E,)^UWKF6O\P>FK.?/[[VKR=W^Y(T+VZ]<["Q(^6E>_W#LOKMVQ_S=N=3F9=VV_8]E)/ MV;0'A(DS9^U*<<1<]2_CX\^7SOIR<+<7DXOT0M&@07_;92C>\'#FR\LW??W( MHUM6[=W4<]"U'Y8D](K[Y\BTC2DW_^7V3D,_$)/7?U4[9G+>JK&#WCLQ,MVY M([G[PK'_VE;:]2^?M9\J7"-]]VEZ]OVUB7_ZZ/8?=*,^>RO*I-?V9"U/_[/% MGV;G=['KOR&EKV;_K87'G]#_5AC_U.34D/._*:DI?;3UOR5^?\L<>7M45)1* M)MV>R+5G/:Z/30.WLA<-!W34Q>AZZ'ZCNX;(JS$N;0K`P+,7WOR<&[ M0B9=I^IC\4%Y^14L+DH7.+>J@[3XI-T$>=U$TZ/0&^-)V`J(@V<.!'1IS^3D M7:C*N_.@8@K7%[[TM]C$[%ML MID0JY7/)22DTO`NK^]!1$_SR>U^;\-L!GMOMSF3ZUT;H[]^S[P6=F4P?OPM< MP[X+7*6"Z\CRQ/R[J;XIZ-CW`=1MNYKUN>_,+W[W0!VWZU7YW!"F#E%APMHU M\JN_9732A3GGJZ/GB*_4!>Y\\'VO^+4*YK(PZ7`L_B]"__AH?B_6)_.9W\+\ MCS-_=^9_BOD',[^O+\XP?Q7S_SF:^C-8XP>P^/^R^![,G\3B_Q9%_:-9_&[F MQ['86Z&/U9-QN$JW!=P#*O]$7U^TH_`GF/]*EG\BR_]VEM_OF?\=%C^4P8]@ MZ;NR^,,L_G46?RV+_YKY3[+X$N9/9_X,YK^=^5E)DW2`ZD9$\3XZ MAUM!X;S.I9ADMZ(S"XKL4";99,D,:7(<:%887NV"W>@HT-FSG8J<;]39\:.1 M40>0O-.LX[-EIZ(37?D.O")!9Z21DR89E0*',`ERG&3/GF1T.R?9^8=U.00H M1V0X-=HD1@C0_SDWB%)!PZ(XF5(K;*B:-CEQ-]!=S?SNZX7.R)5 MY9F_"XEOK[,S_Z^(OYTNGTV+I'.UP`YUK'8!0=PA"XO*#'1A\7H"75A09J$+B\H<=&%AF8LN(-*+Z`(1 M?P5=(,9EZ$(_32@^6'0TUG,O=(&G'/_TA$)KJW7G>E\+$.=NY.`O]NNY&['' MD*KKO'O/P>]&[#D+QGGKB1][T()#XJTB?NQ)"RYYWG+BQQZU($I[7R%^[%D+ MDG'O+.+''K;@%M([A?BQIRU(UKP.XL<>MR!Y]#Y(_-CSEF'H'T/\.`*6,>C/ M('X<"0N:[/`F$S^.B.5!]'/$CR-CP09YNQ`_CI#%@7X=\>-(61Y&_XD?T(\C M9IE"VD_\.'*6&:3]Q(\C:)E%VD_\.)*6N:3]Q(\C:GF%M)_X<60MKY'V$S^. ML*6$C[B1]GB.4$:3_QXTRQG";M_Q_ZR\CX1V'[P>^CT89C?RYN M*#IP8LSXL9977L(S9L$<&U=T5(]EZ>Z?6GU/(BQ[ ML\]"GM-W*AT\\^#EGOMKJV>1'YV/4VX[@?-.B3]R;5&5?GXY3L6=I>B4=)F^ MTWUD8P>,CX)$%+YT"&PKIUS]!WW7_A/L^/0-@9RJ_ MUZU%.G=D92G2L^+,H[..O%V!6\J2Z[#.:_$5_#Z2=HDJ;!N\L MV5^AU4H@Q1:68C5I!"0;3I/A+MKS'D2N3,6.>P!2C0RDF@,1JX9CQ'A:F(NF M(I$K\#W3@UWF+?H!WP^0]WQX7ZO#+L+QPXK00FEM]%C/7JHZ3\'"-XI7[=R63/=//N0>59!V=7JODE&0>Q91S(24A7A7^>53P2G")-V*)`'H9 M9.Q]&D#7(.CJLU@QA!2HX*_@' M6%J2&3MQ_M?0B.J9>72]ZE%*)M#$T\7NLU@$+K30JM_Z^^!49Q(@E$P\70(@ M1R=6TWKNG1]:S[%8^,92+``(QY\P^4VP<2BN]CP,,2N/TB1=R"`!HI;H6$)" M&S9`XTJ7LE*Q_SR#(=&J$S11/#8T.,$<@#GR?N/V0<'1);J)U<6G*G0K8:K` M*Z,7&#\A%@AE3$EF?'&[B=7@K$(8?*?]`?%W)2S/BA]7X@;BJ"3.V)RPO+;* MTZ&HX?J$Y1\7'8J:>>WS)>/TQ?6[/$5[HPP;=QTR?%4\3G__G[$\DM>1;<6G M9E;>1,I;B4LZ=O*`L\43ORZ>?+:XVE`U<_+N8O?IF>[ZDV\4_WA=0^_BBVRJ11)?<5@%.WO7%WZ\A8HJ2P?&W M%U63[AH./97>&Z'RO25#];=E0$V4WT',:EQE/'_`WL>PXNNJL(,OJX&_4\_A MNW)%\8#NGH[8TR3J2BRC`O.?C]D5U<3?_^=)_OH4';T:ITHUG=4P'3[$J:V> M`J._A9I4*W&!Z5+T0[3[RXJ3,(Q'/I^YG-!OUC^L?0\49U853Z@LSJKP##P- M)'MR?'1Q9DU^%Q@>I-9E0*V74^I?3YW=Z&S,W`.OV_"U:'(LT"-8\N8?K!=AA54>+>''6B.'-I2>;2A.6Z M`8_,R/PD_[*$Y550Y`-89!YD6Q.5,.WS=KX"U$M0PK1[.J#G4UJ5_5$EF9_. MG'"B9/+FDHD5Q5V[1VV:"<3R>UR28$YB!]U\&KO*\]]3L%97%6]4_DJ6ICL@ MEZ+3/R1,NQ9?)L='N=.+3D?EI17O68EC$-1?"VG4=T>XDLS*],QM+NB&"9^43*PJR=PVHVO[0N"G")S%%F=O:3\G\)/E:B;(:_[=U2R83-I%:Q><:2B4NG_"7J3^[[ M$I;?WV[&OZ=X;YCRW77Y(PD6[-J[:-V=NG[*'TNR*M.S6'NSH+VQ?X*,,](S M*_-B2K*J-NJC'L""JMJI0OZ$+9LQ\9/B"9M)S_7!YJ^*(D.9]8F/K%3MVE>2 MU67Z^*EZYAWYAAC@*KN+IZXM/BS]*KB=2<7)DQ_`;:>2F/B*;X`V>%[ZGGB`MGR+WG_B%F93PO+V#,>G52D]CUP6M)[!,*1G5>9% METRHFJ&_'#*$@(3E^FNF5;D/(%$SG80L)M<#H8@JSMSM_?![W+4T(B%1+'MO M!1:?64E6CPE5&]MU`U"D60`:%0`E6YZ9"#JY: M_3WB&HS1M!51A(Q$47(T_2CM-,#'Z0W_(S0/2$W/_V''8A=WA#5^EO=]\)\&!,P^XBE_X,B>\KPC>\LEI-='MM,4N\^2`MHE M3,\CI1(E8"'^DB-;0_+]PHQK( MBB1,#D&)9,R*(-8J%&!L!+3W_@#U*W=ZKX/)5)Y#.$J8E4"^//._1`2.]3X- M"WWZY-@\;LJM.N6W,T='%>\Y$@_M!WHX]PFI8:L]VO)_F&M.G^R/O8JSO043SA0G+77 M8\"\KZN@#$N)KCCS1$FF!R;1C+%(+B=X;KT2%VVGN_C#VZJ*D_D%W)]4HL,FQ%T#JHE\?5'ANSMWBHOG@C MKI[C`^F!UX-M-UE))_^`D_E$R82CQ>V*:GH5GR`D7KT7?@/7MRMQK^-K(=GB MP/ZMO3+X_#GS*H#MG_T-T:#/JPLPEK M8`OI/@V#OP9'C1#UF9/K81K,G%A_<@'9E;,>7O`-SJC8E2@),51A^_X/:QXT M^LOI?K$_RS\66SV2,F&/0;):W_X+9D,:3*R9$S\N_MCS(F:!'G.63);BZ.*/P_0\ MG<^^_>W7)1-C6876'84]TR=0I7[*#17=56B#V^KB/P9E-6LUP=^/U?P(:7\\ MVZ`0_OT8;L*4=K7`+WA&1-&)%4)(@NJND*HP1$[92G M*TO&\L_"WI]>6]).^=UMT+E_R^\6557'S[W,-GES25[!`3#_E3H0%Z<;IY]R=9GF*`*"H7T?EUQ4$Y1K3>T/5D2.P M5]GGN1_@-M%/1PAXY`-_^4#_/2CWG%IMOP:H\='XF477XH"NQK\WGUYWNMU, MQ[FB'\XI\8"_Y[JN&`7O[F_9^'CP`Y^?@^7$NN[1Z0"4Z`;CPXSN.!7*96 MOW-\`XGBOQH_>,`E+B7OZG/+KP'Z_ MZ(C^7'U)>PAU?WW/_1-G5<\*D8<.@U(]>AB->^^OK:;?2-KI0HYYTA,].OPD M0T_N]$`+>S>Z;B;W`.,IG$C67,.?(PHYW*,S][=(2EZD^^R##Q0%GU3R&ND05.G>I(CHX=7]']G(WR7L"%ZBU\_(^6][.SL-W,(VT7>ZHNZ!?SA#[V M5_#HPCR&F>'#;V/AP\%]^A5]K.\;-9YK?>D5^GW,I_,P7^6'::Y;HO*C/M): ME3\OC#Z%VK(R8N*-+ETWW8.ZFW0V"'<"!KD0$>G?1-Z6SQ>X=$:=X#+R#D%G M9*2BB5_YD_I8?`8\HX^]')Z33^MCI[.G[[/Z6.ZI\$\JQ&T'^`7P[('G0WAF M0]@33:2YT*?DF<`SA;FV.=3=]TQP/(9?RE/2*+^O9M,Z7&J^%U+^3E7[U&Y+ M/&;6;C/KZZK9]*EY*M1MJ0?+^PXV.5%E^M@8>+K"\VMX3KZLC[VN+(`W44S_ MJ3W3M^K`])U"S)2S'^Y/U*XOCRY,MVO,#'TLZCW5,+V585/UL9CGB6GZ6,QW MRJ/Z6"0C4Z.H;E47IA_DT[NZ&G9GY&QX%-6QNISIA@W[.^`8TF1P?\5TEJY@ M^F;`3"9Y'X/D'/"_!\S8\:^#9#L]G M\!R'YP=X.C\.Z>%)@N>/\(R"Y\_P2/`\\GB@+ADK*+U$)C:*=3RRS[JA@P?W M)WN8F[FT)$-2"CE(;#`8>G,]'$Y8EVT"[Q)NYGH,$;+Q@C^$,22F&&[6:2FU ME+^PE$DNBTMQ*GRV+@EVGH+3H4N29$5(&CAH>*+"FW5)%MYET269"B17@9VZ MBE-'ML2^#;?:,PGBH#B$HR\.FX(YB_"7*+8EY8`'HF38@O.Z),$R*+MHA`)DA?RAN=&4V2X`,\IVM*W0?)K6247CB4YR5$!G M5:T/>P73QT4XHCL);&-UOQ]:($WJZ-C2N7U^ZYL@(A[3=&4UM8>I5.KSX#&'K`;[C MFE`53>],5I>+OW%,S[@=6U.X&+JFJ-N!A/Z^F]ST'ZH:C[JX)+?E4?FWQ3(`]U?L4,#L>.Z*K?1/NA M,=PL%=P`@!L0`>Y9%1PJ&PV)4.Z+K*WMV3J-NO";V?BJQVV!*K\NL%YVZ1%: MKI[I%$>I]AY7]Z!U;@RW1`7W&L"]UB,\?JQDY;=GNM*;`_4P`<`,`"@`` ` end python-debian-0.1.21+nmu2ubuntu1/tests/test_modify_changelog30000644000000000000000000000117511703667743021152 0ustar gnutls14 (1:1.4.1-3) experimental; urgency=low * Foo did not work, let us try bar -- James Westby Sat, 16 Jul 2008 11:11:08 +0200 gnutls14 (1:1.4.1-2) experimental; urgency=medium [ James Westby ] * New upstream release. * Remove the following patches as they are now included upstream: - 10_certtoolmanpage.diff - 15_fixcompilewarning.diff - 30_man_hyphen_*.patch * Link the API reference in /usr/share/gtk-doc/html as gnutls rather than gnutls-api so that devhelp can find it. * Add magic foo -- James Westby Sat, 16 Jul 2008 11:11:08 -0200 python-debian-0.1.21+nmu2ubuntu1/tests/test_Sources.mixed_encoding0000644000000000000000000000263511703667743022171 0ustar Package: amarok Binary: amarok, amarok-engines, amarok-xine Version: 1.4.4-4etch1 Priority: optional Section: kde Maintainer: Adeodato Simó Build-Depends: cdbs, debhelper (>= 5), quilt, bzip2, automake1.9, libtool, kdelibs4-dev, kdemultimedia-dev, kdebase-dev, libxine-dev, libtag1-dev (>> 1.4), libsqlite3-dev, libtunepimp3-dev, libmysqlclient15-dev, libpq-dev, xmms-dev, libvisual-0.4-dev, libsdl1.2-dev, libifp-dev, libusb-dev, libgpod-dev, libnjb-dev, ruby, ruby1.8-dev, dpkg-dev (>= 1.13.19) Architecture: any Standards-Version: 3.7.2 Format: 1.0 Directory: pool/main/a/amarok Files: f8e80af55fbd8386e6b13b0b12d798f4 986 amarok_1.4.4-4etch1.dsc 0adbbd8373da2198b80e509618a2dab9 17628566 amarok_1.4.4.orig.tar.gz c29b0538c033ededacc6d31339d17700 42402 amarok_1.4.4-4etch1.diff.gz Uploaders: Ana Beatriz Guerrero Lopez Package: texinfo Binary: texinfo, info Version: 4.8.dfsg.1-4 Priority: important Section: doc Maintainer: Norbert Preining Build-Depends: debhelper (>= 5), dpatch, libncurses5-dev | libncurses-dev, gettext Architecture: any Standards-Version: 3.7.2 Format: 1.0 Directory: pool/main/t/texinfo Files: 2c233d2bf6627eac32deb9bb87726ea1 680 texinfo_4.8.dfsg.1-4.dsc 614273ac8568a25926aae374cd9a6683 1926534 texinfo_4.8.dfsg.1.orig.tar.gz e01520524bc114d90a2a1e5eefe71b50 101211 texinfo_4.8.dfsg.1-4.diff.gz Uploaders: Frank Küster python-debian-0.1.21+nmu2ubuntu1/tests/test-broken.deb.uu0000644000000000000000000000166211703667743020151 0ustar begin 644 hello-broken_2.2-2_i386.deb M(3QAZ0[K+;WL4-V6PZ[5;V?.W>1\-\.!L-Q],\ MY_W9;'37@VGO!30QZ0#0"T3I;W'_^OZ?RFX-^13(77C^^=W=G^8_&DW&O\^? M=]KY#V7^%[?2YJLN<0$5.D?J$4.TY!AM,91.'-8&+V,D\5P]8HR_B`IS=F!RN[U]SM4F6#_(;M?3<;N>P&*SM M=TZ8SN?JO;8^\0_#`M;:)ZM+@D?K-+R*VN_YY4V!&ZM]1J&\Y_+1!%N?SO6Q M0C!.QV@-E`$Q65_V0?L"-)1$!>`WO:L=JB[RW8=/IWM"':@,>M>N16,P9K;EP;"OQIERG@9XW<\":Z8Y\W MJ2FK!:\V`C_:GZ\&M(6*#FUF07R*AZY74)\&RU66J4W@JN=/^].0V\3TU)55 MH"\\Q*L(GT\-.E!PQ=6Y3PJNN^.WA6R*Z+:_'F!+X7FAFTSUA!!"""&$$$(( 8(8000@@AA!!"""'$"_@!MPFYS0`H```* ` end python-debian-0.1.21+nmu2ubuntu1/tests/test_Packages0000644000000000000000000000743411703667743017313 0ustar Package: a2ps Priority: optional Section: text Installed-Size: 4244 Maintainer: Masayuki Hatta (mhatta) Architecture: i386 Version: 1:4.14-1 Depends: file, libc6 (>= 2.7-1), libpaper1, psutils Recommends: bzip2, lpr | rlpr | cupsys-client, wdiff Suggests: emacsen-common, ghostscript, graphicsmagick-imagemagick-compat | imagemagick, groff, gv, html2ps, t1-cyrillic, texlive-base-bin Filename: pool/main/a/a2ps/a2ps_4.14-1_i386.deb Size: 926602 MD5sum: ada9c133500447bc22f32419298e2d38 SHA1: 757d7b81efb518d16dc7792a9c48ed63d338eefe SHA256: 2841c4c5bb68c1571615d1d32778b69bd03e95abd65b717a5fcd6f112a6b78fa Description: GNU a2ps - 'Anything to PostScript' converter and pretty-printer GNU a2ps converts files into PostScript for printing or viewing. It uses a nice default format, usually two pages on each physical page, borders surrounding pages, headers with useful information (page number, printing date, file name or supplied header), line numbering, symbol substitution as well as pretty printing for a wide range of programming languages. . Historically, a2ps started as a text to PostScript converter, but thanks to powerful delegations it is able to let you use it for any kind of files, ie it can also digest manual pages, dvi files, texinfo, .... . Among the other most noticeable features of a2ps are: - various encodings (all the Latins and others), - various fonts (automatic font down loading), - various medias, - various printer interfaces, - various output styles, - various programming languages, - various helping applications, - and various spoken languages. Tag: devel::prettyprint, interface::commandline, role::program, scope::utility, suite::gnu, use::converting, use::printing, works-with::text, works-with-format::postscript Package: zssh Priority: optional Section: net Installed-Size: 88 Maintainer: Ben Wong Architecture: i386 Version: 1.5c.debian.1-3 Depends: lrzsz, openssh-client | telnet | telnet-ssl, libc6 (>= 2.6.1-1), libncurses5 (>= 5.6), libreadline5 (>= 5.2) Filename: pool/main/z/zssh/zssh_1.5c.debian.1-3_i386.deb Size: 21864 MD5sum: 21e4ac1bbdd405873aa969918e78b95c SHA1: 22b132fef92822e0bc8b612bbaf06165aa459bfc SHA256: 9d0d77df0312ef6487e0b06eb125446e130bd9de80df70ac0180abf7d4e4a0c5 Description: interactive file transfers over ssh zssh (Zmodem SSH) is a program for interactively transferring files to a remote machine while using the secure shell (ssh). It is intended to be a convenient alternative to scp, allowing to transfer files without having to open another session and re-authenticate oneself. . Files are transferred through the zmodem protocol, using the rz and sz commands. . Homepage: http://zssh.sourceforge.net/ Tag: hardware::modem, interface::commandline, protocol::ssh, role::program, scope::utility, uitoolkit::ncurses, use::downloading Package: dummy-package Priority: optional Section: net Installed-Size: 42 Maintainer: Stefano Zacchiroli Architecture: i386 Version: 1.0 Depends: dcoprss (>= 4:3.5.9-2), kdenetwork-kfile-plugins (>= 4:3.5.9-2), kdict (>= 4:3.5.9-2), kdnssd (>= 4:3.5.9-2), kget (>= 4:3.5.9-2), knewsticker (>= 4:3.5.9-2), kopete (>= 4:3.5.9-2), kpf (>= 4:3.5.9-2), kppp (>= 4:3.5.9-2), krdc (>= 4:3.5.9-2), krfb (>= 4:3.5.9-2), ksirc (>= 4:3.5.9-2), kwifimanager (>= 4:3.5.9-2), librss1 (>= 4:3.5.9-2) Filename: pool/main/d/dummy-package/dummy-package_1.0_i386.deb Size: 424242 MD5sum: 21e4ac1bbdd405873aa969918e78b95c SHA1: 22b132fef92822e0bc8b612bbaf06165aa459bfc SHA256: 9d0d77df0312ef6487e0b06eb125446e130bd9de80df70ac0180abf7d4e4a0c5 Description: inexistent dummy package, dep parsing test case inexistent dummy package, dep parsing test case Tag: hardware::modem, interface::commandline, protocol::ssh, role::program, scope::utility, uitoolkit::ncurses, use::downloading python-debian-0.1.21+nmu2ubuntu1/tests/test_changelog.py0000755000000000000000000002651412015175027020140 0ustar #!/usr/bin/python # vim: fileencoding=utf-8 # # changelog.py -- Python module for Debian changelogs # Copyright (C) 2006-7 James Westby # Copyright (C) 2008 Canonical Ltd. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # The parsing code is based on that from dpkg which is: # Copyright 1996 Ian Jackson # Copyright 2005 Frank Lichtenheld # and licensed under the same license as above. from __future__ import absolute_import import sys import unittest import six sys.path.insert(0, '../lib/') from debian import changelog def open_utf8(filename, mode='r'): """Open a UTF-8 text file in text mode.""" if sys.version < '3': return open(filename, mode=mode) else: return open(filename, mode=mode, encoding='UTF-8') class ChangelogTests(unittest.TestCase): def test_create_changelog(self): f = open('test_changelog') c = f.read() f.close() cl = changelog.Changelog(c) cs = str(cl) clines = c.split('\n') cslines = cs.split('\n') for i in range(len(clines)): self.assertEqual(clines[i], cslines[i]) self.assertEqual(len(clines), len(cslines), "Different lengths") def test_create_changelog_single_block(self): f = open('test_changelog') c = f.read() f.close() cl = changelog.Changelog(c, max_blocks=1) cs = str(cl) self.assertEqual(cs, """gnutls13 (1:1.4.1-1) unstable; urgency=HIGH [ James Westby ] * New upstream release. * Remove the following patches as they are now included upstream: - 10_certtoolmanpage.diff - 15_fixcompilewarning.diff - 30_man_hyphen_*.patch * Link the API reference in /usr/share/gtk-doc/html as gnutls rather than gnutls-api so that devhelp can find it. -- Andreas Metzler Sat, 15 Jul 2006 11:11:08 +0200 """) def test_modify_changelog(self): f = open('test_modify_changelog1') c = f.read() f.close() cl = changelog.Changelog(c) cl.package = 'gnutls14' cl.version = '1:1.4.1-2' cl.distributions = 'experimental' cl.urgency = 'medium' cl.add_change(' * Add magic foo') cl.author = 'James Westby ' cl.date = 'Sat, 16 Jul 2008 11:11:08 -0200' f = open('test_modify_changelog2') c = f.read() f.close() clines = c.split('\n') cslines = str(cl).split('\n') for i in range(len(clines)): self.assertEqual(clines[i], cslines[i]) self.assertEqual(len(clines), len(cslines), "Different lengths") def test_add_changelog_section(self): f = open('test_modify_changelog2') c = f.read() f.close() cl = changelog.Changelog(c) cl.new_block(package='gnutls14', version=changelog.Version('1:1.4.1-3'), distributions='experimental', urgency='low', author='James Westby ') self.assertRaises(changelog.ChangelogCreateError, cl.__str__) cl.set_date('Sat, 16 Jul 2008 11:11:08 +0200') cl.add_change('') cl.add_change(' * Foo did not work, let us try bar') cl.add_change('') f = open('test_modify_changelog3') c = f.read() f.close() clines = c.split('\n') cslines = str(cl).split('\n') for i in range(len(clines)): self.assertEqual(clines[i], cslines[i]) self.assertEqual(len(clines), len(cslines), "Different lengths") def test_strange_changelogs(self): """ Just opens and parses a strange changelog """ f = open('test_strange_changelog') c = f.read() f.close() cl = changelog.Changelog(c) def test_set_version_with_string(self): f = open('test_modify_changelog1') c1 = changelog.Changelog(f.read()) f.seek(0) c2 = changelog.Changelog(f.read()) f.close() c1.version = '1:2.3.5-2' c2.version = changelog.Version('1:2.3.5-2') self.assertEqual(c1.version, c2.version) self.assertEqual((c1.full_version, c1.epoch, c1.upstream_version, c1.debian_version), (c2.full_version, c2.epoch, c2.upstream_version, c2.debian_version)) def test_changelog_no_author(self): cl_no_author = """gnutls13 (1:1.4.1-1) unstable; urgency=low * New upstream release. -- """ c1 = changelog.Changelog() c1.parse_changelog(cl_no_author, allow_empty_author=True) self.assertEqual(c1.author, None) self.assertEqual(c1.date, None) self.assertEqual(c1.package, "gnutls13") c2 = changelog.Changelog() self.assertRaises(changelog.ChangelogParseError, c2.parse_changelog, cl_no_author) def test_magic_version_properties(self): f = open('test_changelog') c = changelog.Changelog(f) f.close() self.assertEqual(c.debian_version, '1') self.assertEqual(c.full_version, '1:1.4.1-1') self.assertEqual(c.upstream_version, '1.4.1') self.assertEqual(c.epoch, '1') self.assertEqual(str(c.version), c.full_version) def test_allow_full_stops_in_distribution(self): f = open('test_changelog_full_stops') c = changelog.Changelog(f) f.close() self.assertEqual(c.debian_version, None) self.assertEqual(c.full_version, '1.2.3') self.assertEqual(str(c.version), c.full_version) def test_str_consistent(self): # The parsing of the changelog (including the string representation) # should be consistent whether we give a single string, a list of # lines, or a file object to the Changelog initializer f = open('test_changelog') cl_data = f.read() f.seek(0) c1 = changelog.Changelog(f) f.close() c2 = changelog.Changelog(cl_data) c3 = changelog.Changelog(cl_data.splitlines()) for c in (c1, c2, c3): self.assertEqual(str(c), cl_data) def test_utf8_encoded_file_input(self): f = open_utf8('test_changelog_unicode') c = changelog.Changelog(f) f.close() u = six.text_type(c) expected_u = six.u("""haskell-src-exts (1.8.2-3) unstable; urgency=low * control: Use versioned Replaces: and Conflicts: -- Marco T\xfalio Gontijo e Silva Wed, 05 May 2010 18:01:53 -0300 haskell-src-exts (1.8.2-2) unstable; urgency=low * debian/control: Rename -doc package. -- Marco T\xfalio Gontijo e Silva Tue, 16 Mar 2010 10:59:48 -0300 """) self.assertEqual(u, expected_u) self.assertEqual(bytes(c), u.encode('utf-8')) def test_unicode_object_input(self): f = open('test_changelog_unicode', 'rb') c_bytes = f.read() f.close() c_unicode = c_bytes.decode('utf-8') c = changelog.Changelog(c_unicode) self.assertEqual(six.text_type(c), c_unicode) self.assertEqual(bytes(c), c_bytes) def test_non_utf8_encoding(self): f = open('test_changelog_unicode', 'rb') c_bytes = f.read() f.close() c_unicode = c_bytes.decode('utf-8') c_latin1_str = c_unicode.encode('latin1') c = changelog.Changelog(c_latin1_str, encoding='latin1') self.assertEqual(six.text_type(c), c_unicode) self.assertEqual(bytes(c), c_latin1_str) for block in c: self.assertEqual(bytes(block), six.text_type(block).encode('latin1')) def test_block_iterator(self): f = open('test_changelog') c = changelog.Changelog(f) f.close() self.assertEqual([str(b) for b in c._blocks], [str(b) for b in c]) def test_len(self): f = open('test_changelog') c = changelog.Changelog(f) f.close() self.assertEqual(len(c._blocks), len(c)) class VersionTests(unittest.TestCase): def _test_version(self, full_version, epoch, upstream, debian): v = changelog.Version(full_version) self.assertEqual(v.full_version, full_version, "Full version broken") self.assertEqual(v.epoch, epoch, "Epoch broken") self.assertEqual(v.upstream_version, upstream, "Upstram broken") self.assertEqual(v.debian_version, debian, "Debian broken") def testversions(self): self._test_version('1:1.4.1-1', '1', '1.4.1', '1') self._test_version('7.1.ds-1', None, '7.1.ds', '1') self._test_version('10.11.1.3-2', None, '10.11.1.3', '2') self._test_version('4.0.1.3.dfsg.1-2', None, '4.0.1.3.dfsg.1', '2') self._test_version('0.4.23debian1', None, '0.4.23debian1', None) self._test_version('1.2.10+cvs20060429-1', None, '1.2.10+cvs20060429', '1') self._test_version('0.2.0-1+b1', None, '0.2.0', '1+b1') self._test_version('4.3.90.1svn-r21976-1', None, '4.3.90.1svn-r21976', '1') self._test_version('1.5+E-14', None, '1.5+E', '14') self._test_version('20060611-0.0', None, '20060611', '0.0') self._test_version('0.52.2-5.1', None, '0.52.2', '5.1') self._test_version('7.0-035+1', None, '7.0', '035+1') self._test_version('1.1.0+cvs20060620-1+2.6.15-8', None, '1.1.0+cvs20060620-1+2.6.15', '8') self._test_version('1.1.0+cvs20060620-1+1.0', None, '1.1.0+cvs20060620', '1+1.0') self._test_version('4.2.0a+stable-2sarge1', None, '4.2.0a+stable', '2sarge1') self._test_version('1.8RC4b', None, '1.8RC4b', None) self._test_version('0.9~rc1-1', None, '0.9~rc1', '1') self._test_version('2:1.0.4+svn26-1ubuntu1', '2', '1.0.4+svn26', '1ubuntu1') self._test_version('2:1.0.4~rc2-1', '2', '1.0.4~rc2', '1') self.assertRaises( ValueError, changelog.Version, 'a1:1.8.8-070403-1~priv1') def test_version_updating(self): v = changelog.Version('1:1.4.1-1') v.debian_version = '2' self.assertEqual(v.debian_version, '2') self.assertEqual(v.full_version, '1:1.4.1-2') v.upstream_version = '1.4.2' self.assertEqual(v.upstream_version, '1.4.2') self.assertEqual(v.full_version, '1:1.4.2-2') v.epoch = '2' self.assertEqual(v.epoch, '2') self.assertEqual(v.full_version, '2:1.4.2-2') self.assertEqual(str(v), v.full_version) v.full_version = '1:1.4.1-1' self.assertEqual(v.full_version, '1:1.4.1-1') self.assertEqual(v.epoch, '1') self.assertEqual(v.upstream_version, '1.4.1') self.assertEqual(v.debian_version, '1') if __name__ == '__main__': unittest.main() python-debian-0.1.21+nmu2ubuntu1/tests/test_debtags.py0000755000000000000000000000527212015175027017620 0ustar #! /usr/bin/python ## vim: fileencoding=utf-8 # Copyright (C) 2006 Enrico Zini # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; version 2 # dated June, 1991. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. from __future__ import absolute_import import sys import unittest sys.path.insert(0, '../lib/') from debian import debtags class TestDebtags(unittest.TestCase): def mkdb(self): db = debtags.DB() with open("test_tagdb", "r") as f: db.read(f) return db def test_insert(self): db = debtags.DB() db.insert("test", set(("a", "b"))); assert db.has_package("test") assert not db.has_package("a") assert not db.has_package("b") assert db.has_tag("a") assert db.has_tag("b") assert not db.has_tag("test") self.assertEqual(db.tags_of_package("test"), set(("a", "b"))) self.assertEqual(db.packages_of_tag("a"), set(("test"))) self.assertEqual(db.packages_of_tag("b"), set(("test"))) self.assertEqual(db.package_count(), 1) self.assertEqual(db.tag_count(), 2) def test_reverse(self): db = debtags.DB() db.insert("test", set(("a", "b"))); db = db.reverse() assert db.has_package("a") assert db.has_package("b") assert not db.has_package("test") assert db.has_tag("test") assert not db.has_tag("a") assert not db.has_tag("b") self.assertEqual(db.packages_of_tag("test"), set(("a", "b"))) self.assertEqual(db.tags_of_package("a"), set(("test"))) self.assertEqual(db.tags_of_package("b"), set(("test"))) self.assertEqual(db.package_count(), 2) self.assertEqual(db.tag_count(), 1) def test_read(self): db = self.mkdb() self.assertEqual(db.tags_of_package("polygen"), set(("devel::interpreter", "game::toys", "interface::commandline", "works-with::text"))) assert "polygen" in db.packages_of_tag("interface::commandline") self.assertEqual(db.package_count(), 144) self.assertEqual(db.tag_count(), 94) if __name__ == '__main__': unittest.main() # vim:set ts=4 sw=4 expandtab: python-debian-0.1.21+nmu2ubuntu1/tests/test_deb822.py0000755000000000000000000012422312015175027017173 0ustar #! /usr/bin/python ## vim: fileencoding=utf-8 # Copyright (C) 2006 Adeodato Simó # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; version 2 # dated June, 1991. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. from __future__ import absolute_import import os import re import sys import tempfile import unittest import warnings try: from StringIO import StringIO BytesIO = StringIO except ImportError: from io import BytesIO, StringIO import six sys.path.insert(0, '../lib/') from debian import deb822 UNPARSED_PACKAGE = '''\ Package: mutt Priority: standard Section: mail Installed-Size: 4471 Maintainer: Adeodato Simó Architecture: i386 Version: 1.5.12-1 Replaces: mutt-utf8 Provides: mail-reader, imap-client Depends: libc6 (>= 2.3.6-6), libdb4.4, libgnutls13 (>= 1.4.0-0), libidn11 (>= 0.5.18), libncursesw5 (>= 5.4-5), libsasl2 (>= 2.1.19.dfsg1), exim4 | mail-transport-agent Recommends: locales, mime-support Suggests: urlview, aspell | ispell, gnupg, mixmaster, openssl, ca-certificates Conflicts: mutt-utf8 Filename: pool/main/m/mutt/mutt_1.5.12-1_i386.deb Size: 1799444 MD5sum: d4a9e124beea99d8124c8e8543d22e9a SHA1: 5e3c295a921c287cf7cb3944f3efdcf18dd6701a SHA256: 02853602efe21d77cd88056a4e2a4350f298bcab3d895f5f9ae02aacad81442b Description: text-based mailreader supporting MIME, GPG, PGP and threading Mutt is a sophisticated text-based Mail User Agent. Some highlights: . * MIME support (including RFC1522 encoding/decoding of 8-bit message headers and UTF-8 support). * PGP/MIME support (RFC 2015). * Advanced IMAP client supporting SSL encryption and SASL authentication. * POP3 support. * Mailbox threading (both strict and non-strict). * Default keybindings are much like ELM. * Keybindings are configurable; Mush and PINE-like ones are provided as examples. * Handles MMDF, MH and Maildir in addition to regular mbox format. * Messages may be (indefinitely) postponed. * Colour support. * Highly configurable through easy but powerful rc file. Tag: interface::text-mode, made-of::lang:c, mail::imap, mail::pop, mail::user-agent, protocol::imap, protocol::ipv6, protocol::pop, protocol::ssl, role::sw:client, uitoolkit::ncurses, use::editing, works-with::mail Task: mail-server ''' PARSED_PACKAGE = deb822.Deb822Dict([ ('Package', 'mutt'), ('Priority', 'standard'), ('Section', 'mail'), ('Installed-Size', '4471'), ('Maintainer', 'Adeodato Simó '), ('Architecture', 'i386'), ('Version', '1.5.12-1'), ('Replaces', 'mutt-utf8'), ('Provides', 'mail-reader, imap-client'), ('Depends', 'libc6 (>= 2.3.6-6), libdb4.4, libgnutls13 (>= 1.4.0-0), libidn11 (>= 0.5.18), libncursesw5 (>= 5.4-5), libsasl2 (>= 2.1.19.dfsg1), exim4 | mail-transport-agent'), ('Recommends', 'locales, mime-support'), ('Suggests', 'urlview, aspell | ispell, gnupg, mixmaster, openssl, ca-certificates'), ('Conflicts', 'mutt-utf8'), ('Filename', 'pool/main/m/mutt/mutt_1.5.12-1_i386.deb'), ('Size', '1799444'), ('MD5sum', 'd4a9e124beea99d8124c8e8543d22e9a'), ('SHA1', '5e3c295a921c287cf7cb3944f3efdcf18dd6701a'), ('SHA256', '02853602efe21d77cd88056a4e2a4350f298bcab3d895f5f9ae02aacad81442b'), ('Description', '''text-based mailreader supporting MIME, GPG, PGP and threading Mutt is a sophisticated text-based Mail User Agent. Some highlights: . * MIME support (including RFC1522 encoding/decoding of 8-bit message headers and UTF-8 support). * PGP/MIME support (RFC 2015). * Advanced IMAP client supporting SSL encryption and SASL authentication. * POP3 support. * Mailbox threading (both strict and non-strict). * Default keybindings are much like ELM. * Keybindings are configurable; Mush and PINE-like ones are provided as examples. * Handles MMDF, MH and Maildir in addition to regular mbox format. * Messages may be (indefinitely) postponed. * Colour support. * Highly configurable through easy but powerful rc file.'''), ('Tag', 'interface::text-mode, made-of::lang:c, mail::imap, mail::pop, mail::user-agent, protocol::imap, protocol::ipv6, protocol::pop, protocol::ssl, role::sw:client, uitoolkit::ncurses, use::editing, works-with::mail'), ('Task', 'mail-server'), ]) GPG_SIGNED = [ '''\ -----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 %s -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.3 (GNU/Linux) Comment: Signed by Adeodato Simó iEYEARECAAYFAkRqYxkACgkQgyNlRdHEGIKccQCgnnUgfwYjQ7xd3zGGS2y5cXKt CcYAoOLYDF5G1h3oR1iDNyeCI6hRW03S =Um8T -----END PGP SIGNATURE----- ''', '''\ -----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 %s -----BEGIN PGP SIGNATURE----- iEYEARECAAYFAkRqYxkACgkQgyNlRdHEGIKccQCgnnUgfwYjQ7xd3zGGS2y5cXKt CcYAoOLYDF5G1h3oR1iDNyeCI6hRW03S =Um8T -----END PGP SIGNATURE----- ''', ] CHANGES_FILE = six.u('''\ Format: 1.7 Date: Fri, 28 Dec 2007 17:08:48 +0100 Source: bzr-gtk Binary: bzr-gtk Architecture: source all Version: 0.93.0-2 Distribution: unstable Urgency: low Maintainer: Debian Bazaar Maintainers Changed-By: Chris Lamb Description: bzr-gtk - provides graphical interfaces to Bazaar (bzr) version control Closes: 440354 456438 Changes: bzr-gtk (0.93.0-2) unstable; urgency=low . [ Chris Lamb ] * Add patch for unclosed progress window. (Closes: #440354) Patch by Jean-François Fortin Tam * Fix broken icons in .desktop files (Closes: #456438). Files: 0fd797f4138a9d4fdeb8c30597d46bc9 1003 python optional bzr-gtk_0.93.0-2.dsc d9523676ae75c4ced299689456f252f4 3860 python optional bzr-gtk_0.93.0-2.diff.gz 8960459940314b21019dedd5519b47a5 168544 python optional bzr-gtk_0.93.0-2_all.deb ''') CHECKSUM_CHANGES_FILE = '''\ Format: 1.8 Date: Wed, 30 Apr 2008 23:58:24 -0600 Source: python-debian Binary: python-debian Architecture: source all Version: 0.1.10 Distribution: unstable Urgency: low Maintainer: Debian python-debian Maintainers Changed-By: John Wright Description: python-debian - Python modules to work with Debian-related data formats Closes: 473254 473259 Changes: python-debian (0.1.10) unstable; urgency=low . * debian_bundle/deb822.py, tests/test_deb822.py: - Do not cache _CaseInsensitiveString objects, since it causes case preservation issues under certain circumstances (Closes: #473254) - Add a test case * debian_bundle/deb822.py: - Add support for fixed-length subfields in multivalued fields. I updated the Release and PdiffIndex classes to use this. The default behavior for Release is that of apt-ftparchive, just because it's simpler. Changing the behavior to resemble dak requires simply setting the size_field_behavior attribute to 'dak'. (Ideally, deb822 would detect which behavior to use if given an actual Release file as input, but this is not implemented yet.) (Closes: #473259) - Add support for Checksums-{Sha1,Sha256} multivalued fields in Dsc and Changes classes * debian/control: - "python" --> "Python" in the Description field - Change the section to "python" Checksums-Sha1: d12d7c95563397ec37c0d877486367b409d849f5 1117 python-debian_0.1.10.dsc 19efe23f688fb7f2b20f33d563146330064ab1fa 109573 python-debian_0.1.10.tar.gz 22ff71048921a788ad9d90f9579c6667e6b3de3a 44260 python-debian_0.1.10_all.deb Checksums-Sha256: aae63dfb18190558af8e71118813dd6a11157c6fd92fdc9b5c3ac370daefe5e1 1117 python-debian_0.1.10.dsc d297c07395ffa0c4a35932b58e9c2be541e8a91a83ce762d82a8474c4fc96139 109573 python-debian_0.1.10.tar.gz 4c73727b6438d9ba60aeb5e314e2d8523f021da508405dc54317ad2b392834ee 44260 python-debian_0.1.10_all.deb Files: 469202dfd24d55a932af717c6377ee59 1117 python optional python-debian_0.1.10.dsc 4857552b0156fdd4fa99d21ec131d3d2 109573 python optional python-debian_0.1.10.tar.gz 81864d535c326c082de3763969c18be6 44260 python optional python-debian_0.1.10_all.deb ''' SIGNED_CHECKSUM_CHANGES_FILE = '''\ -----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 %s -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.6 (GNU/Linux) iD8DBQFIGWQO0UIZh3p4ZWERAug/AJ93DWD9o+1VMgPDjWn/dsmPSgTWGQCeOfZi 6LAP26zP25GAeTlKwJQ17hs= =fwnP -----END PGP SIGNATURE----- ''' UNPARSED_PARAGRAPHS_WITH_COMMENTS = '''\ # Leading comments should be ignored. Source: foo Section: bar # An inline comment in the middle of a paragraph should be ignored. Priority: optional Homepage: http://www.debian.org/ # Comments in the middle shouldn't result in extra blank paragraphs either. # Ditto. # A comment at the top of a paragraph should be ignored. Package: foo Architecture: any Description: An awesome package # This should still appear in the result. Blah, blah, blah. # So should this. # A comment at the end of a paragraph should be ignored. # Trailing comments shouldn't cause extra blank paragraphs. ''' PARSED_PARAGRAPHS_WITH_COMMENTS = [ deb822.Deb822Dict([ ('Source', 'foo'), ('Section', 'bar'), ('Priority', 'optional'), ('Homepage', 'http://www.debian.org/'), ]), deb822.Deb822Dict([ ('Package', 'foo'), ('Architecture', 'any'), ('Description', 'An awesome package\n' ' # This should still appear in the result.\n' ' Blah, blah, blah. # So should this.'), ]), ] def open_utf8(filename, mode='r'): """Open a UTF-8 text file in text mode.""" if sys.version < '3': return open(filename, mode=mode) else: return open(filename, mode=mode, encoding='UTF-8') class TestDeb822Dict(unittest.TestCase): def make_dict(self): d = deb822.Deb822Dict() d['TestKey'] = 1 d['another_key'] = 2 return d def test_case_insensitive_lookup(self): d = self.make_dict() self.assertEqual(1, d['testkey']) self.assertEqual(2, d['Another_keY']) def test_case_insensitive_assignment(self): d = self.make_dict() d['testkey'] = 3 self.assertEqual(3, d['TestKey']) self.assertEqual(3, d['testkey']) d.setdefault('foo', 4) self.assertEqual(4, d['Foo']) def test_case_preserved(self): d = self.make_dict() self.assertEqual(sorted(['another_key', 'TestKey']), sorted(d.keys())) def test_order_preserved(self): d = self.make_dict() d['Third_key'] = 3 d['another_Key'] = 2.5 keys = ['TestKey', 'another_key', 'Third_key'] self.assertEqual(keys, list(d.keys())) self.assertEqual(keys, list(six.iterkeys(d))) self.assertEqual(list(zip(keys, d.values())), list(d.items())) keys2 = [] for key in d: keys2.append(key) self.assertEqual(keys, keys2) def test_derived_dict_equality(self): d1 = self.make_dict() d2 = dict(d1) self.assertEqual(d1, d2) def test_unicode_key_access(self): d = self.make_dict() self.assertEqual(1, d[six.u('testkey')]) class TestDeb822(unittest.TestCase): def assertWellParsed(self, deb822_, dict_): """Check that the given Deb822 object has the very same keys and values as the given dict. """ self.assertEqual(deb822_.keys(), dict_.keys()) for k, v in dict_.items(): self.assertEqual(v, deb822_[k]) self.assertEqual(deb822_, dict_) def gen_random_string(length=20): from random import choice import string chars = string.ascii_letters + string.digits return ''.join([choice(chars) for i in range(length)]) gen_random_string = staticmethod(gen_random_string) def deb822_from_format_string(self, string, dict_=PARSED_PACKAGE, cls=deb822.Deb822): """Construct a Deb822 object by formatting string with % dict. Returns the formatted string, and a dict object containing only the keys that were used for the formatting.""" dict_subset = DictSubset(dict_) string = string % dict_subset parsed = cls(string.splitlines()) return parsed, dict_subset def test_parser(self): deb822_ = deb822.Deb822(UNPARSED_PACKAGE.splitlines()) self.assertWellParsed(deb822_, PARSED_PACKAGE) def test_parser_with_newlines(self): deb822_ = deb822.Deb822([ l+'\n' for l in UNPARSED_PACKAGE.splitlines()]) self.assertWellParsed(deb822_, PARSED_PACKAGE) def test_strip_initial_blanklines(self): deb822_ = deb822.Deb822(['\n'] * 3 + UNPARSED_PACKAGE.splitlines()) self.assertWellParsed(deb822_, PARSED_PACKAGE) def test_gpg_stripping(self): for string in GPG_SIGNED: unparsed_with_gpg = string % UNPARSED_PACKAGE deb822_ = deb822.Deb822(unparsed_with_gpg.splitlines()) self.assertWellParsed(deb822_, PARSED_PACKAGE) def test_gpg_info(self): if not (os.path.exists('/usr/bin/gpgv') and os.path.exists('/usr/share/keyrings/debian-keyring.gpg')): return unparsed_with_gpg = SIGNED_CHECKSUM_CHANGES_FILE % CHECKSUM_CHANGES_FILE deb822_from_str = deb822.Dsc(unparsed_with_gpg) result_from_str = deb822_from_str.get_gpg_info() deb822_from_file = deb822.Dsc(StringIO(unparsed_with_gpg)) result_from_file = deb822_from_file.get_gpg_info() deb822_from_lines = deb822.Dsc(unparsed_with_gpg.splitlines()) result_from_lines = deb822_from_lines.get_gpg_info() valid = { 'GOODSIG': ['D14219877A786561', 'John Wright '], 'VALIDSIG': ['8FEFE900783CF175827C2F65D14219877A786561', '2008-05-01', '1209623566', '0', '3', '0', '17', '2', '01', '8FEFE900783CF175827C2F65D14219877A786561'], 'SIG_ID': ['j3UjSpdky92fcQISbm8W5PlwC/g', '2008-05-01', '1209623566'], } for result in result_from_str, result_from_file, result_from_lines: # The second part of the GOODSIG field could change if the primary # uid changes, so avoid checking that. Also, the first part of the # SIG_ID field has undergone at least one algorithm changein gpg, # so don't bother testing that either. self.assertEqual(set(result.keys()), set(valid.keys())) self.assertEqual(result['GOODSIG'][0], valid['GOODSIG'][0]) self.assertEqual(result['VALIDSIG'], valid['VALIDSIG']) self.assertEqual(result['SIG_ID'][1:], valid['SIG_ID'][1:]) def test_iter_paragraphs_array(self): text = (UNPARSED_PACKAGE + '\n\n\n' + UNPARSED_PACKAGE).splitlines() for d in deb822.Deb822.iter_paragraphs(text): self.assertWellParsed(d, PARSED_PACKAGE) def test_iter_paragraphs_file(self): text = StringIO(UNPARSED_PACKAGE + '\n\n\n' + UNPARSED_PACKAGE) for d in deb822.Deb822.iter_paragraphs(text): self.assertWellParsed(d, PARSED_PACKAGE) def test_iter_paragraphs_with_gpg(self): for string in GPG_SIGNED: string = string % UNPARSED_PACKAGE text = (string + '\n\n\n' + string).splitlines() count = 0 for d in deb822.Deb822.iter_paragraphs(text): count += 1 self.assertWellParsed(d, PARSED_PACKAGE) self.assertEqual(count, 2) def _test_iter_paragraphs(self, filename, cls, **kwargs): """Ensure iter_paragraphs consistency""" f = open(filename, 'rb') packages_content = f.read() f.close() # XXX: The way multivalued fields parsing works, we can't guarantee # that trailing whitespace is reproduced. packages_content = b"\n".join([line.rstrip() for line in packages_content.splitlines()] + [b'']) s = BytesIO() l = [] f = open_utf8(filename) for p in cls.iter_paragraphs(f, **kwargs): p.dump(s) s.write(b"\n") l.append(p) f.close() self.assertEqual(s.getvalue(), packages_content) if kwargs["shared_storage"] is False: # If shared_storage is False, data should be consistent across # iterations -- i.e. we can use "old" objects s = BytesIO() for p in l: p.dump(s) s.write(b"\n") self.assertEqual(s.getvalue(), packages_content) def test_iter_paragraphs_apt_shared_storage_packages(self): self._test_iter_paragraphs("test_Packages", deb822.Packages, use_apt_pkg=True, shared_storage=True) def test_iter_paragraphs_apt_no_shared_storage_packages(self): self._test_iter_paragraphs("test_Packages", deb822.Packages, use_apt_pkg=True, shared_storage=False) def test_iter_paragraphs_no_apt_no_shared_storage_packages(self): self._test_iter_paragraphs("test_Packages", deb822.Packages, use_apt_pkg=False, shared_storage=False) def test_iter_paragraphs_apt_shared_storage_sources(self): self._test_iter_paragraphs("test_Sources", deb822.Sources, use_apt_pkg=True, shared_storage=True) def test_iter_paragraphs_apt_no_shared_storage_sources(self): self._test_iter_paragraphs("test_Sources", deb822.Sources, use_apt_pkg=True, shared_storage=False) def test_iter_paragraphs_no_apt_no_shared_storage_sources(self): self._test_iter_paragraphs("test_Sources", deb822.Sources, use_apt_pkg=False, shared_storage=False) def test_parser_empty_input(self): self.assertEqual({}, deb822.Deb822([])) def test_iter_paragraphs_empty_input(self): generator = deb822.Deb822.iter_paragraphs([]) self.assertRaises(StopIteration, next, generator) def test_parser_limit_fields(self): wanted_fields = [ 'Package', 'MD5sum', 'Filename', 'Description' ] deb822_ = deb822.Deb822(UNPARSED_PACKAGE.splitlines(), wanted_fields) self.assertEqual(sorted(wanted_fields), sorted(deb822_.keys())) for key in wanted_fields: self.assertEqual(PARSED_PACKAGE[key], deb822_[key]) def test_iter_paragraphs_limit_fields(self): wanted_fields = [ 'Package', 'MD5sum', 'Filename', 'Tag' ] for deb822_ in deb822.Deb822.iter_paragraphs( UNPARSED_PACKAGE.splitlines(), wanted_fields): self.assertEqual(sorted(wanted_fields), sorted(deb822_.keys())) for key in wanted_fields: self.assertEqual(PARSED_PACKAGE[key], deb822_[key]) def test_dont_assume_trailing_newline(self): deb822a = deb822.Deb822(['Package: foo']) deb822b = deb822.Deb822(['Package: foo\n']) self.assertEqual(deb822a['Package'], deb822b['Package']) deb822a = deb822.Deb822(['Description: foo\n', 'bar']) deb822b = deb822.Deb822(['Description: foo', 'bar\n']) self.assertEqual(deb822a['Description'], deb822b['Description']) def test__delitem__(self): parsed = deb822.Deb822(UNPARSED_PACKAGE.splitlines()) deriv = deb822.Deb822(_parsed=parsed) dict_ = PARSED_PACKAGE.copy() for key in ('Package', 'MD5sum', 'Description'): del dict_[key] for d in (parsed, deriv): del d[key] d.keys() # ensure this does not raise error self.assertWellParsed(d, dict_) def test_policy_compliant_whitespace(self): string = ( 'Package: %(Package)s\n' 'Version :%(Version)s \n' 'Priority:%(Priority)s\t \n' 'Section \t :%(Section)s \n' 'Empty-Field: \t\t\t\n' 'Multiline-Field : a \n b\n c\n' ) % PARSED_PACKAGE deb822_ = deb822.Deb822(string.splitlines()) dict_ = PARSED_PACKAGE.copy() dict_['Empty-Field'] = '' dict_['Multiline-Field'] = 'a\n b\n c' # XXX should be 'a\nb\nc'? for k, v in deb822_.items(): self.assertEqual(dict_[k], v) def test_case_insensitive(self): # PARSED_PACKAGE is a deb822.Deb822Dict object, so we can test # it directly self.assertEqual(PARSED_PACKAGE['Architecture'], PARSED_PACKAGE['architecture']) c_i_dict = deb822.Deb822Dict() test_string = self.gen_random_string() c_i_dict['Test-Key'] = test_string self.assertEqual(test_string, c_i_dict['test-key']) test_string_2 = self.gen_random_string() c_i_dict['TeSt-KeY'] = test_string_2 self.assertEqual(test_string_2, c_i_dict['Test-Key']) deb822_ = deb822.Deb822(StringIO(UNPARSED_PACKAGE)) # deb822_.keys() will return non-normalized keys for k in deb822_: self.assertEqual(deb822_[k], deb822_[k.lower()]) def test_multiline_trailing_whitespace_after_colon(self): """Trailing whitespace after the field name on multiline fields If the field's value starts with a newline (e.g. on MD5Sum fields in Release files, or Files field in .dsc's, the dumped string should not have a trailing space after the colon. If the value does not start with a newline (e.g. the control file Description field), then there should be a space after the colon, as with non-multiline fields. """ # bad_re: match a line that starts with a "Field:", and ends in # whitespace bad_re = re.compile(r"^\S+:\s+$") for cls in deb822.Deb822, deb822.Changes: parsed = cls(CHANGES_FILE.splitlines()) for line in parsed.dump().splitlines(): self.assertTrue(bad_re.match(line) is None, "There should not be trailing whitespace " "after the colon in a multiline field " "starting with a newline") control_paragraph = """Package: python-debian Architecture: all Depends: ${python:Depends} Suggests: python-apt Provides: python-deb822 Conflicts: python-deb822 Replaces: python-deb822 Description: python modules to work with Debian-related data formats This package provides python modules that abstract many formats of Debian related files. Currently handled are: * Debtags information (debian_bundle.debtags module) * debian/changelog (debian_bundle.changelog module) * Packages files, pdiffs (debian_bundle.debian_support module) * Control files of single or multple RFC822-style paragraphs, e.g debian/control, .changes, .dsc, Packages, Sources, Release, etc. (debian_bundle.deb822 module) """ parsed_control = deb822.Deb822(control_paragraph.splitlines()) field_re = re.compile(r"^\S+:") field_with_space_re = re.compile(r"^\S+: ") for line in parsed_control.dump().splitlines(): if field_re.match(line): self.assertTrue(field_with_space_re.match(line), "Multiline fields that do not start with " "newline should have a space between the " "colon and the beginning of the value") def test_blank_value(self): """Fields with blank values are parsable--so they should be dumpable""" d = deb822.Deb822() d['Foo'] = 'bar' d['Baz'] = '' d['Another-Key'] = 'another value' # Previous versions would raise an exception here -- this makes the # test fail and gives useful information, so I won't try to wrap around # it. dumped = d.dump() # May as well make sure the resulting string is what we want expected = "Foo: bar\nBaz:\nAnother-Key: another value\n" self.assertEqual(dumped, expected) def test_copy(self): """The copy method of Deb822 should return another Deb822 object""" d = deb822.Deb822() d['Foo'] = 'bar' d['Bar'] = 'baz' d_copy = d.copy() self.assertTrue(isinstance(d_copy, deb822.Deb822)) expected_dump = "Foo: bar\nBar: baz\n" self.assertEqual(d_copy.dump(), expected_dump) def test_bug457929_multivalued_dump_works(self): """dump() was not working in multivalued classes, see #457929.""" changesobj = deb822.Changes(CHANGES_FILE.splitlines()) self.assertEqual(CHANGES_FILE, changesobj.dump()) def test_bug487902_multivalued_checksums(self): """New multivalued field Checksums was not handled correctly, see #487902.""" changesobj = deb822.Changes(CHECKSUM_CHANGES_FILE.splitlines()) self.assertEqual(CHECKSUM_CHANGES_FILE, changesobj.dump()) def test_case_preserved_in_input(self): """The field case in the output from dump() should be the same as the input, even if multiple Deb822 objects have been created using different case conventions. This is related to bug 473254 - the fix for this issue is probably the same as the fix for that bug. """ input1 = "Foo: bar\nBaz: bang\n" input2 = "foo: baz\nQux: thud\n" d1 = deb822.Deb822(input1.splitlines()) d2 = deb822.Deb822(input2.splitlines()) self.assertEqual(input1, d1.dump()) self.assertEqual(input2, d2.dump()) d3 = deb822.Deb822() if 'some-test-key' not in d3: d3['Some-Test-Key'] = 'some value' self.assertEqual(d3.dump(), "Some-Test-Key: some value\n") def test_unicode_values(self): """Deb822 objects should contain only unicode values (Technically, they are allowed to contain any type of object, but when parsed from files, and when only string-type objects are added, the resulting object should have only unicode values.) """ objects = [] objects.append(deb822.Deb822(UNPARSED_PACKAGE)) objects.append(deb822.Deb822(CHANGES_FILE)) with open_utf8('test_Packages') as f: objects.extend(deb822.Deb822.iter_paragraphs(f)) with open_utf8('test_Packages') as f: objects.extend(deb822.Packages.iter_paragraphs(f)) with open_utf8('test_Sources') as f: objects.extend(deb822.Deb822.iter_paragraphs(f)) with open('test_Sources.iso8859-1', 'rb') as f: objects.extend(deb822.Deb822.iter_paragraphs( f, encoding="iso8859-1")) for d in objects: for value in d.values(): self.assertTrue(isinstance(value, six.text_type)) # The same should be true for Sources and Changes except for their # _multivalued fields multi = [] multi.append(deb822.Changes(CHANGES_FILE)) multi.append(deb822.Changes(SIGNED_CHECKSUM_CHANGES_FILE % CHECKSUM_CHANGES_FILE)) with open_utf8('test_Sources') as f: multi.extend(deb822.Sources.iter_paragraphs(f)) for d in multi: for key, value in d.items(): if key.lower() not in d.__class__._multivalued_fields: self.assertTrue(isinstance(value, six.text_type)) def test_encoding_integrity(self): with open_utf8('test_Sources') as f: utf8 = list(deb822.Deb822.iter_paragraphs(f)) with open('test_Sources.iso8859-1', 'rb') as f: latin1 = list(deb822.Deb822.iter_paragraphs( f, encoding='iso8859-1')) # dump() with no fd returns a unicode object - both should be identical self.assertEqual(len(utf8), len(latin1)) for i in range(len(utf8)): self.assertEqual(utf8[i].dump(), latin1[i].dump()) # XXX: The way multiline fields parsing works, we can't guarantee # that trailing whitespace is reproduced. with open('test_Sources', 'rb') as f: utf8_contents = b"\n".join([line.rstrip() for line in f] + [b'']) with open('test_Sources.iso8859-1', 'rb') as f: latin1_contents = b"\n".join([line.rstrip() for line in f] + [b'']) utf8_to_latin1 = BytesIO() for d in utf8: d.dump(fd=utf8_to_latin1, encoding='iso8859-1') utf8_to_latin1.write(b"\n") latin1_to_utf8 = BytesIO() for d in latin1: d.dump(fd=latin1_to_utf8, encoding='utf-8') latin1_to_utf8.write(b"\n") self.assertEqual(utf8_contents, latin1_to_utf8.getvalue()) self.assertEqual(latin1_contents, utf8_to_latin1.getvalue()) def test_mixed_encodings(self): """Test that we can handle a simple case of mixed encodings In general, this isn't guaranteed to work. It uses the chardet package, which tries to determine heuristically the encoding of the text given to it. But as far as I've seen, it's reliable for mixed latin1 and utf-8 in maintainer names in old Sources files... """ # Avoid spitting out the encoding warning during testing. warnings.filterwarnings(action='ignore', category=UnicodeWarning) filename = 'test_Sources.mixed_encoding' # TODO: With Python >= 2.7, this might be better written as: # with open(filename, 'rb') as f1, open(filename, 'rb') as f2: f1 = open(filename, 'rb') f2 = open(filename, 'rb') for paragraphs in [deb822.Sources.iter_paragraphs(f1), deb822.Sources.iter_paragraphs(f2, use_apt_pkg=False)]: p1 = next(paragraphs) self.assertEqual(p1['maintainer'], six.u('Adeodato Sim\xf3 ')) p2 = next(paragraphs) self.assertEqual(p2['uploaders'], six.u('Frank K\xfcster ')) f2.close() f1.close() def test_bug597249_colon_as_first_value_character(self): """Colon should be allowed as the first value character. See #597249. """ data = 'Foo: : bar' parsed = {'Foo': ': bar'} self.assertWellParsed(deb822.Deb822(data), parsed) @staticmethod def _dictset(d, key, value): d[key] = value def test_field_value_ends_in_newline(self): """Field values are not allowed to end with newlines""" d = deb822.Deb822() self.assertRaises(ValueError, self._dictset, d, 'foo', 'bar\n') self.assertRaises(ValueError, self._dictset, d, 'foo', 'bar\nbaz\n') def test_field_value_contains_blank_line(self): """Field values are not allowed to contain blank lines""" d = deb822.Deb822() self.assertRaises(ValueError, self._dictset, d, 'foo', 'bar\n\nbaz') self.assertRaises(ValueError, self._dictset, d, 'foo', '\n\nbaz') def test_multivalued_field_contains_newline(self): """Multivalued field components are not allowed to contain newlines""" d = deb822.Dsc() # We don't check at set time, since one could easily modify the list # without deb822 knowing. We instead check at get time. d['Files'] = [{'md5sum': 'deadbeef', 'size': '9605', 'name': 'bad\n'}] self.assertRaises(ValueError, d.get_as_string, 'files') def _test_iter_paragraphs_comments(self, paragraphs): self.assertEqual(len(paragraphs), len(PARSED_PARAGRAPHS_WITH_COMMENTS)) for i in range(len(paragraphs)): self.assertWellParsed(paragraphs[i], PARSED_PARAGRAPHS_WITH_COMMENTS[i]) def test_iter_paragraphs_comments_use_apt_pkg(self): paragraphs = list(deb822.Deb822.iter_paragraphs( UNPARSED_PARAGRAPHS_WITH_COMMENTS.splitlines(), use_apt_pkg=True)) self._test_iter_paragraphs_comments(paragraphs) def test_iter_paragraphs_comments_native(self): paragraphs = list(deb822.Deb822.iter_paragraphs( UNPARSED_PARAGRAPHS_WITH_COMMENTS.splitlines(), use_apt_pkg=False)) self._test_iter_paragraphs_comments(paragraphs) class TestPkgRelations(unittest.TestCase): def test_packages(self): f = open('test_Packages') pkgs = deb822.Packages.iter_paragraphs(f) pkg1 = next(pkgs) rel1 = {'breaks': [], 'conflicts': [], 'depends': [[{'name': 'file', 'version': None, 'arch': None}], [{'name': 'libc6', 'version': ('>=', '2.7-1'), 'arch': None}], [{'name': 'libpaper1', 'version': None, 'arch': None}], [{'name': 'psutils', 'version': None, 'arch': None}]], 'enhances': [], 'pre-depends': [], 'provides': [], 'recommends': [[{'name': 'bzip2', 'version': None, 'arch': None}], [{'name': 'lpr', 'version': None, 'arch': None}, {'name': 'rlpr', 'version': None, 'arch': None}, {'name': 'cupsys-client', 'version': None, 'arch': None}], [{'name': 'wdiff', 'version': None, 'arch': None}]], 'replaces': [], 'suggests': [[{'name': 'emacsen-common', 'version': None, 'arch': None}], [{'name': 'ghostscript', 'version': None, 'arch': None}], [{'name': 'graphicsmagick-imagemagick-compat', 'version': None, 'arch': None}, {'name': 'imagemagick', 'version': None, 'arch': None}], [{'name': 'groff', 'version': None, 'arch': None}], [{'name': 'gv', 'version': None, 'arch': None}], [{'name': 'html2ps', 'version': None, 'arch': None}], [{'name': 't1-cyrillic', 'version': None, 'arch': None}], [{'name': 'texlive-base-bin', 'version': None, 'arch': None}]]} self.assertEqual(rel1, pkg1.relations) pkg2 = next(pkgs) rel2 = {'breaks': [], 'conflicts': [], 'depends': [[{'name': 'lrzsz', 'version': None, 'arch': None}], [{'name': 'openssh-client', 'version': None, 'arch': None}, {'name': 'telnet', 'version': None, 'arch': None}, {'name': 'telnet-ssl', 'version': None, 'arch': None}], [{'name': 'libc6', 'version': ('>=', '2.6.1-1'), 'arch': None}], [{'name': 'libncurses5', 'version': ('>=', '5.6'), 'arch': None}], [{'name': 'libreadline5', 'version': ('>=', '5.2'), 'arch': None}]], 'enhances': [], 'pre-depends': [], 'provides': [], 'recommends': [], 'replaces': [], 'suggests': []} self.assertEqual(rel2, pkg2.relations) pkg3 = next(pkgs) dep3 = [[{'arch': None, 'name': 'dcoprss', 'version': ('>=', '4:3.5.9-2')}], [{'arch': None, 'name': 'kdenetwork-kfile-plugins', 'version': ('>=', '4:3.5.9-2')}], [{'arch': None, 'name': 'kdict', 'version': ('>=', '4:3.5.9-2')}], [{'arch': None, 'name': 'kdnssd', 'version': ('>=', '4:3.5.9-2')}], [{'arch': None, 'name': 'kget', 'version': ('>=', '4:3.5.9-2')}], [{'arch': None, 'name': 'knewsticker', 'version': ('>=', '4:3.5.9-2')}], [{'arch': None, 'name': 'kopete', 'version': ('>=', '4:3.5.9-2')}], [{'arch': None, 'name': 'kpf', 'version': ('>=', '4:3.5.9-2')}], [{'arch': None, 'name': 'kppp', 'version': ('>=', '4:3.5.9-2')}], [{'arch': None, 'name': 'krdc', 'version': ('>=', '4:3.5.9-2')}], [{'arch': None, 'name': 'krfb', 'version': ('>=', '4:3.5.9-2')}], [{'arch': None, 'name': 'ksirc', 'version': ('>=', '4:3.5.9-2')}], [{'arch': None, 'name': 'kwifimanager', 'version': ('>=', '4:3.5.9-2')}], [{'arch': None, 'name': 'librss1', 'version': ('>=', '4:3.5.9-2')}]] self.assertEqual(dep3, pkg3.relations['depends']) f.close() bin_rels = ['file, libc6 (>= 2.7-1), libpaper1, psutils'] src_rels = ['apache2-src (>= 2.2.9), libaprutil1-dev, ' \ 'libcap-dev [!kfreebsd-i386 !kfreebsd-amd64 !hurd-i386], ' \ 'autoconf, debhelper (>> 5.0.0)'] for bin_rel in bin_rels: self.assertEqual(bin_rel, deb822.PkgRelation.str(deb822.PkgRelation.parse_relations( bin_rel))) for src_rel in src_rels: self.assertEqual(src_rel, deb822.PkgRelation.str(deb822.PkgRelation.parse_relations( \ src_rel))) def test_sources(self): f = open_utf8('test_Sources') pkgs = deb822.Sources.iter_paragraphs(f) pkg1 = next(pkgs) rel1 = {'build-conflicts': [], 'build-conflicts-indep': [], 'build-depends': [[{'name': 'apache2-src', 'version': ('>=', '2.2.9'), 'arch': None}], [{'name': 'libaprutil1-dev', 'version': None, 'arch': None}], [{'arch': [(False, 'kfreebsd-i386'), (False, 'kfreebsd-amd64'), (False, 'hurd-i386')], 'name': 'libcap-dev', 'version': None}], [{'name': 'autoconf', 'version': None, 'arch': None}], [{'name': 'debhelper', 'version': ('>>', '5.0.0'), 'arch': None}]], 'build-depends-indep': [], 'binary': [[{'name': 'apache2-mpm-itk', 'version': None, 'arch': None}]]} self.assertEqual(rel1, pkg1.relations) pkg2 = next(pkgs) rel2 = {'build-conflicts': [], 'build-conflicts-indep': [], 'build-depends': [[{'name': 'dpkg-dev', 'version': ('>=', '1.13.9'), 'arch': None}], [{'name': 'autoconf', 'version': ('>=', '2.13'), 'arch': None}], [{'name': 'bash', 'version': None, 'arch': None}], [{'name': 'bison', 'version': None, 'arch': None}], [{'name': 'flex', 'version': None, 'arch': None}], [{'name': 'gettext', 'version': None, 'arch': None}], [{'name': 'texinfo', 'version': None, 'arch': None}], [{'arch': [(True, 'hppa')], 'name': 'expect-tcl8.3', 'version': ('>=', '5.32.2')}], [{'name': 'dejagnu', 'version': ('>=', '1.4.2-1.1'), 'arch': None}], [{'name': 'dpatch', 'version': None, 'arch': None}], [{'name': 'file', 'version': None, 'arch': None}], [{'name': 'bzip2', 'version': None, 'arch': None}], [{'name': 'lsb-release', 'version': None, 'arch': None}]], 'build-depends-indep': [], 'binary': [[{'name': 'binutils', 'version': None, 'arch': None}], [{'name': 'binutils-dev', 'version': None, 'arch': None}], [{'name': 'binutils-multiarch', 'version': None, 'arch': None}], [{'name': 'binutils-hppa64', 'version': None, 'arch': None}], [{'name': 'binutils-spu', 'version': None, 'arch': None}], [{'name': 'binutils-doc', 'version': None, 'arch': None}], [{'name': 'binutils-source', 'version': None, 'arch': None}]]} self.assertEqual(rel2, pkg2.relations) f.close() class TestGpgInfo(unittest.TestCase): def setUp(self): # These tests can only run with gpgv and a keyring available. When we # can use Python >= 2.7, we can use the skip decorator; for now just # check in each test method whether we should actually run. self.should_run = ( os.path.exists('/usr/bin/gpgv') and os.path.exists('/usr/share/keyrings/debian-keyring.gpg')) self.data = SIGNED_CHECKSUM_CHANGES_FILE % CHECKSUM_CHANGES_FILE self.data = self.data.encode() self.valid = { 'GOODSIG': ['D14219877A786561', 'John Wright '], 'VALIDSIG': ['8FEFE900783CF175827C2F65D14219877A786561', '2008-05-01', '1209623566', '0', '3', '0', '17', '2', '01', '8FEFE900783CF175827C2F65D14219877A786561'], 'SIG_ID': ['j3UjSpdky92fcQISbm8W5PlwC/g', '2008-05-01', '1209623566'], } def _validate_gpg_info(self, gpg_info): # The second part of the GOODSIG field could change if the primary # uid changes, so avoid checking that. Also, the first part of the # SIG_ID field has undergone at least one algorithm changein gpg, # so don't bother testing that either. self.assertEqual(set(gpg_info.keys()), set(self.valid.keys())) self.assertEqual(gpg_info['GOODSIG'][0], self.valid['GOODSIG'][0]) self.assertEqual(gpg_info['VALIDSIG'], self.valid['VALIDSIG']) self.assertEqual(gpg_info['SIG_ID'][1:], self.valid['SIG_ID'][1:]) def test_from_sequence_string(self): if not self.should_run: return gpg_info = deb822.GpgInfo.from_sequence(self.data) self._validate_gpg_info(gpg_info) def test_from_sequence_newline_terminated(self): if not self.should_run: return sequence = BytesIO(self.data) gpg_info = deb822.GpgInfo.from_sequence(sequence) self._validate_gpg_info(gpg_info) def test_from_sequence_no_newlines(self): if not self.should_run: return sequence = self.data.splitlines() gpg_info = deb822.GpgInfo.from_sequence(sequence) self._validate_gpg_info(gpg_info) def test_from_file(self): if not self.should_run: return fd, filename = tempfile.mkstemp() fp = os.fdopen(fd, 'wb') fp.write(self.data) fp.close() try: gpg_info = deb822.GpgInfo.from_file(filename) finally: os.remove(filename) self._validate_gpg_info(gpg_info) if __name__ == '__main__': unittest.main()