From 2ce129600509f83a8a3c866f10719cb4d22b250a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fatih=20A=C5=9F=C4=B1c=C4=B1?= Date: Tue, 27 Oct 2009 10:12:48 +0000 Subject: [PATCH] Move pisi-1.0_alpha2 tag --- tags/pisi-1.0_alpha2/CODING | 75 -- tags/pisi-1.0_alpha2/COPYING | 346 ------- tags/pisi-1.0_alpha2/MANIFEST.in | 4 - tags/pisi-1.0_alpha2/README | 12 - tags/pisi-1.0_alpha2/TODO | 159 --- tags/pisi-1.0_alpha2/doc/algorithm.sty | 79 -- tags/pisi-1.0_alpha2/doc/algorithmic.sty | 158 --- tags/pisi-1.0_alpha2/doc/dependency.tex | 381 ------- .../doc/images/pisi-gelistirici-small.png | Bin 51698 -> 0 bytes .../doc/images/pisi-gelistirici.odg | Bin 11112 -> 0 bytes .../doc/images/pisi-gelistirici.png | Bin 89333 -> 0 bytes .../doc/images/pisi-istemci-small.png | Bin 40089 -> 0 bytes .../doc/images/pisi-istemci.odg | Bin 11610 -> 0 bytes .../doc/images/pisi-istemci.png | Bin 73039 -> 0 bytes tags/pisi-1.0_alpha2/doc/prettyref.sty | 37 - tags/pisi-1.0_alpha2/pisi-cli | 54 - tags/pisi-1.0_alpha2/pisi.e3p | 351 ------- tags/pisi-1.0_alpha2/pisi/__init__.py | 26 - tags/pisi-1.0_alpha2/pisi/actionsapi/TODO | 11 - .../pisi/actionsapi/__init__.py | 20 - .../pisi/actionsapi/autotools.py | 142 --- .../pisi/actionsapi/coreutils.py | 77 -- tags/pisi-1.0_alpha2/pisi/actionsapi/get.py | 155 --- tags/pisi-1.0_alpha2/pisi/actionsapi/kde.py | 72 -- .../pisi/actionsapi/libtools.py | 66 -- .../pisi/actionsapi/pisitools.py | 219 ---- .../pisi/actionsapi/pisitoolsfunctions.py | 72 -- .../pisi/actionsapi/pythonmodules.py | 57 -- .../pisi/actionsapi/shelltools.py | 198 ---- .../pisi/actionsapi/variables.py | 82 -- tags/pisi-1.0_alpha2/pisi/api.py | 589 ----------- tags/pisi-1.0_alpha2/pisi/archive.py | 213 ---- tags/pisi-1.0_alpha2/pisi/build.py | 466 --------- tags/pisi-1.0_alpha2/pisi/cli/README | 23 - tags/pisi-1.0_alpha2/pisi/cli/__init__.py | 85 -- tags/pisi-1.0_alpha2/pisi/cli/colors.py | 60 -- tags/pisi-1.0_alpha2/pisi/cli/commands.py | 952 ------------------ tags/pisi-1.0_alpha2/pisi/cli/pisicli.py | 96 -- tags/pisi-1.0_alpha2/pisi/config.py | 61 -- tags/pisi-1.0_alpha2/pisi/configfile.py | 132 --- tags/pisi-1.0_alpha2/pisi/constants.py | 90 -- tags/pisi-1.0_alpha2/pisi/context.py | 36 - tags/pisi-1.0_alpha2/pisi/dependency.py | 134 --- tags/pisi-1.0_alpha2/pisi/fetcher.py | 154 --- tags/pisi-1.0_alpha2/pisi/files.py | 97 -- tags/pisi-1.0_alpha2/pisi/graph.py | 159 --- tags/pisi-1.0_alpha2/pisi/index.py | 98 -- tags/pisi-1.0_alpha2/pisi/install.py | 182 ---- tags/pisi-1.0_alpha2/pisi/installdb.py | 165 --- tags/pisi-1.0_alpha2/pisi/lockeddbshelve.py | 60 -- tags/pisi-1.0_alpha2/pisi/metadata.py | 150 --- tags/pisi-1.0_alpha2/pisi/operations.py | 105 -- tags/pisi-1.0_alpha2/pisi/package.py | 114 --- tags/pisi-1.0_alpha2/pisi/packagedb.py | 161 --- tags/pisi-1.0_alpha2/pisi/pgraph.py | 79 -- tags/pisi-1.0_alpha2/pisi/repodb.py | 105 -- tags/pisi-1.0_alpha2/pisi/sourcearchive.py | 69 -- tags/pisi-1.0_alpha2/pisi/sourcedb.py | 71 -- tags/pisi-1.0_alpha2/pisi/sourcefetcher.py | 80 -- tags/pisi-1.0_alpha2/pisi/specfile.py | 471 --------- tags/pisi-1.0_alpha2/pisi/specfilenew.py | 149 --- tags/pisi-1.0_alpha2/pisi/ui.py | 84 -- tags/pisi-1.0_alpha2/pisi/uri.py | 102 -- tags/pisi-1.0_alpha2/pisi/util.py | 374 ------- tags/pisi-1.0_alpha2/pisi/version.py | 161 --- tags/pisi-1.0_alpha2/pisi/xmlext.py | 163 --- tags/pisi-1.0_alpha2/pisi/xmlfile.py | 595 ----------- tags/pisi-1.0_alpha2/setup.py | 52 - tags/pisi-1.0_alpha2/tests/README | 10 - tags/pisi-1.0_alpha2/tests/a.xml | 23 - tags/pisi-1.0_alpha2/tests/actionsapitests.py | 51 - tags/pisi-1.0_alpha2/tests/archivetests.py | 116 --- tags/pisi-1.0_alpha2/tests/beta-light.sh | 20 - tags/pisi-1.0_alpha2/tests/beta-remoterepo.sh | 25 - tags/pisi-1.0_alpha2/tests/beta-upgrade.sh | 23 - tags/pisi-1.0_alpha2/tests/beta.sh | 45 - tags/pisi-1.0_alpha2/tests/configfiletests.py | 45 - tags/pisi-1.0_alpha2/tests/constantstests.py | 67 -- tags/pisi-1.0_alpha2/tests/fetchertests.py | 38 - tags/pisi-1.0_alpha2/tests/graphtests.py | 34 - .../tests/helloworld/actions.py | 102 -- .../tests/helloworld/pspec.xml | 35 - tags/pisi-1.0_alpha2/tests/installdbtests.py | 41 - tags/pisi-1.0_alpha2/tests/metadatatests.py | 43 - tags/pisi-1.0_alpha2/tests/packagedbtests.py | 43 - tags/pisi-1.0_alpha2/tests/packagetests.py | 49 - tags/pisi-1.0_alpha2/tests/pccts/actions.py | 53 - .../pccts/files/pccts-1.33.33-gentoo.diff | 69 -- tags/pisi-1.0_alpha2/tests/pccts/pspec.xml | 41 - tags/pisi-1.0_alpha2/tests/pisi.conf | 6 - tags/pisi-1.0_alpha2/tests/popt/actions.py | 25 - tags/pisi-1.0_alpha2/tests/popt/files.xml | 507 ---------- .../tests/popt/files/popt-1.7-uclibc.patch.gz | Bin 891 -> 0 bytes tags/pisi-1.0_alpha2/tests/popt/metadata.xml | 108 -- tags/pisi-1.0_alpha2/tests/popt/pspec.xml | 80 -- tags/pisi-1.0_alpha2/tests/run.py | 71 -- tags/pisi-1.0_alpha2/tests/sourcedbtests.py | 38 - tags/pisi-1.0_alpha2/tests/specfiletests.py | 80 -- tags/pisi-1.0_alpha2/tests/unzip/actions.py | 35 - .../tests/unzip/files/unzip-5.50-dotdot.patch | 85 -- tags/pisi-1.0_alpha2/tests/unzip/pspec.xml | 49 - tags/pisi-1.0_alpha2/tests/unzip2/actions.py | 37 - .../unzip2/files/unzip-5.50-dotdot.patch | 85 -- tags/pisi-1.0_alpha2/tests/unzip2/pspec.xml | 49 - tags/pisi-1.0_alpha2/tests/utiltests.py | 34 - tags/pisi-1.0_alpha2/tests/versiontests.py | 52 - tags/pisi-1.0_alpha2/tests/xmlfiletests.py | 73 -- tags/pisi-1.0_alpha2/tests/zip/actions.py | 32 - .../tests/zip/files/zcrypt.patch | 752 -------------- .../files/zip-2.3-unix_configure-pic.patch | 12 - .../tests/zip/files/zip-CAN-2004-1010.patch | 25 - tags/pisi-1.0_alpha2/tests/zip/pspec.xml | 50 - tags/pisi-1.0_alpha2/tests/zip2/actions.py | 34 - .../tests/zip2/files/zcrypt.patch | 752 -------------- .../files/zip-2.3-unix_configure-pic.patch | 12 - .../tests/zip2/files/zip-CAN-2004-1010.patch | 25 - tags/pisi-1.0_alpha2/tests/zip2/pspec.xml | 50 - tags/pisi-1.0_alpha2/tools/cat-db.py | 28 - tags/pisi-1.0_alpha2/tools/ebuild2pisi.py | 192 ---- tags/pisi-1.0_alpha2/tools/fetchAll.py | 58 -- tags/pisi-1.0_alpha2/tools/graphAllDeps.sh | 12 - .../tools/installAllAvailable.sh | 8 - tags/pisi-1.0_alpha2/tools/lspisi | 57 -- tags/pisi-1.0_alpha2/tools/repostats.py | 181 ---- 124 files changed, 14457 deletions(-) delete mode 100644 tags/pisi-1.0_alpha2/CODING delete mode 100644 tags/pisi-1.0_alpha2/COPYING delete mode 100644 tags/pisi-1.0_alpha2/MANIFEST.in delete mode 100644 tags/pisi-1.0_alpha2/README delete mode 100644 tags/pisi-1.0_alpha2/TODO delete mode 100644 tags/pisi-1.0_alpha2/doc/algorithm.sty delete mode 100644 tags/pisi-1.0_alpha2/doc/algorithmic.sty delete mode 100644 tags/pisi-1.0_alpha2/doc/dependency.tex delete mode 100644 tags/pisi-1.0_alpha2/doc/images/pisi-gelistirici-small.png delete mode 100644 tags/pisi-1.0_alpha2/doc/images/pisi-gelistirici.odg delete mode 100644 tags/pisi-1.0_alpha2/doc/images/pisi-gelistirici.png delete mode 100644 tags/pisi-1.0_alpha2/doc/images/pisi-istemci-small.png delete mode 100644 tags/pisi-1.0_alpha2/doc/images/pisi-istemci.odg delete mode 100644 tags/pisi-1.0_alpha2/doc/images/pisi-istemci.png delete mode 100644 tags/pisi-1.0_alpha2/doc/prettyref.sty delete mode 100755 tags/pisi-1.0_alpha2/pisi-cli delete mode 100644 tags/pisi-1.0_alpha2/pisi.e3p delete mode 100644 tags/pisi-1.0_alpha2/pisi/__init__.py delete mode 100644 tags/pisi-1.0_alpha2/pisi/actionsapi/TODO delete mode 100644 tags/pisi-1.0_alpha2/pisi/actionsapi/__init__.py delete mode 100644 tags/pisi-1.0_alpha2/pisi/actionsapi/autotools.py delete mode 100644 tags/pisi-1.0_alpha2/pisi/actionsapi/coreutils.py delete mode 100644 tags/pisi-1.0_alpha2/pisi/actionsapi/get.py delete mode 100644 tags/pisi-1.0_alpha2/pisi/actionsapi/kde.py delete mode 100644 tags/pisi-1.0_alpha2/pisi/actionsapi/libtools.py delete mode 100644 tags/pisi-1.0_alpha2/pisi/actionsapi/pisitools.py delete mode 100644 tags/pisi-1.0_alpha2/pisi/actionsapi/pisitoolsfunctions.py delete mode 100644 tags/pisi-1.0_alpha2/pisi/actionsapi/pythonmodules.py delete mode 100644 tags/pisi-1.0_alpha2/pisi/actionsapi/shelltools.py delete mode 100644 tags/pisi-1.0_alpha2/pisi/actionsapi/variables.py delete mode 100644 tags/pisi-1.0_alpha2/pisi/api.py delete mode 100644 tags/pisi-1.0_alpha2/pisi/archive.py delete mode 100644 tags/pisi-1.0_alpha2/pisi/build.py delete mode 100644 tags/pisi-1.0_alpha2/pisi/cli/README delete mode 100644 tags/pisi-1.0_alpha2/pisi/cli/__init__.py delete mode 100644 tags/pisi-1.0_alpha2/pisi/cli/colors.py delete mode 100644 tags/pisi-1.0_alpha2/pisi/cli/commands.py delete mode 100644 tags/pisi-1.0_alpha2/pisi/cli/pisicli.py delete mode 100644 tags/pisi-1.0_alpha2/pisi/config.py delete mode 100644 tags/pisi-1.0_alpha2/pisi/configfile.py delete mode 100644 tags/pisi-1.0_alpha2/pisi/constants.py delete mode 100644 tags/pisi-1.0_alpha2/pisi/context.py delete mode 100644 tags/pisi-1.0_alpha2/pisi/dependency.py delete mode 100644 tags/pisi-1.0_alpha2/pisi/fetcher.py delete mode 100644 tags/pisi-1.0_alpha2/pisi/files.py delete mode 100644 tags/pisi-1.0_alpha2/pisi/graph.py delete mode 100644 tags/pisi-1.0_alpha2/pisi/index.py delete mode 100644 tags/pisi-1.0_alpha2/pisi/install.py delete mode 100644 tags/pisi-1.0_alpha2/pisi/installdb.py delete mode 100644 tags/pisi-1.0_alpha2/pisi/lockeddbshelve.py delete mode 100644 tags/pisi-1.0_alpha2/pisi/metadata.py delete mode 100644 tags/pisi-1.0_alpha2/pisi/operations.py delete mode 100644 tags/pisi-1.0_alpha2/pisi/package.py delete mode 100644 tags/pisi-1.0_alpha2/pisi/packagedb.py delete mode 100644 tags/pisi-1.0_alpha2/pisi/pgraph.py delete mode 100644 tags/pisi-1.0_alpha2/pisi/repodb.py delete mode 100644 tags/pisi-1.0_alpha2/pisi/sourcearchive.py delete mode 100644 tags/pisi-1.0_alpha2/pisi/sourcedb.py delete mode 100644 tags/pisi-1.0_alpha2/pisi/sourcefetcher.py delete mode 100644 tags/pisi-1.0_alpha2/pisi/specfile.py delete mode 100644 tags/pisi-1.0_alpha2/pisi/specfilenew.py delete mode 100644 tags/pisi-1.0_alpha2/pisi/ui.py delete mode 100644 tags/pisi-1.0_alpha2/pisi/uri.py delete mode 100644 tags/pisi-1.0_alpha2/pisi/util.py delete mode 100644 tags/pisi-1.0_alpha2/pisi/version.py delete mode 100644 tags/pisi-1.0_alpha2/pisi/xmlext.py delete mode 100644 tags/pisi-1.0_alpha2/pisi/xmlfile.py delete mode 100755 tags/pisi-1.0_alpha2/setup.py delete mode 100644 tags/pisi-1.0_alpha2/tests/README delete mode 100644 tags/pisi-1.0_alpha2/tests/a.xml delete mode 100644 tags/pisi-1.0_alpha2/tests/actionsapitests.py delete mode 100644 tags/pisi-1.0_alpha2/tests/archivetests.py delete mode 100755 tags/pisi-1.0_alpha2/tests/beta-light.sh delete mode 100755 tags/pisi-1.0_alpha2/tests/beta-remoterepo.sh delete mode 100755 tags/pisi-1.0_alpha2/tests/beta-upgrade.sh delete mode 100755 tags/pisi-1.0_alpha2/tests/beta.sh delete mode 100644 tags/pisi-1.0_alpha2/tests/configfiletests.py delete mode 100644 tags/pisi-1.0_alpha2/tests/constantstests.py delete mode 100644 tags/pisi-1.0_alpha2/tests/fetchertests.py delete mode 100644 tags/pisi-1.0_alpha2/tests/graphtests.py delete mode 100644 tags/pisi-1.0_alpha2/tests/helloworld/actions.py delete mode 100644 tags/pisi-1.0_alpha2/tests/helloworld/pspec.xml delete mode 100644 tags/pisi-1.0_alpha2/tests/installdbtests.py delete mode 100644 tags/pisi-1.0_alpha2/tests/metadatatests.py delete mode 100644 tags/pisi-1.0_alpha2/tests/packagedbtests.py delete mode 100644 tags/pisi-1.0_alpha2/tests/packagetests.py delete mode 100644 tags/pisi-1.0_alpha2/tests/pccts/actions.py delete mode 100644 tags/pisi-1.0_alpha2/tests/pccts/files/pccts-1.33.33-gentoo.diff delete mode 100644 tags/pisi-1.0_alpha2/tests/pccts/pspec.xml delete mode 100644 tags/pisi-1.0_alpha2/tests/pisi.conf delete mode 100644 tags/pisi-1.0_alpha2/tests/popt/actions.py delete mode 100644 tags/pisi-1.0_alpha2/tests/popt/files.xml delete mode 100644 tags/pisi-1.0_alpha2/tests/popt/files/popt-1.7-uclibc.patch.gz delete mode 100644 tags/pisi-1.0_alpha2/tests/popt/metadata.xml delete mode 100644 tags/pisi-1.0_alpha2/tests/popt/pspec.xml delete mode 100755 tags/pisi-1.0_alpha2/tests/run.py delete mode 100644 tags/pisi-1.0_alpha2/tests/sourcedbtests.py delete mode 100644 tags/pisi-1.0_alpha2/tests/specfiletests.py delete mode 100644 tags/pisi-1.0_alpha2/tests/unzip/actions.py delete mode 100644 tags/pisi-1.0_alpha2/tests/unzip/files/unzip-5.50-dotdot.patch delete mode 100644 tags/pisi-1.0_alpha2/tests/unzip/pspec.xml delete mode 100644 tags/pisi-1.0_alpha2/tests/unzip2/actions.py delete mode 100644 tags/pisi-1.0_alpha2/tests/unzip2/files/unzip-5.50-dotdot.patch delete mode 100644 tags/pisi-1.0_alpha2/tests/unzip2/pspec.xml delete mode 100644 tags/pisi-1.0_alpha2/tests/utiltests.py delete mode 100644 tags/pisi-1.0_alpha2/tests/versiontests.py delete mode 100644 tags/pisi-1.0_alpha2/tests/xmlfiletests.py delete mode 100644 tags/pisi-1.0_alpha2/tests/zip/actions.py delete mode 100644 tags/pisi-1.0_alpha2/tests/zip/files/zcrypt.patch delete mode 100644 tags/pisi-1.0_alpha2/tests/zip/files/zip-2.3-unix_configure-pic.patch delete mode 100644 tags/pisi-1.0_alpha2/tests/zip/files/zip-CAN-2004-1010.patch delete mode 100644 tags/pisi-1.0_alpha2/tests/zip/pspec.xml delete mode 100644 tags/pisi-1.0_alpha2/tests/zip2/actions.py delete mode 100644 tags/pisi-1.0_alpha2/tests/zip2/files/zcrypt.patch delete mode 100644 tags/pisi-1.0_alpha2/tests/zip2/files/zip-2.3-unix_configure-pic.patch delete mode 100644 tags/pisi-1.0_alpha2/tests/zip2/files/zip-CAN-2004-1010.patch delete mode 100644 tags/pisi-1.0_alpha2/tests/zip2/pspec.xml delete mode 100755 tags/pisi-1.0_alpha2/tools/cat-db.py delete mode 100755 tags/pisi-1.0_alpha2/tools/ebuild2pisi.py delete mode 100755 tags/pisi-1.0_alpha2/tools/fetchAll.py delete mode 100755 tags/pisi-1.0_alpha2/tools/graphAllDeps.sh delete mode 100755 tags/pisi-1.0_alpha2/tools/installAllAvailable.sh delete mode 100755 tags/pisi-1.0_alpha2/tools/lspisi delete mode 100755 tags/pisi-1.0_alpha2/tools/repostats.py diff --git a/tags/pisi-1.0_alpha2/CODING b/tags/pisi-1.0_alpha2/CODING deleted file mode 100644 index a30e7cca..00000000 --- a/tags/pisi-1.0_alpha2/CODING +++ /dev/null @@ -1,75 +0,0 @@ -Like every serious project, there are guidelines. -Oooooo. "Coding Standards". - -Guidelines ----------- - -1. When using dirnames, don't expect the dir to end - with a trailing slash, and please use the dirnames - in pisiconfig -2. Python indentation is usually 4 chars. -3. Follow python philosophy of 'batteries included' -4. Don't make the code have runtime dependencies on - a particular distribution (as much as possible) -5. Don't assume narrow use cases. -6. If you are changing something, check if that change - breaks anything and fix breakage. For instance a - name. Running the tests is not always enough! - -Unit testing ------------- - -Unit tests are located in unittests directory. Running the tests is -trivial. But you must synchronize your code and data with the test -code, which can be a tedious work if you lose discipline. - -Sample data files are located in the same directory with test modules. - -For running the entire test suite, use the following command: - -$ ./unittests/run.py - -If you know what you are doing, you can run the tests seperately. But -keep in your mind that tests can depend on each other. (?) The unit test -system doesn't know about that. The following command will run tests -in specfiletests and archivetests in unittests dir: - -$ ./unittests/run.py specfile archive - - -Misc. Suggestions ------------------ - -1. Demeter's Law - -In OO programming, try to invoke Demeter's law. -One of the "rules" there is not directly accessing any -objects that are further than, 2/3 refs, away. So the -following code is OK. - destroy_system(a.system().name()) -but the following isn't as robust - destroy_system(object_store.root().a.system.name()) -As you can tell, this introduces too many implementation -dependencies. The rule of thumb is that, in these cases -this statement must have been elsewhere.... It may be a -good idea to not count the object scope in this case, -so in Python self.a means only one level of reference, -not two. - -One quibble with this: it may be preferable not to insist -on this where it would be inefficient. So if everything -is neatly packed into one object contained in another -object, why replicate everything in the upper level? If -the semantics prevents dependency changes, then chains -of 3 or even 4 could be acceptable. - -OTOH, in Python and C++, it's not always good to implement -accessor/modifier pairs for every property of an object. -It would be much simpler if you are not doing any special -processing on the property (e.g. if what the type system -does is sufficient). - -The main rule of thumb in Demeter's Law is avoiding -putting more than, say, 10 methods in a class. That works -really well in practice, forcing refactoring every now -and then. diff --git a/tags/pisi-1.0_alpha2/COPYING b/tags/pisi-1.0_alpha2/COPYING deleted file mode 100644 index 5ff207a8..00000000 --- a/tags/pisi-1.0_alpha2/COPYING +++ /dev/null @@ -1,346 +0,0 @@ -NOTE! The GPL below is copyrighted by the Free Software Foundation, but -the instance of code that it refers to (the kde programs) are copyrighted -by the authors who actually wrote it. - ---------------------------------------------------------------------------- - - GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 - - Copyright (C) 1989, 1991 Free Software Foundation, Inc. - 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -License is intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. This -General Public License applies to most of the Free Software -Foundation's software and to any other program whose authors commit to -using it. (Some other Free Software Foundation software is covered by -the GNU Library General Public License instead.) You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -this service if you wish), that you receive source code or can get it -if you want it, that you can change the software or use pieces of it -in new free programs; and that you know you can do these things. - - To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if you -distribute copies of the software, or if you modify it. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must give the recipients all the rights that -you have. You must make sure that they, too, receive or can get the -source code. And you must show them these terms so they know their -rights. - - We protect your rights with two steps: (1) copyright the software, and -(2) offer you this license which gives you legal permission to copy, -distribute and/or modify the software. - - Also, for each author's protection and ours, we want to make certain -that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, we -want its recipients to know that what they have is not the original, so -that any problems introduced by others will not reflect on the original -authors' reputations. - - Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that redistributors of a free -program will individually obtain patent licenses, in effect making the -program proprietary. To prevent this, we have made it clear that any -patent must be licensed for everyone's free use or not licensed at all. - - The precise terms and conditions for copying, distribution and -modification follow. - - GNU GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License applies to any program or other work which contains -a notice placed by the copyright holder saying it may be distributed -under the terms of this General Public License. The "Program", below, -refers to any such program or work, and a "work based on the Program" -means either the Program or any derivative work under copyright law: -that is to say, a work containing the Program or a portion of it, -either verbatim or with modifications and/or translated into another -language. (Hereinafter, translation is included without limitation in -the term "modification".) Each licensee is addressed as "you". - -Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running the Program is not restricted, and the output from the Program -is covered only if its contents constitute a work based on the -Program (independent of having been made by running the Program). -Whether that is true depends on what the Program does. - - 1. You may copy and distribute verbatim copies of the Program's -source code as you receive it, in any medium, provided that you -conspicuously and appropriately publish on each copy an appropriate -copyright notice and disclaimer of warranty; keep intact all the -notices that refer to this License and to the absence of any warranty; -and give any other recipients of the Program a copy of this License -along with the Program. - -You may charge a fee for the physical act of transferring a copy, and -you may at your option offer warranty protection in exchange for a fee. - - 2. You may modify your copy or copies of the Program or any portion -of it, thus forming a work based on the Program, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) You must cause the modified files to carry prominent notices - stating that you changed the files and the date of any change. - - b) You must cause any work that you distribute or publish, that in - whole or in part contains or is derived from the Program or any - part thereof, to be licensed as a whole at no charge to all third - parties under the terms of this License. - - c) If the modified program normally reads commands interactively - when run, you must cause it, when started running for such - interactive use in the most ordinary way, to print or display an - announcement including an appropriate copyright notice and a - notice that there is no warranty (or else, saying that you provide - a warranty) and that users may redistribute the program under - these conditions, and telling the user how to view a copy of this - License. (Exception: if the Program itself is interactive but - does not normally print such an announcement, your work based on - the Program is not required to print an announcement.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Program, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Program, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Program. - -In addition, mere aggregation of another work not based on the Program -with the Program (or with a work based on the Program) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may copy and distribute the Program (or a work based on it, -under Section 2) in object code or executable form under the terms of -Sections 1 and 2 above provided that you also do one of the following: - - a) Accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of Sections - 1 and 2 above on a medium customarily used for software interchange; or, - - b) Accompany it with a written offer, valid for at least three - years, to give any third party, for a charge no more than your - cost of physically performing source distribution, a complete - machine-readable copy of the corresponding source code, to be - distributed under the terms of Sections 1 and 2 above on a medium - customarily used for software interchange; or, - - c) Accompany it with the information you received as to the offer - to distribute corresponding source code. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form with such - an offer, in accord with Subsection b above.) - -The source code for a work means the preferred form of the work for -making modifications to it. For an executable work, complete source -code means all the source code for all modules it contains, plus any -associated interface definition files, plus the scripts used to -control compilation and installation of the executable. However, as a -special exception, the source code distributed need not include -anything that is normally distributed (in either source or binary -form) with the major components (compiler, kernel, and so on) of the -operating system on which the executable runs, unless that component -itself accompanies the executable. - -If distribution of executable or object code is made by offering -access to copy from a designated place, then offering equivalent -access to copy the source code from the same place counts as -distribution of the source code, even though third parties are not -compelled to copy the source along with the object code. - - 4. You may not copy, modify, sublicense, or distribute the Program -except as expressly provided under this License. Any attempt -otherwise to copy, modify, sublicense or distribute the Program is -void, and will automatically terminate your rights under this License. -However, parties who have received copies, or rights, from you under -this License will not have their licenses terminated so long as such -parties remain in full compliance. - - 5. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Program or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Program (or any work based on the -Program), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Program or works based on it. - - 6. Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the -original licensor to copy, distribute or modify the Program subject to -these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to -this License. - - 7. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Program at all. For example, if a patent -license would not permit royalty-free redistribution of the Program by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Program. - -If any portion of this section is held invalid or unenforceable under -any particular circumstance, the balance of the section is intended to -apply and the section as a whole is intended to apply in other -circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system, which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 8. If the distribution and/or use of the Program is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Program under this License -may add an explicit geographical distribution limitation excluding -those countries, so that distribution is permitted only in or among -countries not thus excluded. In such case, this License incorporates -the limitation as if written in the body of this License. - - 9. The Free Software Foundation may publish revised and/or new versions -of the General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - -Each version is given a distinguishing version number. If the Program -specifies a version number of this License which applies to it and "any -later version", you have the option of following the terms and conditions -either of that version or of any later version published by the Free -Software Foundation. If the Program does not specify a version number of -this License, you may choose any version ever published by the Free Software -Foundation. - - 10. If you wish to incorporate parts of the Program into other free -programs whose distribution conditions are different, write to the author -to ask for permission. For software which is copyrighted by the Free -Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals -of preserving the free status of all derivatives of our free software and -of promoting the sharing and reuse of software generally. - - NO WARRANTY - - 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -REPAIR OR CORRECTION. - - 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) 19yy - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - - -Also add information on how to contact you by electronic and paper mail. - -If the program is interactive, make it output a short notice like this -when it starts in an interactive mode: - - Gnomovision version 69, Copyright (C) 19yy name of author - Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, the commands you use may -be called something other than `show w' and `show c'; they could even be -mouse-clicks or menu items--whatever suits your program. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the program, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the program - `Gnomovision' (which makes passes at compilers) written by James Hacker. - - , 1 April 1989 - Ty Coon, President of Vice - -This General Public License does not permit incorporating your program into -proprietary programs. If your program is a subroutine library, you may -consider it more useful to permit linking proprietary applications with the -library. If this is what you want to do, use the GNU Library General -Public License instead of this License. diff --git a/tags/pisi-1.0_alpha2/MANIFEST.in b/tags/pisi-1.0_alpha2/MANIFEST.in deleted file mode 100644 index f11596b2..00000000 --- a/tags/pisi-1.0_alpha2/MANIFEST.in +++ /dev/null @@ -1,4 +0,0 @@ -recursive-include tests *.py -recursive-include doc * -recursive-include spec * -recursive-include samples * diff --git a/tags/pisi-1.0_alpha2/README b/tags/pisi-1.0_alpha2/README deleted file mode 100644 index 8c149544..00000000 --- a/tags/pisi-1.0_alpha2/README +++ /dev/null @@ -1,12 +0,0 @@ -PISI - Packages Installed Succesfully as Intented - -PISI is a new package manager implemented in python for the PARDUS -distribution. - -Main features - - - Implemented in python. - - All specification and intermediate data is in an XML format. - - Fast database access implemented with berkeley DB. - - Integrates both low-level and high-level package operations. - diff --git a/tags/pisi-1.0_alpha2/TODO b/tags/pisi-1.0_alpha2/TODO deleted file mode 100644 index ad4ac20b..00000000 --- a/tags/pisi-1.0_alpha2/TODO +++ /dev/null @@ -1,159 +0,0 @@ - - -PiSi TODO List -============== - -A list of tasks to accomplish, organized into priority sections - -Legend: - -- Todo -? Not determined if/how we have to do -/ In progress -+ Accomplished - -1. Pre-Alpha - - + reading spec file - + writing - + files - + metadata - + Actions API framework - + sample api modules (autotools) - + unit test - + source building - + package module - + extraction - + add file / compression - + package creation - + install database - + package database - + command line interface - + pisi-build - + pisi-install - + pisi-index - + pisi-updatedb - + single repository index - + simple dependency checking - + Package/Files - -2. Alpha - + multi-package dependency analysis (eray) - + design a package operation planner - + install/remove operations (eray) - + command line interface: - + pisi remove - + svn-like CLI - + configuration file - + define the format of the configuration file - + extend Config module (config.py) accordingly - + define configuration keys (baris - meren) - + query - + list of installed packages (eray) - + refactor actionsAPI (caglar) - + fix xml indentation (meren) - + define file types (doc, executable, conf, etc...) (baris - meren) - + write specfile (eray) - + file locking for database access [not tested!] (eray) - + implement file uri - + extend the modules dealing with files accordingly - + ui module improvements - + methods for interaction (yes, no questions, etc.) - + implement source database (eray) - + easy package preparation - + convert ebuild to pisi - + COMAR interface - + what do we need to specify in a package. - + package install: register config script - + package remove: unregister config script - + internet installation - + support URI's whereever a filename is supported - + http server - + pisi updatedb over internet - -3. Beta - - - configure-pending (eray) - / autoxml: automated xml processing (eray) - + design - + basic types - / list type - - class type - / localtext type - / components (eray) - + requirements - - xml format - - query components: list of components/packages in a component - - install/remove components - / API: we even have application users (eray) - / multiple package repository (eray) - + decide how to implement - + support repo order - - support medium types - - internet: http/ftp - - local repository: file:// - - removable media: media:// - / i18n support - / UI - ? pyqt UI class to interface - - PISIMAT - / pykde GUI (cartman) - + improve interface - + refactor UI, reintroduce base class - + add an ack interface, start cleaning up a bit - + cli - + ask alternatives to choose from (eray) - + eliminate \n's from infos, what's the point? - + metaclass coolness for CLI command framework (eray) - / overhaul installdb (try to merge its use with packagedb) (eray) - - partial caching and automatic resume for file download (meren) - - use a separate "partial/" subdir like urpmi - / exception handling (baris) - / better/more sensible exception hierarchy (eray) - ? recovery from exceptions where necessary - / query - - comar OM information (Provides) - + package by name, summary/description - - files (rpm -ql) - / actionsAPI documentation, unittests (caglar - meren) - / versioning information document - / verify metodlari - + SpecFile - + MetaData - + Files - - Index (o kadar onemli degil) - ? non-interactive use (baris) - + implement missing unit tests - + sourcedb - + package (baris) - + configuration file (baris) - + upgrade (eray) - + upgrade operation - + test upgrade op - + system-wide upgrade (upgrade-all) komutu - + incremental build (eray) - + generate binary release number by comparing MD5s (eray) - + keep track of successfully completed configure, make, install - steps (necessary for large-scale builds?) (meren) - + database locking bugs (eray) - + provide a library interface for users outside (YALI, TASMA). "import pisi" (baris) - -4. Release (bug fix, guzellestirme vs.) - -5. Post Release - - - a database of components: faster access - - more support for categories: - - put categories into a database - - fast query for categories - / make a package and sourcedb for each repo - + packagedb - - sourceb - - transaction stuff for database (eray) - - multi-architecture support (baris, caglar) - / design decisions - / extend XML specs to support that - - cross-platform building support - - incremental build/fetch for repository index (pisi-index.xml) - - diffsets (caglar) - diff --git a/tags/pisi-1.0_alpha2/doc/algorithm.sty b/tags/pisi-1.0_alpha2/doc/algorithm.sty deleted file mode 100644 index 843e3d5b..00000000 --- a/tags/pisi-1.0_alpha2/doc/algorithm.sty +++ /dev/null @@ -1,79 +0,0 @@ -% ALGORITHM STYLE -- Released 8 April 1996 -% for LaTeX-2e -% Copyright -- 1994 Peter Williams -% E-mail Peter.Williams@dsto.defence.gov.au -\NeedsTeXFormat{LaTeX2e} -\ProvidesPackage{algorithm} -\typeout{Document Style `algorithm' - floating environment} - -\RequirePackage{float} -\RequirePackage{ifthen} -\newcommand{\ALG@within}{nothing} -\newboolean{ALG@within} -\setboolean{ALG@within}{false} -\newcommand{\ALG@floatstyle}{ruled} -\newcommand{\ALG@name}{Algorithm} -\newcommand{\listalgorithmname}{List of \ALG@name s} - -% Declare Options -% first appearance -\DeclareOption{plain}{ - \renewcommand{\ALG@floatstyle}{plain} -} -\DeclareOption{ruled}{ - \renewcommand{\ALG@floatstyle}{ruled} -} -\DeclareOption{boxed}{ - \renewcommand{\ALG@floatstyle}{boxed} -} -% then numbering convention -\DeclareOption{part}{ - \renewcommand{\ALG@within}{part} - \setboolean{ALG@within}{true} -} -\DeclareOption{chapter}{ - \renewcommand{\ALG@within}{chapter} - \setboolean{ALG@within}{true} -} -\DeclareOption{section}{ - \renewcommand{\ALG@within}{section} - \setboolean{ALG@within}{true} -} -\DeclareOption{subsection}{ - \renewcommand{\ALG@within}{subsection} - \setboolean{ALG@within}{true} -} -\DeclareOption{subsubsection}{ - \renewcommand{\ALG@within}{subsubsection} - \setboolean{ALG@within}{true} -} -\DeclareOption{nothing}{ - \renewcommand{\ALG@within}{nothing} - \setboolean{ALG@within}{true} -} -\DeclareOption*{\edef\ALG@name{\CurrentOption}} - -% ALGORITHM -% -\ProcessOptions -\floatstyle{\ALG@floatstyle} -\ifthenelse{\boolean{ALG@within}}{ - \ifthenelse{\equal{\ALG@within}{part}} - {\newfloat{algorithm}{htbp}{loa}[part]}{} - \ifthenelse{\equal{\ALG@within}{chapter}} - {\newfloat{algorithm}{htbp}{loa}[chapter]}{} - \ifthenelse{\equal{\ALG@within}{section}} - {\newfloat{algorithm}{htbp}{loa}[section]}{} - \ifthenelse{\equal{\ALG@within}{subsection}} - {\newfloat{algorithm}{htbp}{loa}[subsection]}{} - \ifthenelse{\equal{\ALG@within}{subsubsection}} - {\newfloat{algorithm}{htbp}{loa}[subsubsection]}{} - \ifthenelse{\equal{\ALG@within}{nothing}} - {\newfloat{algorithm}{htbp}{loa}}{} -}{ - \newfloat{algorithm}{htbp}{loa} -} -\floatname{algorithm}{\ALG@name} - -\newcommand{\listofalgorithms}{\listof{algorithm}{\listalgorithmname}} - diff --git a/tags/pisi-1.0_alpha2/doc/algorithmic.sty b/tags/pisi-1.0_alpha2/doc/algorithmic.sty deleted file mode 100644 index 68f18aa0..00000000 --- a/tags/pisi-1.0_alpha2/doc/algorithmic.sty +++ /dev/null @@ -1,158 +0,0 @@ -% ALGORITHMIC STYLE -- Released 8 APRIL 1996 -% for LaTeX version 2e -% Copyright -- 1994 Peter Williams -% E-mail PeterWilliams@dsto.defence.gov.au -\NeedsTeXFormat{LaTeX2e} -\ProvidesPackage{algorithmic} -\typeout{Document Style `algorithmic' - environment} -% -\RequirePackage{ifthen} -\RequirePackage{calc} -\newboolean{ALC@noend} -\setboolean{ALC@noend}{false} -\newcounter{ALC@line} -\newcounter{ALC@rem} -\newlength{\ALC@tlm} -% -\DeclareOption{noend}{\setboolean{ALC@noend}{true}} -% -\ProcessOptions -% -% ALGORITHMIC -\newcommand{\algorithmicrequire}{\textbf{Require:}} -\newcommand{\algorithmicensure}{\textbf{Ensure:}} -\newcommand{\algorithmiccomment}[1]{\{#1\}} -\newcommand{\algorithmicend}{\textbf{end}} -\newcommand{\algorithmicif}{\textbf{if}} -\newcommand{\algorithmicthen}{\textbf{then}} -\newcommand{\algorithmicelse}{\textbf{else}} -\newcommand{\algorithmicelsif}{\algorithmicelse\ \algorithmicif} -\newcommand{\algorithmicendif}{\algorithmicend\ \algorithmicif} -\newcommand{\algorithmicfor}{\textbf{for}} -\newcommand{\algorithmicforall}{\textbf{for all}} -\newcommand{\algorithmicdo}{\textbf{do}} -\newcommand{\algorithmicendfor}{\algorithmicend\ \algorithmicfor} -\newcommand{\algorithmicwhile}{\textbf{while}} -\newcommand{\algorithmicendwhile}{\algorithmicend\ \algorithmicwhile} -\newcommand{\algorithmicloop}{\textbf{loop}} -\newcommand{\algorithmicendloop}{\algorithmicend\ \algorithmicloop} -\newcommand{\algorithmicrepeat}{\textbf{repeat}} -\newcommand{\algorithmicuntil}{\textbf{until}} -\def\ALC@item[#1]{% -\if@noparitem \@donoparitem - \else \if@inlabel \indent \par \fi - \ifhmode \unskip\unskip \par \fi - \if@newlist \if@nobreak \@nbitem \else - \addpenalty\@beginparpenalty - \addvspace\@topsep \addvspace{-\parskip}\fi - \else \addpenalty\@itempenalty \addvspace\itemsep - \fi - \global\@inlabeltrue -\fi -\everypar{\global\@minipagefalse\global\@newlistfalse - \if@inlabel\global\@inlabelfalse \hskip -\parindent \box\@labels - \penalty\z@ \fi - \everypar{}}\global\@nobreakfalse -\if@noitemarg \@noitemargfalse \if@nmbrlist \refstepcounter{\@listctr}\fi \fi -\sbox\@tempboxa{\makelabel{#1}}% -\global\setbox\@labels - \hbox{\unhbox\@labels \hskip \itemindent - \hskip -\labelwidth \hskip -\ALC@tlm - \ifdim \wd\@tempboxa >\labelwidth - \box\@tempboxa - \else \hbox to\labelwidth {\unhbox\@tempboxa}\fi - \hskip \ALC@tlm}\ignorespaces} -% -\newenvironment{algorithmic}[1][0]{ -\let\@item\ALC@item - \newcommand{\ALC@lno}{% -\ifthenelse{\equal{\arabic{ALC@rem}}{0}} -{{\footnotesize \arabic{ALC@line}:}}{}% -} -\let\@listii\@listi -\let\@listiii\@listi -\let\@listiv\@listi -\let\@listv\@listi -\let\@listvi\@listi -\let\@listvii\@listi - \newenvironment{ALC@g}{ - \begin{list}{\ALC@lno}{ \itemsep\z@ \itemindent\z@ - \listparindent\z@ \rightmargin\z@ - \topsep\z@ \partopsep\z@ \parskip\z@\parsep\z@ - \leftmargin 1em - \addtolength{\ALC@tlm}{\leftmargin} - } - } - {\end{list}} - \newcommand{\ALC@it}{\addtocounter{ALC@line}{1}\addtocounter{ALC@rem}{1}\ifthenelse{\equal{\arabic{ALC@rem}}{#1}}{\setcounter{ALC@rem}{0}}{}\item} - \newcommand{\ALC@com}[1]{\ifthenelse{\equal{##1}{default}}% -{}{\ \algorithmiccomment{##1}}} - \newcommand{\REQUIRE}{\item[\algorithmicrequire]} - \newcommand{\ENSURE}{\item[\algorithmicensure]} - \newcommand{\STATE}{\ALC@it} - \newcommand{\COMMENT}[1]{\algorithmiccomment{##1}} - \newenvironment{ALC@if}{\begin{ALC@g}}{\end{ALC@g}} - \newenvironment{ALC@for}{\begin{ALC@g}}{\end{ALC@g}} - \newenvironment{ALC@whl}{\begin{ALC@g}}{\end{ALC@g}} - \newenvironment{ALC@loop}{\begin{ALC@g}}{\end{ALC@g}} - \newenvironment{ALC@rpt}{\begin{ALC@g}}{\end{ALC@g}} - \renewcommand{\\}{\@centercr} - \newcommand{\IF}[2][default]{\ALC@it\algorithmicif\ ##2\ \algorithmicthen% -\ALC@com{##1}\begin{ALC@if}} - \newcommand{\ELSE}[1][default]{\end{ALC@if}\ALC@it\algorithmicelse% -\ALC@com{##1}\begin{ALC@if}} - \newcommand{\ELSIF}[2][default]% -{\end{ALC@if}\ALC@it\algorithmicelsif\ ##2\ \algorithmicthen% -\ALC@com{##1}\begin{ALC@if}} - \newcommand{\FOR}[2][default]{\ALC@it\algorithmicfor\ ##2\ \algorithmicdo% -\ALC@com{##1}\begin{ALC@for}} - \newcommand{\FORALL}[2][default]{\ALC@it\algorithmicforall\ ##2\ % -\algorithmicdo% -\ALC@com{##1}\begin{ALC@for}} - \newcommand{\WHILE}[2][default]{\ALC@it\algorithmicwhile\ ##2\ % -\algorithmicdo% -\ALC@com{##1}\begin{ALC@whl}} - \newcommand{\LOOP}[1][default]{\ALC@it\algorithmicloop% -\ALC@com{##1}\begin{ALC@loop}} - \newcommand{\REPEAT}[1][default]{\ALC@it\algorithmicrepeat% -\ALC@com{##1}\begin{ALC@rpt}} - \newcommand{\UNTIL}[1]{\end{ALC@rpt}\ALC@it\algorithmicuntil\ ##1} - \ifthenelse{\boolean{ALC@noend}}{ - \newcommand{\ENDIF}{\end{ALC@if}} - \newcommand{\ENDFOR}{\end{ALC@for}} - \newcommand{\ENDWHILE}{\end{ALC@whl}} - \newcommand{\ENDLOOP}{\end{ALC@loop}} - }{ - \newcommand{\ENDIF}{\end{ALC@if}\ALC@it\algorithmicendif} - \newcommand{\ENDFOR}{\end{ALC@for}\ALC@it\algorithmicendfor} - \newcommand{\ENDWHILE}{\end{ALC@whl}\ALC@it\algorithmicendwhile} - \newcommand{\ENDLOOP}{\end{ALC@loop}\ALC@it\algorithmicendloop} - } - \renewcommand{\@toodeep}{} - \begin{list}{\ALC@lno}{\setcounter{ALC@line}{0}\setcounter{ALC@rem}{0}% - \itemsep\z@ \itemindent\z@ \listparindent\z@% - \partopsep\z@ \parskip\z@ \parsep\z@% - \labelsep 0.5em \topsep 0.2em% -\ifthenelse{\equal{#1}{0}} - {\labelwidth 0.5em } - {\labelwidth 1.2em } -\leftmargin\labelwidth \addtolength{\leftmargin}{\labelsep} - \ALC@tlm\labelsep - } -} -{\end{list}} - - - - - - - - - - - - - - - diff --git a/tags/pisi-1.0_alpha2/doc/dependency.tex b/tags/pisi-1.0_alpha2/doc/dependency.tex deleted file mode 100644 index 0cff823e..00000000 --- a/tags/pisi-1.0_alpha2/doc/dependency.tex +++ /dev/null @@ -1,381 +0,0 @@ -%%% Local Variables: -%%% mode: latex -%%% TeX-master: t -%%% End: - -\documentclass[a4paper,11pt]{article} -\usepackage{graphicx} -\usepackage{algorithm} -\usepackage{algorithmic} -\usepackage{amsmath} -\usepackage{amstext} -\usepackage{amsfonts} -\usepackage{amsbsy} -\usepackage{amsthm} -\usepackage{prettyref} -%\newrefformat{alg}{Algorithm~\ref{#1}} -%\newrefformat{eq}{Equation~\ref{#1}} -%\newrefformat{lem}{Lemma~\ref{#1}} -%\newrefformat{thm}{Theorem~\ref{#1}} -%\newrefformat{chp}{Chapter~\ref{#1}} -%\newrefformat{sec}{Section~\ref{#1}} -%\newrefformat{apx}{Appendix~\ref{#1}} -%\newrefformat{tab}{Table~\ref{#1}} -%\newrefformat{fig}{Figure~\ref{#1}} -%usepackage[active]{srcltx} -\title{ Dependency Resolution in PISI} - -\author{Eray \"{O}zkural} - -\date{\today} - -\begin{document} - -\maketitle - -\section{Introduction} - -Dependency resolution in package management systems have a -significance in that they are the key to providing system stability -and internet upgrades. The scale of package databases requires the -dependency resolution mechanism to be efficient and correct, -motivating a closer look at the theory. - - -\section{Review} - -Dependency resolution has been taken in the most general setting as -the famous SAT problem of propositional logic. If we consider a system -$D$ of dependency statements $D_i$, each statement can be taken as a -proposition in propositional logic which states, for instance: - -$D_i$: if package $a$ is installed or package $b$ is installed, then -package $i$ is installable.\\ -... - -The system is thus understood as the conjunction of such facts, giving -us a logical programming formulation to determine installation -conditions. Note that for simplicity we do not consider the nuances in -upgrade and remove operations at the moment. - -However, using a SAT solver for this operation may be shooting a fly -with a bazooka. We observe that only certain forms of propositions -will be necessary for a dependency system. Furthermore, as we shall -see further constraints and optimizations may be required of the -system that are not modelled well with the SAT problem. - -We use a graph theoretic approach instead. A directed graph (digraph) -$G=(V,E)$ is formally a set of vertices $V$ and a set of edges $E$ -where each edge $(u,v)$ represents an edge from a vertex to another. -Accessor functions $V(G)$ and $E(G)$ yield the vertex and edge set of -the graph $G$. Topological sort of a graph gives a total ordering of -the vertices in which there are only forward edges. A vertex induced -subgraph of $G$ by vertex set $A$ contains only the vertex set $A$ and -edges incident to members of $A$. - -\section{Package operation planning} - -The dependency resolution problem may be viewed as a simple forward -chaining problem, where we would like to begin from an initial state -$S_0$ and by following allowable system transitions $t_i: S \to S$, -arrive at a desired system state $S_f$ (where $S$ is the set of all states). - -A system state $S_i$ is defined as the set of installed packages on -the system together with their versions, i.e. $S_i = \{ (x,v) : x -\text{ is installed}, v=version(x)\} $. An atomic system transition -$t_i$ chains one system state into another, making one ACID change on -the system. The usual atomic transitions are the single package -install, remove and reinstall (upgrade or downgrade) operations found -in low-level package management code of PISI. Note that in PISI, an -upgrade operation is identical to a remove operation followed by an -install operation (which sets it apart from some other packaging -systems). - -A package operation plan is thus naturally conceived of as a sequence -of atomic system transitions. Given an initial state and a final -state, the job of the package operation planner is to determine -whether there is a plan, and if so find the "best" one. - -Where there are no versions involved (e.g. upgrade/downgrade), we will -replace the pair $(x,v)$ with $x$ in the definitions for simplicity. - -\subsection{System consistency} - -It is worth mentioning here the concept of system consistency. As in a -database transaction, it is not acceptable that the system violates an -invariant afterwards. In the context of PISI, system consistency is -composed of two conditions for the current set of installed packages. -\begin{enumerate} -\item All package dependencies are satisfied (we may call this a - closed system) -\item No package conflicts are present. -\end{enumerate} - -Therefore, by atomic transition we also mean one that does not corrupt -system consistency. The system is thus never in an inconsistent -state. We will explain the conflicts later, for the present let us -look at the dependency condition. - -\subsection{Solving the simplest case with topological sorting} - -We will now concentrate on a simple form of the problem which can be -solved with topological sorting. This form is not concerned with -versions. Neither do we consider remote repositories. From an initial -set of packages $S_0$, we would like to install in addition a new set -$A$ of packages obtaining $S_f = S_0 \cup A$, for a static set of package -relations. - -The only relations considered are of the form: $a$ Depends on $b$, or -more briefly $aDb$. The graph of all such simple dependency relations -is a digraph $G$. For each dependency relation $aDb$, there is an -edge $a \to b$ in $G$. Accessing graph $G$ usually requires a database -operation and is therefore expensive. - -We now consider the digraph $G_A$ of the minimal set of simple -dependency relations which contains all information required to -construct a plan to install packages $A$. $G_A$ is a vertex induced -graph such that the fringe of $A$, e.g. vertices with out-degree $0$ -depend only on packages that are already installed (or none). Vertices -of $G_A$ are taken from $S_f$. First, let us explain the labelling -scheme. Already installed vertices are labelled with 'i'. Packages to -be added are labelled with 'a', and packages to be installed due to -dependencies are labelled with 'd'. We construct the graph as follows -\begin{algorithm} - \caption{$\textsc{Make-}G_A(G, A)$} - \label{alg:cons-graph} - \begin{algorithmic}[1] -\STATE $G_A \gets$ vertex induced subgraph of $G$ by $A$ labelled with 'a' -\REPEAT - \STATE done $\gets$ true - \FOR{each $u \in V(G_A)$ with out-degree $0$} - \FOR{ $v \in adj(u) $ of $G$} - \IF{$v \notin V(G_A)$} - \STATE done $\gets$ false - \IF{$v$ is installed} - \STATE label $v$ with 'i' - \ELSE - \STATE label $v$ with 'd' - \ENDIF - \STATE add $(u,v)$ to $G_A$ - \ENDIF - \ENDFOR - \ENDFOR -\UNTIL{done} -\end{algorithmic} -\end{algorithm} - -By this iterative expansion, we do a minimum number of database -accesses to $G$ and construct a dependency graph in memory. If the -$G_A$'s fringe has vertices with non 'i'-labels, then $A$ cannot be -installed. Otherwise, we find a topological sort $L$ of $G_A$, and in -the reverse order, install packages for vertices labelled with -'a' or 'd'. Observe that, by definition of a topological sort, -installing packages in the reverse order of a topological sort -guarantees that no package is installed before all of its dependencies -are installed. Thus, this yields a consistency-preserving plan. - -\subsection{Dependency conditions} - -In the PISI specification, we allow a dependency to specify a local -condition, for instance a program may require a dependency on -\texttt{libx} with pardus source release $3$ or greater. Another -program may require a dependency on a particular source release. These -conditions are local because they can be computed over the elements of -system state $S_i$, e.g. package (name, version) pairs. Let us denote this -condition by a predicate $P(b)$ such that $aDb$ iff $P(b)$. The -predicate $P$ for the dependency $aDb$ can be stored as edge data for -$(u,v)$ on the graph. -% thus when we say $P(u,v)$, this means the predicate stored on -%$(u,v)$ edge for vertex $v$. - -In this case, the vertices of the package dependency graph $G$ and the -planning graph $G_A$ retain the version information along with the -package name. The dependency relation thus holds between two pairs -$(p_1,v_1)$ and $(p_2,v_2)$, satisfying a given predicate -$P(p_2,v_2)$. When constructing the graph, we therefore take this -predicate into account and admit a new edge $(u,v)$, and thus a new -vertex $v$ into $G_A$ if and only if the target vertex satisfies -$P(v)$. - -\subsection{Conflicts and COMAR dependencies} - -The tags \texttt{Conflicts} and \texttt{Provides} in PISI are -inherited from Debian distribution. A conflict between two packages -($a$ conflicts with $b$) is a symmetric relation that prevents the -packages $a,b$ from being installed simultaneously (It is -sufficient that only one direction of the relation is declared, the -other direction is inferred). Provision in the form of $a$ provides $A$ -denotes that $a$ implements a virtual package abstraction $A$. - -In PISI, a package can provide an object of a COMAR Object Model (OM), -and is currently the only model of a ``virtual package''. In the -following example, let $a_1,a_2,\ldots,a_n$ provide the OM $A$. A package -can depend on another package's OM, for instance $b$ comar-depends on -$A$ (or in short form $bDA$) (Currently, conditions on virtual -dependencies are not supported). In this case, it is sufficient that -only one of the $a_i$ are installed. To resolve this, the user is -asked to choose from a list of alternatives immediately, since -otherwise there is unavoidable combinatorial explosion (in the form of -having to consider $\Pi_{bDA}num(A)$ graphs in the worst case where -$num(A)$ is the number of alternatives for comar OM $A$; the problem -is that there seems to be no simple solution to solve satisfiability -with arbitrary disjunctions in package dependency, short of a $SAT$ -solver). - -The resolution of conflicts to maintain system consistency condition -$2$ is easier to achieve. This can be satisfied by disallowing -installation of a package that would violate the condition, or -removing currently installed packages which conflict with the newly -installed package and its dependencies. In most package managers, the -second option is confirmed by the user for making it easier. In the -install operation, after constructing the partial dependency graph -$G_A$, we merely have to check whether any conflict appears among the -vertices of $G_A$. If so, then the operation is untenable, since $G_A$ -shows the future state of the installed system. Since a conflict is -symmetric, it is represented as a bidirectional edge $a \leftrightarrow b$. To -distinguish dependencies from conflicts, the edges would have to be -labelled in this case, for instance with 'd' and 'c'. The removal -option can be implemented by invoking a multi-package remove operation -on the packages in conflict. - -\subsection{Remove operation} - -Dependency resolution for remove operation is similar to install. The -only significant difference is that we remove the packages in the -topological order rather than installing packages in the reverse -topological order. - -\section{Remote repositories and upgrade operation} - - -The upgrade operation is more complicated. First of all, the system -has to distinguish between the current relation graph (e.g. -dependencies and conflicts), and the future relation graph which may -be different in rather important aspects. In theory, we allow any -dependency and conflict to change. Therefore, we have a $G_0$ which -represents the current relations (among installed packages) in the -system, and a $G_f$ which is probably taken from a remote package -repository. We begin by noting that $G_0$ and $G_f$ have to be -compatible. That is, to say, if a package $(p_1,v_1)$ is shared across -two graphs, then the declarations made by the package are one and the -same. - -$G_0$ can be calculated from the package information (e.g. metadata) -of the installed packages and is stored by PISI in a dedicated -database. $G_f$ is most likely constructed from a PISI Index file -corresponding to a particular package repository. Accessing both of -these entities is expensive and we should take care to minimize access -as in the previous section. - -To preserve consistency during individual transitions, the planner can -choose to remove a minimum number of packages from the system to bring -it to a clean state, and then install the new versions of these -packages in the correct order. Let us assume that it is indeed -possible to achieve this ``clean state''. Apparently, this is not -always possible because other packages may depend on the package(s) to -upgrade. At any rate, to achieve this, first we need to -calculate subgraphs of $G_0$ and $G_f$. We can calculate alternative -plans from these subgraphs if need be. - -Let $A$ be the set of packages to be upgraded from a given repository. -$G_{A,0}$ is the subgraph of $G_{0}$ induced by the ``upgrade -closure'' of $A$. The ``upgrade closure'' of a set $A$ of packages is -defined as a minimal set of packages $B \supseteq A$ such that there is no -package in $B$ that requires an upgrade for $A$ to be upgraded. This -is found by assuming that the current system state $S_0$ is -consistent, and by constructing a relation graph of the future state -of the system to detect the dependencies that have changed. - -Obviously, to make a plan, we must first know the goal state. In a -multi-package upgrade, the exact details of the goal state depend on -the graph $G_f$ of the repository. Thus, we construct a graph -$G_{A,f}$ that is a vertex-induced subgraph of $G_f$ such that it -contains all information relevant to upgrading packages $A$. We begin -by a vertex induced subgraph of $G_f$ by $A$. These are the packages -that will be upgraded in any case. Then, we make a pass on the -vertices, and look at all the outgoing edges, we compare whether this -edge has changed in any substantial way from the previous version. In -particular, we are interested in whether the predicate of the edge is -valid for the version of the same package in our current system. Every -compared vertex in this manner is marked done, and the edges not valid -for the current system pull new unmarked vertices into $G_f$, this -continues until there are no unmarked vertices left. Hence, the -vertices of $G_f$ are the packages that must be upgraded. - -To actually carry out the upgrade a strategy is to upgrade all the -packages in $G_{A,f}$ in some order. A good order is again the reverse -topological order order, in fact, the upgrade operation is merely a -special case of a multi-package installation code that can install -from a remote repository, since a multi-package installation can -contain upgrades in addition to new packages. However, in case no -package depends on the packages to be upgraded, then we can carry out -a completely consistency-preserving plan as discussed above. The -conflicts are resolved in the usual fashion, by removing those -packages in conflict with new packages that are installed. This can be -accomplished by invoking a remove operation prior to the upgrade -operation. - -\section{Examples} - -\subsection{A single package upgrade} - -goal: upgrade $(a,1)$ to $(a,2)$\\ -\\ -rules:\\ - $(a,1)$ depends on $(b,1), (c,1)$ \\ - $(a,1)$ conflicts with $(d,1)$\\ - $(a,2)$ depends on $(c,3), (d,2)$\\ - $(a,2)$ conflicts with $(b,1)$\\ -\\ -initial state:\\ - $(a,1), (b,1), (c,1)$ installed \\ - -In this case, we can find a consistency-preserving plan in terms of -install and remove operations. -\\ -plan:\\ - remove $(a,1)$\\ - remove $(b,1)$\\ - remove $(c,1)$\\ - install $(c,3)$\\ - install $(d,2)$\\ - install $(a,2)$\\ - -\subsection{Another upgrade} - -goal: upgrade $(b,1) \to (b,2)$\\ -\\ -current dep: $(a,1) \to[=1] (b,1) \to[=1] (c,1) \to[=1] (d,1)$\\ -repo dep: \nobreakspace{} \nobreakspace$(a,1) \to[=2] (b,2) \to[=2] (c,2) \to[=1] (d,1)$\\ - -In this case, we cannot remove $(b,1)$ because it's locked in the -chain. In fact, here there is no consistency-preserving plan in terms -of atomic single package transitions: install, remove, upgrade. In -these cases, it seems best to resort to upgrade in place, and in the -reverse topological order of dependencies. -\\ -plan:\\ - upgrade $(c,1) \to (c,2)$\\ - upgrade $(b,1) \to (b,2)$\\ - - -\subsection{A multi package remove} - -goal: remove $(a,2), (b,3), (c,2)$\\ -\\ -rules:\\ - $(c,2)$ depends $(a,2)$\\ - $(d,2)$ depends on $(b,3), (c,2)$\\ - $(e,1)$ depends on $(a,2)$\\ - $(f,2)$ depends on $(e,1)$\\ - $(g,2)$ depends on $(e,1)$\\\\ -plan:\\ - remove $(f,2)$\\ - remove $(g,2)$\\ - remove $(e,1)$\\ - remove $(d,2)$\\ - remove $(b,3)$\\ - remove $(c,2)$\\ - remove $(a,2)$\\ -\end{document} - diff --git a/tags/pisi-1.0_alpha2/doc/images/pisi-gelistirici-small.png b/tags/pisi-1.0_alpha2/doc/images/pisi-gelistirici-small.png deleted file mode 100644 index a8b4d4dc5f5e7ed2bd314b492221fa00c1d1ad28..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 51698 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 z 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 t|Gfu;Cg6YX0RoNx_oM&!)5O>Q3)@c9$ diff --git a/tags/pisi-1.0_alpha2/doc/images/pisi-gelistirici.odg b/tags/pisi-1.0_alpha2/doc/images/pisi-gelistirici.odg deleted file mode 100644 index d124f0a5110ef1b2ec8c7a9f7e07fafb2be95d13..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11112 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

e2vod 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

#U)z#3q-n`XGUym(#b<|GA5Uv_b`8MhDJ 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 - - - - - - Python - Qt - The package management software of Pardus distribution. - - PiSi Development Team - - - -

tools - ebuild2pisi.py - - - tools - cat-db.py - - - tests - popt - actions.py - - - tests - zip - actions.py - - - tests - unzip - actions.py - - - tests - configfiletests.py - - - tests - sourcedbtests.py - - - tests - utiltests.py - - - tests - installdbtests.py - - - tests - run.py - - - tests - metadatatests.py - - - tests - constantstests.py - - - tests - fetchertests.py - - - tests - archivetests.py - - - tests - versiontests.py - - - tests - actionsapitests.py - - - tests - specfiletests.py - - - tests - packagetests.py - - - tests - packagedbtests.py - - - tests - graphtests.py - - - pisi - actionsapi - get.py - - - pisi - actionsapi - shelltools.py - - - pisi - actionsapi - variables.py - - - pisi - actionsapi - autotools.py - - - pisi - actionsapi - pisitools.py - - - pisi - actionsapi - kde.py - - - pisi - actionsapi - __init__.py - - - pisi - actionsapi - libtools.py - - - pisi - actionsapi - coreutils.py - - - pisi - cli - pisicli.py - - - pisi - cli - __init__.py - - - pisi - cli - commands.py - - - pisi - lockeddbshelve.py - - - pisi - specfile.py - - - pisi - package.py - - - pisi - __init__.py - - - pisi - build.py - - - pisi - xmlfile.py - - - pisi - packagedb.py - - - pisi - files.py - - - pisi - config.py - - - pisi - index.py - - - pisi - repodb.py - - - pisi - ui.py - - - pisi - util.py - - - pisi - dependency.py - - - pisi - metadata.py - - - pisi - pgraph.py - - - pisi - operations.py - - - pisi - context.py - - - pisi - sourcefetcher.py - - - pisi - graph.py - - - pisi - sourcearchive.py - - - pisi - configfile.py - - - pisi - sourcedb.py - - - pisi - install.py - - - pisi - installdb.py - - - pisi - constants.py - - - pisi - fetcher.py - - - pisi - archive.py - - - pisi - xmlext.py - - - pisi - version.py - - - setup.py - - - pisi - actionsapi - pisitoolsfunctions.py - - - tools - repostats.py - - - pisi - uri.py - - - pisi - api.py - - - tests - xmlfiletests.py - - - pisi - specfilenew.py - - - TODO - - - - - - - - - - - pisi-cli - - - tests - beta-light.sh - - - tests - beta-upgrade.sh - - - tests - beta.sh - - - tests - sandbox - - - - pisi-cli - - - Subversion - {'status': [], 'log': [], 'global': [], 'update': [], 'remove': [], 'add': [], 'tag': [], 'export': [], 'diff': [], 'commit': [], 'checkout': [], 'history': []} - {'standardLayout': 1} - - - - - - - - - diff --git a/tags/pisi-1.0_alpha2/pisi/__init__.py b/tags/pisi-1.0_alpha2/pisi/__init__.py deleted file mode 100644 index 2aab1609..00000000 --- a/tags/pisi-1.0_alpha2/pisi/__init__.py +++ /dev/null @@ -1,26 +0,0 @@ -# Copyright (C) 2005, TUBITAK/UEKAE -# -# 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. -# -# Please read the COPYING file. -# - -# PiSi version - -__version__ = "1.0_alpha2" - -__all__ = [ 'api', 'packagedb', 'installdb' ] - -class Error(Exception): - """Class of exceptions that lead to program termination""" - pass - -class Exception(Exception): - """Class of exceptions that must be caught and handled within PISI""" - pass - -# FIXME: can't do this due to name clashes in config and other singletons booo -#from pisi.api import * diff --git a/tags/pisi-1.0_alpha2/pisi/actionsapi/TODO b/tags/pisi-1.0_alpha2/pisi/actionsapi/TODO deleted file mode 100644 index c4c65e35..00000000 --- a/tags/pisi-1.0_alpha2/pisi/actionsapi/TODO +++ /dev/null @@ -1,11 +0,0 @@ -ReFactor ActionsAPI: - -pisitools.py - %90 -autotools.py - %100 -get.py - %75 -kde.py - %75 -libtools.py - %90 -pisitoolsfunctions.py - %75 -shelltools.py - %75 -variables.py - %100 -coreutils.py - %100 diff --git a/tags/pisi-1.0_alpha2/pisi/actionsapi/__init__.py b/tags/pisi-1.0_alpha2/pisi/actionsapi/__init__.py deleted file mode 100644 index 16bacc25..00000000 --- a/tags/pisi-1.0_alpha2/pisi/actionsapi/__init__.py +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright (C) 2005, TUBITAK/UEKAE -# -# 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. -# -# Please read the COPYING file. -# - -# pisi.actionsapi version -__version__ = '1.0_alpha2' - -import pisi - -class Error(pisi.Error): - pass - -class Exception(pisi.Exception): - pass diff --git a/tags/pisi-1.0_alpha2/pisi/actionsapi/autotools.py b/tags/pisi-1.0_alpha2/pisi/actionsapi/autotools.py deleted file mode 100644 index 51d3171d..00000000 --- a/tags/pisi-1.0_alpha2/pisi/actionsapi/autotools.py +++ /dev/null @@ -1,142 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# -# Copyright (C) 2005, TUBITAK/UEKAE -# -# 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. -# -# Please read the COPYING file. - -# Standard Python Modules -import os - -# Pisi Modules -import pisi.context as ctx - -# ActionsAPI Modules -import pisi.actionsapi -import pisi.actionsapi.get as get -from pisi.actionsapi.shelltools import system -from pisi.actionsapi.shelltools import can_access_file -from pisi.actionsapi.libtools import gnuconfig_update - -class ConfigureError(pisi.actionsapi.Error): - def __init__(self, Exception): - ctx.ui.error(Exception) - if can_access_file('config.log'): - ctx.ui.error('\n!!! Please attach the config.log to your bug report:\n%s/config.log\n' % os.getcwd()) - -class MakeError(pisi.actionsapi.Error): - def __init__(self, Exception): - ctx.ui.error(Exception) - -class InstallError(pisi.actionsapi.Error): - def __init__(self, Exception): - ctx.ui.error(Exception) - -class RunTimeError(pisi.actionsapi.Error): - def __init__(self, Exception): - ctx.ui.error(Exception) - -def configure(parameters = ''): - '''configure source with given parameters = "--with-nls --with-libusb --with-something-usefull"''' - if can_access_file('configure'): - gnuconfig_update() - - args = './configure \ - --prefix=/%s \ - --host=%s \ - --mandir=/%s \ - --infodir=/%s \ - --datadir=/%s \ - --sysconfdir=/%s \ - --localstatedir=/%s \ - %s' % (get.defaultprefixDIR(), \ - get.HOST(), get.manDIR(), \ - get.infoDIR(), get.dataDIR(), \ - get.confDIR(), get.localstateDIR(), parameters) - - if system(args): - raise ConfigureError('!!! Configure failed...\n') - else: - raise ConfigureError('!!! No configure script found...\n') - -def rawConfigure(parameters = ''): - '''configure source with given parameters = "--prefix=/usr --libdir=/usr/lib --with-nls"''' - if can_access_file('configure'): - gnuconfig_update() - - if system('./configure %s' % parameters): - raise ConfigureError('!!! Configure failed...\n') - else: - raise ConfigureError('!!! No configure script found...\n') - -def compile(parameters = ''): - #FIXME: Only one package uses this until now, hmmm - system('%s %s %s' % (get.GCC(), get.CFLAGS(), parameters)) - -def make(parameters = ''): - '''make source with given parameters = "all" || "doc" etc.''' - if system('make %s' % parameters): - raise MakeError('!!! Make failed...\n') - -def install(parameters = '', argument = 'install'): - '''install source into install directory with given parameters''' - if can_access_file('makefile') or can_access_file('Makefile') or can_access_file('GNUmakefile'): - args = 'make prefix=%(prefix)s/%(defaultprefix)s \ - datadir=%(prefix)s/%(data)s \ - infodir=%(prefix)s/%(info)s \ - localstatedir=%(prefix)s/%(localstate)s \ - mandir=%(prefix)s/%(man)s \ - sysconfdir=%(prefix)s/%(conf)s \ - %(parameters)s \ - %(argument)s' % {'prefix': get.installDIR(), - 'defaultprefix': get.defaultprefixDIR(), - 'man': get.manDIR(), - 'info': get.infoDIR(), - 'localstate': get.localstateDIR(), - 'conf': get.confDIR(), - 'data': get.dataDIR(), - 'parameters': parameters, - 'argument':argument} - - if system(args): - raise InstallError('!!! Install failed...\n') - else: - raise InstallError('!!! No Makefile found...\n') - -def rawInstall(parameters = '', argument = 'install'): - '''install source into install directory with given parameters = PREFIX=%s % get.installDIR()''' - if can_access_file('makefile') or can_access_file('Makefile') or can_access_file('GNUmakefile'): - if system('make %s %s' % (parameters, argument)): - raise InstallError('!!! Install failed...\n') - else: - raise InstallError('!!! No Makefile found...\n') - -def aclocal(parameters = ''): - '''generates an aclocal.m4 based on the contents of configure.in.''' - if system('aclocal %s' % parameters): - raise RunTimeError('!!! Running aclocal failed...') - -def autoconf(parameters = ''): - '''generates a configure script''' - if system('autoconf %s' % parameters): - raise RunTimeError('!!! Running autoconf failed...') - -def autoreconf(parameters = ''): - '''re-generates a configure script''' - if system('autoreconf %s' % parameters): - raise RunTimeError('!!! Running autoconf failed...') - -def automake(parameters = ''): - '''generates a makefile''' - if system('automake %s' % parameters): - raise RunTimeError('!!! Running automake failed...') - -def autoheader(parameters = ''): - '''generates templates for configure''' - if system('autoheader %s' % parameters): - raise RunTimeError('!!! Running autoheader failed...') diff --git a/tags/pisi-1.0_alpha2/pisi/actionsapi/coreutils.py b/tags/pisi-1.0_alpha2/pisi/actionsapi/coreutils.py deleted file mode 100644 index 3e52c340..00000000 --- a/tags/pisi-1.0_alpha2/pisi/actionsapi/coreutils.py +++ /dev/null @@ -1,77 +0,0 @@ -#!/usr/bin/python -#-*- coding: utf-8 -*- -# -# Copyright (C) 2005, TUBITAK/UEKAE -# -# 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. -# -# Please read the COPYING file. - -# Standard Python Modules -import sys -import re -from itertools import izip, imap, count, ifilter, ifilterfalse - -# ActionsAPI -import pisi.actionsapi - -def cat(filename): - return file(filename).xreadlines() - -class grep: - '''keep only lines that match the regexp''' - def __init__(self, pat, flags = 0): - self.fun = re.compile(pat, flags).match - def __ror__(self, input): - return ifilter(self.fun, input) - -class tr: - '''apply arbitrary transform to each sequence element''' - def __init__(self, transform): - self.tr = transform - def __ror__(self, input): - return imap(self.tr, input) - -class printto: - '''print sequence elements one per line''' - def __init__(self, out = sys.stdout): - self.out = out - def __ror__(self,input): - for line in input: - print >> self.out, line - -printlines = printto(sys.stdout) - -class terminator: - def __init__(self,method): - self.process = method - def __ror__(self,input): - return self.process(input) - -aslist = terminator(list) -asdict = terminator(dict) -astuple = terminator(tuple) -join = terminator(''.join) -enum = terminator(enumerate) - -class sort: - def __ror__(self,input): - ll = list(input) - ll.sort() - return ll -sort = sort() - -class uniq: - def __ror__(self,input): - for i in input: - try: - if i == prev: - continue - except NameError: - pass - prev = i - yield i -uniq = uniq() diff --git a/tags/pisi-1.0_alpha2/pisi/actionsapi/get.py b/tags/pisi-1.0_alpha2/pisi/actionsapi/get.py deleted file mode 100644 index 521598e8..00000000 --- a/tags/pisi-1.0_alpha2/pisi/actionsapi/get.py +++ /dev/null @@ -1,155 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# -# Copyright (C) 2005, TUBITAK/UEKAE -# -# 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. -# -# Please read the COPYING file. - -# Standart Python Modules -import os - -# PISI Modules -import pisi.actionsapi -import pisi.context as ctx - -# ActionsAPI Modules -from variables import glb - -class BinutilsError(pisi.actionsapi.Error): - pass - -env = glb.env -dirs = glb.dirs - -def curDIR(): - return os.getcwd() - -def ENV(environ): - return os.environ[environ]; - -# variables.Env - -def pkgDIR(): - return env.pkg_dir - -def workDIR(): - return env.work_dir - -def installDIR(): - return env.install_dir - -def srcNAME(): - return env.src_name - -def srcVERSION(): - return env.src_version - -def srcRELEASE(): - return env.src_release - -def srcTAG(): - return env.src_name + '-' + env.src_version + '-' + env.src_release - -def srcDIR(): - return env.src_name + '-' + env.src_version - -def HOST(): - return env.host - -def CFLAGS(): - return env.cflags - -def CXXFLAGS(): - return env.cxxflags - -def LDFLAGS(): - return env.ldflags - -# variables.Dirs - -def docDIR(): - return dirs.doc - -def sbinDIR(): - return dirs.sbin - -def infoDIR(): - return dirs.info - -def manDIR(): - return dirs.man - -def dataDIR(): - return dirs.data - -def confDIR(): - return dirs.conf - -def localstateDIR(): - return dirs.localstate - -def defaultprefixDIR(): - return dirs.defaultprefix - -def kdeDIR(): - return dirs.kde - -def qtDIR(): - return dirs.qt - -def qtLIBDIR(): - return '%s/lib/' % qtDIR() - -# Binutils Variables - -def exists_binary(bin): - # determine if path has binary - path = os.environ['PATH'].split(':') - for directory in path: - if os.path.exists(os.path.join(directory, bin) ): - return True - return False - -def getBinutilsInfo(util): - cross_build_name = '%s-%s' % (HOST(), util) - if not exists_binary(cross_build_name): - if not exists_binary(util): - raise BinutilsError('util %s cannot be found' % util) - else: - ctx.ui.debug('Warning: %s does not exist, using plain name %s' \ - % (cross_build_name, util)) - return util - else: - return cross_build_name - -def AR(): - return getBinutilsInfo('ar') - -def AS(): - return getBinutilsInfo('as') - -def CC(): - return getBinutilsInfo('gcc') - -def CXX(): - return getBinutilsInfo('g++') - -def LD(): - return getBinutilsInfo('ld') - -def NM(): - return getBinutilsInfo('nm') - -def RANLIB(): - return getBinutilsInfo('ranlib') - -def F77(): - return getBinutilsInfo('f77') - -def GCJ(): - return getBinutilsInfo('gcj') diff --git a/tags/pisi-1.0_alpha2/pisi/actionsapi/kde.py b/tags/pisi-1.0_alpha2/pisi/actionsapi/kde.py deleted file mode 100644 index a764fc7a..00000000 --- a/tags/pisi-1.0_alpha2/pisi/actionsapi/kde.py +++ /dev/null @@ -1,72 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# -# Copyright (C) 2005, TUBITAK/UEKAE -# -# 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. -# -# Please read the COPYING file. - -# standard python modules -import os - -# Pisi Modules -import pisi.context as ctx - -# ActionsAPI Modules -import pisi.actionsapi -import pisi.actionsapi.get as get -from pisi.actionsapi.shelltools import system, can_access_file - -class ConfigureError(pisi.actionsapi.Error): - def __init__(self, Exception): - ctx.ui.error(Exception) - if can_access_file('config.log'): - ctx.ui.error('\n!!! Please attach the config.log to your bug report:\n%s/config.log' % os.getcwd()) - -class MakeError(pisi.actionsapi.Error): - def __init__(self, Exception): - ctx.ui.error(Exception) - -class InstallError(pisi.actionsapi.Error): - def __init__(self, Exception): - ctx.ui.error(Exception) - -def configure(parameters = ''): - ''' parameters = '--with-nls --with-libusb --with-something-usefull ''' - if can_access_file('configure'): - args = './configure \ - --prefix=%s \ - --host=%s \ - --with-x \ - --enable-mitshm \ - --with-xinerama \ - --with-qt-dir=%s \ - --enable-mt \ - --with-qt-libraries=%s \ - --enable-final \ - --disable-dependency-tracking \ - --disable-debug \ - %s' % (get.kdeDIR(), get.HOST(), get.qtDIR(), get.qtLIBDIR(), parameters) - - if system(args): - raise ConfigureError('!!! Configure failed...\n') - else: - raise ConfigureError('!!! No configure script found...\n') - -def make(parameters = ''): - '''make source with given parameters = "all" || "doc" etc.''' - if system('make %s' % parameters): - raise MakeError('!!! Make failed...\n') - -def install(parameters = 'install'): - if can_access_file('Makefile'): - args = 'make DESTDIR=%s destdir=%s %s' % (get.installDIR(), get.installDIR(), parameters) - - if system(args): - raise InstallError('!!! Install failed...\n') - else: - raise InstallError('!!! No Makefile found...\n') diff --git a/tags/pisi-1.0_alpha2/pisi/actionsapi/libtools.py b/tags/pisi-1.0_alpha2/pisi/actionsapi/libtools.py deleted file mode 100644 index a73f9d57..00000000 --- a/tags/pisi-1.0_alpha2/pisi/actionsapi/libtools.py +++ /dev/null @@ -1,66 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# -# Copyright (C) 2005, TUBITAK/UEKAE -# -# 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. -# -# Please read the COPYING file. - -# Standard Python Modules -import os - -# Pisi-Core Modules -import pisi.context as ctx - -# ActionsAPI Modules -import pisi.actionsapi -from pisi.actionsapi.shelltools import * -import pisi.actionsapi.get as get - -class RunTimeError(pisi.actionsapi.Error): - def __init__(self, Exception): - ctx.ui.error(Exception) - -def preplib(sourceDirectory = '/usr/lib'): - sourceDirectory = get.installDIR() + sourceDirectory - if can_access_directory(sourceDirectory): - if system('/sbin/ldconfig -n -N %s' % sourceDirectory): - raise RunTimeError('!!! Running ldconfig failed...') - -def preplib_so(sourceDirectory): - pass - -def gnuconfig_update(): - ''' copy newest config.* onto source\'s ''' - for root, dirs, files in os.walk(os.getcwd()): - for file in files: - if file in ['config.sub', 'config.guess']: - copy('/usr/share/gnuconfig/%s' % file, os.path.join(root, file)) - ctx.ui.info('GNU Config Update Finished.') - -def libtoolize(parameters = ''): - if system('/usr/bin/libtoolize %s' % parameters): - raise RunTimeError('Running libtoolize failed...') - -def gen_usr_ldscript(dynamicLib): - - makedirs('%s/usr/lib' % get.installDIR()) - - destinationFile = open('%s/usr/lib/%s' % (get.installDIR(), dynamicLib), 'w') - content = ''' -/* GNU ld script - Since Pardus has critical dynamic libraries - in /lib, and the static versions in /usr/lib, - we need to have a "fake" dynamic lib in /usr/lib, - otherwise we run into linking problems. -*/ -GROUP ( /lib/%s ) -''' % dynamicLib - - destinationFile.write(content) - destinationFile.close() - chmod('%s/usr/lib/%s' % (get.installDIR(), dynamicLib)) diff --git a/tags/pisi-1.0_alpha2/pisi/actionsapi/pisitools.py b/tags/pisi-1.0_alpha2/pisi/actionsapi/pisitools.py deleted file mode 100644 index d69053a3..00000000 --- a/tags/pisi-1.0_alpha2/pisi/actionsapi/pisitools.py +++ /dev/null @@ -1,219 +0,0 @@ -#!/usr/bin/python -#-*- coding: utf-8 -*- -# -# Copyright (C) 2005, TUBITAK/UEKAE -# -# 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. -# -# Please read the COPYING file. - -# Standart Python Modules -import os -import glob -import sys -import fileinput -import re - -# Pisi Modules -import pisi.context as ctx - -# ActionsAPI Modules -import pisi.actionsapi -import pisi.actionsapi.get as get -from pisi.actionsapi.pisitoolsfunctions import * -from pisi.actionsapi.shelltools import * - -def dobin(sourceFile, destinationDirectory = '/usr/bin'): - '''insert a executable file into /bin or /usr/bin''' - - ''' example call: pisitools.dobin("bin/xloadimage", "/bin", "xload") ''' - executable_insinto(get.installDIR() + destinationDirectory, sourceFile) - -def dodir(destinationDirectory): - '''creates a directory tree''' - makedirs(get.installDIR() + destinationDirectory) - -def dodoc(*sourceFiles): - '''inserts the files in the list of files into /usr/share/doc/PACKAGE''' - readable_insinto(get.installDIR() + os.path.join('/usr/share/doc', get.srcTAG()), *sourceFiles) - -def doexe(sourceFile, destinationDirectory): - '''insert a executable file into destination directory''' - - ''' example call: pisitools.doexe("kde-3.4.sh", "/etc/X11/Sessions")''' - executable_insinto(get.installDIR() + destinationDirectory, sourceFile) - -def dohard(sourceFile, destinationFile): - '''creates hard link between sourceFile and destinationFile''' - #FIXME: How can i use hard-links in Python? - pass - -def dohtml(*sourceFiles): - '''inserts the files in the list of files into /usr/share/doc/PACKAGE/html''' - - ''' example call: pisitools.dohtml("doc/doxygen/html/*")''' - destionationDirectory = os.path.join(get.installDIR(), 'usr/share/doc' ,get.srcTAG(), 'html') - - if not can_access_directory(destionationDirectory): - makedirs(destionationDirectory) - - allowed_extensions = ['.png', '.gif', '.html', '.htm', '.jpg', '.css', '.js'] - disallowed_directories = ['CVS'] - - for sourceFile in sourceFiles: - for source in glob.glob(sourceFile): - if os.path.isfile(source) and os.path.splitext(source)[1] in allowed_extensions: - system('install -m0644 %s %s' % ('"' + source + '"', destionationDirectory)) - if os.path.isdir(source) and os.path.basename(source) not in disallowed_directories: - for root, dirs, files in os.walk(source): - for source in files: - if os.path.splitext(source)[1] in allowed_extensions: - makedirs(destionationDirectory) - system('install -m0644 %s %s' % (os.path.join(root, source), destionationDirectory)) - -def doinfo(*sourceFiles): - '''inserts the into files in the list of files into /usr/share/info''' - readable_insinto(os.path.join(get.installDIR(), get.infoDIR()), *sourceFiles) - -def dojar(): - '''installs jar files into /usr/share/PACKAGE/lib, and adds to /usr/share/PACKAGE/classpath.env''' - pass - -def dolib(sourceFile, destinationDirectory = '/usr/lib'): - '''insert the library into /usr/lib''' - - '''example call: pisitools.dolib_a("libz.a")''' - '''example call: pisitools.dolib_a("libz.so")''' - sourceFile = os.path.join(os.getcwd(), sourceFile) - destinationDirectory = get.installDIR() + destinationDirectory - - lib_insinto(sourceFile, destinationDirectory, 755) - -def dolib_a(sourceFile, destinationDirectory = '/usr/lib'): - '''insert the static library into /usr/lib with permission 0644''' - - '''example call: pisitools.dolib_a("staticlib/libvga.a")''' - sourceFile = os.path.join(os.getcwd(), sourceFile) - destinationDirectory = get.installDIR() + destinationDirectory - - lib_insinto(sourceFile, destinationDirectory, 644) - -def dolib_so(sourceFile, destinationDirectory = '/usr/lib'): - '''insert the static library into /usr/lib with permission 0755''' - - '''example call: pisitools.dolib_so("pppd/plugins/minconn.so")''' - sourceFile = os.path.join(os.getcwd(), sourceFile) - destinationDirectory = get.installDIR() + destinationDirectory - - lib_insinto(sourceFile, destinationDirectory, 755) - -def doman(*sourceFiles): - '''inserts the man pages in the list of files into /usr/share/man/''' - - '''example call: pisitools.doman("man.1", "pardus.*")''' - manDIR = os.path.join(get.installDIR(), get.manDIR()) - if not can_access_directory(manDIR): - makedirs(manDIR) - - for sourceFile in sourceFiles: - for source in glob.glob(sourceFile): - try: - pageName, pageDirectory = source[:source.rindex('.')], \ - source[source.rindex('.')+1:] - except ValueError: - ctx.ui.error('\n!!! ActionsAPI [doman]: Wrong man page file...') - - makedirs(manDIR + '/man%s' % pageDirectory) - system('install -m0644 %s %s' % (source, manDIR + '/man%s' % pageDirectory)) - -def domo(sourceFile, locale, destinationFile ): - '''inserts the mo files in the list of files into /usr/share/locale/LOCALE/LC_MESSAGES''' - - '''example call: pisitools.domo("po/tr.po", "tr", "pam_login.mo")''' - - system('msgfmt %s' % sourceFile) - makedirs('%s/usr/share/locale/%s/LC_MESSAGES/' % (get.installDIR(), locale)) - move('messages.mo', '%s/usr/share/locale/%s/LC_MESSAGES/%s' % (get.installDIR(), locale, destinationFile)) - -def domove(sourceFile, destination, destinationFile = ''): - '''moves sourceFile/Directory into destinationFile/Directory''' - - ''' example call: pisitools.domove("/usr/bin/bash", "/bin/bash")''' - ''' example call: pisitools.domove("/usr/bin/", "/usr/sbin")''' - makedirs(get.installDIR() + destination) - - for file in glob.glob(get.installDIR() + sourceFile): - if not destinationFile: - move(file, get.installDIR() + os.path.join(destination, os.path.basename(file))) - else: - move(file, get.installDIR() + os.path.join(destination, destinationFile)) - -def dopython(): - '''FIXME: What the hell is this?''' - pass - -def dosed(sourceFile, findPattern, replacePattern = ''): - '''replaces patterns in sourceFile''' - - ''' example call: pisitools.dosed("/etc/passwd", "caglar", "cem")''' - ''' example call: pisitools.dosym("/etc/passwd", "caglar")''' - ''' example call: pisitools.dosym("Makefile", "(?m)^(HAVE_PAM=.*)no", r"\1yes")''' - - if can_access_file(sourceFile): - for line in fileinput.input(sourceFile, inplace = 1): - #FIXME: In-place filtering is disabled when standard input is read - line = re.sub(findPattern, replacePattern, line) - sys.stdout.write(line) - else: - raise FileError('File doesn\'t exists or permission denied...') - -def dosbin(sourceFile, destinationDirectory = '/usr/sbin'): - '''insert a executable file into /sbin or /usr/sbin''' - - ''' example call: pisitools.dobin("bin/xloadimage", "/sbin") ''' - executable_insinto(get.installDIR() + destinationDirectory, sourceFile) - -def dosym(sourceFile, destinationFile): - '''creates soft link between sourceFile and destinationFile''' - - ''' example call: pisitools.dosym("/usr/bin/bash", "/bin/bash")''' - makedirs(get.installDIR() + os.path.dirname(destinationFile)) - - try: - os.symlink(sourceFile, get.installDIR() + destinationFile) - except OSError: - ctx.ui.error('\n!!! ActionsAPI [dosym]: File exists...') - -def insinto(destinationDirectory, sourceFile, destinationFile = ''): - '''insert a sourceFile into destinationDirectory as a destinationFile with same uid/guid/permissions''' - makedirs(get.installDIR() + destinationDirectory) - - if not destinationFile: - for file in glob.glob(sourceFile): - if can_access_file(file): - copy(file, get.installDIR() + os.path.join(destinationDirectory, os.path.basename(file))) - else: - copy(sourceFile, get.installDIR() + os.path.join(destinationDirectory, destinationFile)) - -def newdoc(sourceFile, destinationFile): - '''inserts a sourceFile into /usr/share/doc/PACKAGE/ directory as a destinationFile''' - move(sourceFile, destinationFile) - readable_insinto(os.path.join(get.installDIR(), 'usr/share/doc', get.srcTAG()), destinationFile) - -def newman(sourceFile, destinationFile): - '''inserts a sourceFile into /usr/share/man/manPREFIX/ directory as a destinationFile''' - move(sourceFile, destinationFile) - doman(destinationFile) - -def remove(sourceFile): - '''removes sourceFile''' - for file in glob.glob(get.installDIR() + sourceFile): - unlink(file) - -def removeDir(destinationDirectory): - '''removes destinationDirectory and its subtrees''' - for directory in glob.glob(get.installDIR() + destinationDirectory): - unlinkDir(directory) diff --git a/tags/pisi-1.0_alpha2/pisi/actionsapi/pisitoolsfunctions.py b/tags/pisi-1.0_alpha2/pisi/actionsapi/pisitoolsfunctions.py deleted file mode 100644 index 546b9bee..00000000 --- a/tags/pisi-1.0_alpha2/pisi/actionsapi/pisitoolsfunctions.py +++ /dev/null @@ -1,72 +0,0 @@ -#!/usr/bin/python -#-*- coding: utf-8 -*- -# -# Copyright (C) 2005, TUBITAK/UEKAE -# -# 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. -# -# Please read the COPYING file. - -# Generic functions for common usage of pisitools # - -# Standart Python Modules -import os -import glob - -# Pisi Modules -import pisi.context as ctx - -# ActionsAPI Modules -import pisi.actionsapi -from pisi.actionsapi.shelltools import * - -class FileError(pisi.actionsapi.Error): - def __init__(self, Exception): - ctx.ui.error(Exception) - -class ArgumentError(pisi.actionsapi.Error): - def __init__(self, Exception): - ctx.ui.error(Exception) - -def executable_insinto(destinationDirectory, *sourceFiles): - '''insert a executable file into destinationDirectory''' - - if not sourceFiles or not destinationDirectory: - raise ArgumentError('Insufficient arguments...') - - if not can_access_directory(destinationDirectory): - makedirs(destinationDirectory) - - for sourceFile in sourceFiles: - for source in glob.glob(sourceFile): - system('install -m0755 -o root -g root %s %s' % (source, destinationDirectory)) - -def readable_insinto(destinationDirectory, *sourceFiles): - '''inserts file list into destinationDirectory''' - - if not sourceFiles or not destinationDirectory: - raise ArgumentError('Insufficient arguments...') - - if not can_access_directory(destinationDirectory): - makedirs(destinationDirectory) - - for sourceFile in sourceFiles: - for source in glob.glob(sourceFile): - system('install -m0644 %s %s' % (source, destinationDirectory)) - -def lib_insinto(sourceFile, destinationDirectory, permission = 0644): - '''inserts a library fileinto destinationDirectory with given permission''' - - if not sourceFile or not destinationDirectory: - raise ArgumentError('Insufficient arguments...') - - if not can_access_directory(destinationDirectory): - makedirs(destinationDirectory) - - if os.path.islink(sourceFile): - os.symlink(os.path.realpath(sourceFile), os.path.join(destinationDirectory, sourceFile)) - else: - system('install -m%s %s %s' % (permission, sourceFile, destinationDirectory)) diff --git a/tags/pisi-1.0_alpha2/pisi/actionsapi/pythonmodules.py b/tags/pisi-1.0_alpha2/pisi/actionsapi/pythonmodules.py deleted file mode 100644 index 45b077d1..00000000 --- a/tags/pisi-1.0_alpha2/pisi/actionsapi/pythonmodules.py +++ /dev/null @@ -1,57 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# -# Copyright (C) 2005, TUBITAK/UEKAE -# -# 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. -# -# Please read the COPYING file. - -# standard python modules -import os - -# Pisi Modules -import pisi.context as ctx - -# ActionsAPI Modules -import pisi.actionsapi -import pisi.actionsapi.get as get -from pisi.actionsapi.shelltools import system, can_access_file -from pisi.actionsapi.pisitools import dodoc - -class CompileError(pisi.actionsapi.Error): - def __init__(self, Exception): - ctx.ui.error(Exception) - -class InstallError(pisi.actionsapi.Error): - def __init__(self, Exception): - ctx.ui.error(Exception) - -class RunTimeError(pisi.actionsapi.Error): - def __init__(self, Exception): - ctx.ui.error(Exception) - -def compile(parameters = ''): - '''compile source with given parameters.''' - if system('python setup.py build %s' % (get.installDIR(), parameters)): - raise CompileError('!!! Make failed...\n') - -def install(parameters = ''): - '''does python setup.py install''' - if system('python setup.py install --root=%s --no-compile %s' % (get.installDIR(), parameters)): - raise InstallError('!!! Install failed...\n') - - DDOCS = 'CHANGELOG COPYRIGHT KNOWN_BUGS MAINTAINERS PKG-INFO \ - CONTRIBUTORS LICENSE COPYING* Change* MANIFEST* README*' - - for doc in DDOCS: - if can_access_file(doc): - pisitools.dodoc(doc) - -def run(parameters = ''): - '''executes parameters with python''' - if system('python %s' % (parameters)): - raise RunTimeError('!!! Running %s failed...\n' % parameters) diff --git a/tags/pisi-1.0_alpha2/pisi/actionsapi/shelltools.py b/tags/pisi-1.0_alpha2/pisi/actionsapi/shelltools.py deleted file mode 100644 index a568290b..00000000 --- a/tags/pisi-1.0_alpha2/pisi/actionsapi/shelltools.py +++ /dev/null @@ -1,198 +0,0 @@ -#!/usr/bin/python -#-*- coding: utf-8 -*- -# -# Copyright (C) 2005, TUBITAK/UEKAE -# -# 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. -# -# Please read the COPYING file. - -# Standart Python Modules -import os -import glob -import shutil - -# Pisi Modules -import pisi.context as ctx - -# ActionsAPI Modules -import pisi.actionsapi -import pisi.actionsapi.get - -def can_access_file(sourceFile): - '''test the existence of file''' - return os.access(sourceFile, os.F_OK) - -def can_access_directory(destinationDirectory): - '''test readability, writability and executablility of directory''' - return os.access(destinationDirectory, os.R_OK | os.W_OK | os.X_OK) - -def makedirs(destinationDirectory): - '''recursive directory creation function''' - try: - os.makedirs(destinationDirectory) - except OSError: - pass - -def echo(destionationFile, content): - try: - f = open(destionationFile, 'a') - f.write("%s\n" % content) - f.close() - except IOError: - ctx.ui.error(' ActionsAPI [echo]: Can\'t append to file...') - -def chmod(sourceFile, mode = 0755): - '''change the mode of sourceFile to the mode''' - for file in glob.glob(sourceFile): - if can_access_file(file): - try: - os.chmod(file, mode) - except OSError: - ctx.ui.error(' ActionsAPI [chmod]: Operation not permitted...') - else: - ctx.ui.error(' ActionsAPI [chmod]: File doesn\'t exists...') - -def chown(sourceFile, uid = 0, gid = 0): - '''change the owner and group id of sourceFile to the numeric uid and gid''' - if can_access_file(sourceFile): - try: - os.chown(sourceFile, uid, gid) - except OSError: - ctx.ui.error(' ActionsAPI [chown]: Operation not permitted...') - else: - ctx.ui.error(' ActionsAPI [chown]: File doesn\'t exists...') - -def sym(sourceFile, destinationFile): - '''creates symbolic link''' - try: - os.symlink(sourceFile, destinationFile) - except OSError: - ctx.ui.error(' ActionsAPI [sym]: Permission denied...') - -def unlink(sourceFile): - '''remove the file path''' - if isFile(sourceFile) or isLink(sourceFile): - try: - os.unlink(sourceFile) - except OSError: - ctx.ui.error(' ActionsAPI [unlink]: Permission denied.') - elif isDirectory(sourceFile): - pass - else: - ctx.ui.error(' ActionsAPI [unlink]: File doesn\'t exists.') - -def unlinkDir(sourceDirectory): - '''delete an entire directory tree''' - if isDirectory(sourceDirectory) or isLink(sourceDirectory): - try: - shutil.rmtree(sourceDirectory) - except OSError: - ctx.ui.error(' ActionsAPI [unlinkDir]: Operation not permitted.') - elif isFile(sourceDirectory): - pass - else: - ctx.ui.error(' ActionsAPI [unlinkDir]: Directory doesn\'t exists.') - -def move(sourceFile, destinationFile): - '''recursively move a sourceFile or directory to destinationFile''' - for file in glob.glob(sourceFile): - if isFile(file) or isLink(file) or isDirectory(file): - try: - shutil.move(file, destinationFile) - except OSError: - ctx.ui.error(' ActionsAPI [move]: Permission denied.') - else: - ctx.ui.error(' ActionsAPI [move]: File doesn\'t exists.') - -def copy(sourceFile, destinationFile): - '''recursively copy a sourceFile or directory to destinationFile''' - for file in glob.glob(sourceFile): - if isFile(file) or isLink(file): - try: - shutil.copy(file, destinationFile) - except IOError: - ctx.ui.error(' ActionsAPI [copy]: Permission denied.') - else: - ctx.ui.error(' ActionsAPI [copy]: File doesn\'t exists.') - -def copytree(source, destination, sym = False): - '''recursively copy an entire directory tree rooted at source''' - if isDirectory(source) or isLink(source): - try: - shutil.copytree(source, destination, sym) - except OSError: - ctx.ui.error(' ActionsAPI [copytree]: Permission denied.') - else: - ctx.ui.error(' ActionsAPI [copytree]: Directory doesn\'t exists.') - -def touch(sourceFile): - '''changes the access time of the 'sourceFile', or creates it if it is not exist''' - if glob.glob(sourceFile): - for file in glob.glob(sourceFile): - os.utime(file, None) - else: - try: - f = open(sourceFile, 'w') - f.close() - except IOError: - ctx.ui.error(' ActionsAPI [touch]: Permission denied.') - -def cd(directoryName = ''): - '''change directory''' - current = os.getcwd() - if directoryName: - os.chdir(directoryName) - else: - os.chdir(os.path.dirname(current)) - -def ls(source): - '''listdir''' - if os.path.isdir(source): - return os.listdir(source) - else: - return glob.glob(source) - -def export(key, value): - '''export environ variable''' - os.environ[key] = value - -def isLink(sourceFile): - '''return True if sourceFile refers to a symbolic link''' - return os.path.islink(sourceFile) - -def isFile(sourceFile): - '''return True if sourceFile is an existing regular file''' - return os.path.isfile(sourceFile) - -def isDirectory(sourceDirectory): - '''Return True if sourceFile is an existing directory''' - return os.path.isdir(sourceDirectory) - -def realPath(sourceFile): - '''return the canonical path of the specified filename, eliminating any symbolic links encountered in the path''' - return os.path.realpath(sourceFile) - -def baseName(sourceFile): - '''return the base name of pathname sourceFile''' - return os.path.basename(sourceFile) - -def dirName(sourceFile): - '''return the directory name of pathname path''' - return os.path.dirname(sourceFile) - -def system(command): - #FIXME: String formatting - command = command.replace(" ", " ") - ctx.ui.debug('executing %s' % command) - p = os.popen(command) - while 1: - line = p.readline() - if not line: - break - ctx.ui.debug(line[0:len(line)-1]) - - return p.close() diff --git a/tags/pisi-1.0_alpha2/pisi/actionsapi/variables.py b/tags/pisi-1.0_alpha2/pisi/actionsapi/variables.py deleted file mode 100644 index 7d3eb5ea..00000000 --- a/tags/pisi-1.0_alpha2/pisi/actionsapi/variables.py +++ /dev/null @@ -1,82 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# -# Copyright (C) 2005, TUBITAK/UEKAE -# -# 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. -# -# Please read the COPYING file. - -# Standard Python Modules -from os import getenv, environ - -# Pisi-Core Modules -import pisi.context as ctx - -# Set individual information, that are generally needed for ActionsAPI - -def exportFlags(): - '''General flags used in actions API.''' - - # Build systems depend on these environment variables. That is why - # we export them instead of using as (instance) variables. - values = ctx.config.values - environ['HOST'] = values.build.host - environ['CFLAGS'] = values.build.cflags - environ['CXXFLAGS'] = values.build.cxxflags - environ['LDFLAGS'] = values.build.ldflags - -class Env(object): - '''General environment variables used in actions API''' - def __init__(self): - - exportFlags() - - self.__vars = { - 'pkg_dir': 'PKG_DIR', - 'work_dir': 'WORK_DIR', - 'install_dir': 'INSTALL_DIR', - 'src_name': 'SRC_NAME', - 'src_version': 'SRC_VERSION', - 'src_release': 'SRC_RELEASE', - 'host': 'HOST', - 'cflags': 'CFLAGS', - 'cxxflags': 'CXXFLAGS', - 'ldflags': 'LDFLAGS' - } - - def __getattr__(self, attr): - - # Using environment variables is somewhat tricky. Each time - # you need them you need to check for their value. - if self.__vars.has_key(attr): - return getenv(self.__vars[attr]) - else: - return None - -class Dirs: - '''General directories used in actions API.''' - # TODO: Eventually we should consider getting these from a/the - # configuration file - doc = 'usr/share/doc' - sbin = 'usr/sbin' - man = 'usr/share/man' - info = 'usr/share/info' - data = 'usr/share' - conf = 'etc' - localstate = 'var/lib' - defaultprefix = 'usr' - - #FIXME: Get these from config or somewhere else! - kde = '/usr/kde/3.4' - qt = '/usr/qt/3' - -def initVariables(): - ctx.env = Env() - ctx.dirs = Dirs() - return ctx - -glb = initVariables() diff --git a/tags/pisi-1.0_alpha2/pisi/api.py b/tags/pisi-1.0_alpha2/pisi/api.py deleted file mode 100644 index 4b78d3a4..00000000 --- a/tags/pisi-1.0_alpha2/pisi/api.py +++ /dev/null @@ -1,589 +0,0 @@ -# Copyright (C) 2005, TUBITAK/UEKAE -# -# 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. -# -# Please read the COPYING file. -# -# Authors: Eray Ozkural -# Baris Metin - -"""Top level PISI interfaces. a facade to the entire PISI system""" - -import os -import sys -ver = sys.version_info -if ver[0] <= 2 and ver[1] < 4: - from sets import Set as set - -import pisi - -import pisi.context as ctx -from pisi.uri import URI -import pisi.util as util -import pisi.dependency as dependency -import pisi.pgraph as pgraph -import pisi.operations as operations -import pisi.packagedb as packagedb -import pisi.repodb -import pisi.installdb -from pisi.index import Index -import pisi.cli - - -class Error(pisi.Error): - pass - - -def init(database = True, options = None, ui = None ): - """Initialize PiSi subsystem""" - - import pisi.config - ctx.config = pisi.config.Config(options) - - if ui is None: - if options: - pisi.context.ui = pisi.cli.CLI(options.debug) - else: - pisi.context.ui = pisi.cli.CLI() - else: - pisi.context.ui = ui - - # initialize repository databases - if database: - ctx.repodb = pisi.repodb.init() - ctx.installdb = pisi.installdb.init() - - # TODO: bunun da ctx'de olmasi gerek, global hesabi - packagedb.init() -# import pisi.sourcedb -# pisi.sourcedb.init() - -def install(packages): - """install a list of packages (either files/urls, or names)""" - - # FIXME: this function name "install" makes impossible to import - # and use install module directly. - from install import InstallError - - try: - # determine if this is a list of files/urls or names - if packages[0].endswith(ctx.const.package_prefix): # they all have to! - return install_pkg_files(packages) - else: - return install_pkg_names(packages) - - #FIXME: As Gurer warns, something's fishy with this exception proc. - except InstallError, e: - ctx.ui.error("InstallError:%s" % e) - - except packagedb.Error, e: - ctx.ui.error("PackageDBError: (%s)" % e) - ctx.ui.error("Package is not installable.") - - #except Exception, e: - # print e - # ctx.ui.error("Error: %s" % e) - -def install_pkg_files(package_URIs): - """install a number of pisi package files""" - from package import Package - - ctx.ui.debug('A = %s' % str(package_URIs)) - - for x in package_URIs: - if not x.endswith(ctx.const.package_prefix): - ctx.ui.error('Mixing file names and package names not supported YET.\n') - return False - - # read the package information into memory first - # regardless of which distribution they come from - d_t = {} - dfn = {} - for x in package_URIs: - package = Package(x) - package.read() - name = str(package.metadata.package.name) - d_t[name] = package.metadata.package - dfn[name] = x - - def satisfiesDep(dep): - return dependency.installed_satisfies_dep(dep) \ - or dependency.dict_satisfies_dep(d_t, dep) - - # for this case, we have to determine the dependencies - # that aren't already satisfied and try to install them - # from the repository - dep_unsatis = [] - for name in d_t.keys(): - pkg = d_t[name] - deps = pkg.runtimeDeps - for dep in deps: - if not satisfiesDep(dep): - dep_unsatis.append(dep) - - # now determine if these unsatisfied dependencies could - # be satisfied by installing packages from the repo - - # if so, then invoke install_pkg_names - extra_packages = [x.package for x in dep_unsatis] - if (extra_packages and install_pkg_names(extra_packages)) or \ - (not extra_packages): - - class PackageDB: - def __init__(self): - self.d = d_t - - def get_package(self, key): - return d_t[str(key)] - - packagedb = PackageDB() - - A = d_t.keys() - - if len(A)==0: - ctx.ui.info('No packages to install.') - return True - - # try to construct a pisi graph of packages to - # install / reinstall - - G_f = pgraph.PGraph(packagedb) # construct G_f - - # find the "install closure" graph of G_f by package - # set A using packagedb - print A - for x in A: - G_f.add_package(x) - B = A - #state = {} - while len(B) > 0: - Bp = set() - for x in B: - pkg = packagedb.get_package(x) - print pkg - for dep in pkg.runtimeDeps: - print 'checking ', dep - if dependency.dict_satisfies_dep(d_t, dep): - if not dep.package in G_f.vertices(): - Bp.add(str(dep.package)) - G_f.add_dep(x, dep) - B = Bp - G_f.write_graphviz(sys.stdout) - order = G_f.topological_sort() - order.reverse() - print order - - for x in order: - operations.install_single_file(dfn[x]) - else: - raise Error('External dependencies not satisfied') - - return True # everything went OK. - -def install_pkg_names(A): - """This is the real thing. It installs packages from - the repository, trying to perform a minimum number of - installs""" - - ctx.ui.debug('A = %s' % str(A)) - - if len(A)==0: - ctx.ui.info('No packages to install.') - return True - - # try to construct a pisi graph of packages to - # install / reinstall - - G_f = pgraph.PGraph(packagedb) # construct G_f - - # find the "install closure" graph of G_f by package - # set A using packagedb - print A - for x in A: - G_f.add_package(x) - B = A - #state = {} - while len(B) > 0: - Bp = set() - for x in B: - pkg = packagedb.get_package(x) - print pkg - for dep in pkg.runtimeDeps: - print 'checking ', dep - # we don't deal with already *satisfied* dependencies - if not dependency.installed_satisfies_dep(dep): - if not dep.package in G_f.vertices(): - Bp.add(str(dep.package)) - G_f.add_dep(x, dep) - B = Bp - G_f.write_graphviz(sys.stdout) - order = G_f.topological_sort() - order.reverse() - print order - for x in order: - operations.install_single_name(x) - - return True # everything went OK :) - -def package_graph(A): - """Construct a package relations graph, containing - all dependencies of packages A""" - - ctx.ui.debug('A = %s' % str(A)) - - # try to construct a pisi graph of packages to - # install / reinstall - - G_f = pgraph.PGraph(packagedb) # construct G_f - - # find the "install closure" graph of G_f by package - # set A using packagedb - for x in A: - G_f.add_package(x) - B = A - #state = {} - while len(B) > 0: - Bp = set() - for x in B: - pkg = packagedb.get_package(x) - print pkg - for dep in pkg.runtimeDeps: - # we don't deal with already *satisfied* dependencies - if not dep.package in G_f.vertices(): - Bp.add(str(dep.package)) - G_f.add_dep(x, dep) -# if not dependency.installed_satisfies_dep(dep): -# if not dep.package in G_f.vertices(): -# Bp.add(str(dep.package)) -# G_f.add_dep(x, dep) - B = Bp - return G_f - -def upgrade(A): - upgrade_pkg_names(A) - - -def upgrade_pkg_names(A): - """Re-installs packages from the repository, trying to perform - a maximum number of upgrades.""" - - ignore_build = ctx.config.options and ctx.config.options.ignore_build_no - - # filter packages that are not upgradable - Ap = [] - for x in A: - if not ctx.installdb.is_installed(x): - ctx.ui.info('Package %s is not installed.' % x) - continue - (version, release, build) = ctx.installdb.get_version(x) - pkg = packagedb.get_package(x) - if ignore_build or (not build): - if release < pkg.release: - Ap.append(x) - elif build < pkg.build: - Ap.append(x) - else: - #ctx.ui.info('Package %s cannot be upgraded. ' % x) - ctx.ui.info('Package %s is already at its latest version %s,\ - release %s, build %s.' - % (x, pkg.version, pkg.release, pkg.build)) - A = Ap - - if len(A)==0: - ctx.ui.info('No packages to upgrade.') - return True - - ctx.ui.debug('A = %s' % str(A)) - - # try to construct a pisi graph of packages to - # install / reinstall - - G_f = pgraph.PGraph(packagedb) # construct G_f - - # find the "install closure" graph of G_f by package - # set A using packagedb - for x in A: - G_f.add_package(x) - B = A - #state = {} - while len(B) > 0: - Bp = set() - for x in B: - pkg = packagedb.get_package(x) - print pkg - for dep in pkg.runtimeDeps: - print 'checking ', dep - # add packages that can be upgraded - if dependency.repo_satisfies_dep(dep): - if ctx.installdb.is_installed(dep.package): - (v,r,b) = ctx.installdb.get_version(dep.package) - rep_pkg = packagedb.get_package(dep.package) - (vp,rp,bp) = (rep_pkg.version, rep_pkg.release, - rep_pkg.build) - if ignore_build or (not b) or (not bp): - # if we can't look at build - if r >= rp: # installed already new - continue - elif b and bp and b >= bp: - continue - if not dep.package in G_f.vertices(): - Bp.add(str(dep.package)) - G_f.add_dep(x, dep) - B = Bp - G_f.write_graphviz(sys.stdout) - order = G_f.topological_sort() - order.reverse() - print order - for x in order: - operations.install_single_name(x, True) - - return True # everything went OK :) - -def list_upgradable(): - ignore_build = ctx.config.options and ctx.config.options.ignore_build_no - - A = ctx.installdb.list_installed() - # filter packages that are not upgradable - Ap = [] - for x in A: - if not ctx.installdb.is_installed(x): - continue - (version, release, build) = ctx.installdb.get_version(x) - pkg = packagedb.get_package(x) - if ignore_build or (not build): - if release < pkg.release: - Ap.append(x) - elif build < pkg.build: - Ap.append(x) - else: - pass - #ctx.ui.info('Package %s cannot be upgraded. ' % x) - return Ap - -def remove(A): - """remove set A of packages from system (A is a list of package names)""" - - # filter packages that are not installed - Ap = [] - for x in A: - if ctx.installdb.is_installed(x): - Ap.append(x) - else: - ctx.ui.info('Package %s does not exist. Cannot remove.' % x) - A = Ap - - if len(A)==0: - ctx.ui.info('No packages to remove.') - return True - - # try to construct a pisi graph of packages to - # install / reinstall - - G_f = pgraph.PGraph(packagedb) # construct G_f - - # find the (install closure) graph of G_f by package - # set A using packagedb - print A - for x in A: - G_f.add_package(x) - B = A - #state = {} - while len(B) > 0: - Bp = set() - for x in B: - pkg = packagedb.get_package(x) - print 'processing', pkg.name - rev_deps = packagedb.get_rev_deps(x) - for (rev_dep, depinfo) in rev_deps: - print 'checking ', rev_dep - # we don't deal with unsatisfied dependencies - if dependency.installed_satisfies_dep(depinfo): - if not rev_dep in G_f.vertices(): - Bp.add(rev_dep) - G_f.add_plain_dep(rev_dep, x) - B = Bp - G_f.write_graphviz(sys.stdout) - order = G_f.topological_sort() - print order - for x in order: - if ctx.installdb.is_installed(x): - operations.remove_single(x) - else: - ctx.ui.info('Package %s is not installed. Cannot remove.' % x) - - return True # everything went OK :) - -def configure_pending(): - # TODO: not coded yet - # start with pending packages - # configure them in reverse topological order of configuration dependency - pass - -def info(package): - if package.endswith(ctx.const.package_prefix): - return info_file(package) - else: - return info_name(package) - -def info_file(package): - from package import Package - - if not os.path.exists(package): - raise Error ('File %s not found' % package) - - package = Package(package) - package.read() - return package.metadata, package.files - -def info_name(package_name): - """fetch package information for a package""" - if packagedb.has_package(package_name): - package = packagedb.get_package(package_name) - from pisi.metadata import MetaData - metadata = MetaData() - metadata.package = package - #FIXME: get it from sourcedb - metadata.source = None - #TODO: fetch the files from server if possible - if ctx.installdb.is_installed(package.name): - files = ctx.installdb.files(package.name) - else: - files = None - return metadata, files - else: - raise Error('Package %s not found' % package_name) - -def index(repo_dir = '.'): - - ctx.ui.info('* Building index of PISI files under %s' % repo_dir) - index = Index() - index.index(repo_dir) - index.write(ctx.const.pisi_index) - ctx.ui.info('* Index file written') - - -def add_repo(name, indexuri): - repo = pisi.repodb.Repo(URI(indexuri)) - ctx.repodb.add_repo(name, repo) - -def remove_repo(name): - if ctx.repodb.has_repo(name): - ctx.repodb.remove_repo(name) - else: - ctx.ui.error('* Repository %s does not exist. Cannot remove.' - % name) - -def update_repo(repo): - - ctx.ui.info('* Updating repository: %s' % repo) - index = Index() - index.read(ctx.repodb.get_repo(repo).indexuri.get_uri(), repo) - index.update_db(repo) - ctx.ui.info('* Package database updated.') - - -# build functions... -def prepare_for_build(pspecfile, authInfo=None): - - # FIXME: there is a function named "build" in this module which - # makes it impossible to use build module directly. - from build import PisiBuild - - url = URI(pspecfile) - if url.is_remote_file(): - from sourcefetcher import SourceFetcher - fs = SourceFetcher(url, authInfo) - url.uri = fs.fetch_all() - - pb = PisiBuild(url.uri) - - # find out the build dependencies that are not satisfied... - dep_unsatis = [] - for dep in pb.spec.source.buildDeps: - if not dependency.installed_satisfies_dep(dep): - dep_unsatis.append(dep) - - # FIXME: take care of the required buildDeps... - # For now just report an error! - if dep_unsatis: - ctx.ui.error("Unsatisfied Build Dependencies:") - for dep in dep_unsatis: - ctx.ui.error(dep.package) -# FIXME: Don't exit for now! It's annoying to test on a system that -# doesn't has all packages made with pisi. -# Will be enabled on the full-pisi system. -# sys.exit(1) - - return pb - -def build(pspecfile, authInfo=None): - pb = prepare_for_build(pspecfile, authInfo) - pb.build() - - -order = {"none": 0, - "unpack": 1, - "setupaction": 2, - "buildaction": 3, - "installaction": 4, - "buildpackages": 5} - -def __buildState_unpack(pb): - # unpack is the first state to run. - pb.fetch_source_archive() - pb.unpack_source_archive() - pb.apply_patches() - -def __buildState_setupaction(pb, last): - - if order[last] < order["unpack"]: - __buildState_unpack(pb) - pb.run_setup_action() - -def __buildState_buildaction(pb, last): - - if order[last] < order["setupaction"]: - __buildState_setupaction(pb, last) - pb.run_build_action() - -def __buildState_installaction(pb, last): - - if order[last] < order["buildaction"]: - __buildState_buildaction(pb, last) - pb.run_install_action() - -def __buildState_buildpackages(pb, last): - - if order[last] < order["installaction"]: - __buildState_installaction(pb, last) - pb.build_packages() - -def build_until(pspecfile, state, authInfo=None): - pb = prepare_for_build(pspecfile, authInfo) - pb.compile_action_script() - - last = pb.get_state() - ctx.ui.info("Last state was %s"%last) - - if not last: last = "none" - - if state == "unpack": - __buildState_unpack(pb) - return - - if state == "setupaction": - __buildState_setupaction(pb, last) - return - - if state == "buildaction": - __buildState_buildaction(pb, last) - return - - if state == "installaction": - __buildState_installaction(pb, last) - return - - __buildState_buildpackages(pb, last) diff --git a/tags/pisi-1.0_alpha2/pisi/archive.py b/tags/pisi-1.0_alpha2/pisi/archive.py deleted file mode 100644 index 5e428a38..00000000 --- a/tags/pisi-1.0_alpha2/pisi/archive.py +++ /dev/null @@ -1,213 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright (C) 2005, TUBITAK/UEKAE -# -# 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. -# -# Please read the COPYING file. -# -# Archive module provides access to regular archive file types. -# maintainer baris and meren - -# standard library modules -import os -import tarfile -import zipfile - -# PiSi modules -import pisi -import pisi.util as util - - -class ArchiveError(pisi.Error): - pass - - -class ArchiveBase(object): - """Base class for Archive classes.""" - def __init__(self, file_path, atype): - self.file_path = file_path - self.type = atype - - def unpack(self, target_dir, clean_dir = False): - self.target_dir = target_dir - # first we check if we need to clean-up our working env. - if os.path.exists(self.target_dir) and clean_dir: - util.clean_dir(self.target_dir) - - os.makedirs(self.target_dir) - - -class ArchiveTar(ArchiveBase): - """ArchiveTar handles tar archives depending on the compression - type. Provides access to tar, tar.gz and tar.bz2 files. - - This class provides the unpack magic for tar archives.""" - def __init__(self, file_path, arch_type = "tar"): - super(ArchiveTar, self).__init__(file_path, arch_type) - - def unpack(self, target_dir, clean_dir = False): - """Unpack tar archive to a given target directory(target_dir).""" - super(ArchiveTar, self).unpack(target_dir, clean_dir) - - rmode = "" - if self.type == 'tar': - rmode = 'r:' - elif self.type == 'targz': - rmode = 'r:gz' - elif self.type == 'tarbz2': - rmode = 'r:bz2' - else: - raise ArchiveError("Archive type not recognized") - - tar = tarfile.open(self.file_path, rmode) - oldwd = os.getcwd() - os.chdir(self.target_dir) - for tarinfo in tar: - tar.extract(tarinfo) - os.chdir(oldwd) - tar.close() - - -class ArchiveZip(ArchiveBase): - """ArchiveZip handles zip archives. - - Being a zip archive PISI packages also use this class - extensively. This class provides unpacking and packing magic for - zip archives.""" - - symmagic = 2716663808 #long of hex val '0xA1ED0000L' - - def __init__(self, file_path, arch_type = "zip", mode = 'r'): - super(ArchiveZip, self).__init__(file_path, arch_type) - - self.zip_obj = zipfile.ZipFile(self.file_path, mode) - - def close(self): - """Close the zip archive.""" - self.zip_obj.close() - - def add_to_archive(self, file_name): - """Add file or directory path to the zip file""" - # It's a pity that zipfile can't handle unicode strings. Grrr! - file_name = str(file_name) - if os.path.isdir(file_name) and not os.path.islink(file_name): - self.zip_obj.writestr(file_name + '/', '') - for f in os.listdir(file_name): - self.add_to_archive(os.path.join(file_name, f)) - else: - if os.path.islink(file_name): - dest = os.readlink(file_name) - attr = zipfile.ZipInfo() - attr.filename = file_name - attr.create_system = 3 - attr.external_attr = self.symmagic - self.zip_obj.writestr(attr, dest) - else: - self.zip_obj.write(file_name, file_name, zipfile.ZIP_DEFLATED) - - def add_basename_to_archive(self, file_name): - """Add only the basepath to the zip file. For example; if the given - file_name parameter is /usr/local/bin/somedir, this function - will create only the base directory/file somedir in the - archive.""" - cwd = os.getcwd() - path_name = os.path.dirname(file_name) - file_name = os.path.basename(file_name) - if path_name: - os.chdir(path_name) - self.add_to_archive(file_name) - os.chdir(cwd) - - def unpack_file_cond(self, pred, target_dir, archive_root = ''): - """Unpack/Extract a file according to predicate function filename -> - bool""" - zip_obj = self.zip_obj - for info in zip_obj.infolist(): - if pred(info.filename): # check if condition holds - - # below code removes that, so we find it here - is_dir = info.filename.endswith('/') - - # calculate output file name - if archive_root == '': - outpath = info.filename - else: - # change archive_root - if util.subpath(archive_root, info.filename): - outpath = util.removepathprefix(archive_root, - info.filename) - else: - continue # don't extract if not under - - ofile = os.path.join(target_dir, outpath) - - if is_dir: # this is a directory - d = os.path.join(target_dir, outpath) - if not os.path.isdir(d): - os.makedirs(d) - continue - - # check that output dir is present - util.check_dir(os.path.dirname(ofile)) - - # remove output file we might be overwriting - if os.path.exists(ofile): - os.remove(ofile) - - if info.external_attr == self.symmagic: - target = zip_obj.read(info.filename) - os.symlink(target, ofile) - else: - perm = info.external_attr - perm &= 0x00FF0000 - perm >>= 16 - perm |= 0x00000100 - buff = open (ofile, 'wb') - file_content = zip_obj.read(info.filename) - buff.write(file_content) - buff.close() - os.chmod(ofile, perm) - - def unpack_files(self, paths, target_dir): - self.unpack_file_cond(lambda f:f in paths, target_dir) - - def unpack_dir(self, path, target_dir): - self.unpack_file_cond(lambda f:util.subpath(path, f), target_dir) - - def unpack_dir_flat(self, path, target_dir): - self.unpack_file_cond(lambda f:util.subpath(path, f), target_dir, path) - - def unpack(self, target_dir, clean_dir=False): - super(ArchiveZip, self).unpack(target_dir, clean_dir) - - self.unpack_file_cond(lambda f: True, target_dir) - self.close() - return - - -class Archive: - """Archive is the main factory for ArchiveClasses, regarding the - Abstract Factory Pattern :).""" - - def __init__(self, file_path, arch_type): - """accepted archive types: - targz, tarbz2, zip, tar""" - - handlers = { - 'targz': ArchiveTar, - 'tarbz2': ArchiveTar, - 'tar': ArchiveTar, - 'zip': ArchiveZip - } - - self.archive = handlers.get(arch_type)(file_path, arch_type) - - def unpack(self, target_dir, clean_dir = False): - self.archive.unpack(target_dir, clean_dir) - - def unpack_files(self, files, target_dir): - self.archive.unpack_files(files, target_dir) diff --git a/tags/pisi-1.0_alpha2/pisi/build.py b/tags/pisi-1.0_alpha2/pisi/build.py deleted file mode 100644 index 69856ff8..00000000 --- a/tags/pisi-1.0_alpha2/pisi/build.py +++ /dev/null @@ -1,466 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright (C) 2005, TUBITAK/UEKAE -# -# 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. -# -# Please read the COPYING file. -# - -# package bulding stuff -# maintainer: baris and meren - -# python standard library -import os -import sys - -import gettext -__trans = gettext.translation('pisi', fallback=True) -_ = __trans.ugettext - -import pisi -import pisi.util as util -import pisi.context as ctx -from pisi.sourcearchive import SourceArchive -from pisi.files import Files, FileInfo -from pisi.metadata import MetaData -from pisi.package import Package - - -class Error(pisi.Error): - pass - - -# Helper Functions -def get_file_type(path, pinfoList): - """Return the file type of a path according to the given PathInfo - list""" - # The usage of depth is somewhat confusing. It is used for finding - # the best match to paths(in pinfolist). For an example, if paths - # contain ['/usr/share','/usr/share/doc'] and path is - # /usr/share/doc/filename our iteration over paths should match - # the second item. - depth = 0 - ftype = "" - path = "/"+path # we need a real path. - for pinfo in pinfoList: - if util.subpath(pinfo.pathname, path): - length = len(pinfo.pathname) - if depth < length: - depth = length - ftype = pinfo.fileType - return ftype - -def check_path_collision(package, pkgList): - """This function will check for collision of paths in a package with - the paths of packages in pkgList. The return value will be the - list containing the paths that collide.""" - collisions = [] - for pinfo in package.paths: - for pkg in pkgList: - if pkg is package: - continue - for path in pkg.paths: - # if pinfo.pathname is a subpath of path.pathname like - # the example below. path.patname is marked as a - # collide. Exp: - # pinfo.pathname: /usr/share - # path.pathname: /usr/share/doc - if util.subpath(pinfo.pathname, path.pathname): - collisions.append(path.pathname) - ctx.ui.error(_('Path %s belongs in multiple packages') % - path.pathname) - return collisions - -# a dynamic build context -from pisi.specfile import SpecFile - - -class BuildContext(object): - """Build Context Singleton""" - - def __init__(self, pspecfile): - super(BuildContext, self).__init__() - self.set_spec_file(pspecfile) - - def set_spec_file(self, pspecfile): - self.pspecfile = pspecfile - spec = SpecFile() - spec.read(pspecfile) - # FIXME: following checks the integrity but does nothing when it is wrong - # -gurer - #spec.verify() # check pspec integrity - self.spec = spec - - # directory accessor functions - - # pkg_x_dir: per package directory for storing info type x - - def pkg_dir(self): - "package build directory" - packageDir = self.spec.source.name + '-' + \ - self.spec.source.version + '-' + self.spec.source.release - - return ctx.config.destdir + ctx.config.values.dirs.tmp_dir \ - + '/' + packageDir - - def pkg_work_dir(self): - return self.pkg_dir() + ctx.const.work_dir_suffix - - def pkg_install_dir(self): - return self.pkg_dir() + ctx.const.install_dir_suffix - - -class PisiBuild: - """PisiBuild class, provides the package build and creation routines""" - def __init__(self, pspec): - self.bctx = BuildContext(pspec) - self.pspecDir = os.path.dirname(os.path.realpath(self.bctx.pspecfile)) - self.spec = self.bctx.spec - self.sourceArchive = SourceArchive(self.bctx) - - self.set_environment_vars() - - self.actionLocals = None - self.actionGlobals = None - self.srcDir = None - - def set_state(self, state): - stateFile = os.path.join(self.bctx.pkg_work_dir(), "pisiBuildState") - open(stateFile, "w").write(state) - - def get_state(self): - stateFile = os.path.join(self.bctx.pkg_work_dir(), "pisiBuildState") - if not os.path.exists(stateFile): # no state - return None - return open(stateFile, "r").read() - - def build(self): - """Build the package in one shot.""" - - ctx.ui.info(_("Building PISI source package: %s") % self.spec.source.name) - util.xterm_title(_("Building PISI source package: %s\n") % self.spec.source.name) - - self.compile_action_script() - - # check if all patch files exists, if there are missing no need to unpack! - self.patch_exists() - - self.fetch_source_archive() - - self.unpack_source_archive() - - self.solve_build_dependencies() - - # apply the patches and prepare a source directory for build. - self.apply_patches() - - self.run_setup_action() - self.run_build_action() - self.run_install_action() - - # after all, we are ready to build/prepare the packages - self.build_packages() - - def set_environment_vars(self): - """Sets the environment variables for actions API to use""" - evn = { - "PKG_DIR": self.bctx.pkg_dir(), - "WORK_DIR": self.bctx.pkg_work_dir(), - "INSTALL_DIR": self.bctx.pkg_install_dir(), - "SRC_NAME": self.spec.source.name, - "SRC_VERSION": self.spec.source.version, - "SRC_RELEASE": self.spec.source.release - } - os.environ.update(evn) - - def fetch_source_archive(self): - ctx.ui.info(_("Fetching source from: %s") % self.spec.source.archiveUri) - self.sourceArchive.fetch() - ctx.ui.info(_("Source archive is stored: %s/%s") - %(ctx.config.archives_dir(), self.spec.source.archiveName)) - - def unpack_source_archive(self): - ctx.ui.info(_("Unpacking archive...")) - self.sourceArchive.unpack() - ctx.ui.info(_(" unpacked (%s)") % self.bctx.pkg_work_dir()) - self.set_state("unpack") - - def run_setup_action(self): - # Run configure, build and install phase - ctx.ui.action(_("Setting up source...")) - self.run_action_function(ctx.const.setup_func) - self.set_state("setupaction") - - def run_build_action(self): - ctx.ui.action(_("Building source...")) - self.run_action_function(ctx.const.build_func) - self.set_state("buildaction") - - def run_install_action(self): - ctx.ui.action(_("Installing...")) - - # Before install make sure install_dir is clean - if os.path.exists(self.bctx.pkg_install_dir()): - util.clean_dir(self.bctx.pkg_install_dir()) - - # install function is mandatory! - self.run_action_function(ctx.const.install_func, True) - self.set_state("installaction") - - def compile_action_script(self): - """Compiles actions.py and sets the actionLocals and actionGlobals""" - specdir = os.path.dirname(self.bctx.pspecfile) - scriptfile = os.path.join(specdir, ctx.const.actions_file) - try: - localSymbols = globalSymbols = {} - buf = open(scriptfile).read() - exec compile(buf, "error", "exec") in localSymbols, globalSymbols - except IOError, e: - ctx.ui.error(_("Unable to read Action Script (%s): %s") %(scriptfile,e)) - sys.exit(1) - except SyntaxError, e: - ctx.ui.error (_("SyntaxError in Action Script (%s): %s") %(scriptfile,e)) - sys.exit(1) - - self.actionLocals = localSymbols - self.actionGlobals = globalSymbols - self.srcDir = self.pkg_src_dir() - - def pkg_src_dir(self): - """Returns the real path of WorkDir for an unpacked archive.""" - try: - workdir = self.actionGlobals['WorkDir'] - except KeyError: - workdir = self.spec.source.name + "-" + self.spec.source.version - - return os.path.join(self.bctx.pkg_work_dir(), workdir) - - def run_action_function(self, func, mandatory=False): - """Calls the corresponding function in actions.py. - - If mandatory parameter is True, and function is not present in - actionLocals pisi.build.Error will be raised.""" - # we'll need our working directory after actionscript - # finished its work in the archive source directory. - curDir = os.getcwd() - os.chdir(self.srcDir) - - - if func in self.actionLocals: - self.actionLocals[func]() - else: - if mandatory: - Error, _("unable to call function from actions: %s") %func - - os.chdir(curDir) - - def solve_build_dependencies(self): - """fail if dependencies not satisfied""" - #TODO: we'll have to do better than plugging a fxn here - pass - - def patch_exists(self): - """check existence of patch files declared in PSPEC""" - - files_dir = os.path.abspath(os.path.join(self.pspecDir, - ctx.const.files_dir)) - for patch in self.spec.source.patches: - patchFile = os.path.join(files_dir, patch.filename) - if not os.access(patchFile, os.F_OK): - raise Error(_("Patch file is missing: %s\n") % patch.filename) - - def apply_patches(self): - files_dir = os.path.abspath(os.path.join(self.pspecDir, - ctx.const.files_dir)) - - for patch in self.spec.source.patches: - patchFile = os.path.join(files_dir, patch.filename) - if patch.compressionType: - patchFile = util.uncompress(patchFile, - compressType=patch.compressionType, - targetDir=ctx.config.tmp_dir()) - - ctx.ui.action(_("* Applying patch: %s") % patch.filename) - util.do_patch(self.srcDir, patchFile, level=patch.level, target=patch.target) - - def gen_metadata_xml(self, package): - """Generate the metadata.xml file for build source. - - metadata.xml is composed of the information from specfile plus - some additional information.""" - metadata = MetaData() - metadata.from_spec(self.spec.source, package) - - metadata.package.distribution = ctx.config.values.general.distribution - metadata.package.distributionRelease = ctx.config.values.general.distribution_release - metadata.package.architecture = "Any" - - # FIXME: Bu hatalı. installsize'ı almak için tüm - # pkg_install_dir()'ın boyutunu hesaplayamayız. Bir source - # birden fazla kaynak üretebilir. package.paths ile - # karşılaştırarak file listesinden boyutları hesaplatmalıyız. - d = self.bctx.pkg_install_dir() - size = util.dir_size(d) - metadata.package.installedSize = str(size) - - # build no - if ctx.config.options.ignore_build_no: - metadata.package.build = None # means, build no information n/a - ctx.ui.warning('build number is not available.') - else: - metadata.package.build = self.calc_build_no(metadata.package.name) - - metadata.write(os.path.join(self.bctx.pkg_dir(), ctx.const.metadata_xml)) - self.metadata = metadata - - def gen_files_xml(self, package): - """Generetes files.xml using the path definitions in specfile and - generated files by the build system.""" - files = Files() - install_dir = self.bctx.pkg_install_dir() - collisions = check_path_collision(package, - self.spec.packages) - if collisions: - raise Error(_('Path collisions detected')) - d = {} - for pinfo in package.paths: - path = install_dir + pinfo.pathname - for fpath, fhash in util.get_file_hashes(path, collisions, install_dir): - frpath = util.removepathprefix(install_dir, fpath) # relative path - ftype = get_file_type(frpath, package.paths) - try: # broken links can cause problem - fsize = str(os.path.getsize(fpath)) - except OSError: - fsize = "0" - d[frpath] = FileInfo(frpath, ftype, fsize, fhash) - for (p, fileinfo) in d.iteritems(): - files.append(fileinfo) - files.write(os.path.join(self.bctx.pkg_dir(), ctx.const.files_xml)) - self.files = files - - def calc_build_no(self, package_name): - """Calculate build number""" - - # find previous build in ctx.config.options.output_dir - found = [] - for root, dirs, files in os.walk(ctx.config.options.output_dir): - for fn in files: - fn = fn.decode('utf-8') - if fn.startswith(package_name + '-') and \ - fn.endswith(ctx.const.package_prefix): - old_package_fn = os.path.join(root, fn) - ctx.ui.info('(found old version %s)' % old_package_fn) - old_pkg = Package(old_package_fn, 'r') - old_pkg.read(os.path.join(ctx.config.tmp_dir(), 'oldpkg')) - old_build = old_pkg.metadata.package.build - found.append( (old_package_fn, old_build) ) - if not found: - return 0 - ctx.ui.warning('(no previous build found, setting build no to 0.)') - else: - a = filter(lambda (x,y): y != None, found) - if a: - a.sort(lambda x,y : cmp(x[1],y[1])) - old_package_fn = a[0][0] - old_build = a[0][1] - else: - old_build = None - - # compare old files.xml with the new one.. - old_pkg = Package(old_package_fn, 'r') - old_pkg.read(os.path.join(ctx.config.tmp_dir(), 'oldpkg')) - - # FIXME: TAKE INTO ACCOUNT MINOR CHANGES IN METADATA - changed = False - fnew = self.files.list - fold = old_pkg.files.list - fold.sort(lambda x,y : cmp(x.path,y.path)) - fnew.sort(lambda x,y : cmp(x.path,y.path)) - if len(fnew) != len(fold): - changed = True - else: - for i in range(len(fold)): - fo = fold.pop(0) - fn = fnew.pop(0) - if fo.path != fn.path: - changed = True - break - else: - if fo.hash != fn.hash: - changed = True - break - - # set build number - if old_build is None: - ctx.ui.warning('(old package lacks a build no, setting build no to 0.)') - return 0 - elif changed: - return old_build + 1 - else: - return old_build - - def build_packages(self): - """Build each package defined in PSPEC file. After this process there - will be .pisi files hanging around, AS INTENDED ;)""" - for package in self.spec.packages: - - # store additional files - c = os.getcwd() - os.chdir(self.pspecDir) - install_dir = self.bctx.pkg_dir() + ctx.const.install_dir_suffix - for afile in package.additionalFiles: - src = os.path.join(ctx.const.files_dir, afile.filename) - dest = os.path.join(install_dir + os.path.dirname(afile.target), os.path.basename(afile.target)) - util.copy_file(src, dest) - if afile.permission: - # mode is octal! - os.chmod(dest, int(afile.permission, 8)) - - os.chdir(c) - - name = util.package_name(package.name, - self.spec.source.version, - self.spec.source.release) - - ctx.ui.action(_("** Building package %s") % package.name); - - ctx.ui.action(_("Generating %s...") % ctx.const.files_xml) - self.gen_files_xml(package) - ctx.ui.info(_(" done.")) - - ctx.ui.action(_("Generating %s...") % ctx.const.metadata_xml) - self.gen_metadata_xml(package) - ctx.ui.info(_(" done.")) - - ctx.ui.action(_("Creating PISI package %s") % name) - - pkg = Package(name, 'w') - - # add comar files to package - os.chdir(self.pspecDir) - for pcomar in package.providesComar: - fname = os.path.join(ctx.const.comar_dir, - pcomar.script) - pkg.add_to_package(fname) - - # add xmls and files - os.chdir(self.bctx.pkg_dir()) - - pkg.add_to_package(ctx.const.metadata_xml) - pkg.add_to_package(ctx.const.files_xml) - - # Now it is time to add files to the packages using newly - # created files.xml - files = Files() - files.read(ctx.const.files_xml) - for finfo in files.list: - pkg.add_to_package("install/" + finfo.path) - - pkg.close() - os.chdir(c) - self.set_state("buildpackages") - util.xterm_title_reset() diff --git a/tags/pisi-1.0_alpha2/pisi/cli/README b/tags/pisi-1.0_alpha2/pisi/cli/README deleted file mode 100644 index 5ad52a09..00000000 --- a/tags/pisi-1.0_alpha2/pisi/cli/README +++ /dev/null @@ -1,23 +0,0 @@ -Writing commands: ------------------ - -TODO: this is a *bit* out of date now. - -Subclass from Command. - -Be careful not to import PiSi modules before Command is run, e.g. -do it in run() method. This is necessary to prevent an obscure -initialization error. - -We used to import pisi.operations in pisi.cli - -This prevented pisi.operations from having the current global -pisi.ui.ui because it's loaded before the UI is set! So, despite -what you might understand from the python FAQ 1.2.3 -http://www.python.org/doc/faq/programming.html#how-do-i-share-global-variables-across-modules -you have to be careful about the initialization order when using -singleton modules. - -Another way to solve this is to ensure that the global module is -always imported again in every function it is called (see -ui.CLI.confirm() ) but this is more expensive. diff --git a/tags/pisi-1.0_alpha2/pisi/cli/__init__.py b/tags/pisi-1.0_alpha2/pisi/cli/__init__.py deleted file mode 100644 index d8f80e34..00000000 --- a/tags/pisi-1.0_alpha2/pisi/cli/__init__.py +++ /dev/null @@ -1,85 +0,0 @@ -# Copyright (C) 2005, TUBITAK/UEKAE -# -# 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. -# -# Please read the COPYING file. -# - -# pisi.cli package version -__version__ = "1.0_alpha2" - -import sys - -import pisi -import pisi.context as ctx -from pisi.ui import UI -from pisi.cli.colors import colorize - - -class Error(pisi.Error): - pass - - -class Exception(pisi.Exception): - pass - - -class CLI(UI): - "Command Line Interface" - - def __init__(self, show_debug = False, show_verbose = False): - super(CLI, self).__init__(show_debug, show_verbose) - - def output(self, str): - sys.stdout.write(str) - sys.stdout.flush() - - def info(self, msg, verbose = False): - # TODO: need to look at more kinds of info messages - # let's cheat from KDE :) - if verbose and self.show_verbose: - self.output(msg + '\n') - elif not verbose: - self.output(msg + '\n') - - def warning(self,msg): - self.output(colorize('Warning:' + msg + '\n', 'purple')) - - def error(self,msg): - self.output(colorize('Error:' + msg + '\n', 'red')) - - def action(self,msg): - #TODO: this seems quite redundant? - self.output(colorize(msg + '\n', 'green')) - - def choose(self, msg, opts): - print msg - for i in range(0,len(opts)): - print i + 1, opts(i) - while True: - s = raw_input(msg + colorize('1-%d' % len(opts), 'red')) - try: - opt = int(s) - if 1 <= opt and opt <= len(opts): - return opts(opt-1) - except (Exception,e): - pass - - def confirm(self, msg): - if ctx.config.options and ctx.config.options.yes_all: - return True - while True: - s = raw_input(msg + colorize('(yes/no)', 'red')) - if s.startswith('y') or s.startswith('Y'): - return True - if s.startswith('n') or s.startswith('N'): - return False - - def display_progress(self, pd): - out = '\r%-30.30s %3d%% %12.2f %s' % \ - (pd['filename'], pd['percent'], pd['rate'], pd['symbol']) - self.output(out) - diff --git a/tags/pisi-1.0_alpha2/pisi/cli/colors.py b/tags/pisi-1.0_alpha2/pisi/cli/colors.py deleted file mode 100644 index 676c18e8..00000000 --- a/tags/pisi-1.0_alpha2/pisi/cli/colors.py +++ /dev/null @@ -1,60 +0,0 @@ -# Copyright (C) 2005, TUBITAK/UEKAE -# -# 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. -# -# Please read the COPYING file. -# - -# Colors module provides some color codes for console output. - -colors = {'black' : "\033[30m", - 'red' : "\033[31m", - 'green' : "\033[32m", - 'yellow' : "\033[33m", - 'blue' : "\033[34m", - 'purple' : "\033[35m", - 'cyan' : "\033[36m", - 'white' : "\033[37m", - 'brightblack' : "\033[01;30m", - 'brightred' : "\033[01;31m", - 'brightgreen' : "\033[01;32m", - 'brightyellow' : "\033[01;33m", - 'brightblue' : "\033[01;34m", - 'brightmagenta' : "\033[01;35m", - 'brightcyan' : "\033[01;36m", - 'brightwhite' : "\033[01;37m", - 'underlineblack' : "\033[04;30m", - 'underlinered' : "\033[04;31m", - 'underlinegreen' : "\033[04;32m", - 'underlineyellow' : "\033[04;33m", - 'underlineblue' : "\033[04;34m", - 'underlinemagenta' : "\033[04;35m", - 'underlinecyan' : "\033[04;36m", - 'underlinewhite' : "\033[04;37m", - 'blinkingblack' : "\033[05;30m", - 'blinkingred' : "\033[05;31m", - 'blinkinggreen' : "\033[05;32m", - 'blinkingyellow' : "\033[05;33m", - 'blinkingblue' : "\033[05;34m", - 'blinkingmagenta' : "\033[05;35m", - 'blinkingcyan' : "\033[05;36m", - 'blinkingwhite' : "\033[05;37m", - 'backgroundblack' : "\033[07;30m", - 'backgroundred' : "\033[07;31m", - 'backgroundgreen' : "\033[07;32m", - 'backgroundyellow' : "\033[07;33m", - 'backgroundblue' : "\033[07;34m", - 'backgroundmagenta' : "\033[07;35m", - 'backgroundcyan' : "\033[07;36m", - 'backgroundwhite' : "\033[07;37m", - 'default' : "\033[0m" } - -def colorize(msg, color): - """Colorize the given message for console output""" - if colors.has_key(color): - return colors[color] + msg + colors['default'] - else: - return msg diff --git a/tags/pisi-1.0_alpha2/pisi/cli/commands.py b/tags/pisi-1.0_alpha2/pisi/cli/commands.py deleted file mode 100644 index 300bdf96..00000000 --- a/tags/pisi-1.0_alpha2/pisi/cli/commands.py +++ /dev/null @@ -1,952 +0,0 @@ -# -*- coding:utf-8 -*- -# -# Copyright (C) 2005, TUBITAK/UEKAE -# -# 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. -# -# Please read the COPYING file. -# - -import sys -from optparse import OptionParser - -import pisi -import pisi.cli -import pisi.context as ctx -from pisi.uri import URI - - -class Command(object): - """generic help string for any command""" - - # class variables - - cmd = [] - cmd_dict = {} - - def commands_string(): - s = '' - list = [x.name[0] for x in Command.cmd] - list.sort() - for x in list: - s += x + '\n' - return s - commands_string = staticmethod(commands_string) - - def get_command(cmd, fail=False): - - if Command.cmd_dict.has_key(cmd): - return Command.cmd_dict[cmd]() - - if fail: - print "Unrecognized command: ", cmd - sys.exit(1) - else: - return None - get_command = staticmethod(get_command) - - # instance variabes - - def __init__(self): - # now for the real parser - import pisi - self.parser = OptionParser(usage=getattr(self, "__doc__"), - version="%prog " + pisi.__version__) - self.options() - self.commonopts() - (self.options, self.args) = self.parser.parse_args() - self.args.pop(0) # exclude command arg - - self.check_auth_info() - - def commonopts(self): - '''common options''' - p = self.parser - p.add_option("-D", "--destdir", action="store") - p.add_option("", "--yes-all", action="store_true", - default=False, help = "assume yes in all yes/no queries") - p.add_option("-u", "--username", action="store") - p.add_option("-p", "--password", action="store") - p.add_option("-P", action="store_true", dest="getpass", default=False, - help="Get password from the command line") - p.add_option("-v", "--verbose", action="store_true", - dest="verbose", default=False, - help="detailed output") - p.add_option("-d", "--debug", action="store_true", - default=True, help="show debugging information") - p.add_option("-n", "--dry-run", action="store_true", default=False, - help = "do not perform any action, just show what\ - would be done") - return p - - def options(self): - """This is a fall back function. If the implementer module provides an - options function it will be called""" - pass - - def check_auth_info(self): - username = self.options.username - password = self.options.password - - # TODO: We'll get the username, password pair from a configuration - # file from users home directory. Currently we need user to - # give it from the user interface. - # if not username and not password: - # if someauthconfig.username and someauthconfig.password: - # self.authInfo = (someauthconfig.username, - # someauthconfig.password) - # return - if username and password: - self.authInfo = (username, password) - return - - if username and self.options.getpass: - from getpass import getpass - password = getpass("Password: ") - self.authInfo = (username, password) - else: - self.authInfo = None - - def init(self, database = True): - """initialize PiSi components""" - - # NB: command imports here or in the command class run fxns - import pisi.api - pisi.api.init(database, self.options) - - def finalize(self): - """do cleanup work for PiSi components""" - pass - - def get_name(self): - return self.__class__.name - - def format_name(self): - (name, shortname) = self.get_name() - if shortname: - return "%s (%s)" % (name, shortname) - else: - return name - - def help(self): - """print help for the command""" - ctx.ui.info(self.format_name() + ': ') - print getattr(self, "__doc__") - print self.parser.format_option_help() - - def die(self): - """exit program""" - print 'Program terminated abnormally.' - sys.exit(-1) - - -class autocommand(type): - def __init__(cls, name, bases, dict): - super(autocommand, cls).__init__(name, bases, dict) - Command.cmd.append(cls) - name = getattr(cls, 'name', None) - if name is None: - raise pisi.cli.Error('command lacks name') - longname, shortname = name - def add_cmd(cmd): - if Command.cmd_dict.has_key(cmd): - raise pisi.cli.Error('duplicate command %s' % cmd) - else: - Command.cmd_dict[cmd] = cls - add_cmd(longname) - if shortname: - add_cmd(shortname) - - -class Help(Command): - """Prints help for given commands. - -Usage: help [ ... ] - -If run without parameters, it prints the general help.""" - - __metaclass__ = autocommand - - def __init__(self): - #TODO? Discard Help's own usage doc in favor of general usage doc - #self.__doc__ = usage_text - #self.__doc__ += commands_string() - super(Help, self).__init__() - - name = ("help", "h") - - def run(self): - if not self.args: - self.parser.set_usage(usage_text) - self.parser.print_help() - return - - self.init() - - for arg in self.args: - obj = Command.get_command(arg, True) - obj.help() - print - - self.finalize() - -class Clean(Command): - """Clean stale locks.""" - - __metaclass__ = autocommand - - def __init__(self): - super(Clean, self).__init__() - - name = ("clean", None) - - def run(self): - self.init() - pisi.util.clean_locks() - self.finalize() - - -class Graph(Command): - """Graph package relations. -Usage: graph ... - -Write a graph of package relations, tracking dependency and -conflicts relations starting from given packages. -""" - - __metaclass__ = autocommand - - def __init__(self): - super(Graph, self).__init__() - - name = ("graph", None) - - def run(self): - self.init() - if self.args: - g = pisi.api.package_graph(self.args) - g.write_graphviz(file('pgraph.dot', 'w')) - self.finalize() - - -def buildno_opts(self): - self.parser.add_option("", "--ignore-build-no", action="store_true", - default=False, - help="do not take build no into account.") - - -class Build(Command): - """Build a PISI package using a pspec.xml file - -Usage: build - -You can give a URI of the pspec.xml file. PISI will -fetch all necessary files and build the package for you. -""" - __metaclass__ = autocommand - - def __init__(self): - super(Build, self).__init__() - - name = ("build", "bi") - - def options(self): - buildno_opts(self) - self.parser.add_option("-O", "--output-dir", action="store", default=".", - help="output directory for produced packages") - - def run(self): - if not self.args: - self.help() - return - - self.init() - ctx.ui.info('Output directory: %s\n' % ctx.config.options.output_dir) - for arg in self.args: - pisi.api.build(arg, self.authInfo) - self.finalize() - - -class PackageOp(Command): - """Abstract package operation command""" - def __init__(self): - super(PackageOp, self).__init__() - - def options(self): - self.parser.add_option("", "--ignore-comar", action="store_true", - default=False, help="bypass comar configuration agent") -## self.parser.add_option("", "--ignore-dependency", -## action="store_true", -## default=False, help="death") - - def init(self): - super(PackageOp, self).init(True) - import pisi - if not self.options.ignore_comar: - import comar - try: - ctx.comard = comar.Link() # context - except comar.Error: - ctx.ui.error('Comar error encountered\n') - self.die() - - def finalize(self): - #self.finalize_db() - if not self.options.ignore_comar: - pass - #try: - # pisi.comariface.finalize() - #except pisi.comariface.ComarError: - # ui.error('Comar error encountered\n') - - -class Install(PackageOp): - """Install PISI packages - -Usage: install ... - -You may use filenames, URIs or package names for packages. If you have -specified a package name, it should exist in a specified repository. -""" - __metaclass__ = autocommand - - def __init__(self): - super(Install, self).__init__() - - name = "install", "it" - - def options(self): - super(Install, self).options() - buildno_opts(self) - - def run(self): - if not self.args: - self.help() - return - - self.init() - pisi.api.install(self.args) - self.finalize() - -class Upgrade(PackageOp): - """Upgrade PISI packages - -Usage: Upgrade ... - -You may use filenames, URIs or package names for packages. If you have -specified a package name, it should exist in a specified repository. -""" - __metaclass__ = autocommand - - def __init__(self): - super(Upgrade, self).__init__() - - name = ("upgrade", "up") - - def options(self): - super(Upgrade, self).options() - buildno_opts(self) - - def run(self): - if not self.args: - self.help() - return - - self.init() - pisi.api.upgrade(self.args) - self.finalize() - - -class Remove(PackageOp): - """Remove PISI packages - -Usage: remove ... - -Remove package(s) from your system. Just give the package names to remove. -""" - __metaclass__ = autocommand - - def __init__(self): - super(Remove, self).__init__() - - name = ("remove", "rm") - - def run(self): - if not self.args: - self.help() - return - - self.init() - pisi.api.remove(self.args) - self.finalize() - - -class UpgradeAll(PackageOp): - """Upgrade system - -Usage: Upgrade - -Upgrade the entire system. -""" - __metaclass__ = autocommand - - def __init__(self): - super(UpgradeAll, self).__init__() - - name = ("upgrade-all", None) - - def options(self): - super(UpgradeAll, self).options() - buildno_opts(self) - - def run(self): - if self.args: - self.help() - return - - self.init() - pisi.api.upgrade(ctx.installdb.list_installed()) - self.finalize() - - -class ConfigurePending(PackageOp): - """configure pending packages - """ - - __metaclass__ = autocommand - - def __init__(self): - super(ConfigurePending, self).__init__() - - name = ("configure-pending", "cp") - - def run(self): - - self.init() - pisi.api.configure_pending() - self.finalize() - - -class Info(Command): - """Display package information - -Usage: info ... - -""" - __metaclass__ = autocommand - - def __init__(self): - super(Info, self).__init__() - - name = ("info", "i") - - def options(self): - self.parser.add_option("-f", "--files", action="store_true", - default=False, - help="Show a list of package files.") - self.parser.add_option("-F", "--files-path", action="store_true", - default=False, - help="Show only paths.") - - def run(self): - if not self.args: - self.help() - return - - self.init(True) - for arg in self.args: - self.printinfo(arg) - self.finalize() - - def printinfo(self, arg): - import os.path - - metadata, files = pisi.api.info(arg) - print metadata.package - if self.options.files or self.options.files_path: - if files: - print - print 'Files:' - for fileinfo in files.list: - if self.options.files: - print fileinfo - else: - print fileinfo.path - else: - print 'File information not available' - - -class Index(Command): - """Index PISI files in a given directory - -Usage: index - -This command searches for all PiSi files in a directory, collects PiSi -tags from them and accumulates the information in an output XML file, -named by default 'pisi-index.xml'. In particular, it indexes both -source and binary packages. -""" - __metaclass__ = autocommand - - def __init__(self): - super(Index, self).__init__() - - name = ("index", "ix") - - def options(self): - self.parser.add_option("-a", "--absolute-uris", action="store_true", - default=False, - help="store absolute links for indexed files.") - - def run(self): - - self.init() - from pisi.api import index - if len(self.args)==1: - index(self.args[0]) - elif len(self.args)==0: - print 'Indexing current directory.' - index() - else: - print 'Indexing only a single directory supported.' - return - self.finalize() - - -class ListInstalled(Command): - """Print the list of all installed packages - -Usage: list-installed - -""" - - __metaclass__ = autocommand - - def __init__(self): - super(ListInstalled, self).__init__() - - name = ("list-installed", "li") - - def options(self): - self.parser.add_option("-l", "--long", action="store_true", - default=False, help="show in long format") - self.parser.add_option("-i", "--install-info", action="store_true", - default=False, help="show detailed install info") - - def run(self): - self.init(True) - list = ctx.installdb.list_installed() - list.sort() - if self.options.install_info: - print 'Package Name |St| Version| Rel.| Build| Distro| Date' - print '========================================================================' - for pkg in list: - package = pisi.packagedb.inst_packagedb.get_package(pkg) - inst_info = ctx.installdb.get_info(pkg) - if self.options.long: - print package - print inst_info - elif self.options.install_info: - print '%-15s | %s ' % (package.name, inst_info.one_liner()) - else: - print '%15s - %s ' % (package.name, package.summary) - self.finalize() - - -class UpdateRepo(Command): - """Update repository databases - -Usage: update-repo ... - -: repository name -Synchronizes the PiSi databases with the current repository. -""" - __metaclass__ = autocommand - - def __init__(self): - super(UpdateRepo, self).__init__() - - name = ("update-repo", "ur") - - def run(self): - if not self.args: - self.help() - return - - self.init(True) - for repo in self.args: - pisi.api.update_repo(repo) - self.finalize() - - -class AddRepo(Command): - """Add a repository - -Usage: add-repo - -: name of repository to add -: URI of index file - -NB: We support only local files (e.g., /a/b/c) and http:// URIs at the moment -""" - __metaclass__ = autocommand - - def __init__(self): - super(AddRepo, self).__init__() - - name = ("add-repo", "ar") - - def run(self): - - if len(self.args)>=2: - self.init() - name = self.args[0] - indexuri = self.args[1] - pisi.api.add_repo(name, indexuri) - self.init() - else: - self.help() - return - - -class RemoveRepo(Command): - """Remove repositories - -Usage: remove-repo ... - -Remove all repository information from the system. -""" - __metaclass__ = autocommand - - def __init__(self): - super(RemoveRepo, self).__init__() - - name = ("remove-repo", "rr") - - def run(self): - - if len(self.args)>=1: - self.init() - for repo in self.args: - pisi.api.remove_repo(repo) - self.finalize() - else: - self.help() - return - - -class ListRepo(Command): - """List repositories - -Usage: list-repo - -Lists currently tracked repositories. -""" - __metaclass__ = autocommand - - def __init__(self): - super(ListRepo, self).__init__() - - name = ("list-repo", "lr") - - def run(self): - - self.init() - for repo in ctx.repodb.list(): - print repo - print ' ', ctx.repodb.get_repo(repo).indexuri.get_uri() - self.finalize() - - -class ListAvailable(Command): - """List available packages in the repositories - -Usage: list-available [ ... repon ] - -Gives a brief list of PiSi components published in the repository. -""" - __metaclass__ = autocommand - - def __init__(self): - super(ListAvailable, self).__init__() - - name = ("list-available", "la") - - def run(self): - - self.init(True) - - if self.args: - for arg in self.args: - self.print_packages(arg) - else: - # print for all repos - for repo in ctx.repodb.list(): - ctx.ui.info("Repository : %s\n" % repo) - self.print_packages(repo) - self.finalize() - - def print_packages(self, repo): - from pisi import packagedb - from colors import colorize - - pkg_db = packagedb.get_db(repo) - list = pkg_db.list_packages() - installed_list = ctx.installdb.list_installed() - list.sort() - for p in list: - if p in installed_list: - print colorize(p, "cyan") - else: - print p - -class ListUpgrades(Command): - """List packages to be upgraded - -Usage: list-upgrades [ ... repon ] - -""" - __metaclass__ = autocommand - - def __init__(self): - super(ListUpgrades, self).__init__() - - name = ("list-upgrades", "lu") - - def options(self): - self.parser.add_option("-l", "--long", action="store_true", - default=False, help="show in long format") - self.parser.add_option("-i", "--install-info", action="store_true", - default=False, help="show detailed install info") - buildno_opts(self) - - def run(self): - self.init(True) - list = pisi.api.list_upgradable() - list.sort() - if self.options.install_info: - print 'Package Name |St| Version| Rel.| Build| Distro| Date' - print '========================================================================' - for pkg in list: - package = pisi.packagedb.inst_packagedb.get_package(pkg) - inst_info = ctx.installdb.get_info(pkg) - if self.options.long: - print package - print inst_info - elif self.options.install_info: - print '%-15s | %s ' % (package.name, inst_info.one_liner()) - else: - print '%15s - %s ' % (package.name, package.summary) - self.finalize() - - -class ListPending(Command): - """List pending packages""" - - __metaclass__ = autocommand - - def __init__(self): - super(ListPending, self).__init__() - - name = ("list-pending", "lp") - - def run(self): - self.init(True) - - list = ctx.installdb.list_pending() - list.sort() - for p in list: - print p - - self.finalize() - - -class SearchAvailable(Command): - """Search in available packages - -Usage: search-available - -FIXME: this is bogus -""" - pass - - -# Partial build commands - -class BuildUntil(Build): - """Run the build process partially - -Usage: -sStateName build-until - -where states are: -unpack, setupaction, buildaction, installaction, buildpackages - -You can give an URI of the pspec.xml file. PISI will fetch all -necessary files and unpack the source and prepare a source directory -for you. -""" - __metaclass__ = autocommand - - def __init__(self): - super(BuildUntil, self).__init__() - - name = ("build-until", "bu") - - def options(self): - super(BuildUntil, self).options() - self.parser.add_option("-s", action="store", dest="state") - - def run(self): - - if not self.args: - self.help() - return - - self.init() - state = self.options.state - - for arg in self.args: - pisi.api.build_until(arg, state, self.authInfo) - self.finalize() - - -class BuildUnpack(Build): - """Unpack the source archive - -Usage: build-unpack - -TODO: desc. -""" - __metaclass__ = autocommand - - def __init__(self): - super(BuildUnpack, self).__init__() - - name = ("build-unpack", "biu") - - def run(self): - if not self.args: - self.help() - return - - self.init() - for arg in self.args: - pisi.api.build_until(arg, "unpack", self.authInfo) - self.finalize() - - -class BuildSetup(Build): - """Setup the source - -Usage: build-setup - -TODO: desc. -""" - __metaclass__ = autocommand - - def __init__(self): - super(BuildSetup, self).__init__() - - name = ("build-setup", "bis") - - def run(self): - if not self.args: - self.help() - return - - self.init() - for arg in self.args: - pisi.api.build_until(arg, "setupaction", - self.authInfo) - self.finalize() - - -class BuildBuild(Command): - """Setup the source - -Usage: build-build - -TODO: desc. -""" - __metaclass__ = autocommand - - def __init__(self): - super(BuildBuild, self).__init__() - - name = ("build-build", "bib") - - def run(self): - if not self.args: - self.help() - return - - self.init() - for arg in self.args: - pisi.api.build_until(arg, "buildaction", self.authInfo) - self.finalize() - - -class BuildInstall(Build): - """Install to the sandbox - -Usage: build-install - -TODO: desc. -""" - __metaclass__ = autocommand - - def __init__(self): - super(BuildInstall, self).__init__() - - name = ("build-install", "bii") - - def run(self): - if not self.args: - self.help() - return - - self.init() - for arg in self.args: - pisi.api.build_until(arg, "installaction", - self.authInfo) - self.finalize() - - -class BuildPackage(Build): - """Setup the source - -Usage: build-build - -TODO: desc. -""" - __metaclass__ = autocommand - - def __init__(self): - super(BuildPackage, self).__init__() - - name = ("build-package", "bip") - - def run(self): - if not self.args: - self.help() - return - - self.init() - for arg in self.args: - pisi.api.build_until(arg, "buildpackages", self.authInfo) - self.finalize() - -usage_text1 = """%prog [options] [arguments] - -where is one of: - -""" - -usage_text2 = """ -Use \"%prog help \" for help on a specific command. -""" - -usage_text = (usage_text1 + Command.commands_string() + usage_text2) diff --git a/tags/pisi-1.0_alpha2/pisi/cli/pisicli.py b/tags/pisi-1.0_alpha2/pisi/cli/pisicli.py deleted file mode 100644 index 0c96ac54..00000000 --- a/tags/pisi-1.0_alpha2/pisi/cli/pisicli.py +++ /dev/null @@ -1,96 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright (C) 2005, TUBITAK/UEKAE -# -# 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. -# -# Please read the COPYING file. -# - -import sys -from optparse import OptionParser - -import pisi -from pisi.uri import URI -from pisi.cli.commands import * - -class ParserError(Exception): - pass - - -class PreParser(OptionParser): - """consumes any options, and finds arguments from command line""" - - def __init__(self, version): - OptionParser.__init__(self, usage=usage_text, version=version) - - def error(self, msg): - raise ParserError, msg - - def parse_args(self, args=None): - self.rargs = self._get_args(args) - self._process_args() - return self.args - - def _process_args(self): - args = [] - rargs = self.rargs - if not self.allow_interspersed_args: - first_arg = False - while rargs: - arg = rargs[0] - def option(): - if not self.allow_interspersed_args and first_arg: - self.error('Options must precede non-option arguments') - del rargs[0] - return - # We handle bare "--" explicitly, and bare "-" is handled by the - # standard arg handler since the short arg case ensures that the - # len of the opt string is greater than 1. - if arg == "--": - del rargs[0] - break - elif arg[0:2] == "--": - # process a single long option (possibly with value(s)) - option() - elif arg[:1] == "-" and len(arg) > 1: - # process a cluster of short options (possibly with - # value(s) for the last one only) - option() - else: # then it must be an argument - args.append(arg) - del rargs[0] - self.args = args - - -class PisiCLI(object): - - def __init__(self): - # first construct a parser for common options - # this is really dummy - self.parser = PreParser(version="%prog " + pisi.__version__) - - try: - args = self.parser.parse_args() - if len(args)==0: # more explicit than using IndexError - print 'No command given' - self.die() - cmd_name = args[0] - except ParserError: - print 'Command line parsing error' - self.die() - - self.command = Command.get_command(cmd_name) - if not self.command: - print "Unrecognized command: ", cmd - self.die() - - def die(self): - self.parser.print_help() - sys.exit(1) - - def run_command(self): - self.command.run() diff --git a/tags/pisi-1.0_alpha2/pisi/config.py b/tags/pisi-1.0_alpha2/pisi/config.py deleted file mode 100644 index ee1bc82b..00000000 --- a/tags/pisi-1.0_alpha2/pisi/config.py +++ /dev/null @@ -1,61 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright (C) 2005, TUBITAK/UEKAE -# -# 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. -# -# Please read the COPYING file. -# - -# PISI Configuration module is used for gathering and providing -# regular PISI configurations. - -# Authors: Baris Metin - -import pisi -import pisi.context as ctx -from pisi.configfile import ConfigurationFile - -class Config(object): - """Config Singleton""" - - def __init__(self, options = None): - self.options = options - self.values = ConfigurationFile("/etc/pisi/pisi.conf") - self.destdir = self.values.general.destinationdirectory - - # directory accessor functions - # here is how it goes - # x_dir: system wide directory for storing info type x - # pkg_x_dir: per package directory for storing info type x - - def lib_dir(self): - return self.destdir + self.values.dirs.lib_dir - - def db_dir(self): - return self.destdir + self.values.dirs.db_dir - - def archives_dir(self): - return self.destdir + self.values.dirs.archives_dir - - def packages_dir(self): - return self.destdir + self.values.dirs.packages_dir - - def index_dir(self): - return self.destdir + self.values.dirs.index_dir - - def tmp_dir(self): - return self.destdir + self.values.dirs.tmp_dir - - # bu dizini neden kullanıyoruz? Yalnızca index.py içerisinde - # kullanılıyor ama /var/tmp/pisi/install gibi bir dizine niye - # ihtiyacımız var? (baris) - def install_dir(self): - return self.tmp_dir() + ctx.const.install_dir_suffix - -#TODO: remove this -config = Config() diff --git a/tags/pisi-1.0_alpha2/pisi/configfile.py b/tags/pisi-1.0_alpha2/pisi/configfile.py deleted file mode 100644 index 787d75d6..00000000 --- a/tags/pisi-1.0_alpha2/pisi/configfile.py +++ /dev/null @@ -1,132 +0,0 @@ -# -*- conding: utf-8 -*- -# -# Copyright (C) 2005, TUBITAK/UEKAE -# -# 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. -# -# Please read the COPYING file. -# - -# PISI Configuration File module, obviously, is used to read from the -# configuration file. Module also defines default values for -# configuration parameters. -# -# Configuration file is located in /etc/pisi/pisi.conf by default, -# having an INI like format like below. -# -#[general] -#destinationdirectory = /tmp -# -#[build] -#host = i686-pc-linux-gnu -#CFLAGS= -mcpu=i686 -O2 -pipe -fomit-frame-pointer -#CXXFLAGS= -mcpu=i686 -O2 -pipe -fomit-frame-pointer -#LDFLAGS= -# -#[directories] -#lib_dir = /var/lib/pisi -#db_dir = /var/db/pisi -#archives_dir = /var/cache/pisi/archives -#packages_dir = /var/cache/pisi/packages -#index_dir = /var/cache/pisi/index -#tmp_dir = /var/tmp/pisi -#icon_theme_dir = /usr/share/icons/Tulliana-1.0 - -import os -from ConfigParser import ConfigParser, NoSectionError - - -class ConfigException(Exception): - pass - -class GeneralDefaults: - """Default values for [general] section""" - destinationdirectory = os.getcwd() + "/tmp" # FOR ALPHA - distribution = "Pardus" - distribution_release = "0.1" - -class BuildDefaults: - """Default values for [build] section""" - host = "i686-pc-linux-gnu" - CFLAGS = "-mcpu=i686 -O2 -pipe -fomit-frame-pointer" - CXXFLAGS = "-mcpu=i686 -O2 -pipe -fomit-frame-pointer" - LDFLAGS = "" - -class DirsDefaults: - "Default values for [directories] section" - lib_dir = "/var/lib/pisi" - db_dir = "/var/db/pisi" - archives_dir = "/var/cache/pisi/archives" - packages_dir = "/var/cache/pisi/packages" - index_dir = "/var/cache/pisi/index" - tmp_dir = "/var/tmp/pisi" - icon_theme_dir = "/usr/share/icons/Tulliana-1.0" - - -class ConfigurationSection(object): - """ConfigurationSection class defines a section in the configuration - file, using defaults (above) as a fallback.""" - def __init__(self, section, items=[]): - self.items = items - - if section == "general": - self.defaults = GeneralDefaults - elif section == "build": - self.defaults = BuildDefaults - elif section == "directories": - self.defaults = DirsDefaults - else: - e = "No section by name '%s'" % section - raise ConfigException, e - - self.section = section - - def __getattr__(self, attr): - - # first search for attribute in the items provided in the - # configuration file. - if self.items: - for item in self.items: - if item[0] == attr: - return item[1] - - # then fall back to defaults - if hasattr(self.defaults, attr): - return getattr(self.defaults, attr) - - return "" - - # We'll need to access configuration keys by their names as a - # string. Like; ["default"]... - def __getitem__(self, key): - return self.__getattr__(key) - - -class ConfigurationFile(object): - """Parse and get configuration values from the configuration file""" - def __init__(self, filePath): - parser = ConfigParser() - self.filePath = filePath - - parser.read(self.filePath) - - try: - generalitems = parser.items("general") - except NoSectionError: - generalitems = [] - self.general = ConfigurationSection("general", generalitems) - - try: - builditems = parser.items("build") - except NoSectionError: - builditems = [] - self.build = ConfigurationSection("build", builditems) - - try: - dirsitems = parser.items("directories") - except NoSectionError: - dirsitems = [] - self.dirs = ConfigurationSection("directories", dirsitems) diff --git a/tags/pisi-1.0_alpha2/pisi/constants.py b/tags/pisi-1.0_alpha2/pisi/constants.py deleted file mode 100644 index fb9409d4..00000000 --- a/tags/pisi-1.0_alpha2/pisi/constants.py +++ /dev/null @@ -1,90 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright (C) 2005, TUBITAK/UEKAE -# -# 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. -# -# Please read the COPYING file. -# - -# PISI constants. -# If you have a "magic" constant value this is where it should be -# defined. - -# Author: Baris Metin - -# global variables here - -import pisi.constants - -const = pisi.constants.Constants() - -config = None - -# default UI is CLI -ui = None # not now - -installdb = None -repodb = None - -comard = None - -#def register(_impl): -# """ Register a UI implementation""" -# ui = _impl diff --git a/tags/pisi-1.0_alpha2/pisi/dependency.py b/tags/pisi-1.0_alpha2/pisi/dependency.py deleted file mode 100644 index 746738cc..00000000 --- a/tags/pisi-1.0_alpha2/pisi/dependency.py +++ /dev/null @@ -1,134 +0,0 @@ -# Copyright (C) 2005, TUBITAK/UEKAE -# -# 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. -# -# Please read the COPYING file. -# - -# dependency analyzer - -# Author: Eray Ozkural - -#import pisi.db as db -import pisi.context as ctx -import pisi.packagedb as packagedb -from pisi.version import Version -from pisi.xmlext import * -from pisi.xmlfile import XmlFile -from pisi.util import Checks - -class DepInfo: - def __init__(self, node = None): - if node: - self.package = getNodeText(node).strip() - self.versionFrom = getNodeAttribute(node, "versionFrom") - self.versionTo = getNodeAttribute(node, "versionTo") - self.releaseFrom = getNodeAttribute(node, "releaseFrom") - self.releaseTo = getNodeAttribute(node, "releaseTo") - else: - self.versionFrom = self.versionTo = None - self.releaseFrom = self.releaseFrom = None - - def elt(self, xml): - node = xml.newNode("Dependency") - xml.addText(node, self.package) - if self.versionFrom: - node.setAttribute("versionFrom", self.versionFrom) - if self.versionTo: - node.setAttribute("versionTo", self.versionTo) - if self.releaseFrom: - node.setAttribute("releaseFrom", self.versionFrom) - if self.releaseTo: - node.setAttribute("releaseTo", self.versionTo) - return node - - def has_errors(self): - if not self.package: - return [ "Dependency should have a package string" ] - return None - - def satisfies(self, pkg_name, version, release): - """determine if a package ver. satisfies given dependency spec""" - ret = True - from version import Version - if self.versionFrom: - ret &= Version(version) >= Version(self.versionFrom) - if self.versionTo: - ret &= Version(version) <= Version(self.versionTo) - if self.releaseFrom: - ret &= Version(release) <= Version(self.releaseFrom) - if self.releaseTo: - ret &= Version(release) <= Version(self.releaseTo) - return ret - - def __str__(self): - s = self.package - if self.versionFrom: - s += 'ver >= ' + self.versionFrom - if self.versionTo: - s += 'ver <= ' + self.versionTo - if self.releaseFrom: - s += 'rel >= ' + self.releaseFrom - if self.releaseTo: - s += 'rel <= ' + self.releaseTo - return s - -def dict_satisfies_dep(dict, depinfo): - """determine if a package in a dictionary satisfies given dependency spec""" - pkg_name = depinfo.package - if not dict.has_key(pkg_name): - return False - else: - pkg = dict[pkg_name] - (version, release) = (pkg.version, pkg.release) - return depinfo.satisfies(pkg_name, version, release) - -def installed_satisfies_dep(depinfo): - """determine if a package in *repository* satisfies given -dependency spec""" - pkg_name = depinfo.package - if not ctx.installdb.is_installed(pkg_name): - return False - else: - pkg = packagedb.inst_packagedb.get_package(pkg_name) - (version, release) = (pkg.version, pkg.release) - return depinfo.satisfies(pkg_name, version, release) - -def repo_satisfies_dep(depinfo): - """determine if a package in *repository* satisfies given -dependency spec""" - pkg_name = depinfo.package - if not packagedb.has_package(pkg_name): - return False - else: - pkg = packagedb.get_package(pkg_name) - (version, release) = (pkg.version, pkg.release) - return depinfo.satisfies(pkg_name, version, release) - -def satisfies_dependencies(pkg, deps, sat = installed_satisfies_dep): - for dep in deps: - if not sat(dep): - ctx.ui.error('Package %s does not satisfy dependency %s' % - (pkg,dep)) - return False - return True - -def satisfies_runtime_deps(pkg): - deps = packagedb.get_package(pkg).runtimeDeps - return satisfies_dependencies(pkg, deps) - -def installable(pkg): - """calculate if pkg is installable currently - which means it has to satisfy both install and runtime dependencies""" - if not packagedb.has_package(pkg): - ctx.ui.info("Package " + pkg + " is not present in the package database"); - return False - elif satisfies_runtime_deps(pkg): - return True - else: - #ctx.ui.info("package " + pkg + " does not satisfy dependencies\n"); - return False - diff --git a/tags/pisi-1.0_alpha2/pisi/fetcher.py b/tags/pisi-1.0_alpha2/pisi/fetcher.py deleted file mode 100644 index 6519238a..00000000 --- a/tags/pisi-1.0_alpha2/pisi/fetcher.py +++ /dev/null @@ -1,154 +0,0 @@ -# -*- coding: utf-8 -*- - -# Copyright (C) 2005, TUBITAK/UEKAE -# -# 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. -# -# Please read the COPYING file. -# - -# Yet another Pisi module for fetching files from various sources. Of -# course, this is not limited to just fetching source files. We fetch -# all kinds of things: source tarballs, index files, packages, and God -# knows what. - -# python standard library modules -import urllib2 -import os -from base64 import encodestring - -# pisi modules -import pisi -import pisi.util as util -import pisi.context as ctx -from pisi.uri import URI - - -class FetchError(pisi.Error): - pass - - -# helper functions -def fetch_url(url, dest, progress=None): - fetch = Fetcher(url, dest) - fetch.progress = progress - fetch.fetch() - if progress: - pass - #ctx.ui.info('\n') - - -class Fetcher: - """Fetcher can fetch a file from various sources using various - protocols.""" - def __init__(self, url, dest): - if not isinstance(url, URI): - url = URI(url) - - self.url = url - self.filedest = dest - util.check_dir(self.filedest) - self.percent = 0 - self.rate = 0.0 - self.progress = None - - def fetch (self): - """Return value: Fetched file's full path..""" - - if not self.url.filename(): - self.err("filename error") - - if not os.access(self.filedest, os.W_OK): - self.err("no perm to write to dest dir") - - if self.url.is_local_file(): - self.fetchLocalFile() - else: - self.fetchRemoteFile() - - return os.path.join(self.filedest, self.url.filename()) - - def _do_grab(self, fileURI, dest, totalsize): - symbols = [' B/s', 'KB/s', 'MB/s', 'GB/s'] - from time import time - tt, oldsize = int(time()), 0 - bs, size = 1024, 0 - symbol, depth = "B/s", 0 - st = time() - chunk = fileURI.read(bs) - size = size + len(chunk) - if self.progress: - p = self.progress(totalsize) - self.percent = p.update(size) - while chunk: - dest.write(chunk) - chunk = fileURI.read(bs) - size = size + len(chunk) - ct = time() - if int(tt) != int(ct): - self.rate = size / (ct - st) - while self.rate > 1000 and depth < 3: - self.rate /= 1024 - depth += 1 - symbol, depth = symbols[depth], 0 - oldsize, tt = size, time() - if self.progress: - if p.update(size): - self.percent = p.percent - retval = {'filename': self.url.filename(), - 'percent' : self.percent, - 'rate': self.rate, - 'symbol': symbol} - ctx.ui.display_progress(retval) - - dest.close() - - def fetchLocalFile (self): - url = self.url - - if not os.access(url.path(), os.F_OK): - self.err("no such file or no perm to read") - - dest = open(os.path.join(self.filedest, url.filename()) , "w") - totalsize = os.path.getsize(url.path()) - fileObj = open(url.path()) - self._do_grab(fileObj, dest, totalsize) - - def fetchRemoteFile (self): - from httplib import HTTPException - - try: - fileObj = urllib2.urlopen(self.formatRequest\ - (urllib2.Request(self.url.uri))) - headers = fileObj.info() - - except ValueError, e: - self.err('%s' % (e, )) - except IOError, e: - self.err('%s' % (e, )) - except OSError, e: - self.err('%s' % (e, )) - except HTTPException, e: - self.err(('(%s): %s') % (e.__class__.__name__, e)) - - try: - totalsize = int(headers['Content-Length']) - except: - totalsize = 0 - - dest = open(os.path.join(self.filedest, self.url.filename()) , "w") - self._do_grab(fileObj, dest, totalsize) - - def formatRequest(self, request): - authinfo = self.url.auth_info() - if authinfo: - enc = encodestring("%s:%s" % authinfo) - request.add_header('Authorization', 'Basic %s' % enc) - return request - - def err (self, error): - raise FetchError(error) - diff --git a/tags/pisi-1.0_alpha2/pisi/files.py b/tags/pisi-1.0_alpha2/pisi/files.py deleted file mode 100644 index 9e29651f..00000000 --- a/tags/pisi-1.0_alpha2/pisi/files.py +++ /dev/null @@ -1,97 +0,0 @@ -# Copyright (C) 2005, TUBITAK/UEKAE -# -# 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. -# -# Please read the COPYING file. -# - -# Files module provides access to files.xml. files.xml is genarated -# during the build process of a package and used in installation. - -# Authors: Eray Ozkural - -from pisi.xmlext import * -from pisi.xmlfile import XmlFile -from pisi.util import Checks - -class FileInfo: - """FileInfo holds the information for a File node/tag in files.xml""" - def __init__(self, _path = "", _type = "", _size = "", _hash = ""): - self.path = _path - self.type = _type - self.size = _size - self.hash = _hash - - def readnew(node): - f = FileInfo() - f.read(node) - return f - readnew = staticmethod(readnew) - - def read(self, node): - self.path = getNodeText(getNode(node, "Path")) - self.type = getNodeText(getNode(node, "Type")) - self.size = getNodeText(getNode(node, "Size")) - self.hash = getNodeText(getNode(node, "SHA1Sum")) - - def elt(self, dom): - ## FIXME: looking for a better way to do it - ## could apparently use helper functions to do this shorter - elt = dom.createElement("File") - pathElt = dom.createElement("Path") - pathElt.appendChild(dom.createTextNode(self.path)) - typeElt = dom.createElement("Type") - typeElt.appendChild(dom.createTextNode(self.type)) - sizeElt = dom.createElement("Size") - sizeElt.appendChild(dom.createTextNode(self.size)) - hashElt = dom.createElement("SHA1Sum") - hashElt.appendChild(dom.createTextNode(self.hash)) - elt.appendChild(pathElt) - elt.appendChild(typeElt) - elt.appendChild(sizeElt) - elt.appendChild(hashElt) - return elt - - def has_errors(self): - err = Checks() - err.has_tag(self.path, "File", "Path") - err.has_tag(self.type, "File", "Type") - err.has_tag(self.size, "File", "Size") - err.has_tag(self.hash, "File", "SHA1Sum") - return err.list - - def __str__(self): - s = "%s, type: %s, size: %s, sha1sum: %s" % (self.path, self.type, - self.size, self.hash) - return s - -class Files(XmlFile): - - def __init__(self): - XmlFile.__init__(self, "Files") - self.list = [] - - def append(self, fileinfo): - self.list.append(fileinfo) - - def read(self, filename): - self.readxml(filename) - - fileElts = self.getAllNodes("File") - self.list = [FileInfo.readnew(x) for x in fileElts] - - def write(self, filename): - self.newDOM() - document = self.dom.documentElement - for x in self.list: - document.appendChild(x.elt(self.dom)) - self.writexml(filename) - - def has_errors(self): - err = Checks() - for finfo in self.list: - err.join(finfo.has_errors()) - return err.list diff --git a/tags/pisi-1.0_alpha2/pisi/graph.py b/tags/pisi-1.0_alpha2/pisi/graph.py deleted file mode 100644 index 7a90ff1b..00000000 --- a/tags/pisi-1.0_alpha2/pisi/graph.py +++ /dev/null @@ -1,159 +0,0 @@ -# Copyright (C) 2005, TUBITAK/UEKAE -# -# 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. -# -# Please read the COPYING file. - -# the most simple minded digraph class ever - - -# for python 2.3 compatibility -import sys -ver = sys.version_info -if ver[0] <= 2 and ver[1] < 4: - from sets import Set as set - -import pisi - -# not an error! - -class CycleException(pisi.Exception): - pass - -class Digraph(object): - - def __init__(self): - self.__v = set() - self.__adj = {} - self.__vdata = {} - self.__edata = {} - - def vertices(self): - "return set of vertex descriptors" - return self.__v - - def edges(self): - "return a list of edge descriptors" - list = [] - for u in self.__v: - for v in self.__u: - list.append( (u,v) ) - return list - - def from_list(self, el): - "convert a list of edges (u,v) to graph" - for (u,v) in el: - self.add_edge(u,v) - - def add_vertex(self, u, data = None): - "add vertex u, optionally with data" - assert not u in self.__v - self.__v.add(u) - self.__adj[u] = set() - if data: - self.__vdata[u] = data - self.__edata[u] = {} - - def add_edge(self, u, v, edata = None, udata = None, vdata = None): - "add edge u -> v" - if not u in self.__v: - self.add_vertex(u, udata) - if not v in self.__v: - self.add_vertex(v, vdata) - self.__adj[u].add(v) - if edata != None: - self.__edata[u][v] = edata - - def add_biedge(self, u, v, edata = None): - self.add_edge(u, v, edata) - self.add_edge(v, u, edata) - - def set_vertex_data(self, u, data): - self.__vdata[u] = data - - def vertex_data(self, u): - return self.__vdata[u] - - def edge_data(self, u, v): - return self.__edata[u][v] - - def has_vertex(self, u): - return u in self.__v - - def has_edge(self, u,v): - if u in self.__v: - return v in self.__adj[u] - else: - return False - - def adj(self, u): - return self.__adj[u] - - def dfs(self, finish_hook = None): - self.color = {} - self.p = {} - self.d = {} - self.f = {} - for u in self.__v: - self.color[u] = 'w' # mark white (unexplored) - self.p[u] = None - self.time = 0 - for u in self.__v: - if self.color[u] == 'w': - self.dfs_visit(u, finish_hook) - - def dfs_visit(self, u, finish_hook): - self.color[u] = 'g' # mark green (discovered) - self.d[u] = self.time = self.time + 1 - for v in self.adj(u): - if self.color[v] == 'w': # explore unexplored vertices - self.p[v] = u - self.dfs_visit(v, finish_hook) - elif self.color[v] == 'g': # cycle detected - raise CycleException - self.color[u] = 'b' # mark black (completed) - if finish_hook: - finish_hook(u) - self.f[u] = self.time = self.time + 1 - - def cycle_free(self): - try: - self.dfs() - return True - except CycleException: - return False - - def topological_sort(self): - list = [] - self.dfs(lambda u: list.append(u)) - list.reverse() - return list - - def id_str(self, u): - s = str(u) - return s.replace('-', '_') - - def write_graphviz(self, f): - f.write('digraph G {\n') - for u in self.vertices(): - f.write(self.id_str(u)) - self.write_graphviz_vlabel(f, u) - f.write(';\n') - f.write('\n') - for u in self.vertices(): - for v in self.adj(u): - f.write( self.id_str(u) + ' -> ' + self.id_str(v)) - self.write_graphviz_elabel(f, u, v) - f.write(';\n') - f.write('\n') - f.write('}\n') - - def write_graphviz_vlabel(self, f, u): - pass - - def write_graphviz_elabel(self, f, u, v): - pass - diff --git a/tags/pisi-1.0_alpha2/pisi/index.py b/tags/pisi-1.0_alpha2/pisi/index.py deleted file mode 100644 index e20e36d7..00000000 --- a/tags/pisi-1.0_alpha2/pisi/index.py +++ /dev/null @@ -1,98 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright (C) 2005, TUBITAK/UEKAE -# -# 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. -# -# Please read the COPYING file. -# - -# PISI source/package index - -# Author: Eray Ozkural - -import os - -import pisi.context as ctx -import pisi.metadata as metadata -import pisi.packagedb as packagedb -import pisi.util as util -from pisi.package import Package -from pisi.xmlfile import XmlFile -from pisi.uri import URI - -class Index(XmlFile): - - def __init__(self): - XmlFile.__init__(self,"PISI") - self.sources = [] - self.packages = [] - - def read(self, filename, repo = None): - """Read PSPEC file""" - - self.filepath = filename - url = URI(filename) - if url.is_remote_file(): - from fetcher import fetch_url - - dest = os.path.join(ctx.config.index_dir(), repo) - if not os.path.exists(dest): - os.makedirs(dest) - fetch_url(url, dest, ctx.ui.Progress) - - self.filepath = os.path.join(dest, url.filename()) - - self.readxml(self.filepath) - - # find all binary packages - packageElts = self.getAllNodes("Package") - self.packages = [metadata.PackageInfo(p) for p in packageElts] - - self.unlink() - - def write(self, filename): - """Write index file""" - self.newDOM() - for pkg in self.packages: - self.addChild(pkg.elt(self)) - self.writexml(filename) - self.unlink() - - def index(self, repo_uri): - self.repo_dir = repo_uri - for root, dirs, files in os.walk(repo_uri): - for fn in files: - if fn.endswith(ctx.const.package_prefix): - ctx.ui.info('Adding ' + fn + ' to package index') - self.add_package(os.path.join(root, fn), repo_uri) - - def update_db(self, repo): - pkgdb = packagedb.get_db(repo) - pkgdb.clear() - for pkg in self.packages: - pkgdb.add_package(pkg) - - def add_package(self, path, repo_uri): - package = Package(path, 'r') - # extract control files - util.clean_dir(ctx.config.install_dir()) - package.extract_PISI_files(ctx.config.install_dir()) - - md = metadata.MetaData() - md.read(os.path.join(ctx.config.install_dir(), ctx.const.metadata_xml)) - if ctx.config.options and ctx.config.options.absolute_uris: - md.package.packageURI = os.path.realpath(path) - else: # create relative path by default - # TODO: in the future we'll do all of this with purl/pfile/&helpers - # After that, we'll remove the ugly repo_uri parameter from this - # function. - md.package.packageURI = util.removepathprefix(repo_uri, path) - # check package semantics - if md.has_errors(): - ctx.ui.error('Package ' + md.package.name + ': metadata corrupt') - else: - self.packages.append(md.package) diff --git a/tags/pisi-1.0_alpha2/pisi/install.py b/tags/pisi-1.0_alpha2/pisi/install.py deleted file mode 100644 index 4e4d6301..00000000 --- a/tags/pisi-1.0_alpha2/pisi/install.py +++ /dev/null @@ -1,182 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright (C) 2005, TUBITAK/UEKAE -# -# 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. -# -# Please read the COPYING file. -# - -# Package install operation - -# Author: Eray Ozkural - -import os - -import pisi - -import pisi.context as ctx -import pisi.packagedb as packagedb -import pisi.dependency as dependency -import pisi.operations as operations -from pisi.specfile import * -from pisi.package import Package -from pisi.metadata import MetaData -#import conflicts - -class InstallError(pisi.Error): - pass - -class Installer: - "Installer class, provides install routines for pisi packages" - - def __init__(self, package_fname): - "initialize from a file name" - self.package = Package(package_fname) - self.package.read() - self.metadata = self.package.metadata - self.files = self.package.files - self.pkginfo = self.metadata.package - - def install(self, ask_reinstall = True): - "entry point" - ctx.ui.info('Installing %s, version %s, release %s, build %s' % - (self.pkginfo.name, self.pkginfo.version, - self.pkginfo.release, self.pkginfo.build)) - self.ask_reinstall = ask_reinstall - self.check_requirements() - self.check_relations() - self.reinstall() - self.extract_install() - self.store_pisi_files() - self.register_comar_scripts() - self.update_databases() - - def check_requirements(self): - """check system requirements""" - #TODO: IS THERE ENOUGH SPACE? - # what to do if / is split into /usr, /var, etc. - pass - - def check_relations(self): - # check if package is in database - # If it is not, put it into 3rd party packagedb - if not packagedb.has_package(self.pkginfo.name): - db = packagedb.thirdparty_packagedb - db.add_package(self.pkginfo) - - # check conflicts - for pkg in self.metadata.package.conflicts: - if ctx.installdb.is_installed(self.pkginfo): - raise InstallError("Package conflicts " + pkg) - - # check dependencies - if not dependency.installable(self.pkginfo.name): - ctx.ui.error('Dependencies for ' + self.pkginfo.name + - ' not satisfied') - raise InstallError("Package not installable") - - def reinstall(self): - "check reinstall, confirm action, and remove package if reinstall" - - pkg = self.pkginfo - - if ctx.installdb.is_installed(pkg.name): # is this a reinstallation? - (iversion, irelease, ibuild) = ctx.installdb.get_version(pkg.name) - - # determine if same version - same_ver = False - ignore_build = ctx.config.options and ctx.config.options.ignore_build_no - if (not ibuild) or (not pkg.build) or ignore_build: - # we don't look at builds to compare two package versions - if pkg.version == iversion and pkg.release == irelease: - same_ver = True - else: - if pkg.build == ibuild: - same_ver = True - - if same_ver: - if self.ask_reinstall: - if not ctx.ui.confirm('Re-install same version package?'): - raise InstallError('Package re-install declined') - else: - upgrade = False - # is this an upgrade? - # determine and report the kind of upgrade: version, release, build - if pkg.version > iversion: - ctx.ui.info('Upgrading to new upstream version') - upgrade = True - elif pkg.release > irelease: - ctx.ui.info('Upgrading to new distribution release') - upgrade = True - elif ((not ignore_build) and ibuild and pkg.build - and pkg.build > ibuild): - ctx.ui.info('Upgrading to new distribution build') - upgrade = True - - # is this a downgrade? confirm this action. - if self.ask_reinstall and (not upgrade): - if pkg.version < iversion: - x = 'Downgrade to old upstream version?' - elif pkg.release < irelease: - x = 'Downgrade to old distribution release?' - else: - x = 'Downgrade to old distribution build?' - if not ctx.ui.confirm(x): - raise InstallError('Package downgrade declined') - - # remove old package then - operations.remove_single(pkg.name) - - def extract_install(self): - "unzip package in place" - - ctx.ui.info('Extracting files,') - self.package.extract_dir_flat('install', ctx.config.destdir) - - def store_pisi_files(self): - """put files.xml, metadata.xml, actions.py and COMAR scripts - somewhere in the file system. We'll need these in future...""" - - ctx.ui.info('Storing %s, ' % ctx.const.files_xml) - self.package.extract_file(ctx.const.files_xml, self.package.pkg_dir()) - - ctx.ui.info('%s.' % ctx.const.metadata_xml) - self.package.extract_file(ctx.const.metadata_xml, self.package.pkg_dir()) - - for pcomar in self.metadata.package.providesComar: - fpath = os.path.join(ctx.const.comar_dir, pcomar.script) - # comar prefix is added to the pkg_dir while extracting comar - # script file. so we'll use pkg_dir as destination. - ctx.ui.info('Storing %s' % fpath) - self.package.extract_file(fpath, self.package.pkg_dir()) - - def register_comar_scripts(self): - "register COMAR scripts" - - for pcomar in self.metadata.package.providesComar: - scriptPath = os.path.join(self.package.comar_dir(),pcomar.script) - ctx.ui.info("Registering COMAR script %s" % pcomar.script) - # FIXME: We must check the result of the command (possibly - # with id?) - if comard: - comard.register(pcomar.om, - self.metadata.package.name, - scriptPath) - - - def update_databases(self): - "update databases" - - # installdb - ctx.installdb.install(self.metadata.package.name, - self.metadata.package.version, - self.metadata.package.release, - self.metadata.package.build, - self.metadata.package.distribution) - - # installed packages - packagedb.inst_packagedb.add_package(self.pkginfo) diff --git a/tags/pisi-1.0_alpha2/pisi/installdb.py b/tags/pisi-1.0_alpha2/pisi/installdb.py deleted file mode 100644 index 145e1bf8..00000000 --- a/tags/pisi-1.0_alpha2/pisi/installdb.py +++ /dev/null @@ -1,165 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright (C) 2005, TUBITAK/UEKAE -# -# 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. -# -# Please read the COPYING file. -# -# -# installation database -# -# Author: Eray Ozkural - -# System -import os -import fcntl - -# PiSi -import pisi -import pisi.context as ctx -import pisi.lockeddbshelve as shelve -from pisi.files import Files -import pisi.util as util - - -class InstallDBError(pisi.Error): - pass - - -class InstallInfo: - # some data is replicated from packagedb.inst_packagedb - # we store as an object, hey, we can waste O(1) space. - # this is also easier to modify in the future, without - # requiring database upgrades! wow! - def __init__(self, state, version, release, build, distribution): - self.state = state - self.version = version - self.release = release - self.build = build - self.distribution = distribution - import time - self.time = time.localtime() - - def one_liner(self): - import time - time_str = time.strftime("%d %b %Y %H:%M", self.time) - s = '%2s|%10s|%6s|%6s|%8s|%12s' % (self.state, self.version, self.release, - self.build, self.distribution, - time_str) - return s - - state_map = { 'i': 'installed', 'ip':'installed-pending', 'r:removed' - 'p': 'purged' } - - def __str__(self): - s = "State: %s\nVersion: %s, Release: %s, Build: %s\n" % \ - (InstallInfo.state_map[self.state], self.version, - self.release, self.build) - import time - time_str = time.strftime("%d %b %Y %H:%M", self.time) - s += 'Distribution: %s, Install Time: %s\n' % (self.distribution, - time_str) - return s - - -class InstallDB: - - def __init__(self): - from os.path import join - self.d = shelve.LockedDBShelf('install') - self.dp = shelve.LockedDBShelf('configpending') - self.files_dir = os.path.join(ctx.config.db_dir(), 'files') - - def files_name(self, pkg, version, release): - from os.path import join - pkg_dir = join(ctx.config.lib_dir(), pkg + '-' + version + '-' + release) - return join(pkg_dir, ctx.const.files_xml) - - def files(self, pkg): - pkg = str(pkg) - pkginfo = self.d[pkg] - files = Files() - files.read(self.files_name(pkg,pkginfo.version,pkginfo.release)) - return files - - def is_recorded(self, pkg): - pkg = str(pkg) - return self.d.has_key(pkg) - - def is_installed(self, pkg): - pkg = str(pkg) - if self.is_recorded(pkg): - info = self.d[pkg] - return info.state=='i' or info.state=='ip' - else: - return False - - def list_installed(self): - list = [] - for (pkg, info) in self.d.iteritems(): - if info.state=='i' or info.state=='ip': - list.append(pkg) - return list - - def list_pending(self): - list = [] - for (pkg, x) in self.dp.iteritems(): - list.append(pkg) - return list - - def get_info(self, pkg): - pkg = str(pkg) - return self.d[pkg] - - def get_version(self, pkg): - pkg = str(pkg) - info = self.d[pkg] - return (info.version, info.release, info.build) - - def is_removed(self, pkg): - pkg = str(pkg) - if self.is_recorded(pkg): - info = self.d[pkg] - return info.state=='r' - else: - return False - - def install(self, pkg, version, release, build, distro = ""): - """install package with specific version, release, build""" - pkg = str(pkg) - if self.is_installed(pkg): - raise InstallDBError("already installed") - if ctx.config.options and ctx.config.options.ignore_comar: - state = 'ip' - self.dp[pkg] = True - else: - state = 'i' - - self.d[pkg] = InstallInfo(state, version, release, build, distro) - - def remove(self, pkg): - pkg = str(pkg) - info = self.d[pkg] - info.state = 'r' - self.d[pkg] = info - - def purge(self, pkg): - pkg = str(pkg) - if self.d.has_key(pkg): - del self.d[pkg] - - -db = None - -def init(): - global db - if db: - return db - - db = InstallDB() - return db - diff --git a/tags/pisi-1.0_alpha2/pisi/lockeddbshelve.py b/tags/pisi-1.0_alpha2/pisi/lockeddbshelve.py deleted file mode 100644 index 6d4c1832..00000000 --- a/tags/pisi-1.0_alpha2/pisi/lockeddbshelve.py +++ /dev/null @@ -1,60 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright (C) 2005, TUBITAK/UEKAE -# -# 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. -# -# Please read the COPYING file. -# -# A simple wrapper to implement locking for bsddb's dbshelf -# -# Authors: Eray Ozkural - - -import bsddb.dbshelve as shelve -import bsddb.db as db -import os -import fcntl - -import pisi -import pisi.context - -class LockedDBShelf(shelve.DBShelf): - - def __init__(self, dbname, flags=db.DB_CREATE, mode=0660, - filetype=db.DB_HASH, dbenv=None): - shelve.DBShelf.__init__(self, dbenv) - if type(flags) == type(''): - sflag = flags - if sflag == 'r': - flags = db.DB_RDONLY - elif sflag == 'rw': - flags = 0 - elif sflag == 'w': - flags = db.DB_CREATE - elif sflag == 'c': - flags = db.DB_CREATE - elif sflag == 'n': - flags = db.DB_TRUNCATE | db.DB_CREATE - else: - raise error, "flags should be one of 'r', 'w', 'c' or 'n' or use the bsddb.db.DB_* flags" - filename = os.path.join( pisi.context.config.db_dir(), dbname + '.bdb') - self.open(filename, dbname, filetype, flags, mode) - - def open(self, filename, dbname, filetype, flags, mode): - pisi.util.check_dir(pisi.context.config.db_dir()) - self.lockfile = file(filename + '.lock', 'w') - try: - fcntl.flock(self.lockfile, fcntl.LOCK_EX | fcntl.LOCK_NB) - except IOError: - import sys - pisi.context.ui.error("Another instance of PISI is running. Try later!") - sys.exit(1) - return self.db.open(filename, dbname, filetype, flags, mode) - - def close(self): - self.db.close() - self.lockfile.close() diff --git a/tags/pisi-1.0_alpha2/pisi/metadata.py b/tags/pisi-1.0_alpha2/pisi/metadata.py deleted file mode 100644 index 1907a3d6..00000000 --- a/tags/pisi-1.0_alpha2/pisi/metadata.py +++ /dev/null @@ -1,150 +0,0 @@ -# Copyright (C) 2005, TUBITAK/UEKAE -# -# 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. -# -# Please read the COPYING file. -# - -# Metadata module provides access to metadata.xml. metadata.xml is -# generated during the build process of a package and used in the -# installation. Package repository also uses metadata.xml for building -# a package index. - -# Authors: Eray Ozkural -# Baris Metin -# Baris Metin - -# we basically store everything in PackageInfo class -# yes, we are cheap - -import bsddb.dbshelve as shelve -import os, fcntl -from bsddb import db - -import pisi -import pisi.util as util -import pisi.context as ctx - -class Error(pisi.Error): - pass - -class PackageDB(object): - """PackageDB class provides an interface to the package database with - a delegated dbshelve object""" - def __init__(self, id): - util.check_dir(ctx.config.db_dir()) - self.fname = os.path.join(ctx.config.db_dir(), 'package-%s.bdb' % id ) - self.fname2 = os.path.join(ctx.config.db_dir(), 'revdep-%s.bdb' % id ) - self.lockfile = file(self.fname + '.lock', 'w') - try: - fcntl.flock(self.lockfile, fcntl.LOCK_EX | fcntl.LOCK_NB) - except IOError, e: - import sys - ctx.ui.error("PackageDB: %s" % e) - sys.exit(1) - self.d = shelve.open(self.fname) - self.dr = shelve.open(self.fname2) - - def __del__(self): - pass - #self.d.close() - #self.dr.close() - #self.lockfile.close() - - def has_package(self, name): - name = str(name) - return self.d.has_key(name) - - def get_package(self, name): - name = str(name) - return self.d[name] - - def get_rev_deps(self, name): - name = str(name) - if self.dr.has_key(name): - return self.dr[name] - else: - return [] - - def list_packages(self): - list = [] - for (pkg, x) in self.d.iteritems(): - list.append(pkg) - return list - - #TODO: list_upgrades? - - def add_package(self, package_info): - name = str(package_info.name) - self.d[name] = package_info - for dep in package_info.runtimeDeps: - dep_name = str(dep.package) - if self.dr.has_key(dep_name): - self.dr[dep_name].append( (name, dep) ) - else: - self.dr[dep_name] = [ (name, dep) ] - - def clear(self): - self.d.clear() - - def remove_package(self, name): - name = str(name) - del self.d[name] - - -packagedbs = {} - -def add_db(name): - packagedbs[name] = PackageDB('repo-' + name) - -def get_db(name): - return packagedbs[name] - -def remove_db(name): - del packagedbs[name] - #erase database file - -def has_package(name): - repo = which_repo(name) - if repo or thirdparty_packagedb.has_package(name) or inst_packagedb.has_package(name): - return True - return False - -def which_repo(name): - import pisi.repodb - for repo in pisi.repodb.db.list(): - if get_db(repo).has_package(name): - return repo - return None - -def get_package(name): - repo = which_repo(name) - if repo: - return get_db(repo).get_package(name) - if thirdparty_packagedb.has_package(name): - return thirdparty_packagedb.get_package(name) - if inst_packagedb.has_package(name): - return inst_packagedb.get_package(name) - raise Error('get_package: package %s not found' % name) - -def get_rev_deps(name): - repo = which_repo(name) - if repo: - return get_db(repo).get_rev_deps(name) - if thirdparty_packagedb.has_package(name): - return thirdparty_packagedb.get_rev_deps(name) - if inst_packagedb.has_package(name): - return inst_packagedb.get_rev_deps(name) - - return None - -def remove_package(name): - # remove the guy from the tracking databases - inst_packagedb.remove_package(name) - if thirdparty_packagedb.has_package(name): - thirdparty_packagedb.remove_package(name) - -# tracking databases for non-repository information - -thirdparty_packagedb = None -inst_packagedb = None - -def init(): - global thirdparty_packagedb - global inst_packagedb - - if not thirdparty_packagedb: - thirdparty_packagedb = PackageDB('thirdparty') - if not inst_packagedb: - inst_packagedb = PackageDB('installed') diff --git a/tags/pisi-1.0_alpha2/pisi/pgraph.py b/tags/pisi-1.0_alpha2/pisi/pgraph.py deleted file mode 100644 index 297b3161..00000000 --- a/tags/pisi-1.0_alpha2/pisi/pgraph.py +++ /dev/null @@ -1,79 +0,0 @@ -# Copyright (C) 2005, TUBITAK/UEKAE -# -# 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. -# -# Please read the COPYING file. -# - -# PISI package relation graph that represents the state of packagedb - -from graph import * - -# Cache the results from packagedb queries in a graph - -class PGraph(Digraph): - - def __init__(self, packagedb): - super(PGraph, self).__init__() - self.packagedb = packagedb - - def add_package(self, pkg): - pkg1 = self.packagedb.get_package(pkg) - self.add_vertex(str(pkg), (pkg1.version, pkg1.release)) - - def add_plain_dep(self, pkg1name, pkg2name): - pkg1data = None - if not pkg1name in self.vertices(): - pkg1 = self.packagedb.get_package(pkg1name) - pkg1data = (pkg1.version, pkg1.release) - pkg2data = None - if not pkg2name in self.vertices(): - pkg2 = self.packagedb.get_package(pkg2name) - pkg2data = (pkg2.version, pkg2.release) - self.add_edge(str(pkg1name), str(pkg2name), ('d', None), - pkg1data, pkg2data ) - - def add_dep(self, pkg, depinfo): - pkg1data = None - if not pkg in self.vertices(): - pkg1 = self.packagedb.get_package(pkg) - pkg1data = (pkg1.version, pkg1.release) - pkg2data = None - if not depinfo.package in self.vertices(): - pkg2 = self.packagedb.get_package(depinfo.package) - pkg2data = (pkg2.version, pkg2.release) - self.add_edge(str(pkg), str(depinfo.package), ('d', depinfo), - pkg1data, pkg2data ) - - def add_rev_dep(self, depinfo, pkg): - pkg1data = None - if not pkg in self.vertices(): - pkg1 = self.packagedb.get_package(depinfo.package) - pkg1data = (pkg1.version, pkg1.release) - pkg2data = None - if not depinfo.package in self.vertices(): - pkg2 = self.packagedb.get_package(pkg) - pkg2data = (pkg2.version, pkg2.release) - self.add_edge(str(depinfo.package), str(pkg), ('d', depinfo), - pkg1data, pkg2data ) - - def add_conflict(self, pkg, conflinfo): - pkg1data = None - if not pkg in self.vertices(): - pkg1 = self.packagedb.get_package(pkg) - pkg1data = (pkg1.version, pkg1.release) - pkg2data = None - if not pkg in self.vertices(): - pkg2 = self.packagedb.get_package(conflinfo.package) - pkg2data = (pkg2.version, pkg2.release) - - self.add_biedge(str(pkg), str(conflinfo.package), ('c', conflinfo) - , pkg1data, pkg2data ) - - def write_graphviz_vlabel(self, f, u): - (v, r) = self.vertex_data(u) - f.write('[ label = \"' + str(u) + '(' + str(v) + ',' + str(r) + ')\" ]') - diff --git a/tags/pisi-1.0_alpha2/pisi/repodb.py b/tags/pisi-1.0_alpha2/pisi/repodb.py deleted file mode 100644 index e64d672f..00000000 --- a/tags/pisi-1.0_alpha2/pisi/repodb.py +++ /dev/null @@ -1,105 +0,0 @@ -# Copyright (C) 2005, TUBITAK/UEKAE -# -# 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. -# -# Please read the COPYING file. -# - -# Author: Eray Ozkural - -from bsddb import db -import os, fcntl - -import pisi -import pisi.lockeddbshelve as shelve -import pisi.context as ctx -import pisi.packagedb as packagedb -import pisi.util as util -from pisi.uri import URI - -class Error(pisi.Error): - pass - -class Repo: - def __init__(self, indexuri): - self.indexuri = indexuri - -#class HttpRepo - -#class FtpRepo - -#class RemovableRepo - - -class RepoDB(object): - """RepoDB maps repo ids to repository information""" - - def __init__(self): - self.d = shelve.LockedDBShelf("repo") - if not self.d.has_key("order"): - self.d["order"] = [] - - def init_dbs(self): - # initialize package/source dbs - for x in self.list(): - packagedb.add_db(x) - - def __del__(self): - self.d.close() - - def repo_name(self, ix): - l = self.list() - return l[ix] - - def swap(self, x,y): - l = d["order"] - t = l[x] - l[x] = l[y] - l[y] = t - d["order"] = l - - def has_repo(self, name): - name = str(name) - return self.d.has_key("repo-" + name) - - def get_repo(self, name): - name = str(name) - return self.d["repo-" + name] - - def add_repo(self, name, repo_info): - if self.d.has_key("repo-" + name): - raise Error('Repository %s already exists' % name) - self.d["repo-" + name] = repo_info - order = self.d["order"] - order.append(name) - self.d["order"] = order - packagedb.add_db(name) - - def list(self): - return self.d["order"] - - def clear(self): - self.d.clear() - - def remove_repo(self, name): - name = str(name) - del self.d["repo-" + name] - l = self.d["order"] - l.remove(name) - self.d["order"] = l - - -db = None - -def init(): - global db - if db: - return db - - db = RepoDB() - db.init_dbs() - return db - diff --git a/tags/pisi-1.0_alpha2/pisi/sourcearchive.py b/tags/pisi-1.0_alpha2/pisi/sourcearchive.py deleted file mode 100644 index 90645610..00000000 --- a/tags/pisi-1.0_alpha2/pisi/sourcearchive.py +++ /dev/null @@ -1,69 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright (C) 2005, TUBITAK/UEKAE -# -# 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. -# -# Please read the COPYING file. -# -# Authors: Baris Metin -# Eray Ozkural - -# python standard library - -from os.path import join -from os import access, R_OK - - -# pisi modules -import pisi -import pisi.util as util -import pisi.context as ctx -from pisi.archive import Archive -from pisi.uri import URI -from pisi.fetcher import fetch_url - -class SourceArchiveError(pisi.Error): - pass - -class SourceArchive: - """source archive. this is a class responsible for fetching - and unpacking a source archive""" - def __init__(self, bctx): - self.url = URI(bctx.spec.source.archiveUri) - self.archiveFile = join(ctx.config.archives_dir(), self.url.filename()) - self.archiveName = bctx.spec.source.archiveName - self.archiveType = bctx.spec.source.archiveType - self.archiveSHA1 = bctx.spec.source.archiveSHA1 - self.bctx = bctx - - def fetch(self, interactive=True): - if not self.is_cached(interactive): - if interactive: - progress = ctx.ui.Progress - else: progress = None - fetch_url(self.url, ctx.config.archives_dir(), progress) - - def is_cached(self, interactive=True): - if not access(self.archiveFile, R_OK): - return False - - # check hash - if util.check_file_hash(self.archiveFile, self.archiveSHA1): - if interactive: - ctx.ui.info('%s [cached]' % self.archiveName) - return True - - return False - - def unpack(self, cleanDir=True): - - # check archive file's integrity - if not util.check_file_hash(self.archiveFile, self.archiveSHA1): - raise SourceArchiveError, "unpack: check_file_hash failed" - - archive = Archive(self.archiveFile, self.archiveType) - archive.unpack(self.bctx.pkg_work_dir(), cleanDir) diff --git a/tags/pisi-1.0_alpha2/pisi/sourcedb.py b/tags/pisi-1.0_alpha2/pisi/sourcedb.py deleted file mode 100644 index 3a7996cb..00000000 --- a/tags/pisi-1.0_alpha2/pisi/sourcedb.py +++ /dev/null @@ -1,71 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright (C) 2005, TUBITAK/UEKAE -# -# 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. -# -# Please read the COPYING file. -# - -# package source database -# interface for update/query to local package repository - -# Author: Eray Ozkural - -# we basically store everything in sourceinfo class -# yes, we are cheap - -import bsddb.dbshelve as shelve -import os -import fcntl -from bsddb import db - -import pisi.util as util -import pisi.context as ctx - -class SourceDB(object): - - def __init__(self): - util.check_dir(ctx.config.db_dir()) - self.filename = os.path.join(ctx.config.db_dir(), 'source.bdb') - self.d = shelve.open(self.filename) - self.fdummy = file(self.filename + '.lock', 'w') - fcntl.flock(self.fdummy, fcntl.LOCK_EX) - - def __del__(self): - #fcntl.flock(self.fdummy, fcntl.LOCK_UN) - self.fdummy.close() - #os.unlink(self.filename + '.lock') - - def has_source(self, name): - name = str(name) - return self.d.has_key(name) - - def get_source(self, name): - name = str(name) - return self.d[name] - - def add_source(self, source_info): - # FIXME: how can you make a negative assertion -gurer - # and yes i'm not very clever :) - # assert source_info.has_errors() - name = str(source_info.name) - self.d[name] = source_info - - def remove_source(self, name): - name = str(name) - del self.d[name] - -sourcedb = None - -def init(): - global sourcedb - if sourcedb: - return sourcedb - - sourcedb = SourceDB() - return sourcedb - diff --git a/tags/pisi-1.0_alpha2/pisi/sourcefetcher.py b/tags/pisi-1.0_alpha2/pisi/sourcefetcher.py deleted file mode 100644 index d9314651..00000000 --- a/tags/pisi-1.0_alpha2/pisi/sourcefetcher.py +++ /dev/null @@ -1,80 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright (C) 2005, TUBITAK/UEKAE -# -# 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. -# -# Please read the COPYING file. -# - -from os.path import basename, dirname, join - -import pisi.context as ctx -from pisi.uri import URI -from pisi.specfile import SpecFile - -class SourceFetcher(object): - def __init__(self, url, authInfo=None): - self.url = url - if authInfo: - self.url.set_auth_info(authInfo) - self.location = dirname(self.url.uri) - - pkgname = basename(dirname(self.url.path())) - self.dest = join(ctx.config.tmp_dir(), pkgname) - - def fetch_all(self): - # fetch pspec file - self.fetch() - pspec = join(self.dest, self.url.filename()) - self.spec = SpecFile() - self.spec.read(pspec) - - self.fetch_actionsfile() - self.fetch_patches() - self.fetch_comarfiles() - self.fetch_additionalFiles() - - return pspec - - def fetch_actionsfile(self): - actionsuri = join(self.location, ctx.const.actions_file) - self.url.uri = actionsuri - self.fetch() - - def fetch_patches(self): - spec = self.spec - for patch in spec.source.patches: - patchuri = join(self.location, - ctx.const.files_dir, patch.filename) - self.url.uri = patchuri - self.fetch(ctx.const.files_dir) - - def fetch_comarfiles(self): - spec = self.spec - for package in spec.packages: - for pcomar in package.providesComar: - comaruri = join(self.location, - ctx.const.comar_dir, pcomar.script) - self.url.uri = comaruri - self.fetch(ctx.const.comar_dir) - - def fetch_additionalFiles(self): - spec = self.spec - for pkg in spec.packages: - for afile in pkg.additionalFiles: - afileuri = join(self.location, - ctx.const.files_dir, afile.filename) - self.url.uri = afileuri - self.fetch(ctx.const.files_dir) - - def fetch(self, appendDest=""): - from fetcher import fetch_url - - ctx.ui.info("Fetching %s" % self.url.uri) - dest = join(self.dest, appendDest) - fetch_url(self.url, dest) - diff --git a/tags/pisi-1.0_alpha2/pisi/specfile.py b/tags/pisi-1.0_alpha2/pisi/specfile.py deleted file mode 100644 index afc38893..00000000 --- a/tags/pisi-1.0_alpha2/pisi/specfile.py +++ /dev/null @@ -1,471 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright (C) 2005, TUBITAK/UEKAE -# -# 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. -# -# Please read the COPYING file. -# -# Authors: Eray Ozkural -# Gurer Ozen -# Baris Metin -# History: -# Baris wrote the first version, then Baris and Eray did -# several revisions of it. It was rewritten in accordance -# with Gurer's observations. - - -"""Specfile module is our handler for PSPEC files. PSPEC (PISI SPEC) -files are specification files for PISI source packages. This module -provides read and write access to PSPEC files.""" - -# standard python modules -from os.path import basename - -# pisi modules -import pisi -from pisi.xmlext import * -from pisi.xmlfile import XmlFile -from pisi.dependency import DepInfo -from pisi.util import Checks - -#class Packager: -# __metaclass__ = xmlfile.autoxml - -class PackagerInfo: - def __init__(self, node = None): - if node: - self.name = getNodeText(getNode(node, "Name")) - self.email = getNodeText(getNode(node, "Email")) - else: - self.name = None - self.email = None - - def elt(self, xml): - node = xml.newNode("Packager") - xml.addTextNodeUnder(node, "Name", self.name) - xml.addTextNodeUnder(node, "Email", self.email) - return node - - def has_errors(self): - err = Checks() - err.has_tag(self.name, "Packager", "Name") - err.has_tag(self.email, "Packager", "Email") - return err.list - - def __str__(self): - s = "%s <%s>" % (self.name, self.email) - return s - -class AdditionalFileInfo: - def __init__(self, node = None): - if node: - self.filename = getNodeText(node) - self.target = getNodeAttribute(node, "target") - self.permission = getNodeAttribute(node, "permission") - - def elt(self, xml): - node = xml.newNode("AdditionalFile") - xml.addText(node, self.filename) - node.setAttribute("target", self.target) - if self.permission: - node.setAttribute("permission", self.permission) - - def has_errors(self): - err = Checks() - if not self.filename: - err.add("AdditionalFile should have file name string") - if not self.target: - err.add("AdditionalFile should have a target attribute") - return err.list - - def __str__(self): - s = "->".join(self.filename, self.target) - s += s + '(' + self.permission + ')' - return s - -class PatchInfo: - def __init__(self, node = None): - if node: - self.filename = getNodeText(node) - self.compressionType = getNodeAttribute(node, "compressionType") - self.level = getNodeAttribute(node, "level") - self.target = getNodeAttribute(node, "target") - else: - self.compressionType = None - if not self.level: - self.level = 0 - else: - self.level = int(self.level) - if not self.target: - self.target = '' - - def elt(self, xml): - node = xml.newNode("Patch") - xml.addText(node, self.filename) - if self.compressionType: - node.setAttribute("compressionType", self.compressionType) - if self.level: - node.setAttribute("level", str(self.level)) - if self.target: - node.setAttribute("target", self.target) - return node - - def has_errors(self): - if not self.filename: - return [ "Patch should have a filename string" ] - return None - - def __str__(self): - s = self.filename - s += ' (' + self.compressionType + ')' - s += ' level:' + self.level - return s - - -class UpdateInfo: - def __init__(self, node = None): - if node: - self.date = getNodeText(getNode(node, "Date")) - self.version = getNodeText(getNode(node, "Version")) - self.release = getNodeText(getNode(node, "Release")) - self.type = getNodeText(getNode(node, "Type")) - else: - self.type = None - - def elt(self, xml): - node = xml.newNode("Update") - xml.addTextNodeUnder(node, "Date", self.date) - xml.addTextNodeUnder(node, "Version", self.version) - xml.addTextNodeUnder(node, "Release", self.release) - if self.type: - xml.addTextNodeUnder(node, "Type", self.type) - return node - - def has_errors(self): - err = Checks() - err.has_tag(self.date, "Update", "Date") - err.has_tag(self.version, "Update", "Version") - err.has_tag(self.release, "Update", "Release") - return err.list - - def __str__(self): - s = self.date - s += ", ver=" + self.version - s += ", rel=" + self.release - s += ", type=" + self.type - return s - -class PathInfo: - def __init__(self, node = None): - if node: - self.pathname = getNodeText(node) - self.fileType = getNodeAttribute(node, "fileType") - if not self.fileType: - self.fileType = "other" - - def elt(self, xml): - node = xml.newNode("Path") - xml.addText(node, self.pathname) - node.setAttribute("fileType", self.fileType) - return node - - def has_errors(self): - if not self.pathname: - return [ "Path tag should have a name string" ] - return None - - def __str__(self): - s = self.pathname - s += ", type=" + self.fileType - return s - -class ComarProvide: - def __init__(self, node = None): - if node: - self.om = getNodeText(node) - self.script=getNodeAttribute(node, "script") - - def elt(self, xml): - node = xml.newNode("COMAR") - xml.addText(node, self.om) - node.setAttribute("script", self.script) - return node - - def has_errors(self): - if not self.om or not self.script: - return [ "COMAR provide should have something :)" ] - return None - - def __str__(self): - s = self.script - s += ' (' + self.om + ')' - return s - -class SourceInfo: - """A structure to hold source information. Source information is - located under tag in PSPEC file.""" - def __init__(self, node = None): - if node: - self.name = getNodeText(node, "Name") - self.homepage = getNodeText(node, "HomePage") - self.packager = PackagerInfo(getNode(node, "Packager")) - self.summary = getNodeText(node, "Summary") - self.description = getNodeText(node, "Description") - self.license = map(getNodeText, getAllNodes(node, "License")) - self.icon = getNodeText(node, "Icon") - self.isa = map(getNodeText, getAllNodes(node, "IsA")) - self.partof = getNodeText(node, "PartOf") - archiveNode = getNode(node, "Archive") - self.archiveUri = getNodeText(archiveNode).strip() - self.archiveName = basename(self.archiveUri) - self.archiveType = getNodeAttribute(archiveNode, "type") - self.archiveSHA1 = getNodeAttribute(archiveNode, - "sha1sum") - patchElts = getAllNodes(node, "Patches/Patch") - self.patches = [PatchInfo(p) for p in patchElts] - buildDepElts = getAllNodes(node, - "BuildDependencies/Dependency") - self.buildDeps = [DepInfo(d) for d in buildDepElts] - historyElts = getAllNodes(node, "History/Update") - self.history = [UpdateInfo(x) for x in historyElts] - - def elt(self, xml): - node = xml.newNode("Source") - xml.addTextNodeUnder(node, "Name", self.name) - if self.homepage: - xml.addTextNodeUnder(node, "Homepage", self.homepage) - node.appendChild(self.packager.elt(xml)) - xml.addTextNodeUnder(node, "Summary", self.summary) - xml.addTextNodeUnder(node, "Description", self.description) - if self.icon: - xml.addTextNodeUnder(node, "Icon", self.icon) - for lic in self.license: - xml.addTextNodeUnder(node, "License", lic) - for isa in self.isa: - xml.addTextNodeUnder(node, "IsA", isa) - xml.addTextNodeUnder(node, "PartOf", self.partof) - archiveNode = xml.addNodeUnder(node, "Archive") - archiveNode.setAttribute("type", self.archiveType) - archiveNode.setAttribute("sha1sum", self.archiveSHA1) - for patch in self.patches: - xml.addNodeUnder(node, "Patches", patch.elt(xml)) - for dep in self.buildDeps: - xml.addNodeUnder(node, "BuildDependencies", dep.elt(xml)) - for update in self.history: - xml.addNodeUnder(node, "History", update.elt(xml)) - return node - - def has_errors(self): - err = Checks() - err.has_tag(self.name, "Source", "Name") - err.has_tag(self.description, "Source", "Description") - err.has_tag(self.summary, "Source", "Summary") - err.has_tag(self.packager, "Source", "Packager") - err.has_tag(self.license, "Source", "License") - if (not self.archiveUri) or (not self.archiveType): - err.add("Source archive URI and type should be given") - if not self.archiveSHA1: - errd.add("Source archive should have a SHA1 sum") - if len(self.history) <= 0: - err.add("Source needs some education about History :)") - - err.join(self.packager.has_errors()) - for update in self.history: - err.join(update.has_errors()) - for patch in self.patches: - err.join(patch.has_errors()) - for dep in self.buildDeps: - err.join(dep.has_errors()) - - return err.list - - -class PackageInfo: - """A structure to hold package information. Package information is - located under tag in PSPEC file. Opposite to Source each - PSPEC file can have more than one Package tag.""" - def __init__(self, node = None): - if node: - self.name = getNodeText(node, "Name") - self.summary = getNodeText(node, "Summary") - self.description = getNodeText(node, "Description") - self.isa = map(getNodeText, getAllNodes(node, "IsA")) - self.partof = getNodeText(node, "PartOf") - self.license = map(getNodeText, getAllNodes(node, "License")) - self.icon = getNodeText(node, "Icon") - rtDepElts = getAllNodes(node, "RuntimeDependencies/Dependency") - self.runtimeDeps = [DepInfo(x) for x in rtDepElts] - self.paths = [PathInfo(x) for x in getAllNodes(node, "Files/Path")] - historyElts = getAllNodes(node, "History/Update") - self.history = [UpdateInfo(x) for x in historyElts] - conflElts = getAllNodes(node, "Conflicts/Package") - self.conflicts = map(getNodeText, conflElts) - provComarElts = getAllNodes(node, "Provides/COMAR") - self.providesComar = [ComarProvide(x) for x in provComarElts] - reqComarElts = getAllNodes(node, "Requires/COMAR") - self.requiresComar = map(getNodeText, reqComarElts) - aFilesElts = getAllNodes(node, "AdditionalFiles/AdditionalFile") - self.additionalFiles = [AdditionalFileInfo(f) for f in aFilesElts] - - def elt(self, xml): - node = xml.newNode("Package") - xml.addTextNodeUnder(node, "Name", self.name) - xml.addTextNodeUnder(node, "Summary", self.summary) - xml.addTextNodeUnder(node, "Description", self.description) - if self.icon: - xml.addTextNodeUnder(node, "Icon", self.icon) - for lic in self.license: - xml.addTextNodeUnder(node, "License", lic) - for isa in self.isa: - xml.addTextNodeUnder(node, "IsA", isa) - if self.partof: - xml.addTextNodeUnder(node, "PartOf", self.partof) - for dep in self.runtimeDeps: - xml.addNodeUnder(node, "RuntimeDependencies", dep.elt(xml)) - for path in self.paths: - xml.addNodeUnder(node, "Files", path.elt(xml)) - for update in self.history: - xml.addNodeUnder(node, "History", update.elt(xml)) - for conflict in self.conflicts: - xml.addTextNodeUnder(node, "Conflicts/Package", conflict) - for pcomar in self.providesComar: - xml.addNodeUnder(node, "Provides", pcomar.elt(xml)) - for rcomar in self.requiresComar: - xml.addTextNodeUnder(node, "Requires/COMAR", rcomar) - for afile in self.additionalFiles: - xml.addNodeUnder(node, "AdditionalFiles", afile.elt(xml)) - return node - - def has_errors(self): - err = Checks() - err.has_tag(self.name, "Package", "Name") - err.has_tag(self.summary, "Package", "Summary") - err.has_tag(self.description, "Package", "Description") - err.has_tag(self.license, "Package", "License") - if len(self.paths) <= 0: - err.add("Package should have some files") - - for path in self.paths: - err.join(path.has_errors()) - for dep in self.runtimeDeps: - err.join(dep.has_errors()) - for afile in self.additionalFiles: - err.join(afile.has_errors()) - - return err.list - - def __str__(self): - s = 'Name: ' + self.name - s += '\nSummary: ' + self.summary - s += '\nDescription: ' + self.description - return s - - def pkg_dir(self): - packageDir = self.name + '-' \ - + self.version + '-' \ - + self.release - - return join( config.lib_dir(), packageDir) - -class SpecFile(XmlFile): - """A class for reading/writing from/to a PSPEC (PISI SPEC) file.""" - - def __init__(self): - XmlFile.__init__(self,"PISI") - - def read(self, filename): - """Read PSPEC file""" - - self.readxml(filename) - - self.source = SourceInfo(self.getNode("Source")) - - # As we have no Source/Version tag we need to get - # the last version and release information - # from the first child of History/Update. And it works :) - self.source.version = self.source.history[0].version - self.source.release = self.source.history[0].release - - # find all binary packages - packageElts = self.getAllNodes("Package") - self.packages = [PackageInfo(p) for p in packageElts] - - self.merge_tags() - self.override_tags() - - self.unlink() - - errs = self.has_errors() - if errs: - e = "" - for x in errs: - e += x + "\n" - raise XmlError("File '%s' has errors:\n%s" % (filename, e)) - - def override_tags(self): - """Override tags from Source in Packages. Some tags in Packages - overrides the tags from Source. There is a more detailed - description in documents.""" - - tmp = [] - for pkg in self.packages: - - if not pkg.summary: - pkg.summary = self.source.summary - - if not pkg.description: - pkg.description = self.source.description - - if not pkg.partof: - pkg.partof = self.source.partof - - if not pkg.license: - pkg.license = self.source.license - - if not pkg.icon: - pkg.icon = self.source.icon - - tmp.append(pkg) - - self.packages = tmp - - def merge_tags(self): - """Merge tags from Source in Packages. Some tags in Packages merged - with the tags from Source. There is a more detailed - description in documents.""" - - tmp = [] - for pkg in self.packages: - - if pkg.isa and self.source.isa: - pkg.isa.append(self.source.isa) - elif not pkg.isa and self.source.isa: - pkg.isa = self.source.isa - - tmp.append(pkg) - - self.packages = tmp - - def has_errors(self): - """Return errors of the PSPEC file if there are any.""" - #FIXME: has_errors name is misleading for a function that does - #not just return a boolean value. check() would be better - exa - err = Checks() - err.join(self.source.has_errors()) - if len(self.packages) <= 0: - errs.add("There should be at least one Package section") - for p in self.packages: - err.join(p.has_errors()) - return err.list - - def write(self, filename): - """Write PSPEC file""" - self.newDOM() - self.addChild(self.source.elt(self)) - for pkg in self.packages: - self.addChild(pkg.elt(self)) - self.writexml(filename) diff --git a/tags/pisi-1.0_alpha2/pisi/specfilenew.py b/tags/pisi-1.0_alpha2/pisi/specfilenew.py deleted file mode 100644 index 0e775e53..00000000 --- a/tags/pisi-1.0_alpha2/pisi/specfilenew.py +++ /dev/null @@ -1,149 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright (C) 2005, TUBITAK/UEKAE -# -# 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. -# -# Please read the COPYING file. -# -# Authors: Eray Ozkural -# Baris Metin - -""" - Specfile module is our handler for PSPEC files. PSPEC (PISI SPEC) - files are specification files for PISI source packages. This module - provides read and write routines for PSPEC files. -""" - -# standard python modules -from os.path import basename - -# pisi modules -from pisi.xmlext import * -import pisi.xmlfile as xmlfile -from pisi.xmlfile import XmlFile -from pisi.ui import ui -from pisi.dependency import DepInfo -from pisi.util import Checks - -__metaclass__ = xmlfile.autoxml - - -class Packager: - t_Name = [types.StringType, xmlfile.mandatory] - t_Email = [types.StringType, xmlfile.mandatory] - def __str__(self): - s = "%s <%s>" % (self.name, self.email) - return s - - -class AdditionalFileInfo: - s_Filename = xmlfile.mandatory - a_Target = [types.StringType, xmlfile.mandatory] - a_Permission = [types.StringType, xmlfile.optional] - - def __str__(self): - s = "%s -> %s " % (self.filename, self.target) - if self.permission: - s += '(%s)' % self.permission - return s - - -class Patch: - s_Filename = xmlfile.mandatory - a_compressionType = [types.StringType, xmlfile.optional] - a_level = [types.StringType, xmlfile.optional] - a_target = [types.StringType, xmlfile.optional] - - def __str__(self): - s = self.filename - if self.compressionType: - s += ' (' + self.compressionType + ')' - if self.level: - s += ' level:' + self.level - if self.target: - s += ' target:' + self.target - return s - - -class Update: - - t_Date = [types.StringType, xmlfile.mandatory] - t_Version = [types.StringType, xmlfile.mandatory] - t_Release = [types.StringType, xmlfile.mandatory] - t_Type = [types.StringType, xmlfile.optional] - - def __str__(self): - s = self.date - s += ", ver=" + self.version - s += ", rel=" + self.release - if self.type: - s += ", type=" + self.type - return s - - -class Path: - - s_Path = xmlfile.mandatory - a_fileType = [types.StringType, xmlfile.optional] - - def __str__(self): - s = self.pathname - s += ", type=" + self.fileType - return s - - -class ComarProvide: - - s_om = [types.StringType, xmlfile.mandatory] - a_script = [types.StringType, xmlfile.mandatory] - - def __str__(self): - # FIXME: descriptive enough? - s = self.script - s += ' (' + self.om + ')' - return s - - -class Archive: - - s_uri = [ types.StringType, xmlfile.mandatory ] - a_type =[ types.StringType, xmlfile.mandatory ] - a_sha1sum =[ types.StringType, xmlfile.mandatory ] - - def decode_post(self): - self.name = basename(self.uri) - - -class Source: - - t_Name = [types.StringType, xmlfile.mandatory] - t_HomePage = [types.StringType, xmlfile.mandatory] - t_Packager = [Packager, xmlfile.mandatory] - t_Summary = [types.StringType, xmlfile.mandatory] - t_Description = [types.StringType, xmlfile.mandatory] - t_License = [ [types.StringType], xmlfile.mandatory] - t_IsA = [ [types.StringType], xmlfile.mandatory] - t_PartOf = [types.StringType, xmlfile.mandatory] - t_Archive = [Archive, xmlfile.mandatory ] - t_Patch = [ [Patch], xmlfile.mandatory, "Patches/Patch"] - t_BuildDep = [ [Dependency], xmlfile.mandatory, "BuildDependencies/Dependency"] - t_History = [ [Update], xmlfile.mandatory, "History/Update"] - - -class Package: - - t_Name = [ types.StringType, xmlfile.mandatory ] - t_Summary = [ types.StringType, xmlfile.mandatory ] - t_Description = [ types.StringType, xmlfile.mandatory ] - t_IsA = [ [types.StringType], xmlfile.mandatory] - t_PartOf = [types.StringType, xmlfile.mandatory] - t_History = [ [Update], xmlfile.mandatory, "History/Update"] - t_Conflicts = [ [types.StringType], xmlfile.mandatory, "Conflicts/Package"] - t_ProvidesComar = [ [ComarProvide], xmlfile.mandatory, "Provides/COMAR"] - t_RequriesComar = [ [types.StringType], xmlfile.mandatory, "Requires/COMAR"] - t_AdditionalFiles = [ [AdditionalFile], xmlfile.mandatory, "AdditionalFiles/AdditionalFile"] - diff --git a/tags/pisi-1.0_alpha2/pisi/ui.py b/tags/pisi-1.0_alpha2/pisi/ui.py deleted file mode 100644 index e29e6bde..00000000 --- a/tags/pisi-1.0_alpha2/pisi/ui.py +++ /dev/null @@ -1,84 +0,0 @@ -# Copyright (C) 2005, TUBITAK/UEKAE -# -# 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. -# -# Please read the COPYING file. -# -# generic user interface -# -# Authors: Eray Ozkural -# Murat Eren - -import sys - -import pisi -import pisi.context as ctx - -class UI(object): - "Abstract class for UI operations, derive from this." - - class Progress: - def __init__(self, totalsize): - self.totalsize = totalsize - self.percent = 0 - - def update(self, size): - if not self.totalsize: - return 100 - - percent = (size * 100) / self.totalsize - if percent and self.percent is not percent: - self.percent = percent - return percent - else: - return 0 - - def __init__(self, debuggy = False, verbose = False): - self.show_debug = debuggy - self.show_verbose = verbose - - def set_verbose(self, flag): - self.show_verbose = flag - - def set_debug(self, flag): - self.show_debug = flag - - def info(self, msg, verbose = False): - "give an informative message" - pass - - def ack(self, msg): - "inform the user of an important event and wait for acknowledgement" - pass - - def debug(self, msg): - "show debugging info" - if self.show_debug: - self.info('DEBUG: ' + msg) - - def warning(self,msg): - "warn the user" - pass - - def error(self,msg): - "inform a (possibly fatal) error" - pass - - def action(self,msg): - "uh?" - pass - - def choose(self, msg, list): - "ask the user to choose from a list of alternatives" - pass - - def confirm(self, msg): - "ask a yes/no question" - pass - - def display_progress(self, pd): - "display progress" - pass diff --git a/tags/pisi-1.0_alpha2/pisi/uri.py b/tags/pisi-1.0_alpha2/pisi/uri.py deleted file mode 100644 index a9ac0589..00000000 --- a/tags/pisi-1.0_alpha2/pisi/uri.py +++ /dev/null @@ -1,102 +0,0 @@ -# Copyright (C) 2005, TUBITAK/UEKAE -# -# 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. -# -# Please read the COPYING file. -# -# Simplifies working with URLs, purl module provides common URL -# parsing and processing - -from urlparse import urlparse -from os.path import basename - -import pisi.util as util - -class URI(object): - """URI class provides a URL parser and simplifies working with - URLs.""" - - def __init__(self, uri=None): - if uri: - self.set_uri(uri) - else: - self.__scheme = None - self.__location = None - self.__path = None - self.__filename = None - self.__params = None - self.__query = None - self.__fragment = None - self.__uri = None - - self.__authinfo = None - - def get_uri(self): - if self.__uri: - return self.__uri - return None - - def set_uri(self, uri): - # (scheme, location, path, params, query, fragment) - u = urlparse(uri, "file") - self.__scheme = u[0] - self.__location = u[1] - self.__path = u[2] - self.__filename = basename(self.__path) - self.__params = u[3] - self.__query = u[4] - self.__fragment = u[5] - - self.__uri = uri - - def is_local_file(self): - if self.scheme() == "file": - return True - else: - return False - - def is_remote_file(self): - return not self.is_local_file() - - def is_absolute_path(self): - return util.absolute_path(self.__path) - - def is_relative_path(self): - return not self.is_absolute_path() - - def set_auth_info(self, authTuple): - if not isinstance(authTuple, tuple): - raise Exception, "setAuthInfo needs a tuple (user, pass)" - self.__authinfo = authTuple - - def auth_info(self): - return self.__authinfo - - def scheme(self): - return self.__scheme - - def location(self): - return self.__location - - def path(self): - return self.__path - - def filename(self): - return self.__filename - - def params(self): - return self.__params - - def query(self): - return self.__query - - def fragment(self): - return self.__fragment - - def __str__(self): - return self.get_uri() - - uri = property(get_uri, set_uri) diff --git a/tags/pisi-1.0_alpha2/pisi/util.py b/tags/pisi-1.0_alpha2/pisi/util.py deleted file mode 100644 index 2917f6b6..00000000 --- a/tags/pisi-1.0_alpha2/pisi/util.py +++ /dev/null @@ -1,374 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright (C) 2005, TUBITAK/UEKAE -# -# 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. -# -# Please read the COPYING file. -# - -# misc. utility functions, including process and file utils - -# Authors: Eray Ozkural -# Baris Metin -# S. Caglar Onur -# A. Murat Eren - -# standard python modules -import os -import sys -import sha -import shutil -import statvfs - -# pisi modules -import pisi -import pisi.context as ctx - -class FileError(pisi.Error): - pass - -class UtilError(pisi.Error): - pass - - -######################### -# spec validation utility # -######################### - -class Checks: - def __init__(self): - self.list = None - - def add(self, err): - if not self.list: - self.list = [] - self.list.append(err) - - def join(self, list): - if list != None: - if not self.list: - self.list = [] - self.list.extend(list) - - def has_tag(self, var, section, name): - if not var: - if not self.list: - self.list = [] - self.list.append("%s section should have a '%s' tag" % (section, name)) - - -######################### -# string/list functions # -######################### - -def unzip(seq): - return zip(*seq) - -def concat(l): - '''concatenate a list of lists''' - return reduce( lambda x,y: x+y, l ) - -def strlist(l): - """concatenate string reps of l's elements""" - return "".join(map(lambda x: str(x) + ' ', l)) - -def multisplit(str, chars): - """ split str with any of chars""" - l = [str] - for c in chars: - l = concat(map(lambda x:x.split(c), l)) - return l - -def same(l): - '''check if all elements of a sequence are equal''' - if len(l)==0: - return True - else: - last = l.pop() - for x in l: - if x!=last: - return False - return True - -def prefix(a, b): - '''check if sequence a is a prefix of sequence b''' - if len(a)>len(b): - return False - for i in range(0,len(a)): - if a[i]!=b[i]: - return False - return True - -def remove_prefix(a,b): - "remove prefix a from sequence b" - assert prefix(a,b) - return b[len(a):] - - -############################## -# Process Releated Functions # -############################## - -def run_batch(cmd): - """run command non-interactively and report return value and output""" - ui.info('running ' + cmd) - a = os.popen(cmd) - lines = a.readlines() - ret = a.close() - ui.debug('return value ' + ret) - successful = ret == None - if not successful: - ui.error('ERROR: executing command: ' + cmd + '\n' + strlist(lines)) - return (successful,lines) - - -def xterm_title(message): - """sets message as a console window's title""" - if os.environ.has_key("TERM") and sys.stderr.isatty(): - terminalType = os.environ["TERM"] - for term in ["xterm", "Eterm", "aterm", "rxvt", "screen", "kterm", "rxvt-unicode"]: - if terminalType.startswith(term): - sys.stderr.write("\x1b]2;"+str(message)+"\x07") - sys.stderr.flush() - break - -def xterm_title_reset(): - """resets console window's title""" - if os.environ.has_key("TERM"): - terminalType = os.environ["TERM"] - xterm_title(os.environ["TERM"]) - -############################# -# Path Processing Functions # -############################# - -def splitpath(a): - """split path into components and return as a list - os.path.split doesn't do what I want like removing trailing /""" - comps = a.split(os.path.sep) - if comps[len(comps)-1]=='': - comps.pop() - return comps - -# I'm not sure how necessary this is. Ahem. -def commonprefix(l): - """an improved version of os.path.commonprefix, - returns a list of path components""" - common = [] - comps = map(splitpath, l) - for i in range(0, min(len,l)): - compi = map(lambda x: x[i], comps) # get ith slice - if same(compi): - common.append(compi[0]) - return common - -# but this one is necessary -def subpath(a, b): - "find if path a is before b in the directory tree" - return prefix(splitpath(a), splitpath(b)) - -def removepathprefix(prefix, path): - "remove path prefix a from b, finding the pathname rooted at a" - comps = remove_prefix(splitpath(prefix), splitpath(path)) - if len(comps) > 0: - return os.path.join(*tuple(comps)) - else: - return "" - -def absolute_path(path): - "determine if given @path is absolute" - comps = splitpath(path) - return comps[0] == '' - -#################################### -# File/Directory Related Functions # -#################################### - -def check_file(file, mode = os.F_OK): - "shorthand to check if a file exists" - if not os.access(file, mode): - raise FileError("File " + file + " not found") - return True - -def check_dir(dir): - """check if directory exists, and create if it doesn't. - works recursively""" - dir = dir.strip().rstrip("/") - if not os.access(dir, os.F_OK): - os.makedirs(dir) - -def clean_dir(path): - "Remove all content of a directory (top)" - # don't reimplement the wheel - if os.path.exists(path): - shutil.rmtree(path) - -def dir_size(dir): - """ calculate the size of files under a dir - based on the os module example""" - # It's really hard to give an approximate value for package's - # installed size. Gettin a sum of all files' sizes if far from - # being true. Using 'du' command (like Debian does) can be a - # better solution :(. - getsize = os.path.getsize - join = os.path.join - islink = os.path.islink - def sizes(): - for root, dirs, files in os.walk(dir): - yield sum([getsize(join(root, name)) for name in files if not islink(join(root,name))]) - return sum( sizes() ) - -def copy_file(src,dest): - """copy source file to destination file""" - check_file(src) - check_dir(os.path.dirname(dest)) - shutil.copyfile(src, dest) - -def get_file_hashes(top, exclude_prefix=None, removePrefix=None): - """Generator function iterates over a toplevel path and returns the - (filePath, sha1Hash) tuple for all files. If excludePrefixes list - is given as a parameter, function will exclude the filePaths - matching those prefixes. The removePrefix string parameter will be - used to remove prefix from filePath while matching excludes, if - given.""" - - # also handle single files - if os.path.isfile(top): - yield (top, sha1_file(top)) - return - - def has_excluded_prefix(filename): - if exclude_prefix and removePrefix: - tempfnam = remove_prefix(removePrefix, filename) - for p in exclude_prefix: - if tempfnam.startswith(p): - return 1 - else: - return 0 - return 0 - - for root, dirs, files in os.walk(top, topdown=False): - #bug 339 - if os.path.islink(root) and not has_excluded_prefix(root): - #yield the symlink.. - #bug 373 - yield (root, sha1_data(os.readlink(root))) - exclude_prefix.append(remove_prefix(removePrefix, root) + "/") - continue - - #bug 397 - for dir in dirs: - d = os.path.join(root, dir) - if os.path.islink(d) and not has_excluded_prefix(d): - yield (d, sha1_data(os.readlink(d))) - exclude_prefix.append(remove_prefix(removePrefix, d) + "/") - - #bug 340 - if os.path.isdir(root) and not has_excluded_prefix(root): - parent, r, d, f = root, '', '', '' - for r, d, f in os.walk(parent, topdown=False): pass - if not f and not d: - yield (parent, sha1_file(parent)) - - for fname in files: - f = os.path.join(root, fname) - if has_excluded_prefix(f): - continue - #bug 373 - elif os.path.islink(f): - yield (f, sha1_data(os.readlink(f))) - else: - yield (f, sha1_file(f)) - -def copy_dir(src, dest): - """copy source dir to destination dir recursively""" - shutil.copytree(src, dest) - -def check_file_hash(filename, hash): - """Check the files integrity with a given hash""" - if sha1_file(filename) == hash: - return True - - return False - -def sha1_file(filename): - """calculate sha1 hash of filename""" - # Broken links can cause problem! - try: - m = sha.new() - f = file(filename, 'rb') - for line in f: - m.update(line) - return m.hexdigest() - except IOError: - return "0" - -def sha1_data(data): - """calculate sha1 hash of given data""" - try: - m = sha.new() - m.update(data) - return m.hexdigest() - except: - return "0" - -def uncompress(patchFile, compressType="gz", targetDir=None): - """uncompresses a file and returns the path of the uncompressed - file""" - if targetDir: - filePath = os.path.join(targetDir, - os.path.basename(patchFile)) - else: - filePath = os.path.basename(patchFile) - - if compressType == "gz": - from gzip import GzipFile - obj = GzipFile(patchFile) - elif compressType == "bz2": - from bz2 import BZ2File - obj = BZ2File(patchFile) - - open(filePath, "w").write(obj.read()) - return filePath - - -def do_patch(sourceDir, patchFile, level, target = ''): - """simple function to apply patches..""" - cwd = os.getcwd() - os.chdir(sourceDir) - - check_file(patchFile) - level = int(level) - cmd = "patch -p%d %s< %s" % (level, target, patchFile) - p = os.popen(cmd) - o = p.readlines() - retval = p.close() - if retval: - raise UtilError("ERROR: patch (%s) failed: %s" % (patchFile, - strlist (o))) - - os.chdir(cwd) - -def partition_freespace(directory): - """ returns free space of given directory's partition """ - st = os.statvfs(directory) - return st[statvfs.F_BSIZE] * st[statvfs.F_BFREE] - -def clean_locks(top = '.'): - for root, dirs, files in os.walk(top): - for fn in files: - if fn.endswith('.lock'): - path = os.path.join(root, fn) - ctx.ui.info('Removing lock %s', path) - os.unlink(path) - -######################################## -# Package/Repository Related Functions # -######################################## - -def package_name(name, version, release): - return name + '-' + version + '-' + release + ctx.const.package_prefix diff --git a/tags/pisi-1.0_alpha2/pisi/version.py b/tags/pisi-1.0_alpha2/pisi/version.py deleted file mode 100644 index 263b6a2f..00000000 --- a/tags/pisi-1.0_alpha2/pisi/version.py +++ /dev/null @@ -1,161 +0,0 @@ -# Copyright (C) 2005, TUBITAK/UEKAE -# -# 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. -# -# Please read the COPYING file. -# - -# version structure - -# Authors: Eray Ozkural - -import re - -import pisi.util as util - - - -# Basic rule is: -# p > (no suffix) > rc > pre > beta > alpha -keywords = {"alpha": 0, - "beta" : 1, - "pre" : 2, - "rc" : 3, - "NOKEY" : 4, - "p" : 5} - -# helper functions -def has_keyword(versionitem): - if versionitem._keyword != "NOKEY": - return True - - return False - - - -class VersionItem: - _keyword = "NOKEY" - _value = 0 - - def __init__(self, itemstring): - - for keyword in keywords.keys(): - if itemstring.startswith(keyword): - - if self._keyword == "NOKEY": - self._keyword = keyword - else: - # longer match is correct - if len(keyword) > len(self._keyword): - self._keyword = keyword - - if self._keyword == "NOKEY": - self._value = itemstring - else: - # rest is the version item's value. And each must have - # one! - self._value = itemstring[len(self._keyword):] - - def __lt__(self,rhs): - l = keywords[self._keyword] - r = keywords[rhs._keyword] - if l < r: - return True - elif l == r: - return self._value < rhs._value - else: # l > r - return False - - def __le__(self,rhs): - l = keywords[self._keyword] - r = keywords[rhs._keyword] - if l < r: - return True - elif l == r: - return self._value <= rhs._value - else: # l > r - return False - - def __gt__(self,rhs): - l = keywords[self._keyword] - r = keywords[rhs._keyword] - if l > r: - return True - elif l == r: - return self._value > rhs._value - else: # l < r - return False - - def __ge__(self,rhs): - l = keywords[self._keyword] - r = keywords[rhs._keyword] - if l > r: - return True - elif l == r: - return self._value >= rhs._value - else: # l < r - return False - - - -class Version: - - def __init__(self, verstring): - self.comps = [] - for i in util.multisplit(verstring,'.-_'): - # some version strings can contain ascii chars at the - # back. As an example: 2.11a - # We split '11a' as two items like '11' and 'a' - s = re.compile("[a-z-A-Z]$").search(i) - if s: - head = i[:s.start()] - tail = s.group() - self.comps.append(VersionItem(head)) - self.comps.append(VersionItem(tail)) - else: - self.comps.append(VersionItem(i)) - - self.verstring = verstring - - def string(self): - return self.verstring - - def pred(self, rhs, pred): - - loop = len(self.comps) - if len(rhs.comps) > loop: - loop = len(rhs.comps) - - for i in range(0, loop): - try: - litem = self.comps[i] - except IndexError: - litem = VersionItem("") - - try: - ritem = rhs.comps[i] - except IndexError: - ritem = VersionItem("") - - if pred(litem, ritem): - return True - - else: - return False - - def __lt__(self,rhs): - return self.pred(rhs, lambda x,y: xy) - - def __ge__(self,rhs): - return self.pred(rhs, lambda x,y: x>=y) - - diff --git a/tags/pisi-1.0_alpha2/pisi/xmlext.py b/tags/pisi-1.0_alpha2/pisi/xmlext.py deleted file mode 100644 index a97de5f2..00000000 --- a/tags/pisi-1.0_alpha2/pisi/xmlext.py +++ /dev/null @@ -1,163 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright (C) 2005, TUBITAK/UEKAE -# -# 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. -# -# Please read the COPYING file. -# -# Authors: Eray Ozkural -# Baris Metin 0 - - # iterative code to search for the path - - # get DOM for top node - nodeList = getTagByName(node, tags[0]) - if len(nodeList) == 0: - return None # not found - - node = nodeList[0] # discard other matches - for tag in tags[1:]: - nodeList = getTagByName(node, tag) - if len(nodeList) == 0: - return None - else: - node = nodeList[0] - - return node - -def getAllNodes(node, tagPath): - """retrieve all nodes that match a given tag path.""" - - tags = tagPath.split('/') - - if len(tags) == 0: - return [] - - nodeList = getTagByName(node, tags[0]) - if len(nodeList) == 0: - return [] - - for tag in tags[1:]: - results = map(lambda x: getTagByName(x, tag), nodeList) - nodeList = [] - for x in results: - nodeList.extend(x) - pass # emacs indentation error, keep it here - - if len(nodeList) == 0: - return [] - - return nodeList - -def createTagPath(dom, node, tags): - """create new child at the end of a tag chain starting from node - no matter what""" - if len(tags)==0: - return node - for tag in tags: - node = node.appendChild(dom.createElement(tag)) - return node - -def addTagPath(dom, node, tags, newnode=None): - """add newnode at the end of a tag chain, smart one""" - node = createTagPath(dom, node, tags) - if newnode: # node to add specified - node.appendChild(newnode) - return node - -def addNode(dom, node, tagpath, newnode = None): - """add a new node at the end of the tree""" - - assert type(tagpath)==str - tags = [] - if tagpath != "": - tags = tagpath.split('/') # tag chain - else: - addTagPath(dom, node, [], newnode) - return node - - assert len(tags)>0 # we want a chain - - # iterative code to search for the path - - # get DOM for top node - nodeList = getTagByName(node, tags[0]) - - if len(nodeList) == 0: - return addTagPath(dom, node, tags, newnode) - - node = nodeList[len(nodeList)-1] # discard other matches - tags.pop(0) - while len(tags)>0: - tag = tags.pop(0) - nodeList = getTagByName(node, tag) - if len(nodeList) == 0: # couldn't find - tags.insert(0, tag) # put it back in - return addTagPath(dom, node, tags, newnode) - else: - node = nodeList[len(nodeList)-1] - else: - # had only one tag.. - return addTagPath(dom, node, tags, newnode) - - return node diff --git a/tags/pisi-1.0_alpha2/pisi/xmlfile.py b/tags/pisi-1.0_alpha2/pisi/xmlfile.py deleted file mode 100644 index 8bf280f3..00000000 --- a/tags/pisi-1.0_alpha2/pisi/xmlfile.py +++ /dev/null @@ -1,595 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright (C) 2005, TUBITAK/UEKAE -# -# 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. -# -# Please read the COPYING file. -# -# -# Authors: Eray Ozkural -# Gurer Ozen -# Baris Metin - - -""" - xmlfile is a helper module for accessing XML files using - xml.dom.minidom. - - XmlFile class further abstracts a dom object using the - high-level dom functions provided in xmlext module (and sorely lacking - in xml.dom :( ) - - autoxml is a metaclass for automatic XML translation, using - a miniature type system. (w00t!) This is based on an excellent - high-level XML processing prototype that Gurer prepared. - - Method names are mixedCase for compatibility with minidom, - an old library. -""" - -# System -import xml.dom.minidom as mdom -from xml.parsers.expat import ExpatError -import codecs -import types - -# PiSi -import pisi -from pisi.xmlext import * -import pisi.context as ctx - - -class Error(pisi.Error): - pass - - -mandatory, optional = range(2) # poor man's enum - -# basic types - -Text = types.StringType -Integer = types.IntType - -class LocalText(object): - """Handles XML tags/attributes with localized text""" - - def __init__(): - locs = {} - - def decode(nodes, req): - # flags, tag name, instance attribute - if not nodes: - if req == mandatory: - pass - #errs.append("Tag '%s' should have at least one '%s' tag\n" % (parent.tagName, d[2])) - else: - for node in nodes: - lang = getAttribute(node, "xml:lang") - c = getText(node) - if not c: - #errs.append("Tag '%s' should have some text data\n" % node.tagName) - break - # FIXME: check for dups and 'en' - if not lang: - lang = 'en' - self.locs[lang] = c - # FIXME: return full list too - L = language - if not locs.has_key(L): - L = 'en' - if not locs.has_key(L): - #errs.append("Tag '%s' should have an English version\n" % d[2]) - return "" - return locs[L] - - -class autoxml(type): - """High-level automatic XML transformation interface for xmlfile. - The idea is to declare a class for each XML tag. Inside the - class the tags and attributes nested in the tag are further - elaborated. A simple example follows: - - class Employee: - __metaclass__ = autoxml - t_Name = [xmlfile.Text, xmlfile.mandatory] - a_Type = [xmlfile.Integer, xmlfile.optional] - - This class defines a tag and an attribute nested in Employee - class. Name is a string and type is an integer, called basic - types. - While the tag is mandatory, the attribute may be left out. - - Other basic types supported are: xmlfile.Float, xmlfile.Double - and (not implemented yet): xmlfile.Binary - - By default, the class name is taken as the corresponding tag, - which may be overridden by defining a tag attribute. Thus, - the same tag may also be written as: - - class EmployeeXML: - ... - tag = 'Employee' - ... - - In addition to basic types, we allow for two kinds of complex - types: class types and list types. - - A declared class can be nested in another class as follows - - class Position: - __metaclass__ = autoxml - t_Name = [xmlfile.Text, xmlfile.mandatory] - t_Description = [xmlfile.Text, xmlfile.optional] - - which we can add to our Employee class. - - class Employee: - __metaclass__ = autoxml - t_Name = [xmlfile.Text, xmlfile.mandatory] - a_Type = [xmlfile.Integer, xmlfile.optional] - t_Position = [Position, xmlfile.mandatory] - - Note some unfortunate redundancy here with Position; this is - justified by the implementation (kidding). Still, you might - want to assign a different name than the class name that - goes in there, which may be fully qualified. - - There is more! Suppose we want to define a company, with - of course many employees. - - class Company: - __metaclass__ = autoxml - t_Employees = [ [Employee], xmlfile.mandatory, 'Employee'] - - Logically, inside the Company/Employees tag, we will have several - Employes tags, which are inserted to the Employees instance variable of - Company in order of appearance. - - The mandatory flag here asserts that at least one such record - is to be found. - - You see, it works like magic, when it works of course. All of it - done without a single brain exploding. - - """ - - def __init__(cls, name, bases, dict): - """entry point for metaclass code""" - print 'generating class', name - - # add XmlFile as one of the superclasses, we're smart - bases = list(bases) - if not XmlFile in bases: - bases.append(XmlFile) - - # standard initialization - super(autoxml, cls).__init__(name, bases, dict) - - #TODO: initialize class attribute __xml_tags - #setattr(cls, 'xml_variables', []) - - # default class tag is class name - if not dict.has_key('tag'): - cls.tag = name - - # generate helper routines, for each XML component - inits = [] - decoders = [] - encoders = [] - formatters = [] - for var in dict: - if var.startswith('t_') or var.startswith('a_'): - name = var[2:] - print 'generating member', name - if var.startswith('a_'): - x = autoxml.gen_attr_member(cls, name) - elif var.startswith('t_'): - x = autoxml.gen_tag_member(cls, name) - (init, decoder, encoder, formatter) = x - inits.append(init) - decoders.append(decoder) - encoders.append(encoder) - formatters.append(formatter) - - # generate top-level helper functions - cls.initializers = inits - def initialize(self): -# XmlFile.__init__(self, cls.tag) - for init in self.__class__.initializers: - init(self) - cls.__init__ = initialize - - cls.decoders = decoders - def decode(self, node): - for decode_member in self.__class__.decoders: - decode_member(self, node) - cls.decode = decode - - cls.encoders = encoders - def encode(self, xml): - node = xml.newNode(cls.tag) - for encode_member in self.__class__.encoders: - encode_member(self, xml, node) - xml.dom.documentElement = node - return node - cls.encode = encode - - cls.formatters = formatters - def format(self): - string = '' - for formatter in self.__class__.formatters: - string += formatter(self) - return string - cls.format = format - if not dict.has_key('__str__'): - cls.__str__ = format - - def gen_attr_member(cls, attr): - """generate readers and writers for an attribute member""" - print 'attr:', attr - spec = getattr(cls, 'a_' + attr) - tag_type = spec[0] - assert type(tag_type) == type(type) - def readtext(node, attr): - return getNodeAttribute(node, attr) - def createnode(xml, attr_name): - return xml.newAttribute(attr_name) # create an attribute node - def writetext(xml, attr, nil, text): - attr.value = text - anonfuns = cls.gen_anon_basic(attr, spec, readtext, createnode, writetext) - def mergetext(node, attr): - node.setAttributeNode(attr) - return cls.gen_named_comp(attr, spec, anonfuns, mergetext) - - def gen_tag_member(cls, tag): - """generate helper funs for tag member of class""" - spec = getattr(cls, 't_' + tag) - anonfuns = cls.gen_tag(tag, spec) - def mergetext(node, newnode): - print 'mergenode', node, newnode - node.appendChild(newnode) - return cls.gen_named_comp(tag, spec, anonfuns, mergetext) - - def gen_tag(cls, tag, spec): - """generate readers and writers for the tag""" - tag_type = spec[0] - if type(tag_type) is types.TypeType: - def readtext(node, tagpath): - return getNodeText(node, tagpath) - def createnode(xml, tag): - return xml.newNode(tag) - def writetext(xml, node, tagpath, text): - xml.addTextNodeUnder(node, tagpath, text) - return cls.gen_anon_basic(tag, spec, readtext, - createnode, writetext) - elif type(tag_type) is types.ListType: - return cls.gen_list_tag(tag, spec) - elif type(tag_type) is autoxml or type(tag_type) is types.ClassType: - return cls.gen_class_tag(tag, spec) - - def gen_named_comp(cls, token, spec, anonfuns, mergetext): - """generate a named component tag/attr. a decoration of - anonymous functions that do not bind to variable names""" - name = cls.mixed_case(token) - token_type = spec[0] - req = spec[1] - (init_a, decode_a, encode_a, format_a) = anonfuns - - def init(self): - """initialize component""" - setattr(self, name, init_a()) - - def decode(self, node): - """decode component from DOM node""" - setattr(self, name, decode_a(node)) - - def encode(self, xml, node): - """encode self inside, possibly new, DOM node using xml""" - if hasattr(self, name): - value = getattr(self, name) - else: - value = None - newnode = encode_a(xml, value) - if newnode: - mergetext(node, newnode) - - def format(self): - if hasattr(self, name): - value = getattr(self,name) - return '%s: %s\n' % (token, format_a(value)) - else: - if req == mandatory: - raise Error('Mandatory variable %s not available' % name) - return '' - - return (init, decode, encode, format) - - def mixed_case(cls, identifier): - """helper function to turn token name into mixed case""" - if identifier is "": - return "" - else: - return identifier[0].lower() + identifier[1:] - - def tagpath_head_last(cls, tagpath): - "returns split of the tag path into last tag and the rest" - try: - lastsep = tagpath.rindex('/') - except ValueError, e: - return ('', tagpath) - return (tagpath[:lastsep], tagpath[lastsep+1:]) - - def parse_spec(cls, token, spec): - """decompose member specification""" - name = cls.mixed_case(token) - token_type = spec[0] - req = spec[1] - - if len(spec)>=3: - path = spec[2] # an alternative path specified - else: - path = token # otherwise it's the same name as - # the token - return name, token_type, req, path - - def gen_anon_basic(cls, token, spec, readtext, createnode, writetext): - """Generate a tag or attribute with one of the basic - types like integer. This has got to be pretty generic - so that we can invoke it from the complex types such as Class - and List. The readtext and writetext arguments achieve - the DOM text access for this datatype.""" - - name, token_type, req, tagpath = cls.parse_spec(token, spec) - - def initialize(): - """default value for all basic types is None""" - return None - - def decode(node): - """decode from DOM node, the value, watching the spec""" - text = readtext(node, token) - print 'read text ', text - if text: - try: - value = autoxml.basic_cons_map[token_type](text) - except Error: - raise Error('Type mismatch: read text cannot be decoded') - return value - else: - if req == mandatory: - raise Error('Mandatory token %s not available' % token) - else: - return None - - def encode(xml, value): - """encode given value inside DOM node""" - if value: - node = createnode(xml, token) - writetext(xml, node, token, str(value)) - return node - else: - if req == mandatory: - raise Error('Mandatory argument not available') - - def format(value): - """format value for pretty printing""" - return str(value) - - return initialize, decode, encode, format - - def gen_class_tag(cls, tag, spec): - """generate a class datatype""" - name, tag_type, req, path = cls.parse_spec(tag, spec) - - def make_object(): - return tag_type.__new__(tag_type) - - def init(): - return make_object() - - def decode(node): - node = getNode(node, tag) - if node: - try: - obj = make_object() - obj.decode(node) - return obj - except Error: - raise Error('Type mismatch: DOM cannot be decoded') - else: - if req == mandatory: - raise Error('Mandatory argument not available') - else: - return None - - def encode(xml, obj): - if obj: - try: - node = obj.encode(xml) - #FIXME: change node's tag? - return - except Error: - raise Error('Object cannot be encoded') - else: - if req == mandatory: - raise Error('Mandatory argument not available') - - def format(obj): - return obj.format() - - return (init, decode, encode, format) - - def gen_list_tag(cls, tag, spec): - """generate a list datatype. stores comps in tag/comp_tag""" - name, tag_type, req, comp_tag = cls.parse_spec(tag, spec) - #head, last = cls.tagpath_head_last(path) - - if len(tag_type) != 1: - raise Error('List type must contain only one element') - - x = cls.gen_tag(comp_tag, [tag_type[0], mandatory]) - (init_item, decode_item, encode_item, format_item) = x - - def init(): - return [] - - def decode(node): - l = [] - nodes = getAllNodes(node, tag + '/' + comp_tag) - print node, tag + '/' + comp_tag - print 'F U', nodes - if len(nodes) is 0 and req is mandatory: - raise Error('Mandatory list empty') - for node in nodes: - dummy = node.ownerDocument.createElement("Dummy") - dummy.appendChild(node) - l.append(decode_item(dummy)) - return l - - def encode(xml, l): - dummy = xml.newNode("Dummy") - if len(l) > 0: - for item in l: - item_node = encode_item(xml, item) - xml.addNodeUnder(dummy, comp_tag, item_node) - return getNode(dummy, "Dummy") - else: - if req is mandatory: - raise Error('Mandatory list empty') - - def format(l): - #print 'format:', name - s = '' - for ix in range(len(l)): - s += str(ix+1) + ': ' + format_item(l[ix]) - if ix != len(l)-1: - s += ', ' - return s - - return (init, decode, encode, format) - - basic_cons_map = { - types.StringType : str, - types.IntType : int - } - - -class XmlFile(object): - """A class to help reading and writing an XML file""" - - def __init__(self, rootTag): - self.rootTag = rootTag - self.newDOM() - - def newDOM(self): - """clear DOM""" - impl = mdom.getDOMImplementation() - self.dom = impl.createDocument(None, self.rootTag, None) - - def unlink(self): - """deallocate DOM structure""" - self.dom.unlink() - - def readxml(self, fileName): - try: - self.dom = mdom.parse(fileName) - except ExpatError, inst: - raise Error("File '%s' has invalid XML: %s\n" % (fileName, - str(inst))) - - def writexml(self, fileName): - f = codecs.open(fileName,'w', "utf-8") - f.write(self.dom.toprettyxml()) - f.close() - - def verifyRootTag(self): - actual_roottag = self.dom.documentElement.tagName - if actual_roottag != self.rootTag: - raise Error("Root tagname %s not identical to %s as expected " % - (actual_roottag, self.rootTag) ) - - # construction helpers - - def newNode(self, tag): - return self.dom.createElement(tag) - - def newTextNode(self, text): - return self.dom.createTextNode(text) - - def newAttribute(self, attr): - return self.dom.createAttribute(attr) - - # read helpers - - def getNode(self, tagPath = ""): - """returns the *first* matching node for given tag path.""" - self.verifyRootTag() - return getNode(self.dom.documentElement, tagPath) - - def getNodeText(self, tagPath): - """returns the text of *first* matching node for given tag path.""" - node = self.getNode(tagPath) - if not node: - return None - return getNodeText(node) - - def getAllNodes(self, tagPath): - """returns all nodes matching a given tag path.""" - self.verifyRootTag() - return getAllNodes(self.dom.documentElement, tagPath) - - def getChildren(self, tagpath): - """ returns the children of the given path""" - node = self.getNode(tagpath) - return node.childNodes - - # get only elements of a given type - #FIXME: this doesn't work - def getChildrenWithType(self, tagpath, type): - """ returns the children of the given path, only with given type """ - node = self.getNode(tagpath) - return filter(lambda x:x.nodeType == type, node.childNodes) - - # get only child elements - def getChildElts(self, tagpath): - """ returns the children of the given path, only with given type """ - node = self.getNode(tagpath) - try: - return filter(lambda x:x.nodeType == x.ELEMENT_NODE, - node.childNodes) - except AttributeError: - return None - - # write helpers - - def addNode(self, tagPath, newnode = None): - "this adds the newnode under given tag path" - self.verifyRootTag() - return addNode(self.dom, self.dom.documentElement, tagPath, - newnode) - - def addNodeUnder(self, node, tagPath, newnode = None): - "this adds the new stuff under node and then following tag path" - self.verifyRootTag() - return addNode(self.dom, node, tagPath, newnode) - - def addChild(self, newnode): - "add a new child node right under root element document" - self.dom.documentElement.appendChild(newnode) - - def addText(self, node, text): - "add text to node" - node.appendChild(self.newTextNode(text)) - - def addTextNode(self, tagPath, text): - "add a text node with given tag path" - node = self.addNode(tagPath, self.newTextNode(text)) - return node - - def addTextNodeUnder(self, node, tagPath, text): - "add a text node under given node with tag path (phew)" - return self.addNodeUnder(node, tagPath, self.newTextNode(text)) diff --git a/tags/pisi-1.0_alpha2/setup.py b/tags/pisi-1.0_alpha2/setup.py deleted file mode 100755 index 79ea43da..00000000 --- a/tags/pisi-1.0_alpha2/setup.py +++ /dev/null @@ -1,52 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (C) 2005, TUBITAK/UEKAE -# -# 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. -# -# Please read the COPYING file. - -# -# Install script for tengis. -# - -from distutils.core import setup - -PISI_VERSION = "0.1" - -def getRevision(): - import os - try: - p = os.popen("svn info 2> /dev/null") - for line in p.readlines(): - line = line.strip() - if line.startswith("Revision:"): - return line.split(":")[1].strip() - except: - pass - - # doesn't working in a Subversion directory - return None - -def getVersion(): - rev = getRevision() - if rev: - return "-r".join([PISI_VERSION, rev]) - else: - return PISI_VERSION - -setup(name="pisi", - version= getVersion(), - description="PISI (Packages Installed Successfully as Intended)", - long_description="PISI is the package management system of Pardus Linux.", - license="GNU GPL2", - author="Pardus Developers", - author_email="pisi@uludag.org.tr", - url="http://www.uludag.org.tr/eng/pisi/", - package_dir = {'': ''}, - packages = ['pisi', 'pisi.cli', 'pisi.actionsapi'], - scripts = ['pisi-cli'] - ) diff --git a/tags/pisi-1.0_alpha2/tests/README b/tags/pisi-1.0_alpha2/tests/README deleted file mode 100644 index 4ab7e004..00000000 --- a/tags/pisi-1.0_alpha2/tests/README +++ /dev/null @@ -1,10 +0,0 @@ -PISI test suite ---------------- - - -There are python unit tests and shell scripts in this directory. - -Shell scripts use the CLIs to perform package operations. - -For this there must be a symlink "packages" to the PISI package repository -until we can find a few sample packages to put here. diff --git a/tags/pisi-1.0_alpha2/tests/a.xml b/tags/pisi-1.0_alpha2/tests/a.xml deleted file mode 100644 index 4ca73e79..00000000 --- a/tags/pisi-1.0_alpha2/tests/a.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - Eray Ozkural - 868 - Lazy Tin - Tembel Teneke - - pisi - noatun - kdevelop - - - 18071976 - AI - - Baris - Gurer - Caglar - Meren - - - diff --git a/tags/pisi-1.0_alpha2/tests/actionsapitests.py b/tags/pisi-1.0_alpha2/tests/actionsapitests.py deleted file mode 100644 index dcb86358..00000000 --- a/tags/pisi-1.0_alpha2/tests/actionsapitests.py +++ /dev/null @@ -1,51 +0,0 @@ -# Copyright (C) 2005, TUBITAK/UEKAE -# -# 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. -# -# Please read the COPYING file. - -import unittest -import zipfile - -class ActionsAPITestCase(unittest.TestCase): - def setUp(self): - self.f = zipfile.ZipFile("helloworld-0.1-1.pisi", "r") - self.filelist = [] - - for file in self.f.namelist(): - self.filelist.append(file) - - def testFileList(self): - fileContent = ["files.xml", \ - "install/bin/helloworld", \ - "install/opt/PARDUS", \ - "install/opt/helloworld/helloworld", \ - "install/opt/uludag", \ - "install/sbin/helloworld", \ - "install/sys/PARDUS", \ - "install/sys/uludag", \ - "install/usr/bin/goodbye", \ - "install/usr/bin/helloworld", \ - "install/usr/lib/helloworld.o", \ - "install/usr/sbin/goodbye", \ - "install/usr/sbin/helloworld", \ - "install/usr/share/doc/helloworld-0.1-1/Makefile.am", \ - "install/usr/share/doc/helloworld-0.1-1/goodbyeworld.cpp", \ - "install/usr/share/info/Makefile.am", \ - "install/usr/share/info/Makefile.cvs", \ - "install/usr/share/info/Makefile.in", \ - "install/var/goodbye", \ - "install/var/hello", \ - "metadata.xml"] - - '''check number of files in package''' - self.assertEqual(fileContent.__len__(), self.filelist.__len__()) - - '''check file content''' - for file in self.filelist: - self.assert_(fileContent.__contains__(file)) - -suite = unittest.makeSuite(ActionsAPITestCase) diff --git a/tags/pisi-1.0_alpha2/tests/archivetests.py b/tags/pisi-1.0_alpha2/tests/archivetests.py deleted file mode 100644 index 7acbbd2f..00000000 --- a/tags/pisi-1.0_alpha2/tests/archivetests.py +++ /dev/null @@ -1,116 +0,0 @@ -# Copyright (C) 2005, TUBITAK/UEKAE -# -# 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. -# -# Please read the COPYING file. -# - -import unittest -import os -from os.path import exists as pathexists -from os.path import basename, islink, join - -import pisi.context as ctx -import pisi.api -from pisi import archive -from pisi import sourcearchive -from pisi import fetcher -from pisi import util -from pisi.build import BuildContext -from pisi.config import config -from pisi import uri - -class ArchiveFileTestCase(unittest.TestCase): - def setUp(self): - pisi.api.init() - - def testUnpackTar(self): - bctx = BuildContext("tests/popt/pspec.xml") - - achv = sourcearchive.SourceArchive(bctx) - - assert bctx.spec.source.archiveType == "targz" - - # skip fetching and directly unpack the previously fetched (by - # fetchertests) archive - if not achv.is_cached(interactive=False): - achv.fetch(interactive=False) - achv.unpack() - - targetDir = bctx.pkg_work_dir() - # but testing is hard - # "var/tmp/pisi/popt-1.7-3/work" (targetDir) - assert pathexists(targetDir + "/popt-1.7") - - testfile = targetDir + "/popt-1.7/Makefile.am" - assert pathexists(testfile) - - # check file integrity - self.assertEqual(util.sha1_file(testfile), - "5af9dd7d754f788cf511c57ce0af3d555fed009d") - - def testUnpackZip(self): - bctx = BuildContext("tests/pccts/pspec.xml") - - assert bctx.spec.source.archiveType == "zip" - - achv = sourcearchive.SourceArchive(bctx) - achv.fetch(interactive=False) - achv.unpack(cleanDir=True) - - targetDir = bctx.pkg_work_dir() - assert pathexists(targetDir + "/pccts") - - testfile = targetDir + "/pccts/history.txt" - assert pathexists(testfile) - - # check file integrity - self.assertEqual(util.sha1_file(testfile), - "f2be0f9783e84e98fe4e2b8201a8f506fcc07a4d") - -# TODO: no link file in pccts package. Need to find a ZIP file -# containing a symlink - # check for symbolic links -# testfile = targetDir + "/sandbox/testdir/link1" -# assert islink(testfile) - - def testMakeZip(self): - # first unpack our dear sandbox.zip - bctx = BuildContext("tests/pccts/pspec.xml") - targetDir = bctx.pkg_work_dir() - achv = sourcearchive.SourceArchive(bctx) - achv.fetch(interactive=False) - achv.unpack(cleanDir=True) - del achv - - newZip = targetDir + "/new.zip" - zip = archive.ArchiveZip(newZip, 'zip', 'w') - sourceDir = targetDir + "/pccts" - zip.add_to_archive(sourceDir) - zip.close() - - #TODO: do some more work to test the integrity of new zip file - - - def testUnpackZipCond(self): - bctx = BuildContext("tests/pccts/pspec.xml") - url = uri.URI(bctx.spec.source.archiveUri) - targetDir = bctx.pkg_work_dir() - filePath = join(config.archives_dir(), url.filename()) - - # check cached - if util.sha1_file(filePath) != bctx.spec.source.archiveSHA1: - fetch = fetcher.Fetcher(bctx.spec.source.archiveUri, targetDir) - fetch.fetch() - assert bctx.spec.source.archiveType == "zip" - - achv = archive.Archive(filePath, bctx.spec.source.archiveType) - achv.unpack_files(["pccts/history.txt"], targetDir) - assert pathexists(targetDir + "/pccts") - testfile = targetDir + "/pccts/history.txt" - assert pathexists(testfile) - -suite = unittest.makeSuite(ArchiveFileTestCase) diff --git a/tags/pisi-1.0_alpha2/tests/beta-light.sh b/tags/pisi-1.0_alpha2/tests/beta-light.sh deleted file mode 100755 index 962a52fb..00000000 --- a/tags/pisi-1.0_alpha2/tests/beta-light.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/sh - -pwd -PATH=$PATH:. -set -x -pisi-cli --ignore-comar remove unzip -set -e -pisi-cli --ignore-build-no build tests/zip/pspec.xml tests/unzip/pspec.xml -pisi-cli index . -pisi-cli add-repo repo1 pisi-index.xml -pisi-cli list-repo -pisi-cli update-repo repo1 -pisi-cli list-available -pisi-cli --ignore-comar install zip -pisi-cli list-installed -pisi-cli --ignore-comar remove unzip -pisi-cli info zip*.pisi -pisi-cli --ignore-comar install zip*.pisi -pisi-cli list-pending -pisi-cli configure-pending diff --git a/tags/pisi-1.0_alpha2/tests/beta-remoterepo.sh b/tags/pisi-1.0_alpha2/tests/beta-remoterepo.sh deleted file mode 100755 index 92d53964..00000000 --- a/tags/pisi-1.0_alpha2/tests/beta-remoterepo.sh +++ /dev/null @@ -1,25 +0,0 @@ -#!/bin/sh - -export PATH=$PATH:. -set -x # xtrace -set -e # errexit - -echo "beta functionality test script for testing remote repos" -echo "working directory:" `pwd` -echo "cleaning destination dir: tmp" -rm -rf tmp -#echo "*** repository tests" -pisi-cli add-repo pardus ftp://ftp.uludag.org.tr/pub/pisi/binary/system/base/pisi-index.xml -pisi-cli update-repo pardus -pisi-cli list-repo - -#echo "*** package ops" -pisi-cli list-available -pisi-cli info python -pisi-cli install python - -echo "*** database contents" -for x in `find tmp -iname '*.bdb'`; do - echo "contents of database " $x; - tools/cat-db.py $x; -done diff --git a/tags/pisi-1.0_alpha2/tests/beta-upgrade.sh b/tags/pisi-1.0_alpha2/tests/beta-upgrade.sh deleted file mode 100755 index 46a41943..00000000 --- a/tags/pisi-1.0_alpha2/tests/beta-upgrade.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/sh - -pwd -PATH=$PATH:. -set -x -e - -pisi-cli --ignore-build-no build tests/zip/pspec.xml tests/unzip/pspec.xml -pisi-cli --yes-all --ignore-comar install unzip-5.50-1.pisi zip-2.3-1.pisi - -mkdir -p myrepo -cd myrepo -../pisi-cli --ignore-build-no build ../tests/zip2/pspec.xml ../tests/unzip2/pspec.xml -cd .. -pisi-cli --absolute-uris index myrepo -pisi-cli remove-repo repo1 -pisi-cli add-repo repo1 pisi-index.xml -pisi-cli list-repo -pisi-cli update-repo repo1 -pisi-cli list-available -pisi-cli --install-info list-installed -pisi-cli list-upgrades -pisi-cli --ignore-comar upgrade zip -pisi-cli --install-info list-installed diff --git a/tags/pisi-1.0_alpha2/tests/beta.sh b/tags/pisi-1.0_alpha2/tests/beta.sh deleted file mode 100755 index 6e6a2eca..00000000 --- a/tags/pisi-1.0_alpha2/tests/beta.sh +++ /dev/null @@ -1,45 +0,0 @@ -#!/bin/sh - -echo "beta functionality test script" -echo "working directory:" `pwd` -echo "cleaning destination dir: tmp" -PATH=$PATH:. -set -x # xtrace -set -e # errexit -rm -rf tmp -#echo "*** build tests" -pisi-cli build https://svn.uludag.org.tr/pisi/trunk/system/base/zip/pspec.xml \ - https://svn.uludag.org.tr/pisi/trunk/system/base/unzip/pspec.xml - -#partial-builds -pisi-cli build-setup https://svn.uludag.org.tr/pisi/trunk/system/base/hdparm/pspec.xml -pisi-cli build-build https://svn.uludag.org.tr/pisi/trunk/system/base/hdparm/pspec.xml -pisi-cli build-install https://svn.uludag.org.tr/pisi/trunk/system/base/hdparm/pspec.xml -pisi-cli build-package https://svn.uludag.org.tr/pisi/trunk/system/base/hdparm/pspec.xml - -#echo "*** repository tests" - -pisi-cli index . -pisi-cli add-repo repo1 pisi-index.xml -pisi-cli update-repo repo1 -pisi-cli list-repo - -pisi-cli build https://svn.uludag.org.tr/pisi/trunk/system/base/grep/pspec.xml \ - https://svn.uludag.org.tr/pisi/trunk/system/base/flex/pspec.xml - -#echo "*** package ops" -pisi-cli info *.pisi -# pisi-cli list-available -pisi-cli install --ignore-comar zip -pisi-cli list-installed -pisi-cli remove --ignore-comar unzip -pisi-cli install --ignore-comar zip*.pisi -pisi-cli install --ignore-comar hdparm*.pisi flex*.pisi grep*.pisi -pisi-cli remove-repo repo1 -# pisi-cli list-available - -echo "*** database contents" -for x in `find tmp -iname '*.bdb'`; do - echo "contents of database " $x; - tools/cat-db.py $x; -done diff --git a/tags/pisi-1.0_alpha2/tests/configfiletests.py b/tags/pisi-1.0_alpha2/tests/configfiletests.py deleted file mode 100644 index 5de677ce..00000000 --- a/tags/pisi-1.0_alpha2/tests/configfiletests.py +++ /dev/null @@ -1,45 +0,0 @@ -# Copyright (C) 2005, TUBITAK/UEKAE -# -# 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. -# -# Please read the COPYING file. -# - -import unittest - -from pisi.configfile import ConfigurationFile - -class ConfigFileTestCase(unittest.TestCase): - - def setUp(self): - self.cf = ConfigurationFile("tests/pisi.conf") - - def testSections(self): - cf = self.cf - if not cf.general: - self.fail("No 'general' section found in ConfigurationFile") - if not cf.build: - self.fail("No 'build' section found in ConfigurationFile") - if not cf.dirs: - self.fail("No 'dirs' section found in ConfigurationFile") - - def testValues(self): - cf = self.cf - - # test values from pisi.conf file - self.assertEqual(cf.general.destinationdirectory, "/testing") - self.assertEqual(cf.dirs.archives_dir, "/disk2/pisi/archives") - - # test default values - self.assertEqual(cf.dirs.tmp_dir, "/var/tmp/pisi") - - def testAccessMethods(self): - cf = self.cf - - self.assertEqual(cf.build.host, cf.build["host"]) - self.assertEqual(cf.dirs.index_dir, cf.dirs["index_dir"]) - -suite = unittest.makeSuite(ConfigFileTestCase) diff --git a/tags/pisi-1.0_alpha2/tests/constantstests.py b/tags/pisi-1.0_alpha2/tests/constantstests.py deleted file mode 100644 index eb678e21..00000000 --- a/tags/pisi-1.0_alpha2/tests/constantstests.py +++ /dev/null @@ -1,67 +0,0 @@ -# Copyright (C) 2005, TUBITAK/UEKAE -# -# 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. -# -# Please read the COPYING file. -# - -import unittest - -import pisi.context as ctx - -class ContextTestCase(unittest.TestCase): - - def testConstness(self): - const = ctx.const - - # test if we can get a const attribute? - try: - test = const.package_prefix - self.assertNotEqual(test, "") - except AttributeError: - self.fail("Couldn't get const attribute") - - # test binding a new constant - const.test = "test binding" - - # test re-binding (which is illegal) - try: - const.test = "test rebinding" - # we shouldn't reach here - self.fail("Rebinding a constant works. Something is wrong!") - except: - # we achived our goal with this error. infact, this is a - # ConstError but we can't catch it directly here - pass - - # test unbinding (which is also illegal) - try: - del const.test - # we shouldn't reach here - self.fail("Unbinding a constant works. Something is wrong!") - except: - # we achived our goal with this error. infact, this is a - # ConstError but we can't catch it directly here - pass - - def testConstValues(self): - const = ctx.const - - constDict = { - "actions_file": "actions.py", - "setup_func": "setup", - "metadata_xml": "metadata.xml" - } - - for k in constDict.keys(): - if hasattr(const, k): - value = getattr(const, k) - self.assertEqual(value, constDict[k]) - else: - self.fail("Constants does not have an attribute named %s" % k) - - -suite = unittest.makeSuite(ContextTestCase) diff --git a/tags/pisi-1.0_alpha2/tests/fetchertests.py b/tags/pisi-1.0_alpha2/tests/fetchertests.py deleted file mode 100644 index 2cf232f1..00000000 --- a/tags/pisi-1.0_alpha2/tests/fetchertests.py +++ /dev/null @@ -1,38 +0,0 @@ -# Copyright (C) 2005, TUBITAK/UEKAE -# -# 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. -# -# Please read the COPYING file. -# - -import unittest -import os - -import pisi.context as ctx -import pisi.api -from pisi.specfile import SpecFile -from pisi import fetcher -from pisi import util -from pisi import uri - -class FetcherTestCase(unittest.TestCase): - def setUp(self): - pisi.api.init() - - self.spec = SpecFile() - self.spec.read("tests/popt/pspec.xml") - self.url = uri.URI(self.spec.source.archiveUri) - self.destpath = ctx.config.archives_dir() - self.fetch = fetcher.Fetcher(self.url, self.destpath) - - def testFetch(self): - self.fetch.fetch() - fetchedFile = os.path.join(self.destpath, self.url.filename()) - if os.access(fetchedFile, os.R_OK): - self.assertEqual(util.sha1_file(fetchedFile), - self.spec.source.archiveSHA1) - -suite = unittest.makeSuite(FetcherTestCase) diff --git a/tags/pisi-1.0_alpha2/tests/graphtests.py b/tags/pisi-1.0_alpha2/tests/graphtests.py deleted file mode 100644 index 60eb564d..00000000 --- a/tags/pisi-1.0_alpha2/tests/graphtests.py +++ /dev/null @@ -1,34 +0,0 @@ -# Copyright (C) 2005, TUBITAK/UEKAE -# -# 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. -# -# Please read the COPYING file. -# - -import unittest -import os - -from pisi import graph -from pisi.config import config - -class GraphTestCase(unittest.TestCase): - def setUp(self): - self.g0 = graph.Digraph() - self.g0.from_list([ (1,2), (1,3), (2,3), (3,4), (4, 5), (4,1)]) - - self.g1 = graph.Digraph() - self.g1.from_list([ (0,2), (0,3), (3,4), (2,4), (0,5), (5,4) ]) - - def testCycle(self): - self.assert_(not self.g0.cycle_free()) - self.assert_(self.g1.cycle_free()) - - def testTopologicalSort(self): - order = self.g1.topological_sort() - self.assertEqual(order[0], 0) - self.assertEqual(order[len(order)-1], 4) - -suite = unittest.makeSuite(GraphTestCase) diff --git a/tags/pisi-1.0_alpha2/tests/helloworld/actions.py b/tags/pisi-1.0_alpha2/tests/helloworld/actions.py deleted file mode 100644 index da6b1f5c..00000000 --- a/tags/pisi-1.0_alpha2/tests/helloworld/actions.py +++ /dev/null @@ -1,102 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# -# Copyright (C) 2005, TUBITAK/UEKAE -# -# 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. -# -# Please read the COPYING file. - -from pisi.actionsapi import autotools -from pisi.actionsapi import pisitools -from pisi.actionsapi import shelltools -from pisi.actionsapi import libtools -from pisi.actionsapi import get - -WorkDir = "helloworld" - -def setup(): - autotools.configure() - -def build(): - autotools.make() - -def install(): - autotools.install() - - '''/opt/helloworld/''' - pisitools.dodir("/opt/helloworld") - - '''/usr/share/doc/helloworld-0.1-1/Makefile.am''' - pisitools.dodoc("Makefile.am") - - '''/opt/helloworld/helloworld''' - pisitools.doexe("src/helloworld", "/opt/helloworld") - - '''/usr/share/info/Makefile.am''' - '''/usr/share/info/Makefile.cvs''' - '''/usr/share/info/Makefile.in''' - pisitools.doinfo("Makefile.*") - - '''/usr/lib/helloworld.o''' - pisitools.dolib("src/helloworld.o") - - '''/opt/hello''' - pisitools.insinto("/opt/", "src/helloworld", "hello") - '''/opt/hi''' - pisitools.insinto("/opt/", "src/helloworld", "hi") - - '''/opt/hello -> /var/hello''' - pisitools.domove("/opt/hello", "/var/") - '''/opt/hi -> /var/goodbye''' - pisitools.domove("/opt/hi", "/var/", "goodbye") - - '''/usr/bin/helloworld''' - pisitools.dobin("src/helloworld") - '''/bin/helloworld''' - pisitools.dobin("src/helloworld", "/bin") - - '''/usr/sbin/helloworld''' - pisitools.dosbin("src/helloworld") - '''/sbin/helloworld''' - pisitools.dosbin("src/helloworld", "/sbin") - - '''Hello, world! -> Goodbye, world!''' - pisitools.dosed("src/helloworld.cpp", "Hello, world!", "Goodbye, world!") - - '''/usr/sbin/goodbye --> helloworld''' - pisitools.dosym("helloworld", "/usr/sbin/goodbye") - '''/usr/bin/goodbye --> helloworld''' - pisitools.dosym("helloworld", "/usr/bin/goodbye") - - '''/home/pardus/''' - pisitools.dodir("/home/pardus") - '''delete pardus''' - pisitools.removeDir("/home/pardus") - '''delete home''' - pisitools.removeDir("/home") - - '''src/helloworld.cpp --> /usr/share/doc/helloworld-0.1-1/goodbyeworld.cpp''' - pisitools.newdoc("src/helloworld.cpp", "goodbyeworld.cpp") - - '''/opt/pardus''' - shelltools.touch("%s/opt/pardus" % get.installDIR()) - - '''/opt/pardus --> /opt/uludag''' - shelltools.copy("%s/opt/pardus" % get.installDIR(), "%s/opt/uludag" % get.installDIR()) - '''/opt/pardus --> /opt/Pardus''' - shelltools.move("%s/opt/pardus" % get.installDIR(), "%s/opt/PARDUS" % get.installDIR()) - - '''/opt/ --> /sys/''' - shelltools.copytree("%s/opt/" % get.installDIR(), "%s/sys/" % get.installDIR()) - - '''delete /sys/helloworld/helloworld''' - shelltools.unlink("%s/sys/helloworld/helloworld" % get.installDIR()) - '''delete /sys/helloworld''' - shelltools.unlinkDir("%s/sys/helloworld" % get.installDIR()) - - '''generate /usr/lib/helloworld.o''' - libtools.gen_usr_ldscript("helloworld.o") diff --git a/tags/pisi-1.0_alpha2/tests/helloworld/pspec.xml b/tags/pisi-1.0_alpha2/tests/helloworld/pspec.xml deleted file mode 100644 index a50bfda9..00000000 --- a/tags/pisi-1.0_alpha2/tests/helloworld/pspec.xml +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - helloworld - http://www.uludag.org.tr - - S.Çağlar Onur - caglar@uludag.org.tr - - GPL-2 - category - component - Dummy HelloWorld for testing ActionsAPI - Dummy HelloWorld for testing ActionsAPI - http://cekirdek.uludag.org.tr/~caglar/helloworld.tar.gz - - - 2005-08-21 - 0.1 - 1 - - - - - - helloworld - - / - - - - diff --git a/tags/pisi-1.0_alpha2/tests/installdbtests.py b/tags/pisi-1.0_alpha2/tests/installdbtests.py deleted file mode 100644 index 9a0c4b6e..00000000 --- a/tags/pisi-1.0_alpha2/tests/installdbtests.py +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright (C) 2005, TUBITAK/UEKAE -# -# 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. -# -# Please read the COPYING file. -# - -import unittest -import os - -import pisi.context as ctx -import pisi.api -import pisi.installdb -from pisi import util - -class InstallDBTestCase(unittest.TestCase): - - def setUp(self): - pisi.api.init() - self.installdb = ctx.installdb - - def testRemoveDummy(self): - self.installdb.remove('installtest') - self.assert_(not self.installdb.is_installed('installtest')) - - def testInstall(self): - self.installdb.purge('installtest') - self.installdb.install('installtest', '0.1', '2', '3') - - def testRemovePurge(self): - self.installdb.install('installtest', '0.1', '2', '3') - self.assert_(self.installdb.is_installed('installtest')) - self.installdb.remove('installtest') - self.assert_(self.installdb.is_removed('installtest')) - self.installdb.purge('installtest') - self.assert_(not self.installdb.is_recorded('installtest')) - -suite = unittest.makeSuite(InstallDBTestCase) diff --git a/tags/pisi-1.0_alpha2/tests/metadatatests.py b/tags/pisi-1.0_alpha2/tests/metadatatests.py deleted file mode 100644 index a37380c4..00000000 --- a/tags/pisi-1.0_alpha2/tests/metadatatests.py +++ /dev/null @@ -1,43 +0,0 @@ -# Copyright (C) 2005, TUBITAK/UEKAE -# -# 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. -# -# Please read the COPYING file. -# - -import unittest -import os - -from pisi import metadata -from pisi import util -from pisi.config import config - -class MetaDataTestCase(unittest.TestCase): - def setUp(self): - pass - - def testRead(self): - md = metadata.MetaData() - md.read('tests/popt/metadata.xml') - - self.assertEqual(md.package.license, ["As-Is"]) - - self.assertEqual(md.package.version, "1.7") - - self.assertEqual(md.package.installedSize, 149691) - return md - - def testWrite(self): - md = self.testRead() - md.write(os.path.join(config.tmp_dir(),'metadata-test.xml' )) - - def testVerify(self): - md = self.testRead() - if md.has_errors(): - self.fail("Couldn't verify!") - - -suite = unittest.makeSuite(MetaDataTestCase) diff --git a/tags/pisi-1.0_alpha2/tests/packagedbtests.py b/tags/pisi-1.0_alpha2/tests/packagedbtests.py deleted file mode 100644 index 50ba0375..00000000 --- a/tags/pisi-1.0_alpha2/tests/packagedbtests.py +++ /dev/null @@ -1,43 +0,0 @@ -# Copyright (C) 2005, TUBITAK/UEKAE -# -# 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. -# -# Please read the COPYING file. -# - -import unittest -import os - -import pisi.context as ctx -import pisi.api -from pisi.packagedb import PackageDB -from pisi import util -from pisi.specfile import SpecFile - -class PackageDBTestCase(unittest.TestCase): - - def setUp(self): - # setUp will be called for each test individually - pisi.api.init() - - self.spec = SpecFile() - self.spec.read('tests/popt/pspec.xml') - - self.pdb = PackageDB('testdb') - - def testAdd(self): - self.pdb.add_package(self.spec.packages[0]) - self.assert_(self.pdb.has_package('popt-libs')) - # close the database and remove lock - del self.pdb - - def testRemove(self): - self.pdb.remove_package('popt-libs') - self.assert_(not self.pdb.has_package('popt-libs')) - del self.pdb - -suite = unittest.makeSuite(PackageDBTestCase) - diff --git a/tags/pisi-1.0_alpha2/tests/packagetests.py b/tags/pisi-1.0_alpha2/tests/packagetests.py deleted file mode 100644 index 701c88e7..00000000 --- a/tags/pisi-1.0_alpha2/tests/packagetests.py +++ /dev/null @@ -1,49 +0,0 @@ -# Copyright (C) 2005, TUBITAK/UEKAE -# -# 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. -# -# Please read the COPYING file. -# - -import unittest -import os - -from pisi import util -from pisi.config import config -from pisi import package - - -class PackageTestCase(unittest.TestCase): - - def setUp(self): - self.pkgName = util.package_name("testing", - "5.1", - "2") - - def testAddExtract(self): - cur = os.getcwd() - tmpdir = config.tmp_dir() - testdir = os.path.join(cur, "tests/popt") - - pkg_path = os.path.join(tmpdir, self.pkgName) - pkg = package.Package(pkg_path, "w") - - os.chdir(testdir) - pkg.add_to_package("files.xml") - pkg.add_to_package("metadata.xml") - os.chdir(cur) - - pkg.close() - - pkg = package.Package(pkg_path) - pkg.extract_file("files.xml", cur) - if not os.path.exists("files.xml"): - self.fail("Package extract error") - - os.remove("files.xml") - os.remove(pkg_path) - -suite = unittest.makeSuite(PackageTestCase) diff --git a/tags/pisi-1.0_alpha2/tests/pccts/actions.py b/tags/pisi-1.0_alpha2/tests/pccts/actions.py deleted file mode 100644 index 50add5ca..00000000 --- a/tags/pisi-1.0_alpha2/tests/pccts/actions.py +++ /dev/null @@ -1,53 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# -# Copyright (C) 2005, TUBITAK/UEKAE -# -# 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. -# -# Please read the COPYING file. -# Timu EREN - -from pisi.actionsapi import autotools -from pisi.actionsapi import get -from pisi.actionsapi import pisitools -from pisi.actionsapi import shelltools - -WorkDir = "pccts" - -def setup(): - shelltools.export("COPT", get.CFLAGS()) - -def build(): -# autotools.make("COPT=%s" % (get.CFLAGS())) - autotools.make() - -def install(): - #autotools.install("") - - pisitools.dobin("bin/antlr") - pisitools.dobin("bin/dlg") - pisitools.dobin("bin/genmk") - pisitools.dobin("bin/sor") - - pisitools.insinto("/usr/include/pccts","h/*.h") - pisitools.insinto("/usr/include/pccts","h/*.c") - pisitools.insinto("/usr/include/pccts","h/*.cpp") - - - pisitools.insinto("/usr/include/pccts/sorcerer","sorcerer/h/*.h") - pisitools.insinto("/usr/include/pccts/sorcerer","sorcerer/h/*.c") - pisitools.insinto("/usr/include/pccts/sorcerer","sorcerer/h/*.cpp") - - - pisitools.insinto("/usr/include/pccts/sorcerer/lib","sorcerer/lib/*.h") - pisitools.insinto("/usr/include/pccts/sorcerer/lib","sorcerer/lib/*.c") - pisitools.insinto("/usr/include/pccts/sorcerer/lib","sorcerer/lib/*.cpp") - - pisitools.dodoc("CHANGES*", "KNOWN_PROBLEMS*", "README", "RIGHTS", "history.txt", "history.ps") - pisitools.dodoc("sorcerer/README", "sorcerer/UPDATES") - - pisitools.doman("dlg/dlg.1", "antlr/antlr.1") diff --git a/tags/pisi-1.0_alpha2/tests/pccts/files/pccts-1.33.33-gentoo.diff b/tags/pisi-1.0_alpha2/tests/pccts/files/pccts-1.33.33-gentoo.diff deleted file mode 100644 index 1594cd96..00000000 --- a/tags/pisi-1.0_alpha2/tests/pccts/files/pccts-1.33.33-gentoo.diff +++ /dev/null @@ -1,69 +0,0 @@ ---- pccts-1.33.33/support/genmk/genmk.c Fri Aug 3 17:12:51 2001 -+++ pccts-1.33.33/support/genmk/genmk-gentoo.c Fri Aug 3 17:12:14 2001 -@@ -7,6 +7,11 @@ - * U of MN - */ - -+/* modified 20010803 by Peter Kadau -+ * for better fhs-compliance -+ * i.e. from "none" to "hmmm soso" ;-) -+ */ -+ - #include - #include - #include "pcctscfg.h" /* be sensitive to what ANTLR/DLG call the files */ -@@ -65,7 +70,7 @@ - static int nondef_comp = 0; /* 1=compiler is non default */ - static char *compilerCCC="CC"; - static char *compilerCC="cc"; --static char *pccts_path="/usr/local/pccts"; -+static char *pccts_path="/usr/include/pccts"; - - #ifdef __STDC__ - void help(void); -@@ -356,7 +361,7 @@ - { "-cfiles",1,pCFiles, "Additional files in C or C++ to compile"}, - { "-sor",0,pSor, "Start of sorcerer group"}, - { "-pccts_path",1,ppccts_path, -- "Path for $PCCTS directory (default is /usr/local/pccts)"}, -+ "Path for $PCCTS directory (default is /usr/include/pccts)"}, - { "-compiler",1,pCompiler, - "Default compiler (default is CC/cc)"}, - { "*", 0,pFile, "" }, /* anything else is a file */ -@@ -495,13 +500,13 @@ - else printf("SCAN = %s%s\n", DIR(), dlg_class); - - printf("PCCTS = %s\n",pccts_path); -- printf("ANTLR_H = $(PCCTS)%sh\n", DirectorySymbol); -+ printf("ANTLR_H = $(PCCTS)\n"); - if (num_sors>0) { -- printf("SOR_H = $(PCCTS)%ssorcerer%sh\n", DirectorySymbol, DirectorySymbol); -+ printf("SOR_H = $(PCCTS)%ssorcerer\n", DirectorySymbol); - printf("SOR_LIB = $(PCCTS)%ssorcerer%slib\n", - DirectorySymbol, DirectorySymbol); - } -- printf("BIN = $(PCCTS)%sbin\n", DirectorySymbol); -+ printf("BIN = %susr%sbin\n", DirectorySymbol, DirectorySymbol); - printf("ANTLR = $(BIN)%santlr\n", DirectorySymbol); - printf("DLG = $(BIN)%sdlg\n", DirectorySymbol); - if (num_sors>0) printf("SOR = $(BIN)%ssor\n", DirectorySymbol); ---- pccts-1.33.33/sorcerer/lib/sorlist.c 1999-06-30 03:15:56.000000000 +0200 -+++ pccts-1.33.33/sorcerer/lib/sorlist.c.new 2003-05-23 11:11:09.000000000 +0200 -@@ -28,6 +28,7 @@ - */ - #include - #include -+#include "pcctscfg.h" - - #ifdef PCCTS_USE_STDARG - #include ---- pccts-1.33.33/sorcerer/lib/sintstack.c 1999-06-30 15:08:06.000000000 +0200 -+++ pccts-1.33.33/sorcerer/lib/sintstack.c.new 2003-05-23 12:54:26.000000000 +0200 -@@ -28,6 +28,7 @@ - */ - #include - #include -+#include "pcctscfg.h" - - #ifdef PCCTS_USE_STDARG - #include diff --git a/tags/pisi-1.0_alpha2/tests/pccts/pspec.xml b/tags/pisi-1.0_alpha2/tests/pccts/pspec.xml deleted file mode 100644 index 3886c0e6..00000000 --- a/tags/pisi-1.0_alpha2/tests/pccts/pspec.xml +++ /dev/null @@ -1,41 +0,0 @@ - - - - - pccts - http://www.polhode.com - - Timu EREN - selamtux@gmail.com - - BSD - category - component - Purdue Compiler Construction Tool Set is an embedded C/C++ parser generator - Purdue Compiler Construction Tool Set is an embedded C/C++ parser generator - http://www.polhode.com/pccts133mr33.zip - - pccts-1.33.33-gentoo.diff - - - unzip - patch - - - - 2005-09-16 - 1.33.33 - 1 - - - - - pccts - - /usr/include - /usr/share/doc - /usr/bin - /usr/share/man - - - diff --git a/tags/pisi-1.0_alpha2/tests/pisi.conf b/tags/pisi-1.0_alpha2/tests/pisi.conf deleted file mode 100644 index 560e8567..00000000 --- a/tags/pisi-1.0_alpha2/tests/pisi.conf +++ /dev/null @@ -1,6 +0,0 @@ -[general] -destinationdirectory = /testing - -[directories] -archives_dir = /disk2/pisi/archives - diff --git a/tags/pisi-1.0_alpha2/tests/popt/actions.py b/tags/pisi-1.0_alpha2/tests/popt/actions.py deleted file mode 100644 index c8fc6046..00000000 --- a/tags/pisi-1.0_alpha2/tests/popt/actions.py +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# -# Copyright (C) 2005, TUBITAK/UEKAE -# -# 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. -# -# Please read the COPYING file. -# - -from pisi.actionsapi import autotools - -WorkDir='popt-1.7' - -def setup(): - autotools.configure( '--with-nls' ) - -def build(): - autotools.make() - -def install(): - autotools.install() diff --git a/tags/pisi-1.0_alpha2/tests/popt/files.xml b/tags/pisi-1.0_alpha2/tests/popt/files.xml deleted file mode 100644 index 34564b0c..00000000 --- a/tags/pisi-1.0_alpha2/tests/popt/files.xml +++ /dev/null @@ -1,507 +0,0 @@ - - - - - usr/share/locale/es/LC_MESSAGES/popt.mo - - - localedata - - - 1593 - - - 29481dbd4051e5c6a0f000436664ba00e47572ae - - - - - usr/share/locale/de/LC_MESSAGES/popt.mo - - - localedata - - - 345 - - - 6ceb513b02af4f612a20b49e5d615c158fdcee31 - - - - - usr/share/locale/is/LC_MESSAGES/popt.mo - - - localedata - - - 1793 - - - edbcbb430b122626216ea9f6bba268d5de8454b6 - - - - - usr/lib/libpopt.la - - - sharedLib - - - 699 - - - 04bac6fce89f2c84d4bb1c5adced729f7d312980 - - - - - usr/share/locale/sl/LC_MESSAGES/popt.mo - - - localedata - - - 514 - - - 2031bdefffcf57121e46f3f68f0747fe6b7f1fcc - - - - - usr/share/locale/fr/LC_MESSAGES/popt.mo - - - localedata - - - 345 - - - 6ceb513b02af4f612a20b49e5d615c158fdcee31 - - - - - usr/share/locale/it/LC_MESSAGES/popt.mo - - - localedata - - - 345 - - - 6ceb513b02af4f612a20b49e5d615c158fdcee31 - - - - - usr/share/locale/eu_ES/LC_MESSAGES/popt.mo - - - localedata - - - 345 - - - 6ceb513b02af4f612a20b49e5d615c158fdcee31 - - - - - usr/share/locale/da/LC_MESSAGES/popt.mo - - - localedata - - - 1599 - - - 3ae14880a8bc2cddd9dd0a03861a55d663ac8b0d - - - - - usr/share/locale/ko/LC_MESSAGES/popt.mo - - - localedata - - - 1916 - - - 2079233b9e3284c866e5d17e5aca001f678d4c23 - - - - - usr/share/locale/ja/LC_MESSAGES/popt.mo - - - localedata - - - 345 - - - 6ceb513b02af4f612a20b49e5d615c158fdcee31 - - - - - usr/share/locale/pt/LC_MESSAGES/popt.mo - - - localedata - - - 1870 - - - 7db5d457fa3cc169fea2f747f25ba0353ed5fb69 - - - - - usr/lib/libpopt.so - - - sharedLib - - - 38532 - - - 617c2afa4e79faee954cb95715470f875c044e5a - - - - - usr/share/locale/no/LC_MESSAGES/popt.mo - - - localedata - - - 1800 - - - b33d41a1fbde6b879f0cce06cf2d8395012cc0ec - - - - - usr/lib/libpopt.a - - - sharedLib - - - 36100 - - - f067421ebdb9a68f89c90526feb64a70763c95cf - - - - - usr/share/locale/wa/LC_MESSAGES/popt.mo - - - localedata - - - 517 - - - 92f1f582ae24dbf5631fee1ace8bfc99cb5c1b66 - - - - - usr/share/locale/tr/LC_MESSAGES/popt.mo - - - localedata - - - 1601 - - - 16e3e8e12af497102c75ccbb52edf8d1783545c3 - - - - - usr/share/locale/zh/LC_MESSAGES/popt.mo - - - localedata - - - 345 - - - 6ceb513b02af4f612a20b49e5d615c158fdcee31 - - - - - usr/share/locale/id/LC_MESSAGES/popt.mo - - - localedata - - - 345 - - - 6ceb513b02af4f612a20b49e5d615c158fdcee31 - - - - - usr/share/locale/ro/LC_MESSAGES/popt.mo - - - localedata - - - 1150 - - - 914729e4200d68e5c55d114e6a083c74e692707b - - - - - usr/share/locale/sr/LC_MESSAGES/popt.mo - - - localedata - - - 345 - - - 6ceb513b02af4f612a20b49e5d615c158fdcee31 - - - - - usr/share/locale/hu/LC_MESSAGES/popt.mo - - - localedata - - - 496 - - - 236bf8c1fea44a5a93c6e18e0c88b3dae1762264 - - - - - usr/share/locale/pt_BR/LC_MESSAGES/popt.mo - - - localedata - - - 345 - - - 6ceb513b02af4f612a20b49e5d615c158fdcee31 - - - - - usr/share/locale/sv/LC_MESSAGES/popt.mo - - - localedata - - - 1844 - - - 63517df43bd74c84496441d8894f2b67533c6060 - - - - - usr/lib/libpopt.so.0.0.0 - - - sharedLib - - - 38532 - - - 1f2d3c41ba8e4d6b1c4979225cdf2dd97d4a0aea - - - - - usr/share/locale/ru/LC_MESSAGES/popt.mo - - - localedata - - - 1955 - - - 4742785bd5235eb437922f76a9faa30ec314d09b - - - - - usr/share/locale/zh_CN.GB2312/LC_MESSAGES/popt.mo - - - localedata - - - 468 - - - 5e0a688794619278a452c851a3fc86b50b7f781e - - - - - usr/lib/libpopt.so.0 - - - sharedLib - - - 38532 - - - 617c2afa4e79faee954cb95715470f875c044e5a - - - - - usr/include/popt.h - - - header - - - 15982 - - - 091692391fd1c539b19298f30e8ae074e3210459 - - - - - usr/share/locale/sk/LC_MESSAGES/popt.mo - - - localedata - - - 510 - - - f7e081a22577eb5429809b7ce999b8b1e2b1f9b3 - - - - - usr/share/locale/pl/LC_MESSAGES/popt.mo - - - localedata - - - 345 - - - 6ceb513b02af4f612a20b49e5d615c158fdcee31 - - - - - usr/share/locale/uk/LC_MESSAGES/popt.mo - - - localedata - - - 504 - - - f91c695161926679401201fdaf3edcef4664f6ca - - - - - usr/share/locale/cs/LC_MESSAGES/popt.mo - - - localedata - - - 1806 - - - 920f5b99199361f1d169430bf13d2bfac1a02fab - - - - - usr/share/man/man3/popt.3 - - - doc - - - 31011 - - - 5c0014442b4ef38d71733c952884573071978328 - - - - - usr/share/locale/gl/LC_MESSAGES/popt.mo - - - localedata - - - 1636 - - - 8486430a6ecbb006b5d374cbcb109e8303c7d5f8 - - - - - usr/share/locale/fi/LC_MESSAGES/popt.mo - - - localedata - - - 345 - - - 6ceb513b02af4f612a20b49e5d615c158fdcee31 - - - diff --git a/tags/pisi-1.0_alpha2/tests/popt/files/popt-1.7-uclibc.patch.gz b/tags/pisi-1.0_alpha2/tests/popt/files/popt-1.7-uclibc.patch.gz deleted file mode 100644 index 83bbae58fcb13d6d70a3f408cf7ee02484e4e350..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 891 zcmV->1BCn^iwFqvtfoQ$18{F}bS*J1H!XEzY-wU+E^uLVV`u>FR&8(FHW2zerCsUIZ}hwCI-FQRW;pF9CSNtUP6Q}EmxMS!|e$o9RzlkH?Y003?%vuoK9c1?0o za#GQqV5H%uadP^OvqC|DN!vf088tab{siTB7UAq{-~kN0EjGhU<05LR$@ zMJnDDDobl>454LtFnd4>?S;d3fjt)(1|WH}HCb1b$iX=f^6c_ALf|6|FD}4;20ni7 zG_UV=et{t4*MV%y@(h}SOV_r=JpOsQ1aC2)C%B?27dFO^@J3Txn1xvScJYg z4h_pQJcE%5{@VOBoA|Rnv(4CwoAEH%L>6=t2O~VR?(kj7r>j+HnS$$L8YlNKCEH6{ z;i;hditBXr`-`{1r8R*v!=o7W9JrSO*IXN+Bx&|FPu;fFO3pKc+mc%IT)a=Ey3_U! zy$WqK)q8>oa6?SZSvneiK&@4L8h+5cZ&)2Hrp!to`S8yZB61LoR99e_BMR#=r$nPV>&U4FA&+(EVFr(5sa%qvX z9{Po9_!{Y63k1uxstydA0+kt5bdSk4)|1w?R<#zi=@Kib#(rfSLpI8C!lZj{I^1(( zT1>h%LhkGxh3@{}5)wK>I+(9GdeU1uf}ZrV!aJT7N7OW}RK{xWp{D=kG^eaojFz)1 z`TjT$SN}fF4;T-g=h1SpvV*B#{||#{9z{z#nB4j}m>vhy<6!!Vn(m=D54(rnJWd`T RdJlT&{R522x}2U1004y>xIO>? diff --git a/tags/pisi-1.0_alpha2/tests/popt/metadata.xml b/tags/pisi-1.0_alpha2/tests/popt/metadata.xml deleted file mode 100644 index 873d0d6a..00000000 --- a/tags/pisi-1.0_alpha2/tests/popt/metadata.xml +++ /dev/null @@ -1,108 +0,0 @@ - - - - - popt - - - - Kırmızı kafalar - - - hotmail@redhat.com - - - - - - popt-libs - - - Command line option parsing library - - - library files for popt - - - As-Is - - - library:util:optparser - - - rpm:archive - - - - gettext - - - - - /usr/lib - - - /usr/share/doc - - - /usr/share/man - - - /usr/share/locale - - - /usr/include/popt.h - - - - - - 06/14/2005 - - - 1.7 - - - 3 - - - - - 06/10/2005 - - - 1.7 - - - 2 - - - - - 05/05/2005 - - - 1.7 - - - 1 - - - - - 0 - - - Pardus - - - 0.1 - - - Any - - - 149691 - - - diff --git a/tags/pisi-1.0_alpha2/tests/popt/pspec.xml b/tags/pisi-1.0_alpha2/tests/popt/pspec.xml deleted file mode 100644 index 698947a0..00000000 --- a/tags/pisi-1.0_alpha2/tests/popt/pspec.xml +++ /dev/null @@ -1,80 +0,0 @@ - - - - - - - - popt - http://www.rpm.org/ - - Kırmızı kafalar - hotmail@redhat.com - - As-Is - library:util:optparser - rpm:archive - Popt command line option parser - Command line option parsing library. - While it is similiar to getopt(3), it contains a number of enhancements, including: - - 1) popt is fully reentrant - 2) popt can parse arbitrary argv[] style arrays while - getopt(2) makes this quite difficult - 3) popt allows users to alias command line arguments - 4) popt provides convience functions for parsing strings - into argv[] style arrays - - - ftp://ftp.rpm.org/pub/rpm/dist/rpm-4.1.x/popt-1.7.tar.gz - - - popt-1.7-uclibc.patch.gz - - - make - - - - 06/14/2005 - 1.7 - 3 - - - 06/10/2005 - 1.7 - 2 - - - 05/05/2005 - 1.7 - 1 - - - - - - popt-libs - Command line option parsing library - Komut satırı seçenekleri işleme kütüphanesi - - gettext - - library files for popt - - /usr/lib - /usr/share/doc - /usr/share/man - /usr/share/locale - /usr/include/popt.h - - - - 06/14/2005 - 1.7 - 2 - - - - diff --git a/tags/pisi-1.0_alpha2/tests/run.py b/tags/pisi-1.0_alpha2/tests/run.py deleted file mode 100755 index 1cca4e1c..00000000 --- a/tags/pisi-1.0_alpha2/tests/run.py +++ /dev/null @@ -1,71 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2005, TUBITAK/UEKAE -# -# 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. -# -# Please read the COPYING file. -# - -import unittest -import sys -import os - -sys.path.append('.') -sys.path.append('..') - -runTestSuite = lambda(x): unittest.TextTestRunner(verbosity=2).run(x) - -def run_all(): - - import utiltests - import xmlfiletests - import specfiletests - import metadatatests - import constantstests - import fetchertests - import archivetests - import installdbtests - import sourcedbtests - import packagedbtests - import actionsapitests - import graphtests - import versiontests - import configfiletests - import packagetests - - alltests = unittest.TestSuite(( - utiltests.suite, - xmlfiletests.suite, - specfiletests.suite, - metadatatests.suite, - constantstests.suite, - fetchertests.suite, - archivetests.suite, - installdbtests.suite, - sourcedbtests.suite, - packagedbtests.suite, -# FIXME: actionsapitests requires tester to run a specific command first. -# actionsapitests.suite, - graphtests.suite, - versiontests.suite, - configfiletests.suite, - packagetests.suite - )) - - runTestSuite(alltests) - -if __name__ == "__main__": - args = sys.argv - if len(args) > 1: # run modules given from the command line - tests = sys.argv[1:] - for test in tests: - module = __import__(test + 'tests') - print "\nRunning tests in '%s'...\n" % (test) - runTestSuite(module.suite) - else: # run all tests - print "\nRunning all tests in order...\n" - run_all() diff --git a/tags/pisi-1.0_alpha2/tests/sourcedbtests.py b/tags/pisi-1.0_alpha2/tests/sourcedbtests.py deleted file mode 100644 index 02edee3e..00000000 --- a/tags/pisi-1.0_alpha2/tests/sourcedbtests.py +++ /dev/null @@ -1,38 +0,0 @@ -# Copyright (C) 2005, TUBITAK/UEKAE -# -# 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. -# -# Please read the COPYING file. -# - -import unittest -import os - -import pisi.context as ctx -import pisi.api -import pisi.sourcedb -from pisi import util -from pisi.specfile import SpecFile - -class SourceDBTestCase(unittest.TestCase): - - def setUp(self): - pisi.api.init() - - self.sourcedb = pisi.sourcedb.init() - self.spec = SpecFile() - self.spec.read("tests/popt/pspec.xml") - - def testAdd(self): - self.sourcedb.add_source(self.spec.source) - self.assert_(self.sourcedb.has_source("popt")) - - def testRemove(self): - self.testAdd() - self.sourcedb.remove_source("popt") - self.assert_(not self.sourcedb.has_source("popt")) - -suite = unittest.makeSuite(SourceDBTestCase) diff --git a/tags/pisi-1.0_alpha2/tests/specfiletests.py b/tags/pisi-1.0_alpha2/tests/specfiletests.py deleted file mode 100644 index 58d644d3..00000000 --- a/tags/pisi-1.0_alpha2/tests/specfiletests.py +++ /dev/null @@ -1,80 +0,0 @@ -# Copyright (C) 2005, TUBITAK/UEKAE -# -# 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. -# -# Please read the COPYING file. -# - -import unittest -import os - -from pisi import specfile -from pisi.config import config -import pisi.util as util - -class SpecFileTestCase(unittest.TestCase): - def setUp(self): - self.spec = specfile.SpecFile() - self.spec.read("tests/popt/pspec.xml") - - def testReadSpec(self): - self.assertEqual(self.spec.source.name, "popt") - - self.assertEqual(self.spec.source.version, "1.7") - - self.assertEqual(self.spec.source.release, "3") - - self.assertEqual(self.spec.source.archiveSHA1, - "66f3c77b87a160951b180447f4a6dce68ad2f71b") - - patches = self.spec.source.patches - self.assertEqual(len(patches), 1) - patch = patches[0] #get first and the only patch - self.assertEqual(patch.filename, "popt-1.7-uclibc.patch.gz") - self.assertEqual(patch.compressionType, "gz") - - packages = self.spec.packages - self.assertEqual(len(packages), 1) - package = packages[0] # get the first and the only package - self.assertEqual(package.name, "popt-libs") - - # search for a path in package.paths - pn = "/usr/lib" - matched = [p for p in package.paths if p.pathname == pn] - if not matched: - self.fail("Failed to match pathname: %s" %pn) - - def testIsAPartOf(self): - # test existence in Source - if not "library:util:optparser" in self.spec.source.isa: - self.fail("Failed to match IsA in Source") - if not isinstance(self.spec.source.isa, list): - self.fail("source.isa is not a list, but it must be...") - - if "rpm:archive" != self.spec.source.partof: - self.fail("Failed to match PartOf in Source") - - # test existence in Package - pkg = self.spec.packages[0] - if not "library:util:optparser" in pkg.isa: - self.fail("Failed to match IsA in Package") - if not isinstance(pkg.isa, list): - self.fail("source.isa is not a list, but it must be...") - - if "rpm:archive" != pkg.partof: - self.fail("Failed to match PartOf in Package") - - def testVerify(self): - if self.spec.has_errors(): - self.fail("Failed to verify specfile") - - def testCopy(self): - util.check_dir(config.tmp_dir()) - self.spec.read("tests/popt/pspec.xml") - self.spec.write(os.path.join(config.tmp_dir(), 'popt-copy.pspec.xml')) - - -suite = unittest.makeSuite(SpecFileTestCase) diff --git a/tags/pisi-1.0_alpha2/tests/unzip/actions.py b/tags/pisi-1.0_alpha2/tests/unzip/actions.py deleted file mode 100644 index ed12a90d..00000000 --- a/tags/pisi-1.0_alpha2/tests/unzip/actions.py +++ /dev/null @@ -1,35 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# -# Copyright (C) 2005, TUBITAK/UEKAE -# -# 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. -# -# Please read the COPYING file. -# - -from pisi.actionsapi import autotools -from pisi.actionsapi import get -from pisi.actionsapi import pisitools - -def setup(): - pisitools.dosed("unix/Makefile", "-O3", get.CFLAGS()) - pisitools.dosed("unix/Makefile", "CC=gcc LD=gcc", "CC=${CC:-gcc} LD=${CC:-gcc}") - pisitools.dosed("unix/Makefile", "-O ", get.CFLAGS()) - pass - -def build(): - autotools.make("-f unix/Makefile linux") - -def install(): - pisitools.insinto("/usr/bin/", "unzip") - pisitools.insinto("/usr/bin/", "funzip") - pisitools.insinto("/usr/bin/", "unzipsfx") - pisitools.insinto("/usr/bin/", "unix/zipgrep") - - pisitools.dosym("/usr/bin/unzip", "/usr/bin/zipinfo") - pisitools.doman("man/*.1") - pisitools.dodoc("BUGS", "History*", "README", "ToDo", "WHERE") diff --git a/tags/pisi-1.0_alpha2/tests/unzip/files/unzip-5.50-dotdot.patch b/tags/pisi-1.0_alpha2/tests/unzip/files/unzip-5.50-dotdot.patch deleted file mode 100644 index 73441f7b..00000000 --- a/tags/pisi-1.0_alpha2/tests/unzip/files/unzip-5.50-dotdot.patch +++ /dev/null @@ -1,85 +0,0 @@ -Only in unzip-5.50-lhh/: cscope.files -Only in unzip-5.50-lhh/: cscope.out -diff -ur unzip-5.50/unix/unix.c unzip-5.50-lhh/unix/unix.c ---- unzip-5.50/unix/unix.c 2002-01-21 17:54:42.000000000 -0500 -+++ unzip-5.50-lhh/unix/unix.c 2003-06-11 18:35:38.000000000 -0400 -@@ -421,7 +421,8 @@ - */ - { - char pathcomp[FILNAMSIZ]; /* path-component buffer */ -- char *pp, *cp=(char *)NULL; /* character pointers */ -+ char *pp, *cp=(char *)NULL, /* character pointers */ -+ *dp=(char *)NULL; - char *lastsemi=(char *)NULL; /* pointer to last semi-colon in pathcomp */ - #ifdef ACORN_FTYPE_NFS - char *lastcomma=(char *)NULL; /* pointer to last comma in pathcomp */ -@@ -429,6 +430,7 @@ - #endif - int quote = FALSE; /* flags */ - int killed_ddot = FALSE; /* is set when skipping "../" pathcomp */ -+ int snarf_ddot = FALSE; /* Is set while scanning for "../" */ - int error = MPN_OK; - register unsigned workch; /* hold the character being tested */ - -@@ -467,6 +469,9 @@ - while ((workch = (uch)*cp++) != 0) { - - if (quote) { /* if character quoted, */ -+ if ((pp == pathcomp) && (workch == '.')) -+ /* Oh no you don't... */ -+ goto ddot_hack; - *pp++ = (char)workch; /* include it literally */ - quote = FALSE; - } else -@@ -481,15 +486,44 @@ - break; - - case '.': -- if (pp == pathcomp) { /* nothing appended yet... */ -+ if (pp == pathcomp) { -+ddot_hack: -+ /* nothing appended yet... */ - if (*cp == '/') { /* don't bother appending "./" to */ - ++cp; /* the path: skip behind the '/' */ - break; -- } else if (!uO.ddotflag && *cp == '.' && cp[1] == '/') { -- /* "../" dir traversal detected */ -- cp += 2; /* skip over behind the '/' */ -- killed_ddot = TRUE; /* set "show message" flag */ -- break; -+ } else if (!uO.ddotflag) { -+ -+ /* -+ * SECURITY: Skip past control characters if the user -+ * didn't OK use of absolute pathnames. lhh - this is -+ * a very quick, ugly, inefficient fix. -+ */ -+ dp = cp; -+ do { -+ workch = (uch)(*dp); -+ if (workch == '/' && snarf_ddot) { -+ /* "../" dir traversal detected */ -+ cp = dp + 1; /* skip past the '/' */ -+ killed_ddot = TRUE; /* set "show msg" flag */ -+ break; -+ } else if (workch == '.' && !snarf_ddot) { -+ snarf_ddot = TRUE; -+ } else if (isprint(workch) || -+ ((workch > 127) && (workch <= 254))) { -+ /* -+ * Since we found a printable, non-ctrl char, -+ * we can stop looking for '../', the amount -+ * in ../! -+ */ -+ break; -+ } -+ -+ dp++; -+ } while (*dp != 0); -+ -+ if (killed_ddot) -+ break; - } - } - *pp++ = '.'; -Only in unzip-5.50-lhh/unix: .unix.c.swp diff --git a/tags/pisi-1.0_alpha2/tests/unzip/pspec.xml b/tags/pisi-1.0_alpha2/tests/unzip/pspec.xml deleted file mode 100644 index 8ff86f0f..00000000 --- a/tags/pisi-1.0_alpha2/tests/unzip/pspec.xml +++ /dev/null @@ -1,49 +0,0 @@ - - - - - - - unzip - ftp://ftp.info-zip.org/pub/infozip/UnZip.html - - A. Murat Eren - meren@uludag.org.tr - - Info-ZIP - category - component - Unzipper for pkzip-compressed files - Unzipper for pkzip-compressed files - ftp://ftp.linux.org.tr/pub/mirrors/gentoo/distfiles/unzip550.tar.gz - - unzip-5.50-dotdot.patch - - - libc - sed - patch - - - - 2005-07-15 - 5.50 - 1 - - - - - - unzip - - - - - /usr/bin - /usr/share/doc - /usr/share/man - - - - - diff --git a/tags/pisi-1.0_alpha2/tests/unzip2/actions.py b/tags/pisi-1.0_alpha2/tests/unzip2/actions.py deleted file mode 100644 index 09b91126..00000000 --- a/tags/pisi-1.0_alpha2/tests/unzip2/actions.py +++ /dev/null @@ -1,37 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# -# Copyright (C) 2005, TUBITAK/UEKAE -# -# 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. -# -# Please read the COPYING file. -# - -from pisi.actionsapi import autotools -from pisi.actionsapi import get -from pisi.actionsapi import pisitools - -WorkDir="unzip-5.50" - -def setup(): - pisitools.dosed("unix/Makefile", "-O3", get.CFLAGS()) - pisitools.dosed("unix/Makefile", "CC=gcc LD=gcc", "CC=${CC:-gcc} LD=${CC:-gcc}") - pisitools.dosed("unix/Makefile", "-O ", get.CFLAGS()) - pass - -def build(): - autotools.make("-f unix/Makefile linux") - -def install(): - pisitools.insinto("/usr/bin/", "unzip") - pisitools.insinto("/usr/bin/", "funzip") - pisitools.insinto("/usr/bin/", "unzipsfx") - pisitools.insinto("/usr/bin/", "unix/zipgrep") - - pisitools.dosym("/usr/bin/unzip", "/usr/bin/zipinfo") - pisitools.doman("man/*.1") - pisitools.dodoc("BUGS", "History*", "README", "ToDo", "WHERE") diff --git a/tags/pisi-1.0_alpha2/tests/unzip2/files/unzip-5.50-dotdot.patch b/tags/pisi-1.0_alpha2/tests/unzip2/files/unzip-5.50-dotdot.patch deleted file mode 100644 index 73441f7b..00000000 --- a/tags/pisi-1.0_alpha2/tests/unzip2/files/unzip-5.50-dotdot.patch +++ /dev/null @@ -1,85 +0,0 @@ -Only in unzip-5.50-lhh/: cscope.files -Only in unzip-5.50-lhh/: cscope.out -diff -ur unzip-5.50/unix/unix.c unzip-5.50-lhh/unix/unix.c ---- unzip-5.50/unix/unix.c 2002-01-21 17:54:42.000000000 -0500 -+++ unzip-5.50-lhh/unix/unix.c 2003-06-11 18:35:38.000000000 -0400 -@@ -421,7 +421,8 @@ - */ - { - char pathcomp[FILNAMSIZ]; /* path-component buffer */ -- char *pp, *cp=(char *)NULL; /* character pointers */ -+ char *pp, *cp=(char *)NULL, /* character pointers */ -+ *dp=(char *)NULL; - char *lastsemi=(char *)NULL; /* pointer to last semi-colon in pathcomp */ - #ifdef ACORN_FTYPE_NFS - char *lastcomma=(char *)NULL; /* pointer to last comma in pathcomp */ -@@ -429,6 +430,7 @@ - #endif - int quote = FALSE; /* flags */ - int killed_ddot = FALSE; /* is set when skipping "../" pathcomp */ -+ int snarf_ddot = FALSE; /* Is set while scanning for "../" */ - int error = MPN_OK; - register unsigned workch; /* hold the character being tested */ - -@@ -467,6 +469,9 @@ - while ((workch = (uch)*cp++) != 0) { - - if (quote) { /* if character quoted, */ -+ if ((pp == pathcomp) && (workch == '.')) -+ /* Oh no you don't... */ -+ goto ddot_hack; - *pp++ = (char)workch; /* include it literally */ - quote = FALSE; - } else -@@ -481,15 +486,44 @@ - break; - - case '.': -- if (pp == pathcomp) { /* nothing appended yet... */ -+ if (pp == pathcomp) { -+ddot_hack: -+ /* nothing appended yet... */ - if (*cp == '/') { /* don't bother appending "./" to */ - ++cp; /* the path: skip behind the '/' */ - break; -- } else if (!uO.ddotflag && *cp == '.' && cp[1] == '/') { -- /* "../" dir traversal detected */ -- cp += 2; /* skip over behind the '/' */ -- killed_ddot = TRUE; /* set "show message" flag */ -- break; -+ } else if (!uO.ddotflag) { -+ -+ /* -+ * SECURITY: Skip past control characters if the user -+ * didn't OK use of absolute pathnames. lhh - this is -+ * a very quick, ugly, inefficient fix. -+ */ -+ dp = cp; -+ do { -+ workch = (uch)(*dp); -+ if (workch == '/' && snarf_ddot) { -+ /* "../" dir traversal detected */ -+ cp = dp + 1; /* skip past the '/' */ -+ killed_ddot = TRUE; /* set "show msg" flag */ -+ break; -+ } else if (workch == '.' && !snarf_ddot) { -+ snarf_ddot = TRUE; -+ } else if (isprint(workch) || -+ ((workch > 127) && (workch <= 254))) { -+ /* -+ * Since we found a printable, non-ctrl char, -+ * we can stop looking for '../', the amount -+ * in ../! -+ */ -+ break; -+ } -+ -+ dp++; -+ } while (*dp != 0); -+ -+ if (killed_ddot) -+ break; - } - } - *pp++ = '.'; -Only in unzip-5.50-lhh/unix: .unix.c.swp diff --git a/tags/pisi-1.0_alpha2/tests/unzip2/pspec.xml b/tags/pisi-1.0_alpha2/tests/unzip2/pspec.xml deleted file mode 100644 index 0242c56c..00000000 --- a/tags/pisi-1.0_alpha2/tests/unzip2/pspec.xml +++ /dev/null @@ -1,49 +0,0 @@ - - - - - - - unzip - ftp://ftp.info-zip.org/pub/infozip/UnZip.html - - A. Murat Eren - meren@uludag.org.tr - - Info-ZIP - category - component - Unzipper for pkzip-compressed files - Unzipper for pkzip-compressed files - ftp://ftp.linux.org.tr/pub/mirrors/gentoo/distfiles/unzip550.tar.gz - - unzip-5.50-dotdot.patch - - - libc - sed - patch - - - - 2005-08-10 - 5.52 - 2 - - - - - - unzip - - - - - /usr/bin - /usr/share/doc - /usr/share/man - - - - - diff --git a/tags/pisi-1.0_alpha2/tests/utiltests.py b/tags/pisi-1.0_alpha2/tests/utiltests.py deleted file mode 100644 index 0b6a05b4..00000000 --- a/tags/pisi-1.0_alpha2/tests/utiltests.py +++ /dev/null @@ -1,34 +0,0 @@ -# Copyright (C) 2005, TUBITAK/UEKAE -# -# 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. -# -# Please read the COPYING file. -# - -import unittest -import os - -from pisi import version -from pisi.util import * - -class UtilTestCase(unittest.TestCase): - def setUp(self): - pass - - def testSubPath(self): - self.assert_(subpath('usr', 'usr')) - self.assert_(subpath('usr', 'usr/local/src')) - self.assert_(not subpath('usr/local', 'usr')) - - def testRemovePathPrefix(self): - a = removepathprefix('usr/local', 'usr/local/lib') - self.assertEqual(a, 'lib') - - a = removepathprefix('usr/local/', 'usr/local/lib') - self.assertEqual(a, 'lib') - - -suite = unittest.makeSuite(UtilTestCase) diff --git a/tags/pisi-1.0_alpha2/tests/versiontests.py b/tags/pisi-1.0_alpha2/tests/versiontests.py deleted file mode 100644 index 19250b62..00000000 --- a/tags/pisi-1.0_alpha2/tests/versiontests.py +++ /dev/null @@ -1,52 +0,0 @@ -# Copyright (C) 2005, TUBITAK/UEKAE -# -# 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. -# -# Please read the COPYING file. -# - -import unittest -import os - -from pisi import version - -class VersionTestCase(unittest.TestCase): - def setUp(self): - pass - - def testOps(self): - v1 = version.Version("0.3.1") - v2 = version.Version("0.3.5") - v3 = version.Version("1.5.2-4") - v4 = version.Version("0.3.1-1") - self.assert_(v1 < v2) - self.assert_(v3 > v2) - self.assert_(v1 <= v3) - self.assert_(v4 >= v4) - - # with keywords - v1 = version.Version("2.23_pre10") - v2 = version.Version("2.23") - v3 = version.Version("2.21") - v4 = version.Version("2.23_p1") - v5 = version.Version("2.23_beta1") - self.assert_(v1 < v2) - self.assert_(v1 > v3) - self.assert_(v1 < v4) - self.assert_(v1 > v5) - self.assert_(v2 < v4) - self.assert_(v2 > v5) - - # with character - v1 = version.Version("2.10a") - v2 = version.Version("2.10") - v3 = version.Version("2.10d") - self.assert_(v1 > v2) - self.assert_(v1 < v3) - self.assert_(v2 < v3) - - -suite = unittest.makeSuite(VersionTestCase) diff --git a/tags/pisi-1.0_alpha2/tests/xmlfiletests.py b/tags/pisi-1.0_alpha2/tests/xmlfiletests.py deleted file mode 100644 index 0eb9dc31..00000000 --- a/tags/pisi-1.0_alpha2/tests/xmlfiletests.py +++ /dev/null @@ -1,73 +0,0 @@ -# Copyright (C) 2005, TUBITAK/UEKAE -# -# 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. -# -# Please read the COPYING file. -# - -import unittest -import os -import xml.dom.minidom as mdom -from xml.parsers.expat import ExpatError -import types - -import pisi -import pisi.api -from pisi import xmlfile -from pisi.config import config -import pisi.util as util -from pisi.xmlext import * - -class XmlFileTestCase(unittest.TestCase): - - def setUp(self): - pisi.api.init(False) - - def testMetaClass(self): - - class OtherInfo: - __metaclass__ = xmlfile.autoxml - t_BirthDate = [types.StringType, xmlfile.mandatory] - t_Interest = [types.StringType, xmlfile.optional] - t_CodesWith = [ [types.StringType], xmlfile.optional, 'Person'] - - class A: - __metaclass__ = xmlfile.autoxml - t_Name = [types.StringType, xmlfile.mandatory] - t_Number = [types.IntType, xmlfile.optional] - t_Email = [types.StringType, xmlfile.optional] - a_href = [types.StringType, xmlfile.mandatory] - #t_Projects = [ [types.StringType], xmlfile.mandatory, 'Project'] - #t_OtherInfo = [ OtherInfo, xmlfile.optional ] - - a = A() - self.assertEqual(a.href, None) - dom = mdom.parse('tests/a.xml') - node = getNode(dom, 'A') - self.assert_(len(A.decoders)>0) - a.decode(node) - self.assert_(a.href.startswith('http')) - self.assertEqual(a.number, 868) - self.assertEqual(a.name, 'Eray Ozkural') - string = a.format() - print '*', string - #self.assert_(string.startswith('Name')) - xml = xmlfile.XmlFile('A') - a.encode(xml) - xml.writexml('/tmp/a.xml') - print '/tmp/a.xml written' - return - xml = xmlfile.XmlFile('A') - a2 = A() - a2.name = "Baris Metin" - a2.email = "baris@uludag.org.tr" - a2.href = 'http://cekirdek.uludag.org.tr/~baris' - a2.projects = [ 'pisi', 'tasma', 'plasma' ] - a2.encode(xml) - xml.writexml('/tmp/a2.xml') - string = a2.format() - -suite = unittest.makeSuite(XmlFileTestCase) diff --git a/tags/pisi-1.0_alpha2/tests/zip/actions.py b/tags/pisi-1.0_alpha2/tests/zip/actions.py deleted file mode 100644 index 730924b4..00000000 --- a/tags/pisi-1.0_alpha2/tests/zip/actions.py +++ /dev/null @@ -1,32 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# -# Copyright (C) 2005, TUBITAK/UEKAE -# -# 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. -# -# Please read the COPYING file. -# - -from pisi.actionsapi import autotools -from pisi.actionsapi import get -from pisi.actionsapi import pisitools - -def setup(): - pisitools.dosed("unix/Makefile", "-O2", get.CFLAGS()) - -def build(): - autotools.make("-f unix/Makefile CC=%s CPP=%s generic" % (get.CC(), get.CXX())) - -def install(): - pisitools.dobin("zip") - pisitools.dobin("zipcloak") - pisitools.dobin("zipnote") - pisitools.dobin("zipsplit") - - pisitools.doman("man/*.1") - pisitools.dodoc("BUGS", "CHANGES", "MANUAL", "README", "TODO", "WHATSNEW", "WHERE") - diff --git a/tags/pisi-1.0_alpha2/tests/zip/files/zcrypt.patch b/tags/pisi-1.0_alpha2/tests/zip/files/zcrypt.patch deleted file mode 100644 index 6b696af5..00000000 --- a/tags/pisi-1.0_alpha2/tests/zip/files/zcrypt.patch +++ /dev/null @@ -1,752 +0,0 @@ -diff -ur zip-2.3.orig/crypt.c zip-2.3/crypt.c ---- zip-2.3.orig/crypt.c 1999-10-09 23:09:26.000000000 +0300 -+++ zip-2.3/crypt.c 2005-07-15 16:19:00.000000000 +0300 -@@ -1,20 +1,577 @@ - /* -- Copyright (c) 1990-1999 Info-ZIP. All rights reserved. -+ Copyright (c) 1990-2000 Info-ZIP. All rights reserved. - -- See the accompanying file LICENSE, version 1999-Oct-05 or later -+ See the accompanying file LICENSE, version 2000-Apr-09 or later - (the contents of which are also included in zip.h) for terms of use. -- If, for some reason, both of these files are missing, the Info-ZIP license -- also may be found at: ftp://ftp.cdrom.com/pub/infozip/license.html -+ If, for some reason, all these files are missing, the Info-ZIP license -+ also may be found at: ftp://ftp.info-zip.org/pub/infozip/license.html - */ - /* -- crypt.c (dummy version) by Info-ZIP. Last revised: 15 Aug 98 -+ crypt.c (full version) by Info-ZIP. Last revised: [see crypt.h] - -- This is a non-functional version of Info-ZIP's crypt.c encryption/ -- decryption code for Zip, ZipCloak, UnZip and fUnZip. This file is -- not copyrighted and may be distributed freely. :-) See the "WHERE" -- file for sites from which to obtain the full encryption/decryption -- sources (zcrypt28.zip or later). -+ The encryption/decryption parts of this source code (as opposed to the -+ non-echoing password parts) were originally written in Europe. The -+ whole source package can be freely distributed, including from the USA. -+ (Prior to January 2000, re-export from the US was a violation of US law.) - */ - -+/* -+ This encryption code is a direct transcription of the algorithm from -+ Roger Schlafly, described by Phil Katz in the file appnote.txt. This -+ file (appnote.txt) is distributed with the PKZIP program (even in the -+ version without encryption capabilities). -+ */ -+ -+#define ZCRYPT_INTERNAL -+#include "zip.h" -+#include "crypt.h" -+#include "ttyio.h" -+ -+#if CRYPT -+ -+#ifndef FALSE -+# define FALSE 0 -+#endif -+ -+#ifdef ZIP -+ /* For the encoding task used in Zip (and ZipCloak), we want to initialize -+ the crypt algorithm with some reasonably unpredictable bytes, see -+ the crypthead() function. The standard rand() library function is -+ used to supply these `random' bytes, which in turn is initialized by -+ a srand() call. The srand() function takes an "unsigned" (at least 16bit) -+ seed value as argument to determine the starting point of the rand() -+ pseudo-random number generator. -+ This seed number is constructed as "Seed = Seed1 .XOR. Seed2" with -+ Seed1 supplied by the current time (= "(unsigned)time()") and Seed2 -+ as some (hopefully) nondeterministic bitmask. On many (most) systems, -+ we use some "process specific" number, as the PID or something similar, -+ but when nothing unpredictable is available, a fixed number may be -+ sufficient. -+ NOTE: -+ 1.) This implementation requires the availability of the following -+ standard UNIX C runtime library functions: time(), rand(), srand(). -+ On systems where some of them are missing, the environment that -+ incorporates the crypt routines must supply suitable replacement -+ functions. -+ 2.) It is a very bad idea to use a second call to time() to set the -+ "Seed2" number! In this case, both "Seed1" and "Seed2" would be -+ (almost) identical, resulting in a (mostly) "zero" constant seed -+ number passed to srand(). -+ -+ The implementation environment defined in the "zip.h" header should -+ supply a reasonable definition for ZCR_SEED2 (an unsigned number; for -+ most implementations of rand() and srand(), only the lower 16 bits are -+ significant!). An example that works on many systems would be -+ "#define ZCR_SEED2 (unsigned)getpid()". -+ The default definition for ZCR_SEED2 supplied below should be regarded -+ as a fallback to allow successful compilation in "beta state" -+ environments. -+ */ -+# include /* time() function supplies first part of crypt seed */ -+ /* "last resort" source for second part of crypt seed pattern */ -+# ifndef ZCR_SEED2 -+# define ZCR_SEED2 (unsigned)3141592654L /* use PI as default pattern */ -+# endif -+# ifdef GLOBAL /* used in Amiga system headers, maybe others too */ -+# undef GLOBAL -+# endif -+# define GLOBAL(g) g -+#else /* !ZIP */ -+# define GLOBAL(g) G.g -+#endif /* ?ZIP */ -+ -+ -+#ifdef UNZIP -+ /* char *key = (char *)NULL; moved to globals.h */ -+# ifndef FUNZIP -+ local int testp OF((__GPRO__ ZCONST uch *h)); -+ local int testkey OF((__GPRO__ ZCONST uch *h, ZCONST char *key)); -+# endif -+#endif /* UNZIP */ -+ -+#ifndef UNZIP /* moved to globals.h for UnZip */ -+ local ulg keys[3]; /* keys defining the pseudo-random sequence */ -+#endif /* !UNZIP */ -+ -+#ifndef Trace -+# ifdef CRYPT_DEBUG -+# define Trace(x) fprintf x -+# else -+# define Trace(x) -+# endif -+#endif -+ -+#ifndef CRC_32_TAB -+# define CRC_32_TAB crc_32_tab -+#endif -+ -+#define CRC32(c, b) (CRC_32_TAB[((int)(c) ^ (b)) & 0xff] ^ ((c) >> 8)) -+ -+/*********************************************************************** -+ * Return the next byte in the pseudo-random sequence -+ */ -+int decrypt_byte(__G) -+ __GDEF -+{ -+ unsigned temp; /* POTENTIAL BUG: temp*(temp^1) may overflow in an -+ * unpredictable manner on 16-bit systems; not a problem -+ * with any known compiler so far, though */ -+ -+ temp = ((unsigned)GLOBAL(keys[2]) & 0xffff) | 2; -+ return (int)(((temp * (temp ^ 1)) >> 8) & 0xff); -+} -+ -+/*********************************************************************** -+ * Update the encryption keys with the next byte of plain text -+ */ -+int update_keys(__G__ c) -+ __GDEF -+ int c; /* byte of plain text */ -+{ -+ GLOBAL(keys[0]) = CRC32(GLOBAL(keys[0]), c); -+ GLOBAL(keys[1]) += GLOBAL(keys[0]) & 0xff; -+ GLOBAL(keys[1]) = GLOBAL(keys[1]) * 134775813L + 1; -+ { -+ register int keyshift = (int)(GLOBAL(keys[1]) >> 24); -+ GLOBAL(keys[2]) = CRC32(GLOBAL(keys[2]), keyshift); -+ } -+ return c; -+} -+ -+ -+/*********************************************************************** -+ * Initialize the encryption keys and the random header according to -+ * the given password. -+ */ -+void init_keys(__G__ passwd) -+ __GDEF -+ ZCONST char *passwd; /* password string with which to modify keys */ -+{ -+ GLOBAL(keys[0]) = 305419896L; -+ GLOBAL(keys[1]) = 591751049L; -+ GLOBAL(keys[2]) = 878082192L; -+ while (*passwd != '\0') { -+ update_keys(__G__ (int)*passwd); -+ passwd++; -+ } -+} -+ -+ -+#ifdef ZIP -+ -+/*********************************************************************** -+ * Write encryption header to file zfile using the password passwd -+ * and the cyclic redundancy check crc. -+ */ -+void crypthead(passwd, crc, zfile) -+ ZCONST char *passwd; /* password string */ -+ ulg crc; /* crc of file being encrypted */ -+ FILE *zfile; /* where to write header */ -+{ -+ int n; /* index in random header */ -+ int t; /* temporary */ -+ int c; /* random byte */ -+ int ztemp; /* temporary for zencoded value */ -+ uch header[RAND_HEAD_LEN-2]; /* random header */ -+ static unsigned calls = 0; /* ensure different random header each time */ -+ -+ /* First generate RAND_HEAD_LEN-2 random bytes. We encrypt the -+ * output of rand() to get less predictability, since rand() is -+ * often poorly implemented. -+ */ -+ if (++calls == 1) { -+ srand((unsigned)time(NULL) ^ ZCR_SEED2); -+ } -+ init_keys(passwd); -+ for (n = 0; n < RAND_HEAD_LEN-2; n++) { -+ c = (rand() >> 7) & 0xff; -+ header[n] = (uch)zencode(c, t); -+ } -+ /* Encrypt random header (last two bytes is high word of crc) */ -+ init_keys(passwd); -+ for (n = 0; n < RAND_HEAD_LEN-2; n++) { -+ ztemp = zencode(header[n], t); -+ putc(ztemp, zfile); -+ } -+ ztemp = zencode((int)(crc >> 16) & 0xff, t); -+ putc(ztemp, zfile); -+ ztemp = zencode((int)(crc >> 24) & 0xff, t); -+ putc(ztemp, zfile); -+} -+ -+ -+#ifdef UTIL -+ -+/*********************************************************************** -+ * Encrypt the zip entry described by z from file source to file dest -+ * using the password passwd. Return an error code in the ZE_ class. -+ */ -+int zipcloak(z, source, dest, passwd) -+ struct zlist far *z; /* zip entry to encrypt */ -+ FILE *source, *dest; /* source and destination files */ -+ ZCONST char *passwd; /* password string */ -+{ -+ int c; /* input byte */ -+ int res; /* result code */ -+ ulg n; /* holds offset and counts size */ -+ ush flag; /* previous flags */ -+ int t; /* temporary */ -+ int ztemp; /* temporary storage for zencode value */ -+ -+ /* Set encrypted bit, clear extended local header bit and write local -+ header to output file */ -+ if ((n = (ulg)ftell(dest)) == (ulg)-1L) return ZE_TEMP; -+ z->off = n; -+ flag = z->flg; -+ z->flg |= 1, z->flg &= ~8; -+ z->lflg |= 1, z->lflg &= ~8; -+ z->siz += RAND_HEAD_LEN; -+ if ((res = putlocal(z, dest)) != ZE_OK) return res; -+ -+ /* Initialize keys with password and write random header */ -+ crypthead(passwd, z->crc, dest); -+ -+ /* Skip local header in input file */ -+ if (fseek(source, (long)(4 + LOCHEAD + (ulg)z->nam + (ulg)z->ext), -+ SEEK_CUR)) { -+ return ferror(source) ? ZE_READ : ZE_EOF; -+ } -+ -+ /* Encrypt data */ -+ for (n = z->siz - RAND_HEAD_LEN; n; n--) { -+ if ((c = getc(source)) == EOF) { -+ return ferror(source) ? ZE_READ : ZE_EOF; -+ } -+ ztemp = zencode(c, t); -+ putc(ztemp, dest); -+ } -+ /* Skip extended local header in input file if there is one */ -+ if ((flag & 8) != 0 && fseek(source, 16L, SEEK_CUR)) { -+ return ferror(source) ? ZE_READ : ZE_EOF; -+ } -+ if (fflush(dest) == EOF) return ZE_TEMP; -+ return ZE_OK; -+} -+ -+/*********************************************************************** -+ * Decrypt the zip entry described by z from file source to file dest -+ * using the password passwd. Return an error code in the ZE_ class. -+ */ -+int zipbare(z, source, dest, passwd) -+ struct zlist far *z; /* zip entry to encrypt */ -+ FILE *source, *dest; /* source and destination files */ -+ ZCONST char *passwd; /* password string */ -+{ -+ int c0, c1; /* last two input bytes */ -+ ulg offset; /* used for file offsets */ -+ ulg size; /* size of input data */ -+ int r; /* size of encryption header */ -+ int res; /* return code */ -+ ush flag; /* previous flags */ -+ -+ /* Save position and skip local header in input file */ -+ if ((offset = (ulg)ftell(source)) == (ulg)-1L || -+ fseek(source, (long)(4 + LOCHEAD + (ulg)z->nam + (ulg)z->ext), -+ SEEK_CUR)) { -+ return ferror(source) ? ZE_READ : ZE_EOF; -+ } -+ /* Initialize keys with password */ -+ init_keys(passwd); -+ -+ /* Decrypt encryption header, save last two bytes */ -+ c1 = 0; -+ for (r = RAND_HEAD_LEN; r; r--) { -+ c0 = c1; -+ if ((c1 = getc(source)) == EOF) { -+ return ferror(source) ? ZE_READ : ZE_EOF; -+ } -+ Trace((stdout, " (%02x)", c1)); -+ zdecode(c1); -+ Trace((stdout, " %02x", c1)); -+ } -+ Trace((stdout, "\n")); -+ -+ /* If last two bytes of header don't match crc (or file time in the -+ * case of an extended local header), back up and just copy. For -+ * pkzip 2.0, the check has been reduced to one byte only. -+ */ -+#ifdef ZIP10 -+ if ((ush)(c0 | (c1<<8)) != -+ (z->flg & 8 ? (ush) z->tim & 0xffff : (ush)(z->crc >> 16))) { -+#else -+ c0++; /* avoid warning on unused variable */ -+ if ((ush)c1 != (z->flg & 8 ? (ush) z->tim >> 8 : (ush)(z->crc >> 24))) { -+#endif -+ if (fseek(source, offset, SEEK_SET)) { -+ return ferror(source) ? ZE_READ : ZE_EOF; -+ } -+ if ((res = zipcopy(z, source, dest)) != ZE_OK) return res; -+ return ZE_MISS; -+ } -+ -+ /* Clear encrypted bit and local header bit, and write local header to -+ output file */ -+ if ((offset = (ulg)ftell(dest)) == (ulg)-1L) return ZE_TEMP; -+ z->off = offset; -+ flag = z->flg; -+ z->flg &= ~9; -+ z->lflg &= ~9; -+ z->siz -= RAND_HEAD_LEN; -+ if ((res = putlocal(z, dest)) != ZE_OK) return res; -+ -+ /* Decrypt data */ -+ for (size = z->siz; size; size--) { -+ if ((c1 = getc(source)) == EOF) { -+ return ferror(source) ? ZE_READ : ZE_EOF; -+ } -+ zdecode(c1); -+ putc(c1, dest); -+ } -+ /* Skip extended local header in input file if there is one */ -+ if ((flag & 8) != 0 && fseek(source, 16L, SEEK_CUR)) { -+ return ferror(source) ? ZE_READ : ZE_EOF; -+ } -+ if (fflush(dest) == EOF) return ZE_TEMP; -+ -+ return ZE_OK; -+} -+ -+ -+#else /* !UTIL */ -+ -+/*********************************************************************** -+ * If requested, encrypt the data in buf, and in any case call fwrite() -+ * with the arguments to zfwrite(). Return what fwrite() returns. -+ */ -+unsigned zfwrite(buf, item_size, nb, f) -+ zvoid *buf; /* data buffer */ -+ extent item_size; /* size of each item in bytes */ -+ extent nb; /* number of items */ -+ FILE *f; /* file to write to */ -+{ -+ int t; /* temporary */ -+ -+ if (key != (char *)NULL) { /* key is the global password pointer */ -+ ulg size; /* buffer size */ -+ char *p = (char*)buf; /* steps through buffer */ -+ -+ /* Encrypt data in buffer */ -+ for (size = item_size*(ulg)nb; size != 0; p++, size--) { -+ *p = (char)zencode(*p, t); -+ } -+ } -+ /* Write the buffer out */ -+ return fwrite(buf, item_size, nb, f); -+} -+ -+#endif /* ?UTIL */ -+#endif /* ZIP */ -+ -+ -+#if (defined(UNZIP) && !defined(FUNZIP)) -+ -+/*********************************************************************** -+ * Get the password and set up keys for current zipfile member. -+ * Return PK_ class error. -+ */ -+int decrypt(__G__ passwrd) -+ __GDEF -+ ZCONST char *passwrd; -+{ -+ ush b; -+ int n, r; -+ uch h[RAND_HEAD_LEN]; -+ -+ Trace((stdout, "\n[incnt = %d]: ", GLOBAL(incnt))); -+ -+ /* get header once (turn off "encrypted" flag temporarily so we don't -+ * try to decrypt the same data twice) */ -+ GLOBAL(pInfo->encrypted) = FALSE; -+ defer_leftover_input(__G); -+ for (n = 0; n < RAND_HEAD_LEN; n++) { -+ b = NEXTBYTE; -+ h[n] = (uch)b; -+ Trace((stdout, " (%02x)", h[n])); -+ } -+ undefer_input(__G); -+ GLOBAL(pInfo->encrypted) = TRUE; -+ -+ if (GLOBAL(newzip)) { /* this is first encrypted member in this zipfile */ -+ GLOBAL(newzip) = FALSE; -+ if (passwrd != (char *)NULL) { /* user gave password on command line */ -+ if (!GLOBAL(key)) { -+ if ((GLOBAL(key) = (char *)malloc(strlen(passwrd)+1)) == -+ (char *)NULL) -+ return PK_MEM2; -+ strcpy(GLOBAL(key), passwrd); -+ GLOBAL(nopwd) = TRUE; /* inhibit password prompting! */ -+ } -+ } else if (GLOBAL(key)) { /* get rid of previous zipfile's key */ -+ free(GLOBAL(key)); -+ GLOBAL(key) = (char *)NULL; -+ } -+ } -+ -+ /* if have key already, test it; else allocate memory for it */ -+ if (GLOBAL(key)) { -+ if (!testp(__G__ h)) -+ return PK_COOL; /* existing password OK (else prompt for new) */ -+ else if (GLOBAL(nopwd)) -+ return PK_WARN; /* user indicated no more prompting */ -+ } else if ((GLOBAL(key) = (char *)malloc(IZ_PWLEN+1)) == (char *)NULL) -+ return PK_MEM2; -+ -+ /* try a few keys */ -+ n = 0; -+ do { -+ r = (*G.decr_passwd)((zvoid *)&G, &n, GLOBAL(key), IZ_PWLEN+1, -+ GLOBAL(zipfn), GLOBAL(filename)); -+ if (r == IZ_PW_ERROR) { /* internal error in fetch of PW */ -+ free (GLOBAL(key)); -+ GLOBAL(key) = NULL; -+ return PK_MEM2; -+ } -+ if (r != IZ_PW_ENTERED) { /* user replied "skip" or "skip all" */ -+ *GLOBAL(key) = '\0'; /* We try the NIL password, ... */ -+ n = 0; /* and cancel fetch for this item. */ -+ } -+ if (!testp(__G__ h)) -+ return PK_COOL; -+ if (r == IZ_PW_CANCELALL) /* User replied "Skip all" */ -+ GLOBAL(nopwd) = TRUE; /* inhibit any further PW prompt! */ -+ } while (n > 0); -+ -+ return PK_WARN; -+ -+} /* end function decrypt() */ -+ -+ -+ -+/*********************************************************************** -+ * Test the password. Return -1 if bad, 0 if OK. -+ */ -+local int testp(__G__ h) -+ __GDEF -+ ZCONST uch *h; -+{ -+ int r; -+ char *key_translated; -+ -+ /* On systems with "obscure" native character coding (e.g., EBCDIC), -+ * the first test translates the password to the "main standard" -+ * character coding. */ -+ -+#ifdef STR_TO_CP1 -+ /* allocate buffer for translated password */ -+ if ((key_translated = malloc(strlen(GLOBAL(key)) + 1)) == (char *)NULL) -+ return -1; -+ /* first try, test password translated "standard" charset */ -+ r = testkey(__G__ h, STR_TO_CP1(key_translated, GLOBAL(key))); -+#else /* !STR_TO_CP1 */ -+ /* first try, test password as supplied on the extractor's host */ -+ r = testkey(__G__ h, GLOBAL(key)); -+#endif /* ?STR_TO_CP1 */ -+ -+#ifdef STR_TO_CP2 -+ if (r != 0) { -+#ifndef STR_TO_CP1 -+ /* now prepare for second (and maybe third) test with translated pwd */ -+ if ((key_translated = malloc(strlen(GLOBAL(key)) + 1)) == (char *)NULL) -+ return -1; -+#endif -+ /* second try, password translated to alternate ("standard") charset */ -+ r = testkey(__G__ h, STR_TO_CP2(key_translated, GLOBAL(key))); -+#ifdef STR_TO_CP3 -+ if (r != 0) -+ /* third try, password translated to another "standard" charset */ -+ r = testkey(__G__ h, STR_TO_CP3(key_translated, GLOBAL(key))); -+#endif -+#ifndef STR_TO_CP1 -+ free(key_translated); -+#endif -+ } -+#endif /* STR_TO_CP2 */ -+ -+#ifdef STR_TO_CP1 -+ free(key_translated); -+ if (r != 0) { -+ /* last resort, test password as supplied on the extractor's host */ -+ r = testkey(__G__ h, GLOBAL(key)); -+ } -+#endif /* STR_TO_CP1 */ -+ -+ return r; -+ -+} /* end function testp() */ -+ -+ -+local int testkey(__G__ h, key) -+ __GDEF -+ ZCONST uch *h; /* decrypted header */ -+ ZCONST char *key; /* decryption password to test */ -+{ -+ ush b; -+#ifdef ZIP10 -+ ush c; -+#endif -+ int n; -+ uch *p; -+ uch hh[RAND_HEAD_LEN]; /* decrypted header */ -+ -+ /* set keys and save the encrypted header */ -+ init_keys(__G__ key); -+ memcpy(hh, h, RAND_HEAD_LEN); -+ -+ /* check password */ -+ for (n = 0; n < RAND_HEAD_LEN; n++) { -+ zdecode(hh[n]); -+ Trace((stdout, " %02x", hh[n])); -+ } -+ -+ Trace((stdout, -+ "\n lrec.crc= %08lx crec.crc= %08lx pInfo->ExtLocHdr= %s\n", -+ GLOBAL(lrec.crc32), GLOBAL(pInfo->crc), -+ GLOBAL(pInfo->ExtLocHdr) ? "true":"false")); -+ Trace((stdout, " incnt = %d unzip offset into zipfile = %ld\n", -+ GLOBAL(incnt), -+ GLOBAL(cur_zipfile_bufstart)+(GLOBAL(inptr)-GLOBAL(inbuf)))); -+ -+ /* same test as in zipbare(): */ -+ -+#ifdef ZIP10 /* check two bytes */ -+ c = hh[RAND_HEAD_LEN-2], b = hh[RAND_HEAD_LEN-1]; -+ Trace((stdout, -+ " (c | (b<<8)) = %04x (crc >> 16) = %04x lrec.time = %04x\n", -+ (ush)(c | (b<<8)), (ush)(GLOBAL(lrec.crc32) >> 16), -+ ((ush)GLOBAL(lrec.last_mod_dos_datetime) & 0xffff)))); -+ if ((ush)(c | (b<<8)) != (GLOBAL(pInfo->ExtLocHdr) ? -+ ((ush)GLOBAL(lrec.last_mod_dos_datetime) & 0xffff) : -+ (ush)(GLOBAL(lrec.crc32) >> 16))) -+ return -1; /* bad */ -+#else -+ b = hh[RAND_HEAD_LEN-1]; -+ Trace((stdout, " b = %02x (crc >> 24) = %02x (lrec.time >> 8) = %02x\n", -+ b, (ush)(GLOBAL(lrec.crc32) >> 24), -+ ((ush)GLOBAL(lrec.last_mod_dos_datetime) >> 8) & 0xff)); -+ if (b != (GLOBAL(pInfo->ExtLocHdr) ? -+ ((ush)GLOBAL(lrec.last_mod_dos_datetime) >> 8) & 0xff : -+ (ush)(GLOBAL(lrec.crc32) >> 24))) -+ return -1; /* bad */ -+#endif -+ /* password OK: decrypt current buffer contents before leaving */ -+ for (n = (long)GLOBAL(incnt) > GLOBAL(csize) ? -+ (int)GLOBAL(csize) : GLOBAL(incnt), -+ p = GLOBAL(inptr); n--; p++) -+ zdecode(*p); -+ return 0; /* OK */ -+ -+} /* end function testkey() */ -+ -+#endif /* UNZIP && !FUNZIP */ -+ -+#else /* !CRYPT */ -+ - /* something "externally visible" to shut up compiler/linker warnings */ - int zcr_dummy; -+ -+#endif /* ?CRYPT */ -diff -ur zip-2.3.orig/crypt.h zip-2.3/crypt.h ---- zip-2.3.orig/crypt.h 1999-10-09 23:10:00.000000000 +0300 -+++ zip-2.3/crypt.h 2005-07-15 16:19:00.000000000 +0300 -@@ -1,19 +1,13 @@ - /* -- Copyright (c) 1990-1999 Info-ZIP. All rights reserved. -+ Copyright (c) 1990-2000 Info-ZIP. All rights reserved. - -- See the accompanying file LICENSE, version 1999-Oct-05 or later -+ See the accompanying file LICENSE, version 2000-Apr-09 or later - (the contents of which are also included in zip.h) for terms of use. -- If, for some reason, both of these files are missing, the Info-ZIP license -- also may be found at: ftp://ftp.cdrom.com/pub/infozip/license.html -+ If, for some reason, all these files are missing, the Info-ZIP license -+ also may be found at: ftp://ftp.info-zip.org/pub/infozip/license.html - */ - /* -- crypt.h (dummy version) by Info-ZIP. Last revised: 15 Aug 98 -- -- This is a non-functional version of Info-ZIP's crypt.h encryption/ -- decryption header file for Zip, ZipCloak, UnZip and fUnZip. This -- file is not copyrighted and may be distributed without restriction. -- See the "WHERE" file for sites from which to obtain the full crypt -- sources (zcrypt28.zip or later). -+ crypt.h (full version) by Info-ZIP. Last revised: [see CR_VERSION_DATE] - */ - - #ifndef __crypt_h /* don't include more than once */ -@@ -22,11 +16,131 @@ - #ifdef CRYPT - # undef CRYPT - #endif --#define CRYPT 0 /* dummy version */ -+ -+#if !defined(NO_CRYPT) -+# define CRYPT 1 /* full version */ -+#else -+# define CRYPT 0 /* dummy version */ -+#endif -+ -+#if CRYPT -+/* full version */ -+ -+#ifdef CR_BETA -+# undef CR_BETA /* this is not a beta release */ -+#endif -+ -+#define CR_MAJORVER 2 -+#define CR_MINORVER 9 -+#ifdef CR_BETA -+# define CR_BETA_VER "a BETA" -+# define CR_VERSION_DATE "9 April 2000" /* last real code change */ -+#else -+# define CR_BETA_VER "" -+# define CR_VERSION_DATE "22 April 2000" /* last public release date */ -+# define CR_RELEASE -+#endif -+ -+#ifndef __G /* UnZip only, for now (DLL stuff) */ -+# define __G -+# define __G__ -+# define __GDEF -+# define __GPRO void -+# define __GPRO__ -+#endif -+ -+#if defined(MSDOS) || defined(OS2) || defined(WIN32) -+# ifndef DOS_OS2_W32 -+# define DOS_OS2_W32 -+# endif -+#endif -+ -+#if defined(DOS_OS2_W32) || defined(__human68k__) -+# ifndef DOS_H68_OS2_W32 -+# define DOS_H68_OS2_W32 -+# endif -+#endif -+ -+#if defined(VM_CMS) || defined(MVS) -+# ifndef CMS_MVS -+# define CMS_MVS -+# endif -+#endif -+ -+/* To allow combining of Zip and UnZip static libraries in a single binary, -+ * the Zip and UnZip versions of the crypt core functions have to be named -+ * differently. -+ */ -+#ifdef ZIP -+# ifdef REALLY_SHORT_SYMS -+# define decrypt_byte zdcrby -+# else -+# define decrypt_byte zp_decrypt_byte -+# endif -+# define update_keys zp_update_keys -+# define init_keys zp_init_keys -+#else /* !ZIP */ -+# ifdef REALLY_SHORT_SYMS -+# define decrypt_byte dcrbyt -+# endif -+#endif /* ?ZIP */ -+ -+#define IZ_PWLEN 80 /* input buffer size for reading encryption key */ -+#ifndef PWLEN /* for compatibility with previous zcrypt release... */ -+# define PWLEN IZ_PWLEN -+#endif -+#define RAND_HEAD_LEN 12 /* length of encryption random header */ -+ -+/* the crc_32_tab array has to be provided externally for the crypt calculus */ -+#ifndef CRC_32_TAB /* UnZip provides this in globals.h */ -+# if (!defined(USE_ZLIB) || defined(USE_OWN_CRCTAB)) -+ extern ZCONST ulg near *crc_32_tab; -+# else -+ extern ZCONST ulg Far *crc_32_tab; -+# endif -+#endif /* !CRC_32_TAB */ -+ -+/* encode byte c, using temp t. Warning: c must not have side effects. */ -+#define zencode(c,t) (t=decrypt_byte(__G), update_keys(c), t^(c)) -+ -+/* decode byte c in place */ -+#define zdecode(c) update_keys(__G__ c ^= decrypt_byte(__G)) -+ -+int decrypt_byte OF((__GPRO)); -+int update_keys OF((__GPRO__ int c)); -+void init_keys OF((__GPRO__ ZCONST char *passwd)); -+ -+#ifdef ZIP -+ void crypthead OF((ZCONST char *, ulg, FILE *)); -+# ifdef UTIL -+ int zipcloak OF((struct zlist far *, FILE *, FILE *, ZCONST char *)); -+ int zipbare OF((struct zlist far *, FILE *, FILE *, ZCONST char *)); -+# else -+ unsigned zfwrite OF((zvoid *, extent, extent, FILE *)); -+ extern char *key; -+# endif -+#endif /* ZIP */ -+ -+#if (defined(UNZIP) && !defined(FUNZIP)) -+ int decrypt OF((__GPRO__ ZCONST char *passwrd)); -+#endif -+ -+#ifdef FUNZIP -+ extern int encrypted; -+# ifdef NEXTBYTE -+# undef NEXTBYTE -+# endif -+# define NEXTBYTE \ -+ (encrypted? update_keys(__G__ getc(G.in)^decrypt_byte(__G)) : getc(G.in)) -+#endif /* FUNZIP */ -+ -+#else /* !CRYPT */ -+/* dummy version */ - - #define zencode - #define zdecode - - #define zfwrite fwrite - -+#endif /* ?CRYPT */ - #endif /* !__crypt_h */ diff --git a/tags/pisi-1.0_alpha2/tests/zip/files/zip-2.3-unix_configure-pic.patch b/tags/pisi-1.0_alpha2/tests/zip/files/zip-2.3-unix_configure-pic.patch deleted file mode 100644 index 45b137b0..00000000 --- a/tags/pisi-1.0_alpha2/tests/zip/files/zip-2.3-unix_configure-pic.patch +++ /dev/null @@ -1,12 +0,0 @@ ---- unix/configure.orig 2004-09-28 06:45:33.000000000 +0000 -+++ unix/configure 2004-09-28 06:54:53.000000000 +0000 -@@ -29,6 +29,9 @@ - - echo Check if we can use asm code - OBJA="" -+piclib="$(echo | $CPP -dM $CFLAGS - | grep -i __pic__)" -+echo "Checking if compiler wants to create pic code" -+[ "$piclib" == "" ] && \ - if eval "$CPP match.S > _match.s 2>/dev/null"; then - if test ! -s _match.s || grep error < _match.s > /dev/null; then - : diff --git a/tags/pisi-1.0_alpha2/tests/zip/files/zip-CAN-2004-1010.patch b/tags/pisi-1.0_alpha2/tests/zip/files/zip-CAN-2004-1010.patch deleted file mode 100644 index 150d6407..00000000 --- a/tags/pisi-1.0_alpha2/tests/zip/files/zip-CAN-2004-1010.patch +++ /dev/null @@ -1,25 +0,0 @@ ---- zip-2.3/unix/unix.c.orig 2004-11-05 15:44:41.000000000 -0500 -+++ zip-2.3/unix/unix.c 2004-11-05 15:50:28.000000000 -0500 -@@ -319,8 +319,8 @@ iztimes *t; /* return value: - a file size of -1 */ - { - struct stat s; /* results of stat() */ -- char name[FNMAX]; -- int len = strlen(f); -+ char *name; -+ size_t len = strlen(f); - - if (f == label) { - if (a != NULL) -@@ -331,6 +331,11 @@ iztimes *t; /* return value: - t->atime = t->mtime = t->ctime = label_utim; - return label_time; - } -+ -+ name = malloc(len+1); -+ if (!name) -+ return 0; -+ - strcpy(name, f); - if (name[len - 1] == '/') - name[len - 1] = '\0'; diff --git a/tags/pisi-1.0_alpha2/tests/zip/pspec.xml b/tags/pisi-1.0_alpha2/tests/zip/pspec.xml deleted file mode 100644 index e64035a5..00000000 --- a/tags/pisi-1.0_alpha2/tests/zip/pspec.xml +++ /dev/null @@ -1,50 +0,0 @@ - - - - - - - zip - ftp://ftp.freesoftware.com/pub/infozip/Zip.html - - PACKAGER - PACKAGER_EMAIL - - Info-ZIP - category - component - Info ZIP (encryption support) - Info ZIP (encryption support) - ftp://ftp.linux.org.tr/pub/mirrors/gentoo/distfiles/zip23.tar.gz - - zip-2.3-unix_configure-pic.patch - zip-CAN-2004-1010.patch - zcrypt.patch - - - unzip - patch - - - - 2005-07-15 - 2.3 - 1 - - - - - - zip - - unzip - - - /usr/bin - /usr/share/doc - /usr/share/man - - - - - diff --git a/tags/pisi-1.0_alpha2/tests/zip2/actions.py b/tags/pisi-1.0_alpha2/tests/zip2/actions.py deleted file mode 100644 index a2c1b060..00000000 --- a/tags/pisi-1.0_alpha2/tests/zip2/actions.py +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# -# Copyright (C) 2005, TUBITAK/UEKAE -# -# 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. -# -# Please read the COPYING file. -# - -from pisi.actionsapi import autotools -from pisi.actionsapi import get -from pisi.actionsapi import pisitools - -WorkDir="zip-2.3" - -def setup(): - pisitools.dosed("unix/Makefile", "-O2", get.CFLAGS()) - -def build(): - autotools.make("-f unix/Makefile CC=%s CPP=%s generic" % (get.CC(), get.CXX())) - -def install(): - pisitools.dobin("zip") - pisitools.dobin("zipcloak") - pisitools.dobin("zipnote") - pisitools.dobin("zipsplit") - - pisitools.doman("man/*.1") - pisitools.dodoc("BUGS", "CHANGES", "MANUAL", "README", "TODO", "WHATSNEW", "WHERE") - diff --git a/tags/pisi-1.0_alpha2/tests/zip2/files/zcrypt.patch b/tags/pisi-1.0_alpha2/tests/zip2/files/zcrypt.patch deleted file mode 100644 index 6b696af5..00000000 --- a/tags/pisi-1.0_alpha2/tests/zip2/files/zcrypt.patch +++ /dev/null @@ -1,752 +0,0 @@ -diff -ur zip-2.3.orig/crypt.c zip-2.3/crypt.c ---- zip-2.3.orig/crypt.c 1999-10-09 23:09:26.000000000 +0300 -+++ zip-2.3/crypt.c 2005-07-15 16:19:00.000000000 +0300 -@@ -1,20 +1,577 @@ - /* -- Copyright (c) 1990-1999 Info-ZIP. All rights reserved. -+ Copyright (c) 1990-2000 Info-ZIP. All rights reserved. - -- See the accompanying file LICENSE, version 1999-Oct-05 or later -+ See the accompanying file LICENSE, version 2000-Apr-09 or later - (the contents of which are also included in zip.h) for terms of use. -- If, for some reason, both of these files are missing, the Info-ZIP license -- also may be found at: ftp://ftp.cdrom.com/pub/infozip/license.html -+ If, for some reason, all these files are missing, the Info-ZIP license -+ also may be found at: ftp://ftp.info-zip.org/pub/infozip/license.html - */ - /* -- crypt.c (dummy version) by Info-ZIP. Last revised: 15 Aug 98 -+ crypt.c (full version) by Info-ZIP. Last revised: [see crypt.h] - -- This is a non-functional version of Info-ZIP's crypt.c encryption/ -- decryption code for Zip, ZipCloak, UnZip and fUnZip. This file is -- not copyrighted and may be distributed freely. :-) See the "WHERE" -- file for sites from which to obtain the full encryption/decryption -- sources (zcrypt28.zip or later). -+ The encryption/decryption parts of this source code (as opposed to the -+ non-echoing password parts) were originally written in Europe. The -+ whole source package can be freely distributed, including from the USA. -+ (Prior to January 2000, re-export from the US was a violation of US law.) - */ - -+/* -+ This encryption code is a direct transcription of the algorithm from -+ Roger Schlafly, described by Phil Katz in the file appnote.txt. This -+ file (appnote.txt) is distributed with the PKZIP program (even in the -+ version without encryption capabilities). -+ */ -+ -+#define ZCRYPT_INTERNAL -+#include "zip.h" -+#include "crypt.h" -+#include "ttyio.h" -+ -+#if CRYPT -+ -+#ifndef FALSE -+# define FALSE 0 -+#endif -+ -+#ifdef ZIP -+ /* For the encoding task used in Zip (and ZipCloak), we want to initialize -+ the crypt algorithm with some reasonably unpredictable bytes, see -+ the crypthead() function. The standard rand() library function is -+ used to supply these `random' bytes, which in turn is initialized by -+ a srand() call. The srand() function takes an "unsigned" (at least 16bit) -+ seed value as argument to determine the starting point of the rand() -+ pseudo-random number generator. -+ This seed number is constructed as "Seed = Seed1 .XOR. Seed2" with -+ Seed1 supplied by the current time (= "(unsigned)time()") and Seed2 -+ as some (hopefully) nondeterministic bitmask. On many (most) systems, -+ we use some "process specific" number, as the PID or something similar, -+ but when nothing unpredictable is available, a fixed number may be -+ sufficient. -+ NOTE: -+ 1.) This implementation requires the availability of the following -+ standard UNIX C runtime library functions: time(), rand(), srand(). -+ On systems where some of them are missing, the environment that -+ incorporates the crypt routines must supply suitable replacement -+ functions. -+ 2.) It is a very bad idea to use a second call to time() to set the -+ "Seed2" number! In this case, both "Seed1" and "Seed2" would be -+ (almost) identical, resulting in a (mostly) "zero" constant seed -+ number passed to srand(). -+ -+ The implementation environment defined in the "zip.h" header should -+ supply a reasonable definition for ZCR_SEED2 (an unsigned number; for -+ most implementations of rand() and srand(), only the lower 16 bits are -+ significant!). An example that works on many systems would be -+ "#define ZCR_SEED2 (unsigned)getpid()". -+ The default definition for ZCR_SEED2 supplied below should be regarded -+ as a fallback to allow successful compilation in "beta state" -+ environments. -+ */ -+# include /* time() function supplies first part of crypt seed */ -+ /* "last resort" source for second part of crypt seed pattern */ -+# ifndef ZCR_SEED2 -+# define ZCR_SEED2 (unsigned)3141592654L /* use PI as default pattern */ -+# endif -+# ifdef GLOBAL /* used in Amiga system headers, maybe others too */ -+# undef GLOBAL -+# endif -+# define GLOBAL(g) g -+#else /* !ZIP */ -+# define GLOBAL(g) G.g -+#endif /* ?ZIP */ -+ -+ -+#ifdef UNZIP -+ /* char *key = (char *)NULL; moved to globals.h */ -+# ifndef FUNZIP -+ local int testp OF((__GPRO__ ZCONST uch *h)); -+ local int testkey OF((__GPRO__ ZCONST uch *h, ZCONST char *key)); -+# endif -+#endif /* UNZIP */ -+ -+#ifndef UNZIP /* moved to globals.h for UnZip */ -+ local ulg keys[3]; /* keys defining the pseudo-random sequence */ -+#endif /* !UNZIP */ -+ -+#ifndef Trace -+# ifdef CRYPT_DEBUG -+# define Trace(x) fprintf x -+# else -+# define Trace(x) -+# endif -+#endif -+ -+#ifndef CRC_32_TAB -+# define CRC_32_TAB crc_32_tab -+#endif -+ -+#define CRC32(c, b) (CRC_32_TAB[((int)(c) ^ (b)) & 0xff] ^ ((c) >> 8)) -+ -+/*********************************************************************** -+ * Return the next byte in the pseudo-random sequence -+ */ -+int decrypt_byte(__G) -+ __GDEF -+{ -+ unsigned temp; /* POTENTIAL BUG: temp*(temp^1) may overflow in an -+ * unpredictable manner on 16-bit systems; not a problem -+ * with any known compiler so far, though */ -+ -+ temp = ((unsigned)GLOBAL(keys[2]) & 0xffff) | 2; -+ return (int)(((temp * (temp ^ 1)) >> 8) & 0xff); -+} -+ -+/*********************************************************************** -+ * Update the encryption keys with the next byte of plain text -+ */ -+int update_keys(__G__ c) -+ __GDEF -+ int c; /* byte of plain text */ -+{ -+ GLOBAL(keys[0]) = CRC32(GLOBAL(keys[0]), c); -+ GLOBAL(keys[1]) += GLOBAL(keys[0]) & 0xff; -+ GLOBAL(keys[1]) = GLOBAL(keys[1]) * 134775813L + 1; -+ { -+ register int keyshift = (int)(GLOBAL(keys[1]) >> 24); -+ GLOBAL(keys[2]) = CRC32(GLOBAL(keys[2]), keyshift); -+ } -+ return c; -+} -+ -+ -+/*********************************************************************** -+ * Initialize the encryption keys and the random header according to -+ * the given password. -+ */ -+void init_keys(__G__ passwd) -+ __GDEF -+ ZCONST char *passwd; /* password string with which to modify keys */ -+{ -+ GLOBAL(keys[0]) = 305419896L; -+ GLOBAL(keys[1]) = 591751049L; -+ GLOBAL(keys[2]) = 878082192L; -+ while (*passwd != '\0') { -+ update_keys(__G__ (int)*passwd); -+ passwd++; -+ } -+} -+ -+ -+#ifdef ZIP -+ -+/*********************************************************************** -+ * Write encryption header to file zfile using the password passwd -+ * and the cyclic redundancy check crc. -+ */ -+void crypthead(passwd, crc, zfile) -+ ZCONST char *passwd; /* password string */ -+ ulg crc; /* crc of file being encrypted */ -+ FILE *zfile; /* where to write header */ -+{ -+ int n; /* index in random header */ -+ int t; /* temporary */ -+ int c; /* random byte */ -+ int ztemp; /* temporary for zencoded value */ -+ uch header[RAND_HEAD_LEN-2]; /* random header */ -+ static unsigned calls = 0; /* ensure different random header each time */ -+ -+ /* First generate RAND_HEAD_LEN-2 random bytes. We encrypt the -+ * output of rand() to get less predictability, since rand() is -+ * often poorly implemented. -+ */ -+ if (++calls == 1) { -+ srand((unsigned)time(NULL) ^ ZCR_SEED2); -+ } -+ init_keys(passwd); -+ for (n = 0; n < RAND_HEAD_LEN-2; n++) { -+ c = (rand() >> 7) & 0xff; -+ header[n] = (uch)zencode(c, t); -+ } -+ /* Encrypt random header (last two bytes is high word of crc) */ -+ init_keys(passwd); -+ for (n = 0; n < RAND_HEAD_LEN-2; n++) { -+ ztemp = zencode(header[n], t); -+ putc(ztemp, zfile); -+ } -+ ztemp = zencode((int)(crc >> 16) & 0xff, t); -+ putc(ztemp, zfile); -+ ztemp = zencode((int)(crc >> 24) & 0xff, t); -+ putc(ztemp, zfile); -+} -+ -+ -+#ifdef UTIL -+ -+/*********************************************************************** -+ * Encrypt the zip entry described by z from file source to file dest -+ * using the password passwd. Return an error code in the ZE_ class. -+ */ -+int zipcloak(z, source, dest, passwd) -+ struct zlist far *z; /* zip entry to encrypt */ -+ FILE *source, *dest; /* source and destination files */ -+ ZCONST char *passwd; /* password string */ -+{ -+ int c; /* input byte */ -+ int res; /* result code */ -+ ulg n; /* holds offset and counts size */ -+ ush flag; /* previous flags */ -+ int t; /* temporary */ -+ int ztemp; /* temporary storage for zencode value */ -+ -+ /* Set encrypted bit, clear extended local header bit and write local -+ header to output file */ -+ if ((n = (ulg)ftell(dest)) == (ulg)-1L) return ZE_TEMP; -+ z->off = n; -+ flag = z->flg; -+ z->flg |= 1, z->flg &= ~8; -+ z->lflg |= 1, z->lflg &= ~8; -+ z->siz += RAND_HEAD_LEN; -+ if ((res = putlocal(z, dest)) != ZE_OK) return res; -+ -+ /* Initialize keys with password and write random header */ -+ crypthead(passwd, z->crc, dest); -+ -+ /* Skip local header in input file */ -+ if (fseek(source, (long)(4 + LOCHEAD + (ulg)z->nam + (ulg)z->ext), -+ SEEK_CUR)) { -+ return ferror(source) ? ZE_READ : ZE_EOF; -+ } -+ -+ /* Encrypt data */ -+ for (n = z->siz - RAND_HEAD_LEN; n; n--) { -+ if ((c = getc(source)) == EOF) { -+ return ferror(source) ? ZE_READ : ZE_EOF; -+ } -+ ztemp = zencode(c, t); -+ putc(ztemp, dest); -+ } -+ /* Skip extended local header in input file if there is one */ -+ if ((flag & 8) != 0 && fseek(source, 16L, SEEK_CUR)) { -+ return ferror(source) ? ZE_READ : ZE_EOF; -+ } -+ if (fflush(dest) == EOF) return ZE_TEMP; -+ return ZE_OK; -+} -+ -+/*********************************************************************** -+ * Decrypt the zip entry described by z from file source to file dest -+ * using the password passwd. Return an error code in the ZE_ class. -+ */ -+int zipbare(z, source, dest, passwd) -+ struct zlist far *z; /* zip entry to encrypt */ -+ FILE *source, *dest; /* source and destination files */ -+ ZCONST char *passwd; /* password string */ -+{ -+ int c0, c1; /* last two input bytes */ -+ ulg offset; /* used for file offsets */ -+ ulg size; /* size of input data */ -+ int r; /* size of encryption header */ -+ int res; /* return code */ -+ ush flag; /* previous flags */ -+ -+ /* Save position and skip local header in input file */ -+ if ((offset = (ulg)ftell(source)) == (ulg)-1L || -+ fseek(source, (long)(4 + LOCHEAD + (ulg)z->nam + (ulg)z->ext), -+ SEEK_CUR)) { -+ return ferror(source) ? ZE_READ : ZE_EOF; -+ } -+ /* Initialize keys with password */ -+ init_keys(passwd); -+ -+ /* Decrypt encryption header, save last two bytes */ -+ c1 = 0; -+ for (r = RAND_HEAD_LEN; r; r--) { -+ c0 = c1; -+ if ((c1 = getc(source)) == EOF) { -+ return ferror(source) ? ZE_READ : ZE_EOF; -+ } -+ Trace((stdout, " (%02x)", c1)); -+ zdecode(c1); -+ Trace((stdout, " %02x", c1)); -+ } -+ Trace((stdout, "\n")); -+ -+ /* If last two bytes of header don't match crc (or file time in the -+ * case of an extended local header), back up and just copy. For -+ * pkzip 2.0, the check has been reduced to one byte only. -+ */ -+#ifdef ZIP10 -+ if ((ush)(c0 | (c1<<8)) != -+ (z->flg & 8 ? (ush) z->tim & 0xffff : (ush)(z->crc >> 16))) { -+#else -+ c0++; /* avoid warning on unused variable */ -+ if ((ush)c1 != (z->flg & 8 ? (ush) z->tim >> 8 : (ush)(z->crc >> 24))) { -+#endif -+ if (fseek(source, offset, SEEK_SET)) { -+ return ferror(source) ? ZE_READ : ZE_EOF; -+ } -+ if ((res = zipcopy(z, source, dest)) != ZE_OK) return res; -+ return ZE_MISS; -+ } -+ -+ /* Clear encrypted bit and local header bit, and write local header to -+ output file */ -+ if ((offset = (ulg)ftell(dest)) == (ulg)-1L) return ZE_TEMP; -+ z->off = offset; -+ flag = z->flg; -+ z->flg &= ~9; -+ z->lflg &= ~9; -+ z->siz -= RAND_HEAD_LEN; -+ if ((res = putlocal(z, dest)) != ZE_OK) return res; -+ -+ /* Decrypt data */ -+ for (size = z->siz; size; size--) { -+ if ((c1 = getc(source)) == EOF) { -+ return ferror(source) ? ZE_READ : ZE_EOF; -+ } -+ zdecode(c1); -+ putc(c1, dest); -+ } -+ /* Skip extended local header in input file if there is one */ -+ if ((flag & 8) != 0 && fseek(source, 16L, SEEK_CUR)) { -+ return ferror(source) ? ZE_READ : ZE_EOF; -+ } -+ if (fflush(dest) == EOF) return ZE_TEMP; -+ -+ return ZE_OK; -+} -+ -+ -+#else /* !UTIL */ -+ -+/*********************************************************************** -+ * If requested, encrypt the data in buf, and in any case call fwrite() -+ * with the arguments to zfwrite(). Return what fwrite() returns. -+ */ -+unsigned zfwrite(buf, item_size, nb, f) -+ zvoid *buf; /* data buffer */ -+ extent item_size; /* size of each item in bytes */ -+ extent nb; /* number of items */ -+ FILE *f; /* file to write to */ -+{ -+ int t; /* temporary */ -+ -+ if (key != (char *)NULL) { /* key is the global password pointer */ -+ ulg size; /* buffer size */ -+ char *p = (char*)buf; /* steps through buffer */ -+ -+ /* Encrypt data in buffer */ -+ for (size = item_size*(ulg)nb; size != 0; p++, size--) { -+ *p = (char)zencode(*p, t); -+ } -+ } -+ /* Write the buffer out */ -+ return fwrite(buf, item_size, nb, f); -+} -+ -+#endif /* ?UTIL */ -+#endif /* ZIP */ -+ -+ -+#if (defined(UNZIP) && !defined(FUNZIP)) -+ -+/*********************************************************************** -+ * Get the password and set up keys for current zipfile member. -+ * Return PK_ class error. -+ */ -+int decrypt(__G__ passwrd) -+ __GDEF -+ ZCONST char *passwrd; -+{ -+ ush b; -+ int n, r; -+ uch h[RAND_HEAD_LEN]; -+ -+ Trace((stdout, "\n[incnt = %d]: ", GLOBAL(incnt))); -+ -+ /* get header once (turn off "encrypted" flag temporarily so we don't -+ * try to decrypt the same data twice) */ -+ GLOBAL(pInfo->encrypted) = FALSE; -+ defer_leftover_input(__G); -+ for (n = 0; n < RAND_HEAD_LEN; n++) { -+ b = NEXTBYTE; -+ h[n] = (uch)b; -+ Trace((stdout, " (%02x)", h[n])); -+ } -+ undefer_input(__G); -+ GLOBAL(pInfo->encrypted) = TRUE; -+ -+ if (GLOBAL(newzip)) { /* this is first encrypted member in this zipfile */ -+ GLOBAL(newzip) = FALSE; -+ if (passwrd != (char *)NULL) { /* user gave password on command line */ -+ if (!GLOBAL(key)) { -+ if ((GLOBAL(key) = (char *)malloc(strlen(passwrd)+1)) == -+ (char *)NULL) -+ return PK_MEM2; -+ strcpy(GLOBAL(key), passwrd); -+ GLOBAL(nopwd) = TRUE; /* inhibit password prompting! */ -+ } -+ } else if (GLOBAL(key)) { /* get rid of previous zipfile's key */ -+ free(GLOBAL(key)); -+ GLOBAL(key) = (char *)NULL; -+ } -+ } -+ -+ /* if have key already, test it; else allocate memory for it */ -+ if (GLOBAL(key)) { -+ if (!testp(__G__ h)) -+ return PK_COOL; /* existing password OK (else prompt for new) */ -+ else if (GLOBAL(nopwd)) -+ return PK_WARN; /* user indicated no more prompting */ -+ } else if ((GLOBAL(key) = (char *)malloc(IZ_PWLEN+1)) == (char *)NULL) -+ return PK_MEM2; -+ -+ /* try a few keys */ -+ n = 0; -+ do { -+ r = (*G.decr_passwd)((zvoid *)&G, &n, GLOBAL(key), IZ_PWLEN+1, -+ GLOBAL(zipfn), GLOBAL(filename)); -+ if (r == IZ_PW_ERROR) { /* internal error in fetch of PW */ -+ free (GLOBAL(key)); -+ GLOBAL(key) = NULL; -+ return PK_MEM2; -+ } -+ if (r != IZ_PW_ENTERED) { /* user replied "skip" or "skip all" */ -+ *GLOBAL(key) = '\0'; /* We try the NIL password, ... */ -+ n = 0; /* and cancel fetch for this item. */ -+ } -+ if (!testp(__G__ h)) -+ return PK_COOL; -+ if (r == IZ_PW_CANCELALL) /* User replied "Skip all" */ -+ GLOBAL(nopwd) = TRUE; /* inhibit any further PW prompt! */ -+ } while (n > 0); -+ -+ return PK_WARN; -+ -+} /* end function decrypt() */ -+ -+ -+ -+/*********************************************************************** -+ * Test the password. Return -1 if bad, 0 if OK. -+ */ -+local int testp(__G__ h) -+ __GDEF -+ ZCONST uch *h; -+{ -+ int r; -+ char *key_translated; -+ -+ /* On systems with "obscure" native character coding (e.g., EBCDIC), -+ * the first test translates the password to the "main standard" -+ * character coding. */ -+ -+#ifdef STR_TO_CP1 -+ /* allocate buffer for translated password */ -+ if ((key_translated = malloc(strlen(GLOBAL(key)) + 1)) == (char *)NULL) -+ return -1; -+ /* first try, test password translated "standard" charset */ -+ r = testkey(__G__ h, STR_TO_CP1(key_translated, GLOBAL(key))); -+#else /* !STR_TO_CP1 */ -+ /* first try, test password as supplied on the extractor's host */ -+ r = testkey(__G__ h, GLOBAL(key)); -+#endif /* ?STR_TO_CP1 */ -+ -+#ifdef STR_TO_CP2 -+ if (r != 0) { -+#ifndef STR_TO_CP1 -+ /* now prepare for second (and maybe third) test with translated pwd */ -+ if ((key_translated = malloc(strlen(GLOBAL(key)) + 1)) == (char *)NULL) -+ return -1; -+#endif -+ /* second try, password translated to alternate ("standard") charset */ -+ r = testkey(__G__ h, STR_TO_CP2(key_translated, GLOBAL(key))); -+#ifdef STR_TO_CP3 -+ if (r != 0) -+ /* third try, password translated to another "standard" charset */ -+ r = testkey(__G__ h, STR_TO_CP3(key_translated, GLOBAL(key))); -+#endif -+#ifndef STR_TO_CP1 -+ free(key_translated); -+#endif -+ } -+#endif /* STR_TO_CP2 */ -+ -+#ifdef STR_TO_CP1 -+ free(key_translated); -+ if (r != 0) { -+ /* last resort, test password as supplied on the extractor's host */ -+ r = testkey(__G__ h, GLOBAL(key)); -+ } -+#endif /* STR_TO_CP1 */ -+ -+ return r; -+ -+} /* end function testp() */ -+ -+ -+local int testkey(__G__ h, key) -+ __GDEF -+ ZCONST uch *h; /* decrypted header */ -+ ZCONST char *key; /* decryption password to test */ -+{ -+ ush b; -+#ifdef ZIP10 -+ ush c; -+#endif -+ int n; -+ uch *p; -+ uch hh[RAND_HEAD_LEN]; /* decrypted header */ -+ -+ /* set keys and save the encrypted header */ -+ init_keys(__G__ key); -+ memcpy(hh, h, RAND_HEAD_LEN); -+ -+ /* check password */ -+ for (n = 0; n < RAND_HEAD_LEN; n++) { -+ zdecode(hh[n]); -+ Trace((stdout, " %02x", hh[n])); -+ } -+ -+ Trace((stdout, -+ "\n lrec.crc= %08lx crec.crc= %08lx pInfo->ExtLocHdr= %s\n", -+ GLOBAL(lrec.crc32), GLOBAL(pInfo->crc), -+ GLOBAL(pInfo->ExtLocHdr) ? "true":"false")); -+ Trace((stdout, " incnt = %d unzip offset into zipfile = %ld\n", -+ GLOBAL(incnt), -+ GLOBAL(cur_zipfile_bufstart)+(GLOBAL(inptr)-GLOBAL(inbuf)))); -+ -+ /* same test as in zipbare(): */ -+ -+#ifdef ZIP10 /* check two bytes */ -+ c = hh[RAND_HEAD_LEN-2], b = hh[RAND_HEAD_LEN-1]; -+ Trace((stdout, -+ " (c | (b<<8)) = %04x (crc >> 16) = %04x lrec.time = %04x\n", -+ (ush)(c | (b<<8)), (ush)(GLOBAL(lrec.crc32) >> 16), -+ ((ush)GLOBAL(lrec.last_mod_dos_datetime) & 0xffff)))); -+ if ((ush)(c | (b<<8)) != (GLOBAL(pInfo->ExtLocHdr) ? -+ ((ush)GLOBAL(lrec.last_mod_dos_datetime) & 0xffff) : -+ (ush)(GLOBAL(lrec.crc32) >> 16))) -+ return -1; /* bad */ -+#else -+ b = hh[RAND_HEAD_LEN-1]; -+ Trace((stdout, " b = %02x (crc >> 24) = %02x (lrec.time >> 8) = %02x\n", -+ b, (ush)(GLOBAL(lrec.crc32) >> 24), -+ ((ush)GLOBAL(lrec.last_mod_dos_datetime) >> 8) & 0xff)); -+ if (b != (GLOBAL(pInfo->ExtLocHdr) ? -+ ((ush)GLOBAL(lrec.last_mod_dos_datetime) >> 8) & 0xff : -+ (ush)(GLOBAL(lrec.crc32) >> 24))) -+ return -1; /* bad */ -+#endif -+ /* password OK: decrypt current buffer contents before leaving */ -+ for (n = (long)GLOBAL(incnt) > GLOBAL(csize) ? -+ (int)GLOBAL(csize) : GLOBAL(incnt), -+ p = GLOBAL(inptr); n--; p++) -+ zdecode(*p); -+ return 0; /* OK */ -+ -+} /* end function testkey() */ -+ -+#endif /* UNZIP && !FUNZIP */ -+ -+#else /* !CRYPT */ -+ - /* something "externally visible" to shut up compiler/linker warnings */ - int zcr_dummy; -+ -+#endif /* ?CRYPT */ -diff -ur zip-2.3.orig/crypt.h zip-2.3/crypt.h ---- zip-2.3.orig/crypt.h 1999-10-09 23:10:00.000000000 +0300 -+++ zip-2.3/crypt.h 2005-07-15 16:19:00.000000000 +0300 -@@ -1,19 +1,13 @@ - /* -- Copyright (c) 1990-1999 Info-ZIP. All rights reserved. -+ Copyright (c) 1990-2000 Info-ZIP. All rights reserved. - -- See the accompanying file LICENSE, version 1999-Oct-05 or later -+ See the accompanying file LICENSE, version 2000-Apr-09 or later - (the contents of which are also included in zip.h) for terms of use. -- If, for some reason, both of these files are missing, the Info-ZIP license -- also may be found at: ftp://ftp.cdrom.com/pub/infozip/license.html -+ If, for some reason, all these files are missing, the Info-ZIP license -+ also may be found at: ftp://ftp.info-zip.org/pub/infozip/license.html - */ - /* -- crypt.h (dummy version) by Info-ZIP. Last revised: 15 Aug 98 -- -- This is a non-functional version of Info-ZIP's crypt.h encryption/ -- decryption header file for Zip, ZipCloak, UnZip and fUnZip. This -- file is not copyrighted and may be distributed without restriction. -- See the "WHERE" file for sites from which to obtain the full crypt -- sources (zcrypt28.zip or later). -+ crypt.h (full version) by Info-ZIP. Last revised: [see CR_VERSION_DATE] - */ - - #ifndef __crypt_h /* don't include more than once */ -@@ -22,11 +16,131 @@ - #ifdef CRYPT - # undef CRYPT - #endif --#define CRYPT 0 /* dummy version */ -+ -+#if !defined(NO_CRYPT) -+# define CRYPT 1 /* full version */ -+#else -+# define CRYPT 0 /* dummy version */ -+#endif -+ -+#if CRYPT -+/* full version */ -+ -+#ifdef CR_BETA -+# undef CR_BETA /* this is not a beta release */ -+#endif -+ -+#define CR_MAJORVER 2 -+#define CR_MINORVER 9 -+#ifdef CR_BETA -+# define CR_BETA_VER "a BETA" -+# define CR_VERSION_DATE "9 April 2000" /* last real code change */ -+#else -+# define CR_BETA_VER "" -+# define CR_VERSION_DATE "22 April 2000" /* last public release date */ -+# define CR_RELEASE -+#endif -+ -+#ifndef __G /* UnZip only, for now (DLL stuff) */ -+# define __G -+# define __G__ -+# define __GDEF -+# define __GPRO void -+# define __GPRO__ -+#endif -+ -+#if defined(MSDOS) || defined(OS2) || defined(WIN32) -+# ifndef DOS_OS2_W32 -+# define DOS_OS2_W32 -+# endif -+#endif -+ -+#if defined(DOS_OS2_W32) || defined(__human68k__) -+# ifndef DOS_H68_OS2_W32 -+# define DOS_H68_OS2_W32 -+# endif -+#endif -+ -+#if defined(VM_CMS) || defined(MVS) -+# ifndef CMS_MVS -+# define CMS_MVS -+# endif -+#endif -+ -+/* To allow combining of Zip and UnZip static libraries in a single binary, -+ * the Zip and UnZip versions of the crypt core functions have to be named -+ * differently. -+ */ -+#ifdef ZIP -+# ifdef REALLY_SHORT_SYMS -+# define decrypt_byte zdcrby -+# else -+# define decrypt_byte zp_decrypt_byte -+# endif -+# define update_keys zp_update_keys -+# define init_keys zp_init_keys -+#else /* !ZIP */ -+# ifdef REALLY_SHORT_SYMS -+# define decrypt_byte dcrbyt -+# endif -+#endif /* ?ZIP */ -+ -+#define IZ_PWLEN 80 /* input buffer size for reading encryption key */ -+#ifndef PWLEN /* for compatibility with previous zcrypt release... */ -+# define PWLEN IZ_PWLEN -+#endif -+#define RAND_HEAD_LEN 12 /* length of encryption random header */ -+ -+/* the crc_32_tab array has to be provided externally for the crypt calculus */ -+#ifndef CRC_32_TAB /* UnZip provides this in globals.h */ -+# if (!defined(USE_ZLIB) || defined(USE_OWN_CRCTAB)) -+ extern ZCONST ulg near *crc_32_tab; -+# else -+ extern ZCONST ulg Far *crc_32_tab; -+# endif -+#endif /* !CRC_32_TAB */ -+ -+/* encode byte c, using temp t. Warning: c must not have side effects. */ -+#define zencode(c,t) (t=decrypt_byte(__G), update_keys(c), t^(c)) -+ -+/* decode byte c in place */ -+#define zdecode(c) update_keys(__G__ c ^= decrypt_byte(__G)) -+ -+int decrypt_byte OF((__GPRO)); -+int update_keys OF((__GPRO__ int c)); -+void init_keys OF((__GPRO__ ZCONST char *passwd)); -+ -+#ifdef ZIP -+ void crypthead OF((ZCONST char *, ulg, FILE *)); -+# ifdef UTIL -+ int zipcloak OF((struct zlist far *, FILE *, FILE *, ZCONST char *)); -+ int zipbare OF((struct zlist far *, FILE *, FILE *, ZCONST char *)); -+# else -+ unsigned zfwrite OF((zvoid *, extent, extent, FILE *)); -+ extern char *key; -+# endif -+#endif /* ZIP */ -+ -+#if (defined(UNZIP) && !defined(FUNZIP)) -+ int decrypt OF((__GPRO__ ZCONST char *passwrd)); -+#endif -+ -+#ifdef FUNZIP -+ extern int encrypted; -+# ifdef NEXTBYTE -+# undef NEXTBYTE -+# endif -+# define NEXTBYTE \ -+ (encrypted? update_keys(__G__ getc(G.in)^decrypt_byte(__G)) : getc(G.in)) -+#endif /* FUNZIP */ -+ -+#else /* !CRYPT */ -+/* dummy version */ - - #define zencode - #define zdecode - - #define zfwrite fwrite - -+#endif /* ?CRYPT */ - #endif /* !__crypt_h */ diff --git a/tags/pisi-1.0_alpha2/tests/zip2/files/zip-2.3-unix_configure-pic.patch b/tags/pisi-1.0_alpha2/tests/zip2/files/zip-2.3-unix_configure-pic.patch deleted file mode 100644 index 45b137b0..00000000 --- a/tags/pisi-1.0_alpha2/tests/zip2/files/zip-2.3-unix_configure-pic.patch +++ /dev/null @@ -1,12 +0,0 @@ ---- unix/configure.orig 2004-09-28 06:45:33.000000000 +0000 -+++ unix/configure 2004-09-28 06:54:53.000000000 +0000 -@@ -29,6 +29,9 @@ - - echo Check if we can use asm code - OBJA="" -+piclib="$(echo | $CPP -dM $CFLAGS - | grep -i __pic__)" -+echo "Checking if compiler wants to create pic code" -+[ "$piclib" == "" ] && \ - if eval "$CPP match.S > _match.s 2>/dev/null"; then - if test ! -s _match.s || grep error < _match.s > /dev/null; then - : diff --git a/tags/pisi-1.0_alpha2/tests/zip2/files/zip-CAN-2004-1010.patch b/tags/pisi-1.0_alpha2/tests/zip2/files/zip-CAN-2004-1010.patch deleted file mode 100644 index 150d6407..00000000 --- a/tags/pisi-1.0_alpha2/tests/zip2/files/zip-CAN-2004-1010.patch +++ /dev/null @@ -1,25 +0,0 @@ ---- zip-2.3/unix/unix.c.orig 2004-11-05 15:44:41.000000000 -0500 -+++ zip-2.3/unix/unix.c 2004-11-05 15:50:28.000000000 -0500 -@@ -319,8 +319,8 @@ iztimes *t; /* return value: - a file size of -1 */ - { - struct stat s; /* results of stat() */ -- char name[FNMAX]; -- int len = strlen(f); -+ char *name; -+ size_t len = strlen(f); - - if (f == label) { - if (a != NULL) -@@ -331,6 +331,11 @@ iztimes *t; /* return value: - t->atime = t->mtime = t->ctime = label_utim; - return label_time; - } -+ -+ name = malloc(len+1); -+ if (!name) -+ return 0; -+ - strcpy(name, f); - if (name[len - 1] == '/') - name[len - 1] = '\0'; diff --git a/tags/pisi-1.0_alpha2/tests/zip2/pspec.xml b/tags/pisi-1.0_alpha2/tests/zip2/pspec.xml deleted file mode 100644 index 661acf2e..00000000 --- a/tags/pisi-1.0_alpha2/tests/zip2/pspec.xml +++ /dev/null @@ -1,50 +0,0 @@ - - - - - - - zip - ftp://ftp.freesoftware.com/pub/infozip/Zip.html - - PACKAGER - PACKAGER_EMAIL - - Info-ZIP - category - component - Info ZIP (encryption support) - Info ZIP (encryption support) - ftp://ftp.linux.org.tr/pub/mirrors/gentoo/distfiles/zip23.tar.gz - - zip-2.3-unix_configure-pic.patch - - zcrypt.patch - - - unzip - patch - - - - 2005-08-11 - 2.4 - 3 - - - - - - zip - - unzip - - - /usr/bin - /usr/share/doc - /usr/share/man - - - - - diff --git a/tags/pisi-1.0_alpha2/tools/cat-db.py b/tags/pisi-1.0_alpha2/tools/cat-db.py deleted file mode 100755 index 20f8b9cf..00000000 --- a/tags/pisi-1.0_alpha2/tools/cat-db.py +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2005, TUBITAK/UEKAE -# -# 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. -# -# Please read the COPYING file. -# - -# perform analysis on problem instances and store results in a database -# avoiding multiple invocations of the same instance - -import sys -import bsddb.dbshelve as shelve - -sys.path.append('.') - -import pisi - -d = shelve.open( sys.argv[1] ) - -for (k, data) in d.iteritems(): - print k, data - -d.close() diff --git a/tags/pisi-1.0_alpha2/tools/ebuild2pisi.py b/tags/pisi-1.0_alpha2/tools/ebuild2pisi.py deleted file mode 100755 index 27868649..00000000 --- a/tags/pisi-1.0_alpha2/tools/ebuild2pisi.py +++ /dev/null @@ -1,192 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2005, TUBITAK/UEKAE -# -# 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. -# -# Please read the COPYING file. -# - -import sys -import re -import datetime - -sys.path.insert(0, "/usr/lib/portage/pym") -import portage - -PSPEC_TEMPLATE=''' - - - - - - %(packagename)s - %(homepage)s - - PACKAGER - PACKAGER_EMAIL - - %(license)s - category - component - %(description)s - %(description)s - %(archiveUri)s - -%(patches)s - - -%(buildDep)s - - - - %(updateDate)s - %(updateVersion)s - %(updateRelease)s - - - - - - %(packagename)s - -%(runDep)s - - -FILES - - - - - -''' - -def packageVersion(pkg): - if len(pkg) > 1: - parts = portage.catpkgsplit(pkg) - if parts == None: - return "" - - if parts[3] != 'r0': - version = parts[2] + "-" + parts[3] - else: - version = parts[2] - return version - else: - return False - -def getArchiveType(uri): - if uri.endswith("gz"): - return "targz" - elif uri.endswith("bz2"): - return "tarbz2" - elif uri.endswith("tar"): - return "tar" - elif uri.endswith("zip"): - return "zip" - return "UNKNOWN_TYPE" - -def getDepString(deps): - depString = "Dependencies should be reformated and check for correctness" - for dep in portage.flatten(portage.tokenize(deps)): - depString += "\n "+dep+"" - - return depString - - -def getPatches(filename, pkgName, version): - fileObj = open(filename) - patchList=[] - - # ebuild variables - rc = re.compile - dispatcher = { - rc("\$\{FILESDIR\}"): "", - rc('"'): "", - rc('^/'): "", - rc("\$\{P\}|\$\{PF\}"): "-".join([pkgName,version]), - rc("\$\{PN\}"): pkgName - } - - patched = False - for line in fileObj: - line = line.strip() - if line.startswith('#'): - continue - - p = rc("\Aepatch ").search(line) - if not p: - p = rc("\Apatch ").search(line) - if p: - patched = True - line = line[p.end():] - for r in dispatcher.keys(): - m = r.search(line) - while m: - line = line[:m.start()] + dispatcher[r] + line[m.end():] - m = r.search(line) - - patchList.append(line) - - patchString = "Patches should be checked for correctness" - for patch in patchList: - patchString += "\n "+patch+"" - - return patchString - -def main(ebuild): - dict = {} - db = portage.portdb - getKeys = lambda x: db.aux_get(ebuild, [x]) - getKey = lambda x: getKeys(x)[0] - - dict["homepage"] = getKey("HOMEPAGE") - dict["archiveUri"] = getKey("SRC_URI").split()[0] - dict["archiveType"] = getArchiveType(dict["archiveUri"]) - dict["packagename"] = portage.catpkgsplit(ebuild)[1] - dict["license"] = getKey("LICENSE") - dict["description"] = getKey("DESCRIPTION") - dict["updateDate"] = datetime.date.today() - - # version & release - if '-' in packageVersion(ebuild): - (version, release) = packageVersion(ebuild).split("-") - release = release.replace("r","") - else: - version = packageVersion(ebuild) - release = '' - dict["updateVersion"] = version - dict["updateRelease"] = release - - # buildDep & runDep - deps = getKeys("DEPEND") - dict["buildDep"] = getDepString(deps) - dict["runDep"] = getDepString(getKeys("RDEPEND")) - - # patches - dict["patches"] = getPatches(db.findname(ebuild), - dict["packagename"], - dict["updateVersion"]) - - print PSPEC_TEMPLATE % dict - -if __name__ == "__main__": - usage = """Usage: -PORTDIR=/path/to/portage/overlay %s category/ebuild - -Example: -PORTDIR=/trunk/ %s app-admin/fam-2.7.0-r2 - -You don't have to define PORTDIR if you are converting a package from -the main portage repository. -""" - try: - ebuild = sys.argv[1] - except: - print usage - sys.exit(1) - - main(ebuild) diff --git a/tags/pisi-1.0_alpha2/tools/fetchAll.py b/tags/pisi-1.0_alpha2/tools/fetchAll.py deleted file mode 100755 index 4ce26c70..00000000 --- a/tags/pisi-1.0_alpha2/tools/fetchAll.py +++ /dev/null @@ -1,58 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2005, TUBITAK/UEKAE -# -# 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. -# -# Please read the COPYING file. - -import os -import sys - -sys.path.append('.') - -import pisi.uri -import pisi.specfile -import pisi.util as util - -from pisi.config import config -from pisi.fetcher import fetch_url - -def scanPSPEC(folder): - packages = [] - for root, dirs, files in os.walk(folder): - if "pspec.xml" in files: - packages.append(root) - # dont walk into the versioned stuff - if ".svn" in dirs: - dirs.remove(".svn") - return packages - -def isCached(file, sha1sum): - return util.check_file_hash(os.path.join(config.archives_dir(), file), sha1sum) - -if __name__ == "__main__": - try: - packages = scanPSPEC(sys.argv[1]) - except: - print "Usage: fetchAll.py path2repo" - sys.exit(1) - - for package in packages: - spec = pisi.specfile.SpecFile() - spec.read(os.path.join(package, "pspec.xml")) - - URI = pisi.uri.URI(spec.source.archiveUri) - - if not isCached(URI.filename(), spec.source.archiveSHA1): - print URI, " -> " , os.path.join(config.archives_dir(), URI.filename()) - try: - fetch_url(URI, config.archives_dir()) - except pisi.fetcher.FetchError, e: - print e - pass - else: - print URI, "already downloaded..." diff --git a/tags/pisi-1.0_alpha2/tools/graphAllDeps.sh b/tags/pisi-1.0_alpha2/tools/graphAllDeps.sh deleted file mode 100755 index a4276932..00000000 --- a/tags/pisi-1.0_alpha2/tools/graphAllDeps.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/sh - -argNUM=${#} -if [ ${argNUM} -lt 1 ] ; then - echo "At least one repo name needed" - exit 1 -fi - -for x in "$@" ; do - ./pisi-cli graph `./pisi-cli list-available $x | tail -n+3 | xargs` - dot -Tpng pgraph.dot -o ~/packageDEP4${x}.png -done diff --git a/tags/pisi-1.0_alpha2/tools/installAllAvailable.sh b/tags/pisi-1.0_alpha2/tools/installAllAvailable.sh deleted file mode 100755 index f8b7a4b3..00000000 --- a/tags/pisi-1.0_alpha2/tools/installAllAvailable.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/sh - -argNUM=${#} -if [ ${argNUM} -lt 1 ] ; then - echo "At least one repo name needed" - exit 1 -fi -./pisi-cli --ignore-comar install `./pisi-cli list-available $@ | tail -n+3 | xargs` diff --git a/tags/pisi-1.0_alpha2/tools/lspisi b/tags/pisi-1.0_alpha2/tools/lspisi deleted file mode 100755 index e1a80f72..00000000 --- a/tags/pisi-1.0_alpha2/tools/lspisi +++ /dev/null @@ -1,57 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# -# Copyright (C) 2005, TUBITAK/UEKAE -# -# 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. -# -# Please read the COPYING file. - -import os -import sys -import zipfile - -def openZip(fileName): - if not zipfile.is_zipfile(fileName): - print "ZIP file required..." - sys.exit() - else: - f = zipfile.ZipFile(fileName, "r") - return f - -def fillFileList(zipFile): - filelist = [] - - for file in zipFile.namelist(): - filelist.append(file) - - filelist.sort() - return filelist - -def uniq(alist): - set = {} - return [set.setdefault(e,e) for e in alist if e not in set] - -if __name__ == "__main__": - - if sys.argv[1] == "dirs": - f = openZip(sys.argv[2]) - dirlist =[] - for file in fillFileList(f): - dirlist.append(os.path.dirname(file)) - - for dir in uniq(dirlist): - print "" + dir.replace("install/", "/") + "" - elif sys.argv[1]: - f = openZip(sys.argv[1]) - for file in fillFileList(f): - if f.getinfo(file).external_attr == 2716663808: - print file + " --> symlink" - else: - print file - else: - print "pisi file name required..." - sys.exit() diff --git a/tags/pisi-1.0_alpha2/tools/repostats.py b/tags/pisi-1.0_alpha2/tools/repostats.py deleted file mode 100755 index e4bd9d33..00000000 --- a/tags/pisi-1.0_alpha2/tools/repostats.py +++ /dev/null @@ -1,181 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -# -# Copyright (C) 2005, TUBITAK/UEKAE -# -# 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. -# - -import sys -import os - -import gettext -__trans = gettext.translation('pisi', fallback=True) -_ = __trans.ugettext - -sys.path.append('.') -import pisi.specfile -import pisi.uri - -def echo(string): - print string.encode("utf8") - -def valuesort(x, y): - if x[1] > y[1]: - return -1 - elif x[1] == y[1]: - return 0 - else: - return 1 - - -class Histogram: - def __init__(self, title): - self.list = {} - self.title = title - - def add(self, name, value=None): - if value: - self.list[name] = value - else: - if self.list.has_key(name): - self.list[name] = self.list[name] + 1 - else: - self.list[name] = 1 - - def get_list(self): - items = self.list.items() - items.sort(valuesort) - return items - - def html_out(self, max=0): - echo("

%s

" % self.title) - list = self.get_list() - if max != 0: - list = list[:max] - for name,cnt in list: - echo("" % (name, cnt)) - echo("
%s%s
") - - -def scan_pspec(folder): - paks = [] - for root, dirs, files in os.walk(folder): - if "pspec.xml" in files: - paks.append(root) - # dont walk into the versioned stuff - if ".svn" in dirs: - dirs.remove(".svn") - return paks - -def add_deps(deps, spec): - a = {} - for d in spec.source.buildDeps: - p = d.package - if not a.has_key(p): - a[p] = 1 - for pak in spec.packages: - for d in pak.runtimeDeps: - p = d.package - if not a.has_key(p): - a[p] = 1 - for p in a: - deps.add(p) - -hosts = Histogram(_("Source hosts")) -people = Histogram(_("Packagers")) -licenses = Histogram(_("Licenses")) -categories = Histogram(_("Categories")) -components = Histogram(_("Components")) -dependencies = Histogram(_("Dependencies")) -releases = Histogram(_("Releases")) -types = Histogram(_("File types")) -mostpatched = Histogram(_("Top five most patched source")) -longpy = Histogram(_("Top five longest action.py scripts")) -nr_binpaks = 0 -nr_patches = 0 -paknames = {} - -errors = [] - -paks = scan_pspec(sys.argv[1]) -for pak in paks: - spec = pisi.specfile.SpecFile() - try: - spec.read(os.path.join(pak, "pspec.xml")) - except Exception, inst: - errors.append([pak, str(inst)]) - continue - try: - f = file(os.path.join(pak, "actions.py")) - L = len(f.readlines()) - longpy.add(spec.source.name, L) - f.close() - except: - pass - nr_binpaks += len(spec.packages) - nr_patches += len(spec.source.patches) - mostpatched.add(spec.source.name, len(spec.source.patches)) - releases.add(str(spec.source.release)) - for p in spec.packages: - if p.partof: - components.add(p.partof) - for x in p.isa: - categories.add(x) - for x in p.paths: - types.add(x.fileType) - paknames[spec.source.name] = 1 - add_deps(dependencies, spec) - name = spec.source.packager.name - lices = spec.source.license - people.add(name) - for lice in lices: - licenses.add(lice) - host = pisi.uri.URI(spec.source.archiveUri).location() - hosts.add(host) - -print "%s İstatistikleri" % (sys.argv[1]) -print '' -echo("") - -print "

Toplam %d kod paketi, ve bunlardan oluşturulacak %d ikili paket var.

" % (len(paks), nr_binpaks) -echo(u"

Toplam peç sayısı %d

" % nr_patches) - -mostpatched.html_out(5) -longpy.html_out(5) - -if errors != []: - print "

Hatalar

" - for e in errors: - print "" % (e[0], e[1]) - print "
%s%s
" -else: - print "

Hata yok, tebrikler!

" - -components.html_out() -categories.html_out() -people.html_out() -licenses.html_out() -releases.html_out() -types.html_out() - -echo(_("

Dependencies
")) -echo(_("package name with italics are not available in the repository

")) -print "" -notavail = 0 -for name,cnt in dependencies.get_list(): - if paknames.has_key(name): - print "" % (name, cnt) - else: - notavail += 1 - print "" % (name, cnt) -print "
%s%d
%s%d
" - -print "

%d packages are not available in repository.

" % notavail - -hosts.html_out() - -print ""

yuVW@VWJ)LYYx6jsD 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%0AW(nRropS0k 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 ZcIrf?#QsY%6F}?0NQ%mdlz!0j`yUz=z25); diff --git a/tags/pisi-1.0_alpha2/doc/images/pisi-istemci-small.png b/tags/pisi-1.0_alpha2/doc/images/pisi-istemci-small.png deleted file mode 100644 index c98fc21abb0ad8025155f20a5872f8f9f4e2f379..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 40089 zcmb5WbyQVf^erxmc%{3$K}s4FkV{H;he&sKDJdWz-5?;{-5}lFE#2MyHlO!h#|q_!@qd(0_n53u)>QMFV$YWfboaC1bd35@VCH^ zw>IMH_Ag$b>4LvHFh@N;dSK@phtJX?Zzf?eF_~a15(0}}ydZt?Sy)iXWp01bSqW1W z=fyEsMN?_1@~``DEnIl=*0*3Cit=A2n5! zdNvDmS(Ks;8VlC8tnna!a6B3qO1at`B=|`Rfd$vBROR*qKVDEmpFh#boAR&{(KNA+Viddg1~@J@#n)oX$o7qI;g3sLE9)G7~o*$g1pboUptR5;WzAQ+Wh=- zJ44=mc>|8TF6XrRsY;8PMvexZqy+K?v|FiscDp!%%>5-258P2 zKK?*>?WekAT1HEszdqH0?dkdXWAiTLTD2^7s?6>)v%j^2f+ZHJ`Zp+;SZHz{hXq0! z9NzoS_;6CQv6)WNZ*itVii#YmrBA=5AkD>OWVD4h4-oSeiiRJ;mc6zdn5$xL?r~co z2BYx*bf0GCOSJ)TwoXnW4U1eBoQn%u6_ZvvU%#RWRxTr-Sy(vGVb+GhT7HKLQc__x zZFPJk9-mPoLSNa!z>2B5%8!UmH@y4EYaZzvesEz`m2`D5#vpbie_%gN$=_(F*YZg@ z=Jl4`@uaBVk8Y6oVj>vpf4qI$}=eOCTy5eY1J(V z(h5VQr`JpKyr<1@^SpXaDc8MuP$KP0Iw#kzHJEV73?`Su3bJ-L3|o!HTZ*scR#0JH zeB-1eC#O0xwK8>x#UrbsTxZg%rw$B84RfI^ZW2cv&TCe>xtDWWJd>JvtZ2~nBo)>nfnWhVJUw&v zFLpIk3sW%9@l#o+@GooYy=ME}@s2<)>$b;c+#DhTar?{kdY@JEvoP&Sw#%pLob>en zLb@@dT%p!@n9mP2fQ&OqPcMCE!?1VFbI$%Ml%N-k!CH5NeI4RP;Os0`j!ft6T|=Yy ztl{0|e1Dv7VgB6xV*$TCwbki*`|PYaJ9}-~YQg8M)6f>(ov7%lnV-`VPVd8I@Ur^R zVq*LsJ%V`}D_UC2_da5#KXASH*PokfqE>(IVPwPuRZ~}A+#E#1#+GzGs4tN7XzRB7 zqj1ycnnS|Zr{D!e-7aadI+(E)lRkKr2-8rPz@)+cjgVO2@poW%y?2TTwIVzF4}nD? zF#;MAH7%_L^^skCHa5Cz7p!E@e^Y11H*#RIewz*@Ud+=KAh6@+zCNim6$||d+uVHF z*Ql&GP8rq9Ll{tAs-`$j8`F2=;pFI;2^GxEE5k#~XDyhV4)KG&_xCBtE89MwmbE1& zHX`CRA^CtkS)lsn^s`*LoX4v0dHnm7$;rABMnTdqig}oL(&ryv$Ls#gvpud^93QMrYAOp<|e2doDy$+zEtddB*thj-E^(Vm!QS&uhj|& z?(4UvjOf|h6G0C`?0&DTj2-p)?#^CaM8r2cTS7ncPnmq%%7|-YoNIBugg7c}-5@0W z$B$_K4ABn5nmHoA5pl<{{WVRd`@dT?^`U+sZ<#Q@d|AK0TW>JK%cK(z5i87 zDORiapABxy6TidX7n*(}VBo)gwG#_JbU0Z(J3S3x(w1`G$1O;2Ri^!6zm-~~?Ov5G zD2$q`NV6vJh?}01Gv$Hx62|!2CT_9eXme8~F~OAFZz#=MMVA>Fvh9N!+5a3pRkh9_P?WS=2+_s&*F-@Y+%KeFPaez-cnKH2v1!DnPV)b$Q^ zvS$;4FlsujJU*N!CQ|!D_000iEa!0%5E%VN*1BgG8>|jICp>B%RpzUP+nU0odvqmvI?c?~h|9`5f6iHMLC6jigbi&@#mOUp+2 zY`%QeT3~+vUa~9H{_bY0A|YX;Zzt_zFQ4<_d~U8teZASTw|7Q$^;qI;KLz4D*;+2I|osGMK>)ryVxSzGW zs(Rw(dKi?++?3o$+}$oGyAieRf~VJTtdhogH(x?CwUCsrnS}zT?SkWLg(-OiCYh3& zn*D{@*Q-O7tgMT0k}Ymt{He(xCfzU^n(=8>0*=0z!R-0@y>R>ue0+SGD7s>;&wu2O z6WE%#2}J{uc`<0}b(@nv86|EO7CG|qG0G(W$Sc92qQc^Hi@q4-((Xl{-x)6hWn@B$ zf|oa^va;CTIEq4yn9i7xXKZpnN{IB|$WAnNvri)a9T?D)HI(bSBOdolQ+0JuE2}KK z{egtV*Du4eshtlOmU??T-@f&SkkMm3oedMT2OcHza?lF2@KRIf+`Q}#KguW?R8-wM zKU{nd;zfI}3ZoQSQJ}eHRfOHIXfAw4RH?DGDLA>UNh#25YjV@USW}=*Z+4U@DoD%u z+AUnOvLf)Q2DNNhl(KLcbDQs?=1UFCnnwKo{**trKd-qYuJC?=qMV#`Yz;mEm9lbM zPtU;lIcxd$RE@Qm@?>0<86mQ^bm)V#!aA4}7_jmh8r@&yVk}ufqES{3wT^}4DS3eK z!Jr{_yJdeG%i7Xn((i!8wM^?MYR*?WA9*6SLtc^zI~-bMBu6yTU6*)0aO`&kZBG^i zs%e;EVXK7WMbSmkMeAQ&Oh&&VkEpD!&a16;+Mjs{2)N%FZ{nTptx;>UzhAJR*R{m|&tQx%Z z9aq>w;0`O{PWYXl{$}MiSxsvaiw5I*4vtY4yVQUSitD#mps2lwOPmT_D%P*q@!&V! z95{@kc5UP_^lZv4SIEul6@hH5EA8&)f5jEej*X1uWu0ALS28iPTee{KMLoSd{9?f{ z)BNCMV2};!PepRRkwu}`{>(%Te`k8*@D)Cy&|s2}PVKS9wp>__!Ub!yUpfN%-JqH#va@MVj2e8zwQsQ(xhJO{SzA z85e^}WmRZ=2J5z2tpSIP8-@Yf=kho*)*F&Z|LtsAYt^DN(-dpz zC*15`{j8}1m4^#SoytOxAU{XEVd4oD%yC=>MheMSl4A6sVW{*&5?w4>+S9n?H&rGU z(H-45g<5#o8EwfQYPi&g2tLl}O&2%F$OKeZ*CQdV*4EzZH1Yl#$HAI}d(D@~Q{k?l zATKUHaDAektK>048o2D$P;S^iXxQ%oGB+|&Fd^r_O_672l(f-QNegux#(1_Xy#NW< z5FLzKATxrxA|YYs=%9oKt*_6wwRHt?UC%q3%IB<` zAZM>cQ{m@WbsC+! zLg7%E*7xfft%rrZHy;~VDJ$|-$gLyMSbcEyhq(TEynYC)-V=_;_dGk_B)NkfP^C49 zh(ku$^Fr1l$v3u&i^0Weq%O5z9#z7B=naj^c}zB+7**~c$;hCkIlF1^6dc8O%Z5#d z=wNVLjml`V5paiiIyfq4wVJ=wb_vY=Osc+(to#%;Tn|ZunE%GXd>44vZ8oI60qmm4*I^calK6`s}ca%O(Cq*qSD?2#f z(7w_h)#CuoQRK8b*R466>xpnTGfSM9P(wzB>k~H7OZV4{59znAs*(ovgh^-LS1-P- z%+2O_!myu7P2;i6V9dkwK(&NeDyzAPs-q{O_=D3zb@`Z+!+nlVx+KncdtgqXw!7nE zy*YOn9sxou6VY`^kj?IdmAt%qSXhA}tBBG>1}N+A^}EWDiDKvHcBRt}Z#2?JhKF0H zoi&}D{C#~}mzvf9VDj~ST>>4)i)7 z(p7r=Y5sTVUm)Eq+MJAkSM_z8rzO_!6yBjiSzim1vUeqyNaV24aVOnQRtlOPDQDt_ z-3>pyBAbv?ETb!(K8Tus6}ztz7W28oxSDBy!uq_&HHn3g1~WKvfgOuOn%mWrI8`WT z-tC+Q(pk1_th3ORp}0Ic*uBJEA0eSXFzb~vxP}2cIw7a2s|+W27p$gk8r1ng_YwWQ z-x_Y%+Mqei>z6~f7wyx9coHffIlW;hLq!v~O0G}@T%yvVVq$Rv+^(t*XUBs*DAOc5 zHFK35Y*=XD-92Nq1oZB*#|O*X^D^qVZ)>=93IJl1xfq|(Z4K=3nNRiC{F$BGU2J)Z zf(oL-W{lb(jt7To=gXmThpljP+8NN;bwyBU2O`;LkJ3Iq7Q+hC;>e``L%_LA5@Mi6o_x&^l0w@{hik)k_u##Dnj zJNbjQ)3-Sl1cXABs_(vco~rV~aGwI4e=n;ERpvFUPQix$2x#)AVxnwQ32MiHID{5R z=$J-`pa$RW+QB8gi|fzjCTh3U9uGq;81^jlSqUIYCiW*H!wgoH2#v{Nrw&<=#SJJ^ zo|rkZT7cEqNbS_H^7LZohc3fKd%q?oV&<2gACkKJ=Ms2a<&}D3V*)r4@P2}y(-@I?H{`SrF{H))Z)ueLP8?4 zC|&_1(yEqLU~>=q5naMoGr1W_H}3Z`dxQf`0xl~%%4#ZZMmeq@OF z0r7v?O$XvC;Heb3?Ioa8u3;Jjq>m=N2a?hnE-#O^rviI7^lVP~qWYy4H`Q zi%hd0y$F4ork+`vC#;mt^opgi1Juw~1;fVu*dPgGxgF~xDir)ZZ(eTy+o|KPxW)H` zIdaR6WNj;gEzbY3I^Rk#1W_+8zW!)`gBD_l3JVyTZ=4zV64&i0WQ+CcpJL3@1IIqxjRNU%KUwWak)M#X1Oemo4PEbpigF~5Js;(VoiZu%G1hXs zOJXadBboHE)g#?{ta8htYV*EF#t>EvS|gZQH;2QK#HAA3W$iF8+75I!hh$) zE+YD%k5`?jJ%>$Z6}^P!I3-srOZHam4vQW~+h-ZV4cL(3koOK@!& z*@kUxX>tAy16_Fg0iP!tlw41{Z|yky79TD9UCG{IH^Ar_mvvt2wi>!j*D#@i>nxRLa1e=5yAEOF9%RhHXd;xJ|Cp-U-lQ^@A| zeF}snuhYEVIiWDJCaoa-+}(j67BoeA(u#%Hy$JV{0_^fH$!iO&A%6vkIxk8r{{+?` z@!>XPWFM7WZW{k~Mb+lR-N!o5i~tQk)R@+DVh)7WS1+!A%vl%6`+S0sA^L{ZYHTf$ zB|1k?V{rwEguX>7(VDAx>GSG2Vr@;fljL@OW)vE5Lo~>UqQXKQCqaO(r!{pA5VL#M zon|$F>Vg@p#9)?6ht`(M&g+K^D8ysWgqixCM2ALuy9OPhL0iVVI_fj>7a+%$@792?11R>)a6^6hDQ>X>~@E}eMz;5?e^EN%|ODu^#&hn*7zyh1i$c z@ghm8IUuNYHSvR!zMTuI=yL|UqIJcMe@7B!Jv8ecUvX9HJjemE1GfXeCsRi#FE@ds zjo00GN#oi}yq(+XI7)K@O~#MMFJ3}kxX6x71Ag^?^RxexSiPRX*_Y~ zd-4Y0Ox0{MV)kNT6=Mekc>G7m^7G^1WJUWt z|2=f;LqI|CBPlDiH$X!h=Q3@)vy!9xKPI@6h?BFPvmh`#JJx_2dr9ExPf~5~=ah#V zt~EOy%kl)PBP{+it!AGkS~9>wf6~;qECQS2{4_Ny`OR($`{bzx*tn#_vgp^wi`W}W ziwi%i7^Kr=Dtb=eT=U|4F|Pdg%$8C4^!>(u1;dc(xw&jWP{lWxroVj+A=9wTlrV$g z3C-l4*A6*LmqeB+G5ghv28E~3>blr4cfFN4)X&PvE-8`7QfMyzRCOtQece}vQ|3Is zCHPa>h|FN#jEk9#|Fml~6fm|1onqt@uZAlAz3cmtpU?VbI55y)!>yjEpBaz7hFMMZt@gA>!qpi*b+Jz_r0gUgvtmgT8N z*HJXDgX@m5IMGbf7tJ#4_#&h(>hR}>#%qhi0Rv%2whC>z6P(wDtzt5M;Bu(9G`jFlWtlNQ9UYIYDGrmGc{i0=?d57ZZWjL|)^cZ|1=Yf^DxRICF zD?-f|jdULxyq24zzkSox&=BOZ#)f`+VWLO{9c%S9eS?y1%dI3JpzrO?W^9ba#)kES z!(?l0%#4JOzR8R4X}wp<=bOum5p0Z=3gZ|&ysAC{f2cY*wYYe809=J6P7xj(uLPys zYuLGzTQ^f1o30=XJm({9E_QKUf}~`v!L?C-lkXxTq0Bn${2uYMDikW3^RR(P*BTfr z5fsczZX_|JzqDj}Heg}mT7PQ;zRiy3Buu`eK{V^n1UqM+mNv%{X9$$Zy5gV*@QP_^ zuLwE+6-fDMXfPllWuMA&Ch^+*9_@+9W@1Xj#{TFhXGXt(408|`3~#wAJRXEm6d6@O zK_OY9%gk+0E$J$4V$Hn0q$a1m&&IaLB9-OtEct`+MG+Gky>zn4WKBtP zPEJ_R+XZ~se^dkn&7nx%bL@LRf6g5rw`tY{Lxn0x8D3OY3FD%ES=loFQ#5E5!>kMY zTe-AUuBucn<$b2UtE(!18Vn4~Z)qwhXHuV$P>tn_`w#H1$477A>(}LIAM5;s^dP<= zTF=7FNY}*V!?;8RX~}L|-pr;cDnj_vmMjw1>QNXK8q@*K@D`=%kta!x&VrL@_3g88 zqwyg^93Lmf@yhHa6oU2xCn1~A@NYUJr@XtN8BL1nBLky6vIJ&F^Y^4^?>^XtuCUwn zV?XsKB*Yt3v;VFbES_~bFmNiSx@wM&phcgLv-h|dJE(Hj!uTspf4POzS{m!-X|&fJ zVgF~cyTkTB3j?!!nASJXC_z{1^Ji@a)D&!OG{TY=t#NGMpPpr=i+g zAVdDN=4$GzwDM=j=lKSFP!v^E(&E*Gk+;^Bj0|I<+Ue*zJ3FgLDsutj+Sr)y99mRl z863|1OJW1{tR#*&@Up4Mh|BVD)31Eyv}=rd2WR=uxyH#x<6P#B^4WeBwR&DV|I1&H z);IGtN6k4~aCR<__v7Q)L{x!q5NGO56+7NAnzi`}h2W&QPtP15V~TZ8VqqbHbElNS zKhTIBNR;i{=^S)jkAc$|@9(QBQ%Sxu>PY1!s1|$>Ix>Nl7L(E z#-2F|iLRWMma%2gVp%2=kF9dduXfiP{+Zp~E*e?8-Mvl^{`nypUB#$(=$|De_b}m2 zCa1GUYzot0O44AMD`d22B1oi5@}v4Dw=t2ZV*>Prq#u`yr~48OoJ01|pgb4oc!Fxj zxY!>>X{4*+Fl1PPz;q4EX@yvlXWHm!{@7n|^s^j~E$l69^7i(A7?C3iadO92MP^31 zXz1Jrcc+rm(_0`FYH2OaRkc$~*@!_HwcT0=q$s#u%)GoVehbb6F(x#$SdBqZ+3;YA z2o)=a&un5TBLk_v-Ue$KF5wIpLS~G_3C$HHFENSyjMXt`%&c21-2EPA%~KWuUO1Yg zw?|K!x+RFgs0smzqB8?mR>-wY9B|k4c=KPMMn7ZP@vXQ~G|XerM(ojGy9zy4&M@GbR}1 zlZ)PRr1yhYv+7ptY=PV zkEW2OJ|n1q@1o*Vs|+QDkCvvo!$jhuC!`bC(+M4~Z7AdV*N&(xpy?|C#dv^Y4NtgNv7FrQWQN?tcNv!Nl;^-_O-Bcb;GTUjx&SJp?!++l+$ z?!m*xBs{FMgM&&u)8PV_i#11HqM|o{zdIpdpJ$HJLdXaRwE6fr{C?H*QjOIw{U0yD z)%i~C!ySoumfgE|DgXYNf?RfUqnz>~>6aqV=qaHi8d3>kj(yR^>&F2|6wztAjn{lU zEXFcNTwCR(BOmPy`pij)xmh9zOKYZ=%k=ln_1!PZ!@_hBVcxu`#28V7doeP%*qD&` z>$tqNLY%V6iE*bZ)FAzMt#S1D@AtrmyQd4m3Sr?tT8-EDtDRe_#gx=kSP$k$7-yR) zwPfyuE8Vm5JnwV}m)rkmzI|2F=P-Q@}%2V`JDQ-L$thm2Fn^mun0m)AngM#7$ z$Xim>L{OM!r)5uPkE5kK2w9n0^%ue1mZwyZc==ThJOu?WqG;+!*!zFtbz?K~y6-CO zKR#SN+&eModI>UF?;!ok%cBA+*HO#8<6iO2wT0)|*__7c@k+x`iu>Gsdx0uOAkw_D ziprCqZtLG~B}Icm5RmvsCNHk85~%o^*r~GqNE3gN+2*VW0+QrioO4NW_W zMrTRO?Sm&b$Dco#HD@IF(GVDuEG;c|)j}I0zPbA<@8)Jvpvh*6IlE1_G<$xiZZZLd z8Kj!muSF~uwx8NjmiidBck1+(8Wq=Vlw;&b<8kou15xWeKS-0tJ74ZAS|3R#CJJz} z|9I`=+8r(yrKr=A%78lHUh)0B*7l9)&zYk8@fjD}{t~Bj1-hlBDMv@?^@0&VdQatz z<69oQ-rSTl9^{o*JiG0baCP-fTXq6F1qI~|V4VA7laP@MDywkL_iCt^m{7>h?~YWR zj+&EVl*v#BFd&;EB8GIvIdaE&iVb|Ok$2~bQ66hDb*2|ta zam&wV*{?fUYI3kE4<%@qmWuWB@BF}9d^l~50L`^tX8iuW^$iNYRvoJpWn|H-|7V5IYoaTX;hir&T;teNdgX!JM5fsZs<_vFBHi?*{|(28gky=FHm@ zrh`C->^Zg7P7x=Kud&}sp;HXo-eKIIvTO(HX&-q>)&{G%-;>);4!14&W>Lv(l*9&k zMD#sPM9)}GPMnU(Eg{y{@oxOU)tu$OkTd4Ee zqbupj=Oz<}i!jSx^S;oVtqx^X8Z(RNdrzs1-CfaB zE~=Qmf&zl8Bkxjo=fA#h5YTRoqwNZzC%0ah@STH4ea497CGK`|*amZ?BXF?R@3OyxS+KVp7U)F!(>Dbz~Dbhb8& zoSNdSPR%Tcp?=mMJ?{=%F0Kf1aSJn8=X*@bf_l+uawe0w#Kb9|HvEO>YnGp1UccAi zd;QDXYl}M+w#_Zo!8C7Pu9{QboP|m{pN~n?(;+G)&B=+kFv|BR#0G`B-c9}!ZC?iy zxlcv{b%JyA1|RlHk{D$bb#w&0A9qmZ=3@N=bME&fbkDZZR-6wTT@Hl!)0*_!kqq0B zj(JM(>$CzgGx;ych}_=5IUNB*l+c@l@Mb>eqvm*lr}Oh2v8`bNBGi%2$;ls5e$Q9b zz;e1ira%sq(eaH$#|XK-dXl7978V{jyQu1BQBtP*=&-#425D$0fHmPGOR2klCM7#N za`$_}?)QlDd6@JuK25j&lM`y0qIJM}P}$g+o-g6DDOH%3<{O5LLPiN*;coF|CJcVqpaY1-Z$MWKI?^3wS1m>*d~{826xxp`fr45$SyT6nA~? z1D5V8E)MdV8)(e)^%p8nkJU%Jw_{5>0!)WELBc?^U7M{C;&qz1I4Lt%rSXr9tRGCG z5Dncmu(GADgKh-})0@jZA%q3>W|5CPwJm>2wdK)B6N*)&P|16(IlSpW%=<8=n<|t;%UhPiw z#zR}d?0a~)Qc`{kd?f~EeV--Y(b48wd7Vh4%Q@}i<}@ZuPf#+_=JMG5Tj`CG?Kve3 z?^cSnT2^;zEepa}L?LvsSrbK18tm!I2i^?^SK7Xf;KIV--QC(21Oo^elp&Vh+*VrZ z@d1p%JUf^@ZEcC~^?8%xbBzsSxLSO3(_1&rX4gxzvZiJ$Y$55nx%(^a&aQ46PDfU) z24lF0W@pxdmlpo03d)8Y9G?YbEhRL?{cdh1LBI?oT`^zX?Kte#+FBK;`185^T3OS- zS{2zzV6z1Z10QCIiFQP;uI5t@`u(sUr3u;mvIm7FRpfjb=*c`!w%>txXl_ z)cuu%#g)TFoaqvptu0mCx}VLSmO!w>L!@_|2ngs}s*lMp8cgD;=nlrhd3ex~dJ+~f3=HUsjh-+y%{xDsxBd6&F4o+N3SAw*U_rrf1J+I; zDu3i-G&95B8vGE7s?l&cmo5~c-HaBJNDJw&zrLlTs(?U~I=YVyH_@m2KuDrVt zH2d8<#2trxH3kaE%l6WI`W16~I%X9X#L*okL%yvf#m6Rg5aUoL)n7qH{#)ex`{y~* z?i-|^CFtFbN!O-|ZWikkqOj;oq=&zb8Vw`Mt8TftX^{Gvu)~LUYgL&YgG5V$39qTi z2P{QGf#kCm^}fE%At9s7%V=w7v-Vq;>g630fJ@qhrR{ z81DvpaIo3Sm&g{2ZvC^fa<13F-Sv}j$IPnuV$r$VdA~OF8Ldetwc#-G8k-k@t;Wr9 zCJ1BHpi|p0%cjlGx3x9g{pv6`Gn3b9;d_4^1}aof?*xOI5FNt(_{fxq(#TckprK9Awe{gk_P9*g@Lu<`g%q>dHe9t zDj2oio@Q`za%OqiAKKa-w&;DJp@6X0H&l$v>#)`2z5_(^f83ld$3yY)gnn`=DmOq+ zS5%br@Yn}dgkI)TmDbi#+yJjf*H2H6oMdG0`>dMN1ekGI4a9_eUDm}_0qxrzZm;n; z1@7!<`H7U0GFuF#puRpkUC0^KwhA?eQymeo8Un^7cUN_K%#KK^El_U`R=;kq=v8~% zWL@l)M9}2DQ$??_ZXrU8V72+J4Tb7``ujaN81A%?{cFczMh3pF?j3+SG0t}opP81Y zR^TS%;Hcy0cM3%2OXfL?m!hVDd)a%!XkIr?WXflY7VR?I<&h6WfLs{#xS78!}tw2`kffBo7K74?zNyo?mf{Pg$o zqzvJ0Q14(-bU&7~^w`gztmS|IWOt#57_LX$Q*$N}qyBR`(z(6Z`wS{%etvoMG%ST! zeJT%z7?T)7tf8^$y*bw{;DCKT^}*q>2KpoS$FY-n3$(Y;vj&HgPILM2~JG zEuZ(}%}BZo1lUtYXK3k|iY?6|1N=G~%_}r3rqsx#WSG8I?7W28(#DvW3@X-^L=VZL zk?D}5z5Yce14*}CTR1%{C@-gvm(lbT|Uayav`?H^Pb>00&j#q{T zi#_|`kmhtWG`zP**2#njd;7D}yorCM>S!n_nH%R!=S2bIUBube6(e09A1G45DCT`% z#vGsr21dVI=MlJjU9d@XZPvO7KYZ}?Aw50K2LLv*q{I`5gMmUQi2La!Pt8YtAJMLxBI0i13_7%7*5QFKA#EA}G>WLmVET6i_0Bc5 zUVJ<2z43y+`^(w|t}S_=%dMATiHMfK;0&kX+@ExZD&`m^bDscHXu0DauD*UNc&b-$ zjSqDCWol06CQ9J>jg0UyXxx4hH88WXhFIjq$Es+k)7@Xr6CW+dC&}b6sg20W%1*Yo zgJikIWkm^!N0AuHJHKB^NuR;DA?e1$r&k9JB$(`mGc3k-vA`+#8rGq#asmn|Q#%Jf zL^X!Q=4OrIRBwLw*ySAA?sgzuI`mBy=~q@N!6DflpWgavY1`T!WJ|BG8M`s6v!Zst zfA_AaSepwL&Z(wie7!P%+OT2=plr{g zkS~5C-(aeFc|YbU@tgb-I@1^jBjfdWv%fuh$4$r^BayCfLLru2c^_~9^UC6b!^p|S zBJgBsm9>?at5#Sy?_ADX$AuHQOR4N8^Ii<2w2mq40l)xkJiWsu)m4*JA*mqQ1P0Qe z;FXV${8}zb>YP?n9K*-Z^K6Qj=-fGX=BHFrr~Bw~OcoQIlwb-xLO>S^Mdfv3Fq_Qp z@9KI9s2EpQPtT?w7G#&}pP6EUK`~qz+|X)D6wgs4C*O?Vz*?t!1fv3w%))|J)S!`p zq4KZJ@_c$dLBXoPKs@z zjkdP_?e3lexp{c2^1d@A1*x|aA0J+r+*CUSLdI?=F}=~x?B-H>tZR?+v$64>ariZ8CE(DYh$JSE-!DT zs6!jKu|cL=yv~tD0DyXOz2_!uuV{Ty~pF%97hZVFnh-a1m`9m%w zB&ebyGF`m6e(|fI04?d^vKfMlh##h3a&ZX5pJ%_-tjw8GQ91&Tkff}oB}vQ)xG^rP z0g!1nPSoJw%u-Xa4Gbd#1J!}0w=b9qQK&tzp|&moJfN95s1AuOE$>`&hm&`*8u>Xn zCb_*I=iA!wu|tf^@<%Ijux=)9%-hh!2|7F#BAl|Mq`w93%C?Ei*=A#Txm$wN$rQoy z?d|=KAFlAP^{k3#@9m|fWBA;=wlqcwqd9q;ZD zM@4&IQ(LP-tr#Dl%a%}yL?G&MXNi)hWMx2UVipPbB1P9K#<+Lq7n9``@80^byqBk` znF8^DxEkg*>$^4P@I9=N~6Y#J4ECIK&t>CtZS3e$3RiV>0>I?tn=3T z`U@5+wo;>_$kg{z;?+QNL{pP{Hwww2RF2}-01qBIx{!+f8aGo^lSS&>{U%2Bp{N)$nBt1G5R$Ya#+vmG+l9eFgZb(*O!z5}NhNxw zxOdT4B1;!09T61riHQpVaA@i<=H>-BXc3Vq+NMU?dSCvwc-#bn1!04;uAiUqn>PZ; z2uYK6319`*yC9@v(0Ead$kY1xfbsgJQ-%Hax#cPyZdU@I&h~5$G@rZX77CqPp}(o-ucT?8z_{GhR&6pL=Wo8a93c z#CUXj3v?3JHBJ)A^nZiS-}C7uZ*q!@&H6Vch6_2CC>(4?aNwz|8Sxqs1b^q#r&n`^ z4`%mo1c%_P#bmmjmjZ=!ovpfLXL;FZx%n;NVHC3oiwn%5khlKm5#nZF{^pG50c5@5 z8!$Kb(cV6EZ#t@X+`+{q_%tCDv@g)p?^2=H`TNJ7k_0q;jDCY$saa!nU2f=WwIKdz zt-OT^Z)KMM-pOK1yWwR3vLkf?9W7?EL}$!FYks~TZU7?EjS_uos?Kdm=v`A&5i482 z>&I+6-`=SluxQ8ua=%e*g#TI%zYch(wr56tHOrUWE;eJ|UtypS#@E_!)!w>Alw*ra zhfX#%Y?K=|zd@0akz(+;WO!;JjN_KcQi!hM;&tX+vBOJ9FdXz9_*ymLt_8RVKnHbp zazN6%M9WSCLL<DN?##%8QXqUvJqSXx@5;R~8%$Qp$fiBa zw=cfskB0wkZ%-!uvA^GU<<0pG+S>JVrx8$nKtamL(8xPayKAqmcI%J58A@{AeSAO^ z67u@`b!9B;m9}901ONbFw@lBmmBN zrR@<==J|ydBV!7A`RMfh3SM4epl*ZGOZ;&rqFdj{P??(>_x@b5g~PmsT zL1W78^~_^U{u$t&BA6$N3NgWYIx^Js;(UUE5s---17OzW$aFmyb2VGr>*GZLvuJ2= znw)m7vj6@ij)+KjxV1dFsLC0o1v(<|W9oM~5VK{Ubn2nl)pHh}S@{B#!{xcP}G@-mcOxAi0o;8^@=o`8heKCzMk5 z_vIB84IggNzJKoq%4=2CWC74LL;4%spTpjKXYBiU4ha@K74YPbjnz9l8YU!av>A;j zwd}#aI9ARq)T%4@A4MTvI6ZY+-g+YWNPBWRW@_u>#$1B@R8NQ;7!j$UrL#0r5Oro3ICmh8~vc4ZVa3?EN#8GC$wj`&;M@(P{M7y1`6I zTBs6zP5D?V=b-(3^?Z8(PeVQaxJk62FX6|J1Na14Qq)E6&IM=`TA;9kRGc=99b^Ey z7l3*r@Hi80arm_BoX*RO|7-9eK}ut~*a+m{?IWX+#Rg4y*yLjE!Cs@~xw%HrwnDW` zI=sOOSd>*8T=XC*Rn;QUv?QO&E1y>zi+z-ncAF4y!p(y>H@8dX7cXU2V+H89UGA{F z{Bn>blvKCMXU|77Kg`c;!~3HXhx6F{_5=)u621Rm_T62>_VzIgA^`SFSs{5@St@7r z!hUi(Ix!_BDW^L4>L07h$2M)YtE+DTYyfBdS{4m$eYhv|e)8-|3Qdida=SW=HRz;H zkx{my$Ae{NmJ|}=4-o;h+Gph$I_!RcnL&@s!h+QOgLcPPya2LOJls=$SYJVVXD*68 z9~Q*56E!|JH9*TKE5(CQ-8|#Lgy(m*vIeq=xjE744cohS2exbVBPN)D^)br6dAM3K zD3a+8tM`SQjUoYHUzPNqo0|r$`B-$dRgcje^dY6f`# z2#E%2TKasnWogc}_Znw;TY{l8=1^7c*S@OW48u*0OBdTT!9N)soztM{9{2o4Ww8yk znhlq6ogvFt2gK&n{D2A1>wF83!%Te7AL4e&;C;9dWjyRP51k(!mU5j}ppH74cYexA z8~W&4ey&{u|49=lQb$`g6x`QI&H{N4n|dVmE+^d#{6{b7H#I?>z7PfNQ-k#`kRomZbua zS29GlK*J&s%)#1;L1+J!dMOb(WOZ2JVcFs*=*@Mj8WaAprZ$|aQe+kdxE(G_{DdgQ z&!~P038LnEw+J4@j5QmtKohXVctpl3@M+1x0~Z)4oEs8wz&?9JfZOx`Zxi;oNKOfr zXtW3+$SQI|IEvwN(&fdy#C$&Bnle{u!M<{ytofgo7tv^6wX`TIfg(L|gY*7WJNzZ4 z38Mhsv!^sXDCjn_NFNXvQ_K*&;T*hDQ(cd!4!hOQp|lFpj)#M3%afi&FP{4xJ*1|l zcthGYmkzh;({tY!$Y?!me3p9ed?(g)O;KeeRXPD!l4RhEXNn{aCWT;WAFnnMoY+rL0W zdX4J+e3p@5hh2l8sQBC?K-N%Gi;7WRqKqg(@)8c)kobeGTy<1O`#PZ8yk)>X-wpqA zd4|HT*_n+>*Aw~KHS)H7w6jQO1Y z?F;^t>}=*U&Yj^z$aG;@Y`;ipg+L2%Ld{mpMo|l1J?Va0ZYB~qd>I;Ahk&yRX_b9j z$y6g(h@TW8Z*0=zFINepM33Bn{;h-q&4<~CRL;dk(8;MuOGb_s z7Z&uGCqCN;(&T78uS+X)bKi4w6J>yI^Iwqplp;-x-tW6+Z-rkyKU?*Ot>LX-uNs`F zgxH@SO7BiVX(B#eO&S>o69UXzWYl19P$|({q(L*Y$cT1mIy-AQ>{pP`?)dML-86HxXIpo@8^S`q=L>boZ!s9~5aFMH2QEZGO-&C;ujdWQrXF8Negk9r z%FcdIwRoVkd@Mlj8)pD%UmSz2MN-f&(qB+PQonzuRxcjCS@GU~`8NJ+B*6R~s@`zf zgQI@5RV#*zCjTkxFjL_Q3maZf7CeZF^6xUl=PvUDd!^91*|$DEjc8C0{#WF{JNtVv z24ZebMZ}xywEbH{=h3l+o0_Tk>r5%Zxg(eK<|ig1_1c{b4V4?syTESn@0hr0uBXz{(#_teh1*+iuRAKLD&SZE{WVWpPLH?d zGe<4DH?+DpK%8bXBD@>09qP@aT)SWYZ1wf4ysPUgLDV$M0|cDZ+42o)!h^ID;p;_b z6su(>D8%!r>EQBUaml`Tqd)D};_veV^W(GOup%A!1hySk0% z^Og0;$i^)#F91JK#S#)2IGcpwuxG??9(y%aRC9a2Gt*FWF_hwbv^>o1&MBu!K)fjk zlq|49gcStc*=hVh)L`SL$ywbVwPr!YpQ#BK7Dvtf{V6_LNm-k*i$zMrKRdIXlS3H| zvb4vap_DH!Q#>?Kh!L`pm{>TJ!lA6ZbEF~f;GhoXMqZvoj!YsZJarVBt?kC_>;?Gm z53Cj);s>VF-ZQ^54-PM(XSV*zLVJ1nQc_a+)Sj6+<ii&Z~dWYXYEjpMttuRV~prjS# z{C$Q~4?F=V2Of{9VxnsB1t3p(dhDY@CnrNeyDtwlbxn0Opq*GO6LWymGE5&-nZzgeYTid#;!>pu4GX`zmkOYMOlWK(u~zmB3p zo15o>kL%jQNlp$kyc^Jb&FlGZUuuKKdBB!~AO$d$bylEr0Z8=6E(0K~greHnYZ9Q2 zWR;hbF)-*Y*6V@R?6E&W1e`y)?bhX;oHq7nm%O}?fe`O@&C=fAe>$!E(8Frw;nBJ| z$c2G{08CApnXSn@O)1HUnm^e9HMPIA2$-W*K-p{6mbUo%!a%oYEAFL}$XC}8U|$Xa zRSc*s0A>Jlis#+)T$r2+Mns$dY7q%vz5SOjHm0VF32fiF!^r%c4(i=kg$G766)Dkg zzykS4I5TR{A5cImjIZpRx!mnv0p8{MpP-S5NK-7f_4&3826ZhPu9BbJ)geJ3qOFAq zDlqPNY)A-TU|ay8pThkWYg`s9B*iuIUYwz$d3M~Vs zn~PR%u8XT8plun-jX;m_pX@ME2%o!3-4{V6DXBU+T4h=YoNq=>H-Je)`d#r+6+iLp zqY71s1eT!m%KJ+x_;N4F)0Ck`|Ee zF6r(#bV_%ZNOwusUHi!R6F{upb!@Rv zIbt=rD=KQ0&UyE4GMr!omGE;}AzDbUJz@X!G_FB^ip|pOI|$}`1p%{3BKyJG+6g-B zP3U;vz*gw+Fbfe8F|@EsE+$Qr4nqPR3W|K7_PaVt&C2hSlwlw}kOJy|phMndZ02@y zItJ9ZAmjyZ`%Pjer_mI4;em@fXtu3AOB&BiEVOeh-Q#YAQyk2C++35u;Wsz`lxf%i zvjknxSAbqAg1Gf$we=McUK$x)GHSQvZMeRBcMRB2u6(nA*nxx-1n`T)g;q2u;3ZL0 ze*|+_F6YBk@J{dhqqF;K9~QPvLC+*+EA_9sx{+raFwo@K1Nw{+gpRWzX3XUIk(F_w zYU2us0>vYwx6f3Yn&QE^1MJ}+z9J%We!Z62=p$&b`?Vj4=JKVv_4Ts3EfP4PpXm~A z?_m$;LmhYS6Zv1z!JYONHB+ju(g@JmOxYw24HNwRg{-Z&0C)pL-+ulD0$wSA96(*A zdbQ%hFAUWOjDi-29WED_8bBiiBdY3oekb#B&~O3E4-h2?D11!pGfYgsL3fQzXnH** z`$&$l0Mb?8j|G>LRar2@pPK~%Uhj56g%a!yzzZOkHe(wEEC9r8fV=^itH$;0TUHh| z9d;`SEbgYpB8j&^>a;N!-%@9hNsSp0jqdXMlQ2bWO6n3Co|?S+VgT?x2zhvTz10KF zmZq}&ga|0&0bkYCN5-?~92^Lkklj2E)YOVHc;e&Zm99rPU5XTk*Vd3BirT9p4C|-s zI9OPuMR5=Dk_}c;Kf>wT9SV|K!XX#~HY+rX)gSe?Bvn}WpKlDNpB{?_Qwi<4<A@N#6!S@zNHSz7^9%*VHT(Xb&B)01RFYOah{#)jB&B0KGg` zVQ<&e)Ci1ky4X199_I1@C=L&1Qm`xYuj7~i9X0YhTwA-^9+?I}RiNKjDog+kfPw2{ zI##Lp58Ga*TKCH+>y_DmaS5c^C+gmxzG=9+N{Wf4h>)r%Y-j?#J_R%&s;+Jnz{Frt z0YuF2&Bw!BH9j6if{AIQpHrnPq(dBOWL@sI-Zl02?|B2m!oYy-`DVFc!th9$2x2(i z)WXcnuP&%VZ>W?ajeu=ds|cxs?RdMx&Z}fZUKb9)CTMk{t;^4s&`OU@P>xAae-H@y z`r-FiDp#jEG>GH&utJq;vwdD>RTbD-Pu9DKkCtp?GmqmFRYY^;K$SQ$%r-pC_6rYy z$m)LuuSR(pLUY46YCKwTB37d*KKRWe`K`=X_RQ-uy3tQy8b~uUr|6b44B1&NQPgSlN-^Jb&+;|Fl+uArgJ6|oFr@G~P;lZ4(9`CZxc$#zx*+ zG}3?md4*@wjd<|9yJzFg9e{Bi4P8(%Rp0-mD_}v@d z2GR|M3=aNXs&_gb@61*x5WYTE+P&;aW(~a9X3u1!);*kOML;nm3%EKN%MWA{XtSNl zSB&||g#3oS|{C&8p`+Yl4~q5fPD_r@so* zvGO=XD?WWc(nm*Y{QR|o-dS)POKU7*-6Efm94#RuIW)jikZ=CoyGR8fS}KROCIb>= zxY@zXwvv@zkeHYQ<4whA=M379F;KIeUtGA10{ajgCCU3*oV%^Tm7ldS_p6F1R%UT*2GX=|}Al0vE za%#U1dqWNRwTe>!m-YR&QfKk)>n|ZhB_%xK_J^P~-CN^;uXN>hqQDH~u`gU+P|z%v zf(88DV9ijA=6!Efb1)}m20=!tzr9*Ac6h^_uOTTu=hDpZcLgT0oa%`57b`uzTa*5z5081Y zD)qVI{Ph+)_AME z;!V1p-*EVQw?`5`Vyr$r#)^_!i;7;Ru$z=!D=R7r{(Xb@LkvpiZH_aw(SP!hV%ymG zX?L4~6MrO;!AMzIS#U(NmR6i@+nnunY=~hE9;T?0vVapELhOJmbo!tT2b*$&u|3W6 z`LPn<2ao^;&Cfb*4{qYrJ^cj);l|B)vUJ;c$lLqov7dXg%Y-ai!oq;7$5?fB91g?J z=d=FX!oryXSs#4^GD6PsmGi9v96B!h!}%2wpF=>KMuV0pr|->ATvxRBu_phBi~E!5 z&2zpfaeXB9Yk+!eAf&zyYtJ!Xz11Xn*hPObp3@;(PL8;~9*Ab#o6QGemflllSWFrF zOy$E88QT>~PA=c)DOQ8_0zH$Ddv#Ty4}5hS5YX%L5a})4R~}LrSJ@&9Cz$7y(W_(w zB#A36#iD?y01?uh6M|+wDe_cX4g|a(0wg7iKgU=uOd2fK%FwI3f}Qu`1arlZlnvj1 zK%}RSf+{}pOQ-AajL7%ePxpO-*jp*;UiGb>?Q@6ib zXYmmWt8Y-!Ut4x_+r0hek0i_)gd%q9U! z7#lKSltzh_*jpSe=I=``v40r{`!7p?#ZDht6oL|5B)@llLHi`?ga%>L7byX5E`Lwj z7e~GvA2U?2dyh^jFzZ2)CFC1ycD|(JFI4C4Oik`h=GnoV!v$`BN9KRgQlk^ItqhYf z@w_SuMzwJ|)?7SYKk)ZYA2F_WvKX_NtJX=d%a=X*JEL-+lcRibduYO$1v)eD(@ymO zsiw z1%-LD+drSzMvn329nCs(}9{Ko8zNU>Ic)Yne%0q^HK0f_C7vl{!Q%WVHS10W} zOyd$%n=Pp&DJ^)A9;WHyc&!p;XCzkSg#Ya)R(1D0r&iiB~f=>){@19h?3 zfGb~qVq%iBz4xWk?tGPZWTL#2v-7j}ck8cFKUN#<^T_$Nj!twgT;t>S_9V&ST99hZ zxA1T{M<(-np`X4PXfCta*Z|w8yGydEDJfc-;wH_6ii+0QSa`^EbDawKH7~5$Be2%y zgb0W83xf|E9T(g5(ajlNQBf}`IwyHgok|4-3JS*1>aWl3vt|6OERtAn^lcPj!k8Ri>o?Dh2-A`Bpqay2!hgp6>XhqG)bKE?`A{vsgoPK~agZ7jJQ?kXt$%%|GiBtN@wsB~rgLg6=N5{Wl8YajaG z%am8}TwB`-OA8?{UZgG7UsnI{1U_h9i(cjyv^>t1cV!xGmKc^2B*H^Hi-ZaC+5E+_9XFjf%ouW=ASReX2Dy-ur?s8twNs- z)it3kzW9WX5X=+fA9k$44T%EjTGKx_?>))cEXyuU93Jl89DKJ4Ibz1Od}5ngf6g=#A0=~Vr3T&fQ*9r4pEv@;0q z3n$7L4%u1+n$^HchnA6;Br zKvXU-?=A)-JvH^TeUpaiD~F5q1!G%d&P#V{9I_!s#wSfwnvU&34T~z%ju2Zq^YzR znb3~h2i*ySeRL>hU~$O^PkQq4OxHRSIJrEvvjJ9RrDR7QR_OQMu01<+9wIp4m9o ztz{7$1Rm~pX0+sjj^_mCAsl%9t_~Q4!hHiT2Y(QO=_0tU{w)z4T}U{gl2RmPp?-R} z|Kw1O0s{I&ESPLWJcLa65&`4OO?sfbCJpHO!R>IO{Sj)q`8gA3|M9s3_22P6A?mQK zd3&U5>px|qk-mP3gP6p?;1?-~g3sDF#3%Sc9=D+<25l05*m6sZL`hyM#Qa zO+q-a7lojXhL+W8!--I)0Q*!6HkQ|CS<>#EXW_o%l@czO^6%Z9Fee8)XKX&a_35J3 zzlNup$GJ}Ib?O&^NTOzDH0cq)FOnl7JV{8ZGBe}KV~S_;fBrllmadaGV@m#<{9~mw znUq}QEQj?MCeFd}GN_&=#;3-)c+MAVtFBL7_ov0Twt~8Y9TaIcdP1HoIbIS=f@OV8 z8vp{4#)vF{lq`L*&SAcZ^(Ao0GO8A_bzX(S#5iptfbkS;RAkuL9@JY7JFDY+h{(eF z%pMopzx29gW_T(!8oFyOK+~5$F+q zxrt~J+{T)g^44TELnZw~Oc}DXpm8_%#5n%dS+57eNZ%FKgF)nU&%b=!d@i%J2ga3%g+myklyq6{Z-d%*ZBO4*S88x_ybBM#}oWg z-CdRq)<=dW{prjbliMRjV*-`Ui)m?^A!z5cM}W9uI6zk^;Uc4b$0y`po~L}{XB187j3< zp1IMQFb=nQrW0^ieU9m3Vl$0O{Q4_L$>HpehR3z>*0wo2{(A`l`*ZU;iZJnM=um?5 z>0gkXT7=)^9BpsbRb*;GiX zMb^`H&p^jDPr<=KOD3$PSxQSiku1HrHT1E@M2Fw8uzw=ANg==Iz!5&eKuCy%lJdWx z>|Ak&y*Tbw zfe)hd#`Ywg7nUZlazB|@I6rUg$R2l9N6Z~lGKG$qoj`9DNpgaPGsj9naXt{5|G5XB zeGs9axh5HKz3s(<@FAGQ$G?xXVoSX4iN~Apd1p_0LVzrb-(=*i?+yUR-~!Mf0mo|< zs_g=9E6tm?7w;)#BMM$>X~H{|`)N=@AvG>1Da~^y?Q5Cr2m51!ltA~lCy{7CPeX&e z(U;EeNX5%2ON&-XNOVhz9%lXOr?{i)-7j!|Ub^R0Po5q%%pFKO?|!uj^b11+>iHb1 z9^;MfAU6&2sTdifylC>GzmAuT?H$c_9U9`+*1c_BA*mc%Joe-gd6TV0mWoPy4OXzb z>ow|xr&WsgRc;qs2K~oR{c#%`Q?0G-1A{ak)VE;P+xmD{=5A#yA;YlPc=pF1K~n0o zy81xSI~*1kpnD4pq_H`%k;!QBInxhnxaQp3%VRufnJRc@H~h6|B7)0pzadUyg zU6QCFCwEy>5- q$eyXEsdW!W>{kv5T$HUUd~~Fo5Dd42&%SOJ3;RT)XQ!`Tt-E$ za76?L^Zwy7Uil@yaY%>DHSTb^@9|fbk>kd}s8>M*js>Xf-j( z3pg9X+=&*)y#aQM(V&1vZeFR2a~gps*Z#Z9+^%jfZ6D)C&Dkagv#P2${gGp)i_@LcLAtF{N(82TvYglfRW=~ ztr(s7_}~xeD@bX{Y={B!YvYf`-+T4v7wt7_D*^D)ZlQv6sE6?7buLAIUVr|U>Vw|O z%s@!Hk55BIdzd&I)pfPoh7^1-T@0b_LlDRtZ`Kp{x z!oP^xUzKrk7Q|K2p!U`(=j(S%9WW2bSyXrZrmCQ6i9h+$vf<#Y*IT92&kER+CzhkU4J;K4H|JT= zY{tBFeG^t^XV6nK(`=*TqK-+Ai@dS_gAkM4!4BQfSXqMn5bWpf!&%Y~HDi8)_jL0- zq9}AM#T@JrQ`dPd(~>tlWSFGJy+@-D3|lS$8izQMgsuZ_opNV4QKm)uM=bd06+iBWm@QrxUpnoYtG5^-rWHAjI1%P&CI1m2I#l;3`H&{@yW@8?(Q_(V}dP~3q@G8K@WeYK#O@+q>x!}c2N@? z9G;tN*dGBB)j1JwUw`jMdL8dYQ?}=ai*MP&8k3YnAUDpGpvFmts}6^SzP&w$tzt-6 zRj7ijKqgiTya>1iGJy;AS){<*lj(uTgiMDW`Sv?=9dmQ$$o~DQg1vt;D&I}pe9udz zvBSf2kiA2DmjjUyt`2bg^uB-i^^Sl6*b6ilezr29OyjOuDwMObBea;a*7Nq>3dJe_ z6YqvPto-~@0uDrpE_0>Y^pnT zGgx;zB8cW{)pMbc-6=}fi5wxsY;1b(x9^-Z+Pw`Q-BB4rSYQg?U zL70KKn2_(4VDk;wX?PtAmyq0V?YEzw8k~B*D=#b{aaa@9ffT$oB&%`t^yo5}=@YS9 zWzdyCuL96QHY@ebqa_E$VlYgL(D?KBU=ApZWy3~GnoZNX;2X&veg24j+g zs6fMC=(M`p>M!_EdC9qU2y>)%*c!}c!F$fm9NGEZNqu{Z#Hf{$HkqeZx!gbO>JnvUHqbYCuUsK;Z#YPYZ7`a) zit)zhPY*ooB-P@8m8@w6;V;)`& zfYKg;y~$V$6$#ZvwJImQ4)sdubgv+h!%EU?w+(GLb1g@^@jqKam~$B$4-dLyURzY0 zqBdl$trhRn=~Co8mUxL_<|x89X|HsR|l z1KzQ5Ha|LgNSQfy(sn>ZkUF33FME9)sf_C66yMuN0W#jMWB&R1KFo#WK#$4MG_2zj zWx#l%&eeSL<$g4oM5_nFoSWA1F$V(bs(RZ)oMav?eVO~|r{qMH^-aa8I4rz9*}0Hl z6ssoVk*4M4x15}}S4VT8)VTZm#+c=-urS|ZE~BEb(3{1GIFjGZK0vy!;k%1!k_MP` ztI~-T$UK4b?vJpd#IWEL>DktIPkpyx!M`h*Y0g)u>NVNfc6_o`Qol8P? z^Nr;uufs!g<0C=GT@<0HOz%Ze()GZA#t4ve!6rPCzBD(h{2gt7f~?TZ!6jh)cjpMu zLMqG48<|k5*KAvYfgL`l^RGdgi))wD)5?YB%h0;?%+0&Y!x9w|LLN4-C5Z<_<@MPd zaf%^gVHX3B{llZ)GIcjUv^Hxn(r9Sd$tx&Pq9KdyKRw5AIody8^(e2y?Y&4SM z#YRF!jFL&lnEx5lqOB@TW%zh&0$(V_|L5s3Gyp!&>9cAQDa&M&0wtx6&cNltGmDx) zCVl+*+qa5=TYZ$D-wF+yk?~gQv$poD#36gfIKCfQRP<#z7eCPKofFi{ zdrjF=N8q7Uv{E5xz^rIQN~iWz<@w%0F*b{ zf877Ox7z+NSr0(;A+(w27nx%j>?QrZVR%S5#I#g}Y^i`!G6*0^41!b;Qge*tlm(uE z8s*+f4S{fSaM;aPVvl7ue(KPx!3zm#0-0x9dN2?P@A+eiC5OU?-{3JjyO;R{oYj$p z62(}0JEH^uN5K!*+Zt?7mTvL5{#NkuW9*rNd?*{fu#C)Y{+GneokNiFcs)2SA8y%V zL%6(nwzfwO6P--*%Z`O`1`u8bT_BBt8$zsT@wpg3qU*4fOi}IwWw# z>Lek1dms6TX#t1>0JeQ|10^MXZS=r5I)repNXY`N1GJJ&4kpiEQpFh6?CrrvA(uFF zE&T@9k4~lz2>=<-`zCwsAHK^dAQPjQDez@iecE9Xly*HKL~Xam&(%f+U0uuWm*#+% zME%T@X>DE2*tXkdK9k)3)C_1D^Dvj#%}vq+M1WT)6j+b_qUvU;_9`o%RyW6vV?*x3kbmPIXcLuinZf=qjgnRS>hbz~3WO1tX-X1#)5_`5Yc4DHn z)86aX0u%=eSj9Z{?Ps1rgtiS>OA5r$egla-Pr`ml zDR$f-u#iVGbLQcgqIOMm$v7mX=Ti)VNt06~g}AlN*D~kbYPI7uV-fBClCyHM$^COm4z_(HToC z(dVR(QV3{B$giz$smKC$Wgl|o`GUwXGAa=7H|^DC-Rfs`4{R;)461NvVM)XN z>M{a&2nhyTK>z2!^ic1-Q@QV#~A}--VA=NP87J)>TewX!}ACK#lHgqJq7sl|M~`> z;h8CGb#;}W=g!TK1E4MpADZr8hmGX=e;^1t{4)r&P896%o`Q2jeAy&vy( zB6YOV<<#7^xXGmQTnl8>z94XWmcOIXLO8U!E$!j5g}6$=)%iV zIS(??eLKG*!l#?RdK=h9t&ra_^$M&)2f?R`x%cowHWgsxYRabSyyv*{CZ|yO;XC3I zI+;k_i+@v>f}mS#R_5s4SK44p*?PteCLf!7J7FGD(ROzP%!JlI1TYQBy10xL7G9Gi zV5wSi5D@GByQJEmP8DgeTD`Ug7kFLDr@FA?2HV7zxB{;T_#*zLvUEvf`Q>-atRE8 zLMgfJj&BFEr(!U0Ca$S}W$ zIZdLT-eZrxM?h3qSXg2rP7(?LJXcBo{Q2{vZ@;JK2oaJ!zPq6y6YO)7{bS(gQZg6f zf^YV>9ZJ4sd9PA4);1R#Bhf%SNL&o*e2ZED+WD!eiOJ4di<<*4uWK_L8ua(2yrQDx z)u9@&72cPLm6)r_RsPUH@uj+UXP{H0LyGc4M|1T2cQhJ0K6$w+pL^#B`GLRZ`y(3* zH#awI7PH8f|8AQK_*U(9qap0rqUx;Qm1Xqi3mD>vbr$^wOt|CWuUyHvcFOjTKEad|+_!{oS2k{J%442US9PETt?>JdBI5*vE(2-&Z0J%7k=U zzy7-e_`Sh$Pi+;o(u=271bw`xwnr$c8gnysmO$9}hWvZ~s}G2OD%!z=gfzzw7;o;8 za&mrZmcL7SRmgaHi!6|Nw(4OCw!XNyc&DR-iXEn8Z9*@6hF&Vx2{9=qgpsRDE--j;xa|aiA+t<{| z$rz`DKF8g!EA{kTg1$imJt4%eySLaaP~O4=Qs8}D|Az0ERd!u|=w*`{ErQ3IYlDWShm@1v7I^ojN@(JctuCXd!S2y4=4;Kz%I*ck))BxkNa4Gn`<@ zpZZ^Z{KwN@ygch?s=_<9hQwFL4I=0wfMi>^UeSKuf!M(-YHttjHk!(pAk$5cm!&=? z&~xh|{3co-j6Yv$P!bRrCq)5C2Q_*?wbh-wR5O{FAQg2M4xeeRLZwtKHUw>dX)a_e zm>qqMTYL00WO;cRbOh=A9@zbB5Th+yE>`1V(_Qsn87#fLf;tWQqRl0=j5LFIt9V(E z=p|3;oxKz<$bp|I8Cf|lk71Tsmj(87#d`T}}mFObL;)T*&XjohkX z6IntF{HkpN#&vwQ%-H~)&~Ky<0om4=iK!{=-3i7iNA65YNl6H;%3w`(Eg{ONuI}SK zZ?^+diy;UfB7vKZ8Tsmn@Da|`qR_XwW%LC~wA|2NBjIDZpZ+6oy}-oWMSgwW`O->0 zM*V~C`RxzovdvZxUO%IMw^?52`fuT&>F@exMPNbg1IDAtMB#-*#THqT8{69vOW|8& z&M%w*Lk=vpWZuL;NdNk8Vq*3TWH{GksO#;yQSO8(5g2BcEx(|UNFu1rbA^V4*(kW` zX9She_cxb5Ua#dDv%38eVTOJt*T1`;qm!L)X)+3Qhr6j*ZY?PnEi4{P zODoOqo+}?=OQjBo$|X~wFdz%qw0AZw#H4%uKxQ%;hXF6Da<5499vC)!rNdxG2Zm6S zg;bPG=%;r9Uvq3Nr)yVW#zoVqJHPF2T|lPBrr-QqZlTLNC~Z%Y-YnOOq&Q{8mssqUc#lg} z&-Dg)r>7Gk(7guv=;UpAd1Qhg5+a&L{kzxlxo&^6TU@ISe|JKMWH|^Lh!JM&HBJ_94#M1PC8KFJ-%SM z%wm1+h8C7=6Ilv~ITl58tc|wWbgUOe7L)q8#q4~J53aIFvB<8J0jK*TOQRC5GpT4^YsIC508)#9VRbRgD@FRvh z4f+p9@Lx6ck2~={zl3X{3JHl9kHGXLg3Ho=X$3Cnce5!B2Fyy{A|XqlBPXwq%hYi@ocvul zc+uyn>0|}A@B5PRUZPGT1(qaS-vVr=@BfZTFqb zj%btWK?1DTl!>_I+1%`NyMvLCkS@#A2Q(TIrn(U%VgLn2tMRLtoZ?b_sPV{8$3}TG zpuXnfRH4LR)~JnQx1az{zyg}8%EpB3#m1$-_Gj>(p7hpw0>gB!M|VB#(o z*`};){0VWTwM4s(?`Vke#Lz@VMG==$y0OjK3R@Hs3I(>vKpHg@`c^?H-fg!yb z0wxVTJ2HXuM+%DIq25b({e^0_JIDS=oaf8A+@hBpmab?dIqF2@DS?fdc-Ac|jnWeoW!B|ld-bT~6~c7O6R zPhQ#Fe5Rs3pHT~+5Nk_X?lXhQ^PPpe;m@*$hDs}q-cJ8OgMRnvl=4Hf#^!Tc+AsgO zTH=iuz$c60{f2QNP*@7L;Z{lziV+m+ml&(tk@7eTxD%*ItC8aKRDR?D&^87yt4*ZmOc!LzG%(PWnHG^*C&j8 z_CIkU#iheZsTx|EmX!@VZKlT36fvD0yX^Rk+KTpEH$U(i(4d&2W_3N`1Xu}4wW#mA zzk=YaHZpT_<4KR8V&oJPnV&ER#%U{Q+%8j-Y}wh9<6jpV)>f@nHD}Ayzw}4c*Vk8$ zqW=r<4q^5Bxc;8nT9gGC=krOs6sNW2>d%k>a=8K-6Y7#t20|1Dj4F8rcFDZx<8=u% z%%E&k0o!$3+yql%;s>ATJ@94K7SYQHIiTxFX~t zKXF-yd^3GjSF=&7f#baHcYbaJ;40w8W&(KigA>tVdf%N3^>JBJsYCrtKoJB;c@SXe zkRd~^&tXkxTy;voNfRfTd#UZ=VeRM`J0<`FB~r*x0=tvL2B#Tmw#Rae|DL`f-H4tC-J|yZ)K0Jy-Ao4EPi!(iOamA1KNI%4Yfs(_as@$h< zuKS8McRw2fZd zjMQ>t10c>kl$6#D*6dPZY!(!c8QFA*tQs+ch)70*Dq9ni)ycds z{(e1&a@82td@c&wgK z;k>_85t-iR80dOo8%ZU!6S{H}(b+phv}~D5+Am8>(;k zQ_IlssP45L;lX5Yn^akN;#fHwqF{2wP*S@z zUw+Hd0;*8bH=#LbLF-&EJycIA-mpS(gEq(|J_O)$z2v8fRUjAoiW$g?`yEOy!IV_0 zUZlqru@Tu!9z`w`O%9$JwUMBpaqh3jHLCj|fGU)2zcvN%C@g#MkPkfD6zJ4z3*9cZ zkCqxts#H%|a`O#96+wt7AtI#sub8;e#55^b=4!Io#i89ov35lCA26Tr`tFo=uOdtQ z@OWSCS1Q(N73@l$gR{xe zG7bwHd5-F13R(@As;xUm+lb_3FFQ?&E-z zYtwEutlznb3wO%sww_2SNH&l)KCXZLs1_R;%*2t#hQD&f%?*@nASB1pN`gr;QL&d;0lla%#{;;T8R^V{>m9j?3I{2cZ^4gqe){ReyCwO$XGCC!n?$4U{L)7yMgC4TMHZ5u07_rpQ|1_pR|VE1yZCf zm~3oL+_j=EXtue0_ISoluOM2AKr1e$YO=FeIk*-uynr9_bLuzn3qZt`<{QEX;kz0$mexzNr;MtnDRfvR$NUOr8 zUHdsn7*<%gd%Xq>@vqmqySmcS(%i2e=CDQ-LpIBf>GPCR#Uh;_ambc^O=|3oDE!Zq z=e{`ci7j4i3q@Qaf{UM;y6J0c#!k-xOlZGDcyAgJ8ot>jkVNY}Md5p#m z%FmB!;)oCtZnsyzktu>1anCMKDC_CeF4Zt)sosQq-{<+mn(^ zgIibP6i&p@`e+6}OndDKTr?FG6%FrDk(`c>4)9c2U^#vMaC?E+`v@}xoE;snnM13# z)A@M>B_%NrkDC`QhK-t|4QutTsSd6e#%hw1-UhpG;2&@xjuy+(m|*RThJD^Y8h`9TYImS!1>>+qZVHK4J6(z9cBS)-P1{d3kOSK0+RaJPatm)0^LWzZF zzU-SRlMkT7PzK7?cJIf?J}+&&bO;3U{4pJr zj8#=1>+9J+W>>Q)I6D4Wa;_2;of%4O2y}Vc%2CAkx&1gbQf91cWVW~DdY~Xr10E;y z!W#Q@5>QX4^*Tf{zSD{^gakCckju?KGQD-f&k!nG6#p_0`!lqWisSLR4%vK; zY~w1=B(t~ODAAfsK=7u2*&r30=br72>D1BSI&J`3Y%ejBRcEK*_S4yxt{+nkw`WM# zx=+|u%1FL{yH5oTEgN1jdQj6XXPB$cTPT(NDW<;+Y^z_wI5xU>`}?LdiDz3HWz1cW zD17aH^Zt}KGDXRC05^RzsuzOIbN-i9ZAl(=dS=@!S&@K^CC@6Gb%f^sbXlU;*BQvf z@7lH+GBSj*;IvcFSa64x`GbQ+{<11sP{kP9xoT!75f9H5?^IG8t<^}=vi=Q=hu0HB zWL`)UV#U5B+1zpF;{TAF;ABI9eb z!GvP6@Q7o#K0z=4e zPpQ99^HbirBb!vZd7{XEN5hcBip4&ju}~tHXem|hb_pFxt$CBvSCba2FtSyAyh`u0 z#B#eHETGRUZT~lBp@veYjALTSysjwgTWK*%t5x!k&~sa7?2eHa+VNbDcbx?kH}J6}%Mv^Ke%6mBkR-b>EH$}b^j5=gB3HSZVEwy{ zWxc9YN_=+C#Hm9bEG4)>a|?AlW@o-T+07}J3nra5*VjB!MQq>D+8iY%3&#X)o;Y?+ z0nxga4GR%+Iv9A$9uH~4XEDkIT{N*&j@() z5f4*8OX^mg`gk3Dp@_*rOMNsCRz-xD_V@k9`-%-7awuS~d@Wb=n}VH>hW6bGtY52j zkwp5eyvof%T_VK#>JW|T)#(-76w7~%;UA@}#lais$p{Dkw zyHdIjaO9W*#@D;K6A*z~vV;1JSPT)K{u`*`k)4oDgmBCbl2uk^60&=rRsvh!hp%LfifE#Ala{hSKz3?WVIHO zVlaUXL{xOh0gTVvI3owyN?uj#-%JU4#l;3N-droiWXlW_NZ^sf$(jBwEmpwIsU_ve z&e8yJYPPwlF_vN0b^Tj%D-QcmR8%bV^>j;%v$ZvJi?^^$0oW_Z$sI*S5gD+&;=XEU zm~4-n#40-UStT z+w%3x^aGZV^&Y^7z0UFAc)QH9G`g*|&RUoUSBRdPw|VeL>KrT+-qK5AG4JjcjpNwq z78-c@n3>~%b;MS?4~Jth9@gFX63e;TzqD9tHA_v=BSIJ5DUpl*Oh^ZZ0Y>wOb$tUX zRHuX^>Ib~EexO3qUK<8xEc+4hhL9l>b&@I=*9Pf6N2DK&subl@OvaOP5o6cG5(Ndf zDOE1+kkim`JovyctyU>YnWxh8NuM}NyQN~x_ICece*Nx3%OR3ewjc4-G03V1e-h&< z!1AEk|Kg+KcxNZ&CdpWIn+E=61i`p;;=IN&VX#ZG6Y{`=9@J+;C)~;(a>0725IQZ7+@IO{o-oM219t;mI@ICa<^h3}^&&J%*3&EZ;xPn-) z;Ztv}jF11=UutZ;{PFs$>Q^man`#_CfsmA)tw1~roNKmeexiH&UNrPyG}Zum`@1EE zmoMYlB;CZtV`=VbUD;?OXGrdU;mIs0^np(;{h*CH7C;+%BmPTxtw=i{LEhvr<(8gd za?*~>`37Gd!T_wRYXnFYyUiw{-zC@@Gl$pQIgqat95zgY-rG2ZFv{{cG16`OJX({0B~&j414s*U->rxK#0YirNE zy^8k_zV7}cxqCGzct!Aoeq z6QFNhF5y>>jf2-Lo1Z&7Q!{qQ*sEsQmdHCh6|4V+0r7Q|plCzE1G78`eI5t>Kv(3| zM9;3+gAmMguE-mH*Iz+T=Tmp^eIe1&!)b`b-f!3GA^6oetP1y10pehZJ3>~vtuWV?v7i9a;})idteE#Pu*v~tN3_9Y2F!T zzGD98wX!UISgO#s%B_|un&|n_;Iko@%03~EQ%Fx>?jk6&yBZo$w{St#|RS}FhC#rqfY&Q+BKhC=0dZTp{9&QYNFQQjh4WTwN&cwI|G z-rQ$drwGF{+!p$LKI_4Q!m4xmlpeqx$v08R9F`C*64rau*=}QlBpgqio!)`+^fkXM zs;f2w9V~lqf+OhumglpDpR3G$qNQ~?2AL?hf5dj}+I?Q-_g7h)@G)W{_FL}1&ty{9Lvl3xMqF8%Qy|vjk*lnij)CrHAAjI;Y)ln zt)}_1eGcislMUQu*t!i}K1wqS&Xb@5!jg21kbaqdJ{x7g>Jyrm%HF`d)Yb~#Q&5QM z=3I)BeFVbJ&PPitrM&!GXZ}&xdVp%B&oj&At}ll#zdDX#Y>nrd9!?aSWB2rKxC;6r zLROrikSfA1a9>423BL|fpimfZ{K=>;DXTUmMNeNp1BK@F)QijXZon%^p$Vex=6^0@ zrv$xg#!jQNoUybWt=;4|;8!FD`|H3O@(Jbm>EEsSz!#?GywtmoaJ_@MPfcz=O*5H^ zs9|-r%5xR@ZjnbB$(&whp=-g0Hh1Rm^|e7vb>AF0iHiC2%q{|_p^J# zm&PX7JV~Wl_M)Q5qZkn&CqMAflQ{XWGxN{R%T(dTje#ujY|L~g?bj6nGPg^Lug_3+ zsbKh$4PnIH2JJImd|CLC8g-Z`djx5w%J1V07)dKk#)F|(!kHo2RppN516`od;Wd+b5c_| z1zg?Tc{n)ubILgpHA?B$DTP7G{nkG2yJ1Ebx4VDG=W9pea}2iqY*nd(jP6*~4$($U z^S$B_$id-yE{KwICkZU`zmM&Ngl6iL$TLH5stqdhAH<=J^SW>(lD>mxe_znt{8Z19 zEAXhg%v$Nk78HB(>xBK01n-&9!uEWr=dz2=*4*?Lr&u(~2*ddiu36`Q;3l9PJi{Cf z*lWQjt5Xt4CGYPm`%?}drmIYyhMV!F1AEI8qpE(5jhNtI4}re^#G>)`>Kmhx3<1`r zGEc)~;@W_gi5~7Co1$ZUbbRc+eiQU;A51?HEDkIt5@`G(Y~R?{3qNyji8gdBksyM0 zvj$zUt~Wc{k*<-^42Pvf_e#uu6zG^2n%O7n>B8CiQIlNFUY#eM?5b!Q05#j3Z3b_R z)HL^a)uhJYGofUPP8GFwc?+5sya`oX|L{g3@QZ|~EyU`ffV@Uf|>7JKa)YpKmG z0sw6N-CMS};8{2ZU>NP(Fhp_;2$Y!c2nx@1;|7aWgd9pv+WxnCO z7T0jss-)%b#X8*VQKqv3BQsi~m5UczkcZlC75`i~;xY9uCW%agiENI|6Vm)%b1 z{8YdE;-=UKx)B2s8m&M76&DwCa{WEq?cf-F_pVO{OdmJvfgW(xe?1|_@*_Ffwz_!UdCDHf9@ z!1GAujx;KN&+#uD1PWrB}^^cH)_~ zfZdpR80f-N)azKeVqxYcB`vLs1n2!#jq7->q~t;KscY?Bzc~k%f&u~PPFPphtN3eh zcxq@&(pY7oSwT6mX}?0gPiZ{ncGINB*%M@cN)?w`_7rc zi@uLWTxUy)3t~N_lhb`ST|$Tz1xzq|Pme~3%^$yD`nLPB@S+GAo&E4HTm zlwJjmxSBICI!js|byGq{AaEu!!LBXeq{=mJqtYUOuU`W&2_h+t zR-F$Pi+$AsN;|dwF!uOkcc%ik_3sY#pZu`QpX6RHg>C%QR%9+3e`_WtERcIWzd$&f zGB>z+_h>5_CBjNKyX7HJT#5lSoR`X?r$NhL>$Tp*TN2M4rkk{5k~m|vElVu~&{<9- znl#J8oUsA0jG>_#a_S3RIPdV4B!c^#%k2!#-LtDpGWu}PS|eB2y;47>+f<~?QdfnQ z?u1yGKbB{r863>UOiR%u_&5E;_s>dVGia4zaP&^&ou##Pjo%(iG+cmfnae|ishy{d zsEs|vDZM zvTX!acxV(SHN_PJCRJ5c1_p+?%E1YySSpmuMUb9H84C5yzS&YzOR`P+%QV^CR&kN& z2#!}kAoiCXS|k>AM;{=i#O`Xp=8x#jtE?QguVg|`&Q4Aq3n9FESu8{Z1yU6ZbIj~5 zG!ovxr{dzKe2v1x$L6>2hqDuwXDfJU;Hp70qO?J1&ithKhK0gvI>1 z^rrKgKkQK$Ql4{kf3-!xaX9_*^)y`;X+HX-Dz_Xy&NKu0`g)|IC_nl(nC^wPTn53b zbr9_1wqU|zyT*&&}#oE*PD9(gd6#asN2|BzoFL@G`B(VgCmq C9a!W5 diff --git a/tags/pisi-1.0_alpha2/doc/images/pisi-istemci.odg b/tags/pisi-1.0_alpha2/doc/images/pisi-istemci.odg deleted file mode 100644 index a98b1f95ee5ce2422eabeed3b1dabe781e9097e2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11610 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 V1lEBd5GwHEhXaAoZ;wxd{ts;)i%tLl diff --git a/tags/pisi-1.0_alpha2/doc/images/pisi-istemci.png b/tags/pisi-1.0_alpha2/doc/images/pisi-istemci.png deleted file mode 100644 index 4b5b3eeda0a73fe33f6cfb800fa2b9c52d7295ea..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 73039 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