From 1b80fb16c22db72457d7a456ffbf1f70a8dfc0a5 Mon Sep 17 00:00:00 2001 From: Ian Jackson Date: Thu, 4 Apr 1996 01:58:40 +0100 Subject: [PATCH] dpkg (1.1.4); priority=MEDIUM * Allow overwriting of conflicting packages being removed. (Bug#2614.) * a.out control file says Pre-Depends: libc4 | libc. (Bug#2640.) * ELF control file and libc dependencies changed to use finalised scheme. * ELF control file and libc dependencies for i386 only. (Bug#2617.) * Guidelines say use only released libraries and compilers. * Install wishlist as /usr/doc/dpkg/WISHLIST. * Remove spurious entries for Guidelines in info dir file. * dpkg-deb --build checks permissions on control (DEBIAN) directory. * Spaces in control file fields not copied by dpkg-split. (Bug#2633.) * Spaces in split file part control data ignore. (Bug#2633.) * Portability fixes, including patch from Richard Kettlewell. * Fixed minor configure.in bug causing mangled GCC -W options. -- Ian Jackson Thu, 4 Apr 1996 01:58:40 +0100 --- COPYING | 339 +++++ INSTALL | 146 ++ Makefile.in | 131 ++ NEWS | 72 + README | 23 + TODO | 57 + acconfig.h | 31 + archtable | 21 + config.h.bot | 129 ++ config.h.in | 212 +++ configure | 2374 ++++++++++++++++++++++++++++++++ configure.in | 242 ++++ debian.Changelog | 1253 +++++++++++++++++ debian.README | 36 + debian.control | 15 + debian.controlaout | 15 + debian.postinst | 193 +++ debian.preinst | 86 ++ debian.prerm | 12 + debian.rules | 85 ++ doc/Makefile.in | 81 ++ doc/auto-deconfiguration.txt | 62 + doc/database-structure.fig | 487 +++++++ doc/deb-control.5 | 104 ++ doc/deb.5 | 69 + doc/dependency-ordering.txt | 97 ++ doc/descriptions.txt | 112 ++ doc/disappear-replace.txt | 44 + doc/diversions.text | 131 ++ doc/dpkg.texi | 101 ++ doc/essential-flag.txt | 43 + doc/guidelines.info-1 | 1039 ++++++++++++++ doc/guidelines.info-2 | 744 ++++++++++ doc/guidelines.texi | 1936 ++++++++++++++++++++++++++ doc/guidelines.texi.beforeeric | 1056 ++++++++++++++ doc/guidelines.texi.rej | 33 + doc/junk | 29 + doc/maintainer-script-args.txt | 173 +++ doc/upgrades+errors.txt | 220 +++ doc/version-ordering.txt | 59 + doc/virtual-dependencies.txt | 105 ++ dpkg-deb/Makefile.in | 66 + dpkg-deb/build.c | 267 ++++ dpkg-deb/debugmake | 3 + dpkg-deb/dpkg-deb.8 | 130 ++ dpkg-deb/dpkg-deb.8-vuori | 133 ++ dpkg-deb/dpkg-deb.h | 40 + dpkg-deb/extract.c | 316 +++++ dpkg-deb/info.c | 240 ++++ dpkg-deb/main.c | 151 ++ dpkg-deb/mkdeb.sh | 67 + dselect/Makefile.in | 119 ++ dselect/basecmds.cc | 264 ++++ dselect/baselist.cc | 351 +++++ dselect/basetop.cc | 61 + dselect/bindings.cc | 166 +++ dselect/bindings.h | 94 ++ dselect/checkunimp.pl | 17 + dselect/curkeys.cc | 33 + dselect/debugmake | 3 + dselect/dselect.8 | 89 ++ dselect/dselect.h | 149 ++ dselect/helpmsgs.src | 179 +++ dselect/junk | 162 +++ dselect/keyoverride | 59 + dselect/keys.c | 91 ++ dselect/kt.c | 30 + dselect/kt.cc | 30 + dselect/main.cc | 308 +++++ dselect/methkeys.cc | 112 ++ dselect/methlist.cc | 213 +++ dselect/method.cc | 272 ++++ dselect/method.h | 79 ++ dselect/methparse.cc | 289 ++++ dselect/mkcurkeys.pl | 121 ++ dselect/mkhelpmsgs.pl | 74 + dselect/pkgcmds.cc | 217 +++ dselect/pkgdepcon.cc | 303 ++++ dselect/pkgdisplay.cc | 142 ++ dselect/pkginfo.cc | 163 +++ dselect/pkgkeys.cc | 139 ++ dselect/pkglist.cc | 378 +++++ dselect/pkglist.h | 183 +++ dselect/pkgsublist.cc | 206 +++ dselect/pkgtop.cc | 249 ++++ dselect/rp | 2 + dselect/t.c | 1 + include/Makefile.in | 26 + include/dpkg-db.h | 272 ++++ include/dpkg.h | 179 +++ include/myopt.h | 38 + include/tarfn.h | 57 + insert-version.pl | 14 + install.sh | 119 ++ lib/Makefile.in | 91 ++ lib/compat.c | 102 ++ lib/database.c | 249 ++++ lib/dbmodify.c | 256 ++++ lib/debugmake | 3 + lib/dump.c | 289 ++++ lib/ehandle.c | 276 ++++ lib/fields.c | 354 +++++ lib/lock.c | 82 ++ lib/mlib.c | 124 ++ lib/myopt.c | 84 ++ lib/nfmalloc.c | 91 ++ lib/parse.c | 371 +++++ lib/parsedump.h | 73 + lib/parsehelp.c | 153 ++ lib/showcright.c | 35 + lib/star.c | 158 +++ lib/tarfn.c | 165 +++ lib/varbuf.c | 65 + lib/vercmp.c | 64 + main/Makefile.in | 87 ++ main/archives.c | 706 ++++++++++ main/archives.h | 65 + main/archtable.inc | 8 + main/cleanup.c | 226 +++ main/configure.c | 516 +++++++ main/debugmake | 3 + main/depcon.c | 490 +++++++ main/dpkg.8 | 20 + main/dpkg.8-vuori | 588 ++++++++ main/enquiry.c | 601 ++++++++ main/errors.c | 111 ++ main/filesdb.c | 649 +++++++++ main/filesdb.h | 127 ++ main/help.c | 476 +++++++ main/junk | 398 ++++++ main/main.c | 378 +++++ main/main.h | 197 +++ main/packages.c | 399 ++++++ main/processarc.c | 1004 ++++++++++++++ main/remove.c | 453 ++++++ main/update.c | 75 + md5sum/Makefile.in | 44 + md5sum/md5.c | 241 ++++ md5sum/md5.h | 39 + md5sum/md5sum.1 | 70 + md5sum/md5sum.c | 246 ++++ methods/Makefile.in | 60 + methods/disk.desc.cdrom | 3 + methods/disk.desc.harddisk | 9 + methods/disk.desc.mounted | 12 + methods/disk.desc.nfs | 9 + methods/disk.install | 127 ++ methods/disk.names | 4 + methods/disk.setup | 562 ++++++++ methods/disk.update | 71 + methods/floppy.desc.floppy | 9 + methods/floppy.install | 100 ++ methods/floppy.names | 1 + methods/floppy.setup | 76 + methods/floppy.update | 76 + methods/hd.setup | 90 ++ methods/hd.unpack | 36 + methods/hd.update | 37 + mkinstalldirs | 35 + scripts/Makefile.in | 79 ++ scripts/dpkg-divert.pl | 220 +++ scripts/dpkg-name.1 | 60 + scripts/dpkg-name.sh | 94 ++ scripts/dpkg-scanpackages.pl | 178 +++ scripts/install-info.8 | 245 ++++ scripts/install-info.pl | 342 +++++ scripts/lib.pl | 565 ++++++++ scripts/perl-dpkg.pl | 1482 ++++++++++++++++++++ scripts/start-stop-daemon.8 | 19 + scripts/start-stop-daemon.pl | 160 +++ scripts/update-alternatives.8 | 19 + scripts/update-alternatives.pl | 429 ++++++ scripts/update-rc.d.8 | 79 ++ scripts/update-rc.d.sh | 104 ++ split/Makefile.in | 80 ++ split/debugmake | 3 + split/dpkg-split.8 | 19 + split/dpkg-split.h | 72 + split/info.c | 230 ++++ split/join.c | 134 ++ split/junk | 36 + split/magic | 5 + split/main.c | 176 +++ split/mksplit | 88 ++ split/mksplit.pl | 88 ++ split/old-mksplit.sh | 113 ++ split/queue.c | 273 ++++ split/split.c | 71 + version.h | 1 + 189 files changed, 37746 insertions(+) create mode 100644 COPYING create mode 100644 INSTALL create mode 100644 Makefile.in create mode 100644 NEWS create mode 100644 README create mode 100644 TODO create mode 100644 acconfig.h create mode 100644 archtable create mode 100644 config.h.bot create mode 100644 config.h.in create mode 100755 configure create mode 100644 configure.in create mode 100644 debian.Changelog create mode 100644 debian.README create mode 100644 debian.control create mode 100644 debian.controlaout create mode 100755 debian.postinst create mode 100755 debian.preinst create mode 100644 debian.prerm create mode 100755 debian.rules create mode 100644 doc/Makefile.in create mode 100644 doc/auto-deconfiguration.txt create mode 100644 doc/database-structure.fig create mode 100644 doc/deb-control.5 create mode 100644 doc/deb.5 create mode 100644 doc/dependency-ordering.txt create mode 100644 doc/descriptions.txt create mode 100644 doc/disappear-replace.txt create mode 100644 doc/diversions.text create mode 100644 doc/dpkg.texi create mode 100644 doc/essential-flag.txt create mode 100644 doc/guidelines.info-1 create mode 100644 doc/guidelines.info-2 create mode 100644 doc/guidelines.texi create mode 100644 doc/guidelines.texi.beforeeric create mode 100644 doc/guidelines.texi.rej create mode 100644 doc/junk create mode 100644 doc/maintainer-script-args.txt create mode 100644 doc/upgrades+errors.txt create mode 100644 doc/version-ordering.txt create mode 100644 doc/virtual-dependencies.txt create mode 100644 dpkg-deb/Makefile.in create mode 100644 dpkg-deb/build.c create mode 100755 dpkg-deb/debugmake create mode 100644 dpkg-deb/dpkg-deb.8 create mode 100644 dpkg-deb/dpkg-deb.8-vuori create mode 100644 dpkg-deb/dpkg-deb.h create mode 100644 dpkg-deb/extract.c create mode 100644 dpkg-deb/info.c create mode 100644 dpkg-deb/main.c create mode 100644 dpkg-deb/mkdeb.sh create mode 100644 dselect/Makefile.in create mode 100644 dselect/basecmds.cc create mode 100644 dselect/baselist.cc create mode 100644 dselect/basetop.cc create mode 100644 dselect/bindings.cc create mode 100644 dselect/bindings.h create mode 100755 dselect/checkunimp.pl create mode 100644 dselect/curkeys.cc create mode 100755 dselect/debugmake create mode 100644 dselect/dselect.8 create mode 100644 dselect/dselect.h create mode 100644 dselect/helpmsgs.src create mode 100644 dselect/junk create mode 100644 dselect/keyoverride create mode 100644 dselect/keys.c create mode 100644 dselect/kt.c create mode 100644 dselect/kt.cc create mode 100644 dselect/main.cc create mode 100644 dselect/methkeys.cc create mode 100644 dselect/methlist.cc create mode 100644 dselect/method.cc create mode 100644 dselect/method.h create mode 100644 dselect/methparse.cc create mode 100755 dselect/mkcurkeys.pl create mode 100755 dselect/mkhelpmsgs.pl create mode 100644 dselect/pkgcmds.cc create mode 100644 dselect/pkgdepcon.cc create mode 100644 dselect/pkgdisplay.cc create mode 100644 dselect/pkginfo.cc create mode 100644 dselect/pkgkeys.cc create mode 100644 dselect/pkglist.cc create mode 100644 dselect/pkglist.h create mode 100644 dselect/pkgsublist.cc create mode 100644 dselect/pkgtop.cc create mode 100755 dselect/rp create mode 100644 dselect/t.c create mode 100644 include/Makefile.in create mode 100644 include/dpkg-db.h create mode 100644 include/dpkg.h create mode 100644 include/myopt.h create mode 100644 include/tarfn.h create mode 100755 insert-version.pl create mode 100755 install.sh create mode 100644 lib/Makefile.in create mode 100644 lib/compat.c create mode 100644 lib/database.c create mode 100644 lib/dbmodify.c create mode 100755 lib/debugmake create mode 100644 lib/dump.c create mode 100644 lib/ehandle.c create mode 100644 lib/fields.c create mode 100644 lib/lock.c create mode 100644 lib/mlib.c create mode 100644 lib/myopt.c create mode 100644 lib/nfmalloc.c create mode 100644 lib/parse.c create mode 100644 lib/parsedump.h create mode 100644 lib/parsehelp.c create mode 100644 lib/showcright.c create mode 100644 lib/star.c create mode 100644 lib/tarfn.c create mode 100644 lib/varbuf.c create mode 100644 lib/vercmp.c create mode 100644 main/Makefile.in create mode 100644 main/archives.c create mode 100644 main/archives.h create mode 100644 main/archtable.inc create mode 100644 main/cleanup.c create mode 100644 main/configure.c create mode 100755 main/debugmake create mode 100644 main/depcon.c create mode 100644 main/dpkg.8 create mode 100644 main/dpkg.8-vuori create mode 100644 main/enquiry.c create mode 100644 main/errors.c create mode 100644 main/filesdb.c create mode 100644 main/filesdb.h create mode 100644 main/help.c create mode 100644 main/junk create mode 100644 main/main.c create mode 100644 main/main.h create mode 100644 main/packages.c create mode 100644 main/processarc.c create mode 100644 main/remove.c create mode 100644 main/update.c create mode 100644 md5sum/Makefile.in create mode 100644 md5sum/md5.c create mode 100644 md5sum/md5.h create mode 100644 md5sum/md5sum.1 create mode 100644 md5sum/md5sum.c create mode 100644 methods/Makefile.in create mode 100644 methods/disk.desc.cdrom create mode 100644 methods/disk.desc.harddisk create mode 100644 methods/disk.desc.mounted create mode 100644 methods/disk.desc.nfs create mode 100644 methods/disk.install create mode 100644 methods/disk.names create mode 100644 methods/disk.setup create mode 100644 methods/disk.update create mode 100644 methods/floppy.desc.floppy create mode 100644 methods/floppy.install create mode 100644 methods/floppy.names create mode 100644 methods/floppy.setup create mode 100644 methods/floppy.update create mode 100755 methods/hd.setup create mode 100755 methods/hd.unpack create mode 100755 methods/hd.update create mode 100755 mkinstalldirs create mode 100644 scripts/Makefile.in create mode 100644 scripts/dpkg-divert.pl create mode 100644 scripts/dpkg-name.1 create mode 100755 scripts/dpkg-name.sh create mode 100755 scripts/dpkg-scanpackages.pl create mode 100644 scripts/install-info.8 create mode 100755 scripts/install-info.pl create mode 100644 scripts/lib.pl create mode 100755 scripts/perl-dpkg.pl create mode 100644 scripts/start-stop-daemon.8 create mode 100755 scripts/start-stop-daemon.pl create mode 100644 scripts/update-alternatives.8 create mode 100755 scripts/update-alternatives.pl create mode 100644 scripts/update-rc.d.8 create mode 100755 scripts/update-rc.d.sh create mode 100644 split/Makefile.in create mode 100755 split/debugmake create mode 100644 split/dpkg-split.8 create mode 100644 split/dpkg-split.h create mode 100644 split/info.c create mode 100644 split/join.c create mode 100644 split/junk create mode 100644 split/magic create mode 100644 split/main.c create mode 100755 split/mksplit create mode 100644 split/mksplit.pl create mode 100644 split/old-mksplit.sh create mode 100644 split/queue.c create mode 100644 split/split.c create mode 100644 version.h diff --git a/COPYING b/COPYING new file mode 100644 index 00000000..e77696ae --- /dev/null +++ b/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/INSTALL b/INSTALL new file mode 100644 index 00000000..8a7d026f --- /dev/null +++ b/INSTALL @@ -0,0 +1,146 @@ + This is a generic INSTALL file for utilities distributions. +If this package does not come with, e.g., installable documentation or +data files, please ignore the references to them below. + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation, and +creates the Makefile(s) (one in each subdirectory of the source +directory). In some packages it creates a C header file containing +system-dependent definitions. It also creates a file `config.status' +that you can run in the future to recreate the current configuration. + +To compile this package: + +1. Configure the package for your system. + + Normally, you just `cd' to the directory containing the package's +source code and type `./configure'. If you're using `csh' on an old +version of System V, you might need to type `sh configure' instead to +prevent `csh' from trying to execute `configure' itself. + + Running `configure' takes awhile. While it is running, it +prints some messages that tell what it is doing. If you don't want to +see any messages, run `configure' with its standard output redirected +to `/dev/null'; for example, `./configure >/dev/null'. + + To compile the package in a different directory from the one +containing the source code, you must use a version of `make' that +supports the `VPATH' variable, such as GNU `make'. `cd' to the +directory where you want the object files and executables to go and run +the `configure' script. `configure' automatically checks for the +source code in the directory that `configure' is in and in `..'. If +for some reason `configure' is not in the source code directory that +you are configuring, then it will report that it can't find the source +code. In that case, run `configure' with the option `--srcdir=DIR', +where DIR is the directory that contains the source code. + + By default, `make install' will install the package's files in +`/usr/local/bin', `/usr/local/man', etc. You can specify an +installation prefix other than `/usr/local' by giving `configure' the +option `--prefix=PATH'. Alternately, you can do so by consistently +giving a value for the `prefix' variable when you run `make', e.g., + make prefix=/usr/gnu + make prefix=/usr/gnu install + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +give `configure' the option `--exec-prefix=PATH' or set the `make' +variable `exec_prefix' to PATH, the package will use PATH as the prefix +for installing programs and libraries. Data files and documentation +will still use the regular prefix. Normally, all files are installed +using the same prefix. + + Some packages pay attention to `--with-PACKAGE' options to +`configure', where PACKAGE is something like `gnu-as' or `x' (for the +X Window System). They may also pay attention to `--enable-FEATURE' +options, where FEATURE indicates an optional part of the package. The +README should mention any `--with-' and `--enable-' options that the +package recognizes. + + `configure' also recognizes the following options: + +`--help' + Print a summary of the options to `configure', and exit. + +`--quiet' +`--silent' + Do not print messages saying which checks are being made. + +`--verbose' + Print the results of the checks. + +`--version' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`--x-includes=DIR' + X include files are in DIR. + +`--x-libraries=DIR' + X library files are in DIR. + + `configure' also accepts and ignores some other options. + + On systems that require unusual options for compilation or linking +that the package's `configure' script does not know about, you can give +`configure' initial values for variables by setting them in the +environment. In Bourne-compatible shells, you can do that on the +command line like this: + + CC='gcc -traditional' LIBS=-lposix ./configure + +On systems that have the `env' program, you can do it like this: + + env CC='gcc -traditional' LIBS=-lposix ./configure + + Here are the `make' variables that you might want to override with +environment variables when running `configure'. + + For these variables, any value given in the environment overrides the +value that `configure' would choose: + + - Variable: CC + C compiler program. The default is `cc'. + + - Variable: INSTALL + Program to use to install files. The default is `install' if you + have it, `cp' otherwise. + + For these variables, any value given in the environment is added to +the value that `configure' chooses: + + - Variable: DEFS + Configuration options, in the form `-Dfoo -Dbar...'. Do not use + this variable in packages that create a configuration header file. + + - Variable: LIBS + Libraries to link with, in the form `-lfoo -lbar...'. + + If you need to do unusual things to compile the package, we encourage +you to figure out how `configure' could check whether to do them, and +mail diffs or instructions to the address given in the README so we +can include them in the next release. + +2. Type `make' to compile the package. If you want, you can override +the `make' variables CFLAGS and LDFLAGS like this: + + make CFLAGS=-O2 LDFLAGS=-s + +3. If the package comes with self-tests and you want to run them, +type `make check'. If you're not sure whether there are any, try it; +if `make' responds with something like + make: *** No way to make target `check'. Stop. +then the package does not come with self-tests. + +4. Type `make install' to install programs, data files, and +documentation. + +5. You can remove the program binaries and object files from the +source directory by typing `make clean'. To also remove the +Makefile(s), the header file containing system-dependent definitions +(if the package uses one), and `config.status' (all the files that +`configure' created), type `make distclean'. + + The file `configure.in' is used to create `configure' by a program +called `autoconf'. You only need it if you want to regenerate +`configure' using a newer version of `autoconf'. diff --git a/Makefile.in b/Makefile.in new file mode 100644 index 00000000..40f01b68 --- /dev/null +++ b/Makefile.in @@ -0,0 +1,131 @@ +# Copyright (C) 1994 Ian Murdock +# Copyright (C) 1994,1995 Ian Jackson +# +# This 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, +# or (at your option) any later version. +# +# This 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 dpkg; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +srcdir = @srcdir@ +VPATH = @srcdir@ + +prefix = @prefix@ +exec_prefix = $(prefix) +docdir = $(prefix)/doc +devdocdir = $(docdir)/dpkg +copyingfile = $(docdir)/copyright/dpkg +infodir = $(prefix)/info +bindir = $(exec_prefix)/bin +sbindir = $(exec_prefix)/sbin +libdir = $(prefix)/lib +dpkglibdir = $(libdir)/dpkg +methodsdir = $(dpkglibdir)/methods +datadir = /var/lib/dpkg +methodsdatadir = $(datadir)/methods +methodsmnt = $(datadir)/methods/mnt +pinfodir = $(datadir)/info +pupdatesdir = $(datadir)/updates +altsdatadir = $(datadir)/alternatives +partsdir = $(datadir)/parts +mandir = $(prefix)/man +man1dir = $(mandir)/man1 +man5dir = $(mandir)/man5 +man8dir = $(mandir)/man8 +man1 = 1 +man5 = 5 +man8 = 8 +etcdir= /etc +altsetcdir = $(etcdir)/alternatives + +BOURNESHELL = /bin/sh + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ + +CC = @CC@ + +CFLAGS = @CFLAGS@ @CWARNS@ $(XCFLAGS) +LDFLAGS = $(XLDFLAGS) +ALL_CFLAGS = -I../include -I.. @DEFS@ $(CFLAGS) + +SUBDIRS = lib main dpkg-deb split md5sum scripts doc include dselect methods +PORTABLEDIRS = lib dpkg-deb split md5sum + +.SUFFIXES: .c .o + +.c.o: + $(CC) $(ALL_CFLAGS) -c $< + +all: version + set -e; for d in $(SUBDIRS) ; do \ + cd $$d ; \ + $(MAKE) 'CC=$(CC)' 'LDFLAGS=$(LDFLAGS)' 'XLIBS=$(XLIBS)'; \ + cd .. ; \ + done + +install: all + $(BOURNESHELL) $(srcdir)/mkinstalldirs $(bindir) $(sbindir) \ + $(man1dir) $(man5dir) $(man8dir) $(devdocdir) \ + $(libdir) $(datadir) $(pinfodir) $(pupdatesdir) \ + $(dpkglibdir) $(methodsdir) $(methodsdatadir) $(methodsmnt) \ + $(altsdatadir) $(altsetcdir) $(partsdir) $(infodir) + set -e; for d in $(SUBDIRS) ; do \ + cd $$d ; \ + $(MAKE) 'CC=$(CC)' 'LDFLAGS=$(LDFLAGS)' 'XLIBS=$(XLIBS)' \ + install ; \ + cd .. ; \ + done + $(INSTALL_DATA) COPYING $(copyingfile) + +portable: version + set -e; for d in lib dpkg-deb split md5sum ; do \ + cd $$d ; \ + $(MAKE) 'CC=$(CC)' 'LDFLAGS=$(LDFLAGS)' 'XLIBS=$(XLIBS)' ; \ + cd .. ; \ + done + +install-portable: portable + $(BOURNESHELL) $(srcdir)/mkinstalldirs $(bindir) \ + $(man1dir) $(man8dir) $(libdir) $(dpkglibdir) + set -e; for d in $(PORTABLEDIRS) ; do \ + cd $$d ; \ + $(MAKE) 'CC=$(CC)' 'LDFLAGS=$(LDFLAGS)' 'XLIBS=$(XLIBS)' \ + install ; \ + cd .. ; \ + done +# $(INSTALL_DATA) COPYING $(copyingfile) + +autoconf: + autoheader + autoconf + +clean: + set -e; for d in $(SUBDIRS) ; do \ + cd $$d ; \ + $(MAKE) clean ; \ + cd .. ; \ + done + rm -f core version.h.new + +distclean: clean + set -e; for d in $(SUBDIRS) ; do \ + cd $$d ; \ + $(MAKE) distclean ; \ + cd .. ; \ + done + rm -f Makefile *.orig *~ *.~* ./#*# + rm -f config.h config.status install config.cache config.log + +version: + perl insert-version.pl version.h.new + cmp -s version.h.new version.h || mv version.h.new version.h diff --git a/NEWS b/NEWS new file mode 100644 index 00000000..865bad74 --- /dev/null +++ b/NEWS @@ -0,0 +1,72 @@ +(-*- indented-text -*-) + + +CHANGES SINCE dpkg 0.93.7: + + * a Perl-based front-end with many new features has replaced dpkg.sh. + The new features are detailed below. dpkg.pl is not finished, + however, as it does not yet support CONFLICTS, DEPENDS, OPTIONAL, + or RECOMMENDED. All of the above will be supported in Debian 0.93. + + In addition, it does not yet support PARTS, but that may wait until + Debian 0.94. (In the meantime, we can create disk-sized packages by + hand.) + + dpkg.pl has been constructed from code originally by Matt Welsh, + with additions by Carl Streeter, Ian Murdock, and Ian Jackson. + +New features in dpkg.pl: + + * dpkg.pl can now manipulate many packages at a time; dpkg.sh could + only manipulate one package at a time. + + * dpkg.pl supports pre-installation, post-installation, pre-removal, + and post-removal scripts. During batch installation, + post-installation scripts are executed after all packages have + been installed. (dpkg.sh supported all of the above except the + batch installation feature, because dpkg.sh did not itself support + batch installation.) + + * dpkg.pl now supports the upgrading of packages. A newer version of + a package, when released, can be obtained and installed normally on + the system to replace an older, previously installed version of it, + all with a minimum of manuals steps. + + During the installation process, dpkg.pl determines whether or not + a version of the package is currently installed on the system. If + it is, it determines whether it is an older version than the + package being installed (in which case we are ``upgrading'', and + the installation proceeds after saving the configuration files and + removing the older version), or whether it is the same or a newer + version than the package being installed (in which case we are, for + whatever reason, attempting to install a package twice or + ``downgrading'', and dpkg.pl asks for confirmation). + + * dpkg.pl now supports automatic and intelligent configuration file + updates during the upgrade process. During a package upgrade, it + determines when a configuration file has changed since the last + installation and, for those that have, prompts the user whether to + keep the older or newer configuration file, providing them with + default responses. + +New features in dpkg-deb: + + * dpkg-util.deb has been renamed to dpkg-deb, for brevity. + + * dpkg-deb is much less verbose, and in most cases it says nothing + unless an error has occurred. The front-end is expected to provide + the necessary verbosity when appropriate. + + * `dpkg-deb --describe' now outputs the description only, without the + ``Description:'' label. + + * `dpkg-deb --list' output is now sorted by package name. + + * `dpkg-deb --remove' bug fix: in dpkg 0.93.7 (or earlier), some + directories did not get properly removed. This has been fixed. + + * `dpkg-deb --version' now outputs the version information only, + without the `Version:'' label, and without maintainer information. + + * `dpkg-deb --version' now provides the version of dpkg if no + argument is specified. diff --git a/README b/README new file mode 100644 index 00000000..5fd12aa6 --- /dev/null +++ b/README @@ -0,0 +1,23 @@ +-*- text -*- + +Please see the file `Guidelines' for the guidelines for creating and +maintaining Debian packages. dpkg Texinfo documentation and a manual +page will be available with the next release, but in the interest of +time I am releasing dpkg 0.93.8 without them. + +Please send bug reports and comments to . +Please be sure to put the following at the top of the message body: + +Package: dpkg + +This will enter your bug report in our tracking system, and will help +us to investigate and respond to your problem as promptly as possible. + +Please read the file `COPYING' for copying conditions. + +Please read the file `INSTALL' for installation instructions. + +Please read the file `NEWS' for a description of new features, etc. + +[ Note for users of GCC 2.7.0: you must compile at least + `dselect/main.cc' with only -O2, due to a bug in GCC. ] diff --git a/TODO b/TODO new file mode 100644 index 00000000..33418687 --- /dev/null +++ b/TODO @@ -0,0 +1,57 @@ +Here are some currently-known inadequacies: + +urgent + * Pre-Depends installation ordering + +done + * a.out version + * uncomment ELF preinst modification + * Replaces (auto-deselect for conflicts) + * Replaces (don't overwrite otherwise) + * compile with ELF GCC out of the box + * dpkg --print-architecture + * Architecture field check + * symlink rename change + +bugs that need to be fixed quickly + * version numbers not starting digit early. + * check description in dpkg-deb + * field overflow in dpkg --list + * _always_ show section in --yet-to-unpack + * automatically do --yet-to-unpack in installation methods + * check depending packages when installing new version. + * error handling from some dselect actions. 1399 + * Several things ought to be configurable but aren't. + * Filenames containing newlines. Conffile names containing spaces. + * dpkg --status for virtual packages + * dselect bottom window too large, and/or resize + * update-alternatives prompting. + * logging, both transcript logs (kept briefly) + and action logs (kept forever?) 957 + * start-stop-daemon process status check. 1480 + * remove old docs from /usr/doc/dpkg. + +other stuff unlikely to get done soon + * single maintainer script, and new package getting there first + * dpkg -s show something for virtual packages + * dpkg --listfiles should do better for multi-package files (pkg, pkg: ...) + * settable defaults for update-rc.d + * local conffiles + * hooks + * gzip -0 option for dpkg-deb + * There is no documentation. 1526 + * newbie interface to dselect. 1037 + * FTP installation method + * `template' file giving default selections of dselect. + * update-rc.d in C + * start-stop-daemon in C. 1670 + * dselect per-half focus and keybindings improvements. 1555 + * floppy map (where are the files) + * highlight or pre-sort or something new or changed packages, + during upgrade selection. + * how to change case of package names + * support for adding files to dpkg's file lists + * side-by-side version display + * `fake' or `null' packages + * --purge remove empty directories which used too contain conffiles + * conffiles handling options, including `replace removed files' diff --git a/acconfig.h b/acconfig.h new file mode 100644 index 00000000..0dae8ac0 --- /dev/null +++ b/acconfig.h @@ -0,0 +1,31 @@ +/* Additional tests: */ + +/* Define if inline functions a la GCC are available. */ +#undef HAVE_INLINE + +/* Define if sysinfo is available. */ +#undef HAVE_SYSINFO + +/* Define if __NR_sysinfo is available. */ +#undef HAVE_NRSYSINFO + +/* Define if inline functions a la GCC are available. */ +#undef HAVE_ALPHASORT_DECLARATION + +/* Define if function attributes a la GCC 2.5 and higher are available. */ +#undef HAVE_GNUC25_ATTRIB + +/* Define if constant functions a la GCC 2.5 and higher are available. */ +#undef HAVE_GNUC25_CONST + +/* Define if nonreturning functions a la GCC 2.5 and higher are available. */ +#undef HAVE_GNUC25_NORETURN + +/* Define if printf-format argument lists a la GCC are available. */ +#undef HAVE_GNUC25_PRINTFFORMAT + +/* Set this to the canonical Debian architecture string for this CPU type. */ +#undef ARCHITECTURE + +/* Set this to 1 to build new archives by default. */ +#define BUILDOLDPKGFORMAT 0 diff --git a/archtable b/archtable new file mode 100644 index 00000000..97a1cad5 --- /dev/null +++ b/archtable @@ -0,0 +1,21 @@ +# This file contains a table of known architecture strings, with +# things to map them to. `configure' will take the output of gcc +# --print-libgcc-file-name, strip off leading directories up to and +# including gcc-lib, strip off trailing /libgcc.a and trailing version +# number directory, and then strip off everything after the first +# hyphen. The idea is that you're left with this bit: +# $ gcc --print-libgcc-file-name +# /usr/lib/gcc-lib/i486-linux/2.7.2/libgcc.a +# ^^^^ +# This is then looked up in the table below, to find out what to map +# it to. If it isn't found then configure will print a warning and +# continue. You can override configure's ideas using --with-arch. + +i386 i386 +i486 i386 +i586 i386 +sparc sparc +alpha alpha +m68k m68k +arm arm +ppc ppc diff --git a/config.h.bot b/config.h.bot new file mode 100644 index 00000000..06751db0 --- /dev/null +++ b/config.h.bot @@ -0,0 +1,129 @@ + +/* These are from config.h.bot, pasted onto the end of config.h.in. */ + +#ifdef HAVE_SYS_CDEFS_H +#include +#endif + +/* Use the definitions: */ + +/* Give us an unsigned 32-bit data type. */ +#if SIZEOF_UNSIGNED_LONG==4 +#define UWORD32 unsigned long +#elif SIZEOF_UNSIGNED_INT==4 +#define UWORD32 unsigned int +#else +#error I do not know what to use for a UWORD32. +#endif + +/* The maximum length of a #! interpreter displayed by dpkg-deb. */ +#ifdef PATH_MAX +#define INTERPRETER_MAX PATH_MAX +#else +#define INTERPRETER_MAX 1024 +#endif + +/* GNU C attributes. */ +#ifndef FUNCATTR +#ifdef HAVE_GNUC25_ATTRIB +#define FUNCATTR(x) __attribute__(x) +#else +#define FUNCATTR(x) +#endif +#endif + +/* GNU C printf formats, or null. */ +#ifndef ATTRPRINTF +#ifdef HAVE_GNUC25_PRINTFFORMAT +#define ATTRPRINTF(si,tc) format(printf,si,tc) +#else +#define ATTRPRINTF(si,tc) +#endif +#endif +#ifndef PRINTFFORMAT +#define PRINTFFORMAT(si,tc) FUNCATTR((ATTRPRINTF(si,tc))) +#endif + +/* GNU C nonreturning functions, or null. */ +#ifndef ATTRNORETURN +#ifdef HAVE_GNUC25_NORETURN +#define ATTRNORETURN noreturn +#else +#define ATTRNORETURN +#endif +#endif +#ifndef NONRETURNING +#define NONRETURNING FUNCATTR((ATTRNORETURN)) +#endif + +/* Combination of both the above. */ +#ifndef NONRETURNPRINTFFORMAT +#define NONRETURNPRINTFFORMAT(si,tc) FUNCATTR((ATTRPRINTF(si,tc),ATTRNORETURN)) +#endif + +/* GNU C constant functions, or null. */ +#ifndef ATTRCONST +#ifdef HAVE_GNUC25_CONST +#define ATTRCONST const +#else +#define ATTRCONST +#endif +#endif +#ifndef CONSTANT +#define CONSTANT FUNCATTR((ATTRCONST)) +#endif + +/* Declare strerror if we don't have it already. */ +#ifndef HAVE_STRERROR +const char *strerror(int); +#endif + +/* Declare strsignal if we don't have it already. */ +#ifndef HAVE_STRSIGNAL +const char *strsignal(int); +#endif + +/* Declare scandir if we don't have it already. */ +#ifndef HAVE_SCANDIR +struct dirent; +int scandir(const char *dir, struct dirent ***namelist, + int (*select)(const struct dirent *), + int (*compar)(const void*, const void*)); +#endif + +/* Declare alphasort if we don't have it already. */ +#if !defined(HAVE_ALPHASORT) || !defined(HAVE_ALPHASORT_DECLARATION) +struct dirent; +int alphasort(const struct dirent *a, const struct dirent *b); +#endif + +/* Declare unsetenv if we don't have it already. */ +#ifndef HAVE_UNSETENV +void unsetenv(const char *x); +#endif + +/* Define strtoul if we don't have it already. */ +#ifndef HAVE_STRTOUL +#define strtoul strtol +#endif + +/* Sort out sysinfo */ +#if !defined(HAVE_SYSINFO) && defined(HAVE_NRSYSINFO) +#include +#include +#include +static inline _syscall1(int,sysinfo,struct sysinfo*,info) +#endif + +/* Define WCOREDUMP if we don't have it already - coredumps won't be + * detected, though. + */ +#ifndef WCOREDUMP +#define WCOREDUMP(x) 0 +#endif + +/* Set BUILDOLDPKGFORMAT to 1 to build old-format archives by default. + */ +#ifndef BUILDOLDPKGFORMAT +#define BUILDOLDPKGFORMAT 0 +#endif diff --git a/config.h.in b/config.h.in new file mode 100644 index 00000000..ad44f3f9 --- /dev/null +++ b/config.h.in @@ -0,0 +1,212 @@ +/* config.h.in. Generated automatically from configure.in by autoheader. */ + +/* Define to empty if the keyword does not work. */ +#undef const + +/* Define if you don't have vprintf but do have _doprnt. */ +#undef HAVE_DOPRNT + +/* Define if you have the vprintf function. */ +#undef HAVE_VPRINTF + +/* Define to `int' if doesn't define. */ +#undef mode_t + +/* Define to `int' if doesn't define. */ +#undef pid_t + +/* Define to `unsigned' if doesn't define. */ +#undef size_t + +/* Define if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* Define if your processor stores words with the most significant + byte first (like Motorola and SPARC, unlike Intel and VAX). */ +#undef WORDS_BIGENDIAN + +/* Define if inline functions a la GCC are available. */ +#undef HAVE_INLINE + +/* Define if sysinfo is available. */ +#undef HAVE_SYSINFO + +/* Define if __NR_sysinfo is available. */ +#undef HAVE_NRSYSINFO + +/* Define if inline functions a la GCC are available. */ +#undef HAVE_ALPHASORT_DECLARATION + +/* Define if function attributes a la GCC 2.5 and higher are available. */ +#undef HAVE_GNUC25_ATTRIB + +/* Define if constant functions a la GCC 2.5 and higher are available. */ +#undef HAVE_GNUC25_CONST + +/* Define if nonreturning functions a la GCC 2.5 and higher are available. */ +#undef HAVE_GNUC25_NORETURN + +/* Define if printf-format argument lists a la GCC are available. */ +#undef HAVE_GNUC25_PRINTFFORMAT + +/* Set this to the canonical Debian architecture string for this CPU type. */ +#undef ARCHITECTURE + +/* Set this to 1 to build new archives by default. */ +#define BUILDOLDPKGFORMAT 0 + +/* The number of bytes in a unsigned int. */ +#undef SIZEOF_UNSIGNED_INT + +/* The number of bytes in a unsigned long. */ +#undef SIZEOF_UNSIGNED_LONG + +/* Define if you have the alphasort function. */ +#undef HAVE_ALPHASORT + +/* Define if you have the scandir function. */ +#undef HAVE_SCANDIR + +/* Define if you have the strerror function. */ +#undef HAVE_STRERROR + +/* Define if you have the strsignal function. */ +#undef HAVE_STRSIGNAL + +/* Define if you have the strtoul function. */ +#undef HAVE_STRTOUL + +/* Define if you have the unsetenv function. */ +#undef HAVE_UNSETENV + +/* Define if you have the header file. */ +#undef HAVE_SYS_CDEFS_H + +/* These are from config.h.bot, pasted onto the end of config.h.in. */ + +#ifdef HAVE_SYS_CDEFS_H +#include +#endif + +/* Use the definitions: */ + +/* Give us an unsigned 32-bit data type. */ +#if SIZEOF_UNSIGNED_LONG==4 +#define UWORD32 unsigned long +#elif SIZEOF_UNSIGNED_INT==4 +#define UWORD32 unsigned int +#else +#error I do not know what to use for a UWORD32. +#endif + +/* The maximum length of a #! interpreter displayed by dpkg-deb. */ +#ifdef PATH_MAX +#define INTERPRETER_MAX PATH_MAX +#else +#define INTERPRETER_MAX 1024 +#endif + +/* GNU C attributes. */ +#ifndef FUNCATTR +#ifdef HAVE_GNUC25_ATTRIB +#define FUNCATTR(x) __attribute__(x) +#else +#define FUNCATTR(x) +#endif +#endif + +/* GNU C printf formats, or null. */ +#ifndef ATTRPRINTF +#ifdef HAVE_GNUC25_PRINTFFORMAT +#define ATTRPRINTF(si,tc) format(printf,si,tc) +#else +#define ATTRPRINTF(si,tc) +#endif +#endif +#ifndef PRINTFFORMAT +#define PRINTFFORMAT(si,tc) FUNCATTR((ATTRPRINTF(si,tc))) +#endif + +/* GNU C nonreturning functions, or null. */ +#ifndef ATTRNORETURN +#ifdef HAVE_GNUC25_NORETURN +#define ATTRNORETURN noreturn +#else +#define ATTRNORETURN +#endif +#endif +#ifndef NONRETURNING +#define NONRETURNING FUNCATTR((ATTRNORETURN)) +#endif + +/* Combination of both the above. */ +#ifndef NONRETURNPRINTFFORMAT +#define NONRETURNPRINTFFORMAT(si,tc) FUNCATTR((ATTRPRINTF(si,tc),ATTRNORETURN)) +#endif + +/* GNU C constant functions, or null. */ +#ifndef ATTRCONST +#ifdef HAVE_GNUC25_CONST +#define ATTRCONST const +#else +#define ATTRCONST +#endif +#endif +#ifndef CONSTANT +#define CONSTANT FUNCATTR((ATTRCONST)) +#endif + +/* Declare strerror if we don't have it already. */ +#ifndef HAVE_STRERROR +const char *strerror(int); +#endif + +/* Declare strsignal if we don't have it already. */ +#ifndef HAVE_STRSIGNAL +const char *strsignal(int); +#endif + +/* Declare scandir if we don't have it already. */ +#ifndef HAVE_SCANDIR +struct dirent; +int scandir(const char *dir, struct dirent ***namelist, + int (*select)(const struct dirent *), + int (*compar)(const void*, const void*)); +#endif + +/* Declare alphasort if we don't have it already. */ +#if !defined(HAVE_ALPHASORT) || !defined(HAVE_ALPHASORT_DECLARATION) +struct dirent; +int alphasort(const struct dirent *a, const struct dirent *b); +#endif + +/* Declare unsetenv if we don't have it already. */ +#ifndef HAVE_UNSETENV +void unsetenv(const char *x); +#endif + +/* Define strtoul if we don't have it already. */ +#ifndef HAVE_STRTOUL +#define strtoul strtol +#endif + +/* Sort out sysinfo */ +#if !defined(HAVE_SYSINFO) && defined(HAVE_NRSYSINFO) +#include +#include +#include +static inline _syscall1(int,sysinfo,struct sysinfo*,info) +#endif + +/* Define WCOREDUMP if we don't have it already - coredumps won't be + * detected, though. + */ +#ifndef WCOREDUMP +#define WCOREDUMP(x) 0 +#endif + +/* Set BUILDOLDPKGFORMAT to 1 to build old-format archives by default. + */ +#ifndef BUILDOLDPKGFORMAT +#define BUILDOLDPKGFORMAT 0 +#endif diff --git a/configure b/configure new file mode 100755 index 00000000..36f526a1 --- /dev/null +++ b/configure @@ -0,0 +1,2374 @@ +#! /bin/sh + +# Guess values for system-dependent variables and create Makefiles. +# Generated automatically using autoconf version 2.4 +# Copyright (C) 1992, 1993, 1994 Free Software Foundation, Inc. +# +# This configure script is free software; the Free Software Foundation +# gives unlimited permission to copy, distribute and modify it. + +# Defaults: +ac_help= +ac_default_prefix=/usr/local +# Any additions from configure.in: +ac_default_prefix=/usr +ac_help="$ac_help + --with-arch=value set/override (Debian) architecture value" +ac_help="$ac_help + --with-newdeb make dpkg-deb default to new archives" +ac_help="$ac_help + --with-olddeb make dpkg-deb default to old archives" +ac_help="$ac_help + --with-newdeb make dpkg-deb default to new archives" + +# Initialize some variables set by options. +# The variables have the same names as the options, with +# dashes changed to underlines. +build=NONE +cache_file=./config.cache +exec_prefix=NONE +host=NONE +no_create= +nonopt=NONE +no_recursion= +prefix=NONE +program_prefix=NONE +program_suffix=NONE +program_transform_name=s,x,x, +silent= +site= +srcdir= +target=NONE +verbose= +x_includes=NONE +x_libraries=NONE + +# Initialize some other variables. +subdirs= + +ac_prev= +for ac_option +do + + # If the previous option needs an argument, assign it. + if test -n "$ac_prev"; then + eval "$ac_prev=\$ac_option" + ac_prev= + continue + fi + + case "$ac_option" in + -*=*) ac_optarg=`echo "$ac_option" | sed 's/[-_a-zA-Z0-9]*=//'` ;; + *) ac_optarg= ;; + esac + + # Accept the important Cygnus configure options, so we can diagnose typos. + + case "$ac_option" in + + -build | --build | --buil | --bui | --bu | --b) + ac_prev=build ;; + -build=* | --build=* | --buil=* | --bui=* | --bu=* | --b=*) + build="$ac_optarg" ;; + + -cache-file | --cache-file | --cache-fil | --cache-fi \ + | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) + ac_prev=cache_file ;; + -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ + | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) + cache_file="$ac_optarg" ;; + + -disable-* | --disable-*) + ac_feature=`echo $ac_option|sed -e 's/-*disable-//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_feature| sed 's/[-a-zA-Z0-9_]//g'`"; then + { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; } + fi + ac_feature=`echo $ac_feature| sed 's/-/_/g'` + eval "enable_${ac_feature}=no" ;; + + -enable-* | --enable-*) + ac_feature=`echo $ac_option|sed -e 's/-*enable-//' -e 's/=.*//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_feature| sed 's/[-_a-zA-Z0-9]//g'`"; then + { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; } + fi + ac_feature=`echo $ac_feature| sed 's/-/_/g'` + case "$ac_option" in + *=*) ;; + *) ac_optarg=yes ;; + esac + eval "enable_${ac_feature}='$ac_optarg'" ;; + + -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ + | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ + | --exec | --exe | --ex) + ac_prev=exec_prefix ;; + -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ + | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ + | --exec=* | --exe=* | --ex=*) + exec_prefix="$ac_optarg" ;; + + -gas | --gas | --ga | --g) + # Obsolete; use --with-gas. + with_gas=yes ;; + + -help | --help | --hel | --he) + # Omit some internal or obsolete options to make the list less imposing. + # This message is too long to be a string in the A/UX 3.1 sh. + cat << EOF +Usage: configure [options] [host] +Options: [defaults in brackets after descriptions] +Configuration: + --cache-file=FILE cache test results in FILE + --help print this message + --no-create do not create output files + --quiet, --silent do not print \`checking...' messages + --version print the version of autoconf that created configure +Directory and file names: + --prefix=PREFIX install architecture-independent files in PREFIX + [$ac_default_prefix] + --exec-prefix=PREFIX install architecture-dependent files in PREFIX + [same as prefix] + --srcdir=DIR find the sources in DIR [configure dir or ..] + --program-prefix=PREFIX prepend PREFIX to installed program names + --program-suffix=SUFFIX append SUFFIX to installed program names + --program-transform-name=PROGRAM run sed PROGRAM on installed program names +Host type: + --build=BUILD configure for building on BUILD [BUILD=HOST] + --host=HOST configure for HOST [guessed] + --target=TARGET configure for TARGET [TARGET=HOST] +Features and packages: + --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) + --enable-FEATURE[=ARG] include FEATURE [ARG=yes] + --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] + --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) + --x-includes=DIR X include files are in DIR + --x-libraries=DIR X library files are in DIR +--enable and --with options recognized:$ac_help +EOF + exit 0 ;; + + -host | --host | --hos | --ho) + ac_prev=host ;; + -host=* | --host=* | --hos=* | --ho=*) + host="$ac_optarg" ;; + + -nfp | --nfp | --nf) + # Obsolete; use --without-fp. + with_fp=no ;; + + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c) + no_create=yes ;; + + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) + no_recursion=yes ;; + + -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) + ac_prev=prefix ;; + -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) + prefix="$ac_optarg" ;; + + -program-prefix | --program-prefix | --program-prefi | --program-pref \ + | --program-pre | --program-pr | --program-p) + ac_prev=program_prefix ;; + -program-prefix=* | --program-prefix=* | --program-prefi=* \ + | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) + program_prefix="$ac_optarg" ;; + + -program-suffix | --program-suffix | --program-suffi | --program-suff \ + | --program-suf | --program-su | --program-s) + ac_prev=program_suffix ;; + -program-suffix=* | --program-suffix=* | --program-suffi=* \ + | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) + program_suffix="$ac_optarg" ;; + + -program-transform-name | --program-transform-name \ + | --program-transform-nam | --program-transform-na \ + | --program-transform-n | --program-transform- \ + | --program-transform | --program-transfor \ + | --program-transfo | --program-transf \ + | --program-trans | --program-tran \ + | --progr-tra | --program-tr | --program-t) + ac_prev=program_transform_name ;; + -program-transform-name=* | --program-transform-name=* \ + | --program-transform-nam=* | --program-transform-na=* \ + | --program-transform-n=* | --program-transform-=* \ + | --program-transform=* | --program-transfor=* \ + | --program-transfo=* | --program-transf=* \ + | --program-trans=* | --program-tran=* \ + | --progr-tra=* | --program-tr=* | --program-t=*) + program_transform_name="$ac_optarg" ;; + + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + silent=yes ;; + + -site | --site | --sit) + ac_prev=site ;; + -site=* | --site=* | --sit=*) + site="$ac_optarg" ;; + + -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) + ac_prev=srcdir ;; + -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) + srcdir="$ac_optarg" ;; + + -target | --target | --targe | --targ | --tar | --ta | --t) + ac_prev=target ;; + -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) + target="$ac_optarg" ;; + + -v | -verbose | --verbose | --verbos | --verbo | --verb) + verbose=yes ;; + + -version | --version | --versio | --versi | --vers) + echo "configure generated by autoconf version 2.4" + exit 0 ;; + + -with-* | --with-*) + ac_package=`echo $ac_option|sed -e 's/-*with-//' -e 's/=.*//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_package| sed 's/[-_a-zA-Z0-9]//g'`"; then + { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; } + fi + ac_package=`echo $ac_package| sed 's/-/_/g'` + case "$ac_option" in + *=*) ;; + *) ac_optarg=yes ;; + esac + eval "with_${ac_package}='$ac_optarg'" ;; + + -without-* | --without-*) + ac_package=`echo $ac_option|sed -e 's/-*without-//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_package| sed 's/[-a-zA-Z0-9_]//g'`"; then + { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; } + fi + ac_package=`echo $ac_package| sed 's/-/_/g'` + eval "with_${ac_package}=no" ;; + + --x) + # Obsolete; use --with-x. + with_x=yes ;; + + -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ + | --x-incl | --x-inc | --x-in | --x-i) + ac_prev=x_includes ;; + -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ + | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) + x_includes="$ac_optarg" ;; + + -x-libraries | --x-libraries | --x-librarie | --x-librari \ + | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) + ac_prev=x_libraries ;; + -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ + | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) + x_libraries="$ac_optarg" ;; + + -*) { echo "configure: error: $ac_option: invalid option; use --help to show usage" 1>&2; exit 1; } + ;; + + *) + if test -n "`echo $ac_option| sed 's/[-a-z0-9.]//g'`"; then + echo "configure: warning: $ac_option: invalid host type" 1>&2 + fi + if test "x$nonopt" != xNONE; then + { echo "configure: error: can only configure for one host and one target at a time" 1>&2; exit 1; } + fi + nonopt="$ac_option" + ;; + + esac +done + +if test -n "$ac_prev"; then + { echo "configure: error: missing argument to --`echo $ac_prev | sed 's/_/-/g'`" 1>&2; exit 1; } +fi + +trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15 + +# File descriptor usage: +# 0 standard input +# 1 file creation +# 2 errors and warnings +# 3 some systems may open it to /dev/tty +# 4 used on the Kubota Titan +# 6 checking for... messages and results +# 5 compiler messages saved in config.log +if test "$silent" = yes; then + exec 6>/dev/null +else + exec 6>&1 +fi +exec 5>./config.log + +echo "\ +This file contains any messages produced by compilers while +running configure, to aid debugging if configure makes a mistake. +" 1>&5 + +# Strip out --no-create and --no-recursion so they do not pile up. +# Also quote any args containing shell metacharacters. +ac_configure_args= +for ac_arg +do + case "$ac_arg" in + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c) ;; + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) ;; + *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?]*) + ac_configure_args="$ac_configure_args '$ac_arg'" ;; + *) ac_configure_args="$ac_configure_args $ac_arg" ;; + esac +done + +# NLS nuisances. +# Only set LANG and LC_ALL to C if already set. +# These must not be set unconditionally because not all systems understand +# e.g. LANG=C (notably SCO). +if test "${LC_ALL+set}" = set; then LC_ALL=C; export LC_ALL; fi +if test "${LANG+set}" = set; then LANG=C; export LANG; fi + +# confdefs.h avoids OS command line length limits that DEFS can exceed. +rm -rf conftest* confdefs.h +# AIX cpp loses on an empty file, so make sure it contains at least a newline. +echo > confdefs.h + +# A filename unique to this package, relative to the directory that +# configure is in, which we can look for to find out if srcdir is correct. +ac_unique_file=include/dpkg.h + +# Find the source files, if location was not specified. +if test -z "$srcdir"; then + ac_srcdir_defaulted=yes + # Try the directory containing this script, then its parent. + ac_prog=$0 + ac_confdir=`echo $ac_prog|sed 's%/[^/][^/]*$%%'` + test "x$ac_confdir" = "x$ac_prog" && ac_confdir=. + srcdir=$ac_confdir + if test ! -r $srcdir/$ac_unique_file; then + srcdir=.. + fi +else + ac_srcdir_defaulted=no +fi +if test ! -r $srcdir/$ac_unique_file; then + if test "$ac_srcdir_defaulted" = yes; then + { echo "configure: error: can not find sources in $ac_confdir or .." 1>&2; exit 1; } + else + { echo "configure: error: can not find sources in $srcdir" 1>&2; exit 1; } + fi +fi +srcdir=`echo "${srcdir}" | sed 's%\([^/]\)/*$%\1%'` + +# Prefer explicitly selected file to automatically selected ones. +if test -z "$CONFIG_SITE"; then + if test "x$prefix" != xNONE; then + CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site" + else + CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site" + fi +fi +for ac_site_file in $CONFIG_SITE; do + if test -r "$ac_site_file"; then + echo "loading site script $ac_site_file" + . "$ac_site_file" + fi +done + +if test -r "$cache_file"; then + echo "loading cache $cache_file" + . $cache_file +else + echo "creating cache $cache_file" + > $cache_file +fi + +ac_ext=c +# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CPP $CPPFLAGS' +ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5 2>&5' +ac_link='${CC-cc} -o conftest $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5 2>&5' + +if (echo "testing\c"; echo 1,2,3) | grep c >/dev/null; then + # Stardent Vistra SVR4 grep lacks -e, says ghazi@caip.rutgers.edu. + if (echo -n testing; echo 1,2,3) | sed s/-n/xn/ | grep xn >/dev/null; then + ac_n= ac_c=' +' ac_t=' ' + else + ac_n=-n ac_c= ac_t= + fi +else + ac_n= ac_c='\c' ac_t= +fi + + + + + + +# Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:" + for ac_dir in $PATH; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_prog_CC="gcc" + break + fi + done + IFS="$ac_save_ifs" + test -z "$ac_cv_prog_CC" && ac_cv_prog_CC="cc" +fi +fi +CC="$ac_cv_prog_CC" +if test -n "$CC"; then + echo "$ac_t""$CC" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + + +echo $ac_n "checking whether we are using GNU C""... $ac_c" 1>&6 +if eval "test \"`echo '$''{'ac_cv_prog_gcc'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.c <&5 | egrep yes >/dev/null 2>&1; then + ac_cv_prog_gcc=yes +else + ac_cv_prog_gcc=no +fi +fi +echo "$ac_t""$ac_cv_prog_gcc" 1>&6 +if test $ac_cv_prog_gcc = yes; then + GCC=yes + if test "${CFLAGS+set}" != set; then + echo $ac_n "checking whether ${CC-cc} accepts -g""... $ac_c" 1>&6 +if eval "test \"`echo '$''{'ac_cv_prog_gcc_g'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + echo 'void f(){}' > conftest.c +if test -z "`${CC-cc} -g -c conftest.c 2>&1`"; then + ac_cv_prog_gcc_g=yes +else + ac_cv_prog_gcc_g=no +fi +rm -f conftest* + +fi + echo "$ac_t""$ac_cv_prog_gcc_g" 1>&6 + if test $ac_cv_prog_gcc_g = yes; then + CFLAGS="-g -O" + else + CFLAGS="-O" + fi + fi +else + GCC= + test "${CFLAGS+set}" = set || CFLAGS="-g" +fi + +for ac_prog in $CCC c++ g++ gcc CC cxx +do +# Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +if eval "test \"`echo '$''{'ac_cv_prog_CXX'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$CXX"; then + ac_cv_prog_CXX="$CXX" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:" + for ac_dir in $PATH; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_prog_CXX="$ac_prog" + break + fi + done + IFS="$ac_save_ifs" +fi +fi +CXX="$ac_cv_prog_CXX" +if test -n "$CXX"; then + echo "$ac_t""$CXX" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + +test -n "$CXX" && break +done +test -n "$CXX" || CXX="gcc" + + +echo $ac_n "checking whether we are using GNU C++""... $ac_c" 1>&6 +if eval "test \"`echo '$''{'ac_cv_prog_gxx'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.C <&5 | egrep yes >/dev/null 2>&1; then + ac_cv_prog_gxx=yes +else + ac_cv_prog_gxx=no +fi +fi +echo "$ac_t""$ac_cv_prog_gxx" 1>&6 +if test $ac_cv_prog_gxx = yes; then + GXX=yes + if test "${CXXFLAGS+set}" != set; then + echo $ac_n "checking whether ${CXX-g++} accepts -g""... $ac_c" 1>&6 +if eval "test \"`echo '$''{'ac_cv_prog_gxx_g'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + echo 'void f(){}' > conftest.cc +if test -z "`${CXX-g++} -g -c conftest.cc 2>&1`"; then + ac_cv_prog_gxx_g=yes +else + ac_cv_prog_gxx_g=no +fi +rm -f conftest* + +fi + echo "$ac_t""$ac_cv_prog_gxx_g" 1>&6 + if test $ac_cv_prog_gxx_g = yes; then + CXXFLAGS="-g -O" + else + CXXFLAGS="-O" + fi + fi +else + GXX= + test "${CXXFLAGS+set}" = set || CXXFLAGS="-g" +fi + + +# Check whether --with-arch or --without-arch was given. +withval="$with_arch" +if test -n "$withval"; then + + if test "x$with_arch" = x; then + { echo "configure: error: --with-arch requires an architecture name" 1>&2; exit 1; } + fi + cat >> confdefs.h <&6 + dpkg_archwhy='' + if eval "test \"`echo '$''{'dpkg_cv_arch'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + + dpkg_arch="`dpkg --print-architecture 2>/dev/null`" + if test "x$dpkg_arch" != x; then + dpkg_archwhy=" (obtained from dpkg)" + elif test "${GCC-no}" = yes; then + dpkg_arch="`$(CC) --print-libgcc-file-name | + sed -e 's,^.*/gcc-lib/,,; s,/libgcc\.a$,,; s,/[0-9.][0-9.]*$,,; s/-.*$//'`" + if test "x`echo \"$dpkg_arch\" | tr -d a-z0-9-`" != x -o "x$dpkg_arch" = x + then + dpkg_arch="" + dpkg_archwhy=" (bad output from --print-libgcc-file-name)" + else + dpkg_arch2="`sed -e '/^'$dpkg_arch'[ ]/!d; s/^[-0-9a-z]*[ ]*//' \ + $srcdir/archtable`" + if test "x$dpkg_arch2" = "x$dpkg_arch"; then + dpkg_archwhy=" (obtained from gcc)" + elif test "x$dpkg_arch2" != x; then + dpkg_archwhy=" (from gcc, canonical form of $dpkg_arch)" + dpkg_arch="$dpkg_arch2" + else + dpkg_archwhy=" (from gcc, but not in archtable)" + fi + fi + else + dpkg_archwhy='' + fi + dpkg_cv_arch="$dpkg_arch" + +fi + + if test "x$dpkg_cv_arch" != x; then + echo "$ac_t"""$dpkg_cv_arch$dpkg_archwhy"" 1>&6 + cat >> confdefs.h <&6 + fi + +fi + + +# Check whether --with-newdeb or --without-newdeb was given. +withval="$with_newdeb" +if test -n "$withval"; then + + cat >> confdefs.h <<\EOF +#define BUILDOLDPKGFORMAT 0 +EOF + + +else + + # Check whether --with-olddeb or --without-olddeb was given. +withval="$with_olddeb" +if test -n "$withval"; then + + cat >> confdefs.h <<\EOF +#define BUILDOLDPKGFORMAT 1 +EOF + + +else + + case "$CC" in + /*) + pathccompiler="$CC" + ;; + *) + # Extract the first word of ""$CC"", so it can be a program name with args. +set dummy "$CC"; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +if eval "test \"`echo '$''{'ac_cv_path_pathccompiler'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + case "$pathccompiler" in + /*) + ac_cv_path_pathccompiler="$pathccompiler" # Let the user override the test with a path. + ;; + *) + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:" + for ac_dir in $PATH; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_path_pathccompiler="$ac_dir/$ac_word" + break + fi + done + IFS="$ac_save_ifs" + ;; +esac +fi +pathccompiler="$ac_cv_path_pathccompiler" +if test -n "$pathccompiler"; then + echo "$ac_t""$pathccompiler" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + + ;; + esac + echo $ac_n "checking .deb format to build by default""... $ac_c" 1>&6 + if file "$pathccompiler" 2>/dev/null | grep ELF >/dev/null 2>&1; then + echo "$ac_t""new (C compiler looks like ELF)" 1>&6 + cat >> confdefs.h <<\EOF +#define BUILDOLDPKGFORMAT 0 +EOF + + else + echo "$ac_t""old (C compiler not ELF)" 1>&6 + cat >> confdefs.h <<\EOF +#define BUILDOLDPKGFORMAT 1 +EOF + + fi + +fi + +fi + + +# Check whether --with-newdeb or --without-newdeb was given. +withval="$with_newdeb" +if test -n "$withval"; then + cat >> confdefs.h <<\EOF +#define BUILDOLDPKGFORMAT 0 +EOF + +fi + + + +echo $ac_n "checking for /usr/bin/perl""... $ac_c" 1>&6 +if test -f /usr/bin/perl +then + echo "$ac_t""yes" 1>&6 + perlpath=/usr/bin/perl + +else + echo "$ac_t""no" 1>&6 + # Extract the first word of "perl", so it can be a program name with args. +set dummy perl; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +if eval "test \"`echo '$''{'ac_cv_path_perlpath'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + case "$perlpath" in + /*) + ac_cv_path_perlpath="$perlpath" # Let the user override the test with a path. + ;; + *) + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:" + for ac_dir in $PATH; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_path_perlpath="$ac_dir/$ac_word" + break + fi + done + IFS="$ac_save_ifs" + ;; +esac +fi +perlpath="$ac_cv_path_perlpath" +if test -n "$perlpath"; then + echo "$ac_t""$perlpath" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + +fi + +echo $ac_n "checking how to run the C preprocessor""... $ac_c" 1>&6 +# On Suns, sometimes $CPP names a directory. +if test -n "$CPP" && test -d "$CPP"; then + CPP= +fi +if test -z "$CPP"; then +if eval "test \"`echo '$''{'ac_cv_prog_CPP'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + # This must be in double quotes, not single quotes, because CPP may get + # substituted into the Makefile and "${CC-cc}" will confuse make. + CPP="${CC-cc} -E" + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. + cat > conftest.$ac_ext < +Syntax Error +EOF +eval "$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +ac_err=`grep -v '^ *+' conftest.out` +if test -z "$ac_err"; then + : +else + echo "$ac_err" >&5 + rm -rf conftest* + CPP="${CC-cc} -E -traditional-cpp" + cat > conftest.$ac_ext < +Syntax Error +EOF +eval "$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +ac_err=`grep -v '^ *+' conftest.out` +if test -z "$ac_err"; then + : +else + echo "$ac_err" >&5 + rm -rf conftest* + CPP=/lib/cpp +fi +rm -f conftest* +fi +rm -f conftest* + ac_cv_prog_CPP="$CPP" +fi + CPP="$ac_cv_prog_CPP" +else + ac_cv_prog_CPP="$CPP" +fi +echo "$ac_t""$CPP" 1>&6 + +# If we cannot run a trivial program, we must be cross compiling. +echo $ac_n "checking whether cross-compiling""... $ac_c" 1>&6 +if eval "test \"`echo '$''{'ac_cv_c_cross'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test "$cross_compiling" = yes; then + ac_cv_c_cross=yes +else +cat > conftest.$ac_ext </dev/null; then + ac_cv_c_cross=no +else + ac_cv_c_cross=yes +fi +fi +rm -fr conftest* +fi +cross_compiling=$ac_cv_c_cross +echo "$ac_t""$ac_cv_c_cross" 1>&6 + +echo $ac_n "checking for ANSI C header files""... $ac_c" 1>&6 +if eval "test \"`echo '$''{'ac_cv_header_stdc'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +#include +#include +#include +EOF +eval "$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +ac_err=`grep -v '^ *+' conftest.out` +if test -z "$ac_err"; then + rm -rf conftest* + ac_cv_header_stdc=yes +else + echo "$ac_err" >&5 + rm -rf conftest* + ac_cv_header_stdc=no +fi +rm -f conftest* + +if test $ac_cv_header_stdc = yes; then + # SunOS 4.x string.h does not declare mem*, contrary to ANSI. +cat > conftest.$ac_ext < +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "memchr" >/dev/null 2>&1; then + : +else + rm -rf conftest* + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. +cat > conftest.$ac_ext < +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "free" >/dev/null 2>&1; then + : +else + rm -rf conftest* + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. +if test "$cross_compiling" = yes; then + ac_cv_header_stdc=no +else +cat > conftest.$ac_ext < +#define ISLOWER(c) ('a' <= (c) && (c) <= 'z') +#define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) +#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) +int main () { int i; for (i = 0; i < 256; i++) +if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) exit(2); +exit (0); } + +EOF +eval $ac_link +if test -s conftest && (./conftest; exit) 2>/dev/null; then + : +else + ac_cv_header_stdc=no +fi +fi +rm -fr conftest* +fi +fi +echo "$ac_t""$ac_cv_header_stdc" 1>&6 +if test $ac_cv_header_stdc = yes; then + cat >> confdefs.h <<\EOF +#define STDC_HEADERS 1 +EOF + +fi + +ac_aux_dir= +for ac_dir in $srcdir $srcdir/.. $srcdir/../..; do + if test -f $ac_dir/install-sh; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install-sh -c" + break + elif test -f $ac_dir/install.sh; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install.sh -c" + break + fi +done +if test -z "$ac_aux_dir"; then + { echo "configure: error: can not find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." 1>&2; exit 1; } +fi +ac_config_guess=$ac_aux_dir/config.guess +ac_config_sub=$ac_aux_dir/config.sub +ac_configure=$ac_aux_dir/configure # This should be Cygnus configure. + +# Find a good install program. We prefer a C program (faster), +# so one script is as good as another. But avoid the broken or +# incompatible versions: +# SysV /etc/install, /usr/sbin/install +# SunOS /usr/etc/install +# IRIX /sbin/install +# AIX /bin/install +# AFS /usr/afsws/bin/install, which mishandles nonexistent args +# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" +# ./install, which can be erroneously created by make from ./install.sh. +echo $ac_n "checking for a BSD compatible install""... $ac_c" 1>&6 +if test -z "$INSTALL"; then +if eval "test \"`echo '$''{'ac_cv_path_install'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:" + for ac_dir in $PATH; do + # Account for people who put trailing slashes in PATH elements. + case "$ac_dir/" in + /|./|.//|/etc/*|/usr/sbin/*|/usr/etc/*|/sbin/*|/usr/afsws/bin/*|/usr/ucb/*) ;; + *) + # OSF1 and SCO ODT 3.0 have their own names for install. + for ac_prog in ginstall installbsd scoinst install; do + if test -f $ac_dir/$ac_prog; then + if test $ac_prog = install && + grep dspmsg $ac_dir/$ac_prog >/dev/null 2>&1; then + # AIX install. It has an incompatible calling convention. + # OSF/1 installbsd also uses dspmsg, but is usable. + : + else + ac_cv_path_install="$ac_dir/$ac_prog -c" + break 2 + fi + fi + done + ;; + esac + done + IFS="$ac_save_ifs" + # As a last resort, use the slow shell script. + test -z "$ac_cv_path_install" && ac_cv_path_install="$ac_install_sh" +fi + INSTALL="$ac_cv_path_install" +fi +echo "$ac_t""$INSTALL" 1>&6 + +# Use test -z because SunOS4 sh mishandles braces in ${var-val}. +# It thinks the first close brace ends the variable substitution. +test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' + +test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' + +# Extract the first word of "ranlib", so it can be a program name with args. +set dummy ranlib; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +if eval "test \"`echo '$''{'ac_cv_prog_RANLIB'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$RANLIB"; then + ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:" + for ac_dir in $PATH; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_prog_RANLIB="ranlib" + break + fi + done + IFS="$ac_save_ifs" + test -z "$ac_cv_prog_RANLIB" && ac_cv_prog_RANLIB=":" +fi +fi +RANLIB="$ac_cv_prog_RANLIB" +if test -n "$RANLIB"; then + echo "$ac_t""$RANLIB" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + +echo $ac_n "checking for mode_t""... $ac_c" 1>&6 +if eval "test \"`echo '$''{'ac_cv_type_mode_t'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +#if STDC_HEADERS +#include +#endif +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "mode_t" >/dev/null 2>&1; then + rm -rf conftest* + ac_cv_type_mode_t=yes +else + rm -rf conftest* + ac_cv_type_mode_t=no +fi +rm -f conftest* + +fi +echo "$ac_t""$ac_cv_type_mode_t" 1>&6 +if test $ac_cv_type_mode_t = no; then + cat >> confdefs.h <<\EOF +#define mode_t int +EOF + +fi + +echo $ac_n "checking for pid_t""... $ac_c" 1>&6 +if eval "test \"`echo '$''{'ac_cv_type_pid_t'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +#if STDC_HEADERS +#include +#endif +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "pid_t" >/dev/null 2>&1; then + rm -rf conftest* + ac_cv_type_pid_t=yes +else + rm -rf conftest* + ac_cv_type_pid_t=no +fi +rm -f conftest* + +fi +echo "$ac_t""$ac_cv_type_pid_t" 1>&6 +if test $ac_cv_type_pid_t = no; then + cat >> confdefs.h <<\EOF +#define pid_t int +EOF + +fi + +echo $ac_n "checking for size_t""... $ac_c" 1>&6 +if eval "test \"`echo '$''{'ac_cv_type_size_t'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +#if STDC_HEADERS +#include +#endif +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "size_t" >/dev/null 2>&1; then + rm -rf conftest* + ac_cv_type_size_t=yes +else + rm -rf conftest* + ac_cv_type_size_t=no +fi +rm -f conftest* + +fi +echo "$ac_t""$ac_cv_type_size_t" 1>&6 +if test $ac_cv_type_size_t = no; then + cat >> confdefs.h <<\EOF +#define size_t unsigned +EOF + +fi + +echo $ac_n "checking for vprintf""... $ac_c" 1>&6 +if eval "test \"`echo '$''{'ac_cv_func_vprintf'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +/* Override any gcc2 internal prototype to avoid an error. */ +char vprintf(); + +int main() { return 0; } +int t() { + +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_vprintf) || defined (__stub___vprintf) +choke me +#else +vprintf(); +#endif + +; return 0; } +EOF +if eval $ac_link; then + rm -rf conftest* + eval "ac_cv_func_vprintf=yes" +else + rm -rf conftest* + eval "ac_cv_func_vprintf=no" +fi +rm -f conftest* + +fi +if eval "test \"`echo '$ac_cv_func_'vprintf`\" = yes"; then + echo "$ac_t""yes" 1>&6 + cat >> confdefs.h <<\EOF +#define HAVE_VPRINTF 1 +EOF + +else + echo "$ac_t""no" 1>&6 +fi + +if test "$ac_cv_func_vprintf" != yes; then +echo $ac_n "checking for _doprnt""... $ac_c" 1>&6 +if eval "test \"`echo '$''{'ac_cv_func__doprnt'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +/* Override any gcc2 internal prototype to avoid an error. */ +char _doprnt(); + +int main() { return 0; } +int t() { + +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub__doprnt) || defined (__stub____doprnt) +choke me +#else +_doprnt(); +#endif + +; return 0; } +EOF +if eval $ac_link; then + rm -rf conftest* + eval "ac_cv_func__doprnt=yes" +else + rm -rf conftest* + eval "ac_cv_func__doprnt=no" +fi +rm -f conftest* + +fi +if eval "test \"`echo '$ac_cv_func_'_doprnt`\" = yes"; then + echo "$ac_t""yes" 1>&6 + cat >> confdefs.h <<\EOF +#define HAVE_DOPRNT 1 +EOF + +else + echo "$ac_t""no" 1>&6 +fi + +fi + +echo $ac_n "checking for working const""... $ac_c" 1>&6 +if eval "test \"`echo '$''{'ac_cv_c_const'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <j = 5; +} +{ /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */ + const int foo = 10; +} + +; return 0; } +EOF +if eval $ac_compile; then + rm -rf conftest* + ac_cv_c_const=yes +else + rm -rf conftest* + ac_cv_c_const=no +fi +rm -f conftest* + +fi +echo "$ac_t""$ac_cv_c_const" 1>&6 +if test $ac_cv_c_const = no; then + cat >> confdefs.h <<\EOF +#define const +EOF + +fi + +echo $ac_n "checking whether byte ordering is bigendian""... $ac_c" 1>&6 +if eval "test \"`echo '$''{'ac_cv_c_bigendian'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_cv_c_bigendian=unknown +# See if sys/param.h defines the BYTE_ORDER macro. +cat > conftest.$ac_ext < +#include +int main() { return 0; } +int t() { + +#if !BYTE_ORDER || !BIG_ENDIAN || !LITTLE_ENDIAN + bogus endian macros +#endif +; return 0; } +EOF +if eval $ac_compile; then + rm -rf conftest* + # It does; now see whether it defined to BIG_ENDIAN or not. +cat > conftest.$ac_ext < +#include +int main() { return 0; } +int t() { + +#if BYTE_ORDER != BIG_ENDIAN + not big endian +#endif +; return 0; } +EOF +if eval $ac_compile; then + rm -rf conftest* + ac_cv_c_bigendian=yes +else + rm -rf conftest* + ac_cv_c_bigendian=no +fi +rm -f conftest* + +fi +rm -f conftest* + +if test $ac_cv_c_bigendian = unknown; then +if test "$cross_compiling" = yes; then + { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; } +else +cat > conftest.$ac_ext </dev/null; then + ac_cv_c_bigendian=no +else + ac_cv_c_bigendian=yes +fi +fi +rm -fr conftest* +fi +fi +echo "$ac_t""$ac_cv_c_bigendian" 1>&6 +if test $ac_cv_c_bigendian = yes; then + cat >> confdefs.h <<\EOF +#define WORDS_BIGENDIAN 1 +EOF + +fi + +echo $ac_n "checking size of unsigned long""... $ac_c" 1>&6 +if eval "test \"`echo '$''{'ac_cv_sizeof_unsigned_long'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test "$cross_compiling" = yes; then + { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; } +else +cat > conftest.$ac_ext < +main() +{ + FILE *f=fopen("conftestval", "w"); + if (!f) exit(1); + fprintf(f, "%d\n", sizeof(unsigned long)); + exit(0); +} +EOF +eval $ac_link +if test -s conftest && (./conftest; exit) 2>/dev/null; then + ac_cv_sizeof_unsigned_long=`cat conftestval` +else + ac_cv_sizeof_unsigned_long=0 +fi +fi +rm -fr conftest* +fi +echo "$ac_t""$ac_cv_sizeof_unsigned_long" 1>&6 +cat >> confdefs.h <&6 +if eval "test \"`echo '$''{'ac_cv_sizeof_unsigned_int'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test "$cross_compiling" = yes; then + { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; } +else +cat > conftest.$ac_ext < +main() +{ + FILE *f=fopen("conftestval", "w"); + if (!f) exit(1); + fprintf(f, "%d\n", sizeof(unsigned int)); + exit(0); +} +EOF +eval $ac_link +if test -s conftest && (./conftest; exit) 2>/dev/null; then + ac_cv_sizeof_unsigned_int=`cat conftestval` +else + ac_cv_sizeof_unsigned_int=0 +fi +fi +rm -fr conftest* +fi +echo "$ac_t""$ac_cv_sizeof_unsigned_int" 1>&6 +cat >> confdefs.h <&6 +if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +/* Override any gcc2 internal prototype to avoid an error. */ +char $ac_func(); + +int main() { return 0; } +int t() { + +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +$ac_func(); +#endif + +; return 0; } +EOF +if eval $ac_link; then + rm -rf conftest* + eval "ac_cv_func_$ac_func=yes" +else + rm -rf conftest* + eval "ac_cv_func_$ac_func=no" +fi +rm -f conftest* + +fi +if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_func=HAVE_`echo $ac_func | tr '[a-z]' '[A-Z]'` + cat >> confdefs.h <&6 +fi +done + +for ac_hdr in sys/cdefs.h +do +ac_safe=`echo "$ac_hdr" | tr './\055' '___'` +echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6 +if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +EOF +eval "$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +ac_err=`grep -v '^ *+' conftest.out` +if test -z "$ac_err"; then + rm -rf conftest* + eval "ac_cv_header_$ac_safe=yes" +else + echo "$ac_err" >&5 + rm -rf conftest* + eval "ac_cv_header_$ac_safe=no" +fi +rm -f conftest* +fi +if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_hdr=HAVE_`echo $ac_hdr | tr '[a-z]./\055' '[A-Z]___'` + cat >> confdefs.h <&6 +fi +done + + +echo $ac_n "checking for sysinfo""... $ac_c" 1>&6 +if eval "test \"`echo '$''{'ac_cv_func_sysinfo'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +/* Override any gcc2 internal prototype to avoid an error. */ +char sysinfo(); + +int main() { return 0; } +int t() { + +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_sysinfo) || defined (__stub___sysinfo) +choke me +#else +sysinfo(); +#endif + +; return 0; } +EOF +if eval $ac_link; then + rm -rf conftest* + eval "ac_cv_func_sysinfo=yes" +else + rm -rf conftest* + eval "ac_cv_func_sysinfo=no" +fi +rm -f conftest* + +fi +if eval "test \"`echo '$ac_cv_func_'sysinfo`\" = yes"; then + echo "$ac_t""yes" 1>&6 + cat >> confdefs.h <<\EOF +#define HAVE_SYSINFO 1 +EOF + +else + echo "$ac_t""no" 1>&6 +echo $ac_n "checking __NR_sysinfo""... $ac_c" 1>&6 + cat > conftest.$ac_ext < +#include +#include +#ifdef __NR_sysinfo + yes +#endif + +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "yes" >/dev/null 2>&1; then + rm -rf conftest* + echo "$ac_t""yes; tsk tsk - get your libc fixed." 1>&6 + cat >> confdefs.h <<\EOF +#define HAVE_NRSYSINFO 1 +EOF + +else + rm -rf conftest* + echo "$ac_t""no" 1>&6 + echo "configure: warning: sysinfo missing; compilation of dpkg proper may fail." 1>&2 +fi +rm -f conftest* + +fi + + + +if test "${GCC-no}" = yes; then + CFLAGS=-O2; OPTCFLAGS=-O3 +else + CFLAGS=-O +fi + + + + + echo $ac_n "checking your C compiler""... $ac_c" 1>&6 + if eval "test \"`echo '$''{'dpkg_cv_c_works'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + + cat > conftest.$ac_ext < +int main() { return 0; } +int t() { +strcmp("a","b") +; return 0; } +EOF +if eval $ac_compile; then + rm -rf conftest* + dpkg_cv_c_works=yes +else + rm -rf conftest* + dpkg_cv_c_works=no +fi +rm -f conftest* + + +fi + + if test "x$dpkg_cv_c_works" = xyes; then + true + echo "$ac_t""works" 1>&6 + else + true + echo "$ac_t""broken" 1>&6 + { echo "configure: error: C compiler is broken" 1>&2; exit 1; } + fi + + + + echo $ac_n "checking alphasort declaration""... $ac_c" 1>&6 + if eval "test \"`echo '$''{'dpkg_cv_header_alphasort'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + + cat > conftest.$ac_ext < +#include + +int main() { return 0; } +int t() { +alphasort +; return 0; } +EOF +if eval $ac_compile; then + rm -rf conftest* + dpkg_cv_header_alphasort=yes +else + rm -rf conftest* + dpkg_cv_header_alphasort=no +fi +rm -f conftest* + + +fi + + if test "x$dpkg_cv_header_alphasort" = xyes; then + true + echo "$ac_t""yes" 1>&6 + cat >> confdefs.h <<\EOF +#define HAVE_ALPHASORT_DECLARATION 1 +EOF + + else + true + echo "$ac_t""no" 1>&6 + fi + + + + echo $ac_n "checking inlines""... $ac_c" 1>&6 + if eval "test \"`echo '$''{'dpkg_cv_c_inline'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + + cat > conftest.$ac_ext <&6 + cat >> confdefs.h <<\EOF +#define HAVE_INLINE 1 +EOF + + else + true + echo "$ac_t""no" 1>&6 + fi + + + + echo $ac_n "checking __attribute__((,,))""... $ac_c" 1>&6 + if eval "test \"`echo '$''{'dpkg_cv_c_attribute_supported'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + + cat > conftest.$ac_ext <&6 + cat >> confdefs.h <<\EOF +#define HAVE_GNUC25_ATTRIB 1 +EOF + + + echo $ac_n "checking __attribute__((noreturn))""... $ac_c" 1>&6 + if eval "test \"`echo '$''{'dpkg_cv_c_attribute_noreturn'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + + cat > conftest.$ac_ext <&6 + cat >> confdefs.h <<\EOF +#define HAVE_GNUC25_NORETURN 1 +EOF + + else + true + echo "$ac_t""no" 1>&6 + fi + + + echo $ac_n "checking __attribute__((const))""... $ac_c" 1>&6 + if eval "test \"`echo '$''{'dpkg_cv_c_attribute_const'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + + cat > conftest.$ac_ext <&6 + cat >> confdefs.h <<\EOF +#define HAVE_GNUC25_CONST 1 +EOF + + else + true + echo "$ac_t""no" 1>&6 + fi + + + echo $ac_n "checking __attribute__((format...))""... $ac_c" 1>&6 + if eval "test \"`echo '$''{'dpkg_cv_attribute_format'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + + cat > conftest.$ac_ext <&6 + cat >> confdefs.h <<\EOF +#define HAVE_GNUC25_PRINTFFORMAT 1 +EOF + + else + true + echo "$ac_t""no" 1>&6 + fi + + else + true + echo "$ac_t""no" 1>&6 + fi + + + +CWARNS="" + + + + + echo $ac_n "checking GCC warning flag(s) -Wall -Wno-implicit""... $ac_c" 1>&6 + if test "${GCC-no}" = yes + then + if eval "test \"`echo '$''{'dpkg_cv_c_gcc_warn_all'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + + oldcflags="${CFLAGS-}" + CFLAGS="${CFLAGS-} ${CWARNS} -Wall -Wno-implicit -Werror" + cat > conftest.$ac_ext < +#include + +int main() { return 0; } +int t() { + + strcmp("a","b"); fprintf(stdout,"test ok\n"); + +; return 0; } +EOF +if eval $ac_compile; then + rm -rf conftest* + dpkg_cv_c_gcc_warn_all=yes +else + rm -rf conftest* + dpkg_cv_c_gcc_warn_all=no +fi +rm -f conftest* + + CFLAGS="${oldcflags}" +fi + + if test "x$dpkg_cv_c_gcc_warn_all" = xyes; then + CWARNS="${CWARNS} -Wall -Wno-implicit" + echo "$ac_t""ok" 1>&6 + else + dpkg_cv_c_gcc_warn_all='' + echo "$ac_t""no" 1>&6 + fi + else + echo "$ac_t""no" 1>&6 + fi + + + echo $ac_n "checking GCC warning flag(s) -Wwrite-strings""... $ac_c" 1>&6 + if test "${GCC-no}" = yes + then + if eval "test \"`echo '$''{'dpkg_cv_c_gcc_warn_writestrings'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + + oldcflags="${CFLAGS-}" + CFLAGS="${CFLAGS-} ${CWARNS} -Wwrite-strings -Werror" + cat > conftest.$ac_ext < +#include + +int main() { return 0; } +int t() { + + strcmp("a","b"); fprintf(stdout,"test ok\n"); + +; return 0; } +EOF +if eval $ac_compile; then + rm -rf conftest* + dpkg_cv_c_gcc_warn_writestrings=yes +else + rm -rf conftest* + dpkg_cv_c_gcc_warn_writestrings=no +fi +rm -f conftest* + + CFLAGS="${oldcflags}" +fi + + if test "x$dpkg_cv_c_gcc_warn_writestrings" = xyes; then + CWARNS="${CWARNS} -Wwrite-strings" + echo "$ac_t""ok" 1>&6 + else + dpkg_cv_c_gcc_warn_writestrings='' + echo "$ac_t""no" 1>&6 + fi + else + echo "$ac_t""no" 1>&6 + fi + + + echo $ac_n "checking GCC warning flag(s) -Wpointer-arith""... $ac_c" 1>&6 + if test "${GCC-no}" = yes + then + if eval "test \"`echo '$''{'dpkg_cv_c_gcc_warn_pointerarith'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + + oldcflags="${CFLAGS-}" + CFLAGS="${CFLAGS-} ${CWARNS} -Wpointer-arith -Werror" + cat > conftest.$ac_ext < +#include + +int main() { return 0; } +int t() { + + strcmp("a","b"); fprintf(stdout,"test ok\n"); + +; return 0; } +EOF +if eval $ac_compile; then + rm -rf conftest* + dpkg_cv_c_gcc_warn_pointerarith=yes +else + rm -rf conftest* + dpkg_cv_c_gcc_warn_pointerarith=no +fi +rm -f conftest* + + CFLAGS="${oldcflags}" +fi + + if test "x$dpkg_cv_c_gcc_warn_pointerarith" = xyes; then + CWARNS="${CWARNS} -Wpointer-arith" + echo "$ac_t""ok" 1>&6 + else + dpkg_cv_c_gcc_warn_pointerarith='' + echo "$ac_t""no" 1>&6 + fi + else + echo "$ac_t""no" 1>&6 + fi + + + echo $ac_n "checking GCC warning flag(s) -Wimplicit -Wnested-externs""... $ac_c" 1>&6 + if test "${GCC-no}" = yes + then + if eval "test \"`echo '$''{'dpkg_cv_c_gcc_warn_implicit'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + + oldcflags="${CFLAGS-}" + CFLAGS="${CFLAGS-} ${CWARNS} -Wimplicit -Wnested-externs -Werror" + cat > conftest.$ac_ext < +#include + +int main() { return 0; } +int t() { + + strcmp("a","b"); fprintf(stdout,"test ok\n"); + +; return 0; } +EOF +if eval $ac_compile; then + rm -rf conftest* + dpkg_cv_c_gcc_warn_implicit=yes +else + rm -rf conftest* + dpkg_cv_c_gcc_warn_implicit=no +fi +rm -f conftest* + + CFLAGS="${oldcflags}" +fi + + if test "x$dpkg_cv_c_gcc_warn_implicit" = xyes; then + CWARNS="${CWARNS} -Wimplicit -Wnested-externs" + echo "$ac_t""ok" 1>&6 + else + dpkg_cv_c_gcc_warn_implicit='' + echo "$ac_t""no" 1>&6 + fi + else + echo "$ac_t""no" 1>&6 + fi + + +if test "${GCC-no}" = yes; then + CWARNS="${CWARNS} -Wmissing-prototypes -Wstrict-prototypes" +fi + +trap '' 1 2 15 +cat > confcache <<\EOF +# This file is a shell script that caches the results of configure +# tests run on this system so they can be shared between configure +# scripts and configure runs. It is not useful on other systems. +# If it contains results you don't want to keep, you may remove or edit it. +# +# By default, configure uses ./config.cache as the cache file, +# creating it if it does not exist already. You can give configure +# the --cache-file=FILE option to use a different cache file; that is +# what configure does when it calls configure scripts in +# subdirectories, so they share the cache. +# Giving --cache-file=/dev/null disables caching, for debugging configure. +# config.status only pays attention to the cache file if you give it the +# --recheck option to rerun configure. +# +EOF +# Ultrix sh set writes to stderr and can't be redirected directly, +# and sets the high bit in the cache file unless we assign to the vars. +(set) 2>&1 | + sed -n "s/^\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\)=\(.*\)/\1=\${\1='\2'}/p" \ + >> confcache +if cmp -s $cache_file confcache; then + : +else + if test -w $cache_file; then + echo "updating cache $cache_file" + cat confcache > $cache_file + else + echo "not updating unwritable cache $cache_file" + fi +fi +rm -f confcache + +trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15 + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +# Any assignment to VPATH causes Sun make to only execute +# the first set of double-colon rules, so remove it if not needed. +# If there is a colon in the path, we need to keep it. +if test "x$srcdir" = x.; then + ac_vpsub='/^[ ]*VPATH[ ]*=[^:]*$/d' +fi + +trap 'rm -f $CONFIG_STATUS conftest*; exit 1' 1 2 15 + +DEFS=-DHAVE_CONFIG_H + +# Without the "./", some shells look in PATH for config.status. +: ${CONFIG_STATUS=./config.status} + +echo creating $CONFIG_STATUS +rm -f $CONFIG_STATUS +cat > $CONFIG_STATUS </dev/null | sed 1q`: +# +# $0 $ac_configure_args +# +# Compiler output produced by configure, useful for debugging +# configure, is in ./config.log if it exists. + +ac_cs_usage="Usage: $CONFIG_STATUS [--recheck] [--version] [--help]" +for ac_option +do + case "\$ac_option" in + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + echo "running \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion" + exec \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion ;; + -version | --version | --versio | --versi | --vers | --ver | --ve | --v) + echo "$CONFIG_STATUS generated by autoconf version 2.4" + exit 0 ;; + -help | --help | --hel | --he | --h) + echo "\$ac_cs_usage"; exit 0 ;; + *) echo "\$ac_cs_usage"; exit 1 ;; + esac +done + +ac_given_srcdir=$srcdir +ac_given_INSTALL="$INSTALL" + +trap 'rm -fr `echo "Makefile dpkg-deb/Makefile lib/Makefile include/Makefile + dselect/Makefile split/Makefile methods/Makefile + md5sum/Makefile main/Makefile doc/Makefile scripts/Makefile config.h" | sed "s/:[^ ]*//g"` conftest*; exit 1' 1 2 15 + +# Protect against being on the right side of a sed subst in config.status. +sed 's/%@/@@/; s/@%/@@/; s/%g$/@g/; /@g$/s/[\\\\&%]/\\\\&/g; + s/@@/%@/; s/@@/@%/; s/@g$/%g/' > conftest.subs <<\CEOF +$ac_vpsub +$extrasub +s%@CFLAGS@%$CFLAGS%g +s%@CPPFLAGS@%$CPPFLAGS%g +s%@CXXFLAGS@%$CXXFLAGS%g +s%@DEFS@%$DEFS%g +s%@LDFLAGS@%$LDFLAGS%g +s%@LIBS@%$LIBS%g +s%@exec_prefix@%$exec_prefix%g +s%@prefix@%$prefix%g +s%@program_transform_name@%$program_transform_name%g +s%@CC@%$CC%g +s%@CXX@%$CXX%g +s%@pathccompiler@%$pathccompiler%g +s%@perlpath@%$perlpath%g +s%@CPP@%$CPP%g +s%@INSTALL_PROGRAM@%$INSTALL_PROGRAM%g +s%@INSTALL_DATA@%$INSTALL_DATA%g +s%@RANLIB@%$RANLIB%g +s%@OPTCFLAGS@%$OPTCFLAGS%g +s%@CWARNS@%$CWARNS%g + +CEOF +EOF +cat >> $CONFIG_STATUS <> $CONFIG_STATUS <<\EOF +for ac_file in .. $CONFIG_FILES; do if test "x$ac_file" != x..; then + # Support "outfile[:infile]", defaulting infile="outfile.in". + case "$ac_file" in + *:*) ac_file_in=`echo "$ac_file"|sed 's%.*:%%'` + ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;; + *) ac_file_in="${ac_file}.in" ;; + esac + + # Adjust relative srcdir, etc. for subdirectories. + + # Remove last slash and all that follows it. Not all systems have dirname. + ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'` + if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then + # The file is in a subdirectory. + test ! -d "$ac_dir" && mkdir "$ac_dir" + ac_dir_suffix="/`echo $ac_dir|sed 's%^\./%%'`" + # A "../" for each directory in $ac_dir_suffix. + ac_dots=`echo $ac_dir_suffix|sed 's%/[^/]*%../%g'` + else + ac_dir_suffix= ac_dots= + fi + + case "$ac_given_srcdir" in + .) srcdir=. + if test -z "$ac_dots"; then top_srcdir=. + else top_srcdir=`echo $ac_dots|sed 's%/$%%'`; fi ;; + /*) srcdir="$ac_given_srcdir$ac_dir_suffix"; top_srcdir="$ac_given_srcdir" ;; + *) # Relative path. + srcdir="$ac_dots$ac_given_srcdir$ac_dir_suffix" + top_srcdir="$ac_dots$ac_given_srcdir" ;; + esac + + case "$ac_given_INSTALL" in + [/$]*) INSTALL="$ac_given_INSTALL" ;; + *) INSTALL="$ac_dots$ac_given_INSTALL" ;; + esac + echo creating "$ac_file" + rm -f "$ac_file" + configure_input="Generated automatically from `echo $ac_file_in|sed 's%.*/%%'` by configure." + case "$ac_file" in + *Makefile*) ac_comsub="1i\\ +# $configure_input" ;; + *) ac_comsub= ;; + esac + sed -e "$ac_comsub +s%@configure_input@%$configure_input%g +s%@srcdir@%$srcdir%g +s%@top_srcdir@%$top_srcdir%g +s%@INSTALL@%$INSTALL%g +" -f conftest.subs $ac_given_srcdir/$ac_file_in > $ac_file +fi; done +rm -f conftest.subs + +# These sed commands are passed to sed as "A NAME B NAME C VALUE D", where +# NAME is the cpp macro being defined and VALUE is the value it is being given. +# +# ac_d sets the value in "#define NAME VALUE" lines. +ac_dA='s%^\([ ]*\)#\([ ]*define[ ][ ]*\)' +ac_dB='\([ ][ ]*\)[^ ]*%\1#\2' +ac_dC='\3' +ac_dD='%g' +# ac_u turns "#undef NAME" with trailing blanks into "#define NAME VALUE". +ac_uA='s%^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)' +ac_uB='\([ ]\)%\1#\2define\3' +ac_uC=' ' +ac_uD='\4%g' +# ac_e turns "#undef NAME" without trailing blanks into "#define NAME VALUE". +ac_eA='s%^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)' +ac_eB='$%\1#\2define\3' +ac_eC=' ' +ac_eD='%g' + +CONFIG_HEADERS=${CONFIG_HEADERS-"config.h"} +for ac_file in .. $CONFIG_HEADERS; do if test "x$ac_file" != x..; then + # Support "outfile[:infile]", defaulting infile="outfile.in". + case "$ac_file" in + *:*) ac_file_in=`echo "$ac_file"|sed 's%.*:%%'` + ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;; + *) ac_file_in="${ac_file}.in" ;; + esac + + echo creating $ac_file + + rm -f conftest.frag conftest.in conftest.out + cp $ac_given_srcdir/$ac_file_in conftest.in + +EOF + +# Transform confdefs.h into a sed script conftest.vals that substitutes +# the proper values into config.h.in to produce config.h. And first: +# Protect against being on the right side of a sed subst in config.status. +# Protect against being in an unquoted here document in config.status. +rm -f conftest.vals +cat > conftest.hdr <<\EOF +s/[\\&%]/\\&/g +s%[\\$`]%\\&%g +s%#define \([A-Za-z_][A-Za-z0-9_]*\) \(.*\)%${ac_dA}\1${ac_dB}\1${ac_dC}\2${ac_dD}%gp +s%ac_d%ac_u%gp +s%ac_u%ac_e%gp +EOF +sed -n -f conftest.hdr confdefs.h > conftest.vals +rm -f conftest.hdr + +# This sed command replaces #undef with comments. This is necessary, for +# example, in the case of _POSIX_SOURCE, which is predefined and required +# on some systems where configure will not decide to define it. +cat >> conftest.vals <<\EOF +s%^[ ]*#[ ]*undef[ ][ ]*[a-zA-Z_][a-zA-Z_0-9]*%/* & */% +EOF + +# Break up conftest.vals because some shells have a limit on +# the size of here documents, and old seds have small limits too. +# Maximum number of lines to put in a single here document. +ac_max_here_lines=12 + +rm -f conftest.tail +while : +do + ac_lines=`grep -c . conftest.vals` + # grep -c gives empty output for an empty file on some AIX systems. + if test -z "$ac_lines" || test "$ac_lines" -eq 0; then break; fi + # Write a limited-size here document to conftest.frag. + echo ' cat > conftest.frag <> $CONFIG_STATUS + sed ${ac_max_here_lines}q conftest.vals >> $CONFIG_STATUS + echo 'CEOF + sed -f conftest.frag conftest.in > conftest.out + rm -f conftest.in + mv conftest.out conftest.in +' >> $CONFIG_STATUS + sed 1,${ac_max_here_lines}d conftest.vals > conftest.tail + rm -f conftest.vals + mv conftest.tail conftest.vals +done +rm -f conftest.vals + +cat >> $CONFIG_STATUS <<\EOF + rm -f conftest.frag conftest.h + echo "/* $ac_file. Generated automatically by configure. */" > conftest.h + cat conftest.in >> conftest.h + rm -f conftest.in + if cmp -s $ac_file conftest.h 2>/dev/null; then + echo "$ac_file is unchanged" + rm -f conftest.h + else + rm -f $ac_file + mv conftest.h $ac_file + fi +fi; done + + + +exit 0 +EOF +chmod +x $CONFIG_STATUS +rm -fr confdefs* $ac_clean_files +test "$no_create" = yes || ${CONFIG_SHELL-/bin/sh} $CONFIG_STATUS || exit 1 + diff --git a/configure.in b/configure.in new file mode 100644 index 00000000..0fe0f9ee --- /dev/null +++ b/configure.in @@ -0,0 +1,242 @@ +dnl Process this file with autoconf to produce a configure script. + +AC_INIT(include/dpkg.h) +AC_CONFIG_HEADER(config.h) + +AC_PREFIX_DEFAULT(/usr) + +AC_PROG_CC +AC_PROG_CXX + +AC_ARG_WITH(arch, +[ --with-arch=value set/override (Debian) architecture value], +[ + if test "x$with_arch" = x; then + AC_MSG_ERROR(--with-arch requires an architecture name) + fi + AC_DEFINE_UNQUOTED(ARCHITECTURE, "${with_arch}") +],[ + AC_MSG_CHECKING(system architecture) + dpkg_archwhy='' + AC_CACHE_VAL(dpkg_cv_arch,[ + dpkg_arch="`dpkg --print-architecture 2>/dev/null`" + if test "x$dpkg_arch" != x; then + dpkg_archwhy=" (obtained from dpkg)" + elif test "${GCC-no}" = yes; then +changequote(, )dnl + dpkg_arch="`$(CC) --print-libgcc-file-name | + sed -e 's,^.*/gcc-lib/,,; s,/libgcc\.a$,,; s,/[0-9.][0-9.]*$,,; s/-.*$//'`" +changequote([, ])dnl + if test "x`echo \"$dpkg_arch\" | tr -d a-z0-9-`" != x -o "x$dpkg_arch" = x + then + dpkg_arch="" + dpkg_archwhy=" (bad output from --print-libgcc-file-name)" + else +changequote(, )dnl + dpkg_arch2="`sed -e '/^'$dpkg_arch'[ ]/!d; s/^[-0-9a-z]*[ ]*//' \ + $srcdir/archtable`" +changequote([, ])dnl + if test "x$dpkg_arch2" = "x$dpkg_arch"; then + dpkg_archwhy=" (obtained from gcc)" + elif test "x$dpkg_arch2" != x; then + dpkg_archwhy=" (from gcc, canonical form of $dpkg_arch)" + dpkg_arch="$dpkg_arch2" + else + dpkg_archwhy=" (from gcc, but not in archtable)" + fi + fi + else + dpkg_archwhy='' + fi + dpkg_cv_arch="$dpkg_arch" + ]) + if test "x$dpkg_cv_arch" != x; then + AC_MSG_RESULT("$dpkg_cv_arch$dpkg_archwhy") + AC_DEFINE_UNQUOTED(ARCHITECTURE, "${dpkg_cv_arch}") + else + AC_MSG_RESULT("failed$dpkg_archwhy") + fi +]) + +AC_ARG_WITH(newdeb, +[ --with-newdeb make dpkg-deb default to new archives], +[ + AC_DEFINE(BUILDOLDPKGFORMAT, 0) +],[ + AC_ARG_WITH(olddeb, +[ --with-olddeb make dpkg-deb default to old archives], +[ + AC_DEFINE(BUILDOLDPKGFORMAT, 1) +], +[ + case "$CC" in + /*) + pathccompiler="$CC" + ;; + *) + AC_PATH_PROG(pathccompiler,"$CC") + ;; + esac + AC_MSG_CHECKING(.deb format to build by default) + if file "$pathccompiler" 2>/dev/null | grep ELF >/dev/null 2>&1; then + AC_MSG_RESULT([new (C compiler looks like ELF)]) + AC_DEFINE(BUILDOLDPKGFORMAT, 0) + else + AC_MSG_RESULT([old (C compiler not ELF)]) + AC_DEFINE(BUILDOLDPKGFORMAT, 1) + fi +])]) + +AC_ARG_WITH(newdeb, +[ --with-newdeb make dpkg-deb default to new archives], +[AC_DEFINE(BUILDOLDPKGFORMAT, 0)]) + + +AC_MSG_CHECKING(for /usr/bin/perl) +if test -f /usr/bin/perl +then + AC_MSG_RESULT(yes) + perlpath=/usr/bin/perl + AC_SUBST(perlpath) +else + AC_MSG_RESULT(no) + AC_PATH_PROG(perlpath,perl) +fi + +AC_STDC_HEADERS +AC_PROG_INSTALL +dnl AC_PROGRAM_PATH(GZIP, gzip, ) use these from $PATH +dnl AC_PROGRAM_PATH(TAR, tar, ) use these from $PATH +AC_PROG_RANLIB +AC_MODE_T +AC_PID_T +AC_SIZE_T +AC_VPRINTF +AC_C_CONST +AC_C_BIGENDIAN +AC_CHECK_SIZEOF(unsigned long) +AC_CHECK_SIZEOF(unsigned int) +AC_CHECK_FUNCS(unsetenv alphasort scandir strerror strsignal strtoul) +AC_CHECK_HEADERS(sys/cdefs.h) + +AC_CHECK_FUNC(sysinfo, + AC_DEFINE(HAVE_SYSINFO), + AC_MSG_CHECKING(__NR_sysinfo) + AC_EGREP_CPP(yes, [ +#include +#include +#include +#ifdef __NR_sysinfo + yes +#endif +], + AC_MSG_RESULT([yes; tsk tsk - get your libc fixed.]) + AC_DEFINE(HAVE_NRSYSINFO), + AC_MSG_RESULT(no) + AC_MSG_WARN([sysinfo missing; compilation of dpkg proper may fail.]))) + +AC_SUBST(OPTCFLAGS) +if test "${GCC-no}" = yes; then + CFLAGS=-O2; OPTCFLAGS=-O3 +else + CFLAGS=-O +fi + +dnl DPKG_CACHED_TRY_COMPILE(,,,,,) +define(DPKG_CACHED_TRY_COMPILE,[ + AC_MSG_CHECKING($1) + AC_CACHE_VAL($2,[ + AC_TRY_COMPILE([$3],[$4],[$2=yes],[$2=no]) + ]) + if test "x$$2" = xyes; then + true + $5 + else + true + $6 + fi +]) + +DPKG_CACHED_TRY_COMPILE(your C compiler,dpkg_cv_c_works, + [#include ], [strcmp("a","b")], + AC_MSG_RESULT(works), + AC_MSG_RESULT(broken) + AC_MSG_ERROR(C compiler is broken)) + +DPKG_CACHED_TRY_COMPILE(alphasort declaration,dpkg_cv_header_alphasort,[ +#include +#include +], alphasort, + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_ALPHASORT_DECLARATION), + AC_MSG_RESULT(no)) + +DPKG_CACHED_TRY_COMPILE(inlines,dpkg_cv_c_inline,, + [} inline int foo (int x) {], + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_INLINE), + AC_MSG_RESULT(no)) + +DPKG_CACHED_TRY_COMPILE(__attribute__((,,)),dpkg_cv_c_attribute_supported,, + [extern int testfunction(int x) __attribute__((,,))], + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_GNUC25_ATTRIB) + DPKG_CACHED_TRY_COMPILE(__attribute__((noreturn)),dpkg_cv_c_attribute_noreturn,, + [extern int testfunction(int x) __attribute__((noreturn))], + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_GNUC25_NORETURN), + AC_MSG_RESULT(no)) + DPKG_CACHED_TRY_COMPILE(__attribute__((const)),dpkg_cv_c_attribute_const,, + [extern int testfunction(int x) __attribute__((const))], + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_GNUC25_CONST), + AC_MSG_RESULT(no)) + DPKG_CACHED_TRY_COMPILE(__attribute__((format...)),dpkg_cv_attribute_format,, + [extern int testfunction(char *y, ...) __attribute__((format(printf,1,2)))], + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_GNUC25_PRINTFFORMAT), + AC_MSG_RESULT(no)), + AC_MSG_RESULT(no)) + +AC_SUBST(CWARNS) +CWARNS="" + +dnl DPKG_C_GCC_TRY_WARNS(,) +define(DPKG_C_GCC_TRY_WARNS,[ + AC_MSG_CHECKING([GCC warning flag(s) $1]) + if test "${GCC-no}" = yes + then + AC_CACHE_VAL($2,[ + oldcflags="${CFLAGS-}" + CFLAGS="${CFLAGS-} ${CWARNS} $1 -Werror" + AC_TRY_COMPILE([ +#include +#include +],[ + strcmp("a","b"); fprintf(stdout,"test ok\n"); +], [$2=yes], [$2=no]) + CFLAGS="${oldcflags}"]) + if test "x$$2" = xyes; then + CWARNS="${CWARNS} $1" + AC_MSG_RESULT(ok) + else + $2='' + AC_MSG_RESULT(no) + fi + else + AC_MSG_RESULT(no, not using GCC) + fi +]) + +DPKG_C_GCC_TRY_WARNS(-Wall -Wno-implicit, dpkg_cv_c_gcc_warn_all) +DPKG_C_GCC_TRY_WARNS(-Wwrite-strings, dpkg_cv_c_gcc_warn_writestrings) +DPKG_C_GCC_TRY_WARNS(-Wpointer-arith, dpkg_cv_c_gcc_warn_pointerarith) +DPKG_C_GCC_TRY_WARNS(-Wimplicit -Wnested-externs, dpkg_cv_c_gcc_warn_implicit) + +if test "${GCC-no}" = yes; then + CWARNS="${CWARNS} -Wmissing-prototypes -Wstrict-prototypes" +fi + +AC_OUTPUT(Makefile dpkg-deb/Makefile lib/Makefile include/Makefile + dselect/Makefile split/Makefile methods/Makefile + md5sum/Makefile main/Makefile doc/Makefile scripts/Makefile) diff --git a/debian.Changelog b/debian.Changelog new file mode 100644 index 00000000..0a1bd71e --- /dev/null +++ b/debian.Changelog @@ -0,0 +1,1253 @@ +dpkg (1.1.4); priority=MEDIUM + + * Allow overwriting of conflicting packages being removed. (Bug#2614.) + + * a.out control file says Pre-Depends: libc4 | libc. (Bug#2640.) + * ELF control file and libc dependencies changed to use finalised scheme. + * ELF control file and libc dependencies for i386 only. (Bug#2617.) + + * Guidelines say use only released libraries and compilers. + * Install wishlist as /usr/doc/dpkg/WISHLIST. + * Remove spurious entries for Guidelines in info dir file. + + * dpkg-deb --build checks permissions on control (DEBIAN) directory. + + * Spaces in control file fields not copied by dpkg-split. (Bug#2633.) + * Spaces in split file part control data ignore. (Bug#2633.) + + * Portability fixes, including patch from Richard Kettlewell. + * Fixed minor configure.in bug causing mangled GCC -W options. + + -- Ian Jackson Thu, 4 Apr 1996 01:58:40 +0100 + +dpkg (1.1.3); priority=LOW + + * dselect disk methods support Pre-Depends installation ordering. + * When dpkg fails and --auto-deconfigure would help it says so. + * dpkg --search output lists several packages with same file on one line. + * Improved dpkg usage message somewhat. + + * dpkg-deb --build checks permissions and types of maintainer scripts. + * dpkg-deb --build treats misspecified conffiles as error, not warning. + * dpkg --print-architecture prints compiler's architecture while + dpkg --version (&c) print system's arch (this to help cross-compiling). + * More minor guidelines changes, including dir entry fixup. + + * configure script caches more values. + * Changed maintainer email address to ian@chiark.chu.cam.ac.uk. + + -- Ian Jackson Sat, 16 Mar 1996 19:18:08 +0000 + +dpkg (1.1.2); priority=LOW + + * Packaging guidelines installed properly (and as guidelines + rather than debian-guidelines). + * ELF version has more checks to stop you wrecking your dpkg installation. + * dselect disk methods now look for a `local' tree as well, for + people who want locally-available software of various kinds. + * dpkg-divert has debugging message removed. + * Minor guidelines changes. + + * Various makefile cleanups, mainly to do with ELF vs. a.out support. + * debian.rules cleans out ~ files itself, as well as calling make clean. + * debian.rules names .nondebbin.tar.gz file ELF too, if appropriate. + + -- Ian Jackson Thu, 14 Mar 1996 03:38:29 +0000 + +dpkg (1.1.1elf); priority=LOW + + * Added /usr/lib/dpkg/elf-executables-ok and elf-in-kernel. + * Replaces field now allows automatic removal of conflicting packages. + * Replaces field now required to overwrite other packages' files. + * Architecture field, and dpkg --print-architecture, supported. + * build new format archives by default when compiled with ELF compiler. + + * symlinks are now installed atomically (good for shared libraries). + * create /var/lib/dpkg/diversions in postinst if necessary (Bug#2465.) + * Pre-Depends now correctly fails if package never configured. + * dselect disk methods mount with -o nosuid,nodev. + * update-rc.d defaults doesn't add both K and S in any one runlevel; + dpkg postinst fixes up this situation if it sees it. + + * Assorted fixups to the Guidelines, which are now in one piece. + * dpkg --list prints version string in one piece. + * dpkg-scanpackages doesn't produce notice on output with list of + packages with Section and/or Priority control file fields. + + * control file and debian.rules work both for ELF and non-ELF compiles. + * most files compiled with -O2 (-O3 only for some critical files) - + this fixes ELF build. + + -- Ian Jackson Mon, 11 Mar 1996 04:25:28 +0000 + +dpkg (1.1.0); priority=LOW + + * dpkg supports Pre-Depends. + * postinst script gets most-recently-configured version as $2. + + * lib/tarfn.c #includes (portability fix). + + -- Ian Jackson Sun, 11 Feb 1996 21:07:03 +0000 + +dpkg (1.0.17); priority=LOW + + * dpkg --recursive follows symlinks (useful for devel tree). + + -- Ian Jackson Sat, 10 Feb 1996 15:58:46 +0000 + +dpkg (1.0.16); priority=LOW + + * dpkg-deb much faster reading new format archives. (Bug#2256.) + * Developers' documentation in /usr/doc/dpkg/, /usr/info/. + + * Fixed typo in control file Description. + + * configure script tries to improve matters wrt sysinfo. + * any debian-tmp.deb is deleted by `./debian.rules clean'. + + -- Ian Jackson Sun, 4 Feb 1996 15:51:59 +0000 + +dpkg (1.0.15); priority=LOW + + * dselect disk methods should never unmount things they didn't mount. + * debian.README aka /usr/doc/copyright updated. + + -- Ian Jackson Tue, 30 Jan 1996 15:05:39 +0000 + +dpkg (1.0.14); priority=MEDIUM + + * fixed file descriptor leak in dpkg introduced in 1.0.11. + * included dpkg-name in this package (conflicts with dpkg-name). + + * redraw in dselect main menu changed to use clearok (like in lists). + * sa_restorer in struct sigaction no longer used (portability fix). + * removed Guidelines from source package. + + -- Ian Jackson Tue, 30 Jan 1996 02:52:29 +0000 + +dpkg (1.0.13); priority=MEDIUM + + * dselect partition and mounted methods work again. + * dpkg-deb --no-act in usage message. + + -- Ian Jackson Fri, 26 Jan 1996 18:37:03 +0000 + +dpkg (1.0.12); priority=MEDIUM (HIGH for users of 1.0.11) + + * Fixed frequent dpkg coredump introduced in 1.0.11. (Bug#2219.) + * dpkg-deb ensures version numbers start with alphanumerics. + + -- Ian Jackson Wed, 24 Jan 1996 00:42:31 +0000 + +dpkg (1.0.11); priority=MEDIUM + + * corrected potentially serious problem with dpkg low-memory in-core + files database. + * dpkg-split --msdos puts output files in right directory. (Bug#2165.) + + * diversions implemented - see `dpkg-divert --help'. + + * dselect shows and uses (for dependencies) currently installed + version of a package if that is more recent. + * dpkg --force-... options are in separate help screen. + * better error messages for corrupted .deb archives. (Bug#2178.) + * dselect NFS method will unmount correct copy of NFS area if mounted + twice. + + * removes some ELF compilation warnings. + + -- Ian Jackson Fri, 19 Jan 1996 02:41:46 +0000 + +dpkg (1.0.10); priority=MEDIUM + + * dpkg-deb option parsing unmuddled (-I option was removed + in 1.0.9 and broke dpkg-deb). (Bug#2124.) + + * dpkg-split will work when ELF `ar' is installed, and is faster. + + * nfs dselect method now available. + * disk methods don't prompt spuriously for Packages files. + * cdrom+harddisk methods can find Packages files. + + * dpkg-scanpackages (creates Packages files) now in /usr/sbin. + + * various changes to help compilation of dpkg-deb, dpkg-split + and md5sum on non-Debian systems. + * replaced by throughout. + + -- Ian Jackson Sun, 14 Jan 1996 02:55:19 +0000 + +dpkg (1.0.9); priority=MEDIUM + + * dselect uninitialised variable coredump fixed (thanks Carl). + + * version numbers printed by --version fixed. (Bug#2115.) + * disk method problem with missing Packages files fixed. (Bug#2114.) + * dependency version relationships now <= >= << >> =. (Bug#2060.) + + * install-info is in /usr/sbin, not /usr/bin. (Bug#1924.) + * dpkg regards Revision field as obsolete. + + * changed to (for m68k port). + * scripts/Makefile.in `clean' target deletes scripts. + + -- Ian Jackson Thu, 11 Jan 1996 20:51:20 +0000 + +dpkg (1.0.8); priority=LOW + + * update-alternatives slightly more helpful message. (Bug#1975.) + * cosmetic improvements to disk installation method. (Bug#1970,1956.) + * mounted filesystem and unmounted partition separate methods. (Bug#1957.) + + -- Ian Jackson Tue, 12 Dec 1995 04:07:47 +0000 + +dpkg (1.0.7); priority=MEDIUM (HIGH to upgrade syslogd) + + * dselect harddisk/CDROM method script handles multiple package + areas. + * Everything has a manpage, though many are very unhelpful indeed. + + -- Ian Jackson Thu, 30 Nov 1995 03:59:14 +0000 + +dpkg (1.0.6); priority=MEDIUM (HIGH to upgrade syslogd) + + * conffiles can now be taken over properly from one package by + another which replaces it. (Bug#1482.) + * dpkg will not deconfigure essential packages when --auto-deconfigure + is set (this bug was fairly unlikely ever to be exercised). + + * dpkg checks for the presence of certain important programs on the PATH. + * dselect is now more informative when a dependency is missing, saying + " does not appear to be available". (Bug#1642, 1705). + + * `make distclean' fixed; config.* &c removed from source archive. + * lib/lock.c now uses fcntl rather than flock, for better portability. + + * `Package_Revision: 0' removed from control file. + * Some inaccuracies and bad formatting in various messages corrected. + + -- Ian Jackson Tue, 21 Nov 1995 20:15:18 +0000 + +dpkg (1.0.5); priority=LOW + + * dpkg-split allows some space for the header. (Bug#1649.) + * dpkg-split now has --msdos option for 8.3 filenames. + * dpkg-split --join &c will not complain about trailing garbage. + + * dselect & dpkg will no longer ignore SIGHUP will running subprocesses. + + -- Ian Jackson Fri, 13 Oct 1995 13:59:51 +0100 + +dpkg (1.0.4); priority=MEDIUM (HIGH for dselect users with 1.0.3) + + * fixed bug which prevented dselect from displaying the bottom line of + any listing screen. This was introduced in 1.0.3, sorry ! + + * a conffile will never cause a prompt if the package maintainer + distributes a file identical to the user's, even if the file has + been edited by both the user and the maintainer or is a + newly-registered conffile. (Bug#1639.) + + * dselect disk/cdrom method script says where to get Packages file. + * dselect help has better descriptions of the functions of Return and Q. + + * postinst now warns about some problems with /usr/lib/dpkg/methods/hd. + + -- Ian Jackson Thu, 12 Oct 1995 01:45:38 +0100 + +dpkg (1.0.3); priority=MEDIUM + + * dselect: fixed segfault when doing some multiple (de)selections. + + -- Ian Jackson Tue, 10 Oct 1995 03:21:12 +0100 + +dpkg (1.0.2); priority=MEDIUM + + * problem with screen refresh after `o' (change order) corrected. + + -- Ian Jackson Mon, 9 Oct 1995 13:11:04 +0100 + +dpkg (1.0.1); priority=LOW + + * much better dpkg performance on low-memory systems. + * start-stop-daemon --name should now work. (oops!) + * fixed typo which could turn into memory overwriting bug sometime. + + -- Ian Jackson Sun, 8 Oct 1995 20:12:29 +0100 + +dpkg (1.0.0); priority=LOW + + * Version 1.0.0: dpkg is no longer beta. + + * tar extractor no longer looks up an empty string using getgrnam + (this causes the libc to coredump when using NIS). + + -- Ian Jackson Sun, 1 Oct 1995 13:07:36 +0100 + +dpkg (0.93.80); priority=LOW + + * dselect help screen intro changed to remove `much' before `help'. + + * update-alternatives.pl contains hardcoded ENOENT value, instead + of requiring POSIX.pm to be present. + + * Makefiles changed to strip when installing instead of when building. + + -- Ian Jackson Sat, 30 Sep 1995 01:44:12 +0100 + +dpkg (0.93.79) BETA; priority=LOW + + * DPKG_NO_TSTP environment variable which stops dpkg sending the + process group a SIGTSTP (Bug#1496). + * End key should work in dselect lists (Bug#1501). + * various message typos (missing \n's) fixed (Bug#1504). + + -- Ian Jackson Fri, 29 Sep 1995 03:27:01 +0100 + +dpkg (0.93.78) BETA; priority=LOW + + * dselect keystrokes help file typo fix. + + -- Ian Jackson Thu, 28 Sep 1995 20:31:02 +0100 + +dpkg (0.93.77) BETA; priority=MEDIUM + + * dpkg --remove --pending will purge things when appropriate. + + * fixed failure to null-terminate dpkg conflict problem messages. + * fixed bug in formatting of dependency version problem messages. + + * Conffiles resolution prompt for new conffile: typo fixed. + * Changed dpkg usage error to suggest `-Dhelp' instead of `--Dhelp'. + + -- Ian Jackson Wed, 20 Sep 1995 23:44:35 +0100 + +dpkg (0.93.76) BETA; priority=MEDIUM + + * dpkg --auto-deconfigure option (used automatically by dselect) allows + `important' packages which many others depend on to be split. + * dpkg should no longer fail an assertion during complicated + multiple configurations involving packages which are on hold. + + * update-alternatives supports negative priorities. + * /etc/alternatives is included in the .deb archive. + + * Package priorities changed: Required (Req), Important (Imp), Standard (Std), + Optional (Opt) and Extra (Xtr). For backward compatibility Base is an + alias for Required, and Recommended is kept as a level just below Standard. + + * dselect shows introductory help screen when entering package lists (both + main and recursive). + * dselect help messages made more friendly. + * dselect package list `quit, confirm, and check dependencies' key is + now Return rather than lowercase `q'; likewise method list `select this + one and configure it' key. + * dselect selects packages with priority `standard' or better by default. + * dselect `v=verbose' becomes `v=terse' when in verbose mode. + + * hard disk method unmounts /var/lib/dpkg/methods/mnt on failure. + * disk methods' install message uses `stty' to find out what the + interrupt character is, and uses that in the prompt (rather than ^C). + * dpkg now tolerates ^Z characters in Packages files. + * harddisk method doesn't display extra slash when updating packages file. + * harddisk method burbles less if it doesn't have a good default. + + * dpkg-deb now supports new flexible format, but old format still default. + + -- Ian Jackson Wed, 20 Sep 1995 02:49:41 +0100 + +dpkg (0.93.75) BETA; priority=MEDIUM + + * dselect no longer segfaults when you try to modify the last item. + + * dselect Makefile compiles with -g, and links without -s, but installs + with -s, so that built source directory has debugabble binary. + + -- Ian Jackson Tue, 12 Sep 1995 02:59:29 +0100 + +dpkg (0.93.74) BETA; priority=LOW + + * dpkg-split implemented and installed in /usr/bin/dpkg-split. + (NB this is not compatible with Carl Streeter's old dpkg-split script.) + * dpkg uses dpkg-split. + * floppy disk method available - NB this is a first attempt only. + + * hard disk method uses --merge-avail rather than --update-avail. + * installation by default of `standard' packages removed again. + (I don't think this is the right place to do this.) + * update-alternatives --remove correctly deletes all slave links; + minor cosmetic improvements. + + -- Ian Jackson Mon, 11 Sep 1995 02:06:05 +0100 + +dpkg (0.93.73) BETA; priority=LOW + + * dselect multi-package selection now done by `divider' lines + actually in the package list, rather than horizontal highlight + movement. + * dselect help available, and keybindings rationalised. + + * postinst removes /usr/lib/dpkg/methods/hd if upgrading from + 0.93.42.3 or earlier. + * `hold' flag changed to be settable by the user only, and + made orthogonal to the `reinstallation required' flag. + * dpkg will install by default any packages with priority of + `standard' or better unless they're explictly deselected. + + * dselect dependency/conflict resolution will suggest marking absent + packages for `purge' rather than `deinstall'. + * disk method script produces message about invoking dpkg. + * dpkg produces warning, not error, when it gets EPERM trying to + remove a directory belonging to a package being removed. + * dpkg, dpkg-deb usage error reporting improved. + * dselect detects too-dumb terminals and stops. + * dpkg-deb(8) updated a little (thanks to Bill Mitchell). + + * dselect debugmake script uses -O0. + + -- Ian Jackson Sun, 10 Sep 1995 12:23:05 +0100 + +dpkg (0.93.72) BETA; priority=MEDIUM + + * /usr/sbin/update-alternatives added. + + * New names for certain control file fields (old names work + as aliases): Optional -> Suggests, Recommended -> Recommends, + Class -> Priority. + + -- Ian Jackson Sun, 3 Sep 1995 16:37:41 +0100 + +dpkg (0.93.71) BETA; priority=LOW + + * dpkg doesn't silently overwrite `new' conffiles (Bug#1283). + * case now not significant in Essential, Status and Class (Bug#1280). + * dselect checks method scripts for execute, not for write. + + * spelling fixes in lib/dbmodify.c and dselect/helpmsgs.src. + + * dselect `clean' target deletes helpmsgs.cc and helpmsgs.cc.new. + + -- Ian Jackson Thu, 31 Aug 1995 13:56:08 +0100 + +dpkg (0.93.70) BETA; priority=MEDIUM + + * dselect unmounted harddisk method has many silly bugs fixed. + + * dpkg --root option restored (was removed by mistake in 0.93.68). + * minor cosmetic change to dselect subprocess failure message. + + -- Ian Jackson Wed, 9 Aug 1995 22:18:55 +0100 + +dpkg (0.93.69) BETA; priority=MEDIUM + + * dpkg --configure and --remove should work properly when + they have to defer processing (this tends to happen when many + packages are processed at once). (Bug#1209.) + + * dpkg --configure and --remove work better when `processing' + several related packages with --no-act. + + * dpkg --auto is now two options, --pending or -a (for configure, + remove, &c) and --recursive or -R (for install, unpack, &c). + + * dpkg debug options in usage message, and values available (-Dh). + + -- Ian Jackson Wed, 9 Aug 1995 22:18:55 +0100 + +dpkg (0.93.68) BETA; priority=MEDIUM + + * dpkg won't get an internal error if you try to use the default + conffiles response (ie, if you just hit return). (Bug#1208.) + + * dselect hard disk and CD-ROM methods - the real thing, but ALPHA. + + * dselect allows you to go straight to `update' or `install' if + you have already set up an access method. + * new dpkg options --yet-to-unpack, --merge-avail and --update-avail. + * dpkg -G is an abbreviation for dpkg --refuse-downgrade. + * dpkg -R alias for --root withdrawn, pending reuse with different meaning. + * dpkg --help message rationalised somewhat. + + * Obsolete `examples' and `dpkg-split' directories removed from + source tree. The `hello' package is a better example. + + -- Ian Jackson Mon, 7 Aug 1995 02:16:25 +0100 + +dpkg (0.93.67) BETA; priority=LOW for C dpkg alpha testers, HIGH for others + + * dpkg no longer statically linked and -g. + * calls to abort() changed to print string, file and line number first. + * removed unused variable from dpkg source. + + -- Ian Jackson Fri, 4 Aug 1995 01:39:52 +0100 + +dpkg (0.93.66) ALPHA; priority=MEDIUM + + * dpkg will correctly remove overwritten files from the lists of + the package(s) that used to contain them. + + * dpkg --purge is now an action, rather than a modifier for --remove, + and the -P alias for it is withdrawn. + + * dpkg --unpack/--install filenames in messages are now more sensible + about when to use .../ (show as many trailing components as possible + in 40 characters, or the whole path if that the last component is + longer than that). + + -- Ian Jackson Thu, 3 Aug 1995 02:11:03 +0100 + +dpkg (0.93.65) ALPHA; priority=MEDIUM + + * dpkg --remove should, when a package being removed is depended-on + by another that is also queued for removal, defer the depended-on + package rather than aborting it. (Bug#1188.) + + * dpkg will not attempt to configure or remove a package more than + once in the same run. (Bug#1169.) + + * dpkg cosmetic fix to dependency problems message (this bug + hasn't been triggered to my knowledge). + + * perl-dpkg no longer installed in /usr/bin. + + -- Ian Jackson Wed, 2 Aug 1995 13:02:58 +0100 + +dpkg (0.93.64) ALPHA; priority=MEDIUM + + * dpkg marks a package as no longer `to be configured in this run' + when an error occurs, so that other packages which depend on it + will fail (rather than causing a loop and an assertion failure, + packages.c:166: failed assertion `dependtry <= 4'). + + * dselect initial selection granularity is single-package. + * dpkg --no-also-select option renamed to --selected-only (old option + still accepted, but no longer in --help). Changed -N to -O. + + * dselect `update' option changed to `install' (and other options + renamed too). NB: old access methods will not work, because + the `update' script should now be an `install' script. + + * dselect `installation methods' renamed to `access methods'. + * dpkg --skip-same-version and --refuse-downgrade produce friendlier + messages when they skip packages. + * --licence option now properly mentioned in all programs' --version + messages. + + * bad fix for ELF compile problem involving myopt.h removed (compile + problem turned out to be a GCC bug.) + + -- Ian Jackson Tue, 1 Aug 1995 03:03:58 +0100 + +dpkg (0.93.63) ALPHA; priority=LOW + + * preinst works around shell bug/misfeature involving `trap'. + + * dpkg --skip-same-version doesn't skip packages which have + an error flag set or which aren't in a standard `installed' state. + + * dpkg --search now does a substring search if the string doesn't + start with a wildcard character (*, [ or ?) or slash. + + * problem with C/C++ linkage of stuff in "myopt.h" fixed, to help + with compiling with GCC 2.7.0. + + * dselect Makefile.in `clean' deletes curkeys.inc &c, so that they are + not shipped in the distribution source and will be rebuilt on the + target system. + + -- Ian Jackson Thu, 27 Jul 1995 13:38:47 +0100 + +dpkg (0.93.62) ALPHA; priority=HIGH + + * dpkg purges leftover control scripts from /var/lib/dpkg/tmp.ci, + rather than associating them with the wrong package. (Bug#1101.) + + * dpkg won't `disappear' packages containing no files or directories, + nor a package required for depends/recommended. (Bug#1128.) + + * dpkg follows directory symlinks. (Bug#1125.) + + * dselect fixups for ELF/GCC2.7.0 compilation. + + -- Ian Jackson Fri, 21 Jul 1995 21:43:41 +0100 + +dpkg (0.93.61) ALPHA; priority=LOW + + * dselect keybindings and status characters and descriptions changed + (in pursuance of Bug#1037, user interface problems, still open.) + + * Some cleanups to fix mistakes discovered by ELF-GCC 2.7.0, and fixup + for newer C++ draft standard (`for' variable declaration scope change). + + -- Ian Jackson Tue, 18 Jul 1995 01:42:51 +0100 + +dpkg (0.93.60) ALPHA; priority=HIGH + + * dpkg doesn't think packages have `disappeared' if you install + several packages at once. (later reported as Bug#1132.) + + * usage error messages tidied up. + + -- Ian Jackson Sun, 16 Jul 1995 17:56:56 +0100 + +dpkg (0.93.59) ALPHA; priority=HIGH + + * dpkg doesn't break maintainer scripts &c if package `foo' exists + when processing package `foobar'. (Related to Bug#1101.) + + * dpkg implements `disappear' functionality. + * dpkg/dselect remove dead entries from /var/lib/dpkg/status. + + * dpkg --list now sorted correctly and output somewhat improved. + * some debugging messages moved from dbg_stupidlyverbose to dbg_scripts. + * dpkg prints `Removing foo' message even if foo is not configured. + * dpkg only prints `serious warning: files list file ... missing' + once for each package. + + -- Ian Jackson Sun, 16 Jul 1995 02:32:11 +0100 + +dpkg (0.93.58) ALPHA; priority=HIGH + + * dpkg should write out status even for packages which it has only + encountered in the `available' file so far. + + -- Ian Jackson Fri, 14 Jul 1995 20:19:21 +0100 + +dpkg (0.93.57) ALPHA; priority=LOW + + * dpkg does chroot when running maintainer scripts (--instdir + should work right now, though I haven't been able to test it). + + -- Ian Jackson Fri, 14 Jul 1995 01:32:30 +0100 + +dpkg (0.93.56) ALPHA; priority=HIGH + + * dpkg can now overwrite symlinks to directories, and will + do correct handling of symlinks to plain files. + * dpkg should not replace any directory with a symlink. + + -- Ian Jackson Thu, 13 Jul 1995 02:43:36 +0100 + +dpkg (0.93.55) ALPHA; priority=MEDIUM + + * dpkg can now extract hardlinks. + * dpkg configuration/removal works in the presence of dependency cycles. + * dpkg should no longer fail an assertion at processarc.c:193. + + -- Ian Jackson Wed, 12 Jul 1995 01:34:44 +0100 + +dpkg (0.93.54) ALPHA; priority=MEDIUM + + * dpkg and dselect no longer throw away all Class and Section + information in /var/lib/dpkg/available. (Oops.) + * dpkg --refuse- now works (this broke some dselect + method scripts' attempts to use --refuse-downgrade). + * dpkg --audit and --list implemented. + + -- Ian Jackson Mon, 10 Jul 1995 00:35:13 +0100 + +dpkg (0.93.53) ALPHA; priority=LOW + + * dpkg --install/--unpack only skips on-hold packages with --auto. + * dpkg doesn't fclose() the --fsys-tarfile pipe twice. + * dpkg error handling and reporting cleaned up. + * dpkg now lists any failed packages/files just before exiting. + + -- Ian Jackson Sun, 9 Jul 1995 16:31:36 +0100 + +dpkg (0.93.52) ALPHA; priority=MEDIUM + + * dpkg won't segfault due to missing (Package_)Revision fields. + * dpkg --search works. + * dpkg will set execute permissions on scripts if necessary. + * dpkg prints filenames in --unpack and --install. + + -- Ian Jackson Sat, 8 Jul 1995 12:41:39 +0100 + +dpkg (0.93.51) ALPHA; priority=HIGH + + * dpkg --status and --listfiles now work. + + * dpkg --remove --auto won't try to remove everything (!) + * dpkg --unpack doesn't coredump after unpacking the first package. + * dpkg won't fail an assertion if it bombs out of --configure + or --remove because of too many errors. + + * Support for `Essential' in dpkg (not yet in dselect). + * `available' (Packages) file class and section override those + from package control files. + * `Essential: yes' added to control file. + + * Locking strategy changed, now uses flock (no more stale locks). + * preinst now more helpful about conffiles upgrade problem. + + -- Ian Jackson Sat, 8 Jul 1995 01:15:26 +0100 + +dpkg (0.93.50) ALPHA + + * C dpkg now in service. + + * dselect now installs in /usr/bin instead of /usr/sbin. + * Improved `explanation of display' help and changed HSOC to EIOW. + * dselect goes back to top of info display when you move the + highlight. + + * Added to md5sum/md5.c, for the benefit of FreeBSD. + * --admindir doesn't append `var/lib/dpkg' to its argument. + + -- Ian Jackson Fri, 19 May 1995 21:03:08 +0100 + +dpkg (0.93.42.3) BETA; priority=LOW + + * Rebuilt using ncurses 1.9.2c-0. + * Silenced `subcritical error' message if errno == ENOENT. + + -- Ian Jackson Mon, 12 Jun 1995 13:09:24 +0100 + +dpkg (0.93.42.2) BETA; priority=HIGH + + * install-info --remove properly removes multi-line entries. + * Slightly changed ^L redraw code in dselect package list. + + -- Ian Jackson Sat, 10 Jun 1995 14:06:01 +0100 + +dpkg (0.93.42.1) BETA; priority=HIGH esp. for new installations + + * update-rc.d default no longer adds K entries in runlevels 2345. + + -- Ian Jackson Tue, 6 Jun 1995 18:56:23 +0100 + +dpkg (0.93.42) BETA; priority=LOW; HIGH for dselect users + + * Fix unitialised variable reference bug in dselect (#890). + * Fix problem with wordwrapping package and method descriptions. + * Create /var/lib/dpkg/methods/mnt. + + -- Ian Jackson Fri, 19 May 1995 21:03:08 +0100 + +dpkg (0.93.41) BETA; priority=LOW + + * Create /var/lib/dpkg/methods. + * dpkg.pl noisily ignores --skip-same-version rather than barfing. + + -- Ian Jackson Tue, 16 May 1995 13:28:27 +0100 + +dpkg (0.93.40) BETA; priority=LOW + + * dselect's subprogram failure message made to stand out more. + + * When switching out of curses, always move the cursor to the + bottom right corner of the screen. + + -- Ian Jackson Tue, 16 May 1995 01:03:38 +0100 + +dpkg (0.93.39) BETA; priority=LOW + + * dselect can now: + - allow you to select and configure an installation method; + - invoke installation method scripts to update the available file + and unpack packages; + - invoke dpkg to configure and remove packages. + There are no installation methods available yet. + + * Search feature in dselect works (it was purely an ncurses bug). + + * dpkg-*.nondebbin.tar.gz now available (built by debian.rules). + + * The target directory for dpkg-deb --extract (also available as + dpkg --extract) is no longer optional. dpkg-deb suggests the use + of dpkg --install if you omit it. + + * Added to lib/lock.c and fixed ref. to `byte' in + md5sum/md5.c, for portability to Solaris 2. + + * Rebuilt `configure' and `config.h.in' using autoconf 2.3. + * Revised function attribute support checking in configure script. + * Removed obsolete `dselect.pl' from scripts directory. + * New option --licence on all the C programs. + + -- Ian Jackson Sun, 14 May 1995 18:05:38 +0100 + +dpkg (0.93.38) BETA; priority=MEDIUM + + * Version number comparisons (in dpkg and dselect) now >= <= + as documented (Bug#831; thanks to Christian Linhart). + + * dselect now has a non-superuser readonly mode. + * dselect doesn't pop up unsatisfied `Optional's when quitting. + * `unable to delete saved old file' message fixed dpkg_tmp to dpkg-tmp. + + * Made dpkg convert `revision' to `package_revision' when reading + (eg) the `status' file. libdpkg.a has `revision' as a synonym + for `package_revision' and writes the former. + + * Major improvements and many changes to C option parsing, database + management, error handling, Makefiles &c to support dpkg. + * dpkg-deb should now work if sizeof(void*) < sizeof(void(*)()). + + -- Ian Jackson Mon, 24 Apr 1995 12:34:39 +0100 + +dpkg (0.93.37) BETA; priority=LOW (MEDIUM for dselect users) + + * Fixed segfault if no description available (Bug#735); + thanks to Peter Tobias for the bug report. + * Fixed other assorted minor bugs in description displays. + + * Changed dpkg-deb --info short option from -i to -I, to make + it unique across dpkg and dpkg-deb (-i still works with + dpkg-deb for backwards compatibility). + + * Produce more sensible error when main package list is empty. + + -- Ian Jackson Fri, 7 Apr 1995 02:24:55 +0100 + +dpkg (0.93.36) BETA; priority=LOW (MEDIUM for dselect users) + + * All the C code (including dselect) updated to support `provides' + (virtual packages). + * Revamped dselect's related package selection/deselection + algorithms. + * Everything can now handle arbitrary `class' values (as well + as the predefined ones which we understand and can interpret). + * Fixed bug that prevented display update when moving down a small + recursive package list in dselect. + * Column heading characters corrected from `SHOC' to `HSOC'. + + -- Ian Jackson Thu, 6 Apr 1995 12:48:13 +0100 + +dpkg (0.93.35) BETA; priority=MEDIUM + + * Preserve ownerships and permissions on configuration files. + * Fix bug in conffile updating that could leave a hardlink + .dpkg-new to the conffile . + + * Improved dselect's package list help messages. + * Highlight now moves on after (de)selecting just one package. + * Better algorithm for scrolling up/down when moving highlight. + * Fixed bug in display of `preformatted' extended Description lines. + (dselect is still ALPHA, but is fairly stable.) + + * Improved dpkg's message when configuring a package that doesn't + exist, and when selecting or skipping a package that isn't + currently selected (during unpack processing). + + * Description in control file expanded. + + * Scroll back to top when changing what is in the `info' area. + +dpkg (0.93.34) BETA; priority=LOW (HIGH for dselect users) + + * dselect: Fixed bug which caused a coredump if you exited the + package list if you'd made any changes. Ouch ! + + * dselect: Improved selection algorithm to show fewer extraneous + packages; improved display for unavailable packages. + + * dpkg: Improved progress messages during unpacking somewhat. + +dpkg (0.93.33) BETA; priority=LOW (HIGH for dselect users) + + * dselect now has a main menu. + + * Fixed nasty uninitialised data bug in dselect. + + * dselect now locks and unlocks the packages database. + +Mon, 27 Mar 1995 03:30:51 BST Ian Jackson + + * dpkg (0.93.32): Alpha dselect released and installed in + /usr/sbin/dselect. + * dpkg (0.93.32): Many portability enhancements: should now + compile using GCC 2.6.3, and dpkg-deb should + compile better on non-Linux systems. + * dpkg (0.93.32): dpkg will not loop if its stdin disappears + and it needs to prompt. + * dpkg (0.93.32): Fixed removal dependency error to show + correct package (Bug #648). + * dpkg (0.93.32): Tidied up copyright notices. + * dpkg (0.93.32): First draft of update-rc.d manpage, not yet + installed in /usr/man. + * dpkg (0.93.32): Changes to top-level Makefile.in to improve + error trapping. + * dpkg (0.93.32): Improved Makefile `clean' and `distclean' + targets. + * dpkg (0.93.32): Deleted irrelevant `t.c' from lib and + dselect directories. + * dpkg (0.93.32): Added vercmp.c with version comparison code. + * dpkg (0.93.32): varbufextend message changed - varbufs not + just for input buffers. + * dpkg (0.93.32): varbuf has C++ member functions in header + #ifdef __cplusplus. + +Changes in dpkg 0.93.31: + +* start-stop-daemon --pidfile now works (Bug#571). +* Fixed dependency processing bugs which could require a rerun of + dpkg --configure (Bug#566). +* Fixed garbage output for `language' of control file in dpkg-deb --info. + +Changes in dpkg 0.93.30: + +* Added /usr/sbin/start-stop-daemon. + +Changes in dpkg 0.93.29: + +* Made postinst scripts really be run when dpkg --purge used. +* Added new --force-extractfail option - VERY DANGEROUS. + +Changes in dpkg 0.93.28: + +* Removed undef of %xx_p21 in read_database_file, which caused the + the whole status database to become trashed when any update files + were read. +* Make infinite-loop prevention and cycle detection work. +* Made findbreakcycle work (ie, break properly when cycle detected). +* New script, update-rc.d, to update links /etc/rc?.d/[KS]??*. +* dpkg.pl now sets the umask to 022. +* Cosmetic error message fix to dpkg-deb. +* Deleted OLD directory altogether. +* Improved error-trapping in top-level Makefile loops. + +Changes in dpkg 0.93.27: + +* Make version number specifications in Depends &c work. +* Added AC_PROG_CXX to autoconf.in for dselect. +* Changed myopt.h not to have cipaction field in cmdinfo (this was + specially for dpkg-deb) - now we have a generic void*. +* Renamed `class' member of `pkginfoperfile' to `clas' [sic]. +* Much work in `dselect' subdirectory. +* Deleted executables, objects and libraries from OLD tree ! +* Minor changes to various copyright notices and top-of-file comments. +* Don't install nasty Perl dselectish thing as /usr/bin/dselect. + +Changes in dpkg 0.93.26: + +* Added --no-also-select instead of not auto-selecting on --unpack + but doing so on --install; removed --force-unpack-any. + +Changes in dpkg 0.93.25: + +* Fixed duplicate output (failure to flush before fork) bug. +* More clarification of md5sum.c copyright. +* Corrected typo in ChangeLog in 0.93.24 source package. + +Changes in dpkg 0.93.24: + +* dpkg could copy conffiles info from one package to another. Aargh. + Bug #426. +* dpkg failed to initialise status if you tried to remove or + configure a nonexistent package. Bug #419. +* install-info now handles START-INFO-DIR-ENTRY entries like: + * Gdb:: The GNU debugger. + Previously it would only accept (Bug #407): + * Gdb: (gdb). The GNU debugger. +* When installing a new foo.info[.gz], install-info now replaces + * Foo: (foo.info). The Gnoo Foo. + as well as just * Foo: (foo). ... +* Moved option parsing out of dpkg-deb into libdpkg. +* Assorted minor source code rearrangements. +* Fixed assorted copyright notices, clarified md5sum copyright. +* Corrected typo in 0.93.23 source package's ChangeLog. + +Changes in dpkg 0.93.23: + +* `dpkg-deb' --build now does a syntax check on the control file. +* `dselect' is now no longer called `debian', spurious copy removed + from package top-level source directory. +* C control information parsing complete and somewhat tested. +* Moved `global' include files into $(srcdir)/include from ../lib, + added some files to the lib Makefile, and arranged for pop_cleanup(). + +Changes in dpkg 0.93.22: + +* Fixed bug which caused dpkg to see failures of md5sum where there + were none (would also have caused dpkg to miss a real failure). +* Fixed failure to update some `status' database fields. + +Changes in dpkg 0.93.21: + +* Fixed error-handling bug which could corrupt database. + +Changes in dpkg 0.93.20: + +* Fixed bug which ran old (/var/adm/dpkg) postinst scripts. +* Fixed dpkg usage message which claimed -i => both --install & --info. +* Use Colin Plumb's MD5 code - faster, and better copyright. +* Manpages: dpkg-deb(8), deb-control(5), deb(5) - thanks to Raul + Deluth Miller. Also, an xfig picture of some C program innards. + +Changes in dpkg 0.93.19: + +* Don't delete the `list' file from the dpkg database. +* Fixed various bugs in the conffile handling. +* Conffiles that are symlinks will now be treated as if the + `dereferenced' name of the file was listed in conffiles. This means + that /etc/foo -> /usr/etc/foo will cause all conffile updates of + /etc/foo to create /usr/etc/foo.dpkg-tmp &c instead. However, the + link will be removed if --purge is used to delete all the conffiles. +* When doing a new installation, or when updating a conffile that + wasn't listed as a conffile in the old version of the package, don't + do any prompting but just install the version from the archive. +* Corrected error message if exec of dpkg --vextract failed + and --instroot or --root specified. +* Added new --force-unpack-any option. +* Extra newline after --status output. +* Added -W options to CFLAGS. +* Fixed mistake in previous ChangeLog entry. + +Changes in dpkg 0.93.18: + +* Fixed invocation of dpkg-deb --vextract if --root or --instdir + not specified. +* Create /var/lib/dpkg/updates. + +Changes in dpkg 0.93.17: + +* install-info --remove exits with status 0 if it doesn't find the + thing to remove, instead of status 1. +* Error handling functions have __attribute__((format...)) if GCC. +* push_cleanup its arg takes void **argv instead of char **argv. +* Top-level Makefile.in has set -e before `for' loops. +* dpkg-deb --info not-an-existing-file produces fewer error messages. + +Changes in dpkg 0.93.16: + +* Made --root= option really extract to $instroot instead of `/'. +* install-info clears the 0444 bits in its umask. +* Fixed a few database handling bugs which cause dpkg always to fail, + and usually to corrupt the status database in various ways. +* dpkg-deb completely rewritten, now doesn't tinker with + /var/{adm,lib}/dpkg. Should be faster. +* Directory structure and Makefiles in source package reorganised. + +Changes in dpkg 0.93.15: + +* Added `debian' (dselect), still very primitive. +* Database format changed, and moved from /var/adm to /var/lib. +* Added dpkg --avail mode, --list, --status and --search. +* Set of dpkg => dpkg-deb pass-through operations changed (but + dpkg-deb not yet updated). +* Added --root, --admindir and --instdir, as well as --isok &c. +* Moved much stuff into /usr/lib/dpkg-lib.pl, rewritten status + database handling. +* Put packages in `purge' state even if `deinstall' requested if + they have no postrm and no conffiles. +* Version number comparisons fixed. +* insert-version.pl now installes lib.pl filename too. +* Strip trailing slashes when reading files from file lists. + +Changes in dpkg 0.93.14: + +* Fixed parsing of DEPENDS &c fields with trailing whitespace. +* postinst now fixes up broken ispell.control file. +* Cyclic dependency/multiple package removal processing: don't consider + packages we've just removed when looking for a reason not to go ahead. +* Added call to postinst with `purge' argument for expunging old + configuration etc. that aren't listed in conffiles. + +Changes in dpkg 0.93.13: + +* sub S_ISREG defined in dpkg.pl. +* Checking of DEPENDS &c fields was too lax, causing an internal error + if you fed it certain kinds of broken control file. +* Fixed misleading message from bogus installationstatus call. +* New -u and -U options to dpkg-deb which don't unpack the /DEBIAN + directory, and use these in dpkg.pl; clean up /DEBIAN in postinst. + +Changes in dpkg 0.93.12: + +* No longer needs *.ph files, since these appear to be broken. +* Postinst fixes up *.control files with curly brackets. +* embryo of dselect. + +Changes in dpkg 0.93.11: + +* New --ignore-depends option. +* This ChangeLog changed format here. + +Wed Nov 30 15:38:21 GMT 1994 Ian Jackson + + * dpkg 0.93.11 released. + + * conffile updating fixed. + + * Message `updgrade' in dpkg changed to `replace'. + + * install-info now copes with multi-line entries. + + * version numbers now done automatically in dpkg and install-info. + + * more debugging around conffiles updates. + + * *.hash files not deleted so soon. + + * adds brand new packages to status array so we can install them. + + * postinst does h2ph for {sys,linux}/{stat,types}.ph if required. + +Mon Nov 28 02:00:13 GMT 1994 Ian Jackson + + * dpkg 0.93.10 released. + + * dpkg.pl completely rewritten. + + * dpkg-deb: removed dabase-processing and --install option. + + * Makefiles reworked, debian.rules added. + + * Don't install anything in /usr/doc/examples. + + * dpkg-*.deb contains /usr/bin/dpkg-deb.dist, fixed up by postinst. + +Thu Oct 20 13:22:20 1994 Ian Murdock (imurdock@debra.debian.org) + + * dpkg 0.93.9 released. + + * dpkg.pl: Use $argument, not $package, with `--build'. + Make sure that saved postinst scripts are executable. + +Tue Oct 18 09:40:57 1994 Ian Murdock (imurdock@debra.debian.org) + + * dpkg 0.93.8 released. + + * deb/remove.c (pkg_remove): Do not report an error from rmdir () + when `errno' is ENOTEMPTY (Directory not empty), because in this + case we have found the highest-level directory in the package and + are ready to exit the loop (i.e., it is a normal occurrence). + +Mon Oct 17 10:44:32 1994 Ian Murdock (imurdock@debra.debian.org) + + * Makefile.in: Adapted all Makefiles to the GNU Coding Standards. + + * deb/remove.c (pkg_remove): Make sure that parent directories are + removed LAST! This will result in complete removal of packages + when --remove is called. dpkg 0.93.7 (and earlier) had problems + with this because it tried to remove directories in order, which + will work most of the time, but not necessarily all of the time. + + * deb/list.c (pkg_list): Output is sorted by package name. + +Tue Oct 4 12:27:10 1994 Ian Murdock (imurdock@debra.debian.org) + + * deb/contents.c (pkg_contents): When a list file cannot be + opened, silently fail and let the front-end explain the problem. + + * deb/util.c (return_info): When a control file cannot be opened, + silently fail and let the front-end explain the problem. + + * deb/search.c (pkg_search): Exit 0 if the regular expression is + matched and 1 if it is not. + +Mon Oct 3 18:38:53 1994 Ian Murdock (imurdock@debra.debian.org) + + * dpkg.pl: New file. Replaces dpkg.sh. + + * deb/Makefile.in: Renamed `dpkg-util.deb' to `dpkg-deb'. + + * deb/build.c (pkg_build): `--build' is less verbose, instead + letting the front-end add verbosity where appropriate. + + * deb/install.c (pkg_install): Ditto. + + * deb/remove.c (pkg_remove): Ditto. + + * deb/search.c (pkg_search): Ditto. + + * deb/describe.c (pkg_describe): `--describe' is less verbose, + instead letting the front-end add verbosity where appropriate. + The ``Description:'' label has been removed. + + * deb/version.c (pkg_version): `--version' is less verbose, + instead letting the front-end add verbosity where appropriate. + The ``Version:'' label has been removed, as has the maintainer + information. + +Mon Sep 12 14:22:04 1994 Ian Murdock (imurdock@debra.debian.org) + + * deb/version.c (pkg_version): `--version' now reports the + version number of dpkg if no argument is specified. + +Thu Sep 1 13:31:37 1994 Ian Murdock (imurdock@debra.debian.org) + + * dpkg 0.93.7 released. + + * deb/build.c (pkg_build): check status and exit if non-zero. + + * deb/contents.c (pkg_contents): ditto. + + * deb/install.c (archive_extract): ditto. + +Thu Sep 1 13:20:08 1994 Ian Murdock (imurdock@debra.debian.org) + + * deb/version.c (pkg_version): indent to the same point as + pkg_describe. + +Thu Sep 1 12:21:11 1994 Ian Murdock (imurdock@debra.debian.org) + + * Makefile.in (dist): added debian.rules binary, source and + dist targets to make final distribution easier to make. + (install): install programs to /usr/bin. + + * deb/Makefile.in (install): install programs to /usr/bin. + + * deb/list.c (pkg_list): enforce a maximum limit of ten characters + for the package name in the output. + (pkg_list): left-justify the version number to make it easier for + the front-end to parse the output. + (pkg_list): replace first '\n' character in packages[n].description + with '\0'. + + * deb/install.c (archive_extract): use the `p' option to `tar' to + ensure that permissions are preserved. + +Sat Aug 27 09:53:37 1994 Ian Murdock (imurdock@debra.debian.org) + + * dpkg 0.93.6 released. + + * deb/util.c (return_info): only unlink CONTROL if ARCHIVE_FLAG is + true! + +Fri Aug 26 15:38:22 1994 Ian Murdock (imurdock@debra.debian.org) + + * dpkg 0.93.5 released. + + * deb/contents.c (pkg_contents): merged function archive_contents + into function pkg_contents. + + * deb/contents.c (pkg_contents): use lstat (rather than stat) so + that symbolic links are recognized. + (pkg_contents): print the usual ` -> ' now that we + recognize symbolic links. + + * deb/util.c (return_info): create a FIFO to pipe the needed + information to the ``formatter'' rather than creating a directory + in /tmp for the package information, which is what we used to do. + +Thu Aug 25 11:46:27 1994 Ian Murdock (imurdock@debra.debian.org) + + * lib/fake-ls.c (mk_date_string): return a pointer to malloc'ed + area. + (mk_mode_string): ditto. + + * dpkg.sh: make sure the control information is extracted to a + uniquely-named temporary directory during package installation. + + * dpkg.sh: execute the pre- and post-removal scripts during + package removal. + + * dpkg.sh: exit immediately if dpkg-util.deb reports failure. + + * deb/install.c (pkg_control): make sure that `package' exists and + is a Debian archive before doing anything. + + * deb/install.c (pkg_extract): make sure that `package' exists and + is a Debian archive before doing anything. + + * deb/install.c (pkg_install): unlink `extract_output' when done. + + * deb/remove.c (pkg_remove): use lstat (rather than stat) so that + --remove does not get confused and think that a symbolic link to a + directory is actually a directory, which results in the symbolic + link never being removed at all. + +ChangeLog begins Thu Aug 25 11:46:27 1994 for dpkg 0.93.5. diff --git a/debian.README b/debian.README new file mode 100644 index 00000000..027735c9 --- /dev/null +++ b/debian.README @@ -0,0 +1,36 @@ +This is Debian/GNU Linux's package maintenance system. + +For an example of how to construct packages see the `hello' package +which is part of Debian. + +[ Note for users of GCC 2.7.0: you must compile at least + `dselect/main.cc' with only -O2, due to a bug in GCC. ] + +Copyright (C) 1994,1995,1996 Ian Jackson +Copyright (C) 1995 Bruce Perens +Copyright (C) 1994 Carl Streeter +Copyright (C) 1994 Matt Welsh +Copyright (C) 1994 Ian Murdock +Copyright (C) 1995,1996 Erick Branderhorst + +This 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, or (at your option) any later +version. + +This 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 with +your Debian GNU/Linux system, in /usr/doc/copyright/GPL, or with the +dpkg source package as the file COPYING. If not, write to the Free +Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + +/usr/bin/md5sum is compiled from md5.[ch] (written by Colin Plumb in +1993 and modified by Ian Jackson in 1995) and md5sum.c (written by +Branko Lankester in 1993 and modified by Colin Plumb in 1993 and Ian +Jackson in 1995). The sources and the binary are all in the public +domain. diff --git a/debian.control b/debian.control new file mode 100644 index 00000000..19ffe299 --- /dev/null +++ b/debian.control @@ -0,0 +1,15 @@ +Package: dpkg +Version: =elf +Architecture: = +Essential: yes +Pre-Depends: libc5=, ncurses3.0 +Conflicts: dpkgname +Replaces: dpkgname +Maintainer: Ian Jackson +Description: Package maintenance system for Debian GNU/Linux + This package contains the programs which handle the installation and + removal of packages on your system. + . + The primary interface for the dpkg suite is the `dselect' program; + a more low-level and less user-friendly interface is available in + the form of the `dpkg' command. diff --git a/debian.controlaout b/debian.controlaout new file mode 100644 index 00000000..d2827f97 --- /dev/null +++ b/debian.controlaout @@ -0,0 +1,15 @@ +Package: dpkg +Version: = +Architecture: = +Essential: yes +Conflicts: dpkgname +Replaces: dpkgname +Pre-Depends: libc4 | libc +Maintainer: Ian Jackson +Description: Package maintenance system for Debian GNU/Linux + This package contains the programs which handle the installation and + removal of packages on your system. + . + The primary interface for the dpkg suite is the `dselect' program; + a more low-level and less user-friendly interface is available in + the form of the `dpkg' command. diff --git a/debian.postinst b/debian.postinst new file mode 100755 index 00000000..aa1fd3aa --- /dev/null +++ b/debian.postinst @@ -0,0 +1,193 @@ +#!/bin/sh - + +set -e + +install-info --section Development Development \ + --quiet /usr/info/guidelines.info.gz + +dupdaemonhelp () { + cat <<'END' + +Some daemons and similar services whose scripts have links in the +/etc/rcN.d directories have both start (S) and stop (K) links in +some runlevels. Thus these services get stopped and immediately +restarted at some runlevel changes, which is probably not what +you want. I can remove the probably-spurious K links if you like. + +Type Y to remove then, N to leave them, or L to list them. +If you don't know what to do you can say N now and then delete +them by hand later. + +END +} + +if [ "x$1" = xconfigure ]; then case "$2" in 0.* | 1.0.* | 1.1.0 | 1.1.0[^0-9]* | '' ) + for f in 0 1 2 3 4 5 6 + do + cd /etc/rc$f.d + for g in K[0-9][0-9]* + do + if [ -n "`echo \"x$g\" | tr -d 0-9A-Za-z_-`" ] + then + continue + fi + h="`echo $g | sed -e 's/^K/S/'`" + if ! [ -L $h -a -L $g ] \ + || [ "`ls -Li $g 2>/dev/null | awk '{print $1}'`" != \ + "`ls -Li $h 2>/dev/null | awk '{print $1}'`" ] + then + continue + fi + removes="$removes rc$f.d/$g" + done + done + if [ -n "$removes" ] + then + cd /etc + dupdaemonhelp + while [ -n "$removes" ] + do + echo -n 'y=remove, n=leave, l=list, h=help ? ' + read response + case "$response" in + [Yy]*) + echo "Removing duplicate K links ..." + rm -v $removes + removes="" + ;; + [Nn]*) + echo -e "OK, leaving them.\n" + removes="" + ;; + [Ll]*) + echo + echo $removes + echo + ;; + [Hh]*) + dupdaemonhelp + ;; + esac + done + fi +;; esac ; fi + +cd /var/lib/dpkg +if ! test -f diversions +then + touch diversions +fi + +cd /usr/bin +if test dpkg-deb.dist -ef dpkg-deb; then rm dpkg-deb.dist; fi +if test -f dpkg-deb.dist; then mv dpkg-deb.dist dpkg-deb; fi + +if test -d /DEBIAN +then + echo 'Removing /DEBIAN directory which was created by a dpkg bug ...' + rm -r /DEBIAN +fi + +if test -d /usr/lib/dpkg/methods/hd -a ! -x /usr/lib/dpkg/methods/hd/install +then + echo \ +'Warning - /usr/lib/dpkg/methods/hd/ exists, but .../hd/install does not. +This is probably left over from some previous manual installation of +now-obsolete dselect-related software. I suggest you remove the whole +/usr/lib/dpkg/methods/hd directory and all its contents; otherwise dselect +is unlikely to function correctly.' +fi + +if test -f /var/lib/dpkg/status; then exit 0; fi + +cd /var/adm/dpkg + +if [ ! -f status ] +then + echo 'Adding "status" file to dpkg system database ...' + + rm -f /tmp/dpp.$$ || true + + ls -1 deb/*.control >/tmp/dpp.$$ + sed -e 's:^deb/::; s:\.control$: Install OK Installed:;' \ + status.new + + rm /tmp/dpp.$$ + mv status.new status +fi + +if grep '{' deb/*.control >/dev/null +then + echo 'Fixing up curly brackets in control files ...' + perl -i~ -pe \ + 'y/{}//d if m/^(depends|recommended|optional|conflicts):/i' \ + deb/*.control + rm deb/*.control~ +fi + +if grep 'Optional: idanish ifrench' deb/ispell.control >/dev/null 2>&1 +then + echo 'Fixing up broken ispell.control file ...' + perl -i~ -pe 's/ /, /g, s/,// if m/^Optional:/' deb/ispell.control +fi + +newdb=/var/lib/dpkg +echo "Moving datatabase /var/adm/dpkg to $newdb and changing format ..." + +exec 4>$newdb/status.new +exec 5&4 + else + echo >&4 "Package: $package" + fi + echo >&4 "Status: $status" + if test -f deb/$package.conffiles -a -s deb/$package.conffiles + then + echo >&4 "Conffiles:" + exec 6/dev/null || continue + if test -f deb/$package.hash + then + hash="`grep \"^$cfile \" /dev/null || hash="/$hash" + echo >&4 " $hash" + done + elif test -f deb/$package.hash -a -s deb/$package.conffiles + then + echo >&4 "Conffiles:" + sed -ne '/^[^\/]/ s:^:/:; s/^/ /; / ./p' >&4 deb/$package.hash + fi + echo >&4 + for s in {pre,post}{rm,inst} list + do + if test -f deb/$package.$s + then + mv deb/$package.$s $newdb/info/$package.$s + fi + done +done + +if ! test -f $newdb/available +then + if test -f database + then + cp database $newdb/available + else + touch $newdb/available + fi +fi + +mv $newdb/status.new $newdb/status diff --git a/debian.preinst b/debian.preinst new file mode 100755 index 00000000..b7a3b21b --- /dev/null +++ b/debian.preinst @@ -0,0 +1,86 @@ +#!/bin/sh - + +if [ "$1" != "upgrade" ]; then exit 0; fi + +set -e +# i386elf: dpkg --assert-support-predepends + +oldver="$2" + +case "$oldver" in + 0.93.[01234]* | - ) ;; + * ) exit 0 ;; +esac + +echo ' +contemplating upgrade of dpkg from pre-0.93.50 version ...' + +trap 'es=$?; rm -f /tmp/bp.$$; exit $es' 0 + +perl -000 -ne 'print $x if m/^Package:\s+(\S+\n)/im && + ($x=$1) ne "dpkg\n" && + m/^Status:.*(unpacked|postinst)/im' \ + /var/lib/dpkg/status >/tmp/bp.$$ + +if test -s /tmp/bp.$$ +then + echo ' + +WARNING - have you read the release notes for this upgrade ? + +The following packages have been unpacked but not yet configured:' + echo '' `cat /tmp/bp.$$` + echo -n ' +If you proceed with the dpkg upgrade with these packages in this state +you will LOSE ANY CONFIGURATION CHANGES that have been made to their +configuration files. I recommend that you back out of the upgrade +now (see below) and then configure each of these packages using: + dpkg --configure --force-hold + +If you do this and it fails for some packages they are broken anyway, in +which case you probably don'"'"'t have that much to lose by going ahead +with the upgrade. + +Type "yes" to confirm that you really want to do the upgrade in +spite of my warning above; if you give any other response we'"'"'ll back +off the upgrade to give you a chance to fix things. + +Continue with upgrade despite probable loss of config data ? ' + read response + case "$response" in + [Yy][Ye][Ss] ) echo OK ... ;; + * ) echo 'Aborting dpkg upgrade.'; exit 1 ;; + esac +fi + +echo -n ' +IMPORTANT - you must install this upgrade on its own, not together in +the same dpkg run as any other packages. Otherwise you risk losing +configuration information. + +If you say "no" to the question below we'"'"'ll back off the upgrade now, +and you can then do it later using: + dpkg --install dpkg-0.93.51.deb +If you'"'"'re not sure what to do, say "no", and then run that command +(with the appropriate dpkg-*.deb filename) from a root shell prompt. + +Are you installing only the dpkg upgrade in this dpkg run ? [y/n] ' +read response +case "$response" in +[yY]* | '' ) + echo 'OK, going ahead.' + ;; +* ) + echo ' +Aborting dpkg upgrade (you will see error messages from dpkg about this).' + exit 1 + ;; +esac + +if [ -d /usr/lib/dpkg/methods/hd ] +then + echo 'Removing obsolete /usr/lib/dpkg/methods/hd ...' + rm -r /usr/lib/dpkg/methods/hd +fi + +exit 0 diff --git a/debian.prerm b/debian.prerm new file mode 100644 index 00000000..59a60677 --- /dev/null +++ b/debian.prerm @@ -0,0 +1,12 @@ +#!/bin/sh - + +set -e + +cd /usr/bin +test -f dpkg-deb.dist || ln dpkg-deb dpkg-deb.dist + +install-info --quiet --remove /usr/info/Guidelines +install-info --quiet --remove /usr/info/debian-guidelines +install-info --quiet --remove /usr/info/guidelines +install-info --quiet --remove /usr/info/debian-guidelines.info.gz +install-info --quiet --remove /usr/info/guidelines.info.gz diff --git a/debian.rules b/debian.rules new file mode 100755 index 00000000..a981e8f8 --- /dev/null +++ b/debian.rules @@ -0,0 +1,85 @@ +#!/usr/bin/make -f + +package=dpkg +version=1.1.4 + +archi=$(shell dpkg --print-architecture) +DIR:=$(shell pwd) + +build: + $(checkdir) + ./configure --prefix=/usr + $(MAKE) + touch build + +clean: + $(checkdir) + -rm -f build + -$(MAKE) -i distclean + -rm -rf debian-tmp* *~ *.orig ./#*# tmp.* + -rm -f config.cache config.status config.h install config.log + find -name '*~' -print0 | xargs -r0 rm -- + +binary: +#checkroot build + -rm -rf debian-tmp + mkdir debian-tmp debian-tmp/DEBIAN + install -d debian-tmp/usr/doc/{copyright,dpkg} + cp debian.preinst debian-tmp/DEBIAN/preinst + if file main/dpkg | grep -q ELF; then \ + if [ $(archi) = i386 ]; then \ + sed -e '5s/=/ (>= 5.2.18-2)/' tmp.control ; \ + sed -e 's/^# i386elf: //' debian-tmp/DEBIAN/preinst ; \ + else \ + sed -e '5s/=//' tmp.control ; \ + fi ; \ + else \ + cp debian.controlaout tmp.control ; \ + fi + sed -e '2s/=/$(version)/; 3s/=/$(archi)/' tmp.control >debian-tmp/DEBIAN/control + cp debian.prerm debian-tmp/DEBIAN/prerm + cp debian.postinst debian-tmp/DEBIAN/postinst + chmod +x debian-tmp/DEBIAN/{postinst,prerm,preinst} + $(MAKE) prefix=$(DIR)/debian-tmp/usr \ + datadir=$(DIR)/debian-tmp/var/lib/dpkg \ + etcdir=$(DIR)/debian-tmp/etc \ + install + gzip -9 debian-tmp/usr/info/guidelines.info* + cp debian.README debian-tmp/usr/doc/copyright/dpkg + cp TODO debian-tmp/usr/doc/dpkg/WISHLIST + touch debian-tmp/var/lib/dpkg/{status,available} + chown -R root.root debian-tmp + chmod -R g-ws debian-tmp + cd debian-tmp && \ + tar cf ../../$(package)-$(version).nondebbin.tar usr var && \ + gzip -9vf ../../$(package)-$(version).nondebbin.tar + mv debian-tmp/usr/bin/dpkg-deb{,.dist} + rm debian-tmp/var/lib/dpkg/{status,available} + dpkg --build debian-tmp + if file main/dpkg | grep -q ELF; then \ + mv debian-tmp.deb ../dpkg-$(version)elf.deb ; \ + mv ../dpkg-$(version).nondebbin.tar.gz \ + ../dpkg-$(version)elf.nondebbin.tar.gz ; \ + else \ + mv debian-tmp.deb ../dpkg-$(version).deb ; \ + fi + +define checkdir + test -f include/dpkg.h +endef + +source: clean + chmod +x debian.rules + cd .. && \ + tar cf $(package)-$(version).tar $(package)-$(version) && \ + gzip -9vf $(package)-$(version).tar + +diff: + @echo '((( no diff - this package is a Debian special )))' + +checkroot: + $(checkdir) + test root = "`whoami`" + +.PHONY: binary source diff clean checkroot diff --git a/doc/Makefile.in b/doc/Makefile.in new file mode 100644 index 00000000..9b9724c1 --- /dev/null +++ b/doc/Makefile.in @@ -0,0 +1,81 @@ +# Copyright (C) 1994 Ian Murdock +# Copyright (C) 1994,1995 Ian Jackson +# +# This 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, +# or (at your option) any later version. +# +# This 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 dpkg; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +srcdir = @srcdir@ +VPATH = @srcdir@ + +prefix = @prefix@ +infodir = $(prefix)/info +mandir = $(prefix)/man +man5dir = $(mandir)/man5 +man5 = 5 +docdir = $(prefix)/doc +devdocdir = $(docdir)/dpkg + +DIST = Makefile.in $(SRC) $(MAN) + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ + +MAKEINFO = makeinfo +TEXI2DVI = texi2dvi + +DEVDOCS= auto-deconfiguration.txt dependency-ordering.txt \ + disappear-replace.txt diversions.text \ + essential-flag.txt version-ordering.txt + +# Files folded into main guidelines document +OBSOLETEDOCS= descriptions.txt upgrades+errors.txt \ + maintainer-script-args.txt virtual-dependencies.txt + +all: $(DEVDOCS) guidelines.info + +guidelines.info: guidelines.texi + $(MAKEINFO) $(srcdir)/guidelines.texi + +database-structure.ps: database-structure.fig + fig2dev -L ps -c -l _ -P ps + mv ps database-structure.ps + +database-structure.monops: database-structure.ps + perl -pe 's:^/(col[0-7]) \{[01 ]*1[01 ]* setrgbcolor\}\ + bind def$$:/$$1 {} bind def:' database-structure.ps >ps + mv ps database-structure.monops + +#dpkg.dvi: +# $(TEXI2DVI) $(srcdir)/dpkg.texi +# +#dpkg.info: +# $(MAKEINFO) $(srcdir)/dpkg.texi + +clean: + rm -f database-structure.ps database-structure.monops ps + rm -f *.{aux,cp,dvi,fn,ky,log,pg,toc,tp,vr} + rm -f guidelines.info + +distclean: + rm -f Makefile *.orig *~ *.~* ./#*# + +install: all + $(INSTALL_DATA) deb.5 $(man5dir)/deb.$(man5) + $(INSTALL_DATA) deb-control.5 $(man5dir)/deb-control.$(man5) + $(INSTALL_DATA) guidelines.info guidelines.info-*[0-9] \ + $(infodir)/. + set -e; for d in $(DEVDOCS) ; do \ + $(INSTALL_DATA) $$d $(devdocdir)/$$d ; \ + done diff --git a/doc/auto-deconfiguration.txt b/doc/auto-deconfiguration.txt new file mode 100644 index 00000000..5cc0ef55 --- /dev/null +++ b/doc/auto-deconfiguration.txt @@ -0,0 +1,62 @@ +To: Debian developers list +Subject: Re: dpkg maintainer script calls and arguments +In-Reply-To: <1506077@toto.iv> +FCC: ~/mail/Outbound +--text follows this line-- +In order to support easier upgrades where important packages get split +into several pieces, I have implemented the following scheme, which I +described on debian-private a while ago. It is enabled by the use of +`--auto-deconfigure', or `-B', and the dselect method scripts in +0.93.76 have been changed to supply this option. + +] I plan to make it possible to `deconfigure' packages at installation +] time, in order to keep dependency invariants satisfied. In a sane +] installation this will mean that everything will work right, even when +] (for example) an important package is split into two pieces or +] packages. +] +] Basically, suppose that package A is being split into A1 and A2, and B +] and C depend on A1 and A2 respectively. If you try to install A1 dpkg +] will consider removing A (because of the conflict between A and each +] of A1 and A2), but then C's dependency is not satisifed, and if you +] try to install A2 B's dependency wouldn't be satisfied. +] +] At the moment dpkg will simply refuse to do it. You have to say +] --force-depends, or remove either B or C. +] +] I'm going to arrange that dpkg will automatically deconfigure B or C, +] as appropriate, and try to reconfigure it later. +] +] So, if you do `dpkg -i A1.deb A2.deb' all will be well; if you do +] `dpkg -i A1.deb' you'll get A1 installed and configured correctly, but +] error messages about C being broken, and in order to fix C you'll have +] to install A2 as well, or return to A (dpkg will remove A1). +] +] All of this will appear very automatic to people who use dselect. +] People who do things manually will have a slightly more complicated +] task, as dpkg won't remove A (in the scenario above) unless it has +] been selected for deinstallation using dselect or dpkg --remove (which +] would fail because of the dependencies from B and C). + +This means that maintainer scripts can get called in two new ways: + deconfigure in-favour removing + abort-deconfigure in-favour removing +using the example package names above. + +The first call happens before the prerm script of the package which is +being removed (A) is called; the second happens if an error occurs and +dpkg wants to back out of the installation. + +If the installation of both A1 and A2 is successful dpkg will then +call both + configure + configure +as usual. + +Some time ago I posted a message documenting all the maintainer script +calls and their arguments. Below is a revised version of that +message. I shall upload it as maintainer-script-args.txt, and it +should go in project/standards. The top half of this message will go +in auto-deconfiguration.txt. + +Ian. diff --git a/doc/database-structure.fig b/doc/database-structure.fig new file mode 100644 index 00000000..462e2f6c --- /dev/null +++ b/doc/database-structure.fig @@ -0,0 +1,487 @@ +#FIG 2.1 +80 2 +6 59 74 199 169 +2 1 2 1 -1 0 0 0 3.000 -1 0 0 + 69 129 199 129 9999 9999 +2 1 2 1 -1 0 0 0 3.000 -1 0 0 + 69 149 199 149 9999 9999 +2 1 2 1 -1 0 0 0 3.000 -1 0 0 + 69 129 69 169 9999 9999 +2 1 2 1 -1 0 0 0 3.000 -1 0 0 + 59 109 199 109 9999 9999 +2 1 2 1 -1 0 0 0 3.000 -1 0 0 + 139 129 139 169 9999 9999 +2 2 0 1 -1 0 0 0 0.000 0 0 0 + 199 169 199 89 59 89 59 169 199 169 9999 9999 +2 1 2 1 -1 0 0 0 3.000 -1 0 0 + 139 89 139 109 9999 9999 +4 0 12 10 0 -1 0 0.000 4 5 24 64 104 name +4 0 12 10 0 -1 0 0.000 4 9 84 64 122 pkginfoperfile +4 0 12 10 0 -1 0 0.000 4 9 42 74 144 depends +4 0 12 10 0 -1 0 0.000 4 9 48 74 162 depended +4 0 12 10 0 -1 0 0.000 4 9 42 59 84 pkginfo +-6 +6 59 394 199 489 +2 1 2 1 -1 0 0 0 3.000 -1 0 0 + 69 449 199 449 9999 9999 +2 1 2 1 -1 0 0 0 3.000 -1 0 0 + 69 469 199 469 9999 9999 +2 1 2 1 -1 0 0 0 3.000 -1 0 0 + 69 449 69 489 9999 9999 +2 1 2 1 -1 0 0 0 3.000 -1 0 0 + 59 429 199 429 9999 9999 +2 1 2 1 -1 0 0 0 3.000 -1 0 0 + 139 449 139 489 9999 9999 +2 2 0 1 -1 0 0 0 0.000 0 0 0 + 199 489 199 409 59 409 59 489 199 489 9999 9999 +2 1 2 1 -1 0 0 0 3.000 -1 0 0 + 139 409 139 429 9999 9999 +4 0 12 10 0 -1 0 0.000 4 5 24 64 424 name +4 0 12 10 0 -1 0 0.000 4 9 84 64 442 pkginfoperfile +4 0 12 10 0 -1 0 0.000 4 9 42 74 464 depends +4 0 12 10 0 -1 0 0.000 4 9 48 74 482 depended +4 0 12 10 0 -1 0 0.000 4 9 42 59 404 pkginfo +-6 +6 59 234 199 329 +2 1 2 1 -1 0 0 0 3.000 -1 0 0 + 69 289 199 289 9999 9999 +2 1 2 1 -1 0 0 0 3.000 -1 0 0 + 69 309 199 309 9999 9999 +2 1 2 1 -1 0 0 0 3.000 -1 0 0 + 69 289 69 329 9999 9999 +2 1 2 1 -1 0 0 0 3.000 -1 0 0 + 59 269 199 269 9999 9999 +2 1 2 1 -1 0 0 0 3.000 -1 0 0 + 139 289 139 329 9999 9999 +2 2 0 1 -1 0 0 0 0.000 0 0 0 + 199 329 199 249 59 249 59 329 199 329 9999 9999 +2 1 2 1 -1 0 0 0 3.000 -1 0 0 + 139 249 139 269 9999 9999 +4 0 12 10 0 -1 0 0.000 4 5 24 64 264 name +4 0 12 10 0 -1 0 0.000 4 9 84 64 282 pkginfoperfile +4 0 12 10 0 -1 0 0.000 4 9 42 74 304 depends +4 0 12 10 0 -1 0 0.000 4 9 48 74 322 depended +4 0 12 10 0 -1 0 0.000 4 9 42 59 244 pkginfo +-6 +6 559 74 699 169 +2 1 2 1 -1 0 0 0 3.000 -1 0 0 + 569 129 699 129 9999 9999 +2 1 2 1 -1 0 0 0 3.000 -1 0 0 + 569 149 699 149 9999 9999 +2 1 2 1 -1 0 0 0 3.000 -1 0 0 + 569 129 569 169 9999 9999 +2 1 2 1 -1 0 0 0 3.000 -1 0 0 + 559 109 699 109 9999 9999 +2 1 2 1 -1 0 0 0 3.000 -1 0 0 + 639 129 639 169 9999 9999 +2 2 0 1 -1 0 0 0 0.000 0 0 0 + 699 169 699 89 559 89 559 169 699 169 9999 9999 +2 1 2 1 -1 0 0 0 3.000 -1 0 0 + 639 89 639 109 9999 9999 +4 0 12 10 0 -1 0 0.000 4 5 24 564 104 name +4 0 12 10 0 -1 0 0.000 4 9 84 564 122 pkginfoperfile +4 0 12 10 0 -1 0 0.000 4 9 42 574 144 depends +4 0 12 10 0 -1 0 0.000 4 9 48 574 162 depended +4 0 12 10 0 -1 0 0.000 4 9 42 559 84 pkginfo +-6 +6 399 119 499 214 +2 2 0 1 -1 0 0 0 0.000 0 0 0 + 499 214 499 134 399 134 399 214 499 214 9999 9999 +2 1 2 1 -1 0 0 0 3.000 -1 0 0 + 399 154 499 154 9999 9999 +2 1 2 1 -1 0 0 0 3.000 -1 0 0 + 399 174 499 174 9999 9999 +2 1 2 1 -1 0 0 0 3.000 -1 0 0 + 399 194 499 194 9999 9999 +2 1 2 1 -1 0 0 0 3.000 -1 0 0 + 439 134 439 214 9999 9999 +4 0 12 10 0 -1 0 0.000 4 7 12 404 149 up +4 0 12 10 0 -1 0 0.000 4 7 24 404 169 next +4 0 12 10 0 -1 0 0.000 4 7 24 404 189 list +4 0 12 10 0 -1 0 0.000 4 9 24 404 209 type +4 0 12 10 0 -1 0 0.000 4 9 60 399 129 dependency +-6 +6 654 224 754 319 +2 2 0 1 -1 0 0 0 0.000 0 0 0 + 754 319 754 239 654 239 654 319 754 319 9999 9999 +2 1 2 1 -1 0 0 0 3.000 -1 0 0 + 654 259 754 259 9999 9999 +2 1 2 1 -1 0 0 0 3.000 -1 0 0 + 654 279 754 279 9999 9999 +2 1 2 1 -1 0 0 0 3.000 -1 0 0 + 654 299 754 299 9999 9999 +2 1 2 1 -1 0 0 0 3.000 -1 0 0 + 694 239 694 319 9999 9999 +4 0 12 10 0 -1 0 0.000 4 7 12 659 254 up +4 0 12 10 0 -1 0 0.000 4 7 24 659 274 next +4 0 12 10 0 -1 0 0.000 4 7 24 659 294 list +4 0 12 10 0 -1 0 0.000 4 9 24 659 314 type +4 0 12 10 0 -1 0 0.000 4 9 60 654 234 dependency +-6 +6 164 294 174 304 +2 1 0 1 1 0 0 0 0.000 7 0 0 + 164 294 174 304 9999 9999 +2 1 0 1 1 0 0 0 0.000 7 0 0 + 164 304 174 294 9999 9999 +-6 +6 164 454 174 464 +2 1 0 1 1 0 0 0 0.000 7 0 0 + 164 454 174 464 9999 9999 +2 1 0 1 1 0 0 0 0.000 7 0 0 + 164 464 174 454 9999 9999 +-6 +6 464 159 474 169 +2 1 0 1 1 0 0 0 0.000 7 0 0 + 464 159 474 169 9999 9999 +2 1 0 1 1 0 0 0 0.000 7 0 0 + 464 169 474 159 9999 9999 +-6 +6 719 264 729 274 +2 1 0 1 1 0 0 0 0.000 7 0 0 + 719 264 729 274 9999 9999 +2 1 0 1 1 0 0 0 0.000 7 0 0 + 719 274 729 264 9999 9999 +-6 +6 164 154 174 164 +2 1 0 1 5 0 0 0 0.000 7 0 0 + 164 154 174 164 9999 9999 +2 1 0 1 5 0 0 0 0.000 7 0 0 + 164 164 174 154 9999 9999 +-6 +6 354 339 364 349 +2 1 0 1 5 0 0 0 0.000 7 0 0 + 354 339 364 349 9999 9999 +2 1 0 1 5 0 0 0 0.000 7 0 0 + 354 349 364 339 9999 9999 +-6 +6 269 259 389 394 +2 1 2 1 -1 0 0 0 3.000 -1 0 0 + 269 294 389 294 9999 9999 +2 1 2 1 -1 0 0 0 3.000 -1 0 0 + 269 314 389 314 9999 9999 +2 1 2 1 -1 0 0 0 3.000 -1 0 0 + 269 334 389 334 9999 9999 +2 1 2 1 -1 0 0 0 3.000 -1 0 0 + 269 354 389 354 9999 9999 +2 1 2 1 -1 0 0 0 3.000 -1 0 0 + 329 274 329 394 9999 9999 +2 2 0 1 -1 0 0 0 0.000 0 0 0 + 389 394 389 274 269 274 269 394 389 394 9999 9999 +2 1 2 1 -1 0 0 0 3.000 -1 0 0 + 269 374 389 374 9999 9999 +4 0 12 10 0 -1 0 0.000 4 9 48 269 269 deppossi +4 0 12 10 0 -1 0 0.000 4 7 12 274 289 up +4 0 12 10 0 -1 0 0.000 4 7 24 274 309 next +4 0 12 10 0 -1 0 0.000 4 7 12 274 329 ed +4 0 12 10 0 -1 0 0.000 4 7 42 274 389 version +4 0 12 10 0 -1 0 0.000 4 7 42 274 349 nextrev +4 0 12 10 0 -1 0 0.000 4 7 42 274 369 backrev +-6 +6 354 359 364 369 +2 1 0 1 5 0 0 0 0.000 7 0 0 + 354 359 364 369 9999 9999 +2 1 0 1 5 0 0 0 0.000 7 0 0 + 354 369 364 359 9999 9999 +-6 +6 74 564 194 599 +4 0 12 12 0 -1 0 0.000 4 12 84 74 579 Package: foo +4 0 12 12 0 -1 0 0.000 4 12 119 74 595 Depends: a | b, c +-6 +6 389 449 399 459 +2 1 0 1 2 0 0 0 0.000 7 0 0 + 389 449 399 459 9999 9999 +2 1 0 1 2 0 0 0 0.000 7 0 0 + 389 459 399 449 9999 9999 +-6 +6 389 509 399 519 +2 1 0 1 5 0 0 0 0.000 7 0 0 + 389 509 399 519 9999 9999 +2 1 0 1 5 0 0 0 0.000 7 0 0 + 389 519 399 509 9999 9999 +-6 +6 304 409 424 544 +2 1 2 1 -1 0 0 0 3.000 -1 0 0 + 304 444 424 444 9999 9999 +2 1 2 1 -1 0 0 0 3.000 -1 0 0 + 304 464 424 464 9999 9999 +2 1 2 1 -1 0 0 0 3.000 -1 0 0 + 304 484 424 484 9999 9999 +2 1 2 1 -1 0 0 0 3.000 -1 0 0 + 304 504 424 504 9999 9999 +2 1 2 1 -1 0 0 0 3.000 -1 0 0 + 364 424 364 544 9999 9999 +2 2 0 1 -1 0 0 0 0.000 0 0 0 + 424 544 424 424 304 424 304 544 424 544 9999 9999 +2 1 2 1 -1 0 0 0 3.000 -1 0 0 + 304 524 424 524 9999 9999 +4 0 12 10 0 -1 0 0.000 4 9 48 304 419 deppossi +4 0 12 10 0 -1 0 0.000 4 7 12 309 439 up +4 0 12 10 0 -1 0 0.000 4 7 24 309 459 next +4 0 12 10 0 -1 0 0.000 4 7 12 309 479 ed +4 0 12 10 0 -1 0 0.000 4 7 42 309 539 version +4 0 12 10 0 -1 0 0.000 4 7 42 309 499 nextrev +4 0 12 10 0 -1 0 0.000 4 7 42 309 519 backrev +-6 +6 259 119 359 214 +2 2 0 1 -1 0 0 0 0.000 0 0 0 + 359 214 359 134 259 134 259 214 359 214 9999 9999 +2 1 2 1 -1 0 0 0 3.000 -1 0 0 + 259 154 359 154 9999 9999 +2 1 2 1 -1 0 0 0 3.000 -1 0 0 + 259 174 359 174 9999 9999 +2 1 2 1 -1 0 0 0 3.000 -1 0 0 + 259 194 359 194 9999 9999 +2 1 2 1 -1 0 0 0 3.000 -1 0 0 + 299 134 299 214 9999 9999 +4 0 12 10 0 -1 0 0.000 4 7 12 264 149 up +4 0 12 10 0 -1 0 0.000 4 7 24 264 169 next +4 0 12 10 0 -1 0 0.000 4 7 24 264 189 list +4 0 12 10 0 -1 0 0.000 4 9 24 264 209 type +4 0 12 10 0 -1 0 0.000 4 9 60 259 129 dependency +-6 +6 479 279 599 414 +6 564 359 574 369 +2 1 0 1 5 0 0 0 0.000 7 0 0 + 564 359 574 369 9999 9999 +2 1 0 1 5 0 0 0 0.000 7 0 0 + 564 369 574 359 9999 9999 +-6 +6 564 319 574 329 +2 1 0 1 2 0 0 0 0.000 7 0 0 + 564 319 574 329 9999 9999 +2 1 0 1 2 0 0 0 0.000 7 0 0 + 564 329 574 319 9999 9999 +-6 +6 479 279 599 414 +2 1 2 1 -1 0 0 0 3.000 -1 0 0 + 479 314 599 314 9999 9999 +2 1 2 1 -1 0 0 0 3.000 -1 0 0 + 479 334 599 334 9999 9999 +2 1 2 1 -1 0 0 0 3.000 -1 0 0 + 479 354 599 354 9999 9999 +2 1 2 1 -1 0 0 0 3.000 -1 0 0 + 479 374 599 374 9999 9999 +2 1 2 1 -1 0 0 0 3.000 -1 0 0 + 539 294 539 414 9999 9999 +2 2 0 1 -1 0 0 0 0.000 0 0 0 + 599 414 599 294 479 294 479 414 599 414 9999 9999 +2 1 2 1 -1 0 0 0 3.000 -1 0 0 + 479 394 599 394 9999 9999 +4 0 12 10 0 -1 0 0.000 4 9 48 479 289 deppossi +4 0 12 10 0 -1 0 0.000 4 7 12 484 309 up +4 0 12 10 0 -1 0 0.000 4 7 24 484 329 next +4 0 12 10 0 -1 0 0.000 4 7 12 484 349 ed +4 0 12 10 0 -1 0 0.000 4 7 42 484 409 version +4 0 12 10 0 -1 0 0.000 4 7 42 484 369 nextrev +4 0 12 10 0 -1 0 0.000 4 7 42 484 389 backrev +-6 +6 564 379 574 389 +2 1 0 1 5 0 0 0 0.000 7 0 0 + 564 379 574 389 9999 9999 +2 1 0 1 5 0 0 0 0.000 7 0 0 + 564 389 574 379 9999 9999 +-6 +1 3 0 1 4 0 0 0 0.000 1 0.000 569 304 5 5 569 304 574 309 +1 3 0 1 6 0 0 0 0.000 1 0.000 569 344 5 5 569 344 574 349 +4 0 0 12 0 -1 0 0.000 4 6 5 589 289 c +-6 +6 644 409 764 544 +6 729 489 739 499 +2 1 0 1 5 0 0 0 0.000 7 0 0 + 729 489 739 499 9999 9999 +2 1 0 1 5 0 0 0 0.000 7 0 0 + 729 499 739 489 9999 9999 +-6 +6 729 449 739 459 +2 1 0 1 2 0 0 0 0.000 7 0 0 + 729 449 739 459 9999 9999 +2 1 0 1 2 0 0 0 0.000 7 0 0 + 729 459 739 449 9999 9999 +-6 +6 644 409 764 544 +2 1 2 1 -1 0 0 0 3.000 -1 0 0 + 644 444 764 444 9999 9999 +2 1 2 1 -1 0 0 0 3.000 -1 0 0 + 644 464 764 464 9999 9999 +2 1 2 1 -1 0 0 0 3.000 -1 0 0 + 644 484 764 484 9999 9999 +2 1 2 1 -1 0 0 0 3.000 -1 0 0 + 644 504 764 504 9999 9999 +2 1 2 1 -1 0 0 0 3.000 -1 0 0 + 704 424 704 544 9999 9999 +2 2 0 1 -1 0 0 0 0.000 0 0 0 + 764 544 764 424 644 424 644 544 764 544 9999 9999 +2 1 2 1 -1 0 0 0 3.000 -1 0 0 + 644 524 764 524 9999 9999 +4 0 12 10 0 -1 0 0.000 4 9 48 644 419 deppossi +4 0 12 10 0 -1 0 0.000 4 7 12 649 439 up +4 0 12 10 0 -1 0 0.000 4 7 24 649 459 next +4 0 12 10 0 -1 0 0.000 4 7 12 649 479 ed +4 0 12 10 0 -1 0 0.000 4 7 42 649 539 version +4 0 12 10 0 -1 0 0.000 4 7 42 649 499 nextrev +4 0 12 10 0 -1 0 0.000 4 7 42 649 519 backrev +-6 +1 3 0 1 4 0 0 0 0.000 1 0.000 734 434 5 5 734 434 739 439 +1 3 0 1 6 0 0 0 0.000 1 0.000 734 474 5 5 734 474 739 479 +1 3 0 1 5 0 0 0 0.000 1 0.000 734 514 5 5 734 514 739 519 +4 0 0 12 0 -1 0 0.000 4 9 6 744 419 b +4 0 12 12 0 7 0 0.000 4 9 28 719 539 >1.0 +-6 +6 449 564 589 599 +4 0 12 12 0 -1 0 0.000 4 12 70 449 579 Package: c +4 0 12 12 0 -1 0 0.000 4 11 140 449 595 Recommends: b (>1.0) +-6 +1 3 0 1 1 0 0 0 0.000 1 0.000 169 139 5 5 169 139 174 144 +1 3 0 1 1 0 0 0 0.000 1 0.000 669 139 5 5 669 139 674 144 +1 3 0 1 1 0 0 0 0.000 1 0.000 329 164 5 5 329 164 334 169 +1 3 0 1 2 0 0 0 0.000 1 0.000 329 184 5 5 329 184 334 189 +1 3 0 1 2 0 0 0 0.000 1 0.000 469 184 5 5 469 184 474 189 +1 3 0 1 2 0 0 0 0.000 1 0.000 724 289 5 5 724 289 729 294 +1 3 0 1 5 0 0 0 0.000 1 0.000 169 479 5 5 169 479 174 484 +1 3 0 1 5 0 0 0 0.000 1 0.000 669 159 5 5 669 159 674 164 +1 3 0 1 4 0 0 0 0.000 1 0.000 724 249 5 5 724 249 729 254 +1 3 0 1 4 0 0 0 0.000 1 0.000 329 144 5 5 329 144 334 149 +1 3 0 1 4 0 0 0 0.000 1 0.000 469 144 5 5 469 144 474 149 +1 3 0 1 5 0 0 0 0.000 1 0.000 169 319 5 5 169 319 174 324 +1 3 0 1 2 0 0 0 0.000 1 0.000 359 304 5 5 359 304 364 309 +1 3 0 1 4 0 0 0 0.000 1 0.000 359 284 5 5 359 284 364 289 +1 3 0 1 6 0 0 0 0.000 1 0.000 359 324 5 5 359 324 364 329 +1 3 0 1 5 0 0 0 0.000 1 0.000 394 494 5 5 394 494 399 499 +1 3 0 1 4 0 0 0 0.000 1 0.000 394 434 5 5 394 434 399 439 +1 3 0 1 6 0 0 0 0.000 1 0.000 394 474 5 5 394 474 399 479 +2 1 0 1 5 0 0 0 0.000 7 1 0 + 0 0 1.000 4.000 8.000 + 169 479 299 479 9999 9999 +2 1 0 1 1 0 0 0 0.000 -1 0 1 + 0 0 1.000 4.000 8.000 + 394 164 329 164 9999 9999 +3 2 0 1 1 0 0 0 0.000 1 0 + 0 0 1.000 4.000 8.000 + 169 139 199 139 219 144 239 149 254 149 9999 9999 + 0.000 -190.000 186.321 138.362 193.821 138.362 203.746 139.584 + 214.444 142.861 223.556 145.139 234.254 148.416 241.590 149.319 + 245.340 149.319 0.000 -190.000 +3 2 0 1 1 0 0 0 0.000 1 0 + 0 0 1.000 4.000 8.000 + 669 139 704 144 719 154 734 234 9999 9999 + 0.000 -190.000 689.270 140.465 698.020 141.715 708.066 145.553 + 716.306 149.925 728.121 167.798 731.871 187.798 0.000 -190.000 +3 2 0 1 2 0 0 0 0.000 1 0 + 0 0 1.000 4.000 8.000 + 329 184 319 184 314 184 304 194 304 199 304 259 9999 9999 + 0.000 0.000 323.209 184.000 320.709 184.000 317.861 184.000 + 315.246 183.484 310.477 185.460 305.459 190.476 303.484 195.246 + 304.000 197.861 304.000 209.251 304.000 224.251 0.000 0.000 +3 2 0 1 2 0 0 0 0.000 1 0 + 0 0 1.000 4.000 8.000 + 359 304 389 309 399 339 404 384 404 419 9999 9999 + 0.000 0.000 376.183 303.206 383.683 304.456 396.372 315.299 + 397.434 331.869 401.243 349.210 403.427 373.663 404.332 389.993 + 404.332 398.743 0.000 0.000 +3 2 0 1 2 0 0 0 0.000 1 0 + 0 0 1.000 4.000 8.000 + 469 184 504 189 534 229 549 289 9999 9999 + 0.000 0.000 489.097 184.107 497.847 185.357 515.602 195.870 + 528.871 218.224 538.758 238.997 542.508 253.997 0.000 0.000 +3 2 0 1 2 0 0 0 0.000 1 0 + 0 0 1.000 4.000 8.000 + 724 289 709 299 704 414 9999 9999 + 0.000 0.000 714.657 293.115 710.907 295.615 696.821 320.614 + 695.571 349.364 0.000 0.000 +3 2 0 1 5 0 0 0 0.000 1 0 + 0 0 1.000 4.000 8.000 + 394 494 444 499 569 499 639 499 9999 9999 + 0.000 0.000 422.899 497.321 435.399 498.571 472.526 500.423 + 540.524 499.000 580.960 499.000 598.460 499.000 0.000 0.000 +3 2 0 1 5 0 0 0 0.000 1 0 + 0 0 1.000 4.000 8.000 + 669 159 649 159 634 164 604 204 579 289 9999 9999 + 0.000 0.000 657.478 158.436 652.478 158.436 645.334 159.595 + 637.243 161.663 623.745 171.392 609.302 193.426 596.954 218.053 + 590.704 239.303 0.000 0.000 +3 2 0 1 4 0 0 0 0.000 1 0 + 0 0 1.000 4.000 8.000 + 394 434 414 429 419 424 414 279 364 219 9999 9999 + 0.000 0.000 405.666 432.138 410.666 430.888 415.525 428.137 + 418.291 425.798 433.545 387.100 427.322 313.905 408.621 264.907 + 396.121 249.907 0.000 0.000 +3 2 0 1 4 0 0 0 0.000 1 0 + 0 0 1.000 4.000 8.000 + 569 304 554 234 529 189 504 174 9999 9999 + 0.000 0.000 562.162 263.254 558.412 245.754 549.770 222.731 + 537.823 198.122 525.252 185.125 519.002 181.375 0.000 0.000 +3 2 0 1 4 0 0 0 0.000 1 0 + 0 0 1.000 4.000 8.000 + 359 284 359 249 354 219 9999 9999 + 0.000 0.000 359.497 263.759 359.497 255.009 358.568 243.779 + 357.318 236.279 0.000 0.000 +3 2 0 1 4 0 0 0 0.000 1 0 + 0 0 1.000 4.000 8.000 + 734 434 724 379 724 324 9999 9999 + 0.000 0.000 727.366 402.355 724.866 388.605 723.148 369.549 + 723.148 355.799 0.000 0.000 +3 2 0 1 4 0 0 0 0.000 1 0 + 0 0 1.000 4.000 8.000 + 724 249 719 209 699 174 9999 9999 + 0.000 -190.000 722.530 225.840 721.280 215.840 716.720 202.160 + 711.720 193.410 0.000 -190.000 +3 2 0 1 4 0 0 0 0.000 1 0 + 0 0 1.000 4.000 8.000 + 329 144 304 144 269 139 219 124 204 124 9999 9999 + 0.000 -190.000 314.537 144.305 308.287 144.305 295.917 143.426 + 276.919 140.743 257.308 136.426 231.067 125.771 216.399 123.618 + 212.649 123.618 0.000 -190.000 +3 2 0 1 5 0 0 0 0.000 1 0 + 0 0 1.000 4.000 8.000 + 169 319 189 319 229 324 254 329 264 329 9999 9999 + 0.000 0.000 180.574 318.787 185.574 318.787 198.208 319.573 + 219.921 322.527 234.742 324.932 248.152 328.421 255.720 329.170 + 258.220 329.170 0.000 0.000 +3 2 0 1 6 0 0 0 0.000 1 0 + 0 0 1.000 4.000 8.000 + 394 474 339 469 299 469 259 469 234 469 204 469 9999 9999 + 0.000 0.000 362.200 470.679 348.450 469.429 329.874 468.586 + 308.112 469.000 289.888 469.000 268.112 469.000 253.305 469.000 + 239.695 469.000 228.874 469.000 221.374 469.000 0.000 0.000 +3 2 0 1 6 0 0 0 0.000 1 0 + 0 0 1.000 4.000 8.000 + 734 474 659 469 434 484 299 489 204 489 9999 9999 + 0.000 0.000 690.661 470.250 671.911 469.000 607.354 469.001 + 485.316 481.339 403.258 485.594 329.783 488.430 282.765 489.301 + 259.015 489.301 0.000 0.000 +3 2 0 1 6 0 0 0 0.000 1 0 + 0 0 1.000 4.000 8.000 + 569 344 594 339 619 304 634 204 644 174 9999 9999 + 0.000 0.000 583.473 343.015 589.723 341.765 603.620 332.780 + 615.079 313.689 628.219 281.222 628.581 226.603 635.271 198.699 + 637.771 191.199 0.000 0.000 +3 2 0 1 6 0 0 0 0.000 1 0 + 0 0 1.000 4.000 8.000 + 359 324 294 319 204 309 9999 9999 + 0.000 0.000 321.344 321.293 305.094 320.043 278.591 317.552 + 256.091 315.052 0.000 0.000 +3 2 0 1 5 0 0 0 0.000 0 1 + 0 0 1.000 4.000 8.000 + 429 514 564 519 714 519 734 514 9999 9999 + 0.000 0.000 507.163 517.323 540.913 518.573 598.180 519.633 + 679.471 523.251 717.559 518.562 722.559 517.312 0.000 0.000 +3 2 0 1 4 0 0 0 0.000 0 1 + 0 0 1.000 4.000 8.000 + 204 99 364 109 414 139 469 144 9999 9999 + 0.000 0.000 296.049 97.811 336.049 100.311 377.555 113.214 + 400.605 134.627 423.515 142.106 437.265 143.356 0.000 0.000 +4 0 0 12 0 -1 0 0.000 4 9 15 159 104 foo +4 0 0 12 0 -1 0 0.000 4 6 5 479 129 c +4 0 0 12 0 -1 0 0.000 4 12 42 309 209 depends +4 0 0 12 0 -1 0 0.000 4 12 42 449 209 depends +4 0 0 12 0 -1 0 0.000 4 9 6 744 234 b +4 0 0 12 0 -1 0 0.000 4 6 18 714 314 rec. +4 0 0 12 0 -1 0 0.000 4 6 6 164 264 a +4 0 0 12 0 -1 0 0.000 4 9 6 164 424 b +4 0 0 12 0 -1 0 0.000 4 6 5 664 104 c +4 0 0 18 0 -1 0 0.000 4 17 632 64 54 example of the structures (in the C code) which contain related packages information +4 0 12 12 0 -1 0 0.000 4 12 70 284 589 Package: a +4 0 0 12 0 -1 0 0.000 4 6 6 374 269 a +4 0 0 12 0 -1 0 0.000 4 9 6 384 419 b +4 0 0 12 0 -1 0 0.000 4 9 15 339 129 a|b +4 0 12 12 0 -1 0 0.000 4 12 70 674 589 Package: b diff --git a/doc/deb-control.5 b/doc/deb-control.5 new file mode 100644 index 00000000..9436641c --- /dev/null +++ b/doc/deb-control.5 @@ -0,0 +1,104 @@ +.\" Hey, Emacs! This is an -*- nroff -*- source file. +.\" Author: Raul Miller +.\" Includes text from the debian Guidelines by Ian Jackson, Ian Murdock +.TH DEB-CONTROL 5 "29th November 1995" "Debian Project" "Debian GNU/Linux" +.SH NAME +deb\-control \- Debian GNU/Linux packages' master control file format +.SH SYNOPSIS +control +.SH DESCRIPTION +Master control file format: +.LP +The `control' file contains a number of fields. Each field begins +with a tag, such as `PACKAGE' or `VERSION' (case insensitive), +followed by a colon, and the body of the field. Fields are delimited +only by field tags. In other words, field text may be multiple lines +in length, but the installation tools will generally join lines when +processing the body of the field. +.SH REQUIRED FIELDS +.TP +.BR PACKAGE: \ +The value of this field is used to generate file names by some +installation tools. +.TP +.BR VERSION: \ +typically, this is the original portable package's version +number in whatever form the program's author uses. +.TP +.BR PACKAGE_REVISION: \ +this should usually be a plain number, or perhaps two numbers +separated by a full stop. +.TP +.BR MAINTAINER: \ +should be in the format Joe Bloggs . +.TP +.BR DESCRIPTION: \ +.SH OPTIONAL FIELDS +.TP +.BR DEPENDS: \ +list of packages that are required for this package to provide a +non-trivial amount of functionality. The package maintenance software +will not allow a package to be installed without also installing +packages listed in its +.B DEPENDS +field, and will rin the postinst scripts of packages listed in DEPENDS +fields before those of the packages which depend on them, and run +prerm scripts before. +.TP +.BR RECOMMENDED: \ +lists packages that would be found together with +this one in all but unusual installations. The package maintenance +software will warn the user if they install a package without those +listed in its +.B RECOMMENDED +field. +.LP +The syntax of +.B DEPENDS +and +.B RECOMMENDED +is a list of groups of alternative packages. Each group is a list of +packages separated by vertical bar (or `pipe') symbols, `|'. The +groups are separated by commas. Each package is a package name +optionally followed by a version number specification in parentheses. +A version number may start with a `>', in which case any later version +will match, and may specify or omit the Debian packaging revision +(separated by a hyphen). Commas are to be read as `AND', and pipes as +`OR', with pipes binding more tightly. +.TP +.BR OPTIONAL: \ +lists packages that are related to this one and can perhaps enhance +its usefulness, but without which installing this package is perfectly +reasonable. The package maintenance software will not moan at the +user for not selecting +.B OPTIONAL +related packages, but may use the information in the +.B OPTIONAL +field to assist the user during package selection. +.TP +.BR CONFLICTS: \ +lists packages that conflict with this one, for example by containing +files with the same names (an example would be Smail vs. Sendmail). +The package maintenance software will not allow conflicting packages +to be installed. Two conflicting packages should each include a +.B CONFLICTS +line mentioning the other. +.LP +The syntax of +.B OPTIONAL +and +.B CONFLICTS +is a list of package names, separated by commas (and optional +whitespace). In the +.B CONFLICTS +field, the comma should be read as `OR'. + +.SH BUGS +This manpage is seriously out of date. + +.SH SEE ALSO +.BR deb (5), +.BR dpkg (5), +.BR dpkg (8), +.BR dpkg-dep (8), +.BR dselect (8). diff --git a/doc/deb.5 b/doc/deb.5 new file mode 100644 index 00000000..35070eb8 --- /dev/null +++ b/doc/deb.5 @@ -0,0 +1,69 @@ +.\" Hey, Emacs! This is an -*- nroff -*- source file. +.\" Author: Raul Miller +.TH DEB 8 "29th November 1995" "Debian Project" "Debian GNU/Linux" +.SH NAME +deb - Debian GNU/Linux package format +.SH SYNOPSIS +.IB .deb +.SH DESCRIPTION +Debian archive file format. +Version 0.93 is implemented as follows: +.TP +line 1: +version number +.RB ( 0.93 ...), +followed by +.BR newline . +.TP +line 2: +number of characters occupied by control area expressed in decimal, +followed by +.BR newline . +.TP +control area: +compressed gzipped ustar formatted archive. Must contain file named +.BR control . +May optionally contain files named: +.BR conffiles , +.BR preinst , +.BR prerm , +.BR postint , +.BR postrm . +.TP +files archive area: +compressed gzipped ustar formatted archive. [with file structures +designed to be unpacked in the root directory]. +.SH FILES +The files represented in the control area have special significance: +.TP +.B control +see +.BR deb-control (5). +.TP +.B conffiles +a line delimited list of "configuration files" which have special +significance to +.BR dpkg (8). +.TP +.B preinst +an executable to be run before unpacking the archived files. +.TP +.B prerm +an executable to be run before removing files from a prior installation. +.TP +.B postinst +an executable to be run after unpacking the archived files. +.TP +.B postrm +an executable to be run after removing files from a prior +installation. + +.BUGS +There is a new package format, which is not documented here. + +.SH SEE ALSO +.BR deb-control (5), +.BR dpkg (5), +.BR dpkg (8), +.BR dpkg-dep (8), +.BR dselect (8). diff --git a/doc/dependency-ordering.txt b/doc/dependency-ordering.txt new file mode 100644 index 00000000..f3f67940 --- /dev/null +++ b/doc/dependency-ordering.txt @@ -0,0 +1,97 @@ +To: Debian developers list +Subject: Note about the default for virtal package dependencies + +As I wrote some time ago (see below), ordering is significant in the +Depends and Recommended fields - in the absence of other information +dselect will suggest to the user that they select the first named +package in a list of options. + +However, there is no way to specify the `order' of several packages +which all Provide the same thing, when that thing is listed as a +Dependency. + +Eg, if we have: + Package: glibcdoc + Recommended: info-browser + + Package: info + Provides: info-browser + + Package: emacs + Provides: info-browser + +then (if emacs and info are both in the same Class) dselect's choice +will be essentially random. + +It is important to think about this problem, and to consider whether +to list one the the packages explicitly. + +For example, + Package: glibcdoc + Recommended: info | info-browser + +will do the same as the above, except that it will ensure that `info' +is the package which dselect will suggest to the user they also select +if the user has neither it nor Emacs and asks to select glibcdoc. + +This is not necessary if one of the packages has a more fundamental +Class - see the details below. + +Ian. + +------- Start of forwarded message ------- +To: Debian developers list +Subject: Ordering is significant in Depends: and Recommends: + +For dselect, the ordering of alternative packages in a Depends: or +Recommended: line is significant. + +When an unsatisfied dependency (Depends or Recommended) or a conflict +is detected dselect will go into a `recursive package list', where the +user gets to choose how to resolve the problem. + +Usually dselect will suggest to the user that they select the package +with the most `fundamental' class (eg, it will prefer Base packages to +Optional ones), or the one that they `most wanted' to select in some +sense. + +However, in the absence of other information dselect will prefer +packages listed earlier in the unsatisfied entry in the Depends or +Recommended field. + +NB: this doesn't apply to constructions of the form: + Package: auctex + Depends: emacs, tex +which specifies that auctex depends on *both* emacs and tex. In this +case dselect will suggest to the user that they select both packages. + +It applies to constructions of the form: + Package: a2gs + Recommended: gs_x | gs_both | gs_svga +Here, dselect will prefer gs_x because it is listed earlier. (In the +future I may make it more clever - it may be able to notice, to +continue the example, that the dependencies of gs_x are not yet +satisfied while those of gs_svga, are, and thus prefer the latter, or +in a different situation to notice that gs_both has extra dependencies +which are satisfied, and thus prefer it to gs_x and gs_svga. More +thought is needed in this area.) + +One final example. In the more complicated construction: + Package: trn + Depends: smail | sendmail, inn | inewsinn +dselect will prefer smail because it is a Standard package, and +Sendmail is only Optional, and will prefer inewsinn because it is +Recommended and inn is only Optional. So, the default (if none of the +other packages were selected) would be to select smail and inewsinn. + +However, if inewsinn were moved to Optional this would change, and inn +would be preferred whenever the issue arose after the change. + +Optional fields have the same structure as Depends and Recommended +fields, but they will not arrange for the packages they list to be +suggested for selection, though they will be offered to the user. + +Ian M: can this go in an appendix to the Guidelines ? + +Ian. +------- End of forwarded message ------- diff --git a/doc/descriptions.txt b/doc/descriptions.txt new file mode 100644 index 00000000..fdc302b1 --- /dev/null +++ b/doc/descriptions.txt @@ -0,0 +1,112 @@ +To: Debian developers list +Subject: Package maintainers please look at your Description fields. + +dselect will be much more useful when more packages are more +informative in the Description they provide in their control file. + +So, when you next release a package, could you please check whether +the `control' file has a good description of the package, formatted as +described below ? + +A small amount of effort here on the part of package maintainers will +improve the looks of things quite a bit, I think. + +BTW, a number of packages have been indenting continuation lines in +their Description fields thus: + Description: gnomovision + Gnomovision is .... + further blurb ... +Please don't do this. According to the scheme described below (which +I've now implemented), dselect interprets the extra indentation to +mean `preformatted' text, and doesn't wordwrap it. + +Ian. + + +The format of the Description field is as follows: + +Description: + + +The extended description has several kinds of line: + + - those starting with a single space are part of a paragraph. +Successive lines of this form will be word-wrapped when displayed. +The leading space will usually be stripped off. + + - those starting with two or more spaces. These will be displayed +verbatim. If the display cannot be panned horizontally the displaying +program will linewrap them `hard' (ie, without taking account of word +breaks). If it can they will be allowed to trail off to the right. +None, one or two initial spaces may be deleted, but the number of +spaces deleted from each line will be the same (so that you can have +indenting work right, for example). + + - those containing a single space followed by a single full stop +character. These are rendered as blank lines. This is the ONLY way +to get a blank line - see below. + + - those containing a space, a full stop and some more characters. +These are for future expansion. Don't use them. + +IMPORTANT and not so important TIPS: + +* ALWAYS START EXTENDED DESCRIPTION LINES WITH AT LEAST ONE WHITESPACE +CHARACTER. Fields in the control file and in the Packages file are +separated by field names starting in the first column, just as in +RFC822. Forgetting the whitespace will cause dpkg-deb (>=0.93.23) to +produce a syntax error when trying to build the package. If you force +it to build anyway dpkg will refuse to install the resulting mess. + +* DO NOT INCLUDE ANY COMPLETELY EMPTY LINES. These separate different +records in the Packages file, and are forbidden in control files. See +the previous paragraph for what happens if you get this wrong. + +* The single line synopsis should be kept brief - certainly under 80 +characters. My current working half-dselect displays the first 49 +characters if you're using an 80-column terminal. + +* Don't include the package name in the synopsis line. The display +software knows how to display this already, and you don't need to +state it. + +* The extended description should describe what the package does, and +what component it forms of any larger subsystem of which it is a part. + +* Put important information first, both in the synopis and extended +description. Sometimes only the first part of the synopsis or of the +description will be displayed. You can assume that there will usually +be a way to see the whole extended description. + +* You may include information about dependencies and so forth in the +extended description, if you wish. + +* Don't use tab characters. Their effect is not predictable. + +Example control file for Smail: + +Package: smail +Version: 3.1.29.1 +Package_Revision: 8 +Maintainer: Ian Jackson +Recommended: pine | elm | emacs | mh | mailx +Optional: metamail +Depends: cron +Conflicts: sendmail +Description: Electronic mail transport system. + Smail is the recommended mail transport agent (MTA) for Debian. + . + An MTA is the innards of the mail system - it takes messages from + user-friendly mailer programs and arranges for them to be delivered + locally or passed on to other systems as required. + . + In order to make use of it you must have one or more user level + mailreader programs such as elm, pine, mailx or Emacs (which has Rmail + and VM as mailreaders) installed. If you wish to send messages other + than just to other users of your system you must also have appropriate + networking support, in the form of IP or UUCP. + +-- +Ian Jackson, at home. ijackson@nyx.cs.du.edu or iwj10@cus.cam.ac.uk ++44 1223 575512 Escoerea on IRC. http://www.cl.cam.ac.uk/users/iwj10/ +2 Lexington Close, Cambridge, CB4 3LS, England. Urgent: iwj@cam-orl.co.uk diff --git a/doc/disappear-replace.txt b/doc/disappear-replace.txt new file mode 100644 index 00000000..8335a0ea --- /dev/null +++ b/doc/disappear-replace.txt @@ -0,0 +1,44 @@ +From ian Tue Apr 18 23:30:04 1995 +X-VM-v5-Data: ([nil nil nil nil nil nil nil nil nil] + [nil nil nil nil nil nil nil nil nil nil nil nil "^To:" nil nil nil nil nil nil nil] + nil) +X-VM-Summary-Format: "%3n %a %2d %3m %-19.19F %s\n" +X-VM-Labels: nil +X-VM-VHeader: ("Resent-" "From:" "Sender:" "To:" "Apparently-To:" "Cc:" "Subject:" "Date:") nil +X-VM-Bookmark: 5 +To: Debian developers list +Subject: Handling of base packages + +I propose to implement the following scheme to enable obsolete base +packages to disappear, and to allow files in the base packages to move +between one package and another. + +1. When a package is installed, and contains files that are already +marked as belonging to some other package, the older package will have +the files that have been overwritten removed from its file list. + +2. When a package ceases to contain any files due to the action of +point 1 above, its postrm script is run with the argument `disappear' +(in place of `remove', `purge' or whatever). It will then be moved +into the `purge ok not-installed' state, so that it will cease to +appear in dpkg and dselect lists. Its conffiles will be ignored, +*not* purged. The prerm will *not* be run as the packaging system +doesn't know what files are in a package until it unpacks it. + +This will all happen during the `unpack' phase of the replacing +package. + +3. If a base system package which is being installed conflicts with +another base system package which is currently installed on the +system, the currently installed one will be removed first (the prerm +will be run with `replace ' as arguments, then the +package will be removed, then the postrm will be run, likewise with +`replace'). If the replacement fails the removal will be aborted, +involving running the old packages' scripts with `abort-replace'. + +4. Base system packages may not be removed except under 2. or 3. +above. (There will be a --force-remove-base flag to allow foolhardy +users to go ahead anyway.) + +Ian. + diff --git a/doc/diversions.text b/doc/diversions.text new file mode 100644 index 00000000..8cfec095 --- /dev/null +++ b/doc/diversions.text @@ -0,0 +1,131 @@ +(These messages have been edited to conform to the terminology +eventually decided on.) + +------- start of digest (2 messages) (RFC 934 encapsulation) ------- +Resent-Message-Id: +Resent-From: ijackson (Ian Jackson) +Resent-To: ian +From: ian@chiark.chu.cam.ac.uk (Ian Jackson) +To: Debian developers list +Subject: `diverting' dpkg for a particular file +Date: Wed, 17 Jan 96 18:31 GMT + +I'm almost finished with the implementation of a feature for dpkg that +will allow the sysadmin or package maintainer to prevent dpkg from +overwriting a particular file. + +Instead, whenever dpkg finds a package containing that file it +`redirects' the reference to an alternative name. + +Eg, if you were to `divert' /usr/sbin/smail to /usr/sbin/smail.real +then any package containing /usr/sbin/smail would have the file placed +as /usr/sbin/smail.real instead. The feature will work during package +removal, as well. + +There's provision for a single package to be named which is allowed to +provide a version of the file which will installed under the original +name. + +This feature shouldn't be mixed with the conffiles update mechanism, +as this is unlikely to produce useful results and likely to produce +confusion on the part of the user at the very least and possibly on +the part of dpkg too :-). + +No package should contain a file whose name is the diverted name of +another file; dpkg will spot this and balk if such a package is +installed when the diversion is in place, or if a diversion is set up +which involves overwriting an existing file whether managed by dpkg +or not (this latter check only happens if dpkg-divert is given the +--rename option which makes it actually rename any copy of the file +found in the filesystem). + +Only one diversion for a particular file is allowed, and you can't +divert A to B and then B to C. + +[...] + +This feature is intended to be used sparingly; a system administrator +can use it to keep a locally-installed version of a piece of system +software that has to live in a particular place. + +A package should preferably only use it if the package's main function +is to replace the file in question (whether or not the diverted - ie, +replaced, in this case - version of the file needs to be available); +otherwise a sysadmin might find that the feature wasn't available to +them when they wanted to install their own version of the file because +a package had already done so. + +It's possible that I might introduce a facility that would allow +*requests* for redirection of files to be redirected themselves, by +using a special 2nd-level redirection option. + +Ian. +------------------------------ +To: debian-devel@Pixar.com +Subject: Re: `overriding' dpkg for a particular file + +[...] +Forgive me for being perhaps rather baseic, but here are two examples, +diagrammatically: + +1. Administrator wants to replace a Debian-provided program with + their own version, or wants to put a wrapper around it: + ____________________ + smail.deb___________ / \ + | ... | |\____________________/| + | /usr/sbin/smail --+----. | | + | ... | \ | / | + |___________________| \ | /usr | + \ | /usr/sbin | + `-------> /usr/sbin/smail.real | + .-------> /usr/sbin/smail | + ~/stuff/smail/wrapper.c / | | + ~/stuff/smail/wrapper ----' \____________________/ + + # dpkg-divert --divert /usr/sbin/smail.real /usr/sbin/smail + # cp ~fred/stuff/smail/wrapper /usr/sbin/smail + +2. Package maintainer wants to provide an `improved' version of a + file in another package. + + fileutils.deb_______ ____________________ + | ... | / \ + | /bin/ls ----------+----. |\____________________/| + | ... | \ | | + |___________________| \ | / | + \ | /bin | + colour-ls.deb_______ `-------> /bin/ls.mono | + | ... | .--------> /bin/ls | + | /bin/ls ----------+------' | | + | ... | \____________________/ + |...................| + |preinst: | + | dpkg-divert --divert /bin/ls.mono \ + | --package colour-ls /bin/ls + |...................| + |postrm: | + | dpkg-divert --remove --divert /bin/ls.mono \ + | --package colour-ls /bin/ls + |___________________| + +We need a name that applies to `/usr/sbin/smail.real' and +`/bin/ls.mono', the filenames, in both situations, and another name +to apply to `/usr/sbin/smail' and `/bin/ls'. + +Raul Miller writes ("Re: `overriding' dpkg for a particular file"): +[...] +> Also, it would be nice to see either some documentation or some sort +> of warning about the case where the file is a directory. + +If the file is a directory there will be no good effect. This is +because the redirection would affect only the entry for the directory +itself in the packages whose instances if it were being redirected, +and would not affect any of the files in it. + +The most likely result is that dpkg will fail to install the package +because one of the directories where it has files doesn't exist. It +would probably create the `diverted' name of the directory, fail, and +then clean it up. + +Ian. +------- end ------- diff --git a/doc/dpkg.texi b/doc/dpkg.texi new file mode 100644 index 00000000..b9530d6a --- /dev/null +++ b/doc/dpkg.texi @@ -0,0 +1,101 @@ +\input texinfo @c -*-texinfo-*- +@c %**start of header +@setfilename dpkg.info +@settitle The @code{dpkg} Package Maintenance System +@c %**end of header + +@ifinfo +@format +START-INFO-DIR-ENTRY +* dpkg: (dpkg). The @code{dpkg} package maintenance system. +END-INFO-DIR-ENTRY +@end format +@end ifinfo + +@setchapternewpage off + +@ifinfo +This file documents the @code{dpkg} package maintenance system. + +Copyright (C) 1994 Ian A. Murdock + +Permission is granted to make and distribute verbatim copies of this +document provided the copyright notice and this permission notice are +preserved on all copies. + +@ignore +Permission is granted to process this file through TeX and print the +results, provided the printed document carries a copying permission +notice identical to this one, except for the removal of this paragraph +(this paragraph not being relevant to the printed manual). +@end ignore + +Permission is granted to copy and distribute modified versions of this +document under the conditions for verbatim copying, provided that the +entire resulting derived work is distributed under the terms of a +permission notice identical to this one. + +Permission is granted to copy and distribute translations of this document +into another language, under the above conditions for modified versions, +except that this permission notice may be stated in a translation approved +by The Debian Linux Association. +@end ifinfo + +@titlepage +@title The @code{dpkg} Package Maintenance System +@author Ian A. Murdock +@page + +@vskip 0pt plus 1filll +Copyright @copyright{} 1994 Ian A. Murdock + +Permission is granted to make and distribute verbatim copies of this +document provided the copyright notice and this permission notice are +preserved on all copies. + +Permission is granted to copy and distribute modified versions of this +document under the conditions for verbatim copying, provided that the +entire resulting derived work is distributed under the terms of a +permission notice identical to this one. + +Permission is granted to copy and distribute translations of this document +into another language, under the above conditions for modified versions, +except that this permission notice may be stated in a translation approved +by The Debian Linux Association. +@end titlepage + +@node Top, Overview, (dir), (dir) + +@top Introduction +@menu +* Overview:: An overview of the @code{dpkg} package + maintenance system. +* Installation:: How to install a package with @code{dpkg}. +* Removal:: How to remove a package with @code{dpkg}. +* Information:: How to obtain information about both + installed and not-yet-installed packages. +* Extension:: How to extend @code{dpkg} to support new + package formats. +* Guidelines:: Guidelines for creating and maintaining + packages for Debian GNU/Linux. +@end menu + +@node Overview +@chapter Overview + +@node Installation +@chapter Package Installation + +@node Removal +@chapter Package Removal + +@node Information +@chapter Package Information + +@node Guidelines +@chapter Debian GNU/Linux Guidelines + +@node Extension +@chapter How to Extend @code{dpkg} to Support New Package Formats + +@bye diff --git a/doc/essential-flag.txt b/doc/essential-flag.txt new file mode 100644 index 00000000..34e82113 --- /dev/null +++ b/doc/essential-flag.txt @@ -0,0 +1,43 @@ +From ian Thu Jul 6 21:14:08 +0100 1995 +X-VM-v5-Data: ([nil nil nil nil nil nil nil nil nil] + [nil nil nil nil nil nil nil nil nil nil nil nil "^To:" nil nil nil nil nil nil nil] + nil) +In-Reply-To: +References: +To: debian-devel@pixar.com +Subject: Re: non-uninstallable packages + +Bruce Perens writes ("non-uninstallable packages"): +> Assume that a package, such as one in the base, is supposed to be +> non-uninstallable because it is a critical system component. We should +> specify that in an unambiguous form, rather than indicating it by +> specifying "Class: base" in the control file. +> +> I suggest yet another control file field, called "Flags: ". This is +> followed by a comma-delimited set of flags. An example might be: +> Flags: no-uninstall +> +> Another alternative would be to add control-file fields for each flag. +> In this case, the field would appear as: +> Allow-uninstall: no +> ...and the default would be "yes". + +I think you're right. Bill spotted that we were trying to overload +the `Class' field. + +I don't think there's any need for a generic `Flags:' field; a simple +extra field is fine, unless we're going to have an awful lot of +boolean flags applying to packages (any attribute with a value is +better handled using a field of its own anyway). + +I propose to call the new field `Essential', with allowable values +`yes' and `no' and a default of `no'. + +This will be clearer all round, I think, than attempting to describe +dpkg behaviour in the field name. + +If I don't have to make an emergency bugfix release of dpkg first this +will be in the next version. I'll modify dselect too. + +Ian. + diff --git a/doc/guidelines.info-1 b/doc/guidelines.info-1 new file mode 100644 index 00000000..ac917144 --- /dev/null +++ b/doc/guidelines.info-1 @@ -0,0 +1,1039 @@ +This is Info file guidelines.info, produced by Makeinfo-1.63 from the +input file ./guidelines.texi. + +START-INFO-DIR-ENTRY +* Guidelines: (guidelines). How to make Debian packages. +END-INFO-DIR-ENTRY + + +File: guidelines.info, Node: Top, Next: Additional Information, Prev: (dir), Up: (dir) + +Debian GNU/Linux Packaging Guidelines +************************************* + + This file documents the steps that must be taken in the preparation +of a Debian GNU/Linux package. All submissions to be included in the +distribution proper and all packages to be considered for `contrib' or +`non-free' availability *must* conform to the guidelines and standards +described in this document or they cannot be included or made available +at the archive with the distribution. + + Please read the Guidelines carefully. If you have comments or +questions, please contact `debian-devel@pixar.com'. If you are +planning on going further than just contributing a package (i.e., if +you plan to maintain it for an extended period of time or if you are +generally interested in becoming more involved in the Project), you +should join the `debian-devel' mailing list. For more details, read +`info/mailing-lists.txt', available at any Debian GNU/Linux archive. + + (This file was last updated on 26th January 1996. Please check the +most recent `dpkg' package at any Debian GNU/Linux archive for a +potentially more up to date copy.) + +* Menu: + +* Additional Information:: Where other info is to be found. +* Package Copyright:: A few words about the importance of + understanding the package copyright. +* Package Content:: Requirements for the package content. +* Source Package:: Creating the source package. +* Binary Package:: Creating the binary package. +* Control Files:: The binary package control files. +* Appendix:: More specific details about some aspects. + + +File: guidelines.info, Node: Additional Information, Next: Package Copyright, Prev: Top, Up: Top + +Additional Information +********************** + + These Guidelines are intended to be fairly general. More specific +information is available about certain aspects of building packages, +such as how to use the features of Init under Debian GNU/Linux and how +to interact with some more technical details of dpkg's operation. This +information can be found in the directory `doc/package-developer' at +any Debian GNU/Linux archive. At the time of this writing, the +following documents are available: + +`virtual-package-names-list.text' + The list of virtual package names currently in use, together with + the procedure for getting new virtual package names allocated. + +`auto-deconfiguration.txt' + How dpkg can sometimes automatically deconfigure packages in order + to do bulk installations smoothly. + +`dpkg-essential-flag.txt' + How to tell dpkg a package is essential and should not be removed. + (This is for the use of base system packages only.) + +`dpkg-disappear-replace.txt' + What happens when a package appears to have been completely + replaced. + + In the future, we hope also to make available: + +`copyright.txt' + How to choose a good copyright notice to attach to new programs. + +`version-ordering.txt' + The algorithm with which packages' version numbers are compared. + + Also, you should download the sample files and the sample package +(GNU Hello) available in `standards/samples'. You may use any of this +material as a starting point for new packages. The following sample +files, incidentally, are available: + + * debian.README + + * debian.control + + * debian.postinst + + * debian.postrm + + * debian.rules + + Some more detailed information about certain topics is available in +the appendix to this document (*note Appendix::.). + + +File: guidelines.info, Node: Package Copyright, Next: Package Content, Prev: Additional Information, Up: Top + +Package Copyright +***************** + + Please study the copyright of your submission *carefully* and +*understand it* before proceeding! If you have doubts or questions, +please ask! + + In order to understand how we classify and distribute certain +packages, it is important to understand the distinction between being +freely available and being freely redistributable. + + Being "freely available", quite simply, means that the software can +be made available freely, at least for non-commercial purposes and in +its original, unmodified form. This includes packages made available +freely that have restrictions on non-commercial use, redistribution of +modifications, etc. Being freely available, therefore, has nothing to +do with being able to modify and redistribute the software. It only +means that you can get a copy of the software without having to pay +(and it does not necessarily mean that you can *use* the software +without having to pay--shareware is an example of freely available +software). + + "freely redistributable", while generally being freely available, +goes beyond just being freely available. Freely redistributable means +that that the software, in addition to being able to be made available +freely, must be able to be freely modified and redistributed without +restriction. + + All submissions to be included in the distribution proper *must* be +freely redistributable. + + In addition to the distribution, the Project maintains two separate +archives of software packages with the distribution: the `contrib' +archive and the `non-free' archive. + + `contrib' is an archive of user-contributed packages that are not +maintained by the Project, packages that were once maintained by the +Project but that are no longer actively maintained, and packages that +are maintained by the Project but that are not yet considered ready for +inclusion in the distribution proper (i.e., ALPHA and BETA packages). +As above, all submissions for inclusion in the `contrib' archive *must* +be freely redistributable. + + `non-free' is an archive of packages with either restrictive or +unclear terms of copying or modification. If a package has *any* +restrictions on modification or redistribution, it can not be included +in the distribution or `contrib' archive. It can only be included in +the `non-free' archive, and then only if it is freely available. + + In summary, in order to be included in the distribution proper or the +`contrib' archive, a package must be *freely redistributable*. Anyone +must be able to make copies of it, modify it, redistribute it with +their modifications in place, include it on a CD-ROM, or generally sell +it. To be included in the `non-free' archive, a package may have +restrictions, as long as the package remains *freely available*. We +must be available to make it available freely at the archive, and anyone +must be able to make copies of it and use it for at least +non-commercial, personal purposes. Software that will typically be +included in `non-free' are software that does not allow commercial +distribution, software that does not allow modification or +redistribution of modifications, commercial "demos", and "shareware". + + When in doubt, send mail to `iwj10@cus.cam.ac.uk' and +`imurdock@debian.org'. Be prepared to provide us with the copyright +statement. Software covered by the GPL, public domain software and +BSD-like copyrights are safe; be wary of the phrases "commercial use +prohibited" and "distribution restricted". + + Every package submission *must* be accompanied by verbatim copy of +its copyright (with the exceptions of public domain packages and those +covered by the UCB BSD licence or the GNU GPL or LGPL; in these cases +simply indicate which is appropriate). This information must be +included in a file installed to the directory `/usr/doc/copyright'. +See below for details. + + +File: guidelines.info, Node: Package Content, Next: Source Package, Prev: Package Copyright, Up: Top + +Package Content +*************** + + The following requirements apply equally to both the binary and +source packages. In either case, when files have been installed, they +must conform to the requirements described in this section. + + The primary rule in Debian GNU/Linux is to follow the Linux "File +System Standard" ("FSSTND"). The location of installed files *must* +comply *fully* with the FSSTND. The latest version of this document +can be found alongside the Guidelines or at `tsx-11.mit.edu' in +`/pub/linux/docs/linux-standards/fsstnd'. Specific questions about +following the standard should be addressed to Daniel Quinlan, the +FSSTND coordinator, at `quinlan@yggdrasil.com'. + + In addition to the FSSTND, all Debian GNU/Linux packages must follow +the guidelines below. + + * Directories should be mode 755 or (for group-writability) mode + 2775, with the exception of special "system" directories that need + to be another mode. The ownership of the directory should be + consistent with its mode--if a directory is mode 2775, it should + be owned by the group that needs write access to it, of course. + Use common sense in assigning permissions and ownerships to + directories, and make sure that what is done is secure if it is + "non-standard". + + * Normal binaries should be mode 755 and owned by `root.root'. If + there is a good reason to use a different mode or ownership, you + may do so, but you must try to be as consistent as possible with + the rest of the system. If you need to use a different mode or + ownership, please discuss it with `imurdock@debian.org'. + + * Setuid binaries should normally be mode 4755 (not 4711!) and, of + course, owned by the appropriate user. + + * Setgid binaries should normally be mode 2755 (not 2711!) and, of + course, owned by the appropriate group. + + * Library files should generally be mode 644 and owned by + `root.root'. If the package requires different permissions or + ownerships to function correctly, they should be used instead. + + * Manual pages should be mode 644 and owned by `root.root'. The + `nroff' source must be installed. You should *not* install a + preformatted "cat page", and you should only use sections 1 to + 9--see the FSSTND for more details. If no manual page is + available for a particular program, utility or function and this + is reported as a bug on debian-bugs, a symbolic link from the + requested manual page to the `undocumented'(7) manual page should + be provided. This symbolic link can be created from `debian.rules' + like this: + + ln -s ../man7/undocumented.7 debian-tmp/usr/man/man[1-9]/the_requested_manpage.[1-9] + + Do not close the bug report until a proper manpage is available. + You may forward the complaint to the upstream maintainers, and + mark the bug as forwarded in the Debian bug tracking system. The + GNU Project do not in general consider the lack of a manpage to be + a bug, but we do - if they tell you to go away leave the bug open + anyway. + + * Info documents should be mode 644, owned by `root.root', and + compressed with `gzip -9' when installed. The package must call + `install-info' to update the Info `dir' file. This should be done + in the post-installation script (`postinst'), like this: + + install-info --quiet /usr/info/foobar.info + + The entries should be removed by the pre-removal script (`prerm'), + like this: + + install-info --quiet --remove /usr/info/foobar.info + + It is also a good idea to specify a section for the Info `dir' + entry. This is done with the `--section' switch. To determine + which section to use, you should use look at `/usr/info/dir' on + your system and choose the most relevant (or create a new section + if none of the current sections are relevant). + + If `install-info' cannot find a description entry in the Info file + you will have to supply one. See `install-info'(8) for details. + + * If a package contains any shared libraries you will have to invoke + `ldconfig' in both the `postinst' and `prerm' scripts to correctly + update the library links. See `ldconfig'(8) for details. + + * Any additional documentation that comes with the package can be + installed at the discretion of the package maintainer. Text + documentation should be mode 644, owned by `root.root', installed + to `/usr/doc', and compressed with `gzip -9' unless it is small. + + If a subdirectory of `/usr/doc' is warranted, please do create one. + Please do not install DVI, PostScript, or large textual + documentation in the same package; upload such documentation as a + separate package (installing its files in `/usr/doc') so that it + can be made available with the distribution. If a user has the + need for the documentation, they can easily get it from the + archive, CD-ROM, etc., but it should not take up disk space on the + machines of the user who do not need or want it installed. + + * Create a file named `/usr/doc/copyright/' which gives + details of the authorship and copyright of the package. If the + package is distributed under the GNU General Public Licence, the + GNU Library General Public Licence or the Regents of the + University of California at Berkeley (BSD) licence, please say so + instead of including a copy of the licence. The files `BSD', + `GPL', and `LGPL' will be available in the `/usr/doc/copyright' + directory for you to refer to. `/usr/doc/copyright/' + should not be compressed. + + *All* authorship and copyright information from the original source + package must be included in the `/usr/doc/copyright/' + file. + + * Any example files (for example, sample configuration files) should + be placed in the directory `/usr/doc/examples'. If the file is + normally a hidden file, such as `.emacs', then please call it + `dot.emacs', to avoid confusion. Again, you may create a + subdirectory if it is needed. + + * All symbolic links should be relative, not absolute. Absolute + links, in general, cause problems when a file system is not + mounted where it "normally" resides (for example, when mounted via + NFS). In certain cases, however, relative links may also cause + similar problems. I have generally made links into `/etc' and + `/var' absolute and all other links relative. There may be other + cases in which absolute links are necessary. + + Therefore, in the `Makefile' or `debian.rules', do not do: + install: all + [...] + ln -fs /usr/bin/gcc /usr/bin/cc + [...] + Instead, do: + ln -fs gcc /usr/bin/cc + or + ( cd /usr/bin ; ln -fs gcc cc ) + + Please do not create hard links in the manual page directories. In + these cases, you should use relative symbolic links or files that + `.so' (roff for `source') others instead. + + * All command scripts should have a `#!' line naming the shell to be + used to interpret them. + + * In the case of Perl scripts this should be `#!/usr/bin/perl' or + sometimes `#!/bin/perl', as follows: if the script is a critical + one that may be called when the `/usr' partition is unmounted or + broken it should use `/bin/perl'. Otherwise (especially if the + script is not specifically targetted at Debian) it should use + Perl's standard location, `/usr/bin/perl'. + + * Generally the following compilation parameters should be used: + + CC = gcc + CFLAGS = -O2 -g -Wall # sane warning options vary between programs + LDFLAGS = # none (or -N, if appropriate; see below) + install -s (or strip) + + Note that all installed binaries should be stripped, either by + using the `-s' flag to `install', or by calling `strip' on the + binaries after they have been copied into the `debian-tmp' but + before the tree is made into a package. + + Make sure that you do not link with `-g', as this makes a.out + compilers produce huge statically linked binaries. The `-g' flag + is useful on compilation so that you have available a full set of + debugging symbols in your built source tree, in case anyone should + file a bug report involving (for example) a core dump. + + `-N' should only be used on binaries that are very small (less than + 8K with the `-N' option, roughly) and are not likely to have + multiple instances in memory. Do not use `-N' on daemons, no + matter how small they are. + + It is up to the package maintainer to decide what compilation + options are best for the package. Certain binaries (such as + computationally-intensive programs) may function better with + certain flags (`-O3', for example); feel free to use them. Please + use good judgment here. Don't add flags for the sake of adding + flags; only add flags if there is good reason to do so. + + * Please make sure that you use only released versions of shared + libraries to build your packages; otherwise other users will not + be able to run your binaries properly. Producing source packages + that depend on unreleased compilers is also usually a bad idea. + + * Logfiles should usually be named `/var/log/', or + `/var/log/.' if you have several logfiles. It + may be appropriate to create a directory. Make sure that any + logfiles are rotated occasionally so that they don't grow + indefinitely; the best way to do this is to use `savelog' from the + cron package in an `/etc/cron.daily', `/etc/cron.weekly' or + `/etc/cron.monthly' script. + + * Please check with the base system maintainer (Ian Murdock) before + using users or groups other than `root' and others specified in + this document. + + +File: guidelines.info, Node: Source Package, Next: Binary Package, Prev: Package Content, Up: Top + +Source Package +************** + + The source package should contain a file called `debian.rules' which +contains at least the following targets, to be invoked in the top level +directory: + + build + binary + clean + + `debian.rules' should start with + + #!/usr/bin/make -f + +and be executable. It is a good idea to arrange for it not to fail +obscurely when invoked in the wrong directory, for example by testing +for the existence of a file in the source directory. + + * The `build' target should perform all non-interactive configuration + and compilation of the package. If a package has an interactive + pre-build configuration routine, the source package should be built + *after* this has taken place. + + For some packages, notably ones where the same source tree is + compiled in different ways to produce two binary packages, the + `build' target does not make much sense. For these packages it is + good enough to provide two (or more) targets (`build-a' and + `build-b' or whatever) for each of the ways of building the + package, and a `build' target that does nothing. The `binary' + target will have to build the package in each of the possible ways + and make the binary package out of each. + + * The `binary' target of `debian.rules' should be all that is + necessary for the user to build the binary package. The binary + package should be created using `dpkg' and placed in the parent of + the top level directory. The next section describes how to + construct binary packages from the `binary' target. + + * The `clean' target should undo the effects of the `build' target + and the `binary' target, except that it should leave alone any + `../-.deb' file created by a run of `binary'. + + * Additional targets may exist in `debian.rules'. We recommend using + `source' and `diff' targets to build the Debianised source package + and the Debianisation context diff, respectively. These files + should be placed in `../foo-.tar.gz' and + `../foo-.diff.gz'. The `install' target, for installing + into a running system direct from the Debianised source tree, is + no longer required. The sample `debian.rules' provides `source' + and `diff' targets that should work with little or no alteration, + providing that the package-specific variables at the top of the + script have been properly defined. + + * If you need to edit a `Makefile' where `configure' scripts are + used, you should edit the `.in' files rather than editing the + `Makefile' directly. This allows the user to reconfigure the + package if necessary. You should *not* configure the package and + edit the generated `Makefile'! This makes it impossible for + someone else to later reconfigure the package. + + * Please document your changes to the source package so that future + package maintainers know what has been changed. To do this, + include a description of your changes in the `debian.README' + (which, as described above, should already contain authorship and + copyright information!) and include relevant information such as + your name, electronic mail address, date, etc. The + `debian.README' file should also document any `unusual' packages + which must be installed for this one to compile. + + * If changes to the source code are made that are applicable to Linux + systems or systems in general please try to get them included in + the upstream version of the package by supplying the upstream + authors with the changes in whatever form they prefer. + + If changes to the source code are made, please use a `define'. If + they are changes required to compile or function under Linux in + general, use `LINUX'. If it is a cosmetic or functional change, + use `DEBIAN'. + + * Create the source package using `tar', and use `gzip -9' to + compress it. Source packages should be named in the form + -.tar.gz--for example, `fileutils-3.9-3.tar.gz'. + + NB, here `' is the full Debian version number, in the + form `-' (see below), but the + tarfile should unpack into a directory named + `-' (again, see the section below on + version numbering). + + * Create the context diff against the original package using `diff + -cNr', and use `gzip -9' to compress it. Context diffs should be + named in the form -.diff.gz--for example, + `fileutils-3.9-3.diff.gz'. + + Please note that the package and patch filenames do *not* need to +fit in MS-DOS 8+3. They will be made available under an alternative +8+3 name in the archive by the archive maintainer, using a symlink. + + +File: guidelines.info, Node: Binary Package, Next: Control Files, Prev: Source Package, Up: Top + +Binary Package +************** + + The `binary' target of the source package `debian.rules' file should +do the following (see the sample `debian.rules' for an implementation +that you are free to modify and use in your own packages, of course): + + * Create an empty directory in the top-level directory of the source + package (deleting it first, if necessary), and install the files + belonging to this package in that directory. For example, the + directory could be called `debian-tmp' and would probably contain + directories `debian-tmp/usr/bin', `debian-tmp/usr/lib', etc. + (`debian-tmp' is the name traditionally used, and it is used in + the sample `debian.rules' file, so we will use that name in the + Guidelines.) + + * Make sure that all the files under `debian-tmp' have the correct + ownerships and permissions (*note Package Content::., for more + information about file locations, ownerships, and permissions.) + + * Create a subdirectory of `debian-tmp' called `DEBIAN'. This + directory contains the package control information, including at + the very least the master information file named `control'. The + next section describes the semantics and syntax of the files + required and allowed here. + + * Run `dpkg' to create the binary package, using something like + + dpkg --build debian-tmp + + This will create a file called `debian-tmp.deb', from the + `debian-tmp' directory. You should rename this file to + `../-.deb' after it is built. + + After the `binary' target has done all this, the + `-.deb' file in the parent directory is the + binary distribution. This file may be distributed and installed on + any Debian GNU/Linux system with `dpkg' in the same manner and + using the same methods as all packages are installed to the system. + + * If a single source package corresponds to several binary packages, + there should usually be a `debian.rules' file with a single + `binary' target that builds all the binary packages involved and + move all packages to the parent directory of that containing the + source package. + + In this case, you should choose binary package names which are + meant to make clear the close relationship between the binary + packages and which source package the binary packages came from + (for example, the `texinfo' source package build two binary + packages: `texidoc' and `texinfo'). You should place the + appropriate binary package name in the `Package' field of the + control file (not the source package name), and you should + consider whether the other binary packages that come from the same + source tree should be mentioned in the `Depends', `Recommends' or + `Suggests' fields. You should put the source package name in the + `Source' field. + + You should retain the source package version numbering in the + `Version' field, if possible--the version number should be the + same for the Debianised source tree and all the binary packages + generated from it. It is more important, though, that the version + numbers sort correctly. See below for details of version numbers. + + +File: guidelines.info, Node: Control Files, Next: Appendix, Prev: Binary Package, Up: Top + +Control Files +************* + + Each binary package contains, in addition to the files that comprise +the actual package, a set of text files that control how `dpkg' +installs, configures, upgrades, removes, etc. the package. These files +are called "control files". When creating the package, the control +files should placed in a directory called `DEBIAN', as described +earlier (*note Binary Package::., for further information). + + The control information files are: + +`control' + The master package control information file. + +`conffiles' + A list of package configuration files. + +`preinst' + The package pre-installation script. + +`postinst' + The package post-installation script. + +`prerm' + The package pre-removal script. + +`postrm' + The package post-removal script. + + Of these, only `control' is required. The various installation +scripts, and the configuration files list, will only be used if they are +present. + +* Menu: + +* control:: +* conffiles:: +* Installation and Removal Scripts:: +* Dependencies and Conflicts:: +* Package Classification Fields:: + + +File: guidelines.info, Node: control, Next: conffiles, Prev: Control Files, Up: Control Files + +control +======= + + The `control' file contains a number of fields. Each field begins +with a field name, such as `Package' or `Version' (case insensitive), +followed by a colon and optionally some spaces or tabs (a single space +is conventional). Then comes the body of the field, which may be +several lines long; each continuation line must start with at least one +space or tab. (These are the same rules as apply to RFC822 mail +headers.) Blank lines are not permitted in the control file. + + The required fields in the control file are the following: + +`Package' + The name of the package. + +`Description' + The description of the package. How to write an extended and more + usefull description field can be found in *note How to write the + Description control file field::.. + +`Maintainer' + The name and e-mail address of the maintainer of the package. + +`Version' + The version number in the format + `-'. + + Each field has a particular format and meaning for the package +installation tools. + + The value of `Package' should be the name of the package. Package +names must start with an alphanumeric, must be at least two characters, +and may contain only alphanumerics and the characters - + . @ : = % _ +(that is, hyphen, plus, stop, at, colon, equals, percent and +underscore). They are not case sensitive. + + The `Maintainer' field should be in the form + + Joe J. Bloggs + +Note that this will not be useable as an email address if the name +given contains full stop characters, because of a silly restriction in +the Internet mail standards. If you want to use this as an email +address in a program you should check for full stops and change the +string to the form `jbloggs@foo.com (Joe J. Bloggs)' if you find any. + + The `Version' field should be the version number of the package. +For most packages which are not written specifically for Debian, this +should be in the form + + Version: - + +where `' is the original package version number in +whatever form the original package uses and `' +indicates which "debianisation" this is (this should usually be a plain +number or perhaps a two numbers separated by a full stop, and should be +incremented each time the package is changed or updated). + + Packages which are written specifically for Debian do not have a +debian_revision, and their version number should simply be version +(which should not contain any hyphens, to avoid confusion). + + There is an ordering imposed on version numbers, described in +`version-ordering.txt'. This ordering is designed to `do the right +thing' in most circumstances; if your package has an version number in +an unusual format you may need to reformat it somewhat to get the +ordering right. This is important because `dpkg' is (for example) +reluctant to downgrade packages. + + The optional fields in the control file are the following: + +`Depends' + The names of prerequisite packages. + +`Recommends' + The names of related, recommended packages. + +`Suggests' + The names of related, optional packages. + +`Conflicts' + The names of packages which conflict with this package. + +`Provides' + The names of virtual packages which this package provides. + +`Priority' + The `priority' of the package, as shown and used by `dselect'. + +`Section' + The `section' of the package, as shown and used by `dselect', and + used as a location for the package in the distribution. + +`Essential' + A boolean field used by the base packages. + +`Pre-Depends' + Used by base packages to ensure that (for example) shared + libraries are present befoore they are upgraded. + +`Source' + Gives the name of the source package when several binary packages + are generated from a single source tree. + +See below for details of the semantics and syntax of these fields. +Most packages will need at least a `Depends' field. + + An example of a `control' file would be: + + Package: smail + Version: 3.1.29.1-13 + Maintainer: Ian Jackson + Recommends: pine | mailx | elm | emacs | mail-user-agent + Suggests: metamail + Depends: cron, libc5 + Conflicts: sendmail + Provides: mail-transport-agent + Description: Electronic mail transport system. + Smail is the recommended mail transport agent (MTA) for Debian. + . + An MTA is the innards of the mail system - it takes messages from + user-friendly mailer programs and arranges for them to be delivered + locally or passed on to other systems as required. + . + In order to make use of it you must have one or more user level + mailreader programs such as elm, pine, mailx or Emacs (which has Rmail + and VM as mailreaders) installed. If you wish to send messages other + than just to other users of your system you must also have appropriate + networking support, in the form of IP or UUCP. + + In this case, `mail-user-agent' is a virtual package representing +any user mailer program; the actual package names `pine' is quoted for +the reasons described in `dependency-ordering.txt', and the others +because older versions of those packages do not have the appropriate +`Provides' field. + + +File: guidelines.info, Node: conffiles, Next: Installation and Removal Scripts, Prev: control, Up: Control Files + +conffiles +========= + + The contents of `conffiles' is simply a list of configuration files +in the package. When installing the package, `dpkg' uses an +intelligent method to update these files. This will ensure that +package-specific configuration files are not overwritten when a package +is upgraded, unless the user wishes the installation tools to do so. + + Typically, files listed in conffiles are package-specific +configuration files, which (according to the Linux Filesystem Standard) +are stored in `/etc'. For example, the `sendmail' package may contain +the file `/etc/sendmail.cf', which we do not wish to overwrite +automatically when the user upgrades the sendmail package. Only those +files listed in `DEBIAN/conffiles' will be updated intelligently when a +package is upgraded; all other files in the package will be overwritten +by the upgrade process. + + Configuration files which will be functional as shipped and will +probably need little or no local editing should simply be listed the +`conffiles' file; in this case you need read no further. + + For packages whose configuration files will need modification on +most systems there are two sensible approaches. Which one is chosen +depends on how hard the configuration problem is and how much time the +package maintainer has available. + + One option is for you to ship a minimal `best-effort' file in +`/etc', and list the file in `conffiles'. This will mean that the user +will have to go and edit the file themselves to get the package to work +properly, of course. The next time they upgrade the package, if you +haven't changed the file version, their old file will be left in place. +If you have modified your version then the user will get a prompt +asking them which version of the file they want, theirs or yours. They +will then usually have to resolve the discrepancies manually. + + The other option is to be preferred, if you can do it: do not put a +copy of the configuration file in the package at all. Instead, you +check in the postinst whether the file exists, and if it doesn't you +prompt the user for the information you need to create a good one. This +is obviously harder work. + + You also have to remember that you will have to keep up with your +package's changes: if you discover a bug in the program which generates +the configuration file, or if the format of the file changes from one +version to the next, you will have to arrange for the postinst script to +do something sensible--usually this will mean editing the installed +configuration file to remove the problem or change the syntax. You will +have to do this very carefully, since the user may have changed the +file, perhaps to fix the very problem that your script is trying to deal +with--you will have to detect these situations and deal with them +correctly. + + If you do go down this route it's probably a good idea to make the +program that generates the configuration file(s) a separate program in +`/usr/sbin', by convention called package`config', and then run that if +appropriate from the post-installation script. The package`config' +program should not unquestioningly overwrite an existing +configuration--if its mode of operation is geared towards setting up a +package for the first time (rather than any arbitrary reconfiguration +later) you should have it check whether the configuration already +exists, and require a `--force' flag to overwrite it. + + `conffiles' should almost certainly list all the files contained in +your package in the `/etc' directory. There may also be other files +somewhere that the user is expected to edit, which should also be +included. Note, however, that the FSSTND specifies that configuration +files must be in `/etc'. No Debian package should contain +configuration files in `/usr/etc', and all programs should refer to +configuration files in `/etc'. + +For example, the TCP/IP package might use a conffiles which contains + + /etc/init.d/netbase + /etc/gateways + /etc/protocols + /etc/services + /etc/hosts.allow + /etc/hosts.deny + /etc/rpc + +and so on; the files + + /etc/hosts + /etc/inetd.conf + /etc/host.conf + /etc/networks + /etc/resolv.conf + +might be generated by an interactive configuration program, and would +then not be included in the package or listed in the `conffiles'. + + +File: guidelines.info, Node: Installation and Removal Scripts, Next: Dependencies and Conflicts, Prev: conffiles, Up: Control Files + +Installation and Removal Scripts +================================ + + The scripts `preinst', `postinst', `prerm', and `postrm' are +optional (Bash or Perl) scripts. As the names would indicate, if these +scripts exist, they will be executed before installing the package, +after installation, before package removal, and after removal, +respectively. + + They are given arguments which indicate the precise situation and +action being performed--see `maintainer-script-args.txt' for details of +exactly when each of the scripts is invoked and what its arguments are. +Extra arguments and situations may be added later, so you should not +test the number of arguments to your script to determine the situation, +and you should choose the sense of your `if it is this then do this +otherwise do that' tests carefully. + + These scripts can be used to perform any site-specific package +configuration. + + Because the scripts will be exectued by the dpkg front-end, it is +guaranteed that the scripts will be executed interactively. User input +from the scripts should be read from standard input, not the user's +terminal. Similarly, output should be sent to standard output. + + If your maintainer scripts need to prompt for passwords and/or do +full-screen interaction should do these things to and from `/dev/tty', +since `dpkg' will at some point redirect scripts' standard input and +output so that it can log the installation process. Likewise, because +these scripts may be executed with standard output redirected into a +pipe for logging purposes, Perl scripts should set unbuffered output by +setting `$|=1' so that the output is printed immediately rather than +being buffered. + + The scripts must be idempotent, and they must clean up after +themselves properly. Ie, they must do the right thing if run multiple +times, even if previous runs failed halfway through. This is so that if +any errors occur, or if the `dpkg' run is interrupted, the user can +recover by rerunning `dpkg', and/or by upgrading to a new version and +then rerunning the failed operation. + + These scripts should avoid producing output which it is unnecessary +for the user to see and should rely on `dpkg' to stave off boredom on +the part of a user installing many packages. This means, amongst other +things, using the `--quiet' option on `install-info'. + + Packages should try to minimise the amount of prompting they need to +do, and they should ensure that the user will only every be asked each +question once. This means that packages should try to use appropriate +shared configuration files (such as `/etc/papersize' and +`/etc/news/server'), rather than each prompting for their own list of +required pieces of information. + + It also means that an upgrade should not ask the same questions +again, unless the user has used `dpkg --purge' to remove the package's +configuration. The answers to configuration questions should be stored +in an appropriate place in `/etc' so that the user can modify them, and +how this has been done should be documented. + + If a package has a vitally important piece of information to pass to +the user (such as "don't run me as I am, you must edit the following +configuration files first or you risk your system emitting +badly-formatted messages"), it should display this in the `postinst' +script and prompt the user to hit Return to acknowledge the message. +Copyright messages do not count as vitally important (they belong in +`/usr/doc/copyright'; neither do instructions on how to use a program +(these should be in on line documentation, where all the users can see +them). + + They should return a zero exit status for success, or a nonzero one +for failure. Note that if a script is a `#!/bin/sh' script it should +probably start with `set -e', to avoid continuing after errors--see +`bash'(1) for details. Perl scripts should check for errors when +making calls such as `open', `print', `close', `rename' and `system'. + + If these scripts exist they should be left in the `DEBIAN' directory +with execute permission enabled and should contain an appropriate `#!' +line, such as `#!/bin/bash' for a `bash' script or `#!/bin/perl' for a +Perl script (see above). + + +File: guidelines.info, Node: Dependencies and Conflicts, Next: Package Classification Fields, Prev: Installation and Removal Scripts, Up: Control Files + +Conflicts, Depends, Suggests, Recommends and Provides +===================================================== + + The `Depends' field lists packages that are required for this +package to provide a significant amount of functionality. The package +maintenance software will not allow a package to be installed without +also installing packages listed in its `Depends' field, and will run +the `postinst' scripts of packages listed in `Depends' fields before +those of the packages which depend on them, and run the `prerm' scripts +before. + + Packages containing dynamically-linked executable binaries (this +includes almost all C programs) should include a `Depends' field which +mentions the shared C library required for the program to run. For +a.out binaries linked against `libc.so.4' the relevant package name is +`libc' (for the a.out stable 0.93 tree) or `libc4' (for the unstable +development 1.1 tree); for ELF binaries linked against `libc.so.5' the +relevant package name is `libc5'. + + The `Recommends' field lists packages that would be found together +with this one in all but unusual installations. The user-level package +maintenance program `dselect' will warn the user if they select a +package without those listed in its `Recommends' field. Note that +`Recommends' fields do not currently have any implications for the +order in which the maintainer scripts are run. + + The `Suggests' field lists packages that are related to this one and +can perhaps enhance its usefulness, but without which installing this +package is perfectly reasonable. The package maintenance software will +not moan at the user for not selecting `Suggests' related packages, but +may use the information in the `Suggests' field to assist the user +during package selection. + + The syntax of `Depends', `Recommends' and `Suggests' is a list of +groups of alternative packages. Each group is a list of packages +separated by vertical bar (or `pipe') symbols, `|'. The groups are +separated by commas. Each package is a package name optionally +followed by a version number specification in parentheses. A version +number may start with a `>=', in which case that version or any later +will match, or `<=' for that version or any earlier version. A version +number starting with a `>>' or `<<' will respectively match any later +or earlier version. If a version number or a version number starting +with `=' is specified an exact match is required. Commas are to be read +as `AND', and pipes as `OR', with pipes binding more tightly. + + Versions of dpkg before 1.0.9 used `<' and `>' for `<=' and `>=' +(these are still supported for backward compatibility), and did not +support `<<' and `>>'. + + The `Conflicts' field lists packages that conflict with this one, +for example by containing files with the same names (an example would +be Smail vs. Sendmail). The package maintenance software will not +allow conflicting packages to be installed. Two conflicting packages +should each include a `Conflicts' line mentioning the other. + + The syntax of `Conflicts' is a list of package names (with optional +version numbers), separated by commas (and optional whitespace). In +the `Conflicts' field the comma should be read as `OR'. + + The `Provides' field lists the names of any `virtual packages' of +which this packages is to be considered an instantiation. Virtual +packages are used to allow packages to refer to a service they require +(such as the availability of `/usr/sbin/sendmail') without having to +know the names of all the relevant packages. The virtual package names +defined in `Provides' fields may be used in other packages' `Depends', +`Recommends', `Suggests' and `Conflicts' fields. For more information +about how to use virtual packages and which virtual package names to +use read *note Virtual dependencies::. and +`doc/package-developer/virtual-package-names-list.text'. + + The syntax of `Provides' is a list of package names separated by +commas (and optional whitespace). + + +File: guidelines.info, Node: Package Classification Fields, Prev: Dependencies and Conflicts, Up: Control Files + +Priority, Section and Essential +=============================== + + The `Priority' and `Section' fields are used by `dselect' when +displaying the list of packages to the user. There is no need to put +them into a package, since these are usually set by the distribution +maintainers in the `Packages' file. + + However, if a user installs a package which is not part of the +standard distribution, or without downloading and updating from a new +`Packages' file, the information about the priority and section of a +package will be absent, and the `dselect' package listing will have the +package listed under `unclassified'. It is permissible for a package +to include `Section' or `Priority' fields to improve this; however, if +you do this you should make sure you keep the information up to date so +that users are not shown conflicting information. The `Section' field +can also be used by the distribution maintainers as a suggestion about +which section you think is most appropriate for your package. + + The values for the `Section' and `Priority' fields should be +determined by the distribution maintainers; if you don't know what to +put in them just leave them out. You can add them later, if you like, +but remember that you'll then have to reissue your package if the +distribution maintainers change the classification of your package. + + The `Essential' field should only appear in packages in the +installation's base system. If it is set to `yes' then `dpkg' will not +remove the package even if asked to, and will make certain minor +modifications to its installation procedures. The only other legal +value is `no', which is equivalent to the absence of the field. + + + + +File: guidelines.info, Node: Appendix, Prev: Control Files, Up: Top + +Appendix +******** + +* Menu: + +* configuration files - /etc/skel vs /usr/doc/examples:: +* How to write the Description control file field:: +* Configuration of init:: +* Maintainer script arguments and how `dpkg' does things:: +* Mail processing packages:: +* Virtual dependencies:: + diff --git a/doc/guidelines.info-2 b/doc/guidelines.info-2 new file mode 100644 index 00000000..8018f09f --- /dev/null +++ b/doc/guidelines.info-2 @@ -0,0 +1,744 @@ +This is Info file guidelines.info, produced by Makeinfo-1.63 from the +input file ./guidelines.texi. + +START-INFO-DIR-ENTRY +* Guidelines: (guidelines). How to make Debian packages. +END-INFO-DIR-ENTRY + + +File: guidelines.info, Node: configuration files - /etc/skel vs /usr/doc/examples, Next: How to write the Description control file field, Prev: Appendix, Up: Appendix + +configuration files - /etc/skel vs /usr/doc/examples +==================================================== + + There seems to be a certain amount of confusion about `/etc/skel' +and `/usr/doc/examples'. The most important thing to remember is the +following: + + Files in `/etc/skel' will *automatically* be copied into *new* user +accounts by `adduser'. They should not be referenced there by any +program. Files in `/usr/doc/examples' should not be installed +automatically. + + Therefore, if the program in question need a dotfile to exist in +advance in `$HOME' to work *sensibly* that dotfile should be installed +in `/etc/skel' (and listed in conffiles; *note conffiles::.). + + However, programs that require dotfiles in order to operate sensibly +(dotfiles that they do not create themselves automatically, that is) are +a bad thing, and that programs should be configured by the Debian +default installation as close to normal as possible. + + Therefore, if a program in a Debian package needs to be configured in +some way in order to operate sensibly that configuration should be done +in a site-wide global configuration file elsewhere in `/etc' (and that +file should be listed in conffiles). Only if the program doesn't +support a site-wide default configuration should a default per-user file +be placed in `/etc/skel' (and listed in conffiles; *note conffiles::.). + + The idea is as follows: + + The sysadmin should ideally not have to do any configuration other +than that done (semi-)automatically by the postinst script. + + However, if they wish to change their configuration themselves +(because the configuration they want is beyond the scope of the +autoconfiguration, or because the autoconfiguration doesn't exist yet, +or because they just want to do it themselves for any reason) then +`/usr/doc/examples' exists as *documentation* for their benefit. + + The only time these files should be read are by the sysadmin using +their favourite editor or pager, or *perhaps* (in very complex packages) +by the postinst as a template to build on or modify. + + `/etc/skel' is part of the *implementation* of this configuration. +It contains the files that are copied into new user accounts. It +should probably be as empty as we can make it. + + Examples: +`.profile' + `/etc/skel' should not contain a `.profile' file. Anything that + needs to be done there should be done in `/etc/profile'. Anything + that should not go in `/etc/profile' (users can't avoid running + `/etc/profile') probably should not be in the default + configuration. bash has generally good default behaviour. + +`.bash_logout' + Likewise, bash functions perfectly happily without a + `.bash_logout', so none should be provided, since anything in it is + a deviation from the sensible default behaviour. + +`.xsession' + `/etc/skel' should not contain a `.xsession'. `xdm''s system-wide + startup file `/usr/lib/X11/xdm/Xsession' supports a system-wide + default user configuration (which should probably be + `/etc/X11/Xsession' or some such) which may be overridden by + `.xsession' in the user's home directory. Therefore there is no + need for a `.xsession' to be installed by default and none should + be provided. + + Instead, a sensible `/etc/X11/Xsession' should be provided, and if + desired this can be used as a template by users who wish to install + their own configuration, or alternatively a more comprehensive + example with much commented-out interesting stuff could be put in + `/usr/doc/examples'. + + If the sysadmin wishes to change the system-wide default they + should probably do this by editing `/etc/X11/Xsession' rather than + creating the file in `/etc/skel', because the former will affect + all user accounts that haven't explicitly overridden things by + creating their own file while the latter will only affect new + accounts. + + All the configuration necessary for a program to function should be + provided. Therefore sysadmins will not need to go through + `/usr/doc/examples' while editing configuration files in `/etc' + except in extreme cases (like INN) where the configuration was too + difficult to do automatically. + +`site-wide defaults' + Site-wide defaults should not go in `/etc/skel'. In the case of + twm, for example, the system-wide default should be in + `/etc/X11/system.twmrc'. (The default location for this in X11R5, + btw, is in `/usr/lib/X11' somewhere, but we can't put it on `/usr' + because of CDROM distributions, etc - hence the FSSTND's mandate + to put configuration files in `/etc'.) + +`.twmrc' + There should be no `.twmrc' file in `/etc/skel'. You can have one + in `/usr/doc/examples' if you *like*, but why bother if + `system.twmrc' is a good example (and indeed is the one the user is + using before they create their own)? + +`m4' + `/usr/doc/examples' isn't mainly for example *configuration + files*. It's for any kind of example file distributed with a + package. For example, GNU m4 comes with a whole pile of example + m4 macro scripts, which is exactly what `/usr/doc/examples' is for. + + Summary + + Files that should be installed in new user accounts should be in +`/etc/skel', as that will ensure that they *are* installed in new user +accounts! However, we should try to avoid the need for this. + + `/usr/doc/examples' is just what it says: documentation in the form +of examples. If a sysadmin is required to go and read these files for +their system to work they should be told about it. For example, here +is what the Smail postinst script says right at the start: + + I can do certain kinds of automatic configuration of your + mail system, by asking you a number of questions. Later you + may to confirm and/or correct your answers. In any case, + comprehensive information on configuring Smail is in + smail(5) and in /usr/doc/examples/smail and + /usr/doc/smail-admin-guide. + + +File: guidelines.info, Node: How to write the Description control file field, Next: Configuration of init, Prev: configuration files - /etc/skel vs /usr/doc/examples, Up: Appendix + +How to write the Description control file field +=============================================== + + The format of the `Description' field is as follows: + + Description: + + + The extended description has several kinds of line: + + * Those starting with a single space are part of a paragraph. + Successive lines of this form will be word-wrapped when displayed. + The leading space will usually be stripped off. + + * Those starting with two or more spaces. These will be displayed + verbatim. If the display cannot be panned horizontally the + displaying program will linewrap them `hard' (ie, without taking + account of word breaks). If it can they will be allowed to trail + off to the right. None, one or two initial spaces may be deleted, + but the number of spaces deleted from each line will be the same + (so that you can have indenting work correctly, for example). + + * Those containing a single space followed by a single full stop + character. These are rendered as blank lines. This is the *only* + way to get a blank line - see below. + + * Those containing a space, a full stop and some more characters. + These are for future expansion. *Do not* use them. + + IMPORTANT and not so important TIPS: + + * *Always* start extended description lines with at least *one* + whitespace character. Fields in the control file and in the + Packages file are separated by field names starting in the first + column, just as in RFC822. Forgetting the whitespace will cause + `dpkg-deb' (>=0.93.23) to produce a syntax error when trying to + build the package. If you force it to build anyway `dpkg' will + refuse to install the resulting mess. + + * *Do not* include any completely *empty* lines. These separate + different records in the Packages file, and are forbidden in + control files. See the previous paragraph for what happens if you + get this wrong. + + * The single line synopsis should be kept brief - certainly under 80 + characters. `dselect' displays the *first 49* characters if + you're using an 80-column terminal. + + * Do not include the package name in the synopsis line. The display + software knows how to display this already, and you do not need to + state it. Remember that in many situations the user may only see + the synopsis line - make it as informative as you can. + + * The extended description should describe what the package does and + how it relates to the rest of the system (in terms of, for + example, which subsystem it is which part of). + + * Put important information first, both in the synopis and extended + description. Sometimes only the first part of the synopsis or of + the description will be displayed. You can assume that there will + usually be a way to see the whole extended description. + + * You may include information about dependencies and so forth in the + extended description, if you wish. + + * Do not use tab characters. Their effect is not predictable. + + Example control file for Smail: + + Package: smail + Version: 3.1.29.1-13 + Maintainer: Ian Jackson + Recommends: pine | mailx | elm | emacs | mail-user-agent + Suggests: metamail + Depends: cron, libc5 + Conflicts: sendmail + Provides: mail-transport-agent + Description: Electronic mail transport system. + Smail is the recommended mail transport agent (MTA) for Debian. + . + An MTA is the innards of the mail system - it takes messages from + user-friendly mailer programs and arranges for them to be delivered + locally or passed on to other systems as required. + . + In order to make use of it you must have one or more user level + mailreader programs such as elm, pine, mailx or Emacs (which has Rmail + and VM as mailreaders) installed. If you wish to send messages other + than just to other users of your system you must also have appropriate + networking support, in the form of IP or UUCP. + + +File: guidelines.info, Node: Configuration of init, Next: Maintainer script arguments and how `dpkg' does things, Prev: How to write the Description control file field, Up: Appendix + +Configuration of init +===================== + + The `/etc/init.d' directory contains the scripts executed by init(8) +when init state (or "runlevel") is changed. This includes the boot +process, when the multi-user state begins. Several of these scripts +are included with init and are intended to be executed *once*, usually +at boot time. An example is `/etc/init.d/boot', which is executed at +boot time to check and mount file systems, activate swap, load kernel +modules, etc.-everything that needs to be done before the multi-user +state begins. `/etc/init.d' also contains the scripts that are +executed when entering runlevel 0 (halt), runlevel 1 (single-user) and +runlevel 6 (reboot). + + Packages can (and should) place scripts in `/etc/init.d' to start or +stop services at boot time or during a change of runlevel. These +scripts should be named `/etc/init.d/', and they should accept +one of two arguments: "start", which starts the services, or "stop", +which stops the services. These scripts should ensure that they will +behave sensibly if invoked with "start" when the service is already +running, or with "stop2 when it isn't--the best way to achieve this is +often to use `start-stop-daemon'. + + This script should not fail obscurely when the configuration files +remain but the package has been removed, as the default in dpkg is to +leave configuration files on the system after the package has been +removed. Only when it is executed with the `-purge' option will dpkg +remove configuration files. Therefore, you should include a `test' +statement at the top of the script, like this: + + test -f || exit 0 + + These scripts should be referenced, when appropriate, by symbolic +links in the `/etc/rc?.d' directories, as below. + + When changing runlevels, init looks in the directory `/etc/rc.d' +for the scripts it should execute, where is the runlevel that is +being changed to. Please note that the "scripts" in `/etc/rc?.d' are +not actually scripts; they are symbolic links, referencing actual +scripts in `/etc/init.d'. For simplicity, we refer to them as +"scripts". + + First, the scripts prefixed with a "K" are executed, followed by the +scripts prefixed with an "S". The "K" scripts are responsible for +killing certain services and the "S" scripts for starting certain +services upon *entering* the runlevel. For example, if we are changing +from runlevel 2 to runlevel 3, init will first execute all of the "K" +prefixed scripts it finds in `/etc/rc3.d' (to kill services), and then +all of the "S" prefixed scripts it finds in `/etc/rc3.d' (to start +services). The "K" scripts will execute the file it references with an +argument of "stop", and the "S" scripts will execute this file with an +argument of "start". + + After the "K" or "S" prefix, there should be a number specified, and +this number should be between 00 and 99. The number determines the +order in which the scripts are run. For example, the "K20" scripts will +be executed before the "K30" scripts. You can use this number to make +sure that a certain service is started before another. For example, on +some machines, the program `setserial' may need to properly set an IRQ +before the `ppp' program uses a modem to connect to a network. In this +case, the script that runs `setserial' should have a lower number than +the script that starts `ppp' so that it runs first: + + `/etc/rc2.d/S10setserial' + `/etc/rc2.d/S20ppp' + + If it does not matter when or in which order the script is run, use +the number "20". If it does, then you should talk to the maintainer of +the `sysvinit' package or post to `debian-devel', and they will help +you choose a number. + + In Debian GNU/Linux, we try to ship our software in as much of a +"default" state as possible. Therefore, unless there is a good reason +for doing differently, we ask that you start and stop the services in +each of the multi-user state runlevels (2, 3, 4, and 5). If a service +needs to be stopped before a file system can be unmounted (an example is +process accounting or quota services), then be sure to stop them in the +halt runlevel (0), the single-user runlevel (1) and the reboot runlevel +(6). + + The system administrator will have the opportunity to customize +runlevels by simply adding, moving, or removing the symbolic links in +`/etc/rc?.d'. This is why we default to running everything in the +multi-user state-a reasonable default-and the administrator can easily +customize init to be as complex and sophisticated as he or she wants it +to be beyond this. + + We provide a script, `update-rc.d', to make it easier for package +maintainers to arrange for the proper creation and removal of +`/etc/rc?.d' symbolic links from their postinst and postrm scripts. +You should use this script to make changes to `/etc/rc?.d' and *never* +include any `/etc/rc.?.d' symbolic links in the actual archive. + + * In the postinst script, you need only do the following to setup + `/etc/rc?.d'. You should redirect standard output to `/dev/null', + as `update-rc.d' produces insignificant output: + + update-rc.d default >/dev/null + + where is the name of the file as it appears in + `/etc/init.d'. It will use the default number of "20", as + mentioned above. If you need to use a different number, you can + specify it after "default": + + update-rc.d default 30 >/dev/null + + * In the postrm script, you need only do the following *if and only + if* it is called with the `purge' argument: + + if [ purge = "$1" ] + then + update-rc.d remove >/dev/null + fi + +Important Note: +--------------- + + *Do not* include the `/etc/rc?.d/*' symbolic links in the archive! +*This will cause problems!* You should create them with update-rc.d, +as above. + + *Do not* include the `/etc/rc?.d/*' symbolic links in conffiles! +*This will cause problems!* *Do*, however, include the `/etc/init.d' +scripts in conffiles. + +Example: +-------- + + The process accounting package wants to make sure that process +accounting is started at boot time and that it is stopped before the +system is halted, enters the single-user state, or is rebooted (so that +the `/var' file system can be properly unmounted). It puts a script +that does this in `/etc/init.d', naming the script appropriately +"acct". This script accepts one of two arguments: either "start", +which starts process accounting, or "stop", which stops it. To ensure +that it does not fail obscurely when the configuration files remain but +the package has been removed, we include a `test' statement at the top +of the script: + + #! /bin/sh + # + # Start process accounting. + . /etc/init.d/functions + test -f /usr/sbin/accton || exit 0 + case "$1" in + start) + echo "Starting process accounting" + /usr/sbin/accton /var/account/pacct + ;; + stop) + echo "Stopping process accounting" + /usr/sbin/accton + ;; + *) + echo "Usage: /etc/init.d/acct {start|stop}" + exit 1 + esac + exit 0 + + You may find a skeletal script from which to base your `/etc/init.d' +scripts in `/etc/init.d/skeleton'. + + We want to stop then (re)start process accounting when entering a +multi-user state-runlevels 2, 3, 4, and 5-and we want to stop it when +leaving such a state-runlevels 0 (halt), 1 (single) and 6 (reboot). +These are good defaults, and we accomplish this by including the +following in the postinst: + + update-rc.d acct default >/dev/null + + When the user removes the acct packages with the `-purge' option, we +want to make sure the `/etc/rc?.d' symbolic links are properly removed, +so we include the following in the postrm: + + update-rc.d acct remove >/dev/null + + Otherwise, the `/etc/rc?.d' symbolic links will remain on the system +along with `/etc/init.d/acct' script. + + +File: guidelines.info, Node: Maintainer script arguments and how `dpkg' does things, Next: Mail processing packages, Prev: Configuration of init, Up: Appendix + +Maintainer script arguments and how `dpkg' does things +====================================================== + + This appendix describes exactly how maintainer scripts are called, +with what arguments, in what order, and what `dpkg' does in between. + + In all cases version numbers are -, if the package +has both, or just . `upgrade' is used even when the new +version number looks lower than the old. + +Summary +------- + + install + install + upgrade + abort-upgrade + + configure + abort-upgrade + abort-remove in-favour + abort-deconfigure \ + in-favour + removing + + remove + upgrade + failed-upgrade + remove in-favour + deconfigure \ + in-favour \ + removing + + remove + purge + upgrade + failed-upgrade + abort-install + abort-install + abort-upgrade + disappear + +Details of unpack phase of installation or upgrade +-------------------------------------------------- + + The procedure on installation/upgrade/overwrite/disappear (ie, when +running `dpkg --unpack', or the unpack stage of `dpkg --install') is as +follows. In each case if an error occurs the actions in are general +run backwards - this means that the maintainer scripts are run with +different arguments in reverse order. These are the `error unwind' +calls listed below. + + 1. + a. If a version the package is already installed, call + upgrade + + b. If this gives an error (ie, a non-zero exit status), dpkg will + attempt instead: + failed-upgrade + error unwind, for both the above cases: + abort-upgrade + + 2. If a `conflicting' package is being removed at the same time: + a. If any packages depended on that conflicting package and + `--auto-deconfigure' is specified, call, for each such + package: + deconfigure \ + in-favour \ + removing + error unwind: + abort-deconfigure \ + in-favour + removing + The deconfigured packages are marked as requiring + configuration, so that if -install is used they will be + configured again if possible. + + b. To prepare for removal of the conflicting package, call: + remove in-favour + error unwind: + abort-remove in-favour + + 3. + a. If the package is being upgraded, call + upgrade + + b. otherwise, if the package had some configuration files from a + previous version installed (ie, it is in the conffiles-only + state): + install + + c. otherwise (ie, the package was completely purged): + install + error unwind versions, respectively: + abort-upgrade + abort-install + abort-install + + 4. The new package's files are unpacked, overwriting any that may be + on the system already, for example any from the old package or + from another package (backups of the old files are left around, + and if anything goes wrong dpkg will attempt to put them back as + part of the error unwind). + + 5. + a. If the package is being upgraded, call + upgrade + + b. If this fails, dpkg will attempt: + failed-upgrade + error unwind, for both cases: + abort-upgrade + This is the point of no return - if dpkg gets this far, it + won't back off past this point if an error occurs. This will + leave the package in a fairly bad state, which will require a + successful reinstallation to clear up, but it's when dpkg starts + doing things that are irreversible. + + 6. Any files which were in the old version of the package but not in + the new are removed. + + 7. The new file list replaces the old. + + 8. The new maintainer scripts replace the old. + + 9. Any packages all of whose files have been overwritten during the + installation, and which aren't required for dependencies, are + considered to have been removed. For each such package, + a. dpkg calls: + disappear + + b. The package's maintainer scripts are removed. + + c. It is noted in the status database as being in a sane state, + namely not installed (any conffiles it may have are ignored). + Note that disappearing packages do not have their prerm + called, because dpkg doesn't know in advance that the package + is going to vanish. + + 10. Any files in the package we're unpacking that are also listed in + the file lists of other packages are removed from those lists. + (This will lobotomise the file list of the `conflicting' package + if there is one.) + + 11. The backup files made at 4. are deleted. + + 12. The new package's status is now sane, and recorded as `unpacked'. + Here is another point of no return - if the conflicting package's + removal fails we do not unwind the rest of the installation; the + conflicting package is left in a half-removed limbo. + + 13. If there was a conflicting package we go and do the removal + actions, starting from point 2. of the removal, below. + +Details of configuration +------------------------ + + When we configure a package (this happens with `dpkg --install', or +with `--configure'), we first update the conffiles and then call: + configure + + No attempt is made to unwind after errors during configuration. + +Details of removal and/or configration purging +---------------------------------------------- + + 1. remove + + 2. The package's files are removed (except conffiles). + + 3. remove + + 4. All the maintainer scripts except the postrm are removed. + + If we aren't purging the package we stop here. Note that packages + which have no postrm and no conffiles are automatically purged + when removed, as there is no difference except for the dpkg status. + + 5. The conffiles and any backup files (`~'-files, `#*#' files, + `%'-files, .dpkg-{old,new,tmp}, etc.) are removed. + + 6. purge + + 7. The package's file list is removed. + No attempt is made to unwind after errors during removal. + + +File: guidelines.info, Node: Mail processing packages, Next: Virtual dependencies, Prev: Maintainer script arguments and how `dpkg' does things, Up: Appendix + +Mail processing packages +======================== + + Debian packages which process electronic mail (whether +mail-user-agents (MUA) or alternative mail-transport-agents (MTA)) +*must* make sure that they are compatible with the configuration +decisions below. Failure to do this may result in lost mail, broken +`From:' lines, and other serious brain damage! + + * The mail spool is `/var/spool/mail' and the interface to send a + mail message is `/usr/sbin/sendmail' (as per the FSSTND). The mail + spool is part of the base and not part of the MTA package. + + * Mailboxes are locked using the `.lock' lockfile convention, rather + than fcntl, flock or lockf. + + * Mailboxes are generally 660 `.mail' unless the user has + chosen otherwise. A MUA may remove a mailbox (unless it has + nonstandard permissions) in which case the MTA or another MUA must + recreate it if needed. Mailboxes must be writeable by group mail. + + * The mail spool is 2775 mail.mail, and MUA's need to be setgid mail + to do the locking mentioned above (and obviously need to avoid + accessing other users' mailboxes using this privilege). + + * `/etc/aliases' is the source file for the system mail aliases (e.g. + postmaster, usenet, etc.) - it is the one which the sysadmin and + postinst scripts may edit. + + * The convention of writing `forward to
' in the mailbox + itself is not supported. Use a `.forward' file instead. + + * The location for the `rmail' program used by UUCP for incoming mail + is `/usr/sbin/rmail', as per the FSSTND. Likewise, `rsmtp', for + receiving batch-SMTP-over-UUCP, is in `/usr/sbin/rsmtp' if it is + supported. + + * Smail is not using HoneyDanBer UUCP, whose uux apparently accepts + -a and -g options. + + * If you need to know what name to use (for example) on outgoing + news and mail messages which are generated locally, you should use + the file `/etc/mailname'. It will contain the portion after the + username and `@' sign for email addresses of users on the machine + (followed by a newline). + + A package should check for the existence of this file. If it exists +it should use it without comment (1). If it does not exist it should +prompt the user for the value and store it in `/etc/mailname' as well +as using it in the package's configuration. The prompt should make it +clear that the name will not just be used by that package. E.g., in +the same situation the INN package says: + + Please enter the `mail name' of your system. This is the hostname + portion of the address to be shown on outgoing news and mail messages. + The default is `$syshostname', your system's host name. + Mail name [`$syshostname']: + ($syshostname is the output of `hostname -fqdn'). + + ---------- Footnotes ---------- + + (1) An MTA's prompting configuration script may wish to prompt the +user even if it finds this file exists. + + +File: guidelines.info, Node: Virtual dependencies, Prev: Mail processing packages, Up: Appendix + +Virtual dependencies +==================== + + Virtual packages are in the same namespace as real packages, and may +have the same name. The meaning of a virtual package in a +dependency/conflicts list is exactly that of listing all the real +packages which state that they are an instantiation of that virtual +package. + + This is done with a new Provides field in the control file, with a +syntax much like the Conflicts field. + + The idea is that we can have something like: + Package: elm + Depends: mta + + Package: smail + Provides: mta + Conflicts: mta + + Package: sendmail + Provides: mta + Conflicts: mta + The result is equivalent to elm having said + Package: elm + Depends: smail | sendmail + + (There'll be a special case to say that a package may conflict with a +virtual package which it provides - clearly ...) + + If there are both a real and a virtual package of the same name then +the dependency may be satisfied (or the conflict caused) by either the +real package or any of the virtual packages which provide it. This is +so that, for example, supposing we have + Package: lout + Optional: ghostview + (this is a fictional example - the Lout package should not mention +ghostview), and someone else comes up with a nice PostScript previewer, +then they can just say + Package: marvelpostview + Provides: ghostview + and all will work in the interim (until, say, the Lout maintainer +changes things). + + If a dependency or a conflict has a version number attached then only +real packages will be considered to see whether the relationship is +satisfied (or prohibited, for a conflict) - it is assumed that a real +package which provides virtual package is not of the `right' version. +If there is demand it can be arranged that a package which provides a +virtual package may mention a version number, though this is unlikely to +be helpful: + Provides: mta (2.0) + + If you want to specify which of a set of real packages should be the +default to satisfy a particular dependency on a virtual package, you can +simply list the real package as alternative before the virtual one: + Package: xbaseR6 + Recommended: xsvga | x-server + Provides: x-base, xr6shlib + + Package: xsvga + Recommended: x-base + Provides: x-server + + Package: x8514 + Recommended: x-base + Provides: x-server + + Virtual package names should generally not be used in the names of +`/etc/init.d' scripts, configuration files, logfiles, and so on, so +that several programs providing the same virtual package name can be +installed. + + diff --git a/doc/guidelines.texi b/doc/guidelines.texi new file mode 100644 index 00000000..78662e4d --- /dev/null +++ b/doc/guidelines.texi @@ -0,0 +1,1936 @@ +\input texinfo @c -*-texinfo-*- +@setfilename guidelines.info + +@set DATE 26th January 1996 + +@setchapternewpage off + +@iftex +@center @titlefont{Debian GNU/Linux Packaging Guidelines} +@tex +\vskip2pt \hrule height 2pt width \hsize \vskip2pt +@end tex +@sp 0.5 +@center @value{DATE} +@end iftex + +@ifinfo +@format +START-INFO-DIR-ENTRY +* Guidelines: (guidelines). How to make Debian packages. +END-INFO-DIR-ENTRY +@end format +@end ifinfo + +@node Top, Additional Information, (dir), (dir) + +@ifinfo +@top Debian GNU/Linux Packaging Guidelines +@end ifinfo + + This file documents the steps that must be taken in the preparation +of a Debian GNU/Linux package. All submissions to be included in the +distribution proper and all packages to be considered for @file{contrib} +or @file{non-free} availability @emph{must} conform to the guidelines +and standards described in this document or they cannot be included or +made available at the archive with the distribution. + + Please read the Guidelines carefully. If you have comments or +questions, please contact @code{debian-devel@@pixar.com}. If you are +planning on going further than just contributing a package (i.e., if +you plan to maintain it for an extended period of time or if you are +generally interested in becoming more involved in the Project), you +should join the @code{debian-devel} mailing list. For more details, +read @file{info/mailing-lists.txt}, available at any Debian GNU/Linux +archive. + + (This file was last updated on @value{DATE}. Please check the most +recent @file{dpkg} package at any Debian GNU/Linux archive for a +potentially more up to date copy.) + +@menu +* Additional Information:: Where other info is to be found. +* Package Copyright:: A few words about the importance of + understanding the package copyright. +* Package Content:: Requirements for the package content. +* Source Package:: Creating the source package. +* Binary Package:: Creating the binary package. +* Control Files:: The binary package control files. +* Appendix:: More specific details about some aspects. +@end menu + + + +@node Additional Information, Package Copyright, Top, Top +@unnumbered Additional Information + + These Guidelines are intended to be fairly general. More specific +information is available about certain aspects of building packages, +such as how to use the features of Init under Debian GNU/Linux and how +to interact with some more technical details of dpkg's operation. This +information can be found in the directory @file{doc/package-developer} +at any Debian GNU/Linux archive. At the time of this writing, the +following documents are available: + +@table @file +@item virtual-package-names-list.text +The list of virtual package names currently in use, together with the +procedure for getting new virtual package names allocated. +@item auto-deconfiguration.txt +How dpkg can sometimes automatically deconfigure packages in order to +do bulk installations smoothly. +@item dpkg-essential-flag.txt +How to tell dpkg a package is essential and should not be removed. +(This is for the use of base system packages only.) +@item dpkg-disappear-replace.txt +What happens when a package appears to have been completely replaced. +@end table + + In the future, we hope also to make available: + +@table @file +@item copyright.txt +How to choose a good copyright notice to attach to new programs. +@item version-ordering.txt +The algorithm with which packages' version numbers are compared. +@end table + + Also, you should download the sample files and the sample package +(GNU Hello) available in @file{standards/samples}. You may use any +of this material as a starting point for new packages. The following +sample files, incidentally, are available: + +@itemize @bullet +@item debian.README +@item debian.control +@item debian.postinst +@item debian.postrm +@item debian.rules +@end itemize + +Some more detailed information about certain topics is available in the +appendix to this document (@pxref{Appendix}). + + +@node Package Copyright, Package Content, Additional Information, Top +@unnumbered Package Copyright + + Please study the copyright of your submission @emph{carefully} +and @emph{understand it} before proceeding! If you have doubts or +questions, please ask! + + In order to understand how we classify and distribute certain +packages, it is important to understand the distinction between being +freely available and being freely redistributable. + + Being @dfn{freely available}, quite simply, means that the software +can be made available freely, at least for non-commercial purposes and +in its original, unmodified form. This includes packages made available +freely that have restrictions on non-commercial use, redistribution of +modifications, etc. Being freely available, therefore, has nothing to +do with being able to modify and redistribute the software. It only +means that you can get a copy of the software without having to pay +(and it does not necessarily mean that you can @emph{use} the software +without having to pay---shareware is an example of freely available +software). + + @dfn{freely redistributable}, while generally being freely available, +goes beyond just being freely available. Freely redistributable means +that that the software, in addition to being able to be made available +freely, must be able to be freely modified and redistributed without +restriction. + + All submissions to be included in the distribution proper @emph{must} +be freely redistributable. + + In addition to the distribution, the Project maintains two separate +archives of software packages with the distribution: the @file{contrib} +archive and the @file{non-free} archive. + + @file{contrib} is an archive of user-contributed packages that are +not maintained by the Project, packages that were once maintained by the +Project but that are no longer actively maintained, and packages that +are maintained by the Project but that are not yet considered ready for +inclusion in the distribution proper (i.e., ALPHA and BETA packages). +As above, all submissions for inclusion in the @file{contrib} archive +@emph{must} be freely redistributable. + + @file{non-free} is an archive of packages with either restrictive or +unclear terms of copying or modification. If a package has @emph{any} +restrictions on modification or redistribution, it can not be included +in the distribution or @file{contrib} archive. It can only be included +in the @file{non-free} archive, and then only if it is freely available. + + In summary, in order to be included in the distribution proper or the +@file{contrib} archive, a package must be @emph{freely redistributable}. +Anyone must be able to make copies of it, modify it, redistribute it with +their modifications in place, include it on a CD-ROM, or generally sell +it. To be included in the @file{non-free} archive, a package may have +restrictions, as long as the package remains @emph{freely available}. We +must be available to make it available freely at the archive, and anyone +must be able to make copies of it and use it for at least non-commercial, +personal purposes. Software that will typically be included in +@file{non-free} are software that does not allow commercial distribution, +software that does not allow modification or redistribution of +modifications, commercial ``demos'', and ``shareware''. + + When in doubt, send mail to @file{iwj10@@cus.cam.ac.uk} and +@file{imurdock@@debian.org}. Be prepared to provide us with the +copyright statement. Software covered by the GPL, public domain +software and BSD-like copyrights are safe; be wary of the phrases +``commercial use prohibited'' and ``distribution restricted''. + + Every package submission @emph{must} be accompanied by verbatim copy +of its copyright (with the exceptions of public domain packages and +those covered by the UCB BSD licence or the GNU GPL or LGPL; in these +cases simply indicate which is appropriate). This information must be +included in a file installed to the directory @file{/usr/doc/copyright}. +See below for details. + + + +@node Package Content, Source Package, Package Copyright, Top +@unnumbered Package Content + + The following requirements apply equally to both the binary and +source packages. In either case, when files have been installed, +they must conform to the requirements described in this section. + + The primary rule in Debian GNU/Linux is to follow the Linux @dfn{File +System Standard} (@dfn{FSSTND}). The location of installed files +@emph{must} comply @emph{fully} with the FSSTND. The latest version of +this document can be found alongside the Guidelines or at +@file{tsx-11.mit.edu} in @file{/pub/linux/docs/linux-standards/fsstnd}. +Specific questions about following the standard should be addressed to +Daniel Quinlan, the FSSTND coordinator, at @code{quinlan@@yggdrasil.com}. + + In addition to the FSSTND, all Debian GNU/Linux packages must follow +the guidelines below. + +@itemize @bullet +@item +Directories should be mode 755 or (for group-writability) mode 2775, +with the exception of special ``system'' directories that need to be +another mode. The ownership of the directory should be consistent with +its mode---if a directory is mode 2775, it should be owned by the group +that needs write access to it, of course. Use common sense in assigning +permissions and ownerships to directories, and make sure that what is +done is secure if it is ``non-standard''. + +@item +Normal binaries should be mode 755 and owned by @code{root.root}. If +there is a good reason to use a different mode or ownership, you may do +so, but you must try to be as consistent as possible with the rest of +the system. If you need to use a different mode or ownership, please +discuss it with @code{imurdock@@debian.org}. + +@item +Setuid binaries should normally be mode 4755 (not 4711!) and, of course, +owned by the appropriate user. + +@item +Setgid binaries should normally be mode 2755 (not 2711!) and, of course, +owned by the appropriate group. + +@item +Library files should generally be mode 644 and owned by +@code{root.root}. If the package requires different permissions +or ownerships to function correctly, they should be used instead. + +@item +Manual pages should be mode 644 and owned by @code{root.root}. The +@file{nroff} source must be installed. You should @emph{not} install a +preformatted ``cat page'', and you should only use sections 1 to 9---see +the FSSTND for more details. If no manual page is available for a +particular program, utility or function and this is reported as a bug on +debian-bugs, a symbolic link from the requested manual page to the +@file{undocumented}(7) manual page should be provided. This symbolic +link can be created from @file{debian.rules} like this: + +@smallexample +ln -s ../man7/undocumented.7 debian-tmp/usr/man/man[1-9]/the_requested_manpage.[1-9] +@end smallexample + +Do not close the bug report until a proper manpage is available. You +may forward the complaint to the upstream maintainers, and mark the bug +as forwarded in the Debian bug tracking system. The GNU Project do not +in general consider the lack of a manpage to be a bug, but we do - if +they tell you to go away leave the bug open anyway. + +@item +Info documents should be mode 644, owned by @code{root.root}, and +compressed with @file{gzip -9} when installed. The package must call +@file{install-info} to update the Info @file{dir} file. This should +be done in the post-installation script (@file{postinst}), like this: + +@smallexample +install-info --quiet /usr/info/foobar.info +@end smallexample + +The entries should be removed by the pre-removal script (@file{prerm}), +like this: + +@smallexample +install-info --quiet --remove /usr/info/foobar.info +@end smallexample + +It is also a good idea to specify a section for the Info @file{dir} +entry. This is done with the @file{--section} switch. To determine +which section to use, you should use look at @file{/usr/info/dir} on +your system and choose the most relevant (or create a new section if +none of the current sections are relevant). + +If @file{install-info} cannot find a description entry in the Info file +you will have to supply one. See @file{install-info}(8) for details. + +@item +If a package contains any shared libraries you will have to invoke +@file{ldconfig} in both the @file{postinst} and @file{prerm} scripts +to correctly update the library links. See @file{ldconfig}(8) for +details. + +@item +Any additional documentation that comes with the package can be +installed at the discretion of the package maintainer. Text +documentation should be mode 644, owned by @code{root.root}, installed +to @file{/usr/doc}, and compressed with @file{gzip -9} unless it is small. + +If a subdirectory of @file{/usr/doc} is warranted, please do create one. +Please do not install DVI, PostScript, or large textual documentation in +the same package; upload such documentation as a separate package +(installing its files in @file{/usr/doc}) so that it can be made +available with the distribution. If a user has the need for the +documentation, they can easily get it from the archive, CD-ROM, etc., +but it should not take up disk space on the machines of the user who do +not need or want it installed. + +@item +Create a file named @file{/usr/doc/copyright/<@i{package}>} which gives +details of the authorship and copyright of the package. If the package +is distributed under the GNU General Public Licence, the GNU Library +General Public Licence or the Regents of the University of California at +Berkeley (BSD) licence, please say so instead of including a copy of the +licence. The files @file{BSD}, @file{GPL}, and @file{LGPL} will be +available in the @file{/usr/doc/copyright} directory for you to refer +to. @file{/usr/doc/copyright/<@i{package}>} should not be compressed. + +@emph{All} authorship and copyright information from the original source +package must be included in the @file{/usr/doc/copyright/<@i{package}>} +file. + +@item +Any example files (for example, sample configuration files) should +be placed in the directory @file{/usr/doc/examples}. If the file is +normally a hidden file, such as @file{.emacs}, then please call it +@file{dot.emacs}, to avoid confusion. Again, you may create a +subdirectory if it is needed. + +@item +All symbolic links should be relative, not absolute. Absolute links, +in general, cause problems when a file system is not mounted where it +``normally'' resides (for example, when mounted via NFS). In certain +cases, however, relative links may also cause similar problems. I +have generally made links into @file{/etc} and @file{/var} absolute +and all other links relative. There may be other cases in which +absolute links are necessary. + +Therefore, in the @file{Makefile} or @file{debian.rules}, do not do: +@smallexample +install: all + [...] + ln -fs /usr/bin/gcc /usr/bin/cc + [...] +@end smallexample +Instead, do: +@smallexample +ln -fs gcc /usr/bin/cc +@end smallexample +or +@smallexample +( cd /usr/bin ; ln -fs gcc cc ) +@end smallexample + +Please do not create hard links in the manual page directories. In +these cases, you should use relative symbolic links or files that +@file{.so} (roff for `source') others instead. + +@item +All command scripts should have a @code{#!} line naming the shell to be +used to interpret them. + +@item +In the case of Perl scripts this should be @code{#!/usr/bin/perl} or +sometimes @code{#!/bin/perl}, as follows: if the script is a critical +one that may be called when the @file{/usr} partition is unmounted or +broken it should use @file{/bin/perl}. Otherwise (especially if the +script is not specifically targetted at Debian) it should use Perl's +standard location, @file{/usr/bin/perl}. + +@item +Generally the following compilation parameters should be used: + +@display + CC = gcc + CFLAGS = -O2 -g -Wall # sane warning options vary between programs + LDFLAGS = # none (or -N, if appropriate; see below) + install -s (or strip) +@end display + +Note that all installed binaries should be stripped, either by using the +@code{-s} flag to @file{install}, or by calling @file{strip} on the +binaries after they have been copied into the @file{debian-tmp} but +before the tree is made into a package. + +Make sure that you do not link with @code{-g}, as this makes a.out +compilers produce huge statically linked binaries. The @code{-g} flag +is useful on compilation so that you have available a full set of +debugging symbols in your built source tree, in case anyone should file +a bug report involving (for example) a core dump. + +@code{-N} should only be used on binaries that are very small (less than +8K with the @code{-N} option, roughly) and are not likely to have +multiple instances in memory. Do not use @code{-N} on daemons, no +matter how small they are. + +It is up to the package maintainer to decide what compilation options +are best for the package. Certain binaries (such as +computationally-intensive programs) may function better with certain +flags (@code{-O3}, for example); feel free to use them. Please use good +judgment here. Don't add flags for the sake of adding flags; only add +flags if there is good reason to do so. + +@item +Please make sure that you use only released versions of shared libraries +to build your packages; otherwise other users will not be able to run +your binaries properly. Producing source packages that depend on +unreleased compilers is also usually a bad idea. + +@item +Logfiles should usually be named @file{/var/log/}, or +@file{/var/log/.} if you have several logfiles. It +may be appropriate to create a directory. Make sure that any logfiles +are rotated occasionally so that they don't grow indefinitely; the best +way to do this is to use @file{savelog} from the cron package in an +@file{/etc/cron.daily}, @file{/etc/cron.weekly} or +@file{/etc/cron.monthly} script. + + +@item +Please check with the base system maintainer (Ian Murdock) before using +users or groups other than @code{root} and others specified in this +document. +@end itemize + + + +@node Source Package, Binary Package, Package Content, Top +@unnumbered Source Package + + The source package should contain a file called @file{debian.rules} +which contains at least the following targets, to be invoked in the top +level directory: + +@smallexample +build +binary +clean +@end smallexample + +@file{debian.rules} should start with + +@smallexample +#!/usr/bin/make -f +@end smallexample + +@noindent and be executable. It is a good idea to arrange for it not +to fail obscurely when invoked in the wrong directory, for example by +testing for the existence of a file in the source directory. + +@itemize @bullet +@item +The @file{build} target should perform all non-interactive configuration +and compilation of the package. If a package has an interactive +pre-build configuration routine, the source package should be built +@emph{after} this has taken place. + + For some packages, notably ones where the same source tree is +compiled in different ways to produce two binary packages, the +@file{build} target does not make much sense. For these packages it is +good enough to provide two (or more) targets (@file{build-a} and +@file{build-b} or whatever) for each of the ways of building the +package, and a @file{build} target that does nothing. The @file{binary} +target will have to build the package in each of the possible ways and +make the binary package out of each. + +@item +The @file{binary} target of @file{debian.rules} should be all that is +necessary for the user to build the binary package. The binary package +should be created using @file{dpkg} and placed in the parent of the top +level directory. The next section describes how to construct binary +packages from the @file{binary} target. + +@item +The @file{clean} target should undo the effects of the @file{build} +target and the @file{binary} target, except that it should leave alone +any @file{../<@i{package}>-<@i{version}>.deb} file created by a run of +@file{binary}. + +@item +Additional targets may exist in @file{debian.rules}. We recommend using +@file{source} and @file{diff} targets to build the Debianised source +package and the Debianisation context diff, respectively. These files +should be placed in @file{../foo-<@i{version}>.tar.gz} and +@file{../foo-<@i{version}>.diff.gz}. The @file{install} target, for +installing into a running system direct from the Debianised source +tree, is no longer required. The sample @file{debian.rules} provides +@file{source} and @file{diff} targets that should work with little or +no alteration, providing that the package-specific variables at the top +of the script have been properly defined. + +@item +If you need to edit a @file{Makefile} where @file{configure} scripts +are used, you should edit the @file{.in} files rather than editing +the @file{Makefile} directly. This allows the user to reconfigure +the package if necessary. You should @emph{not} configure the package +and edit the generated @file{Makefile}! This makes it impossible for +someone else to later reconfigure the package. + +@item +Please document your changes to the source package so that future +package maintainers know what has been changed. To do this, include +a description of your changes in the @file{debian.README} (which, as +described above, should already contain authorship and copyright +information!) and include relevant information such as your name, +electronic mail address, date, etc. The @file{debian.README} file +should also document any `unusual' packages which must be installed for +this one to compile. + +@item +If changes to the source code are made that are applicable to Linux +systems or systems in general please try to get them included in the +upstream version of the package by supplying the upstream authors with +the changes in whatever form they prefer. + +If changes to the source code are made, please use a @file{define}. If +they are changes required to compile or function under Linux in general, +use @file{LINUX}. If it is a cosmetic or functional change, use +@file{DEBIAN}. + +@item +Create the source package using @file{tar}, and use @file{gzip -9} to +compress it. Source packages should be named in the form +<@i{package}>-<@i{version}>.tar.gz---for example, +@file{fileutils-3.9-3.tar.gz}. + +NB, here @code{<@i{version}>} is the full Debian version number, in the +form @code{<@i{original_version}>-<@i{debian_revision}>} (see below), +but the tarfile should unpack into a directory named +@code{<@i{package}>-<@i{original_version}>} (again, see the section +below on version numbering). + +@item +Create the context diff against the original package using @file{diff +-cNr}, and use @file{gzip -9} to compress it. Context diffs should be +named in the form <@i{package}>-<@i{version}>.diff.gz---for example, +@file{fileutils-3.9-3.diff.gz}. +@end itemize + + Please note that the package and patch filenames do @emph{not} need +to fit in MS-DOS 8+3. They will be made available under an alternative +8+3 name in the archive by the archive maintainer, using a symlink. + + + +@node Binary Package, Control Files, Source Package, Top +@unnumbered Binary Package + + The @file{binary} target of the source package @file{debian.rules} +file should do the following (see the sample @file{debian.rules} +for an implementation that you are free to modify and use in your own +packages, of course): + +@itemize @bullet +@item +Create an empty directory in the top-level directory of the source +package (deleting it first, if necessary), and install the files +belonging to this package in that directory. For example, the directory +could be called @file{debian-tmp} and would probably contain directories +@file{debian-tmp/usr/bin}, @file{debian-tmp/usr/lib}, etc. +(@file{debian-tmp} is the name traditionally used, and it is used in +the sample @file{debian.rules} file, so we will use that name in the +Guidelines.) + +@item +Make sure that all the files under @file{debian-tmp} have the correct +ownerships and permissions (@pxref{Package Content}, for more information +about file locations, ownerships, and permissions.) + +@item +Create a subdirectory of @file{debian-tmp} called @file{DEBIAN}. This +directory contains the package control information, including at the +very least the master information file named @file{control}. The next +section describes the semantics and syntax of the files required and +allowed here. + +@item +Run @file{dpkg} to create the binary package, using something like + +@smallexample +dpkg --build debian-tmp +@end smallexample + +This will create a file called @file{debian-tmp.deb}, from the +@file{debian-tmp} directory. You should rename this file to +@file{../<@i{package}>-<@i{version}>.deb} after it is built. + +After the @file{binary} target has done all this, the +@file{<@i{package}>-<@i{version}>.deb} file in the parent directory is +the binary distribution. This file may be distributed and installed on +any Debian GNU/Linux system with @file{dpkg} in the same manner and +using the same methods as all packages are installed to the system. + +@item +If a single source package corresponds to several binary packages, there +should usually be a @file{debian.rules} file with a single @file{binary} +target that builds all the binary packages involved and move all packages +to the parent directory of that containing the source package. + +In this case, you should choose binary package names which are meant to +make clear the close relationship between the binary packages and which +source package the binary packages came from (for example, the +@file{texinfo} source package build two binary packages: @file{texidoc} +and @file{texinfo}). You should place the appropriate binary package +name in the @file{Package} field of the control file (not the source +package name), and you should consider whether the other binary packages +that come from the same source tree should be mentioned in the +@file{Depends}, @file{Recommends} or @file{Suggests} fields. You +should put the source package name in the @file{Source} field. + +You should retain the source package version numbering in the +@file{Version} field, if possible---the version number should be the +same for the Debianised source tree and all the binary packages +generated from it. It is more important, though, that the version +numbers sort correctly. See below for details of version numbers. +@end itemize + + + +@node Control Files, Appendix, Binary Package, Top +@unnumbered Control Files + + Each binary package contains, in addition to the files that comprise +the actual package, a set of text files that control how @file{dpkg} +installs, configures, upgrades, removes, etc. the package. These files +are called @dfn{control files}. When creating the package, the control +files should placed in a directory called @file{DEBIAN}, as described +earlier (@pxref{Binary Package}, for further information). + +The control information files are: + +@table @code +@item control +The master package control information file. +@item conffiles +A list of package configuration files. +@item preinst +The package pre-installation script. +@item postinst +The package post-installation script. +@item prerm +The package pre-removal script. +@item postrm +The package post-removal script. +@end table + + Of these, only @file{control} is required. The various installation +scripts, and the configuration files list, will only be used if they are +present. + +@menu +* control:: +* conffiles:: +* Installation and Removal Scripts:: +* Dependencies and Conflicts:: +* Package Classification Fields:: +@end menu + +@node control, conffiles, Control Files, Control Files +@unnumberedsec control + + The @file{control} file contains a number of fields. Each field +begins with a field name, such as @file{Package} or @file{Version} +(case insensitive), followed by a colon and optionally some spaces or +tabs (a single space is conventional). Then comes the body of the +field, which may be several lines long; each continuation line must +start with at least one space or tab. (These are the same rules as +apply to RFC822 mail headers.) Blank lines are not permitted in the +control file. + + The required fields in the control file are the following: + +@table @code +@item Package +The name of the package. +@item Description +The description of the package. How to write an extended and more +usefull description field can be found in @pxref{How to write the Description control file field}. +@item Maintainer +The name and e-mail address of the maintainer of the package. +@item Version +The version number in the format +@code{<@i{original_version}>-<@i{debian_revision}>}. +@end table + + Each field has a particular format and meaning for the package +installation tools. + +The value of @file{Package} should be the name of the package. +Package names must start with an alphanumeric, must be at least two +characters, and may contain only alphanumerics and the characters +- + . @@ : = % _ (that is, hyphen, plus, stop, at, colon, equals, +percent and underscore). They are not case sensitive. + + The @code{Maintainer} field should be in the form + +@smallexample +Joe J. Bloggs +@end smallexample + +@noindent Note that this will not be useable as an email address if +the name given contains full stop characters, because of a silly +restriction in the Internet mail standards. If you want to use this +as an email address in a program you should check for full stops and +change the string to the form @code{jbloggs@@foo.com (Joe J. Bloggs)} +if you find any. + + The @code{Version} field should be the version number of the +package. For most packages which are not written specifically for +Debian, this should be in the form + +@smallexample +Version: <@i{original_version}>-<@i{debian_revision}> +@end smallexample + +@noindent where @file{<@i{original_version}>} is the original package +version number in whatever form the original package uses and +@file{<@i{debian_revision}>} indicates which ``debianisation'' this is +(this should usually be a plain number or perhaps a two numbers +separated by a full stop, and should be incremented each time the +package is changed or updated). + + Packages which are written specifically for Debian do not have a +@i{debian_revision}, and their version number should simply be +@i{version} (which should not contain any hyphens, to avoid +confusion). + + There is an ordering imposed on version numbers, described in +@file{version-ordering.txt}. This ordering is designed to `do the right +thing' in most circumstances; if your package has an version number in +an unusual format you may need to reformat it somewhat to get the +ordering right. This is important because @file{dpkg} is (for example) +reluctant to downgrade packages. + + The optional fields in the control file are the following: + +@table @code +@item Depends +The names of prerequisite packages. +@item Recommends +The names of related, recommended packages. +@item Suggests +The names of related, optional packages. +@item Conflicts +The names of packages which conflict with this package. +@item Provides +The names of virtual packages which this package provides. +@item Priority +The `priority' of the package, as shown and used by @file{dselect}. +@item Section +The `section' of the package, as shown and used by @file{dselect}, and +used as a location for the package in the distribution. +@item Essential +A boolean field used by the base packages. +@item Pre-Depends +Used by base packages to ensure that (for example) shared libraries are +present befoore they are upgraded. +@item Source +Gives the name of the source package when several binary packages are +generated from a single source tree. +@end table + +@noindent See below for details of the semantics and syntax of these +fields. Most packages will need at least a @code{Depends} field. + + An example of a @file{control} file would be: + +@smallexample +Package: smail +Version: 3.1.29.1-13 +Maintainer: Ian Jackson +Recommends: pine | mailx | elm | emacs | mail-user-agent +Suggests: metamail +Depends: cron, libc5 +Conflicts: sendmail +Provides: mail-transport-agent +Description: Electronic mail transport system. + Smail is the recommended mail transport agent (MTA) for Debian. + . + An MTA is the innards of the mail system - it takes messages from + user-friendly mailer programs and arranges for them to be delivered + locally or passed on to other systems as required. + . + In order to make use of it you must have one or more user level + mailreader programs such as elm, pine, mailx or Emacs (which has Rmail + and VM as mailreaders) installed. If you wish to send messages other + than just to other users of your system you must also have appropriate + networking support, in the form of IP or UUCP. +@end smallexample + + In this case, @file{mail-user-agent} is a virtual package +representing any user mailer program; the actual package names +@file{pine} is quoted for the reasons described in +@file{dependency-ordering.txt}, and the others because older versions +of those packages do not have the appropriate @file{Provides} field. + +@node conffiles, Installation and Removal Scripts, control, Control Files +@unnumberedsec conffiles + + The contents of @file{conffiles} is simply a list of configuration +files in the package. When installing the package, @file{dpkg} uses +an intelligent method to update these files. This will ensure that +package-specific configuration files are not overwritten when a package +is upgraded, unless the user wishes the installation tools to do so. + + Typically, files listed in conffiles are package-specific +configuration files, which (according to the Linux Filesystem Standard) +are stored in @file{/etc}. For example, the @code{sendmail} package may +contain the file @file{/etc/sendmail.cf}, which we do not wish to +overwrite automatically when the user upgrades the sendmail package. +Only those files listed in @file{DEBIAN/conffiles} will be updated +intelligently when a package is upgraded; all other files in the package +will be overwritten by the upgrade process. + + Configuration files which will be functional as shipped and will +probably need little or no local editing should simply be listed the +@file{conffiles} file; in this case you need read no further. + + For packages whose configuration files will need modification on +most systems there are two sensible approaches. Which one is chosen +depends on how hard the configuration problem is and how much time the +package maintainer has available. + + One option is for you to ship a minimal `best-effort' file in +@file{/etc}, and list the file in @file{conffiles}. This will mean that +the user will have to go and edit the file themselves to get the package +to work properly, of course. The next time they upgrade the package, if +you haven't changed the file version, their old file will be left in +place. If you have modified your version then the user will get a +prompt asking them which version of the file they want, theirs or yours. +They will then usually have to resolve the discrepancies manually. + + The other option is to be preferred, if you can do it: do not put a +copy of the configuration file in the package at all. Instead, you +check in the postinst whether the file exists, and if it doesn't you +prompt the user for the information you need to create a good one. This +is obviously harder work. + + You also have to remember that you will have to keep up with your +package's changes: if you discover a bug in the program which generates +the configuration file, or if the format of the file changes from one +version to the next, you will have to arrange for the postinst script to +do something sensible---usually this will mean editing the installed +configuration file to remove the problem or change the syntax. You will +have to do this very carefully, since the user may have changed the +file, perhaps to fix the very problem that your script is trying to deal +with---you will have to detect these situations and deal with them +correctly. + + If you do go down this route it's probably a good idea to make the +program that generates the configuration file(s) a separate program in +@file{/usr/sbin}, by convention called @i{package}@code{config}, and +then run that if appropriate from the post-installation script. The +@i{package}@code{config} program should not unquestioningly overwrite an +existing configuration---if its mode of operation is geared towards +setting up a package for the first time (rather than any arbitrary +reconfiguration later) you should have it check whether the +configuration already exists, and require a @code{--force} flag to +overwrite it. + + @file{conffiles} should almost certainly list all the files contained +in your package in the @file{/etc} directory. There may also be other +files somewhere that the user is expected to edit, which should also be +included. Note, however, that the FSSTND specifies that configuration +files must be in @file{/etc}. No Debian package should contain +configuration files in @file{/usr/etc}, and all programs should refer to +configuration files in @file{/etc}. + +@noindent For example, the TCP/IP package might use a conffiles which contains + +@smallexample +/etc/init.d/netbase +/etc/gateways +/etc/protocols +/etc/services +/etc/hosts.allow +/etc/hosts.deny +/etc/rpc +@end smallexample + +@noindent and so on; the files + +@smallexample +/etc/hosts +/etc/inetd.conf +/etc/host.conf +/etc/networks +/etc/resolv.conf +@end smallexample + +@noindent might be generated by an interactive configuration program, +and would then not be included in the package or listed in the +@file{conffiles}. + +@node Installation and Removal Scripts, Dependencies and Conflicts, conffiles, Control Files +@unnumberedsec Installation and Removal Scripts + + The scripts @file{preinst}, @file{postinst}, @file{prerm}, and +@file{postrm} are optional (Bash or Perl) scripts. As the names +would indicate, if these scripts exist, they will be executed before +installing the package, after installation, before package removal, +and after removal, respectively. + + They are given arguments which indicate the precise situation and +action being performed---see @file{maintainer-script-args.txt} for +details of exactly when each of the scripts is invoked and what its +arguments are. Extra arguments and situations may be added later, so +you should not test the number of arguments to your script to determine +the situation, and you should choose the sense of your `if it is this +then do this otherwise do that' tests carefully. + + These scripts can be used to perform any site-specific package +configuration. + + Because the scripts will be exectued by the dpkg front-end, it is +guaranteed that the scripts will be executed interactively. User input +from the scripts should be read from standard input, not the user's +terminal. Similarly, output should be sent to standard output. + + If your maintainer scripts need to prompt for passwords and/or do +@i{full-screen} interaction should do these things to and from +@file{/dev/tty}, since @file{dpkg} will at some point redirect scripts' +standard input and output so that it can log the installation process. +Likewise, because these scripts may be executed with standard output +redirected into a pipe for logging purposes, Perl scripts should set +unbuffered output by setting @code{$|=1} so that the output is printed +immediately rather than being buffered. + + The scripts must be idempotent, and they must clean up after +themselves properly. Ie, they must do the right thing if run multiple +times, even if previous runs failed halfway through. This is so that if +any errors occur, or if the @file{dpkg} run is interrupted, the user can +recover by rerunning @file{dpkg}, and/or by upgrading to a new version +and then rerunning the failed operation. + + These scripts should avoid producing output which it is unnecessary +for the user to see and should rely on @file{dpkg} to stave off boredom +on the part of a user installing many packages. This means, amongst +other things, using the @file{--quiet} option on @file{install-info}. + + Packages should try to minimise the amount of prompting they need to +do, and they should ensure that the user will only every be asked each +question once. This means that packages should try to use appropriate +shared configuration files (such as @file{/etc/papersize} and +@file{/etc/news/server}), rather than each prompting for their own list +of required pieces of information. + + It also means that an upgrade should not ask the same questions +again, unless the user has used @code{dpkg --purge} to remove the +package's configuration. The answers to configuration questions should +be stored in an appropriate place in @file{/etc} so that the user can +modify them, and how this has been done should be documented. + + If a package has a vitally important piece of information to pass to +the user (such as "don't run me as I am, you must edit the following +configuration files first or you risk your system emitting +badly-formatted messages"), it should display this in the +@file{postinst} script and prompt the user to hit Return to acknowledge +the message. Copyright messages do not count as vitally important (they +belong in @file{/usr/doc/copyright}; neither do instructions on how to +use a program (these should be in on line documentation, where all the +users can see them). + + They should return a zero exit status for success, or a nonzero one +for failure. Note that if a script is a @code{#!/bin/sh} script it +should probably start with @code{set -e}, to avoid continuing after +errors---see @file{bash}(1) for details. Perl scripts should check for +errors when making calls such as @code{open}, @code{print}, +@code{close}, @code{rename} and @code{system}. + + If these scripts exist they should be left in the @file{DEBIAN} +directory with execute permission enabled and should contain an +appropriate @code{#!} line, such as @code{#!/bin/bash} for a +@code{bash} script or @code{#!/bin/perl} for a Perl script (see +above). + +@node Dependencies and Conflicts, Package Classification Fields, Installation and Removal Scripts, Control Files +@unnumberedsec Conflicts, Depends, Suggests, Recommends and Provides + + The @file{Depends} field lists packages that are required for this +package to provide a significant amount of functionality. The package +maintenance software will not allow a package to be installed without +also installing packages listed in its @code{Depends} field, and will +run the @code{postinst} scripts of packages listed in @code{Depends} +fields before those of the packages which depend on them, and run the +@code{prerm} scripts before. + + Packages containing dynamically-linked executable binaries (this +includes almost all C programs) should include a @file{Depends} field +which mentions the shared C library required for the program to run. +For a.out binaries linked against @file{libc.so.4} the relevant package +name is @file{libc} (for the a.out stable 0.93 tree) or @file{libc4} +(for the unstable development 1.1 tree); for ELF binaries linked against +@file{libc.so.5} the relevant package name is @file{libc5}. + + The @code{Recommends} field lists packages that would be found +together with this one in all but unusual installations. The user-level +package maintenance program @file{dselect} will warn the user if they +select a package without those listed in its @code{Recommends} field. +Note that @code{Recommends} fields do not currently have any implications +for the order in which the maintainer scripts are run. + + The @code{Suggests} field lists packages that are related to this one +and can perhaps enhance its usefulness, but without which installing +this package is perfectly reasonable. The package maintenance software +will not moan at the user for not selecting @code{Suggests} related +packages, but may use the information in the @code{Suggests} field to +assist the user during package selection. + + The syntax of @code{Depends}, @code{Recommends} and @code{Suggests} +is a list of groups of alternative packages. Each group is a list of +packages separated by vertical bar (or `pipe') symbols, @code{|}. The +groups are separated by commas. Each package is a package name +optionally followed by a version number specification in parentheses. A +version number may start with a @code{>=}, in which case that version or +any later will match, or @code{<=} for that version or any earlier +version. A version number starting with a @code{>>} or @code{<<} will +respectively match any later or earlier version. If a version number or +a version number starting with @code{=} is specified an exact match is +required. Commas are to be read as `AND', and pipes as `OR', with pipes +binding more tightly. + + Versions of dpkg before 1.0.9 used @code{<} and @code{>} for +@code{<=} and @code{>=} (these are still supported for backward +compatibility), and did not support @code{<<} and @code{>>}. + + The @code{Conflicts} field lists packages that conflict with this +one, for example by containing files with the same names (an example +would be Smail vs. Sendmail). The package maintenance software will not +allow conflicting packages to be installed. Two conflicting packages +should each include a @code{Conflicts} line mentioning the other. + + The syntax of @code{Conflicts} is a list of package names (with +optional version numbers), separated by commas (and optional +whitespace). In the @code{Conflicts} field the comma should be read as +`OR'. + + The @code{Provides} field lists the names of any `virtual packages' +of which this packages is to be considered an instantiation. Virtual +packages are used to allow packages to refer to a service they require +(such as the availability of @file{/usr/sbin/sendmail}) without having +to know the names of all the relevant packages. The virtual package +names defined in @code{Provides} fields may be used in other packages' +@code{Depends}, @code{Recommends}, @code{Suggests} and @code{Conflicts} +fields. For more information about how to use virtual packages and +which virtual package names to use read @pxref{Virtual dependencies} and +@file{doc/package-developer/virtual-package-names-list.text}. + + The syntax of @code{Provides} is a list of package names separated by +commas (and optional whitespace). + +@node Package Classification Fields, , Dependencies and Conflicts, Control Files +@unnumberedsec Priority, Section and Essential + + The @code{Priority} and @code{Section} fields are used by +@file{dselect} when displaying the list of packages to the user. There +is no need to put them into a package, since these are usually set by +the distribution maintainers in the @file{Packages} file. + + However, if a user installs a package which is not part of the +standard distribution, or without downloading and updating from a new +@file{Packages} file, the information about the priority and section of +a package will be absent, and the @file{dselect} package listing will +have the package listed under `unclassified'. It is permissible for a +package to include @code{Section} or @code{Priority} fields to improve +this; however, if you do this you should make sure you keep the +information up to date so that users are not shown conflicting +information. The @code{Section} field can also be used by the +distribution maintainers as a suggestion about which section you think +is most appropriate for your package. + + The values for the @code{Section} and @code{Priority} fields should be +determined by the distribution maintainers; if you don't know what to +put in them just leave them out. You can add them later, if you like, +but remember that you'll then have to reissue your package if the +distribution maintainers change the classification of your package. + + The @code{Essential} field should only appear in packages in the +installation's base system. If it is set to @code{yes} then @file{dpkg} +will not remove the package even if asked to, and will make certain +minor modifications to its installation procedures. The only other +legal value is @code{no}, which is equivalent to the absence of the +field. + +@appendix +@node Appendix, , Control Files, Top +@unnumbered Appendix +@comment node-name, next, previous, up +@menu +* configuration files -- /etc/skel vs /usr/doc/examples:: +* How to write the Description control file field:: +* Configuration of init:: +* Maintainer script arguments and how @file{dpkg} does things:: +* Mail processing packages:: +* Virtual dependencies:: +@end menu + +@node configuration files -- /etc/skel vs /usr/doc/examples, How to write the Description control file field, Appendix, Appendix +@comment node-name, next, previous, up +@unnumberedsec configuration files -- /etc/skel vs /usr/doc/examples + +There seems to be a certain amount of confusion about @file{/etc/skel} +and @file{/usr/doc/examples}. The most important thing to remember is +the following: + +Files in @file{/etc/skel} will @emph{automatically} be copied into +@emph{new} user accounts by @file{adduser}. They should not +be referenced there by any program. Files in @file{/usr/doc/examples} +should not be installed automatically. + +Therefore, if the program in question need a dotfile to exist in advance +in @file{$HOME} to work @emph{sensibly} that dotfile should be installed +in @file{/etc/skel} (and listed in conffiles; @pxref{conffiles}). + +However, programs that require dotfiles in order to operate sensibly +(dotfiles that they do not create themselves automatically, that is) are +a bad thing, and that programs should be configured by the Debian +default installation as close to normal as possible. + +Therefore, if a program in a Debian package needs to be configured in +some way in order to operate sensibly that configuration should be done +in a site-wide global configuration file elsewhere in @file{/etc} (and +that file should be listed in conffiles). Only if the program doesn't +support a site-wide default configuration should a default per-user file +be placed in @file{/etc/skel} (and listed in conffiles; +@pxref{conffiles}). + +The idea is as follows: + +The sysadmin should ideally not have to do any configuration other than +that done @w{(semi-)}automatically by the postinst script. + +However, if they wish to change their configuration themselves +(because the configuration they want is beyond the scope of the +autoconfiguration, or because the autoconfiguration doesn't exist yet, +or because they just want to do it themselves for any reason) then +@file{/usr/doc/examples} exists as @emph{documentation} for their benefit. + +The only time these files should be read are by the sysadmin using their +favourite editor or pager, or @emph{perhaps} (in very complex packages) +by the postinst as a template to build on or modify. + +@file{/etc/skel} is part of the @emph{implementation} of this +configuration. It contains the files that are copied into new user +accounts. It should probably be as empty as we can make it. + +Examples: +@table @code +@item .profile +@file{/etc/skel} should not contain a @file{.profile} file. Anything +that needs to be done there should be done in @file{/etc/profile}. +Anything that should not go in @file{/etc/profile} (users can't avoid +running @file{/etc/profile}) probably should not be in the default +configuration. bash has generally good default behaviour. + +@item .bash_logout +Likewise, bash functions perfectly happily without a +@file{.bash_logout}, so none should be provided, since anything in it is +a deviation from the sensible default behaviour. + +@item .xsession +@file{/etc/skel} should not contain a @file{.xsession}. @file{xdm}'s +system-wide startup file @file{/usr/lib/X11/xdm/Xsession} supports a +system-wide default user configuration (which should probably be +@file{/etc/X11/Xsession} or some such) which may be overridden by +@file{.xsession} in the user's home directory. Therefore there is no +need for a @file{.xsession} to be installed by default and none should +be provided. + +Instead, a sensible @file{/etc/X11/Xsession} should be provided, and if +desired this can be used as a template by users who wish to install +their own configuration, or alternatively a more comprehensive example +with much commented-out interesting stuff could be put in +@file{/usr/doc/examples}. + +If the sysadmin wishes to change the system-wide default they should +probably do this by editing @file{/etc/X11/Xsession} rather than +creating the file in @file{/etc/skel}, because the former will affect +all user accounts that haven't explicitly overridden things by creating +their own file while the latter will only affect new accounts. + +All the configuration necessary for a program to function should be +provided. Therefore sysadmins will not need to go through +@file{/usr/doc/examples} while editing configuration files in +@file{/etc} except in extreme cases (like INN) where the configuration +was too difficult to do automatically. + +@item site-wide defaults +Site-wide defaults should not go in @file{/etc/skel}. In the case of +twm, for example, the system-wide default should be in +@file{/etc/X11/system.twmrc}. (The default location for this in X11R5, +btw, is in @file{/usr/lib/X11} somewhere, but we can't put it on +@file{/usr} because of CDROM distributions, etc - hence the FSSTND's +mandate to put configuration files in @file{/etc}.) + +@item .twmrc +There should be no @file{.twmrc} file in @file{/etc/skel}. You can have +one in @file{/usr/doc/examples} if you @emph{like}, but why bother if +@file{system.twmrc} is a good example (and indeed is the one the user is +using before they create their own)? + +@item m4 +@file{/usr/doc/examples} isn't mainly for example @emph{configuration +files}. It's for any kind of example file distributed with a package. +For example, GNU m4 comes with a whole pile of example m4 macro scripts, +which is exactly what @file{/usr/doc/examples} is for. +@end table + +Summary + +Files that should be installed in new user accounts should be in +@file{/etc/skel}, as that will ensure that they @emph{are} installed in +new user accounts! However, we should try to avoid the need for this. + +@file{/usr/doc/examples} is just what it says: documentation in the form +of examples. If a sysadmin is required to go and read these files for +their system to work they should be told about it. For example, here +is what the Smail postinst script says right at the start: + +@smallexample +I can do certain kinds of automatic configuration of your +mail system, by asking you a number of questions. Later you +may to confirm and/or correct your answers. In any case, +comprehensive information on configuring Smail is in +smail(5) and in /usr/doc/examples/smail and +/usr/doc/smail-admin-guide. +@end smallexample + +@node How to write the Description control file field, Configuration of init, configuration files -- /etc/skel vs /usr/doc/examples, Appendix +@unnumberedsec How to write the Description control file field + +The format of the @code{Description} field is as follows: + +@smallexample +Description: + +@end smallexample + +The extended description has several kinds of line: + +@itemize @bullet +@item +Those starting with a single space are part of a paragraph. Successive +lines of this form will be word-wrapped when displayed. The leading +space will usually be stripped off. + +@item +Those starting with two or more spaces. These will be displayed +verbatim. If the display cannot be panned horizontally the displaying +program will linewrap them `hard' (ie, without taking account of word +breaks). If it can they will be allowed to trail off to the right. +None, one or two initial spaces may be deleted, but the number of spaces +deleted from each line will be the same (so that you can have indenting +work correctly, for example). + +@item +Those containing a single space followed by a single full stop +character. These are rendered as blank lines. This is the @emph{only} +way to get a blank line - see below. + +@item +Those containing a space, a full stop and some more characters. These +are for future expansion. @emph{Do not} use them. +@end itemize + +IMPORTANT and not so important TIPS: + +@itemize @bullet +@item +@emph{Always} start extended description lines with at least @emph{one} +whitespace character. Fields in the control file and in the Packages +file are separated by field names starting in the first column, just as +in RFC822. Forgetting the whitespace will cause @file{dpkg-deb} +(>=0.93.23) to produce a syntax error when trying to build the package. +If you force it to build anyway @file{dpkg} will refuse to install the +resulting mess. + +@item +@emph{Do not} include any completely @emph{empty} lines. These separate +different records in the Packages file, and are forbidden in control +files. See the previous paragraph for what happens if you get this +wrong. + +@item +The single line synopsis should be kept brief - certainly under 80 +characters. @file{dselect} displays the @emph{first 49} characters if +you're using an 80-column terminal. + +@item +Do not include the package name in the synopsis line. The display +software knows how to display this already, and you do not need to +state it. Remember that in many situations the user may only see the +synopsis line - make it as informative as you can. + +@item +The extended description should describe what the package does and how +it relates to the rest of the system (in terms of, for example, which +subsystem it is which part of). + +@item +Put important information first, both in the synopis and extended +description. Sometimes only the first part of the synopsis or of the +description will be displayed. You can assume that there will usually +be a way to see the whole extended description. + +@item +You may include information about dependencies and so forth in the +extended description, if you wish. + +@item +Do not use tab characters. Their effect is not predictable. +@end itemize + +Example control file for Smail: + +@smallexample +Package: smail +Version: 3.1.29.1-13 +Maintainer: Ian Jackson +Recommends: pine | mailx | elm | emacs | mail-user-agent +Suggests: metamail +Depends: cron, libc5 +Conflicts: sendmail +Provides: mail-transport-agent +Description: Electronic mail transport system. + Smail is the recommended mail transport agent (MTA) for Debian. + . + An MTA is the innards of the mail system - it takes messages from + user-friendly mailer programs and arranges for them to be delivered + locally or passed on to other systems as required. + . + In order to make use of it you must have one or more user level + mailreader programs such as elm, pine, mailx or Emacs (which has Rmail + and VM as mailreaders) installed. If you wish to send messages other + than just to other users of your system you must also have appropriate + networking support, in the form of IP or UUCP. +@end smallexample + +@node Configuration of init, Maintainer script arguments and how @file{dpkg} does things, How to write the Description control file field, Appendix +@unnumberedsec Configuration of init + +The @file{/etc/init.d} directory contains the scripts executed by +init(8) when init state (or "runlevel") is changed. This includes the +boot process, when the multi-user state begins. Several of these +scripts are included with init and are intended to be executed +@emph{once}, usually at boot time. An example is +@file{/etc/init.d/boot}, which is executed at boot time to check and +mount file systems, activate swap, load kernel modules, etc.--everything +that needs to be done before the multi-user state begins. +@file{/etc/init.d} also contains the scripts that are executed when +entering runlevel 0 (halt), runlevel 1 (single-user) and runlevel 6 +(reboot). + +Packages can (and should) place scripts in @file{/etc/init.d} to start +or stop services at boot time or during a change of runlevel. These +scripts should be named @file{/etc/init.d/}, and they should +accept one of two arguments: "start", which starts the services, or +"stop", which stops the services. These scripts should ensure that they +will behave sensibly if invoked with "start" when the service is already +running, or with "stop2 when it isn't---the best way to achieve this is +often to use @file{start-stop-daemon}. + +This script should not fail obscurely when the configuration files +remain but the package has been removed, as the default in dpkg is to +leave configuration files on the system after the package has been +removed. Only when it is executed with the `--purge' option will dpkg +remove configuration files. Therefore, you should include a `test' +statement at the top of the script, like this: + +@smallexample +test -f || exit 0 +@end smallexample + +These scripts should be referenced, when appropriate, by symbolic links +in the @file{/etc/rc?.d} directories, as below. + +When changing runlevels, init looks in the directory @file{/etc/rc.d} +for the scripts it should execute, where is the runlevel that is +being changed to. Please note that the "scripts" in @file{/etc/rc?.d} +are not actually scripts; they are symbolic links, referencing actual +scripts in @file{/etc/init.d}. For simplicity, we refer to them as +"scripts". + +First, the scripts prefixed with a "K" are executed, followed by the +scripts prefixed with an "S". The "K" scripts are responsible for +killing certain services and the "S" scripts for starting certain +services upon @emph{entering} the runlevel. For example, if we are +changing from runlevel 2 to runlevel 3, init will first execute all of +the "K" prefixed scripts it finds in @file{/etc/rc3.d} (to kill +services), and then all of the "S" prefixed scripts it finds in +@file{/etc/rc3.d} (to start services). The "K" scripts will execute the +file it references with an argument of "stop", and the "S" scripts will +execute this file with an argument of "start". + +After the "K" or "S" prefix, there should be a number specified, and +this number should be between 00 and 99. The number determines the +order in which the scripts are run. For example, the "K20" scripts will +be executed before the "K30" scripts. You can use this number to make +sure that a certain service is started before another. For example, on +some machines, the program @file{setserial} may need to properly set an +IRQ before the @file{ppp} program uses a modem to connect to a network. +In this case, the script that runs @file{setserial} should have a lower +number than the script that starts @file{ppp} so that it runs first: + +@smallexample +@file{/etc/rc2.d/S10setserial} +@file{/etc/rc2.d/S20ppp} +@end smallexample + +If it does not matter when or in which order the script is run, use the +number "20". If it does, then you should talk to the maintainer of the +@code{sysvinit} package or post to @code{debian-devel}, and they will +help you choose a number. + +In Debian GNU/Linux, we try to ship our software in as much of a +"default" state as possible. Therefore, unless there is a good reason +for doing differently, we ask that you start and stop the services in +each of the multi-user state runlevels (2, 3, 4, and 5). If a service +needs to be stopped before a file system can be unmounted (an example is +process accounting or quota services), then be sure to stop them in the +halt runlevel (0), the single-user runlevel (1) and the reboot runlevel +(6). + +The system administrator will have the opportunity to customize +runlevels by simply adding, moving, or removing the symbolic links in +@file{/etc/rc?.d}. This is why we default to running everything in the +multi-user state--a reasonable default--and the administrator can easily +customize init to be as complex and sophisticated as he or she wants it +to be beyond this. + +We provide a script, @file{update-rc.d}, to make it easier for package +maintainers to arrange for the proper creation and removal of +@file{/etc/rc?.d} symbolic links from their postinst and postrm scripts. +You should use this script to make changes to @file{/etc/rc?.d} and +@emph{never} include any @file{/etc/rc.?.d} symbolic links in the actual +archive. + +@itemize @bullet +@item +In the postinst script, you need only do the following to setup +@file{/etc/rc?.d}. You should redirect standard output to +@file{/dev/null}, as @file{update-rc.d} produces insignificant output: + +@smallexample +update-rc.d default >/dev/null +@end smallexample + +where is the name of the file as it appears in +@file{/etc/init.d}. It will use the default number of "20", as +mentioned above. If you need to use a different number, you can specify +it after "default": + +@smallexample +update-rc.d default 30 >/dev/null +@end smallexample + +@item +In the postrm script, you need only do the following @emph{if and only +if} it is called with the `purge' argument: + +@smallexample +if [ purge = "$1" ] +then + update-rc.d remove >/dev/null +fi +@end smallexample +@end itemize + +@unnumberedsubsec Important Note: + +@emph{Do not} include the @file{/etc/rc?.d/*} symbolic links in the +archive! @emph{This will cause problems!} You should create them with +update-rc.d, as above. + +@emph{Do not} include the @file{/etc/rc?.d/*} symbolic links in +conffiles! @emph{This will cause problems!} @emph{Do}, however, +include the @file{/etc/init.d} scripts in conffiles. + + +@unnumberedsubsec Example: + +The process accounting package wants to make sure that process +accounting is started at boot time and that it is stopped before the +system is halted, enters the single-user state, or is rebooted (so +that the @file{/var} file system can be properly unmounted). It puts +a script that does this in @file{/etc/init.d}, naming the script +appropriately "acct". This script accepts one of two arguments: +either "start", which starts process accounting, or "stop", which +stops it. To ensure that it does not fail obscurely when the +configuration files remain but the package has been removed, we +include a `test' statement at the top of the script: + +@smallexample +#! /bin/sh +# +# Start process accounting. +. /etc/init.d/functions +test -f /usr/sbin/accton || exit 0 +case "$1" in + start) + echo "Starting process accounting" + /usr/sbin/accton /var/account/pacct + ;; + stop) + echo "Stopping process accounting" + /usr/sbin/accton + ;; + *) + echo "Usage: /etc/init.d/acct @{start|stop@}" + exit 1 +esac +exit 0 +@end smallexample + +You may find a skeletal script from which to base your @file{/etc/init.d} +scripts in @file{/etc/init.d/skeleton}. + +We want to stop then (re)start process accounting when entering a +multi-user state--runlevels 2, 3, 4, and 5--and we want to stop it when +leaving such a state--runlevels 0 (halt), 1 (single) and 6 (reboot). +These are good defaults, and we accomplish this by including the +following in the postinst: + +@smallexample +update-rc.d acct default >/dev/null +@end smallexample + +When the user removes the acct packages with the `--purge' option, we +want to make sure the @file{/etc/rc?.d} symbolic links are properly +removed, so we include the following in the postrm: + +@smallexample +update-rc.d acct remove >/dev/null +@end smallexample + +Otherwise, the @file{/etc/rc?.d} symbolic links will remain on the system +along with @file{/etc/init.d/acct} script. + +@node Maintainer script arguments and how @file{dpkg} does things, Mail processing packages, Configuration of init, Appendix +@unnumberedsec Maintainer script arguments and how @file{dpkg} does things + +This appendix describes exactly how maintainer scripts are called, with +what arguments, in what order, and what @file{dpkg} does in between. + +In all cases version numbers are -, if the package +has both, or just . @code{upgrade} is used even when the new +version number looks lower than the old. + + +@unnumberedsubsec Summary + +@smallexample + install + install + upgrade + abort-upgrade + + configure + abort-upgrade + abort-remove in-favour + abort-deconfigure \ + in-favour + removing + + remove + upgrade + failed-upgrade + remove in-favour + deconfigure \ + in-favour \ + removing + + remove + purge + upgrade + failed-upgrade + abort-install + abort-install + abort-upgrade + disappear +@end smallexample + + +@unnumberedsubsec Details of unpack phase of installation or upgrade + +The procedure on installation/upgrade/overwrite/disappear (ie, when +running @code{dpkg --unpack}, or the unpack stage of @code{dpkg +--install}) is as follows. In each case if an error occurs the actions +in are general run backwards - this means that the maintainer scripts +are run with different arguments in reverse order. These are the `error +unwind' calls listed below. + +@enumerate +@item +@noindent @enumerate a +@item +If a version the package is already +installed, call +@smallexample + upgrade +@end smallexample +@item +If this gives an error (ie, a non-zero exit status), dpkg will +attempt instead: +@smallexample + failed-upgrade +@end smallexample +@noindent error unwind, for both the above cases: +@smallexample + abort-upgrade +@end smallexample +@end enumerate +@item +If a `conflicting' package is being removed at the same time: +@noindent @enumerate a +@item +If any packages depended on that conflicting package and +@code{--auto-deconfigure} is specified, call, for each such package: +@smallexample + deconfigure \ + in-favour \ + removing +@end smallexample +@noindent error unwind: +@smallexample + abort-deconfigure \ + in-favour + removing +@end smallexample +The deconfigured packages are marked as requiring configuration, so +that if --install is used they will be configured again if possible. +@item +To prepare for removal of the conflicting package, call: +@smallexample + remove in-favour +@end smallexample +@noindent error unwind: +@smallexample + abort-remove in-favour +@end smallexample +@end enumerate +@item +@noindent @enumerate a +@item +If the package is being upgraded, call +@smallexample + upgrade +@end smallexample +@item +otherwise, if the package had some configuration files from a previous +version installed (ie, it is in the conffiles-only state): +@smallexample + install +@end smallexample +@item +otherwise (ie, the package was completely purged): +@smallexample + install +@end smallexample +@noindent error unwind versions, respectively: +@smallexample + abort-upgrade + abort-install + abort-install +@end smallexample +@end enumerate +@item +The new package's files are unpacked, overwriting any that may be on the +system already, for example any from the old package or from another +package (backups of the old files are left around, and if anything goes +wrong dpkg will attempt to put them back as part of the error unwind). +@item +@noindent @enumerate a +@item +If the package is being upgraded, call +@smallexample + upgrade +@end smallexample +@item +If this fails, dpkg will attempt: +@smallexample + failed-upgrade +@end smallexample +@noindent error unwind, for both cases: +@smallexample + abort-upgrade +@end smallexample +@end enumerate +This is the point of no return - if dpkg gets this far, it won't back +off past this point if an error occurs. This will leave the package in +a fairly bad state, which will require a successful reinstallation to +clear up, but it's when dpkg starts doing things that are irreversible. +@item +Any files which were in the old version of the package but not in the +new are removed. +@item +The new file list replaces the old. +@item +The new maintainer scripts replace the old. +@item +Any packages all of whose files have been overwritten during the +installation, and which aren't required for dependencies, are considered +to have been removed. For each such package, +@noindent @enumerate a +@item +dpkg calls: +@smallexample + disappear +@end smallexample +@item +The package's maintainer scripts are removed. +@item +It is noted in the status database as being in a sane state, namely not +installed (any conffiles it may have are ignored). Note that +disappearing packages do not have their prerm called, because dpkg +doesn't know in advance that the package is going to vanish. +@end enumerate +@item +Any files in the package we're unpacking that are also listed in the +file lists of other packages are removed from those lists. (This will +lobotomise the file list of the `conflicting' package if there is one.) +@item +The backup files made at 4. are deleted. +@item +The new package's status is now sane, and recorded as `unpacked'. Here +is another point of no return - if the conflicting package's removal +fails we do not unwind the rest of the installation; the conflicting +package is left in a half-removed limbo. +@item +If there was a conflicting package we go and do the removal actions, +starting from point 2. of the removal, below. +@end enumerate + + +@unnumberedsubsec Details of configuration + +When we configure a package (this happens with @code{dpkg --install}, or with +@code{--configure}), we first update the conffiles and then call: +@smallexample + configure +@end smallexample + +No attempt is made to unwind after errors during configuration. + + +@unnumberedsubsec Details of removal and/or configration purging + +@enumerate +@item +@smallexample + remove +@end smallexample +@item +The package's files are removed (except conffiles). +@item +@smallexample + remove +@end smallexample +@item +All the maintainer scripts except the postrm are removed. + +If we aren't purging the package we stop here. Note that packages which +have no postrm and no conffiles are automatically purged when removed, +as there is no difference except for the dpkg status. + +@item +The conffiles and any backup files (@samp{~}-files, @samp{#*#} files, +@samp{%}-files, .dpkg-@{old,new,tmp@}, etc.) are removed. +@item +@smallexample + purge +@end smallexample +@item +The package's file list is removed. +@end enumerate +No attempt is made to unwind after errors during removal. + +@node Mail processing packages, Virtual dependencies, Maintainer script arguments and how @file{dpkg} does things, Appendix +@unnumberedsec Mail processing packages + +Debian packages which process electronic mail (whether mail-user-agents +(MUA) or alternative mail-transport-agents (MTA)) @emph{must} make sure +that they are compatible with the configuration decisions below. +Failure to do this may result in lost mail, broken @code{From:} lines, +and other serious brain damage! + +@itemize @bullet +@item +The mail spool is @file{/var/spool/mail} and the interface to send a +mail message is @file{/usr/sbin/sendmail} (as per the FSSTND). The mail +spool is part of the base and not part of the MTA package. + +@item +Mailboxes are locked using the @file{.lock} lockfile convention, rather +than fcntl, flock or lockf. + +@item +Mailboxes are generally 660 @file{.mail} unless the user has +chosen otherwise. A MUA may remove a mailbox (unless it has nonstandard +permissions) in which case the MTA or another MUA must recreate it if +needed. Mailboxes must be writeable by group mail. + +@item +The mail spool is 2775 mail.mail, and MUA's need to be setgid mail to do +the locking mentioned above (and obviously need to avoid accessing other +users' mailboxes using this privilege). + +@item +@file{/etc/aliases} is the source file for the system mail aliases (e.g. +postmaster, usenet, etc.) - it is the one which the sysadmin and +postinst scripts may edit. + +@item +The convention of writing `forward to
' in the mailbox itself +is not supported. Use a @file{.forward} file instead. + +@item +The location for the @file{rmail} program used by UUCP for incoming mail +is @file{/usr/sbin/rmail}, as per the FSSTND. Likewise, @file{rsmtp}, +for receiving batch-SMTP-over-UUCP, is in @file{/usr/sbin/rsmtp} if it +is supported. + +@item +Smail is not using HoneyDanBer UUCP, whose uux apparently accepts -a and +-g options. + +@item +If you need to know what name to use (for example) on outgoing news and +mail messages which are generated locally, you should use the file +@file{/etc/mailname}. It will contain the portion after the username +and @samp{@@} sign for email addresses of users on the machine (followed +by a newline). +@end itemize + +A package should check for the existence of this file. If it exists it +should use it without comment @footnote{An MTA's prompting configuration +script may wish to prompt the user even if it finds this file exists.}. +If it does not exist it should prompt the user for the value and store +it in @file{/etc/mailname} as well as using it in the package's +configuration. The prompt should make it clear that the name will not +just be used by that package. E.g., in the same situation the INN +package says: + +@smallexample +Please enter the `mail name' of your system. This is the hostname +portion of the address to be shown on outgoing news and mail messages. +The default is `$syshostname', your system's host name. +Mail name [`$syshostname']: +@end smallexample +($syshostname is the output of `hostname --fqdn'). + +@node Virtual dependencies, , Mail processing packages, Appendix +@comment node-name, next, previous, up +@unnumberedsec Virtual dependencies + +Virtual packages are in the same namespace as real packages, and may +have the same name. The meaning of a virtual package in a +dependency/conflicts list is exactly that of listing all the real +packages which state that they are an instantiation of that virtual +package. + +This is done with a new Provides field in the control file, with a +syntax much like the Conflicts field. + +The idea is that we can have something like: +@smallexample +Package: elm +Depends: mta + +Package: smail +Provides: mta +Conflicts: mta + +Package: sendmail +Provides: mta +Conflicts: mta +@end smallexample +@noindent The result is equivalent to elm having said +@smallexample +Package: elm +Depends: smail | sendmail +@end smallexample + +(There'll be a special case to say that a package may conflict with a +virtual package which it provides - clearly ...) + +If there are both a real and a virtual package of the same name then +the dependency may be satisfied (or the conflict caused) by either the +real package or any of the virtual packages which provide it. This is +so that, for example, supposing we have +@smallexample +Package: lout +Optional: ghostview +@end smallexample +(this is a fictional example - the Lout package should not mention +ghostview), and someone else comes up with a nice PostScript +previewer, then they can just say +@smallexample +Package: marvelpostview +Provides: ghostview +@end smallexample +and all will work in the interim (until, say, the Lout maintainer +changes things). + +If a dependency or a conflict has a version number attached then only +real packages will be considered to see whether the relationship is +satisfied (or prohibited, for a conflict) - it is assumed that a real +package which provides virtual package is not of the `right' version. +If there is demand it can be arranged that a package which provides a +virtual package may mention a version number, though this is unlikely to +be helpful: +@smallexample +Provides: mta (2.0) +@end smallexample + +If you want to specify which of a set of real packages should be the +default to satisfy a particular dependency on a virtual package, you can +simply list the real package as alternative before the virtual one: +@smallexample +Package: xbaseR6 +Recommended: xsvga | x-server +Provides: x-base, xr6shlib + +Package: xsvga +Recommended: x-base +Provides: x-server + +Package: x8514 +Recommended: x-base +Provides: x-server +@end smallexample + +Virtual package names should generally not be used in the names of +@file{/etc/init.d} scripts, configuration files, logfiles, and so on, so +that several programs providing the same virtual package name can be +installed. + +@bye diff --git a/doc/guidelines.texi.beforeeric b/doc/guidelines.texi.beforeeric new file mode 100644 index 00000000..fb2f2953 --- /dev/null +++ b/doc/guidelines.texi.beforeeric @@ -0,0 +1,1056 @@ +\input texinfo @c -*-texinfo-*- +@setfilename guidelines.info + +@set DATE 26th January 1996 + +@setchapternewpage off + +@iftex +@center @titlefont{Debian GNU/Linux Packaging Guidelines} +@tex +\vskip2pt \hrule height 2pt width \hsize \vskip2pt +@end tex +@sp 0.5 +@center @value{DATE} +@end iftex + +@ifinfo +@format +START-INFO-DIR-ENTRY +* debian-guidelines: (debian-guidelines). How to make Debian packages. +END-INFO-DIR-ENTRY +@end format +@end ifinfo + +@node Top, Additional Information, (dir), (dir) + +@ifinfo +@top Debian GNU/Linux Packaging Guidelines +@end ifinfo + + This file documents the steps that must be taken in the preparation +of a Debian GNU/Linux package. All submissions to be included in the +distribution proper and all packages to be considered for @file{contrib} +or @file{non-free} availability @emph{must} conform to the guidelines +and standards described in this document or they cannot be included or +made available at the archive with the distribution. + + Please read the Guidelines carefully. If you have comments or +questions, please contact @code{debian-devel@@pixar.com}. If you are +planning on going further than just contributing a package (i.e., if +you plan to maintain it for an extended period of time or if you are +generally interested in becoming more involved in the Project), you +should join the @code{debian-devel} mailing list. For more details, +read @file{info/mailing-lists.txt}, available at any Debian GNU/Linux +archive. + + (This file was last updated on @value{DATE}. Please check the most +recent @file{dpkg} package at any Debian GNU/Linux archive for a +potentially more up to date copy.) + +@menu +* Additional Information:: +* Package Copyright:: A few words about the importance of + understanding the package copyright. +* Package Content:: Requirements for the package content. +* Source Package:: Creating the source package. +* Binary Package:: Creating the binary package. +* Control Files:: The binary package control files. +@end menu + + + +@node Additional Information, Package Copyright, Top, Top +@unnumbered Additional Information + + These Guidelines are intended to be fairly general. More specific +information is available about certain aspects of building packages, +such as how to use the features of Init under Debian GNU/Linux. This +information can be found in the directory @file{project/standards} +at any Debian GNU/Linux archive. At the time of this writing, the +following documents are available: + +@table @file +@item README.etc-skel +A description of @file{/etc/skel} and @file{/usr/doc/examples}. +@item descriptions.txt +How to write an extended (and more useful) @file{DESCRIPTION} field. +@item README.init +How to use the features of Init under Debian GNU/Linux in packages. +@item mailers.txt +How to properly configure packages to use the Debian GNU/Linux mail +system. +@item maintainer-script-args.txt +All the ways that the package maintainer scripts inside a package can +be called by dpkg. +@item dpkg-upgrades+errors.txt +What order things happen in during a package upgrade. +@item virtual-dependencies.txt +How to use ``virtual dependencies'' in packages. +@item virtual-package-names-list.text +The list of virtual package names currently in use, together with the +procedure for getting new virtual package names allocated. +@item dependency-ordering.txt +How to properly order package names in the @file{DEPENDS} field. +@end table + + There are a number of documents that describe more technical +details of dpkg's operation, that will probably only be of minority +interest. Please read them if you're doing anything complicated. + +@table @file +@item auto-deconfiguration.txt +How dpkg can sometimes automatically deconfigure packages in order to +do bulk installations smoothly. +@item dpkg-essential-flag.txt +How to tell dpkg a package is essential and should not be removed. +(This is for the use of base system packages only.) +@item dpkg-disappear-replace.txt +What happens when a package appears to have been completely replaced. +@end table + + In the future, we hope also to make available: + +@table @file +@item copyright.txt +How to choose a good copyright notice to attach to new programs. +@item version-ordering.txt +The algorithm with which packages' version numbers are compared. +@end table + + Also, you should download the sample files and the sample package +(GNU Hello) available in @file{standards/samples}. You may use any +of this material as a starting point for new packages. The following +sample files, incidentally, are available: + +@itemize @bullet +@item debian.README +@item debian.control +@item debian.postinst +@item debian.postrm +@item debian.rules +@end itemize + + + +@node Package Copyright, Package Content, Additional Information, Top +@unnumbered Package Copyright + + Please study the copyright of your submission @emph{carefully} +and @emph{understand it} before proceeding! If you have doubts or +questions, please ask! + + In order to understand how we classify and distribute certain +packages, it is important to understand the distinction between being +freely available and being freely redistributable. + + Being @dfn{freely available}, quite simply, means that the software +can be made available freely, at least for non-commercial purposes and +in its original, unmodified form. This includes packages made available +freely that have restrictions on non-commercial use, redistribution of +modifications, etc. Being freely available, therefore, has nothing to +do with being able to modify and redistribute the software. It only +means that you can get a copy of the software without having to pay +(and it does not necessarily mean that you can @emph{use} the software +without having to pay---shareware is an example of freely available +software). + + @dfn{freely redistributable}, while generally being freely available, +goes beyond just being freely available. Freely redistributable means +that that the software, in addition to being able to be made available +freely, must be able to be freely modified and redistributed without +restriction. + + All submissions to be included in the distribution proper @emph{must} +be freely redistributable. + + In addition to the distribution, the Project maintains two separate +archives of software packages with the distribution: the @file{contrib} +archive and the @file{non-free} archive. + + @file{contrib} is an archive of user-contributed packages that are +not maintained by the Project, packages that were once maintained by the +Project but that are no longer actively maintained, and packages that +are maintained by the Project but that are not yet considered ready for +inclusion in the distribution proper (i.e., ALPHA and BETA packages). +As above, all submissions for inclusion in the @file{contrib} archive +@emph{must} be freely redistributable. + + @file{non-free} is an archive of packages with either restrictive or +unclear terms of copying or modification. If a package has @emph{any} +restrictions on modification or redistribution, it can not be included +in the distribution or @file{contrib} archive. It can only be included +in the @file{non-free} archive, and then only if it is freely available. + + In summary, in order to be included in the distribution proper or the +@file{contrib} archive, a package must be @emph{freely redistributable}. +Anyone must be able to make copies of it, modify it, redistribute it with +their modifications in place, include it on a CD-ROM, or generally sell +it. To be included in the @file{non-free} archive, a package may have +restrictions, as long as the package remains @emph{freely available}. We +must be available to make it available freely at the archive, and anyone +must be able to make copies of it and use it for at least non-commercial, +personal purposes. Software that will typically be included in +@file{non-free} are software that does not allow commercial distribution, +software that does not allow modification or redistribution of +modifications, commercial ``demos'', and ``shareware''. + + When in doubt, send mail to @file{iwj10@@cus.cam.ac.uk} and +@file{imurdock@@debian.org}. Be prepared to provide us with the +copyright statement. Software covered by the GPL, public domain +software and BSD-like copyrights are safe; be wary of the phrases +``commercial use prohibited'' and ``distribution restricted''. + + Every package submission @emph{must} be accompanied by verbatim copy +of its copyright (with the exceptions of public domain packages and +those covered by the UCB BSD licence or the GNU GPL or LGPL; in these +cases simply indicate which is appropriate). This information must be +included in a file installed to the directory @file{/usr/doc/copyright}. +See below for details. + + + +@node Package Content, Source Package, Package Copyright, Top +@unnumbered Package Content + + The following requirements apply equally to both the binary and +source packages. In either case, when files have been installed, +they must conform to the requirements described in this section. + + The primary rule in Debian GNU/Linux is to follow the Linux @dfn{File +System Standard} (@dfn{FSSTND}). The location of installed files +@emph{must} comply @emph{fully} with the FSSTND. The latest version of +this document can be found alongside the Guidelines or at +@file{tsx-11.mit.edu} in @file{/pub/linux/docs/linux-standards/fsstnd}. +Specific questions about following the standard should be addressed to +Daniel Quinlan, the FSSTND coordinator, at @code{quinlan@@yggdrasil.com}. + + In addition to the FSSTND, all Debian GNU/Linux packages must follow +the guidelines below. + +@itemize @bullet +@item +Directories should be mode 755 or (for group-writability) mode 2775, +with the exception of special ``system'' directories that need to be +another mode. The ownership of the directory should be consistent with +its mode---if a directory is mode 2775, it should be owned by the group +that needs write access to it, of course. Use common sense in assigning +permissions and ownerships to directories, and make sure that what is +done is secure if it is ``non-standard''. + +@item +Normal binaries should be mode 755 and owned by @code{root.root}. If +there is a good reason to use a different mode or ownership, you may do +so, but you must try to be as consistent as possible with the rest of +the system. If you need to use a different mode or ownership, please +discuss it with @code{imurdock@@debian.org}. + +@item +Setuid binaries should normally be mode 4755 (not 4711!) and, of course, +owned by the appropriate user. + +@item +Setgid binaries should normally be mode 2755 (not 2711!) and, of course, +owned by the appropriate group. + +@item +Library files should generally be mode 644 and owned by +@code{root.root}. If the package requires different permissions +or ownerships to function correctly, they should be used instead. + +@item +Manual pages should be mode 644 and owned by @code{root.root}. The +@file{nroff} source must be installed. You should @emph{not} install +a preformatted ``cat page'', and you should only use sections 1 to +9---see the FSSTND for more details. + +@item +Info documents should be mode 644, owned by @code{root.root}, and +compressed with @file{gzip -9} when installed. The package must call +@file{install-info} to update the Info @file{dir} file. This should +be done in the post-installation script (@file{postinst}), like this: + +@smallexample + install-info --quiet /usr/info/foobar.info +@end smallexample + +The entries should be removed by the pre-removal script (@file{prerm}), +like this: + +@example + install-info --quiet --remove /usr/info/foobar.info +@end example + +It is also a good idea to specify a section for the Info @file{dir} +entry. This is done with the @file{--section} switch. To determine +which section to use, you should use look at @file{/usr/info/dir} on +your system and choose the most relevant (or create a new section if +none of the current sections are relevant). + +If @file{install-info} cannot find a description entry in the Info file +you will have to supply one. See @file{install-info}(8) for details. + +@item +If a package contains any shared libraries you will have to invoke +@file{ldconfig} in both the @file{postinst} and @file{prerm} scripts +to correctly update the library links. See @file{ldconfig}(8) for +details. + +@item +Any additional documentation that comes with the package can be +installed at the discretion of the package maintainer. Text +documentation should be mode 644, owned by @code{root.root}, installed +to @file{/usr/doc}, and compressed with @file{gzip -9} unless it is small. + +If a subdirectory of @file{/usr/doc} is warranted, please do create one. +Please do not install DVI, PostScript, or large textual documentation to +@file{/usr/doc}. However, please do upload such documentation as a +separate package so that it can be made available with the distribution. +If a user has the need for the documentation, they can easily get it +from the archive, CD-ROM, etc., but it should not take up disk space +on the machines of the user who do not need or want it installed. + +@item +Create a file named @file{/usr/doc/copyright/<@i{package}>} which gives +details of the authorship and copyright of the package. If the package +is distributed under the GNU General Public Licence, the GNU Library +General Public Licence or the Regents of the University of California at +Berkeley (BSD) licence, please say so instead of including a copy of the +licence. The files @file{BSD}, @file{GPL}, and @file{LGPL} will be +available in the @file{/usr/doc/copyright} directory for you to refer +to. @file{/usr/doc/copyright/<@i{package}>} should not be compressed. + +@emph{All} authorship and copyright information from the original source +package must be included in the @file{/usr/doc/copyright/<@i{package}>} +file. + +@item +Any example files (for example, sample configuration files) should +be placed in the directory @file{/usr/doc/examples}. If the file is +normally a hidden file, such as @file{.emacs}, then please call it +@file{dot.emacs}, to avoid confusion. Again, you may create a +subdirectory if it is needed. + +@item +All symbolic links should be relative, not absolute. Absolute links, +in general, cause problems when a file system is not mounted where it +``normally'' resides (for example, when mounted via NFS). In certain +cases, however, relative links may also cause similar problems. I +have generally made links into @file{/etc} and @file{/var} absolute +and all other links relative. There may be other cases in which +absolute links are necessary. + +Therefore, in the Makefile, do not do (even though it is easier): +@smallexample + install: all + [...] + ln -fs /usr/bin/gcc /usr/bin/cc + [...] +@end smallexample +Instead, do: +@smallexample + ln -fs gcc /usr/bin/cc +@end smallexample + or +@smallexample + ( cd /usr/bin ; ln -fs gcc cc ) +@end smallexample + +Please do not create hard links in the manual page directories. In +these cases, you should use relative symbolic links or files that +@file{.so} (roff for `source') others instead. + +@item +All command scripts should have a @code{#!} line naming the shell to be +used to interpret them. + +@item +In the case of Perl scripts this should be @code{#!/usr/bin/perl} or +sometimes @code{#!/bin/perl}, as follows: if the script is a critical +one that may be called when the @file{/usr} partition is unmounted or +broken it should use @file{/bin/perl}. Otherwise (especially if the +script is not specifically targetted at Debian) it should use Perl's +standard location, @file{/usr/bin/perl}. + +@item +Generally the following compilation parameters should be used: + +@display + CC = gcc + CFLAGS = -O2 -g -Wall # sane warning options vary between programs + LDFLAGS = # none (or -N, if appropriate; see below) + install -s (or strip) +@end display + +Note that all installed binaries should be stripped, either by using the +@code{-s} flag to @file{install}, or by calling @file{strip} on the +binaries after they have been copied into the @file{debian-tmp} but +before the tree is made into a package. + +Make sure that you do not link with @code{-g}, as this makes a.out +compilers produce huge statically linked binaries. The @code{-g} flag +is useful on compilation so that you have available a full set of +debugging symbols in your built source tree, in case anyone should file +a bug report involving (for example) a core dump. + +@code{-N} should only be used on binaries that are very small (less than +8K with the @code{-N} option, roughly) and are not likely to have +multiple instances in memory. Do not use @code{-N} on daemons, no +matter how small they are. + +It is up to the package maintainer to decide what compilation options +are best for the package. Certain binaries (such as +computationally-intensive programs) may function better with certain +flags (@code{-O3}, for example); feel free to use them. Please use good +judgment here. Don't add flags for the sake of adding flags; only add +flags if there is good reason to do so. + +@item +Please check with the base system maintainer (Ian Murdock) before using +users or groups other than @code{root} and others specified in this +document. +@end itemize + + + +@node Source Package, Binary Package, Package Content, Top +@unnumbered Source Package + + The source package should contain a file called @file{debian.rules} +which contains at least the following targets, to be invoked in the top +level directory: + +@smallexample +build +binary +clean +@end smallexample + +@file{debian.rules} should start with + +@example + #!/usr/bin/make -f +@end example + +@noindent and be executable. It is a good idea to arrange for it not +to fail obscurely when invoked in the wrong directory, for example by +testing for the existence of a file in the source directory. + +@itemize @bullet +@item +The @file{build} target should perform all non-interactive configuration +and compilation of the package. If a package has an interactive +pre-build configuration routine, the source package should be built +@emph{after} this has taken place. + + For some packages, notably ones where the same source tree is +compiled in different ways to produce two binary packages, the +@file{build} target does not make much sense. For these packages it is +good enough to provide two (or more) targets (@file{build-a} and +@file{build-b} or whatever) for each of the ways of building the +package, and a @file{build} target that does nothing. The @file{binary} +target will have to build the package in each of the possible ways and +make the binary package out of each. + +@item +The @file{binary} target of @file{debian.rules} should be all that is +necessary for the user to build the binary package. The binary package +should be created using @file{dpkg} and placed in the parent of the top +level directory. The next section describes how to construct binary +packages from the @file{binary} target. + +@item +The @file{clean} target should undo the effects of the @file{build} +target and the @file{binary} target, except that it should leave alone +any @file{../<@i{package}>-<@i{version}>.deb} file created by a run of +@file{binary}. + +@item +Additional targets may exist in @file{debian.rules}. We recommend using +@file{source} and @file{diff} targets to build the Debianised source +package and the Debianisation context diff, respectively. These files +should be placed in @file{../foo-<@i{version}>.tar.gz} and +@file{../foo-<@i{version}>.diff.gz}. The @file{install} target, for +installing into a running system direct from the Debianised source +tree, is no longer required. The sample @file{debian.rules} provides +@file{source} and @file{diff} targets that should work with little or +no alteration, providing that the package-specific variables at the top +of the script have been properly defined. + +@item +If you need to edit a @file{Makefile} where @file{configure} scripts +are used, you should edit the @file{.in} files rather than editing +the @file{Makefile} directly. This allows the user to reconfigure +the package if necessary. You should @emph{not} configure the package +and edit the generated @file{Makefile}! This makes it impossible for +someone else to later reconfigure the package. + +@item +Please document your changes to the source package so that future +package maintainers know what has been changed. To do this, include +a description of your changes in the @file{debian.README} (which, as +described above, should already contain authorship and copyright +information!) and include relevant information such as your name, +electronic mail address, date, etc. + +@item +If changes to the source code are made, please use a @file{define}. If +they are changes required to compile or function under Linux in general, +use @file{LINUX}. If it is a cosmetic or functional change, use +@file{DEBIAN}. + +@item +Create the source package using @file{tar}, and use @file{gzip -9} to +compress it. Source packages should be named in the form +<@i{package}>-<@i{version}>.tar.gz---for example, +@file{fileutils-3.9-3.tar.gz}. + +NB, here @code{<@i{version}>} is the full Debian version number, in the +form @code{<@i{original_version}>-<@i{debian_revision}>} (see below), +but the tarfile should unpack into a directory named +@code{<@i{package}>-<@i{original_version}>} (again, see the section +below on version numbering). + +@item +Create the context diff against the original package using @file{diff +-cNr}, and use @file{gzip -9} to compress it. Context diffs should be +named in the form <@i{package}>-<@i{version}>.diff.gz---for example, +@file{fileutils-3.9-3.diff.gz}. +@end itemize + + Please note that the package and patch filenames do @emph{not} need +to fit in MS-DOS 8+3. They will be made available under an alternative +8+3 name in the archive by the archive maintainer, using a symlink. + + + +@node Binary Package, Control Files, Source Package, Top +@unnumbered Binary Package + + The @file{binary} target of the source package @file{debian.rules} +file should do the following (see the sample @file{debian.rules} +for an implementation that you are free to modify and use in your own +packages, of course): + +@itemize @bullet +@item +Create an empty directory in the top-level directory of the source +package (deleting it first, if necessary), and install the files +belonging to this package in that directory. For example, the directory +could be called @file{debian-tmp} and would probably contain directories +@file{debian-tmp/usr/bin}, @file{debian-tmp/usr/lib}, etc. +(@file{debian-tmp} is the name traditionally used, and it is used in +the sample @file{debian.rules} file, so we will use that name in the +Guidelines.) + +@item +Make sure that all the files under @file{debian-tmp} have the correct +ownerships and permissions (@pxref{Package Content}, for more information +about file locations, ownerships, and permissions.) + +@item +Create a subdirectory of @file{debian-tmp} called @file{DEBIAN}. This +directory contains the package control information, including at the +very least the master information file named @file{control}. The next +section describes the semantics and syntax of the files required and +allowed here. + +@item +Run @file{dpkg} to create the binary package, using something like + +@smallexample + dpkg --build debian-tmp +@end smallexample + +This will create a file called @file{debian-tmp.deb}, from the +@file{debian-tmp} directory. You should rename this file to +@file{../<@i{package}>-<@i{version}>.deb} after it is built. + +After the @file{binary} target has done all this, the +@file{<@i{package}>-<@i{version}>.deb} file in the parent directory is +the binary distribution. This file may be distributed and installed on +any Debian GNU/Linux system with @file{dpkg} in the same manner and +using the same methods as all packages are installed to the system. + +@item +If a single source package corresponds to several binary packages, there +should usually be a @file{debian.rules} file with a single @file{binary} +target that builds all the binary packages involved and move all packages +to the parent directory of that containing the source package. + +In this case, you should choose binary package names which are meant to +make clear the close relationship between the binary packages and which +source package the binary packages came from (for example, the +@file{texinfo} source package build two binary packages: @file{texidoc} +and @file{texinfo}). You should place the appropriate binary package +name in the @file{Package} field of the control file (not the source +package name), and you should consider whether the other binary packages +that come from the same source tree should be mentioned in the +@file{Depends}, @file{Recommends} or @file{Suggests} fields. You +should put the source package name in the @file{Source} field. + +You should retain the source package version numbering in the +@file{Version} field---the version number should be the same for the +Debianised source tree and all the binary packages generated from it. +See below for details of version numbers. +@end itemize + + + +@node Control Files, , Binary Package, Top +@unnumbered Control Files + + Each binary package contains, in addition to the files that comprise +the actual package, a set of text files that control how @file{dpkg} +installs, configures, upgrades, removes, etc. the package. These files +are called @dfn{control files}. When creating the package, the control +files should placed in a directory called @file{DEBIAN}, as described +earlier (@pxref{Binary Package}, for further information). + +The control information files are: + +@table @code +@item control +The master package control information file. +@item conffiles +A list of package configuration files. +@item preinst +The package pre-installation script. +@item postinst +The package post-installation script. +@item prerm +The package pre-removal script. +@item postrm +The package post-removal script. +@end table + + Of these, only @file{control} is required. The various installation +scripts, and the configuration files list, will only be used if they are +present. + +@menu +* control:: +* conffiles:: +* Installation and Removal Scripts:: +* Dependencies and Conflicts:: +* Package Classification Fields:: +@end menu + +@node control, conffiles, Control Files, Control Files +@unnumberedsec control + + The @file{control} file contains a number of fields. Each field +begins with a field name, such as @file{Package} or @file{Version} +(case insensitive), followed by a colon and optionally some spaces or +tabs (a single space is conventional). Then comes the body of the +field, which may be several lines long; each continuation line must +start with at least one space or tab. (These are the same rules as +apply to RFC822 mail headers.) Blank lines are not permitted in the +control file. + + The required fields in the control file are the following: + +@table @code +@item Package +The name of the package. +@item Description +The description of the package. +@item Maintainer +The name and e-mail address of the maintainer of the package. +@item Version +The version number in the format +@code{<@i{original_version}>-<@i{debian_revision}>}. +@end table + + Each field has a particular format and meaning for the package +installation tools. + +The value of @file{Package} should be the name of the package. +Package names must start with an alphanumeric, must be at least two +characters, and may contain only alphanumerics and the characters +- + . @@ : = % _ (that is, hyphen, plus, stop, at, colon, equals, +percent and underscore). They are not case sensitive. + + The @code{Maintainer} field should be in the form + +@smallexample +Joe J. Bloggs +@end smallexample + +@noindent Note that this will not be useable as an email address if +the name given contains full stop characters, because of a silly +restriction in the Internet mail standards. If you want to use this +as an email address in a program you should check for full stops and +change the string to the form @code{jbloggs@@foo.com (Joe J. Bloggs)} +if you find any. + + The @code{Version} field should be the version number of the +package. For most packages which are not written specifically for +Debian, this should be in the form + +@smallexample +Version: <@i{original_version}>-<@i{debian_revision}> +@end smallexample + +@noindent where @file{<@i{original_version}>} is the original package +version number in whatever form the original package uses and +@file{<@i{debian_revision}>} indicates which ``debianisation'' this is +(this should usually be a plain number or perhaps a two numbers +separated by a full stop, and should be incremented each time the +package is changed or updated). + + Packages which are written specifically for Debian do not have a +@i{debian_revision}, and their version number should simply be +@i{version} (which should not contain any hyphens, to avoid +confusion). + + There is an ordering imposed on version numbers, described in +@file{version-ordering.txt}. This ordering is designed to `do the right +thing' in most circumstances; if your package has an version number in +an unusual format you may need to reformat it somewhat to get the +ordering right. This is important because @file{dpkg} is (for example) +reluctant to downgrade packages. + + The optional fields in the control file are the following: + +@table @code +@item Depends +The names of prerequisite packages. +@item Recommends +The names of related, recommended packages. +@item Suggests +The names of related, optional packages. +@item Conflicts +The names of packages which conflict with this package. +@item Provides +The names of virtual packages which this package provides. +@item Priority +The `priority' of the package, as shown and used by @file{dselect}. +@item Section +The `section' of the package, as shown and used by @file{dselect}, and +used as a location for the package in the distribution. +@item Essential +A boolean field used by the base packages. +@end table + +@noindent See below for details of the semantics and syntax of these +fields. Most packages will need at least a @code{Depends} field. + + An example of a @file{control} file would be: + +@example + Package: smail + Version: 3.1.29.1-13 + Maintainer: Ian Jackson + Recommends: pine | mailx | elm | emacs | mail-user-agent + Suggests: metamail + Depends: cron, libc5 + Conflicts: sendmail + Provides: mail-transport-agent + Description: Electronic mail transport system. + Smail is the recommended mail transport agent (MTA) for Debian. + . + An MTA is the innards of the mail system - it takes messages from + user-friendly mailer programs and arranges for them to be delivered + locally or passed on to other systems as required. + . + In order to make use of it you must have one or more user level + mailreader programs such as elm, pine, mailx or Emacs (which has Rmail + and VM as mailreaders) installed. If you wish to send messages other + than just to other users of your system you must also have appropriate + networking support, in the form of IP or UUCP. +@end example + + In this case, @file{mail-user-agent} is a virtual package +representing any user mailer program; the actual package names +@file{pine} is quoted for the reasons described in +@file{dependency-ordering.txt}, and the others because older versions +of those packages do not have the appropriate @file{Provides} field. + +@node conffiles, Installation and Removal Scripts, control, Control Files +@unnumberedsec conffiles + + The contents of @file{conffiles} is simply a list of configuration +files in the package. When installing the package, @file{dpkg} uses +an intelligent method to update these files. This will ensure that +package-specific configuration files are not overwritten when a package +is upgraded, unless the user wishes the installation tools to do so. + + Typically, files listed in conffiles are package-specific +configuration files, which (according to the Linux Filesystem Standard) +are stored in @file{/etc}. For example, the @code{sendmail} package may +contain the file @file{/etc/sendmail.cf}, which we do not wish to +overwrite automatically when the user upgrades the sendmail package. +Only those files listed in @file{DEBIAN/conffiles} will be updated +intelligently when a package is upgraded; all other files in the package +will be overwritten by the upgrade process. + + Configuration files which will be functional as shipped and will +probably need little or no local editing should simply be listed the +@file{conffiles} file; in this case you need read no further. + + For packages whose configuration files will need modification on +most systems there are two sensible approaches. Which one is chosen +depends on how hard the configuration problem is and how much time the +package maintainer has available. + + One option is for you to ship a minimal `best-effort' file in +@file{/etc}, and list the file in @file{conffiles}. This will mean that +the user will have to go and edit the file themselves to get the package +to work properly, of course. The next time they upgrade the package, if +you haven't changed the file version, their old file will be left in +place. If you have modified your version then the user will get a +prompt asking them which version of the file they want, theirs or yours. +They will then usually have to resolve the discrepancies manually. + + The other option is to be preferred, if you can do it: don't put a +copy of the configuration file in the package at all. Instead, you +check in the postinst whether the file exists, and if it doesn't you +prompt the user for the information you need to create a good one. This +is obviously harder work. + + You also have to remember that you will have to keep up with your +package's changes: if you discover a bug in the program which generates +the configuration file, or if the format of the file changes from one +version to the next, you will have to arrange for the postinst script to +do something sensible---usually this will mean editing the installed +configuration file to remove the problem or change the syntax. You will +have to do this very carefully, since the user may have changed the +file, perhaps to fix the very problem that your script is trying to deal +with---you will have to detect these situations and deal with them +correctly. + + If you do go down this route it's probably a good idea to make the +program that generates the configuration file(s) a separate program in +@file{/usr/sbin}, by convention called @i{package}@code{config}, and +then run that if appropriate from the post-installation script. The +@i{package}@code{config} program should not unquestioningly overwrite an +existing configuration---if its mode of operation is geared towards +setting up a package for the first time (rather than any arbitrary +reconfiguration later) you should have it check whether the +configuration already exists, and require a @code{--force} flag to +overwrite it. + + @file{conffiles} should almost certainly list all the files contained +in your package in the @file{/etc} directory. There may also be other +files somewhere that the user is expected to edit, which should also be +included. Note, however, that the FSSTND specifies that configuration +files must be in @file{/etc}. No Debian package should contain +configuration files in @file{/usr/etc}, and all programs should refer to +configuration files in @file{/etc}. + +@noindent For example, the TCP/IP package might use a conffiles which contains + +@example + /etc/init.d/netbase + /etc/gateways + /etc/protocols + /etc/services + /etc/hosts.allow + /etc/hosts.deny + /etc/rpc +@end example + +@noindent and so on; the files + +@example + /etc/hosts + /etc/inetd.conf + /etc/host.conf + /etc/networks + /etc/resolv.conf +@end example + +@noindent might be generated by an interactive configuration program, +and would then not be included in the package or listed in the +@file{conffiles}. + +@node Installation and Removal Scripts, Dependencies and Conflicts, conffiles, Control Files +@unnumberedsec Installation and Removal Scripts + + The scripts @file{preinst}, @file{postinst}, @file{prerm}, and +@file{postrm} are optional (Bash or Perl) scripts. As the names +would indicate, if these scripts exist, they will be executed before +installing the package, after installation, before package removal, +and after removal, respectively. + + They are given arguments which indicate the precise situation and +action being performed---see @file{maintainer-script-args.txt} for +details of exactly when each of the scripts is invoked and what its +arguments are. Extra arguments and situations may be added later, so +you should not test the number of arguments to your script to determine +the situation, and you should choose the sense of your `if it is this +then do this otherwise do that' tests carefully. + + These scripts can be used to perform any site-specific package +configuration. + + Because the scripts will be exectued by the dpkg front-end, it is +guaranteed that the scripts will be executed interactively. User input +from the scripts should be read from standard input, not the user's +terminal. Similarly, output should be sent to standard output. + + If your maintainer scripts need to prompt for passwords and/or do +@i{full-screen} interaction should do these things to and from +@file{/dev/tty}, since @file{dpkg} will at some point redirect scripts' +standard input and output so that it can log the installation process. +Likewise, because these scripts may be executed with standard output +redirected into a pipe for logging purposes, Perl scripts should set +unbuffered output by setting @code{$|=1} so that the output is printed +immediately rather than being buffered. + + The scripts must be idempotent, and they must clean up after +themselves properly. Ie, they must do the right thing if run multiple +times, even if previous runs failed halfway through. This is so that if +any errors occur, or if the @file{dpkg} run is interrupted, the user can +recover by rerunning @file{dpkg}, and/or by upgrading to a new version +and then rerunning the failed operation. + + These scripts should avoid producing output which it is unnecessary +for the user to see and should rely on @file{dpkg} to stave off boredom +on the part of a user installing many packages. This means, amongst +other things, using the @file{--quiet} option on @file{install-info}. + + Packages should try to minimise the amount of prompting they need to +do, and they should ensure that the user will only every be asked each +question once. This means that packages should try to use appropriate +shared configuration files (such as @file{/etc/papersize} and +@file{/etc/news/server}), rather than each prompting for their own list +of required pieces of information. + + It also means that an upgrade should not ask the same questions +again, unless the user has used @code{dpkg --purge} to remove the +package's configuration. The answers to configuration questions should +be stored in an appropriate place in @file{/etc} so that the user can +modify them, and how this has been done should be documented. + + If a package has a vitally important piece of information to pass to +the user (such as "don't run me as I am, you must edit the following +configuration files first or you risk your system emitting +badly-formatted messages"), it should display this in the +@file{postinst} script and prompt the user to hit Return to acknowledge +the message. Copyright messages do not count as vitally important (they +belong in @file{/usr/doc/copyright}; neither do instructions on how to +use a program (these should be in on line documentation, where all the +users can see them). + + They should return a zero exit status for success, or a nonzero one +for failure. Note that if a script is a @code{#!/bin/sh} script it +should probably start with @code{set -e}, to avoid continuing after +errors---see @file{bash}(1) for details. Perl scripts should check for +errors when making calls such as @code{open}, @code{print}, +@code{close}, @code{rename} and @code{system}. + + If these scripts exist they should be left in the @file{DEBIAN} +directory with execute permission enabled and should contain an +appropriate @code{#!} line, such as @code{#!/bin/bash} for a +@code{bash} script or @code{#!/bin/perl} for a Perl script (see +above). + +@node Dependencies and Conflicts, Package Classification Fields, Installation and Removal Scripts, Control Files +@unnumberedsec Conflicts, Depends, Suggests, Recommends and Provides + + The @file{Depends} field lists packages that are required for this +package to provide a significant amount of functionality. The package +maintenance software will not allow a package to be installed without +also installing packages listed in its @code{Depends} field, and will +run the @code{postinst} scripts of packages listed in @code{Depends} +fields before those of the packages which depend on them, and run the +@code{prerm} scripts before. + + Packages containing dynamically-linked executable binaries (this +includes almost all C programs) should include a @file{Depends} field +which mentions the shared C library required for the program to run. +For a.out binaries linked against @file{libc.so.4} the relevant package +name is @file{libc}; for ELF binaries linked against @file{libc.so.5} +the relevant package name is @file{libc5}. + + The @code{Recommends} field lists packages that would be found +together with this one in all but unusual installations. The user-level +package maintenance program @file{dselect} will warn the user if they +select a package without those listed in its @code{Recommends} field. +Note that @code{Recommends} fields don't currently have any implications +for the order in which the maintainer scripts are run. + + The @code{Suggests} field lists packages that are related to this one +and can perhaps enhance its usefulness, but without which installing +this package is perfectly reasonable. The package maintenance software +will not moan at the user for not selecting @code{Suggests} related +packages, but may use the information in the @code{Suggests} field to +assist the user during package selection. + + The syntax of @code{Depends}, @code{Recommends} and @code{Suggests} +is a list of groups of alternative packages. Each group is a list of +packages separated by vertical bar (or `pipe') symbols, @code{|}. The +groups are separated by commas. Each package is a package name +optionally followed by a version number specification in parentheses. A +version number may start with a @code{>=}, in which case that version or +any later will match, or @code{<=} for that version or any earlier +version. A version number starting with a @code{>>} or @code{<<} will +respectively match any later or earlier version. If a version number or +a version number starting with @code{=} is specified an exact match is +required. Commas are to be read as `AND', and pipes as `OR', with pipes +binding more tightly. + + Versions of dpkg before 1.0.9 used @code{<} and @code{>} for +@code{<=} and @code{>=} (these are still supported for backward +compatibility), and did not support @code{<<} and @code{>>}. + + The @code{Conflicts} field lists packages that conflict with this +one, for example by containing files with the same names (an example +would be Smail vs. Sendmail). The package maintenance software will not +allow conflicting packages to be installed. Two conflicting packages +should each include a @code{Conflicts} line mentioning the other. + + The syntax of @code{Conflicts} is a list of package names (with +optional version numbers), separated by commas (and optional +whitespace). In the @code{Conflicts} field the comma should be read as +`OR'. + + The @code{Provides} field lists the names of any `virtual packages' +of which this packages is to be considered an instantiation. Virtual +packages are used to allow packages to refer to a service they require +(such as the availability of @file{/usr/sbin/sendmail}) without having +to know the names of all the relevant packages. The virtual package +names defined in @code{Provides} fields may be used in other packages' +@code{Depends}, @code{Recommends}, @code{Suggests} and @code{Conflicts} +fields. For more information about how to use virtual packages and +which virtual package names to use read @file{virtual-dependencies.txt} +and @file{virtual-package-names-list.text}. + + The syntax of @code{Provides} is a list of package names separated by +commas (and optional whitespace). + +@node Package Classification Fields, , Dependencies and Conflicts, Control Files +@unnumberedsec Priority, Section and Essential + + The @code{Priority} and @code{Section} fields are used by +@file{dselect} when displaying the list of packages to the user. There +is no need to put them into a package, since these are usually set by +the distribution maintainers in the @file{Packages} file. + + However, if a user installs a package which is not part of the +standard distribution, or without downloading and updating from a new +@file{Packages} file, the information about the priority and section of +a package will be absent, and the @file{dselect} package listing will +have the package listed under `unclassified'. It is permissible for a +package to include @code{Section} or @code{Priority} fields to improve +this; however, if you do this you should make sure you keep the +information up to date so that users are not shown conflicting +information. The @code{Section} field can also be used by the +distribution maintainers as a suggestion about which section you think +is most appropriate for your package. + + The @code{Essential} field should only appear in packages in the +installation's base system. If it is set to @code{yes} then @file{dpkg} +will not remove the package even if asked to, and will make certain +minor modifications to its installation procedures. The only other +legal value is @code{no}, which is equivalent to the absence of the +field. + +@bye + +@c local variables: +@c kept-new-versions: 100 +@c version-control: t +@c end: diff --git a/doc/guidelines.texi.rej b/doc/guidelines.texi.rej new file mode 100644 index 00000000..15354007 --- /dev/null +++ b/doc/guidelines.texi.rej @@ -0,0 +1,33 @@ +*************** +*** 376,390 **** + Generally the following compilation parameters should be used: + + @display +- CC = gcc +- CFLAGS = -O2 +- LDFLAGS = -s (and -N, if appropriate; see below) + @end display + + All installed binaries should be stripped, hence @code{-s}. @code{-N} + should only be used on binaries that are very small (less than 8K with + the @code{-N} option, roughly) and are not likely to have multiple +- instances in memory. Do not use @code{-N} on daemons, no matter how + small they are. + + It is up to the package maintainer to decide what compilation options +--- 383,397 ---- + Generally the following compilation parameters should be used: + + @display ++ CC = gcc ++ CFLAGS = -O2 ++ LDFLAGS = -s (and -N, if appropriate; see below) + @end display + + All installed binaries should be stripped, hence @code{-s}. @code{-N} + should only be used on binaries that are very small (less than 8K with + the @code{-N} option, roughly) and are not likely to have multiple ++ instances in memory. Don't use @code{-N} on daemons, no matter how + small they are. + + It is up to the package maintainer to decide what compilation options diff --git a/doc/junk b/doc/junk new file mode 100644 index 00000000..46c35873 --- /dev/null +++ b/doc/junk @@ -0,0 +1,29 @@ + +@table @file +@item virtual-package-names-list.text +The list of virtual package names currently in use, together with the +procedure for getting new virtual package names allocated. +@item (obsolete) README.etc-skel +A description of @file{/etc/skel} and @file{/usr/doc/examples} +(@pxref{Skeleton and examples}). +@item (obsolete) descriptions.txt +The description of the package. How to write an extended and more useful +description field (@pxref{Package description}). +@item (obsolete) README.init +How to use the features of Init under Debian GNU/Linux in packages +(@pxref{Configuration of init}). +@item (obsolete) mailers.txt +How to properly configure packages to use the Debian GNU/Linux mail +nnsystem (@pxref{Mail processing packages}). +@item (obsolete) maintainer-script-args.txt +All the ways that the package maintainer scripts inside a package can be +called by dpkg (@pxref{Maintainer script arguments}). +@item (obsolete) dpkg-upgrades+errors.txt +What order things happen in during a package upgrade (@pxref{What +happends during a package upgrade?}). +@item (obsolete) virtual-dependencies.txt +How to use ``virtual dependencies'' in packages (@pxref{Virtual +dependencies}). +@item (obsolete) dependency-ordering.txt +How to properly order package names in the @file{DEPENDS} field. +@end table diff --git a/doc/maintainer-script-args.txt b/doc/maintainer-script-args.txt new file mode 100644 index 00000000..a1cd3c75 --- /dev/null +++ b/doc/maintainer-script-args.txt @@ -0,0 +1,173 @@ +Richard Kettlewell has asked me to document this, so here is a brief +summary. More documentation will probably have to wait until someone +has time ... + +... hmm, it has turned out not to be so brief, and I've spent the last +hour or so writing it. Perhaps someone can use it as the basis for a +spec or a manpage or something. + +In all cases version numbers are -, if the package +has both, or just . `upgrade' is used even when the new +version number looks lower than the old. + +*** SUMMARY - listing of possible scripts with arguments: + + install + install + upgrade + abort-upgrade + + configure + abort-upgrade + abort-remove in-favour + abort-deconfigure \ + in-favour + removing + + remove + upgrade + failed-upgrade + remove in-favour + deconfigure \ + in-favour \ + removing + + remove + purge + upgrade + failed-upgrade + abort-install + abort-install + abort-upgrade + disappear + +*** INSTALLATION (unpack): + +The procedure on installation/upgrade/overwrite/disappear (ie, when +running dpkg --unpack, or the unpack stage of dpkg --install) is as +follows. In each case if an error occurs the actions in are general +run backwards - this means that the maintainer scripts are run with +different arguments in reverse order. These are the `error unwind' +calls listed below. + +1a. If a version the package is already installed, call + upgrade +1b. If this gives an error (ie, a non-zero exit status), dpkg will +attempt instead: + failed-upgrade + ... error unwind, for both the above cases: + abort-upgrade + +2. If a `conflicting' package is being removed at the same time: +2a. If any packages depended on that conflicting package and +--auto-deconfigure is specified, call, for each such package: + deconfigure \ + in-favour \ + removing + ... error unwind: + abort-deconfigure \ + in-favour + removing +The deconfigured packages are marked as requiring configuration, so +that if --install is used they will be configured again if possible. +2b. To prepare for removal of the conflicting package, call: + remove in-favour + ... error unwind: + abort-remove in-favour + +3a. If the package is being upgraded, call + upgrade +3b. otherwise, if the package had some configuration files from a +previous version installed (ie, it is in the conffiles-only state): + install +3c. otherwise (ie, the package was completely purged): + install + ... error unwind versions, respectively: + abort-upgrade + abort-install + abort-install + +4. The new package's files are unpacked, overwriting any that may be +on the system already, for example any from the old package or from +another package (backups of the old files are left around, and if +anything goes wrong dpkg will attempt to put them back as part of the +error unwind). + +5a. If the package is being upgraded, call + upgrade +5b. If this fails, dpkg will attempt: + failed-upgrade + ... error unwind, for both cases: + abort-upgrade + +This is the point of no return - if dpkg gets this far, it won't back +off past this point if an error occurs. This will leave the package +in a fairly bad state, which will require a successful reinstallation +to clear up, but it's when dpkg starts doing things that are +irreversible. + +6. Any files which were in the old version of the package but not in + the new are removed. +7. The new file list replaces the old. +8. The new maintainer scripts replace the old. + +9. Any packages all of whose files have been overwritten during the +installation, and which aren't required for dependencies, are +considered to have been removed. For each such package, +9a. dpkg calls: + disappear +9b. The package's maintainer scripts are removed. +9c. It is noted in the status database as being in a sane state, +namely not installed (any conffiles it may have are ignored). +Note that disappearing packages don't have their prerm called, because +dpkg doesn't know in advance that the package is going to vanish. + +10. Any files in the package we're unpacking that are also listed in +the file lists of other packages are removed from those lists. (This +will lobotomise the file list of the `conflicting' package if there is +one.) + +11. The backup files made at 4. are deleted. +12. The new package's status is now sane, and recorded as `unpacked'. + +Here is another point of no return - if the conflicting package's +removal fails we don't unwind the rest of the installation; the +conflicting package is left in a half-removed limbo. + +13. If there was a conflicting package we go and do the removal +actions, starting from point 2. of the removal, below. + + +*** CONFIGURATION: + +When we configure a package (this happens with dpkg --install, or with +--configure), we first update the conffiles and then call: + configure +(I'm planning to make available as an argument the version of the most +recent successfully-configured version of the package.) + +No attempt is made to unwind after errors during configuration. + + +*** REMOVAL: + +1. remove + +2. The package's files are removed (except conffiles). + +3. remove + +4. All the maintainer scripts except the postrm are removed. + +If we aren't purging the package we stop here. Note that packages +which have no postrm and no conffiles are automatically purged when +removed, as there is no difference except for the dpkg status. + +5. The conffiles and any backup files (~-files, #*# files, %-files, +.dpkg-{old,new,tmp}, &c &c &c) are removed. + +6. purge + +7. The package's file list is removed. + +No attempt is made to unwind after errors during removal. diff --git a/doc/upgrades+errors.txt b/doc/upgrades+errors.txt new file mode 100644 index 00000000..09b5bd5e --- /dev/null +++ b/doc/upgrades+errors.txt @@ -0,0 +1,220 @@ +From ian Wed Nov 16 15:12:56 1994 +X-VM-v5-Data: ([nil nil nil nil t nil nil nil nil] + [nil "Wed" "16" "November" "1994" "15:12:56" nil "ian" "ian" "" nil "dpkg - upgrades and error handling, take 2" "^To:" nil nil "11" nil nil nil nil] + nil) +To: Debian dpkg list +Subject: dpkg - upgrades and error handling, take 2 + +Here is a revised scheme for upgrade procedure and error handling. + +If you can see any serious problems with this please say so; if there +are no compelling reasons not to I intend to implement the following. + + +Procedure for new unpack: + * Run the preinst script (argument: `install'). + * Back up the conffiles. + * Unpack the files in the package. +This leaves the package in the `unpacked but not configured' +state. + +If the preinst fails the installation is aborted immediately, leaving +the package in whatever state it was originally. The preinst should +clean up after its own failure. + +If the conffiles can't be backed up then any which have been are +restored to their original names, if possible, and the postrm script +is run if there is one (argument: `abort-install'). This leaves the +package in its original state. + +If the unpack fails any files already unpacked are removed, the +conffiles are restored to their original names, and the postrm is run +(argument: `abort-install'). Again, the package remains in its +original state. + +Errors found when running the postrm are ignored. + + +Procedure for configuration: + * Do the conffile updating, including prompting, as in the spec. + * Run the postinst (argument: `configure'). + +If the conffile updating fails anything that has been done is undone +if possible and the package is left in the `unpacked but not +configured' state. + +If the postinst fails dpkg gives up and leaves the package in the +`postinst failed' state. Next time it will just rerun the +postinst; if the postinst has a bug a new *.deb can be provided. In +that case installation of the new *.deb will proceed almost as if it +were a normal upgrade. + + +Procedure for removal: + * Run the prerm (argument: `remove'). + * Delete the files in the package and any resulting empty + directories. + * Run the postrm (argument: `remove'). + +If the prerm fails dpkg leaves the package in the original state; if +the user asks again to remove the package the prerm will be run again. + +If the deletion fails the removal is aborted, and the package left in +the `removal failed' state. + +If the postrm fails dpkg leaves the package in the `removal failed' +state. + +If the package is in the `removal failed' state to start with it will +start again with deleting the files and empty directories and rerun +the postrm. + +If the postrm has a bug a new *.deb must be installed first (using the +upgrade procedure) and then removed. This is so that a working postrm +script is provided. + + +Procedure for upgrade: + * Run the old prerm script (arguments: `upgrade '). + * Move aside all existing files (not directories) for the + package being overwritten. + * Run the old postrm script (arguments: `upgrade '). + * Run the new preinst script (arguments: `upgrade '). + * Back up the conffiles. + * Unpack the files in the package. + * Remove the files which were moved aside. +This leaves the package in the `unpacked but not configured' +state. + +Errors during the removal of the files which were moved aside are +flagged, but don't cause dpkg to attempt to abort the upgrade. + +If the old prerm or postrm script fails the corresponding script from +the new package is run instead (arguments: `failed-upgrade +'). If there is no corresponding script in the new +package the error is ignored. + +If any other stage fails, or if we try to use the script in the new +package because the old prerm/postrm script failed but the new script +fails too, we attempt to undo everything in reverse order, as follows: + +Unpacking the files in the package is undone by removing the files we +unpacked and any empty directories, in reverse order. Errors are +ignored. + +The conffiles backup is undone as much as possible, ignoring errors. + +The new preinst is undone by running the new postrm (arguments: +`abort-upgrade '); errors are ignored. + +The old postrm is undone by running the old preinst (arguments: +`abort-upgrade '); errors are ignored. + +Files we backed up are restored if possible; errors here leave the +package in the `removal failed' state. + +The old prerm is undone by running the old postinst (arguments: +`abort-upgrade '). Errors here leave the package in the +`postinst failed' state. + + +Ian. + +From ian Wed May 17 02:14:09 +0100 1995 +X-VM-v5-Data: ([nil nil nil nil nil nil nil nil nil] + [nil nil nil nil nil nil nil nil nil nil nil nil "^To:" nil nil nil nil nil nil nil] + nil) +To: Debian developers list +Subject: New dpkg overwriting handling + +Here is a comment that describes how I plan to have the new C dpkg act +with respect to overwriting, upgrades, &c. + +The sequence of events during upgrade will change, because the removal +of the old package and the installation of the new will take place +together. So we have, + + (noninteractive, during `dpkg --unpack' ...) + old prerm + new preinst + unpack new archive, possibly overwriting old files + delete any files in the old but not the new package + old postrm + run `postrm disappear' for any vanished packages, then forget them + + (interactive, during `dpkg --configure' ...) + conffile updates + new postinst + +Furthermore, conffiles will not be backed up before unpack and have +the user's old version hang around as `.dpkg-tmp'; instead, the old +file will be left in place and the new one extracted into `.dpkg-new'. +It will only be installed (if desired) at a conffile update. + +Thanks to Bruce for providing nice tar-in-a-function code that will +allow me to handle each file individually rather than having tar splat +them all out (and often do it buggily anyway). + +Ian. + + /* + * Now we unpack the archive, backing things up as we go. + * For each file, we check to see if it already exists. + * There are several possibilities: + * + We are trying to install a non-directory ... + * - It doesn't exist. In this case we simply extract it. + * - It is a plain file, device, symlink, &c. We do an `atomic + * overwrite' using link() and rename(), but leave a backup copy. + * Later, when we delete the backup, we remove it from any other + * packages' lists. + * - It is a directory. We move it aside and extract the file. + * Later, when we delete the backed-up directory, we remove it + * from any other packages' lists. + * + We are trying to install a directory ... + * - It doesn't exist. We create it with the appropriate modes. + * - It is a plain file or a symlink. We move it aside and create + * the directory. Later, when we delete the backup, we remove it + * from any other packages' lists. + * - It exists as a directory. We do nothing. + * + * Install non-dir Install dir + * Exists not X C + * File/node/symlink LXR BCR + * Directory BXR - + * + * C: create directory + * X: extract file/node/link + * LX: atomic overwrite leaving backup + * B: ordinary backup + * R: later remove from other packages' lists + * -: do nothing + * + * After we've done this we go through the remaining things in the + * lists of packages we're trying to remove (including the old + * version of the current package). This happens in reverse order, + * so that we process files before the directories (or symlinks-to- + * directories) containing them. + * + If the thing is a conffile then we leave it alone for the purge + * operation. + * + Otherwise, there are several possibilities too: + * - The listed thing does not exist. We ignore it. + * - The listed thing is a directory or a symlink to a directory. + * We delete it only if it isn't listed in any other package. + * - The listed thing is not a directory or a symlink to one (ie, + * it's a plain file, device, pipe, &c, or a symlink to one, or a + * dangling symlink). We delete it. + * The removed packages' list becomes empty (of course, the new + * version of the package we're installing will have a new list, + * which replaces the old version's list). + * + * If at any stage we remove a file from a package's list, and the + * package isn't one we're already processing, and the package's + * list becomes empty as a result, we `vanish' the package. This + * means that we run its postrm with the `disappear' argument, and + * put the package in the `not-installed' state. Its conffiles are + * ignored and forgotten about. + * + * NOTE THAT THE OLD POSTRM IS RUN AFTER THE NEW PREINST, since the + * files get replaced `as we go'. + */ + diff --git a/doc/version-ordering.txt b/doc/version-ordering.txt new file mode 100644 index 00000000..7bbda630 --- /dev/null +++ b/doc/version-ordering.txt @@ -0,0 +1,59 @@ +(This has been edited to conform to the intent in dpkg 1.0.16. + When recent versions of dpkg compare versions they break the Version + into an upstream version and debian revision first, by splitting the + Version at the last hyphen. The revisions are only considered if the + upstream versions compare equal.) + +To: debian-devel@pixar.com +Subject: Re: dpkg 0.93.8 released + +[...] +Well, here is what I came up with after a bit of thought and testing. +I propose the following algorithm for comparing version numbers: + + forever { + remove initial non-digit substring from string a + remove initial non-digit substring from string b + compare initial non-digit substrings lexically, + counting letters as coming before punctuation + if (they differ) return the answer + remove initial digit substring from string a + remove initial digit substring from string b + compare initial digit substrings numerically + if (they differ) return the answer + if (both strings are now empty) + the version numbers are the same, stop + if (one string is now empty) + it is the `lesser', return the answer + } + +This will have the desired results: + 2.1 < 2.7 < 2.7a < 2.7a-2 < 2.15 + +An implementation in Perl is attached below. + +Ian. + +#!/usr/bin/perl -- + +if (@ARGV) { + print &compare(@ARGV),"\n"; +} else { + while(<>) { chop; chop($x=<>); print &compare($_,$x),"\n"; } +} + +sub compare { + local ($a,$b) = @_; + do { + $a =~ s/^\D*//; $ad= $&; $ad =~ s/\W/ /g; + $b =~ s/^\D*//; $bd= $&; $bd =~ s/\W/ /g; +print "\t[$ad|$a] [$bd|$b]\n"; + $cm = $ad cmp $bd; return $cm if $cm; + $a =~ s/^\d*//; $ad= $&; + $b =~ s/^\d*//; $bd= $&; +print "\t<$ad|$a> <$bd|$b>\n"; + $cm = $ad <=> $bd; return $cm if $cm; + } while (length($a) && length($b)); +print "\t{$a} {$b}\n"; + return length($a) cmp length($b); +} diff --git a/doc/virtual-dependencies.txt b/doc/virtual-dependencies.txt new file mode 100644 index 00000000..1f45eb7a --- /dev/null +++ b/doc/virtual-dependencies.txt @@ -0,0 +1,105 @@ +To: Debian users list +Subject: dpkg 0.93.36: dselect does virtual packages + +This release contains virtual package support in the C parts of the +system, which at the moment includes dselect and dpkg-deb. + +It works as I outlined in a mail to debian-devel a little while ago. + +Package maintainers: please start adding `Provides:' fields to your +packages. dselect will use and understand them; dpkg-deb will check +the syntax of the Provides field instead of warning you about it. +dpkg will not understand the new field and will ignore it. + +When almost all the packages have Provides: fields in place we can +start replacing old-style Recommended: fields. + +Note that we can't change Depends: fields since they're also +interpreted by dpkg, which doesn't know about Provides (yet). + +[...] +-------------------- + +From: iwj10@cus.cam.ac.uk (Ian Jackson) +To: debian-devel@pixar.com +Subject: `virtual' packages in Depends, Conflicts &c +Date: Fri, 31 Mar 95 15:44 BST + +We're starting to have a problem with package name changes and +multiple packages providing the same service. + +I liked the idea (put forward by [Bruce Perens]) of having `virtual' +packages; it seems that it would solve many of these problems straight +away. + +What I'm proposing to do is as follows: + +Depends, Conflicts, Recommended and Optional lines will now be able to +contain `virtual' package names as well as real package names. + +Virtual packages are in the same namespace as real packages, and may +have the same name. The meaning of a virtual package in a +dependency/conflicts list is exactly that of listing all the real +packages which state that they are an instantiation of that virtual +package. + +This is done with a new Provides field in the control file, with a +syntax much like the Conflicts field. + +The idea is that we can have something like: + Package: elm + Depends: mta + + Package: smail + Provides: mta + Conflicts: mta + + Package: sendmail + Provides: mta + Conflicts: mta +&c. The result is equivalent to elm having said + Depends: smail | sendmail + +(There'll be a special case to say that a package may conflict with a +virtual package which it provides - clearly ...) + +If there are both a real and a virtual package of the same name then +the dependency may be satisfied (or the conflict caused) by either the +real package or any of the virtual packages which provide it. This is +so that, for example, supposing we have + Package: lout + Optional: ghostview +(this is a fictional example - the Lout package shouldn't mention +ghostview), and someone else comes up with a nice PostScript +previewer, then they can just say + Package: marvelpostview + Provides: ghostview +and all will work in the interim (until, say, the Lout maintainer +changes things). + +If a dependency or a conflict has a version number attached then only +real packages will be considered to see whether the relationship is +satisfied (or prohibited, for a conflict) - it is assumed that a real +package which provides virtual package is not of the `right' version. +If there is demand I could arrange that a package which provides a +virtual package may mention a version number, eg. + Provides: mta (2.0) +though I tbink this is unlikely to be helpful. + +If you want to specify which of a set of real packages should be the +default to satisfy a particular dependency on a virtual package, you +can simply list the real package as alternative before the virtual +one. E.g.: + Package: xbaseR6 + Recommended: xsvga | x-server + Provides: x-base, xr6shlib + + Package: xsvga + Recommended: x-base + Provides: x-server + + Package: x8514 + Recommended: x-base + Provides: x-server + +Ian. diff --git a/dpkg-deb/Makefile.in b/dpkg-deb/Makefile.in new file mode 100644 index 00000000..91866196 --- /dev/null +++ b/dpkg-deb/Makefile.in @@ -0,0 +1,66 @@ +# Copyright (C) 1994 Ian Murdock +# Copyright (C) 1994,1995 Ian Jackson +# +# This 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, +# or (at your option) any later version. +# +# This 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 dpkg; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +srcdir = @srcdir@ +VPATH = @srcdir@ + +prefix = @prefix@ +exec_prefix = $(prefix) +bindir = $(exec_prefix)/bin +libdir = $(prefix)/lib +mandir = $(prefix)/man +man8dir = $(mandir)/man8 +man8 = 8 + +SRC = main.c build.c extract.c info.c +OBJ = main.o build.o extract.o info.o + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ + +CC = @CC@ + +CFLAGS = @CFLAGS@ @CWARNS@ -g $(XCFLAGS) +LDFLAGS = $(XLDFLAGS) +LIBS = -L../lib -ldpkg $(XLIBS) +ALL_CFLAGS = -I../include -I.. @DEFS@ $(CFLAGS) + +.SUFFIXES: .c .o + +.c.o: + $(CC) $(ALL_CFLAGS) -c $< + +all: dpkg-deb + +dpkg-deb: $(OBJ) ../lib/libdpkg.a + $(CC) $(LDFLAGS) -o dpkg-deb $(OBJ) $(LIBS) + +$(OBJ): dpkg-deb.h ../config.h ../include/dpkg.h +build.o: ../include/dpkg-db.h +info.o extract.o main.o: ../include/myopt.h +main.o: ../version.h + +clean: + rm -f *.o core dpkg-deb + +distclean: clean + rm -f Makefile *.orig *~ *.~* ./#*# + +install: all + $(INSTALL_PROGRAM) -s dpkg-deb $(bindir)/dpkg-deb + $(INSTALL_DATA) dpkg-deb.8 $(man8dir)/dpkg-deb.$(man8) diff --git a/dpkg-deb/build.c b/dpkg-deb/build.c new file mode 100644 index 00000000..79e9f364 --- /dev/null +++ b/dpkg-deb/build.c @@ -0,0 +1,267 @@ +/* + * dpkg-deb - construction and deconstruction of *.deb archives + * build.c - building archives + * + * Copyright (C) 1994,1995 Ian Jackson + * + * This 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, + * or (at your option) any later version. + * + * This 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 dpkg; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "dpkg.h" +#include "dpkg-db.h" +#include "dpkg-deb.h" + +#ifndef S_ISLNK +# define S_ISLNK(mode) ((mode&0xF000) == S_IFLNK) +#endif + +static void checkversion(const char *vstring, const char *fieldname, int *errs) { + const char *p; + if (!vstring || !*vstring) return; + for (p=vstring; *p; p++) if (isdigit(*p)) return; + fprintf(stderr, BACKEND " - error: %s field (`%s') doesn't contain any digits\n", + fieldname, vstring); + (*errs)++; +} + +void do_build(const char *const *argv) { + static const char *const maintainerscripts[]= { + PREINSTFILE, POSTINSTFILE, PRERMFILE, POSTRMFILE, 0 + }; + + char *m; + const char *debar, *directory, *const *mscriptp; + char *controlfile; + struct pkginfo *checkedinfo; + struct arbitraryfield *field; + FILE *ar, *gz, *cf; + int p1[2],p2[2], warns, errs, n, c; + pid_t c1,c2,c3,c4,c5; + struct stat controlstab, datastab, mscriptstab; + char conffilename[MAXCONFFILENAME+1]; + time_t thetime= 0; + + directory= *argv++; if (!directory) badusage("--build needs a directory argument"); + if ((debar= *argv++) !=0) { + if (*argv) badusage("--build takes at most two arguments"); + } else { + m= m_malloc(strlen(directory) + sizeof(DEBEXT)); + strcpy(m,directory); strcat(m,DEBEXT); + debar= m; + } + + if (nocheckflag) { + printf(BACKEND ": warning, not checking contents of control area.\n" + BACKEND ": building an unknown package in `%s'.\n", debar); + } else { + controlfile= m_malloc(strlen(directory) + sizeof(BUILDCONTROLDIR) + + sizeof(CONTROLFILE) + sizeof(CONFFILESFILE) + + sizeof(POSTINSTFILE) + sizeof(PREINSTFILE) + + sizeof(POSTRMFILE) + sizeof(PRERMFILE) + + MAXCONFFILENAME + 5); + strcpy(controlfile, directory); + strcat(controlfile, "/" BUILDCONTROLDIR "/" CONTROLFILE); + warns= 0; errs= 0; + parsedb(controlfile, pdb_recordavailable|pdb_rejectstatus, + &checkedinfo, stderr, &warns); + assert(checkedinfo->available.valid); + if (checkedinfo->priority == pri_other) { + fprintf(stderr, "warning, `%s' contains user-defined Priority value `%s'\n", + controlfile, checkedinfo->otherpriority); + warns++; + } + for (field= checkedinfo->available.arbs; field; field= field->next) { + fprintf(stderr, "warning, `%s' contains user-defined field `%s'\n", + controlfile, field->name); + warns++; + } + checkversion(checkedinfo->available.version,"Version",&errs); + checkversion(checkedinfo->available.revision,"Revision",&errs); + if (errs) ohshit("%d errors in control file",errs); + printf(BACKEND ": building package `%s' in `%s'.\n", checkedinfo->name, debar); + + strcpy(controlfile, directory); + strcat(controlfile, "/" BUILDCONTROLDIR "/"); + if (lstat(controlfile,&mscriptstab)) ohshite("unable to stat control directory"); + if (!S_ISDIR(mscriptstab.st_mode)) ohshit("control directory is not a directory"); + if ((mscriptstab.st_mode & 07757) != 0755) + ohshit("control directory has bad permissions %03lo (must be >=0755 " + "and <=0775)", (unsigned long)(mscriptstab.st_mode & 07777)); + + for (mscriptp= maintainerscripts; *mscriptp; mscriptp++) { + strcpy(controlfile, directory); + strcat(controlfile, "/" BUILDCONTROLDIR "/"); + strcat(controlfile, *mscriptp); + + if (!lstat(controlfile,&mscriptstab)) { + if (S_ISLNK(mscriptstab.st_mode)) continue; + if (!S_ISREG(mscriptstab.st_mode)) + ohshit("maintainer script `%.50s' is not a plain file or symlink",*mscriptp); + if ((mscriptstab.st_mode & 07557) != 0555) + ohshit("maintainer script `%.50s' has bad permissions %03lo " + "(must be >=0555 and <=0775)", + *mscriptp, (unsigned long)(mscriptstab.st_mode & 07777)); + } else if (errno != ENOENT) { + ohshite("maintainer script `%.50s' is not stattable",*mscriptp); + } + } + + strcpy(controlfile, directory); + strcat(controlfile, "/" BUILDCONTROLDIR "/" CONFFILESFILE); + if ((cf= fopen(controlfile,"r"))) { + while (fgets(conffilename,MAXCONFFILENAME+1,cf)) { + n= strlen(conffilename); + if (!n) ohshite("empty string from fgets reading conffiles"); + if (conffilename[n-1] != '\n') { + fprintf(stderr, "warning, conffile name `%.50s...' is too long", conffilename); + warns++; + while ((c= getc(cf)) != EOF && c != '\n'); + continue; + } + conffilename[n-1]= 0; + strcpy(controlfile, directory); + strcat(controlfile, "/"); + strcat(controlfile, conffilename); + if (lstat(controlfile,&controlstab)) { + if (errno == ENOENT) + ohshit("conffile `%.250s' does not appear in package",conffilename); + else + ohshite("conffile `%.250s' is not stattable",conffilename); + } else if (!S_ISREG(controlstab.st_mode)) { + fprintf(stderr, "warning, conffile `%s'" + " is not a plain file\n", conffilename); + warns++; + } + } + if (ferror(cf)) ohshite("error reading conffiles file"); + fclose(cf); + } else if (errno != ENOENT) { + ohshite("error opening conffiles file"); + } + if (warns) { + if (fprintf(stderr, BACKEND ": ignoring %d warnings about the control" + " file(s)\n", warns) == EOF) werr("stderr"); + } + } + if (ferror(stdout)) werr("stdout"); + + if (!(ar=fopen(debar,"wb"))) ohshite("unable to create `%.255s'",debar); + if (setvbuf(ar, 0, _IONBF, 0)) ohshite("unable to unbuffer `%.255s'",debar); + m_pipe(p1); + if (!(c1= m_fork())) { + m_dup2(p1[1],1); close(p1[0]); close(p1[1]); + if (chdir(directory)) ohshite("failed to chdir to `%.255s'",directory); + if (chdir(BUILDCONTROLDIR)) ohshite("failed to chdir to .../" BUILDCONTROLDIR); + execlp(TAR,"tar","-cf","-",".",(char*)0); ohshite("failed to exec tar -cf"); + } + close(p1[1]); + if (!(gz= tmpfile())) ohshite("failed to make tmpfile (control)"); + if (!(c2= m_fork())) { + m_dup2(p1[0],0); m_dup2(fileno(gz),1); close(p1[0]); + execlp(GZIP,"gzip","-9c",(char*)0); ohshite("failed to exec gzip -9c"); + } + close(p1[0]); + waitsubproc(c2,"gzip -9c",0); + waitsubproc(c1,"tar -cf",0); + if (fstat(fileno(gz),&controlstab)) ohshite("failed to fstat tmpfile (control)"); + if (oldformatflag) { + if (fprintf(ar, "%-8s\n%ld\n", OLDARCHIVEVERSION, (long)controlstab.st_size) == EOF) + werr(debar); + } else { + thetime= time(0); + if (fprintf(ar, + "!\n" + "debian-binary %-12lu0 0 100644 %-10ld`\n" + ARCHIVEVERSION "\n" + "%s" + ADMINMEMBER "%-12lu0 0 100644 %-10ld`\n", + thetime, + (long)sizeof(ARCHIVEVERSION), + (sizeof(ARCHIVEVERSION)&1) ? "\n" : "", + (unsigned long)thetime, + (long)controlstab.st_size) == EOF) + werr(debar); + } + + if (lseek(fileno(gz),0,SEEK_SET)) ohshite("failed to rewind tmpfile (control)"); + if (!(c3= m_fork())) { + m_dup2(fileno(gz),0); m_dup2(fileno(ar),1); + execlp(CAT,"cat",(char*)0); ohshite("failed to exec cat (control)"); + } + waitsubproc(c3,"cat (control)",0); + + if (!oldformatflag) { + fclose(gz); + if (!(gz= tmpfile())) ohshite("failed to make tmpfile (data)"); + } + m_pipe(p2); + if (!(c4= m_fork())) { + m_dup2(p2[1],1); close(p2[0]); close(p2[1]); + if (chdir(directory)) ohshite("failed to chdir to `%.255s'",directory); + execlp(TAR,"tar","--exclude",BUILDCONTROLDIR,"-cf","-",".",(char*)0); + ohshite("failed to exec tar --exclude"); + } + close(p2[1]); + if (!(c5= m_fork())) { + m_dup2(p2[0],0); close(p2[0]); + m_dup2(oldformatflag ? fileno(ar) : fileno(gz),1); + execlp(GZIP,"gzip","-9c",(char*)0); + ohshite("failed to exec gzip -9c from tar --exclude"); + } + close(p2[0]); + waitsubproc(c5,"gzip -9c from tar --exclude",0); + waitsubproc(c4,"tar --exclude",0); + if (!oldformatflag) { + if (fstat(fileno(gz),&datastab)) ohshite("failed to fstat tmpfile (data)"); + if (fprintf(ar, + "%s" + DATAMEMBER "%-12lu0 0 100644 %-10ld`\n", + (controlstab.st_size & 1) ? "\n" : "", + (unsigned long)thetime, + (long)datastab.st_size) == EOF) + werr(debar); + + if (lseek(fileno(gz),0,SEEK_SET)) ohshite("failed to rewind tmpfile (data)"); + if (!(c3= m_fork())) { + m_dup2(fileno(gz),0); m_dup2(fileno(ar),1); + execlp(CAT,"cat",(char*)0); ohshite("failed to exec cat (data)"); + } + waitsubproc(c3,"cat (data)",0); + + if (datastab.st_size & 1) + if (putc('\n',ar) == EOF) + werr(debar); + } + if (fclose(ar)) werr(debar); + + exit(0); +} + diff --git a/dpkg-deb/debugmake b/dpkg-deb/debugmake new file mode 100755 index 00000000..638c74dc --- /dev/null +++ b/dpkg-deb/debugmake @@ -0,0 +1,3 @@ +#!/bin/sh +set -x +make XCFLAGS='-g -O0' LDFLAGS=-g LIBS='-lefence -L../lib -ldpkg' "$@" diff --git a/dpkg-deb/dpkg-deb.8 b/dpkg-deb/dpkg-deb.8 new file mode 100644 index 00000000..b9af3dd3 --- /dev/null +++ b/dpkg-deb/dpkg-deb.8 @@ -0,0 +1,130 @@ +.\" Hey, Emacs! This is an -*- nroff -*- source file. +.\" Authors: Raul Miller and Ian Jackson +.TH DPKG\-DEB 8 "29 Nov 1995" "Debian Project" "Debian GNU/Linux" +.SH NAME +dpkg\-deb \- Debian GNU/Linux package archive backend +.SH SYNOPSIS +.B dpkg-deb --version +.LP +.B dpkg-deb +.BR -X | --vextract +.I +.RI [ ] +.LP +.B dpkg-deb +.BR -b | --build +.RB [ --nocheck ] +.I +.RI [ ] +.LP +.B dpkg-deb +.BR -c | --contents +.I +.LP +.B dpkg-deb +.BR -e | --control +.I +.RI [ ] +.LP +.B dpkg-deb +.BR -f | --field +.I +.RI [ ]... +.LP +.B dpkg-deb +.BR -h | --help +.LP +.B dpkg-deb +.BR -I | --info +.I +.RI [ ] +.LP +.B dpkg-deb +.BR -x | --extract +.I +.LP +.B dpkg-deb +.BR -D | --debug +.I +.SH DESCRIPTION +.B dpkg-deb +packs and unpacks debian archives. It tracks file permissions, and +includes support for the staged unpacking mechanism required by debian. +.SH OPTIONS +.I +is the filename of a Debian format archive. +.I +is the name of an administrative file component. +.I +is the name of a field in the main `control' file. +.LP +.B --version +displays the version number. +.LP +.BR -X | --vextract +extracts files from archive and lists archive's contents. +.LP +.BR -b | --build +makes a debian archive from the image of +.IR . +.I +must have a +.B DEBIAN +subdirectory, which is treated specially for the debian-specific +control file and any pre- or post-install scripts. + +The +.B DEBIAN +directory must contain a file called +.B control +whose contents is a valid control file for the Debian package +management system. Errors in this file will abort the processing of +the package. This check can be bypassed by the use of the +.B --nocheck +option. +.LP +.BR -c | --contents +lists the contents of the archive on stdout. +.LP +.BR -e | --control +extracts the control file from the archive. +If no target directory is specified, the control files are extracted +into +.BR ./DEBIAN . +.LP +.BR -f | --field +displays [named field(s) from] the control file. +.LP +.BR -h | --help +displays summary of usage. +.LP +.BR -I | --info +describes the archive, on stdout. +.LP +.BR -x | --extract +extracts files from archive. +.LP +.BR -D | --debug +would enable debugging. +.SH NOTES +.B dpkg-deb +packs and unpacks *.deb files, but does not deal with any of the +larger administrative issues of installing/de-installing packages. +.SH SEE ALSO +.BR deb (5), +.BR deb-control (5), +.BR dpkg (5), +.BR dpkg (8), +.BR dselect (8). +.SH BUGS +.B dpkg-deb -I +.IB package1 .deb +.IB package2 .deb +does the wrong thing. + +This manpage is too terse and fails to document all the options. If +you are a competent and accurate writer and are willing to spend the +time reading the source and writing good manpages please +please write a better man page than this one. +.LP +Still being developed, as of 29th November 1995. diff --git a/dpkg-deb/dpkg-deb.8-vuori b/dpkg-deb/dpkg-deb.8-vuori new file mode 100644 index 00000000..ca22395b --- /dev/null +++ b/dpkg-deb/dpkg-deb.8-vuori @@ -0,0 +1,133 @@ +.TH dpkg-deb 8 +.SH NAME +dpkg-deb - a low-level package manager for Debian GNU/Linux + +.SH SYNOPSIS + +.B dpkb-deb +[options] action + +.SH DESCRIPTION + +.B Dpkg-deb +is a low-level tool to build, and manage Debian GNU/Linux packages. +The dpkg-deb is intended to be used via +.B dpkg(8) +tool. The actions described here can be given to +.B dpkg +also. What +.B dpkg +actually does with them is that it runs +.B dpkg-deb +with those parameters. However, using +.B dpkg +instead of +.B dpkg-deb +is a better idea, for it can be used more generally with all Debian +package handling. + +dpkg-deb is operated via two different types of command +line parameters: actions and options. The actions tell dpkg-deb what +to do and options control the behaviour of the action in some way. + +.SS ACTIONS + +.TP +.B dpkg-deb -b|--build [] +Build a Debian package named +.I +from files in +.I . +If filename is not specified, a file called +.I .deb +is created instead. The directory +.I /DEBIAN +must contain a special file named +.I control +and may optionally contain other control files too. +See +.B deb(5) +for more information about those files. +.TP +.B dpkg-deb -I|--info [...] +Show information about a package named +.I . +By default this command shows the control-file of this package (see +.B deb-control(5) +) and some statistics (file lengths, etc.). If +.I +is specified, the specified file is displayed instead. See +.B deb(5) +for more information about control-files. +.TP +.B dpkg-deb -c|--contents +List contents of Debian GNU/Linux package +.I . +.TP +.B dpkg-deb -e|--control [] +Extract control-information from a package named +.I . +This action creates a directory named +.I , +or if it isn't specified, a directory named +.I DEBIAN, +containing control files of specified package. See +.B deb(5) +for more information about those files. +.TP +.B dpkg-deb -f|--field [] +Display control field(s) of a package +.I . +By default all fields are displayed. See +.B deb-control(5) +for more information about these fields. +.TP +.B dpkg-deb --fsys-tarfile +Display the filesystem tar-file contained by a Debian package +.I . +This tar-file is the file that is extracted to +.I / +while the package is installed. +.TP +.B dpkg-deb -X|--vextract | -x|--extract +Extract the files contained by a package named +.I +to +.I . +.B --vextract +also displays the names of files contained by the package. +.TP +.B dpkg-deb --help|-h +Type a brief help. +.TP +.B dpkg-deb --licence +Type licence of +.B dpkg-deb. +.TP +.B dpkg-deb --version +Type version information. + + +.SS OPTIONS + +.TP +.B -D | --debug +Set debugging on. +.TP +.B --new | --old +Select new or old package format. The default format is old. + +.SH SEE ALSO +.B deb(5) +, +.B dpkg-deb(8) +, +.B dselect(8) +and +.B deb-control(5) + +.SH AUTHOR +.B dpkg +is written by Ian Jackson (ian@chiark.chu.cam.ac.uk). Manual page added +by Juho Vuori (javuori@cc.helsinki.fi). + diff --git a/dpkg-deb/dpkg-deb.h b/dpkg-deb/dpkg-deb.h new file mode 100644 index 00000000..946e823c --- /dev/null +++ b/dpkg-deb/dpkg-deb.h @@ -0,0 +1,40 @@ +/* + * dpkg-deb - construction and deconstruction of *.deb archives + * dpkg-deb.h - external definitions for this program + * + * Copyright (C) 1994,1995 Ian Jackson + * + * This 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, + * or (at your option) any later version. + * + * This 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 dpkg; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef DPKG_DEB_H +#define DPKG_DEB_H + +typedef void dofunction(const char *const *argv); +dofunction do_build, do_contents, do_control; +dofunction do_info, do_field, do_extract, do_vextract, do_fsystarfile; + +extern int debugflag, nocheckflag, oldformatflag; +extern const struct cmdinfo *cipaction; +extern dofunction *action; + +void extracthalf(const char *debar, const char *directory, + const char *taroption, int admininfo); + +#define DEBMAGIC "!\ndebian-binary " +#define ADMINMEMBER "control.tar.gz " +#define DATAMEMBER "data.tar.gz " + +#endif /* DPKG_DEB_H */ diff --git a/dpkg-deb/extract.c b/dpkg-deb/extract.c new file mode 100644 index 00000000..8c9327fa --- /dev/null +++ b/dpkg-deb/extract.c @@ -0,0 +1,316 @@ +/* + * dpkg-deb - construction and deconstruction of *.deb archives + * extract.c - extracting archives + * + * Copyright (C) 1994,1995 Ian Jackson + * + * This 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, + * or (at your option) any later version. + * + * This 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 dpkg; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "dpkg.h" +#include "dpkg-deb.h" +#include "myopt.h" + +static void movecontrolfiles(const char *thing) { + char buf[200]; + pid_t c1; + + sprintf(buf, "mv %s/* . && rmdir %s", thing, thing); + if (!(c1= m_fork())) { + execlp("sh","sh","-c",buf,(char*)0); ohshite("failed to exec sh -c mv foo/* &c"); + } + waitsubproc(c1,"sh -c mv foo/* &c",0); +} + +static void readfail(FILE *a, const char *filename, const char *what) { + if (ferror(a)) { + ohshite("error reading %s from %.255s",what,filename); + } else { + ohshit("unexpected end of file in %s in %.255s",what,filename); + } +} + +static unsigned long parseheaderlength(const char *inh, size_t len, + const char *fn, const char *what) { + char lintbuf[15]; + unsigned long r; + char *endp; + + if (memchr(inh,0,len)) + ohshit("file `%.250s' is corrupt - %.250s length contains nulls",fn,what); + assert(sizeof(lintbuf) > len); + memcpy(lintbuf,inh,len); + lintbuf[len]= ' '; + *strchr(lintbuf,' ')= 0; + r= strtoul(lintbuf,&endp,10); + if (*endp) + ohshit("file `%.250s' is corrupt - bad digit (code %d) in %s",fn,*endp,what); + return r; +} + +static void skipmember(FILE *ar, const char *fn, long memberlen) { + int c; + + memberlen += (memberlen&1); + while (memberlen > 0) { + if ((c= getc(ar)) == EOF) readfail(ar,fn,"skipped member data"); + memberlen--; + } +} + +void extracthalf(const char *debar, const char *directory, + const char *taroption, int admininfo) { + char versionbuf[40]; + float versionnum; + char ctrllenbuf[40], *infobuf; + long ctrllennum, memberlen= 0; + int dummy, l= 0; + pid_t c1=0,c2,c3; + unsigned char *ctrlarea= 0; + int p1[2], p2[2]; + FILE *ar, *pi; + struct stat stab; + char nlc; + char *cur; + struct ar_hdr arh; + int readfromfd, oldformat, header_done, adminmember, c; + + ar= fopen(debar,"r"); if (!ar) ohshite("failed to read archive `%.255s'",debar); + if (fstat(fileno(ar),&stab)) ohshite("failed to fstat archive"); + if (!fgets(versionbuf,sizeof(versionbuf),ar)) readfail(ar,debar,"version number"); + + if (!strcmp(versionbuf,"!\n")) { + oldformat= 0; + + ctrllennum= 0; + header_done= 0; + for (;;) { + if (fread(&arh,1,sizeof(arh),ar) != sizeof(arh)) + readfail(ar,debar,"between members"); + if (memcmp(arh.ar_fmag,ARFMAG,sizeof(arh.ar_fmag))) + ohshit("file `%.250s' is corrupt - bad magic at end of first header",debar); + memberlen= parseheaderlength(arh.ar_size,sizeof(arh.ar_size), + debar,"member length"); + if (memberlen<0) + ohshit("file `%.250s' is corrupt - negative member length %ld",debar,memberlen); + if (!header_done) { + if (memcmp(arh.ar_name,"debian-binary ",sizeof(arh.ar_name))) + ohshit("file `%.250s' is not a debian binary archive (try dpkg-split?)",debar); + infobuf= m_malloc(memberlen+1); + if (fread(infobuf,1, memberlen + (memberlen&1), ar) != memberlen + (memberlen&1)) + readfail(ar,debar,"header info member"); + infobuf[memberlen]= 0; + cur= strchr(infobuf,'\n'); + if (!cur) ohshit("archive has no newlines in header"); + *cur= 0; + cur= strchr(infobuf,'.'); + if (!cur) ohshit("archive has no dot in version number"); + *cur= 0; + if (strcmp(infobuf,"2")) + ohshit("archive version %.250s not understood, get newer " BACKEND, infobuf); + *cur= '.'; + strncpy(versionbuf,infobuf,sizeof(versionbuf)); + versionbuf[sizeof(versionbuf)-1]= 0; + header_done= 1; + } else if (arh.ar_name[0] == '_') { + /* Members with `_' are noncritical, and if we don't understand them + * we skip them. + */ + skipmember(ar,debar,memberlen); + } else { + adminmember= + !memcmp(arh.ar_name,ADMINMEMBER,sizeof(arh.ar_name)) ? 1 : + !memcmp(arh.ar_name,DATAMEMBER,sizeof(arh.ar_name)) ? 0 : + -1; + if (adminmember == -1) { + ohshit("file `%.250s' contains ununderstood data member %.*s, giving up", + debar, (int)sizeof(arh.ar_name), arh.ar_name); + } + if (adminmember == 1) { + if (ctrllennum != 0) + ohshit("file `%.250s' contains two control members, giving up", debar); + ctrllennum= memberlen; + } + if (!adminmember != !admininfo) { + skipmember(ar,debar,memberlen); + } else { + break; /* Yes ! - found it. */ + } + } + } + + if (admininfo >= 2) + if (printf(" new debian package, version %s.\n" + " size %ld bytes: control archive= %ld bytes.\n", + versionbuf, (long)stab.st_size, ctrllennum) == EOF || + fflush(stdout)) werr("stdout"); + + } else if (!strncmp(versionbuf,"0.93",4) && + sscanf(versionbuf,"%f%c%d",&versionnum,&nlc,&dummy) == 2 && + nlc == '\n') { + + oldformat= 1; + l= strlen(versionbuf); if (l && versionbuf[l-1]=='\n') versionbuf[l-1]=0; + if (!fgets(ctrllenbuf,sizeof(ctrllenbuf),ar)) + readfail(ar,debar,"ctrl information length"); + if (sscanf(ctrllenbuf,"%ld%c%d",&ctrllennum,&nlc,&dummy) !=2 || nlc != '\n') + ohshit("archive has malformatted ctrl len `%s'",ctrllenbuf); + + if (admininfo >= 2) + if (printf(" old debian package, version %s.\n" + " size %ld bytes: control archive= %ld, main archive= %ld.\n", + versionbuf, (long)stab.st_size, ctrllennum, + (long) (stab.st_size - ctrllennum - strlen(ctrllenbuf) - l)) == EOF || + fflush(stdout)) werr("stdout"); + + ctrlarea= malloc(ctrllennum); if (!ctrlarea) ohshite("malloc ctrlarea failed"); + + errno=0; if (fread(ctrlarea,1,ctrllennum,ar) != ctrllennum) + readfail(ar,debar,"ctrlarea"); + + } else { + ohshit("`%.255s' is not a debian format archive",debar); + } + + fflush(ar); + if (oldformat) { + if (admininfo) { + m_pipe(p1); + if (!(c1= m_fork())) { + close(p1[0]); + if (!(pi= fdopen(p1[1],"w"))) ohshite("failed to fdopen p1 in paste"); + errno=0; if (fwrite(ctrlarea,1,ctrllennum,pi) != ctrllennum) + ohshit("failed to write to gzip -dc"); + if (fclose(pi)) ohshit("failed to close gzip -dc"); + exit(0); + } + close(p1[1]); + readfromfd= p1[0]; + } else { + if (lseek(fileno(ar),l+strlen(ctrllenbuf)+ctrllennum,SEEK_SET) == -1) + ohshite("failed to syscall lseek to files archive portion"); + c1= -1; + readfromfd= fileno(ar); + } + } else { + m_pipe(p1); + if (!(c1= m_fork())) { + close(p1[0]); + if (!(pi= fdopen(p1[1],"w"))) ohshite("failed to fdopen p1 in copy"); + while (memberlen > 0) { + if ((c= getc(ar)) == EOF) readfail(ar,debar,"member data"); + if (putc(c,pi) == EOF) ohshite("failed to write to pipe in copy"); + memberlen--; + } + if (fclose(pi) == EOF) ohshite("failed to close pipe in copy"); + exit(0); + } + close(p1[1]); + readfromfd= p1[0]; + } + + if (taroption) m_pipe(p2); + + if (!(c2= m_fork())) { + m_dup2(readfromfd,0); + if (admininfo) close(p1[0]); + if (taroption) { m_dup2(p2[1],1); close(p2[0]); close(p2[1]); } + execlp(GZIP,"gzip","-dc",(char*)0); ohshite("failed to exec gzip -dc"); + } + if (readfromfd != fileno(ar)) close(readfromfd); + close(p2[1]); + + if (taroption && directory) { + if (chdir(directory)) { + if (errno == ENOENT) { + if (mkdir(directory,0777)) ohshite("failed to create directory"); + if (chdir(directory)) ohshite("failed to chdir to directory after creating it"); + } else { + ohshite("failed to chdir to directory"); + } + } + } + + if (taroption) { + if (!(c3= m_fork())) { + char buffer[30+2]; + if(strlen(taroption) > 30) internerr(taroption); + strcpy(buffer, taroption); + strcat(buffer, "f"); + m_dup2(p2[0],0); + execlp(TAR,"tar",buffer,"-",(char*)0); + ohshite("failed to exec tar"); + } + close(p2[0]); + waitsubproc(c3,"tar",0); + } + + waitsubproc(c2,"gzip -dc",1); + if (c1 != -1) waitsubproc(c1,"paste",0); + if (oldformat && admininfo) { + if (versionnum == 0.931F) { + movecontrolfiles(OLDOLDDEBDIR); + } else if (versionnum == 0.932F || versionnum == 0.933F) { + movecontrolfiles(OLDDEBDIR); + } + } +} + +static void controlextractvextract(int admin, + const char *taroptions, + const char *const *argv) { + const char *debar, *directory; + + if (!(debar= *argv++)) + badusage("--%s needs a .deb filename argument",cipaction->olong); + if (!(directory= *argv++)) { + if (admin) directory= EXTRACTCONTROLDIR; + else ohshit("--%s needs a target directory.\n" + "Perhaps you should be using " DPKG " --install ?",cipaction->olong); + } else if (*argv) { + badusage("--%s takes at most two arguments (.deb and directory",cipaction->olong); + } + extracthalf(debar, directory, taroptions, admin); +} + +void do_fsystarfile(const char *const *argv) { + const char *debar; + + if (!(debar= *argv++)) + badusage("--%s needs a .deb filename argument",cipaction->olong); + if (*argv) + badusage("--%s takes only one argument (.deb filename)",cipaction->olong); + extracthalf(debar,0,0,0); +} + +void do_control(const char *const *argv) { controlextractvextract(1, "x", argv); } +void do_extract(const char *const *argv) { controlextractvextract(0, "xp", argv); } +void do_vextract(const char *const *argv) { controlextractvextract(0, "xpv", argv); } diff --git a/dpkg-deb/info.c b/dpkg-deb/info.c new file mode 100644 index 00000000..987a05bd --- /dev/null +++ b/dpkg-deb/info.c @@ -0,0 +1,240 @@ +/* + * dpkg-deb - construction and deconstruction of *.deb archives + * info.c - providing information + * + * Copyright (C) 1994,1995 Ian Jackson + * + * This 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, + * or (at your option) any later version. + * + * This 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 dpkg; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "dpkg.h" +#include "dpkg-deb.h" +#include "myopt.h" + +static void cu_info_prepare(int argc, void **argv) { + pid_t c1; + int status; + char *directory; + struct stat stab; + + directory= (char*)(argv[0]); + if (chdir("/")) { perror("failed to chdir to `/' for cleanup"); return; } + if (lstat(directory,&stab) && errno==ENOENT) return; + if ((c1= fork()) == -1) { perror("failed to fork for cleanup"); return; } + if (!c1) { + execlp(RM,"rm","-r",directory,(char*)0); + perror("failed to exec " RM " for cleanup"); _exit(1); + } + if (waitpid(c1,&status,0) != c1) { perror("failed to wait for rm cleanup"); return; } + if (status) { fprintf(stderr,"rm cleanup failed, code %d\n",status); } +} + +static void info_prepare(const char *const **argvp, + const char **debarp, + const char **directoryp, + int admininfo) { + static char dbuf[L_tmpnam]; + pid_t c1; + + *debarp= *(*argvp)++; + if (!*debarp) badusage("--%s needs a .deb filename argument",cipaction->olong); + if (!tmpnam(dbuf)) ohshite("failed to make temporary filename"); + *directoryp= dbuf; + + if (!(c1= m_fork())) { + execlp(RM,"rm","-rf",dbuf,(char*)0); ohshite("failed to exec rm -rf"); + } + waitsubproc(c1,"rm -rf",0); + push_cleanup(cu_info_prepare,-1, 0,0, 1, (void*)dbuf); + extracthalf(*debarp, dbuf, "mx", admininfo); +} + +static int ilist_select(const struct dirent *de) { + return strcmp(de->d_name,".") && strcmp(de->d_name,".."); +} + +static void info_spew(const char *debar, const char *directory, + const char *const *argv) { + const char *component; + FILE *co; + pid_t c1; + int re= 0; + + while ((component= *argv++) != 0) { + co= fopen(component,"r"); + if (co) { + if (!(c1= m_fork())) { + m_dup2(fileno(co),0); + execlp(CAT,"cat",(char*)0); ohshite("failed to exec cat component"); + } + waitsubproc(c1,"cat component",0); + } else if (errno == ENOENT) { + if (fprintf(stderr, BACKEND ": `%.255s' contains no control component `%.255s'\n", + debar, component) == EOF) werr("stderr"); + re= 1; + } else { + ohshite("open component `%.255s' (in %.255s) failed in an unexpected way", + component, directory); + } + } + if (re) ohshit("at least one requested control component missing"); +} + +static void info_list(const char *debar, const char *directory) { + char interpreter[INTERPRETER_MAX+1], *p; + int il, lines; + struct dirent **cdlist, *cdep; + int cdn; + FILE *cc; + struct stat stab; + int c; + + cdn= scandir(".", &cdlist, &ilist_select, alphasort); + if (cdn == -1) ohshite("cannot scan directory `%.255s'",directory); + + while (cdn-- >0) { + cdep= *cdlist++; + if (stat(cdep->d_name,&stab)) + ohshite("cannot stat `%.255s' (in `%.255s')",cdep->d_name,directory); + if (S_ISREG(stab.st_mode)) { + if (!(cc= fopen(cdep->d_name,"r"))) + ohshite("cannot open `%.255s' (in `%.255s')",cdep->d_name,directory); + lines= 0; interpreter[0]= 0; + if ((c= getc(cc))== '#') { + if ((c= getc(cc))== '!') { + while ((c= getc(cc))== ' '); + p=interpreter; *p++='#'; *p++='!'; il=2; + while (ild_name,directory); + fclose(cc); + if (printf(" %7ld bytes, %5d lines %c %-20.127s %.127s\n", + (long)stab.st_size, lines, + S_IXUSR & stab.st_mode ? '*' : ' ', + cdep->d_name, interpreter) == EOF) + werr("stdout"); + } else { + if (printf(" not a plain file %.255s\n",cdep->d_name) == EOF) + werr("stdout"); + } + } + if (!(cc= fopen("control","r"))) { + if (errno != ENOENT) ohshite("failed to read `control' (in `%.255s')",directory); + if (!fputs("(no `control' file in control archive!)\n",stdout)) werr("stdout"); + } else { + lines= 1; + while ((c= getc(cc))!= EOF) { + if (lines) if (putc(' ',stdout) == EOF) werr("stdout"); + if (putc(c,stdout) == EOF) werr("stdout"); + lines= c=='\n'; + } + if (!lines) if (putc('\n',stdout) == EOF) werr("stdout"); + } +} + +static void info_field(const char *debar, const char *directory, + const char *const *fields, int showfieldname) { + FILE *cc; + char fieldname[MAXFIELDNAME+1]; + char *pf; + const char *const *fp; + int doing, c, lno, fnl; + + if (!(cc= fopen("control","r"))) ohshite("could not open the `control' component"); + doing= 1; lno= 1; + for (;;) { + c= getc(cc); if (c==EOF) { doing=0; break; } + if (c == '\n') { lno++; doing=1; continue; } + if (!isspace(c)) { + doing= 0; + for (pf=fieldname, fnl=0; + fnl <= MAXFIELDNAME && c!=EOF && !isspace(c) && c!=':'; + c= getc(cc)) { *pf++= c; fnl++; } + *pf++= 0; + doing= fnl >= MAXFIELDNAME || c=='\n' || c==EOF; + for (fp=fields; !doing && *fp; fp++) + if (!strcasecmp(*fp,fieldname)) doing=1; + if (showfieldname) { + if (doing) + fputs(fieldname,stdout); + } else { + if (c==':') c= getc(cc); + while (c != '\n' && isspace(c)) c= getc(cc); + } + } + for(;;) { + if (c == EOF) break; + if (doing) putc(c,stdout); + if (c == '\n') { lno++; break; } + c= getc(cc); + } + if (c == EOF) break; + } + if (ferror(cc)) ohshite("failed during read of `control' component"); + if (doing) putc('\n',stdout); + if (ferror(stdout)) werr("stdout"); +} + +void do_info(const char *const *argv) { + const char *debar, *directory; + + if (*argv && argv[1]) { + info_prepare(&argv,&debar,&directory,1); + info_spew(debar,directory, argv); + } else { + info_prepare(&argv,&debar,&directory,2); + info_list(debar,directory); + } +} + +void do_field(const char *const *argv) { + const char *debar, *directory; + + info_prepare(&argv,&debar,&directory,1); + if (*argv) { + info_field(debar, directory, argv, argv[1]!=0); + } else { + static const char *const controlonly[]= { "control", 0 }; + info_spew(debar,directory, controlonly); + } +} + +void do_contents(const char *const *argv) { + const char *debar; + + if (!(debar= *argv++) || *argv) badusage("--contents takes exactly one argument"); + extracthalf(debar, 0, "tv", 0); +} diff --git a/dpkg-deb/main.c b/dpkg-deb/main.c new file mode 100644 index 00000000..0804ac72 --- /dev/null +++ b/dpkg-deb/main.c @@ -0,0 +1,151 @@ +/* + * dpkg-deb - construction and deconstruction of *.deb archives + * main.c - main program + * + * Copyright (C) 1994,1995 Ian Jackson + * + * This 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, + * or (at your option) any later version. + * + * This 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 dpkg; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "dpkg.h" +#include "version.h" +#include "myopt.h" +#include "dpkg-deb.h" + +static void printversion(void) { + if (!fputs("Debian GNU/Linux `" BACKEND "' package archive backend " + "version " DPKG_VERSION_ARCH ".\n" + "Copyright (C) 1994,1995 Ian Jackson. This is free software; see the\n" + "GNU General Public Licence version 2 or later for copying conditions.\n" + "There is NO warranty. See dpkg-deb --licence for details.\n", + stderr)) werr("stderr"); +} + +static void usage(void) { + if (!fputs("\ +Usage: " BACKEND " -b|--build [] Build an archive.\n\ + " BACKEND " -c|--contents List contents.\n\ + " BACKEND " -I|--info [...] Show info to stdout.\n\ + " BACKEND " -f|--field [...] Show field(s) to stdout.\n\ + " BACKEND " -e|--control [] Extract control info.\n\ + " BACKEND " -x|--extract Extract files.\n\ + " BACKEND " -X|--vextract Extract & list files.\n\ + " BACKEND " --fsys-tarfile Output filesystem tarfile.\n\ + " BACKEND " -h|--help Display this message.\n\ + " BACKEND " --version | --licence Show version/licence.\n\ + is the filename of a Debian format archive.\n\ + is the name of an administrative file component.\n\ + is the name of a field in the main `control' file.\n\ +Options: -D for debugging output; --old or --new controls archive format;\n\ + --no-check to suppress control file check (build bad package).\n\ +\n\ +Use `" DPKG "' to install and remove packages from your system, or\n\ +`" DSELECT "' for user-friendly package management. Packages unpacked\n\ +using `" BACKEND " --extract' will be incorrectly installed !\n", + stderr)) werr("stderr"); +} + +const char thisname[]= BACKEND; +const char printforhelp[]= + "Type " BACKEND " --help for help about manipulating *.deb files;\n" + "Type " DPKG " --help for help about installing and deinstalling packages."; + +int debugflag=0, nocheckflag=0, oldformatflag=BUILDOLDPKGFORMAT; +const struct cmdinfo *cipaction=0; +dofunction *action=0; + +static void helponly(const struct cmdinfo *cip, const char *value) { + usage(); exit(0); +} +static void versiononly(const struct cmdinfo *cip, const char *value) { + printversion(); exit(0); +} + +static void setaction(const struct cmdinfo *cip, const char *value); + +static dofunction *const dofunctions[]= { + do_build, + do_contents, + do_control, + do_info, + do_field, + do_extract, + do_vextract, + do_fsystarfile +}; + +/* NB: the entries using setaction must appear first and be in the + * same order as dofunctions: + */ +static const struct cmdinfo cmdinfos[]= { + { "build", 'b', 0, 0, 0, setaction }, + { "contents", 'c', 0, 0, 0, setaction }, + { "control", 'e', 0, 0, 0, setaction }, + { "info", 'I', 0, 0, 0, setaction }, + { "field", 'f', 0, 0, 0, setaction }, + { "extract", 'x', 0, 0, 0, setaction }, + { "vextract", 'X', 0, 0, 0, setaction }, + { "fsys-tarfile", 0, 0, 0, 0, setaction }, + { "new", 0, 0, &oldformatflag, 0, 0, 0 }, + { "old", 0, 0, &oldformatflag, 0, 0, 1 }, + { "debug", 'D', 0, &debugflag, 0, 0, 1 }, + { "nocheck", 0, 0, &nocheckflag, 0, 0, 1 }, + { "help", 'h', 0, 0, 0, helponly }, + { "version", 0, 0, 0, 0, versiononly }, + { "licence", 0, 0, 0, 0, showcopyright }, /* UK spelling */ + { "license", 0, 0, 0, 0, showcopyright }, /* US spelling */ + { 0, 0 } +}; + +static void setaction(const struct cmdinfo *cip, const char *value) { + if (cipaction) + badusage("conflicting actions --%s and --%s",cip->olong,cipaction->olong); + cipaction= cip; + assert(cip-cmdinfos < sizeof(dofunctions)*sizeof(dofunction*)); + action= dofunctions[cip-cmdinfos]; +} + +int main(int argc, const char *const *argv) { + jmp_buf ejbuf; + + if (setjmp(ejbuf)) { /* expect warning about possible clobbering of argv */ + error_unwind(ehflag_bombout); exit(2); + } + push_error_handler(&ejbuf,print_error_fatal,0); + + myopt(&argv,cmdinfos); + if (!cipaction) badusage("need an action option"); + + unsetenv("GZIP"); + action(argv); + set_error_display(0,0); + error_unwind(ehflag_normaltidy); + exit(0); +} diff --git a/dpkg-deb/mkdeb.sh b/dpkg-deb/mkdeb.sh new file mode 100644 index 00000000..146dd536 --- /dev/null +++ b/dpkg-deb/mkdeb.sh @@ -0,0 +1,67 @@ +#!/bin/bash +# This script is only supposed to be called by dpkg-deb. +# Its arguments are: +# Stdin is also redirected from the source archive by dpkg-split. + +# Copyright (C) 1995 Ian Jackson +# +# This 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, +# or (at your option) any later version. +# +# This 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 dpkg; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +set -e + +if [ "$#" != 4 ]; then echo >&2 'Bad invocation of mksplit.sh.'; exit 1; fi + +sourcefile="$1" +partsize="$2" +prefix="$3" +orgsize="$4" + +myversion=2.0 +binary=i386-linux +#csum=`md5sum <"$sourcefile"` +#package="`dpkg-deb --field \"$sourcefile\" Package`" +#version="`dpkg-deb --field \"$sourcefile\" Version`" +#revision="`dpkg-deb --field \"$sourcefile\" Package_Revision`" +startat=0 +partnum=0 + +mkdir /tmp/ds$$ +ec=1 +trap "rm -r /tmp/ds$$; exit $ec" 0 +dsp=/tmp/ds$$/debian-binary + +echo -n "Splitting package $package into $nparts parts: " + +while [ $startat -lt $orgsize ] +do + showpartnum=$[$partnum+1] + echo $myversion >$dsp + echo $binary >>$dsp + tar -C DEBIAN -cf /tmp/ds$$/control . + tar -- + dd bs=$partsize skip=$partnum count=1 \ + of=/tmp/ds$$/data.$showpartnum \ + 2>&1 | (egrep -v '.* records (in|out)' || true) + rm -f /tmp/ds$$/part + echo -n "$showpartnum " + (cd /tmp/ds$$ && + ar qc part debian-split data.$showpartnum) + mv /tmp/ds$$/part $prefix.${showpartnum}of$nparts.deb + startat=$[$startat+$partsize] + partnum=$showpartnum +done +echo "done" + +ec=0 diff --git a/dselect/Makefile.in b/dselect/Makefile.in new file mode 100644 index 00000000..1ef6f9cd --- /dev/null +++ b/dselect/Makefile.in @@ -0,0 +1,119 @@ +# Copyright (C) 1994 Ian Murdock +# Copyright (C) 1994,1995 Ian Jackson +# +# This 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, +# or (at your option) any later version. +# +# This 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 dpkg; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +srcdir = @srcdir@ +VPATH = @srcdir@ + +prefix = @prefix@ +exec_prefix = $(prefix) +bindir = $(exec_prefix)/bin +libdir = $(prefix)/lib +mandir = $(prefix)/man +man8dir = $(mandir)/man8 +man8 = 8 + +SRC = \ + main.cc bindings.cc curkeys.cc helpmsgs.cc \ + basecmds.cc baselist.cc basetop.cc \ + pkgcmds.cc pkgdepcon.cc pkgdisplay.cc pkginfo.cc pkgkeys.cc \ + pkglist.cc pkgsublist.cc pkgtop.cc \ + methkeys.cc method.cc methparse.cc methlist.cc + +OBJ = \ + main.o bindings.o curkeys.o helpmsgs.o \ + basecmds.o baselist.o basetop.o \ + pkgcmds.o pkgdepcon.o pkgdisplay.o pkginfo.o pkgkeys.o \ + pkglist.o pkgsublist.o pkgtop.o \ + methkeys.o method.o methparse.o methlist.o + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ + +CC = @CC@ +CPLUSPLUS = @CXX@ + +CFLAGS = @CFLAGS@ @CWARNS@ -g $(XCFLAGS) +OPTCFLAGS = @OPTCFLAGS@ +LDFLAGS = $(XLDFLAGS) + +EXTERNLIBS = -lncurses +LIBS = -L../lib -ldpkg $(EXTERNLIBS) $(XLIBS) +ALL_CFLAGS = -I../include -I.. @DEFS@ $(CFLAGS) +ALL_CFLAGS_OPT = $(ALL_CFLAGS) $(OPTCFLAGS) + +.SUFFIXES: .cc .o .c + +.cc.o: + $(CPLUSPLUS) $(ALL_CFLAGS) -c $< + +.c.o: + $(CC) $(ALL_CFLAGS) -c $< + +all: dselect + +dselect: $(OBJ) ../lib/libdpkg.a + $(CC) $(LDFLAGS) -o dselect $(OBJ) $(LIBS) + +# These next few files are very heavily used, and should be optimised +# for speed rather than space. (ALL_CFLAGS_OPT usually means -O3.) +pkgdepcon.o: pkgdepcon.cc + $(CPLUSPLUS) $(ALL_CFLAGS_OPT) -c $< + +pkgdisplay.o: pkgdisplay.cc + $(CPLUSPLUS) $(ALL_CFLAGS_OPT) -c $< + +curkeys.o: curkeys.cc curkeys.inc + $(CPLUSPLUS) $(ALL_CFLAGS) -c curkeys.cc + +helpmsgs.h helpmsgs.cc: helpmsgs.src mkhelpmsgs.pl + perl mkhelpmsgs.pl + +curkeys.inc: keyoverride mkcurkeys.pl + perl mkcurkeys.pl keyoverride \ + `echo '#include ' | \ + $(CC) -E - | grep ncurses | head -1 | \ + sed -e 's/^[^"]*"//; s/".*$$//'` > curkeys.inc.new + mv curkeys.inc.new curkeys.inc + +kt: kt.o + $(CC) $(LDFLAGS) -o kt kt.o $(LIBS) + +kt.o: kt.c curkeys.inc + +clean: + rm -f curkeys.inc curkeys.inc.new + rm -f helpmsgs.h helpmsgs.cc helpmsgs.h.new helpmsgs.cc.new + rm -f *.o core dselect kt + +distclean: clean + rm -f Makefile *.orig *~ *.~* ./#*# + rm -rf t updates status available *.old + +install: all + $(INSTALL_PROGRAM) -s dselect $(bindir)/dselect + +# $(INSTALL_DATA) dselect.8 $(man8dir)/dselect.$(man8) + +$(OBJ): dselect.h ../config.h ../include/dpkg.h ../include/dpkg-db.h +main.o: ../version.h ../include/myopt.h +method.o: method.h +pkgcmds.o pkgdepcon.o pkgdisplay.o pkgtop.o: pkglist.h +baselist.o bindings.o curkeys.o main.o: bindings.h method.h pkglist.h +methlist.o methkeys.o pkginfo.o pkgkeys.o: bindings.h method.h pkglist.h +pkglist.o pkgsublist.o methparse.o: bindings.h method.h pkglist.h +helpmsgs.o methlist.o pkginfo.o basecmds.o: helpmsgs.h helpmsgs.cc diff --git a/dselect/basecmds.cc b/dselect/basecmds.cc new file mode 100644 index 00000000..9181601b --- /dev/null +++ b/dselect/basecmds.cc @@ -0,0 +1,264 @@ +/* + * dselect - Debian GNU/Linux package maintenance user interface + * bcommands.cc - base list keyboard commands display + * + * Copyright (C) 1994,1995 Ian Jackson + * + * This 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, + * or (at your option) any later version. + * + * This 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; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include + +extern "C" { +#include "config.h" +#include "dpkg.h" +#include "dpkg-db.h" +} +#include "dselect.h" +#include "helpmsgs.h" + +void baselist::kd_scrollon() { + topofscreen += list_height; + if (topofscreen > nitems - list_height) topofscreen= nitems - list_height; + if (topofscreen < 0) topofscreen= 0; + if (cursorline < topofscreen) + setcursor(topofscreen); + refreshlist(); +} + +void baselist::kd_scrollon1() { + if (topofscreen >= nitems - list_height) return; + topofscreen++; + if (cursorline < topofscreen) + setcursor(topofscreen); + refreshlist(); +} + +void baselist::kd_scrollback() { + topofscreen -= list_height; + if (topofscreen < 0) topofscreen= 0; + if (cursorline >= topofscreen + list_height) + setcursor(topofscreen + list_height - 1); + refreshlist(); +} + +void baselist::kd_scrollback1() { + if (topofscreen <= 0) return; + topofscreen--; + if (cursorline >= topofscreen + list_height) + setcursor(topofscreen + list_height - 1); + refreshlist(); +} + +void baselist::kd_top() { + topofscreen= 0; setcursor(0); +} + +void baselist::kd_bottom() { + topofscreen= nitems - list_height; + if (topofscreen < 0) topofscreen= 0; + setcursor(lesserint(topofscreen + list_height - 1, nitems-1)); +} + +void baselist::kd_redraw() { +//#define RFSH(x) werase(x); redrawwin(x) +// RFSH(listpad); +// RFSH(infopad); +// RFSH(colheadspad); +// RFSH(thisstatepad); +// RFSH(titlewin); +// RFSH(whatinfowin); /* fixme-ncurses: why does ncurses need this ? */ + clearok(curscr,TRUE); + redrawall(); + if (debug) fprintf(debug,"baselist[%p]::kd_redraw() done\n",this); +} + +void baselist::kd_searchagain() { + if (!searchstring[0]) { beep(); return; } + dosearch(); +} + +void baselist::kd_search() { + werase(querywin); + mvwaddstr(querywin,0,0, "Search for ? "); + echo(); + /* fixme: make / RET do `search again' and / DEL to abort */ + if (wgetnstr(querywin,searchstring,sizeof(searchstring)-1) == ERR) + searchstring[0]= 0; + noecho(); + if (whatinfo_height) { touchwin(whatinfowin); refreshinfo(); } + else if (info_height) { touchwin(infopad); refreshinfo(); } + else if (thisstate_height) redrawthisstate(); + else { touchwin(listpad); refreshlist(); } + if (searchstring[0]) dosearch(); +} + +void baselist::displayhelp(const struct helpmenuentry *helpmenu, int key) { + const struct helpmenuentry *hme; + int maxx, maxy, i, y, x, nextkey; + + getmaxyx(stdscr,maxy,maxx); + clearok(stdscr,TRUE); + for (;;) { + werase(stdscr); + for (hme= helpmenu; hme->key && hme->key != key; hme++); + if (hme->key) { + attrset(list_attr); + mvaddstr(1,0, hme->msg->text); + attrset(title_attr); + mvaddstr(0,0, "Help: "); + addstr(hme->msg->title); + getyx(stdscr,y,x); + while (++xkey; hme++,i++) { + attrset(A_BOLD); + mvaddch(i+3,3, hme->key); + attrset(A_NORMAL); + mvaddstr(i+3,6, hme->msg->title); + } + mvaddstr(i+4,1, + "Press a key from the list above, Space to exit help,\n" + " or `.' (full stop) to read each help page in turn. "); + nextkey= helpmenu[0].key; + } + refresh(); + key= getch(); + if (key == EOF) ohshite("error reading keyboard in help"); + if (key == ' ') { + break; + } else if (key == '?') { + key= 0; + } else if (key == '.') { + key= nextkey; + } + } + werase(stdscr); + clearok(stdscr,TRUE); + wnoutrefresh(stdscr); + + redrawtitle(); + refreshcolheads(); + refreshlist(); + redrawthisstate(); + refreshinfo(); + wnoutrefresh(whatinfowin); +} + +void baselist::kd_help() { + displayhelp(helpmenulist(),0); +} + +void baselist::setcursor(int index) { + if (listpad && cursorline != -1) { + redrawitemsrange(cursorline,cursorline+1); + redraw1itemsel(cursorline,0); + } + if (cursorline != index) infotopofscreen= 0; + cursorline= index; + if (listpad) { + redrawitemsrange(cursorline,cursorline+1); + redraw1itemsel(cursorline,1); + refreshlist(); + redrawthisstate(); + } + redrawinfo(); +} + +void baselist::kd_down() { + int ncursor= cursorline; + // scroll by one line unless the bottom is already visible + // or we're in the top half of the screen ... + if (topofscreen < nitems - list_height && + ncursor >= topofscreen + list_height - 3) topofscreen++; + // move cursor if there are any more ... + if (cursorline+1 < nitems) ncursor++; + setcursor(ncursor); +} + +void baselist::kd_up() { + int ncursor= cursorline; + // scroll by one line if there are any lines not shown yet + // and we're not in the bottom half the screen ... + if (topofscreen > 0 && + ncursor <= topofscreen + 2) topofscreen--; + // move cursor if there are any to move it to ... + if (cursorline > 0) ncursor--; + setcursor(ncursor); +} + +void baselist::kd_iscrollon() { + infotopofscreen += info_height; + if (infotopofscreen > infolines - info_height) + infotopofscreen= infolines - info_height; + if (infotopofscreen < 0) infotopofscreen= 0; + refreshinfo(); +} + +void baselist::kd_iscrollback() { + infotopofscreen -= info_height; + if (infotopofscreen < 0) infotopofscreen= 0; + refreshinfo(); +} + +void baselist::kd_iscrollon1() { + if (infotopofscreen >= infolines - info_height) return; + infotopofscreen++; refreshinfo(); +} + +void baselist::kd_iscrollback1() { + if (infotopofscreen <= 0) return; + infotopofscreen--; refreshinfo(); +} + +void baselist::kd_panon() { + leftofscreen += xmax/3; + if (leftofscreen > total_width - xmax) leftofscreen= total_width - xmax; + if (leftofscreen < 0) leftofscreen= 0; + refreshcolheads(); refreshlist(); redrawthisstate(); refreshinfo(); +} + +void baselist::kd_panback() { + leftofscreen -= xmax/3; + if (leftofscreen < 0) leftofscreen= 0; + refreshcolheads(); refreshlist(); redrawthisstate(); refreshinfo(); +} + +void baselist::kd_panon1() { + leftofscreen++; + if (leftofscreen > total_width - xmax) leftofscreen= total_width - xmax; + if (leftofscreen < 0) leftofscreen= 0; + refreshcolheads(); refreshlist(); redrawthisstate(); refreshinfo(); +} + +void baselist::kd_panback1() { + leftofscreen--; + if (leftofscreen < 0) leftofscreen= 0; + refreshcolheads(); refreshlist(); redrawthisstate(); refreshinfo(); +} diff --git a/dselect/baselist.cc b/dselect/baselist.cc new file mode 100644 index 00000000..5f7e8924 --- /dev/null +++ b/dselect/baselist.cc @@ -0,0 +1,351 @@ +/* + * dselect - Debian GNU/Linux package maintenance user interface + * baselist.cc - list of somethings + * + * Copyright (C) 1994,1995 Ian Jackson + * + * This 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, + * or (at your option) any later version. + * + * This 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; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include + +extern "C" { +#include "config.h" +#include "dpkg.h" +#include "dpkg-db.h" +} +#include "dselect.h" +#include "bindings.h" + +void mywerase(WINDOW *win) { + int my,mx,y,x; + getmaxyx(win,my,mx); + for (y=0; yenddisplay(); + endwin(); initscr(); + p->startdisplay(); + if (doupdate() == ERR) ohshite("doupdate in SIGWINCH handler failed"); +} + +static void cu_sigwinch(int, void **argv) { + struct sigaction *osigactp= (struct sigaction*)argv[0]; + sigset_t *oblockedp= (sigset_t*)argv[1]; + + if (sigaction(SIGWINCH,osigactp,0)) ohshite("failed to restore old SIGWINCH sigact"); + delete osigactp; + if (sigprocmask(SIG_SETMASK,oblockedp,0)) ohshite("failed to restore old signal mask"); + delete oblockedp; +} + +void baselist::setupsigwinch() { + sigemptyset(&sigwinchset); + sigaddset(&sigwinchset,SIGWINCH); + + osigactp= new(struct sigaction); + oblockedp= new(sigset_t); + if (sigprocmask(0,0,oblockedp)) ohshite("failed to get old signal mask"); + if (sigaction(SIGWINCH,0,osigactp)) ohshite("failed to get old SIGWINCH sigact"); + + push_cleanup(cu_sigwinch,~0, 0,0, 2,(void*)osigactp,(void*)oblockedp); + + if (sigprocmask(SIG_BLOCK,&sigwinchset,0)) ohshite("failed to block SIGWINCH"); + memset(&nsigact,0,sizeof(nsigact)); + nsigact.sa_handler= sigwinchhandler; + sigemptyset(&nsigact.sa_mask); + nsigact.sa_flags= SA_INTERRUPT; + if (sigaction(SIGWINCH,&nsigact,0)) ohshite("failed to set new SIGWINCH sigact"); +} + +void baselist::startdisplay() { + if (debug) fprintf(debug,"baselist[%p]::startdisplay()\n",this); + cbreak(); noecho(); nonl(); keypad(stdscr,TRUE); + clear(); wnoutrefresh(stdscr); + + // find attributes + if (has_colors() && start_color()==OK && COLOR_PAIRS >= 3) { + if (init_pair(1,COLOR_WHITE,COLOR_BLACK) != OK || + init_pair(2,COLOR_WHITE,COLOR_RED) != OK || + init_pair(3,COLOR_WHITE,COLOR_BLUE) != OK) + ohshite("failed to allocate colour pairs"); + list_attr= COLOR_PAIR(1); + listsel_attr= list_attr|A_REVERSE; + title_attr= COLOR_PAIR(2); + thisstate_attr= COLOR_PAIR(3); + selstate_attr= list_attr|A_BOLD; + selstatesel_attr= listsel_attr|A_BOLD; + } else { + title_attr= A_REVERSE; + thisstate_attr= A_STANDOUT; + list_attr= 0; + listsel_attr= A_STANDOUT; + selstate_attr= A_BOLD; + selstatesel_attr= A_STANDOUT; + } + query_attr= title_attr; + info_attr= list_attr; + colheads_attr= info_headattr= A_BOLD; + whatinfo_attr= thisstate_attr; + + // set up windows and pads, based on screen size + int y; + getmaxyx(stdscr,ymax,xmax); + title_height= ymax>=6; + colheads_height= ymax>=5; + thisstate_height= ymax>=3; + whatinfo_height= ymax>=2; + y= ymax - (title_height + colheads_height + + thisstate_height + whatinfo_height); + assert(y>=1); + y-= list_height + info_height; + if (y>0) { + list_height += (y+1)/2; + info_height += y/2; + } else if (y<0) { + list_height -= (-y)/2; + info_height -= (-y+1)/2; + } + colheads_row= title_height; + list_row= colheads_row + colheads_height; + thisstate_row= list_row + list_height; + info_row= thisstate_row + thisstate_height; + whatinfo_row= info_row + info_height; + + setwidths(); + + titlewin= newwin(1,xmax, 0,0); + if (!titlewin) ohshite("failed to create title window"); + wattrset(titlewin,title_attr); + + whatinfowin= newwin(1,xmax, whatinfo_row,0); + if (!whatinfowin) ohshite("failed to create whatinfo window"); + wattrset(whatinfowin,whatinfo_attr); + + listpad= newpad(nitems+1, total_width); + if (!listpad) ohshite("failed to create baselist pad"); + + colheadspad= newpad(1, total_width); + if (!colheadspad) ohshite("failed to create heading pad"); + wattrset(colheadspad,colheads_attr); + + thisstatepad= newpad(1, total_width); + if (!thisstatepad) ohshite("failed to create thisstate pad"); + wattrset(thisstatepad,thisstate_attr); + + infopad= newpad(MAX_DISPLAY_INFO, total_width); + if (!infopad) ohshite("failed to create info pad"); + wattrset(infopad,info_attr); + + querywin= newwin(1,xmax,ymax-1,0); + if (!querywin) ohshite("failed to create query window"); + + if (cursorline >= topofscreen + list_height) topofscreen= cursorline; + if (topofscreen > nitems - list_height) topofscreen= nitems - list_height; + if (topofscreen < 0) topofscreen= 0; + + infotopofscreen= 0; leftofscreen= 0; + + redrawall(); + + if (debug) + fprintf(debug, + "baselist::startdisplay() done ...\n\n" + " xmax=%d, ymax=%d;\n\n" + " title_height=%d, colheads_height=%d, list_height=%d;\n" + " thisstate_height=%d, info_height=%d, whatinfo_height=%d;\n\n" + " colheads_row=%d, thisstate_row=%d, info_row=%d;\n" + " whatinfo_row=%d, list_row=%d;\n\n", + xmax, ymax, + title_height, colheads_height, list_height, + thisstate_height, info_height, whatinfo_height, + colheads_row, thisstate_row, info_row, + whatinfo_row, list_row); + +} + +void baselist::enddisplay() { + delwin(titlewin); + delwin(whatinfowin); + delwin(listpad); + delwin(colheadspad); + delwin(thisstatepad); + delwin(infopad); + wmove(stdscr,ymax,0); wclrtoeol(stdscr); + listpad= 0; +} + +void baselist::redrawall() { + redrawtitle(); + redrawcolheads(); + wattrset(listpad,list_attr); mywerase(listpad); + ldrawnstart= ldrawnend= -1; // start is first drawn; end is first undrawn; -1=none + refreshlist(); + redrawthisstate(); + redrawinfo(); +} + +void baselist::redraw1item(int index) { + redraw1itemsel(index, index == cursorline); +} + +baselist::baselist(keybindings *kb) { + if (debug) + fprintf(debug,"baselist[%p]::baselist()\n",this); + + bindings= kb; + nitems= 0; + + xmax= -1; + list_height=0; info_height=0; + topofscreen= 0; leftofscreen= 0; + listpad= 0; cursorline= -1; + + searchstring[0]= 0; +} + +void baselist::itd_keys() { + whatinfovb("keybindings"); + + const int givek= xmax/3; + bindings->describestart(); + const char **ta; + while ((ta= bindings->describenext()) != 0) { + const char **tap= ta+1; + for (;;) { + waddstr(infopad, *tap); + tap++; if (!*tap) break; + waddstr(infopad, ", "); + } + int y,x; + getyx(infopad,y,x); + if (x >= givek) y++; + mvwaddstr(infopad, y,givek, ta[0]); + waddch(infopad,'\n'); + } +} + +void baselist::dosearch() { + int offset, index, searchlen; + searchlen= strlen(searchstring); + if (debug) fprintf(debug,"packagelist[%p]::dosearch(); searchstring=`%s' len=%d\n", + this,searchstring,searchlen); + for (offset=1, index=greaterint(topofscreen,cursorline+1); + offset= nitems) index -= nitems; + int lendiff, i; + const char *thisname; + thisname= itemname(index); + if (!thisname) continue; + lendiff= strlen(thisname) - searchlen; + for (i=0; i<=lendiff; i++) + if (!strncasecmp(thisname + i, searchstring, searchlen)) { + topofscreen= index-1; + if (topofscreen > nitems - list_height) topofscreen= nitems-list_height; + if (topofscreen < 0) topofscreen= 0; + setcursor(index); + return; + } + } + beep(); +} + +void baselist::refreshinfo() { + pnoutrefresh(infopad, infotopofscreen,leftofscreen, info_row,0, + lesserint(info_row + info_height - 1, info_row + MAX_DISPLAY_INFO - 1), + lesserint(total_width - leftofscreen - 1, xmax - 1)); + + if (whatinfo_height) { + mywerase(whatinfowin); + mvwaddstr(whatinfowin,0,0, whatinfovb.string()); + if (infolines > info_height) { + wprintw(whatinfowin," -- %d%%, press ", + (int)((infotopofscreen + info_height) * 100.0 / infolines)); + if (infotopofscreen + info_height < infolines) { + wprintw(whatinfowin,"%s for more", bindings->find("iscrollon")); + if (infotopofscreen) waddstr(whatinfowin, ", "); + } + if (infotopofscreen) + wprintw(whatinfowin, "%s to go back",bindings->find("iscrollback")); + waddch(whatinfowin,'.'); + } + wnoutrefresh(whatinfowin); + } +} + +void baselist::wordwrapinfo(int offset, const char *m) { + int usemax= xmax-5; + if (debug) fprintf(debug,"baselist[%p]::wordwrapinfo(%d, `%s')\n",this,offset,m); + int wrapping=0; + for (;;) { + int offleft=offset; while (*m == ' ' && offleft>0) { m++; offleft--; } + const char *p= strchr(m,'\n'); + int l= p ? (int)(p-m) : strlen(m); + while (l && isspace(m[l-1])) l--; + if (!l || *m == '.' && l == 1) { + if (wrapping) waddch(infopad,'\n'); + waddch(infopad,'\n'); + wrapping= 0; + } else if (*m == ' ' || usemax < 10) { + if (wrapping) waddch(infopad,'\n'); + waddnstr(infopad, m, l); + waddch(infopad,'\n'); wrapping= 0; + } else { + int x,y; + if (wrapping) { + getyx(infopad, y,x); + if (x+1 >= usemax) { + waddch(infopad,'\n'); + } else { + waddch(infopad,' '); + } + } + for (;;) { + getyx(infopad, y,x); + int dosend= usemax-x; + if (l <= dosend) { + dosend=l; + } else { + int i=dosend; + while (i > 0 && m[i] != ' ') i--; + if (i > 0 || x > 0) dosend=i; + } + if (dosend) waddnstr(infopad, m, dosend); + while (dosend < l && m[dosend] == ' ') dosend++; + l-= dosend; m+= dosend; + if (l <= 0) break; + waddch(infopad,'\n'); + } + wrapping= 1; + } + if (!p) break; + m= ++p; + } + if (debug) fprintf(debug,"baselist[%p]::wordwrapinfo() done\n",this); +} + +baselist::~baselist() { } diff --git a/dselect/basetop.cc b/dselect/basetop.cc new file mode 100644 index 00000000..54b79275 --- /dev/null +++ b/dselect/basetop.cc @@ -0,0 +1,61 @@ +/* + * dselect - Debian GNU/Linux package maintenance user interface + * bdrawtop.cc - base list class redraw of top + * + * Copyright (C) 1994,1995 Ian Jackson + * + * This 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, + * or (at your option) any later version. + * + * This 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; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include + +extern "C" { +#include "config.h" +#include "dpkg.h" +#include "dpkg-db.h" +} +#include "dselect.h" + +void baselist::refreshlist() { + redrawitemsrange(topofscreen,lesserint(nitems,topofscreen+list_height)); + int y, x, maxy, maxx; + y= lesserint(list_row + list_height - 1, + list_row + nitems - topofscreen - 1); + x= lesserint(total_width - leftofscreen - 1, + xmax - 1); + pnoutrefresh(listpad, topofscreen,leftofscreen, list_row,0, y,x); + getmaxyx(listpad,maxy,maxx); + y++; + while (y < list_row + list_height - 1) { + pnoutrefresh(listpad, maxy-1,leftofscreen, y,0, y,x); + y++; + } +} + +void baselist::redrawitemsrange(int start, int end) { + if (ldrawnstart==-1) { ldrawnstart= ldrawnend= end; } + while (ldrawnstart > start) { ldrawnstart--; redraw1item(ldrawnstart); } + while (ldrawnend < end) { redraw1item(ldrawnend); ldrawnend++; } +} + +void baselist::refreshcolheads() { + pnoutrefresh(colheadspad, 0,leftofscreen, colheads_row,0, + colheads_row, lesserint(total_width - leftofscreen - 1, xmax - 1)); +} diff --git a/dselect/bindings.cc b/dselect/bindings.cc new file mode 100644 index 00000000..4aa38352 --- /dev/null +++ b/dselect/bindings.cc @@ -0,0 +1,166 @@ +/* + * dselect - Debian GNU/Linux package maintenance user interface + * bindings.cc - keybinding manager object definitions and default bindings + * + * Copyright (C) 1995 Ian Jackson + * + * This 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, + * or (at your option) any later version. + * + * This 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; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include + +extern "C" { +#include "config.h" +#include "dpkg.h" +#include "dpkg-db.h" +} +#include "dselect.h" +#include "bindings.h" + +keybindings::keybindings(const interpretation *ints, const orgbinding *orgbindings) { + interps= ints; + bindings=0; + const orgbinding *b= orgbindings; + while (b->action) { bind(b->key,b->action); b++; } + describestart(); +} + +int keybindings::bind(int key, const char *action) { + if (key == -1) return 0; + + const interpretation *interp; + for (interp=interps; interp->action && strcmp(interp->action,action); interp++); + if (!interp->action) return 0; + + const description *desc; + for (desc=descriptions; desc->action && strcmp(desc->action,action); desc++); + + binding *bind; + for (bind=bindings; bind && bind->key != key; bind=bind->next); + + if (!bind) { + bind= new binding; + bind->key= key; + bind->next= bindings; + bindings= bind; + } + bind->interp= interp; + bind->desc= desc ? desc->desc : 0; + return 1; +} + +const char *keybindings::find(const char *action) { + binding *b; + for (b=bindings; b && strcmp(action,b->interp->action); b=b->next); + if (!b) return "[not bound]"; + const char *n= key2name(b->key); + if (n) return n; + static char buf[50]; + sprintf(buf,"[unk: %d]",b->key); + return buf; +} + +const keybindings::interpretation *keybindings::operator()(int key) { + binding *b; + for (b=bindings; b && b->key != key; b=b->next); + if (!b) return 0; + return b->interp; +} + +const char **keybindings::describenext() { + binding *search; + int count; + for (;;) { + if (!iterate->action) return 0; + for (count=0, search=bindings; search; search=search->next) + if (!strcmp(search->interp->action,iterate->action)) + count++; + if (count) break; + iterate++; + } + const char **retarray= new const char *[count+2]; + retarray[0]= iterate->desc; + for (count=1, search=bindings; search; search=search->next) + if (!strcmp(search->interp->action,iterate->action)) + retarray[count++]= key2name(search->key); + retarray[count]= 0; + iterate++; + return retarray; +} + +const char *keybindings::key2name(int key) { + const keyname *search; + for (search=keynames; search->key != -1 && search->key != key; search++); + return search->kname; +} + +int keybindings::name2key(const char *name) { + const keyname *search; + for (search=keynames; search->kname && strcasecmp(search->kname,name); search++); + return search->key; +} + +keybindings::~keybindings() { + binding *search, *next; + for (search=bindings; search; search=next) { + next= search->next; + delete search; + } +} + +const keybindings::description keybindings::descriptions[]= { + // Actions which apply to both types of list. + { "iscrollon", "Scroll onwards through help/information" }, + { "iscrollback", "Scroll backwards through help/information" }, + { "up", "Move up" }, + { "down", "Move down" }, + { "top", "Go to top of list" }, + { "bottom", "Go to end of list" }, + { "help", "Request help (cycle through help screens)" }, + { "info", "Cycle through information displays" }, + { "redraw", "Redraw display" }, + { "scrollon1", "Scroll onwards through list by 1 line" }, + { "scrollback1", "Scroll backwards through list by 1 line" }, + { "iscrollon1", "Scroll onwards through help/information by 1 line" }, + { "iscrollback1", "Scroll backwards through help/information by 1 line" }, + { "scrollon", "Scroll onwards through list" }, + { "scrollback", "Scroll backwards through list" }, + + // Actions which apply only to lists of packages. + { "select", "Select package(s) for installation" }, + { "deselect", "Mark package(s) for deinstallation" }, + { "purge", "Mark package(s) for deinstall and purge" }, + { "morespecific", "Make highlight more specific" }, + { "lessspecific", "Make highlight less specific" }, + { "search", "Search for a package whose name contains a string" }, + { "searchagain", "Repeat last search." }, + { "swaporder", "Swap sort order priority/section" }, + { "quitcheck", "Quit, confirming, and checking dependencies" }, + { "quitnocheck", "Quit, confirming without check" }, + { "quitrejectsug", "Quit, rejecting conflict/dependency suggestions" }, + { "abortnocheck", "Abort - quit without making changes" }, + { "revert", "Revert to old state for all packages" }, + { "revertsuggest", "Revert to suggested state for all packages" }, + { "revertdirect", "Revert to directly requested state for all packages" }, + + // Actions which apply only to lists of methods. + { "select-and-quit", "Select currently-highlighted access method" }, + { "abort", "Quit without changing selected access method" }, + { 0, 0 } +}; diff --git a/dselect/bindings.h b/dselect/bindings.h new file mode 100644 index 00000000..981cf63e --- /dev/null +++ b/dselect/bindings.h @@ -0,0 +1,94 @@ +/* -*- c++ -*- + * dselect - selection of Debian packages + * bindings.h - keybindings class header file + * + * Copyright (C) 1994,1995 Ian Jackson + * + * This 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, + * or (at your option) any later version. + * + * This 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 dpkg; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef BINDINGS_H +#define BINDINGS_H + +enum quitaction; + +struct keybindings { + struct interpretation; + + struct orgbinding { + int key; + const char *action; + }; + + struct keyname { + int key; + const char *kname; + }; + + struct description { + const char *action, *desc; + }; + + struct binding { + binding *next; + int key; + const struct interpretation *interp; + const char *desc; + }; + + private: + static const keyname keynames[]; + static const description descriptions[]; + + binding *bindings; + const description *iterate; + const interpretation *interps; + + int bind(int key, const char *action); + + public: + int name2key(const char *name); + const char *key2name(int key); + + int bind(const char *name, const char *action) { return bind(name2key(name),action); } + const interpretation *operator()(int key); + const char *find(const char *action); + + void describestart() { iterate=descriptions; } + const char **describenext(); + //... returns array, null-term, first element is description, rest are key strings + // caller must delete[] the array. Null means end. + + keybindings(const interpretation *ints, const orgbinding *orgbindings); + ~keybindings(); +}; + +#include "pkglist.h" +#include "method.h" + +struct keybindings::interpretation { + const char *action; + void (methodlist::*mfn)(); + void (packagelist::*pfn)(); + quitaction qa; +}; + +extern const keybindings::interpretation packagelist_kinterps[]; +extern const keybindings::orgbinding packagelist_korgbindings[]; + +extern const keybindings::interpretation methodlist_kinterps[]; +extern const keybindings::orgbinding methodlist_korgbindings[]; + +#endif /* BINDINGS_H */ diff --git a/dselect/checkunimp.pl b/dselect/checkunimp.pl new file mode 100755 index 00000000..c0b723bc --- /dev/null +++ b/dselect/checkunimp.pl @@ -0,0 +1,17 @@ +#!/usr/bin/perl +while(<>) { + if (m/^\s+\{\s+\"(\w[^"]+)\",\s+0,\s+\w+list\:\:kd_\w+,\s+qa_\w+\s+\},\s*$/ || + m/^\s+\{\s+\"(\w[^"]+)\",\s+\w+list\:\:kd_\w+,\s+0,\s+qa_\w+\s+\},\s*$/) { + $implem{$1}= 1; + } elsif (m/^\s+\{\s+(\S.{0,15}\S),\s+\"(\w[^"]+)\"\s+\},\s*$/) { + $bound{$2} .= $1.', '; + } elsif (m/^\s+\{\s+0,/ || m/^\s+\{\s+-1,/) { + } elsif (m/^\s+\{\s+/) { + print "huh ? $_"; + } +} +for $f (sort keys %bound) { + next if defined($implem{$f}); + $b=$bound{$f}; $b =~ s/, $//; + printf "unimplemented: %-20s (%s)\n", $f, $b; +} diff --git a/dselect/curkeys.cc b/dselect/curkeys.cc new file mode 100644 index 00000000..532b2695 --- /dev/null +++ b/dselect/curkeys.cc @@ -0,0 +1,33 @@ +/* + * dselect - Debian GNU/Linux package maintenance user interface + * curkeys.cc - list of ncurses keys for name <-> key mapping + * + * Copyright (C) 1995 Ian Jackson + * + * This 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, + * or (at your option) any later version. + * + * This 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; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +extern "C" { +#include +#include "config.h" +#include "dpkg.h" +#include "dpkg-db.h" +} +#include "dselect.h" +#include "bindings.h" + +const keybindings::keyname keybindings::keynames[] = { +#include "curkeys.inc" +}; diff --git a/dselect/debugmake b/dselect/debugmake new file mode 100755 index 00000000..02702977 --- /dev/null +++ b/dselect/debugmake @@ -0,0 +1,3 @@ +#!/bin/sh +set -x +make 'XCFLAGS=-g -O0' LDFLAGS=-g 'EXTERNLIBS= -lncurses_g -lefence' "$@" diff --git a/dselect/dselect.8 b/dselect/dselect.8 new file mode 100644 index 00000000..8e7e6d50 --- /dev/null +++ b/dselect/dselect.8 @@ -0,0 +1,89 @@ +.\" Hey, Emacs! This is an -*- nroff -*- source file. +.TH DSELECT 8 "29th November 1995" "Debian Project" "Debian GNU/Linux" +.SH NAME +dselect \- a user tool to manage Debian GNU/Linux packages + +.SH SYNOPSIS +.B dselect +[options] +.br +.B dselect +[options] action ... + +.SH DESCRIPTION +.B dselect +is the primary user interface for installing, removing and managing +Debian GNU/Linux packages. It is an front-end to +.B dpkg(8). +Normally +.B dselect +is invoked without parameters, but some commandline parameters are still +available. + +The usage of +.B dselect +is pretty self-explanatory, and also an internal help-system is +included, which describes the keystrokes and some general concepts. +.I Read the help. + +.SS ACTIONS +.TP +.B access, update, select, install, config, remove +These actions automatically selects corresponding commands from the main +menu, without even showing the main menu to you. +.TP +.B quit +Do nothing but quit. +.TP +.B menu +Displays the default menu. This is equivalent to starting +.B dselect +with no parameters. + +.SS OPTIONS +.TP +.B --admindir +Changes the directory where datafiles are located. This defaults to +.I /var/lib/dpkg. +Note, that these files are just some internal datafiles, actual Debian +packages doesn't have to be located here. +.TP +.B --debug | -D +Turn on debugging. Debugging information is sent to +.I . +.TP +.B --help +Print a brief help text. +.TP +.B --licence +Print the licence of +.B dselect. +.TP +.B --version +Print version information. + +.SH BUGS +This manpage doesn't document the options quite correctly, and has +formatting inconsistent with other manpages. + +The +.B dselect +package selection interface is confusing or even alarming to the new +user. + +There is no easy way automatically to download and install packages +via anonymous FTP. + +.SH SEE ALSO +.B deb(5) +, +.B dpkg-deb(8) +, +.B dpkg(8) +and +.B deb-control(5) + +.SH AUTHOR +.B dselect +was written by Ian Jackson (ijackson@gnu.ai.mit.edu). +This manual page is by Juho Vuori (javuori@cc.helsinki.fi). diff --git a/dselect/dselect.h b/dselect/dselect.h new file mode 100644 index 00000000..5f357505 --- /dev/null +++ b/dselect/dselect.h @@ -0,0 +1,149 @@ +/* -*- c++ -*- + * dselect - selection of Debian packages + * dselect.h - external definitions for this program + * + * Copyright (C) 1994,1995 Ian Jackson + * + * This 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, + * or (at your option) any later version. + * + * This 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 dpkg; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef DSELECT_H +#define DSELECT_H + +#define TOTAL_LIST_WIDTH 180 +#define MAX_DISPLAY_INFO 120 + +struct helpmenuentry { + char key; + const struct helpmessage *msg; +}; + +class keybindings; + +class baselist { +protected: + // Screen dimensions &c. + int xmax, ymax; + int title_height, colheads_height, list_height; + int thisstate_height, info_height, whatinfo_height; + int colheads_row, thisstate_row, info_row, whatinfo_row, list_row; + int list_attr, listsel_attr, title_attr, colheads_attr, info_attr; + int info_headattr, whatinfo_attr; + int thisstate_attr, query_attr; + int selstate_attr, selstatesel_attr; + + int total_width; + + // (n)curses stuff + WINDOW *listpad, *infopad, *colheadspad, *thisstatepad; + WINDOW *titlewin, *whatinfowin, *querywin; + // If listpad is null, then we have not started to display yet, and + // so none of the auto-displaying update routines need to display. + + // SIGWINCH handling + struct sigaction *osigactp, nsigact; + sigset_t *oblockedp, sigwinchset; + void setupsigwinch(); + + static baselist *signallist; + static void sigwinchhandler(int); + + int nitems, ldrawnstart, ldrawnend; + int topofscreen, leftofscreen, cursorline; + int infotopofscreen, infolines; + varbuf whatinfovb; + char searchstring[50]; + + void unsizes(); + void dosearch(); + void displayhelp(const struct helpmenuentry *menu, int key); + + void redrawall(); + void redrawitemsrange(int start /*inclusive*/, int end /*exclusive*/); + void redraw1item(int index); + void refreshlist(); + void refreshinfo(); + void refreshcolheads(); + void setcursor(int index); + + void itd_keys(); + + virtual void redraw1itemsel(int index, int selected) =0; + virtual void redrawcolheads() =0; + virtual void redrawthisstate() =0; + virtual void redrawinfo() =0; + virtual void redrawtitle() =0; + virtual void setwidths() =0; + virtual const char *itemname(int index) =0; + virtual const struct helpmenuentry *helpmenulist() =0; + + void wordwrapinfo(int offset, const char *string); + +public: + + keybindings *bindings; + + void kd_up(); + void kd_down(); + void kd_redraw(); + void kd_scrollon(); + void kd_scrollback(); + void kd_scrollon1(); + void kd_scrollback1(); + void kd_panon(); + void kd_panback(); + void kd_panon1(); + void kd_panback1(); + void kd_top(); + void kd_bottom(); + void kd_iscrollon(); + void kd_iscrollback(); + void kd_iscrollon1(); + void kd_iscrollback1(); + void kd_search(); + void kd_searchagain(); + void kd_help(); + + void startdisplay(); + void enddisplay(); + + baselist(keybindings*); + virtual ~baselist(); +}; + +static inline int lesserint(int a, int b) { return ab ? a : b; } + +void displayhelp(const struct helpmenuentry *menu, int key); + +void mywerase(WINDOW *win); + +void curseson(); +void cursesoff(); + +extern const char *admindir; +extern FILE *debug; + +enum urqresult { urqr_normal, urqr_fail, urqr_quitmenu }; +enum quitaction { qa_noquit, qa_quitchecksave, qa_quitnochecksave }; + +typedef urqresult urqfunction(void); +urqfunction urq_list, urq_quit, urq_menu; +urqfunction urq_setup, urq_update, urq_install, urq_config, urq_remove; + +urqresult falliblesubprocess(const char *exepath, const char *name, + const char *const *args); + +#endif /* DSELECT_H */ diff --git a/dselect/helpmsgs.src b/dselect/helpmsgs.src new file mode 100644 index 00000000..db0fd6a7 --- /dev/null +++ b/dselect/helpmsgs.src @@ -0,0 +1,179 @@ +@@@ listkeys Keystrokes + +Motion keys: Next/Previous, Top/End, Up/Down, Backwards/Forwards: + n, Down-arrow p, Up-arrow move highlight + N, Page-down, Space P, Page-up, Backspace scroll list by 1 page + ^n ^p scroll list by 1 line + t, Home e, End jump to top/end of list + u d scroll info by 1 page + ^u ^d scroll info by 1 line + B, Left-arrow F, Right-arrow pan display by 1/3 screen + ^b ^f pan display by 1 character + +Package states - selection: Package states - hold flag: + +, Insert select package(s) H put package(s) `on hold' + -, Delete deselect package(s) G `go' - take off hold + _ deselect, purge configuration + Miscellaneous: +Quit, exit, overwrite (note capitals!): ? request help (also Help, F1) + Return Confirm and quit (check dependencies) i toggle/cycle info displays + Q Confirm and quit (override dep.s) o cycle through sort orders + X eXit, abandoning any changes made v toggle verbose status display + R Revert to state before this list ^l redraw display + U set all to sUggested state / search (hit Return to cancel) + D set all to Directly selected state \\ repeat last search + +@@@ mainintro Introduction to package list + +Welcome to the main package listing. Please read the help that is available ! + +You will be presented with a list of packages which are installed or available +for installation. You can navigate around the list using the cursor keys, +selecting packages for installation (using `+') or deinstallation (using `-'). + +Packages can be selected either singly or in groups; initially you will see +that the line `All packages' is highlighted. (De)selecting will affect all the +packages described by the highlighted line. Use `o' to change the order of the +list (this also changes which kinds of group selections are possible). + +(Mainly for new installations:) Standard packages will be selected by default. +Use capital `D' or `R' key to override this - see the keybindings help screen. + +Some of your choices will cause conflicts or dependency problems; you will be +given a sub-list of the relevant packages, so that you can solve the problems. + +When you are satisfied with your choices you should press Return to confirm +your changes and leave the package listing. A final check on conflicts and +dependencies will be done - here too you may see a sublist. + +Press Space to leave help and enter the list; press `?' at any time for help. + +@@@ readonlyintro Introduction to package list browser (read-only) + +Welcome to dselect's main package listing. Since you do not have the +privilege necessary to update package selections you are in read-only mode. + +Much on-line help is available, please make use of it ! Press `?' for help. +You should read the list of keys and the explanations of the display. + +You will be presented with a list of packages which are installed or available +for installation. You can navigate around the list using the cursor keys (just +as you would be able to do if you had read/write access - see the keystrokes +help screen) and observe the status of the packages and read information about +them. + +Press Space to leave help and enter the list; press `?' at any time for help. +When you have finished browsing, press `q' or Return to quit. + +@@@ recurintro Introduction to conflict/dependency resolution sub-list + +Dependency/conflict resolution - introduction. + +One or more of your choices have raised a conflict or dependency problem - +some packages should only be installed in conjunction with certain others, and +some combinations of packages may not be installed together. + +You will see a sub-list containing the packages involved. The bottom half of +the display shows relevant conflicts and dependencies; use `i' to cycle between +that, the package descriptions and the internal control information. + +A set of `suggested' selections has been calculated, and the initial selections +in this sub-list have been set to match those, so you can just hit Return to +accept the suggestions if you wish. You may abort the change(s) which caused +the problem(s), and go back to the main list, by pressing capital `X'. + +You can also move around the list and change the selections so that they are +more like what you want, and you can `reject' my suggestions by using the +capital `D' or `R' keys (see the keybindings help screen). You can use capital +`Q' to force me to accept the current selections, in case you want to override +a recommendation or think that the program is mistaken. + +Press Space to leave help and enter the sub-list; remember: press `?' for help. + +@@@ displayexplain1 Display, part 1: package listing and status chars + +The top half of the screen shows a list of packages. For each package you see +four columns for its current status and selected-ness. In terse mode (use `v' +to toggle verbose display) these are single characters, from left to right: + + Hold/error flag: h - you have put the package on Hold - it won't be processed + R - serious error during installation, needs reinstallation; + X - serious error, needs reinstallation, package also on hold + Installed state: Space - not installed; + `*' - installed; + `-' - not installed but config files remain; + `u' - unpacked but not yet configured; + `C' - half-configured (an error happened); + `I' - half-installed (an error happened). + Old selection: whether this package was selected before presenting this list; + Selection: whether this package is selected for installation: + `*': selected for installation; + `-': deselected (selected for removal, but configuration files will remain); + `_': deselected (purge completely, even remove configuration); + `n': package is new to this system (appears under `Old selection'). + +Following those four columns are the Priority and Section of the package, +its name (possibly truncated to fit) and the summary description. + +@@@ displayexplain2 Display, part 2: list highlight; information display + +* Highlight: One line in the package list will be highlighted. It indicates + which package(s) will be selected/deselected by presses of `+', '-' and `_'. + +* The dividing line in the middle of the screen shows a brief explanation of + the status of the currently-highlighted package, or a description of which + group is highlighted if a group line is. If you don't understand the + meaning of some of the status characters displayed, go to the relevant + package and look at this divider line, or use the `v' key for a verbose + display (press `v' again to go back to the terse display). + +* The bottom of the screen shows more information about the + currently-highlighted package (if there is only one). + + It can show an extended description of the package, the internal package + control details, or information about conflicts and dependencies involving + the current package (in conflict/dependency resolution sublists). + + Use the `i' key to cycle through the displays. + +@@@ methintro Introduction to method selection display + +dselect and dpkg can do automatic installation, loading the package files to be +installed from one of a number of different possible places. + +This list allows you to select one of these installation methods. + +Move the highlight to the method you wish to use, and hit Enter. You will then +be prompted for the information required to do the installation. + +As you move the highlight a description of each method, where available, is +displayed in the bottom half of the screen. + +If you wish to quit without changing anything use the `x' key while in the list +of installation methods. + +A full list of keystrokes is available by pressing `k' now, or from the help +menu reachable by pressing `?'. + +@@@ methkeys Keystrokes for method selection + +Motion keys: Next/Previous, Top/End, Up/Down, Backwards/Forwards: + n, Down-arrow p, Up-arrow move highlight + N, Page-down, Space P, Page-up, Backspace scroll list by 1 page + ^n ^p scroll list by 1 line + t, Home e, End jump to top/end of list + u d scroll info by 1 page + ^u ^d scroll info by 1 line + B, Left-arrow F, Right-arrow pan display by 1/3 screen + ^b ^f pan display by 1 character +(These are the same motion keys as in the package list display.) + +Quit: + Return, Enter select this method and go to its configuration dialogue + x, X exit without changing or setting up the installation method + +Miscellaneous: + ?, Help, F1 request help + ^l redraw display + / search (just return to cancel) + \\ repeat last search diff --git a/dselect/junk b/dselect/junk new file mode 100644 index 00000000..e2ae289d --- /dev/null +++ b/dselect/junk @@ -0,0 +1,162 @@ +const packagelist::infotype packagelist::helpinfos[]= { + { itr_nonrecursive, itd_mainwelcome }, + { itr_recursive, itd_recurwelcome }, + { 0, itd_keys }, + { 0, itd_explaindisplay }, + { 0, 0 } +}; + +int packagelist::itr_nonrecursive() { return !recursive; } + +void packagelist::itd_mainwelcome() { + whatinfovb("main welcome page"); + + varbuf vb; + vb("This is dselect's main package listing screen."); + if (!readwrite) vb(" (read-only access.)"); + vb("\n\n" + "Please press `"); + vb(bindings->find("help")); + vb("' repeatedly to show more help, including the list of " + "keystrokes and an explanation of the display; use `"); + vb(bindings->find("iscrollon")); + vb("' and `"); + vb(bindings->find("iscrollback")); + vb("' to scroll this help screen. Press `"); + vb(bindings->find("info")); + vb("' repeatedly to see more information about the highlighted package(s).\n" + "\n"); + if (readwrite) { + vb("Move the highlight and use `"); + vb(bindings->find("select")); + vb("' and `"); + vb(bindings->find("deselect")); + vb("' to select and deselect packages for " + "installation. Press `"); + vb(bindings->find("quit")); + vb("' to confirm your changes and quit, or `"); + vb(bindings->find("abortnocheck")); + vb("' to abort without making changes.\n" + "\n" + "Deselecting a package that is currently " + "installed will arrange for it to be deinstalled later. If you " + "select a package for purging its configuration files will be " + "deleted when the package is; usually they are kept.\n"); + } else { + vb("You do not have read/write access to the package administration " + "area, so you may only examine the state of packages.\n"); + } + wordwrapinfo(0,vb.string()); +} + +void packagelist::itd_explaindisplay() { + whatinfovb("explanation of the display"); + + varbuf vb; + vb("The top half of the screen shows a list of packages. For each " + "package its status (explained in more detail below), class, " + "section, name and a summary of its description is shown.\n" + "\n" + "In the middle of the screen is a status line containing a description " + "of the status of the currently highlighted package (or a description " + "of which packages are highlighted if this is more than one package).\n" + "\n" + "The bottom half of the screen is used to display more extended " + "information about the highlighed package(s), and for help displays " + "like this one.\n" + "\n" + "The four characters at the start of each package's entry in the list " + "show its status: Error, Installed, Old, Wanted.\n" + "\n" + "`Wanted' is whether it is currently " + "selected or not (ie, its desired state as currently being edited); " + "`Old', immediately to the left of that, gives the desired state before " + "this package list was entered, if different (or a space if not)." + "\n" + "`Installed' shows the package's current actual state on " + "the system; the `Error' column is used to mark packages for which " + "installation or removal errors have occurred.\n"); + wordwrapinfo(0,vb.string()); +} + +void packagelist::itd_recurwelcome() { + whatinfovb("recursive package listing welcome page"); + + varbuf vb; + vb("Some of the packages you have selected require or recommend " + "the installation of other packages you haven't selected, or " + "some of them conflict with packages you have selected.\n" + "Details of problems for each package are available by pressing `"); + vb(bindings->find("info")); + vb("'.\n" + "This sub-list of packages is to allow you to resolve these " + "dependencies and conflicts. You may accept my suggestions " + "about which related packages to select or deselect by pressing `"); + vb(bindings->find("quitcheck")); + vb("'.\n" + "\n" + "Alternatively, you can edit the setup I have suggested, or use `"); + vb(bindings->find("revertdirect")); + vb("' to reject my suggestions, before using `"); + vb(bindings->find("quitcheck")); + vb("'.\n" + "\n" + "For a list of the keys available, consult the keymap " + "help screen (available by pressing `"); + vb(bindings->find("help")); + vb("'); see the manual for detailed information.\n" + "\n" + "If you wish to replace a package on which many others depend with " + "a conflicting one you'll probably find it easier to try selecting " + "the new one rather than deselecting the old one first, as doing " + "the latter will cause me to suggest deselecting all that depend on it."); + wordwrapinfo(0,vb.string()); +} + +void methodlist::kd_info() { + showinghelp=0; + redrawinfo(); +} + +void methodlist::kd_help() { + if (showinghelp) ifhelpthenkeys= !ifhelpthenkeys; + showinghelp=1; + redrawinfo(); +} + +void methodlist::itd_welcome() { + whatinfovb("method selection welcome page"); + + varbuf vb; + vb("This is dselect's access method selection screen."); + vb("\n\n" + "Please press `"); + vb(bindings->find("help")); + vb("' to show more help; in particular, you should read the list of " + "keystrokes; use `"); + vb(bindings->find("iscrollon")); + vb("' and `"); + vb(bindings->find("iscrollback")); + vb("' to scroll this help screen. Press `"); + vb(bindings->find("info")); + vb("' to see more information about the highlighted method.\n" + "\n"); + vb("Move the highlight and use `"); + vb(bindings->find("select-and-quit")); + vb("' to select the highlighted method. Press `"); + vb(bindings->find("abort")); + vb("' to abort without changing the access method.\n" + "\n" + "When you have selected the access method you will " + "be prompted for the information it requires to do the " + "installation.\n"); + wordwrapinfo(0,vb.string()); +} + + if (showinghelp) + if (ifhelpthenkeys) + itd_keys(); + else + itd_welcome(); + else + diff --git a/dselect/keyoverride b/dselect/keyoverride new file mode 100644 index 00000000..4de681f1 --- /dev/null +++ b/dselect/keyoverride @@ -0,0 +1,59 @@ +# dselect - Debian GNU/Linux package maintenance user interface +# keyoverride - override strings for ncurses key names +# +# Copyright (C) 1995 Ian Jackson +# +# This 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, +# or (at your option) any later version. +# +# This 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; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +32 Space +13 Return +27 Escape +28 ^\ +29 ^] +30 ^^ +31 ^_ +34 Quote +39 Apostrophe +44 Comma +45 Hyphen +47 Slash +59 Semicolon +92 Backslash +96 Backquote +127 DEL +KEY_UP Up +KEY_DOWN Down +KEY_RIGHT Right +KEY_LEFT Left +KEY_IC Insert +KEY_SIC Shift Insert +KEY_DC Delete +KEY_SDC Shift Delete +KEY_NPAGE Page Down +KEY_PPAGE Page Up +KEY_CATAB Clear Tabs +KEY_EIC EIC +KEY_EOL EOL +KEY_SEOL Shift EOL +KEY_EOS EOS +KEY_LL Bottom +KEY_SF Scroll Forward +KEY_SR Scroll Reverse +KEY_SRESET Soft Reset +KEY_SLEFT Shift Left +KEY_SRIGHT Shift Right +KEY_SPREVIOUS Shift Previous +KEY_MAX [elide] +KEY_MIN [elide] diff --git a/dselect/keys.c b/dselect/keys.c new file mode 100644 index 00000000..92dd16a4 --- /dev/null +++ b/dselect/keys.c @@ -0,0 +1,91 @@ +{ KEY_MIN , "MIN" }, +{ KEY_BREAK , "BREAK" }, +{ KEY_DOWN , "DOWN" }, +{ KEY_UP , "UP" }, +{ KEY_LEFT , "LEFT" }, +{ KEY_RIGHT , "RIGHT" }, +{ KEY_HOME , "HOME" }, +{ KEY_BACKSPACE , "BACKSPACE" }, +{ KEY_F0 , "F0" }, +{ KEY_DL , "DL" }, +{ KEY_IL , "IL" }, +{ KEY_DC , "DC" }, +{ KEY_IC , "IC" }, +{ KEY_EIC , "EIC" }, +{ KEY_CLEAR , "CLEAR" }, +{ KEY_EOS , "EOS" }, +{ KEY_EOL , "EOL" }, +{ KEY_SF , "SF" }, +{ KEY_SR , "SR" }, +{ KEY_NPAGE , "NPAGE" }, +{ KEY_PPAGE , "PPAGE" }, +{ KEY_STAB , "STAB" }, +{ KEY_CTAB , "CTAB" }, +{ KEY_CATAB , "CATAB" }, +{ KEY_ENTER , "ENTER" }, +{ KEY_SRESET , "SRESET" }, +{ KEY_RESET , "RESET" }, +{ KEY_PRINT , "PRINT" }, +{ KEY_LL , "LL" }, +{ KEY_A1 , "A1" }, +{ KEY_A3 , "A3" }, +{ KEY_B2 , "B2" }, +{ KEY_C1 , "C1" }, +{ KEY_C3 , "C3" }, +{ KEY_BTAB , "BTAB" }, +{ KEY_BEG , "BEG" }, +{ KEY_CANCEL , "CANCEL" }, +{ KEY_CLOSE , "CLOSE" }, +{ KEY_COMMAND , "COMMAND" }, +{ KEY_COPY , "COPY" }, +{ KEY_CREATE , "CREATE" }, +{ KEY_END , "END" }, +{ KEY_EXIT , "EXIT" }, +{ KEY_FIND , "FIND" }, +{ KEY_HELP , "HELP" }, +{ KEY_MARK , "MARK" }, +{ KEY_MESSAGE , "MESSAGE" }, +{ KEY_MOVE , "MOVE" }, +{ KEY_NEXT , "NEXT" }, +{ KEY_OPEN , "OPEN" }, +{ KEY_OPTIONS , "OPTIONS" }, +{ KEY_PREVIOUS , "PREVIOUS" }, +{ KEY_REDO , "REDO" }, +{ KEY_REFERENCE , "REFERENCE" }, +{ KEY_REFRESH , "REFRESH" }, +{ KEY_REPLACE , "REPLACE" }, +{ KEY_RESTART , "RESTART" }, +{ KEY_RESUME , "RESUME" }, +{ KEY_SAVE , "SAVE" }, +{ KEY_SBEG , "SBEG" }, +{ KEY_SCANCEL , "SCANCEL" }, +{ KEY_SCOMMAND , "SCOMMAND" }, +{ KEY_SCOPY , "SCOPY" }, +{ KEY_SCREATE , "SCREATE" }, +{ KEY_SDC , "SDC" }, +{ KEY_SDL , "SDL" }, +{ KEY_SELECT , "SELECT" }, +{ KEY_SEND , "SEND" }, +{ KEY_SEOL , "SEOL" }, +{ KEY_SEXIT , "SEXIT" }, +{ KEY_SFIND , "SFIND" }, +{ KEY_SHELP , "SHELP" }, +{ KEY_SHOME , "SHOME" }, +{ KEY_SIC , "SIC" }, +{ KEY_SLEFT , "SLEFT" }, +{ KEY_SMESSAGE , "SMESSAGE" }, +{ KEY_SMOVE , "SMOVE" }, +{ KEY_SNEXT , "SNEXT" }, +{ KEY_SOPTIONS , "SOPTIONS" }, +{ KEY_SPREVIOUS , "SPREVIOUS" }, +{ KEY_SPRINT , "SPRINT" }, +{ KEY_SREDO , "SREDO" }, +{ KEY_SREPLACE , "SREPLACE" }, +{ KEY_SRIGHT , "SRIGHT" }, +{ KEY_SRSUME , "SRSUME" }, +{ KEY_SSAVE , "SSAVE" }, +{ KEY_SSUSPEND , "SSUSPEND" }, +{ KEY_SUNDO , "SUNDO" }, +{ KEY_SUSPEND , "SUSPEND" }, +{ KEY_UNDO , "UNDO" }, +{ KEY_MAX , "MAX" }, diff --git a/dselect/kt.c b/dselect/kt.c new file mode 100644 index 00000000..d322c68b --- /dev/null +++ b/dselect/kt.c @@ -0,0 +1,30 @@ +#include +#include +#include +#include + +struct kd { int v; const char *n; } kds[]= { +#include "curkeys.inc" +}; + +int main(int argc, char **argv) { + int n=0, c, y,x; + struct kd *kdp; + + initscr(); cbreak(); noecho(); nonl(); + keypad(stdscr,TRUE); + getmaxyx(stdscr,y,x); + mvprintw(5,5,"q to quit; b to beep; (y%d x%d)",y,x); + + for (;;) { + refresh(); + c= getch(); if (c==ERR) { endwin(); perror("err"); exit(1); } + for (kdp=kds; kdp->v != -1 && kdp->v != c; kdp++); + n++; mvprintw(10 + (n%4),10,"n %10d keycode %4d %-10s F0 + %4d",n,c, + kdp->n ? kdp->n : "", c-KEY_F0); + if (c == 'q') break; + if (c == 'b') beep(); + } + endwin(); + return 0; +} diff --git a/dselect/kt.cc b/dselect/kt.cc new file mode 100644 index 00000000..d448a587 --- /dev/null +++ b/dselect/kt.cc @@ -0,0 +1,30 @@ +#include +#include +#include +#include + +struct kd { int v; const char *n; } kds[]= { +#include "curkeys.inc" +}; + +int main(int argc, char **argv) { + int n=0, c, y,x; + struct kd *kdp; + + initscr(); cbreak(); noecho(); nonl(); + keypad(stdscr,TRUE); + getmaxyx(stdscr,y,x); + mvprintw(5,5,"q to quit; b to beep; (y%d x%d)",y,x); + + for (;;) { + refresh(); + c= getch(); if (c==ERR) { endwin(); perror("err"); exit(1); } + for (kdp=kds; kdp->v != -1 && kdp->v != c; kdp++); + n++; mvprintw(10 + (n%4),10,"n %10d keycode %4d %-10s F0 + %4d",n,c, + kdp->n, c-KEY_F0); + if (c == 'q') break; + if (c == 'b') beep(); + } + endwin(); + return 0; +} diff --git a/dselect/main.cc b/dselect/main.cc new file mode 100644 index 00000000..e65f6352 --- /dev/null +++ b/dselect/main.cc @@ -0,0 +1,308 @@ +/* + * dselect - Debian GNU/Linux package maintenance user interface + * main.cc - main program + * + * Copyright (C) 1994,1995 Ian Jackson + * + * This 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, + * or (at your option) any later version. + * + * This 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 dpkg; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +extern "C" { +#include "config.h" +#include "dpkg.h" +#include "dpkg-db.h" +#include "version.h" +#include "myopt.h" +} +#include "dselect.h" +#include "bindings.h" +#include "pkglist.h" + +const char thisname[]= DSELECT; +const char printforhelp[]= "Type " DPKG " --help for help."; + +modstatdb_rw readwrite; +const char *admindir= ADMINDIR; +FILE *debug; + +static keybindings packagelistbindings(packagelist_kinterps,packagelist_korgbindings); + +struct menuentry { + const char *option; + const char *menuent; + urqfunction *fn; +}; + +static const menuentry menuentries[]= { + { "access", "Choose the access method to use.", &urq_setup }, + { "update", "Update list of available packages, if possible.", &urq_update }, + { "select", "Select which packages to install (or deinstall).", &urq_list }, + { "install", "Install selected software.", &urq_install }, + { "config", "Configure packages that are unconfigured.", &urq_config }, + { "remove", "Remove software selected for deinstallation.", &urq_remove }, + { "quit", "Quit dselect.", &urq_quit }, + { "menu", 0, &urq_menu }, + { 0 } +}; + +static const char programdesc[]= + "Debian GNU/Linux `" DSELECT "' package handling frontend."; + +static const char copyrightstring[]= + "Version " DPKG_VERSION_ARCH ". Copyright (C) 1994,1995 Ian Jackson. This is\n" + "free software; see the GNU General Public Licence version 2 or later for\n" + "copying conditions. There is NO warranty. See dselect --licence for details.\n"; + +static void printversion(void) { + if (fprintf(stderr,"%s\n%s",programdesc,copyrightstring) == EOF) werr("stderr"); +} + +static void usage(void) { + if (!fputs + ("Usage: dselect [options]\n" + " dselect [options] action ...\n" + "Options: --admindir (default is /var/lib/dpkg)\n" + " --help --version --licence --debug | -D | -D\n" + "Actions: access update select install config remove quit menu\n", + stderr)) werr("stderr"); +} + +/* These are called by C code, so need to have C calling convention */ +extern "C" { + + static void helponly(const struct cmdinfo*, const char*) { + usage(); exit(0); + } + static void versiononly(const struct cmdinfo*, const char*) { + printversion(); exit(0); + } + + static void setdebug(const struct cmdinfo*, const char *v) { + debug= fopen(v,"a"); + if (!debug) ohshite("couldn't open debug file `%.255s'\n",v); + setvbuf(debug,0,_IONBF,0); + } + +} /* End of extern "C" */ + +static const struct cmdinfo cmdinfos[]= { + { "admindir", 0, 1, 0, &admindir, 0 }, + { "debug", 'D', 1, 0, 0, setdebug }, + { "help", 'h', 0, 0, 0, helponly }, + { "version", 0, 0, 0, 0, versiononly }, + { "licence", 0, 0, 0, 0, showcopyright }, /* UK spelling */ + { "license", 0, 0, 0, 0, showcopyright }, /* US spelling */ + { 0, 0 } +}; + +static int cursesareon= 0; +void curseson() { + if (!cursesareon) { + const char *cup, *smso; + initscr(); + cup= tigetstr("cup"); + smso= tigetstr("smso"); + if (!cup || !smso) { + endwin(); + if (!cup) + fputs("Terminal does not appear to support cursor addressing.\n",stderr); + if (!smso) + fputs("Terminal does not appear to support highlighting.\n",stderr); + fputs("Set your TERM variable correctly, use a better terminal,\n" + "or make do with the per-package management tool " DPKG ".\n",stderr); + ohshit("terminal lacks necessary features, giving up"); + } + } + cursesareon= 1; +} + +void cursesoff() { + if (cursesareon) { + clear(); + refresh(); + endwin(); + } + cursesareon=0; +} + +extern void *operator new(size_t size) { + void *p; + p= m_malloc(size); + assert(p); + return p; +} + +extern void operator delete(void *p) { + free(p); +} + +urqresult urq_list(void) { + readwrite= modstatdb_init(admindir, + msdbrw_writeifposs|msdbrw_availablepreferversion); + + curseson(); + + packagelist *l= new packagelist(&packagelistbindings); + l->resolvesuggest(); + l->display(); + delete l; + + modstatdb_shutdown(); + resetpackages(); + return urqr_normal; +} + +void dme(int i, int so) { + char buf[120]; + const menuentry *me= &menuentries[i]; + sprintf(buf," %c %d. [%c]%-10.10s %-80.80s ", + so ? '*' : ' ', i, + toupper(me->option[0]), me->option+1, + me->menuent); + + int y,x; + getmaxyx(stdscr,y,x); + + attrset(so ? A_REVERSE : A_NORMAL); + mvaddnstr(i+2,0, buf,x-1); + attrset(A_NORMAL); +} + +int refreshmenu(void) { + curseson(); cbreak(); noecho(); nonl(); keypad(stdscr,TRUE); + + int y,x; + getmaxyx(stdscr,y,x); + + clear(); + attrset(A_BOLD); + mvaddnstr(0,0, programdesc,x-1); + + attrset(A_NORMAL); + const struct menuentry *mep; int i; + for (mep=menuentries, i=0; mep->option && mep->menuent; mep++, i++) + dme(i,0); + + attrset(A_BOLD); + addstr("\n\n" + "Use ^P and ^N, cursor keys, initial letters, or digits to select;\n" + "Press ENTER to confirm selection. ^L to redraw screen.\n\n"); + + attrset(A_NORMAL); + addstr(copyrightstring); + + return i; +} + +urqresult urq_menu(void) { +#define C(x) ((x)-'a'+1) + int entries, c, i; + entries= refreshmenu(); + int cursor=0; + dme(0,1); + for (;;) { + refresh(); + c= getch(); if (c==ERR) ohshite("failed to getch in main menu"); + if (c==C('n') || c==KEY_DOWN || c==' ') { + dme(cursor,0); cursor++; cursor %= entries; dme(cursor,1); + } else if (c==C('p') || c==KEY_UP || c==C('h') || + c==KEY_BACKSPACE || c==KEY_DC) { + dme(cursor,0); cursor+= entries-1; cursor %= entries; dme(cursor,1); + } else if (c=='\n' || c=='\r' || c==KEY_ENTER) { + clear(); refresh(); + switch (menuentries[cursor].fn()) { /* fixme: trap errors in urq_... */ + case urqr_quitmenu: + return urqr_quitmenu; + case urqr_normal: + cursor++; cursor %= entries; + case urqr_fail: + break; + default: + internerr("unknown menufn"); + } + refreshmenu(); dme(cursor,1); + } else if (c==C('l')) { + clearok(stdscr,TRUE); clear(); refreshmenu(); dme(cursor,1); + } else if (isdigit(c)) { + char buf[2]; buf[0]=c; buf[1]=0; c=atoi(buf); + if (c < entries) { + dme(cursor,0); cursor=c; dme(cursor,1); + } else { + beep(); + } + } else if (isalpha(c)) { + c= tolower(c); + for (i=0; ioption && strcmp(me->option,a); me++); + if (!me->option) badusage("unknown action string `%.50s'",a); + me->fn(); + } + } else { + urq_menu(); + } + + cursesoff(); + set_error_display(0,0); + error_unwind(ehflag_normaltidy); + return(0); +} diff --git a/dselect/methkeys.cc b/dselect/methkeys.cc new file mode 100644 index 00000000..eef8104b --- /dev/null +++ b/dselect/methkeys.cc @@ -0,0 +1,112 @@ +/* + * dselect - Debian GNU/Linux package maintenance user interface + * methkeys.cc - method list keybindings + * + * Copyright (C) 1995 Ian Jackson + * + * This 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, + * or (at your option) any later version. + * + * This 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; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include + +extern "C" { +#include "config.h" +#include "dpkg.h" +#include "dpkg-db.h" +} +#include "dselect.h" +#include "bindings.h" + +const keybindings::interpretation methodlist_kinterps[] = { + { "up", methodlist::kd_up, 0, qa_noquit }, + { "down", methodlist::kd_down, 0, qa_noquit }, + { "top", methodlist::kd_top, 0, qa_noquit }, + { "bottom", methodlist::kd_bottom, 0, qa_noquit }, + { "scrollon", methodlist::kd_scrollon, 0, qa_noquit }, + { "scrollback", methodlist::kd_scrollback, 0, qa_noquit }, + { "iscrollon", methodlist::kd_iscrollon, 0, qa_noquit }, + { "iscrollback", methodlist::kd_iscrollback, 0, qa_noquit }, + { "scrollon1", methodlist::kd_scrollon1, 0, qa_noquit }, + { "scrollback1", methodlist::kd_scrollback1, 0, qa_noquit }, + { "iscrollon1", methodlist::kd_iscrollon1, 0, qa_noquit }, + { "iscrollback1", methodlist::kd_iscrollback1, 0, qa_noquit }, + { "panon", methodlist::kd_panon, 0, qa_noquit }, + { "panback", methodlist::kd_panback, 0, qa_noquit }, + { "panon1", methodlist::kd_panon1, 0, qa_noquit }, + { "panback1", methodlist::kd_panback1, 0, qa_noquit }, + { "help", methodlist::kd_help, 0, qa_noquit }, + { "search", methodlist::kd_search, 0, qa_noquit }, + { "searchagain", methodlist::kd_searchagain, 0, qa_noquit }, + { "redraw", methodlist::kd_redraw, 0, qa_noquit }, + { "select-and-quit", methodlist::kd_quit, 0, qa_quitchecksave }, + { "abort", methodlist::kd_abort, 0, qa_quitnochecksave }, + { 0, 0, qa_noquit } +}; + +#define C(x) ((x)-'a'+1) + +const keybindings::orgbinding methodlist_korgbindings[]= { + { 'n', "down" }, + { KEY_DOWN, "down" }, + { 'p', "up" }, + { KEY_UP, "up" }, + + { 'N', "scrollon" }, + { KEY_NPAGE, "scrollon" }, + { ' ', "scrollon" }, + { 'P', "scrollback" }, + { KEY_PPAGE, "scrollback" }, + { KEY_BACKSPACE, "scrollback" }, + { 0177,/*DEL*/ "scrollback" }, + { C('h'), "scrollback" }, + { C('n'), "scrollon1" }, + { C('p'), "scrollback1" }, + + { 't', "top" }, + { KEY_HOME, "top" }, + { 'e', "bottom" }, + { KEY_LL, "bottom" }, + { KEY_END, "bottom" }, + + { 'u', "iscrollback" }, + { 'd', "iscrollon" }, + { C('u'), "iscrollback1" }, + { C('d'), "iscrollon1" }, + + { 'B', "panback" }, + { KEY_LEFT, "panback" }, + { 'F', "panon" }, + { KEY_RIGHT, "panon" }, + { C('b'), "panback1" }, + { C('f'), "panon1" }, + + { '?', "help" }, + { KEY_HELP, "help" }, + { KEY_F(1), "help" }, + { '/', "search" }, + { '\\', "searchagain" }, + { C('l'), "redraw" }, + + { KEY_ENTER, "select-and-quit" }, + { '\r', "select-and-quit" }, + { 'x', "abort" }, + { 'X', "abort" }, + + { -1, 0 } +}; diff --git a/dselect/methlist.cc b/dselect/methlist.cc new file mode 100644 index 00000000..710fabf3 --- /dev/null +++ b/dselect/methlist.cc @@ -0,0 +1,213 @@ +/* + * dselect - Debian GNU/Linux package maintenance user interface + * methlist.cc - list of access methods and options + * + * Copyright (C) 1995 Ian Jackson + * + * This 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, + * or (at your option) any later version. + * + * This 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; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include + +extern "C" { +#include "config.h" +#include "dpkg.h" +#include "dpkg-db.h" +} +#include "dselect.h" +#include "bindings.h" +#include "method.h" +#include "helpmsgs.h" + +static keybindings methodlistbindings(methodlist_kinterps,methodlist_korgbindings); + +const char *methodlist::itemname(int index) { + return table[index]->name; +} + +void methodlist::kd_abort() { } + +void methodlist::kd_quit() { + if (debug) fprintf(debug,"methodlist[%p]::kd_quit() setting coption=%p\n", + this, table[cursorline]); + coption= table[cursorline]; +} + +void methodlist::setwidths() { + if (debug) fprintf(debug,"methodlist[%p]::setwidths()\n",this); + + status_width= 1; + gap_width= 1; + name_width= 14; + name_column= status_width + gap_width; + description_column= name_column + name_width + gap_width; + + total_width= TOTAL_LIST_WIDTH; + description_width= total_width - description_column; +} + +void methodlist::redrawtitle() { + if (title_height) { + mywerase(titlewin); + mvwaddnstr(titlewin,0,0,"dselect - list of access methods",xmax); + wnoutrefresh(titlewin); + } +} + +void methodlist::redrawthisstate() { + if (!thisstate_height) return; + mywerase(thisstatepad); + wprintw(thisstatepad, + "Access method `%s'.", + table[cursorline]->name); + pnoutrefresh(thisstatepad, 0,0, thisstate_row,0, + thisstate_row, lesserint(total_width - 1, xmax - 1)); +} + +void methodlist::redraw1itemsel(int index, int selected) { + int i; + const char *p; + + wattrset(listpad, selected ? listsel_attr : list_attr); + mvwaddch(listpad,index,0, + table[index] == coption ? '*' : ' '); + wattrset(listpad, selected ? listsel_attr : list_attr); + mvwprintw(listpad,index,name_column-1, " %-*.*s ", + name_width, name_width, table[index]->name); + + i= description_width; + p= table[index]->summary ? table[index]->summary : ""; + while (i>0 && *p && *p != '\n') { + waddch(listpad,*p); + i--; p++; + } + while (i>0) { + waddch(listpad,' '); + i--; + } +} + +void methodlist::redrawcolheads() { + if (colheads_height) { + wattrset(colheadspad,colheads_attr); + mywerase(colheadspad); + mvwaddstr(colheadspad,0,0, " "); + mvwaddnstr(colheadspad,0,name_column, "Abbrev.", name_width); + mvwaddnstr(colheadspad,0,description_column, "Description", description_width); + } + refreshcolheads(); +} + +methodlist::methodlist() : baselist(&methodlistbindings) { + if (debug) + fprintf(debug,"methodlist[%p]::methodlist()\n",this); + + table= new struct option*[noptions]; + + struct option *opt, **ip; + for (opt=options, ip=table, nitems=0; opt; opt=opt->next, nitems++) *ip++= opt; + assert(nitems==noptions); + + setcursor(0); + + if (debug) + fprintf(debug,"methodlist[%p]::methodlist done; noptions=%d\n", this, noptions); +} + +methodlist::~methodlist() { + if (debug) fprintf(debug,"methodlist[%p]::~methodlist()\n",this); + delete[] table; +} + +quitaction methodlist::display() { + int response; + const keybindings::interpretation *interp; + + if (debug) fprintf(debug,"methodlist[%p]::display()\n",this); + + setupsigwinch(); + startdisplay(); + + if (debug) fprintf(debug,"methodlist[%p]::display() entering loop\n",this); + for (;;) { + if (whatinfo_height) wcursyncup(whatinfowin); + if (doupdate() == ERR) ohshite("doupdate failed"); + signallist= this; + if (sigprocmask(SIG_UNBLOCK,&sigwinchset,0)) ohshite("failed to unblock SIGWINCH"); + response= getch(); + if (sigprocmask(SIG_BLOCK,&sigwinchset,0)) ohshite("failed to re-block SIGWINCH"); + if (response == ERR) ohshite("getch failed"); + interp= (*bindings)(response); + if (debug) + fprintf(debug,"methodlist[%p]::display() response=%d interp=%s\n", + this,response, interp ? interp->action : "[none]"); + if (!interp) { beep(); continue; } + (this->*(interp->mfn))(); + if (interp->qa != qa_noquit) break; + } + pop_cleanup(ehflag_normaltidy); // unset the SIGWINCH handler + enddisplay(); + + if (debug) fprintf(debug,"methodlist[%p]::display() done\n",this); + + return interp->qa; +} + +void methodlist::itd_description() { + whatinfovb("explanation of "); + whatinfovb(table[cursorline]->name); + + wattrset(infopad,info_headattr); + waddstr(infopad, table[cursorline]->name); + waddstr(infopad," - "); + waddstr(infopad, table[cursorline]->summary); + wattrset(infopad,info_attr); + + const char *m= table[cursorline]->description; + if (!m || !*m) m= "No explanation available."; + waddstr(infopad,"\n\n"); + wordwrapinfo(0,m); +} + +void methodlist::redrawinfo() { + if (!info_height) return; + whatinfovb.reset(); + werase(infopad); wmove(infopad,0,0); + + if (debug) fprintf(debug,"methodlist[%p]::redrawinfo()\n", this); + + itd_description(); + + whatinfovb.terminate(); + int y,x; + getyx(infopad, y,x); + if (x) y++; + infolines= y; + + refreshinfo(); +} + +const struct helpmenuentry *methodlist::helpmenulist() { + static const struct helpmenuentry list[]= { + { 'i', &hlp_methintro }, + { 'k', &hlp_methkeys }, + { 0 } + }; + return list; +}; diff --git a/dselect/method.cc b/dselect/method.cc new file mode 100644 index 00000000..67939eba --- /dev/null +++ b/dselect/method.cc @@ -0,0 +1,272 @@ +/* + * dselect - Debian GNU/Linux package maintenance user interface + * method.cc - access method handling + * + * Copyright (C) 1995 Ian Jackson + * + * This 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, + * or (at your option) any later version. + * + * This 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 dpkg; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +extern "C" { +#include "config.h" +#include "dpkg.h" +#include "dpkg-db.h" +} +#include "dselect.h" +#include "method.h" + +static const char *const methoddirectories[]= { + LIBDIR "/" METHODSDIR, + LOCALLIBDIR "/" METHODSDIR, + 0 +}; + +static char *methodlockfile= 0; +static int methlockfd= -1; + +static void cu_unlockmethod(int, void**) { + assert(methodlockfile); + assert(methlockfd); + if (flock(methlockfd,LOCK_UN)) + ohshite("unable to unlock access method area"); +} + +static enum urqresult ensureoptions(void) { + const char *const *ccpp; + option *newoptions; + int nread; + + if (!options) { + newoptions= 0; + nread= 0; + for (ccpp= methoddirectories; *ccpp; ccpp++) + readmethods(*ccpp, &newoptions, &nread); + if (!newoptions) { + curseson(); + addstr("No access methods are available.\n\n" + "Press RETURN to continue."); + refresh(); getch(); + return urqr_fail; + } + options= newoptions; + noptions= nread; + } + return urqr_normal; +} + +static void lockmethod(void) { + if (!methodlockfile) { + int l; + l= strlen(admindir); + methodlockfile= new char[l+sizeof(METHLOCKFILE)+2]; + strcpy(methodlockfile,admindir); + strcpy(methodlockfile+l, "/" METHLOCKFILE); + } + if (methlockfd == -1) { + methlockfd= open(methodlockfile, O_RDWR|O_CREAT|O_TRUNC, 0660); + if (methlockfd == -1) { + if (errno == EPERM) + ohshit("you do not have permission to change the access method"); + ohshite("unable to open/create access method lockfile"); + } + } + if (flock(methlockfd,LOCK_EX|LOCK_NB)) { + if (errno == EWOULDBLOCK || errno == EAGAIN) + ohshit("the access method area is already locked"); + ohshite("unable to lock access method area"); + } + push_cleanup(cu_unlockmethod,~0, 0,0, 0); +} + +static int catchsignals[]= { SIGQUIT, SIGINT, 0 }; +#define NCATCHSIGNALS ((signed)(sizeof(catchsignals)/sizeof(int))-1) +static struct sigaction uncatchsignal[NCATCHSIGNALS]; + +void cu_restoresignals(int, void**) { + int i; + for (i=0; imeth->pathinmeth,exepath); + const char *fallibleargs[] = { + exepath, + admindir, + coption->meth->name, + coption->name, + 0 + }; + ur= falliblesubprocess(coption->meth->path,name,fallibleargs); + } else { + curseson(); + addstr("No access method is selected/configured.\n\n" + "Press RETURN to continue."); + refresh(); getch(); + ur= urqr_fail; + } + pop_cleanup(ehflag_normaltidy); + + return ur; +} + +urqresult urq_update(void) { + return runscript(METHODUPDATESCRIPT,"update available list script"); +} + +urqresult urq_install(void) { + return runscript(METHODINSTALLSCRIPT,"installation script"); +} + +static urqresult rundpkgauto(const char *name, const char *dpkgmode) { + const char *fallibleargs[] = { + DPKG, + "--pending", + dpkgmode, + 0 + }; + cursesoff(); + printf("running dpkg --pending %s ...\n",dpkgmode); + fflush(stdout); + return falliblesubprocess(DPKG,name,fallibleargs); +} + +urqresult urq_remove(void) { + return rundpkgauto("dpkg --remove","--remove"); +} + +urqresult urq_config(void) { + return rundpkgauto("dpkg --configure","--configure"); +} + +urqresult urq_setup(void) { + quitaction qa; + urqresult ur; + + ur= ensureoptions(); if (ur != urqr_normal) return ur; + lockmethod(); + getcurrentopt(); + + curseson(); + methodlist *l= new methodlist(); + qa= l->display(); + delete l; + + if (qa == qa_quitchecksave) { + strcpy(coption->meth->pathinmeth,METHODSETUPSCRIPT); + const char *fallibleargs[] = { + METHODSETUPSCRIPT, + admindir, + coption->meth->name, + coption->name, + 0 + }; + ur= falliblesubprocess(coption->meth->path,"query/setup script",fallibleargs); + if (ur == urqr_normal) writecurrentopt(); + } else { + ur= urqr_fail; + } + + pop_cleanup(ehflag_normaltidy); + return ur; +} diff --git a/dselect/method.h b/dselect/method.h new file mode 100644 index 00000000..7482044d --- /dev/null +++ b/dselect/method.h @@ -0,0 +1,79 @@ +/* -*- c++ -*- + * dselect - Debian GNU/Linux package maintenance user interface + * method.h - access method handling declarations + * + * Copyright (C) 1995 Ian Jackson + * + * This 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, + * or (at your option) any later version. + * + * This 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 dpkg; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef METHOD_H +#define METHOD_H + +struct method { + struct method *next, *back; + char *name, *path, *pathinmeth; +}; + +struct option { + option *next; + method *meth; + char index[OPTIONINDEXMAXLEN]; + char *name, *summary; + char *description; +}; + +class methodlist : public baselist { + int status_width, gap_width, name_width, description_width; + int name_column, description_column; + + // Table of methods + struct option **table; + + // Misc. + char searchstring[50]; + + // Information displays + void itd_description(); + + // Define these virtuals + void redraw1itemsel(int index, int selected); + void redrawcolheads(); + void redrawthisstate(); + void redrawinfo(); + void redrawtitle(); + void setwidths(); + const char *itemname(int index); + const struct helpmenuentry *helpmenulist(); + + public: + // Keybinding functions */ + void kd_quit(); + void kd_abort(); + + methodlist(); + quitaction display(); + ~methodlist(); +}; + +extern int noptions; +extern struct option *options, *coption; +extern struct method *methods; + +extern void readmethods(const char *pathbase, option **optionspp, int *nread); +extern void getcurrentopt(); +extern void writecurrentopt(); + +#endif /* METHOD_H */ diff --git a/dselect/methparse.cc b/dselect/methparse.cc new file mode 100644 index 00000000..02e51b41 --- /dev/null +++ b/dselect/methparse.cc @@ -0,0 +1,289 @@ +/* + * dselect - Debian GNU/Linux package maintenance user interface + * methparse.cc - access method list parsing + * + * Copyright (C) 1995 Ian Jackson + * + * This 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, + * or (at your option) any later version. + * + * This 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 dpkg; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +extern "C" { +#include "config.h" +#include "dpkg.h" +#include "dpkg-db.h" +} +#include "dselect.h" +#include "bindings.h" + +int noptions=0; +struct option *options=0, *coption=0; +struct method *methods=0; + +static void badmethod(const char *pathname, const char *why) __NORETURN; +static void badmethod(const char *pathname, const char *why) { + ohshit("syntax error in method options file `%.250s' -- %s", pathname, why); +} + +static void eofmethod(const char *pathname, FILE *f, const char *why) __NORETURN; +static void eofmethod(const char *pathname, FILE *f, const char *why) { + if (ferror(f)) ohshite("error reading options file `%.250s'",pathname); + badmethod(pathname,why); +} + +void readmethods(const char *pathbase, option **optionspp, int *nread) { + static const char *const methodprograms[]= { + METHODSETUPSCRIPT, METHODUPDATESCRIPT, METHODINSTALLSCRIPT, 0 + }; + const char *const *ccpp; + int methodlen, c, baselen; + char *p, *pathinmeth, *pathbuf, *pathmeth; + DIR *dir; + FILE *names, *descfile; + struct dirent *dent; + struct varbuf vb; + method *meth; + option *opt, **optinsert; + struct stat stab; + + baselen= strlen(pathbase); + pathbuf= new char[baselen+IMETHODMAXLEN+IOPTIONMAXLEN+sizeof(OPTIONSDESCPFX)+10]; + strcpy(pathbuf,pathbase); + strcpy(pathbuf+baselen,"/"); + pathmeth= pathbuf+baselen+1; + + dir= opendir(pathbuf); + if (!dir) { + if (errno == ENOENT) return; + ohshite("unable to read `%.250s' directory for reading methods",pathbuf); + } + + if (debug) fprintf(debug,"readmethods(`%s',...) directory open\n", pathbase); + + while ((dent= readdir(dir)) != 0) { + c= dent->d_name[0]; + if (debug) fprintf(debug,"readmethods(`%s',...) considering `%s' ...\n", + pathbase,dent->d_name); + if (c != '_' && !isalpha(c)) continue; + for (p= dent->d_name+1; (c= *p) != 0 && isalnum(c) && c != '_'; p++); + if (c) continue; + methodlen= strlen(dent->d_name); + if (methodlen > IMETHODMAXLEN) + ohshit("method `%.250s' has name that is too long (%d > %d characters)", + dent->d_name, methodlen, IMETHODMAXLEN); + strcpy(pathmeth, dent->d_name); + strcpy(pathmeth+methodlen, "/"); + pathinmeth= pathmeth+methodlen+1; + + for (ccpp= methodprograms; *ccpp; ccpp++) { + strcpy(pathinmeth,*ccpp); + if (access(pathbuf,R_OK|X_OK)) + ohshite("unable to access method script `%.250s'",pathbuf); + } + if (debug) fprintf(debug," readmethods(`%s',...) scripts ok\n", pathbase); + + strcpy(pathinmeth,METHODOPTIONSFILE); + names= fopen(pathbuf,"r"); + if (!names) ohshite("unable to read method options file `%.250s'",pathbuf); + + meth= new method; + meth->name= new char[strlen(dent->d_name)+1]; + strcpy(meth->name,dent->d_name); + meth->path= new char[baselen+1+methodlen+2+50]; + strncpy(meth->path,pathbuf,baselen+1+methodlen); + strcpy(meth->path+baselen+1+methodlen,"/"); + meth->pathinmeth= meth->path+baselen+1+methodlen+1; + meth->next= methods; + meth->back= 0; + if (methods) methods->back= meth; + methods= meth; + if (debug) fprintf(debug," readmethods(`%s',...) new method" + " name=`%s' path=`%s' pathinmeth=`%s'\n", + pathbase, meth->name, meth->path, meth->pathinmeth); + + while ((c= fgetc(names)) != EOF) { + if (isspace(c)) continue; + opt= new option; + opt->meth= meth; + vb.reset(); + do { + if (!isdigit(c)) badmethod(pathbuf,"non-digit where digit wanted"); + vb(c); + c= fgetc(names); + if (c == EOF) eofmethod(pathbuf,names,"EOF in index string"); + } while (!isspace(c)); + if (strlen(vb.string()) > OPTIONINDEXMAXLEN) + badmethod(pathbuf,"index string too long"); + strcpy(opt->index,vb.string()); + do { + if (c == '\n') badmethod(pathbuf,"newline before option name start"); + c= fgetc(names); + if (c == EOF) eofmethod(pathbuf,names,"EOF before option name start"); + } while (isspace(c)); + vb.reset(); + if (!isalpha(c) && c != '_') + badmethod(pathbuf,"nonalpha where option name start wanted"); + do { + if (!isalnum(c) && c != '_') badmethod(pathbuf,"non-alphanum in option name"); + vb(c); + c= fgetc(names); + if (c == EOF) eofmethod(pathbuf,names,"EOF in option name"); + } while (!isspace(c)); + opt->name= new char[strlen(vb.string())+1]; + strcpy(opt->name,vb.string()); + do { + if (c == '\n') badmethod(pathbuf,"newline before summary"); + c= fgetc(names); + if (c == EOF) eofmethod(pathbuf,names,"EOF before summary"); + } while (isspace(c)); + vb.reset(); + do { + vb(c); + c= fgetc(names); + if (c == EOF) eofmethod(pathbuf,names,"EOF in summary - missing newline"); + } while (c != '\n'); + opt->summary= new char[strlen(vb.string())+1]; + strcpy(opt->summary,vb.string()); + + strcpy(pathinmeth,OPTIONSDESCPFX); + strcpy(pathinmeth+sizeof(OPTIONSDESCPFX)-1,opt->name); + descfile= fopen(pathbuf,"r"); + if (!descfile) { + if (errno != ENOENT) + ohshite("unable to open option description file `%.250s'",pathbuf); + opt->description= 0; + } else { /* descfile != 0 */ + if (fstat(fileno(descfile),&stab)) + ohshite("unable to stat option description file `%.250s'",pathbuf); + opt->description= new char[stab.st_size+1]; errno=0; + if (fread(opt->description,1,stab.st_size+1,descfile) != stab.st_size) + ohshite("failed to read option description file `%.250s'",pathbuf); + opt->description[stab.st_size]= 0; + if (ferror(descfile)) + ohshite("error during read of option description file `%.250s'",pathbuf); + fclose(descfile); + } + strcpy(pathinmeth,METHODOPTIONSFILE); + + if (debug) fprintf(debug," readmethods(`%s',...) new option" + " index=`%s' name=`%s' summary=`%.20s'" + " strlen(description=%s)=%d" + " method name=`%s' path=`%s' pathinmeth=`%s'\n", + pathbase, + opt->index, opt->name, opt->summary, + opt->description ? "`...'" : "null", + opt->description ? strlen(opt->description) : -1, + opt->meth->name, opt->meth->path, opt->meth->pathinmeth); + for (optinsert= optionspp; + *optinsert && strcmp(opt->index,(*optinsert)->index) > 0; + optinsert= &(*optinsert)->next); + opt->next= *optinsert; + *optinsert= opt; + (*nread)++; + } + if (ferror(names)) + ohshite("error during read of method options file `%.250s'",pathbuf); + fclose(names); + } + closedir(dir); + if (debug) fprintf(debug,"readmethods(`%s',...) done\n", pathbase); + delete[] pathbuf; +} + +static char *methoptfile= 0; + +void getcurrentopt() { + char methoptbuf[IMETHODMAXLEN+1+IOPTIONMAXLEN+2]; + FILE *cmo; + int l; + int admindirlen; + char *p; + method *meth; + option *opt; + + if (!methoptfile) { + admindirlen= strlen(admindir); + methoptfile= new char[admindirlen + sizeof(CMETHOPTFILE) + 2]; + strcpy(methoptfile,admindir); + strcpy(methoptfile+admindirlen, "/" CMETHOPTFILE); + } + + coption= 0; + cmo= fopen(methoptfile,"r"); + if (!cmo) { + if (errno == ENOENT) return; + ohshite("unable to open current option file `%.250s'",methoptfile); + } + if (debug) fprintf(debug,"getcurrentopt() cmethopt open\n"); + if (!fgets(methoptbuf,sizeof(methoptbuf),cmo)) { fclose(cmo); return; } + if (fgetc(cmo) != EOF) { fclose(cmo); return; } + if (!feof(cmo)) { fclose(cmo); return; } + if (debug) fprintf(debug,"getcurrentopt() cmethopt eof\n"); + fclose(cmo); + if (debug) fprintf(debug,"getcurrentopt() cmethopt read\n"); + l= strlen(methoptbuf); if (!l || methoptbuf[l-1] != '\n') return; + methoptbuf[--l]= 0; + if (debug) fprintf(debug,"getcurrentopt() cmethopt len and newline\n"); + p= strchr(methoptbuf,' '); if (!p) return; + if (debug) fprintf(debug,"getcurrentopt() cmethopt space\n"); + *p++= 0; + if (debug) fprintf(debug,"getcurrentopt() cmethopt meth name `%s'\n", methoptbuf); + for (meth= methods; meth && strcmp(methoptbuf,meth->name); meth= meth->next); + if (!meth) return; + if (debug) fprintf(debug,"getcurrentopt() cmethopt meth found; opt `%s'\n",p); + for (opt= options; opt && (opt->meth != meth || strcmp(p,opt->name)); opt= opt->next); + if (!opt) return; + if (debug) fprintf(debug,"getcurrentopt() cmethopt opt found\n"); + coption= opt; +} + +void writecurrentopt() { + FILE *cmo; + int l; + static char *newfile=0; + + assert(methoptfile); + if (!newfile) { + l= strlen(methoptfile); + newfile= new char[l + sizeof(NEWDBEXT) + 1]; + strcpy(newfile,methoptfile); + strcpy(newfile+l, NEWDBEXT); + } + cmo= fopen(newfile,"w"); + if (!cmo) ohshite("unable to open new option file `%.250s'",newfile); + if (fprintf(cmo,"%s %s\n",coption->meth->name,coption->name) == EOF) { + fclose(cmo); + ohshite("unable to write new option to `%.250s'",newfile); + } + if (fclose(cmo)) + ohshite("unable to close new option file `%.250s'",newfile); + if (rename(newfile,methoptfile)) + ohshite("unable to install new option as `%.250s'",methoptfile); +} diff --git a/dselect/mkcurkeys.pl b/dselect/mkcurkeys.pl new file mode 100755 index 00000000..19291429 --- /dev/null +++ b/dselect/mkcurkeys.pl @@ -0,0 +1,121 @@ +#!/usr/bin/perl -- +# +# dselect - Debian GNU/Linux package maintenance user interface +# mkcurkeys.pl - generate strings mapping key names to ncurses numbers +# +# Copyright (C) 1995 Ian Jackson +# +# This 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, +# or (at your option) any later version. +# +# This 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; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +@ARGV == 2 || die; + +open(OV,"<$ARGV[0]") || die $!; +while () { + m/^#/ && next; + m/\S/ || next; + m/^(\w+)\s+(\S.*\S)\s+$/ || die; + $over{$1}= $2; + $base{$1}= ''; +} +close(OV); + +for ($i=1, $let='A'; $i<=26; $i++, $let++) { + $name{$i}= "^$let"; + $base{$i}= ''; +} + +open(NCH,"<$ARGV[1]") || die $!; +while () { + s/\s+$//; + m/#define KEY_(\w+)\s+\d+\s+/ || next; + $rhs= $'; + $k= "KEY_$1"; + $_= $1; + &capit; + $base{$k}= $_; + $_= $rhs; + s/(\w)[\(\)]/$1/g; + s/\w+ \((\w+)\)/$1/; + next unless m|^/\* (\w[\w ]+\w) \*/$|; + $_= $1; + s/ key$//; + next if s/^shifted /shift / ? m/ .* .* / : m/ .* /; + &capit; + $name{$k}= $_; +} +close(NCH); + +printf(<<'END') || die $!; +/* + * WARNING - THIS FILE IS GENERATED AUTOMATICALLY - DO NOT EDIT + * It is generated by mkcurkeys.pl from + * and keyoverride. If you want to override things try adding + * them to keyoverride. + */ + +END + +for ($i=33; $i<=126; $i++) { + $k= $i; + $v= pack("C",$i); + if ($v eq ',') { $comma=$k; next; } + &p; +} + +for $k (sort { + $a+0 eq $a ? + $b+0 eq $b ? $a <=> $b : -1 + : $b+0 eq $b ? 1 : + $a cmp $b + } keys %base) { + $v= $base{$k}; + $v= $name{$k} if defined($name{$k}); + $v= $over{$k} if defined($over{$k}); + next if $v eq '[elide]'; + &p; +} + +for ($i=1; $i<64; $i++) { + $k= "KEY_F($i)"; $v= "F$i"; + &p; +} + +$k=$comma; $v=','; &p; + +print(<<'END') || die $!; + { -1, 0 } +END + +close(STDOUT) || die $!; +exit(0); + +sub capit { + $o= ''; y/A-Z/a-z/; $_= " $_"; + while (m/ (\w)/) { + $o .= $`.' '; + $_ = $1; + y/a-z/A-Z/; + $o .= $_; + $_ = $'; + } + $_= $o.$_; s/^ //; +} + +sub p { + $v =~ s/["\\]/\\$&/g; + printf(" { %-15s \"%-20s },\n", + $k.',', + $v.'"') || die $!; +} diff --git a/dselect/mkhelpmsgs.pl b/dselect/mkhelpmsgs.pl new file mode 100755 index 00000000..878620fe --- /dev/null +++ b/dselect/mkhelpmsgs.pl @@ -0,0 +1,74 @@ +#!/usr/bin/perl + +$maxnlines= 22; + +open(SRC,"helpmsgs.src") || die $!; +open(NC,">helpmsgs.cc.new") || die $!; +open(NH,">helpmsgs.h.new") || die $!; + +&autowarn('NC'); &autowarn('NH'); + +print(NC "#include \"helpmsgs.h\"\n") || die $!; +print(NH <<'END') || die $!; +#ifndef HELPMSGS_H +#define HELPMSGS_H +struct helpmessage { const char *title; const char *text; }; +END + +$state= 'start'; +$nblanks= 0; $nlines= 0; +while () { + s/\"/\\\"/g; + if (m/^\@\@\@ (\w+)\s+(\S.*\S)\s+$/) { + &finishif; + $currentname= $1; $currenttitle= $2; + print(NH "extern const struct helpmessage hlp_$currentname;\n") || die $!; + print(NC + "const struct helpmessage hlp_$currentname = {\n". + " \"$currenttitle\", \"") || die $!; + } elsif (m/^\@\@\@/) { + die; + } elsif (!m/\S/) { + $nblanks++; + } else { + if ($state ne 'start' && $nblanks) { + print(NC ("\\n"x$nblanks)."\\\n") || die $!; + $nlines+= $nblanks; + } + $state= 'middle'; $nblanks= 0; + s/\s*\n$//; + print(NC "\\\n".$_."\\n") || die $!; + $nlines++; + } +} + +&finishif; + +close(NC) || die $!; +print(NH "#endif /* HELPMSGS_H */\n") || die $!; +close(NH) || die $!; + +rename("helpmsgs.cc.new","helpmsgs.cc") || die $!; +rename("helpmsgs.h.new","helpmsgs.h") || die $!; + +sub finishif { + if ($state ne 'start') { + print(NC "\"\n};\n") || die $!; + printf "\t\t%s: %d lines\n",$currentname,$nlines; + if ($nlines > $maxnlines) { warn "Too many lines in $currentname"; } + } + $state= 'start'; + $nblanks= 0; $nlines= 0; +} + + +sub autowarn { + $fh= $_[0]; + print($fh <<'END') || die $!; +/* + * WARNING - THIS FILE IS GENERATED AUTOMATICALLY - DO NOT EDIT + * It is generated by mkhelpmsgs.pl from helpmsgs.src. + */ + +END +} diff --git a/dselect/pkgcmds.cc b/dselect/pkgcmds.cc new file mode 100644 index 00000000..bc728af2 --- /dev/null +++ b/dselect/pkgcmds.cc @@ -0,0 +1,217 @@ +/* + * dselect - Debian GNU/Linux package maintenance user interface + * pkgcmds.cc - package list keyboard commands + * + * Copyright (C) 1994,1995 Ian Jackson + * + * This 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, + * or (at your option) any later version. + * + * This 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; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include + +extern "C" { +#include "config.h" +#include "dpkg.h" +#include "dpkg-db.h" +} +#include "dselect.h" +#include "pkglist.h" + +static int matches(struct pkginfo *pkg, struct pkginfo *comparewith) { + if (comparewith->priority != pkginfo::pri_unset && + (comparewith->priority != pkg->priority || + comparewith->priority == pkginfo::pri_other && + strcasecmp(comparewith->otherpriority,pkg->otherpriority))) + return 0; + if (comparewith->section && + strcasecmp(comparewith->section, + pkg->section ? + pkg->section : "")) + return 0; + return 1; +} + +void packagelist::affectedrange(int *startp, int *endp) { + if (table[cursorline]->pkg->name) { + *startp= cursorline; + *endp= cursorline+1; + return; + } + int index; + for (index= cursorline; index < nitems && !table[index]->pkg->name; index++); + if (index >= nitems) { + *startp= *endp= cursorline; + return; + } + *startp= index; + while (index < nitems && matches(table[index]->pkg,table[cursorline]->pkg)) index++; + *endp= index; +} + +void packagelist::movecursorafter(int ncursor) { + if (ncursor >= nitems) ncursor= nitems-1; + topofscreen += ncursor-cursorline; + if (topofscreen > nitems - list_height) topofscreen= nitems - list_height; + if (topofscreen < 0) topofscreen= 0; + setcursor(ncursor); + refreshlist(); redrawthisstate(); +} + +void packagelist::setwant(pkginfo::pkgwant nw) { + int index, top, bot; + + if (!readwrite) { beep(); return; } + + if (recursive) { + redrawitemsrange(cursorline,cursorline+1); + table[cursorline]->selected= nw; + redraw1item(cursorline); + top= cursorline; + bot= cursorline+1; + } else { + packagelist *sub= new packagelist(bindings,0); + + affectedrange(&top,&bot); + for (index= top; index < bot; index++) { + if (!table[index]->pkg->name || + table[index]->selected == nw || + table[index]->selected == pkginfo::want_purge && nw == pkginfo::want_deinstall) + continue; + sub->add(table[index]->pkg,nw); + } + + repeatedlydisplay(sub,dp_may,this); + for (index=top; index < bot; index++) + redraw1item(index); + } + movecursorafter(bot); +} + +void packagelist::kd_select() { setwant(pkginfo::want_install); } +void packagelist::kd_deselect() { setwant(pkginfo::want_deinstall); } +void packagelist::kd_purge() { setwant(pkginfo::want_purge); } + +void packagelist::sethold(int hold) { + int top, bot, index; + + if (!readwrite) { beep(); return; } + + affectedrange(&top,&bot); + for (index= top; index < bot; index++) { + // Sorry about the contortions, but GCC produces a silly warning otherwise + unsigned int neweflag= table[index]->pkg->eflag; + if (hold) neweflag |= pkginfo::eflagf_hold; + else neweflag &= ~pkginfo::eflagf_hold; + table[index]->pkg->eflag= (enum pkginfo::pkgeflag)neweflag; + redraw1item(index); + } + movecursorafter(bot); +} + +void packagelist::kd_hold() { sethold(1); } +void packagelist::kd_unhold() { sethold(0); } + +const char *packagelist::itemname(int index) { + return table[index]->pkg->name; +} + +void packagelist::kd_swaporder() { + switch (sortorder) { + case so_priority: sortorder= so_section; break; + case so_section: sortorder= so_alpha; break; + case so_alpha: sortorder= so_priority; break; + case so_unsorted: return; + default: internerr("unknown sort in kd_swaporder"); + } + const char *oldname= table[cursorline]->pkg->name; + sortmakeheads(); + int newcursor; + newcursor= 0; + if (oldname) { + int index; + for (index=0; indexpkg->name && !strcasecmp(oldname,table[index]->pkg->name)) { + newcursor= index; + break; + } + } + } + topofscreen= newcursor-1; + if (topofscreen > nitems - list_height) topofscreen= nitems-list_height; + if (topofscreen < 0) topofscreen= 0; + setwidths(); + redrawtitle(); + redrawcolheads(); + ldrawnstart= ldrawnend= -1; + cursorline= -1; + setcursor(newcursor); + refreshlist(); +} + +void packagelist::kd_verbose() { + verbose= !verbose; + setwidths(); + leftofscreen= 0; + ldrawnstart= ldrawnend= -1; + redrawtitle(); + redrawcolheads(); + redrawitemsrange(topofscreen,lesserint(topofscreen+list_height,nitems)); + refreshlist(); +} + +void packagelist::kd_quit_noop() { } + +void packagelist::kd_revert_abort() { + int index; + for (index=0; indexpkg->name) + table[index]->selected= table[index]->original; + ldrawnstart= ldrawnend= -1; + } + refreshlist(); redrawthisstate(); +} + +void packagelist::kd_revertdirect() { + int index; + for (index=0; indexpkg->name) + table[index]->selected= table[index]->direct; + ldrawnstart= ldrawnend= -1; + } + refreshlist(); redrawthisstate(); +} + +void packagelist::kd_revertsuggest() { + int index; + for (index=0; indexpkg->name) + table[index]->selected= table[index]->suggested; + ldrawnstart= ldrawnend= -1; + } + refreshlist(); redrawthisstate(); +} + +/* fixme: configurable purge/deselect */ +/* fixme: un-hold things */ + +void packagelist::kd_info() { + currentinfo++; + infotopofscreen=0; + redrawinfo(); +} diff --git a/dselect/pkgdepcon.cc b/dselect/pkgdepcon.cc new file mode 100644 index 00000000..6026a5db --- /dev/null +++ b/dselect/pkgdepcon.cc @@ -0,0 +1,303 @@ +/* + * dselect - Debian GNU/Linux package maintenance user interface + * pkgdepcon.cc - dependency and conflict resolution + * + * Copyright (C) 1995 Ian Jackson + * + * This 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, + * or (at your option) any later version. + * + * This 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; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include + +extern "C" { +#include "config.h" +#include "dpkg.h" +#include "dpkg-db.h" +} +#include "dselect.h" +#include "pkglist.h" + +static const int depdebug= 1; + +int packagelist::resolvesuggest() { + // We continually go around looking for things to change, but we may + // only change the `suggested' value if we also increase the `priority' + // Return 2 if we made a change due to a Recommended, Depends or Conficts, + // or 1 if we offered or made a change because of an Optional line. + if (debug) + fprintf(debug,"packagelist[%p]::resolvesuggest()\n",this); + int changemade, maxchangemade; + maxchangemade= 0; + for (;;) { + changemade= 0; + int index; + for (index=0; indexpkg->name) continue; + if (depdebug) + fprintf(debug,"packagelist[%p]::resolvesuggest() loop[%i] %s / %d\n", + this, index, table[index]->pkg->name, changemade); + dependency *depends; + for (depends= table[index]->pkg->available.depends; + depends; + depends= depends->next) + changemade= greaterint(changemade, resolvedepcon(depends)); + deppossi *possi; + for (possi= table[index]->pkg->available.depended; + possi; + possi= possi->nextrev) + changemade= greaterint(changemade, resolvedepcon(possi->up)); + for (depends= table[index]->pkg->available.depends; + depends; + depends= depends->next) + if (depends->type == dep_provides) + for (possi= depends->list->ed->available.valid + ? depends->list->ed->available.depended : 0; + possi; + possi= possi->nextrev) + changemade= greaterint(changemade, resolvedepcon(possi->up)); + if (depdebug) + fprintf(debug,"packagelist[%p]::resolvesuggest() loop[%i] %s / -> %d\n", + this, index, table[index]->pkg->name, changemade); + } + if (!changemade) break; + maxchangemade= greaterint(maxchangemade, changemade); + } + if (debug) + fprintf(debug,"packagelist[%p]::resolvesuggest() done; maxchangemade=%d\n", + this,maxchangemade); + return maxchangemade; +} + +static int dep_update_best_to_change_stop(perpackagestate *& best, pkginfo *trythis) { + // There's no point trying to select a pure virtual package. + if (!trythis->clientdata) return 0; + + if (depdebug) + fprintf(debug,"update_best_to_change(best=%s{%d}, test=%s{%d});\n", + best ? best->pkg->name : "", best ? (int)best->spriority : -1, + trythis->name, trythis->clientdata->spriority); + + // If the problem is caused by us deselecting one of these packages + // we should not try to select another one instead. + if (trythis->clientdata->spriority == sp_deselecting) return 1; + + // If we haven't found anything yet then this is our best so far. + if (!best) goto yes; + + // Select the package with the lowest priority (ie, the one of whom + // we were least sure we wanted it deselected). + if (trythis->clientdata->spriority > best->spriority) return 0; + if (trythis->clientdata->spriority < best->spriority) goto yes; + + // Pick the package with the must fundamental recommendation level. + if (trythis->priority > best->pkg->priority) return 0; + if (trythis->priority < best->pkg->priority) goto yes; + + // If we're still unsure we'll change the first one in the list. + return 0; + + yes: + if (depdebug) fprintf(debug,"update_best_to_change(); yes\n"); + + best=trythis->clientdata; return 0; +} + +int packagelist::deselect_one_of(pkginfo *per, pkginfo *ped, dependency *display) { + perpackagestate *er= per->clientdata; + perpackagestate *ed= ped->clientdata; + + if (!er || er->selected != pkginfo::want_install || + !ed || ed->selected != pkginfo::want_install) return 0; + + add(display,dp_must); + + er= per->clientdata; // these can be changed by add + ed= ped->clientdata; + + if (depdebug) + fprintf(debug,"packagelist[%p]::deselect_one_of(): er %s{%d} ed %s{%d} [%p]\n", + this, er->pkg->name, er->spriority, ed->pkg->name, ed->spriority, display); + + perpackagestate *best; + if (er->spriority < ed->spriority) best= er; // We'd rather change the + else if (er->spriority > ed->spriority) best= ed; // one with the lowest priority. + + else if (er->pkg->priority > + er->pkg->priority) best= er; // ... failing that the one with + else if (er->pkg->priority < // the highest priority + er->pkg->priority) best= ed; + + else best= ed; // ... failing that, the second + + if (depdebug) + fprintf(debug,"packagelist[%p]::deselect_one_of(): best %s{%d}\n", + this, best->pkg->name, best->spriority); + + if (best->spriority >= sp_deselecting) return 0; + best->suggested= + best->pkg->status == pkginfo::stat_notinstalled + ? pkginfo::want_purge : pkginfo::want_deinstall; /* fixme: configurable */ + best->selected= best->suggested; + best->spriority= sp_deselecting; + + return 2; +} + +int packagelist::resolvedepcon(dependency *depends) { + perpackagestate *best; + deppossi *possi, *provider; + int r, foundany; + + if (depdebug) { + fprintf(debug,"packagelist[%p]::resolvedepcon([%p] %s --%s-->", + this,depends,depends->up->name,relatestrings[depends->type]); + for (possi=depends->list; possi; possi=possi->next) + fprintf(debug," %s",possi->ed->name); + fprintf(debug,"); (ing)->want=%s\n", + depends->up->clientdata + ? wantstrings[depends->up->clientdata->suggested] + : "(no clientdata)"); + } + + if (!depends->up->clientdata) return 0; + + if (depends->up->clientdata->selected != pkginfo::want_install) return 0; + + switch (depends->type) { + + case dep_provides: + case dep_replaces: + return 0; + + case dep_suggests: + if (0) return 0; /* fixme: configurable */ + // fall through ... + case dep_recommends: + case dep_depends: + case dep_predepends: + for (possi= depends->list; + possi && !deppossatisfied(possi); + possi= possi->next); + if (depdebug) + fprintf(debug,"packagelist[%p]::resolvedepcon([%p]): depends found %s\n", + this,depends, possi ? possi->ed->name : "[none]"); + if (possi) return 0; + + // Ensures all in the recursive list; adds info strings; ups priorities + r= add(depends, depends->type == dep_suggests ? dp_may : dp_must); + + if (depends->type == dep_suggests) return r; + + best= 0; + for (possi= depends->list; + possi; + possi= possi->next) { + foundany= 0; + if (possi->ed->clientdata) foundany= 1; + if (dep_update_best_to_change_stop(best, possi->ed)) goto mustdeselect; + for (provider= possi->ed->available.valid ? possi->ed->available.depended : 0; + provider; + provider= provider->nextrev) { + if (provider->up->type != dep_provides) continue; + if (provider->up->up->clientdata) foundany= 1; + if (dep_update_best_to_change_stop(best, provider->up->up)) goto mustdeselect; + } + if (!foundany) addunavailable(possi); + } + + if (!best) { + if (depdebug) fprintf(debug,"packagelist[%p]::resolvedepcon([%p]): " + "mustdeselect nobest\n", this,depends); + return r; + } + if (depdebug) + fprintf(debug,"packagelist[%p]::resolvedepcon([%p]): select best=%s{%d}\n", + this,depends, best->pkg->name, best->spriority); + if (best->spriority >= sp_selecting) return r; + best->selected= best->suggested= pkginfo::want_install; + best->spriority= sp_selecting; + return 2; + + mustdeselect: + best= depends->up->clientdata; + if (depdebug) + fprintf(debug,"packagelist[%p]::resolvedepcon([%p]): mustdeselect best=%s{%d}\n", + this,depends, best->pkg->name, best->spriority); + + if (best->spriority >= sp_deselecting) return r; + best->selected= best->suggested= + best->pkg->status == pkginfo::stat_notinstalled + ? pkginfo::want_purge : pkginfo::want_deinstall; /* fixme: configurable */ + best->spriority= sp_deselecting; + return 2; + + case dep_conflicts: + if (!deppossatisfied(depends->list)) return 0; + if (depends->up != depends->list->ed) { + r= deselect_one_of(depends->up, depends->list->ed, depends); if (r) return r; + } + for (provider= depends->list->ed->available.valid ? + depends->list->ed->available.depended : 0; + provider; + provider= provider->nextrev) { + if (provider->up->type != dep_provides) continue; + if (provider->up->up == depends->up) continue; // conflicts & provides same thing + r= deselect_one_of(depends->up, provider->up->up, depends); if (r) return r; + } + if (depdebug) + fprintf(debug,"packagelist[%p]::resolvedepcon([%p]): no desel\n", this,depends); + return 0; + + default: + internerr("unknown deptype"); + } +} + +int deppossatisfied(deppossi *possi) { + if (possi->ed->clientdata && + possi->ed->clientdata->selected == pkginfo::want_install && + !(possi->up->type == dep_conflicts && possi->up->up == possi->ed)) { + // If it's installed, then either it's of the right version, + // and therefore OK, or a version must have been specified, + // in which case we don't need to look at the rest anyway. + if (possi->verrel == deppossi::dvr_none) return 1; + int r= versioncompare(possi->ed->available.version, + possi->ed->available.revision, + possi->version, + possi->revision); + switch (possi->verrel) { + case deppossi::dvr_earlierequal: return r <= 0; + case deppossi::dvr_laterequal: return r >= 0; + case deppossi::dvr_earlierstrict: return r < 0; + case deppossi::dvr_laterstrict: return r > 0; + case deppossi::dvr_exact: return r == 0; + default: internerr("unknown verrel"); + } + } + if (possi->verrel != deppossi::dvr_none) return 0; + deppossi *provider; + for (provider= possi->ed->available.valid ? possi->ed->available.depended : 0; + provider; + provider= provider->nextrev) { + if (provider->up->type != dep_provides) continue; + if (provider->up->up->clientdata && + provider->up->up->clientdata->selected == pkginfo::want_install) + return 1; + } + return 0; +} diff --git a/dselect/pkgdisplay.cc b/dselect/pkgdisplay.cc new file mode 100644 index 00000000..4704e217 --- /dev/null +++ b/dselect/pkgdisplay.cc @@ -0,0 +1,142 @@ +/* + * dselect - Debian GNU/Linux package maintenance user interface + * pkgdisplay.cc - package list display + * + * Copyright (C) 1994,1995 Ian Jackson + * + * This 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, + * or (at your option) any later version. + * + * This 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; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include + +extern "C" { +#include "config.h" +#include "dpkg.h" +#include "dpkg-db.h" +} +#include "dselect.h" +#include "pkglist.h" + +/* These MUST be in the same order as the corresponding enums in dpkg-db.h */ +const char + *const wantstrings[]= { "new package", "selected", "deselected", "purge", 0 }, + *const holdstrings[]= { "", "on hold", "REINSTALL", + "hold,REINSTALL", 0 }, + *const statusstrings[]= { "not installed", "unpacked (not set up)", + "failed config", "installed", "half installed", + "removed (configs remain)", 0 }, + *const prioritystrings[]= { "Required", "Important", "Standard", "Recommended", + "Optional", "Extra", "Contrib", + "!Bug!", "Unclassified", 0 }, + *const relatestrings[]= { "suggests", "recommends", "depends on", "pre-depends on", + "conflicts with", "provides", 0 }, + *const priorityabbrevs[]= { "Req", "Imp", "Std", "Rec", + "Opt", "Xtr", "Ctb", + "bUG", "?" }; +const char statuschars[]= " uF*H-"; +const char holdchars[]= " hRX"; +const char wantchars[]= "n*-_"; + +static int maximumstring(const char *const *array) { + int maxlen= 0; + while (*array) { + int l= strlen(*array); + const char *p= strchr(*array, '('); + if (p && p > *array && *--p == ' ') l= p - *array; + if (l > maxlen) maxlen= l; + array++; + } + return maxlen; +} + +void packagelist::setwidths() { + if (debug) fprintf(debug,"packagelist[%p]::setwidths()\n",this); + + if (verbose) { + status_hold_width= 9; + status_status_width= maximumstring(statusstrings); + status_want_width= maximumstring(wantstrings); + status_width= status_hold_width+status_status_width+status_want_width*2+3; + priority_width= 8; + package_width= 16; + } else { + status_width= 4; + priority_width= 3; + package_width= 12; + } + section_width= 8; + + gap_width= 1; + + if (sortorder == so_section) { + section_column= status_width + gap_width; + priority_column= section_column + section_width + gap_width; + package_column= priority_column + priority_width + gap_width; + } else { + priority_column= status_width + gap_width; + section_column= priority_column + priority_width + gap_width; + package_column= section_column + section_width + gap_width; + } + + description_column= package_column + package_width + gap_width; + + total_width= TOTAL_LIST_WIDTH; + description_width= total_width - description_column; +} + +void packagelist::redrawtitle() { + int x,y; + + if (title_height) { + mywerase(titlewin); + mvwaddnstr(titlewin,0,0, + recursive ? "dselect - recursive package listing" : + !readwrite ? "dselect - inspection of package states" : + "dselect - main package listing", + xmax); + getyx(titlewin,y,x); + if (x < xmax) { + switch (sortorder) { + case so_section: + waddnstr(titlewin, " (by section)", xmax-x); + break; + case so_priority: + waddnstr(titlewin, " (by priority)", xmax-x); + break; + case so_alpha: + waddnstr(titlewin, " (alphabetically)", xmax-x); + break; + case so_unsorted: + break; + default: + internerr("bad sort in redrawtitle"); + } + } + const char *helpstring= readwrite ? (verbose ? " +/-=select v=terse ?=help" + : " +/-=select v=verbose ?=help") + : (verbose ? " v=terse ?=help" + : " v=verbose ?=help"); + int l= strlen(helpstring); + getyx(titlewin,y,x); + if (xmax-l > 0) { + mvwaddstr(titlewin,0,xmax-l, helpstring); + } + wnoutrefresh(titlewin); + } +} diff --git a/dselect/pkginfo.cc b/dselect/pkginfo.cc new file mode 100644 index 00000000..69b0df92 --- /dev/null +++ b/dselect/pkginfo.cc @@ -0,0 +1,163 @@ +/* + * dselect - Debian GNU/Linux package maintenance user interface + * pkginfo.cc - handles (re)draw of package list window infopad + * + * Copyright (C) 1995 Ian Jackson + * + * This 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, + * or (at your option) any later version. + * + * This 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; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include + +extern "C" { +#include "config.h" +#include "dpkg.h" +#include "dpkg-db.h" +} +#include "dselect.h" +#include "bindings.h" +#include "helpmsgs.h" + +const struct helpmenuentry *packagelist::helpmenulist() { + static const struct helpmenuentry + rw[]= { + { 'i', &hlp_mainintro }, + { 'k', &hlp_listkeys }, + { 'l', &hlp_displayexplain1 }, + { 'd', &hlp_displayexplain2 }, + { 0 } + }, + ro[]= { + { 'i', &hlp_readonlyintro }, + { 'k', &hlp_listkeys }, + { 'l', &hlp_displayexplain1 }, + { 'd', &hlp_displayexplain2 }, + { 0 } + }, + recur[]= { + { 'i', &hlp_recurintro }, + { 'k', &hlp_listkeys }, + { 'l', &hlp_displayexplain1 }, + { 'd', &hlp_displayexplain2 }, + { 0 } + }; + return + !readwrite ? ro : + !recursive ? rw : + recur; +} + +int packagelist::itr_recursive() { return recursive; } + +const packagelist::infotype packagelist::infoinfos[]= { + { itr_recursive, itd_relations }, + { 0, itd_description }, + { 0, itd_controlfile }, + { 0, 0 } +}; + +const packagelist::infotype *const packagelist::baseinfo= infoinfos; + +void packagelist::severalinfoblurb(const char *whatinfoline) { + whatinfovb(whatinfoline); + varbuf vb; + vb("The line you have highlighted represents many packages; " + "if you ask to select or deselect it you will affect all the " + "packages which match the criterion shown.\n" + "\n" + "If you move the highlight to a line for a particular package " + "you will see information about that package displayed here."); + wordwrapinfo(0,vb.string()); +} + +void packagelist::itd_relations() { + if (table[cursorline]->pkg->name) { + whatinfovb("interrelationships affecting "); + whatinfovb(table[cursorline]->pkg->name); + if (debug) fprintf(debug,"packagelist[%p]::idt_relations(); `%s'\n", + this,table[cursorline]->relations.string()); + waddstr(infopad,table[cursorline]->relations.string()); + } else { + severalinfoblurb("interrelationships"); + } +} + +void packagelist::itd_description() { + if (table[cursorline]->pkg->name) { + whatinfovb("description of "); + whatinfovb(table[cursorline]->pkg->name); + + const char *m= table[cursorline]->pkg->available.description; + if (!m || !*m) m= "no description available."; + const char *p= strchr(m,'\n'); + int l= p ? (int)(p-m) : strlen(m); + wattrset(infopad,info_headattr); + waddstr(infopad, table[cursorline]->pkg->name); + waddstr(infopad," - "); + waddnstr(infopad,m,l); + wattrset(infopad,info_attr); + if (p) { + waddstr(infopad,"\n\n"); + wordwrapinfo(1,++p); + } + } else { + severalinfoblurb("description"); + } +} + +void packagelist::itd_controlfile() { + werase(infopad); + if (!table[cursorline]->pkg->name) { + severalinfoblurb("control file information"); + } else { + whatinfovb("control file information for "); + whatinfovb(table[cursorline]->pkg->name); + varbuf vb; + varbufrecord(&vb,table[cursorline]->pkg,&table[cursorline]->pkg->available); + vb.terminate(); + if (debug) + fprintf(debug,"packagelist[%p]::idt_controlfile(); `%s'\n",this,vb.string()); + waddstr(infopad,vb.string()); + } +} + +void packagelist::redrawinfo() { + for (;;) { + if (!currentinfo || !currentinfo->display) currentinfo= baseinfo; + if (!currentinfo->relevant) break; + if ((this->*currentinfo->relevant)()) break; + currentinfo++; + } + if (!info_height) return; + whatinfovb.reset(); + werase(infopad); wmove(infopad,0,0); + + if (debug) + fprintf(debug,"packagelist[%p]::redrawinfo(); #=%d\n", this, + (int)(currentinfo - baseinfo)); + + (this->*currentinfo->display)(); + whatinfovb.terminate(); + int y,x; + getyx(infopad, y,x); + if (x) y++; + infolines= y; + + refreshinfo(); +} diff --git a/dselect/pkgkeys.cc b/dselect/pkgkeys.cc new file mode 100644 index 00000000..5e47a931 --- /dev/null +++ b/dselect/pkgkeys.cc @@ -0,0 +1,139 @@ +/* + * dselect - Debian GNU/Linux package maintenance user interface + * pkgkeys.cc - package list keybindings + * + * Copyright (C) 1995 Ian Jackson + * + * This 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, + * or (at your option) any later version. + * + * This 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; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include + +extern "C" { +#include "config.h" +#include "dpkg.h" +#include "dpkg-db.h" +} +#include "dselect.h" +#include "bindings.h" + +const keybindings::interpretation packagelist_kinterps[] = { + { "up", 0, packagelist::kd_up, qa_noquit }, + { "down", 0, packagelist::kd_down, qa_noquit }, + { "top", 0, packagelist::kd_top, qa_noquit }, + { "bottom", 0, packagelist::kd_bottom, qa_noquit }, + { "scrollon", 0, packagelist::kd_scrollon, qa_noquit }, + { "scrollback", 0, packagelist::kd_scrollback, qa_noquit }, + { "iscrollon", 0, packagelist::kd_iscrollon, qa_noquit }, + { "iscrollback", 0, packagelist::kd_iscrollback, qa_noquit }, + { "scrollon1", 0, packagelist::kd_scrollon1, qa_noquit }, + { "scrollback1", 0, packagelist::kd_scrollback1, qa_noquit }, + { "iscrollon1", 0, packagelist::kd_iscrollon1, qa_noquit }, + { "iscrollback1", 0, packagelist::kd_iscrollback1, qa_noquit }, + { "panon", 0, packagelist::kd_panon, qa_noquit }, + { "panback", 0, packagelist::kd_panback, qa_noquit }, + { "panon1", 0, packagelist::kd_panon1, qa_noquit }, + { "panback1", 0, packagelist::kd_panback1, qa_noquit }, + { "select", 0, packagelist::kd_select, qa_noquit }, + { "deselect", 0, packagelist::kd_deselect, qa_noquit }, + { "purge", 0, packagelist::kd_purge, qa_noquit }, + { "hold", 0, packagelist::kd_hold, qa_noquit }, + { "unhold", 0, packagelist::kd_unhold, qa_noquit }, + { "info", 0, packagelist::kd_info, qa_noquit }, + { "verbose", 0, packagelist::kd_verbose, qa_noquit }, + { "help", 0, packagelist::kd_help, qa_noquit }, + { "search", 0, packagelist::kd_search, qa_noquit }, + { "searchagain", 0, packagelist::kd_searchagain, qa_noquit }, + { "swaporder", 0, packagelist::kd_swaporder, qa_noquit }, + { "redraw", 0, packagelist::kd_redraw, qa_noquit }, + { "quitcheck", 0, packagelist::kd_quit_noop, qa_quitchecksave }, + { "quitrejectsug", 0, packagelist::kd_revertdirect, qa_quitnochecksave }, + { "quitnocheck", 0, packagelist::kd_quit_noop, qa_quitnochecksave }, + { "abortnocheck", 0, packagelist::kd_revert_abort, qa_quitnochecksave }, + { "revert", 0, packagelist::kd_revert_abort, qa_noquit }, + { "revertsuggest", 0, packagelist::kd_revertsuggest, qa_noquit }, + { "revertdirect", 0, packagelist::kd_revertdirect, qa_noquit }, + { 0, 0, qa_noquit } +}; + +#define C(x) ((x)-'a'+1) + +const keybindings::orgbinding packagelist_korgbindings[]= { + { 'n', "down" }, + { KEY_DOWN, "down" }, + { 'p', "up" }, + { KEY_UP, "up" }, + + { 'N', "scrollon" }, + { KEY_NPAGE, "scrollon" }, + { ' ', "scrollon" }, + { 'P', "scrollback" }, + { KEY_PPAGE, "scrollback" }, + { KEY_BACKSPACE, "scrollback" }, + { 0177,/*DEL*/ "scrollback" }, + { C('h'), "scrollback" }, + { C('n'), "scrollon1" }, + { C('p'), "scrollback1" }, + + { 't', "top" }, + { KEY_HOME, "top" }, + { 'e', "bottom" }, + { KEY_LL, "bottom" }, + { KEY_END, "bottom" }, + + { 'u', "iscrollback" }, + { 'd', "iscrollon" }, + { C('u'), "iscrollback1" }, + { C('d'), "iscrollon1" }, + + { 'B', "panback" }, + { KEY_LEFT, "panback" }, + { 'F', "panon" }, + { KEY_RIGHT, "panon" }, + { C('b'), "panback1" }, + { C('f'), "panon1" }, + + { '+', "select" }, + { KEY_IC, "select" }, + { '-', "deselect" }, + { KEY_DC, "deselect" }, + { '_', "purge" }, + { 'H', "hold" }, + { 'G', "unhold" }, + + { '?', "help" }, + { KEY_HELP, "help" }, + { KEY_F(1), "help" }, + { 'i', "info" }, + { 'o', "swaporder" }, + { 'v', "verbose" }, + { C('l'), "redraw" }, + { '/', "search" }, + { '\\', "searchagain" }, + + { KEY_ENTER, "quitcheck" }, + { '\r', "quitcheck" }, + { 'Q', "quitnocheck" }, + { 'X', "abortnocheck" }, + { 'R', "revert" }, + { 'U', "revertsuggest" }, + { 'D', "revertdirect" }, + + { -1, 0 } +}; diff --git a/dselect/pkglist.cc b/dselect/pkglist.cc new file mode 100644 index 00000000..f619a75e --- /dev/null +++ b/dselect/pkglist.cc @@ -0,0 +1,378 @@ +/* + * dselect - Debian GNU/Linux package maintenance user interface + * pkglist.cc - package list administration + * + * Copyright (C) 1995 Ian Jackson + * + * This 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, + * or (at your option) any later version. + * + * This 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; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include + +extern "C" { +#include "config.h" +#include "dpkg.h" +#include "dpkg-db.h" +} +#include "dselect.h" +#include "bindings.h" + +int packagelist::compareentries(struct perpackagestate *a, + struct perpackagestate *b) { + const char *asection= a->pkg->section; + if (!asection && a->pkg->name) asection= ""; + const char *bsection= b->pkg->section; + if (!bsection && b->pkg->name) bsection= ""; + int c_section= + !asection || !bsection ? + (!bsection) - (!asection) : + !*asection || !*bsection ? + (!asection) - (!bsection) : + strcasecmp(asection,bsection); + int c_priority= + a->pkg->priority - b->pkg->priority; + if (!c_priority && a->pkg->priority == pkginfo::pri_other) + c_priority= strcasecmp(a->pkg->otherpriority, b->pkg->otherpriority); + int c_name= + a->pkg->name && b->pkg->name ? + strcasecmp(a->pkg->name, b->pkg->name) : + (!b->pkg->name) - (!a->pkg->name); + + switch (sortorder) { + case so_section: + return c_section ? c_section : c_priority ? c_priority : c_name; + case so_priority: + return c_priority ? c_priority : c_section ? c_section : c_name; + case so_alpha: + return c_name; + case so_unsorted: + default: + internerr("unsorted or unknown in compareentries"); + } +} + +void packagelist::discardheadings() { + int a,b; + for (a=0, b=0; apkg->name) { + table[b++]= table[a]; + } + } + nitems= b; + + struct perpackagestate *head, *next; + head= headings; + while (head) { + next= head->uprec; + delete head->pkg; + delete head; + head= next; + } + headings= 0; +} + +void packagelist::addheading(pkginfo::pkgpriority priority, + const char *otherpriority, + const char *section) { + assert(nitems <= nallocated); + if (nitems == nallocated) { + nallocated += nallocated+50; + struct perpackagestate **newtable= new struct perpackagestate*[nallocated]; + memcpy(newtable,table,nallocated*sizeof(struct perpackagestate*)); + table= newtable; + } + + if (debug) fprintf(debug,"packagelist[%p]::addheading(%d,%s,%s)\n", + this,priority, + otherpriority ? otherpriority : "", + section ? section : ""); + + struct pkginfo *newhead= new pkginfo; + newhead->name= 0; + newhead->priority= priority; + newhead->otherpriority= (char*)otherpriority; + newhead->section= (char*)section; + + struct perpackagestate *newstate= new perpackagestate; + newstate->pkg= newhead; + newstate->uprec= headings; + headings= newstate; + + table[nitems++]= newstate; +} + +static packagelist *sortpackagelist; + +int qsort_compareentries(const void *a, const void *b) { + return sortpackagelist->compareentries(*(perpackagestate**)a, + *(perpackagestate**)b); +} + +void packagelist::sortinplace() { + sortpackagelist= this; + + if (debug) fprintf(debug,"packagelist[%p]::sortinplace()\n",this); + qsort(table,nitems,sizeof(struct pkginfoperfile*),qsort_compareentries); +} + +void packagelist::sortmakeheads() { + discardheadings(); + sortinplace(); + assert(nitems); + + if (debug) fprintf(debug,"packagelist[%p]::sortmakeheads() sortorder=%d\n", + this,sortorder); + + int nrealitems= nitems; + addheading(pkginfo::pri_unset,0,0); + + if (sortorder == so_alpha) { sortinplace(); return; } + + assert(sortorder == so_section || sortorder == so_priority); + + // Important: do not save pointers into table in this function, because + // addheading may need to reallocate table to make it larger ! + + struct pkginfo *lastpkg; + struct pkginfo *thispkg; + lastpkg= 0; + int a; + for (a=0; apkg; + assert(thispkg->name); + int prioritydiff= (!lastpkg || + thispkg->priority != lastpkg->priority || + (thispkg->priority == pkginfo::pri_other && + strcasecmp(thispkg->otherpriority,lastpkg->otherpriority))); + int sectiondiff= (!lastpkg || + strcasecmp(thispkg->section ? thispkg->section : "", + lastpkg->section ? lastpkg->section : "")); + + if (debug) fprintf(debug,"packagelist[%p]::sortmakeheads()" + " pkg=%s priority=%d otherpriority=%s %s section=%s %s\n", + this, thispkg->name, thispkg->priority, + thispkg->otherpriority ? thispkg->otherpriority : "", + prioritydiff ? "*diff*" : "same", + thispkg->section ? thispkg->section : "", + sectiondiff ? "*diff*" : "same"); + + if (sortorder == so_section && sectiondiff) + addheading(pkginfo::pri_unset,0, thispkg->section ? thispkg->section : ""); + if (sortorder == so_priority && prioritydiff) + addheading(thispkg->priority,thispkg->otherpriority, 0); + if (prioritydiff || sectiondiff) + addheading(thispkg->priority,thispkg->otherpriority, + thispkg->section ? thispkg->section : ""); + lastpkg= thispkg; + } + + if (listpad) { + int maxx, maxy; + getmaxyx(listpad,maxx,maxy); + if (nitems > maxy) { + delwin(listpad); + listpad= newpad(nitems+1, total_width); + if (!listpad) ohshite("failed to create larger baselist pad"); + } else if (nitems < maxy) { + werase(listpad); + } + } + + sortinplace(); +} + +void packagelist::initialsetup() { + if (debug) + fprintf(debug,"packagelist[%p]::initialsetup()\n",this); + + int allpackages= countpackages(); + datatable= new struct perpackagestate[allpackages]; + + nallocated= allpackages+150; // will realloc if necessary, so 150 not critical + table= new struct perpackagestate*[nallocated]; + + depsdone= 0; + unavdone= 0; + currentinfo= 0; + headings= 0; + verbose= 0; +} + +void packagelist::finalsetup() { + setcursor(0); + + if (debug) + fprintf(debug,"packagelist[%p]::finalsetup done; recursive=%d nitems=%d\n", + this, recursive, nitems); +} + +packagelist::packagelist(keybindings *kb) : baselist(kb) { + // nonrecursive + initialsetup(); + struct pkgiterator *iter; + struct pkginfo *pkg; + + for (iter=iterpkgstart(), nitems=0; + (pkg=iterpkgnext(iter)); + ) { + struct perpackagestate *state= &datatable[nitems]; + state->pkg= pkg; + if (!informativeperfile(&pkg->available) && + pkg->status == pkginfo::stat_notinstalled && + pkg->priority == pkginfo::pri_unknown && + !(pkg->section && *pkg->section) && + !pkg->files && + pkg->want != pkginfo::want_install) { + pkg->clientdata= 0; continue; + } + if (!pkg->available.valid) blankpackageperfile(&pkg->available); + state->direct= state->original= pkg->want; + if (readwrite && pkg->want == pkginfo::want_unknown) { + state->suggested= pkg->priority <= pkginfo::pri_standard + ? pkginfo::want_install : pkginfo::want_purge; /* fixme: configurable */ + state->spriority= sp_inherit; + } else { + state->suggested= pkg->want; + state->spriority= sp_fixed; + } + state->dpriority= dp_must; + state->selected= state->suggested; + state->uprec= 0; + state->relations.init(); + pkg->clientdata= state; + table[nitems]= state; + nitems++; + } + if (!nitems) ohshit("There are no packages to select."); + recursive= 0; + sortorder= so_priority; + sortmakeheads(); + finalsetup(); +} + +packagelist::packagelist(keybindings *kb, pkginfo **pkgltab) : baselist(kb) { + // takes over responsibility for pkgltab (recursive) + initialsetup(); + + recursive= 1; + nitems= 0; + if (pkgltab) { + add(pkgltab); + delete[] pkgltab; + } + + sortorder= so_unsorted; + finalsetup(); +} + +void perpackagestate::free(int recursive) { + if (pkg->name) { + if (readwrite) { + if (uprec) { + assert(recursive); + uprec->selected= selected; + pkg->clientdata= uprec; + } else { + assert(!recursive); + if (pkg->want != selected) { + pkg->want= selected; + } + pkg->clientdata= 0; + } + } + relations.free(); + } +} + +packagelist::~packagelist() { + if (debug) fprintf(debug,"packagelist[%p]::~packagelist()\n",this); + + discardheadings(); + + int index; + for (index=0; indexfree(recursive); + delete[] table; + delete[] datatable; + if (debug) fprintf(debug,"packagelist[%p]::~packagelist() tables freed\n",this); + + doneent *search, *next; + for (search=depsdone; search; search=next) { + next= search->next; + delete search; + } + + if (debug) fprintf(debug,"packagelist[%p]::~packagelist() done\n",this); +} + +pkginfo **packagelist::display() { + // returns list of packages as null-terminated array, which becomes owned + // by the caller, if a recursive check is desired. + // returns 0 if no recursive check is desired. + int response, index; + const keybindings::interpretation *interp; + pkginfo **retl; + + if (debug) fprintf(debug,"packagelist[%p]::display()\n",this); + + setupsigwinch(); + startdisplay(); + displayhelp(helpmenulist(),'i'); + + if (debug) fprintf(debug,"packagelist[%p]::display() entering loop\n",this); + for (;;) { + if (whatinfo_height) wcursyncup(whatinfowin); + if (doupdate() == ERR) ohshite("doupdate failed"); + signallist= this; + if (sigprocmask(SIG_UNBLOCK,&sigwinchset,0)) ohshite("failed to unblock SIGWINCH"); + response= getch(); + if (sigprocmask(SIG_BLOCK,&sigwinchset,0)) ohshite("failed to re-block SIGWINCH"); + if (response == ERR) ohshite("getch failed"); + interp= (*bindings)(response); + if (debug) + fprintf(debug,"packagelist[%p]::display() response=%d interp=%s\n", + this,response, interp ? interp->action : "[none]"); + if (!interp) { beep(); continue; } + (this->*(interp->pfn))(); + if (interp->qa != qa_noquit) break; + } + pop_cleanup(ehflag_normaltidy); // unset the SIGWINCH handler + enddisplay(); + + if (interp->qa == qa_quitnochecksave || !readwrite) { + if (debug) fprintf(debug,"packagelist[%p]::display() done - quitNOcheck\n",this); + return 0; + } + + if (recursive) { + retl= new pkginfo*[nitems+1]; + for (index=0; indexpkg; + retl[nitems]= 0; + if (debug) fprintf(debug,"packagelist[%p]::display() done, retl=%p\n",this,retl); + return retl; + } else { + packagelist *sub= new packagelist(bindings,0); + for (index=0; index < nitems; index++) + if (table[index]->pkg->name) + sub->add(table[index]->pkg); + repeatedlydisplay(sub,dp_must); + if (debug) + fprintf(debug,"packagelist[%p]::display() done, not recursive no retl\n",this); + return 0; + } +} diff --git a/dselect/pkglist.h b/dselect/pkglist.h new file mode 100644 index 00000000..a1d045b6 --- /dev/null +++ b/dselect/pkglist.h @@ -0,0 +1,183 @@ +/* -*- c++ -*- + * dselect - selection of Debian packages + * pkglist.h - external definitions for package list handling + * + * Copyright (C) 1994,1995 Ian Jackson + * + * This 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, + * or (at your option) any later version. + * + * This 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 dpkg; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef PKGLIST_H +#define PKGLIST_H + +enum showpriority { + dp_none, // has not been involved in any unsatisfied things + dp_may, // has been involved in an unsatisfied Optional + dp_must // has been involved in an unsatisfied Recommended/Depends/Conflicts +}; + +enum selpriority { + // where did the currently suggested value come from, and how important + // is it to display this package ? + // low + sp_inherit, // inherited from our parent list + sp_selecting, // propagating a selection + sp_deselecting, // propagating a deselection + sp_fixed // it came from the `status' file and we're not a recursive list + // high +}; + +struct perpackagestate { + struct pkginfo *pkg; + /* The `heading' entries in the list, for `all packages of type foo', + * point to a made-up pkginfo, which has pkg->name==0. + * pkg->priority and pkg->section are set to the values if appropriate, or to + * pri_unset resp. null if the heading refers to all priorities resp. sections. + * uprec is used when constructing the list initially and when tearing it + * down and should not otherwise be used; other fields are undefined. + */ + pkginfo::pkgwant original; // set by caller + pkginfo::pkgwant direct; // set by caller + pkginfo::pkgwant suggested; // set by caller, modified by resolvesuggest + pkginfo::pkgwant selected; // not set by caller, will be set by packagelist + selpriority spriority; // monotonically increases (used by sublists) + showpriority dpriority; // monotonically increases (used by sublists) + struct perpackagestate *uprec; // 0 if this is not part of a recursive list + varbuf relations; + + void free(int recursive); +}; + +class packagelist : public baselist { + int status_width, gap_width, section_width, priority_width; + int package_width, description_width; + int section_column, priority_column, package_column, description_column; + + // Only used when `verbose' is set + int status_hold_width, status_status_width, status_want_width; + + // Table of packages + struct perpackagestate *datatable; + struct perpackagestate **table; + + // Misc. + int recursive, nallocated, verbose; + enum { so_unsorted, so_section, so_priority, so_alpha } sortorder; + struct perpackagestate *headings; + + // Information displays + struct infotype { + int (packagelist::*relevant)(); // null means always relevant + void (packagelist::*display)(); // null means end of table + }; + const infotype *currentinfo; + static const infotype infoinfos[]; + static const infotype *const baseinfo; + int itr_recursive(); + int itr_nonrecursive(); + void severalinfoblurb(const char *whatinfoline); + void itd_mainwelcome(); + void itd_explaindisplay(); + void itd_recurwelcome(); + void itd_relations(); + void itd_description(); + void itd_controlfile(); + + // Dependency and sublist processing + struct doneent { doneent *next; void *dep; } *depsdone, *unavdone; + int alreadydone(doneent**, void*); + int resolvedepcon(dependency*); + int deselect_one_of(pkginfo *er, pkginfo *ed, dependency *display); + + // Define these virtuals + void redraw1itemsel(int index, int selected); + void redrawcolheads(); + void redrawthisstate(); + void redrawinfo(); + void redrawtitle(); + void setwidths(); + const char *itemname(int index); + const struct helpmenuentry *helpmenulist(); + + // Miscellaneous internal routines + + void redraw1package(int index, int selected); + int compareentries(struct perpackagestate *a, struct perpackagestate *b); + friend int qsort_compareentries(const void *a, const void *b); + + void sortmakeheads(); + void movecursorafter(int ncursor); + void initialsetup(); + void finalsetup(); + + // To do with building the list, with heading lines in it + void discardheadings(); + void addheading(pkginfo::pkgpriority,const char*, const char *section); + void sortinplace(); + void affectedrange(int *startp, int *endp); + void setwant(pkginfo::pkgwant nw); + void sethold(int hold); + + public: + + // Keybinding functions */ + void kd_quit_noop(); + void kd_revert_abort(); + void kd_revertsuggest(); + void kd_revertdirect(); + void kd_morespecific(); + void kd_lessspecific(); + void kd_swaporder(); + void kd_select(); + void kd_deselect(); + void kd_purge(); + void kd_hold(); + void kd_unhold(); + void kd_info(); + void kd_verbose(); + + packagelist(keybindings *kb); // nonrecursive + packagelist(keybindings *kb, pkginfo **pkgltab); // recursive + void add(pkginfo **arry) { while (*arry) add(*arry++); } + void add(pkginfo*); + void add(pkginfo*, pkginfo::pkgwant); + void add(pkginfo*, const char *extrainfo, showpriority displayimportance); + int add(dependency*, showpriority displayimportance); + void addunavailable(deppossi*); + + int resolvesuggest(); + int deletelessimp_anyleft(showpriority than); + pkginfo **display(); + ~packagelist(); +}; + +void repeatedlydisplay(packagelist *sub, showpriority, packagelist *unredisplay =0); + +extern const char *const wantstrings[]; +extern const char *const holdstrings[]; +extern const char *const statusstrings[]; +extern const char *const prioritystrings[]; +extern const char *const priorityabbrevs[]; +extern const char *const relatestrings[]; +extern const char statuschars[]; +extern const char holdchars[]; +extern const char wantchars[]; + +const struct pkginfoperfile *i2info(struct pkginfo *pkg); +int deppossatisfied(deppossi *possi); + +extern modstatdb_rw readwrite; + +#endif /* PKGLIST_H */ diff --git a/dselect/pkgsublist.cc b/dselect/pkgsublist.cc new file mode 100644 index 00000000..ccaf2e93 --- /dev/null +++ b/dselect/pkgsublist.cc @@ -0,0 +1,206 @@ +/* + * dselect - Debian GNU/Linux package maintenance user interface + * pkgsublist.cc - status modification and recursive package list handling + * + * Copyright (C) 1995 Ian Jackson + * + * This 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, + * or (at your option) any later version. + * + * This 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; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include + +extern "C" { +#include "config.h" +#include "dpkg.h" +#include "dpkg-db.h" +} +#include "dselect.h" +#include "bindings.h" + +void packagelist::add(pkginfo *pkg) { + if (debug) fprintf(debug,"packagelist[%p]::add(pkginfo %s)\n",this,pkg->name); + if (!recursive || // never add things to top level + !pkg->clientdata || // don't add pure virtual packages + pkg->clientdata->uprec) // don't add ones already in the recursive list + return; + if (debug) fprintf(debug,"packagelist[%p]::add(pkginfo %s) adding\n",this,pkg->name); + perpackagestate *state= &datatable[nitems]; + state->pkg= pkg; + state->direct= state->original= pkg->clientdata->selected; + state->suggested= state->selected= pkg->clientdata->selected; + state->spriority= sp_inherit; state->dpriority= dp_none; + state->uprec= pkg->clientdata; + state->relations.init(); + pkg->clientdata= state; + table[nitems]= state; + nitems++; +} + +void packagelist::add(pkginfo *pkg, pkginfo::pkgwant nw) { + if (debug) fprintf(debug,"packagelist[%p]::add(pkginfo %s, %s)\n", + this,pkg->name,wantstrings[nw]); + add(pkg); if (!pkg->clientdata) return; + pkg->clientdata->direct= nw; + selpriority np; + np= (nw == pkginfo::want_install) ? sp_selecting : sp_deselecting; + if (pkg->clientdata->spriority > np) return; + if (debug) fprintf(debug,"packagelist[%p]::add(pkginfo %s, %s) setting\n", + this,pkg->name,wantstrings[nw]); + pkg->clientdata->suggested= pkg->clientdata->selected= nw; + pkg->clientdata->spriority= np; + +} + +void packagelist::add(pkginfo *pkg, const char *extrainfo, showpriority showimp) { + if (debug) + fprintf(debug,"packagelist[%p]::add(pkginfo %s, \"...\", showpriority %d)\n", + this,pkg->name,showimp); + add(pkg); if (!pkg->clientdata) return; + if (pkg->clientdata->dpriority < showimp) pkg->clientdata->dpriority= showimp; + pkg->clientdata->relations(extrainfo); + pkg->clientdata->relations.terminate(); +} + +int packagelist::alreadydone(doneent **done, void *check) { + doneent *search; + + for (search= *done; search && search->dep != check; search=search->next); + if (search) return 1; + if (debug) fprintf(debug,"packagelist[%p]::alreadydone(%p,%p) new\n", + this,done,check); + search= new doneent; + search->next= *done; + search->dep= check; + *done= search; + return 0; +} + +void packagelist::addunavailable(deppossi *possi) { + if (debug) fprintf(debug,"packagelist[%p]::addunavail(%p)\n",this,possi); + + if (!recursive) return; + if (alreadydone(&unavdone,possi)) return; + + assert(possi->up->up->clientdata); + assert(possi->up->up->clientdata->uprec); + + varbuf& vb= possi->up->up->clientdata->relations; + vb(possi->ed->name); + vb(" does not appear to be available\n"); +} + +int packagelist::add(dependency *depends, showpriority displayimportance) { + if (debug) fprintf(debug,"packagelist[%p]::add(dependency[%p])\n",this,depends); + + if (alreadydone(&depsdone,depends)) return 0; + + const char *comma= ""; + varbuf info; + info(depends->up->name); + info(' '); + info(relatestrings[depends->type]); + info(' '); + deppossi *possi; + for (possi=depends->list; + possi; + possi=possi->next, comma=(possi && possi->next ? ", " : " or ")) { + info(comma); + info(possi->ed->name); + if (possi->version && *possi->version) { + switch (possi->verrel) { + case deppossi::dvr_earlierequal: info(" (<= "); break; + case deppossi::dvr_laterequal: info(" (>= "); break; + case deppossi::dvr_earlierstrict: info(" (<< "); break; + case deppossi::dvr_laterstrict: info(" (>> "); break; + case deppossi::dvr_exact: info(" (= "); break; + default: internerr("unknown verrel"); + } + info(possi->version); + if (possi->revision && *possi->revision) { + info('-'); + info(possi->revision); + } + info(")"); + } + } + info('\n'); + add(depends->up,info.string(),displayimportance); + for (possi=depends->list; possi; possi=possi->next) { + add(possi->ed,info.string(),displayimportance); + if (depends->type != dep_provides && (!possi->version || !*possi->version)) { + // providers aren't relevant if a version was specified, or + // if we're looking at a provider relationship already + deppossi *provider; + for (provider= possi->ed->available.valid ? possi->ed->available.depended : 0; + provider; + provider=provider->nextrev) { + if (provider->up->type != dep_provides) continue; + add(provider->up->up,info.string(),displayimportance); + add(provider->up,displayimportance); + } + } + } + return 1; +} + +void repeatedlydisplay(packagelist *sub, + showpriority initial, + packagelist *unredisplay) { + pkginfo **newl; + keybindings *kb; + + if (debug) fprintf(debug,"repeatedlydisplay(packagelist[%p])\n",sub); + if (sub->resolvesuggest() != 0 && sub->deletelessimp_anyleft(initial)) { + if (debug) fprintf(debug,"repeatedlydisplay(packagelist[%p]) once\n",sub); + if (unredisplay) unredisplay->enddisplay(); + for (;;) { + newl= sub->display(); + if (!newl) break; + if (debug) fprintf(debug,"repeatedlydisplay(packagelist[%p]) newl\n",sub); + kb= sub->bindings; delete sub; + sub= new packagelist(kb,newl); + if (sub->resolvesuggest() <= 1) break; + if (!sub->deletelessimp_anyleft(dp_must)) break; + if (debug) fprintf(debug,"repeatedlydisplay(packagelist[%p]) again\n",sub); + } + if (unredisplay) unredisplay->startdisplay(); + } + delete sub; + if (debug) fprintf(debug,"repeatedlydisplay(packagelist[%p]) done\n",sub); +} + +int packagelist::deletelessimp_anyleft(showpriority than) { + if (debug) + fprintf(debug,"packagelist[%p]::dli_al(%d): nitems=%d\n",this,than,nitems); + int insat, runthr; + for (runthr=0, insat=0; + runthr < nitems; + runthr++) { + if (table[runthr]->dpriority < than) { + table[runthr]->free(recursive); + } else { + if (insat != runthr) table[insat]= table[runthr]; + insat++; + } + } + nitems= insat; + if (debug) fprintf(debug,"packagelist[%p]::dli_al(%d) done; nitems=%d\n", + this,than,nitems); + return nitems; +} diff --git a/dselect/pkgtop.cc b/dselect/pkgtop.cc new file mode 100644 index 00000000..845b5dd7 --- /dev/null +++ b/dselect/pkgtop.cc @@ -0,0 +1,249 @@ +/* + * dselect - Debian GNU/Linux package maintenance user interface + * pkgtop.cc - handles (re)draw of package list windows colheads, list, thisstate + * + * Copyright (C) 1994,1995 Ian Jackson + * + * This 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, + * or (at your option) any later version. + * + * This 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; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include + +extern "C" { +#include "config.h" +#include "dpkg.h" +#include "dpkg-db.h" +} +#include "dselect.h" +#include "pkglist.h" + +const char *pkgprioritystring(const struct pkginfo *pkg) { + if (pkg->priority == pkginfo::pri_unset) { + return 0; + } else if (pkg->priority == pkginfo::pri_other) { + return pkg->otherpriority; + } else { + assert(pkg->priority <= pkginfo::pri_unknown); + return prioritystrings[pkg->priority]; + } +} + +static int describemany(char buf[], const char *prioritystring, const char *section) { + if (!prioritystring) { + if (!section) { + strcpy(buf, "All packages"); + return 0; + } else { + if (!*section) { + strcpy(buf, "All packages without a section"); + } else { + sprintf(buf, "All packages in section %s", section); + } + return 1; + } + } else { + if (!section) { + sprintf(buf, "All %s packages", prioritystring); + return 1; + } else { + if (!*section) { + sprintf(buf, "All %s packages without a section", prioritystring); + } else { + sprintf(buf, "All %s packages in section %s", prioritystring, section); + } + return 2; + } + } +} + +void packagelist::redrawthisstate() { + if (!thisstate_height) return; + mywerase(thisstatepad); + + const char *section= table[cursorline]->pkg->section; + const char *priority= pkgprioritystring(table[cursorline]->pkg); + char *buf= new char[220+ + greaterint((table[cursorline]->pkg->name + ? strlen(table[cursorline]->pkg->name) : 0), + (section ? strlen(section) : 0) + + (priority ? strlen(priority) : 0))]; + + if (table[cursorline]->pkg->name) { + sprintf(buf, + "%-*s %s%s%s; %s (was: %s). %s", + package_width, + table[cursorline]->pkg->name, + statusstrings[table[cursorline]->pkg->status], + holdstrings[table[cursorline]->pkg->eflag][0] ? " - " : "", + holdstrings[table[cursorline]->pkg->eflag], + wantstrings[table[cursorline]->selected], + wantstrings[table[cursorline]->original], + priority); + } else { + describemany(buf,priority,section); + } + mvwaddnstr(thisstatepad,0,0, buf, total_width); + pnoutrefresh(thisstatepad, 0,leftofscreen, thisstate_row,0, + thisstate_row, lesserint(total_width - 1, xmax - 1)); + + delete[] buf; +} + +void packagelist::redraw1itemsel(int index, int selected) { + int i, indent, j; + const char *p; + const struct pkginfo *pkg= table[index]->pkg; + const struct pkginfoperfile *info= &pkg->available; + + wattrset(listpad, selected ? listsel_attr : list_attr); + + if (pkg->name) { + + if (verbose) { + + mvwprintw(listpad,index,0, "%-*.*s ", + status_hold_width, status_hold_width, + holdstrings[pkg->eflag]); + wprintw(listpad, "%-*.*s ", + status_status_width, status_status_width, + statusstrings[pkg->status]); + wprintw(listpad, "%-*.*s ", + status_want_width, status_want_width, + /* fixme: keep this ? */ + /*table[index]->original == table[index]->selected ? "(same)" + : */wantstrings[table[index]->original]); + wattrset(listpad, selected ? selstatesel_attr : selstate_attr); + wprintw(listpad, "%-*.*s", + status_want_width, status_want_width, + wantstrings[table[index]->selected]); + wattrset(listpad, selected ? listsel_attr : list_attr); + waddch(listpad, ' '); + + mvwprintw(listpad,index,priority_column-1, " %-*.*s", + priority_width, priority_width, + pkg->priority == pkginfo::pri_other ? pkg->otherpriority : + prioritystrings[pkg->priority]); + + } else { + + mvwaddch(listpad,index,0, holdchars[pkg->eflag]); + waddch(listpad, statuschars[pkg->status]); + waddch(listpad, + /* fixme: keep this feature? */ + /*table[index]->original == table[index]->selected ? ' ' + : */wantchars[table[index]->original]); + + wattrset(listpad, selected ? selstatesel_attr : selstate_attr); + waddch(listpad, wantchars[table[index]->selected]); + wattrset(listpad, selected ? listsel_attr : list_attr); + + wmove(listpad,index,priority_column-1); waddch(listpad,' '); + if (pkg->priority == pkginfo::pri_other) { + int i; + char *p; + for (i=priority_width, p=pkg->otherpriority; + i > 0 && *p; + i--, p++) + waddch(listpad, tolower(*p)); + while (i-- > 0) waddch(listpad,' '); + } else { + wprintw(listpad, "%-*.*s", priority_width, priority_width, + priorityabbrevs[pkg->priority]); + } + + } + + mvwprintw(listpad,index,section_column-1, " %-*.*s", + section_width, section_width, + pkg->section ? pkg->section : "?"); + + mvwprintw(listpad,index,package_column-1, " %-*.*s ", + package_width, package_width, pkg->name); + + i= description_width; + p= info->description ? info->description : ""; + while (i>0 && *p && *p != '\n') { waddch(listpad,*p); i--; p++; } + + } else { + + const char *section= pkg->section; + const char *priority= pkgprioritystring(pkg); + + char *buf= new char[220+ + (section ? strlen(section) : 0) + + (priority ? strlen(priority) : 0)]; + + indent= describemany(buf,priority,section); + + mvwaddstr(listpad,index,0, " "); + i= total_width-6; + j= (indent<<1) + 1; + while (j-- >0) { waddch(listpad,'-'); i--; } + waddch(listpad,' '); + + wattrset(listpad, selected ? selstatesel_attr : selstate_attr); + p= buf; + while (i>0 && *p) { waddch(listpad, *p); p++; i--; } + wattrset(listpad, selected ? listsel_attr : list_attr); + + waddch(listpad,' '); + j= (indent<<1) + 1; + while (j-- >0) { waddch(listpad,'-'); i--; } + + delete[] buf; + + } + + while (i>0) { waddch(listpad,' '); i--; } +} + +void packagelist::redrawcolheads() { + if (colheads_height) { + wattrset(colheadspad,colheads_attr); + mywerase(colheadspad); + if (verbose) { + wmove(colheadspad,0,0); + for (int i=0; i diff --git a/include/Makefile.in b/include/Makefile.in new file mode 100644 index 00000000..b2c0ddc8 --- /dev/null +++ b/include/Makefile.in @@ -0,0 +1,26 @@ +# Copyright (C) 1994 Ian Murdock +# Copyright (C) 1994,1995 Ian Jackson +# +# This 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, +# or (at your option) any later version. +# +# This 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 dpkg; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +all: + +clean: + rm -f core + +distclean: clean + rm -f Makefile *.orig *~ *.~* ./#*# + +install: all diff --git a/include/dpkg-db.h b/include/dpkg-db.h new file mode 100644 index 00000000..59188588 --- /dev/null +++ b/include/dpkg-db.h @@ -0,0 +1,272 @@ +/* + * libdpkg - Debian packaging suite library routines + * dpkg-db.h - declarations for in-core package database management + * + * Copyright (C) 1994,1995 Ian Jackson + * + * This 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, + * or (at your option) any later version. + * + * This 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 dpkg; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef DPKG_DB_H +#define DPKG_DB_H + +#include +#include + +enum deptype { + dep_suggests, + dep_recommends, + dep_depends, + dep_predepends, + dep_conflicts, + dep_provides, + dep_replaces +}; + +struct dependency { /* dy */ + struct pkginfo *up; + struct dependency *next; + struct deppossi *list; + enum deptype type; +}; + +struct deppossi { /* do */ + struct dependency *up; + struct pkginfo *ed; + struct deppossi *next, *nextrev, *backrev; + enum depverrel { + dvrf_earlier= 0001, + dvrf_later= 0002, + dvrf_strict= 0010, + dvrf_orequal= 0020, + dvrf_builtup= 0100, + dvr_none= 0200, + dvr_earlierequal= dvrf_builtup | dvrf_earlier | dvrf_orequal, + dvr_earlierstrict= dvrf_builtup | dvrf_earlier | dvrf_strict, + dvr_laterequal= dvrf_builtup | dvrf_later | dvrf_orequal, + dvr_laterstrict= dvrf_builtup | dvrf_later | dvrf_strict, + dvr_exact= 0400 + } verrel; + char *version, *revision; + int cyclebreak; +}; + +struct arbitraryfield { + struct arbitraryfield *next; + char *name; + char *value; +}; + +struct conffile { + struct conffile *next; + char *name; + char *hash; +}; + +struct filedetails { + struct filedetails *next; + char *name, *msdosname, *size, *md5sum; +}; + +struct pkginfoperfile { /* pif */ + int valid; + struct dependency *depends; + struct deppossi *depended; + int essential; /* The `essential' flag, 1=yes, 0=no (absent) */ + char *description, *maintainer, *version, *revision, *source, *architecture; + struct conffile *conffiles; + struct arbitraryfield *arbs; +}; + +struct perpackagestate; /* used by dselect only, but we keep a pointer here */ + +struct pkginfo { /* pig */ + struct pkginfo *next; + char *name; + enum pkgwant { + want_unknown, want_install, want_deinstall, want_purge + } want; + enum pkgeflag { + eflagv_ok=00, eflagv_hold=01, eflagv_reinstreq=02, eflagv_both=03, + eflagf_hold=01, eflagf_reinstreq=02 + } eflag; /* bitmask */ + enum pkgstatus { + stat_notinstalled, stat_unpacked, stat_halfconfigured, + stat_installed, stat_halfinstalled, stat_configfiles + } status; + enum pkgpriority { + pri_required, pri_important, pri_standard, pri_recommended, + pri_optional, pri_extra, pri_contrib, + pri_other, pri_unknown, pri_unset=-1 + } priority; + char *otherpriority; + char *section; + char *configversion, *configrevision; + struct filedetails *files; + struct pkginfoperfile installed; + struct pkginfoperfile available; + struct perpackagestate *clientdata; +}; + +/*** from lock.c ***/ + +void lockdatabase(const char *admindir); +void unlockdatabase(const char *admindir); + +/*** from dbmodify.c ***/ + +enum modstatdb_rw { + /* Those marked with \*s*\ are possible returns from modstatdb_init. */ + msdbrw_readonly/*s*/, msdbrw_needsuperuserlockonly/*s*/, + msdbrw_writeifposs, + msdbrw_write/*s*/, msdbrw_needsuperuser, + /* Now some optional flags: */ + msdbrw_flagsmask= ~077, + /* Prefer later versions from `status' in `available' info, but do not + * save `available' info: */ + msdbrw_availablepreferversion= 0100 +}; + +enum modstatdb_rw modstatdb_init(const char *admindir, enum modstatdb_rw reqrwflags); +void modstatdb_note(struct pkginfo *pkg); +void modstatdb_shutdown(void); + +extern char *statusfile, *availablefile; /* initialised by modstatdb_init */ + +/*** from database.c ***/ + +struct pkginfo *findpackage(const char *name); +void blankpackage(struct pkginfo *pp); +void blankpackageperfile(struct pkginfoperfile *pifp); +int informative(struct pkginfo *info); +int informativeperfile(struct pkginfoperfile *info); +int countpackages(void); +void resetpackages(void); + +struct pkgiterator *iterpkgstart(void); +struct pkginfo *iterpkgnext(struct pkgiterator*); +void iterpkgend(struct pkgiterator*); + +void hashreport(FILE*); + +/*** from parse.c ***/ + +enum parsedbflags { + pdb_recordavailable =001, /* Store in `available' in-core structures, not `status' */ + pdb_rejectstatus =002, /* Throw up an error if `Status' encountered */ + pdb_weakclassification=004, /* Ignore priority/section info if we already have any */ + pdb_preferversion= 010 /* Discard information about earlier versions */ +}; + +const char *illegal_packagename(const char *p, const char **ep); +int parsedb(const char *filename, enum parsedbflags, struct pkginfo **donep, + FILE *warnto, int *warncount); +void copy_dependency_links(struct pkginfo *pkg, + struct dependency **updateme, + struct dependency *newdepends, + int available); + +/*** from parsehelp.c ***/ + +struct namevalue { + const char *name; + int value; +}; + +extern const struct namevalue booleaninfos[]; +extern const struct namevalue priorityinfos[]; +extern const struct namevalue statusinfos[]; +extern const struct namevalue eflaginfos[]; +extern const struct namevalue wantinfos[]; + +const char *skip_slash_dotslash(const char *p); + +/*** from varbuf.c ***/ + +struct varbuf; + +extern void varbufaddc(struct varbuf *v, int c); +void varbufinit(struct varbuf *v); +void varbufreset(struct varbuf *v); +void varbufextend(struct varbuf *v); +void varbuffree(struct varbuf *v); +void varbufaddstr(struct varbuf *v, const char *s); + +/* varbufinit must be called exactly once before the use of each varbuf + * (including before any call to varbuffree). + * + * However, varbufs allocated `static' are properly initialised anyway and + * do not need varbufinit; multiple consecutive calls to varbufinit before + * any use are allowed. + * + * varbuffree must be called after a varbuf is finished with, if anything + * other than varbufinit has been done. After this you are allowed but + * not required to call varbufinit again if you want to start using the + * varbuf again. + * + * Callers using C++ need not worry about any of this. + */ +struct varbuf { + int used, size; + char *buf; + +#ifdef __cplusplus + void init() { varbufinit(this); } + void free() { varbuffree(this); } + varbuf() { varbufinit(this); } + ~varbuf() { varbuffree(this); } + inline void operator()(int c); // definition below + void operator()(const char *s) { varbufaddstr(this,s); } + inline void terminate(void/*to shut 2.6.3 up*/); // definition below + void reset() { used=0; } + const char *string() { terminate(); return buf; } +#endif +}; + +#if HAVE_INLINE +inline extern void varbufaddc(struct varbuf *v, int c) { + if (v->used >= v->size) varbufextend(v); + v->buf[v->used++]= c; +} +#endif + +#ifdef __cplusplus +inline void varbuf::operator()(int c) { varbufaddc(this,c); } +inline void varbuf::terminate(void/*to shut 2.6.3 up*/) { varbufaddc(this,0); used--; } +#endif + +/*** from dump.c ***/ + +void writerecord(FILE*, const char*, + const struct pkginfo*, const struct pkginfoperfile*); + +void writedb(const char *filename, int available, int mustsync); + +void varbufrecord(struct varbuf*, const struct pkginfo*, const struct pkginfoperfile*); +void varbufdependency(struct varbuf *vb, struct dependency *dep); + /* NB THE VARBUF MUST HAVE BEEN INITIALISED AND WILL NOT BE NULL-TERMINATED */ + +/*** from vercmp.c ***/ + +int versioncompare(const char *version, const char *revision, + const char *refversion, const char *refrevision); + +/*** from nfmalloc.c ***/ + +void *nfmalloc(size_t); +char *nfstrsave(const char*); +void nffreeall(void); + +#endif /* DPKG_DB_H */ diff --git a/include/dpkg.h b/include/dpkg.h new file mode 100644 index 00000000..937c535f --- /dev/null +++ b/include/dpkg.h @@ -0,0 +1,179 @@ + +/* + * libdpkg - Debian packaging suite library routines + * dpkg.h - general header for Debian package handling + * + * Copyright (C) 1994,1995 Ian Jackson + * + * This 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, + * or (at your option) any later version. + * + * This 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 dpkg; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef DPKG_H +#define DPKG_H + +#include +#include +#include +#include + +#define ARCHIVEVERSION "2.0" +#define SPLITVERSION "2.1" +#define OLDARCHIVEVERSION "0.939000" +#define SPLITPARTDEFMAX (450*1024) +#define MAXFIELDNAME 200 +#define MAXCONFFILENAME 1000 +#define MAXDIVERTFILENAME 1024 +#define MAXCONTROLFILENAME 100 +#define BUILDCONTROLDIR "DEBIAN" +#define EXTRACTCONTROLDIR BUILDCONTROLDIR +#define DEBEXT ".deb" +#define OLDDBEXT "-old" +#define NEWDBEXT "-new" +#define OLDOLDDEBDIR ".DEBIAN" +#define OLDDEBDIR "DEBIAN" +#define REMOVECONFFEXTS "~", ".bak", "%", \ + DPKGTEMPEXT, DPKGNEWEXT, DPKGOLDEXT, DPKGDISTEXT + +#define DPKG_VERSION_ARCH DPKG_VERSION " (" ARCHITECTURE ")" + +#define NEWCONFFILEFLAG "newconffile" +#define NONEXISTENTFLAG "nonexistent" + +#define DPKGTEMPEXT ".dpkg-tmp" +#define DPKGNEWEXT ".dpkg-new" +#define DPKGOLDEXT ".dpkg-old" +#define DPKGDISTEXT ".dpkg-dist" + +#define CONTROLFILE "control" +#define CONFFILESFILE "conffiles" +#define PREINSTFILE "preinst" +#define POSTINSTFILE "postinst" +#define PRERMFILE "prerm" +#define POSTRMFILE "postrm" +#define LISTFILE "list" + +#define ADMINDIR "/var/lib/dpkg" +#define STATUSFILE "status" +#define AVAILFILE "available" +#define LOCKFILE "lock" +#define CMETHOPTFILE "cmethopt" +#define METHLOCKFILE "methlock" +#define DIVERSIONSFILE "diversions" +#define UPDATESDIR "updates/" +#define INFODIR "info/" +#define PARTSDIR "parts/" +#define CONTROLDIRTMP "tmp.ci/" +#define IMPORTANTTMP "tmp.i" +#define REASSEMBLETMP "reassemble" DEBEXT +#define IMPORTANTMAXLEN 10 +#define IMPORTANTFMT "%04d" /* change => also change lib/database.c:cleanup_updates */ +#define MAXUPDATES 50 + +#define LIBDIR "/usr/lib/dpkg" +#define LOCALLIBDIR "/usr/local/lib/dpkg" +#define METHODSDIR "methods" + +#define NOJOBCTRLSTOPENV "DPKG_NO_TSTP" +#define SHELLENV "SHELL" +#define DEFAULTSHELL "sh" + +#define IMETHODMAXLEN 50 +#define IOPTIONMAXLEN IMETHODMAXLEN +#define METHODOPTIONSFILE "names" +#define METHODSETUPSCRIPT "setup" +#define METHODUPDATESCRIPT "update" +#define METHODINSTALLSCRIPT "install" +#define OPTIONSDESCPFX "desc." +#define OPTIONINDEXMAXLEN 5 + +#define PKGSCRIPTMAXARGS 10 +#define MD5HASHLEN 32 + +#define CONFFOPTCELLS /* int conffoptcells[2] {* 1= user edited *} \ + [2] {* 1= distributor edited *} = */ \ + /* dist not */ /* dist edited */ \ + /* user did not edit */ { cfo_keep, cfo_install }, \ + /* user did edit */ { cfo_keep, cfo_prompt_keep } + +#define ARCHIVE_FILENAME_PATTERN "*.deb" + +#define BACKEND "dpkg-deb" +#define SPLITTER "dpkg-split" +#define MD5SUM "md5sum" +#define DSELECT "dselect" +#define DPKG "dpkg" + +#define TAR "tar" +#define GZIP "gzip" +#define CAT "cat" +#define RM "rm" +#define FIND "find" +#define SHELL "sh" + +#define SHELLENVIR "SHELL" + +#define FIND_EXPRSTARTCHARS "-(),!" + +#define TARBLKSZ 512 + +extern const char thisname[]; /* defined separately in each program */ +extern const char printforhelp[]; + +/*** from ehandle.c ***/ + +void push_error_handler(jmp_buf *jbufp, + void (*printerror)(const char *, const char *), + const char *contextstring); +void set_error_display(void (*printerror)(const char *, const char *), + const char *contextstring); +void print_error_fatal(const char *emsg, const char *contextstring); +void error_unwind(int flagset); +void push_cleanup(void (*f1)(int argc, void **argv), int flagmask1, + void (*f2)(int argc, void **argv), int flagmask2, + int nargs, ...); +void push_checkpoint(int mask, int value); +void pop_cleanup(int flagset); +enum { ehflag_normaltidy=01, ehflag_bombout=02, ehflag_recursiveerror=04 }; + +void do_internerr(const char *string, int line, const char *file) NONRETURNING; +#define internerr(s) do_internerr(s,__LINE__,__FILE__) + +struct varbuf; +void ohshit(const char *fmt, ...) NONRETURNPRINTFFORMAT(1,2); +void ohshitv(const char *fmt, va_list al) NONRETURNING; +void ohshite(const char *fmt, ...) NONRETURNPRINTFFORMAT(1,2); +void ohshitvb(struct varbuf*) NONRETURNING; +void badusage(const char *fmt, ...) NONRETURNPRINTFFORMAT(1,2); +void werr(const char *what) NONRETURNING; + +/*** from mlib.c ***/ + +void *m_malloc(size_t); +void *m_realloc(void*, size_t); +int m_fork(void); +void m_dup2(int oldfd, int newfd); +void m_pipe(int fds[2]); + +void checksubprocerr(int status, const char *description, int sigpipeok); +void waitsubproc(pid_t pid, const char *description, int sigpipeok); + +extern volatile int onerr_abort; + +/*** from showcright.c ***/ + +struct cmdinfo; +void showcopyright(const struct cmdinfo*, const char*); + +#endif /* DPKG_H */ diff --git a/include/myopt.h b/include/myopt.h new file mode 100644 index 00000000..f7984d29 --- /dev/null +++ b/include/myopt.h @@ -0,0 +1,38 @@ +/* + * libdpkg - Debian packaging suite library routines + * myopt.h - declarations for my very own option parsing + * + * Copyright (C) 1994,1995 Ian Jackson + * + * This 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, + * or (at your option) any later version. + * + * This 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 file; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef MYOPT_H +#define MYOPT_H + +struct cmdinfo { + const char *olong; + char oshort; + int takesvalue; /* 0 = normal 1 = standard value 2 = option string cont */ + int *iassignto; + const char **sassignto; + void (*call)(const struct cmdinfo*, const char *value); + int arg; + void *parg; +}; + +void myopt(const char *const **argvp, const struct cmdinfo *cmdinfos); + +#endif /* MYOPT_H */ diff --git a/include/tarfn.h b/include/tarfn.h new file mode 100644 index 00000000..5e387b07 --- /dev/null +++ b/include/tarfn.h @@ -0,0 +1,57 @@ +#ifndef _TAR_FUNCTION_H_ +#define _TAR_FUNCTION_H_ + +/* + * Functions for extracting tar archives. + * Bruce Perens, April-May 1995 + * Copyright (C) 1995 Bruce Perens + * This is free software under the GNU General Public License. + */ + +#include +#include +#include + +enum TarFileType { + NormalFile0 = '\0', /* For compatibility with decades-old bug */ + NormalFile1 = '0', + HardLink = '1', + SymbolicLink = '2', + CharacterDevice = '3', + BlockDevice = '4', + Directory = '5', + FIFO = '6' +}; +typedef enum TarFileType TarFileType; + +struct TarInfo { + void * UserData; /* User passed this in as argument */ + char * Name; /* File name */ + mode_t Mode; /* Unix mode, including device bits. */ + size_t Size; /* Size of file */ + time_t ModTime; /* Last-modified time */ + TarFileType Type; /* Regular, Directory, Special, Link */ + char * LinkName; /* Name for symbolic and hard links */ + dev_t Device; /* Special device for mknod() */ + uid_t UserID; /* Numeric UID */ + gid_t GroupID; /* Numeric GID */ +}; +typedef struct TarInfo TarInfo; + +typedef int (*TarReadFunction)(void * userData, char * buffer, int length); + +typedef int (*TarFunction)(TarInfo * h); + +struct TarFunctions { + TarReadFunction Read; + TarFunction ExtractFile; + TarFunction MakeDirectory; + TarFunction MakeHardLink; + TarFunction MakeSymbolicLink; + TarFunction MakeSpecialFile; +}; +typedef struct TarFunctions TarFunctions; + +extern int TarExtractor(void * userData, const TarFunctions * functions); + +#endif diff --git a/insert-version.pl b/insert-version.pl new file mode 100755 index 00000000..4b1451d9 --- /dev/null +++ b/insert-version.pl @@ -0,0 +1,14 @@ +#!/usr/bin/perl -- +chop($v=`pwd`); +$v =~ s,^.*/dpkg-,, || die; +$v =~ s,/\w+$,,; +$v =~ m,^[-0-9.]+$, || die; +while () { + $_= $1.$v.$2."\n" if + m|^(#define DPKG_VERSION ")[-0-9.]+(" /\* This line modified by Makefile \*/)$|; + $_= $1.$v.$2."\n" if + m/^(\$version= ')[-0-9.]+('; # This line modified by Makefile)$/; + $_= $1.$v.$2."\n" if + m/^(version=")[-0-9.]+("; # This line modified by Makefile)$/; + print || die; +} diff --git a/install.sh b/install.sh new file mode 100755 index 00000000..0ff4b6a0 --- /dev/null +++ b/install.sh @@ -0,0 +1,119 @@ +#!/bin/sh + +# +# install - install a program, script, or datafile +# This comes from X11R5; it is not part of GNU. +# +# $XConsortium: install.sh,v 1.2 89/12/18 14:47:22 jim Exp $ +# +# This script is compatible with the BSD install script, but was written +# from scratch. +# + + +# set DOITPROG to echo to test this script + +# Don't use :- since 4.3BSD and earlier shells don't like it. +doit="${DOITPROG-}" + + +# put in absolute paths if you don't have them in your path; or use env. vars. + +mvprog="${MVPROG-mv}" +cpprog="${CPPROG-cp}" +chmodprog="${CHMODPROG-chmod}" +chownprog="${CHOWNPROG-chown}" +chgrpprog="${CHGRPPROG-chgrp}" +stripprog="${STRIPPROG-strip}" +rmprog="${RMPROG-rm}" + +instcmd="$mvprog" +chmodcmd="" +chowncmd="" +chgrpcmd="" +stripcmd="" +rmcmd="$rmprog -f" +mvcmd="$mvprog" +src="" +dst="" + +while [ x"$1" != x ]; do + case $1 in + -c) instcmd="$cpprog" + shift + continue;; + + -m) chmodcmd="$chmodprog $2" + shift + shift + continue;; + + -o) chowncmd="$chownprog $2" + shift + shift + continue;; + + -g) chgrpcmd="$chgrpprog $2" + shift + shift + continue;; + + -s) stripcmd="$stripprog" + shift + continue;; + + *) if [ x"$src" = x ] + then + src=$1 + else + dst=$1 + fi + shift + continue;; + esac +done + +if [ x"$src" = x ] +then + echo "install: no input file specified" + exit 1 +fi + +if [ x"$dst" = x ] +then + echo "install: no destination specified" + exit 1 +fi + + +# If destination is a directory, append the input filename; if your system +# does not like double slashes in filenames, you may need to add some logic + +if [ -d $dst ] +then + dst="$dst"/`basename $src` +fi + +# Make a temp file name in the proper directory. + +dstdir=`dirname $dst` +dsttmp=$dstdir/#inst.$$# + +# Move or copy the file name to the temp name + +$doit $instcmd $src $dsttmp + +# and set any options; do chmod last to preserve setuid bits + +if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; fi +if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; fi +if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; fi +if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; fi + +# Now rename the file to the real destination. + +$doit $rmcmd $dst +$doit $mvcmd $dsttmp $dst + + +exit 0 diff --git a/lib/Makefile.in b/lib/Makefile.in new file mode 100644 index 00000000..076c1ae7 --- /dev/null +++ b/lib/Makefile.in @@ -0,0 +1,91 @@ +# Copyright (C) 1994 Ian Murdock +# Copyright (C) 1994,1995 Ian Jackson +# +# This 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, +# or (at your option) any later version. +# +# This 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 dpkg; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +srcdir = @srcdir@ +VPATH = @srcdir@ + +prefix = @prefix@ +copyingfile = $(prefix)/doc/copyright/dpkg + +SRC = ehandle.c mlib.c parse.c parsehelp.c fields.c dump.c nfmalloc.c \ + varbuf.c database.c myopt.c vercmp.c compat.c lock.c dbmodify.c \ + showcright.c tarfn.c star.c +OBJ = ehandle.o mlib.o parse.o parsehelp.o fields.o dump.o nfmalloc.o \ + varbuf.o database.o myopt.o vercmp.o compat.o lock.o dbmodify.o \ + showcright.o tarfn.o star.o + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ + +CC = @CC@ + +CFLAGS = @CFLAGS@ @CWARNS@ $(XCFLAGS) -g # fixme: remove -g +OPTCFLAGS = @OPTCFLAGS@ +LDFLAGS = -s $(XLDFLAGS) +ALL_CFLAGS = -I../include -I.. @DEFS@ $(CFLAGS) +ALL_CFLAGS_OPT = $(ALL_CFLAGS) $(OPTCFLAGS) + +AR = ar crv +RANLIB = @RANLIB@ + +.SUFFIXES: .c .o + +all: libdpkg.a + +libdpkg.a: $(OBJ) + $(AR) libdpkg.a $(OBJ) + $(RANLIB) libdpkg.a + +showcright.o: showcright.c + $(CC) -DCOPYINGFILE=\"$(copyingfile)\" $(ALL_CFLAGS) -c $< + +.c.o: + $(CC) $(ALL_CFLAGS) -c $< + +# These next few files are very heavily used, and should be optimised +# for speed rather than space. (ALL_CFLAGS_OPT usually means -O3.) +database.o: database.c + $(CC) $(ALL_CFLAGS_OPT) -c $< + +fields.o: fields.c + $(CC) $(ALL_CFLAGS_OPT) -c $< + +nfmalloc.o: nfmalloc.c + $(CC) $(ALL_CFLAGS_OPT) -c $< + +parse.o: parse.c + $(CC) $(ALL_CFLAGS_OPT) -c $< + +parsehelp.o: parsehelp.c + $(CC) $(ALL_CFLAGS_OPT) -c $< + +varbuf.o: varbuf.c + $(CC) $(ALL_CFLAGS_OPT) -c $< + +clean: + rm -f *.o core libdpkg.a + +distclean: clean + rm -f Makefile *.orig *~ *.~* ./#*# + +install: all + +dump.o fields.o parse.o parsehelp.o vercmp.o: parsedump.h +$(OBJ): ../include/dpkg.h ../include/dpkg-db.h +myopt.o: ../include/myopt.h +tarfn.o star.o: ../include/tarfn.h diff --git a/lib/compat.c b/lib/compat.c new file mode 100644 index 00000000..94ae4034 --- /dev/null +++ b/lib/compat.c @@ -0,0 +1,102 @@ +/* + * libdpkg - Debian packaging suite library routines + * compat.c - compatibility functions + * + * Copyright (C) 1995 Ian Jackson + * + * This 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, + * or (at your option) any later version. + * + * This 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 dpkg; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include + +#include "config.h" + +#ifndef HAVE_STRERROR +extern const char *const sys_errlist[]; +extern const int sys_nerr; +const char *strerror(int e) { + static char buf[100]; + if (e >= 0 && e < sys_nerr) return sys_errlist[e]; + sprintf(buf, "System error no.%d", e); + return buf; +} +#endif + +#ifndef HAVE_STRSIGNAL +extern const char *const sys_siglist[]; +const char *strsignal(int e) { + static char buf[100]; + if (e >= 0 && e < NSIG) return sys_siglist[e]; + sprintf(buf, "Signal no.%d", e); + return buf; +} +#endif + +#ifndef HAVE_SCANDIR + +static int (*scandir_comparfn)(const void*, const void*); +static int scandir_compar(const void *a, const void *b) { + return scandir_comparfn(*(const struct dirent**)a,*(const struct dirent**)b); +} + +int scandir(const char *dir, struct dirent ***namelist, + int (*select)(const struct dirent *), + int (*compar)(const void*, const void*)) { + DIR *d; + int used, avail; + struct dirent *e, *m; + d= opendir(dir); if (!d) return -1; + used=0; avail=20; + *namelist= malloc(avail*sizeof(struct dirent*)); + if (!*namelist) return -1; + while ((e= readdir(d)) != 0) { + if (!select(e)) continue; + m= malloc(sizeof(struct dirent) + strlen(e->d_name)); + if (!m) return -1; + *m= *e; + strcpy(m->d_name,e->d_name); + if (used >= avail-1) { + avail+= avail; + *namelist= realloc(*namelist, avail*sizeof(struct dirent*)); + if (!*namelist) return -1; + } + (*namelist)[used]= m; + used++; + } + (*namelist)[used]= 0; + scandir_comparfn= compar; + qsort(*namelist, used, sizeof(struct dirent*), scandir_compar); + return used; +} +#endif + +#ifndef HAVE_ALPHASORT +int alphasort(const struct dirent *a, const struct dirent *b) { + return strcmp(a->d_name,b->d_name); +} +#endif + +#ifndef HAVE_UNSETENV +void unsetenv(const char *p) { + char *q; + q= malloc(strlen(p)+3); if (!q) return; + strcpy(q,p); strcat(q,"="); putenv(q); +} +#endif diff --git a/lib/database.c b/lib/database.c new file mode 100644 index 00000000..9561a0fd --- /dev/null +++ b/lib/database.c @@ -0,0 +1,249 @@ +/* + * libdpkg - Debian packaging suite library routines + * dpkg-db.h - Low level package database routines (hash tables, etc.) + * + * Copyright (C) 1995 Ian Jackson + * + * This 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, + * or (at your option) any later version. + * + * This 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 dpkg; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include + +#include "config.h" +#include "dpkg.h" +#include "dpkg-db.h" + +#define BINS (1 << 7) + /* This must always be a power of two. If you change it + * consider changing the per-character hashing factor (currently 5) too. + */ + +static struct pkginfo *bins[BINS]; +static int npackages; + +static int hash(const char *name) { + int v= 0; + while (*name) { v *= 5; v += tolower(*name); name++; } + return v; +/* These results were achieved with 128 bins, and the list of packages + * shown at the bottom of this file. + */ +/* while (*name) { v *= 17; v += tolower(*name); name++; } + * size 5 occurs 1 times + * size 4 occurs 0 times + * size 3 occurs 7 times + * size 2 occurs 32 times + * size 1 occurs 51 times + * size 0 occurs 37 times + */ +/* while (*name) { v *= 5; v += tolower(*name); name++; } + * size 4 occurs 1 times + * size 3 occurs 14 times + * size 2 occurs 20 times + * size 1 occurs 55 times + * size 0 occurs 38 times + */ +/* while (*name) { v *= 11; v += tolower(*name); name++; } + * size 5 occurs 1 times + * size 4 occurs 4 times + * size 3 occurs 9 times + * size 2 occurs 25 times + * size 1 occurs 43 times + * size 0 occurs 46 times + */ +/* while (*name) { v *= 31; v += tolower(*name); name++; } + * size 6 occurs 1 times + * size 5 occurs 0 times + * size 4 occurs 1 times + * size 3 occurs 11 times + * size 2 occurs 27 times + * size 1 occurs 44 times + * size 0 occurs 44 times + */ +/* while (*name) { v *= 111; v += tolower(*name); name++; } + * size 5 occurs 1 times + * size 4 occurs 1 times + * size 3 occurs 14 times + * size 2 occurs 23 times + * size 1 occurs 44 times + * size 0 occurs 45 times + */ +/* while (*name) { v += (v<<3); v += tolower(*name); name++; } + * size 4 occurs 5 times + * size 3 occurs 12 times + * size 2 occurs 22 times + * size 1 occurs 41 times + * size 0 occurs 48 times + */ +/* while (*name) { v *= 29; v += tolower(*name); name++; } + * size 5 occurs 1 times + * size 4 occurs 2 times + * size 3 occurs 10 times + * size 2 occurs 26 times + * size 1 occurs 46 times + * size 0 occurs 43 times + */ +/* while (*name) { v *= 13; v += tolower(*name); name++; } + * size 5 occurs 1 times + * size 4 occurs 2 times + * size 3 occurs 5 times + * size 2 occurs 30 times + * size 1 occurs 53 times + * size 0 occurs 37 times + */ +} + +void blankpackage(struct pkginfo *pigp) { + pigp->name= 0; + pigp->status= stat_notinstalled; + pigp->eflag= eflagv_ok; + pigp->want= want_unknown; + pigp->priority= pri_unknown; + pigp->section= pigp->configversion= pigp->configrevision= 0; + pigp->files= 0; + pigp->installed.valid= 0; + pigp->available.valid= 0; + pigp->clientdata= 0; +} + +void blankpackageperfile(struct pkginfoperfile *pifp) { + pifp->essential= 0; + pifp->depends= 0; + pifp->depended= 0; + pifp->description= pifp->maintainer= pifp->source= 0; + pifp->architecture= pifp->version= pifp->revision= 0; + pifp->conffiles= 0; + pifp->arbs= 0; + pifp->valid= 1; +} + +static int nes(const char *s) { return s && *s; } + +int informativeperfile(struct pkginfoperfile *info) { + /* Used by dselect as an aid to decide whether to display things. */ + if (!info->valid) return 0; + if (info->depends || + nes(info->description) || + nes(info->maintainer) || + nes(info->source) || + nes(info->architecture) || + nes(info->version) || + nes(info->revision) || + info->conffiles || + info->arbs) return 1; + return 0; +} + +int informative(struct pkginfo *pkg) { + return ((pkg->want != want_unknown && pkg->want != want_purge) || + pkg->eflag != eflagv_ok || + pkg->status != stat_notinstalled || + nes(pkg->section) || + pkg->files || + pkg->priority != pri_unknown); +} + +struct pkginfo *findpackage(const char *name) { + struct pkginfo **pointerp, *newpkg; + + pointerp= bins + (hash(name) & (BINS-1)); + while (*pointerp && strcasecmp((*pointerp)->name,name)) + pointerp= &(*pointerp)->next; + if (*pointerp) return *pointerp; + + newpkg= nfmalloc(sizeof(struct pkginfo)); + blankpackage(newpkg); + newpkg->name= nfstrsave(name); + newpkg->next= 0; + *pointerp= newpkg; + npackages++; + + return newpkg; +} + +int countpackages(void) { + return npackages; +} + +struct pkgiterator { + struct pkginfo *pigp; + int nbinn; +}; + +struct pkgiterator *iterpkgstart(void) { + struct pkgiterator *i; + i= m_malloc(sizeof(struct pkgiterator)); + i->pigp= 0; + i->nbinn= 0; + return i; +} + +struct pkginfo *iterpkgnext(struct pkgiterator *i) { + struct pkginfo *r; + while (!i->pigp) { + if (i->nbinn >= BINS) return 0; + i->pigp= bins[i->nbinn++]; + } + r= i->pigp; i->pigp= r->next; return r; +} + +void iterpkgend(struct pkgiterator *i) { + free(i); +} + +void resetpackages(void) { + int i; + nffreeall(); + npackages= 0; + for (i=0; inext); + fprintf(file,"bin %5d has %7d\n",i,c); + freq[c]++; + } + for (i=npackages; i>0 && freq[i]==0; i--); + while (i>=0) { fprintf(file,"size %7d occurs %5d times\n",i,freq[i]); i--; } + if (ferror(file)) ohshite("failed write during hashreport"); +} + +/* + * Test dataset package names were: + * + * agetty bash bc bdflush biff bin86 binutil binutils bison bsdutils + * byacc chfn cron dc dictionaries diff dlltools dpkg e2fsprogs ed + * elisp19 elm emacs emacs-nox emacs-x emacs19 file fileutils find + * flex fsprogs gas gawk gcc gcc1 gcc2 gdb ghostview ghstview glibcdoc + * gnuplot grep groff gs gs_both gs_svga gs_x gsfonts gxditviw gzip + * hello hostname idanish ifrench igerman indent inewsinn info inn + * ispell kbd kern1148 language ldso less libc libgr libgrdev librl + * lilo linuxsrc login lout lpr m4 mailx make man manpages more mount + * mtools ncurses netbase netpbm netstd patch perl4 perl5 procps + * psutils rcs rdev sed sendmail seyon shar shellutils smail svgalib + * syslogd sysvinit tar tcpdump tcsh tex texidoc texinfo textutils + * time timezone trn unzip uuencode wenglish wu-ftpd x8514 xaxe xbase + * xbdm2 xcomp xcoral xdevel xfig xfnt100 xfnt75 xfntbig xfntscl + * xgames xherc xmach32 xmach8 xmono xnet xs3 xsvga xtexstuff xv + * xvga16 xxgdb zip + */ diff --git a/lib/dbmodify.c b/lib/dbmodify.c new file mode 100644 index 00000000..9fdd646d --- /dev/null +++ b/lib/dbmodify.c @@ -0,0 +1,256 @@ +/* + * dpkg - main program for package management + * dbmodify.c - routines for managing dpkg database updates + * + * Copyright (C) 1994,1995 Ian Jackson + * + * This 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, + * or (at your option) any later version. + * + * This 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 dpkg; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "dpkg.h" +#include "dpkg-db.h" + +char *statusfile=0, *availablefile=0; + +static enum modstatdb_rw cstatus=-1, cflags=0; +static char *importanttmpfile=0; +static FILE *importanttmp; +static int nextupdate; +static int updateslength; +static char *updatefnbuf, *updatefnrest; +static const char *admindir; +static struct varbuf uvb; + +static int ulist_select(const struct dirent *de) { + const char *p; + int l; + for (p= de->d_name, l=0; *p; p++, l++) + if (!isdigit(*p)) return 0; + if (l > IMPORTANTMAXLEN) + ohshit("updates directory contains file `%.250s' whose name is too long " + "(length=%d, max=%d)", de->d_name, l, IMPORTANTMAXLEN); + if (updateslength == -1) updateslength= l; + else if (l != updateslength) + ohshit("updates directory contains files with different length names " + "(both %d and %d)", l, updateslength); + return 1; +} + +static void cleanupdates(void) { + struct dirent **cdlist; + int cdn, i; + + parsedb(statusfile, pdb_weakclassification, 0,0,0); + + *updatefnrest= 0; + updateslength= -1; + cdn= scandir(updatefnbuf, &cdlist, &ulist_select, alphasort); + if (cdn == -1) ohshite("cannot scan updates directory `%.255s'",updatefnbuf); + + if (cdn) { + + for (i=0; id_name); + parsedb(updatefnbuf, pdb_weakclassification, 0,0,0); + if (cstatus < msdbrw_write) free(cdlist[i]); + } + + if (cstatus >= msdbrw_write) { + writedb(statusfile,0,1); + + for (i=0; id_name); + if (unlink(updatefnbuf)) + ohshite("failed to remove incorporated update file %.255s",updatefnbuf); + free(cdlist[i]); + } + } + + } + free(cdlist); + + nextupdate= 0; +} + +static void createimptmp(void) { + int i; + + onerr_abort++; + + importanttmp= fopen(importanttmpfile,"w"); + if (!importanttmp) ohshite("unable to create %.250s",importanttmpfile); + for (i=0; i<512; i++) fputs("#padding\n",importanttmp); + if (ferror(importanttmp)) + ohshite("unable to fill %.250s with padding",importanttmpfile); + if (fflush(importanttmp)) + ohshite("unable flush %.250s after padding",importanttmpfile); + if (fseek(importanttmp,0,SEEK_SET)) + ohshite("unable seek to start of %.250s after padding",importanttmpfile); + + onerr_abort--; +} + +enum modstatdb_rw modstatdb_init(const char *adir, enum modstatdb_rw readwritereq) { + static const struct fni { const char *suffix; char **store; } fnis[]= { + { STATUSFILE, &statusfile }, + { AVAILFILE, &availablefile }, + { UPDATESDIR IMPORTANTTMP, &importanttmpfile }, + { 0 } + }, *fnip; + + admindir= adir; + + for (fnip=fnis; fnip->suffix; fnip++) { + free(*fnip->store); + *fnip->store= m_malloc(strlen(adir)+strlen(fnip->suffix)+2); + sprintf(*fnip->store, "%s/%s", adir, fnip->suffix); + } + + cflags= readwritereq & msdbrw_flagsmask; + readwritereq &= ~msdbrw_flagsmask; + + switch (readwritereq) { + case msdbrw_needsuperuser: + case msdbrw_needsuperuserlockonly: + if (getuid() || geteuid()) + ohshit("requested operation requires superuser privilege"); + /* fall through */ + case msdbrw_write: case msdbrw_writeifposs: + if (access(adir,W_OK)) { + if (errno != EACCES) + ohshite("unable to access dpkg status area"); + else if (readwritereq == msdbrw_write) + ohshit("operation requires read/write access to dpkg status area"); + cstatus= msdbrw_readonly; + } else { + lockdatabase(adir); + cstatus= (readwritereq == msdbrw_needsuperuserlockonly ? + msdbrw_needsuperuserlockonly : + msdbrw_write); + } + break; + case msdbrw_readonly: + cstatus= msdbrw_readonly; break; + default: + internerr("unknown readwritereq"); + } + + updatefnbuf= m_malloc(strlen(adir)+sizeof(UPDATESDIR)+IMPORTANTMAXLEN+5); + strcpy(updatefnbuf,adir); + strcat(updatefnbuf,"/" UPDATESDIR); + updatefnrest= updatefnbuf+strlen(updatefnbuf); + + cleanupdates(); + + if (cstatus >= msdbrw_write) { + createimptmp(); + uvb.used= 0; + uvb.size= 10240; + uvb.buf= m_malloc(uvb.size); + } + + if (cstatus != msdbrw_needsuperuserlockonly) { + parsedb(statusfile, pdb_weakclassification, 0,0,0); + parsedb(statusfile, pdb_recordavailable, 0,0,0); + parsedb(availablefile, + pdb_recordavailable|pdb_rejectstatus| + (cflags & msdbrw_availablepreferversion ? pdb_preferversion : 0), + 0,0,0); + } + + return cstatus; +} + +static void checkpoint(void) { + int i; + + assert(cstatus >= msdbrw_write); + writedb(statusfile,0,1); + + for (i=0; i= msdbrw_write); + + onerr_abort++; + + varbufreset(&uvb); + varbufrecord(&uvb, pkg, &pkg->installed); + if (fwrite(uvb.buf, 1, uvb.used, importanttmp) != uvb.used) + ohshite("unable to write updated status of `%.250s'", pkg->name); + if (fflush(importanttmp)) + ohshite("unable to flush updated status of `%.250s'", pkg->name); + if (ftruncate(fileno(importanttmp), uvb.used)) + ohshite("unable to truncate for updated status of `%.250s'", pkg->name); + if (fsync(fileno(importanttmp))) + ohshite("unable to fsync updated status of `%.250s'", pkg->name); + if (fclose(importanttmp)) + ohshite("unable to close updated status of `%.250s'", pkg->name); + sprintf(updatefnrest, IMPORTANTFMT, nextupdate); + if (rename(importanttmpfile, updatefnbuf)) + ohshite("unable to install updated status of `%.250s'", pkg->name); + assert(strlen(updatefnrest)<=IMPORTANTMAXLEN); /* or we've made a real mess */ + + nextupdate++; + + if (nextupdate > MAXUPDATES) checkpoint(); + + createimptmp(); + + onerr_abort--; +} diff --git a/lib/debugmake b/lib/debugmake new file mode 100755 index 00000000..57ac0b17 --- /dev/null +++ b/lib/debugmake @@ -0,0 +1,3 @@ +#!/bin/sh +set -x +make 'XCFLAGS=-g -DMDEBUG -O0' LDFLAGS=-g 'LIBS= -lefence -L../lib -ldpkg' "$@" diff --git a/lib/dump.c b/lib/dump.c new file mode 100644 index 00000000..97d5c430 --- /dev/null +++ b/lib/dump.c @@ -0,0 +1,289 @@ +/* + * libdpkg - Debian packaging suite library routines + * dump.c - code to write in-core database to a file + * + * Copyright (C) 1995 Ian Jackson + * + * This 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, + * or (at your option) any later version. + * + * This 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 dpkg; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* fixme: don't write uninteresting packages */ + +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "dpkg.h" +#include "dpkg-db.h" +#include "parsedump.h" + +void w_name(struct varbuf *vb, + const struct pkginfo *pigp, const struct pkginfoperfile *pifp, + const struct fieldinfo *fip) { + assert(pigp->name); + varbufaddstr(vb,"Package: "); varbufaddstr(vb, pigp->name); + varbufaddc(vb,'\n'); +} + +void w_version(struct varbuf *vb, + const struct pkginfo *pigp, const struct pkginfoperfile *pifp, + const struct fieldinfo *fip) { + /* Revision information is printed in version field too. */ + if ((!pifp->version || !*pifp->version) && + (!pifp->revision || !*pifp->revision)) return; + varbufaddstr(vb,"Version: "); + varbufaddstr(vb,pifp->version ? pifp->version : ""); + if (pifp->revision && *pifp->revision) { + varbufaddc(vb,'-'); + varbufaddstr(vb,pifp->revision); + } + varbufaddc(vb,'\n'); +} + +void w_configversion(struct varbuf *vb, + const struct pkginfo *pigp, const struct pkginfoperfile *pifp, + const struct fieldinfo *fip) { + if ((!pigp->configversion || !*pigp->configversion) && + (!pigp->configrevision || !*pigp->configrevision)) return; + if (pifp != &pigp->installed) return; + if (pigp->status == stat_installed || pigp->status == stat_notinstalled) return; + varbufaddstr(vb,"Config-Version: "); + varbufaddstr(vb,pigp->configversion ? pigp->configversion : ""); + if (pigp->configrevision && *pigp->configrevision) { + varbufaddc(vb,'-'); + varbufaddstr(vb,pigp->configrevision); + } + varbufaddc(vb,'\n'); +} + +void w_null(struct varbuf *vb, + const struct pkginfo *pigp, const struct pkginfoperfile *pifp, + const struct fieldinfo *fip) { +} + +void w_section(struct varbuf *vb, + const struct pkginfo *pigp, const struct pkginfoperfile *pifp, + const struct fieldinfo *fip) { + const char *value= pigp->section; + if (!value || !*value) return; + varbufaddstr(vb,"Section: "); + varbufaddstr(vb,value); + varbufaddc(vb,'\n'); +} + +void w_charfield(struct varbuf *vb, + const struct pkginfo *pigp, const struct pkginfoperfile *pifp, + const struct fieldinfo *fip) { + const char *value= pifp->valid ? PKGPFIELD(pifp,fip->integer,char*) : 0; + if (!value || !*value) return; + varbufaddstr(vb,fip->name); varbufaddstr(vb, ": "); varbufaddstr(vb,value); + varbufaddc(vb,'\n'); +} + +void w_filecharf(struct varbuf *vb, + const struct pkginfo *pigp, const struct pkginfoperfile *pifp, + const struct fieldinfo *fip) { + struct filedetails *fdp; + + if (pifp != &pigp->available) return; + fdp= pigp->files; + if (!fdp || !FILEFFIELD(fdp,fip->integer,char*)) return; + varbufaddstr(vb,fip->name); varbufaddc(vb,':'); + while (fdp) { + varbufaddc(vb,' '); + varbufaddstr(vb,FILEFFIELD(fdp,fip->integer,char*)); + fdp= fdp->next; + } + varbufaddc(vb,'\n'); +} + +void w_booleandefno(struct varbuf *vb, + const struct pkginfo *pigp, const struct pkginfoperfile *pifp, + const struct fieldinfo *fip) { + int value= pifp->valid ? PKGPFIELD(pifp,fip->integer,int) : 0; + if (!value) return; + assert(value==1); + varbufaddstr(vb,"Essential: yes\n"); +} + +void w_priority(struct varbuf *vb, + const struct pkginfo *pigp, const struct pkginfoperfile *pifp, + const struct fieldinfo *fip) { + if (pigp->priority == pri_unknown) return; + assert(pigp->priority <= pri_unknown); + varbufaddstr(vb,"Priority: "); + varbufaddstr(vb, + pigp->priority == pri_other + ? pigp->otherpriority + : priorityinfos[pigp->priority].name); + varbufaddc(vb,'\n'); +} + +void w_status(struct varbuf *vb, + const struct pkginfo *pigp, const struct pkginfoperfile *pifp, + const struct fieldinfo *fip) { + if (pifp != &pigp->installed) return; + assert(pigp->want <= want_purge && + pigp->eflag <= eflagv_both && + pigp->status <= stat_configfiles); + varbufaddstr(vb,"Status: "); + varbufaddstr(vb,wantinfos[pigp->want].name); varbufaddc(vb,' '); + varbufaddstr(vb,eflaginfos[pigp->eflag].name); varbufaddc(vb,' '); + varbufaddstr(vb,statusinfos[pigp->status].name); varbufaddc(vb,'\n'); +} + +void varbufdependency(struct varbuf *vb, struct dependency *dep) { + struct deppossi *dop; + const char *possdel; + + possdel= ""; + for (dop= dep->list; dop; dop= dop->next) { + assert(dop->up == dep); + varbufaddstr(vb,possdel); possdel= " | "; + varbufaddstr(vb,dop->ed->name); + if (dop->verrel != dvr_none) { + varbufaddstr(vb," ("); + switch (dop->verrel) { + case dvr_exact: varbufaddc(vb,'='); break; + case dvr_laterequal: varbufaddstr(vb,">="); break; + case dvr_earlierequal: varbufaddstr(vb,"<="); break; + case dvr_laterstrict: varbufaddstr(vb,">>"); break; + case dvr_earlierstrict: varbufaddstr(vb,"<<"); break; + default: internerr("unknown verrel"); + } + if (!isalnum(dop->version[0])) varbufaddc(vb,' '); + varbufaddstr(vb,dop->version); + if (dop->revision) { varbufaddc(vb,'-'); varbufaddstr(vb,dop->revision); } + varbufaddc(vb,')'); + } + } +} + +void w_dependency(struct varbuf *vb, + const struct pkginfo *pigp, const struct pkginfoperfile *pifp, + const struct fieldinfo *fip) { + char fnbuf[50]; + const char *depdel; + struct dependency *dyp; + + if (!pifp->valid) return; + sprintf(fnbuf,"%s: ",fip->name); depdel= fnbuf; + for (dyp= pifp->depends; dyp; dyp= dyp->next) { + if (dyp->type != fip->integer) continue; + assert(dyp->up == pigp); + varbufaddstr(vb,depdel); depdel= ", "; + varbufdependency(vb,dyp); + } + if (depdel != fnbuf) varbufaddc(vb,'\n'); +} + +void w_conffiles(struct varbuf *vb, + const struct pkginfo *pigp, const struct pkginfoperfile *pifp, + const struct fieldinfo *fip) { + struct conffile *i; + + if (!pifp->valid || !pifp->conffiles || pifp == &pigp->available) return; + varbufaddstr(vb,"Conffiles:\n"); + for (i=pifp->conffiles; i; i= i->next) { + varbufaddc(vb,' '); varbufaddstr(vb,i->name); varbufaddc(vb,' '); + varbufaddstr(vb,i->hash); varbufaddc(vb,'\n'); + } +} + +void varbufrecord(struct varbuf *vb, + const struct pkginfo *pigp, const struct pkginfoperfile *pifp) { + const struct fieldinfo *fip; + const struct arbitraryfield *afp; + + for (fip= fieldinfos; fip->name; fip++) { + fip->wcall(vb,pigp,pifp,fip); + } + if (pifp->valid) { + for (afp= pifp->arbs; afp; afp= afp->next) { + varbufaddstr(vb,afp->name); varbufaddstr(vb,": "); + varbufaddstr(vb,afp->value); varbufaddc(vb,'\n'); + } + } +} + +void writerecord(FILE *file, const char *filename, + const struct pkginfo *pigp, const struct pkginfoperfile *pifp) { + struct varbuf vb; + + varbufinit(&vb); + varbufrecord(&vb,pigp,pifp); + varbufaddc(&vb,'\0'); + if (!fputs(vb.buf,file)) + ohshite("failed to write details of `%.50s' to `%.250s'", pigp->name, filename); +} + +void writedb(const char *filename, int available, int mustsync) { + static char writebuf[8192]; + + struct pkgiterator *it; + struct pkginfo *pigp; + char *oldfn, *newfn; + const char *which; + FILE *file; + struct varbuf vb; + + which= available ? "available" : "status"; + oldfn= m_malloc(strlen(filename)+sizeof(OLDDBEXT)); + strcpy(oldfn,filename); strcat(oldfn,OLDDBEXT); + newfn= m_malloc(strlen(filename)+sizeof(NEWDBEXT)); + strcpy(newfn,filename); strcat(newfn,NEWDBEXT); + varbufinit(&vb); + + file= fopen(newfn,"w"); + if (!file) ohshite("failed to open `%s' for writing %s information",filename,which); + + if (setvbuf(file,writebuf,_IOFBF,sizeof(writebuf))) + ohshite("unable to set buffering on status file"); + + it= iterpkgstart(); + while ((pigp= iterpkgnext(it)) != 0) { + if (!(informative(pigp) || + informativeperfile(&pigp->available) || + informativeperfile(&pigp->installed))) + /* Don't dump records which have no useful content. */ + continue; + varbufrecord(&vb, pigp, available ? &pigp->available : &pigp->installed); + varbufaddc(&vb,'\n'); varbufaddc(&vb,0); + if (!fputs(vb.buf,file)) + ohshite("failed to write %s record about `%.50s' to `%.250s'", + which, pigp->name, filename); + varbufreset(&vb); + } + varbuffree(&vb); + if (mustsync) { + if (fflush(file)) + ohshite("failed to flush %s information to `%.250s'", which, filename); + if (fsync(fileno(file))) + ohshite("failed to fsync %s information to `%.250s'", which, filename); + } + if (fclose(file)) ohshite("failed to close `%.250s' after writing %s information", + filename, which); + unlink(oldfn); + if (link(filename,oldfn) && errno != ENOENT) + ohshite("failed to link `%.250s' to `%.250s' for backup of %s info", + filename, oldfn, which); + if (rename(newfn,filename)) + ohshite("failed to install `%.250s' as `%.250s' containing %s info", + newfn, filename, which); +} diff --git a/lib/ehandle.c b/lib/ehandle.c new file mode 100644 index 00000000..f0843b9d --- /dev/null +++ b/lib/ehandle.c @@ -0,0 +1,276 @@ +/* + * libdpkg - Debian packaging suite library routines + * ehandle.c - error handling + * + * Copyright (C) 1994,1995 Ian Jackson + * + * This 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, + * or (at your option) any later version. + * + * This 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 dpkg; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "dpkg.h" +#include "dpkg-db.h" + +static const char *errmsg; /* points to errmsgbuf or malloc'd */ +static char errmsgbuf[4096]; +/* 6x255 for inserted strings (%.255s &c in fmt; also %s with limited length arg) + * 1x255 for constant text + * 1x255 for strerror() + * same again just in case. + */ + +#define NCALLS 2 + +struct cleanupentry { + struct cleanupentry *next; + struct { + int mask; + void (*call)(int argc, void **argv); + } calls[NCALLS]; + int cpmask, cpvalue; + int argc; + void *argv[1]; +}; + +struct errorcontext { + struct errorcontext *next; + jmp_buf *jbufp; + struct cleanupentry *cleanups; + void (*printerror)(const char *emsg, const char *contextstring); + const char *contextstring; +}; + +static struct errorcontext *volatile econtext= 0; +static struct { struct cleanupentry ce; void *args[20]; } emergency; + +void set_error_display(void (*printerror)(const char *, const char *), + const char *contextstring) { + assert(econtext); + econtext->printerror= printerror; + econtext->contextstring= contextstring; +} + +void push_error_handler(jmp_buf *jbufp, + void (*printerror)(const char *, const char *), + const char *contextstring) { + struct errorcontext *necp; + necp= malloc(sizeof(struct errorcontext)); + if (!necp) { + int e= errno; + strcpy(errmsgbuf, "out of memory pushing error handler: "); + strcat(errmsgbuf, strerror(e)); errmsg= errmsgbuf; + if (econtext) longjmp(*econtext->jbufp,1); + fprintf(stderr, "%s: %s\n", thisname, errmsgbuf); exit(2); + } + necp->next= econtext; + necp->jbufp= jbufp; + necp->cleanups= 0; + necp->printerror= printerror; + necp->contextstring= contextstring; + econtext= necp; + onerr_abort= 0; +} + +static void print_error_cleanup(const char *emsg, const char *contextstring) { + fprintf(stderr, "%s: error while cleaning up:\n %s\n",thisname,emsg); +} + +static void run_cleanups(struct errorcontext *econ, int flagsetin) { + static volatile int preventrecurse= 0; + struct cleanupentry *volatile cep; + struct cleanupentry *ncep; + struct errorcontext recurserr, *oldecontext; + jmp_buf recurejbuf; + volatile int i, flagset; + + if (econ->printerror) econ->printerror(errmsg,econ->contextstring); + + if (++preventrecurse > 3) { + onerr_abort++; + fprintf(stderr, DPKG ": too many nested errors during error recovery !!\n"); + flagset= 0; + } else { + flagset= flagsetin; + } + cep= econ->cleanups; + oldecontext= econtext; + while (cep) { + for (i=0; icalls[i].call && cep->calls[i].mask & flagset) { + if (setjmp(recurejbuf)) { + run_cleanups(&recurserr, ehflag_bombout | ehflag_recursiveerror); + } else { + recurserr.jbufp= &recurejbuf; + recurserr.cleanups= 0; + recurserr.next= 0; + recurserr.printerror= print_error_cleanup; + recurserr.contextstring= 0; + econtext= &recurserr; + cep->calls[i].call(cep->argc,cep->argv); + } + econtext= oldecontext; + } + } + ncep= cep->next; + if (cep != &emergency.ce) free(cep); + cep= ncep; + } + preventrecurse--; +} + +void error_unwind(int flagset) { + struct cleanupentry *cep; + struct errorcontext *tecp; + + tecp= econtext; + cep= tecp->cleanups; + econtext= tecp->next; + run_cleanups(tecp,flagset); + free(tecp); +} + +void push_checkpoint(int mask, int value) { + /* This will arrange that when error_unwind() is called, + * all previous cleanups will be executed with + * flagset= (original_flagset & mask) | value + * where original_flagset is the argument to error_unwind + * (as modified by any checkpoint which was pushed later). + */ + struct cleanupentry *cep; + int i; + + cep= m_malloc(sizeof(struct cleanupentry) + sizeof(char*)); + for (i=0; icalls[i].call=0; cep->calls[i].mask=0; } + cep->cpmask= mask; cep->cpvalue= value; + cep->argc= 0; cep->argv[0]= 0; + cep->next= econtext->cleanups; + econtext->cleanups= cep; +} + +void push_cleanup(void (*call1)(int argc, void **argv), int mask1, + void (*call2)(int argc, void **argv), int mask2, + int nargs, ...) { + struct cleanupentry *cep; + void **args; + int e; + va_list al; + + onerr_abort++; + + cep= malloc(sizeof(struct cleanupentry) + sizeof(char*)*(nargs+1)); + if (!cep) { + if (nargs > sizeof(emergency.args)/sizeof(void*)) + ohshite("out of memory for new cleanup entry with many arguments"); + e= errno; cep= &emergency.ce; + } + cep->calls[0].call= call1; cep->calls[0].mask= mask1; + cep->calls[1].call= call2; cep->calls[1].mask= mask2; + cep->cpmask=~0; cep->cpvalue=0; cep->argc= nargs; + va_start(al,nargs); + args= cep->argv; while (nargs-- >0) *args++= va_arg(al,void*); + *args++= 0; + va_end(al); + cep->next= econtext->cleanups; + econtext->cleanups= cep; + if (cep == &emergency.ce) { e= errno; ohshite("out of memory for new cleanup entry"); } + + onerr_abort--; +} + +void pop_cleanup(int flagset) { + struct cleanupentry *cep; + int i; + + cep= econtext->cleanups; + econtext->cleanups= cep->next; + for (i=0; icalls[i].call && cep->calls[i].mask & flagset) + cep->calls[i].call(cep->argc,cep->argv); + flagset &= cep->cpmask; + flagset |= cep->cpvalue; + } + if (cep != &emergency.ce) free(cep); +} + +void ohshit(const char *fmt, ...) { + va_list al; + va_start(al,fmt); + vsprintf(errmsgbuf,fmt,al); + va_end(al); + errmsg= errmsgbuf; + longjmp(*econtext->jbufp,1); +} + +void print_error_fatal(const char *emsg, const char *contextstring) { + fprintf(stderr, "%s: %s\n",thisname,emsg); +} + +void ohshitvb(struct varbuf *vb) { + char *m; + varbufaddc(vb,0); + m= m_malloc(strlen(vb->buf)); + strcpy(m,vb->buf); + errmsg= m; + longjmp(*econtext->jbufp,1); +} + +void ohshitv(const char *fmt, va_list al) { + vsprintf(errmsgbuf,fmt,al); + errmsg= errmsgbuf; + longjmp(*econtext->jbufp,1); +} + +void ohshite(const char *fmt, ...) { + int e; + va_list al; + + e=errno; + va_start(al,fmt); + vsprintf(errmsgbuf,fmt,al); + va_end(al); + + strcat(errmsgbuf,": "); + strcat(errmsgbuf,strerror(e)); + errmsg= errmsgbuf; + longjmp(*econtext->jbufp,1); +} + +void badusage(const char *fmt, ...) { + va_list al; + va_start(al,fmt); + vsprintf(errmsgbuf,fmt,al); + va_end(al); + strcat(errmsgbuf,"\n\n"); + strcat(errmsgbuf,printforhelp); + errmsg= errmsgbuf; + longjmp(*econtext->jbufp,1); +} + +void werr(const char *fn) { + ohshite("error writing `%.250s'",fn); +} + +void do_internerr(const char *string, int line, const char *file) { + fprintf(stderr,"%s:%d: internal error `%s'\n",file,line,string); + abort(); +} diff --git a/lib/fields.c b/lib/fields.c new file mode 100644 index 00000000..9066c043 --- /dev/null +++ b/lib/fields.c @@ -0,0 +1,354 @@ +/* + * libdpkg - Debian packaging suite library routines + * fields.c - parsing of all the different fields, when reading in + * + * Copyright (C) 1995 Ian Jackson + * + * This 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, + * or (at your option) any later version. + * + * This 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 dpkg; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include + +#include "config.h" +#include "dpkg.h" +#include "dpkg-db.h" +#include "parsedump.h" + +static int convert_string(const char *filename, int lno, const char *what, int otherwise, + FILE *warnto, int *warncount, const struct pkginfo *pigp, + const char *startp, const struct namevalue *nvip, + const char **endpp) { + const char *ep; + int c, l; + + ep= startp; + if (!*ep) parseerr(0,filename,lno, warnto,warncount,pigp,0, "%s is missing",what); + while ((c= *ep) && !isspace(c)) ep++; + l= (int)(ep-startp); + while (nvip->name && (strncasecmp(nvip->name,startp,l) || nvip->name[l])) nvip++; + if (!nvip->name) { + if (otherwise != -1) return otherwise; + parseerr(0,filename,lno, warnto,warncount,pigp,0, "`%.*s' is not allowed for %s", + l > 50 ? 50 : l, startp, what); + } + while (isspace(c)) c= *++ep; + if (c && !endpp) + parseerr(0,filename,lno, warnto,warncount,pigp,0, "junk after %s",what); + if (endpp) *endpp= ep; + return nvip->value; +} + +void f_name(struct pkginfo *pigp, struct pkginfoperfile *pifp, enum parsedbflags flags, + const char *filename, int lno, FILE *warnto, int *warncount, + const char *value, const struct fieldinfo *fip) { + const char *e; + if ((e= illegal_packagename(value,0)) != 0) + parseerr(0,filename,lno, warnto,warncount,pigp,0, "invalid package name (%.250s)",e); + pigp->name= nfstrsave(value); + findpackage(value); /* We discard the value from findpackage, but calling it + * forces an entry in the hash table to be made if it isn't + * already. This is so that we don't reorder the file + * unnecessarily (doing so is bad for debugging). + */ +} + +void f_filecharf(struct pkginfo *pigp, struct pkginfoperfile *pifp, + enum parsedbflags flags, + const char *filename, int lno, FILE *warnto, int *warncount, + const char *value, const struct fieldinfo *fip) { + struct filedetails *fdp, **fdpp; + char *cpos, *space; + int allowextend; + + if (!*value) + parseerr(0,filename,lno, warnto,warncount,pigp,0, + "empty file details field `%s'",fip->name); + if (!(flags & pdb_recordavailable)) + parseerr(0,filename,lno, warnto,warncount,pigp,0, + "file details field `%s' not allowed in status file",fip->name); + allowextend= !pigp->files; + fdpp= &pigp->files; + cpos= nfstrsave(value); + while (*cpos) { + space= cpos; while (*space && !isspace(*space)) space++; + if (*space) *space++= 0; + fdp= *fdpp; + if (!fdp) { + if (!allowextend) + parseerr(0,filename,lno, warnto,warncount,pigp,0, "too many values " + "in file details field `%s' (compared to others)",fip->name); + fdp= nfmalloc(sizeof(struct filedetails)); + fdp->next= 0; + fdp->name= fdp->msdosname= fdp->size= fdp->md5sum= 0; + *fdpp= fdp; + } + FILEFFIELD(fdp,fip->integer,char*)= cpos; + fdpp= &fdp->next; + while (*space && isspace(*space)) space++; + cpos= space; + } + if (*fdpp) + parseerr(0,filename,lno, warnto,warncount,pigp,0, "too few values " + "in file details field `%s' (compared to others)",fip->name); +} + +void f_charfield(struct pkginfo *pigp, struct pkginfoperfile *pifp, + enum parsedbflags flags, + const char *filename, int lno, FILE *warnto, int *warncount, + const char *value, const struct fieldinfo *fip) { + if (*value) PKGPFIELD(pifp,fip->integer,char*)= nfstrsave(value); +} + +void f_boolean(struct pkginfo *pigp, struct pkginfoperfile *pifp, + enum parsedbflags flags, + const char *filename, int lno, FILE *warnto, int *warncount, + const char *value, const struct fieldinfo *fip) { + pifp->essential= + *value ? convert_string(filename,lno,"yes/no in `essential' field", -1, + warnto,warncount,pigp, + value,booleaninfos,0) + : 0; +} + +void f_section(struct pkginfo *pigp, struct pkginfoperfile *pifp, + enum parsedbflags flags, + const char *filename, int lno, FILE *warnto, int *warncount, + const char *value, const struct fieldinfo *fip) { + if (!*value) return; + pigp->section= nfstrsave(value); +} + +void f_priority(struct pkginfo *pigp, struct pkginfoperfile *pifp, + enum parsedbflags flags, + const char *filename, int lno, FILE *warnto, int *warncount, + const char *value, const struct fieldinfo *fip) { + if (!*value) return; + pigp->priority= convert_string(filename,lno,"word in `priority' field", pri_other, + warnto,warncount,pigp, + value,priorityinfos,0); + if (pigp->priority == pri_other) pigp->otherpriority= nfstrsave(value); +} + +void f_status(struct pkginfo *pigp, struct pkginfoperfile *pifp, + enum parsedbflags flags, + const char *filename, int lno, FILE *warnto, int *warncount, + const char *value, const struct fieldinfo *fip) { + const char *ep; + + if (flags & pdb_rejectstatus) + parseerr(0,filename,lno, warnto,warncount,pigp,0, + "value for `status' field not allowed in this context"); + if (flags & pdb_recordavailable) return; + + pigp->want= convert_string(filename,lno,"first (want) word in `status' field", -1, + warnto,warncount,pigp, value,wantinfos,&ep); + pigp->eflag= convert_string(filename,lno,"second (error) word in `status' field", -1, + warnto,warncount,pigp, ep,eflaginfos,&ep); + pigp->status= convert_string(filename,lno,"third (status) word in `status' field", -1, + warnto,warncount,pigp, ep,statusinfos,0); +} + +void f_configversion(struct pkginfo *pigp, struct pkginfoperfile *pifp, + enum parsedbflags flags, + const char *filename, int lno, FILE *warnto, int *warncount, + const char *value, const struct fieldinfo *fip) { + char *mycopy, *hyphen; + + if (flags & pdb_rejectstatus) + parseerr(0,filename,lno, warnto,warncount,pigp,0, + "value for `config-version' field not allowed in this context"); + if (flags & pdb_recordavailable) return; + + mycopy= nfstrsave(value); + hyphen= strrchr(mycopy,'-'); + if (hyphen) *hyphen++= 0; + + pigp->configversion= mycopy; + pigp->configrevision= hyphen ? hyphen : nfstrsave(""); +} + +void f_conffiles(struct pkginfo *pigp, struct pkginfoperfile *pifp, + enum parsedbflags flags, + const char *filename, int lno, FILE *warnto, int *warncount, + const char *value, const struct fieldinfo *fip) { + struct conffile **lastp, *newlink; + const char *endent, *endfn; + int c, namelen, hashlen; + + lastp= &pifp->conffiles; + while (*value) { + c= *value++; + if (c == '\n') continue; + if (c != ' ') parseerr(0,filename,lno, warnto,warncount,pigp,0, "value for" + " `conffiles' has line starting with non-space `%c'", c); + for (endent= value; (c= *endent)!=0 && c != '\n'; endent++); + for (endfn= endent; *endfn != ' '; endfn--); + if (endfn <= value+1 || endfn >= endent-1) + parseerr(0,filename,lno, warnto,warncount,pigp,0, + "value for `conffiles' has malformatted line `%.*s'", + (int)(endent-value > 250 ? 250 : endent-value), value); + newlink= nfmalloc(sizeof(struct conffile)); + value= skip_slash_dotslash(value); + namelen= (int)(endfn-value); + if (namelen <= 0) parseerr(0,filename,lno, warnto,warncount,pigp,0, + "root or null directory is listed as a conffile"); + newlink->name= nfmalloc(namelen+2); + newlink->name[0]= '/'; + memcpy(newlink->name+1,value,namelen); + newlink->name[namelen+1]= 0; + hashlen= (int)(endent-endfn)-1; newlink->hash= nfmalloc(hashlen+1); + memcpy(newlink->hash,endfn+1,hashlen); newlink->hash[hashlen]= 0; + newlink->next =0; + *lastp= newlink; + lastp= &newlink->next; + value= endent; + } +} + +void f_dependency(struct pkginfo *pigp, struct pkginfoperfile *pifp, + enum parsedbflags flags, + const char *filename, int lno, FILE *warnto, int *warncount, + const char *value, const struct fieldinfo *fip) { + char *q, c1, c2; + const char *p, *emsg; + struct varbuf depname, version; + struct dependency *dyp, **ldypp; + struct deppossi *dop, **ldopp; + + if (!*value) return; /* empty fields are ignored */ + varbufinit(&depname); varbufinit(&version); + p= value; + ldypp= &pifp->depends; while (*ldypp) ldypp= &(*ldypp)->next; + for (;;) { /* loop creating new struct dependency's */ + dyp= nfmalloc(sizeof(struct dependency)); + dyp->up= 0; /* Set this to zero for now, as we don't know what our real + * struct pkginfo address (in the database) is going to be yet. + */ + dyp->next= 0; *ldypp= dyp; ldypp= &dyp->next; + dyp->list= 0; ldopp= &dyp->list; + dyp->type= fip->integer; + for (;;) { /* loop creating new struct deppossi's */ + varbufreset(&depname); + while (*p && !isspace(*p) && *p != '(' && *p != ',' && *p != '|') { + varbufaddc(&depname,*p); p++; + } + varbufaddc(&depname,0); + if (!*depname.buf) + parseerr(0,filename,lno, warnto,warncount,pigp,0, "`%s' field, missing" + " package name, or garbage where package name expected", fip->name); + emsg= illegal_packagename(depname.buf,0); + if (emsg) parseerr(0,filename,lno, warnto,warncount,pigp,0, "`%s' field," + " invalid package name `%.255s': %s", + fip->name, depname.buf, emsg); + dop= nfmalloc(sizeof(struct deppossi)); + dop->up= dyp; + dop->ed= findpackage(depname.buf); + dop->next= 0; *ldopp= dop; ldopp= &dop->next; + dop->nextrev= 0; /* Don't link this (which is after all only `newpig' from + dop->backrev= 0; * the main parsing loop in parsedb) into the depended on + * packages' lists yet. This will be done later when we + * install this (in parse.c). For the moment we do the + * `forward' links in deppossi (`ed') only, and the backward + * links from the depended on packages to dop are left undone. + */ + dop->cyclebreak= 0; + while (isspace(*p)) p++; + if (*p == '(') { + varbufreset(&version); + p++; while (isspace(*p)) p++; + c1= *p; + if (c1 == '<' || c1 == '>') { + c2= *++p; + dop->verrel= (c1 == '<') ? dvrf_earlier : dvrf_later; + if (c2 == '=') { + dop->verrel |= (dvrf_orequal | dvrf_builtup); + p++; + } else if (c2 == c1) { + dop->verrel |= (dvrf_strict | dvrf_builtup); + p++; + } else if (c2 == '<' || c2 == '>') { + parseerr(0,filename,lno, warnto,warncount,pigp,0, + "`%s' field, reference to `%.255s':\n" + " bad version relationship %c%c", + fip->name,depname.buf,c1,c2); + dop->verrel= dvr_none; + } else { + parseerr(0,filename,lno, warnto,warncount,pigp,1, + "`%s' field, reference to `%.255s':\n" + " `%c' is obsolete, use `%c=' or `%c%c' instead", + fip->name,depname.buf,c1,c1,c1,c1); + dop->verrel |= (dvrf_orequal | dvrf_builtup); + } + } else if (c1 == '=') { + dop->verrel= dvr_exact; + p++; + } else { + parseerr(0,filename,lno, warnto,warncount,pigp,1, + "`%s' field, reference to `%.255s':\n" + " implicit exact match on version number, suggest using `=' instead", + fip->name,depname.buf); + dop->verrel= dvr_exact; + } + if (!isspace(*p) && !isalnum(*p)) { + parseerr(0,filename,lno, warnto,warncount,pigp,1, + "`%s' field, reference to `%.255s':\n" + " version value starts with non-alphanumeric, suggest adding a space", + fip->name,depname.buf); + } + while (isspace(*p)) p++; + while (*p && *p != ')' && *p != '(') { + if (!isspace(*p)) varbufaddc(&version,*p); + p++; + } + if (*p == '(') parseerr(0,filename,lno, warnto,warncount,pigp,0, + "`%s' field, reference to `%.255s'" + " version contains (", fip->name, depname.buf); + else if (*p == 0) parseerr(0,filename,lno, warnto,warncount,pigp,0, + "`%s' field, reference to `%.255s'" + "version unterminated", fip->name, depname.buf); + varbufaddc(&version,0); + q= strrchr(version.buf,'-'); + if (q) { + *q++= 0; + dop->revision= nfstrsave(q); + } else { + dop->revision= 0; + } + dop->version= nfstrsave(version.buf); + p++; while (isspace(*p)) p++; + } else { + dop->verrel= dvr_none; + dop->revision= dop->version= 0; + } + if (!*p || *p == ',') break; + if (*p != '|') + parseerr(0,filename,lno, warnto,warncount,pigp,0, "`%s' field, syntax" + " error after reference to package `%.255s'", + fip->name, dop->ed->name); + if (fip->integer == dep_conflicts || + fip->integer == dep_provides || + fip->integer == dep_replaces) + parseerr(0,filename,lno, warnto,warncount,pigp,0, + "alternatives (`|') not allowed in %s field", + fip->name); + p++; while (isspace(*p)) p++; + } + if (!*p) break; + p++; while (isspace(*p)) p++; + } +} + diff --git a/lib/lock.c b/lib/lock.c new file mode 100644 index 00000000..e0af62a7 --- /dev/null +++ b/lib/lock.c @@ -0,0 +1,82 @@ +/* + * libdpkg - Debian packaging suite library routines + * lock.c - packages database locking + * + * Copyright (C) 1994,1995 Ian Jackson + * + * This 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, + * or (at your option) any later version. + * + * This 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 dpkg; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "dpkg.h" +#include "dpkg-db.h" + +static char *dblockfile= 0; +static int dblockfd= -1; + +static void cu_unlockdb(int argc, void **argv) { + struct flock fl; + assert(dblockfile); + assert(dblockfd >= 0); + fl.l_type= F_UNLCK; + fl.l_whence= SEEK_SET; + fl.l_start= 0; + fl.l_len= 1; + if (fcntl(dblockfd,F_SETLK,&fl) == -1) + ohshite("unable to unlock dpkg status database"); +} + +void unlockdatabase(const char *admindir) { + pop_cleanup(ehflag_normaltidy); /* calls cu_unlockdb */ +} + +void lockdatabase(const char *admindir) { + int n; + struct flock fl; + + if (!dblockfile) { + n= strlen(admindir); + dblockfile= m_malloc(n+sizeof(LOCKFILE)+2); + strcpy(dblockfile,admindir); + strcpy(dblockfile+n, "/" LOCKFILE); + } + if (dblockfd == -1) { + dblockfd= open(dblockfile, O_RDWR|O_CREAT|O_TRUNC, 0660); + if (dblockfd == -1) { + if (errno == EPERM) + ohshit("you do not have permission to lock the dpkg status database"); + ohshite("unable to open/create status database lockfile"); + } + } + fl.l_type= F_WRLCK; + fl.l_whence= SEEK_SET; + fl.l_start= 0; + fl.l_len= 1; + if (fcntl(dblockfd,F_SETLK,&fl) == -1) { + if (errno == EWOULDBLOCK || errno == EAGAIN) + ohshit("status database area is locked - another dpkg/dselect is running"); + ohshite("unable to lock dpkg status database"); + } + push_cleanup(cu_unlockdb,~0, 0,0, 0); +} diff --git a/lib/mlib.c b/lib/mlib.c new file mode 100644 index 00000000..5fee2a40 --- /dev/null +++ b/lib/mlib.c @@ -0,0 +1,124 @@ +/* + * libdpkg - Debian packaging suite library routines + * mlib.c - `must' library: routines will succeed or longjmp + * + * Copyright (C) 1994,1995 Ian Jackson + * + * This 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, + * or (at your option) any later version. + * + * This 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 dpkg; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "dpkg.h" + +/* Incremented when we do some kind of generally necessary operation, so that + * loops &c know to quit if we take an error exit. Decremented again afterwards. + */ +volatile int onerr_abort= 0; + +void *m_malloc(size_t amount) { +#ifdef MDEBUG + unsigned short *r2, x; +#endif + void *r; + + onerr_abort++; + r= malloc(amount); + if (!r) ohshite("malloc failed (%ld bytes)",(long)amount); + onerr_abort--; + +#ifdef MDEBUG + r2= r; x= (unsigned short)amount ^ 0xf000; + while (amount >= 2) { *r2++= x; amount -= 2; } +#endif + return r; +} + +void *m_realloc(void *r, size_t amount) { + onerr_abort++; + r= realloc(r,amount); + if (!r) ohshite("realloc failed (%ld bytes)",(long)amount); + onerr_abort--; + + return r; +} + +static void print_error_forked(const char *emsg, const char *contextstring) { + fprintf(stderr, "%s (subprocess): %s\n", thisname, emsg); +} + +static void cu_m_fork(int argc, void **argv) { + exit(2); + /* Don't do the other cleanups, because they'll be done by/in the parent + * process. + */ +} + +int m_fork(void) { + pid_t r; + r= fork(); + if (r == -1) { onerr_abort++; ohshite("fork failed"); } + if (r > 0) return r; + push_cleanup(cu_m_fork,~0, 0,0, 0); + set_error_display(print_error_forked,0); + return r; +} + +void m_dup2(int oldfd, int newfd) { + const char *const stdstrings[]= { "in", "out", "err" }; + + if (dup2(oldfd,newfd) == newfd) return; + + onerr_abort++; + if (newfd < 3) ohshite("failed to dup for std%s",stdstrings[newfd]); + ohshite("failed to dup for fd %d",newfd); +} + +void m_pipe(int *fds) { + if (!pipe(fds)) return; + onerr_abort++; + ohshite("failed to create pipe"); +} + +void checksubprocerr(int status, const char *description, int sigpipeok) { + int n; + if (WIFEXITED(status)) { + n= WEXITSTATUS(status); if (!n) return; + ohshit("subprocess %s returned error exit status %d",description,n); + } else if (WIFSIGNALED(status)) { + n= WTERMSIG(status); if (!n || (sigpipeok && n==SIGPIPE)) return; + ohshit("subprocess %s killed by signal (%s)%s", + description, strsignal(n), WCOREDUMP(status) ? ", core dumped" : ""); + } else { + ohshit("subprocess %s failed with wait status code %d",description,status); + } +} + +void waitsubproc(pid_t pid, const char *description, int sigpipeok) { + pid_t r; + int status; + + while ((r= waitpid(pid,&status,0)) == -1 && errno == EINTR); + if (r != pid) { onerr_abort++; ohshite("wait for %s failed",description); } + checksubprocerr(status,description,sigpipeok); +} diff --git a/lib/myopt.c b/lib/myopt.c new file mode 100644 index 00000000..b3c21673 --- /dev/null +++ b/lib/myopt.c @@ -0,0 +1,84 @@ +/* + * libdpkg - Debian packaging suite library routines + * myopt.c - my very own option parsing + * + * Copyright (C) 1994,1995 Ian Jackson + * + * This 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, + * or (at your option) any later version. + * + * This 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 file; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include + +#include "config.h" +#include "myopt.h" +#include "dpkg.h" + +void myopt(const char *const **argvp, const struct cmdinfo *cmdinfos) { + const struct cmdinfo *cip; + const char *p, *value; + int l; + + ++(*argvp); + while ((p= **argvp) && *p == '-') { + ++(*argvp); + if (!strcmp(p,"--")) break; + if (*++p == '-') { + ++p; value=0; + for (cip= cmdinfos; + cip->olong || cip->oshort; + cip++) { + if (!cip->olong) continue; + if (!strcmp(p,cip->olong)) break; + l= strlen(cip->olong); + if (!strncmp(p,cip->olong,l) && + (p[l]== ((cip->takesvalue==2) ? '-' : '='))) { value=p+l+1; break; } + } + if (!cip->olong) badusage("unknown option --%s",p); + if (cip->takesvalue) { + if (!value) { + value= *(*argvp)++; + if (!value) badusage("--%s option takes a value",cip->olong); + } + if (cip->call) cip->call(cip,value); + else *cip->sassignto= value; + } else { + if (value) badusage("--%s option does not take a value",cip->olong); + if (cip->call) cip->call(cip,0); + else *cip->iassignto= cip->arg; + } + } else { + while (*p) { + for (cip= cmdinfos; (cip->olong || cip->oshort) && *p != cip->oshort; cip++); + if (!cip->oshort) badusage("unknown option -%c",*p); + p++; + if (cip->takesvalue) { + if (!*p) { + value= *(*argvp)++; + if (!value) badusage("-%c option takes a value",cip->oshort); + } else { + value= p; p=""; + if (*value == '=') value++; + } + if (cip->call) cip->call(cip,value); + else *cip->sassignto= value; + } else { + if (*p == '=') badusage("-%c option does not take a value",cip->oshort); + if (cip->call) cip->call(cip,0); + else *cip->iassignto= cip->arg; + } + } + } + } +} diff --git a/lib/nfmalloc.c b/lib/nfmalloc.c new file mode 100644 index 00000000..225e6984 --- /dev/null +++ b/lib/nfmalloc.c @@ -0,0 +1,91 @@ +/* + * libdpkg - Debian packaging suite library routines + * nfmalloc.c - non-freeing malloc, used for in-core database + * + * Copyright (C) 1994,1995 Ian Jackson + * + * This 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, + * or (at your option) any later version. + * + * This 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 dpkg; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include + +#include "config.h" +#include "dpkg.h" +#include "dpkg-db.h" + +#define ABLOCKSIZE 65536 +#define UNIQUE 4096 + +union maxalign { + long l; long double d; + void *pv; char *pc; union maxalign *ps; void (*pf)(void); +}; + +static unsigned char *playground= 0; +static long remaining= 0; +static struct piece { struct piece *next; union maxalign space; } *pieces= 0; + +void nffreeall(void) { + struct piece *a,*b; + for (a=pieces; a; a=b) { b=a->next; free(a); } + pieces= 0; remaining= 0; +} + +static void *nfmalloc_sysmalloc(size_t size) { + struct piece *alc; + alc= m_malloc(size + sizeof(struct piece)); + alc->next= pieces; pieces= alc; + return &alc->space; +} + +#ifndef MDEBUG +void *nfmalloc(size_t size) { +#else +static void *nfmalloc_r(size_t size) { +#endif + const size_t alignment= sizeof(union maxalign); + + size -= (size + alignment-1) % alignment; + size += alignment-1; + if (size > UNIQUE) return nfmalloc_sysmalloc(size); + remaining -= size; + if (remaining > 0) return playground -= size; + playground= (unsigned char*)nfmalloc_sysmalloc(ABLOCKSIZE) + ABLOCKSIZE - size; + remaining= ABLOCKSIZE-size; + return playground; +} + +#ifdef MDEBUG +/* If we haven't switched off debugging we wrap nfmalloc in something + * that fills the space with junk that may tell us what happened if + * we dereference a wild pointer. It's better than leaving it full of + * nulls, anyway. + */ +void *nfmalloc(size_t size) { + unsigned short *r, *r2, x; + r= nfmalloc_r(size); r2=r; x= (unsigned short)size; + while (size >= 2) { *r2++= x; size -= 2; } + return r; +} +#endif + +char *nfstrsave(const char *string) { + char *r; + + r= nfmalloc(strlen(string)+1); + strcpy(r,string); + return r; +} diff --git a/lib/parse.c b/lib/parse.c new file mode 100644 index 00000000..15be97b5 --- /dev/null +++ b/lib/parse.c @@ -0,0 +1,371 @@ +/* + * libdpkg - Debian packaging suite library routines + * parse.c - database file parsing, main package/field loop + * + * Copyright (C) 1995 Ian Jackson + * + * This 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, + * or (at your option) any later version. + * + * This 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 dpkg; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include + +#include "config.h" +#include "dpkg.h" +#include "dpkg-db.h" +#include "parsedump.h" + +const struct fieldinfo fieldinfos[]= { + /* NB: capitalisation of these strings is important. */ + { "Package", f_name, w_name }, + { "Essential", f_boolean, w_booleandefno, PKGIFPOFF(essential) }, + { "Status", f_status, w_status }, + { "Priority", f_priority, w_priority }, + { "Section", f_section, w_section }, + { "Maintainer", f_charfield, w_charfield, PKGIFPOFF(maintainer) }, + { "Architecture", f_charfield, w_charfield, PKGIFPOFF(architecture) }, + { "Source", f_charfield, w_charfield, PKGIFPOFF(source) }, + { "Version", f_charfield, w_version, PKGIFPOFF(version) }, + { "Revision", f_charfield, w_null, PKGIFPOFF(revision) }, + { "Config-Version", f_configversion, w_configversion }, + { "Replaces", f_dependency, w_dependency, dep_replaces }, + { "Provides", f_dependency, w_dependency, dep_provides }, + { "Depends", f_dependency, w_dependency, dep_depends }, + { "Pre-Depends", f_dependency, w_dependency, dep_predepends }, + { "Recommends", f_dependency, w_dependency, dep_recommends }, + { "Suggests", f_dependency, w_dependency, dep_suggests }, + { "Conflicts", f_dependency, w_dependency, dep_conflicts }, + { "Conffiles", f_conffiles, w_conffiles }, + { "Filename", f_filecharf, w_filecharf, FILEFOFF(name) }, + { "Size", f_filecharf, w_filecharf, FILEFOFF(size) }, + { "MD5sum", f_filecharf, w_filecharf, FILEFOFF(md5sum) }, + { "MSDOS-Filename", f_filecharf, w_filecharf, FILEFOFF(msdosname) }, + { "Description", f_charfield, w_charfield, PKGIFPOFF(description) }, + /* Note that aliases are added to the nicknames table in parsehelp.c. */ + { 0 /* sentinel - tells code that list is ended */ } +}; +#define NFIELDS (sizeof(fieldinfos)/sizeof(struct fieldinfo)) +const int nfields= NFIELDS; + +static void cu_parsedb(int argc, void **argv) { fclose((FILE*)*argv); } + +int parsedb(const char *filename, enum parsedbflags flags, + struct pkginfo **donep, FILE *warnto, int *warncount) { + /* warnto, warncount and donep may be null. + * If donep is not null only one package's information is expected. + */ + static char readbuf[16384]; + + FILE *file; + struct pkginfo newpig, *pigp; + struct pkginfoperfile *newpifp, *pifp; + struct arbitraryfield *arp, **larpp; + struct varbuf field, value; + int lno; + int pdone; + int fieldencountered[NFIELDS]; + const struct fieldinfo *fip; + const struct nickname *nick; + const char *fieldname; + char *hyphen; + int *ip, i, c; + + if (warncount) *warncount= 0; + newpifp= (flags & pdb_recordavailable) ? &newpig.available : &newpig.installed; + file= fopen(filename,"r"); + if (!file) ohshite("failed to open package info file `%.255s' for reading",filename); + + if (!donep) /* Reading many packages, use a nice big buffer. */ + if (setvbuf(file,readbuf,_IOFBF,sizeof(readbuf))) + ohshite("unable to set buffering on status file"); + + push_cleanup(cu_parsedb,~0, 0,0, 1,(void*)file); + varbufinit(&field); varbufinit(&value); + + lno= 1; + pdone= 0; + for (;;) { /* loop per package */ + i= sizeof(fieldencountered)/sizeof(int); ip= fieldencountered; + while (i--) *ip++= 0; + blankpackage(&newpig); + blankpackageperfile(newpifp); + for (;;) { + c= getc(file); if (c!='\n' && c!=MSDOS_EOF_CHAR) break; + lno++; + } + if (c == EOF) break; + for (;;) { /* loop per field */ + varbufreset(&field); + while (c!=EOF && !isspace(c) && c!=':' && c!=MSDOS_EOF_CHAR) { + varbufaddc(&field,c); + c= getc(file); + } + varbufaddc(&field,0); + while (c != EOF && c != '\n' && isspace(c)) c= getc(file); + if (c == EOF) + parseerr(file,filename,lno, warnto,warncount,&newpig,0, + "EOF after field name `%.50s'",field.buf); + if (c == '\n') + parseerr(file,filename,lno, warnto,warncount,&newpig,0, + "newline in field name `%.50s'",field.buf); + if (c == MSDOS_EOF_CHAR) + parseerr(file,filename,lno, warnto,warncount,&newpig,0, + "MSDOS EOF (^Z) in field name `%.50s'", field.buf); + if (c != ':') + parseerr(file,filename,lno, warnto,warncount,&newpig,0, + "field name `%.50s' must be followed by colon", field.buf); + varbufreset(&value); + for (;;) { + c= getc(file); + if (c == EOF || c == '\n' || !isspace(c)) break; + } + if (c == EOF) + parseerr(file,filename,lno, warnto,warncount,&newpig,0, + "EOF before value of field `%.50s' (missing final newline)", + field.buf); + if (c == MSDOS_EOF_CHAR) + parseerr(file,filename,lno, warnto,warncount,&newpig,0, + "MSDOS EOF char in value of field `%.50s' (missing newline?)", + field.buf); + for (;;) { + if (c == '\n' || c == MSDOS_EOF_CHAR) { + lno++; + c= getc(file); + if (c == EOF || c == '\n' || !isspace(c)) break; + ungetc(c,file); + c= '\n'; + } else if (c == EOF) { + parseerr(file,filename,lno, warnto,warncount,&newpig,0, + "EOF during value of field `%.50s' (missing final newline)", + field.buf); + } + varbufaddc(&value,c); + c= getc(file); + } + while (value.used && isspace(value.buf[value.used-1])) value.used--; + varbufaddc(&value,0); + fieldname= field.buf; + for (nick= nicknames; nick->nick && strcasecmp(nick->nick,fieldname); nick++); + if (nick->nick) fieldname= nick->canon; + for (fip= fieldinfos, ip= fieldencountered; + fip->name && strcasecmp(fieldname,fip->name); + fip++, ip++); + if (fip->name) { + if (*ip++) + parseerr(file,filename,lno, warnto,warncount,&newpig,0, + "duplicate value for `%s' field", fip->name); + fip->rcall(&newpig,newpifp,flags,filename,lno-1,warnto,warncount,value.buf,fip); + } else { + if (strlen(fieldname)<2) + parseerr(file,filename,lno, warnto,warncount,&newpig,0, + "user-defined field name `%s' too short", fieldname); + larpp= &newpifp->arbs; + while ((arp= *larpp) != 0) { + if (!strcasecmp(arp->name,fieldname)) + parseerr(file,filename,lno, warnto,warncount,&newpig,0, + "duplicate value for user-defined field `%.50s'", fieldname); + larpp= &arp->next; + } + arp= nfmalloc(sizeof(struct arbitraryfield)); + arp->name= nfstrsave(fieldname); + arp->value= nfstrsave(value.buf); + arp->next= 0; + *larpp= arp; + } + if (c == EOF || c == '\n' || c == MSDOS_EOF_CHAR) break; + } /* loop per field */ + if (pdone && donep) + parseerr(file,filename,lno, warnto,warncount,&newpig,0, + "several package info entries found, only one allowed"); + parsemustfield(file,filename,lno, warnto,warncount,&newpig,0, + &newpig.name, "package name"); + if ((flags & pdb_recordavailable) || newpig.status != stat_notinstalled) { + parsemustfield(file,filename,lno, warnto,warncount,&newpig,1, + &newpifp->description, "description"); + parsemustfield(file,filename,lno, warnto,warncount,&newpig,1, + &newpifp->maintainer, "maintainer"); + parsemustfield(file,filename,lno, warnto,warncount,&newpig,1, + &newpifp->version, "version"); + } + if (flags & pdb_recordavailable) + parsemustfield(file,filename,lno, warnto,warncount,&newpig,1, + &newpifp->architecture, "architecture"); + else if (newpifp->architecture && *newpifp->architecture) + newpifp->architecture= 0; + + /* Break out the revision */ + if (newpifp->revision) { + parseerr(file,filename,lno, warnto,warncount,&newpig,1, + "obsolete `Revision' or `Package-Revision' field used"); + } else if (newpifp->version) { + hyphen= strrchr(newpifp->version,'-'); + if (hyphen) { + *hyphen++= 0; + newpifp->revision= hyphen; + } else { + newpifp->revision= nfstrsave(""); + } + } + + /* Check the Config-Version information: + * If there is a Config-Version it is definitely to be used, but + * there shouldn't be one if the package is `installed' (in which case + * the Version and/or Revision will be copied) or if the package is + * `not-installed' (in which case there is no Config-Version). + */ + if (!(flags & pdb_recordavailable)) { + if (newpig.configversion) { + if (newpig.status == stat_installed || newpig.status == stat_notinstalled) + parseerr(file,filename,lno, warnto,warncount,&newpig,0, + "Configured-Version for package with inappropriate Status"); + } else { + if (newpig.status == stat_installed) { + newpig.configversion= newpifp->version; + newpig.configrevision= newpifp->revision; + } + } + } + + pigp= findpackage(newpig.name); + pifp= (flags & pdb_recordavailable) ? &pigp->available : &pigp->installed; + if (!pifp->valid) blankpackageperfile(pifp); + + if (!(flags & pdb_preferversion) || + versioncompare(newpifp->version,newpifp->revision, + pifp->version,pifp->revision) >= 0) { + /* If we're ignoring older versions compare version numbers + * and only process this entry if it's a higher version. + */ + + /* Copy the priority and section across, but don't overwrite existing + * values if the pdb_weakclassification flag is set. + */ + if (newpig.section && *newpig.section && + !((flags & pdb_weakclassification) && pigp->section && *pigp->section)) + pigp->section= newpig.section; + if (newpig.priority != pri_unknown && + !((flags & pdb_weakclassification) && pigp->priority != pri_unknown)) { + pigp->priority= newpig.priority; + if (newpig.priority == pri_other) pigp->otherpriority= newpig.otherpriority; + } + + /* Sort out the dependency mess. */ + copy_dependency_links(pigp,&pifp->depends,newpifp->depends, + (flags & pdb_recordavailable) ? 1 : 0); + /* Leave the `depended' pointer alone, we've just gone to such + * trouble to get it right :-). The `depends' pointer in + * pifp was indeed also updated by copy_dependency_links, + * but since the value was that from newpifp anyway there's + * no need to copy it back. + */ + newpifp->depended= pifp->depended; + + /* Copy across data */ + memcpy(pifp,newpifp,sizeof(struct pkginfoperfile)); + if (!(flags & pdb_recordavailable)) { + pigp->want= newpig.want; + pigp->eflag= newpig.eflag; + pigp->status= newpig.status; + pigp->configversion= newpig.configversion; + pigp->configrevision= newpig.configrevision; + pigp->files= 0; + } else { + pigp->files= newpig.files; + } + } + if (donep) *donep= pigp; + pdone++; + if (c == EOF) break; + if (c == '\n') lno++; + } + if (ferror(file)) ohshite("failed to read from `%.255s'",filename); + pop_cleanup(0); + if (fclose(file)) ohshite("failed to close after read: `%.255s'",filename); + if (donep && !pdone) ohshit("no package information in `%.255s'",filename); + + varbuffree(&field); varbuffree(&value); + return pdone; +} + +void copy_dependency_links(struct pkginfo *pkg, + struct dependency **updateme, + struct dependency *newdepends, + int available) { + /* This routine is used to update the `reverse' dependency pointers + * when new `forwards' information has been constructed. It first + * removes all the links based on the old information. The old + * information starts in *updateme; after much brou-ha-ha + * the reverse structures are created and *updateme is set + * to the value from newdepends. + * + * Parameters are: + * pkg - the package we're doing this for. This is used to + * construct correct uplinks. + * updateme - the forwards dependency pointer that we are to + * update. This starts out containing the old forwards + * info, which we use to unthread the old reverse + * links. After we're done it is updated. + * newdepends - the value that we ultimately want to have in + * updateme. + * It is likely that the backward pointer for the package in + * question (`depended') will be updated by this routine, + * but this will happen by the routine traversing the dependency + * data structures. It doesn't need to be told where to update + * that; I just mention it as something that one should be + * cautious about. + */ + struct dependency *dyp; + struct deppossi *dop; + struct pkginfoperfile *addtopifp; + + /* Delete `backward' (`depended') links from other packages to + * dependencies listed in old version of this one. We do this by + * going through all the dependencies in the old version of this + * one and following them down to find which deppossi nodes to + * remove. + */ + for (dyp= *updateme; dyp; dyp= dyp->next) { + for (dop= dyp->list; dop; dop= dop->next) { + if (dop->backrev) + dop->backrev->nextrev= dop->nextrev; + else + if (available) + dop->ed->available.depended= dop->nextrev; + else + dop->ed->installed.depended= dop->nextrev; + if (dop->nextrev) + dop->nextrev->backrev= dop->backrev; + } + } + /* Now fill in new `ed' links from other packages to dependencies listed + * in new version of this one, and set our uplinks correctly. + */ + for (dyp= newdepends; dyp; dyp= dyp->next) { + dyp->up= pkg; + for (dop= dyp->list; dop; dop= dop->next) { + addtopifp= available ? &dop->ed->available : &dop->ed->installed; + if (!addtopifp->valid) blankpackageperfile(addtopifp); + dop->nextrev= addtopifp->depended; + dop->backrev= 0; + if (addtopifp->depended) + addtopifp->depended->backrev= dop; + addtopifp->depended= dop; + } + } + /* Finally, we fill in the new value. */ + *updateme= newdepends; +} diff --git a/lib/parsedump.h b/lib/parsedump.h new file mode 100644 index 00000000..ced6ec78 --- /dev/null +++ b/lib/parsedump.h @@ -0,0 +1,73 @@ +/* + * libdpkg - Debian packaging suite library routines + * parse.c - declarations for in-core database reading/writing + * + * Copyright (C) 1995 Ian Jackson + * + * This 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, + * or (at your option) any later version. + * + * This 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 dpkg; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef DPKG_PARSEDUMP_H +#define DPKG_PARSEDUMP_H + +struct fieldinfo; + +struct nickname { + const char *nick; + const char *canon; +}; + +extern const struct fieldinfo fieldinfos[]; +extern const struct nickname nicknames[]; +extern const int nfields; /* = elements in fieldinfos, including the sentinels */ + +#define PKGIFPOFF(f) ((char*)(&(((struct pkginfoperfile *)0x1000)->f)) - \ + (char*)(struct pkginfoperfile *)0x1000) +#define PKGPFIELD(pifp,of,type) (*(type*)((char*)(pifp)+(of))) + +#define FILEFOFF(f) ((char*)(&(((struct filedetails *)0x1000)->f)) - \ + (char*)(struct filedetails *)0x1000) +#define FILEFFIELD(filedetail,of,type) (*(type*)((char*)(filedetail)+(of))) + +typedef void freadfunction(struct pkginfo *pigp, struct pkginfoperfile *pifp, + enum parsedbflags flags, + const char *filename, int lno, FILE *warnto, int *warncount, + const char *value, const struct fieldinfo *fip); +freadfunction f_name, f_charfield, f_priority, f_section, f_status, f_filecharf; +freadfunction f_boolean, f_dependency, f_conffiles, f_configversion; + +typedef void fwritefunction(struct varbuf*, const struct pkginfo*, + const struct pkginfoperfile*, const struct fieldinfo*); +fwritefunction w_name, w_charfield, w_priority, w_section, w_status, w_configversion; +fwritefunction w_version, w_null, w_booleandefno, w_dependency, w_conffiles, w_filecharf; + +struct fieldinfo { + const char *name; + freadfunction *rcall; + fwritefunction *wcall; + int integer; +}; + +void parseerr(FILE *file, const char *filename, int lno, FILE *warnto, int *warncount, + const struct pkginfo *pigp, int warnonly, + const char *fmt, ...) PRINTFFORMAT(8,9); +void parsemustfield(FILE *file, const char *filename, int lno, + FILE *warnto, int *warncount, + const struct pkginfo *pigp, int warnonly, + char **value, const char *what); + +#define MSDOS_EOF_CHAR '\032' /* ^Z */ + +#endif /* DPKG_PARSEDUMP_H */ diff --git a/lib/parsehelp.c b/lib/parsehelp.c new file mode 100644 index 00000000..8d9c8595 --- /dev/null +++ b/lib/parsehelp.c @@ -0,0 +1,153 @@ +/* + * libdpkg - Debian packaging suite library routines + * parsehelp.c - helpful routines for parsing and writing + * + * Copyright (C) 1995 Ian Jackson + * + * This 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, + * or (at your option) any later version. + * + * This 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 dpkg; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include + +#include "config.h" +#include "dpkg.h" +#include "dpkg-db.h" +#include "parsedump.h" + +void parseerr(FILE *file, const char *filename, int lno, FILE *warnto, int *warncount, + const struct pkginfo *pigp, int warnonly, + const char *fmt, ...) { + va_list al; + char buf1[768], buf2[1000], *p, *q; + if (file && ferror(file)) ohshite("failed to read `%s' at line %d",filename,lno); + sprintf(buf1, "%s, in file `%.255s' near line %d", + warnonly ? "warning" : "parse error", filename, lno); + if (pigp && pigp->name) { + sprintf(buf2, " package `%.255s'", pigp->name); + strcat(buf1,buf2); + } + for (p=buf1, q=buf2; *p; *q++= *p++) if (*p == '%') *q++= '%'; + strcpy(q,":\n "); strcat(q,fmt); + va_start(al,fmt); + if (!warnonly) ohshitv(buf2,al); + if (warncount) (*warncount)++; + if (warnto) { + strcat(q,"\n"); + if (vfprintf(warnto,buf2,al) == EOF) + ohshite("failed to write parsing warning"); + } + va_end(al); +} + +const struct namevalue booleaninfos[]= { /* Note ! These must be in order ! */ + { "no", 0 }, + { "yes", 1 }, + { 0 } +}; + +const struct namevalue priorityinfos[]= { /* Note ! These must be in order ! */ + { "required", pri_required }, + { "important", pri_important }, + { "standard", pri_standard }, + { "recommended", pri_recommended }, /* fixme: obsolete */ + { "optional", pri_optional }, + { "extra", pri_extra }, + { "contrib", pri_contrib }, /* fixme: keep? */ + { "this is a bug - please report", pri_other }, + { "unknown", pri_unknown }, + + { "base", pri_required }, /* fixme: alias, remove */ + { 0 } +}; + +const struct namevalue statusinfos[]= { /* Note ! These must be in order ! */ + { "not-installed", stat_notinstalled }, + { "unpacked", stat_unpacked }, + { "half-configured", stat_halfconfigured }, + { "installed", stat_installed }, + { "half-installed", stat_halfinstalled }, + { "config-files", stat_configfiles }, + /* These are additional entries for reading only, in any order ... */ + { "postinst-failed", stat_halfconfigured }, /* fixme: backwards compat., remove */ + { "removal-failed", stat_halfinstalled }, /* fixme: backwards compat., remove */ + { 0 } +}; + +const struct namevalue eflaginfos[]= { /* Note ! These must be in order ! */ + { "ok", eflagv_ok }, + { "hold", eflagv_hold }, + { "reinstreq", eflagv_reinstreq }, + { "hold-reinstreq", eflagv_both }, + { 0 } +}; + +const struct namevalue wantinfos[]= { /* Note ! These must be in order ! */ + { "unknown", want_unknown }, + { "install", want_install }, + { "deinstall", want_deinstall }, + { "purge", want_purge }, + { 0 } +}; + +const char *illegal_packagename(const char *p, const char **ep) { + static const char alsoallowed[]= "-_+.@:=%"; + static char buf[150]; + int c; + + if (!*p) return "may not be empty string"; + if (!isalnum(*p)) return "must start with an alphanumeric"; + if (!*++p) return "must be at least two characters"; + while ((c= *p++)!=0) + if (!isalnum(c) && !strchr(alsoallowed,c)) break; + if (!c) return 0; + if (isspace(c) && ep) { + while (isspace(*p)) p++; + *ep= p; return 0; + } + sprintf(buf, + "character `%c' not allowed - only letters, digits and %s allowed", + c, alsoallowed); + return buf; +} + +const struct nickname nicknames[]= { + /* NB: capitalisation of these strings is important. */ + { "Recommended", "Recommends" }, + { "Optional", "Suggests" }, + { "Class", "Priority" }, + { "Package-Revision", "Revision" }, + { "Package_Revision", "Revision" }, + { 0 } +}; + +void parsemustfield(FILE *file, const char *filename, int lno, + FILE *warnto, int *warncount, + const struct pkginfo *pigp, int warnonly, + char **value, const char *what) { + if (!*value) { + parseerr(file,filename,lno, warnto,warncount,pigp,warnonly, "missing %s",what); + *value= nfstrsave(""); + } else if (!**value) { + parseerr(file,filename,lno, warnto,warncount,pigp,warnonly, + "empty value for %s",what); + } +} + +const char *skip_slash_dotslash(const char *p) { + while (p[0] == '/' || (p[0] == '.' && p[1] == '/')) p++; + return p; +} diff --git a/lib/showcright.c b/lib/showcright.c new file mode 100644 index 00000000..57de25f2 --- /dev/null +++ b/lib/showcright.c @@ -0,0 +1,35 @@ +/* + * libdpkg - Debian packaging suite library routines + * showcright.c - show copyright file routine + * + * Copyright (C) 1994,1995 Ian Jackson + * + * This 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, + * or (at your option) any later version. + * + * This 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 dpkg; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include + +#include "config.h" +#include "dpkg.h" + +void showcopyright(const struct cmdinfo *c, const char *v) { + int fd; + fd= open(COPYINGFILE,O_RDONLY); + if (fd < 0) ohshite("cannot open GPL file " COPYINGFILE); + m_dup2(fd,0); + execlp(CAT,CAT,"-",(char*)0); + ohshite("unable to exec cat for displaying GPL file"); +} diff --git a/lib/star.c b/lib/star.c new file mode 100644 index 00000000..e43ddf09 --- /dev/null +++ b/lib/star.c @@ -0,0 +1,158 @@ +#include "tarfn.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int +Read(void * userData, char * buffer, int length) +{ + /* + * If the status of the read function is < 0, it will be returned to + * the caller of TarExtractor(). + */ + return read((int)userData, buffer, length); +} + +static int +IOError(TarInfo * i) +{ + int error = errno; /* fflush() could cause errno to change */ + fflush(stdout); + fprintf(stderr, "%s: %s\n", i->Name, strerror(error)); + + /* + * The status returned by a coroutine of TarExtractor(), if it + * is non-zero, will be returned to the caller of TarExtractor(). + */ + return -2; +} + +static int +ExtractFile(TarInfo * i) +{ + /* + * If you don't want to extract the file, you must advance the tape + * by the file size rounded up to the next 512-byte boundary and + * return 0. + */ + + int fd = open(i->Name, O_CREAT|O_TRUNC|O_WRONLY, i->Mode & ~S_IFMT); + char buffer[512]; + size_t size = i->Size; + struct utimbuf t; + + if ( fd < 0 ) + return IOError(i); + + printf("File: %s\n", i->Name); + + while ( size > 0 ) { + size_t writeSize = size >= 512 ? 512 : size; + + if ( Read(i->UserData, buffer, 512) != 512 ) + return -1; /* Something wrong with archive */ + if ( write(fd, buffer, writeSize) != writeSize ) + return IOError(i); /* Write failure. */ + + size -= writeSize; + } + /* fchown() and fchmod() are cheaper than chown() and chmod(). */ + fchown(fd, i->UserID, i->GroupID); + fchmod(fd, i->Mode & ~S_IFMT); + close(fd); + t.actime = time(0); + t.modtime = i->ModTime; + utime(i->Name, &t); + return 0; +} + +static int +SetModes(TarInfo * i) +{ + struct utimbuf t; + chown(i->Name, i->UserID, i->GroupID); + chmod(i->Name, i->Mode & ~S_IFMT); + t.actime = time(0); + t.modtime = i->ModTime; + utime(i->Name, &t); + return 0; +} + +static int +MakeDirectory(TarInfo * i) +{ + printf("Directory: %s\n", i->Name); + if ( mkdir(i->Name, i->Mode & ~S_IFMT) != 0 ) { + if ( errno == EEXIST ) { + struct stat s; + if ( stat(i->Name, &s) != 0 || !(s.st_mode & S_IFDIR) ) + return IOError(i); + } + else + return IOError(i); + } + SetModes(i); + return 0; +} + +static int +MakeHardLink(TarInfo * i) +{ + printf("Hard Link: %s\n", i->Name); + + if ( link(i->LinkName, i->Name) != 0 ) + return IOError(i); + SetModes(i); + return 0; +} + +static int +MakeSymbolicLink(TarInfo * i) +{ + printf("Symbolic Link: %s\n", i->Name); + + if ( symlink(i->LinkName, i->Name) != 0 ) + return -2; + SetModes(i); + return 0; +} + +static int +MakeSpecialFile(TarInfo * i) +{ + printf("Special File: %s\n", i->Name); + + if ( mknod(i->Name, i->Mode, i->Device) != 0 ) + return -2; + SetModes(i); + return 0; +} + +static const TarFunctions functions = { + Read, + ExtractFile, + MakeDirectory, + MakeHardLink, + MakeSymbolicLink, + MakeSpecialFile +}; + +int +main(int argc, char * * argv) +{ + int status = TarExtractor((void *)0, &functions); + + if ( status == -1 ) { + fflush(stdout); + fprintf(stderr, "Error in archive format.\n"); + return -1; + } + else + return status; +} diff --git a/lib/tarfn.c b/lib/tarfn.c new file mode 100644 index 00000000..5a16a164 --- /dev/null +++ b/lib/tarfn.c @@ -0,0 +1,165 @@ +/* + * Functions for extracting tar archives. + * Bruce Perens, April-May 1995 + * Copyright (C) 1995 Bruce Perens + * This is free software under the GNU General Public License. + */ +#include +#include +#include +#include +#include +#include +#include "tarfn.h" + +struct TarHeader { + char Name[100]; + char Mode[8]; + char UserID[8]; + char GroupID[8]; + char Size[12]; + char ModificationTime[12]; + char Checksum[8]; + char LinkFlag; + char LinkName[100]; + char MagicNumber[8]; + char UserName[32]; + char GroupName[32]; + char MajorDevice[8]; + char MinorDevice[8]; +}; +typedef struct TarHeader TarHeader; + +static const unsigned int TarChecksumOffset + = (unsigned int)&(((TarHeader *)0)->Checksum); + +/* Octal-ASCII-to-long */ +static long +OtoL(const char * s, int size) +{ + int n = 0; + + while ( *s == ' ' ) { + s++; + size--; + } + + while ( --size >= 0 && *s >= '0' && *s <= '7' ) + n = (n * 010) + (*s++ - '0'); + + return n; +} + +static int +DecodeTarHeader(char * block, TarInfo * d) +{ + TarHeader * h = (TarHeader *)block; + unsigned char * s = (unsigned char *)block; + struct passwd * passwd = 0; + struct group * group = 0; + unsigned int i; + long sum; + long checksum; + + if ( *h->UserName ) + passwd = getpwnam(h->UserName); + if ( *h->GroupName ) + group = getgrnam(h->GroupName); + + d->Name = h->Name; + d->LinkName = h->LinkName; + d->Mode = (mode_t)OtoL(h->Mode, sizeof(h->Mode)); + d->Size = (size_t)OtoL(h->Size, sizeof(h->Size)); + d->ModTime = (time_t)OtoL(h->ModificationTime + ,sizeof(h->ModificationTime)); + d->Device = ((OtoL(h->MajorDevice, sizeof(h->MajorDevice)) & 0xff) << 8) + | (OtoL(h->MinorDevice, sizeof(h->MinorDevice)) & 0xff); + checksum = OtoL(h->Checksum, sizeof(h->Checksum)); + d->UserID = (uid_t)OtoL(h->UserID, sizeof(h->UserID)); + d->GroupID = (gid_t)OtoL(h->GroupID, sizeof(h->GroupID)); + d->Type = (TarFileType)h->LinkFlag; + + if ( passwd ) + d->UserID = passwd->pw_uid; + + if ( group ) + d->GroupID = group->gr_gid; + + + sum = ' ' * sizeof(h->Checksum);/* Treat checksum field as all blank */ + for ( i = TarChecksumOffset; i > 0; i-- ) + sum += *s++; + s += sizeof(h->Checksum); /* Skip the real checksum field */ + for ( i = (512 - TarChecksumOffset - sizeof(h->Checksum)); i > 0; i-- ) + sum += *s++; + + return ( sum == checksum ); +} + +extern int +TarExtractor( + void * userData +,const TarFunctions * functions) +{ + int status; + char buffer[512]; + TarInfo h; + + h.UserData = userData; + + while ( (status = functions->Read(userData, buffer, 512)) == 512 ) { + int nameLength; + + if ( !DecodeTarHeader(buffer, &h) ) { + if ( h.Name[0] == '\0' ) { + return 0; /* End of tape */ + } else { + errno = 0; /* Indicates broken tarfile */ + return -1; /* Header checksum error */ + } + } + if ( h.Name[0] == '\0' ) { + errno = 0; /* Indicates broken tarfile */ + return -1; /* Bad header data */ + } + + nameLength = strlen(h.Name); + + switch ( h.Type ) { + case NormalFile0: + case NormalFile1: + /* Compatibility with pre-ANSI ustar */ + if ( h.Name[nameLength - 1] != '/' ) { + status = (*functions->ExtractFile)(&h); + break; + } + /* Else, Fall Through */ + case Directory: + h.Name[nameLength - 1] = '\0'; + status = (*functions->MakeDirectory)(&h); + break; + case HardLink: + status = (*functions->MakeHardLink)(&h); + break; + case SymbolicLink: + status = (*functions->MakeSymbolicLink)(&h); + break; + case CharacterDevice: + case BlockDevice: + case FIFO: + status = (*functions->MakeSpecialFile)(&h); + break; + default: + errno = 0; /* Indicates broken tarfile */ + return -1; /* Bad header field */ + } + if ( status != 0 ) + return status; /* Pass on status from coroutine */ + } + if ( status > 0 ) { /* Read partial header record */ + errno = 0; /* Indicates broken tarfile */ + return -1; + } else { + return status; /* Whatever I/O function returned */ + } +} diff --git a/lib/varbuf.c b/lib/varbuf.c new file mode 100644 index 00000000..2f12db77 --- /dev/null +++ b/lib/varbuf.c @@ -0,0 +1,65 @@ +/* + * libdpkg - Debian packaging suite library routines + * varbuf.c - variable length expandable buffer handling + * + * Copyright (C) 1994,1995 Ian Jackson + * + * This 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, + * or (at your option) any later version. + * + * This 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 dpkg; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include + +#include "config.h" +#include "dpkg.h" +#include "dpkg-db.h" + +void varbufaddc(struct varbuf *v, int c) { + if (v->used >= v->size) varbufextend(v); + v->buf[v->used++]= c; +} + +void varbufaddstr(struct varbuf *v, const char *s) { + int l, ou; + l= strlen(s); + ou= v->used; + v->used += l; + if (v->used >= v->size) varbufextend(v); + memcpy(v->buf + ou, s, l); +} + +void varbufinit(struct varbuf *v) { + /* NB: dbmodify.c does its own init to get a big buffer */ + v->size= v->used= 0; + v->buf= 0; +} + +void varbufreset(struct varbuf *v) { + v->used= 0; +} + +void varbufextend(struct varbuf *v) { + int newsize; + char *newbuf; + + newsize= v->size + 80 + v->used; + newbuf= realloc(v->buf,newsize); + if (!newbuf) ohshite("failed to realloc for variable buffer"); + v->size= newsize; + v->buf= newbuf; +} + +void varbuffree(struct varbuf *v) { + free(v->buf); v->buf=0; v->size=0; v->used=0; +} diff --git a/lib/vercmp.c b/lib/vercmp.c new file mode 100644 index 00000000..a2fac63b --- /dev/null +++ b/lib/vercmp.c @@ -0,0 +1,64 @@ +/* + * libdpkg - Debian packaging suite library routines + * vercmp.c - comparison of version numbers + * + * Copyright (C) 1995 Ian Jackson + * + * This 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, + * or (at your option) any later version. + * + * This 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 dpkg; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include + +#include "config.h" +#include "dpkg.h" +#include "dpkg-db.h" +#include "parsedump.h" + +static int verrevcmp(const char *val, const char *ref) { + int vc, rc; + long vl, rl; + const char *vp, *rp; + + if (!val) val= ""; + if (!ref) ref= ""; + for (;;) { + vp= val; while (*vp && !isdigit(*vp)) vp++; + rp= ref; while (*rp && !isdigit(*rp)) rp++; + for (;;) { + vc= val == vp ? 0 : *val++; + rc= ref == rp ? 0 : *ref++; + if (!rc && !vc) break; + if (vc && !isalpha(vc)) vc += 256; /* assumes ASCII character set */ + if (rc && !isalpha(rc)) rc += 256; + if (vc != rc) return vc - rc; + } + val= vp; + ref= rp; + vl=0; if (isdigit(*vp)) vl= strtol(val,(char**)&val,10); + rl=0; if (isdigit(*rp)) rl= strtol(ref,(char**)&ref,10); + if (vl != rl) return vl - rl; + if (!*val && !*ref) return 0; + if (!*val) return -1; + if (!*ref) return +1; + } +} + +int versioncompare(const char *version, const char *revision, + const char *refversion, const char *refrevision) { + int r; + r= verrevcmp(version,refversion); if (r) return r; + return verrevcmp(revision,refrevision); +} diff --git a/main/Makefile.in b/main/Makefile.in new file mode 100644 index 00000000..9bb9a386 --- /dev/null +++ b/main/Makefile.in @@ -0,0 +1,87 @@ +# Copyright (C) 1994 Ian Murdock +# Copyright (C) 1994,1995 Ian Jackson +# +# This 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, +# or (at your option) any later version. +# +# This 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 dpkg; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +srcdir = @srcdir@ +VPATH = @srcdir@ + +prefix = @prefix@ +exec_prefix = $(prefix) +bindir = $(exec_prefix)/bin +libdir = $(prefix)/lib +mandir = $(prefix)/man +man8dir = $(mandir)/man8 +man8 = 8 + +SRC = main.c enquiry.c filesdb.c archives.c processarc.c cleanup.c \ + packages.c configure.c remove.c help.c depcon.c errors.c update.c + +OBJ = main.o enquiry.o filesdb.o archives.o processarc.o cleanup.o \ + packages.o configure.o remove.o help.o depcon.o errors.o update.o + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ + +CC = @CC@ + +CFLAGS = @CFLAGS@ @CWARNS@ -g $(XCFLAGS) +OPTCFLAGS = @OPTCFLAGS@ +LDFLAGS = $(XLDFLAGS) +LIBS = -L../lib -ldpkg $(XLIBS) +ALL_CFLAGS = -I../include -I.. @DEFS@ $(CFLAGS) +ALL_CFLAGS_OPT = $(ALL_CFLAGS) $(OPTCFLAGS) + +.SUFFIXES: .c .o + +.c.o: + $(CC) $(ALL_CFLAGS) -c $< + +all: dpkg + +dpkg: $(OBJ) ../lib/libdpkg.a + $(CC) $(LDFLAGS) -o dpkg $(OBJ) $(LIBS) + +# This next file is very heavily used, and should be optimised +# for speed rather than space. (ALL_CFLAGS_OPT usually means -O3.) +filesdb.o: filesdb.c + $(CC) $(ALL_CFLAGS_OPT) -c $< + +archtable.inc: ../archtable + perl -ne 'print " { \"$$1\",$$2\"$$3\" },\n" \ + if m/^\s*(\w+)(\s+)(\w+)\s*$$/' \ + ../archtable >archtable.inc.new + mv archtable.inc.new archtable.inc + +clean: + rm -f *.o core dpkg *.new + +distclean: clean + rm -f Makefile *.orig *~ *.~* ./#*# + rm -rf t updates status available *.old + +install: all + $(INSTALL_PROGRAM) -s dpkg $(bindir)/dpkg + $(INSTALL_DATA) dpkg.8 $(man8dir)/dpkg.$(man8) + +$(OBJ): main.h ../config.h ../include/dpkg.h ../include/dpkg-db.h +main.o: ../version.h +main.o enquiry.o errors.o update.o: ../include/myopt.h +enquiry.o: filesdb.h archtable.inc +filesdb.o: filesdb.h +packages.o remove.o configure.o: filesdb.h ../include/myopt.h +archives.o processarc.o help.o: filesdb.h ../include/myopt.h +archives.o processarc.o: archives.h ../include/tarfn.h diff --git a/main/archives.c b/main/archives.c new file mode 100644 index 00000000..bfe8de48 --- /dev/null +++ b/main/archives.c @@ -0,0 +1,706 @@ +/* + * dpkg - main program for package management + * archives.c - actions that process archive files, mainly unpack + * + * Copyright (C) 1994,1995 Ian Jackson + * + * This 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, + * or (at your option) any later version. + * + * This 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 dpkg; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "dpkg.h" +#include "dpkg-db.h" +#include "myopt.h" +#include "tarfn.h" + +#include "filesdb.h" +#include "main.h" +#include "archives.h" + +void cu_pathname(int argc, void **argv) { + ensure_pathname_nonexisting((char*)(argv[0])); +} + +void cu_backendpipe(int argc, void **argv) { + FILE *f= *(FILE**)argv[0]; + if (f) fclose(f); +} + +int tarfileread(void *ud, char *buf, int len) { + struct tarcontext *tc= (struct tarcontext*)ud; + int r; + r= fread(buf,1,len,tc->backendpipe); + if (r != len && ferror(tc->backendpipe)) + ohshite("error reading from " BACKEND " pipe"); + return r; +} + +int fnameidlu; +struct varbuf fnamevb; +struct varbuf fnametmpvb; +struct varbuf fnamenewvb; +struct packageinlist *deconfigure= 0; + +static time_t currenttime; + +static int does_replace(struct pkginfo *newpigp, + struct pkginfoperfile *newpifp, + struct pkginfo *oldpigp) { + struct dependency *dep; + + debug(dbg_depcon,"does_replace new=%s old=%s (%s)",newpigp->name, + oldpigp->name,versiondescribe(oldpigp->installed.version, + oldpigp->installed.revision)); + for (dep= newpifp->depends; dep; dep= dep->next) { + if (dep->type != dep_replaces || dep->list->ed != oldpigp) continue; + debug(dbg_depcondetail,"does_replace ... found old, version %s", + versiondescribe(dep->list->version,dep->list->revision)); + if (!versionsatisfied(&oldpigp->installed,dep->list)) continue; + debug(dbg_depcon,"does_replace ... yes"); + return 1; + } + debug(dbg_depcon,"does_replace ... no"); + return 0; +} + +static void newtarobject_utime(const char *path, struct TarInfo *ti) { + struct utimbuf utb; + utb.actime= currenttime; + utb.modtime= ti->ModTime; + if (utime(path,&utb)) + ohshite("error setting timestamps of `%.255s'",ti->Name); +} + +static void newtarobject_allmodes(const char *path, struct TarInfo *ti) { + if (chown(path,ti->UserID,ti->GroupID)) + ohshite("error setting ownership of `%.255s'",ti->Name); + if (chmod(path,ti->Mode & ~S_IFMT)) + ohshite("error setting permissions of `%.255s'",ti->Name); + newtarobject_utime(path,ti); +} + +void setupfnamevbs(const char *filename) { + fnamevb.used= fnameidlu; + varbufaddstr(&fnamevb,filename); + varbufaddc(&fnamevb,0); + + fnametmpvb.used= fnameidlu; + varbufaddstr(&fnametmpvb,filename); + varbufaddstr(&fnametmpvb,DPKGTEMPEXT); + varbufaddc(&fnametmpvb,0); + + fnamenewvb.used= fnameidlu; + varbufaddstr(&fnamenewvb,filename); + varbufaddstr(&fnamenewvb,DPKGNEWEXT); + varbufaddc(&fnamenewvb,0); + + debug(dbg_eachfiledetail, "setupvnamevbs main=`%s' tmp=`%s' new=`%s'", + fnamevb.buf, fnametmpvb.buf, fnamenewvb.buf); +} + +int unlinkorrmdir(const char *filename) { + /* Returns 0 on success or -1 on failure, just like unlink & rmdir */ + int r, e; + + if (!rmdir(filename)) { + debug(dbg_eachfiledetail,"unlinkorrmdir `%s' rmdir OK",filename); + return 0; + } + + if (errno != ENOTDIR) { + e= errno; + debug(dbg_eachfiledetail,"unlinkorrmdir `%s' rmdir %s",filename,strerror(e)); + errno= e; return -1; + } + + r= unlink(filename); e= errno; + debug(dbg_eachfiledetail,"unlinkorrmdir `%s' unlink %s", + filename, r ? strerror(e) : "OK"); + errno= e; return r; +} + +int tarobject(struct TarInfo *ti) { + static struct varbuf conffderefn, hardlinkfn, symlinkfn; + const char *usename; + + struct tarcontext *tc= (struct tarcontext*)ti->UserData; + int statr, fd, r, i, existingdirectory; + struct stat stab, stabd; + size_t sz, wsz; + FILE *thefile; + char databuf[TARBLKSZ]; + struct fileinlist *nifd; + struct pkginfo *divpkg, *otherpkg; + struct filepackages *packageslump; + + /* Append to list of files. + * The trailing / put on the end of names in tarfiles has already + * been stripped by TarExtractor (lib/tarfn.c). + */ + nifd= m_malloc(sizeof(struct fileinlist)); + nifd->namenode= findnamenode(ti->Name); + nifd->next= 0; *tc->newfilesp= nifd; tc->newfilesp= &nifd->next; + nifd->namenode->flags |= fnnf_new_inarchive; + + debug(dbg_eachfile, + "tarobject ti->Name=`%s' Mode=%lo owner=%u.%u Type=%d(%c)" + " ti->LinkName=`%s' namenode=`%s' flags=%o instead=`%s'", + ti->Name, (long)ti->Mode, (unsigned)ti->UserID, (unsigned)ti->GroupID, ti->Type, + ti->Type == '\0' ? '_' : + ti->Type >= '0' && ti->Type <= '6' ? "-hlcbdp"[ti->Type - '0'] : '?', + ti->LinkName, + nifd->namenode->name, nifd->namenode->flags, + nifd->namenode->divert && nifd->namenode->divert->useinstead + ? nifd->namenode->divert->useinstead->name : ""); + + if (nifd->namenode->divert && nifd->namenode->divert->camefrom) { + divpkg= nifd->namenode->divert->pkg; + forcibleerr(fc_overwritediverted, + "trying to overwrite `%.250s', which is the " + "diverted version of `%.250s'%.10s%.100s%.10s", + nifd->namenode->name, + nifd->namenode->divert->camefrom->name, + divpkg ? " (package: " : "", + divpkg ? divpkg->name : "", + divpkg ? ")" : ""); + } + + usename= namenodetouse(nifd->namenode,tc->pkg)->name + 1; /* Skip the leading `/' */ + + if (nifd->namenode->flags & fnnf_new_conff) { + /* If it's a conffile we have to extract it next to the installed + * version (ie, we do the usual link-following). + */ + if (conffderef(tc->pkg, &conffderefn, usename)) + usename= conffderefn.buf; + debug(dbg_conff,"tarobject fnnf_new_conff deref=`%s'",usename); + } + + setupfnamevbs(usename); + + statr= lstat(fnamevb.buf,&stab); + if (statr) { + /* The lstat failed. */ + if (errno != ENOENT && errno != ENOTDIR) + ohshite("unable to stat `%.255s' (which I was about to install)",ti->Name); + /* OK, so it doesn't exist. + * However, it's possible that we were in the middle of some other + * backup/restore operation and were rudely interrupted. + * So, we see if we have .dpkg-tmp, and if so we restore it. + */ + if (rename(fnametmpvb.buf,fnamevb.buf)) { + if (errno != ENOENT && errno != ENOTDIR) + ohshite("unable to clean up mess surrounding `%.255s' before " + "installing another version",ti->Name); + debug(dbg_eachfiledetail,"tarobject nonexistent"); + } else { + debug(dbg_eachfiledetail,"tarobject restored tmp to main"); + statr= lstat(fnamevb.buf,&stab); + if (statr) ohshite("unable to stat restored `%.255s' before installing" + " another version", ti->Name); + } + } else { + debug(dbg_eachfiledetail,"tarobject already exists"); + } + + /* Check to see if it's a directory or link to one and we don't need to + * do anything. This has to be done now so that we don't die due to + * a file overwriting conflict. + */ + existingdirectory= 0; + switch (ti->Type) { + case SymbolicLink: + /* If it's already an existing directory, do nothing. */ + if (!statr && S_ISDIR(stab.st_mode)) { + debug(dbg_eachfiledetail,"tarobject SymbolicLink exists as directory"); + existingdirectory= 1; + } + break; + case Directory: + /* If it's already an existing directory, do nothing. */ + if (!stat(fnamevb.buf,&stabd) && S_ISDIR(stabd.st_mode)) { + debug(dbg_eachfiledetail,"tarobject Directory exists"); + existingdirectory= 1; + } + break; + case NormalFile0: case NormalFile1: + case CharacterDevice: case BlockDevice: + case HardLink: + break; + default: + ohshit("archive contained object `%.255s' of unknown type 0x%x",ti->Name,ti->Type); + } + + if (!existingdirectory) { + for (packageslump= nifd->namenode->packages; + packageslump; + packageslump= packageslump->more) { + for (i=0; i < PERFILEPACKAGESLUMP && packageslump->pkgs[i]; i++) { + otherpkg= packageslump->pkgs[i]; + if (otherpkg == tc->pkg) continue; + debug(dbg_eachfile, "tarobject ... found in %s",otherpkg->name); + if (nifd->namenode->divert && nifd->namenode->divert->useinstead) { + /* Right, so we may be diverting this file. This makes the conflict + * OK iff one of us is the diverting package (we don't need to + * check for both being the diverting package, obviously). + */ + divpkg= nifd->namenode->divert->pkg; + debug(dbg_eachfile, "tarobject ... diverted, divpkg=%s\n",divpkg->name); + if (otherpkg == divpkg || tc->pkg == divpkg) continue; + } + /* Nope ? Hmm, file conflict, perhaps. Check Replaces. */ + if (otherpkg->clientdata->replacingfilesandsaid) continue; + /* Perhaps we're removing a conflicting package ? */ + if (otherpkg->clientdata->istobe == itb_remove) continue; + if (does_replace(tc->pkg,&tc->pkg->available,otherpkg)) { + printf("Replacing files in old package %s ...\n",otherpkg->name); + otherpkg->clientdata->replacingfilesandsaid= 1; + } else { + forcibleerr(fc_overwrite, + "trying to overwrite `%.250s', which is also in package %.250s", + nifd->namenode->name,otherpkg->name); + } + } + } + } + + /* Now, at this stage we want to make sure neither of .dpkg-new and .dpkg-tmp + * are hanging around. + */ + ensure_pathname_nonexisting(fnamenewvb.buf); + ensure_pathname_nonexisting(fnametmpvb.buf); + + if (existingdirectory) return 0; + + /* Now we start to do things that we need to be able to undo + * if something goes wrong. + */ + push_cleanup(cu_installnew,~ehflag_normaltidy, 0,0, 1,(void*)nifd); + + /* Extract whatever it is as .dpkg-new ... */ + switch (ti->Type) { + case NormalFile0: case NormalFile1: + fd= open(fnamenewvb.buf, O_CREAT|O_EXCL|O_WRONLY, + ti->Mode & (S_IRUSR|S_IRGRP|S_IROTH)); + if (fd < 0) ohshite("unable to create `%.255s'",ti->Name); + thefile= fdopen(fd,"w"); + if (!thefile) { close(fd); ohshite("unable to fdopen for `%.255s'",ti->Name); } + push_cleanup(cu_closefile,ehflag_bombout, 0,0, 1,(void*)thefile); + debug(dbg_eachfiledetail,"tarobject NormalFile[01] open size=%lu", + (unsigned long)ti->Size); + for (sz= ti->Size; sz > 0; sz -= wsz) { + wsz= sz > TARBLKSZ ? TARBLKSZ : sz; + r= fread(databuf,1,TARBLKSZ,tc->backendpipe); + if (rbackendpipe)) { + ohshite("error reading " BACKEND " during `%.255s'",ti->Name); + } else { + errno= 0; + return -1; + } + } + if (fwrite(databuf,1,wsz,thefile) != wsz) + ohshite("error writing to `%.255s'",ti->Name); + } + if (fchown(fd,ti->UserID,ti->GroupID)) + ohshite("error setting ownership of `%.255s'",ti->Name); + if (fchmod(fd,ti->Mode & ~S_IFMT)) + ohshite("error setting permissions of `%.255s'",ti->Name); + pop_cleanup(ehflag_normaltidy); /* thefile= fdopen(fd) */ + if (fclose(thefile)) + ohshite("error closing/writing `%.255s'",ti->Name); + newtarobject_utime(fnamenewvb.buf,ti); + break; + case CharacterDevice: case BlockDevice: + if (mknod(fnamenewvb.buf,ti->Mode & S_IFMT,ti->Device)) + ohshite("error creating device `%.255s'",ti->Name); + debug(dbg_eachfiledetail,"tarobject CharacterDevice|BlockDevice"); + newtarobject_allmodes(fnamenewvb.buf,ti); + break; + case HardLink: + varbufreset(&hardlinkfn); + varbufaddstr(&hardlinkfn,instdir); varbufaddc(&hardlinkfn,'/'); + varbufaddstr(&hardlinkfn,ti->LinkName); varbufaddc(&hardlinkfn,0); + if (link(hardlinkfn.buf,fnamenewvb.buf)) + ohshite("error creating hard link `%.255s'",ti->Name); + debug(dbg_eachfiledetail,"tarobject HardLink"); + newtarobject_allmodes(fnamenewvb.buf,ti); + break; + case SymbolicLink: + /* We've already cheched for an existing directory. */ + if (symlink(ti->LinkName,fnamenewvb.buf)) + ohshite("error creating symbolic link `%.255s'",ti->Name); + debug(dbg_eachfiledetail,"tarobject SymbolicLink creating"); + if (chown(fnamenewvb.buf,ti->UserID,ti->GroupID)) + ohshite("error setting ownership of symlink `%.255s'",ti->Name); + break; + case Directory: + /* We've already checked for an existing directory. */ + if (mkdir(fnamenewvb.buf, + ti->Mode & (S_IRUSR|S_IRGRP|S_IROTH | S_IXUSR|S_IXGRP|S_IXOTH))) + ohshite("error creating directory `%.255s'",ti->Name); + debug(dbg_eachfiledetail,"tarobject Directory creating"); + newtarobject_allmodes(fnamenewvb.buf,ti); + break; + default: + internerr("bad tar type, but already checked"); + } + /* + * Now we have extracted the new object in .dpkg-new (or, if the + * file already exists as a directory and we were trying to extract + * a directory or symlink, we returned earlier, so we don't need + * to worry about that here). + */ + + /* First, check to see if it's a conffile. If so we don't install + * it now - we leave it in .dpkg-new for --configure to take care of + */ + if (nifd->namenode->flags & fnnf_new_conff) { + debug(dbg_conffdetail,"tarobject conffile extracted"); + nifd->namenode->flags |= fnnf_elide_other_lists; + return 0; + } + + /* Now we install it. If we can do an atomic overwrite we do so. + * If not we move aside the old file and then install the new. + * The backup file will be deleted later. + */ + if (statr) { /* Don't try to back it up if it didn't exist. */ + debug(dbg_eachfiledetail,"tarobject new - no backup"); + } else { + if (ti->Type == Directory || S_ISDIR(stab.st_mode)) { + /* One of the two is a directory - can't do atomic install. */ + debug(dbg_eachfiledetail,"tarobject directory, nonatomic"); + nifd->namenode->flags |= fnnf_no_atomic_overwrite; + if (rename(fnamevb.buf,fnametmpvb.buf)) + ohshite("unable to move aside `%.255s' to install new version",ti->Name); + } else if (S_ISLNK(stab.st_mode)) { + /* We can't make a symlink with two hardlinks, so we'll have to copy it. + * (Pretend that making a copy of a symlink is the same as linking to it.) + */ + varbufreset(&symlinkfn); + do { + varbufextend(&symlinkfn); + r= readlink(fnamevb.buf,symlinkfn.buf,symlinkfn.size); + if (r<0) ohshite("unable to read link `%.255s'",ti->Name); + } while (r == symlinkfn.size); + symlinkfn.used= r; varbufaddc(&symlinkfn,0); + if (symlink(symlinkfn.buf,fnametmpvb.buf)) + ohshite("unable to make backup symlink for `%.255s'",ti->Name); + if (chown(fnametmpvb.buf,stab.st_uid,stab.st_gid)) + ohshite("unable to chown backup symlink for `%.255s'",ti->Name); + } else { + debug(dbg_eachfiledetail,"tarobject nondirectory, `link' backup"); + if (link(fnamevb.buf,fnametmpvb.buf)) + ohshite("unable to make backup link of `%.255s' before installing new version", + ti->Name); + } + } + + if (rename(fnamenewvb.buf,fnamevb.buf)) + ohshite("unable to install new version of `%.255s'",ti->Name); + + nifd->namenode->flags |= fnnf_elide_other_lists; + + debug(dbg_eachfiledetail,"tarobject done and installed"); + return 0; +} + +static int try_remove_can(struct deppossi *pdep, + struct pkginfo *fixbyrm, + const char *why) { + struct packageinlist *newdeconf; + + if (force_depends(pdep)) { + fprintf(stderr, DPKG ": warning - " + "ignoring dependency problem with removal of %s:\n%s", + fixbyrm->name, why); + return 1; + } else if (f_autodeconf) { + if (pdep->up->up->installed.essential) { + if (fc_removeessential) { + fprintf(stderr, DPKG ": warning - considering deconfiguration of essential\n" + " package %s, to enable removal of %s.\n", + pdep->up->up->name,fixbyrm->name); + } else { + fprintf(stderr, DPKG ": no, %s is essential, will not deconfigure\n" + " it in order to enable removal of %s.\n", + pdep->up->up->name,fixbyrm->name); + return 0; + } + } + pdep->up->up->clientdata->istobe= itb_deconfigure; + newdeconf= m_malloc(sizeof(struct packageinlist)); + newdeconf->next= deconfigure; + newdeconf->pkg= pdep->up->up; + deconfigure= newdeconf; + return 1; + } else { + fprintf(stderr, DPKG ": no, cannot remove %s (--auto-deconfigure will help):\n%s", + fixbyrm->name, why); + return 0; + } +} + +void check_conflict(struct dependency *dep, struct pkginfo *pkg, + const char *pfilename, struct pkginfo **conflictorp) { + struct pkginfo *fixbyrm; + struct deppossi *pdep, flagdeppossi; + struct varbuf conflictwhy, removalwhy; + struct dependency *providecheck; + + varbufinit(&conflictwhy); + varbufinit(&removalwhy); + + fixbyrm= 0; + if (depisok(dep, &conflictwhy, *conflictorp ? 0 : &fixbyrm, 0)) { + varbuffree(&conflictwhy); + varbuffree(&removalwhy); + return; + } + if (fixbyrm && + ((pkg->available.essential && fixbyrm->installed.essential) || + ((fixbyrm->want != want_install || does_replace(pkg,&pkg->available,fixbyrm)) && + (!fixbyrm->installed.essential || fc_removeessential)))) { + ensure_package_clientdata(fixbyrm); + assert(fixbyrm->clientdata->istobe == itb_normal); + fixbyrm->clientdata->istobe= itb_remove; + fprintf(stderr, DPKG ": considering removing %s in favour of %s ...\n", + fixbyrm->name, pkg->name); + if (fixbyrm->status != stat_installed) { + fprintf(stderr, + "%s is not properly installed - ignoring any dependencies on it.\n", + fixbyrm->name); + pdep= 0; + } else { + for (pdep= fixbyrm->installed.depended; + pdep; + pdep= pdep->nextrev) { + if (pdep->up->type != dep_depends && pdep->up->type != dep_predepends) continue; + if (depisok(pdep->up, &removalwhy, 0,0)) continue; + varbufaddc(&removalwhy,0); + if (!try_remove_can(pdep,fixbyrm,removalwhy.buf)) + break; + } + if (!pdep) { + /* If we haven't found a reason not to yet, let's look some more. */ + for (providecheck= fixbyrm->installed.depends; + providecheck; + providecheck= providecheck->next) { + if (providecheck->type != dep_provides) continue; + for (pdep= providecheck->list->ed->installed.depended; + pdep; + pdep= pdep->nextrev) { + if (pdep->up->type != dep_depends && pdep->up->type != dep_predepends) + continue; + if (depisok(pdep->up, &removalwhy, 0,0)) continue; + varbufaddc(&removalwhy,0); + fprintf(stderr, DPKG + ": may have trouble removing %s, as it provides %s ...\n", + fixbyrm->name, providecheck->list->ed->name); + if (!try_remove_can(pdep,fixbyrm,removalwhy.buf)) + goto break_from_both_loops_at_once; + } + } + break_from_both_loops_at_once:; + } + } + if (!pdep && skip_due_to_hold(fixbyrm)) { + pdep= &flagdeppossi; + } + if (!pdep && (fixbyrm->eflag & eflagf_reinstreq)) { + if (fc_removereinstreq) { + fprintf(stderr, DPKG ": package %s requires reinstallation, but will" + " remove anyway as you request.\n", fixbyrm->name); + } else { + fprintf(stderr, DPKG ": package %s requires reinstallation, will not remove.\n", + fixbyrm->name); + pdep= &flagdeppossi; + } + } + if (!pdep) { + /* This conflict is OK - we'll remove the conflictor. */ + *conflictorp= fixbyrm; + varbuffree(&conflictwhy); varbuffree(&removalwhy); + fprintf(stderr, DPKG ": yes, will remove %s in favour of %s.\n", + fixbyrm->name, pkg->name); + return; + } + fixbyrm->clientdata->istobe= itb_normal; /* put it back */ + } + varbufaddc(&conflictwhy,0); + fprintf(stderr, DPKG ": regarding %s containing %s:\n%s", + pfilename, pkg->name, conflictwhy.buf); + if (!force_conflicts(dep->list)) + ohshit("conflicting packages - not installing %.250s",pkg->name); + fprintf(stderr, DPKG ": warning - ignoring conflict, may proceed anyway !\n"); + varbuffree(&conflictwhy); + + return; +} + +void cu_cidir(int argc, void **argv) { + char *cidir= (char*)argv[0]; + char *cidirrest= (char*)argv[1]; + *cidirrest= 0; + ensure_pathname_nonexisting(cidir); +} + +void cu_fileslist(int argc, void **argv) { + struct fileinlist **headp= (struct fileinlist**)argv[0]; + struct fileinlist *current, *next; + for (current= *headp; current; current= next) { + next= current->next; + free(current); + } +} + +void archivefiles(const char *const *argv) { + const char *volatile thisarg; + const char *const *volatile argp; + jmp_buf ejbuf; + int pi[2], fc, nfiles, c, i; + FILE *pf; + static struct varbuf findoutput; + const char **arglist; + char *p; + + if (f_recursive) { + + if (!*argv) + badusage("--%s --recursive needs at least one path argument",cipaction->olong); + + m_pipe(pi); + if (!(fc= m_fork())) { + const char *const *ap; + int i; + m_dup2(pi[1],1); close(pi[0]); + for (i=0, ap=argv; *ap; ap++, i++); + arglist= m_malloc(sizeof(char*)*(i+15)); + arglist[0]= FIND; + for (i=1, ap=argv; *ap; ap++, i++) { + if (strchr(FIND_EXPRSTARTCHARS,(*ap)[0])) { + char *a; + a= m_malloc(strlen(*ap)+10); + strcpy(a,"./"); + strcat(a,*ap); + arglist[i]= a; + } else { + arglist[i]= *ap; + } + } + arglist[i++]= "-follow"; /* When editing these, make sure that */ + arglist[i++]= "-name"; /* arglist is mallocd big enough, above. */ + arglist[i++]= ARCHIVE_FILENAME_PATTERN; + arglist[i++]= "-type"; + arglist[i++]= "f"; + arglist[i++]= "-print0"; + arglist[i++]= 0; + execvp(FIND, (char* const*)arglist); + ohshite("failed to exec " FIND " for --recursive"); + } + + nfiles= 0; + pf= fdopen(pi[0],"r"); if (!pf) ohshite("failed to fdopen find's pipe"); + close(pi[1]); + varbufreset(&findoutput); + while ((c= fgetc(pf)) != EOF) { + varbufaddc(&findoutput,c); + if (!c) nfiles++; + } + if (ferror(pf)) ohshite("error reading find's pipe"); + if (fclose(pf)) ohshite("error closing find's pipe"); + waitsubproc(fc,"find",0); + + if (!nfiles) ohshit("searched, but found no packages (files matching " + ARCHIVE_FILENAME_PATTERN ")"); + + varbufaddc(&findoutput,0); + varbufaddc(&findoutput,0); + + arglist= m_malloc(sizeof(char*)*(nfiles+1)); + p= findoutput.buf; i=0; + while (*p) { + arglist[i++]= p; + while ((c= *p++) != 0); + } + arglist[i]= 0; + argp= arglist; + + } else { + + if (!*argv) badusage("--%s needs at least one package archive file argument", + cipaction->olong); + argp= argv; + + } + + modstatdb_init(admindir, + f_noact ? msdbrw_readonly + : cipaction->arg == act_avail ? msdbrw_write + : msdbrw_needsuperuser); + + currenttime= time(0); + + varbufaddstr(&fnamevb,instdir); varbufaddc(&fnamevb,'/'); + varbufaddstr(&fnametmpvb,instdir); varbufaddc(&fnametmpvb,'/'); + varbufaddstr(&fnamenewvb,instdir); varbufaddc(&fnamenewvb,'/'); + fnameidlu= fnamevb.used; + + ensure_diversions(); + + while ((thisarg= *argp++) != 0) { + if (setjmp(ejbuf)) { + error_unwind(ehflag_bombout); + if (onerr_abort > 0) break; + continue; + } + push_error_handler(&ejbuf,print_error_perpackage,thisarg); + process_archive(thisarg); + onerr_abort++; + if (ferror(stdout)) werr("stdout"); + if (ferror(stderr)) werr("stderr"); + onerr_abort--; + set_error_display(0,0); + error_unwind(ehflag_normaltidy); + } + + switch (cipaction->arg) { + case act_install: + case act_configure: + case act_remove: + case act_purge: + process_queue(); + case act_unpack: + case act_avail: + break; + default: + internerr("unknown action"); + } + + modstatdb_shutdown(); +} diff --git a/main/archives.h b/main/archives.h new file mode 100644 index 00000000..00babbee --- /dev/null +++ b/main/archives.h @@ -0,0 +1,65 @@ +/* + * dpkg - main program for package management + * archives.h - functions common to archives.c and processarc.c + * + * Copyright (C) 1995 Ian Jackson + * + * This 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, + * or (at your option) any later version. + * + * This 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 dpkg; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef ARCHIVES_H +#define ARCHIVES_H + +struct tarcontext { + FILE *backendpipe; + struct pkginfo *pkg; + struct fileinlist **newfilesp; +}; + +extern int fnameidlu; +extern struct varbuf fnamevb; +extern struct varbuf fnametmpvb; +extern struct varbuf fnamenewvb; +extern struct packageinlist *deconfigure; + +void cu_pathname(int argc, void **argv); +void cu_cidir(int argc, void **argv); +void cu_fileslist(int argc, void **argv); +void cu_backendpipe(int argc, void **argv); + +void cu_installnew(int argc, void **argv); + +void cu_prermupgrade(int argc, void **argv); +void cu_prerminfavour(int argc, void **argv); +void cu_preinstverynew(int argc, void **argv); +void cu_preinstnew(int argc, void **argv); +void cu_preinstupgrade(int argc, void **argv); +void cu_postrmupgrade(int argc, void **argv); + +void cu_prermdeconfigure(int argc, void **argv); +void ok_prermdeconfigure(int argc, void **argv); + +void setupfnamevbs(const char *filename); +int unlinkorrmdir(const char *filename); + +int tarobject(struct TarInfo *ti); +int tarfileread(void *ud, char *buf, int len); + +void check_conflict(struct dependency *dep, struct pkginfo *pkg, + const char *pfilename, struct pkginfo **conflictorp); + +extern int cleanup_pkg_failed, cleanup_conflictor_failed; + +#endif /* ARCHIVES_H */ diff --git a/main/archtable.inc b/main/archtable.inc new file mode 100644 index 00000000..4a57a0b7 --- /dev/null +++ b/main/archtable.inc @@ -0,0 +1,8 @@ + { "i386", "i386" }, + { "i486", "i386" }, + { "i586", "i386" }, + { "sparc", "sparc" }, + { "alpha", "alpha" }, + { "m68k", "m68k" }, + { "arm", "arm" }, + { "ppc", "ppc" }, diff --git a/main/cleanup.c b/main/cleanup.c new file mode 100644 index 00000000..b12e3cf1 --- /dev/null +++ b/main/cleanup.c @@ -0,0 +1,226 @@ +/* + * dpkg - main program for package management + * cleanup.c - cleanup functions, used when we need to unwind + * + * Copyright (C) 1995 Ian Jackson + * + * This 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, + * or (at your option) any later version. + * + * This 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 dpkg; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "dpkg.h" +#include "dpkg-db.h" +#include "myopt.h" +#include "tarfn.h" + +#include "filesdb.h" +#include "main.h" +#include "archives.h" + +int cleanup_pkg_failed=0, cleanup_conflictor_failed=0; + +void cu_installnew(int argc, void **argv) { + /* Something went wrong and we're undoing. + * We have the following possible situations for non-conffiles: + * .dpkg-tmp exists - in this case we want to remove + * if it exists and replace it with .dpkg-tmp. + * This undoes the backup operation. We also make sure + * we delete .dpkg-new in case that's still hanging around. + * .dpkg-tmp does not exist - in this case we haven't + * got as far as creating it (or there wasn't an old version). + * In this case we just delete .dpkg-new if it exists, + * as it may be a half-extracted thing. + * For conffiles, we simply delete .dpkg-new. For these, + * .dpkg-tmp shouldn't exist, as we don't make a backup + * at this stage. Just to be on the safe side, though, we don't + * look for it. + */ + struct fileinlist *nifd= (struct fileinlist*)argv[0]; + struct filenamenode *namenode; + struct stat stab; + + cleanup_pkg_failed++; cleanup_conflictor_failed++; + + namenode= nifd->namenode; + debug(dbg_eachfile,"cu_installnew `%s' flags=%o",namenode->name,namenode->flags); + + setupfnamevbs(namenode->name); + + if (!(namenode->flags & fnnf_new_conff) && !lstat(fnametmpvb.buf,&stab)) { + /* OK, .dpkg-tmp exists. Remove and + * restore .dpkg-tmp ... + */ + if (namenode->flags & fnnf_no_atomic_overwrite) { + /* If we can't do an atomic overwrite we have to delete first any + * link to the new version we may have created. + */ + debug(dbg_eachfiledetail,"cu_installnew restoring nonatomic"); + if (unlinkorrmdir(fnamevb.buf) && errno != ENOENT && errno != ENOTDIR) + ohshite("unable to remove newly-installed version of `%.250s' to allow" + " reinstallation of backup copy",namenode->name); + } else { + debug(dbg_eachfiledetail,"cu_installnew restoring atomic"); + } + /* Either we can do an atomic restore, or we've made room: */ + if (rename(fnametmpvb.buf,fnamevb.buf)) + ohshite("unable to restore backup version of `%.250s'",namenode->name); + } else { + debug(dbg_eachfiledetail,"cu_installnew not restoring"); + } + /* Whatever, we delete .dpkg-new now, if it still exists. */ + if (unlinkorrmdir(fnamenewvb.buf) && errno != ENOENT && errno != ENOTDIR) + ohshite("unable to remove newly-extracted version of `%.250s'",namenode->name); + + cleanup_pkg_failed--; cleanup_conflictor_failed--; +} + +void cu_prermupgrade(int argc, void **argv) { + struct pkginfo *pkg= (struct pkginfo*)argv[0]; + + if (cleanup_pkg_failed++) return; + maintainer_script_installed(pkg,POSTINSTFILE,"post-installation", + "abort-upgrade", + versiondescribe(pkg->available.version, + pkg->available.revision), + (char*)0); + pkg->status= stat_installed; + pkg->eflag &= ~eflagf_reinstreq; + modstatdb_note(pkg); + cleanup_pkg_failed--; +} + +void ok_prermdeconfigure(int argc, void **argv) { + struct pkginfo *deconf= (struct pkginfo*)argv[0]; + /* also has conflictor in argv[1] and infavour in argv[2] */ + + if (cipaction->arg == act_install) + add_to_queue(deconf); +} + +void cu_prermdeconfigure(int argc, void **argv) { + struct pkginfo *deconf= (struct pkginfo*)argv[0]; + struct pkginfo *conflictor= (struct pkginfo*)argv[1]; + struct pkginfo *infavour= (struct pkginfo*)argv[2]; + + maintainer_script_installed(deconf,POSTINSTFILE,"post-installation", + "abort-deconfigure", "in-favour", infavour->name, + versiondescribe(infavour->available.version, + infavour->available.revision), + "removing", conflictor->name, + versiondescribe(conflictor->installed.version, + conflictor->installed.revision), + (char*)0); + deconf->status= stat_installed; + modstatdb_note(deconf); +} + +void cu_prerminfavour(int argc, void **argv) { + struct pkginfo *conflictor= (struct pkginfo*)argv[0]; + struct pkginfo *infavour= (struct pkginfo*)argv[1]; + + if (cleanup_conflictor_failed++) return; + maintainer_script_installed(conflictor,POSTINSTFILE,"post-installation", + "abort-remove", "in-favour", infavour->name, + versiondescribe(infavour->available.version, + infavour->available.revision), + (char*)0); + conflictor->status= stat_installed; + conflictor->eflag &= ~eflagf_reinstreq; + modstatdb_note(conflictor); + cleanup_conflictor_failed--; +} + +void cu_preinstverynew(int argc, void **argv) { + struct pkginfo *pkg= (struct pkginfo*)argv[0]; + char *cidir= (char*)argv[1]; + char *cidirrest= (char*)argv[2]; + + if (cleanup_pkg_failed++) return; + maintainer_script_new(POSTRMFILE,"post-removal",cidir,cidirrest, + "abort-install",(char*)0); + pkg->status= stat_notinstalled; + pkg->eflag &= ~eflagf_reinstreq; + modstatdb_note(pkg); + cleanup_pkg_failed--; +} + +void cu_preinstnew(int argc, void **argv) { + struct pkginfo *pkg= (struct pkginfo*)argv[0]; + char *cidir= (char*)argv[1]; + char *cidirrest= (char*)argv[2]; + + if (cleanup_pkg_failed++) return; + maintainer_script_new(POSTRMFILE,"post-removal",cidir,cidirrest, + "abort-install", versiondescribe(pkg->installed.version, + pkg->installed.revision), + (char*)0); + pkg->status= stat_configfiles; + pkg->eflag &= ~eflagf_reinstreq; + modstatdb_note(pkg); + cleanup_pkg_failed--; +} + +void cu_preinstupgrade(int argc, void **argv) { + struct pkginfo *pkg= (struct pkginfo*)argv[0]; + char *cidir= (char*)argv[1]; + char *cidirrest= (char*)argv[2]; + enum pkgstatus *oldstatusp= (enum pkgstatus*)argv[3]; + + if (cleanup_pkg_failed++) return; + maintainer_script_new(POSTRMFILE,"post-removal",cidir,cidirrest, + "abort-upgrade", + versiondescribe(pkg->installed.version, + pkg->installed.revision), + (char*)0); + pkg->status= *oldstatusp; + pkg->eflag &= ~eflagf_reinstreq; + modstatdb_note(pkg); + cleanup_pkg_failed--; +} + +void cu_postrmupgrade(int argc, void **argv) { + struct pkginfo *pkg= (struct pkginfo*)argv[0]; + + if (cleanup_pkg_failed++) return; + maintainer_script_installed(pkg,PREINSTFILE,"pre-installation", + "abort-upgrade", versiondescribe(pkg->available.version, + pkg->available.revision), + (char*)0); + cleanup_pkg_failed--; +} + +void cu_prermremove(int argc, void **argv) { + struct pkginfo *pkg= (struct pkginfo*)argv[0]; + + if (cleanup_pkg_failed++) return; + maintainer_script_installed(pkg,POSTINSTFILE,"post-installation", + "abort-remove", (char*)0); + pkg->status= stat_installed; + pkg->eflag &= ~eflagf_reinstreq; + modstatdb_note(pkg); + cleanup_pkg_failed--; +} diff --git a/main/configure.c b/main/configure.c new file mode 100644 index 00000000..6ec20cfe --- /dev/null +++ b/main/configure.c @@ -0,0 +1,516 @@ +/* + * dpkg - main program for package management + * configure.c - configure packages + * + * Copyright (C) 1995 Ian Jackson + * + * This 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, + * or (at your option) any later version. + * + * This 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 dpkg; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "dpkg.h" +#include "dpkg-db.h" +#include "myopt.h" + +#include "filesdb.h" +#include "main.h" + +int conffoptcells[2][2]= { CONFFOPTCELLS }; + +static void md5hash(struct pkginfo *pkg, char hashbuf[MD5HASHLEN+1], const char *fn); + +void deferred_configure(struct pkginfo *pkg) { + /* The algorithm for deciding what to configure first is as follows: + * Loop through all packages doing a `try 1' until we've been round + * and nothing has been done, then do `try 2' and `try 3' likewise. + * The incrementing of `dependtry' is done by process_queue(). + * Try 1: + * Are all dependencies of this package done ? If so, do it. + * Are any of the dependencies missing or the wrong version ? + * If so, abort (unless --force-depends, in which case defer) + * Will we need to configure a package we weren't given as an + * argument ? If so, abort - except if --force-configure-any, + * in which case we add the package to the argument list. + * If none of the above, defer the package. + * Try 2: + * Find a cycle and break it (see above). + * Do as for try 1. + * Try 3 (only if --force-depends-version). + * Same as for try 2, but don't mind version number in dependencies. + * Try 4 (only if --force-depends). + * Do anyway. + */ + struct varbuf aemsgs, cdr, cdr2; + char *cdr2rest; + int ok, r, useredited, distedited, c, cc, status, c1; + struct conffile *conff; + char currenthash[MD5HASHLEN+1], newdisthash[MD5HASHLEN+1]; + struct stat stab; + enum conffopt what; + const char *s; + + if (pkg->status == stat_notinstalled) + ohshit("no package named `%s' is installed, cannot configure",pkg->name); + if (pkg->status != stat_unpacked && pkg->status != stat_halfconfigured) + ohshit("package %.250s is not ready for configuration\n" + " cannot configure (current status `%.250s')", + pkg->name, statusinfos[pkg->status].name); + + if (dependtry > 1) { if (findbreakcycle(pkg,0)) sincenothing= 0; } + + varbufinit(&aemsgs); + ok= dependencies_ok(pkg,0,&aemsgs); + if (ok == 1) { + varbuffree(&aemsgs); + pkg->clientdata->istobe= itb_installnew; + add_to_queue(pkg); + return; + } else if (ok == 0) { + sincenothing= 0; + varbufaddc(&aemsgs,0); + fprintf(stderr, + DPKG ": dependency problems prevent configuration of %s:\n%s", + pkg->name, aemsgs.buf); + varbuffree(&aemsgs); + ohshit("dependency problems - leaving unconfigured"); + } else if (aemsgs.used) { + varbufaddc(&aemsgs,0); + fprintf(stderr, + DPKG ": %s: dependency problems, but configuring anyway as you request:\n%s", + pkg->name, aemsgs.buf); + } + varbuffree(&aemsgs); + sincenothing= 0; + + if (pkg->eflag & eflagf_reinstreq) + forcibleerr(fc_removereinstreq, + "Package is in a very bad inconsistent state - you should\n" + " reinstall it before attempting configuration."); + + printf("Setting up %s ...\n",pkg->name); + + if (f_noact) { + pkg->status= stat_installed; + pkg->clientdata->istobe= itb_normal; + return; + } + + if (pkg->status == stat_unpacked) { + debug(dbg_general,"deferred_configure updating conffiles"); + + /* This will not do at all the right thing with overridden conffiles + * or conffiles that are the `target' of an override; all the references + * here would be to the `contested' filename, and in any case there'd + * only be one hash for both `versions' of the conffile. + * + * Overriding conffiles is a silly thing to do anyway :-). + */ + + modstatdb_note(pkg); + + /* On entry, the `new' version of each conffile has been + * unpacked as *.dpkg-new, and the `installed' version is + * as-yet untouched in `*'. The hash of the `old distributed' + * version is in the conffiles data for the package. + * If `*.dpkg-new' no longer exists we assume that we've already + * processed this one. + */ + varbufinit(&cdr); + varbufinit(&cdr2); + for (conff= pkg->installed.conffiles; conff; conff= conff->next) { + r= conffderef(pkg, &cdr, conff->name); + if (r == -1) { + conff->hash= nfstrsave("-"); + continue; + } + md5hash(pkg,currenthash,cdr.buf); + + varbufreset(&cdr2); + varbufaddstr(&cdr2,cdr.buf); + cdr2.used+=50; varbufaddc(&cdr2,0); cdr2rest= cdr2.buf+strlen(cdr.buf); + /* From now on we can just strcpy(cdr2rest,extension); */ + + strcpy(cdr2rest,DPKGNEWEXT); + /* If the .dpkg-new file is no longer there, ignore this one. */ + if (lstat(cdr2.buf,&stab)) { + if (errno == ENOENT) continue; + ohshite("unable to stat new dist conffile `%.250s'",cdr2.buf); + } + md5hash(pkg,newdisthash,cdr2.buf); + + /* Copy the permissions from the installed version to the new + * distributed version. + */ + if (!stat(cdr.buf,&stab)) { + if (chown(cdr2.buf,stab.st_uid,stab.st_gid)) + ohshite("unable to change ownership of new dist conffile `%.250s'",cdr2.buf); + if (chmod(cdr2.buf,stab.st_mode & 07777)) + if (errno != ENOENT) + ohshite("unable to set mode of new dist conffile `%.250s'",cdr2.buf); + } else { + if (errno != ENOENT) + ohshite("unable to stat current installed conffile `%.250s'",cdr.buf); + } + + if (!strcmp(currenthash,newdisthash)) { + /* They're both the same so there's no point asking silly questions. */ + useredited= -1; + distedited= -1; + what= cfo_identical; + } else if (!strcmp(conff->hash,NEWCONFFILEFLAG)) { + if (!strcmp(currenthash,NONEXISTENTFLAG)) { + what= cfo_newconff; + useredited= -1; + distedited= -1; + } else { + useredited= 1; + distedited= 1; + what= conffoptcells[useredited][distedited] | cfof_isnew; + } + } else { + useredited= strcmp(conff->hash,currenthash) != 0; + distedited= strcmp(conff->hash,newdisthash) != 0; + what= conffoptcells[useredited][distedited]; + } + + debug(dbg_conff, + "deferred_configure `%s' (= `%s') useredited=%d distedited=%d what=%o", + conff->name, cdr.buf, useredited, distedited, what); + + if (what & cfof_prompt) { + + do { + + fprintf(stderr, "\nConfiguration file `%s'", conff->name); + if (strcmp(conff->name,cdr.buf)) + fprintf(stderr," (actually `%s')",cdr.buf); + + if (cfof_isnew) { + + fprintf(stderr, + "\n" + " ==> File on system created by you or by a script.\n" + " ==> File also in package provided by package maintainer.\n"); + + } else { + + fprintf(stderr, useredited ? + "\n ==> Modified (by you or by a script) since installation.\n" : + "\n Not modified since installation.\n"); + + fprintf(stderr, distedited ? + " ==> Package distributor has shipped an updated version.\n" : + " Version in package is the same as at last installation.\n"); + + } + + fprintf(stderr, + " What would you like to do about it ? Your options are:\n" + " Y or I : install the package maintainer's version\n" + " N or O : keep your currently-installed version\n" + " Z : background this process to examine the situation\n"); + + if (what & cfof_keep) + fprintf(stderr, " The default action is to keep your current version.\n"); + else if (what & cfof_install) + fprintf(stderr, " The default action is to install the new version.\n"); + + s= strrchr(conff->name,'/'); + if (!s || !*++s) s= conff->name; + fprintf(stderr, "*** %s (Y/I/N/O/Z) %s ? ", + s, + (what & cfof_keep) ? "[default=N]" : + (what & cfof_install) ? "[default=Y]" : "[no default]"); + + if (ferror(stderr)) + ohshite("error writing to stderr, discovered before conffile prompt"); + + cc= 0; + while ((c= getchar()) != EOF && c != '\n') + if (!isspace(c) && !cc) cc= tolower(c); + + if (c == EOF) { + if (ferror(stdin)) ohshite("read error on stdin at conffile prompt"); + ohshit("EOF on stdin at conffile prompt"); + } + + if (!cc) { + if (what & cfof_keep) { cc= 'n'; break; } + else if (what & cfof_install) { cc= 'y'; break; } + } + + /* fixme: say something if silently not install */ + + if (cc == 'z') { + + strcpy(cdr2rest, DPKGNEWEXT); + + fprintf(stderr, + "Your currently installed version of the file is in:\n" + " %s\n" + "The version contained in the new version of the package is in:\n" + " %s\n" + "If you decide to take care of the update yourself, perhaps by editing\n" + " the installed version, you should choose `N' when you return, so that\n" + " I do not mess up your careful work.\n", + cdr.buf, cdr2.buf); + + s= getenv(NOJOBCTRLSTOPENV); + if (s && *s) { + fputs("Type `exit' when you're done.\n",stderr); + if (!(c1= m_fork())) { + s= getenv(SHELLENV); + if (!s || !*s) s= DEFAULTSHELL; + execlp(s,s,"-i",(char*)0); + ohshite("failed to exec shell (%.250s)",s); + } + while ((r= waitpid(c1,&status,0)) == -1 && errno == EINTR); + if (r != c1) { onerr_abort++; ohshite("wait for shell failed"); } + } else { + fputs("Don't forget to foreground (`fg') this " + "process when you're done !\n",stderr); + kill(-getpgid(0),SIGTSTP); + } + } + + } while (!strchr("yino",cc)); + + switch (cc) { + case 'i': case 'y': what= cfof_install | cfof_backup; break; + case 'n': case 'o': what= cfof_keep | cfof_backup; break; + default: internerr("unknown response"); + } + + } + + switch (what & ~cfof_isnew) { + case cfo_keep | cfof_backup: + strcpy(cdr2rest,DPKGOLDEXT); + if (unlink(cdr2.buf) && errno != ENOENT) + fprintf(stderr, + DPKG ": %s: warning - failed to remove old backup `%.250s': %s\n", + pkg->name, cdr2.buf, strerror(errno)); + cdr.used--; + varbufaddstr(&cdr,DPKGDISTEXT); + varbufaddc(&cdr,0); + strcpy(cdr2rest,DPKGNEWEXT); + if (rename(cdr2.buf,cdr.buf)) + fprintf(stderr, + DPKG ": %s: warning - failed to rename `%.250s' to `%.250s': %s\n", + pkg->name, cdr2.buf, cdr.buf, strerror(errno)); + break; + + case cfo_keep: + strcpy(cdr2rest,DPKGNEWEXT); + if (unlink(cdr2.buf)) + fprintf(stderr, + DPKG ": %s: warning - failed to remove `%.250s': %s\n", + pkg->name, cdr2.buf, strerror(errno)); + break; + + case cfo_install | cfof_backup: + strcpy(cdr2rest,DPKGDISTEXT); + if (unlink(cdr2.buf) && errno != ENOENT) + fprintf(stderr, DPKG + ": %s: warning - failed to remove old distrib version `%.250s': %s\n", + pkg->name, cdr2.buf, strerror(errno)); + strcpy(cdr2rest,DPKGOLDEXT); + if (unlink(cdr2.buf) && errno != ENOENT) + fprintf(stderr, DPKG + ": %s: warning - failed to remove `%.250s' (before overwrite): %s\n", + pkg->name, cdr2.buf, strerror(errno)); + if (link(cdr.buf,cdr2.buf)) + fprintf(stderr, DPKG + ": %s: warning - failed to link `%.250s' to `%.250s': %s\n", + pkg->name, cdr.buf, cdr2.buf, strerror(errno)); + /* fall through */ + case cfo_install: + printf("Installing new version of config file %s ...\n",conff->name); + case cfo_newconff: + strcpy(cdr2rest,DPKGNEWEXT); + if (rename(cdr2.buf,cdr.buf)) + ohshite("unable to install `%.250s' as `%.250s'",cdr2.buf,cdr.buf); + break; + + default: + internerr("unknown what"); + } + + conff->hash= nfstrsave(newdisthash); + modstatdb_note(pkg); + + } /* for (conff= ... */ + varbuffree(&cdr); + varbuffree(&cdr2); + + pkg->status= stat_halfconfigured; + } + + assert(pkg->status == stat_halfconfigured); + + modstatdb_note(pkg); + + if (maintainer_script_installed(pkg, POSTINSTFILE, "post-installation", + "configure", + versiondescribe(pkg->configversion, + pkg->configrevision), + (char*)0)) + putchar('\n'); + + pkg->status= stat_installed; + pkg->eflag= eflagv_ok; + modstatdb_note(pkg); + +} + +int conffderef(struct pkginfo *pkg, struct varbuf *result, const char *in) { + /* returns 0 if all OK, -1 if some kind of error. */ + static char *linkreadbuf= 0; + static int linkreadbufsize= 0; + struct stat stab; + int r, need; + int loopprotect; + + varbufreset(result); + varbufaddstr(result,instdir); + if (*in != '/') varbufaddc(result,'/'); + varbufaddstr(result,in); + varbufaddc(result,0); + + loopprotect= 0; + + for (;;) { + debug(dbg_conffdetail,"conffderef in=`%s' current working=`%s'", in, result->buf); + if (lstat(result->buf,&stab)) { + if (errno != ENOENT) + fprintf(stderr, DPKG ": %s: warning - unable to stat config file `%s'\n" + " (= `%s'): %s\n", + pkg->name, in, result->buf, strerror(errno)); + debug(dbg_conffdetail,"conffderef nonexistent"); + return 0; + } else if (S_ISREG(stab.st_mode)) { + debug(dbg_conff,"conffderef in=`%s' result=`%s'", in, result->buf); + return 0; + } else if (S_ISLNK(stab.st_mode)) { + debug(dbg_conffdetail,"conffderef symlink loopprotect=%d",loopprotect); + if (loopprotect++ >= 25) { + fprintf(stderr, DPKG ": %s: warning - config file `%s' is a circular link\n" + " (= `%s')\n", pkg->name, in, result->buf); + return -1; + } + need= 255; + for (;;) { + if (need > linkreadbufsize) { + linkreadbuf= m_realloc(linkreadbuf,need); + linkreadbufsize= need; + debug(dbg_conffdetail,"conffderef readlink realloc(%d)=%p",need,linkreadbuf); + } + r= readlink(result->buf,linkreadbuf,linkreadbufsize-1); + if (r < 0) { + fprintf(stderr, DPKG ": %s: warning - unable to readlink conffile `%s'\n" + " (= `%s'): %s\n", + pkg->name, in, result->buf, strerror(errno)); + return -1; + } + debug(dbg_conffdetail,"conffderef readlink gave %d, `%.*s'", + r, r>0 ? r : 0, linkreadbuf); + if (r < linkreadbufsize-1) break; + need= r<<2; + } + linkreadbuf[r]= 0; + if (linkreadbuf[0] == '/') { + varbufreset(result); + varbufaddstr(result,instdir); + debug(dbg_conffdetail,"conffderef readlink absolute"); + } else { + for (r=result->used-2; r>0 && result->buf[r] != '/'; r--); + if (r < 0) { + fprintf(stderr, DPKG + ": %s: warning - conffile `%.250s' resolves to degenerate filename\n" + " (`%s' is a symlink to `%s')\n", + pkg->name, in, result->buf, linkreadbuf); + return -1; + } + if (result->buf[r] == '/') r++; + result->used= r; + debug(dbg_conffdetail,"conffderef readlink relative to `%.*s'", + result->used, result->buf); + } + varbufaddstr(result,linkreadbuf); + varbufaddc(result,0); + } else { + fprintf(stderr, DPKG ": %s: warning - conffile `%.250s' is not a plain" + " file or symlink (= `%s')\n", + pkg->name, in, result->buf); + return -1; + } + } +} + +static void md5hash(struct pkginfo *pkg, char hashbuf[33], const char *fn) { + static int fd, p1[2]; + FILE *file; + pid_t c1; + int ok; + + fd= open(fn,O_RDONLY); + if (fd >= 0) { + push_cleanup(cu_closefd,ehflag_bombout, 0,0, 1,(void*)&fd); + m_pipe(p1); + push_cleanup(cu_closepipe,ehflag_bombout, 0,0, 1,(void*)&p1[0]); + if (!(c1= m_fork())) { + m_dup2(fd,0); m_dup2(p1[1],1); close(p1[0]); + execlp(MD5SUM,MD5SUM,(char*)0); + ohshite("failed to exec md5sum"); + } + close(p1[1]); close(fd); + file= fdopen(p1[0],"r"); + push_cleanup(cu_closefile,ehflag_bombout, 0,0, 1,(void*)file); + if (!file) ohshite("unable to fdopen for md5sum of `%.250s'",fn); + ok= 1; + memset(hashbuf,0,33); + if (fread(hashbuf,1,32,file) != 32) ok=0; + if (getc(file) != '\n') ok=0; + if (getc(file) != EOF) ok=0; + waitsubproc(c1,"md5sum",0); + if (strspn(hashbuf,"0123456789abcdef") != 32) ok=0; + if (ferror(file)) ohshite("error reading pipe from md5sum"); + if (fclose(file)) ohshite("error closing pipe from md5sum"); + pop_cleanup(ehflag_normaltidy); /* file= fdopen(p1[0]) */ + pop_cleanup(ehflag_normaltidy); /* m_pipe() */ + pop_cleanup(ehflag_normaltidy); /* fd= open(cdr.buf) */ + if (!ok) ohshit("md5sum gave malformatted output `%.250s'",hashbuf); + } else if (errno == ENOENT) { + strcpy(hashbuf,NONEXISTENTFLAG); + } else { + fprintf(stderr, DPKG ": %s: warning - unable to open conffile %s for hash: %s\n", + pkg->name, fn, strerror(errno)); + strcpy(hashbuf,"-"); + } +} diff --git a/main/debugmake b/main/debugmake new file mode 100755 index 00000000..7c142c58 --- /dev/null +++ b/main/debugmake @@ -0,0 +1,3 @@ +#!/bin/sh +set -x +make XCFLAGS=-g LDFLAGS=-g 'LIBS= -lefence -L../lib -ldpkg' "$@" diff --git a/main/depcon.c b/main/depcon.c new file mode 100644 index 00000000..7e90464a --- /dev/null +++ b/main/depcon.c @@ -0,0 +1,490 @@ +/* + * dpkg - main program for package management + * depcon.c - dependency and conflict checking + * + * Copyright (C) 1994,1995 Ian Jackson + * + * This 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, + * or (at your option) any later version. + * + * This 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 dpkg; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include + +#include "config.h" +#include "dpkg.h" +#include "dpkg-db.h" + +#include "main.h" + +int versionsatisfied5(const char *itver, const char *itrev, + const char *refver, const char *refrev, + enum depverrel verrel) { + int r; + if (verrel == dvr_none) return 1; + r= versioncompare(itver,itrev,refver,refrev); + switch (verrel) { + case dvr_earlierequal: return r <= 0; + case dvr_laterequal: return r >= 0; + case dvr_earlierstrict: return r < 0; + case dvr_laterstrict: return r > 0; + case dvr_exact: return r == 0; + default: internerr("unknown verrel"); + } +} + +int versionsatisfied(struct pkginfoperfile *it, struct deppossi *against) { + return versionsatisfied5(it->version, it->revision, + against->version, against->revision, against->verrel); +} + +const char *versiondescribe(const char *ver, const char *rev) { + static char bufs[10][512]; + static int bufnum=0; + char *buf; + + buf= bufs[bufnum]; bufnum++; if (bufnum == 10) bufnum= 0; + + if (!ver || !*ver) { + strcpy(buf,""); + } else { + if (rev && *rev) { + sprintf(buf, "%.250s-%.250s", ver, rev); + } else { + sprintf(buf, "%.250s", ver); + } + } + return buf; +} + +struct cyclesofarlink { + struct cyclesofarlink *back; + struct pkginfo *pkg; + struct deppossi *possi; +}; + +static int foundcyclebroken(struct cyclesofarlink *thislink, + struct cyclesofarlink *sofar, + struct pkginfo *dependedon, + struct deppossi *possi) { + struct cyclesofarlink *sol; + const char *postinstfilename; + struct stat stab; + + /* We're investigating the dependency `possi' to see if it + * is part of a loop. To this end we look to see whether the + * depended-on package is already one of the packages whose + * dependencies we're searching. + */ + for (sol=sofar; sol && sol->pkg != dependedon; sol=sol->back); + + /* If not, we do a recursive search on it to see what we find. */ + if (!sol) return findbreakcycle(possi->ed,thislink); + + debug(dbg_depcon,"found cycle"); + /* Right, we now break one of the links. We prefer to break + * a dependency of a package without a postinst script, as + * this is a null operation. If this is not possible we break + * the other link in the recursive calling tree which mentions + * this package (this being the first package involved in the + * cycle). It doesn't particularly matter which we pick, but if + * we break the earliest dependency we came across we may be + * able to do something straight away when findbreakcycle returns. + */ + sofar= thislink; + for (sol= sofar; !(sol != sofar && sol->pkg == dependedon); sol=sol->back) { + postinstfilename= pkgadminfile(sol->pkg,POSTINSTFILE); + if (lstat(postinstfilename,&stab)) { + if (errno == ENOENT) break; + ohshite("unable to check for existence of `%.250s'",postinstfilename); + } + } + /* Now we have either a package with no postinst, or the other + * occurrence of the current package in the list. + */ + sol->possi->cyclebreak= 1; + debug(dbg_depcon,"cycle broken at %s -> %s\n", + sol->possi->up->up->name, sol->possi->ed->name); + return 1; +} + +int findbreakcycle(struct pkginfo *pkg, struct cyclesofarlink *sofar) { + /* Cycle breaking works recursively down the package dependency + * tree. `sofar' is the list of packages we've descended down + * already - if we encounter any of its packages again in a + * dependency we have found a cycle. + */ + struct cyclesofarlink thislink, *sol; + struct dependency *dep; + struct deppossi *possi, *providelink; + struct pkginfo *provider; + + if (f_debug & dbg_depcondetail) { + fprintf(stderr,"D0%05o: findbreakcycle %s ",dbg_depcondetail,pkg->name); + for (sol=sofar; sol; sol=sol->back) fprintf(stderr," <- %s",sol->pkg->name); + fprintf(stderr,"\n"); + } + thislink.pkg= pkg; + thislink.back= sofar; + thislink.possi= 0; + for (dep= pkg->installed.depends; dep; dep= dep->next) { + if (dep->type != dep_depends && dep->type != dep_predepends) continue; + for (possi= dep->list; possi; possi= possi->next) { + /* We can't have any cycles involving packages we're not trying + * to do anything with. + */ + if (possi->ed->clientdata->istobe == itb_normal) continue; + /* Don't find the same cycles again. */ + if (possi->cyclebreak) continue; + thislink.possi= possi; + if (foundcyclebroken(&thislink,sofar,possi->ed,possi)) return 1; + /* Right, now we try all the providers ... */ + for (providelink= possi->ed->installed.depended; + providelink; + providelink= providelink->nextrev) { + if (providelink->up->type != dep_provides) continue; + provider= providelink->up->up; + if (provider->clientdata->istobe == itb_normal) continue; + /* We don't break things at `provides' links, so `possi' is + * still the one we use. + */ + if (foundcyclebroken(&thislink,sofar,provider,possi)) return 1; + } + } + } + /* Nope, we didn't find a cycle to break. */ + return 0; +} + +void describedepcon(struct varbuf *addto, struct dependency *dep) { + varbufaddstr(addto,dep->up->name); + switch (dep->type) { + case dep_depends: varbufaddstr(addto, " depends on "); break; + case dep_predepends: varbufaddstr(addto, " pre-depends on "); break; + case dep_recommends: varbufaddstr(addto, " recommends "); break; + case dep_conflicts: varbufaddstr(addto, " conflicts with "); break; + default: internerr("unknown deptype"); + } + varbufdependency(addto, dep); +} + +int depisok(struct dependency *dep, struct varbuf *whynot, + struct pkginfo **canfixbyremove, int allowunconfigd) { + /* *whynot must already have been initialised; it need not be + * empty though - it will be reset before use. + * If depisok returns 0 for `not OK' it will contain a description, + * newline-terminated BUT NOT NULL-TERMINATED, of the reason. + * If depisok returns 1 it will contain garbage. + * allowunconfigd should be non-zero during the `Pre-Depends' checking + * before a package is unpacked, when it is sufficient for the package + * to be unpacked provided that both the unpacked and previously-configured + * versions are acceptable. + */ + struct deppossi *possi; + struct deppossi *provider; + int nconflicts; + + /* Use this buffer so that when internationalisation comes along we + * don't have to rewrite the code completely, only redo the sprintf strings + * (assuming we have the fancy argument-number-specifiers). + * Allow 250x3 for package names, versions, &c, + 250 for ourselves. + */ + char linebuf[1024]; + + assert(dep->type == dep_depends || dep->type == dep_predepends || + dep->type == dep_conflicts || dep->type == dep_recommends); + + /* The dependency is always OK if we're trying to remove the depend*ing* + * package. + */ + switch (dep->up->clientdata->istobe) { + case itb_remove: case itb_deconfigure: + return 1; + case itb_normal: + /* Only `installed' packages can be make dependency problems */ + switch (dep->up->status) { + case stat_installed: + break; + case stat_notinstalled: case stat_configfiles: case stat_halfinstalled: + case stat_halfconfigured: case stat_unpacked: + return 1; + default: + internerr("unknown status depending"); + } + break; + case itb_installnew: case itb_preinstall: + break; + default: + internerr("unknown istobe depending"); + } + + /* Describe the dependency, in case we have to moan about it. */ + varbufreset(whynot); + varbufaddc(whynot, ' '); + describedepcon(whynot, dep); + varbufaddc(whynot,'\n'); + + if (dep->type == dep_depends || dep->type == dep_predepends || + dep->type == dep_recommends) { + + /* Go through the alternatives. As soon as we find one that + * we like, we return `1' straight away. Otherwise, when we get to + * the end we'll have accumulated all the reasons in whynot and + * can return `0'. + */ + + for (possi= dep->list; possi; possi= possi->next) { + switch (possi->ed->clientdata->istobe) { + case itb_remove: + sprintf(linebuf, " %.250s is to be removed.\n", possi->ed->name); + break; + case itb_deconfigure: + sprintf(linebuf, " %.250s is to be deconfigured.\n", possi->ed->name); + break; + case itb_installnew: + if (versionsatisfied(&possi->ed->available, possi)) return 1; + sprintf(linebuf, " %.250s is to be installed, but is version %.250s.\n", + possi->ed->name, + versiondescribe(possi->ed->available.version, + possi->ed->available.revision)); + break; + case itb_normal: case itb_preinstall: + switch (possi->ed->status) { + case stat_installed: + if (versionsatisfied(&possi->ed->installed, possi)) return 1; + sprintf(linebuf, " %.250s is installed, but is version %.250s.\n", + possi->ed->name, + versiondescribe(possi->ed->available.version, + possi->ed->available.revision)); + break; + case stat_notinstalled: + /* Don't say anything about this yet - it might be a virtual package. + * Later on, if nothing has put anything in linebuf, we know that it + * isn't and issue a diagnostic then. + */ + *linebuf= 0; + break; + case stat_unpacked: + case stat_halfconfigured: + if (allowunconfigd) { + if (!possi->ed->configversion || !*possi->ed->configversion) { + sprintf(linebuf, " %.250s is unpacked, but has never been configured.\n", + possi->ed->name); + break; + } else if (!versionsatisfied(&possi->ed->installed, possi)) { + sprintf(linebuf, " %.250s is unpacked, but is version %.250s.\n", + possi->ed->name, + versiondescribe(possi->ed->available.version, + possi->ed->available.revision)); + break; + } else if (!versionsatisfied5(possi->ed->configversion, + possi->ed->configrevision, + possi->version, possi->revision, + possi->verrel)) { + sprintf(linebuf, " %.250s latest configured version is %.250s.\n", + possi->ed->name, + versiondescribe(possi->ed->configversion, + possi->ed->configrevision)); + break; + } else { + return 1; + } + } + default: + sprintf(linebuf, " %.250s is %s.\n", + possi->ed->name, statusstrings[possi->ed->status]); + break; + } + break; + default: + internerr("unknown istobe depended"); + } + varbufaddstr(whynot, linebuf); + + /* If there was no version specified we try looking for Providers. */ + if (possi->verrel == dvr_none) { + + /* See if the package we're about to install Provides it. */ + for (provider= possi->ed->available.depended; + provider; + provider= provider->nextrev) { + if (provider->up->type != dep_provides) continue; + if (provider->up->up->clientdata->istobe == itb_installnew) return 1; + } + + /* Now look at the packages already on the system. */ + for (provider= possi->ed->installed.depended; + provider; + provider= provider->nextrev) { + if (provider->up->type != dep_provides) continue; + + switch (provider->up->up->clientdata->istobe) { + case itb_installnew: + /* Don't pay any attention to the Provides field of the + * currently-installed version of the package we're trying + * to install. We dealt with that by using the available + * information above. + */ + continue; + case itb_remove: + sprintf(linebuf, " %.250s provides %.250s but is to be removed.\n", + provider->up->up->name, possi->ed->name); + break; + case itb_deconfigure: + sprintf(linebuf, " %.250s provides %.250s but is to be deconfigured.\n", + provider->up->up->name, possi->ed->name); + break; + case itb_normal: case itb_preinstall: + if (provider->up->up->status == stat_installed) return 1; + sprintf(linebuf, " %.250s provides %.250s but is %s.\n", + provider->up->up->name, possi->ed->name, + statusstrings[provider->up->up->status]); + break; + default: + internerr("unknown istobe provider"); + } + varbufaddstr(whynot, linebuf); + } + + if (!*linebuf) { + /* If the package wasn't installed at all, and we haven't said + * yet why this isn't satisfied, we should say so now. + */ + sprintf(linebuf, " %.250s is not installed.\n", possi->ed->name); + varbufaddstr(whynot, linebuf); + } + } + } + + if (canfixbyremove) *canfixbyremove= 0; + return 0; + + } else { + + /* It's a conflict. There's only one main alternative, + * but we also have to consider Providers. We return `0' as soon + * as we find something that matches the conflict, and only describe + * it then. If we get to the end without finding anything we return `1'. + */ + + possi= dep->list; + nconflicts= 0; + + if (possi->ed != possi->up->up) { + /* If the package conflicts with itself it must mean that it conflicts + * with other packages which provide the same virtual name. We therefore + * don't look at the real package and go on to the virtual ones. + */ + + switch (possi->ed->clientdata->istobe) { + case itb_remove: + break; + case itb_installnew: + if (!versionsatisfied(&possi->ed->available, possi)) break; + sprintf(linebuf, " %.250s (version %.250s) is to be installed.\n", + possi->ed->name, + versiondescribe(possi->ed->available.version, + possi->ed->available.revision)); + varbufaddstr(whynot, linebuf); + if (!canfixbyremove) return 0; + nconflicts++; + *canfixbyremove= possi->ed; + break; + case itb_normal: case itb_deconfigure: case itb_preinstall: + switch (possi->ed->status) { + case stat_notinstalled: case stat_configfiles: + break; + default: + if (!versionsatisfied(&possi->ed->installed, possi)) break; + sprintf(linebuf, " %.250s (version %.250s) is %s.\n", + possi->ed->name, + versiondescribe(possi->ed->available.version, + possi->ed->available.revision), + statusstrings[possi->ed->status]); + varbufaddstr(whynot, linebuf); + if (!canfixbyremove) return 0; + nconflicts++; + *canfixbyremove= possi->ed; + } + break; + default: + internerr("unknown istobe conflict"); + } + } + + /* If there was no version specified we try looking for Providers. */ + if (possi->verrel == dvr_none) { + + /* See if the package we're about to install Provides it. */ + for (provider= possi->ed->available.depended; + provider; + provider= provider->nextrev) { + if (provider->up->type != dep_provides) continue; + if (provider->up->up->clientdata->istobe != itb_installnew) continue; + if (provider->up->up == dep->up) continue; /* conflicts and provides the same */ + sprintf(linebuf, " %.250s provides %.250s and is to be installed.\n", + provider->up->up->name, possi->ed->name); + varbufaddstr(whynot, linebuf); + /* We can't remove the one we're about to install: */ + if (canfixbyremove) *canfixbyremove= 0; + return 0; + } + + /* Now look at the packages already on the system. */ + for (provider= possi->ed->installed.depended; + provider; + provider= provider->nextrev) { + if (provider->up->type != dep_provides) continue; + + if (provider->up->up == dep->up) continue; /* conflicts and provides the same */ + + switch (provider->up->up->clientdata->istobe) { + case itb_installnew: + /* Don't pay any attention to the Provides field of the + * currently-installed version of the package we're trying + * to install. We dealt with that by using the available + * information above. + */ + continue; + case itb_remove: + continue; + case itb_normal: case itb_deconfigure: case itb_preinstall: + switch (provider->up->up->status) { + case stat_notinstalled: case stat_configfiles: + continue; + default: + sprintf(linebuf, " %.250s provides %.250s and is %s.\n", + provider->up->up->name, possi->ed->name, + statusstrings[provider->up->up->status]); + varbufaddstr(whynot, linebuf); + if (!canfixbyremove) return 0; + nconflicts++; + *canfixbyremove= provider->up->up; + break; + } + break; + default: + internerr("unknown istobe conflict provider"); + } + } + } + + if (!nconflicts) return 1; + if (nconflicts>1) *canfixbyremove= 0; + return 0; + + } /* if (dependency) {...} else {...} */ +} diff --git a/main/dpkg.8 b/main/dpkg.8 new file mode 100644 index 00000000..bf760f43 --- /dev/null +++ b/main/dpkg.8 @@ -0,0 +1,20 @@ +.\" Hey, Emacs! This is an -*- nroff -*- source file. +.TH DPKG 8 "29th November 1995" "Debian Project" "Debian GNU/Linux" +.SH NAME +dpkg \- Debian package installation tool +.SH DESCRIPTION +.B dpkg +does not have a useful man page. Please do not report this as a bug, +as this has already been done many times. + +Instead, if you are a competent and accurate writer and are willing to +spend the time reading the source code and writing good manpages +please write a better man page than this one. + +Type +.B dpkg \-\-help +for a brief summary of how to use dpkg. + +.SH AUTHOR +Ian Jackson ; tarfile extraction code +contributed by Bruce Perens . diff --git a/main/dpkg.8-vuori b/main/dpkg.8-vuori new file mode 100644 index 00000000..18cc4c0f --- /dev/null +++ b/main/dpkg.8-vuori @@ -0,0 +1,588 @@ +.TH dpkg 8 +.SH NAME +dpkg - a low-level package manager for Debian GNU/Linux + +.SH SYNOPSIS + +.B dpkb +[options] action + +.SH DESCRIPTION + +.B dpkg +is a medium-level tool to install, build, remove and manage Debian +GNU/Linux packages. The primary and more user-friendly front-end +for +.B dpkg +is +.B dselect(8). +.B dpkg +itself is totally controlled via command line parameters, whose include +one or more options and exactly one action. The action-parameter tells +dpkg what to do and options control the behaviour of the action in some +way. + +.B dpkg +can be also be used as a front-end to +.B dpkg-deb. +Actions +.B -b +, +.B --build +, +.B -c +, +.B --contents +, +.B -I +, +.B --info +, +.B -f +, +.B --field +, +.B -e +, +.B --control +, +.B -x +, +.B --extract +, +.B -X +, +.B --vextract +and +.B --fsys-tarfile +are +.B dpkg-deb +actions and if they are encountered, +.B dpkg +just runs +.B dpkg-deb +with the parameters given to it. Please refer to +.B dpkg-deb(8) +for information about these actions. + +.SS ACTIONS + +.TP +.B dpkg -i | --install ... +Install specified packages. If +.B --recursive +or +.B -R +option is specified, +.I +must refer to a directory instead. + +Installation consists of the following steps: +.br +.B 1. +Extract the controlfiles of the new package. +.br +.B 2. +If another version of the same package was installed before the +new installation, execute +.I prerm +script of the old package. +.br +.B 3. +Run +.I preinst +script, if provided by the package. +.br +.B 4. +Unpack the files and at the same time, backup the old files +so that if something goes wrong, we can restore them. +.br +.B 5. +If another version of the same package was installed before +the new installation, execute the +.I postrm +script of the old package. Note that this script is executed after the +.I preinst +script of the new package, because new files are written at the same +time, old files are removed. +.br +.B 6. +eonfigure the package. See +.B --configure +for detailed information about how this is done. +.TP +.B dpkg --unpack ... +Unpack package, but don't configure it. If +.B --recursive +or +.B -R +option is specified, +.I +must refer to a directory instead. +.TP +.B dpkg --configure ... | -a|--pending +Reconfigure an unpacked package. +If +.B -a +or +.B --pending +is given instead of package name, all unpacked, but unconfigured +packages are configured. + +Configuring consists of the following steps: +.br +.B 1. +Unpack the configuration files and at the same time, backup the old +configuration files, so that we can restore them, if +something goes +wrong. +.br +.B 2. +Run +.I postinst +script, if provided by the package. +.TP +.B dpkg -r|--remove | --purge ... | -a|--pending +Remove an installed package. +.B --purge +removes everything, including configuration files, +.B --remove +removes everything, but not configuration files. (configuration files are +the files listed in +.I conffiles +-control file). If +.B -a +or +.B --pending +is given instead of package name all packages unpacked, but marked to be +removed or purged are removed or purged (in file +.I /var/lib/dpkg/status +). + +Removing of a package consists of the following steps: +.br +.B 1. +Run prerm script +.br +.B 2. +Remove the installed files +.br +.B 3. +Run postrm script +.br +.TP +.B dpkg -A | --avail ... +Update +.B dpkg +and +.B dselect's +idea of which packages are available with information about the package +.I . +If +.B --recursive +or +.B -R +option is specified, +.I +must refer to a directory instead. +.TP +.B dpkg --update-avail | --merge-avail +Update +.B dpkg's +and +.B dselect's +idea of which packages are available. With action +.B --merge-avail, +information is combined from the old information and from the +.I . +With action +.B --update-avail, +old information is replaced with the information in the +.I . +The +.I +distributed with Debian GNU/Linux is simply named +.I Packages. +.TP +.B dpkg --yet-to-unpack +Searches for packages selected for installation, but which for some +reason still haven't been installed. +.TP +.B dpkg -l|--list [ ...] +List packages matching given pattern. If no +.B +is given, list all packages in +.I /var/lib/dpkg/available. +Normal shell wildchars are allowed in +.B . +.TP +.B dpkg -L|--listfiles ... +List files installed to your system from a package +.B . +However, note that files created by package-specific +installation-scripts are not listed. +.TP +.B dpkg -C|--audit +Searches for packages that have been installed only partially on your +system. +.B dpkg +will suggest what to do with them to get them working. +.TP +.B dpkg -S|--search ... +Search for a filename from installed packages. All standard shell +wildchars can be used in the pattern. +.TP +.B dpkg -s|--status ... +Report status of specified package. This just displays the entry in from +the installed package status database. +.TP +.B dpkg --help +Display a brief help. +.TP +.B dpkg --licence +Display licence of +.B dpkg. +.TP +.B dpkg --version +Display version information. +.TP +.B dpkg-deb-actions +See +.B dpkg-deb(8) +for more information about these actions. + +.B dpkg -b|--build [] +- Build a Debian GNU/Linux package. +.br +.B dpkg -c|--contents +- List contents of Debian GNU/Linux package. +.br +.B dpkg -e|--control [] +- Extract control-information from a package. +.br +.B dpkg -x|--extract +- Extract the files contained by package. +.br +.B dpkg -f|--field [...] +- Display control field(s) of a package. +.br +.B dpkg --fsys-tarfile +- Display the filesystem tar-file contained by a Debian package. +.br +.B dpkg -I|--info [] +- Show information about a package. +.br +.B dpkg -X|--vextract +- Extract and display the filenames contained by a package. + +.SS OPTIONS + +.TP +.B -B | --auto-deconfigure +While a package is removed, there is a possibility that another +installed package depended on the removed package. Specifying this +option will cause automatical deconfiguration of the package which +depended on the removed package. +.TP +.B -Dh | --debug=help +Give help about debugging options. +.TP +.B -D | --debug= +Set debugging on. +.B +is a octal number formed by bitwise-orring desired values together from +the list below (note that these values may change in future releases). + + number description + 1 Generally helpful progress information + 2 Invocation and status of maintainer scripts + 10 Output for each file processed + 100 Lots of output for each file processed + 20 Output for each configuration file + 200 Lots of output for each configuration file + 40 Dependencies and conflicts + 400 Lots of dependencies/conflicts output + 1000 Lots of drivel about eg the dpkg/info dir + 2000 Insane amounts of drivel +.TP +.B --force- | --no-force- | --refuse- +Force or refuse (no-force and refuse stands for the same thing) to do +some things. +.B +is a comma separated list of things specified below: + +.I downgrade(*): +Install a package, even if newer version of it is already installed. + +.I configure-any: +Configure also unpacked, but unconfigured packages on whose current +package depends on. + +.I remove-reinstreq: +Remove a package, even if it's broken and marked to require +reinstallation. This may, for example, cause parts of the package to +remain on the system, which will then be forgotten by +.B dpkg. + +.I hold: +Don't care, wheter a package is on hold or not. + +.I remove-essential: +Remove, even if the package is considered essential. Essential packages +contains mostly very basic unix commands and such. Removing them might +cause the whole system to stop working, so use with caution. + +.I conflicts: +Install, even if it conflicts with another package. This is dangerous, +for it will usually cause overwriting of some files. + +.I depends: +Remove, even if another package depends on this one. This will usually +break the other package. + +.I depends-version: +Don't care about versions when checking depencies. This will usually +break the other package. + +Things marked with (*) are forced by default. +.I Warning: +These options are mostly intended to be used by experts only. Using them +without fully understanding their effects may break your whole system. + +.TP +.B --ignore-depends=,... +Ignore depency-checking for specified packages (actually, checking is +performed, but only warnings about conflicts are given, nothing else). +.TP +.B --largemem | --smallmem +Tells +.B dpkg +wheter to preserve memory or consume it as much as needed. +.TP +.B --new | --old +Select new or old package format. This is a +.B dpkg-deb(8) +option. +.TP +.B --nocheck +Don't read or check contents of control file while building a package. +This is a +.B dpkg-deb(8) +option. +.TP +.B --no-act +Do everything, which is supposed to be done, but don't write any +changes. This is used to see, what would happen with specified action, +without actually modifying anything. + +Be sure to give +.B --no-act +before action-parameter, or you might end up with undesirable results. +(e.g. +.B dpkg --purge foo --no-act +will first purge package foo and then try to purge package --no-act, +even though you propably expected it to actually do nothing) +.TP +.B -R | --recursive +Recursively handle all regular files matching pattern +.I *.deb +found at specified directories and all of its subdirectories. This +can be used with +.B -i +, +.B -A +, +.B --install +, +.B --unpack +and +.B --avail +actions. +.TP +.B -G +Don't install package, if newer version of the same package is already +installed. This is an alias to +.B--refuse-downgrade. +.TP +.B -R|--root= | --admindir= | --instdir= +Change default directories. +.B admindir +defaults to +.I /var/lib/dpkg +and contains many files that give information about status of installed +or uninstalled packages, etc. +.B instdir +defaults to +.I / +and refers to the directory where packages are to be installed. +.B instdir +is also the directory passed to +.B chroot(2) +before running package's installation scripts, which means that the +scripts see +.B instdir +as a root directory. +Changing +.B root +changes +.B instdir +to +.I +and +.B admindir +to +.I /var/lib/dpkg. +.TP +.B -O | --selected-only +Only process the packages that are selected for installation. The actual +marking is done with +.B dselect +or by +.B dpkg, +when it handles packages. i.e. When, for example a package is removed, +it will be marked selected for installation, etc. +.TP +.B -E | --skip-same-version +Don't install the package, if the same version of the package is already +installed. + +.SH INFORMATION ABOUT PACKAGES +.B dpkg +maintains some usable information about available packages. The +information is divided in three classes: +.B states +, +.B selection states +and +.B flags. +These values are intended to be changed mainly with +.B dselect. +.SS PACKAGE STATES +.TP +.B installed +The package is unpacked and configured ok. +.TP +.B half-installed +The installation of the package has been started, but not completed for +some reason. +.TP +.B not-installed +The package is not installed on your system. +.TP +.B unpacked +The package is unpacked, but not configured. +.TP +.B half-configured +The package is unpacked and configuration has been started, but not yet +completed for some reason. +.TP +.B config-files +Only the configuration files of the package exist on the system. +.SS PACKAGE SELECTION STATES +.TP +.B install +The package is selected for installation. +.TP +.B deinstall +The package is selected for deinstallation (i.e. we want to remove all +files, except configuration files). +.TP +.B purge +The package is selected to be purged (i.e. we want to remove everything, +even configuration files). +.SS PACKAGE FLAGS +.TP +.B hold +A package marked to be on +.B hold +is not handled by +.B dpkg, +unless forced to do that with option +.B --force-hold. +.TP +.B reinst-required +A package marked +.B reinst-required +is broken and requires reinstallation. These packages cannot be removed, +unless forced with option +.B --force-reinstreq. + +.SH FILES +The files listed here are in their default directories, see option +.B --admindir +to see how to change locations of these files. +.TP +.I /var/lib/dpkg/available +List of available packages. +.TP +.I /var/lib/dpkg/status +Statuses of available packages. This file contains information about +wheter a package is marked for removing or not, wheter it is installed +or not, etc. See section +.B INFORMATION ABOUT PACKAGES +for more info. +.TP +.I control +See +.B deb(5) +for more information about this file. +.TP +.I conffiles +.B dpkg. +See +.B deb(5) +for more information about this file. +.TP +.I preinst +See +.B deb(5) +for more information about this file. +.TP +.I postinst +See +.B deb(5) +for more information about this file. +.TP +.I prerm +See +.B deb(5) +for more information about this file. +.TP +.I postrm +See +.B deb(5) +for more information about this file. + +.SH ENVIRONMENT VARIABLES +.TP +.B DPKG_NO_TSTP +Define this to something, if you prefer +.B dpkg +starting a new shell rather than suspending +.B dpkg, +while doing a shell escape. +.TP +.B SHELL +The program +.B dpkg +will execute while starting a new shell. + +.SH SEE ALSO +.B deb(5) +, +.B dpkg-deb(8) +, +.B dselect(8) +and +.B deb-control(5) + +.SH BUGS + +.B --no-act +usually gives less information that might be helpful. +.SH AUTHOR +.B dpkg +is written by Ian Jackson (ian@chiark.chu.cam.ac.uk). Manual page added +by Juho Vuori (javuori@cc.helsinki.fi). + + diff --git a/main/enquiry.c b/main/enquiry.c new file mode 100644 index 00000000..43302bf8 --- /dev/null +++ b/main/enquiry.c @@ -0,0 +1,601 @@ +/* + * dpkg - main program for package management + * enquiry.c - status enquiry and listing options + * + * Copyright (C) 1995 Ian Jackson + * + * This 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, + * or (at your option) any later version. + * + * This 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 dpkg; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* fixme: per-package audit */ + +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "dpkg.h" +#include "dpkg-db.h" +#include "myopt.h" + +#include "filesdb.h" +#include "main.h" + +static int listqcmp(const void *a, const void *b) { + struct pkginfo *pa= *(struct pkginfo**)a; + struct pkginfo *pb= *(struct pkginfo**)b; + return strcmp(pa->name,pb->name); +} + +static void limiteddescription(struct pkginfo *pkg, int maxl, + const char **pdesc_r, int *l_r) { + const char *pdesc, *p; + int l; + + pdesc= pkg->installed.valid ? pkg->installed.description : 0; + if (!pdesc) pdesc= "(no description available)"; + p= strchr(pdesc,'\n'); + if (!p) p= pdesc+strlen(pdesc); + l= (p - pdesc > maxl) ? maxl : (int)(p - pdesc); + *pdesc_r=pdesc; *l_r=l; +} + +static void list1package(struct pkginfo *pkg, int *head) { + int l; + const char *pdesc; + + if (!*head) { + fputs("\ +Desired=Unknown/Install/Remove/Purge\n\ +| Status=Not/Installed/Config-files/Unpacked/Failed-config/Half-installed\n\ +|/ Err?=(none)/Hold/Reinst-required/X=both-problems (Status,Err: uppercase=bad)\n\ +||/ Name Version Description\n\ ++++-===============-==============-============================================\n", + stdout); + *head= 1; + } + + if (!pkg->installed.valid) blankpackageperfile(&pkg->installed); + limiteddescription(pkg,44,&pdesc,&l); + printf("%c%c%c %-15.15s %-14.14s %.*s\n", + "uirp"[pkg->want], + "nUFiHc"[pkg->status], + " hRX"[pkg->eflag], + pkg->name, + versiondescribe(pkg->installed.version,pkg->installed.revision), + l, pdesc); +} + +void listpackages(const char *const *argv) { + struct pkgiterator *it; + struct pkginfo *pkg; + struct pkginfo **pkgl; + const char *thisarg; + int np, i, head, found; + + modstatdb_init(admindir,msdbrw_readonly); + + np= countpackages(); + pkgl= m_malloc(sizeof(struct pkginfo*)*np); + it= iterpkgstart(); i=0; + while ((pkg= iterpkgnext(it))) { + assert(istatus == stat_notinstalled) continue; + list1package(pkg,&head); + } + } else { + while ((thisarg= *argv++)) { + found= 0; + for (i=0; iname,0)) continue; + list1package(pkg,&head); found++; + } + if (!found) + fprintf(stderr,"No packages found matching %s.\n",thisarg); + } + } + if (ferror(stdout)) werr("stdout"); + if (ferror(stderr)) werr("stderr"); +} + +struct badstatinfo { + int (*yesno)(struct pkginfo*, const struct badstatinfo *bsi); + int val; + const char *explanation; +}; + +static int bsyn_reinstreq(struct pkginfo *pkg, const struct badstatinfo *bsi) { + return pkg->eflag &= eflagf_reinstreq; +} + +static int bsyn_status(struct pkginfo *pkg, const struct badstatinfo *bsi) { + if (pkg->eflag &= eflagf_reinstreq) return 0; + return pkg->status == bsi->val; +} + +static const struct badstatinfo badstatinfos[]= { + { + bsyn_reinstreq, 0, + "The following packages are in a mess due to serious problems during\n" + "installation. They must be reinstalled for them (and any packages\n" + "that depend on them) to function properly:\n" + }, { + bsyn_status, stat_unpacked, + "The following packages have been unpacked but not yet configured.\n" + "They must be configured using " DPKG " --configure or the configure\n" + "menu option in " DSELECT " for them to work:\n" + }, { + bsyn_status, stat_halfconfigured, + "The following packages are only half configured, probably due to problems\n" + "configuring them the first time. The configuration should be retried using\n" + DPKG " --configure or the configure menu option in " DSELECT ":\n" + }, { + bsyn_status, stat_halfinstalled, + "The following packages are only half installed, due to problems during\n" + "installation. The installation can probably be completed by retrying it;\n" + "the packages can be removed using " DSELECT " or " DPKG " --remove:\n" + }, { + 0 + } +}; + +static void describebriefly(struct pkginfo *pkg) { + int maxl, l; + const char *pdesc; + + maxl= 57; + l= strlen(pkg->name); + if (l>20) maxl -= (l-20); + limiteddescription(pkg,maxl,&pdesc,&l); + printf(" %-20s %.*s\n",pkg->name,l,pdesc); +} + +void audit(const char *const *argv) { + struct pkgiterator *it; + struct pkginfo *pkg; + const struct badstatinfo *bsi; + int head; + + if (*argv) badusage("--audit does not take any arguments"); + + modstatdb_init(admindir,msdbrw_readonly); + + for (bsi= badstatinfos; bsi->yesno; bsi++) { + head= 0; + it= iterpkgstart(); + while ((pkg= iterpkgnext(it))) { + if (!bsi->yesno(pkg,bsi)) continue; + if (!head) { + fputs(bsi->explanation,stdout); + head= 1; + } + describebriefly(pkg); + } + iterpkgend(it); + if (head) putchar('\n'); + } + if (ferror(stderr)) werr("stderr"); +} + +struct sectionentry { + struct sectionentry *next; + const char *name; + int count; +}; + +static int yettobeunpacked(struct pkginfo *pkg, const char **thissect) { + if (pkg->want != want_install) return 0; + + switch (pkg->status) { + case stat_unpacked: case stat_installed: case stat_halfconfigured: + return 0; + case stat_notinstalled: case stat_halfinstalled: case stat_configfiles: + if (thissect) + *thissect= pkg->section && *pkg->section ? pkg->section : ""; + return 1; + default: + internerr("unknown status checking for unpackedness"); + } +} + +void unpackchk(const char *const *argv) { + int totalcount, sects; + struct sectionentry *sectionentries, *se, **sep; + struct pkgiterator *it; + struct pkginfo *pkg; + const char *thissect; + char buf[20]; + int width; + + if (*argv) badusage("--yet-to-unpack does not take any arguments"); + + modstatdb_init(admindir,msdbrw_readonly); + + totalcount= 0; + sectionentries= 0; + sects= 0; + it= iterpkgstart(); + while ((pkg= iterpkgnext(it))) { + if (!yettobeunpacked(pkg, &thissect)) continue; + for (se= sectionentries; se && strcasecmp(thissect,se->name); se= se->next); + if (!se) { + se= nfmalloc(sizeof(struct sectionentry)); + for (sep= §ionentries; + *sep && strcasecmp(thissect,(*sep)->name) > 0; + sep= &(*sep)->next); + se->name= thissect; + se->count= 0; + se->next= *sep; + *sep= se; + sects++; + } + se->count++; totalcount++; + } + iterpkgend(it); + + if (totalcount == 0) exit(0); + + if (totalcount <= 12) { + it= iterpkgstart(); + while ((pkg= iterpkgnext(it))) { + if (!yettobeunpacked(pkg,0)) continue; + describebriefly(pkg); + } + iterpkgend(it); + } else if (sects <= 12) { + for (se= sectionentries; se; se= se->next) { + sprintf(buf,"%d",se->count); + printf(" %d in %s: ",se->count,se->name); + width= 70-strlen(se->name)-strlen(buf); + while (width > 59) { putchar(' '); width--; } + it= iterpkgstart(); + while ((pkg= iterpkgnext(it))) { + if (!yettobeunpacked(pkg,&thissect)) continue; + if (strcasecmp(thissect,se->name)) continue; + width -= strlen(pkg->name); width--; + if (width < 4) { printf(" ..."); break; } + printf(" %s",pkg->name); + } + iterpkgend(it); + putchar('\n'); + } + } else { + printf(" %d packages, from the following sections:",totalcount); + width= 0; + for (se= sectionentries; se; se= se->next) { + sprintf(buf,"%d",se->count); + width -= (6 + strlen(se->name) + strlen(buf)); + if (width < 0) { putchar('\n'); width= 73 - strlen(se->name) - strlen(buf); } + printf(" %s (%d)",se->name,se->count); + } + putchar('\n'); + } + fflush(stdout); + if (ferror(stdout)) werr("stdout"); +} + +static int searchoutput(struct filenamenode *namenode) { + int found, i; + struct filepackages *packageslump; + + found= 0; + for (packageslump= namenode->packages; + packageslump; + packageslump= packageslump->more) { + for (i=0; i < PERFILEPACKAGESLUMP && packageslump->pkgs[i]; i++) { + if (found) fputs(", ",stdout); + fputs(packageslump->pkgs[i]->name,stdout); + found++; + } + } + if (found) printf(": %s\n",namenode->name); + return found; +} + +void searchfiles(const char *const *argv) { + struct filenamenode *namenode; + struct fileiterator *it; + const char *thisarg; + int found; + static struct varbuf vb; + + if (!*argv) + badusage("--search needs at least one file name pattern argument"); + + modstatdb_init(admindir,msdbrw_readonly); + ensure_allinstfiles_available_quiet(); + + while ((thisarg= *argv++) != 0) { + found= 0; + if (!strchr("*[?/",*thisarg)) { + varbufreset(&vb); + varbufaddc(&vb,'*'); + varbufaddstr(&vb,thisarg); + varbufaddc(&vb,'*'); + varbufaddc(&vb,0); + thisarg= vb.buf; + } + if (strcspn(thisarg,"*[?\\") == strlen(thisarg)) { + namenode= findnamenode(thisarg); + found += searchoutput(namenode); + } else { + it= iterfilestart(); + while ((namenode= iterfilenext(it)) != 0) { + if (fnmatch(thisarg,namenode->name,0)) continue; + found+= searchoutput(namenode); + } + iterfileend(it); + } + if (!found) { + fprintf(stderr,DPKG ": %s not found.\n",thisarg); + if (ferror(stderr)) werr("stderr"); + } else { + if (ferror(stdout)) werr("stdout"); + } + } +} + +void enqperpackage(const char *const *argv) { + int failures; + const char *thisarg; + struct fileinlist *file; + struct pkginfo *pkg; + + if (!*argv) + badusage("--%s needs at least one package name argument", cipaction->olong); + + failures= 0; + modstatdb_init(admindir,msdbrw_readonly); + + while ((thisarg= *argv++) != 0) { + pkg= findpackage(thisarg); + + switch (cipaction->arg) { + + case act_status: + if (pkg->status == stat_notinstalled && + pkg->priority == pri_unknown && + !(pkg->section && *pkg->section) && + !pkg->files && + pkg->want == want_unknown && + !informativeperfile(&pkg->installed)) { + printf("Package `%s' is not installed and no info is available.\n",pkg->name); + failures++; + } else { + writerecord(stdout, "", pkg, &pkg->installed); + } + break; + + case act_listfiles: + switch (pkg->status) { + case stat_notinstalled: + printf("Package `%s' is not installed.\n",pkg->name); + failures++; + break; + + default: + ensure_packagefiles_available(pkg); + file= pkg->clientdata->files; + if (!file) { + printf("Package `%s' does not contain any files (!)\n",pkg->name); + } else { + while (file) { + puts(file->namenode->name); + file= file->next; + } + } + break; + } + break; + + default: + internerr("unknown action"); + } + + putchar('\n'); + if (ferror(stdout)) werr("stdout"); + } + + if (failures) { + puts("Use " DPKG " --info (= " BACKEND " --info) to examine archive files,\n" + "and " DPKG " --contents (= " BACKEND " --contents) to list their contents."); + if (ferror(stdout)) werr("stdout"); + } +} + +void assertsupportpredepends(const char *const *argv) { + struct pkginfo *pkg; + + if (*argv) badusage("--assert-support-predepends does not take any arguments"); + + modstatdb_init(admindir,msdbrw_readonly); + pkg= findpackage("dpkg"); + + switch (pkg->status) { + case stat_installed: + break; + case stat_unpacked: case stat_halfconfigured: + if (versionsatisfied5(pkg->configversion,pkg->configrevision, + "1.1.0","", dvr_laterequal)) + break; + printf("Version of dpkg with Pre-Depends support not yet configured.\n" + " Please use `dpkg --configure dpkg', and then try again.\n"); + exit(1); + default: + printf("dpkg not recorded as installed, cannot check for Pre-Depends support !\n"); + exit(1); + } +} + +void predeppackage(const char *const *argv) { + /* Print a single package which: + * (a) is the target of one or more relevant predependencies. + * (b) has itself no unsatisfied pre-dependencies. + * If such a package is present output is the Packages file entry, + * which can be massaged as appropriate. + * Exit status: + * 0 = a package printed, OK + * 1 = no suitable package available + * 2 = error + */ + static struct varbuf vb; + + struct pkgiterator *it; + struct pkginfo *pkg, *startpkg, *trypkg; + struct dependency *dep; + struct deppossi *possi, *provider; + + if (*argv) badusage("--predep-package does not take any argument"); + + modstatdb_init(admindir,msdbrw_readonly); + clear_istobes(); /* We use clientdata->istobe to detect loops */ + + for (it=iterpkgstart(), dep=0; + !dep && (pkg=iterpkgnext(it)); + ) { + if (pkg->want != want_install) continue; /* Ignore packages user doesn't want */ + if (!pkg->files) continue; /* Ignore packages not available */ + pkg->clientdata->istobe= itb_preinstall; + for (dep= pkg->available.depends; dep; dep= dep->next) { + if (dep->type != dep_predepends) continue; + if (depisok(dep,&vb,0,1)) continue; + break; /* This will leave dep non-NULL, and so exit the loop. */ + } + pkg->clientdata->istobe= itb_normal; + /* If dep is NULL we go and get the next package. */ + } + if (!dep) exit(1); /* Not found */ + assert(pkg); + startpkg= pkg; + pkg->clientdata->istobe= itb_preinstall; + + /* OK, we have found an unsatisfied predependency. + * Now go and find the first thing we need to install, as a first step + * towards satisfying it. + */ + do { + /* We search for a package which would satisfy dep, and put it in pkg */ + for (possi=dep->list, pkg=0; + !pkg && possi; + possi=possi->next) { + trypkg= possi->ed; + if (!trypkg->available.valid) continue; + if (trypkg->files && versionsatisfied(&trypkg->available,possi)) { + if (trypkg->clientdata->istobe == itb_normal) { pkg= trypkg; break; } + } + if (possi->verrel != dvr_none) continue; + for (provider=possi->ed->available.depended; + !pkg && provider; + provider=provider->next) { + if (provider->up->type != dep_provides) continue; + trypkg= provider->up->up; + if (!trypkg->available.valid || !trypkg->files) continue; + if (trypkg->clientdata->istobe == itb_normal) { pkg= trypkg; break; } + } + } + if (!pkg) { + varbufreset(&vb); + describedepcon(&vb,dep); + varbufaddc(&vb,0); + fprintf(stderr, DPKG ": cannot see how to satisfy pre-dependency:\n %s\n",vb.buf); + ohshit("cannot satisfy pre-dependencies for %.250s (wanted due to %.250s)", + dep->up->name,startpkg->name); + } + pkg->clientdata->istobe= itb_preinstall; + for (dep= pkg->available.depends; dep; dep= dep->next) { + if (dep->type != dep_predepends) continue; + if (depisok(dep,&vb,0,1)) continue; + break; /* This will leave dep non-NULL, and so exit the loop. */ + } + } while (dep); + + /* OK, we've found it - pkg has no unsatisfied pre-dependencies ! */ + writerecord(stdout,"",pkg,&pkg->available); + if (fflush(stdout)) werr("stdout"); +} + +static void badlgccfn(const char *compiler, const char *output, const char *why) + NONRETURNING; +static void badlgccfn(const char *compiler, const char *output, const char *why) { + fprintf(stderr, + DPKG ": unexpected output from `%s --print-libgcc-file-name':\n" + " `%s'\n", + compiler,output); + ohshit("compiler libgcc filename not understood: %.250s",why); +} + +void printarchitecture(const char *const *argv) { + static const struct { const char *from, *to; } archtable[]= { +#include "archtable.inc" + { 0,0 } + }, *archp; + + const char *ccompiler, *arch; + int p1[2], c; + pid_t c1; + FILE *ccpipe; + struct varbuf vb; + ptrdiff_t ll; + char *p, *q; + + ccompiler= getenv("CC"); + if (!ccompiler) ccompiler= "gcc"; + varbufinit(&vb); + m_pipe(p1); + ccpipe= fdopen(p1[0],"r"); if (!ccpipe) ohshite("failed to fdopen CC pipe"); + if (!(c1= m_fork())) { + m_dup2(p1[1],1); close(p1[0]); close(p1[1]); + execlp(ccompiler,ccompiler,"--print-libgcc-file-name",(char*)0); + ohshite("failed to exec C compiler `%.250s'",ccompiler); + } + close(p1[1]); + while ((c= getc(ccpipe)) != EOF) varbufaddc(&vb,c); + if (ferror(ccpipe)) ohshite("error reading from CC pipe"); + waitsubproc(c1,"gcc --print-libgcc-file-name",0); + if (!vb.used) badlgccfn(ccompiler,"","empty output"); + varbufaddc(&vb,0); + if (vb.buf[vb.used-2] != '\n') badlgccfn(ccompiler,vb.buf,"no newline"); + vb.used-= 2; varbufaddc(&vb,0); + p= strstr(vb.buf,"/gcc-lib/"); + if (!p) badlgccfn(ccompiler,vb.buf,"no gcc-lib component"); + p+= 9; + q= strchr(p,'-'); if (!q) badlgccfn(ccompiler,vb.buf,"no hyphen after gcc-lib"); + ll= q-p; + for (archp=archtable; + archp->from && !(strlen(archp->from) == ll && !strncmp(archp->from,p,ll)); + archp++); + arch= archp->to; + if (!arch) { + *q= 0; arch= p; + fprintf(stderr,DPKG ": warning, architecture `%s' not in remapping table\n",arch); + } + if (printf("%s\n",arch) == EOF) werr("stdout"); + if (fflush(stdout)) werr("stdout"); +} diff --git a/main/errors.c b/main/errors.c new file mode 100644 index 00000000..b4064b2d --- /dev/null +++ b/main/errors.c @@ -0,0 +1,111 @@ +/* + * dpkg - main program for package management + * main.c - main program + * + * Copyright (C) 1994,1995 Ian Jackson + * + * This 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, + * or (at your option) any later version. + * + * This 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 dpkg; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "dpkg.h" +#include "dpkg-db.h" +#include "myopt.h" + +#include "main.h" + +int nerrs= 0; + +struct error_report { + struct error_report *next; + const char *what; +}; + +static struct error_report *reports=0; +static struct error_report **lastreport= &reports; +static struct error_report emergency; + +void print_error_perpackage(const char *emsg, const char *arg) { + struct error_report *nr; + + fprintf(stderr, DPKG ": error processing %s (--%s):\n %s\n", + arg, cipaction->olong, emsg); + nr= malloc(sizeof(struct error_report)); + if (!nr) { + perror(DPKG ": failed to allocate memory for new entry in list of failed packages."); + onerr_abort++; + nr= &emergency; + } + nr->what= arg; + nr->next= 0; + *lastreport= nr; + lastreport= &nr->next; + + if (nerrs++ < 20) return; + fprintf(stderr, DPKG ": too many errors, stopping\n"); + onerr_abort++; +} + +int reportbroken_retexitstatus(void) { + if (reports) { + fputs("Errors were encountered while processing:\n",stderr); + while (reports) { + fprintf(stderr," %s\n",reports->what); + reports= reports->next; + } + } + if (onerr_abort) { + fputs("Processing was halted because there were too many errors.\n",stderr); + } + return nerrs || onerr_abort ? 1 : 0; +} + +int skip_due_to_hold(struct pkginfo *pkg) { + if (!(pkg->eflag & eflagf_hold)) return 0; + if (fc_hold) { + fprintf(stderr, "Package %s was on hold, processing it anyway as you request\n", + pkg->name); + return 0; + } + printf("Package %s is on hold, not touching it. Use --force-hold to override.\n", + pkg->name); + return 1; +} + +void forcibleerr(int forceflag, const char *fmt, ...) { + va_list al; + va_start(al,fmt); + if (forceflag) { + fputs(DPKG " - warning, overriding problem because you used --force:\n ",stderr); + vfprintf(stderr,fmt,al); + fputc('\n',stderr); + } else { + ohshitv(fmt,al); + } + va_end(al); +} diff --git a/main/filesdb.c b/main/filesdb.c new file mode 100644 index 00000000..642af368 --- /dev/null +++ b/main/filesdb.c @@ -0,0 +1,649 @@ +/* + * dpkg - main program for package management + * filesdb.c - management of database of files installed on system + * + * Copyright (C) 1995 Ian Jackson + * + * This 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, + * or (at your option) any later version. + * + * This 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 dpkg; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include + +#include "config.h" +#include "dpkg.h" +#include "dpkg-db.h" + +#include "filesdb.h" +#include "main.h" + +/*** Generic data structures and routines ***/ + +static int allpackagesdone= 0; +static int nfiles= 0; +static struct diversion *diversions= 0; +static FILE *diversionsfile= 0; + +void note_must_reread_files_inpackage(struct pkginfo *pkg) { + allpackagesdone= 0; + ensure_package_clientdata(pkg); + pkg->clientdata->fileslistvalid= 0; +} + +static int saidread=0; + +void ensure_packagefiles_available(struct pkginfo *pkg) { + static struct varbuf fnvb; + static char stdiobuf[8192]; + + FILE *file; + const char *filelistfile; + struct fileinlist **lendp, *newent, *current; + struct filepackages *packageslump; + int search, findlast, putat, l; + char *thefilename; + char linebuf[1024]; + + if (pkg->clientdata && pkg->clientdata->fileslistvalid) return; + ensure_package_clientdata(pkg); + + /* Throw away any the stale data, if there was any. */ + for (current= pkg->clientdata->files; + current; + current= current->next) { + /* For each file that used to be in the package, + * go through looking for this package's entry in the list + * of packages containing this file, and blank it out. + */ + for (packageslump= current->namenode->packages; + packageslump; + packageslump= packageslump->more) + for (search= 0; + search < PERFILEPACKAGESLUMP && packageslump->pkgs[search]; + search++) + if (packageslump->pkgs[search] == pkg) { + /* Hah! Found it. */ + for (findlast= search+1; + findlast < PERFILEPACKAGESLUMP && packageslump->pkgs[findlast]; + findlast++); + findlast--; + /* findlast is now the last occupied entry, which may be the same as + * search. We blank out the entry for this package. We also + * have to copy the last entry into the empty slot, because + * the list is null-pointer-terminated. + */ + packageslump->pkgs[search]= packageslump->pkgs[findlast]; + packageslump->pkgs[findlast]= 0; + /* This may result in an empty link in the list. This is OK. */ + goto xit_search_to_delete_from_perfilenodelist; + } + xit_search_to_delete_from_perfilenodelist: + ; + /* The actual filelist links were allocated using nfmalloc, so + * we shouldn't free them. + */ + } + pkg->clientdata->files= 0; + + /* Packages which aren't installed don't have a files list. */ + if (pkg->status == stat_notinstalled) { + pkg->clientdata->fileslistvalid= 1; return; + } + + filelistfile= pkgadminfile(pkg,LISTFILE); + + onerr_abort++; + + file= fopen(filelistfile,"r"); + + if (!file) { + if (errno != ENOENT) + ohshite("unable to open files list file for package `%.250s'",pkg->name); + onerr_abort--; + if (pkg->status != stat_configfiles) { + if (saidread == 1) putc('\n',stderr); + fprintf(stderr, + DPKG ": serious warning: files list file for package `%.250s' missing," + " assuming package has no files currently installed.\n", pkg->name); + } + pkg->clientdata->files= 0; + pkg->clientdata->fileslistvalid= 1; + return; + } + + push_cleanup(cu_closefile,ehflag_bombout, 0,0, 1,(void*)file); + + if (setvbuf(file,stdiobuf,_IOFBF,sizeof(stdiobuf))) + ohshite("unable to set buffering on `%.250s'",filelistfile); + + lendp= &pkg->clientdata->files; + varbufreset(&fnvb); + while (fgets(linebuf,sizeof(linebuf),file)) { + /* This is a very important loop, and it is therefore rather messy. + * We break the varbuf abstraction even more than usual, and we + * avoid copying where possible. + */ + l= strlen(linebuf); + if (l == 0) ohshit("fgets gave an empty null-terminated string from `%.250s'", + filelistfile); + l--; + if (linebuf[l] != '\n') { + varbufaddstr(&fnvb,linebuf); + continue; + } else if (!fnvb.used && l>0 && linebuf[l-1] != '/') { /* fast path */ + linebuf[l]= 0; + thefilename= linebuf; + } else { + if (l>0 && linebuf[l-1] == '/') l--; /* strip trailing slashes */ + linebuf[l]= 0; + varbufaddstr(&fnvb,linebuf); + varbufaddc(&fnvb,0); + fnvb.used= 0; + thefilename= fnvb.buf; + } + if (!*thefilename) + ohshit("files list file for package `%.250s' contains empty filename",pkg->name); + newent= nfmalloc(sizeof(struct fileinlist)); + newent->namenode= findnamenode(thefilename); + newent->next= 0; + *lendp= newent; + lendp= &newent->next; + } + if (ferror(file)) + ohshite("error reading files list file for package `%.250s'",pkg->name); + pop_cleanup(ehflag_normaltidy); /* file= fopen() */ + if (fclose(file)) + ohshite("error closing files list file for package `%.250s'",pkg->name); + if (fnvb.used) + ohshit("files list file for package `%.250s' is truncated",pkg->name); + + onerr_abort--; + + for (newent= pkg->clientdata->files; newent; newent= newent->next) { + packageslump= newent->namenode->packages; + if (packageslump) { + for (putat= 0; + putat < PERFILEPACKAGESLUMP && packageslump->pkgs[putat]; + putat++); + if (putat >= PERFILEPACKAGESLUMP) packageslump= 0; + } + if (!packageslump) { + packageslump= nfmalloc(sizeof(struct filepackages)); + packageslump->more= newent->namenode->packages; + newent->namenode->packages= packageslump; + putat= 0; + } + packageslump->pkgs[putat]= pkg; + if (++putat < PERFILEPACKAGESLUMP) packageslump->pkgs[putat]= 0; + } + pkg->clientdata->fileslistvalid= 1; +} + +void ensure_allinstfiles_available(void) { + struct pkgiterator *it; + struct pkginfo *pkg; + + if (allpackagesdone) return; + if (saidread<2) { + saidread=1; + printf(f_largemem>0 ? "(Reading database ... " : "(Scanning database ... "); + } + it= iterpkgstart(); + while ((pkg= iterpkgnext(it)) != 0) ensure_packagefiles_available(pkg); + iterpkgend(it); + allpackagesdone= 1; + + if (saidread==1) { + printf("%d files and directories currently installed.)\n",nfiles); + saidread=2; + } +} + +void ensure_allinstfiles_available_quiet(void) { + saidread=2; + ensure_allinstfiles_available(); +} + +void write_filelist_except(struct pkginfo *pkg, struct fileinlist *list, int leaveout) { + /* If leaveout is nonzero, will not write any file whose filenamenode + * has the fnnf_elide_other_lists flag set. + */ + static struct varbuf vb, newvb; + FILE *file; + + varbufreset(&vb); + varbufaddstr(&vb,admindir); + varbufaddstr(&vb,"/" INFODIR); + varbufaddstr(&vb,pkg->name); + varbufaddstr(&vb,"." LISTFILE); + varbufaddc(&vb,0); + + varbufreset(&newvb); + varbufaddstr(&newvb,vb.buf); + varbufaddstr(&newvb,NEWDBEXT); + varbufaddc(&newvb,0); + + file= fopen(newvb.buf,"w+"); + if (!file) + ohshite("unable to create updated files list file for package %s",pkg->name); + push_cleanup(cu_closefile,ehflag_bombout, 0,0, 1,(void*)file); + while (list) { + if (!(leaveout && (list->namenode->flags & fnnf_elide_other_lists))) { + fputs(list->namenode->name,file); + putc('\n',file); + } + list= list->next; + } + if (ferror(file)) + ohshite("failed to write to updated files list file for package %s",pkg->name); + if (fflush(file)) + ohshite("failed to flush updated files list file for package %s",pkg->name); + if (fsync(fileno(file))) + ohshite("failed to sync updated files list file for package %s",pkg->name); + pop_cleanup(ehflag_normaltidy); /* file= fopen() */ + if (fclose(file)) + ohshite("failed to close updated files list file for package %s",pkg->name); + if (rename(newvb.buf,vb.buf)) + ohshite("failed to install updated files list file for package %s",pkg->name); + + note_must_reread_files_inpackage(pkg); +} + +void reversefilelist_init(struct reversefilelistiter *iterptr, + struct fileinlist *files) { + /* Initialises an iterator that appears to go through the file + * list `files' in reverse order, returning the namenode from + * each. What actually happens is that we walk the list here, + * building up a reverse list, and then peel it apart one + * entry at a time. + */ + struct fileinlist *newent; + + iterptr->todo= 0; + while (files) { + newent= m_malloc(sizeof(struct fileinlist)); + newent->namenode= files->namenode; + newent->next= iterptr->todo; + iterptr->todo= newent; + files= files->next; + } +} + +struct filenamenode *reversefilelist_next(struct reversefilelistiter *iterptr) { + struct filenamenode *ret; + struct fileinlist *todo; + + todo= iterptr->todo; + if (!todo) return 0; + ret= todo->namenode; + iterptr->todo= todo->next; + free(todo); + return ret; +} + +void reversefilelist_abort(struct reversefilelistiter *iterptr) { + /* Clients must call this function to clean up the reversefilelistiter + * if they wish to break out of the iteration before it is all done. + * Calling this function is not necessary if reversefilelist_next has + * been called until it returned 0. + */ + while (reversefilelist_next(iterptr)); +} + +void ensure_diversions(void) { + static struct varbuf vb; + + struct stat stab1, stab2; + char linebuf[MAXDIVERTFILENAME]; + FILE *file; + struct diversion *ov, *oicontest, *oialtname; + int l; + + varbufreset(&vb); + varbufaddstr(&vb,admindir); + varbufaddstr(&vb,"/" DIVERSIONSFILE); + varbufaddc(&vb,0); + + onerr_abort++; + + file= fopen(vb.buf,"r"); + if (!file) { + if (errno != ENOENT) ohshite("failed to open diversions file"); + if (!diversionsfile) { onerr_abort--; return; } + } else if (diversionsfile) { + if (fstat(fileno(diversionsfile),&stab1)) + ohshite("failed to fstat previous diversions file"); + if (fstat(fileno(file),&stab2)) + ohshite("failed to fstat diversions file"); + if (stab1.st_dev == stab2.st_dev && stab1.st_ino == stab2.st_ino) { + fclose(file); onerr_abort--; return; + } + } + if (diversionsfile) fclose(diversionsfile); + diversionsfile= file; + + for (ov= diversions; ov; ov= ov->next) { + ov->useinstead->divert= 0; + ov->camefrom->divert= 0; + } + if (!file) { onerr_abort--; return; } + + while (fgets(linebuf,sizeof(linebuf),file)) { + oicontest= nfmalloc(sizeof(struct diversion)); + oialtname= nfmalloc(sizeof(struct diversion)); + + l= strlen(linebuf); + if (l == 0) ohshit("fgets gave an empty string from diversions [i]"); + if (linebuf[--l] != '\n') ohshit("diversions file has too-long line or EOF [i]"); + linebuf[l]= 0; + oialtname->camefrom= findnamenode(linebuf); + + if (!fgets(linebuf,sizeof(linebuf),file)) + if (ferror(file)) ohshite("read error in diversions [ii]"); + else ohshit("unexpected EOF in diversions [ii]"); + l= strlen(linebuf); + if (l == 0) ohshit("fgets gave an empty string from diversions [ii]"); + if (linebuf[--l] != '\n') ohshit("diversions file has too-long line or EOF [ii]"); + linebuf[l]= 0; + oicontest->useinstead= findnamenode(linebuf); + + if (!fgets(linebuf,sizeof(linebuf),file)) + if (ferror(file)) ohshite("read error in diversions [iii]"); + else ohshit("unexpected EOF in diversions [iii]"); + l= strlen(linebuf); + if (l == 0) ohshit("fgets gave an empty string from diversions [iii]"); + if (linebuf[--l] != '\n') ohshit("diversions file has too-long line or EOF [ii]"); + linebuf[l]= 0; + + oicontest->pkg= oialtname->pkg= + strcmp(linebuf,":") ? findpackage(linebuf) : 0; + + if (oialtname->camefrom->divert || oicontest->useinstead->divert) + ohshit("conflicting diversions involving `%.250s' or `%.250s'", + oicontest->camefrom->name, oicontest->useinstead->name); + + oialtname->camefrom->divert= oicontest; + oicontest->useinstead->divert= oialtname; + } + if (ferror(file)) ohshite("read error in diversions [i]"); + + diversionsfile= file; + onerr_abort--; +} + +/*** Data structures common to both in-core databases ***/ + +struct fileiterator { + union { + struct { + struct filenamenode *current; + } low; + struct { + struct filenamenode *namenode; + int nbinn; + } high; + } u; +}; + +/*** Data structures for fast, large memory usage database ***/ + +#define BINS (1 << 13) + /* This must always be a power of two. If you change it + * consider changing the per-character hashing factor (currently + * 1785 = 137*13) too. + */ + +static struct filenamenode *bins[BINS]; + +/*** Data structures for low-memory-footprint in-core files database ***/ + +struct fdirents { + struct fdirents *more; + struct { const char *component; struct fdirnode *go; } entries[1]; + /* first one has one entry, then two, then four, &c */ +}; + +struct fdirnode { + struct filenamenode *here; + struct fdirents *contents; +}; + +static struct fdirnode fdirroot; +static struct filenamenode *allfiles; + +struct filenamesblock { + struct filenamesblock *next; + char *data; +}; + +static struct filenamesblock *namesarea= 0; +static int namesarealeft= 0; + +/*** Code for both. This is rather hacky, sorry ... ***/ + +struct fileiterator *iterfilestart(void) { + struct fileiterator *i; + i= m_malloc(sizeof(struct fileiterator)); + switch (f_largemem) { + case 1: + i->u.high.namenode= 0; + i->u.high.nbinn= 0; + break; + case -1: + i->u.low.current= allfiles; + break; + default: + internerr("iterfilestart no f_largemem"); + } + return i; +} + +struct filenamenode *iterfilenext(struct fileiterator *i) { + struct filenamenode *r; + switch (f_largemem) { + case 1: + while (!i->u.high.namenode) { + if (i->u.high.nbinn >= BINS) return 0; + i->u.high.namenode= bins[i->u.high.nbinn++]; + } + r= i->u.high.namenode; + i->u.high.namenode= r->next; + break; + case -1: + if (!i->u.low.current) return 0; + r= i->u.low.current; + i->u.low.current= i->u.low.current->next; + break; + default: + internerr("iterfilenext no f_largemem"); + } + return r; +} + +void iterfileend(struct fileiterator *i) { + free(i); +} + +void filesdbinit(void) { + struct filenamenode *fnn; + struct sysinfo info; + int i; + + if (!f_largemem) { + f_largemem= -1; + if (!sysinfo(&info)) { + if (info.freeram + (info.sharedram>>2) + (info.bufferram>>2) >= 4096*1024 || + info.totalram >= 6144) + f_largemem= 1; + } + } + + switch (f_largemem) { + case 1: + for (i=0; inext) { + fnn->flags= 0; + fnn->oldhash= 0; + } + break; + case -1: + for (fnn= allfiles; + fnn; + fnn= fnn->next) { + fnn->flags= 0; + fnn->oldhash= 0; + } + break; + default: + internerr("filesdbinit no f_largemem"); + } +} + +static struct filenamenode *findnamenode_high(const char *name); +static struct filenamenode *findnamenode_low(const char *name); + +struct filenamenode *findnamenode(const char *name) { + switch (f_largemem) { + case 1: + return findnamenode_high(name); + case -1: + return findnamenode_low(name); + default: + internerr("findnamenode no f_largemem"); + } +} + +/*** Code for low-memory-footprint in-core files database ***/ + +static struct filenamenode *findnamenode_low(const char *name) { + struct fdirnode *traverse; + struct fdirents *ents, **addto; + const char *nameleft, *slash; + char *p; + struct filenamesblock *newblock; + int n, i, nentrieshere, alloc; + + /* We skip initial slashes and ./ pairs, and add our own single leading slash. */ + name= skip_slash_dotslash(name); + + nameleft= name; + traverse= &fdirroot; + while (nameleft) { + slash= strchr(nameleft,'/'); + if (slash) { + n= (int)(slash-nameleft); slash++; + } else { + n= strlen(nameleft); + } + ents= traverse->contents; addto= &traverse->contents; i= 0; nentrieshere= 1; + for (;;) { + if (!ents) break; + if (!ents->entries[i].component) break; + if (!strncmp(ents->entries[i].component,nameleft,n) && + !ents->entries[i].component[n]) { + break; + } + i++; + if (i < nentrieshere) continue; + addto= &ents->more; + ents= ents->more; + nentrieshere += nentrieshere; + i=0; + } + if (!ents) { + ents= nfmalloc(sizeof(struct fdirents) + + sizeof(ents->entries[0])*(nentrieshere-1)); + i=0; + ents->entries[i].component= 0; + ents->more= 0; + *addto= ents; + } + if (!ents->entries[i].component) { + p= nfmalloc(n+1); + memcpy(p,nameleft,n); p[n]= 0; + ents->entries[i].component= p; + ents->entries[i].go= nfmalloc(sizeof(struct fdirnode)); + ents->entries[i].go->here= 0; + ents->entries[i].go->contents= 0; + if (i+1 < nentrieshere) + ents->entries[i+1].component= 0; + } + traverse= ents->entries[i].go; + nameleft= slash; + } + if (traverse->here) return traverse->here; + + traverse->here= nfmalloc(sizeof(struct filenamenode)); + traverse->here->packages= 0; + traverse->here->flags= 0; + traverse->here->divert= 0; + + n= strlen(name)+2; + if (namesarealeft < n) { + newblock= m_malloc(sizeof(struct filenamesblock)); + alloc= 256*1024; + if (allocdata= m_malloc(alloc); + newblock->next= namesarea; + namesarea= newblock; + namesarealeft= alloc; + } + namesarealeft-= n; + p= namesarea->data+namesarealeft; + traverse->here->name= p; *p++= '/'; strcpy(p,name); + + traverse->here->next= allfiles; + allfiles= traverse->here; + nfiles++; + return traverse->here; +} + +/*** Code for high memory usage fast database ***/ + +static int hash(const char *name) { + int v= 0; + while (*name) { v *= 1785; v += *name; name++; } + return v; +} + +struct filenamenode *findnamenode_high(const char *name) { + struct filenamenode **pointerp, *newnode; + + /* We skip initial slashes and ./ pairs, and add our own single leading slash. */ + name= skip_slash_dotslash(name); + + pointerp= bins + (hash(name) & (BINS-1)); + while (*pointerp) { + assert((*pointerp)->name[0] == '/'); + if (!strcmp((*pointerp)->name+1,name)) break; + pointerp= &(*pointerp)->next; + } + if (*pointerp) return *pointerp; + + newnode= nfmalloc(sizeof(struct filenamenode)); + newnode->packages= 0; + newnode->name= nfmalloc(strlen(name)+2); + newnode->name[0]= '/'; strcpy(newnode->name+1,name); + newnode->flags= 0; + newnode->next= 0; + newnode->divert= 0; + *pointerp= newnode; + nfiles++; + + return newnode; +} diff --git a/main/filesdb.h b/main/filesdb.h new file mode 100644 index 00000000..f650c51f --- /dev/null +++ b/main/filesdb.h @@ -0,0 +1,127 @@ +/* + * dpkg - main program for package management + * filesdb.h - management of database of files installed on system + * + * Copyright (C) 1995 Ian Jackson + * + * This 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, + * or (at your option) any later version. + * + * This 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 dpkg; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef FILESDB_H +#define FILESDB_H + +/* + * Data structure here is as follows: + * + * For each package we have a `struct fileinlist *', the head of + * a list of files in that package. They are in `forwards' order. + * Each entry has a pointer to the `struct filenamenode'. + * + * The struct filenamenodes are in a hash table, indexed by name. + * (This hash table is not visible to callers.) + * + * Each filenamenode has a (possibly empty) list of `struct + * filepackage', giving a list of the packages listing that + * filename. + * + * When we read files contained info about a particular package + * we set the `files' member of the clientdata struct to the + * appropriate thing. When not yet set the files pointer is + * made to point to `fileslist_uninited' (this is available only + * internally, withing filesdb.c - the published interface is + * ensure_*_available). + */ + +struct pkginfo; + +struct filenamenode { + struct filenamenode *next; + char *name; + struct filepackages *packages; + struct diversion *divert; + /* Fields from here on are used by archives.c &c, and cleared by + * filesdbinit. + */ + enum { + fnnf_new_conff= 000001, /* in the newconffiles list */ + fnnf_new_inarchive= 000002, /* in the new filesystem archive */ + fnnf_old_conff= 000004, /* in the old package's conffiles list */ + fnnf_elide_other_lists= 000010, /* must remove from other packages' lists */ + fnnf_no_atomic_overwrite= 000020, /* >=1 instance is a dir, cannot rename over */ + } flags; /* Set to zero when a new node is created. */ + const char *oldhash; /* valid iff this namenode is in the newconffiles list */ +}; + +struct fileinlist { + struct fileinlist *next; + struct filenamenode *namenode; +}; + +struct diversion { + /* When we deal with an `overridden' file, every package except + * the overriding one is considered to contain the other file + * instead. Both files have entries in the filesdb database, and + * they refer to each other via these diversion structures. + * + * The contended filename's filenamenode has an diversion entry + * with useinstead set to point to the redirected filename's + * filenamenode; the redirected filenamenode has camefrom set to the + * contended filenamenode. Both sides' diversion entries will + * have pkg set to the package (if any) which is allowed to use the + * contended filename. + * + * Packages that contain either version of the file will all + * refer to the contended filenamenode in their per-file package lists + * (both in core and on disk). References are redirected to the other + * filenamenode's filename where appropriate. + */ + struct filenamenode *useinstead; + struct filenamenode *camefrom; + struct pkginfo *pkg; + struct diversion *next; + /* The `contested' halves are in this list for easy cleanup. */ +}; + +#define PERFILEPACKAGESLUMP 10 + +struct filepackages { + struct filepackages *more; + struct pkginfo *pkgs[PERFILEPACKAGESLUMP]; + /* pkgs is a null-pointer-terminated list; anything after the first null + * is garbage + */ +}; + +struct fileiterator; +struct fileiterator *iterfilestart(void); +struct filenamenode *iterfilenext(struct fileiterator *i); +void iterfileend(struct fileiterator *i); + +void ensure_diversions(void); + +void ensure_packagefiles_available(struct pkginfo *pkg); +void ensure_allinstfiles_available(void); +void ensure_allinstfiles_available_quiet(void); +void note_must_reread_files_inpackage(struct pkginfo *pkg); +struct filenamenode *findnamenode(const char *filename); +void write_filelist_except(struct pkginfo *pkg, struct fileinlist *list, int leaveout); + +struct reversefilelistiter { struct fileinlist *todo; }; + +void reversefilelist_init(struct reversefilelistiter *iterptr, struct fileinlist *files); +struct filenamenode *reversefilelist_next(struct reversefilelistiter *iterptr); +void reversefilelist_abort(struct reversefilelistiter *iterptr); + +#endif /* FILESDB_H */ diff --git a/main/help.c b/main/help.c new file mode 100644 index 00000000..48378a6c --- /dev/null +++ b/main/help.c @@ -0,0 +1,476 @@ +/* + * dpkg - main program for package management + * help.c - various helper routines + * + * Copyright (C) 1995 Ian Jackson + * + * This 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, + * or (at your option) any later version. + * + * This 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 dpkg; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "dpkg.h" +#include "dpkg-db.h" +#include "myopt.h" + +#include "filesdb.h" +#include "main.h" + +const char *const statusstrings[]= { + "not installed", "unpacked but not configured", + "broken due to postinst failure", + "installed", + "broken due to failed removal", + "not installed but configs remain" +}; + +struct filenamenode *namenodetouse(struct filenamenode *namenode, struct pkginfo *pkg) { + struct filenamenode *r; + + if (!namenode->divert) return namenode; + + debug(dbg_eachfile,"namenodetouse namenode=`%s' pkg=%s", + namenode->name,pkg->name); + + r= + (namenode->divert->useinstead && namenode->divert->pkg != pkg) + ? namenode->divert->useinstead : namenode; + + debug(dbg_eachfile, + "namenodetouse ... useinstead=%s camefrom=%s pkg=%s return %s", + namenode->divert->useinstead ? namenode->divert->useinstead->name : "", + namenode->divert->camefrom ? namenode->divert->camefrom->name : "", + namenode->divert->pkg ? namenode->divert->pkg->name : "", + r->name); + return r; +} + +void checkpath(void) { + static const char *const checklist[]= { + "ldconfig", "start-stop-daemon", "install-info", "update-rc.d", 0 + }; + + struct stat stab; + const char *const *clp; + const char *path, *s, *p; + char buf[PATH_MAX+2]; + int warned= 0; + long l; + + path= getenv("PATH"); + if (!path) fputs(DPKG " - warning: PATH is not set.\n", stderr); + + for (clp=checklist; *clp; clp++) { + s= path; + while (s) { + p= strchr(s,':'); + l= p ? p-s : strlen(s); + if (l+strlen(*clp)+2>sizeof(buf)) continue; + memcpy(buf,s,l); + if (l) buf[l++]= '/'; + strcpy(buf+l,*clp); + if (stat(buf,&stab) == 0 && (stab.st_mode & 0111)) break; + s= p; if (s) s++; + } + if (!s) { + fprintf(stderr,DPKG ": `%s' not found on PATH.\n",*clp); + warned++; + } + } + + if (warned) + forcibleerr(fc_badpath,"%d expected program(s) not found on PATH.\nNB: root's " + "PATH should usually contain /usr/local/sbin, /usr/sbin and /sbin.", + warned); +} + +void ensure_package_clientdata(struct pkginfo *pkg) { + if (pkg->clientdata) return; + pkg->clientdata= nfmalloc(sizeof(struct pkginfoperfile)); + pkg->clientdata->istobe= itb_normal; + pkg->clientdata->fileslistvalid= 0; + pkg->clientdata->files= 0; +} + +void cu_closepipe(int argc, void **argv) { + int *p1= (int*)argv[0]; + close(p1[0]); close(p1[1]); +} + +void cu_closefile(int argc, void **argv) { + FILE *f= (FILE*)(argv[0]); + fclose(f); +} + +void cu_closedir(int argc, void **argv) { + DIR *d= (DIR*)(argv[0]); + closedir(d); +} + +void cu_closefd(int argc, void **argv) { + int *ip= (int*)(argv[0]); + close(*ip); +} + +int ignore_depends(struct pkginfo *pkg) { + struct packageinlist *id; + for (id= ignoredependss; id; id= id->next) + if (id->pkg == pkg) return 1; + return 0; +} + +int force_depends(struct deppossi *possi) { + return fc_depends || + ignore_depends(possi->ed) || + ignore_depends(possi->up->up); +} + +int force_conflicts(struct deppossi *possi) { + return fc_conflicts; +} + +const char *pkgadminfile(struct pkginfo *pkg, const char *whichfile) { + static struct varbuf vb; + varbufreset(&vb); + varbufaddstr(&vb,admindir); + varbufaddstr(&vb,"/" INFODIR); + varbufaddstr(&vb,pkg->name); + varbufaddc(&vb,'.'); + varbufaddstr(&vb,whichfile); + varbufaddc(&vb,0); + return vb.buf; +} + +static void preexecscript(const char *path, char *const *argv) { + if (*instdir) { + /* fixme: won't work right when instdir != admindir */ + if (chroot(instdir)) ohshite("failed to chroot to `%.250s'",instdir); + } + if (f_debug & dbg_scripts) { + fprintf(stderr,"D0%05o: fork/exec %s (",dbg_scripts,path); + while (*argv) fprintf(stderr," %s",*argv++); + fputs(" )\n",stderr); + } +} + +static char *const *vbuildarglist(const char *scriptname, va_list ap) { + static char *bufs[PKGSCRIPTMAXARGS+1]; + char *nextarg; + int i; + + i=0; + bufs[i++]= (char*)scriptname; /* yes, cast away cost because exec wants it that way */ + for (;;) { + assert(i < PKGSCRIPTMAXARGS); + nextarg= va_arg(ap,char*); + if (!nextarg) break; + bufs[i++]= nextarg; + } + bufs[i]= 0; + return bufs; +} + +static char *const *buildarglist(const char *scriptname, ...) { + char *const *arglist; + va_list ap; + va_start(ap,scriptname); + arglist= vbuildarglist(scriptname,ap); + va_end(ap); + return arglist; +} + +#define NSCRIPTCATCHSIGNALS sizeof(script_catchsignallist)/sizeof(int)-1 +static int script_catchsignallist[]= { SIGQUIT, SIGINT, 0 }; +static struct sigaction script_uncatchsignal[NSCRIPTCATCHSIGNALS]; + +static void cu_restorescriptsignals(int argc, void **argv) { + int i; + for (i=0; ist_mode & 0555) == 0555) return; + if (!chmod(path,0755)) return; + ohshite("unable to set execute permissions on `%.250s'",path); +} + +int maintainer_script_installed(struct pkginfo *pkg, const char *scriptname, + const char *description, ...) { + /* all ...'s are const char*'s */ + const char *scriptpath; + char *const *arglist; + struct stat stab; + va_list ap; + int c1; + char buf[100]; + + scriptpath= pkgadminfile(pkg,scriptname); + va_start(ap,description); + arglist= vbuildarglist(scriptname,ap); + va_end(ap); + sprintf(buf,"%s script",description); + + if (stat(scriptpath,&stab)) { + if (errno == ENOENT) { + debug(dbg_scripts,"maintainer_script_installed nonexistent %s",scriptname); + return 0; + } + ohshite("unable to stat installed %s script `%.250s'",description,scriptpath); + } + setexecute(scriptpath,&stab); + c1= m_fork(); + if (!c1) { + preexecscript(scriptpath,arglist); + execv(scriptpath,arglist); + ohshite("unable to execute %s",buf); + } + script_catchsignals(); /* This does a push_cleanup() */ + waitsubproc(c1,buf,0); + pop_cleanup(ehflag_normaltidy); + + ensure_diversions(); + return 1; +} + +int maintainer_script_new(const char *scriptname, const char *description, + const char *cidir, char *cidirrest, ...) { + char *const *arglist; + struct stat stab; + va_list ap; + char buf[100]; + int c1; + + va_start(ap,cidirrest); + arglist= vbuildarglist(scriptname,ap); + va_end(ap); + sprintf(buf,"%s script",description); + + strcpy(cidirrest,scriptname); + if (stat(cidir,&stab)) { + if (errno == ENOENT) { + debug(dbg_scripts,"maintainer_script_new nonexistent %s `%s'",scriptname,cidir); + return 0; + } + ohshite("unable to stat new %s script `%.250s'",description,cidir); + } + setexecute(cidir,&stab); + c1= m_fork(); + if (!c1) { + preexecscript(cidir,arglist); + execv(cidir,arglist); + ohshite("unable to execute new %s",buf); + } + script_catchsignals(); /* This does a push_cleanup() */ + waitsubproc(c1,buf,0); + pop_cleanup(ehflag_normaltidy); + + ensure_diversions(); + return 1; +} + +int maintainer_script_alternative(struct pkginfo *pkg, + const char *scriptname, const char *description, + const char *cidir, char *cidirrest, + const char *ifok, const char *iffallback) { + const char *oldscriptpath; + char *const *arglist; + struct stat stab; + int c1, n, status; + char buf[100]; + pid_t r; + + oldscriptpath= pkgadminfile(pkg,scriptname); + arglist= buildarglist(scriptname, + ifok,versiondescribe(pkg->available.version, + pkg->available.revision), + (char*)0); + sprintf(buf,"old %s script",description); + if (stat(oldscriptpath,&stab)) { + if (errno == ENOENT) { + debug(dbg_scripts,"maintainer_script_alternative nonexistent %s `%s'", + scriptname,oldscriptpath); + return 0; + } + fprintf(stderr, + DPKG ": warning - unable to stat %s `%.250s': %s\n", + buf,oldscriptpath,strerror(errno)); + } else { + setexecute(oldscriptpath,&stab); + c1= m_fork(); + if (!c1) { + preexecscript(oldscriptpath,arglist); + execv(oldscriptpath,arglist); + ohshite("unable to execute %s",buf); + } + script_catchsignals(); /* This does a push_cleanup() */ + while ((r= waitpid(c1,&status,0)) == -1 && errno == EINTR); + if (r != c1) ohshite("wait for %s failed",buf); + pop_cleanup(ehflag_normaltidy); + if (WIFEXITED(status)) { + n= WEXITSTATUS(status); if (!n) return 1; + fprintf(stderr, DPKG ": warning - %s returned error exit status %d\n",buf,n); + } else if (WIFSIGNALED(status)) { + n= WTERMSIG(status); + fprintf(stderr, DPKG ": warning - %s killed by signal (%s)%s\n", + buf, strsignal(n), WCOREDUMP(status) ? ", core dumped" : ""); + } else { + ohshit("%s failed with unknown wait status code %d",buf,status); + } + ensure_diversions(); + } + fprintf(stderr, DPKG " - trying script from the new package instead ...\n"); + + arglist= buildarglist(scriptname, + iffallback,versiondescribe(pkg->installed.version, + pkg->installed.revision), + (char*)0); + strcpy(cidirrest,scriptname); + sprintf(buf,"new %s script",description); + + if (stat(cidir,&stab)) + if (errno == ENOENT) + ohshit("there is no script in the new version of the package - giving up"); + else + ohshite("unable to stat %s `%.250s'",buf,cidir); + + setexecute(cidir,&stab); + + c1= m_fork(); + if (!c1) { + preexecscript(cidir,arglist); + execv(cidir,arglist); + ohshite("unable to execute %s",buf); + } + script_catchsignals(); /* This does a push_cleanup() */ + waitsubproc(c1,buf,0); + pop_cleanup(ehflag_normaltidy); + fprintf(stderr, DPKG ": ... it looks like that went OK.\n"); + + ensure_diversions(); + return 1; +} + +void clear_istobes(void) { + struct pkgiterator *it; + struct pkginfo *pkg; + + it= iterpkgstart(); + while ((pkg= iterpkgnext(it)) != 0) { + ensure_package_clientdata(pkg); + pkg->clientdata->istobe= itb_normal; + pkg->clientdata->replacingfilesandsaid= 0; + } +} + +void debug(int which, const char *fmt, ...) { + va_list ap; + if (!(f_debug & which)) return; + fprintf(stderr,"D0%05o: ",which); + va_start(ap,fmt); + vfprintf(stderr,fmt,ap); + va_end(ap); + putc('\n',stderr); +} + +int isdirectoryinuse(struct filenamenode *file, struct pkginfo *pkg) { + /* Returns 1 if the file is used by packages other than pkg, 0 otherwise. */ + struct filepackages *packageslump; + int i; + + debug(dbg_veryverbose, "isdirectoryinuse `%s' (except %s)", file->name, + pkg ? pkg->name : ""); + for (packageslump= file->packages; packageslump; packageslump= packageslump->more) { + debug(dbg_veryverbose, "isdirectoryinuse packageslump %s ...", + packageslump->pkgs[0] ? packageslump->pkgs[0]->name : ""); + for (i=0; i < PERFILEPACKAGESLUMP && packageslump->pkgs[i]; i++) { + debug(dbg_veryverbose, "isdirectoryinuse considering [%d] %s ...", i, + packageslump->pkgs[i]->name); + if (packageslump->pkgs[i] == pkg) continue; + return 1; + } + } + debug(dbg_veryverbose, "isdirectoryinuse no"); + return 0; +} + +void oldconffsetflags(struct conffile *searchconff) { + struct filenamenode *namenode; + + while (searchconff) { + namenode= findnamenode(searchconff->name); + namenode->flags |= fnnf_old_conff; + debug(dbg_conffdetail, "oldconffsetflags `%s' namenode %p flags %o", + searchconff->name, namenode, namenode->flags); + searchconff= searchconff->next; + } +} + +void ensure_pathname_nonexisting(const char *pathname) { + int c1; + const char *u; + + u= skip_slash_dotslash(pathname); + assert(*u); + + debug(dbg_eachfile,"ensure_pathname_nonexisting `%s'",pathname); + if (!rmdir(pathname)) return; /* Deleted it OK, it was a directory. */ + if (errno == ENOENT || errno == ELOOP) return; + if (errno == ENOTDIR) { + /* Either it's a file, or one of the path components is. If one + * of the path components is this will fail again ... + */ + if (!unlink(pathname)) return; /* OK, it was */ + if (errno == ENOTDIR) return; + } + if (errno != ENOTEMPTY) /* Huh ? */ + ohshite("failed to rmdir/unlink `%.255s'",pathname); + c1= m_fork(); + if (!c1) { + execlp(RM,"rm","-rf","--",pathname,(char*)0); + ohshite("failed to exec " RM " for cleanup"); + } + debug(dbg_eachfile,"ensure_pathname_nonexisting running rm -rf"); + waitsubproc(c1,"rm cleanup",0); +} diff --git a/main/junk b/main/junk new file mode 100644 index 00000000..d5f060d2 --- /dev/null +++ b/main/junk @@ -0,0 +1,398 @@ +if (!strcmp(newpig.name,"dpkg")) +fprintf(stderr,"flags %o recordavailable %o config %s|%s ver %s|%s stat %d\n", + flags,pdb_recordavailable,newpig.configversion,newpig.configrevision, + newpifp->version,newpifp->revision,newpig.status); + + + if (pkg->want != want_install && f_alsoselect) { + printf("Selecting previously deselected package %s.\n",pkg->name); + pkg->want= want_install; + } else if (pkg->want == want_unknown && pkg->priority <= pri_standard && + fc_autoselect) { + printf("Package %s is %s, selecting it by default.\n",pkg->name, + priorityinfos[pkg->priority].name); + pkg->want= want_install; + } else if (pkg->want != want_install) { + printf("Skipping deselected package %s.\n",pkg->name); + pop_cleanup(ehflag_normaltidy); + return; + } + + + +int terminate_catchsignallist[]= { SIGINT, SIGTERM, 0 }; +int noterminal_catchsignallist[]= { SIGHUP, SIGPIPE, 0 }; + +volatile int quitting= 0; + +static void terminate_sighandler(void) { + + +void terminate_catchsignals(void) { + int i; + struct sigaction catchsig; + + catchsig.sa_handler= terminate_signalhandler; + sigemptyset(&catchsig.sa_mask); + for (i=0; terminate_catchsignallist[i]; i++) + sigaddset(&catchsig.sa_mask,terminate_catchsignallist[i]); + for (i=0; noterminal_catchsignallist[i]; i++) + sigaddset(&catchsig.sa_mask,noterminal_catchsignallist[i]); + catchsig.sa_flags= 0; + + for (i=0; terminate_catchsignallist[i]; i++) + if (sigaction(terminate_catchsignallist[i],&catchsig,0)) + ohshite("unable to set up signal handler for %s", + strsignal(terminate_catchsignallist[i])); + + catchsig.sa_handler= noterminal_signalhandler; + + for (i=0; noterminal_catchsignallist[i]; i++) + if (sigaction(noterminal_catchsignallist[i],&catchsig,0)) + ohshite("unable to set up signal handler for %s", + strsignal(noterminal_catchsignallist[i])); +} + + + +#!/bin/sh - + +set -e + +cd /var/lib/dpkg + +# This won't check for unpacked packages if we're upgrading +# from a *really* old dpkg, but they probably didn't do +# conffiles right anyway. +perl -000 -ne 'print $1 if m/^Package:\s+(\S+\n)/im && + $1 ne "dpkg\n" && + m/^Status:.*unpacked/im' \ + /var/lib/dpkg/status >/tmp/bp.$$ +if test -s /tmp/bp.$$ +then + echo ' +WARNING -- SOME PACKAGES IN "UNPACKED" STATE + +Due to a change in the filenames used for installed vs. newly-distributed +configuration files it is NOT SAFE to upgrade to this version of dpkg +from + + + undef %hash; @configfr= @configf= (); + for $_ (split(/\n/,$st_pk2v{$package,'conffiles'})) { + s/^ //; next unless length($_); + if (!m/^(\S+) (-|newconffile|nonexistent|[0-9a-f]{32})$/) { + &warn("$arg: ignoring bad stuff in old conffiles field \`$_'"); + next; + } + unshift(@configfr,$1); push(@configf,$1); + $hash{$1}= $2; + } + + + + undef %oldhash; @configf=(); + for $_ (split(/\n/,$st_pk2v{$package,'conffiles'})) { + s/^ //; next unless length($_); + if (!m/^(\S+) (-|newconffile|nonexistent|[0-9a-f]{32})$/) { + &warn("$arg: ignoring bad stuff in old conffiles field \`$_'"); + next; + } + $oldhash{$1}= $2; push(@configf,$1); + &debug("old hash of $1 is $2"); + } + undef %newhash; + + + + fextract= fdopen(p1[0],"r"); + + varbufinit(&thisname); + while ((c= fgetc(fextract)) != EOF) { + if (c != '\n') { + varbufaddc(&thisdirname,c); + continue; + } + varbufaddc(&thisdirname,0); + /* Right, this next part gets done for each file (or directory) in the + * archive of the new version of the package: + */ + thisfile= filedatarecord(&filedata,thisname.buf); + thisfile->nstat= ifdnew_inarchive; + varbufreset(&thisname); + } + if (ferror(fextract)) ohshite("failed read from " BACKEND " extract pipe"); + if (thisname.used) ohshit("partial filename in output from " BACKEND " extract"); + if (fclose(fextract)) ohshite("failed to close" BACKEND " extract pipe"); + waitsubproc(c1,BACKEND " extract archive",0); + + /* Save new file list, new conffiles &c */ + + if (chdir(instdir)) ohshite("unable to change to installation root directory" + " for archive extract `%.250s'",instdir); + execlp(BACKEND, BACKEND,"--control",filename,cidir,(char*)0); + ohshite("failed to exec " BACKEND " to extract control information"); + + /*** continue here ***/ + +void remove_with_backup(pkginfo *pkg, struct installingfiledata **ifdlisthead) { + if (f_noact) { + printf("(would back up files in `%s', renaming each * to *" DPKGTEMPEXT ")\n", + pkg->name); + return; + } + filesinpackagerev(pkg, &filelist); + for (barefile= filelist; barefile; barefile= barefile->next) { + thisfile= filedatarecord(ifdlisthead,barefile->name); + initostat(thisfile); + if (thisfile->ncoff) continue; + + switch (thisfile->ostat) { + case ifdold_directory: + push_cleanup(, co_olddirectory, ...); + break; + + case ifdold_none: + break; + + case ifdold_file: + l= strlen(barefile->name); + toremove= m_malloc(instdirlen + l + 1); + strcpy(toremove,instdir); strcpy(toremove+instdirlen,barefile->name); + tempname= m_malloc(instdirlen + l + sizeof(DPKGTEMPEXT) + 1); + strcpy(tempname,toremove); strcpy(tempname+instdirlen+l,DPKGTEMPEXT); + ename= toremove + instdirlen; + tename= tempname + instdirlen; + + if (rename(toremove.buf, tempname.buf)) + ohshite("unable to back up file-to-be-removed `%.250s'", tename); + push_cleanup(cu_backupfile, co_backupfile, + ..., toremove,tempname); + break; + + default: + abort(); + } + } +} + + case ifdold_directory: + for (inwhich= findfile(toremove); inwhich; inwhich= inwhich->more) + for (pkgsp= inwhich->pkgs; *pkgsp; pkgsp++) + switch ((*pkgsp)->clientdata->istobe) { + case itb_normal: + goto exit_both_loops_and_the_switch_at_once; + case itb_remove: + case itb_installnew: + /* We're removing or replacing this package, so that doesn't count. + * For packages that we replace the caller will remove the directories + * in the new version from the list of those to delete. + */ + break; + default: + abort(); + } + exit_both_loops_and_the_switch_at_once: + + if (!inwhich) { /* Find it anywhere else ? */ + struct fileinlist newdir; + newdir= m_malloc(sizeof(struct fileinlist)); + newdir->name= m_malloc(strlen(ename)); + strcpy((*addirs)->name,ename); + newdir->next= 0; + *adddirs= newdir; + adddirs= &newdir->next; + } + free(tempname); free(toremove); + continue; + } + + + + l= strlen(pkg->name); + infdir= m_malloc(admindirlen + sizeof(INFODIR) + l + 50); + strcpy(infdir,admindir); + strcpy(infdir+admindirlen,"/" INFODIR); + strcpy(infdir+admindirlen+sizeof(INFODIR), pkg->name); + infdir[admindirlen+sizeof(INFODIR)+l-1]= '.'; + infdirrest= infdir + admindirlen+sizeof(INFODIR)+l; + strcpy(infdirrest, LISTFILE); + strcpy(infdirrest+sizeof(LISTFILE)-1,".new"); + + for (thisfile= filelist; thisfile; thisfile= thisfile->next) { + for (thisconff= conflictor->installed.conffiles; + thisconff && strcmp(thisconff->name,thisfile->name); + thisconff= thisconff->next); + if (thisconff) continue; + varbufreset(&toremove); + varbufaddstr(&toremove, instdir); + varbufaddstr(&toremove, "/"); + ename= toremove.buf + toremove.used; + varbufaddstr(&toremove, thisfile->name); + varbufaddc(&toremove, 0); + if (unlink(toremove.buf)) { + if (errno == EISDIR) { + if (rmdir(toremove.buf) && errno != ENOTEMPTY) + ohshite("%.250s: cannot remove directory `%.250s'", + conflictor->name, ename); + } else if (errno != ENOENT) { + ohshite("%.250s: cannot remove `%.250s'", conflictor->name, ename); + } + } + varbufaddstr(&toremove, DPKGTEMPEXT); + varbufaddc(&toremove, 0); + if (unlink(toremove.buf) && errno != ENOENT) + ohshite("%.250s: cannot remove old backup file `%.250s'", + conflictor->name, ename); + } + + + if (versionsatisfied(possi, &possi->ed.installed, &whynot)) present=1; + *const relatestrings[]= { "may be more useful with", "recommends", + "requires", "conflicts with", "provides" }, + + + if (possi + + + varbufaddstr(&why, dep->up->name); + return 1; + } + } + varbufaddstr(&why, dep->up->name); + return 1; + } + + || + dep->up + questionstatus= + questionstatus= dep->up->clientdata->istobe == + if (up-> + +, + if (questionstatus->status == stat_notinstalled || + questionstatus->status == stat_configfiles) return 0; + switch (dep->type) { + case dep_conflicts: reln= " conflicts with "; break; + case dep_depends: reln= " depends on "; break; + default: return 1; + } + + varbufaddstr(whynot, dep->up->name); + varbufaddstr(whynot, reln); + reln= " "; + + for (possi= dep->list; possi; possi= possi->next) { + varbufaddstr(whynot, reln); + varbufaddstr(whynot, possi->ed->name); + switch (possi->verrel) { + case dvr_none: reln= 0; break; + case dvr_later: reln= " (>"; break; + case dvr_earlier: reln= " (<"; break; + case dvr_exact: reln= " (="; break; + default: abort(); + } + if (reln) { + varbufaddstr(whynot, reln); + varbufaddstr(whynot, possi->version); + if (possi->revision && *possi->revision) { + varbufaddc(whynot,'-'); + varbufaddstr(whynot,possi->revision); + } + varbufaddc(whynot,')'); + } + reln= " or "; + } } + } + + if (!*linebuf) { + /* If the package wasn't installed at all, and we haven't said + * yet why this isn't satisfied, we should say so now. + */ + sprintf(linebuf, " %.250s is not installed.\n", possi->ed->name); + varbufaddstr(whynot, linebuf); + } + } + } + /* OK so far - now check for Providers. */ + + /* Don't say anything about this yet - it might be a virtual package. + * Later on, if nothing has put anything in linebuf, we know that it + * isn't and issue a diagnostic then. + */ + *linebuf= 0; + break; + default: + sprintf(linebuf, " %.250s is %s.\n", + possi->ed->name, ); + break; + } + break; + default: + abort(); + } + + + + if (possi->ed->clientdata->istobe == itb_install) continue; + default: + break; + + + /* fixme: what about things provided by the package(s) + * which we are about to install ? For these we + * have to use ->available.depended. + */ + varbufaddc(whynot, ' '); + varbufaddstr(whynot, provider->up->up->name); + varbufaddstr(whynot, " provides "); + varbufaddstr(whynot, possi->ed->name); + } + } + + sprintf(linebuf, " No package %.250s + + provider->up->up->status == stat_configfiles) { + varbufaddstr(whynot, " but is not present.\n"); + } else { + varbufaddstr(whynot, " and is present.\n"); + present=1; + } + break; + case itb_install: + varbufaddstr(whynot, " and is to be installed.\n"); + present=1; + break; + var + + varbufaddc(whynot, ' '); + + varbufaddstr(whynot, possi->ed->name); + varbufaddstr(whynot, " is to be removed.\n"); + break; + case itb_normal: + varbufaddstr(whynot, possi->ed->name); + varbufaddstr(whynot, " is "); + if (dep->up->status == stat_installed) { + if (versionsatisfied(possi, &possi->ed.installed, whynot)) return 1; + varbufaddstr(&why, " version"); + + varbufaddstr(&why, " + varbufaddstr(&why, " is not present.\n"); + } else { + varbufaddstr(whynot, statusstrings[dep->ed->status]); + varbufaddstr(whynot, possi->ed->name); + varbufaddstr(whynot, " is present"); + } + break; + case itb_installnew: + varbufaddstr(whynot, possi->ed->name); + varbufaddstr(whynot, " is to be installed"); + if (versionsatisfied(possi, &possi->ed.available, whynot)) present=1; + default: + abort(); + } + } + if (dep->type == dep_conflicts ? present : !present) return 0; + varbufreset(whynot); return 1; +} diff --git a/main/main.c b/main/main.c new file mode 100644 index 00000000..56554105 --- /dev/null +++ b/main/main.c @@ -0,0 +1,378 @@ +/* + * dpkg - main program for package management + * main.c - main program + * + * Copyright (C) 1994,1995 Ian Jackson + * + * This 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, + * or (at your option) any later version. + * + * This 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 dpkg; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "dpkg.h" +#include "dpkg-db.h" +#include "version.h" +#include "myopt.h" + +#include "main.h" + +static void printversion(void) { + if (!fputs("Debian GNU/Linux `" DPKG "' package management program version " + DPKG_VERSION_ARCH ".\n" + "Copyright 1994,1995 Ian Jackson, Bruce Perens. This is free software;\n" + "see the GNU General Public Licence version 2 or later for copying\n" + "conditions. There is NO warranty. See dpkg --licence for details.\n", + stderr)) werr("stderr"); +} + +static void usage(void) { + if (!fputs("\ +Usage: " DPKG " -i|--install <.deb file name> ... | -R|--recursive ...\n\ + " DPKG " --unpack <.deb file name> ... | -R|--recursive ...\n\ + " DPKG " -A|--avail <.deb file name> ... | -R|--recursive ...\n\ + " DPKG " --configure ... | -a|--pending\n\ + " DPKG " -r|--remove | --purge ... | -a|--pending\n\ + " DPKG " -s|--status ...\n\ + " DPKG " -L|--listfiles ...\n\ + " DPKG " -l|--list [ ...]\n\ + " DPKG " -S|--search ...\n\ + " DPKG " -C|--audit | --yet-to-unpack\n\ + " DPKG " --merge-avail | --update-avail \n\ +Use " DPKG " -b|--build|-c|--contents|-e|--control|-I|--info|-f|--field|\n\ + -x|--extract|-X|--vextract|--fsys-tarfile on archives (type " BACKEND " --help.)\n\ +For internal use: " DPKG " --assert-support-predepends | --predep-package\n\ +\n\ +Options: --help --version --licence --force-help -Dh|--debug=help\n\ + --root= --admindir= --instdir=\n\ + -O|--selected-only -E|--skip-same-version -G=--refuse-downgrade\n\ + -B|--auto-deconfigure --ignore-depends=,...\n\ + -D|--debug= --force-... --no-force-...|--refuse-...\n\ + --largemem|--smallmem --no-act\n\ +\n\ +Use `" DSELECT "' for user-friendly package management.\n", + stderr)) werr("stderr"); +} + +const char thisname[]= DPKG; +const char architecture[]= ARCHITECTURE; +const char printforhelp[]= + "Type " DPKG " --help for help about installing and deinstalling packages;\n" + "Use " DSELECT " for user-friendly package management;\n" + "Type " DPKG " -Dhelp for a list of " DPKG " debug flag values;\n" + "Type " DPKG " --force-help for a list of forcing options;\n" + "Type " BACKEND " --help for help about manipulating *.deb files."; + +const struct cmdinfo *cipaction= 0; +int f_pending=0, f_recursive=0, f_alsoselect=1, f_skipsame=0, f_noact=0; +int f_autodeconf=0, f_largemem=0; +unsigned long f_debug=0; +int fc_downgrade=1, fc_configureany=0, fc_hold=0, fc_removereinstreq=0, fc_overwrite= 0; +int fc_removeessential=0, fc_conflicts=0, fc_depends=0, fc_dependsversion=0; +int fc_autoselect=1, fc_badpath=0, fc_overwritediverted=0, fc_architecture=0; + +const char *admindir= ADMINDIR; +const char *instdir= ""; +struct packageinlist *ignoredependss=0; + +static const struct forceinfo { + const char *name; + int *opt; +} forceinfos[]= { + { "downgrade", &fc_downgrade }, + { "configure-any", &fc_configureany }, + { "hold", &fc_hold }, + { "remove-reinstreq", &fc_removereinstreq }, + { "remove-essential", &fc_removeessential }, + { "conflicts", &fc_conflicts }, + { "depends", &fc_depends }, + { "depends-version", &fc_dependsversion }, + { "auto-select", &fc_autoselect }, + { "bad-path", &fc_badpath }, + { "overwrite", &fc_overwrite }, + { "overwrite-diverted", &fc_overwritediverted }, + { "architecture", &fc_architecture }, + { 0 } +}; + +static void helponly(const struct cmdinfo *cip, const char *value) { + usage(); exit(0); +} +static void versiononly(const struct cmdinfo *cip, const char *value) { + printversion(); exit(0); +} + +static void setaction(const struct cmdinfo *cip, const char *value) { + if (cipaction) + badusage("conflicting actions --%s and --%s",cip->olong,cipaction->olong); + cipaction= cip; +} + +static void setdebug(const struct cmdinfo *cpi, const char *value) { + char *endp; + + if (*value == 'h') { + if (!fputs( +DPKG " debugging option, --debug= or -D:\n\n\ + number ref. in source description\n\ + 1 general Generally helpful progress information\n\ + 2 scripts Invocation and status of maintainer scripts\n\ + 10 eachfile Output for each file processed\n\ + 100 eachfiledetail Lots of output for each file processed\n\ + 20 conff Output for each configuration file\n\ + 200 conffdetail Lots of output for each configuration file\n\ + 40 depcon Dependencies and conflicts\n\ + 400 depcondetail Lots of dependencies/conflicts output\n\ + 1000 veryverbose Lots of drivel about eg the dpkg/info directory\n\ + 2000 stupidlyverbose Insane amounts of drivel\n\n\ +Debugging options are be mixed using bitwise-or.\n\ +Note that the meanings and values are subject to change.\n", + stderr)) werr("stderr"); + exit(0); + } + + f_debug= strtoul(value,&endp,8); + if (*endp) badusage("--debug requires an octal argument"); +} + +static void setroot(const struct cmdinfo *cip, const char *value) { + char *p; + instdir= value; + p= m_malloc(strlen(value) + sizeof(ADMINDIR)); + strcpy(p,value); + strcat(p,ADMINDIR); + admindir= p; +} + +static void ignoredepends(const struct cmdinfo *cip, const char *value) { + char *copy, *p; + const char *pnerr; + struct packageinlist *ni; + + copy= m_malloc(strlen(value)+2); + strcpy(copy,value); + copy[strlen(value)+1]= 0; + for (p=copy; *p; p++) { + if (*p != ',') continue; + *p++= 0; + if (!*p || *p==',' || p==copy+1) + badusage("null package name in --ignore-depends comma-separated list `%.250s'", + value); + } + p= copy; + while (*p) { + pnerr= illegal_packagename(value,0); + if (pnerr) ohshite("--ignore-depends requires a legal package name. " + "`%.250s' is not; %s", value, pnerr); + ni= m_malloc(sizeof(struct packageinlist)); + ni->pkg= findpackage(value); + ni->next= ignoredependss; + ignoredependss= ni; + p+= strlen(p)+1; + } +} + +static void setforce(const struct cmdinfo *cip, const char *value) { + const char *comma; + int l; + const struct forceinfo *fip; + + if (!strcmp(value,"help")) { + if (!fputs( +DPKG " forcing options - control behaviour when problems found:\n\ + warn but continue: --force-,,...\n\ + stop with error: --refuse-,,... | --no-force-,...\n\ + Forcing things:\n\ + auto-select [*] (De)select packages to install (remove) them\n\ + dowgrade [*] Replace a package with a lower version\n\ + configure-any Configure any package which may help this one\n\ + hold Process packages which are on hold\n\ + bad-path PATH is missing important programs, problems likely\n\ + overwrite Overwrite a file from one package with another\n\ + overwrite-diverted Overwrite a diverted file with an undiverted version\n\ + depends-version [!] Turn dependency version problems into warnings\n\ + depends [!] Turn all dependency problems into warnings\n\ + conflicts [!] Allow installation of conflicting packages\n\ + architecture [!] Process even packages with wrong architecture\n\ + remove-reinstreq [!] Remove packages which require installation\n\ + remove-essential [!] Remove an essential package\n\ +\n\ +WARNING - use of options marked [!] can seriously damage your installation.\n\ +Forcing options marked [*] are enabled by default.\n", + stderr)) werr("stderr"); + exit(0); + } + + for (;;) { + comma= strchr(value,','); + l= comma ? (int)(comma-value) : strlen(value); + for (fip=forceinfos; fip->name; fip++) + if (!strncmp(fip->name,value,l) && strlen(fip->name)==l) break; + if (!fip->name) + badusage("unknown force/refuse option `%.*s'", l<250 ? l : 250, value); + *fip->opt= cip->arg; + if (!comma) break; + value= ++comma; + } +} + +static const char *const passlongopts[]= { + "build", "contents", "control", "info", "field", "extract", + "vextract", "fsys-tarfile", 0 +}; + +static const char passshortopts[]= "bceIfxX"; +static const char okpassshortopts[]= "D"; + +static const struct cmdinfo cmdinfos[]= { + { "install", 'i', 0, 0, 0, setaction, act_install }, + { "unpack", 0, 0, 0, 0, setaction, act_unpack }, + { "avail", 'A', 0, 0, 0, setaction, act_avail }, + { "configure", 0, 0, 0, 0, setaction, act_configure }, + { "remove", 'r', 0, 0, 0, setaction, act_remove }, + { "purge", 0, 0, 0, 0, setaction, act_purge }, + { "list", 'l', 0, 0, 0, setaction, act_list }, + { "status", 's', 0, 0, 0, setaction, act_status }, + { "search", 'S', 0, 0, 0, setaction, act_search }, + { "audit", 'C', 0, 0, 0, setaction, act_audit }, + { "listfiles", 'L', 0, 0, 0, setaction, act_listfiles }, + { "update-avail", 0, 0, 0, 0, setaction, act_avreplace }, + { "merge-avail", 0, 0, 0, 0, setaction, act_avmerge }, + { "yet-to-unpack", 0, 0, 0, 0, setaction, act_unpackchk }, + { "assert-support-predepends", 0, 0, 0, 0, setaction, act_assuppredep }, + { "print-architecture", 0, 0, 0, 0, setaction, act_printarch }, + { "predep-package", 0, 0, 0, 0, setaction, act_predeppackage }, + { "pending", 'a', 0, &f_pending, 0, 0, 1 }, + { "recursive", 'R', 0, &f_recursive, 0, 0, 1 }, + { "no-act", 0, 0, &f_noact, 0, 0, 1 }, + { 0, 'G', 0, &fc_downgrade, 0, 0, /* alias for --refuse */ 0 }, + { "selected-only", 'O', 0, &f_alsoselect, 0, 0, 0 }, + { "no-also-select", 'N', 0, &f_alsoselect, 0, 0, 0 /* fixme: remove eventually */ }, + { "skip-same-version", 'E', 0, &f_skipsame, 0, 0, 1 }, + { "auto-deconfigure", 'B', 0, &f_autodeconf, 0, 0, 1 }, + { "largemem", 0, 0, &f_largemem, 0, 0, 1 }, + { "smallmem", 0, 0, &f_largemem, 0, 0, -1 }, + { "root", 0, 1, 0, 0, setroot }, + { "admindir", 0, 1, 0, &admindir, 0 }, + { "instdir", 0, 1, 0, &instdir, 0 }, + { "ignore-depends", 0, 1, 0, 0, ignoredepends }, + { "force", 0, 2, 0, 0, setforce, 1 }, + { "refuse", 0, 2, 0, 0, setforce, 0 }, + { "no-force", 0, 2, 0, 0, setforce, 0 }, + { "debug", 'D', 1, 0, 0, setdebug }, + { "help", 'h', 0, 0, 0, helponly }, + { "version", 0, 0, 0, 0, versiononly }, + { "licence",/* UK spelling */ 0,0,0,0, showcopyright }, + { "license",/* US spelling */ 0,0,0,0, showcopyright }, + { 0, 0 } +}; + +static void execbackend(int argc, const char *const *argv) { + execvp(BACKEND, (char* const*) argv); + ohshite("failed to exec " BACKEND); +} + +int main(int argc, const char *const *argv) { + jmp_buf ejbuf; + int c; + const char *argp, *const *blongopts, *const *argvs; + + if (setjmp(ejbuf)) { /* expect warning about possible clobbering of argv */ + error_unwind(ehflag_bombout); exit(2); + } + push_error_handler(&ejbuf,print_error_fatal,0); + + umask(022); /* Make sure all our status databases are readable. */ + + for (argvs=argv+1; (argp= *argvs) && *argp++=='-'; argvs++) { + if (*argp++=='-') { + if (!strcmp(argp,"-")) break; + for (blongopts=passlongopts; *blongopts; blongopts++) { + if (!strcmp(argp,*blongopts)) execbackend(argc,argv); + } + } else { + if (!*--argp) break; + while ((c= *argp++)) { + if (strchr(passshortopts,c)) execbackend(argc,argv); + if (!strchr(okpassshortopts,c)) break; + } + } + } + + myopt(&argv,cmdinfos); + if (!cipaction) badusage("need an action option"); + + setvbuf(stdout,0,_IONBF,0); + filesdbinit(); + + switch (cipaction->arg) { + case act_install: case act_unpack: case act_avail: + checkpath(); + archivefiles(argv); + break; + case act_configure: case act_remove: case act_purge: + checkpath(); + packages(argv); + break; + case act_listfiles: case act_status: + enqperpackage(argv); + break; + case act_avreplace: + availablefrompackages(argv,1); + break; + case act_avmerge: + availablefrompackages(argv,0); + break; + case act_audit: + audit(argv); + break; + case act_unpackchk: + unpackchk(argv); + break; + case act_list: + listpackages(argv); + break; + case act_search: + searchfiles(argv); + break; + case act_assuppredep: + assertsupportpredepends(argv); + break; + case act_predeppackage: + predeppackage(argv); + break; + case act_printarch: + printarchitecture(argv); + break; + default: + internerr("unknown action"); + } + + set_error_display(0,0); + error_unwind(ehflag_normaltidy); + + return reportbroken_retexitstatus(); +} diff --git a/main/main.h b/main/main.h new file mode 100644 index 00000000..21a1f9cf --- /dev/null +++ b/main/main.h @@ -0,0 +1,197 @@ +/* + * dpkg - main program for package management + * dpkg-deb.h - external definitions for this program + * + * Copyright (C) 1995 Ian Jackson + * + * This 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, + * or (at your option) any later version. + * + * This 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 dpkg; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef MAIN_H +#define MAIN_H + +struct fileinlist; /* these two are defined in filesdb.h */ +struct filenamenode; + +struct perpackagestate { + enum istobes { + itb_normal, itb_remove, itb_installnew, itb_deconfigure, itb_preinstall + } istobe; + + /* filelistvalid files meaning + * 0 0 not read yet, must do so if want them + * 0 !=0 read, but rewritten and now out of + * date. If want info must throw away old + * and reread file. + * 1 !=0 read, all is OK + * 1 0 read OK, but, there were no files + */ + int fileslistvalid; + struct fileinlist *files; + int replacingfilesandsaid; +}; + +struct packageinlist { + struct packageinlist *next; + struct pkginfo *pkg; +}; + +enum action { act_unset, act_install, act_unpack, act_avail, act_configure, + act_remove, act_purge, act_list, act_avreplace, act_avmerge, + act_unpackchk, act_status, act_search, act_audit, act_listfiles, + act_assuppredep, act_printarch, act_predeppackage }; + +enum conffopt { + cfof_prompt = 001, + cfof_keep = 002, + cfof_install = 004, + cfof_backup = 0100, + cfof_newconff = 0200, + cfof_isnew = 0400, + cfom_main = 007, + cfo_keep = cfof_keep, + cfo_prompt_keep = cfof_keep | cfof_prompt, + cfo_prompt = cfof_prompt, + cfo_prompt_install = cfof_prompt | cfof_install, + cfo_install = cfof_install, + cfo_newconff = cfof_install | cfof_newconff, + cfo_identical = cfof_keep +}; + +extern int conffoptcells[2][2]; +extern const char *const statusstrings[]; + +extern const struct cmdinfo *cipaction; +extern int f_pending, f_recursive, f_alsoselect, f_skipsame, f_noact; +extern int f_autodeconf, f_largemem; +extern unsigned long f_debug; +extern int fc_downgrade, fc_configureany, fc_hold, fc_removereinstreq, fc_overwrite; +extern int fc_removeessential, fc_conflicts, fc_depends, fc_dependsversion; +extern int fc_autoselect, fc_badpath, fc_overwritediverted, fc_architecture; + +extern const char *admindir; +extern const char *instdir; +extern struct packageinlist *ignoredependss; +extern const char architecture[]; + +/* from filesdb.c */ + +void filesdbinit(void); + +/* from archives.c */ + +void archivefiles(const char *const *argv); +void process_archive(const char *filename); + +/* from update.c */ + +void availablefrompackages(const char *const *argv, int replace); + +/* from enquiry.c */ + +void listpackages(const char *const *argv); +void audit(const char *const *argv); +void unpackchk(const char *const *argv); +void searchfiles(const char *const *argv); +void enqperpackage(const char *const *argv); +void assertsupportpredepends(const char *const *argv); +void predeppackage(const char *const *argv); +void printarchitecture(const char *const *argv); + +/* from packages.c, remove.c and configure.c */ + +void add_to_queue(struct pkginfo *pkg); +void process_queue(void); +void packages(const char *const *argv); +void removal_bulk(struct pkginfo *pkg); +int conffderef(struct pkginfo *pkg, struct varbuf *result, const char *in); +int dependencies_ok(struct pkginfo *pkg, struct pkginfo *removing, + struct varbuf *aemsgs); + +void deferred_remove(struct pkginfo *pkg); +void deferred_configure(struct pkginfo *pkg); + +extern int queuelen, sincenothing, dependtry; + +/* from cleanup.c (most of these are declared in archives.h) */ + +void cu_prermremove(int argc, void **argv); + +/* from errors.c */ + +void print_error_perpackage(const char *emsg, const char *arg); +void forcibleerr(int forceflag, const char *format, ...) PRINTFFORMAT(2,3); +int reportbroken_retexitstatus(void); +int skip_due_to_hold(struct pkginfo *pkg); + +/* from help.c */ + +void cu_closefile(int argc, void **argv); +void cu_closepipe(int argc, void **argv); +void cu_closedir(int argc, void **argv); +void cu_closefd(int argc, void **argv); + +int ignore_depends(struct pkginfo *pkg); +int force_depends(struct deppossi *possi); +int force_conflicts(struct deppossi *possi); +void ensure_package_clientdata(struct pkginfo *pkg); +const char *pkgadminfile(struct pkginfo *pkg, const char *whichfile); +void oldconffsetflags(struct conffile *searchconff); +void ensure_pathname_nonexisting(const char *pathname); +void checkpath(void); +struct filenamenode *namenodetouse(struct filenamenode*, struct pkginfo*); + +/* all ...'s are const char*'s ... */ +int maintainer_script_installed(struct pkginfo *pkg, const char *scriptname, + const char *description, ...); +int maintainer_script_new(const char *scriptname, const char *description, + const char *cidir, char *cidirrest, ...); +int maintainer_script_alternative(struct pkginfo *pkg, + const char *scriptname, const char *description, + const char *cidir, char *cidirrest, + const char *ifok, const char *iffallback); +void clear_istobes(void); +int isdirectoryinuse(struct filenamenode *namenode, struct pkginfo *pkg); + +enum debugflags { + dbg_general= 00001, + dbg_scripts= 00002, + dbg_eachfile= 00010, + dbg_eachfiledetail= 00100, + dbg_conff= 00020, + dbg_conffdetail= 00200, + dbg_depcon= 00040, + dbg_depcondetail= 00400, + dbg_veryverbose= 01000, + dbg_stupidlyverbose= 02000, +}; + +void debug(int which, const char *fmt, ...) PRINTFFORMAT(2,3); + +/* from depcon.c */ + +int depisok(struct dependency *dep, struct varbuf *whynot, + struct pkginfo **fixbyrm, int allowunconfigd); +const char *versiondescribe(const char *ver, const char *rev); +struct cyclesofarlink; +int findbreakcycle(struct pkginfo *pkg, struct cyclesofarlink *sofar); +void describedepcon(struct varbuf *addto, struct dependency *dep); + +int versionsatisfied(struct pkginfoperfile *it, struct deppossi *against); +int versionsatisfied5(const char *itver, const char *itrev, + const char *refver, const char *refrev, + enum depverrel verrel); + +#endif /* MAIN_H */ diff --git a/main/packages.c b/main/packages.c new file mode 100644 index 00000000..1db2db03 --- /dev/null +++ b/main/packages.c @@ -0,0 +1,399 @@ +/* + * dpkg - main program for package management + * packages.c - common to actions that process packages + * + * Copyright (C) 1994,1995 Ian Jackson + * + * This 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, + * or (at your option) any later version. + * + * This 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 dpkg; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "dpkg.h" +#include "dpkg-db.h" +#include "myopt.h" + +#include "filesdb.h" +#include "main.h" + +struct pkginqueue { + struct pkginqueue *next; + struct pkginfo *pkg; +}; + +static struct pkginqueue *queuehead= 0, **queuetail= &queuehead; + +int queuelen=0, sincenothing=0, dependtry=0; + +void add_to_queue(struct pkginfo *pkg) { + struct pkginqueue *newent; + + newent= m_malloc(sizeof(struct pkginqueue)); + newent->pkg= pkg; + newent->next= 0; + *queuetail= newent; + queuetail= &newent->next; + + queuelen++; +} + +void packages(const char *const *argv) { + struct pkgiterator *it; + struct pkginfo *pkg; + const char *thisarg; + int l; + + modstatdb_init(admindir, f_noact ? msdbrw_readonly : msdbrw_needsuperuser); + + if (f_pending) { + + if (*argv) + badusage("--%s --pending does not take any non-option arguments",cipaction->olong); + + it= iterpkgstart(); + while ((pkg= iterpkgnext(it)) != 0) { + switch (cipaction->arg) { + case act_configure: + if (pkg->status != stat_unpacked && pkg->status != stat_halfconfigured) + continue; + if (pkg->want != want_install) + continue; + break; + case act_remove: + case act_purge: + if (pkg->want != want_purge) { + if (pkg->want != want_deinstall) continue; + if (pkg->status == stat_configfiles) continue; + } + if (pkg->status == stat_notinstalled) + continue; + break; + default: + internerr("unknown action for pending"); + } + add_to_queue(pkg); + } + iterpkgend(it); + + } else { + + if (!*argv) + badusage("--%s needs at least one package name argument", cipaction->olong); + + while ((thisarg= *argv++) != 0) { + pkg= findpackage(thisarg); + if (pkg->status == stat_notinstalled) { + l= strlen(pkg->name); + if (l >= sizeof(DEBEXT) && !strcmp(pkg->name+l-sizeof(DEBEXT)+1,DEBEXT)) + badusage("you must specify packages by their own names," + " not by quoting the names of the files they come in"); + } + add_to_queue(pkg); + } + + } + + ensure_diversions(); + + process_queue(); + + modstatdb_shutdown(); +} + +void process_queue(void) { + struct pkginqueue *removeent, *rundown; + struct pkginfo *volatile pkg; + jmp_buf ejbuf; + enum istobes istobe; + + clear_istobes(); + + switch (cipaction->arg) { + case act_configure: case act_install: istobe= itb_installnew; break; + case act_remove: case act_purge: istobe= itb_remove; break; + default: internerr("unknown action for queue start"); + } + for (rundown= queuehead; rundown; rundown= rundown->next) { + ensure_package_clientdata(rundown->pkg); + if (rundown->pkg->clientdata->istobe == istobe) { + /* Remove it from the queue - this is a second copy ! */ + switch (cipaction->arg) { + case act_configure: case act_remove: case act_purge: + printf("Package %s listed more than once, only processing once.\n", + rundown->pkg->name); + break; + case act_install: + printf("More than one copy of package %s has been unpacked\n" + " in this run ! Only configuring it once.\n", + rundown->pkg->name); + break; + default: + internerr("unknown action in duplicate"); + } + rundown->pkg= 0; + } else { + rundown->pkg->clientdata->istobe= istobe; + } + } + + while (queuelen) { + removeent= queuehead; + assert(removeent); + queuehead= queuehead->next; + queuelen--; + if (queuetail == &removeent->next) queuetail= &queuehead; + + pkg= removeent->pkg; + free(removeent); + + if (!pkg) continue; /* duplicate, which we removed earlier */ + + if (skip_due_to_hold(pkg)) { pkg->clientdata->istobe= itb_normal; continue; } + + assert(pkg->status <= stat_configfiles); + + if (setjmp(ejbuf)) { + /* give up on it from the point of view of other packages, ie reset istobe */ + pkg->clientdata->istobe= itb_normal; + error_unwind(ehflag_bombout); + if (onerr_abort > 0) break; + continue; + } + push_error_handler(&ejbuf,print_error_perpackage,pkg->name); + if (sincenothing++ > queuelen*2+2) { + dependtry++; sincenothing= 0; + assert(dependtry <= 4); + } + switch (cipaction->arg) { + case act_configure: + case act_install: + deferred_configure(pkg); + break; + case act_remove: case act_purge: + deferred_remove(pkg); + break; + default: + internerr("unknown action in queue"); + } + if (ferror(stdout)) werr("stdout"); + if (ferror(stderr)) werr("stderr"); + set_error_display(0,0); + error_unwind(ehflag_normaltidy); + } +} + +/*** dependency processing - common to --configure and --remove ***/ + +/* + * The algorithm for deciding what to configure or remove first is as + * follows: + * + * Loop through all packages doing a `try 1' until we've been round and + * nothing has been done, then do `try 2' and `try 3' likewise. + * + * When configuring, in each try we check to see whether all + * dependencies of this package are done. If so we do it. If some of + * the dependencies aren't done yet but will be later we defer the + * package, otherwise it is an error. + * + * When removing, in each try we check to see whether there are any + * packages that would have dependencies missing if we removed this + * one. If not we remove it now. If some of these packages are + * themselves scheduled for removal we defer the package until they + * have been done. + * + * The criteria for satisfying a dependency vary with the various + * tries. In try 1 we treat the dependencies as absolute. In try 2 we + * check break any cycles in the dependency graph involving the package + * we are trying to process before trying to process the package + * normally. In try 3 (which should only be reached if + * --force-depends-version is set) we ignore version number clauses in + * Depends lines. In try 4 (only reached if --force-depends is set) we + * say "ok" regardless. + * + * If we are configuring and one of the packages we depend on is + * awaiting configuration but wasn't specified in the argument list we + * will add it to the argument list if --configure-any is specified. + * In this case we note this as having "done something" so that we + * don't needlessly escalate to higher levels of dependency checking + * and breaking. + */ + +static int deppossi_ok_found(struct pkginfo *possdependee, + struct pkginfo *requiredby, + struct pkginfo *removing, + int *matched, + struct deppossi *checkversion, + int *interestingwarnings, + struct varbuf *oemsgs) { + int thisf; + + if (ignore_depends(possdependee)) { + debug(dbg_depcondetail," ignoring depended package so ok and found"); + return 3; + } + thisf= 0; + if (possdependee == removing) { + varbufaddstr(oemsgs," Package "); + varbufaddstr(oemsgs,possdependee->name); + varbufaddstr(oemsgs," is to be removed.\n"); + *matched= 1; + if (fc_depends) thisf= (dependtry >= 4) ? 2 : 1; + debug(dbg_depcondetail," removing possdependee, returning %d",thisf); + return thisf; + } + switch (possdependee->status) { + case stat_installed: + case stat_unpacked: + case stat_halfconfigured: + assert(possdependee->installed.valid); + if (checkversion && !versionsatisfied(&possdependee->installed,checkversion)) { + varbufaddstr(oemsgs," Version of "); + varbufaddstr(oemsgs,possdependee->name); + varbufaddstr(oemsgs," on system is "); + varbufaddstr(oemsgs,versiondescribe(possdependee->installed.version, + possdependee->installed.revision)); + varbufaddstr(oemsgs,".\n"); + assert(checkversion->verrel != dvr_none); + if (fc_depends) thisf= (dependtry >= 3) ? 2 : 1; + debug(dbg_depcondetail," bad version, returning %d",thisf); + (*interestingwarnings)++; + return thisf; + } + if (possdependee->status == stat_installed) { + debug(dbg_depcondetail," is installed, ok and found"); + return 3; + } + if (possdependee->clientdata && + possdependee->clientdata->istobe == itb_installnew) { + debug(dbg_depcondetail," unpacked/halfconfigured, defer"); + return 1; + } else if (!removing && fc_configureany) { + fprintf(stderr, DPKG + ": also configuring `%s' (required by `%s')\n", + possdependee->name, requiredby->name); + add_to_queue(possdependee); sincenothing=0; return 1; + } else { + varbufaddstr(oemsgs," Package "); + varbufaddstr(oemsgs,possdependee->name); + varbufaddstr(oemsgs," is not configured yet.\n"); + if (fc_depends) thisf= (dependtry >= 4) ? 2 : 1; + debug(dbg_depcondetail," not configured/able - returning %d",thisf); + (*interestingwarnings)++; + return thisf; + } + default: + varbufaddstr(oemsgs," Package "); + varbufaddstr(oemsgs,possdependee->name); + varbufaddstr(oemsgs," is not installed.\n"); + if (fc_depends) thisf= (dependtry >= 4) ? 2 : 1; + debug(dbg_depcondetail," not installed - returning %d",thisf); + (*interestingwarnings)++; + return thisf; + } +} + +int dependencies_ok(struct pkginfo *pkg, struct pkginfo *removing, + struct varbuf *aemsgs) { + int ok, matched, found, thisf, interestingwarnings; + struct varbuf oemsgs; + struct dependency *dep; + struct deppossi *possi, *provider; + + varbufinit(&oemsgs); + interestingwarnings= 0; + ok= 2; /* 2=ok, 1=defer, 0=halt */ + debug(dbg_depcon,"checking dependencies of %s (- %s)", + pkg->name, removing ? removing->name : ""); + assert(pkg->installed.valid); + for (dep= pkg->installed.depends; dep; dep= dep->next) { + if (dep->type != dep_depends && dep->type != dep_predepends) continue; + debug(dbg_depcondetail," checking group ..."); + matched= 0; varbufreset(&oemsgs); + found= 0; /* 0=none, 1=defer, 2=withwarning, 3=ok */ + for (possi= dep->list; found != 3 && possi; possi= possi->next) { + debug(dbg_depcondetail," checking possibility -> %s",possi->ed->name); + if (possi->cyclebreak) { + debug(dbg_depcondetail," break cycle so ok and found"); + found= 3; break; + } + thisf= deppossi_ok_found(possi->ed,pkg,removing, + &matched,possi,&interestingwarnings,&oemsgs); + if (thisf > found) found= thisf; + if (found != 3 && possi->verrel == dvr_none) { + if (possi->ed->installed.valid) { + for (provider= possi->ed->installed.depended; + found != 3 && provider; + provider= provider->nextrev) { + if (provider->up->type != dep_provides) continue; + debug(dbg_depcondetail," checking provider %s",provider->up->up->name); + thisf= deppossi_ok_found(provider->up->up,pkg,removing, + &matched,0,&interestingwarnings,&oemsgs); + if (thisf > found) found= thisf; + } + } + } + debug(dbg_depcondetail," found %d",found); + if (thisf > found) found= thisf; + } + debug(dbg_depcondetail," found %d matched %d",found,matched); + if (removing && !matched) continue; + switch (found) { + case 0: + ok= 0; + case 2: + varbufaddstr(aemsgs, " "); + varbufaddstr(aemsgs, pkg->name); + varbufaddstr(aemsgs, " depends on "); + varbufdependency(aemsgs, dep); + if (interestingwarnings) { + /* Don't print the line about the package to be removed if + * that's the only line. + */ + varbufaddstr(aemsgs, "; however:\n"); + varbufaddc(&oemsgs, 0); + varbufaddstr(aemsgs, oemsgs.buf); + } else { + varbufaddstr(aemsgs, ".\n"); + } + break; + case 1: + if (ok>1) ok= 1; + break; + case 3: + break; + default: + internerr("unknown value for found"); + } + } + if (ok == 0 && (pkg->clientdata && pkg->clientdata->istobe == itb_remove)) + ok= 1; + + varbuffree(&oemsgs); + debug(dbg_depcon,"ok %d msgs >>%.*s<<", ok, aemsgs->used, aemsgs->buf); + return ok; +} diff --git a/main/processarc.c b/main/processarc.c new file mode 100644 index 00000000..2df3d0d6 --- /dev/null +++ b/main/processarc.c @@ -0,0 +1,1004 @@ +/* + * dpkg - main program for package management + * processarc.c - the huge function process_archive + * + * Copyright (C) 1995 Ian Jackson + * + * This 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, + * or (at your option) any later version. + * + * This 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 dpkg; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "dpkg.h" +#include "dpkg-db.h" +#include "myopt.h" +#include "tarfn.h" + +#include "filesdb.h" +#include "main.h" +#include "archives.h" + +void process_archive(const char *filename) { + static const struct TarFunctions tf = { + tarfileread, + tarobject, tarobject, tarobject, tarobject, tarobject + }; + + /* These need to be static so that we can pass their addresses to + * push_cleanup as arguments to the cu_xxx routines; if an error occurs + * we unwind the stack before processing the cleanup list, and these + * variables had better still exist ... + */ + static int p1[2]; + static char cidirtmpnambuf[L_tmpnam+100]; + static char *cidirbuf=0, *reasmbuf=0; + static struct fileinlist *newconffiles; + static enum pkgstatus oldversionstatus; + static struct varbuf infofnvb, fnvb, depprobwhy; + static struct tarcontext tc; + + int c1, r, admindirlen, i, infodirlen, infodirbaseused, status; + struct pkgiterator *it; + struct pkginfo *pkg, *conflictor, *otherpkg, *divpkg; + char *cidir, *cidirrest, *p; + char pfilenamebuf[50], conffilenamebuf[MAXCONFFILENAME]; + const char *pfilename, *newinfofilename; + struct fileinlist *newconff, **newconffileslastp, *newfileslist; + struct fileinlist *cfile; + struct reversefilelistiter rlistit; + struct conffile *searchconff, **iconffileslastp, *newiconff; + struct filepackages *packageslump; + struct dependency *dsearch, *newdeplist, **newdeplistlastp; + struct dependency *newdep, *dep, *providecheck; + struct deppossi *psearch, **newpossilastp, *possi, *newpossi, *pdep; + FILE *conff, *tmpf; + DIR *dsd; + struct filenamenode *namenode; + struct dirent *de; + struct stat stab; + struct packageinlist *deconpil, *deconpiltemp; + + cleanup_pkg_failed= cleanup_conflictor_failed= 0; + admindirlen= strlen(admindir); + + pfilename= filename; + while (strlen(pfilename) > sizeof(pfilenamebuf)-5) { + pfilename= strchr(pfilename,'/'); + if (!pfilename) break; + pfilename++; + } + if (pfilename && pfilename != filename) { + strcpy(pfilenamebuf,".../"); + strcat(pfilenamebuf,pfilename); + pfilename= pfilenamebuf; + } else { + pfilename= filename; + } + + if (!f_noact) { + /* We can't `tentatively-reassemble' packages. */ + if (!reasmbuf) { + reasmbuf= m_malloc(admindirlen+sizeof(REASSEMBLETMP)+5); + strcpy(reasmbuf,admindir); + strcat(reasmbuf,"/" REASSEMBLETMP); + } + if (unlink(reasmbuf) && errno != ENOENT) + ohshite("error ensuring `%.250s' doesn't exist",reasmbuf); + push_cleanup(cu_pathname,~0, 0,0, 1,(void*)reasmbuf); + c1= m_fork(); + if (!c1) { + execlp(SPLITTER, SPLITTER,"-Qao",reasmbuf,filename,(char*)0); + ohshite("failed to exec " SPLITTER " to see if it's part of a multiparter"); + } + while ((r= waitpid(c1,&status,0)) == -1 && errno == EINTR); + if (r != c1) { onerr_abort++; ohshite("wait for " SPLITTER " failed"); } + switch (WIFEXITED(status) ? WEXITSTATUS(status) : -1) { + case 0: + /* It was a part - is it complete ? */ + if (!stat(reasmbuf,&stab)) { /* Yes. */ + filename= reasmbuf; + pfilename= "reassembled package file"; + break; + } else if (errno == ENOENT) { /* No. That's it, we skip it. */ + return; + } + case 1: + /* No, it wasn't a part. */ + break; + default: + checksubprocerr(status,SPLITTER,0); + } + } + + if (f_noact) { + cidir= cidirtmpnambuf; + if (!tmpnam(cidir)) ohshite("unable to get unique filename for control info"); + strcat(cidir,"/"); + } else { + /* We want it to be on the same filesystem so that we can + * use rename(2) to install the postinst &c. + */ + if (!cidirbuf) + cidirbuf= m_malloc(admindirlen+sizeof(CONTROLDIRTMP)+MAXCONTROLFILENAME+10); + cidir= cidirbuf; + strcpy(cidir,admindir); + strcat(cidir, "/" CONTROLDIRTMP); + } + cidirrest= cidir + strlen(cidir); + + assert(*cidir && cidirrest[-1] == '/'); cidirrest[-1]= 0; + ensure_pathname_nonexisting(cidir); cidirrest[-1]= '/'; + + push_cleanup(cu_cidir,~0, 0,0, 2,(void*)cidir,(void*)cidirrest); + c1= m_fork(); + if (!c1) { + cidirrest[-1]= 0; + execlp(BACKEND, BACKEND,"--control",filename,cidir,(char*)0); + ohshite("failed to exec " BACKEND " to extract control information"); + } + waitsubproc(c1,BACKEND " --control",0); + strcpy(cidirrest,CONTROLFILE); + + parsedb(cidir, pdb_recordavailable|pdb_rejectstatus|pdb_weakclassification, + &pkg,0,0); + + if (cipaction->arg == act_avail) { + printf("Read updated information about %s from %s.\n",pkg->name,pfilename); + pop_cleanup(ehflag_normaltidy); + return; + } + + if (pkg->available.architecture && *pkg->available.architecture && + strcmp(pkg->available.architecture,"all") && + strcmp(pkg->available.architecture,architecture)) + forcibleerr(fc_architecture, + "package architecture (%s) does not match system (%s)", + pkg->available.architecture,architecture); + + if (skip_due_to_hold(pkg)) { pop_cleanup(ehflag_normaltidy); return; } + + if (!pkg->installed.valid) blankpackageperfile(&pkg->installed); + assert(pkg->available.valid); + + for (deconpil= deconfigure; + deconpil; + deconpil= deconpiltemp) { + deconpiltemp= deconpil->next; + free(deconpil); + } + deconfigure= 0; + clear_istobes(); + + if (pkg->want != want_install) { + if (f_alsoselect) { + printf("Selecting previously deselected package %s.\n",pkg->name); + pkg->want= want_install; + } else { + printf("Skipping deselected package %s.\n",pkg->name); + return; + } + } + + if (pkg->status == stat_installed) { + r= versioncompare(pkg->available.version,pkg->available.revision, + pkg->installed.version,pkg->installed.revision); + if (r < 0) { + if (fc_downgrade) { + fprintf(stderr, DPKG " - warning: downgrading %.250s from %.250s to %.250s.\n", + pkg->name, + versiondescribe(pkg->installed.version,pkg->installed.revision), + versiondescribe(pkg->available.version,pkg->available.revision)); + } else { + fprintf(stderr, "Will not downgrade" + " %.250s from version %.250s to %.250s, skipping.\n", + pkg->name, + versiondescribe(pkg->installed.version,pkg->installed.revision), + versiondescribe(pkg->available.version,pkg->available.revision)); + pop_cleanup(ehflag_normaltidy); + return; + } + } else if (r == 0 && f_skipsame && /* same version fully installed ? */ + pkg->status == stat_installed && !(pkg->eflag &= eflagf_reinstreq)) { + fprintf(stderr, "Version %.250s of %.250s already installed, skipping.\n", + versiondescribe(pkg->installed.version,pkg->installed.revision), + pkg->name); + pop_cleanup(ehflag_normaltidy); + return; + } + } + + pkg->clientdata->istobe= itb_installnew; + conflictor= 0; + for (dsearch= pkg->available.depends; dsearch; dsearch= dsearch->next) { + switch (dsearch->type) { + case dep_conflicts: + /* Look for things we conflict with. */ + check_conflict(dsearch, pkg, pfilename, &conflictor); + break; + case dep_provides: + /* Look for things that conflict with what we provide. */ + if (dsearch->list->ed->installed.valid) { + for (psearch= dsearch->list->ed->installed.depended; + psearch; + psearch= psearch->nextrev) { + if (psearch->up->type != dep_conflicts) continue; + check_conflict(psearch->up, pkg, pfilename, &conflictor); + } + } + break; + case dep_suggests: case dep_recommends: case dep_depends: case dep_replaces: + /* Ignore these here. */ + break; + case dep_predepends: + if (!depisok(dsearch,&depprobwhy,0,1)) { + varbufaddc(&depprobwhy,0); + fprintf(stderr, DPKG ": regarding %s containing %s, pre-dependency problem:\n%s", + pfilename, pkg->name, depprobwhy.buf); + if (!force_depends(dsearch->list)) + ohshit("pre-dependency problem - not installing %.250s",pkg->name); + fprintf(stderr, DPKG ": warning - ignoring pre-dependency problem !\n"); + } + } + } + /* Look for things that conflict with us. */ + for (psearch= pkg->installed.depended; psearch; psearch= psearch->nextrev) { + if (psearch->up->type != dep_conflicts) continue; + check_conflict(psearch->up, pkg, pfilename, &conflictor); + } + + ensure_allinstfiles_available(); + filesdbinit(); + + if (pkg->status != stat_notinstalled && pkg->status != stat_configfiles) + printf("Preparing to replace %s (using %s) ...\n",pkg->name,pfilename); + else + printf("Unpacking %s (from %s) ...\n",pkg->name,pfilename); + + if (f_noact) { + pop_cleanup(ehflag_normaltidy); + return; + } + + /* OK, we're going ahead. First we read the conffiles, and copy the + * hashes across. + */ + newconffiles= 0; newconffileslastp= &newconffiles; + push_cleanup(cu_fileslist,~0, 0,0, 1,(void*)&newconffiles); + strcpy(cidirrest,CONFFILESFILE); + conff= fopen(cidir,"r"); + if (conff) { + push_cleanup(cu_closefile,ehflag_bombout, 0,0, 1,(void*)conff); + while (fgets(conffilenamebuf,MAXCONFFILENAME-2,conff)) { + p= conffilenamebuf + strlen(conffilenamebuf); + assert(p != conffilenamebuf); + if (p[-1] != '\n') + ohshit("name of conffile (starting `%.250s') is too long (>%d characters)", + conffilenamebuf, MAXCONFFILENAME); + while (p > conffilenamebuf && isspace(p[-1])) --p; + if (p == conffilenamebuf) continue; + *p= 0; + newconff= m_malloc(sizeof(struct fileinlist)); + newconff->next= 0; + newconff->namenode= findnamenode(conffilenamebuf); + *newconffileslastp= newconff; + newconffileslastp= &newconff->next; + newconff->namenode->oldhash= NEWCONFFILEFLAG; + /* Let's see if any packages have this file. If they do we + * check to see if they listed it as a conffile, and if they did + * we copy the hash across. Since (for plain file conffiles, + * which is the only kind we are supposed to have) there will + * only be one package which `has' the file, this will usually + * mean we only look in the package which we're installing now. + * The `conffiles' data in the status file is ignored when a + * package isn't also listed in the file ownership database as + * having that file. If several packages are listed as owning + * the file we pick one at random. + */ + searchconff= 0; + for (packageslump= newconff->namenode->packages; + packageslump; + packageslump= packageslump->more) { + for (i=0; i < PERFILEPACKAGESLUMP && packageslump->pkgs[i]; i++) { + otherpkg= packageslump->pkgs[i]; + debug(dbg_conffdetail,"process_archive conffile `%s' in package %s - conff ?", + newconff->namenode->name,otherpkg->name); + for (searchconff= otherpkg->installed.conffiles; + searchconff && strcmp(newconff->namenode->name,searchconff->name); + searchconff= searchconff->next) + debug(dbg_conffdetail, + "process_archive conffile `%s' in package %s - conff ? not `%s'", + newconff->namenode->name,otherpkg->name,searchconff->name); + if (searchconff) { + debug(dbg_conff,"process_archive conffile `%s' package=%s %s hash=%s", + newconff->namenode->name,otherpkg->name, + otherpkg == pkg ? "same" : "different!", + searchconff->hash); + if (otherpkg == pkg) goto xit_conff_hashcopy_srch; + } + } + } + xit_conff_hashcopy_srch: + if (searchconff) { + newconff->namenode->oldhash= searchconff->hash; + } else { + debug(dbg_conff,"process_archive conffile `%s' no package, no hash", + newconff->namenode->name); + } + newconff->namenode->flags |= fnnf_new_conff; + } + if (ferror(conff)) ohshite("read error in %.250s",cidir); + pop_cleanup(ehflag_normaltidy); /* conff= fopen() */ + if (fclose(conff)) ohshite("error closing %.250s",cidir); + } else { + if (errno != ENOENT) ohshite("error trying to open %.250s",cidir); + } + + /* All the old conffiles are marked with a flag, so that we don't delete + * them if they seem to disappear completely. + */ + oldconffsetflags(pkg->installed.conffiles); + if (conflictor) oldconffsetflags(conflictor->installed.conffiles); + + oldversionstatus= pkg->status; + + assert(oldversionstatus <= stat_configfiles); + debug(dbg_general,"process_archive oldversionstatus=%s conflictor=%s", + statusstrings[oldversionstatus], conflictor ? conflictor->name : ""); + + if (oldversionstatus == stat_halfconfigured || oldversionstatus == stat_installed) { + pkg->eflag |= eflagf_reinstreq; + pkg->status= stat_halfconfigured; + modstatdb_note(pkg); + push_cleanup(cu_prermupgrade,~ehflag_normaltidy, 0,0, 1,(void*)pkg); + maintainer_script_alternative(pkg, PRERMFILE, "pre-removal", cidir, cidirrest, + "upgrade", "failed-upgrade"); + pkg->status= stat_unpacked; + oldversionstatus= stat_unpacked; + modstatdb_note(pkg); + } + + if (conflictor && + (conflictor->status == stat_halfconfigured || + conflictor->status == stat_installed)) { + + for (deconpil= deconfigure; deconpil; deconpil= deconpil->next) { + printf("De-configuring %s, so that we can remove %s ...\n", + deconpil->pkg->name, conflictor->name); + deconpil->pkg->status= stat_halfconfigured; + modstatdb_note(deconpil->pkg); + /* This means that we *either* go and run postinst abort-deconfigure, + * *or* queue the package for later configure processing, depending + * on which error cleanup route gets taken. + */ + push_cleanup(cu_prermdeconfigure,~ehflag_normaltidy, + ok_prermdeconfigure,ehflag_normaltidy, + 3,(void*)deconpil->pkg,(void*)conflictor,(void*)pkg); + maintainer_script_installed(deconpil->pkg, PRERMFILE, "pre-removal", + "deconfigure", "in-favour", pkg->name, + versiondescribe(pkg->available.version, + pkg->available.revision), + "removing", conflictor->name, + versiondescribe(conflictor->installed.version, + conflictor->installed.revision), + (char*)0); + } + conflictor->status= stat_halfconfigured; + modstatdb_note(conflictor); + push_cleanup(cu_prerminfavour,~ehflag_normaltidy, 0,0, + 2,(void*)conflictor,(void*)pkg); + maintainer_script_installed(conflictor, PRERMFILE, "pre-removal", + "remove", "in-favour", pkg->name, + versiondescribe(pkg->available.version, + pkg->available.revision), + (char*)0); + conflictor->status= stat_halfinstalled; + modstatdb_note(conflictor); + } + + pkg->eflag |= eflagf_reinstreq; + pkg->status= stat_halfinstalled; + modstatdb_note(pkg); + if (oldversionstatus == stat_notinstalled) { + push_cleanup(cu_preinstverynew,~ehflag_normaltidy, 0,0, + 3,(void*)pkg,(void*)cidir,(void*)cidirrest); + maintainer_script_new(PREINSTFILE, "pre-installation", cidir, cidirrest, + "install", (char*)0); + } else if (oldversionstatus == stat_configfiles) { + push_cleanup(cu_preinstnew,~ehflag_normaltidy, 0,0, + 3,(void*)pkg,(void*)cidir,(void*)cidirrest); + maintainer_script_new(PREINSTFILE, "pre-installation", cidir, cidirrest, + "install", versiondescribe(pkg->installed.version, + pkg->installed.revision), + (char*)0); + } else { + push_cleanup(cu_preinstupgrade,~ehflag_normaltidy, 0,0, + 4,(void*)pkg,(void*)cidir,(void*)cidirrest,(void*)&oldversionstatus); + maintainer_script_new(PREINSTFILE, "pre-installation", cidir, cidirrest, + "upgrade", versiondescribe(pkg->installed.version, + pkg->installed.revision), + (char*)0); + printf("Unpacking replacement %.250s ...\n",pkg->name); + } + + /* + * Now we unpack the archive, backing things up as we go. + * For each file, we check to see if it already exists. + * There are several possibilities: + * + We are trying to install a non-directory ... + * - It doesn't exist. In this case we simply extract it. + * - It is a plain file, device, symlink, &c. We do an `atomic + * overwrite' using link() and rename(), but leave a backup copy. + * Later, when we delete the backup, we remove it from any other + * packages' lists. + * - It is a directory. In this case it depends on whether we're + * trying to install a symlink or something else. + * = If we're not trying to install a symlink we move the directory + * aside and extract the node. Later, when we recursively remove + * the backed-up directory, we remove it from any other packages' + * lists. + * = If we are trying to install a symlink we do nothing - ie, + * dpkg will never replace a directory tree with a symlink. This + * is to avoid embarrassing effects such as replacing a directory + * tree with a link to a link to the original directory tree. + * + We are trying to install a directory ... + * - It doesn't exist. We create it with the appropriate modes. + * - It exists as a directory or a symlink to one. We do nothing. + * - It is a plain file or a symlink (other than to a directory). + * We move it aside and create the directory. Later, when we + * delete the backup, we remove it from any other packages' lists. + * + * Install non-dir Install symlink Install dir + * Exists not X X X + * File/node/symlink LXR LXR BXR + * Directory BXR - - + * + * X: extract file/node/link/directory + * LX: atomic overwrite leaving backup + * B: ordinary backup + * R: later remove from other packages' lists + * -: do nothing + * + * After we've done this we go through the remaining things in the + * lists of packages we're trying to remove (including the old + * version of the current package). This happens in reverse order, + * so that we process files before the directories (or symlinks-to- + * directories) containing them. + * + If the thing is a conffile then we leave it alone for the purge + * operation. + * + Otherwise, there are several possibilities too: + * - The listed thing does not exist. We ignore it. + * - The listed thing is a directory or a symlink to a directory. + * We delete it only if it isn't listed in any other package. + * - The listed thing is not a directory or a symlink to one (ie, + * it's a plain file, device, pipe, &c, or a symlink to one, or a + * dangling symlink). We delete it. + * The removed packages' list becomes empty (of course, the new + * version of the package we're installing will have a new list, + * which replaces the old version's list). + * + * If at any stage we remove a file from a package's list, and the + * package isn't one we're already processing, and the package's + * list becomes empty as a result, we `vanish' the package. This + * means that we run its postrm with the `disappear' argument, and + * put the package in the `not-installed' state. Its conffiles are + * ignored and forgotten about. + * + * NOTE THAT THE OLD POSTRM IS RUN AFTER THE NEW PREINST, since the + * files get replaced `as we go'. + */ + + m_pipe(p1); + push_cleanup(cu_closepipe,ehflag_bombout, 0,0, 1,(void*)&p1[0]); + c1= m_fork(); + if (!c1) { + m_dup2(p1[1],1); close(p1[0]); close(p1[1]); + execlp(BACKEND, BACKEND, "--fsys-tarfile", filename, (char*)0); + ohshite("unable to exec " BACKEND " to get filesystem archive"); + } + close(p1[1]); + + newfileslist= 0; tc.newfilesp= &newfileslist; + push_cleanup(cu_fileslist,~0, 0,0, 1,(void*)&newfileslist); + tc.pkg= pkg; + tc.backendpipe= fdopen(p1[0],"r"); + if (!tc.backendpipe) ohshite("unable to fdopen " BACKEND " extract pipe"); + push_cleanup(cu_backendpipe,~ehflag_bombout, 0,0, 1,(void*)&tc.backendpipe); + + r= TarExtractor((void*)&tc, &tf); + if (r) { + if (errno) { + ohshite("error reading " BACKEND " tar output"); + } else if (feof(tc.backendpipe)) { + waitsubproc(c1,BACKEND " --fsys-tarfile (EOF)",1); + ohshit("unexpected EOF in filesystem tarfile - corrupted package archive"); + } else { + ohshit("corrupted filesystem tarfile - corrupted package archive"); + } + } + tmpf= tc.backendpipe; + tc.backendpipe= 0; + fclose(tmpf); + waitsubproc(c1,BACKEND " --fsys-tarfile",1); + + if (oldversionstatus == stat_halfinstalled || oldversionstatus == stat_unpacked) { + /* Packages that were in `installed' and `postinstfailed' have been reduced + * to `unpacked' by now, by the running of the prerm script. + */ + pkg->status= stat_halfinstalled; + modstatdb_note(pkg); + push_cleanup(cu_postrmupgrade,~ehflag_normaltidy, 0,0, 1,(void*)pkg); + maintainer_script_alternative(pkg, POSTRMFILE, "post-removal", cidir, cidirrest, + "upgrade", "failed-upgrade"); + } + + /* If anything goes wrong while tidying up it's a bit late to do + * anything about it. However, we don't install the new status + * info yet, so that a future dpkg installation will put everything + * right (we hope). + * + * If something does go wrong later the `conflictor' package will be + * left in the `removal_failed' state. Removing or installing it + * will be impossible if it was required because of the conflict with + * the package we're installing now and (presumably) the dependency + * by other packages. This means that the files it contains in + * common with this package will hang around until we successfully + * get this package installed, after which point we can trust the + * conflicting package's file list, which will have been updated to + * remove any files in this package. + */ + push_checkpoint(~ehflag_bombout, ehflag_normaltidy); + + /* Now we delete all the files that were in the old version of + * the package only, except (old or new) conffiles, which we leave + * alone. + */ + reversefilelist_init(&rlistit,pkg->clientdata->files); + while ((namenode= reversefilelist_next(&rlistit))) { + if ((namenode->flags & fnnf_old_conff) || + (namenode->flags & fnnf_new_conff) || + (namenode->flags & fnnf_new_inarchive)) + continue; + if (isdirectoryinuse(namenode,pkg)) continue; + fnamevb.used= fnameidlu; + varbufaddstr(&fnamevb, namenodetouse(namenode,pkg)->name); + varbufaddc(&fnamevb,0); + if (!rmdir(fnamevb.buf)) continue; + if (errno == ENOENT || errno == ELOOP) continue; + if (errno == ENOTDIR) { + if (!unlink(fnamevb.buf)) continue; + if (errno == ENOTDIR) continue; + } + fprintf(stderr, + DPKG ": warning - unable to delete old file `%.250s': %s\n", + namenode->name, strerror(errno)); + } + + /* OK, now we can write the updated files-in-this package list, + * since we've done away (hopefully) with all the old junk. + */ + write_filelist_except(pkg,newfileslist,0); + + /* We also install the new maintainer scripts, and any other + * cruft that may have come along with the package. First + * we go through the existing scripts replacing or removing + * them as appropriate; then we go through the new scripts + * (any that are left) and install them. + */ + debug(dbg_general, "process_archive updating info directory"); + varbufreset(&infofnvb); + varbufaddstr(&infofnvb,admindir); + varbufaddstr(&infofnvb,"/" INFODIR "/"); + infodirlen= infofnvb.used; + varbufaddc(&infofnvb,0); + dsd= opendir(infofnvb.buf); + if (!dsd) ohshite("cannot read info directory"); + push_cleanup(cu_closedir,~0, 0,0, 1,(void*)dsd); + while ((de= readdir(dsd)) != 0) { + debug(dbg_veryverbose, "process_archive info file `%s'", de->d_name); + if (de->d_name[0] == '.') continue; /* ignore dotfiles, including `.' and `..' */ + p= strrchr(de->d_name,'.'); if (!p) continue; /* ignore anything odd */ + if (strlen(pkg->name) != p-de->d_name || + strncmp(de->d_name,pkg->name,p-de->d_name)) continue; + debug(dbg_stupidlyverbose, "process_archive info this pkg"); + /* Right do we have one ? */ + p++; /* skip past the full stop */ + if (!strcmp(p,LISTFILE)) continue; /* We do the list separately */ + if (strlen(p) > MAXCONTROLFILENAME) + ohshit("old version of package has overly-long info file name starting `%.250s'", + de->d_name); + infofnvb.used= infodirlen; + varbufaddstr(&infofnvb,de->d_name); + varbufaddc(&infofnvb,0); + strcpy(cidirrest,p); + if (!rename(cidir,infofnvb.buf)) { + debug(dbg_scripts, "process_archive info installed %s as %s", + cidir, infofnvb.buf); + } else if (errno == ENOENT) { + /* Right, no new version. */ + if (unlink(infofnvb.buf)) + ohshite("unable to remove obsolete info file `%.250s'",infofnvb.buf); + debug(dbg_scripts, "process_archive info unlinked %s",infofnvb.buf); + } else { + ohshite("unable to install (supposed) new info file `%.250s'",cidir); + } + } + pop_cleanup(ehflag_normaltidy); /* closedir */ + + *cidirrest= 0; /* the directory itself */ + dsd= opendir(cidir); + if (!dsd) ohshite("unable to open temp control directory"); + push_cleanup(cu_closedir,~0, 0,0, 1,(void*)dsd); + while ((de= readdir(dsd))) { + if (strchr(de->d_name,'.')) { + debug(dbg_scripts,"process_archive tmp.ci script/file `%s' contains dot", + de->d_name); + continue; + } + if (strlen(de->d_name) > MAXCONTROLFILENAME) + ohshit("package contains overly-long control info file name (starting `%.50s')", + de->d_name); + strcpy(cidirrest,de->d_name); + /* First we check it's not a directory. */ + if (!rmdir(cidir)) + ohshit("package control info contained directory `%.250s'",cidir); + else if (errno != ENOTDIR) + ohshite("package control info rmdir of `%.250s' didn't say not a dir",de->d_name); + if (!strcmp(de->d_name,CONTROLFILE)) { + debug(dbg_scripts,"process_archive tmp.ci script/file `%s' is control",cidir); + continue; /* ignore the control file */ + } + if (!strcmp(de->d_name,LISTFILE)) { + fprintf(stderr, DPKG ": warning - package %s" + " contained list as info file", pkg->name); + continue; + } + /* Right, install it */ + newinfofilename= pkgadminfile(pkg,de->d_name); + if (rename(cidir,newinfofilename)) + ohshite("unable to install new info file `%.250s' as `%.250s'", + cidir,newinfofilename); + debug(dbg_scripts,"process_archive tmp.ci script/file `%s' installed as `%s'", + cidir, newinfofilename); + } + pop_cleanup(ehflag_normaltidy); /* closedir */ + + /* Update the status database. + * This involves copying each field across from the `available' + * to the `installed' half of the pkg structure. + * For some of the fields we have to do a complicated construction + * operation; for others we can just copy the value. + * We tackle the fields in the order they appear, so that + * we don't miss any out :-). + * At least we don't have to copy any strings that are referred + * to, because these are never modified and never freed. + */ + + /* The dependencies are the most difficult. We have to build + * a whole new forward dependency tree. At least the reverse + * links (linking our deppossi's into the reverse chains) + * can be done by copy_dependency_links. + */ + newdeplist= 0; newdeplistlastp= &newdeplist; + for (dep= pkg->available.depends; dep; dep= dep->next) { + newdep= nfmalloc(sizeof(struct dependency)); + newdep->up= pkg; + newdep->next= 0; + newdep->list= 0; newpossilastp= &newdep->list; + for (possi= dep->list; possi; possi= possi->next) { + newpossi= nfmalloc(sizeof(struct deppossi)); + newpossi->up= newdep; + newpossi->ed= possi->ed; + newpossi->next= 0; newpossi->nextrev= newpossi->backrev= 0; + newpossi->verrel= possi->verrel; + if (possi->verrel != dvr_none) { + newpossi->version= possi->version; + newpossi->revision= possi->revision; + } + newpossi->cyclebreak= 0; + *newpossilastp= newpossi; + newpossilastp= &newpossi->next; + } + newdep->type= dep->type; + *newdeplistlastp= newdep; + newdeplistlastp= &newdep->next; + } + /* Right, now we've replicated the forward tree, we + * get copy_dependency_links to remove all the old dependency + * structures from the reverse links and add the new dependency + * structures in instead. It also copies the new dependency + * structure pointer for this package into the right field. + */ + copy_dependency_links(pkg,&pkg->installed.depends,newdeplist,0); + + /* The `depended' pointer in the structure doesn't represent anything + * that is actually specified by this package - it's there so we + * can find out what other packages refer to this one. So, + * we don't copy it. We go straight on to copy the text fields. + */ + pkg->installed.essential= pkg->available.essential; + pkg->installed.description= pkg->available.description; + pkg->installed.maintainer= pkg->available.maintainer; + pkg->installed.source= pkg->available.source; + pkg->installed.architecture= 0; /* This is irrelevant in the status file. */ + pkg->installed.version= pkg->available.version; + pkg->installed.revision= pkg->available.revision; + + /* We have to generate our own conffiles structure. */ + pkg->installed.conffiles= 0; iconffileslastp= &pkg->installed.conffiles; + for (cfile= newconffiles; cfile; cfile= cfile->next) { + newiconff= nfmalloc(sizeof(struct conffile)); + newiconff->next= 0; + newiconff->name= nfstrsave(cfile->namenode->name); + newiconff->hash= nfstrsave(cfile->namenode->oldhash); + *iconffileslastp= newiconff; + iconffileslastp= &newiconff->next; + } + + /* We can just copy the arbitrary fields list, because it is + * never even rearragned. Phew ! + */ + pkg->installed.arbs= pkg->available.arbs; + + /* Check for disappearing packages: + * We go through all the packages on the system looking for ones + * whose files are entirely part of the one we've just unpacked + * (and which actually *have* some files!). + * + * Any that we find are removed - we run the postrm with `disappear' + * as an argument, and remove their info/... files and status info. + * Conffiles are ignored (the new package had better do something + * with them !). + */ + it= iterpkgstart(); + while ((otherpkg= iterpkgnext(it)) != 0) { + ensure_package_clientdata(otherpkg); + if (otherpkg == pkg || + otherpkg == conflictor || + otherpkg->status == stat_notinstalled || + otherpkg->status == stat_configfiles || + !otherpkg->clientdata->files) continue; + debug(dbg_veryverbose, "process_archive checking disappearance %s",otherpkg->name); + assert(otherpkg->clientdata->istobe == itb_normal || + otherpkg->clientdata->istobe == itb_deconfigure); + for (cfile= otherpkg->clientdata->files; + cfile && !strcmp(cfile->namenode->name,"/."); + cfile= cfile->next); + if (!cfile) { + debug(dbg_stupidlyverbose, "process_archive no non-root, no disappear"); + continue; + } + for (cfile= otherpkg->clientdata->files; + cfile; + cfile= cfile->next) { + if (!(cfile->namenode->flags & fnnf_new_inarchive)) break; + if (cfile->namenode->divert && cfile->namenode->divert->useinstead) { + /* If the file is a contended one and it's overridden by either the package + * we're considering disappearing or the package we're installing then + * they're not actually the same file, so we can't disappear the package. + */ + divpkg= cfile->namenode->divert->pkg; + if (divpkg == pkg || divpkg == otherpkg) break; + } + } + if (cfile) { + debug(dbg_stupidlyverbose, "process_archive saved by `%s'",cfile->namenode->name); + continue; + } + + /* So dependency things will give right answers ... */ + otherpkg->clientdata->istobe= itb_remove; + debug(dbg_veryverbose, "process_archive disappear checking dependencies"); + for (pdep= otherpkg->installed.depended; + pdep; + pdep= pdep->nextrev) { + if (pdep->up->type != dep_depends && pdep->up->type != dep_predepends && + pdep->up->type != dep_recommends) continue; + if (depisok(pdep->up, &depprobwhy, 0,0)) continue; + varbufaddc(&depprobwhy,0); + debug(dbg_veryverbose,"process_archive cannot disappear: %s",depprobwhy.buf); + break; + } + if (!pdep) { + /* If we haven't found a reason not to yet, let's look some more. */ + for (providecheck= otherpkg->installed.depends; + providecheck; + providecheck= providecheck->next) { + if (providecheck->type != dep_provides) continue; + for (pdep= providecheck->list->ed->installed.depended; + pdep; + pdep= pdep->nextrev) { + if (pdep->up->type != dep_depends && pdep->up->type != dep_predepends && + pdep->up->type != dep_recommends) + continue; + if (depisok(pdep->up, &depprobwhy, 0,0)) continue; + varbufaddc(&depprobwhy,0); + debug(dbg_veryverbose,"process_archive cannot disappear (provides %s): %s", + providecheck->list->ed->name, depprobwhy.buf); + goto break_from_both_loops_at_once; + } + } + break_from_both_loops_at_once:; + } + otherpkg->clientdata->istobe= itb_normal; + if (pdep) continue; + + printf("(Noting disappearance of %s, which has been completely replaced.)\n", + otherpkg->name); + debug(dbg_general, "process_archive disappearing %s",otherpkg->name); + /* No, we're disappearing it. This is the wrong time to go and + * run maintainer scripts and things, as we can't back out. But + * what can we do ? It has to be run this late. + */ + maintainer_script_installed(otherpkg, POSTRMFILE, + "post-removal script (for disappearance)", + "disappear", pkg->name, + versiondescribe(pkg->available.version, + pkg->available.revision), + (char*)0); + + /* OK, now we delete all the stuff in the `info' directory .. */ + varbufreset(&fnvb); + varbufaddstr(&fnvb,admindir); + varbufaddstr(&fnvb,"/" INFODIR); + infodirbaseused= fnvb.used; + varbufaddc(&fnvb,0); + dsd= opendir(fnvb.buf); if (!dsd) ohshite("cannot read info directory"); + push_cleanup(cu_closedir,~0, 0,0, 1,(void*)dsd); + + debug(dbg_general, "process_archive disappear cleaning info directory"); + + while ((de= readdir(dsd)) != 0) { + debug(dbg_veryverbose, "process_archive info file `%s'", de->d_name); + if (de->d_name[0] == '.') continue; + p= strrchr(de->d_name,'.'); if (!p) continue; + if (strlen(otherpkg->name) != p-de->d_name || + strncmp(de->d_name,otherpkg->name,p-de->d_name)) continue; + debug(dbg_stupidlyverbose, "process_archive info this pkg"); + fnvb.used= infodirbaseused; + varbufaddstr(&fnvb,de->d_name); + varbufaddc(&fnvb,0); + if (unlink(fnvb.buf)) + ohshite("unable to delete disappearing control info file `%.250s'",fnvb.buf); + debug(dbg_scripts, "process_archive info unlinked %s",fnvb.buf); + } + pop_cleanup(ehflag_normaltidy); /* closedir */ + + otherpkg->status= stat_notinstalled; + otherpkg->want= want_purge; + otherpkg->eflag= eflagv_ok; + + otherpkg->installed.depends= 0; + otherpkg->installed.essential= 0; + otherpkg->installed.description= otherpkg->installed.maintainer= + otherpkg->installed.version= otherpkg->installed.revision= 0; + otherpkg->installed.arbs= 0; + otherpkg->clientdata->fileslistvalid= 0; + + modstatdb_note(otherpkg); + + } /* while (otherpkg= ... */ + iterpkgend(it); + + /* Delete files from any other packages' lists. + * We have to do this before we claim this package is in any + * sane kind of state, as otherwise we might delete by mistake + * a file that we overwrote, when we remove the package which + * had the version we overwrote. To prevent this we make + * sure that we don't claim this package is OK until we + * have claimed `ownership' of all its files. + */ + for (cfile= newfileslist; cfile; cfile= cfile->next) { + if (!(cfile->namenode->flags & fnnf_elide_other_lists)) continue; + if (cfile->namenode->divert && cfile->namenode->divert->useinstead) { + divpkg= cfile->namenode->divert->pkg; + if (divpkg == pkg) { + debug(dbg_eachfile, + "process_archive not overwriting any `%s' (overriding, `%s')", + cfile->namenode->name, cfile->namenode->divert->useinstead->name); + continue; + } else { + debug(dbg_eachfile, + "process_archive looking for overwriting `%s' (overridden by %s)", + cfile->namenode->name, divpkg->name); + } + } else { + divpkg= 0; + debug(dbg_eachfile, "process_archive looking for overwriting `%s'", + cfile->namenode->name); + } + for (packageslump= cfile->namenode->packages; + packageslump; + packageslump= packageslump->more) { + for (i=0; i < PERFILEPACKAGESLUMP && packageslump->pkgs[i]; i++) { + otherpkg= packageslump->pkgs[i]; + debug(dbg_eachfiledetail, "process_archive ... found in %s\n",otherpkg->name); + /* If !fileslistvalid then it's one of the disappeared packages above + * and we don't bother with it here, clearly. + */ + if (otherpkg == pkg || !otherpkg->clientdata->fileslistvalid) continue; + if (otherpkg == divpkg) { + debug(dbg_eachfiledetail, "process_archive ... diverted, skipping\n"); + continue; + } + + /* Found one. We delete remove the list entry for this file, + * (and any others in the same package) and then mark the package + * as requiring a reread. + */ + write_filelist_except(otherpkg, otherpkg->clientdata->files, 1); + ensure_package_clientdata(otherpkg); + debug(dbg_veryverbose, "process_archive overwrote from %s",otherpkg->name); + } + } + } + + /* Right, the package we've unpacked is now in a reasonable state. + * The only thing that we have left to do with it is remove + * backup files, and we can leave the user to fix that if and when + * it happens (we leave the reinstall required flag, of course). + */ + pkg->status= stat_unpacked; + modstatdb_note(pkg); + + /* Now we delete all the backup files that we made when + * extracting the archive - except for files listed as conffiles + * in the new package. + * This time we count it as an error if something goes wrong. + * + * Note that we don't ever delete things that were in the old + * package as a conffile and don't appear at all in the new. + */ + for (cfile= newfileslist; cfile; cfile= cfile->next) { + if (cfile->namenode->flags & fnnf_new_conff) continue; + fnametmpvb.used= fnameidlu; + varbufaddstr(&fnametmpvb,namenodetouse(cfile->namenode,pkg)->name); + varbufaddstr(&fnametmpvb,DPKGTEMPEXT); + varbufaddc(&fnametmpvb,0); + ensure_pathname_nonexisting(fnametmpvb.buf); + } + + /* OK, we're now fully done with the main package. + * This is quite a nice state, so we don't unwind past here. + */ + pkg->eflag= eflagv_ok; + modstatdb_note(pkg); + push_checkpoint(~ehflag_bombout, ehflag_normaltidy); + + /* Only the removal of the conflictor left to do. + * The files list for the conflictor is still a little inconsistent in-core, + * as we have not yet updated the filename->packages mappings; however, + * the package->filenames mapping is + */ + if (conflictor) { + /* We need to have the most up-to-date info about which files are what ... */ + ensure_allinstfiles_available(); + removal_bulk(conflictor); + } + + if (cipaction->arg == act_install) add_to_queue(pkg); +} diff --git a/main/remove.c b/main/remove.c new file mode 100644 index 00000000..94199da8 --- /dev/null +++ b/main/remove.c @@ -0,0 +1,453 @@ +/* + * dpkg - main program for package management + * remove.c - functionality for removing packages + * + * Copyright (C) 1995 Ian Jackson + * + * This 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, + * or (at your option) any later version. + * + * This 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 dpkg; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "dpkg.h" +#include "dpkg-db.h" +#include "myopt.h" + +#include "filesdb.h" +#include "main.h" + +void deferred_remove(struct pkginfo *pkg) { + struct varbuf raemsgs; + int rok, before, ok; + struct deppossi *dep; + struct pkginfo *depender; + + debug(dbg_general,"deferred_remove package %s",pkg->name); + + if (pkg->status == stat_notinstalled) { + fprintf(stderr, DPKG + " - warning: ignoring request to remove %.250s which isn't installed.\n", + pkg->name); + pkg->clientdata->istobe= itb_normal; + return; + } else if (!f_pending && + pkg->status == stat_configfiles && + cipaction->arg != act_purge) { + fprintf(stderr, DPKG + " - warning: ignoring request to remove %.250s, only the config\n" + " files of which are on the system. Use --purge to remove them too.\n", + pkg->name); + pkg->clientdata->istobe= itb_normal; + return; + } + + assert(pkg->installed.valid); + if (pkg->installed.essential) + forcibleerr(fc_removeessential, "This is an essential package -" + " it should not be removed."); + + if (!f_pending) + pkg->want= (cipaction->arg == act_purge) ? want_purge : want_deinstall; + if (!f_noact) modstatdb_note(pkg); + + debug(dbg_general,"checking dependencies for remove `%s'",pkg->name); + varbufinit(&raemsgs); + rok= 2; + for (dep= pkg->installed.depended; dep; dep= dep->nextrev) { + if (dep->up->type != dep_depends && dep->up->type != dep_predepends) continue; + depender= dep->up->up; + debug(dbg_depcon,"checking depending package `%s'",depender->name); + if (depender->status != stat_installed) continue; + if (ignore_depends(depender)) { + debug(dbg_depcon,"ignoring depending package `%s'\n",depender->name); + continue; + } + if (dependtry > 1) { if (findbreakcycle(pkg,0)) sincenothing= 0; } + before= raemsgs.used; + ok= dependencies_ok(depender,pkg,&raemsgs); + if (ok == 0 && depender->clientdata->istobe == itb_remove) ok= 1; + if (ok == 1) raemsgs.used= before; /* Don't burble about reasons for deferral */ + if (ok < rok) rok= ok; + } + if (rok == 1) { + varbuffree(&raemsgs); + pkg->clientdata->istobe= itb_remove; + add_to_queue(pkg); + return; + } else if (rok == 0) { + sincenothing= 0; + varbufaddc(&raemsgs,0); + fprintf(stderr, + DPKG ": dependency problems prevent removal of %s:\n%s", + pkg->name, raemsgs.buf); + ohshit("dependency problems - not removing"); + } else if (raemsgs.used) { + varbufaddc(&raemsgs,0); + fprintf(stderr, + DPKG ": %s: dependency problems, but removing anyway as you request:\n%s", + pkg->name, raemsgs.buf); + } + varbuffree(&raemsgs); + sincenothing= 0; + + if (pkg->eflag & eflagf_reinstreq) + forcibleerr(fc_removereinstreq, + "Package is in a very bad inconsistent state - you should\n" + " reinstall it before attempting a removal."); + + ensure_allinstfiles_available(); + filesdbinit(); + + if (f_noact) { + printf("Would remove or purge %s ...\n",pkg->name); + pkg->status= stat_notinstalled; + pkg->clientdata->istobe= itb_normal; + return; + } + + oldconffsetflags(pkg->installed.conffiles); + + printf("Removing %s ...\n",pkg->name); + if (pkg->status == stat_halfconfigured || pkg->status == stat_installed) { + + if (pkg->status == stat_installed || pkg->status == stat_halfconfigured) { + pkg->status= stat_halfconfigured; + modstatdb_note(pkg); + push_cleanup(cu_prermremove,~ehflag_normaltidy, 0,0, 1,(void*)pkg); + maintainer_script_installed(pkg, PRERMFILE, "pre-removal", + "remove", (char*)0); + } + + pkg->status= stat_unpacked; /* Will turn into halfinstalled soon ... */ + } + + removal_bulk(pkg); +} + +static void push_leftover(struct fileinlist **leftoverp, + struct filenamenode *namenode) { + struct fileinlist *newentry; + newentry= nfmalloc(sizeof(struct fileinlist)); + newentry->next= *leftoverp; + newentry->namenode= namenode; + *leftoverp= newentry; +} + +void removal_bulk(struct pkginfo *pkg) { + /* This is used both by deferred_remove in this file, and at + * the end of process_archive in archives.c if it needs to finish + * removing a conflicting package. + */ + static const char *const removeconffexts[]= { REMOVECONFFEXTS, 0 }; + + static struct varbuf fnvb, removevb; + + int before, r, foundpostrm, removevbbase; + int infodirbaseused, conffnameused, conffbasenamelen, pkgnameused; + char *conffbasename; + struct reversefilelistiter rlistit; + struct conffile *conff, **lconffp; + struct fileinlist *searchfile, *leftover; + struct stat stab; + DIR *dsd; + struct dirent *de; + char *p; + const char *const *ext; + const char *postrmfilename; + struct filenamenode *namenode; + + debug(dbg_general,"removal_bulk package %s",pkg->name); + + if (pkg->status == stat_halfinstalled || pkg->status == stat_unpacked) { + pkg->status= stat_halfinstalled; + modstatdb_note(pkg); + push_checkpoint(~ehflag_bombout, ehflag_normaltidy); + + reversefilelist_init(&rlistit,pkg->clientdata->files); + leftover= 0; + while ((namenode= reversefilelist_next(&rlistit))) { + debug(dbg_eachfile, "removal_bulk `%s' flags=%o", + namenode->name, namenode->flags); + if (namenode->flags & fnnf_old_conff) { + push_leftover(&leftover,namenode); + continue; + } + varbufreset(&fnvb); + varbufaddstr(&fnvb,instdir); + varbufaddstr(&fnvb,namenodetouse(namenode,pkg)->name); + before= fnvb.used; + + varbufaddstr(&fnvb,DPKGTEMPEXT); + varbufaddc(&fnvb,0); + debug(dbg_eachfiledetail, "removal_bulk cleaning temp `%s'", fnvb.buf); + + ensure_pathname_nonexisting(fnvb.buf); + + fnvb.used= before; + varbufaddstr(&fnvb,DPKGNEWEXT); + varbufaddc(&fnvb,0); + debug(dbg_eachfiledetail, "removal_bulk cleaning new `%s'", fnvb.buf); + ensure_pathname_nonexisting(fnvb.buf); + + fnvb.used= before; + varbufaddc(&fnvb,0); + if (!stat(fnvb.buf,&stab) && S_ISDIR(stab.st_mode)) { + debug(dbg_eachfiledetail, "removal_bulk is a directory"); + /* Only delete a directory or a link to one if we're the only + * package which uses it. Other files should only be listed + * in this package (but we don't check). + */ + if (isdirectoryinuse(namenode,pkg)) continue; + } + debug(dbg_eachfiledetail, "removal_bulk removing `%s'", fnvb.buf); + if (!rmdir(fnvb.buf) || errno == ENOENT || errno == ELOOP) continue; + if (errno == ENOTEMPTY) { + fprintf(stderr, DPKG + " - warning: while removing %.250s, directory `%.250s' not empty " + "so not removed.\n", + pkg->name, namenode->name); + push_leftover(&leftover,namenode); + continue; + } else if (errno == EPERM) { + fprintf(stderr, DPKG " - warning: while removing %.250s," + " unable to remove directory `%.250s':" + " Operation not permitted - directory may be a mount point ?\n", + pkg->name, namenode->name); + push_leftover(&leftover,namenode); + continue; + } + if (errno != ENOTDIR) ohshite("cannot remove `%.250s'",fnvb.buf); + debug(dbg_eachfiledetail, "removal_bulk unlinking `%s'", fnvb.buf); + if (unlink(fnvb.buf)) ohshite("cannot remove file `%.250s'",fnvb.buf); + } + write_filelist_except(pkg,leftover,0); + maintainer_script_installed(pkg, POSTRMFILE, "post-removal", + "remove", (char*)0); + varbufreset(&fnvb); + varbufaddstr(&fnvb,admindir); + varbufaddstr(&fnvb,"/" INFODIR); + infodirbaseused= fnvb.used; + varbufaddc(&fnvb,0); + dsd= opendir(fnvb.buf); if (!dsd) ohshite("cannot read info directory"); + push_cleanup(cu_closedir,~0, 0,0, 1,(void*)dsd); + foundpostrm= 0; + + debug(dbg_general, "removal_bulk cleaning info directory"); + + while ((de= readdir(dsd)) != 0) { + debug(dbg_veryverbose, "removal_bulk info file `%s'", de->d_name); + if (de->d_name[0] == '.') continue; + p= strrchr(de->d_name,'.'); if (!p) continue; + if (strlen(pkg->name) != p-de->d_name || + strncmp(de->d_name,pkg->name,p-de->d_name)) continue; + debug(dbg_stupidlyverbose, "removal_bulk info this pkg"); + /* We need the postrm and list files for --purge. */ + if (!strcmp(p+1,LISTFILE)) continue; + if (!strcmp(p+1,POSTRMFILE)) { foundpostrm=1; continue; } + debug(dbg_stupidlyverbose, "removal_bulk info not postrm or list"); + fnvb.used= infodirbaseused; + varbufaddstr(&fnvb,de->d_name); + varbufaddc(&fnvb,0); + if (unlink(fnvb.buf)) + ohshite("unable to delete control info file `%.250s'",fnvb.buf); + debug(dbg_scripts, "removal_bulk info unlinked %s",fnvb.buf); + } + pop_cleanup(ehflag_normaltidy); /* closedir */ + + pkg->status= stat_configfiles; + modstatdb_note(pkg); + push_checkpoint(~ehflag_bombout, ehflag_normaltidy); + + } else { + + postrmfilename= pkgadminfile(pkg,POSTRMFILE); + if (!lstat(postrmfilename,&stab)) foundpostrm= 1; + else if (errno == ENOENT) foundpostrm= 0; + else ohshite("unable to check existence of `%.250s'",postrmfilename); + + } + + debug(dbg_general, "removal_bulk purging? foundpostrm=%d",foundpostrm); + + if (!foundpostrm && !pkg->installed.conffiles) { + /* If there are no config files and no postrm script then we + * go straight into `purge'. + */ + debug(dbg_general, "removal_bulk no postrm, no conffiles, purging"); + pkg->want= want_purge; + + } else if (pkg->want == want_purge) { + + printf("Purging configuration files for %s ...\n",pkg->name); + ensure_packagefiles_available(pkg); /* We may have modified this above. */ + + /* We're about to remove the configuration, so remove the note + * about which version it was ... + */ + pkg->configversion= pkg->configrevision= 0; + modstatdb_note(pkg); + + /* Remove from our list any conffiles that aren't ours any more or + * are involved in diversions, except if we are the package doing the + * diverting. + */ + for (lconffp= &pkg->installed.conffiles; + (conff= *lconffp) != 0; + lconffp= &conff->next) { + for (searchfile= pkg->clientdata->files; + searchfile && strcmp(searchfile->namenode->name,conff->name); + searchfile= searchfile->next); + if (!searchfile) { + debug(dbg_conff,"removal_bulk conffile not ours any more `%s'",conff->name); + *lconffp= conff->next; + } else if (searchfile->namenode->divert && + (searchfile->namenode->divert->camefrom || + (searchfile->namenode->divert->useinstead && + searchfile->namenode->divert->pkg != pkg))) { + debug(dbg_conff,"removal_bulk conffile `%s' ignored due to diversion\n", + conff->name); + *lconffp= conff->next; + } else { + debug(dbg_conffdetail,"removal_bulk set to new conffile `%s'",conff->name); + conff->hash= (char*)NEWCONFFILEFLAG; /* yes, cast away const */ + } + } + modstatdb_note(pkg); + + for (conff= pkg->installed.conffiles; conff; conff= conff->next) { + varbufreset(&fnvb); + r= conffderef(pkg, &fnvb, conff->name); + debug(dbg_conffdetail, "removal_bulk conffile `%s' (= `%s')", + conff->name, r == -1 ? "" : fnvb.buf); + if (r == -1) continue; + conffnameused= fnvb.used-1; + if (unlink(fnvb.buf) && errno != ENOENT && errno != ENOTDIR) + ohshite("cannot remove old config file `%.250s' (= `%.250s')", + conff->name, fnvb.buf); + p= strrchr(fnvb.buf,'/'); if (!p) continue; + *p= 0; + varbufreset(&removevb); + varbufaddstr(&removevb,fnvb.buf); + varbufaddc(&removevb,'/'); + removevbbase= removevb.used; + varbufaddc(&removevb,0); + dsd= opendir(removevb.buf); + if (!dsd) { + int e=errno; + debug(dbg_conffdetail, "removal_bulk conffile no dsd %s %s", + fnvb.buf, strerror(e)); errno= e; + if (errno == ENOENT || errno == ENOTDIR) continue; + ohshite("cannot read config file dir `%.250s' (from `%.250s')", + fnvb.buf, conff->name); + } + debug(dbg_conffdetail, "removal_bulk conffile cleaning dsd %s", fnvb.buf); + push_cleanup(cu_closedir,~0, 0,0, 1,(void*)dsd); + *p= '/'; + conffbasenamelen= strlen(++p); + conffbasename= fnvb.buf+conffnameused-conffbasenamelen; + while ((de= readdir(dsd)) != 0) { + debug(dbg_stupidlyverbose, "removal_bulk conffile dsd entry=`%s'" + " conffbasename=`%s' conffnameused=%d conffbasenamelen=%d", + de->d_name, conffbasename, conffnameused, conffbasenamelen); + if (!strncmp(de->d_name,conffbasename,conffbasenamelen)) { + debug(dbg_stupidlyverbose, "removal_bulk conffile dsd entry starts right"); + for (ext= removeconffexts; *ext; ext++) + if (!strcmp(*ext,de->d_name+conffbasenamelen)) goto yes_remove; + p= de->d_name+conffbasenamelen; + if (*p++ == '~') { + while (*p && isdigit(*p)) p++; + if (*p == '~' && !*++p) goto yes_remove; + } + } + debug(dbg_stupidlyverbose, "removal_bulk conffile dsd entry starts wrong"); + if (de->d_name[0] == '#' && + !strncmp(de->d_name+1,conffbasename,conffbasenamelen) && + !strcmp(de->d_name+1+conffbasenamelen,"#")) + goto yes_remove; + debug(dbg_stupidlyverbose, "removal_bulk conffile dsd entry not it"); + continue; + yes_remove: + removevb.used= removevbbase; + varbufaddstr(&removevb,de->d_name); varbufaddc(&removevb,0); + debug(dbg_conffdetail, "removal_bulk conffile dsd entry removing `%s'", + removevb.buf); + if (unlink(removevb.buf) && errno != ENOENT && errno != ENOTDIR) + ohshite("cannot remove old backup config file `%.250s' (of `%.250s')", + removevb.buf, conff->name); + } + pop_cleanup(ehflag_normaltidy); /* closedir */ + + } + + pkg->installed.conffiles= 0; + modstatdb_note(pkg); + + maintainer_script_installed(pkg, POSTRMFILE, "post-removal", + "purge", (char*)0); + + /* fixme: retry empty directories */ + + } + + if (pkg->want == want_purge) { + + /* ie, either of the two branches above. */ + varbufreset(&fnvb); + varbufaddstr(&fnvb,admindir); + varbufaddstr(&fnvb,"/" INFODIR); + varbufaddstr(&fnvb,pkg->name); + pkgnameused= fnvb.used; + + varbufaddstr(&fnvb,"." LISTFILE); + varbufaddc(&fnvb,0); + debug(dbg_general, "removal_bulk purge done, removing list `%s'",fnvb.buf); + if (unlink(fnvb.buf) && errno != ENOENT) ohshite("cannot remove old files list"); + + fnvb.used= pkgnameused; + varbufaddstr(&fnvb,"." POSTRMFILE); + varbufaddc(&fnvb,0); + debug(dbg_general, "removal_bulk purge done, removing postrm `%s'",fnvb.buf); + if (unlink(fnvb.buf) && errno != ENOENT) ohshite("can't remove old postrm script"); + + pkg->status= stat_notinstalled; + + /* This will mess up reverse links, but if we follow them + * we won't go back because pkg->status is stat_notinstalled. + */ + pkg->installed.depends= 0; + pkg->installed.essential= 0; + pkg->installed.description= pkg->installed.maintainer= + pkg->installed.version= pkg->installed.revision= 0; + pkg->installed.arbs= 0; + } + + pkg->eflag= eflagv_ok; + modstatdb_note(pkg); + + debug(dbg_general, "removal done"); +} + diff --git a/main/update.c b/main/update.c new file mode 100644 index 00000000..31939373 --- /dev/null +++ b/main/update.c @@ -0,0 +1,75 @@ +/* + * dpkg - main program for package management + * update.c - options which update the `available' database + * + * Copyright (C) 1995 Ian Jackson + * + * This 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, + * or (at your option) any later version. + * + * This 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 dpkg; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "dpkg.h" +#include "dpkg-db.h" +#include "myopt.h" + +#include "main.h" + +void availablefrompackages(const char *const *argv, int replace) { + const char *sourcefile= argv[0]; + int count; + static struct varbuf vb; + + if (!sourcefile || argv[1]) + badusage("--%s needs exactly one Packages file argument", cipaction->olong); + + if (!f_noact) { + if (access(admindir,W_OK)) { + if (errno != EACCES) + ohshite("unable to access dpkg status area for bulk available update"); + else + ohshit("bulk available update requires write access to dpkg status area"); + } + lockdatabase(admindir); + } + + if (replace) { + printf("Replacing available packages info, using %s.\n",sourcefile); + } else { + printf("Updating available packages info, using %s.\n",sourcefile); + } + + varbufaddstr(&vb,admindir); + varbufaddstr(&vb,"/" AVAILFILE); + varbufaddc(&vb,0); + + if (!replace) + parsedb(vb.buf, pdb_recordavailable|pdb_rejectstatus, 0,0,0); + + count= parsedb(sourcefile, pdb_recordavailable|pdb_rejectstatus, 0,0,0); + + if (!f_noact) { + writedb(vb.buf,1,0); + unlockdatabase(admindir); + } + + printf("Information about %d package(s) was updated.\n",count); +} diff --git a/md5sum/Makefile.in b/md5sum/Makefile.in new file mode 100644 index 00000000..0cd7f852 --- /dev/null +++ b/md5sum/Makefile.in @@ -0,0 +1,44 @@ +# (-*- Fundamental -*-) + +srcdir = @srcdir@ +VPATH = @srcdir@ + +prefix = @prefix@ +exec_prefix = $(prefix) +bindir = $(exec_prefix)/bin +mandir = $(prefix)/man/man1 + +SRC = md5.c md5sum.c +OBJ = md5.o md5sum.o +HDR = md5.h + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ + +CC = @CC@ + +CFLAGS = @CFLAGS@ $(XCFLAGS) +LDFLAGS = $(XLDFLAGS) +LIBS = -L../lib $(XLIBS) +ALL_CFLAGS = -I../lib -I.. @DEFS@ $(CFLAGS) + +.SUFFIXES: .c .o + +.c.o: + $(CC) $(ALL_CFLAGS) -c $< + +all: md5sum + +md5sum: md5.o md5sum.o + $(CC) $(LDFLAGS) -o md5sum md5.o md5sum.o + +clean: + rm -f *.o core md5sum + +distclean: clean + rm -f Makefile *.orig *~ *.~* ./#*# + +install: all + $(INSTALL_PROGRAM) -s md5sum $(bindir)/md5sum + $(INSTALL_DATA) md5sum.1 $(mandir)/md5sum.1 diff --git a/md5sum/md5.c b/md5sum/md5.c new file mode 100644 index 00000000..0ac9d192 --- /dev/null +++ b/md5sum/md5.c @@ -0,0 +1,241 @@ +/* + * This code implements the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * + * To compute the message digest of a chunk of bytes, declare an + * MD5Context structure, pass it to MD5Init, call MD5Update as + * needed on buffers full of bytes, and then call MD5Final, which + * will fill a supplied 16-byte array with the digest. + * + * Changed so as no longer to depend on Colin Plumb's `usual.h' header + * definitions; now uses stuff from dpkg's config.h. + * - Ian Jackson . + * Still in the public domain. + */ + +#include /* for memcpy() */ +#include /* for stupid systems */ +#include /* for ntohl() */ + +#include "config.h" +#include "md5.h" + +#ifdef WORDS_BIGENDIAN +void +byteSwap(UWORD32 *buf, unsigned words) +{ + md5byte *p = (md5byte *)buf; + + do { + *buf++ = (UWORD32)((unsigned)p[3] << 8 | p[2]) << 16 | + ((unsigned)p[1] << 8 | p[0]); + p += 4; + } while (--words); +} +#else +#define byteSwap(buf,words) +#endif + +/* + * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious + * initialization constants. + */ +void +MD5Init(struct MD5Context *ctx) +{ + ctx->buf[0] = 0x67452301; + ctx->buf[1] = 0xefcdab89; + ctx->buf[2] = 0x98badcfe; + ctx->buf[3] = 0x10325476; + + ctx->bytes[0] = 0; + ctx->bytes[1] = 0; +} + +/* + * Update context to reflect the concatenation of another buffer full + * of bytes. + */ +void +MD5Update(struct MD5Context *ctx, md5byte const *buf, unsigned len) +{ + UWORD32 t; + + /* Update byte count */ + + t = ctx->bytes[0]; + if ((ctx->bytes[0] = t + len) < t) + ctx->bytes[1]++; /* Carry from low to high */ + + t = 64 - (t & 0x3f); /* Space available in ctx->in (at least 1) */ + if (t > len) { + memcpy((md5byte *)ctx->in + 64 - t, buf, len); + return; + } + /* First chunk is an odd size */ + memcpy((md5byte *)ctx->in + 64 - t, buf, t); + byteSwap(ctx->in, 16); + MD5Transform(ctx->buf, ctx->in); + buf += t; + len -= t; + + /* Process data in 64-byte chunks */ + while (len >= 64) { + memcpy(ctx->in, buf, 64); + byteSwap(ctx->in, 16); + MD5Transform(ctx->buf, ctx->in); + buf += 64; + len -= 64; + } + + /* Handle any remaining bytes of data. */ + memcpy(ctx->in, buf, len); +} + +/* + * Final wrapup - pad to 64-byte boundary with the bit pattern + * 1 0* (64-bit count of bits processed, MSB-first) + */ +void +MD5Final(md5byte digest[16], struct MD5Context *ctx) +{ + int count = ctx->bytes[0] & 0x3f; /* Number of bytes in ctx->in */ + md5byte *p = (md5byte *)ctx->in + count; + + /* Set the first char of padding to 0x80. There is always room. */ + *p++ = 0x80; + + /* Bytes of padding needed to make 56 bytes (-8..55) */ + count = 56 - 1 - count; + + if (count < 0) { /* Padding forces an extra block */ + memset(p, 0, count + 8); + byteSwap(ctx->in, 16); + MD5Transform(ctx->buf, ctx->in); + p = (md5byte *)ctx->in; + count = 56; + } + memset(p, 0, count); + byteSwap(ctx->in, 14); + + /* Append length in bits and transform */ + ctx->in[14] = ctx->bytes[0] << 3; + ctx->in[15] = ctx->bytes[1] << 3 | ctx->bytes[0] >> 29; + MD5Transform(ctx->buf, ctx->in); + + byteSwap(ctx->buf, 4); + memcpy(digest, ctx->buf, 16); + memset(ctx, 0, sizeof(ctx)); /* In case it's sensitive */ +} + +#ifndef ASM_MD5 + +/* The four core functions - F1 is optimized somewhat */ + +/* #define F1(x, y, z) (x & y | ~x & z) */ +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) + +/* This is the central step in the MD5 algorithm. */ +#define MD5STEP(f,w,x,y,z,in,s) \ + (w += f(x,y,z) + in, w = (w<>(32-s)) + x) + +/* + * The core of the MD5 algorithm, this alters an existing MD5 hash to + * reflect the addition of 16 longwords of new data. MD5Update blocks + * the data and converts bytes into longwords for this routine. + */ +void +MD5Transform(UWORD32 buf[4], UWORD32 const in[16]) +{ + register UWORD32 a, b, c, d; + + a = buf[0]; + b = buf[1]; + c = buf[2]; + d = buf[3]; + + MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); + MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); + MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); + MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); + MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); + MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); + MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); + MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); + MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); + MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); + MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); + MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); + MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); + MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); + MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); + MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); + + MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); + MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); + MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); + MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); + MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); + MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); + MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); + MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); + MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); + MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); + MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); + MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); + MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); + MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); + MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); + MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); + + MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); + MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); + MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); + MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); + MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); + MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); + MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); + MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); + MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); + MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); + MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); + MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); + MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); + MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); + MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); + MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); + + MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); + MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); + MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); + MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); + MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); + MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); + MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); + MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); + MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); + MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); + MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); + MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); + MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); + MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); + MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); + MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} + +#endif diff --git a/md5sum/md5.h b/md5sum/md5.h new file mode 100644 index 00000000..021766c1 --- /dev/null +++ b/md5sum/md5.h @@ -0,0 +1,39 @@ +/* + * This is the header file for the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * + * To compute the message digest of a chunk of bytes, declare an + * MD5Context structure, pass it to MD5Init, call MD5Update as + * needed on buffers full of bytes, and then call MD5Final, which + * will fill a supplied 16-byte array with the digest. + * + * Changed so as no longer to depend on Colin Plumb's `usual.h' + * header definitions; now uses stuff from dpkg's config.h + * - Ian Jackson . + * Still in the public domain. + */ + +#ifndef MD5_H +#define MD5_H + +#define md5byte unsigned char + +struct MD5Context { + UWORD32 buf[4]; + UWORD32 bytes[2]; + UWORD32 in[16]; +}; + +void MD5Init(struct MD5Context *context); +void MD5Update(struct MD5Context *context, md5byte const *buf, unsigned len); +void MD5Final(unsigned char digest[16], struct MD5Context *context); +void MD5Transform(UWORD32 buf[4], UWORD32 const in[16]); + +#endif /* !MD5_H */ diff --git a/md5sum/md5sum.1 b/md5sum/md5sum.1 new file mode 100644 index 00000000..c5d5943c --- /dev/null +++ b/md5sum/md5sum.1 @@ -0,0 +1,70 @@ +.\" Hey, Emacs! This is an -*- nroff -*- source file. +.TH MD5SUM 1 "29th November 1995" "Lankester et al" "Debian GNU/Linux" +.SH NAME +md5sum \- generates or checks MD5 message digests + +.SH SYNOPSIS +.B md5sum +[-bv] [-c [file]] | [file...] + +.SH DESCRIPTION +.B md5sum +generates or checks MD5 checksums. The algorithm to generate the +checksum is reasonably fast and strong enough for most cases. Exact +specification of the algorithm is in +.I RFC 1321. + +Normally +.B md5sum +generates checksums of all files given to it as a parameter and prints +the checksums followed by the filenames. If, however, +.B -c +is specified, only one filename parameter is allowed. This file should +contain checksums and filenames to which these checksums refer to, and +the files listed in that file are checked against the checksums listed +there. See option +.B -c +for more information. + +.SS OPTIONS +.TP +.B -b +Use binary mode. In unix environment, only difference between this and +the normal mode is an asterix preceding the filename in the output. +.TP +.B -c +Check md5sum of all files listed in +.I file +against the checksum listed in the same file. The actual format of that +file is the same as output of +.B md5sum. +That is, each line in the file describes a file. A line looks like: + +.B + +So, for example, a file containing checksum for this manpage would look +like(don't worry, if the checkusum doesn't match, there is a minor +problem in keeping it up to date): + +.B c6514f34ffe6e1ce146e1f17db2c0f90 md5sum.1 +.TP +.B -v +Be more verbose. Print filenames when checking (with -c). + +.SH BUGS +The related MD4 message digest algorithm was broken in October 1995. +MD5 isn't looking as secure as it used to. + +This manpage is not quite accurate and has formatting inconsistent +with other manpages. + +.B md5sum +does not accept standard options like +.BR -\-\help . + +.SH AUTHOR + +.B md5sum +was originally written by Branko Lankester, and modified afterwards by +Colin Plumb and Ian Jackson (ijackson@gnu.ai.mit.edu). Manual page was +added by Juho Vuori (javuori@cc.helsinki.fi) diff --git a/md5sum/md5sum.c b/md5sum/md5sum.c new file mode 100644 index 00000000..cec2be76 --- /dev/null +++ b/md5sum/md5sum.c @@ -0,0 +1,246 @@ +/* + * md5sum.c - Generate/check MD5 Message Digests + * + * Compile and link with md5.c. If you don't have getopt() in your library + * also include getopt.c. For MSDOS you can also link with the wildcard + * initialization function (wildargs.obj for Turbo C and setargv.obj for MSC) + * so that you can use wildcards on the commandline. + * + * Written March 1993 by Branko Lankester + * Modified June 1993 by Colin Plumb for altered md5.c. + * Modified Feburary 1995 by Ian Jackson for use with Colin Plumb's md5.c. + * This file is in the public domain. + */ +#include +#include +#include "config.h" +#include "md5.h" + +#ifdef UNIX +#define FOPRTXT "r" +#define FOPRBIN "r" +#else +#ifdef VMS +#define FOPRTXT "r","ctx=stm" +#define FOPRBIN "rb","ctx=stm" +#else +#define FOPRTXT "r" +#define FOPRBIN "rb" +#endif +#endif + +extern char *optarg; +extern int optind; + +void usage(); +void print_digest(); +int mdfile(FILE *fp, unsigned char *digest); +int do_check(FILE *chkf); + +char *progname; +int verbose = 0; +int bin_mode = 0; + +void +main(int argc, char **argv) +{ + int opt, rc = 0; + int check = 0; + FILE *fp; + unsigned char digest[16]; + + progname = *argv; + while ((opt = getopt(argc, argv, "cbvp:h")) != EOF) { + switch (opt) { + case 'c': check = 1; break; + case 'v': verbose = 1; break; + case 'b': bin_mode = 1; break; + default: usage(); + } + } + argc -= optind; + argv += optind; + if (check) { + switch (argc) { + case 0: fp = stdin; break; + case 1: if ((fp = fopen(*argv, FOPRTXT)) == NULL) { + perror(*argv); + exit(2); + } + break; + default: usage(); + } + exit(do_check(fp)); + } + if (argc == 0) { + if (mdfile(stdin, digest)) { + fprintf(stderr, "%s: read error on stdin\n", progname); + exit(2); + } + print_digest(digest); + printf("\n"); + exit(0); + } + for ( ; argc > 0; --argc, ++argv) { + if (bin_mode) + fp = fopen(*argv, FOPRBIN); + else + fp = fopen(*argv, FOPRTXT); + if (fp == NULL) { + perror(*argv); + rc = 2; + continue; + } + if (mdfile(fp, digest)) { + fprintf(stderr, "%s: error reading %s\n", progname, *argv); + rc = 2; + } else { + print_digest(digest); + printf(" %c%s\n", bin_mode ? '*' : ' ', *argv); + } + fclose(fp); + } + exit(rc); +} + +void +usage() +{ + fprintf(stderr, "usage: md5sum [-bv] [-c [file]] | [file...]\n"); + fprintf(stderr, "Generates or checks MD5 Message Digests\n"); + fprintf(stderr, " -c check message digests (default is generate)\n"); + fprintf(stderr, " -v verbose, print file names when checking\n"); + fprintf(stderr, " -b read files in binary mode\n"); + fprintf(stderr, "The input for -c should be the list of message digests and file names\n"); + fprintf(stderr, "that is printed on stdout by this program when it generates digests.\n"); + exit(2); +} + +int +mdfile(FILE *fp, unsigned char *digest) +{ + unsigned char buf[1024]; + struct MD5Context ctx; + int n; + + MD5Init(&ctx); + while ((n = fread(buf, 1, sizeof(buf), fp)) > 0) + MD5Update(&ctx, buf, n); + MD5Final(digest, &ctx); + if (ferror(fp)) + return -1; + return 0; +} + +void +print_digest(unsigned char *p) +{ + int i; + + for (i = 0; i < 16; ++i) + printf("%02x", *p++); +} + +int +hex_digit(int c) +{ + if (c >= '0' && c <= '9') + return c - '0'; + if (c >= 'a' && c <= 'f') + return c - 'a' + 10; + return -1; +} + +int +get_md5_line(FILE *fp, unsigned char *digest, char *file) +{ + char buf[1024]; + int i, d1, d2, rc; + char *p = buf; + + if (fgets(buf, sizeof(buf), fp) == NULL) + return -1; + + for (i = 0; i < 16; ++i) { + if ((d1 = hex_digit(*p++)) == -1) + return 0; + if ((d2 = hex_digit(*p++)) == -1) + return 0; + *digest++ = d1*16 + d2; + } + if (*p++ != ' ') + return 0; + /* + * next char is an attribute char, space means text file + * if it's a '*' the file should be checked in binary mode. + */ + if (*p == ' ') + rc = 1; + else if (*p == '*') + rc = 2; + else { + fprintf(stderr, "%s: unrecognized line: %s", progname, buf); + return 0; + } + ++p; + i = strlen(p); + if (i < 2 || i > 255) + return 0; + p[i-1] = '\0'; + strcpy(file, p); + return rc; +} + +int +do_check(FILE *chkf) +{ + int rc, ex = 0, failed = 0, checked = 0; + unsigned char chk_digest[16], file_digest[16]; + char filename[256]; + FILE *fp; + int flen = 14; + + while ((rc = get_md5_line(chkf, chk_digest, filename)) >= 0) { + if (rc == 0) /* not an md5 line */ + continue; + if (verbose) { + if (strlen(filename) > flen) + flen = strlen(filename); + fprintf(stderr, "%-*s ", flen, filename); + } + if (bin_mode || rc == 2) + fp = fopen(filename, FOPRBIN); + else + fp = fopen(filename, FOPRTXT); + if (fp == NULL) { + fprintf(stderr, "%s: can't open %s\n", progname, filename); + ex = 2; + continue; + } + if (mdfile(fp, file_digest)) { + fprintf(stderr, "%s: error reading %s\n", progname, filename); + ex = 2; + fclose(fp); + continue; + } + fclose(fp); + if (memcmp(chk_digest, file_digest, 16) != 0) { + if (verbose) + fprintf(stderr, "FAILED\n"); + else + fprintf(stderr, "%s: MD5 check failed for '%s'\n", progname, filename); + ++failed; + } else if (verbose) + fprintf(stderr, "OK\n"); + ++checked; + } + if (verbose && failed) + fprintf(stderr, "%s: %d of %d file(s) failed MD5 check\n", progname, failed, checked); + if (!checked) { + fprintf(stderr, "%s: no files checked\n", progname); + return 3; + } + if (!ex && failed) + ex = 1; + return ex; +} diff --git a/methods/Makefile.in b/methods/Makefile.in new file mode 100644 index 00000000..47b5d835 --- /dev/null +++ b/methods/Makefile.in @@ -0,0 +1,60 @@ +# Copyright (C) 1994 Ian Murdock +# Copyright (C) 1994,1995 Ian Jackson +# +# This 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, +# or (at your option) any later version. +# +# This 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 dpkg; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +srcdir = @srcdir@ +VPATH = @srcdir@ + +prefix = @prefix@ +libdir = $(prefix)/lib +dpkglibdir = $(libdir)/dpkg +methodsdir = $(dpkglibdir)/methods +datadir = /var/lib/dpkg +methodsdatadir = $(datadir)/methods +methodsmnt = $(datadir)/methods/mnt + +SCRIPTS = setup update install +METHODS = disk floppy +DESCS = disk.desc.harddisk disk.desc.mounted disk.desc.cdrom \ + disk.desc.nfs floppy.desc.floppy + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ + +all: + +install: + set -e; for m in $(METHODS) ; do \ + d=$(methodsdir)/$$m ; \ + test -d $$d || mkdir $$d ; \ + e=$(methodsdatadir)/$$m ; \ + test -d $$e || mkdir $$e ; \ + $(INSTALL_DATA) $$m.names $$d/names ; \ + for s in $(SCRIPTS) ; do \ + $(INSTALL_PROGRAM) $$m.$$s $$d/$$s ; \ + done ; \ + done + set -e; for x in $(DESCS) ; do \ + d=`echo $$x | sed -e 's:\.:/:'` ; \ + $(INSTALL_DATA) $$x $(methodsdir)/$$d ; \ + done + +clean: + rm -f core + +distclean: clean + rm -f Makefile *.orig *~ *.~* ./#*# diff --git a/methods/disk.desc.cdrom b/methods/disk.desc.cdrom new file mode 100644 index 00000000..b400abb0 --- /dev/null +++ b/methods/disk.desc.cdrom @@ -0,0 +1,3 @@ +Installation from a CD-ROM containing a Debian distribution. The +CD-ROM may be or not be mounted already; it should contain a standard +ISO9660 CD-ROM filesystem. diff --git a/methods/disk.desc.harddisk b/methods/disk.desc.harddisk new file mode 100644 index 00000000..ba457503 --- /dev/null +++ b/methods/disk.desc.harddisk @@ -0,0 +1,9 @@ +Installation from filesystem on a hard disk partition that's not +currently mounted. + +The partition should contain the Packages.gz file from each +distribution area being installed (usually stable, contrib and +non-free) as well as the corresponding binary/*/*.deb files. + +The easiest way to get this is to make a (partial) copy of the +distribution site's directory hierarchy, if possible. diff --git a/methods/disk.desc.mounted b/methods/disk.desc.mounted new file mode 100644 index 00000000..b980d70a --- /dev/null +++ b/methods/disk.desc.mounted @@ -0,0 +1,12 @@ +Installation from a directory on a filesystem that has already been +mounted. If the options available for mounting things automatically +are inadequate for your needs you'll need to get a shell command +prompt (for example by switching VT using Alt-F or quitting +dselect) and use it to mount the filesystem before continuing. + +The area you're installing from should contain the Packages.gz file +from each distribution area being installed (usually stable, contrib +and non-free) as well as the corresponding binary/*/*.deb files. + +The easiest way to do get this is to make a (partial) copy of the +distribution site's directory hierarchy, if possible. diff --git a/methods/disk.desc.nfs b/methods/disk.desc.nfs new file mode 100644 index 00000000..77bb6408 --- /dev/null +++ b/methods/disk.desc.nfs @@ -0,0 +1,9 @@ +Installation across the network, from an NFS server which not +currently mounted. + +The server should have contain the Packages.gz file from each +distribution area being installed (usually stable, contrib and +non-free) as well as the corresponding binary/*/*.deb files. + +The easiest way for them to get this is to make a (partial) copy of +the distribution site's directory hierarchy, if possible. diff --git a/methods/disk.install b/methods/disk.install new file mode 100644 index 00000000..a14cc399 --- /dev/null +++ b/methods/disk.install @@ -0,0 +1,127 @@ +#!/bin/sh + +set -e +vardir="$1" +method=$2 +option=$3 + +cd "$vardir/methods/disk" + +. ./shvar.$option + +xit=1 +trap ' + if [ -n "$umount" ] + then + umount "$umount" >/dev/null 2>&1 + fi + exit $xit +' 0 + +if [ -n "$p_blockdev" ] +then + umount="$p_mountpoint" + mount -rt "$p_fstype" -o nosuid,nodev "$p_blockdev" "$p_mountpoint" +fi + +if [ -n "$p_nfs" ] +then + umount="$p_mountpoint" + mount -rt nfs "$p_nfs" -o nosuid,nodev "$p_mountpoint" +fi + +case "$p_mountpoint" in +*/ ) binaryprefix="$p_mountpoint" ;; +'' ) binaryprefix="" ;; +* ) binaryprefix="$p_mountpoint/" ;; +esac + +predep="$vardir/predep-package" +while true +do + set +e + dpkg --predep-package >"$predep" + rc=$? + set -e + if test $rc = 1; then break; fi + test $rc = 0 + + perl -e ' + ($binaryprefix,$predep) = @ARGV; + $binaryprefix =~ s,/*$,/, if length($binaryprefix); + open(P,"< $predep") || die "cannot open $predep: $!\n"; + while (

) { + s/\s*\n$//; + $package= $_ if s/^Package: //i; + @filename= split(/ /,$_) if s/^Filename: //i; + @msdosfilename= split(/ /,$_) if s/^MSDOS-Filename: //i; + } + length($package) || die "internal error - no package"; + @filename || die "internal error - no filename"; + @filename==@msdosfilename || !@filename || !@msdosfilename || + die "internal error - mismatch >@filename< >@msdosfilename<"; + @invoke=(); $|=1; + for ($i=0; $i<=$#filename; $i++) { + $ppart= $i+1; + print "Looking for part $ppart of $package ... "; + if (-f "$binaryprefix$filename[$i]") { + $print= $filename[$i]; + $invoke= "$binaryprefix$filename[$i]"; + } elsif (-f "$binaryprefix$msdosfilename[$i]") { + $print= $msdosfilename[$i]; + $invoke= "$binaryprefix$msdosfilename[$i]"; + } else { + $base= $filename[$i]; $base =~ s,.*/,,; + $msdosbase= $msdosfilename[$i]; $msdosbase =~ s,.*/,,; + defined($c= open(X,"-|")) || + die "failed to fork for find: $!\n"; + if (!$c) { + exec("find", length($binaryprefix) + ? $binaryprefix : ".","-follow", + "-name",$base,"-o","-name",$msdosbase); + die "failed to exec find: $!\n"; + } + while (chop($invoke= )) { last if -f $invoke; } + $print= $invoke; + if (substr($print,0,length($binaryprefix)+1) eq + "$binaryprefix/") { + $print= substr($print,length($binaryprefix)); + } + } + if (!length($invoke)) { + print STDERR " + +Oh dear, I need to install or upgrade $package, but I don'\''t see +the appropriate file(s) anywhere. I'\''m expecting version $version or +later, as that is listed in the Packages file. + +Perhaps you downloaded it with an unexpected name, or something. +In any case, you must find the file(s) and then either place it with +the correct filename(s) (as listed in the Packages file or in +/var/lib/dpkg/available) and rerun the installation, or upgrade the +package by using `dpkg --install --auto-deconfigure'\'' by hand. + +"; + exit(1); + } + print "$print\n"; + push(@invoke,$invoke); + } + print "Running dpkg -iB for $package ...\n"; + exec("dpkg","-iB","--",@invoke); + die "failed to exec dpkg: $!\n"; + ' -- "$binaryprefix$p_main_binary" "$predep" +done + +for f in main ctb nf lcl +do + eval 'this_binary=$p_'$f'_binary' + if [ -z "$this_binary" ]; then continue; fi + echo Running dpkg -iGROEB "$binaryprefix$this_binary" + dpkg -iGROEB "$binaryprefix$this_binary" +done + +echo -n 'Installation OK. Hit RETURN. ' +read response + +xit=0 diff --git a/methods/disk.names b/methods/disk.names new file mode 100644 index 00000000..1f55b78e --- /dev/null +++ b/methods/disk.names @@ -0,0 +1,4 @@ +30 cdrom Install from a CD-ROM. +35 nfs Install from an NFS server (not yet mounted). +40 harddisk Install from a hard disk partition (not yet mounted). +42 mounted Install from a filesystem which is already mounted. diff --git a/methods/disk.setup b/methods/disk.setup new file mode 100644 index 00000000..5a692003 --- /dev/null +++ b/methods/disk.setup @@ -0,0 +1,562 @@ +#!/bin/sh + +set -e +vardir="$1" +method=$2 +option=$3 + +cd "$vardir/methods/disk" +tp=/tmp/ddm$$ + +xit=1 +trap ' + rm -f $tp.? + if [ -n "$umount" ] + then + umount "$umount" >/dev/null 2>&1 + fi + exit $xit +' 0 + +if ls -d "$tp.?" >/dev/null 2>&1 +then + rm $tp.? +fi + +yesno () { + while true + do + echo -n "$2 [$1] " + read response + if [ -z "$response" ] + then + response="$1" + fi + case "$response" in + [Nn]*) yesno=no ; return ;; + [Yy]*) yesno=yes ; return ;; + esac + done +} + +getblockdev () { + mountpoint="$vardir/methods/mnt" + if [ -z "$defaultdevice" ] + then + defaultdevice="$newdefaultdevice" + elif [ "$defaultdevice" != "$newdefaultdevice" ] + then + echo \ + "Last time you specified installation from $defaultdevice." + fi + promptstring="$1" + while [ -z "$blockdevice" ] + do + echo -n "$promptstring [$defaultdevice]: " + read response + if [ -z "$response" ] + then + response="$defaultdevice" + fi + if ! [ -b "$response" ] + then + echo "$response is not a block device."; continue + fi + tryblockdevice="$response" + if [ $option = cdrom ] + then + fstype=iso9660 + elif [ $option = harddisk ] + then + blockbase="`echo \"$tryblockdevice\" | sed -e 's/[0-9]\{1,\}$//'`" + set +e + echo -e "p\nq\n" | fdisk "$blockbase" 2>/dev/null >$tp.f + set -e + proposeddevice="$tryblockdevice" perl -ne ' +next unless /^ *Device +Boot +Begin +Start +End +Blocks +Id +System *$/i .. !/\S/; +next unless s:^/\S+:: && $& eq $ENV{"proposeddevice"}; +next unless s/^ +(\* +)?\d+ +\d+ +\d+ +\d+\+? +//; +next unless m/^([0-9a-f]{1,2}) /i; +%types= ( "1","msdos", "4","msdos", "6","msdos", "7","hpfs", "80","minix", + "81","minix", "83","ext2" ); +print $types{$1}; exit(0); ' <$tp.f >$tp.m + defaultfstype="`cat $tp.m`" + if [ -n "$defaultfstype" ] + then + cat </dev/null + then + echo \ + "Your kernel does not appear to support that filesystem type." + defaultfstype="" + fi + fi + echo -n "Supported filesystems: " + sed -e 's/^.* / /' /proc/filesystems | tr '\n' ' ' + echo -n " +Enter filesystem type (for $tryblockdevice) [$defaultfstype]: " + read fstype + if [ -z "$fstype" ] + then + fstype="$defaultfstype" + fi + fi + umount="$mountpoint" + if mount -rt "$fstype" -o nosuid,nodev "$tryblockdevice" "$mountpoint" + then + echo + blockdevice="$tryblockdevice" + else + umount="" + echo \ + "Unable to mount $tryblockdevice on $mountpoint, type $fstype." + fi + done +} + +outputparam () { + echo "$2" | sed -e "s/'/'\\\\''/; s/^/$1='/; s/$/'/" >&3 +} + +intrkey="`stty -a | sed -n 's/.*intr = \([^;]*\);.*/\1/p'`" +echo " +If you make a mistake, use the interrupt key ($intrkey) to abort. +" + +# State variables, `best first' +# {main,ctb,nf,lcl}_{packages,binary} +# Empty before we've found them or if they're not available, +# set to the relevant bit under mountpoint otherwise. +# hierbase +# A directory containing a Debian FTP site mirror tree. +# mountpoint +# The mountpoint for the filesystem containing the stuff +# empty or unset if we don't know yet, or if we haven't mounted anything; +# may also be empty if `directory' was set. +# blockdevice +# The actual block device to mount. +# fstype +# The filesystem type to use. +# defaultdevice +# The default block device to mount. + +p_usedevel=no +if [ -f shvar.$option ] +then + . ./shvar.$option + defaultdevice="$p_blockdev" + defaultnfsserver="$p_nfsserver" + defaultnfsrempath="$p_nfsrempath" + usedevel="$p_usedevel" +fi + +if [ $option = cdrom ] +then + mount >$tp.m + sed -n 's/ ([^)]*)$//; s/^[^ ]* on //; s/ type iso9660$//p' <$tp.m >$tp.l + ncdroms=`wc -l <$tp.l` + if [ $ncdroms -gt 1 ] + then + response="" + while [ -z "$response" ] + do + echo \ + 'Several CD-ROMs (or other ISO9660 filesystems) are mounted:' + egrep 'type iso9660 \([^)]*\)$' <$tp.m | nl + echo -n \ + 'Is it any of these ? Type a number, or `n'\'' for none. ' + read response + response="`echo \"$response\" | sed -e 's/[ ]*$//'`" + if expr "$response" : '[0-9][0-9]*$' >/dev/null && \ + [ $response -ge 1 -a $response -le $ncdroms ] + then + mountpoint="`sed -n $response'p' <$tp.l`" + echo + elif expr "$response" : '[Nn]' >/dev/null + then + mountpoint="" + else + response="" + fi + done + elif [ $ncdroms = 1 ] + then + mountpoint="`cat $tp.l`" + perl -ne 'print if s/ type iso9660 \([^)]*\)$// && s/ on .*$//;' \ + <$tp.m >$tp.d + blockdevice="`cat $tp.d`" + yesno yes \ + "I see a CD-ROM: $blockdevice, mounted on $mountpoint. Is it the right one ?" + if [ $yesno = no ] + then + echo 'Unmounting it ...' + umount="$mountpoint" + while true + do + echo -n \ + 'Please insert the right disc, and hit return: ' + read response + if mount -rt iso9660 -o nosuid,nodev \ + "$blockdevice" "$mountpoint" + then + echo + break + fi + done + fi + fi + if [ -z "$mountpoint" ] + then + if [ -b /dev/cdrom ] + then + echo \ + 'I see that /dev/cdrom exists and is a block device.' + newdefaultdevice=/dev/cdrom + fi + getblockdev 'Insert the CD-ROM and enter the block device name' + fi +fi + +if [ $option = nfs ] +then + mountpoint="$vardir/methods/mnt" + while [ -z "$nfsserver" ] + do + echo -n \ +"What is the name of the NFS server ? [$defaultnfsserver] " + read response + if [ -z "$response" -a -n "$defaultnfsserver" ] + then + response="$defaultnfsserver" + fi + if [ -z "$response" ]; then continue; fi + if [ -x /usr/bin/rpcinfo ] + then + if rpcinfo -u "$response" mountd >/dev/null + then + nfsserver="$response" + else + echo "$response appears not to be an NFS server." + fi + elif [ -x /bin/ping ] + then + if ping -q -c 1 "$response" | grep -q ', 1 packets received' + then + nfsserver="$response" + else + echo "$response appears to be down or nonexistent." + fi + else + echo \ +"(I can't check that now because there is no /usr/bin/rpcinfo or /bin/ping.)" + nfsserver="$response" + fi + done + while [ -z "$nfsrempath" ] + do + echo -n " +What is the pathname on the NFS server of the filesystem with +the Debian files ? [$defaultnfsrempath] " + read response + if [ -z "$response" -a -n "$defaultnfsrempath" ] + then + response="$defaultnfsrempath" + else + response="`echo \"$response\" | sed -e 's:/$::; s:^/*:/:'`" + fi + umount="$mountpoint" + if mount -rt nfs -o nosuid,nodev "$nfsserver:$response" "$mountpoint" + then + echo + nfsrempath="$response" + else + umount="" + echo \ +"Unable to mount NFS filesystem $nfsserver:$response." + fi + done + nfs="$nfsserver:$nfsrempath" +fi + +if [ $option = harddisk ] +then + set +e + echo -e 'p\nq\n' | fdisk /dev/hda 2>/dev/null >$tp.f + if [ $? != 0 ] + then + echo -e 'p\nq\n' | fdisk /dev/sda 2>/dev/null >$tp.f + fi + set -e + perl -ne ' +next unless /^ *Device +Boot +Begin +Start +End +Blocks +Id +System *$/i .. !/\S/; +next unless / [146] +DOS \d+-bit \S+$/; +next unless m:^/\S+:; +print $&; ' <$tp.f >$tp.d + newdefaultdevice="`cat $tp.d`" + echo " +I need to know which disk partition contains the distribution files; +disk partitions are specified by the block device name in Linux." + if [ -n "$newdefaultdevice" ] + then + echo \ +"By the way, $newdefaultdevice looks like a DOS partition." + fi + getblockdev "Enter the partition's block device name" +fi + +if [ -n "$mountpoint" ] +then + # We must have $mountpoint + if [ $option = cdrom ] + then + echo \ +'All directory names should be entered relative to the root of the CD-ROM. +' + elif [ $option = nfs ] + then + echo \ +"All directory names should be entered relative to the root of the NFS +filesystem, ie relative to $nfsrempath on the server. +" + else + echo \ +"All directory names should be entered relative to the root of the +$fstype filesystem on $blockdevice. +" + fi +fi + +while true +do + if [ $option = cdrom ] + then + echo \ +"I would like to know where on the CD-ROM the top level of the Debian +distribution is - this will usually contain the Packages-Master file. + +If the CD-ROM is badly organised and doesn't have a straightforward copy of +the distribution you may answer \`none' and we'll go through the parts +I need individually." + else + echo \ +"In order to make it easy for me to find the relevant files I'd ideally +like to install from a straightforward copy of the Debian distribution. +To use this I'll need to know where the top level of that copy of the +distribution is - this directory usually contains the Packages-Master file. + +If you do not have a straightforward copy of the distribution available +just answer \`none' and we'll go through the parts I need individually." + fi + defhierbase=none + if [ -n "$p_hierbase" ] + then + if [ -d "$mountpoint/$p_hierbase/stable/binary" ] + then + echo " +Last time you said \`$p_hierbase', and that looks plausible." + defhierbase="$p_hierbase" + else + echo " +Last time you said \`$p_hierbase', but that doesn't look plausible, +since \`$p_hierbase/stable/binary' doesn't seem to exist." + fi + fi + if [ none = "$defhierbase" -a -d "$mountpoint/debian/stable/binary" ] + then + echo " +\`/debian' exists and looks plausible, so that's the default." + defhierbase=/debian + fi + echo -n \ +"Distribution top level ? [$defhierbase] " + read response + if [ -z "$response" ] + then + response="$defhierbase" + fi + if [ none = "$response" ] + then + hierbase="" + break + elif [ -d "$mountpoint/$response/stable/binary" ] + then + hierbase="`echo \"$response\" | sed -e 's:/$::; s:^/*:/:'`" + break + fi + echo \ +"$response/stable/binary does not exist. +" +done + +whichmain=stable +if [ -n "$hierbase" ] +then + if [ -d "$mountpoint/$hierbase/development/binary" ] + then + echo \ +' +Both a stable released distribution and a work-in-progress +development tree are available for installation. Would you like to +use the unreleased development tree (this is only recommended for +experts who like to live dangerously and want to help with testing) ?' + yesno "$p_usedevel" 'Use unreleased development distribution ?' + usedevel="$response" + if [ "$usedevel" = yes ] + then + whichmain=development + fi + else + usedevel=no + fi + echo +fi + +check_binary () { + # args: area-in-messages directory + if ! [ -d "$mountpoint/$2" ] + then + echo "\`$2' does not exist." + return + fi + if ! find "$mountpoint/$2" -follow -name '*.deb' -print \ + 2>/dev/null | head -1 | grep . >/dev/null + then + echo "\`$2' does not contain any *.deb packages. Hmmpf." + return + fi + echo "Using \`$2' as $1 binary dir." + this_binary="$2" +} + +find_area () { + # args: area-in-messages area-in-vars subdirectory-in-hier + # last-time-binary last-time-packages + this_binary='' + this_packages='' + if [ -n "$hierbase" ] + then + check_binary $1 "$hierbase/$3/binary" + fi + if [ $option = cdrom -a $2 = nf -a -z "$this_binary" ] + then + echo ' +Note: most CD-ROM distributions of Debian do not include programs +available in the `non-free'\'' directory of the distribution site. +This is because these programs have copyrights that prevent +distribution for profit on a CD-ROM - ie they are not free software. +If you wish to install these programs you'\''ll have to get them from an +alternative source.' + fi + while [ -z "$this_binary" ] + do + defaultbinary="$4" + echo " +Which directory contains the *.deb packages from the $1 distribution +area (this directory is named \`$3/binary' on the distribution site) ?" + if [ $2 != main ] + then + if [ -z "$defaultbinary" ] + then + defaultbinary=none + fi + echo \ +"Say \`none' if this area is not available." + fi + echo -n \ +"Enter _$1_ binary dir. [$4] + ? " + read response + if [ -z "$response" -a -n "$defaultbinary" ] + then + response="$defaultbinary" + fi + if [ none = "$response" -a $2 != main ] + then + break + fi + case "$response" in + '' | none) continue ;; + esac + check_binary $1 "`echo \"$response\" | sed -e 's:/$::; s:^/*:/:'`" + done + if [ -n "$this_binary" ] + then + for f in Packages.gz packages.gz Packages packages + do + if [ -f "$mountpoint/$this_binary/$f" ] + then + echo "Using \`$this_binary/$f' for $1." + this_packages="$this_binary/$f" + break + fi + done + while [ -z "$this_packages" ] + do + echo -n " +I can't find the $1 \`Packages' file. The information in the +\`Packages' file is important for package selection during new +installations, and is very useful for upgrades. + +If you overlooked it when downloading you should do get it now and +return to this installation procedure when you have done so: you will +find one Packages file and one Packages.gz file -- either will do -- +in the \`binary' subdirectory of each area on the FTP sites and +CD-ROMs. + +You need a separate Packages file from each of the distribution areas +you wish to install. + +Where is the _$1_ \`Packages' file (if none is available, say\`none') +[$5] + ? " + read response + if [ -z "$response" -a -n "$5" ] + then + response="$5" + fi + case "$response" in + '') continue ;; + none) break ;; + /*) this_packages="$response" ;; + *) this_packages="/$response" ;; + esac + done + fi + eval $2'_binary="$this_binary"' + eval $2'_packages="$this_packages"' +} + +find_area main main "$whichmain" "$p_main_binary" "$p_main_packages" +find_area contrib ctb contrib "$p_ctb_binary" "$p_ctb_packages" +find_area non-free nf non-free "$p_nf_binary" "$p_nf_packages" +find_area local lcl local "$p_lcl_binary" "$p_lcl_packages" + +echo -n ' +Hit RETURN to continue. ' +read response + +exec 3>shvar.$option.new + +outputparam p_blockdev "$blockdevice" +outputparam p_fstype "$fstype" +outputparam p_mountpoint "$mountpoint" +outputparam p_nfsserver "$nfsserver" +outputparam p_nfsrempath "$nfsrempath" +outputparam p_nfs "$nfs" +outputparam p_hierbase "$hierbase" +outputparam p_usedevel "$usedevel" +outputparam p_main_packages "$main_packages" +outputparam p_main_binary "$main_binary" +outputparam p_ctb_packages "$ctb_packages" +outputparam p_ctb_binary "$ctb_binary" +outputparam p_nf_packages "$nf_packages" +outputparam p_nf_binary "$nf_binary" +outputparam p_lcl_packages "$lcl_packages" +outputparam p_lcl_binary "$lcl_binary" + +mv shvar.$option.new shvar.$option + +xit=0 diff --git a/methods/disk.update b/methods/disk.update new file mode 100644 index 00000000..68a52592 --- /dev/null +++ b/methods/disk.update @@ -0,0 +1,71 @@ +#!/bin/sh + +set -e +vardir="$1" +method=$2 +option=$3 + +cd "$vardir/methods/disk" + +. ./shvar.$option + +if [ -z "$p_main_packages" -a -z "$p_ctb_packages" -a \ + -z "$p_nf_packages" -a -z "$p_lcl_packages " ] +then + echo ' +No Packages files available, cannot update available packages list. +Hit RETURN to continue. ' + read response + exit 0 +fi + +xit=1 +trap ' + rm -f packages-{main,ctb,nf,lcl} + if [ -n "$umount" ] + then + umount "$umount" >/dev/null 2>&1 + fi + exit $xit +' 0 + +if [ -n "$p_blockdev" ] +then + umount="$p_mountpoint" + mount -rt "$p_fstype" -o nosuid,nodev "$p_blockdev" "$p_mountpoint" +fi + +if [ -n "$p_nfs" ] +then + umount="$p_mountpoint" + mount -rt nfs "$p_nfs" -o nosuid,nodev "$p_mountpoint" +fi + +for f in main ctb nf lcl +do + eval 'this_packages=$p_'$f'_packages' + if [ -z "$this_packages" ]; then continue; fi + case "$p_mountpoint" in + */ ) packagesfile="$p_mountpoint$this_packages" ;; + '' ) packagesfile="$this_packages" ;; + * ) packagesfile="$p_mountpoint/$this_packages" ;; + esac + case "$packagesfile" in + *.gz | *.Z | *.GZ | *.z) + echo -n "Uncompressing $packagesfile ... " + zcat <"$packagesfile" >packages-$f + echo done. + dpkg --merge-avail packages-$f + ;; + '') + ;; + *) + dpkg --merge-avail "$packagesfile" + ;; + esac +done + +echo -n 'Update OK. Hit RETURN. ' +read response + +xit=0 diff --git a/methods/floppy.desc.floppy b/methods/floppy.desc.floppy new file mode 100644 index 00000000..0bd6329e --- /dev/null +++ b/methods/floppy.desc.floppy @@ -0,0 +1,9 @@ +Installation using a pile of floppies, at least one of which (usually +the first) contains the Packages file, and the rest of which contain +the binary *.deb files. + +If you are installing software from the `non-free' or `contrib' +directories as well as the main Debian distribution you must have the +Packages files for those areas on separate floppies. The usual way to +do this is to put each Packages file on the first floppy which contains +packages from the relevant area. diff --git a/methods/floppy.install b/methods/floppy.install new file mode 100644 index 00000000..a9823a49 --- /dev/null +++ b/methods/floppy.install @@ -0,0 +1,100 @@ +#!/bin/sh + +set -e +vardir="$1" +method=$2 +option=$3 + +cd "$vardir/methods/floppy" + +mountpoint="$vardir/methods/mnt" + +. ./shvar.$option + +help () { + echo ' +Now I need the disks containing the packages to be installed. +I shall keep telling you what is left to be done, in case that +is helpful deciding which floppy to use.' +} + +help + +xit=1 +trap ' + if [ -n "$umount" ] + then + umount "$umount" + fi + exit $xit +' 0 + +while [ -z "$goconfigure" ] +do + yet="`dpkg --yet-to-unpack`" + if [ -z "$yet" ] + then + echo ' +All packages unpacked, going on to configure them. +' + goconfigure=1 + continue + fi + echo ' +Packages yet to be unpacked:' + echo "$yet" + dpkg-split -l + + echo -n ' +Insert a disk containing *.deb files, or type q to quit. ' + read response + case "$response" in + [Qq] | [Qq][Uu][Ii][Tt] ) + goconfigure=1 + ;; + * ) + umount="$defaultfloppy" + if mount -rt "$defaultfstype" "$defaultfloppy" "$mountpoint" + then + echo + dpkg --unpack -GROEB "$mountpoint" || true + umount "$defaultfloppy" + fi + umount="" + ;; + esac +done + +if ! [ -z "$yet" ] +then + response="" + while [ -z "$response" ] + do + echo -n ' +Not all the packages have yet been unpacked. Shall I try to +proceed with configuration anyay ? If any of the packages which +have been unpacked so far depend on any that haven'\''t then you'\''ll +see error messages; on the other hand if you say no those packages that +could have been configured will not be. (y/n) ' + read response + case "$response" in + [Nn]* ) + echo ' +OK. Be sure to come back to this, because unpacked-but-not-configured +packages are not in general useable. Alternatively, use the Configure +option on the dselect menu. +' + exit 1 + ;; + [Yy]* ) + ;; + * ) + response="" + ;; + esac + done +fi + +dpkg --configure --pending + +xit=0 diff --git a/methods/floppy.names b/methods/floppy.names new file mode 100644 index 00000000..72b189a1 --- /dev/null +++ b/methods/floppy.names @@ -0,0 +1 @@ +50 floppy Install from a pile of floppy disks. diff --git a/methods/floppy.setup b/methods/floppy.setup new file mode 100644 index 00000000..8c16cd23 --- /dev/null +++ b/methods/floppy.setup @@ -0,0 +1,76 @@ +#!/bin/sh + +set -e +vardir="$1" +method=$2 +option=$3 + +cd "$vardir/methods/floppy" + +defaultfloppy=fd0 +defaultfstype=msdos +if [ -f shvar.$option ] +then + . ./shvar.$option + defaultfloppy="`echo \"$defaultfloppy\" | sed -e 's,^/dev/,,'`" +fi + +while [ -z "$floppy" ] +do + echo -n ' +Which floppy disk drive do you wish to use ? Give the name in +/dev (eg fd0) or the MSDOS drive letter (eg A). ['$defaultfloppy'] ' + read floppy + if [ -z "$floppy" ] + then + floppy="$defaultfloppy" + fi + case "$floppy" in + [ABab] | [ABab]: ) + floppy="`echo $floppy | \ + sed -e 's/:$//; s,^[Aa],/dev/fd0,; s,^[Bb],/dev/fd1,'`" + ;; + /* ) + ;; + * ) + floppy="/dev/$floppy" + ;; + esac + if ! [ -b "$floppy" ] + then + echo "$floppy is not a block device." + floppy="" + fi +done + +while [ -z "$fstype" ] +do + echo -n ' +What kind of filesystem is on the floppies ? ['$defaultfstype'] ' + read fstype + if [ -z "$fstype" ] + then + fstype="$defaultfstype" + fi + if ! grep " $fstype$" /proc/filesystems >/dev/null + then + echo \ + "Your kernel does not appear to support that filesystem type." + fstype="" + fi +done + +echo + +outputparam () { + echo "$2" | sed -e "s/'/'\\\\''/; s/^/$1='/; s/$/'/" >&3 +} + +exec 3>shvar.$option.new + +outputparam defaultfloppy "$floppy" +outputparam defaultfstype "$fstype" + +mv shvar.$option.new shvar.$option + +exit 0 diff --git a/methods/floppy.update b/methods/floppy.update new file mode 100644 index 00000000..19030894 --- /dev/null +++ b/methods/floppy.update @@ -0,0 +1,76 @@ +#!/bin/sh + +set -e +vardir="$1" +method=$2 +option=$3 + +cd "$vardir/methods/floppy" + +mountpoint="$vardir/methods/mnt" + +. ./shvar.$option + +help () { + echo ' +First I need the disk(s) which contain the Packages file(s) for +the parts of the archive you wish to be able to install. If you +want to install packages from the non-free and contrib areas of +the FTP site you need a floppy with a Packages file for each of +those, in addition to the main Packages file for the main Debian +distribution. If you don'\''t then you just need one Packages file.' +} + +help + +xit=1 +trap ' + if [ -n "$umount" ] + then + umount "$umount" + fi + exit $xit +' 0 + +tryupdate () { + if [ $success = 1 ]; then return; fi + if [ ! -f "$mountpoint/Packages" ]; then + echo "$mountpoint/Packages does not exist."; + return + fi + if dpkg --merge-avail "$mountpoint/Packages" + then + success=1 + echo ' +You may incorporate another Packages file from another floppy if you wish.' + fi +} + +while [ $xit = 1 ] +do + echo -n ' +Insert a disk containing a Packages file, or type q to quit. ' + read response + case "$response" in + [Qq] | [Qq][Uu][Ii][Tt] ) + xit=0 + ;; + * ) + umount="$defaultfloppy" + if mount -rt "$defaultfstype" "$defaultfloppy" "$mountpoint" + then + success=0 + tryupdate Packages + tryupdate packages + tryupdate PACKAGES + umount "$defaultfloppy" + fi + umount="" + ;; + esac +done + +echo ' +OK, continuting with installation.' + +xit=0 diff --git a/methods/hd.setup b/methods/hd.setup new file mode 100755 index 00000000..a238c529 --- /dev/null +++ b/methods/hd.setup @@ -0,0 +1,90 @@ +#!/bin/perl +# +# Copyright (C) 1994 Carl Streeter +# +# this script 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, +# or (at your option) any later version. +# +# this script 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 script; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +print "Is the partition to install from mounted? [Y] "; +$ans = ; +if ($ans =~ /^[Nn]/) { + do { + do { + print "Which device should I mount? /dev/"; + $drive = ; + chop $drive; + $drive =~ tr/[A-Z]/[a-z]/; + } while (! -b "/dev/$drive"); + + $mpoint = "/mnt"; + do { + print "Where should I mount it? (Please use full path) [$mpoint] "; + $newmp = ; + chop $newmp; + $mpoint = $newmp if ($newmp !~ /^$/); + } while (($mpoint !~ ?^/?) || (! -d $mpoint)); + + print "These filesystems are available:"; + open(FILESYS, ") { + next if /^nodev/; + chop; + /(\w+)/; + $systems .= "$1 "; + } + print "$systems\n"; + do { + print "What filesystem is the partition to mount? [ext2] "; + $filesys = ; + chop $filesys; + $filesys = "ext2" if ($filesys =~ /^$/); + $filesys =~ tr/[A-Z]/[a-z]/; + } while ($systems !~ /\s$filesys\s/); + + do { + print "Any other options for mount? "; + print "(eg. '-o ro' for cdrom, must start with '-') [] "; + $opts = ; + chop $opts; + } while ($opts !~ /^$/ && $opts !~ /^\-/); + + $command = "/bin/mount -t $filesys $opts /dev/$drive $mpoint"; + print "I will now run \"$command\"\n"; + # system("$command"); + } while ($?); +} # I never knew how hard I could make it to mount a drive. + +# Assumedly, the drive is now mounted + +open (STATUS, ">/var/lib/dpkg/methods/hd/hd.status") || die "Can't open hd.status"; +do { + print "What is the full path to the 'available' file?\n"; + print "This file is found as Packages on the ftp site and CDROM"; + print "Use 'none' if you don't have one."; + $avail = ; + chop $avail; +} while (! -f $avail || $avail !~ ?^/? || $avail !~ /none/); + +do{ + print "What is the full path to the base directory "; + print "containing the .deb packages?\n"; + $debpath = ; + chop $debpath; +} while(! -d $debpath || $debpath !~ ?^/?); + +print STATUS "AVAIL: $avail\n"; +print STATUS "DEBDIR: $debpath\n"; +close (STATUS); +exit (0); diff --git a/methods/hd.unpack b/methods/hd.unpack new file mode 100755 index 00000000..ad22c6b0 --- /dev/null +++ b/methods/hd.unpack @@ -0,0 +1,36 @@ +# Return associative array of fields from control file $file. +sub slurp +{ + local ($file) = @_; + local (%controlinfo); + local (%ci); + + open (CONTROL, $file) || return 1; + + # Get entire text of control file. + undef $/; $* = 1; $_ = ; + + # Join lines. + s/\n[ \t]+/ /g; + + # Split on fields. + %controlinfo = ('PRESTUFF', split (/^(\S+):\s*/)); + + $/ = "\n"; $* = 0; + foreach $key (keys %controlinfo) + { + $key2 = $key; $key2 =~ y/A-Z/a-z/; + chop ($controlinfo{$key}) if (/\n/); + $ci{$key2} = $controlinfo{$key}; + } + + return %ci; +} + +$file = "/var/lib/dpkg/methods/hd/hd.status"; +%info = slurp($file); +$dpkg = "dpkg --auto --unpack --no-auto-select "; +$dpkg .= "--refuse downgrade --skip-same-version"; + +system("$dpkg $info{'debdir'}"); + diff --git a/methods/hd.update b/methods/hd.update new file mode 100755 index 00000000..98bf56b3 --- /dev/null +++ b/methods/hd.update @@ -0,0 +1,37 @@ +# Return associative array of fields from control file $file. +sub slurp +{ + local ($file) = @_; + local (%controlinfo); + local (%ci); + + open (CONTROL, $file) || return 1; + + # Get entire text of control file. + undef $/; $* = 1; $_ = ; + + # Join lines. + s/\n[ \t]+/ /g; + + # Split on fields. + %controlinfo = ('PRESTUFF', split (/^(\S+):\s*/)); + + $/ = "\n"; $* = 0; + foreach $key (keys %controlinfo) + { + $key2 = $key; $key2 =~ y/A-Z/a-z/; + chop ($controlinfo{$key}) if (/\n/); + $ci{$key2} = $controlinfo{$key}; + } + + return %ci; +} + +$file = "/var/lib/dpkg/methods/hd/hd.status"; +%info = slurp($file); +open (IN, "<$info{'avail'}") || die "can't open $info{'avail'}"; +open (OUT, ">/var/lib/dpkg/available") || die "can't open /var/lib/dpkg/available"; +print OUT while (); +close IN; +close OUT; + diff --git a/mkinstalldirs b/mkinstalldirs new file mode 100755 index 00000000..0e293773 --- /dev/null +++ b/mkinstalldirs @@ -0,0 +1,35 @@ +#!/bin/sh +# Make directory hierarchy. +# Written by Noah Friedman +# Public domain. + +defaultIFS=' +' +IFS="${IFS-${defaultIFS}}" + +errstatus=0 + +for file in ${1+"$@"} ; do + oIFS="${IFS}" + # Some sh's can't handle IFS=/ for some reason. + IFS='%' + set - `echo ${file} | sed -e 's@/@%@g' -e 's@^%@/@'` + IFS="${oIFS}" + + pathcomp='' + + for d in ${1+"$@"} ; do + pathcomp="${pathcomp}${d}" + + if test ! -d "${pathcomp}"; then + echo "mkdir $pathcomp" 1>&2 + mkdir "${pathcomp}" || errstatus=$? + fi + + pathcomp="${pathcomp}/" + done +done + +exit $errstatus + +# eof diff --git a/scripts/Makefile.in b/scripts/Makefile.in new file mode 100644 index 00000000..e3fe56dd --- /dev/null +++ b/scripts/Makefile.in @@ -0,0 +1,79 @@ +# Copyright (C) 1994 Ian Murdock +# Copyright (C) 1994,1995 Ian Jackson +# +# This 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, +# or (at your option) any later version. +# +# This 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 dpkg; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +srcdir = @srcdir@ +VPATH = @srcdir@ + +prefix = @prefix@ +bindir = $(prefix)/bin +sbindir = $(prefix)/sbin +datadir = /var/lib/dpkg +altsdatadir = $(datadir)/alternatives +mandir = $(prefix)/man +man1dir = $(mandir)/man1 +man8dir = $(mandir)/man8 +man1 = 1 +man8 = 8 +libdir = $(prefix)/lib +dpkglibdir = $(libdir)/dpkg +etcdir= /etc +altsetcdir = $(etcdir)/alternatives +perlpath = @perlpath@ + +MAN1 = dpkg-name +EXC = dpkg-name +MAN8 = update-rc.d start-stop-daemon update-alternatives install-info +SBIN = update-rc.d start-stop-daemon update-alternatives install-info \ + dpkg-scanpackages dpkg-divert + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ + +.SUFFIXES: .pl .sh .gzuue + +.pl: + sed <$@.pl 's:^#!/usr/bin/perl:#!$(perlpath):' \ + | ../insert-version.pl >$@.new + chmod +x $@.new + mv $@.new $@ + +.sh: + sed <$@.sh 's:^dpkglibdir=/usr/lib/dpkg$$:dpkglibdir=$(dpkglibdir):' \ + | ../insert-version.pl >$@.new + mv $@.new $@ + +.gzuue: + uudecode <$@.gzuue + gunzip <$@.gz >$@.new + test ! -x $@.gz || chmod +x $@.new + rm $@.gz + mv $@.new $@ + +all: $(EXC) $(SBIN) + +clean: + rm -f $(EXC) $(SBIN) core *.new + +distclean: clean + rm -f Makefile *.orig *~ *.~* ./#*# i386elf-hello-world.gz + +install: all + for f in $(EXC) ; do $(INSTALL_PROGRAM) $$f $(bindir)/$$f ; done + for f in $(MAN1) ; do $(INSTALL_DATA) $$f.1 $(man1dir)/$$f.$(man1) ; done + for f in $(SBIN) ; do $(INSTALL_PROGRAM) $$f $(sbindir)/$$f ; done + for f in $(MAN8) ; do $(INSTALL_DATA) $$f.8 $(man8dir)/$$f.$(man8) ; done diff --git a/scripts/dpkg-divert.pl b/scripts/dpkg-divert.pl new file mode 100644 index 00000000..dff1854c --- /dev/null +++ b/scripts/dpkg-divert.pl @@ -0,0 +1,220 @@ +#!/usr/bin/perl -- + +#use POSIX; &ENOENT; +sub ENOENT { 2; } +# Sorry about this, but POSIX.pm isn't necessarily available + +$version= '1.0.11'; # This line modified by Makefile +sub usageversion { + print(STDERR < + dpkg-divert [options] --remove + dpkg-divert [options] --list [] + +Options: --package | --local --divert --rename + --quiet --test --help|--version --admindir + + is the name of a package whose copy of will not be diverted. + is the name used by other packages' versions. +--local specifies that all packages' versions are diverted. +--rename causes dpkg-divert to actually move the file aside (or back). + +When adding, default is --local and --divert .distrib. +When removing, --package or --local and --divert must match if specified. +Package preinst/postrm scripts should always specify --package and --divert. +END + || &quit("failed to write usage: $!"); +} + +$admindir= '/var/lib/dpkg'; +$testmode= 0; +$dorename= 0; +$verbose= 1; +$mode=''; +$|=1; + +sub checkmanymodes { + return unless $mode; + &badusage("two modes specified: $_ and --$mode"); +} + +while (@ARGV) { + $_= shift(@ARGV); + last if m/^--$/; + if (!m/^-/) { + unshift(@ARGV,$_); last; + } elsif (m/^--(help|version)$/) { + &usageversion; exit(0); + } elsif (m/^--test$/) { + $testmode= 1; + } elsif (m/^--rename$/) { + $dorename= 1; + } elsif (m/^--quiet$/) { + $verbose= 0; + } elsif (m/^--local$/) { + $package= ':'; + } elsif (m/^--add$/) { + &checkmanymodes; + $mode= 'add'; + } elsif (m/^--remove$/) { + &checkmanymodes; + $mode= 'remove'; + } elsif (m/^--list$/) { + &checkmanymodes; + $mode= 'list'; + } elsif (m/^--divert$/) { + @ARGV || &badusage("--divert needs a divert-to argument"); + $divertto= shift(@ARGV); + $divertto =~ m/\n/ && &badusage("divert-to may not contain newlines"); + } elsif (m/^--package$/) { + @ARGV || &badusage("--package needs a package argument"); + $package= shift(@ARGV); + $divertto =~ m/\n/ && &badusage("package may not contain newlines"); + } elsif (m/^--admindir$/) { + @ARGV || &badusage("--admindir needs a directory argument"); + $admindir= shift(@ARGV); + } else { + &badusage("unknown option \`$_'"); + } +} + +$mode='add' unless $mode; + +open(O,"$admindir/diversions") || &quit("cannot open diversions: $!"); +while() { + s/\n$//; push(@contest,$_); + $_=; s/\n$// || &badfmt("missing altname"); + push(@altname,$_); + $_=; s/\n$// || &badfmt("missing package"); + push(@package,$_); +} +close(O); + +if ($mode eq 'add') { + @ARGV == 1 || &badusage("--add needs a single argument"); + $file= $ARGV[0]; + $file =~ m/\n/ && &badusage("file may not contain newlines"); + $divertto= "$file.distrib" unless defined($divertto); + $package= ':' unless defined($package); + for ($i=0; $i<=$#contest; $i++) { + if ($contest[$i] eq $file || $altname[$i] eq $file || + $contest[$i] eq $divertto || $altname[$i] eq $divertto) { + if ($contest[$i] eq $file && $altname[$i] eq $divertto && + $package[$i] eq $package) { + print "Leaving \`",&infon($i),"'\n" if $verbose > 0; + exit(0); + } + &quit("\`".&infoa."' clashes with \`".&infon($i)."'"); + } + } + push(@contest,$file); + push(@altname,$divertto); + push(@package,$package); + print "Adding \`",&infon($#contest),"'\n" if $verbose > 0; + &checkrename($file,$divertto); + &save; + &dorename($file,$divertto); + exit(0); +} elsif ($mode eq 'remove') { + @ARGV == 1 || &badusage("--remove needs a single argument"); + $file= $ARGV[0]; + for ($i=0; $i<=$#contest; $i++) { + next unless $file eq $contest[$i]; + &quit("mismatch on divert-to\n when removing \`".&infoa."'\n found \`". + &infon($i)."'") if defined($divertto) && $altname[$i] ne $divertto; + &quit("mismatch on package\n when removing \`".&infoa."'\n found \`". + &infon($i)."'") if defined($package) && $package[$i] ne $package; + print "Removing \`",&infon($i),"'\n" if $verbose > 0; + $orgfile= $contest[$i]; + $orgdivertto= $altname[$i]; + @contest= (($i > 0 ? @contest[0..$i-1] : ()), + ($i < $#contest ? @contest[$i+1,$#contest] : ())); + @altname= (($i > 0 ? @altname[0..$i-1] : ()), + ($i < $#altname ? @altname[$i+1,$#altname] : ())); + @package= (($i > 0 ? @package[0..$i-1] : ()), + ($i < $#package ? @package[$i+1,$#package] : ())); + &checkrename($orgdivertto,$orgfile); + &dorename($orgdivertto,$orgfile); + &save; + exit(0); + } + print "No diversion \`",&infoa,"', none removed\n" if $verbose > 0; + exit(0); +} elsif ($mode eq 'list') { + @ilist= @ARGV ? @ARGV : ('*'); + while (defined($_=shift(@ilist))) { + s/\W/\\$&/g; + s/\\\?/./g; + s/\\\*/.*/g; + push(@list,"^$_\$"); + } + $pat= join('$|^',@list); + for ($i=0; $i<=$#contest; $i++) { + next unless ($contest[$i] =~ m/$pat/o || + $altname[$i] =~ m/$pat/o || + $package[$i] =~ m/$pat/o); + print &infon($i),"\n"; + } + exit(0); +} else { + &quit("internal error - bad mode \`$mode'"); +} + +sub infol { + return (($_[2] eq ':' ? "" : length($_[2]) ? "$_[2]" : ""). + ": $_[0]". + (length($_[1]) ? " -> $_[1]" : "")); +} + +sub checkrename { + return unless $dorename; + ($rsrc,$rdest) = @_; + (@ssrc= lstat($rsrc)) || $! == &ENOENT || + &quit("cannot stat old name \`$rsrc': $!"); + (@sdest= lstat($rdest)) || $! == &ENOENT || + &quit("cannot stat new name \`$rdest': $!"); + if (@ssrc && @sdest && + !($ssrc[0] == $sdest[0] && $ssrc[1] == $sdest[1])) { + &quit("rename involves overwriting \`$rdest' with\n". + " different file \`$rsrc', not allowed"); + } +} + +sub dorename { + return unless $dorename; + if (@ssrc) { + if (@sdest) { + unlink($rsrc) || &quit("rename: remove duplicate old link \`$rsrc': $!"); + } else { + rename($rsrc,$rdest) || &quit("rename: rename \`$rsrc' to \`$rdest': $!"); + } + } +} + +sub save { + return if $testmode; + open(N,"> $admindir/diversions-new") || &quit("create diversions-new: $!"); + for ($i=0; $i<=$#contest; $i++) { + print(N "$contest[$i]\n$altname[$i]\n$package[$i]\n") + || &quit("write diversions-new: $!"); + } + close(N) || &quit("close diversions-new: $!"); + unlink("$admindir/diversions-old") || + $! == &ENOENT || &quit("remove old diversions-old: $!"); + link("$admindir/diversions","$admindir/diversions-old") || + $! == &ENOENT || &quit("create new diversions-old: $!"); + rename("$admindir/diversions-new","$admindir/diversions") + || &quit("install new diversions: $!"); +} + +sub infoa { &infol($file,$divertto,$package); } +sub infon { &infol($contest[$i],$altname[$i],$package[$i]); } + +sub quit { print STDERR "dpkg-divert: @_\n"; exit(2); } +sub badusage { print STDERR "dpkg-divert: @_\n\n"; &usageversion; exit(2); } +sub badfmt { &quit("internal error: $admindir/diversions corrupt: $_[0]"); } diff --git a/scripts/dpkg-name.1 b/scripts/dpkg-name.1 new file mode 100644 index 00000000..b474feb8 --- /dev/null +++ b/scripts/dpkg-name.1 @@ -0,0 +1,60 @@ +.\" This is an -*- nroff -*- source file. +.\" dpkg-name and this manpage are Copyright 1995,1996 by Erick Branderhorst. +.\" +.\" This is free software; see the GNU General Public Licence version 2 +.\" or later for copying conditions. There is NO warranty. +.TH dpkg-name 1 "January 1996" "Debian Project" "Debian GNU/Linux" +.SH NAME +dpkg\-name \- rename Debian packages to full package names +.SH SYNOPSIS +.B dpkg\-name [\-h|\-\-help] [\-V|\-\-version] [\-L|\-\-license] [--] [files] +.SH DESCRIPTION +.PP +This manual page documents the +.B dpkg\-name +sh script which provides an easy way to rename +.B Debian +packages into their full package names. A full package name consists +of -[-].deb as specified in the control +file of the package. +.SH EXAMPLES +.TP +.B dpkg-name toedeledokie +The file `toedeledokie' will be renamed to emacs-19.29-4.deb or +something similar (depending on whatever information is in the control +part of `toedeledokie'). +.TP +.B find /root/debian/ \-name '*.deb' | xargs dpkg\-name +All files with the extension `deb' in the directory /root/debian and +its subdirectory's will be renamed by dpkg\-name if required. +.SS OPTIONS +.TP +.B "\-h, \-\-help" +Print a usage message and exit successfully. +.TP +.B "\-v, \-\-version" +Print version information and exit successfully. +.TP +.B "\-l, \-\-license" +Print copyright information and (a reference to GNU) license +information and exit successfully. +.SH BUGS? +Successfully tested on +.B Debian GNU/Linux +systems only. Some packages don't follow the name structure +-[-].deb. Packages renamed by dpkg-name +will follow this structure. Generally this will have no impact on how +packages are installed by dselect/dpkg. +.SH SEE ALSO +.BR deb (5), +.BR deb-control (5), +.BR dpkg (5), +.BR dpkg (8), +.BR dpkg-deb (8). +.SH COPYRIGHT +Copyright 1995,1996 Erick Branderhorst. +.B dpkg-name +is free software; see the GNU General Public Licence version 2 or +later for copying conditions. There is +.B no +warranty. diff --git a/scripts/dpkg-name.sh b/scripts/dpkg-name.sh new file mode 100755 index 00000000..294010aa --- /dev/null +++ b/scripts/dpkg-name.sh @@ -0,0 +1,94 @@ +#!/bin/sh + +set -e + +prog="`basename \"${0}\"`" +version="0.11"; # This line modified by Makefile +purpose="rename Debian packages to full package names" + +license () { +echo "# ${prog} ${version} -- ${purpose} +# Copyright (C) 1995,1996 Erick Branderhorst . + +# This 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, or (at your option) any +# later version. + +# This 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 file +# /usr/doc/copyright/GPL for more details." +} + +stderr () { + echo "${prog}: $@" >/dev/stderr; +} + +show_version () { + echo "${prog} version ${version} -- ${purpose}"; +} + +usage () { + echo "Usage: ${prog} file[s] + ${purpose} + file.deb changes to -[-].deb + -h|--help|-v|--version|-l|--license Show help/version/license" +} + +rename () { + if [ -f "$1" ]; + then + if p=`dpkg-deb -f -- "$1" package`; + then + p="$p-"`dpkg-deb -f -- "$1" version`; + r=`dpkg-deb -f -- "$1" revision`; + if [ -z "$r" ]; + then + r=`dpkg-deb -f -- "$1" package_revision`; + fi + if [ -n "$r" ]; + then + p=$p-$r; + fi + p=`echo $p|sed 's/ //g'` + p=`dirname "$1"`"/"$p.deb + if [ $p -ef "$1" ]; # same device and inode numbers + then + stderr "skipping \`"$1"'"; + elif [ -f $p ]; + then + stderr "can't move \`"$1"' to existing file"; + elif `mv -- "$1" $p`; + then + echo "moved \``basename "$1"`' to \`${p}'"; + else + stderr "hmm how did this happen?"; + fi + fi + else + stderr "can't deal with \`"$1"'"; + fi +} + +if [ $# = 0 ]; then usage; exit 0; fi +for arg +do + case "$arg" in + --version|-v) show_version; exit 0;; + --help|-[h?]) usage; exit 0;; + --licen[cs]e|-l) license; exit 0;; + --) shift; + for arg + do + rename "$arg"; + done; exit 0;; + *) rename "$arg";; + esac +done +exit 0; + +# Local variables: +# tab-width: 2 +# End: + diff --git a/scripts/dpkg-scanpackages.pl b/scripts/dpkg-scanpackages.pl new file mode 100755 index 00000000..efb6a6e2 --- /dev/null +++ b/scripts/dpkg-scanpackages.pl @@ -0,0 +1,178 @@ +#!/usr/bin/perl -- +# usage: +# dpkg-scanpackages .../binary .../noverride pathprefix >.../Packages.new +# mv .../Packages.new .../Packages +# +# This is the core script that generates Packages files (as found +# on the Debian FTP site and CD-ROMs). +# +# The first argument should preferably be a relative filename, so that +# the Filename field has good information. +# +# Any desired string can be prepended to each Filename value by +# passing it as the third argument. +# +# The noverride file is a series of lines of the form +#

+# where the field is optional. Fields are separated by +# whitespace. + +$version= '1.0.10'; # This line modified by Makefile + +%kmap= ('optional','suggests', + 'recommended','recommends', + 'class','priority', + 'package_revision','revision'); + +%pri= ('priority',300, + 'section',290, + 'maintainer',280, + 'version',270, + 'depends',250, + 'recommends',240, + 'suggests',230, + 'conflicts',220, + 'provides',210, + 'filename',200, + 'size',180, + 'md5sum',170, + 'description',160); + +@ARGV==3 || die; + +$binarydir= shift(@ARGV); +-d $binarydir || die $!; + +$override= shift(@ARGV); +-e $override || die $!; + +$pathprefix= shift(@ARGV); + +open(F,"find $binarydir -name '*.deb' -print |") || die $!; +while () { + chop($fn=$_); + substr($fn,0,length($binarydir)) eq $binarydir || die $fn; + open(C,"dpkg-deb -I $fn control |") || die "$fn $!"; + $t=''; while () { $t.=$_; } + $!=0; close(C); $? && die "$fn $? $!"; + undef %tv; + $o= $t; + while ($t =~ s/^\n*(\S+):[ \t]*(.*(\n[ \t].*)*)\n//) { + $k= $1; $v= $2; + $k =~ y/A-Z/a-z/; + if (defined($kmap{$k})) { $k= $kmap{$k}; } + $v =~ s/\s+$//; + $tv{$k}= $v; +#print STDERR "K>$k V>$v<\n"; + } + $t =~ m/^\n*$/ || die "$fn $o / $t ?"; + defined($tv{'package'}) || die "$fn $o ?"; + $p= $tv{'package'}; delete $tv{'package'}; + defined($p1{$p}) && die "$fn $p repeat"; + if (defined($tv{'filename'})) { + print(STDERR " ! Package $p (filename $fn) has Filename field !\n") || die $!; + } + $tv{'filename'}= "$pathprefix$fn"; + open(C,"md5sum <$fn |") || die "$fn $!"; + chop($_=); m/^[0-9a-f]{32}$/ || die "$fn \`$_' $!"; + $!=0; close(C); $? && die "$fn $? $!"; + $tv{'md5sum'}= $_; + defined(@stat= stat($fn)) || die "$fn $!"; + $stat[7] || die "$fn $stat[7]"; + $tv{'size'}= $stat[7]; + if (length($tv{'revision'})) { + $tv{'version'}.= '-'.$tv{'revision'}; + delete $tv{'revision'}; + } + for $k (keys %tv) { + $pv{$p,$k}= $tv{$k}; + $k1{$k}= 1; + $p1{$p}= 1; + } + $_= substr($fn,length($binarydir)); + s#/[^/]+$##; s#^/*##; + $psubdir{$p}= $_; + $pfilename{$p}= $fn; +} +$!=0; close(F); $? && die "$? $!"; + +select(STDERR); $= = 1000; select(STDOUT); + +format STDERR = + ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< +$packages +. + +sub writelist { + $title= shift(@_); + return unless @_; + print(STDERR " $title\n") || die $!; + $packages= join(' ',sort @_); + while (length($packages)) { write(STDERR) || die $!; } + print(STDERR "\n") || die $!; +} + +@inover=(); +@samemaint=(); + +open(O,"<$override") || die $!; +while() { + s/\s+$//; + ($p,$priority,$section,$maintainer)= split(/\s+/,$_,4); + if (!defined($p1{$p})) { + push(@inover,$p); + next; + } + if (length($maintainer)) { + if ($pv{$p,'maintainer'} eq $maintainer) { + push(@samemaint," $p ($maintainer)\n"); + } else { + $pv{$p,'maintainer'}= $maintainer; + } + } + $pv{$p,'priority'}= $priority; + $pv{$p,'section'}= $section; + if (length($psubdir{$p}) && $section ne $psubdir{$p}) { + print(STDERR " !! Package $p has \`Section: $section',". + " but file is in \`$psubdir{$p}' !!\n") || die $!; + $ouches++; + } + $o1{$p}= 1; +} +close(O); + +if ($ouches) { print(STDERR "\n") || die $!; } + +$k1{'maintainer'}= 1; +$k1{'priority'}= 1; +$k1{'section'}= 1; + +@missingover=(); + +for $p (sort keys %p1) { + if (!defined($o1{$p})) { + push(@missingover,$p); + } + $r= "Package: $p\n"; + for $k (sort { $pri{$b} <=> $pri{$a} } keys %k1) { + next unless length($pv{$p,$k}); + $r.= "$k: $pv{$p,$k}\n"; + } + $r.= "\n"; + $written++; + print(STDOUT $r) || die $!; +} +close(STDOUT) || die $!; + +&writelist("** Packages in archive but missing from override file: **", + @missingover); +&writelist("++ Packages appearing in override file but not in archive: ++", + @inover); +if (@samemaint) { + print(STDERR + " -- Packages specifying same maintainer as override file: --\n", + @samemaint, + "\n") || die $!; +} + +print(STDERR " Wrote $written entries to output Packages file.\n") || die $!; diff --git a/scripts/install-info.8 b/scripts/install-info.8 new file mode 100644 index 00000000..2d881798 --- /dev/null +++ b/scripts/install-info.8 @@ -0,0 +1,245 @@ +.\" Hey, Emacs! This is an -*- nroff -*- source file. +.\" Install-info and this manpage are Copyright 1994 by Ian Jackson. +.\" +.\" This is free software; see the GNU General Public Licence version 2 +.\" or later for copying conditions. There is NO warranty. +.TH INSTALL-INFO 8 "29th November 1995" "Debian Project" "Debian GNU/Linux" +.SH NAME +install\-info \- create or update entry in Info directory +.SH SYNOPSIS +.B install\-info +[\-\-version] [\-\-help] [\-\-debug] [\-\-maxwidth=nnn] +[\-\-section regexp title] [\-\-infodir=xxx] [\-\-align=nnn] +[\-\-quiet] [\-\-menuentry=xxx] [\-\-description=xxx] [\-\-remove] +[\-\-] filename +.SH DESCRIPTION +.PP +.B install-info +makes, updates or removes entries in the Info directory, the +.B dir +file. When updating or creating entries, if no description is +specified on the command line or in the Info file it attempts to guess +a description from the contents of the file. + +See the description of the +.B \-\-section +option for details of where the entry will be placed and a description +of the expected format of the +.B dir +file. +.SS OPTIONS +.TP +.BI "[\-\-] " filename +Gives the filename of the Info file whose menu entry is to be created, +updated or removed. The basename of this filename is used as the +referent of the menu entry which is created. This file must therefore +exist (or be about to be installed, or have previously existed when +removing an entry) in the same directory as the +.B dir +file (see the +.B \-\-infodir +option). + +If +.I filename +ends in +.B .gz +it is taken to refer to a file compressed with GNU gzip; if it doesn't +exist, but a corresponding +.IB filename .gz +does, the latter is used instead. + +When adding or updating entries the file must exist at the path +specified (possibly with an additional +.B .gz +extension). +.TP +.B \-\-remove +Specifies that the entry for the file +.I filename +is to be removed; by default entries are created or updated. + +If the removal results in a section becoming empty the section heading +(and the spare blank line) will be removed as well, unless this is the +last section in the file or +.B \-\-keep\-old +is specified. See the +.B \-\-section +option for details about the expected format of the +.B dir +file. + +If there are several suitable entries in the +.B dir +file only those in the first matching contiguous group will be removed +and the others silently ignored. + +It is not an error for no suitable entry to be found, though +.B install\-info +will issue a warning unless the +.B \-\-quiet +option was specified. + +When +.B \-\-remove +is specified the +.BR \-\-maxwidth ", " \-\-align " and " \-\-calign +formatting options are silently ignored. +.TP +.BI "\-\-section " "regexp title" +Specifies that, if a new entry is to be made, it should be placed in a +section of the +.B dir +file whose title matches +.IR regexp . +If no such section exists one will be created as the second last +section in the file (see below), with title +.IR title . +A section is a part of the +.B dir +menu delimited by blank lines; the first line is assumed to be the +title. + +If a new entry is to be created +.B install-info +will attempt to insert it within the section according in alphabetic +order; if the entries in the section aren't already sorted the new +location within the section will be unpredictable. The order of +existing entries will not be changed. + +The default is to append new entries to the end of the file. The last +section (even if it only consists of the title line) should always +exist, to ensure that new sections are created in the right place. +The final section should be titled to reflect the fact that Info files +with no more well specified location are appended to it. + +If there is already an entry for the Info file being installed it is +replaced in situ with the new entry. + +If a section is specified when removing an entry the section is +ignored and a warning is issued. +.TP +.BI \-\-infodir= infodir +Specifies that the +.B dir +file is, and the installed copy of the new Info file was, is or will +be located in +.IR infodir . +The default is +.BR /usr/info . +.TP +.BI \-\-align= nnn +Specifies that the first line of the description should be indented at +least +.I nnn +characters; extra spaces will be added as required. If necessary +because of the length of the +.B dir +menu entry details it may be offset more. The default is 27. +.TP +.BI \-\-calign= nnn +Specifies that the second and subsequent lines of the description +should be indented at least +.I nnn +characters. The default is 29. +.TP +.BI \-\-maxwidth= nnn +Specifies that the maximum width for the Info file is +.IR nnn . +This is used when wordwrapping the descriptive text. +The default is 79. +.TP +.B \-\-quiet +Prevents the usual display of the new menu entry just before it is +inserted, and of the messages announcing the replacement and removal +of existing entries and the creation and deletion of sections. +.TP +.B \-\-help +Causes +.B install-info +to display its usage information and exit. +.TP +.B \-\-version +Causes +.B install-info +to display its version and copyright information and exit. +.TP +.BI \-\-description= xxx +Specifies that the description to use after the menu entry in new or +updated entries be +.IR xxx . +The default is to use the the value specified in the Info file itself; +this is found by searching for a section of the form +.br +.B START\-INFO\-DIR\-ENTRY +.br +.B * Auto-PGP: (auto-pgp). PGP under GNU Emacs. +.br +.B END\-INFO\-DIR\-ENTRY + +If the entry found in the Info file itself extends across several +lines, each giving a menu entry, the text found in the file is used +verbatim. In this case the alphabetic ordering scheme is turned off, +and the entries are inserted at the top of section in question. In +this case the +.BR \-\-menuentry ", " \-\-maxwidth ", " \-\-align ", " \-\-calign +.RB " and " \-\-menuentry +options are ignored. + +If there is no +.B dir +entry in the file the program will try to find a paragraph early in +the file starting +.BR "this file documents" . +It will capitalise the first character of the remainder, and use that. + +It is an error for none of these methods to yield a description. + +If a description argument is given when +.B \-\-remove +is specified it is ignored and a warning is issued. +.TP +.BI \-\-menuentry= xxx +Specifies that the entry in the menu should be +.IR xxx . +The default is to use the the value specified in the Info file itself. +If this is not present the basename of the Info file is used +.RB "(any " ".info " "is deleted, and the entry is made mixed case)." +See above for details of the format expected for the menu entry in the +Info file. + +When removing entries the value of the +.B \-\-menuentry +option must match the actual menu entry field in the menu item to be +removed (case not significant). If +.B \-\-menuentry +is omitted no check on the menu entry is done. +.TP +.B \-\-keep\-old +Inhibits the replacement of existing entries and the removal of empty +sections. + +If the file being installed alreay has an entry in the directory the +old entry will be left alone instead of being replaced; the default is +to overwrite any old entry found with the newly generated one. + +If +.BR \-\-remove " is specified " \-\-keep\-old +will prevent the removal of the section heading which would otherwise +happen if the section is made empty by the removal. +.TP +.B \-\-test +Enables test mode, which inhibits the update of the directory file. +.TP +.B \-\-debug +Enables debugging mode, in which the results of some internal +processing steps are shown. +.SH "SEE ALSO" +emacs(1), info(1), gzip(1) +.SH COPYRIGHT +Copyright 1994, Ian Jackson. +.B install\-info +is free software; see the GNU General Public Licence version 2 or +later for copying conditions. There is +.I no +warranty. diff --git a/scripts/install-info.pl b/scripts/install-info.pl new file mode 100755 index 00000000..0455ab1a --- /dev/null +++ b/scripts/install-info.pl @@ -0,0 +1,342 @@ +#!/usr/bin/perl -- + +# fixme: --dirfile option +# fixme: sort entries +# fixme: send to FSF ? + +$version= '0.93.42.2'; # This line modified by Makefile +sub version { + print STDERR <&STDERR") || exit 1; + } elsif ($_ eq '--section') { + if (@ARGV < 2) { + print STDERR "$name: --section needs two more args\n"; + &usage; exit 1; + } + $sectionre= shift(@ARGV); + $sectiontitle= shift(@ARGV); + } elsif (m/^--maxwidth=([0-9]+)$/) { + $maxwidth= $1; + } elsif (m/^--align=([0-9]+)$/) { + $align= $1; + } elsif (m/^--calign=([0-9]+)$/) { + $calign= $1; + } elsif (m/^--infodir=/) { + $infodir=$'; + } elsif (m/^--menuentry=/) { + $menuentry=$'; + } elsif (m/^--description=/) { + $description=$'; + } else { + print STDERR "$name: unknown option \`$_'\n"; &usage; exit 1; + } +} + +if (!@ARGV) { &version; print STDERR "\n"; &usage; exit 1; } + +$filename= shift(@ARGV); +if (@ARGV) { print STDERR "$name: too many arguments\n"; &usage; exit 1; } + +if ($remove) { + print STDERR "$name: --section ignored with --remove\n" if length($sectiontitle); + print STDERR "$name: --description ignored with --remove\n" if length($description); +} + +print STDERR "$name: test mode - dir file will not be updated\n" + if $nowrite && !$quiet; + +umask(umask(0777) & ~0444); + +$filename =~ m|[^/]+$|; $basename= $&; $basename =~ s/(\.info)?(\.gz)?$//; +print DEBUG <) { last if m/^START-INFO-DIR-ENTRY$/; } + while() { last if m/^END-INFO-DIR-ENTRY$/; $asread.= $_; } + close(IF); &checkpipe; + if ($asread =~ m/(\* *[^:]+: *\([^\)]+\).*\. *.*\n){2,}/) { + $infoentry= $asread; $multiline= 1; + print DEBUG <) { + if (m/^\s*[Tt]his file documents/) { + $asread=$'; + last; + } + } + if (length($asread)) { + while() { last if m/^\s*$/; $asread.= $_; } + $description= $asread; + } + close(IF); &checkpipe; + } + + if (!length($description)) { + print STDERR < $maxwidth) { + $infoentry .= $cprefix; + $cwidth= $lprefix+1+$l; + $cprefix= $prefix; $lprefix= $calign; + } + $infoentry.= ' '; $infoentry .= $_; + } + + $infoentry.= "\n"; + print $infoentry unless $quiet; + $sortby= $menuentry; $sortby =~ y/A-Z/a-z/; + + } +} + +if (!link("$infodir/dir","$infodir/dir.lock")) { + die "$name: failed to lock dir for editing! $!\n". + ($! =~ m/exists/i ? "try deleting $infodir/dir.lock ?\n" : ''); +} + +open(OLD,"$infodir/dir") || &ulquit("$name: open $infodir/dir: $!\n"); +@work= ; +eof(OLD) || &ulquit("$name: read $infodir/dir: $!\n"); +close(OLD) || &ulquit("$name: close $infodir/dir after read: $!\n"); +while ($work[$#work] !~ m/\S/) { $#work--; } + +if (!$remove) { + + for ($i=0; $i<=$#work; $i++) { + next unless $work[$i] =~ m/^\* *[^:]+: *\(([^\)]+)\).*\.\s/; + last if $1 eq $basename || $1 eq "$basename.info"; + } + for ($j=$i; $j<=$#work+1; $j++) { + next if $work[$j] =~ m/^\s+\S/; + last unless $work[$j] =~ m/^\* *[^:]+: *\(([^\)]+)\).*\.\s/; + last unless $1 eq $basename || $1 eq "$basename.info"; + } + + if ($i < $j) { + if ($keepold) { + print "$name: existing entry for \`$basename' not replaced\n" unless $quiet; + $nowrite=1; + } else { + print "$name: replacing existing dir entry for \`$basename'\n" unless $quiet; + } + $mss= $i; + @work= (@work[0..$i-1], @work[$j..$#work]); + } elsif (length($sectionre)) { + for ($i=0; $i<=$#work && $work[$i] !~ m/^\* *menu/i; $i++) { } + $mss= -1; + for (; $i<=$#work; $i++) { + $_= $work[$i]; + next if m/^\*/; + next unless m/$sectionre/io; + $mss= $i+1; last; + } + if ($mss < 0) { + print "$name: creating new section \`$sectiontitle'\n" unless $quiet; + for ($i= $#work; $i>=0 && $work[$i] =~ m/\S/; $i--) { } + $i >= 0 || &ulquit("$name: nowhere to create new section - giving up\n"); + @work= (@work[0..$i], "$sectiontitle\n", "\n", @work[$i+1..$#work]); + $mss= $i+1; + } + while ($mss <= $#work) { + $work[$mss] =~ m/\S/ || last; + $work[$mss] =~ m/^\* *([^:]+):/ || ($mss++, next); + last if $multiline; + $_=$1; y/A-Z/a-z/; + last if $_ gt $sortby; + $mss++; + } + } else { + print "$name: no section specified for new entry, placing at end\n" + unless $quiet; + $mss= $#work+1; + } + + @work= (@work[0..$mss-1], $infoentry, @work[$mss..$#work]); + +} else { + + for ($i=0; $i<=$#work; $i++) { + next unless $work[$i] =~ m/^\* *([^:]+): *\((\w[^\)]*)\)/; + $tme= $1; $tfile= $2; $match= $&; + next unless $tfile eq $basename; + last if !length($menuentry); + $tme =~ y/A-Z/a-z/; + last if $tme eq $menuentry; + } + for ($j=$i; $j<=$#work+1; $j++) { + next if $work[$j] =~ m/^\s+\S/; + last unless $work[$j] =~ m/^\* *([^:]+): *\((\w[^\)]*)\)/; + $tme= $1; $tfile= $2; + last unless $tfile eq $basename; + next if !length($menuentry); + $tme =~ y/A-Z/a-z/; + last unless $tme eq $menuentry; + } + print DEBUG < $#work || $work[$j] !~ m/^\s*$/) { + s/:?\s+$//; + if ($keepold) { + print "$name: empty section \`$_' not removed\n" unless $quiet; + } else { + $i--; $j++; + print "$name: deleting empty section \`$_'\n" unless $quiet; + } + } + @work= (@work[0..$i-1], @work[$j..$#work]); + } else { + print "$name: no entry for file \`$basename'". + (length($menuentry) ? " and menu entry \`$menuentry'": ''). + ".\n" + unless $quiet; + } +} + +if (!$nowrite) { + open(NEW,"> $infodir/dir.new") || &ulquit("$name: create $infodir/dir.new: $!\n"); + print(NEW @work) || &ulquit("$name: write $infodir/dir.new: $!\n"); + close(NEW) || &ulquit("$name: close $infodir/dir.new: $!\n"); + + unlink("$infodir/dir.old"); + link("$infodir/dir","$infodir/dir.old") || + &ulquit("$name: cannot backup old $infodir/dir, giving up: $!\n"); + rename("$infodir/dir.new","$infodir/dir") || + &ulquit("$name: install new $infodir/dir: $!\n"); +} + +unlink("$infodir/dir.lock") || die "$name: unlock $infodir/dir: $!\n"; + +sub ulquit { + unlink("$infodir/dir.lock") || + warn "$name: warning - unable to unlock $infodir/dir: $!\n"; + die $_[0]; +} + +sub checkpipe { + return if !$pipeit || !$? || $?==0x8D00; + die "$name: read $filename: $?\n"; +} + +exit 0; diff --git a/scripts/lib.pl b/scripts/lib.pl new file mode 100644 index 00000000..ba9c1276 --- /dev/null +++ b/scripts/lib.pl @@ -0,0 +1,565 @@ +# -*- perl -*- +# +# dpkg library: Debian GNU/Linux package maintenance utility, +# useful library functions. +# +# Copyright (C) 1994 Matt Welsh +# Copyright (C) 1994 Carl Streeter +# Copyright (C) 1994 Ian Murdock +# Copyright (C) 1994 Ian Jackson +# +# dpkg 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, +# or (at your option) any later version. +# +# dpkg 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 dpkg; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +# /var/lib/dpkg/ +---- status +# |---- updates/ +---- +# | |---- tmp.i +# | \---- .new +# |---- available +# |---- lock +# |---- info/ |---- .{post,pre}{inst,rm} +# |---- tmp.$$ +# \---- tmp.ci/ +---- control +# |---- conffiles +# |---- {post,pre}{inst,rm} +# |---- list +# \---- conffiles + +$backend = "dpkg-deb"; +$fpextract = "dpkg-deb"; +$md5sum = "md5sum"; +$dselect = "dselect"; +$dpkg = "dpkg"; + +$status_mergeevery = 20; +$tmp = "/tmp"; +$visiblecontroldir = "DEBIAN"; + +sub setadmindir { + $dd = $_[0]; + $statusdb = "$dd/status"; + $updatesdir = "$dd/updates"; + $availabledb = "$dd/available"; + $scriptsdir = "$dd/info"; + $listsdir = "$dd/info"; + $lockfile = "$dd/lock"; + $lockmine = "$dd/tmp.$$"; + $controli = "$dd/tmp.ci"; + $importantspace = "$updatesdir/tmp.i"; +} +$orgadmindir= "/var/lib/dpkg"; +&setadmindir($orgadmindir); + +@nokeepfields= ('package','version','package_revision', + 'depends','recommended','optional','conflicts','part'); +# Don't keep these fields in the Available database if a new record is +# merged which is missing values for any of them. + +$packagere = '\w[-_a-zA-Z0-9+.@:=%]+'; +$packageversionre= $packagere.'(\s*\([^()]+\))?'; +$singledependencyre= "$packageversionre(\\s*\\|\\s*$packageversionre)*"; + +# Abbreviations for dpkg-deb options common to dpkg & dpkg-deb. +%debabbrevact= ('b','build', 'c','contents', 'e','control', 'i','info', + 'f','field', 'x','extract', 'X','vextract'); + +@keysortorder= ('package', 'status', 'version', 'package_revision', + 'maintainer', 'description', + 'depends', 'recommended', 'optional', 'conflicts', + 'list', 'conffiles'); + +#*** replacements for things in headers ***# + +#require 'sys/errno.ph'; +sub ENOENT { 2; } # No such file or directory +sub EEXIST { 17; } # File exists +sub EISDIR { 21; } # Is a directory +sub ENOTEMPTY { 39; } # Directory not empty + +#require 'sys/stat.ph'; +sub S_IFMT { 00170000; } +sub S_IFREG { 0100000; } +sub S_IFLNK { 0120000; } +sub S_ISREG { ($_[0] & &S_IFMT) == &S_IFREG; } +sub S_ISLNK { ($_[0] & &S_IFMT) == &S_IFLNK; } + +#require 'sys/wait.ph'; +sub WIFEXITED { ($_[0] & 0x0ff) == 0; } +sub WIFSTOPPED { ($_[0] & 0x0ff) == 0x07f; } +sub WIFSIGNALED { !&WIFEXITED && !&WIFSTOPPED; } +sub WCOREDUMP { ($_[0] & 0x080) != 0; } +sub WEXITSTATUS { ($_[0] & 0x0ff00) >> 8; } +sub WSTOPSIG { ($_[0] & 0x0ff00) >> 8; } +sub WTERMSIG { $_[0] & 0x07f; } + +#require 'sys/signal.ph'; +sub SIGPIPE { 13; } + +#require 'sys/syscall.ph'; +sub SYS_lseek { 19; } + + +#*** /var/lib/dpkg database management - `exported' routines ***# + +sub database_start { + # Lock the package management databases, amalgamate any + # changes files, and leave the results in: + # From /var/lib/dpkg/status: + # %st_pk2v{ package_name, field_name } = field_value + # %st_p21{ package_name } = 1 + # From /var/lib/dpkg/available: + # %av_pk2v{ package_name, field_name } = field_value + # %av_p21{ package_name } = 1 + # From both: + # %all_k21{ field_name } = 1 + &lock_database; + &read_status_mainfile; + &read_status_extrafiles; + &write_status_mainfile; + &delete_status_extrafiles; + &read_available_file; + &prepare_important_database; + &invent_status_availableonly_packages; +} + +sub database_finish { + # Tidy up and unlock the package management databases. + &release_important_database; + &write_available_file; + &write_status_mainfile; + &delete_status_extrafiles; + &unlock_database; +} + +sub amended_status { + # Record amended status of package (in an `extra' file). + local (@packages) = @_; + local ($p); + &debug("amended @packages"); + for $p (@packages) { + $st_pk2v{$p,'status'}= "$st_p2w{$p} $st_p2h{$p} $st_p2s{$p}"; + $st_p21{$p}= 1; + } + $all_k21{'status'}= 1; + local ($ef) = sprintf("%03d",$next_extrafile++); + &write_database_file("$updatesdir/$ef",*st_pk2v,*st_p21,1,@packages); + push(@status_extrafiles_done,$ef); &sync; + if ($next_extrafile >= $status_mergeevery) { + &write_status_mainfile; + &delete_status_extrafiles; + } + $status_modified= 1; + for $p (@packages) { delete $st_pk2v{$p,'status'}; } + &prepare_important_database; +} + +sub note_amended_status { + # Note the fact that the status has been modified, but don't + # commit yet. + $status_modified= 1; +} + +sub amended_available { + # Record amended available information (in core for the moment - + # noncritical, so we defer writing it out). + $available_modified++; + &invent_status_availableonly_packages(@_); +} + +#*** internal routines ***# + +sub invent_status_availableonly_packages { + local ($p); + for $p (@_ ? @_ : keys %av_p21) { + next if defined($st_p2w{$p}); + $st_p2w{$p}= 'unknown'; + $st_p2h{$p}= 'ok'; + $st_p2s{$p}= 'not-installed'; + } +} + +sub read_status_mainfile { + local ($p, @p); + &read_status_database_file($statusdb); +} + +sub read_status_extrafiles { + local ($fn); + opendir(UPD,$updatesdir) || &bombout("cannot opendir updates $updatesdir: $!"); + for $_ (sort readdir(UPD)) { + next if $_ eq '.' || $_ eq '..'; + if (m/\.new$/ || m/\.old$/ || $_ eq 'tmp.i') { + unlink("$updatesdir/$_") || + &bombout("cannot unlink old update temp file $updatesdir/$_: $!"); + } elsif (m/^\d+$/) { + $fn= $_; + &read_status_database_file("$updatesdir/$fn"); + $status_modified= 1; push(@status_extrafiles_done, $fn); + } else { + warn("$name: ignoring unexpected file in $updatesdir named \`$_'\n"); + } + } + closedir(UPD); +} + +sub read_status_database_file { + local ($filename) = @_; + @p= &read_database_file($filename,*st_pk2v,*st_p21); + for $p (@p) { + if (defined($st_pk2v{$p,'status'})) { + $v= $st_pk2v{$p,'status'}; + $v =~ y/A-Z/a-z/; + $v =~ + m/^(unknown|install|deinstall|purge)\s+(ok|hold)\s+(not-installed|unpacked|postinst-failed|installed|removal-failed|config-files)$/ + || &bombout("package \`$p' has bad status in $statusdb (\`$v')"); + $st_p2w{$p}= $1; + $st_p2h{$p}= $2; + $st_p2s{$p}= $3; + } + delete($st_pk2v{$p,'status'}); + } + $status_modified= 0; @status_extrafiles_done= (); +} + +sub write_status_mainfile { + return unless $status_modified; + local ($p); + for $p (keys %st_p21) { + $st_pk2v{$p,'status'}= "$st_p2w{$p} $st_p2h{$p} $st_p2s{$p}"; + } + $all_k21{'status'}= 1; + unlink("$statusdb.old") || $!==&ENOENT || + &bombout("unable to remove $statusdb.old: $!"); + link("$statusdb","$statusdb.old") || + &bombout("unable to back up $statusdb: $!"); + &write_database_file($statusdb,*st_pk2v,*st_p21,0); + $status_modified= 0; + &sync; + for $p (keys %st_p21) { delete $st_pk2v{$p,'status'}; } +} + +sub delete_status_extrafiles { +#print STDERR "delete @status_extrafiles_done> "; ; + for $_ (@status_extrafiles_done) { + unlink("$updatesdir/$_") || + &bombout("cannot remove already-done update file $updatesdir/$_: $!"); + } + $next_extrafile= 0; + @status_extrafiles_done= (); +} + +sub read_available_file { + &read_database_file($availabledb,*av_pk2v,*av_p21); + $available_modified= 0; +} + +sub write_available_file { + return unless $available_modified; + &write_database_file($availabledb,*av_pk2v,*av_p21,0); + $available_modified= 0; +} + +#*** bottom level of read routines ***# + +sub read_database_file { + local ($filename, *xx_pk2v, *xx_p21) = @_; + local ($quick,$cf,@cf,%cf_k2v,@cwarnings,@cerrors,$p,@p)= 1; + &debug("reading database file $filename"); + open(DB,"<$filename") || &bombout("unable to open $filename for reading: $!"); + $/=""; + @p=(); + while (defined($cf=)) { + chop($cf); +# $cf =~ s/\n+$/\n/; + $p= &parse_control_entry; +# if (@cwarnings) { +# warn("$name: warning, packaging database file $filename\n". +# " contains oddities in entry for package \`$p':\n ". +# join(";\n ",@cwarnings). +# ".\n This is probably a symptom of a bug.\n"); +# } + if (@cerrors) { + &bombout("packaging database corruption - please report:\n". + " file $filename has error(s) in entry for \`$p':\n ". + join(";\n ",@cerrors). "."); + } + $xx_p21{$p}= 1; + for $k (keys %all_k21) { $xx_pk2v{$p,$k}= $cf_k2v{$k}; } + push(@p,$p); + } + &debug("database file $filename read"); + $/="\n"; close(DB); + return @p; +} + +sub parse_control_entry { + # Expects $cf to be a sequence of lines, + # representing exactly one package's information. + # Results are put in cf_k2v. + # @warnings and @errors are made to contain warning and error + # messages, respectively. + local ($ln,$k,$v,$p,$l); + @cwarnings= @cerrors= (); + + undef %cf_k2v; +# &debug(">>>$cf<<<#\n"); + if (!$quick) { + if ($cf =~ s/\n\n+/\n/g) { push(@cwarnings, "blank line(s) found and ignored"); } + if ($cf =~ s/^\n+//) { push(@cwarnings, "blank line(s) at start ignored"); } + if ($cf !~ m/\n$/) { + $cf.= "\n"; push(@cwarnings, "missing newline after last line assumed"); + } + if ($cf =~ s/\0//g) { + push(@cwarnings, "nul characters discarded"); + } + } + $cf =~ s/\n([ \t])/\0$1/g; # join lines +# &debug(">>>$cf<<<*\n"); + $ln = 0; + for $_ (split(/\n/,$cf)) { + $ln++; s/\s+$//; + next if m/^#/; + m/^(\S+):[ \t]*/ || (push(@cerrors, "garbage at line $ln, \`$_'"), next); + $k= $1; $v= $'; $k =~ y/A-Z/a-z/; $k='package_revision' if $k eq 'revision'; +# &debug("key=\`$k' value=\`$v' line=\`$_'\n"); + $ln += ($v =~ s/\0/\n/g); + $cf_k2v{$k}= $v; + $all_k21{$k}= 1; +# while ($cf =~ s/^(\S+):[ \t]*(.*)\n//) { + } + return unless keys %cf_k2v; + $p= $cf_k2v{'package'}; delete $cf_k2v{'package'}; delete $all_k21{'package'}; + $cf_k2v{'class'} =~ y/A-Z/a-z/ if defined($cf_k2v{'class'}); + $cf_k2v{'section'} =~ y/A-Z/a-z/ if defined($cf_k2v{'section'}); +# length($cf) && +# push(@cerrors, "garbage at line $ln, \`".($cf =~ m/\n/ ? $` : $cf)."'"); + if (!$quick) { + defined($p) || push(@cerrors, "no \`package' line"); + $p =~ m/^$packagere$/o || &bad_control_field('package'); + defined($cf_k2v{'version'}) || push(@cerrors, "no Version field"); + for $f ('depends','recommended','optional','conflicts') { + next unless defined($cf_k2v{$f}) && length($cf_k2v{$f}); + $cf_k2v{$f} =~ m/^$singledependencyre(\s*,\s*$singledependencyre)*$/o + || &bad_control_field("$f"); + } + } + return $p; +} + +sub bad_control_field { + push(@cerrors, "bad \`$_[0]' line, contains \`$cf_k2v{$_[0]}'"); +} + +#*** bottom level of database writing code ***# + +sub write_database_file { + local ($filename, *xx_pk2v, *xx_p21, $important, @packages) = @_; + local ($p,$tl,$k,$v); + if (!@packages) { @packages= keys(%xx_p21); } + + &debug("called write_database_file $filename, important=$important, for @packages"); + if (!$important) { + open(DB,">$filename.new") || &bombout("unable to create $filename.new: $!"); + } + $tl= 0; + for $p (@packages) { + &write_database_string("\n") if $tl; + &write_database_string("Package: $p\n"); + for $k (keys %all_k21) { + next unless defined($xx_pk2v{$p,$k}); + $v= $xx_pk2v{$p,$k}; + $v =~ s/\n(\S)/\n $1/g; + &write_database_string("$k: $v\n"); + } + } + if ($important) { + if (!truncate(IMP,$tl)) { + if (print(IMP "#")) { + warn("$name: warning - unable to truncate $importantspace: $!;". + "\n commenting the rest out instead seems to have worked.\n"); + } else { + &database_corrupted("unable to truncate $importantspace: $!"); + } + } + close(IMP) || &database_corrupted("unable to close $importantspace: $!"); + rename($importantspace,$filename) || + &database_corrupted("unable to install $importantspace as $filename: $!"); + } else { + close(DB) || &bombout("unable to close $filename.new: $!"); + rename("$filename.new",$filename) || + &bombout("unable to install $filename.new as $filename: $!"); + } +} + +sub write_database_string { + $tl += length($_[0]); + if ($important) { + print(IMP $_[0]) || + &database_corrupted("failed write to update file $importantspace: $!"); + } else { + print(DB $_[0]) || + &bombout("failed to write to $filename.new: $!"); + } +} + +sub database_corrupted { + &debug("corruptingstatus @_"); + print STDERR "$name - really horrible error:\n @_\n". + "Package manager status data is now out of step with installed system.\n". + "(Last action has not been recorded. Please try re-installing \`@packages'\n". + "to ensure system consistency, or seek assistance from an expert if\n". + "problems persist.)\n"; + &cleanup; exit(2); +} + +sub prepare_important_database { + open(IMP,"+>$importantspace") || &bombout("unable to create $importantspace: $!"); + select((select(IMP),$|=1)[0]); + print(IMP "#padding\n"x512) || &bombout("unable to pad $importantspace: $!"); + seek(IMP,0,0) || &bombout("unable to seek (rewind) $importantspace: $!"); + &debug("important database prepared"); +} + +sub release_important_database { + close(IMP); + unlink($importantspace) || &bombout("unable to delete $importantspace: $!"); + &debug("important database released"); +} + +#*** database lock management ***# + +sub lock_database { + # Lock the package management databases. Stale locks will + # be broken, but there is no concurrency checking on the lock- + # breaking code. + push(@cleanups,'unlink($lockmine)'); + open(PID,">$lockmine") || &bombout("failed to create new pid file $lockmine: $!"); + printf(PID "%010d\n",$$) || &bombout("failed to add pid to $lockmine: $!"); + close(PID) || &bombout("failed to close new pid file $lockmine: $!"); + unless (link($lockmine,$lockfile)) { + $! == &EEXIST || &bombout("failed to create lock on packages database: $!"); + if (open(PID,"<$lockfile")) { + undef $/; $opid= ; $/="\n"; + $opid =~ m/^\d{10}\n$/ || &lockfailed(" (pid missing)"); + close(PID); + -d '/proc/self' || + &bombout("/proc/self not found ($!) - /proc not mounted ?"); + -d sprintf("/proc/%d",$opid) && &lockfailed(" (in use by pid $opid)"); + if (open(PID,"<$lockfile")) { + $opid eq || &lockfailed(' (pid changed)'); + close(PID); + unlink($lockfile) || + &bombout("failed to break stale lock on database: $!"); + print STDERR + "$name: stale lock found on packages database, lock forced\n"; + } else { + $!==&ENOENT || + &bombout("failed to confirm who owns lock on database: $!"); + } + } else { + $!==&ENOENT || &bombout("failed to determine who owns lock on database: $!"); + } + link($lockmine,$lockfile) || + &bombout("failed to create lock on packages database: $!"); + } + push(@cleanups, 'unlink($lockfile) || + warn("$name: failed to unlock packages database: $!\n")'); + unlink($lockmine); +} + +sub unlock_database { + unlink($lockfile) || &bombout("failed to unlock packages database: $!"); + pop(@cleanups); +} + +#*** error handling ***# + +sub lockfailed { &bombout("unable to lock packages database@_"); } +sub bombout { print STDERR "$name - critical error: @_\n"; &cleanup; exit(2); } +sub badusage { print STDERR "$name: @_\n\n"; &usage; &cleanup; exit(3); } + +sub outerr { + &bombout("failed write to stdout: $!"); +} + +sub cleanup { + while (@cleanups) { + eval(pop(@cleanups)); + $@ && print STDERR "error while cleaning up: $@"; + } +} + +sub debug { + return unless $debug; + print "D: @_\n"; +} + +sub ecode { + local ($w,$s) = ($?,$!); + &debug("ecode $w syserr $s"); + return +# (($w & 0x0ffff) == 0x0ff00 ? "problems running program - exit code -1" : +# ($w & 0x0ff) == 0 ? "exit status ".(($w & 0x0ff00) >> 8) : +# ($w & 0x0ff) == 0x07f ? "stopped by signal ".(($w & 0x0ff00) >> 8) : +# "killed by signal ".($w & 0x07f).($w & 0x080 ? " (core dumped)" : '')). + (&WIFEXITED($w) ? "exit status ".&WEXITSTATUS($w) : + &WIFSIGNALED($w) ? "killed by signal ".&WTERMSIG($w). + (&WCOREDUMP($w) ? " (core dumped)" : ""): + &WIFSTOPPED($w) ? "stopped due to signal ".&WSTOPSIG($w) : + "unknown status $w"). + ($s ? ", system error $s" : ''); +} + +#*** miscellaneous helpful routines ***# + +sub readall { + local ($fh) = @_; + local ($r,$n,$this) = ''; + for (;;) { + defined($n=read($fh,$this,4096)) || return undef; + $n || last; + $r.= $this; + } + return $r; +} + +#sub debug_compare_verrevs { +# local (@i)= @_; +# local ($i)= &x_compare_verrevs(@i); +# &debug("compare_verrevs >@i< = >$i<"); +# return $i; +#} + +sub compare_verrevs { + local ($av,$ar,$bv,$br,$c) = @_; + $c = &compare_vnumbers($av,$bv); return $c if $c; + return &compare_vnumbers($ar,$br); +} + +sub compare_vnumbers { + local ($a, $b) = @_; + do { + $a =~ s/^\D*//; $ad= $&; $ad =~ s/\W/ /g; + $b =~ s/^\D*//; $bd= $&; $bd =~ s/\W/ /g; + $cm = $ad cmp $bd; return $cm if $cm; + $a =~ s/^\d*//; $ad= $&; + $b =~ s/^\d*//; $bd= $&; + $cm = $ad <=> $bd; return $cm if $cm; + } while (length ($a) && length ($b)); + return length ($a) cmp length ($b); +} + +sub sync { + system('sync'); +} diff --git a/scripts/perl-dpkg.pl b/scripts/perl-dpkg.pl new file mode 100755 index 00000000..ba70fc5f --- /dev/null +++ b/scripts/perl-dpkg.pl @@ -0,0 +1,1482 @@ +#!/usr/bin/perl -- +# +# dpkg: Debian GNU/Linux package maintenance utility +# +# Copyright (C) 1994 Matt Welsh +# Copyright (C) 1994 Carl Streeter +# Copyright (C) 1994 Ian Murdock +# Copyright (C) 1994 Ian Jackson +# +# dpkg 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, +# or (at your option) any later version. +# +# dpkg 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 dpkg; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# + +$version= '0.93.15'; # This line modified by Makefile + +sub version { + print STDERR < <.deb file name> ... | -a|--auto ... + dpkg --unpack <.deb file name> ... | -a|--auto ... + dpkg -A|--avail <.deb file name> ... | -a|--auto ... + dpkg --configure ... | -a|--auto + dpkg -r|--remove ... | -a|--auto + dpkg -l|--list [ ...] + dpkg -s|--status [ ...] + dpkg -S|--search ... + dpkg -b|--build|-c|--contents|-e|--control|--info|-f|--field| + -x|--extract|-X|--vextract ... (see dpkg-deb --help) +Options: --purge --control-quiet --control-verbose --version --help + -R|--root= --admindir= --instdir= + --no-keep-old-conf --no-keep-new-conf -N|--no-also-select + --ignore-depends=,... + --conf-(same|diff|all)-(new|old|promptnew|promptold) + --force-,,... --no-force-...|--refuse-... +Status selections: --isok-[o][h] (OK, Hold; alternatives are y, n) + --want-[u][i][d][p] (Unknown, Install, Deinstall, Purge) + --stat-[nupircNO] (Not, Unpacked, Postinst-failed, Installed, Removal-failed, + Config-files, Not/Config-files, Not/Config-files/Installed) +Force things: conflicts, depends, downgrade, depends-version, prermfail, + configure-any, hold, extractfail + (default is --no-force everything, except --force-downgrade) +Use \`$dselect\' for user-friendly package management. +END +} + +$instroot= ''; +$controlwarn = 1; +$estatus = 0; +$filename_pattern = "*.deb"; +%force= ( 'conflicts',0, 'depends',0, 'depends-version',0, 'downgrade',1, + 'prermfail',0, 'postrmfail',0, 'hold',0, 'configure-any',0, + 'extractfail',0 ); + +%selectmap_h= ('o','ok', 'h','hold', 'y','ok', 'n','hold'); +%selectmap_w= ('u', 'unknown', 'i', 'install', 'd', 'deinstall', 'p', 'purge'); +%selectmap_s= ('n', 'not-installed', + 'u', 'unpacked', + 'p', 'postinst-failed', + 'i', 'installed', + 'r', 'removal-failed', + 'c', 'config-files', + 'n', 'not-installed,config-files', + 'o', 'not-installed,config-files,installed'); +%selectthings= ('isok','h', 'want','w', 'stat','s'); + +require 'lib.pl'; # This line modified by Makefile +$0 =~ m|[^/]+$|; $name = $dpkg; +$|=1; +umask(022); + +$action= 'none'; + +%myabbrevact= ('i','install', 'r','remove', 'A','avail', + 'S','search', 'l','list', 's','status'); + +# $conf...[0] corresponds to `same', 1 to diff +$confusenew[0]= 0; $confprompt[0]= 0; +$confusenew[1]= 1; $confprompt[1]= 1; +# Ie, default is to prompt only when hashes differ, +# and to use new when hashes differ + +while ($ARGV[0] =~ m/^-/) { + $_= shift(@ARGV); + $noptsdone++; + if (m/^--$/) { + $noptsdone--; last; + } elsif (m/^--(install|remove|unpack|configure|avail|list|status)$/) { + &setaction($1); + } elsif (m/^--(build|contents|control|info|field|extract|vextract)$/) { + $noptsdone--; &backend($1); + } elsif (m/^--ignore-depends=($packagere(,$packagere)*)$/o) { + grep($ignore_depends{$_}=1, split(/,/,$1)); + } elsif (m/^--(force|no-force|refuse)-/) { + $fvalue= ($1 eq 'force'); + for $fv (split(/,/,$')) { + defined($force{$fv}) || &badusage("unknown --force option \`$fv'"); + $force{$fv}= $fvalue; + } + } elsif (m/^--conf-(same|diff|all)-(new|old|promptnew|promptold)$/) { + $new= $2 eq 'new' || $2 eq 'promptnew'; + $prompt= $2 eq 'promptnew' || $2 eq 'promptold'; + if ($1 ne 'same') { $confusenew[1]= $new; $confprompt[1]= $prompt; } + if ($1 ne 'diff') { $confusenew[0]= $new; $confprompt[0]= $prompt; } + } elsif (m/^--(\w+)-(\w+)$/ && defined($selectthings{$1})) { + $thisname= $1; + $thisthing= $selectthings{$thisname}; + $_=$2; + eval '%thismap= %selectmap_'.$thisthing; + while (s/^.//) { + if (!defined($thismap{$&})) { + &badusage("unknown status letter $& for status field $thisname"); + } + $thiscodes= $thismap{$&}; + $selectdo.= "undef \$select_$thisthing;"; + for $v (split(m/,/, $thiscodes)) { + $selectdo .= "\$select_$thisthing{'$v'}=1;"; + } + } + } elsif (m/^--root=/) { + $instroot=$'; &setadmindir("$instroot/$orgadmindir"); + } elsif (m/^--admindir=/) { + &setadmindir("$'"); + } elsif (m/^--instdir=/) { + $instroot=$'; + } elsif (m/^--auto$/) { + $auto= 1; + } elsif (m/^--purge$/) { + $purge= 1; + } elsif (m/^--skip-same-version$/) { + print STDERR + "Warning: dpkg --skip-same-version not implemented, will process\n". + " process even packages the same version of which is installed.\n"; + } elsif (m/^--no-also-select$/) { + $noalsoselect= 1; + } elsif (m/^--control-verbose$/) { + $controlwarn= 1; + } elsif (m/^--control-quiet$/) { + $controlwarn= 0; + } elsif (m/^--no-keep-old-conf$/) { + $nokeepold= 1; + } elsif (m/^--no-keep-new-conf$/) { + $nokeepnew= 1; + } elsif (m/^--succinct-prompts$/) { + $succinct= 1; + } elsif (m/^--debug$/) { + $debug= 1; + } elsif (m/^--help$/) { + &usage; exit(0); + } elsif (m/^--version$/) { + &version; exit(0); + } elsif (m/^--/) { + &badusage("unknown option \`$_'"); + } else { + s/^-//; $noptsdone--; + while (s/^.//) { + $noptsdone++; + if (defined($myabbrevact{$&})) { + &setaction($myabbrevact{$&}); + } elsif (defined($debabbrevact{$&})) { + $noptsdone--; &backend($debabbrevact{$&}); + } elsif ($& eq 'a') { + $auto= 1; + } elsif ($& eq 'D') { + $debug= 1; + } elsif ($& eq 'N') { + $noautoselect= 1; + } elsif ($& eq 'R') { + s/^=// || &badusage("missing value for -R= option"); + $instroot= $_; &setadmindir("$instroot/$orgadmindir"); $_=''; + } else { + &badusage("unknown option \`-$&'"); + } + } + } +} + +$action eq 'none' && &badusage("an action must be specified"); + +&debug("arguments parsed"); + +#*** list, status or search - the nonactive operations ***# + +if ($action eq 'list' || $action eq 'status') { + &database_start; + if ($action eq 'list' || !@ARGV) { + &selectall(*selectmap_h,*select_h); + &selectall(*selectmap_w,*select_w); + &selectall(*selectmap_s,*select_s); + if (@ARGV) { $select_s{'not-installed'}=0; } + } + $ecode= 0; + if ($action eq 'status') { + for ($i=0;$i<=$#keysortorder;$i++) { + $keysortorder{$keysortorder[$i]}= sprintf("%6d ",$i); +# &debug("set $i: $keysortorder[$i], sortorder ". +# "\`$keysortorder{$keysortorder[$i]}'"); + } + @ARGV= &applyselcrit(sort keys %st_p21) unless @ARGV; + for $p (@ARGV) { + if (!$st_p21{$p}) { + print(STDERR "$name: no information available about package $p\n") + || &bombout("writing to stderr: $!"); + $ecode= 1; + } + print("Package: $p\n", + "Status: $st_p2w{$p} $st_p2h{$p} $st_p2s{$p}\n") || &outerr; + for $k (sort { $keysortorder{$a}.$a cmp $keysortorder{$b}.$b; } + keys %all_k21) { +# &debug("field $k, sortorder \`$keysortorder{$k}'"); + next unless defined($st_pk2v{$p,$k}); + $v= $st_pk2v{$p,$k}; next unless length($v); + if ($k eq 'conffiles' || $k eq 'list') { + $v= sprintf("(%d files, not listed)", + scalar(grep(m/\S/, split(/\n/,$v)))); + } + print("$k: $v\n") || &outerr; + } + if (defined($av_p21{$p})) { + print("\n\`Available' version of package $p, where different:\n") + || &outerr; + for $k (keys %all_k21) { + next unless defined($av_pk2v{$p,$k}); + $v= $st_pk2v{$p,$k}; next unless length($v); + $u= $st_pk2v{$p,$k}; next if $u eq $v; + print("$k: $v\n") || &outerr; + } + print("\n") || &outerr; + } + } + } else { # $action eq 'list' + $listhead=0; + if (@ARGV) { + for $r (@ARGV) { + &listinfo(&applyselcrit(sort grep(m/$r/,keys %st_p21))); + } + } else { + undef $r; + &listinfo(&applyselcrit(sort keys %st_p21)); + } + } + &database_finish; + exit($ecode); +} + +sub listinfo { + if (!@_) { + print(STDERR + defined($r) ? + "No selected packages found matching regexp \`$r'.\n" : + "No packages matched selection criteria.\n") || + &bombout("writing to stderr: $!"); + return; + } + + if (!$listhead) { + print <& WP"); close(WP); + exec('find',@ARGV,'-name',$filename_pattern,'-type','f','-print0'); + die "$name: could not exec \`find': $!"; + } + close(WP); + $/="\0"; @ARGV= ; $/="\n"; + eof || &bombout("read filenames from \`find': $!"); + close(RP); + $!=0; waitpid($c,0) eq $c || &bombout("wait for \`find' failed: $!"); + $? && &bombout("\`find' process returned error code ".&ecode); + @ARGV || &bombout("no packages found to $action"); + } else { + @ARGV && &badusage("no package names may be specified with --$action --auto"); + if ($action eq 'remove') { + eval 'sub condition { + $wants eq "deinstall" || $wants eq "purge" || return 0; + $cstatus eq "not-installed" && return 0; + $cstatus eq "config-files" && $wants eq "deinstall" && return 0; + return 1; + } 1;' || &internalerr("sub condition: $@"); + } elsif ($action eq 'configure') { + eval 'sub condition { + $wants eq "install" || return 0; + $cstatus eq "unpacked" || $cstatus eq "postinst-failed" || return 0; + return 1; + } 1;' || &internalerr("sub condition: $@"); + } else { + &internalerr("unknown auto nonames action $action"); + } + for $p (keys %st_p21) { + next if $st_p2h{$p} eq 'hold'; + $wants= $st_p2w{$p}; $cstatus= $st_p2s{$p}; + next unless &condition; + push(@ARGV,$p); + } + } + &debug("auto: arglist @ARGV"); +} else { + @ARGV || &badusage("need a list of packages or filenames"); +} + +if ($action eq 'install' || $action eq 'unpack') { + grep(s:^[^/.]:./$&:, @ARGV); # Sanitise filenames +} + +&debug("action: $action; arglist @ARGV"); + +#*** actually do things ***# + +for $arg (@ARGV) { + $package= ''; @undo=(); + &debug("&do_$action($arg)"); + if (!eval "&do_$action(\$arg); 1;") { &handleerror || last; } + &checkpointstatus; +} +&checkpointstatus; + +if (!$abort) { + &debug("&middle_$action($arg)"); + if (!eval "&middle_$action; 1;") { print STDERR $@; $abort=1; } +} +&checkpointstatus; + +if (!$abort) { + while (@deferred) { + $arg= shift(@deferred); $package= ''; @undo=(); + &debug("&deferred_$action($arg) ($dependtry: $sincenothing)"); + if (!eval "&deferred_$action(\$arg); 1;") { &handleerror || last; } + &checkpointstatus; + } + &checkpointstatus; +} + +if ($errors) { + print STDERR "$name: $errors errors occurred.\n"; + $estatus= 1; +} + +&database_finish; +&cleanup; + +exit($estatus); + +#*** useful subroutines for main control section ***# + +sub handleerror { + print STDERR $@; + if (length($package) && defined($st_p21{$package})) { + $st_p2h{$package}='hold'; &amended_status($package); + } + $errors++; + if ($errors >20) { print STDERR "$name: too many errors, halting\n"; return 0; } + return !$abort; +} + +sub checkpointstatus { + return unless keys %statusupdated; + &amended_status(keys %statusupdated); + undef %statusupdated; +} + +sub backend { + &setaction(''); + ($noptsdone) && &badusage("action \`$_[0]' must be first argument"); + &debug("backend --$_[0]"); + exec($backend, "--$_[0]", @ARGV); + &bombout("unable to run $backend: $!"); +} + +sub setaction { + $action eq 'none' || &badusage("conflicting actions \`$action' and \`$1'"); + $action= $_[0]; +} + +#*** error handlers for use in actions ***# + +sub warn { warn "$name - warning: @_\n"; } +sub subcriterr { warn "$name - subcritical error: @_\n"; $estatus=1; } +sub error { &acleanup; die "$name - error: @_\n"; } +sub internalerr { &acleanup; die "$name - internal error, please report: @_\n"; } +sub fatalerr { &acleanup; die "$name - fatal error, halting: @_\n"; $abort=1; } + +sub corruptingerr { + local ($corruptingerr)= @_; + &acleanup; + die "$name - horrible error: $corruptingerr;\n". + "Package manager data is now out of step with installed system.\n". + "Please re-install \`$package' to ensure system consistency!\n". + "(Seek assistance from an expert if problems persist.)\n"; + $abort=1; +} + +sub forcibleerr { + local ($msg,@forces) = @_; + if (@forces= grep($force{$_},@forces)) { + &warn("$msg (proceeding due to --force-$forces[0])"); + } else { + &error("$msg (skipping this package)"); + } +} + +sub acleanup { + while (@undo) { + eval(pop(@undo)); + $@ && print STDERR "error while cleaning up: $@"; + } +} + +#*** --install ***# + +sub do_install { + &do_unpack($arg); + $arg= $package; + &do_configure($arg); +} + +sub deferred_install { &deferred_configure; } + +sub middle_install { &middle_configure } + +#*** --avail ***# + +sub do_avail { + unlink($controli); + if ($! != &ENOENT) { + system('rm','-rf',$controli); + unlink($controli); + $! == &ENOENT || &fatalerr("unable to get rid of $controli: $!"); + } + &debug("extract control $backend --control $arg $controli"); + $!=0; system($backend,"--control",$arg,$controli); + $? && &error("$arg: could not extract control information (".&ecode.")"); + open(CONTROL,"$controli/control") || + &error("$arg: corrupt - unable to read control file: $!"); + &parse_control("$arg"); + for $k (keys %cf_k2v) { + $av_pk2v{$p,$k}= $cf_k2v{$k}; + } + for $k (@nokeepfields) { + delete $av_pk2v{$p,$k} unless defined($cf_k2v{$k}); + } + &amended_available($p); + $package=$p; +} + +sub deferred_avail { } +sub middle_avail { } + +#*** --unpack ***# + +sub middle_unpack { } + +sub do_unpack { + &do_avail; + $cstatus= $st_p2s{$package}; + if ($st_p2w{$package} ne 'install') { + if (!$noalsoselect) { + $st_p2w{$package}= 'install'; $statusupdated{$package}= 1; + print STDOUT "Selecting previously deselected package $package.\n"; + } else { + print STDOUT "Skipping deselected package $package.\n"; + return; + } + } + for $tp (split(/,/, $av_pk2v{$package,'conflicts'})) { + $tp =~ s/^\s*//; $tp =~ s/\s+$//; + ($tps, $rightver, $inst, $want, $tpp)= &installationstatus($tp); + unless ($tps eq 'not-installed' || $tps eq 'config-files' || !$rightver) { + &forcibleerr("$arg: conflicts with package $tpp ($want),". + " found $inst on system", + 'conflicts'); + } + } + if ($cstatus eq 'installed') { + if (&compare_verrevs($av_pk2v{$package,'version'}, + $av_pk2v{$package,'package_revision'}, + $st_k2v{'version'},$st_k2v{'package_revision'}) <0) { + &forcibleerr("$arg: downgrading installed $package version ". + "$st_k2v{'version'}, ". + "package revision $st_k2v{'package_revision'} ". + "to older version ". + "$av_pk2v{$package,'version'}, ". + "package revision $av_pk2v{$package,'package_revision'}", + 'downgrade'); + } + } + if (open(CONF,"$controli/conffiles")) { + @configf= ; + eof || &error("$arg: unable to read $controli/conffiles: $!"); + close(CONF); + grep((chop, m,^/, || s,^,/,), @configf); + } elsif ($! != &ENOENT) { + &error("$arg: cannot get config files list: $!"); + } else { + @configf= (); + } + + if ($cstatus eq 'installed' || $cstatus eq 'unpacked' || + $cstatus eq 'postinst-failed' || $cstatus eq 'removal-failed') { + &filesinpackage($arg,$package); + print STDOUT "Preparing to replace $package ...\n"; + } + if ($cstatus eq 'installed') { + if (!eval { + &run_script_ne("$scriptsdir/$package.prerm", 'old pre-removal script', + 'upgrade', + $av_pk2v{$package,'version'}.'-'. + $av_pk2v{$package,'package_revision'}); + 1; + }) { + &warn("$@... trying script from new package instead."); + &run_script("$controli/prerm", 'new prerm script', + 'failed-upgrade', + $st_k2v{'version'}.'-'.$st_k2v{'package_revision'}); + } + push(@undo, + '$st_p2s{$package}= "postinst-failed"; $statusupdated{$package}=1; + &run_script_ne("$scriptsdir/$package.postinst", + "old post-installation script", + "abort-upgrade", + $av_pk2v{$package,"version"}."-". + $av_pk2v{$package,"package_revision"}); + $st_p2s{$package}= "installed"; $statusupdated{$package}=1;'); + } + @fbackups=(); + if ($cstatus eq 'installed' || $cstatus eq 'unpacked' || + $cstatus eq 'postinst-failed' || $cstatus eq 'removal-failed') { + for ($i=0; $i<=$#ilist; $i++) { + next if grep($_ eq $ilist[$i], @configf); + $_= $ilist[$i]; + unless (lstat("$instroot/$_")) { + $! == &ENOENT || &error("old file $_ unstattable: $!"); + next; + } + next if -d _; + rename("$instroot/$_","$instroot/$_.dpkg-tmp") || + &error("couldn't rename old file $_ to $_.dpkg-tmp: $!"); + push(@undo, + '$_=pop(@fbackups); rename("$instroot/$_.dpkg-tmp","$instroot/$_") || + die "unable to undo rename of $_ to $_.dpkg-tmp: $!"'); + push(@fbackups, $_); + } + if (!eval { + &run_script_ne("$scriptsdir/$package.postrm", 'old post-removal script', + 'upgrade', + $av_pk2v{$package,'version'}.'-'. + $av_pk2v{$package,'package_revision'}); + 1; + }) { + &warn("$@... trying script from new package instead."); + &run_script("$controli/postrm", 'new post-removal script', + 'failed-upgrade', + $st_k2v{'version'}.'-'.$st_k2v{'package_revision'}); + } + push(@undo, + '&run_script_ne("$scriptsdir/$package.preinst", + "old pre-installation script", + "abort-upgrade", + $av_pk2v{$package,"version"}. + "-".$av_pk2v{$package,"package_revision"})'); + } + $shortarg= $arg; $shortarg =~ s:.{15,}/:.../:; + print STDOUT "Unpacking $arg, containing $package ...\n"; + &run_script("$controli/preinst", 'pre-installation script', + 'upgrade', $st_k2v{'version'}.'-'.$st_k2v{'package_revision'}); + push(@undo,'&run_script_ne("$controli/postrm", "post-removal script", + "abort-upgrade", + $st_k2v{"version"}."-".$st_k2v{"package_revision"})'); + if ($cstatus ne 'not-installed') { + for $_ (split(/\n/,$st_pk2v{$package,'conffiles'})) { + s/^ //; next unless length($_); + if (!m/^(\S+) (-|newconffile|nonexistent|[0-9a-f]{32})$/) { + &warn("$arg: ignoring bad stuff in old conffiles field \`$_'"); + next; + } + $oldhash{$1}= $2; + } + } + for $f (@configf) { + $drf= &conffderef($f); if (!defined($drf)) { next; } + if (lstat("$instroot/$drf.dpkg-tmp")) { + $undo=1; + } else { + $! == &ENOENT || &error("unable to stat backup config file $_.dpkg-tmp: $!"); + if (lstat("$instroot/$drf")) { + rename("$instroot/$drf","$instroot/$drf.dpkg-tmp") || + &error("couldn't back up config file $f (= $drf): $!"); + $undo=1; + } elsif ($! == &ENOENT) { + $undo=0; + } else { + &error("unable to stat config file $drf: $!"); + } + } + if ($undo) { + push(@undo, + '$_=pop(@undof); rename("$instroot/$_.dpkg-tmp","$instroot/$_") || + die "unable to undo backup of config file $_: $!"'); + push(@undof, $drf); + } + } + + open(NL,">$listsdir/$package.list.new") || + &error("$package: cannot create $listsdir/$package.list.new: $!"); + defined($c= fork) || &error("$package: cannot pipe/fork for $backend --vextract"); + if (!$c) { + if (!open(STDOUT,">&NL")) { + print STDERR "$name: cannot redirect stdout: $!\n"; exit(1); + } + $vexroot= length($instroot) ? $instroot : '/'; + exec($backend,"--vextract",$arg,$vexroot); + print STDERR "$name: cannot exec $backend --vextract $arg $vexroot: $!\n"; + exit(1); + } + $!=0; waitpid($c,0) == $c || &error("could not wait for $backend: $!"); + $? && &forcibleerr("$package: failed to install (".&ecode.")", 'extractfail'); + + rename("$listsdir/$package.list.new","$listsdir/$package.list") || + &error("$package: failed to install new $listsdir/$package.list: $!"); + + $newconff=''; + for $f (@configf) { + $h= $oldhash{$f}; + $h='newconffile' unless length($h); + $newconff.= "\n $f $h"; + &debug("new hash, after unpack, of $f, is $h"); + } + + for $k (keys %all_k21) { + next if $k eq 'binary' || $k eq 'source' || $k eq 'section'; + $st_pk2v{$package,$k}= $av_pk2v{$package,$k}; + } + $st_pk2v{$package,'conffiles'}= $newconff; $all_k21{'conffiles'}= 1; + $st_p2s{$package}= 'unpacked'; $st_p2h{$package}= 'ok'; $st_p21{$package}= 1; + $statusupdated{$package}= 1; + @undo=(); @undof=(); + + for $f (@fbackups) { + unlink("$instroot/$f.dpkg-tmp") || $! == &ENOENT || + &subcriterr("$package: unable to delete saved old file $f.dpkg-tmp: $!\n"); + } + + undef %fordeletion; + opendir(PI,"$scriptsdir") || + &corruptingerr("$package: unable to read $scriptsdir directory ($!)"); + while(defined($_=readdir(PI))) { + next unless substr($_,0,length($package)+1) eq $package.'.'; + $fordeletion{$_}= 1; + } + closedir(PI); + opendir(PI,"$controli") || + &corruptingerr("$package: unable to read $controli". + " new package control information directory ($!)"); + $fordeletion{"$package.list"}= 0; + while(defined($_=readdir(PI))) { + next if m/^\.\.?$/ || m/^conffiles$/ || m/^control$/; + rename("$controli/$_","$scriptsdir/$package.$_") || + &corruptingerr("$package: unable to install new package control". + " information file $scriptsdir/$package.$_ ($!)"); + $fordeletion{"$package.$_"}= 0; + } + closedir(PI); + for $_ (keys %fordeletion) { + next unless $fordeletion{$_}; + unlink("$scriptsdir/$_") || + &corruptingerr("$package: unable to remove old package script". + " $scriptsdir/$_ ($!)"); + } +} + +#*** --configure ***# + +sub do_configure { + $package=$arg; $cstatus= $st_p2s{$package}; + if (!defined($st_p21{$package})) { $cstatus= 'not-installed'; } + unless ($cstatus eq 'unpacked' || $cstatus eq 'postinst-failed') { + if ($cstatus eq 'not-installed') { + &error("no package named \`$package' is installed, cannot configure"); + } else { + &error("$package: is currently in state \`$cstatus', cannot configure"); + } + } + push(@deferred,$package); +} + +sub middle_configure { + $sincenothing=0; $dependtry=1; +} + +sub deferred_configure { + # The algorithm for deciding what to configure first is as follows: + # Loop through all packages doing a `try 1' until we've been round + # and nothing has been done, then do `try 2' and `try 3' likewise. + # Try 1: + # Are all dependencies of this package done ? If so, do it. + # Are any of the dependencies missing or the wrong version ? + # If so, abort (unless --force-depends, in which case defer) + # Will we need to configure a package we weren't given as an + # argument ? If so, abort - except if --force-configure-any, + # in which case we add the package to the argument list. + # If none of the above, defer the package. + # Try 2: + # Find a cycle and break it (see above). + # Do as for try 1. + # Try 3 (only if --force-depends-version). + # Same as for try 2, but don't mind version number in dependencies. + # Try 4 (only if --force-depends). + # Do anyway. + $package= $arg; + if ($sincenothing++ > $#deferred*2+2) { + $dependtry++; $sincenothing=0; + &internalerr("$package: nothing configured, but try was already 4 !") + if $dependtry > 4; + } + if ($dependtry > 1) { &findbreakcycle($package); } + ($ok, @aemsgs) = &dependencies_ok($package,''); + if ($ok == 1) { + push(@deferred,$package); return; + } elsif ($ok == 0) { + $sincenothing= 0; + &error("$arg: dependency problems - not configuring this package:\n ". + join("\n ",@aemsgs)); + } elsif (@aemsgs) { + &warn("$arg: dependency problems, configuring anyway as you request:\n ". + join("\n ",@aemsgs)); + } + $sincenothing= 0; + print STDOUT "Setting up $package ...\n"; + if ($st_p2s{$package} eq 'unpacked') { + &debug("conffiles updating >$st_pk2v{$package,'conffiles'}<"); + undef %oldhash; @configf=(); + for $_ (split(/\n/,$st_pk2v{$package,'conffiles'})) { + s/^ //; next unless length($_); + if (!m/^(\S+) (-|newconffile|nonexistent|[0-9a-f]{32})$/) { + &warn("$arg: ignoring bad stuff in old conffiles field \`$_'"); + next; + } + $oldhash{$1}= $2; push(@configf,$1); + &debug("old hash of $1 is $2"); + } + undef %newhash; + for $file (@configf) { + $drf= &conffderef($file); + if (!defined($drf)) { $newhash{$file}= '-'; next; } + $newhash{$file}= &hash("$instroot/$drf"); + &debug("new hash of $file is $newhash{$file} (old $oldhash{$file})"); + if ($oldhash{$file} eq 'newconffile') { + $usenew= 1; + } else { + if (!&files_not_identical("$instroot/$drf", + "$instroot/$drf.dpkg-tmp")) { + rename("$instroot/$drf.dpkg-tmp",$drf) || $!==&ENOENT || + &error("$package: unable to reinstall ". + "existing conffile $drf.dpkg-tmp: $!"); + &debug("files identical $file"); + } else { + $diff= $newhash{$file} ne $oldhash{$file}; + $usenew= $confusenew[$diff]; + &debug("the decision - diff $diff;". + " usenew $usenew prompt $confpromt[$diff]"); + if ($confprompt[$diff]) { + $symlinked = $drf eq $file ? '' : + $succinct ? " (-> $drf)" : + " (which is a symlink to $drf)"; + for (;;) { + print + $succinct ? " +Package $package, file $file$symlinked, ".($diff ? "CHANGED": "not changed") + : $diff ? " +In package $package, distributed version of configuration +file $file$symlinked has changed +since the last time it was installed. You may: + * Install the new version and edit it later to reflect your wishes. + ". ($nokeepold ? + "(Your old version will not be saved.)" : + "(Your old version will be saved in $drf.dpkg-old.)") . " + * Leave your old version in place, and perhaps check later that + you don't want to update it to take account of new features. + ". ($nokeepnew ? + "(The new version be discarded.)" : + "(The new version will be placed in $drf.dpkg-new.)") + : " +Package $package contains the same distributed version of +configuration file $file$symlinked +as the last time it was installed. You may: + * Install the distributed version, overwriting your changes. + ". ($nokeepold ? + "(Your changed version will not be saved.)" : + "(Your changed version will be saved in $drf.dpkg-old.)") . " + * Leave your own version in place. + ". ($nokeepnew ? + "(The distributed version be discarded.)" : + "(The distributed version will be placed in $drf.dpkg-new.)"); + + print " +$file: install new version ? (y/n, default=". ($usenew?'y':'n'). ") "; + + $!=0; defined($iread= ) || + &error("$package: prompting, EOF/error on stdin: $!"); + $_= $iread; s/^\s*//; s/\s+$//; + ($usenew=0, last) if m/^n(o)?$/i; + ($usenew=1, last) if m/^y(es)?$/i; + last if m/^$/; + print "\nPlease answer \`y' or \`n'.\n"; + } + } + &debug("decided, usenew $usenew"); + if ($usenew) { + ©perm("$drf.dpkg-tmp",$drf,$drf); + if ($nokeepold) { + unlink("$instroot/$drf.dpkg-tmp") || $!==&ENOENT || + &error("$package: unable to delete old conffile ". + "$drf.dpkg-tmp: $!"); + unlink("$instroot/$drf.dpkg-old") || $!==&ENOENT || + &error("$package: unable to delete very old ". + "conffile $drf.dpkg-old: $!"); + } else { + rename("$instroot/$drf.dpkg-tmp","$instroot/$drf.dpkg-old") + || $!==&ENOENT || + &error("$package: unable to back up old conffile ". + "$drf.dpkg-tmp as $drf.dpkg-old: $!"); + } + } else { + unlink("$instroot/$drf.dpkg-new") || $!==&ENOENT || + &error("$package: unable to delete old new conffile ". + "$drf.dpkg-new: $!"); + if (!$nokeepnew) { + link("$instroot/$drf","$instroot/$drf.dpkg-new") + || $!==&ENOENT || + &error("$package: unable save new conffile ". + "$drf as $drf.dpkg-new: $!"); + } + if (!rename("$instroot/$drf.dpkg-tmp","$instroot/$drf")) { + $!==&ENOENT || &error("$package: unable reinstall old ". + "conffile $drf.dpkg-tmp as $drf: $!"); + unlink("$instroot/$drf"); + } + } + } + } + } + $newconff=''; + for $f (@configf) { + $h= $newhash{$f}; $newconff.= "\n $f $h"; + } + $st_pk2v{$package,'conffiles'}= $newconff; $all_k21{'conffiles'}= 1; + } + $st_p2s{$package}= 'postinst-failed'; $statusupdated{$package}= 1; + &run_script("$scriptsdir/$package.postinst", + 'post-installation script', 'configure'); + $st_p2s{$package}= 'installed'; + $st_p2h{$package}= 'ok'; $statusupdated{$package}= 1; +} + +#*** --remove ***# + +sub do_remove { + $package=$arg; $cstatus= $st_p2s{$package}; + if (!defined($st_p21{$package}) || + $cstatus eq 'not-installed' || + !$purge && $cstatus eq 'config-files') { + &error("$package: is not installed, cannot remove"); + } + push(@deferred,$package); + if (!$auto) { + $ns= $purge ? 'purge' : 'deinstall'; + if ($st_p2w{$package} ne $ns) { + $st_p2w{$package}= $ns; $statusupdated{$package}= 1; + } + } +} + +sub middle_remove { + $sincenothing=0; $dependtry=1; + for $p (keys %st_p2s) { + $cstatus= $st_p2s{$p}; + next unless $cstatus eq 'installed'; + for $tp (split(/[\|,]/, $st_pk2v{$p,'depends'})) { + $tp =~ s/\s*\(.+\)\s*$//; $tp =~ s/^\s*//; $tp =~ s/\s+$//; + $tp =~ m/^$packagere$/o || + &internalerr("package $p dependency $tp didn't match re"); + $depended{$tp}.= "$p "; + } + } +} + +sub deferred_remove { + $package= $arg; + if ($sincenothing++ > $#deferred*2+2) { + $dependtry++; $sincenothing=0; + &internalerr("$package: nothing configured, but try was already 4 !") + if $dependtry > 4; + } + @raemsgs= (); $rok= 2; + &debug("$package may be depended on by any of >$depended{$package}<"); + for $fp (split(/ /, $depended{$package})) { + next if $fp eq '' || $ignore_depends{$tp}; + $is= $st_p2s{$fp}; + next if $is eq 'not-installed' || $is eq 'unpacked' || + $is eq 'removal-failed' || $is eq 'config-files'; + if ($dependtry > 1) { &findbreakcycle($fp); } + ($ok, @aemsgs) = &dependencies_ok($fp,$package); + if ($rok != 1) { push(@raemsgs,@aemsgs); } + $rok= $ok if $ok < $rok; + } + if ($rok == 1) { + push(@deferred,$package); return; + } elsif ($rok == 0) { + $sincenothing= 0; + &error("$arg: dependency problems - not removing this package:\n ". + join("\n ",@raemsgs)); + } elsif (@raemsgs) { + &warn("$arg: dependency problems, removing anyway as you request:\n ". + join("\n ",@raemsgs)); + } + $sincenothing= 0; + &filesinpackage($arg,$package); + + undef %hash; @configfr= @configf= (); + for $_ (split(/\n/,$st_pk2v{$package,'conffiles'})) { + s/^ //; next unless length($_); + if (!m/^(\S+) (-|newconffile|nonexistent|[0-9a-f]{32})$/) { + &warn("$arg: ignoring bad stuff in old conffiles field \`$_'"); + next; + } + unshift(@configfr,$1); push(@configf,$1); + $hash{$1}= $2; + } + + if ($st_p2s{$package} ne 'config-files') { + print STDOUT "Removing $package ...\n"; + &run_script("$scriptsdir/$package.prerm", 'pre-removal script', 'remove'); + $st_p2s{$package}= 'removal-failed'; $statusupdated{$package}= 1; + for $file (reverse @ilist) { + next if grep($_ eq $file, @configf); + unlink("$instroot/$file.dpkg-tmp") || $! == &ENOENT || + &error("$arg: cannot remove supposed old temp file $file: $!"); + next if unlink("$instroot/$file"); + next if $! == &ENOENT; + &error("$arg: cannot remove file $file: $!") unless $! == &EISDIR; + next if rmdir("$instroot/$file"); + &error("$arg: cannot remove directory $file: $!") unless $! == &ENOTEMPTY; + } + &run_script("$scriptsdir/$package.postrm", 'post-removal script', 'remove'); + opendir(DSD,"$scriptsdir") || + &error("$arg: cannot read directory $scriptsdir: $!"); + for $_ (readdir(DSD)) { + next unless m/\.[^.]$/; + next if $& eq '.postrm' || $& eq '.list'; + # need postrm for --purge, and list has to go last in case it + # goes wrong + next unless $` eq $package; + unlink("$scriptsdir/$_") || + &error("$arg: unable to delete control information $scriptsdir/$_: $!"); + } + closedir(DSD); + $st_p2s{$package}= 'config-files'; + $statusupdated{$package}= 1; + } + if ($purge) { + print STDOUT "Purging configuration files for $package ...\n"; + push(@undo, + '$newconff=""; + for $f (@configf) { $newconff.= "\n $f $hash{$f}"; } + $st_pk2v{$package,"conffiles"}= $newconff; $all_k21{"conffiles"}= 1;'); + for $file (@configfr) { + $drf= &conffderef($file); if (!defined($drf)) { next; } + unlink("$instroot/$drf") || $! == &ENOENT || + &error("$arg: cannot remove old config file $file (= $drf): $!"); + $hash{$file}= 'newconffile'; + unlink("$instroot/$file") || $! == &ENOENT || + &error("$arg: cannot remove old config file $file: $!") + if $file ne $drf; + for $ext ('.dpkg-tmp', '.dpkg-old', '.dpkg-new', '~', '.bak', '%') { + unlink("$instroot/$drf$ext") || $! == &ENOENT || + &error("$arg: cannot remove old config file $drf$ext: $!"); + } + unlink("#$instroot/$drf#") || $! == &ENOENT || + &error("$arg: cannot remove old auto-save file #$drf#: $!"); + $drf =~ m,^(.*)/, || next; $dir= $1; $base= $'; + if (opendir(CFD,"$instroot/$dir")) { + for $_ (readdir(CFD)) { + next unless m/\.~\d+~$/; + next unless $` eq $base; + unlink("$instroot/$dir/$_") || $! == &ENOENT || + &error("$arg: cannot remove old emacs backup file $dir/$_: $!"); + } + closedir(CFD); + if (grep($_ eq $dir, @ilist)) { + rmdir("$instroot/$dir") || $! == &ENOTEMPTY || + &error("$arg: cannot remove config file directory $dir: $!"); + } + } elsif ($! != &ENOENT) { + &error("$arg: cannot read config file dir $dir: $!"); + } + } + &run_script("$scriptsdir/$package.postrm", 'post-removal script for purge', + 'purge'); + unlink("$scriptsdir/$package.postrm") || $! == &ENOENT || + &error("$arg: cannot remove old postrm script: $!"); + &setnotinstalled; + @undo= (); + } elsif (!@configf && !stat("$scripts/$package.postrm")) { + # If there are no config files and no postrm script then we + # go straight into `purge'. However, perhaps the stat didn't + # fail with ENOENT ... + $! == &ENOENT || &error("$package: stat failed on postrm script: $!"); + $st_p2w{$package}= 'purge'; + &setnotinstalled; + } + $st_p2h{$package}= 'ok'; $statusupdated{$package}= 1; +} + +sub setnotinstalled { + unlink("$listsdir/$package.list") || + &error("$arg: unable to delete old file list: $!"); + $st_p2s{$package}= 'not-installed'; + for $k (keys %all_k21) { delete $st_pk2v{$package,$k}; } +} + +#*** dependency processing - common to --configure and --remove ***# + +# The algorithm for deciding what to configure or remove first is as +# follows: +# +# Loop through all packages doing a `try 1' until we've been round and +# nothing has been done, then do `try 2' and `try 3' likewise. +# +# When configuring, in each try we check to see whether all +# dependencies of this package are done. If so we do it. If some of +# the dependencies aren't done yet but will be later we defer the +# package, otherwise it is an error. +# +# When removing, in each try we check to see whether there are any +# packages that would have dependencies missing if we removed this +# one. If not we remove it now. If some of these packages are +# themselves scheduled for removal we defer the package until they +# have been done. +# +# The criteria for satisfying a dependency vary with the various +# tries. In try 1 we treat the dependencies as absolute. In try 2 we +# check break any cycles in the dependency graph involving the package +# we are trying to process before trying to process the package +# normally. In try 3 (which should only be reached if +# --force-depends-version is set) we ignore version number clauses in +# Depends lines. In try 4 (only reached if --force-depends is set) we +# say "ok" regardless. +# +# If we are configuring and one of the packages we depend on is +# awaiting configuration but wasn't specified in the argument list we +# will add it to the argument list if --configure-any is specified. +# In this case we note this as having "done something" so that we +# don't needlessly escalate to higher levels of dependency checking +# and breaking. + +sub dependencies_ok { + local ($dp, $removingp) = @_; + local ($tpo, $however_t, $ok, $found, @aemsgs, @oemsgs); + local ($tp, $rightver, $inst, $want, $thisf, $matched, $tpp); + $ok= 2; # 2=ok, 1=defer, 0=halt + &debug("checking dependencies of $dp (- $removingp)"); + for $tpo (split(/,/, $st_pk2v{$dp,'depends'})) { + $tpo =~ s/^\s*//; $tpo =~ s/\s+$//; + &debug(" checking group $dp -> $tpo"); + $matched= 0; @oemsgs=(); + $found=0; # 0=none, 1=defer, 2=withwarning, 3=ok + for $tp (split(/\|/, $tpo)) { + $tp =~ s/^\s*//; $tp =~ s/\s+$//; + &debug(" checking possibility $dp -> $tp"); + if ($ignore_depends{$tp}) { &debug("ignoring so ok"); $found=3; last; } + if (defined($cyclebreak{$dp,$tp})) { &debug("break cycle"); $found=3; last; } + if ($tp eq $removingp) { + ($tps, $rightver, $inst, $want, $tpp)= ('removing-now', 1, '','', $tp); + $matched= 1; + } else { + ($tps, $rightver, $inst, $want, $tpp)= &installationstatus($tp); + &debug("installationstatus($tp) -> !$tps!$rightver!$inst!$want!$tps|"); + } + if (($tps eq 'installed' || $tps eq 'unpacked' || $tps eq 'postinst-failed') + && !$rightver) { + push(@oemsgs,"version of $tpp on system is $inst (wanted $want)"); + if ($force{'depends'}) { $thisf= $dependtry >= 3 ? 2 : 1; } + } elsif ($tps eq 'unpacked' || $tps eq 'postinst-failed') { + if (grep($_ eq $tpp, @deferred)) { + $thisf=1; + } elsif (!length($removingp) && $force{'configure-any'}) { + &warn("will also configure $tpp"); + push(@deferred,$tpp); $sincenothing=0; $thisf=1; + } else { + push(@oemsgs,"package $tpp is not configured yet"); + if ($force{'depends'}) { $thisf= $dependtry >= 4 ? 2 : 1; } + } + } elsif ($tps eq 'installed') { + $found=3; last; + } elsif ($tps eq 'removing-now') { + push(@oemsgs,"$tpp is to be removed"); + if ($force{'depends'}) { $thisf= $dependtry >= 4 ? 2 : 1; } + } else { + push(@oemsgs,"$tpp ($want) is not installed"); + if ($force{'depends'}) { $thisf= $dependtry >= 4 ? 2 : 1; } + } + &debug(" found $found"); + $found=$thisf if $thisf>$found; + } + &debug(" found $found matched $matched"); + next if length($removingp) && !$matched; + if (length($removingp) && $tpo !~ m/\|/) { + $however_t= ''; + } elsif (@oemsgs > 1) { + $however_t= "\n However, ". join(",\n ", @oemsgs[0..($#oemsgs-1)]). + " and\n ". $oemsgs[$#oemsgs]. "."; + } else { + $however_t= "\n However, @oemsgs."; + } + if ($found == 0) { + push(@aemsgs, "$dp depends on $tpo.$however_t"); + $ok=0; + } elsif ($found == 1) { + $ok=1 if $ok>1; + } elsif ($found == 2) { + push(@aemsgs, "$dp depends on $tpo.$however_t"); + } elsif ($found != 3) { + &internalerr("found value in deferred_configure $found not known"); + } + } + &debug("ok $ok msgs >>@aemsgs<<"); + return ($ok, @aemsgs); +} + +sub findbreakcycle { + # Cycle breaking works recursively down the package dependency + # tree. @sofar is the list of packages we've descended down + # already - if we encounter any of its packages again in a + # dependency we have found a cycle. + # + # Cycles are preferentially broken by ignoring a dependency from + # a package which doesn't have a postinst script. If there isn't + # such a dependency in the cycle we break at the `start' of the + # cycle from the point of view of our package. + # + local ($package,@sofar) = @_; + local ($tp,$tpp,$tps,$rightver,$inst,$want,$i,$dr,$de,@sf); + &debug("findbreakcycle($package; @sofar)"); + push(@sofar,$package); + for $tp (split(/[,|]/, $st_pk2v{$package,'depends'})) { + $tp =~ s/^\s*//; $tp =~ s/\s+$//; + ($tps, $rightver, $inst, $want, $tpp)= &installationstatus($tp); + next unless $tps eq 'config-files' || $tps eq 'unpacked'; + next if $cyclebreak{$package,$tpp}; + if (grep($_ eq $tpp, @sofar)) { + &debug("found cycle $package, $tpp (@sofar)"); + @sf= (@sofar,$tpp); + for ($i=0; + $i<$#sf; + $i++) { + next if stat("$scriptsdir/$sf[$i].postinst"); + last if $! == &ENOENT; + &error("$arg: unable to stat $scriptsdir/$sf[$i].postinst: $!"); + } + $i=0 if $i>=$#sf; + ($dr,$de)= @sf[$i..$i+1]; + if (!defined($cyclebreak{$dr,$de})) { + $sincenothing=0; $cyclebreak{$dr,$de}= 1; + &debug("broken cycle $i (@sf) at $dr -> $de"); + return 1; + } + } else { + return if &findbreakcycle($tpp,@sofar); + } + } + return 0; +} + +#*** useful subroutines for actions ***# + +sub filesinpackage { + # Returns the list in @ilist. + # If error, calls &error("$epfx: ..."); + local ($epfx, $package) = @_; + open(LIST,"$listsdir/$package.list") || + &error("$epfx: database broken for $package - ". + "can't get installed files list: $!"); + @ilist= ; + eof || &error("$epfx: cannot read $listsdir/$package.list: $!"); + close(LIST); + @ilist= grep((chop, + s|/$||, + m|^/| || s|^|/|, + m/./), + @ilist); +} + +sub installationstatus { + local ($controlstring) = @_; + local ($lversion,$lpackage,$lstatus,$lrevision,$cmp) = @_; + local ($cc); + $lversion= $controlstring; + $lversion =~ s/^($packagere)\s*// || + &internalerr("needed installation status of bogus thing \`$lversion'"); + $lpackage= $1; + $lstatus= defined($st_p2s{$lpackage}) ? $st_p2s{$lpackage} : 'not-installed'; + if ($lstatus ne 'not-installed') { + if (length($lversion)) { + $lversion =~ s/^\s*\(\s*// && $lversion =~ s/\s*\)\s*$// || + &internalerr("failed to strip version \`$lversion'"); + if ($lversion =~ s/^[><=]//) { $cc= $&; } else { $cc= '='; } + $lrevision = ($lversion =~ s/-([^-]+)$//) ? $1 : ''; + $wantedstring= "version $lversion"; + $wantedstring .= ", package revision $lrevision" if length($lrevision); + $cmp= &compare_verrevs($st_pk2v{$lpackage,'version'}, + $st_pk2v{$lpackage,'package_revision'}, + $lversion, + $lrevision); + $installedstring= "version $st_pk2v{$lpackage,'version'}"; + $installedstring .= + ", package revision $st_pk2v{$lpackage,'package_revision'}" + if length($st_pk2v{$lpackage,'package_revision'}); + if ($cc eq '>') { + $rightver= $cmp>=0; $wantedstring.= ' or later'; + } elsif ($cc eq '<') { + $rightver= $cmp<=0; $wantedstring.= ' or earlier'; + } else { + s/^=//; + $rightver= !$cmp; $wantedstring= "exactly $wantedstring"; + } + } else { + $rightver= 1; + $wantedstring= "any version"; + $installedstring= $st_pk2v{$lpackage,'version'}.'-'. + $st_pk2v{$lpackage,'package_revision'}; + } + } else { + $rightver= -1; + $installedstring= "not installed"; + } + return ($lstatus,$rightver,$installedstring,$wantedstring,$lpackage); +} + +sub parse_control { + # reads from fh CONTROL + local ($fn) = @_; + local ($cf,$ln,$l,$k,$v); + defined($cf= &readall('CONTROL')) || &error("read control file $fn: $!"); + close(CONTROL); + $p= &parse_control_entry; + if (@cwarnings) { + &warn("$fn: control file contains oddities: ".join("; ",@cwarnings)) + unless $controlwarn; + } + if (@cerrors) { + &error("$fn: control file contains errors: ".join("; ",@cerrors)); + } +} + +sub run_script_ne { + local ($script,$describe,@args) = @_; + local ($extranewlines) = $script =~ m/postinst/; + &debug("running $describe = $script @args"); + if (!stat("$script")) { + return if $! == &ENOENT; + die "couldn't stat $script: $!\n"; + } + if (! -x _) { + chmod(0755, "$script") || die "couldn't make $script executable: $!\n"; + } + print "\n" if $extranewlines; + &debug("forking now"); + defined($rsc= fork) || die "couldn't fork for running $script: $!\n"; + if (!$rsc) { + if ($instroot !~ m|^/*$| && !chroot($instroot)) { + print STDERR "$name: failed to chroot to $instroot for $describe: $!\n"; + exit(1); + } + exec($script,@args); + print STDERR "$name: failed to exec $script: $!\n"; + exit(1); + } + $!=0; waitpid($rsc,0) == $rsc || die "couldn't wait for $describe: $!\n"; + $? && die "$describe failed (".&ecode.")\n"; + &debug("script done"); + print "\n" if $extranewlines; +} + +sub run_script { + return if eval { &run_script_ne; 1; }; + $rse= $@; chop($rse); &error("$package: $rse"); +} + +sub hash { + local ($file) = @_; # NB: filename must already have $instroot here + local ($c); + if (open(HF,"<$file")) { + defined($c= open(MDP,"-|")) || &error("fork/pipe for hash: $!"); + if (!$c) { + if (!open(STDIN,"<&HF")) { + print STDERR "$name: unable to redirect stdin for hash: $!\n"; exit(1); + } + exec($md5sum); print STDERR "$name: unable to exec $md5sum: $!\n"; exit(1); + } + defined($hash= &readall('MDP')) || &error("unable to read from $md5sum: $!\n"); + $!=0; close(MDP); $? && &error("$md5sum returned error (".&ecode.")"); + $hash =~ s/\n+$//; + $hash =~ m/^[0-9a-f]{32}$/i || &error("$md5sum returned bogus output \`$hash'"); + return $hash; + } elsif ($! == &ENOENT) { + return 'nonexistent'; + } else { + &warn("$arg: unable to open conffile $file for hash: $!"); + return '-'; + } +} + +sub files_not_identical { + local ($file1,$file2) = @_; # NB: filenames must already have $instroot here + if (stat($file1)) { + if (stat($file2)) { + system("cmp","-s",$file1,$file2); + if (&WIFEXITED($?)) { + $es= &WEXITSTATUS($?); + return $es if $es == 0 || $es == 1; + } + &error("cmp $file1 $file2 returned error (".&ecode.")"); + } elsif ($! == &ENOENT) { + return 1; + } else { + &error("failed to stat conffile $file2: $!"); + } + } elsif ($! == &ENOENT) { + if (stat($file2)) { + return 1; + } elsif ($! == &ENOENT) { + return 0; + } else { + &error("failed to stat conffile $file2: $!"); + } + } else { + &error("failed to stat conffile $file1: $!"); + } +} + +sub copyperm { + local ($from,$to,$name) = @_; + if (@statv= stat("$instroot/$from")) { + chown($statv[4],$statv[5],"$instroot/$to") || + $!==&ENOENT || + &warn("$package: unable to preserve ownership of $name"); + chmod($statv[2],"$instroot/$to") || + $!==&ENOENT || + &warn("$package: unable to preserve permissions of $name"); + } elsif ($! != &ENOENT) { + &warn("$package: unable to check permissions and ownership of". + " $name in order to preserve them"); + } +} + +sub conffderef { + local ($file) = @_; + local ($drf, $warning); + $drf= $file; $warning=''; + for (;;) { + if (!lstat("$instroot/$drf")) { + last if $! == &ENOENT; $warning= "unable to lstat: $!"; last; + } elsif (-f _) { + last; + } elsif (-l _) { + if (!defined($lv= readlink("$instroot/$drf"))) { + $warning= "unable to readlink: $!"; last; + } + if ($lv =~ m|^/|) { + $drf= $lv; + } else { + $drf =~ s|/[^/]+$|/$lv|; + } + } else { + $warning= "not a plain file or symlink"; last; + } + } + &debug("conffile $file drf $drf warns \`$warning'"); + if ($warning) { + &warn("$arg: possible problem with configuration file $file (= $drf):\n". + " $warning"); + return undef; + } else { + return $drf; + } +} diff --git a/scripts/start-stop-daemon.8 b/scripts/start-stop-daemon.8 new file mode 100644 index 00000000..78707201 --- /dev/null +++ b/scripts/start-stop-daemon.8 @@ -0,0 +1,19 @@ +.\" Hey, Emacs! This is an -*- nroff -*- source file. +.TH START\-STOP\-DAEMON 8 "29th November 1995" "Debian Project" "Debian GNU/Linux" +.SH NAME +start\-stop\-daemon \- Debian package installation tool +.SH DESCRIPTION +.B start\-stop\-daemon +does not have a useful man page. Please do not report this as a bug, +as this has already been done many times. + +Instead, if you are a competent and accurate writer and are willing to +spend the time reading the source code and writing good manpages +please write a better man page than this one. + +Type +.B start\-stop\-daemon \-\-help +for a brief summary of how to use dpkg. + +.SH AUTHOR +Ian Jackson . diff --git a/scripts/start-stop-daemon.pl b/scripts/start-stop-daemon.pl new file mode 100755 index 00000000..ecfeb896 --- /dev/null +++ b/scripts/start-stop-daemon.pl @@ -0,0 +1,160 @@ +#!/usr/bin/perl -- + +$version= '0.93.30'; # This line modified by Makefile +sub usageversion { + print(STDERR < --pidfile + --quiet|--verbose --user | --name + --signal --startas + -- <... all of the rest are arguments to daemon ...> + Be careful - try not to call without --exec. \`start-stop-daemon --stop' + would send a SIGTERM to every process, if it weren't specially prevented. + +Exit status: 0 = done 1 = nothing done (=> 0 if --oknodo) 2 = trouble +END + || &quit("failed to write usage: $!"); +} +sub quit { print STDERR "start-stop-daemon: @_\n"; exit(2); } +sub badusage { print STDERR "start-stop-daemon: @_\n\n"; &usageversion; exit(2); } + +$exitnodo= 1; +$quietmode= 0; +$signal= 15; +undef $operation; +undef $exec; +undef $pidfile; +undef $user; +undef $name; +undef $startas; + +while (@ARGV) { + $_= shift(@ARGV); + last if m/^--$/; + if (!m/^--/) { + &quit("unknown argument \`$_'"); + } elsif (m/^--(help|version)$/) { + &usageversion; exit(0); + } elsif (m/^--test$/) { + $testmode= 1; + } elsif (m/^--quiet$/) { + $quietmode= 1; + } elsif (m/^--verbose$/) { + $quietmode= -1; + } elsif (m/^--oknodo$/) { + $exitnodo= 0; + } elsif (m/^--(start|stop)$/) { + $operation= $1; next; + } elsif (m/^--signal$/) { + $_= shift(@ARGV); m/^\d+$/ || &badusage("--signal takes a numeric argument"); + $signal= $_; + } elsif (m/^--(exec|pidfile|name|startas)$/) { + defined($_= shift(@ARGV)) || &badusage("--$1 takes an argument"); + eval("\$$1= \$_"); + } elsif (m/^--user$/) { + defined($_= shift(@ARGV)) || &badusage("--user takes a username argument"); + if (m/^\d+$/) { + $user= $_; + } else { + (@u= getpwnam($_)) || &quit("user \`$_' not found"); + $user= $u[2]; + } + $userspec= $_; + } else { + &badusage("unknown option \`$_'"); + } +} + +defined($operation) || + &badusage("need --start or --stop"); +defined($exec) || defined($pidfile) || defined($user) || + &badusage("need at least one of --exec, --pidfile or --user"); +$startas= $exec if !defined($startas); +$operation ne 'start' || defined($startas) || + &badusage("--start needs --exec or --startas"); + +if (defined($exec)) { + $exec =~ s,^,./, unless $exec =~ m,^[./],; + (@ve= stat("$exec")) || &quit("unable to stat executable \`$exec': $!"); +} + +@found= (); +if (defined($pidfile)) { + $pidfile =~ s,^,./, unless $pidfile =~ m,^[./],; + if (open(PID,"< $pidfile")) { + $pid= ; + &check($1) if $pid =~ m/^\s*(\d+)\s*$/; + close(PID); + } +} else { + opendir(PROC,"/proc") || &quit("failed to opendir /proc: $!"); + $foundany= 0; + while (defined($pid= readdir(PROC))) { + next unless $pid =~ m/^\d+$/; + $foundany++; &check($pid); + } + $foundany || &quit("nothing in /proc - not mounted ?"); +} + +sub check { + local ($p)= @_; + if (defined($exec)) { + return unless @vp= stat("/proc/$p/exe"); + return unless $vp[0] eq $ve[0] && $vp[1] eq $ve[1]; + } + open(C,"/proc/$p/stat"); + (@vs= stat(C)) || return; + if (defined($user)) { + (close(C), return) unless $vs[4] == $user; + } + if (defined($name)) { + $c= ; close(C); + return unless $c =~ m/^$p \(([^\)]*)\) / && $1 eq $name; + } + close(C); + push(@found,$p); +} + +if ($operation eq 'start') { + if (@found) { + print "$exec already running.\n" unless $quietmode>0; + exit($exitnodo); + } + if ($testmode) { + print "would start $startas @ARGV.\n"; + exit(0); + } + print "starting $exec ...\n" if $quietmode<0; + exec($startas,@ARGV); + &quit("unable to start $exec: $!"); +} + +$what= defined($name) ? $name : + defined($exec) ? $exec : + defined($pidfile) ? "process in pidfile \`$pidfile'" : + defined($user) ? "process(es) owned by \`$userspec'" : + &quit("internal error, this is a bug - please report:". + " no name,exec,pidfile,user"); + +if (!@found) { + print "no $what found; none killed.\n" unless $quietmode>0; + exit($exitnodo); +} + +for $pid (@found) { + if ($testmode) { + print "would send signal $signal to $pid.\n"; + } else { + if (kill($signal,$pid)) { + push(@killed,$pid); + } else { + print "start-stop-daemon: warning: failed to kill $pid: $!\n"; # + } + } +} +print "stopped $what (pid @killed).\n" if $quietmode<0; +exit(0); diff --git a/scripts/update-alternatives.8 b/scripts/update-alternatives.8 new file mode 100644 index 00000000..d6719afd --- /dev/null +++ b/scripts/update-alternatives.8 @@ -0,0 +1,19 @@ +.\" Hey, Emacs! This is an -*- nroff -*- source file. +.TH UPDATE\-ALTERNATIVES 8 "29th November 1995" "Debian Project" "Debian GNU/Linux" +.SH NAME +update\-alternatives \- Debian package installation tool +.SH DESCRIPTION +.B update\-alternatives +does not have a useful man page. Please do not report this as a bug, +as this has already been done many times. + +Instead, if you are a competent and accurate writer and are willing to +spend the time reading the source code and writing good manpages +please write a better man page than this one. + +Type +.B update\-alternatives \-\-help +for a brief summary of how to use dpkg. + +.SH AUTHOR +Ian Jackson . diff --git a/scripts/update-alternatives.pl b/scripts/update-alternatives.pl new file mode 100755 index 00000000..a961253d --- /dev/null +++ b/scripts/update-alternatives.pl @@ -0,0 +1,429 @@ +#!/usr/bin/perl -- + +#use POSIX; &ENOENT; +sub ENOENT { 2; } +# Sorry about this, but POSIX.pm isn't necessarily available + +$version= '0.93.80'; # This line modified by Makefile +sub usageversion { + print(STDERR < + [--slave ] ... + update-alternatives --remove + update-alternatives --auto + update-alternatives --display + is the name in /etc/alternatives. + is the name referred to. + is the link pointing to /etc/alternatives/. + is an integer; options with higher numbers are chosen. + +Options: --verbose|--quiet --test --help --version + --altdir --admindir +END + || &quit("failed to write usage: $!"); +} +sub quit { print STDERR "update-alternatives: @_\n"; exit(2); } +sub badusage { print STDERR "update-alternatives: @_\n\n"; &usageversion; exit(2); } + +$altdir= '/etc/alternatives'; +$admindir= '/var/lib/dpkg/alternatives'; +$testmode= 0; +$verbosemode= 0; +$mode=''; +$manual= 'auto'; +$|=1; + +sub checkmanymodes { + return unless $mode; + &badusage("two modes specified: $_ and --$mode"); +} + +while (@ARGV) { + $_= shift(@ARGV); + last if m/^--$/; + if (!m/^--/) { + &quit("unknown argument \`$_'"); + } elsif (m/^--(help|version)$/) { + &usageversion; exit(0); + } elsif (m/^--test$/) { + $testmode= 1; + } elsif (m/^--verbose$/) { + $verbosemode= +1; + } elsif (m/^--quiet$/) { + $verbosemode= -1; + } elsif (m/^--install$/) { + &checkmanymodes; + @ARGV >= 4 || &badusage("--install needs "); + ($alink,$name,$apath,$apriority,@ARGV) = @ARGV; + $apriority =~ m/^[-+]?\d+/ || &badusage("priority must be an integer"); + $mode= 'install'; + } elsif (m/^--remove$/) { + &checkmanymodes; + @ARGV >= 2 || &badusage("--remove needs "); + ($name,$apath,@ARGV) = @ARGV; + $mode= 'remove'; + } elsif (m/^--(display|auto)$/) { + &checkmanymodes; + @ARGV || &badusage("--$1 needs "); + $mode= $1; + $name= shift(@ARGV); + } elsif (m/^--slave$/) { + @ARGV >= 3 || &badusage("--slave needs "); + ($slink,$sname,$spath,@ARGV) = @ARGV; + defined($aslavelink{$sname}) && &badusage("slave name $sname duplicated"); + $aslavelinkcount{$slink}++ && &badusage("slave link $slink duplicated"); + $aslavelink{$sname}= $slink; + $aslavepath{$sname}= $spath; + } elsif (m/^--altdir$/) { + @ARGV && &badusage("--altdir needs a argument"); + $altdir= shift(@ARGV); + } elsif (m/^--admindir$/) { + @ARGV && &badusage("--admindir needs a argument"); + $admindir= shift(@ARGV); + } else { + &badusage("unknown option \`$_'"); + } +} + +defined($aslavelink{$name}) && &badusage("name $name is both primary and slave"); +$aslavelinkcount{$alink} && &badusage("link $link is both primary and slave"); + +$mode || &badusage("need --display, --install, --remove or --auto"); +$mode eq 'install' || !%slavelink || &badusage("--slave only allowed with --install"); + +if (open(AF,"$admindir/$name")) { + $manual= &gl("manflag"); + $manual eq 'auto' || $manual eq 'manual' || &badfmt("manflag"); + $link= &gl("link"); + while (($sname= &gl("sname")) ne '') { + push(@slavenames,$sname); + defined($slavenum{$sname}) && &badfmt("duplicate slave $tsname"); + $slavenum{$sname}= $#slavenames; + $slink= &gl("slink"); + $slink eq $link && &badfmt("slave link same as main link $link"); + $slavelinkcount{$slink}++ && &badfmt("duplicate slave link $slink"); + push(@slavelinks,$slink); + } + while (($version= &gl("version")) ne '') { + defined($versionnum{$version}) && &badfmt("duplicate path $tver"); + push(@versions,$version); + $versionnum{$version}= $i= $#versions; + $priority= &gl("priority"); + $priority =~ m/^[-+]?\d+$/ || &badfmt("priority $version $priority"); + $priorities[$i]= $priority; + for ($j=0; $j<=$#slavenames; $j++) { + $slavepath{$i,$j}= &gl("spath"); + } + } + close(AF); + $dataread=1; +} elsif ($! != &ENOENT) { + &quit("failed to open $admindir/$name: $!"); +} + +if ($mode eq 'display') { + if (!$dataread) { + &pr("No alternatives for $name."); + } else { + &pr("$name - status is $manual."); + if (defined($linkname= readlink("$altdir/$name"))) { + &pr(" link currently points to $linkname"); + } elsif ($! == &ENOENT) { + &pr(" link currently absent"); + } else { + &pr(" link unreadable - $!"); + } + $best= ''; + for ($i=0; $i<=$#versions; $i++) { + if ($best eq '' || $priorities[$i] > $bestpri) { + $best= $versions[$i]; $bestpri= $priorities[$i]; + } + &pr("$versions[$i] - priority $priorities[$i]"); + for ($j=0; $j<=$#slavenames; $j++) { + next unless length($tspath= $slavepath{$i,$j}); + &pr(" slave $slavenames[$j]: $tspath"); + } + } + if ($best eq '') { + &pr("No versions available."); + } else { + &pr("Current \`best' version is $best."); + } + } + exit 0; +} + +$best= ''; +for ($i=0; $i<=$#versions; $i++) { + if ($best eq '' || $priorities[$i] > $bestpri) { + $best= $versions[$i]; $bestpri= $priorities[$i]; + } +} + +if (defined($linkname= readlink("$altdir/$name"))) { + if ($linkname eq $best) { + $state= 'expected'; + } elsif (defined($linkname2= readlink("$altdir/$name.dpkg-tmp"))) { + $state= 'expected-inprogress'; + } else { + $state= 'unexpected'; + } +} elsif ($! == &ENOENT) { + $state= 'nonexistent'; +} else { + $state= 'unexpected'; +} + +# Possible values for: +# $manual manual, auto +# $state expected, expected-inprogress, unexpected, nonexistent +# $mode auto, install, remove +# all independent + +if ($mode eq 'auto') { + &pr("Setting up automatic selection of $name."); + unlink("$altdir/$name.dpkg-tmp") || $! == &ENOENT || + &quit("unable to remove $altdir/$name.dpkg-tmp: $!"); + unlink("$altdir/$name") || $! == &ENOENT || + &quit("unable to remove $altdir/$name.dpkg-tmp: $!"); + $state= 'nonexistent'; + $manual= 'auto'; +} elsif ($state eq 'nonexistent') { + if ($mode eq 'manual') { + &pr("$altdir/$name has been deleted, returning to automatic selection."); + $mode= 'auto'; + } +} + +# $manual manual, auto +# $state expected, expected-inprogress, unexpected, nonexistent +# $mode auto, install, remove +# mode=auto <=> state=nonexistent + +if ($state eq 'unexpected' && $manual eq 'auto') { + &pr("$altdir/$name has been changed (manually or by a script).\n". + "Switching to manual updates only."); + $manual= 'manual'; +} + +# $manual manual, auto +# $state expected, expected-inprogress, unexpected, nonexistent +# $mode auto, install, remove +# mode=auto <=> state=nonexistent +# state=unexpected => manual=manual + +&pr("Checking available versions of $name, updating links in $altdir ...\n". + "(You may modify the symlinks there yourself if desired - see \`man ln'.)"); + +if ($mode eq 'install') { + if ($link ne $alink && $link ne '') { + &pr("Renaming $name link from $link to $alink."); + rename($link,$alink) || $! == &ENOENT || + &quit("unable to rename $link to $alink: $!"); + } + $link= $alink; + if (!defined($i= $versionnum{$apath})) { + push(@versions,$apath); + $versionnum{$apath}= $i= $#versions; + } + $priorities[$i]= $apriority; + for $sname (keys %aslavelink) { + if (!defined($j= $slavenum{$sname})) { + push(@slavenames,$sname); + $slavenum{$sname}= $j= $#slavenames; + } + $oldslavelink= $slavelinks[$j]; + $newslavelink= $aslavelink{$sname}; + $slavelinkcount{$oldslavelink}-- if $oldslavelink ne ''; + $slavelinkcount{$newslavelink}++ && + &quit("slave link name $newslavelink duplicated"); + if ($newslavelink ne $oldslavelink && $oldslavelink ne '') { + &pr("Renaming $sname slave link from $oldslavelink to $newslavelink."); + rename($oldslavelink,$newslavelink) || $! == &ENOENT || + &quit("unable to rename $oldslavelink to $newslavelink: $!"); + } + $slavelinks[$j]= $newslavelink; + } + for ($j=0; $j<=$#slavenames; $j++) { + $slavepath{$i,$j}= $aslavepath{$slavenames[$j]}; + } +} + +if ($mode eq 'remove') { + if (defined($i= $versionnum{$apath})) { + $k= $#versions; + $versionnum{$versions[$k]}= $i; + delete $versionnum{$versions[$i]}; + $versions[$i]= $versions[$k]; $#versions--; + $priorities[$i]= $priorities[$k]; $#priorities--; + for ($j=0; $j<=$#slavenames; $j++) { + $slavepath{$i,$j}= $slavepath{$k,$j}; + delete $slavepath{$k,$j}; + } + } else { + &pr("Alternative $apath for $name not registered, not removing."); + } +} + +for ($j=0; $j<=$#slavenames; $j++) { + for ($i=0; $i<=$#versions; $i++) { + last if $slavepath{$i,$j} ne ''; + } + if ($i > $#versions) { + &pr("Discarding obsolete slave link $slavenames[$j] ($slavelinks[$j])."); + unlink("$altdir/$slavenames[$j]") || $! == &ENOENT || + &quit("unable to remove $slavenames[$j]: $!"); + unlink($slavelinks[$j]) || $! == &ENOENT || + &quit("unable to remove $slavelinks[$j]: $!"); + $k= $#slavenames; + $slavenum{$slavenames[$k]}= $j; + delete $slavenum{$slavenames[$j]}; + $slavelinkcount{$slavelinks[$j]}--; + $slavenames[$j]= $slavenames[$k]; $#slavenames--; + $slavelinks[$j]= $slavelinks[$k]; $#slavelinks--; + for ($i=0; $i<=$#versions; $i++) { + $slavepath{$i,$j}= $slavepath{$i,$k}; + delete $slavepath{$i,$k}; + } + $j--; + } +} + +if ($manual eq 'manual') { + &pr("Automatic updates of $altdir/$name are disabled, leaving it alone."); + &pr("To return to automatic updates use \`update-alternatives --auto $name'."); +} else { + if ($state eq 'expected-inprogress') { + &pr("Recovering from previous failed update of $name ..."); + rename("$altdir/$name.dpkg-tmp","$altdir/$name") || + &quit("unable to rename $altdir/$name.dpkg-tmp to $altdir/$name: $!"); + $state= 'expected'; + } +} + +# $manual manual, auto +# $state expected, expected-inprogress, unexpected, nonexistent +# $mode auto, install, remove +# mode=auto <=> state=nonexistent +# state=unexpected => manual=manual +# manual=auto => state!=expected-inprogress && state!=unexpected + +open(AF,">$admindir/$name.dpkg-new") || + &quit("unable to open $admindir/$name.dpkg-new for write: $!"); +&paf($manual); +&paf($link); +for ($j=0; $j<=$#slavenames; $j++) { + &paf($slavenames[$j]); + &paf($slavelinks[$j]); +} +&paf(''); +$best= ''; +for ($i=0; $i<=$#versions; $i++) { + if ($best eq '' || $priorities[$i] > $bestpri) { + $best= $versions[$i]; $bestpri= $priorities[$i]; $bestnum= $i; + } + &paf($versions[$i]); + &paf($priorities[$i]); + for ($j=0; $j<=$#slavenames; $j++) { + &paf($slavepath{$i,$j}); + } +} +&paf(''); +close(AF) || &quit("unable to close $admindir/$name.dpkg-new: $!"); + +if ($manual eq 'auto') { + if ($best eq '') { + &pr("Last package providing $name ($link) removed, deleting it."); + unlink("$altdir/$name") || $! == &ENOENT || + &quit("unable to remove $altdir/$name: $!"); + unlink("$link") || $! == &ENOENT || + &quit("unable to remove $altdir/$name: $!"); + unlink("$admindir/$name.dpkg-new") || + &quit("unable to remove $admindir/$name.dpkg-new: $!"); + unlink("$admindir/$name") || $! == &ENOENT || + &quit("unable to remove $admindir/$name: $!"); + exit(0); + } else { + if (!defined($linkname= readlink($link)) && $! != &ENOENT) { + &pr("warning: $link is supposed to be a symlink to $altdir/$name\n". + " (or nonexistent); however, readlink failed: $!"); + } elsif ($linkname ne "$altdir/$name") { + unlink("$link.dpkg-tmp") || $! == &ENOENT || + &quit("unable to ensure $link.dpkg-tmp nonexistent: $!"); + symlink("$altdir/$name","$link.dpkg-tmp") || + &quit("unable to make $link.dpkg-tmp a symlink to $altdir/$name: $!"); + rename("$link.dpkg-tmp",$link) || + &quit("unable to install $link.dpkg-tmp as $link: $!"); + } + if (defined($linkname= readlink("$altdir/$name")) && $linkname eq $best) { + &pr("Leaving $name ($link) pointing to $best."); + } else { + &pr("Updating $name ($link) to point to $best."); + } + unlink("$altdir/$name.dpkg-tmp") || $! == &ENOENT || + &quit("unable to ensure $altdir/$name.dpkg-tmp nonexistent: $!"); + symlink($best,"$altdir/$name.dpkg-tmp"); + } +} + +rename("$admindir/$name.dpkg-new","$admindir/$name") || + &quit("unable to rename $admindir/$name.dpkg-new to $admindir/$name: $!"); + +if ($manual eq 'auto') { + rename("$altdir/$name.dpkg-tmp","$altdir/$name") || + &quit("unable to install $altdir/$name.dpkg-tmp as $altdir/$name"); + for ($j=0; $j<=$#slavenames; $j++) { + $sname= $slavenames[$j]; + $slink= $slavelinks[$j]; + if (!defined($linkname= readlink($slink)) && $! != &ENOENT) { + &pr("warning: $slink is supposed to be a slave symlink to\n". + " $altdir/$sname, or nonexistent; however, readlink failed: $!"); + } elsif ($linkname ne "$altdir/$sname") { + unlink("$slink.dpkg-tmp") || $! == &ENOENT || + &quit("unable to ensure $slink.dpkg-tmp nonexistent: $!"); + symlink("$altdir/$sname","$slink.dpkg-tmp") || + &quit("unable to make $slink.dpkg-tmp a symlink to $altdir/$sname: $!"); + rename("$slink.dpkg-tmp",$slink) || + &quit("unable to install $slink.dpkg-tmp as $slink: $!"); + } + $spath= $slavepath{$bestnum,$j}; + unlink("$altdir/$sname.dpkg-tmp") || $! == &ENOENT || + &quit("unable to ensure $altdir/$sname.dpkg-tmp nonexistent: $!"); + if ($spath eq '') { + &pr("Removing $sname ($slink), not appropriate with $best."); + unlink("$altdir/$sname") || $! == &ENOENT || + &quit("unable to remove $altdir/$sname: $!"); + } else { + if (defined($linkname= readlink("$altdir/$sname")) && $linkname eq $spath) { + &pr("Leaving $sname ($slink) pointing to $spath."); + } else { + &pr("Updating $sname ($slink) to point to $spath."); + } + symlink("$spath","$altdir/$sname.dpkg-tmp") || + &quit("unable to make $altdir/$sname.dpkg-tmp a symlink to $spath: $!"); + rename("$altdir/$sname.dpkg-tmp","$altdir/$sname") || + &quit("unable to install $altdir/$sname.dpkg-tmp as $altdir/$sname: $!"); + } + } +} + +sub pr { print(STDOUT "@_\n") || &quit("error writing stdout: $!"); } +sub paf { + $_[0] =~ m/\n/ && &quit("newlines prohibited in update-alternatives files ($_[0])"); + print(AF "$_[0]\n") || &quit("error writing stdout: $!"); +} +sub gl { + $!=0; $_= ; + length($_) || &quit("error or eof reading $admindir/$name for $_[0] ($!)"); + s/\n$// || &badfmt("missing newline after $_[0]"); + $_; +} +sub badfmt { + &quit("internal error: $admindir/$name corrupt: $_[0]"); +} + +exit(0); diff --git a/scripts/update-rc.d.8 b/scripts/update-rc.d.8 new file mode 100644 index 00000000..3ed3d904 --- /dev/null +++ b/scripts/update-rc.d.8 @@ -0,0 +1,79 @@ +.\" Hey, Emacs! This is an -*- nroff -*- source file. +.TH UPDATE\-RC.D 8 "29th November 1995" "Debian Project" "Debian/GNU Linux" +.SH NAME +update\-rc.d \- install and remove System-V style init script links +.SH SYNOPSIS +.B update\-rc.d +.I +\&remove +.LP +.B update-rc.d +.I +\&defaults +.RI [ " " | " " " " " " ] +.LP +.B update-rc.d +.I +\&start | stop +.I +.RI [ " " [ " " [ ... ]]] +\&. +.SH DESCRIPTION +This manual page explains the Debian +.B "update-rc.d" +System-V init script link utility. It should be used when installing and +removing init scripts on a Debian system. + +.SH REMOVING SCRIPTS +When invoked with the +.I remove +option, update-rc.d removes the script and links to the script for the package +.RI "" . +It first finds and removes the script in +.B /etc/init.d/ +and then removes all links to the script in +.RB "" /etc/rc[0123456].d/ . + +.SH INSTALLING SCRIPTS +When run with either the +.RI "" defaults ", " start ", or " stop +options, update-rc.d makes links pointing to the script in +.RB "" /etc/init.d/ . +The script must be installed before update-rc.d is run. +The +.I +arguments specify the order in which the script will be executed. +When +.B init +changes runlevels it executes the scripts in the order of their +.I codenumber +from lowest to highest. + +The +.I +arguments specify the runlevels that the script will be run in. +As many as seven runlevels (0-6) may be specified. +The last runlevel must be followed by a period. + +When invoked with the +.I defaults +option the start runlevels are +.B 2 3 4 5 +and the stop runlevels are +.RB "" "0 1 2 3 4 5 6" . +If neither +.I +or +.I +and +.I +are specified, then the stop and start codenumbers default to 20. + +.SH FILES +.B /etc/init.d/ +.bl +.B /etc/rc[0123456].d/ + +.SH "SEE ALSO" +.BR init (1), +.BR inittab (1), diff --git a/scripts/update-rc.d.sh b/scripts/update-rc.d.sh new file mode 100755 index 00000000..d471cc17 --- /dev/null +++ b/scripts/update-rc.d.sh @@ -0,0 +1,104 @@ +#!/bin/sh +# +# Usage: +# update-rc.d remove +# update-rc.d [options] +# Options are: +# start . +# stop . +# defaults [ | ] +# (means start 2 3 4 5 +# as well as stop 0 1 2 3 4 5 6 +# defaults to 20) + +set -e +cd /etc + +initd='init.d' + +usage () { echo >&2 "\ +update-rc.d: error: $1. +usage: update-rc.d remove + update-rc.d defaults [ | ] + update-rc.d start|stop . ..."; exit 1 } + +getinode () { + inode="`ls -Li1 \"$1\" | sed -e 's/^ *//; s/ .*//'`" +} + +if [ $# -lt 2 ]; then usage "too few arguments"; fi +bn="$1"; shift + +if [ xremove = "x$1" ]; then + if [ $# != 1 ]; then usage "remove must be only action"; fi + if [ -f "$initd/$bn" ]; then + echo >&2 "update-rc.d: error: /etc/$initd/$bn exists during rc.d purge." + exit 1 + fi + echo " Removing any system startup links to /etc/$initd/$bn ..." + trap 'rm -f "$initd/$bn"' 0 + touch "$initd/$bn" + getinode "$initd/$bn" + own="$inode" + for f in rc?.d/[SK]*; do + getinode "$f" + if [ "x$inode" = "x$own" ]; then + rm "$f"; + echo " $f" + fi + done + exit 0 +elif [ xdefaults = "x$1" ]; then + if [ $# = 1 ]; then sn=20; kn=20; + elif [ $# = 2 ]; then sn="$2"; kn="$2"; + elif [ $# = 3 ]; then sn="$2"; kn="$3"; + else usage "defaults takes only one or two codenumbers"; fi + set start "$sn" 2 3 4 5 . stop "$kn" 0 1 6 . +elif ! [ xstart = "x$1" -o xstop = "x$1" ]; then + usage "unknown mode or add action $1" +fi + +if ! [ -f "$initd/$bn" ]; then + echo >&2 "update-rc.d: warning /etc/$initd/$bn doesn't exist during rc.d setup." + exit 0 +fi + +getinode "$initd/$bn" +own="$inode" +for f in rc?.d/[SK]*; do + getinode "$f" + if [ "x$inode" = "x$own" ]; then + echo " System startup links pointing to /etc/$initd/$bn already exist." + exit 0 + fi +done + +echo " Adding system startup links pointing to /etc/$initd/$bn ..." +while [ $# -ge 3 ]; do + if [ xstart = "x$1" ]; then ks=S + elif [ xstop = "x$1" ]; then ks=K + else usage "unknown action $1"; fi + number="$2" + shift; shift + while [ $# -ge 1 ]; do + case "$1" in + .) + break + ;; + ?) + ln -s "../$initd/$bn" "rc$1.d/$ks$number$bn" + echo " rc$1.d/$ks$number$bn -> ../$initd/$bn" + shift + continue + ;; + esac + usage 'runlevel is more than one character (forgotten . ?)' + done + shift +done + +if [ $# != 0 ]; then + usage "surplus arguments, but not enough for an add action: $*" +fi + +exit 0 diff --git a/split/Makefile.in b/split/Makefile.in new file mode 100644 index 00000000..8cdeaf0a --- /dev/null +++ b/split/Makefile.in @@ -0,0 +1,80 @@ +# Copyright (C) 1994 Ian Murdock +# Copyright (C) 1994,1995 Ian Jackson +# +# This 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, +# or (at your option) any later version. +# +# This 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 dpkg; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +srcdir = @srcdir@ +VPATH = @srcdir@ + +prefix = @prefix@ +exec_prefix = $(prefix) +bindir = $(exec_prefix)/bin +libdir = $(prefix)/lib +dpkglibdir = $(libdir)/dpkg +datadir = /var/lib/dpkg +partsdir = $(datadir)/parts +mandir = $(prefix)/man +man8dir = $(mandir)/man8 +man8 = 8 +perlpath = @perlpath@ + +SRC = main.c split.c info.c queue.c join.c +OBJ = main.o split.o info.o queue.o join.o + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ + +CC = @CC@ + +CFLAGS = @CFLAGS@ @CWARNS@ -g $(XCFLAGS) +LDFLAGS = $(XLDFLAGS) +LIBS = -L../lib -ldpkg $(XLIBS) +ALL_CFLAGS = -I../include -I.. @DEFS@ $(CFLAGS) + +.SUFFIXES: .c .o .pl + +.c.o: + $(CC) $(ALL_CFLAGS) -c $< + +.pl: + sed <$@.pl 's:^#!/usr/bin/perl:#!$(perlpath):' \ + | ../insert-version.pl >$@.new + chmod +x $@.new + mv $@.new $@ + +all: dpkg-split mksplit + +dpkg-split: $(OBJ) ../lib/libdpkg.a + $(CC) $(LDFLAGS) -o dpkg-split $(OBJ) $(LIBS) + +split.o: split.c + $(CC) -DMKSPLITSCRIPT=\"$(dpkglibdir)/mksplit\" $(ALL_CFLAGS) -c $< + +$(OBJ): dpkg-split.h ../config.h ../include/dpkg.h +build.o split.o queue.o join.o main.o: ../include/dpkg-db.h +info.o extract.o main.o: ../include/myopt.h +main.o: ../version.h + +clean: + rm -f *.o core dpkg-split + +distclean: clean + rm -f Makefile *.orig *~ *.~* ./#*# + +install: all + $(INSTALL_PROGRAM) -s dpkg-split $(bindir)/dpkg-split + $(INSTALL_PROGRAM) mksplit $(dpkglibdir)/mksplit +# $(INSTALL_DATA) dpkg-split.8 $(man8dir)/dpkg-split.$(man8) diff --git a/split/debugmake b/split/debugmake new file mode 100755 index 00000000..53497bcd --- /dev/null +++ b/split/debugmake @@ -0,0 +1,3 @@ +#!/bin/sh +set -x +make 'XCFLAGS=-g -O0' LDFLAGS=-g 'LIBS= -lefence -L../lib -ldpkg' "$@" diff --git a/split/dpkg-split.8 b/split/dpkg-split.8 new file mode 100644 index 00000000..399868d4 --- /dev/null +++ b/split/dpkg-split.8 @@ -0,0 +1,19 @@ +.\" Hey, Emacs! This is an -*- nroff -*- source file. +.TH DPKG\-SPLIT 8 "29th November 1995" "Debian Project" "Debian GNU/Linux" +.SH NAME +dpkg\-split \- Debian multipart package manipulation tool +.SH DESCRIPTION +.B dpkg\-split +does not have a useful man page. Please do not report this as a bug, +as this has already been done many times. + +Instead, if you are a competent and accurate writer and are willing to +spend the time reading the source code and writing good manpages +please write a better man page than this one. + +Type +.B dpkg\-split \-\-help +for a brief summary of how to use dpkg. + +.SH AUTHOR +Ian Jackson . diff --git a/split/dpkg-split.h b/split/dpkg-split.h new file mode 100644 index 00000000..7caee533 --- /dev/null +++ b/split/dpkg-split.h @@ -0,0 +1,72 @@ +/* + * dpkg-split - splitting and joining of multipart *.deb archives + * dpkg-split.h - external definitions for this program + * + * Copyright (C) 1995 Ian Jackson + * + * This 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, + * or (at your option) any later version. + * + * This 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 dpkg; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef DPKG_SPLIT_H +#define DPKG_SPLIT_H + +typedef void dofunction(const char *const *argv); +dofunction do_split, do_join, do_info, do_auto, do_queue, do_discard; + +struct partinfo { + const char *filename; + const char *fmtversion; + const char *package; + const char *version; + const char *md5sum; + unsigned long orglength; + int thispartn, maxpartn; + unsigned long maxpartlen; + unsigned long thispartoffset; + unsigned long thispartlen; + unsigned long headerlen; /* size of header in part file */ + unsigned long filesize; +}; + +struct partqueue { + struct partqueue *nextinqueue; + struct partinfo info; + /* only fields filename, md5sum, maxpartlen, thispartn, maxpartn + * are valid; the rest are null. If the file is not named correctly + * to be a part file md5sum is null too and the numbers are zero. + */ +}; + +extern dofunction *action; +extern const struct cmdinfo *cipaction; +extern long maxpartsize; +extern const char *depotdir, *outputfile; +extern struct partqueue *queue; +extern int npquiet, msdos; + +void rerr(const char *fn) NONRETURNING; +void rerreof(FILE *f, const char *fn) NONRETURNING; +void print_info(const struct partinfo *pi); +struct partinfo *read_info(FILE *partfile, const char *fn, struct partinfo *ir); + +void scandepot(void); +void reassemble(struct partinfo **partlist, const char *outputfile); +void mustgetpartinfo(const char *filename, struct partinfo *ri); +void addtopartlist(struct partinfo**, struct partinfo*, struct partinfo *refi); + +#define PARTMAGIC "!\ndebian-split " +#define HEADERALLOWANCE 1024 + +#endif /* DPKG_SPLIT_H */ diff --git a/split/info.c b/split/info.c new file mode 100644 index 00000000..fcc36d2b --- /dev/null +++ b/split/info.c @@ -0,0 +1,230 @@ +/* + * dpkg-split - splitting and joining of multipart *.deb archives + * info.c - information about split archives + * + * Copyright (C) 1995 Ian Jackson + * + * This 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, + * or (at your option) any later version. + * + * This 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 dpkg; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "dpkg.h" +#include "dpkg-db.h" +#include "dpkg-split.h" + +static unsigned long unsignedlong(const char *value, const char *fn, const char *what) { + unsigned long r; + char *endp; + + r= strtoul(value,&endp,10); + if (*endp) + ohshit("file `%.250s' is corrupt - bad digit (code %d) in %s",fn,*endp,what); + return r; +} + +static unsigned long parseheaderlength(const char *inh, size_t len, + const char *fn, const char *what) { + char lintbuf[15]; + + if (memchr(inh,0,len)) + ohshit("file `%.250s' is corrupt - %.250s length contains nulls",fn,what); + assert(sizeof(lintbuf) > len); + memcpy(lintbuf,inh,len); + lintbuf[len]= ' '; + *strchr(lintbuf,' ')= 0; + return unsignedlong(lintbuf,fn,what); +} + +static char *nextline(char **ripp, const char *fn, const char *what) { + char *newline, *rip; + + rip= *ripp; + if (!rip) ohshit("file `%.250s' is corrupt - %.250s missing",fn,what); + newline= strchr(rip,'\n'); + if (!newline) + ohshit("file `%.250s' is corrupt - missing newline after %.250s",fn,what); + *ripp= newline+1; + while (newline > rip && isspace(newline[-1])) newline--; + *newline= 0; + return rip; +} + +struct partinfo *read_info(FILE *partfile, const char *fn, struct partinfo *ir) { + /* returns info (nfmalloc'd) if was an archive part and we read it, 0 if it wasn't */ + static char *readinfobuf= 0; + static int readinfobuflen= 0; + + unsigned long thisilen, templong; + char magicbuf[sizeof(PARTMAGIC)-1], *rip, *partnums, *slash; + struct ar_hdr arh; + int c; + struct stat stab; + + if (fread(magicbuf,1,sizeof(PARTMAGIC)-1,partfile) != sizeof(PARTMAGIC)-1) + if (ferror(partfile)) rerr(fn); else return 0; + if (memcmp(magicbuf,PARTMAGIC,sizeof(magicbuf))) return 0; + if (fseek(partfile,-sizeof(arh.ar_name),SEEK_CUR)) + ohshite("unable to seek back"); + + if (fread(&arh,1,sizeof(arh),partfile) != sizeof(arh)) rerreof(partfile,fn); + if (memcmp(arh.ar_fmag,ARFMAG,sizeof(arh.ar_fmag))) + ohshit("file `%.250s' is corrupt - bad magic at end of first header",fn); + thisilen= parseheaderlength(arh.ar_size,sizeof(arh.ar_size),fn,"info length"); + if (thisilen >= readinfobuflen) { + readinfobuflen= thisilen+1; + readinfobuf= m_realloc(readinfobuf,readinfobuflen); + } + if (fread(readinfobuf,1,thisilen,partfile) != thisilen) rerreof(partfile,fn); + if (thisilen & 1) { + c= getc(partfile); if (c==EOF) rerreof(partfile,fn); + if (c != '\n') + ohshit("file `%.250s' is corrupt - bad padding character (code %d)",fn,c); + } + readinfobuf[thisilen]= 0; + if (memchr(readinfobuf,0,thisilen)) + ohshit("file `%.250s' is corrupt - nulls in info section",fn); + + ir->filename= fn; + + rip= readinfobuf; + ir->fmtversion= nfstrsave(nextline(&rip,fn,"format version number")); + if (strcmp(ir->fmtversion,SPLITVERSION)) + ohshit("file `%.250s' is format version `%.250s' - you need a newer " SPLITTER, + fn,ir->fmtversion); + + ir->package= nfstrsave(nextline(&rip,fn,"package name")); + ir->version= nfstrsave(nextline(&rip,fn,"package version number")); + ir->md5sum= nfstrsave(nextline(&rip,fn,"package file MD5 checksum")); + if (strlen(ir->md5sum) != 32 || + strspn(ir->md5sum,"0123456789abcdef") != 32) + ohshit("file `%.250s' is corrupt - bad MD5 checksum `%.250s'",fn,ir->md5sum); + + ir->orglength= unsignedlong(nextline(&rip,fn,"total length"),fn,"total length"); + ir->maxpartlen= unsignedlong(nextline(&rip,fn,"part offset"),fn,"part offset"); + + partnums= nextline(&rip,fn,"part numbers"); + slash= strchr(partnums,'/'); + if (!slash) ohshit("file `%.250s' is corrupt - no slash between part numbers",fn); + *slash++= 0; + + templong= unsignedlong(slash,fn,"number of parts"); + if (templong <= 0 || templong > INT_MAX) + ohshit("file `%.250s' is corrupt - bad number of parts",fn); + ir->maxpartn= templong; + templong= unsignedlong(partnums,fn,"parts number"); + if (templong <= 0 || templong > ir->maxpartn) + ohshit("file `%.250s' is corrupt - bad part number",fn); + ir->thispartn= templong; + + if (fread(&arh,1,sizeof(arh),partfile) != sizeof(arh)) rerreof(partfile,fn); + if (memcmp(arh.ar_fmag,ARFMAG,sizeof(arh.ar_fmag))) + ohshit("file `%.250s' is corrupt - bad magic at end of second header",fn); + if (strncmp(arh.ar_name,"data",4)) + ohshit("file `%.250s' is corrupt - second member is not data member",fn); + + ir->thispartlen= parseheaderlength(arh.ar_size,sizeof(arh.ar_size),fn,"data length"); + ir->thispartoffset= (ir->thispartn-1)*ir->maxpartlen; + + if (ir->maxpartn != (ir->orglength+ir->maxpartlen-1)/ir->maxpartlen) + ohshit("file `%.250s' is corrupt - wrong number of parts for quoted sizes",fn); + if (ir->thispartlen != + (ir->thispartn == ir->maxpartn + ? ir->orglength - ir->thispartoffset : ir->maxpartlen)) + ohshit("file `%.250s' is corrupt - size is wrong for quoted part number",fn); + + ir->filesize= (SARMAG + + sizeof(arh) + thisilen + (thisilen&1) + + sizeof(arh) + ir->thispartlen + (ir->thispartlen&1)); + + if (fstat(fileno(partfile),&stab)) ohshite("unable to fstat part file `%.250s'",fn); + if (S_ISREG(stab.st_mode)) { + /* Don't do this check if it's coming from a pipe or something. It's + * only an extra sanity check anyway. + */ + if (stab.st_size < ir->filesize) + ohshit("file `%.250s' is corrupt - too short",fn); + } + + ir->headerlen= SARMAG + sizeof(arh) + thisilen + (thisilen&1) + sizeof(arh); + + return ir; +} + +void mustgetpartinfo(const char *filename, struct partinfo *ri) { + FILE *part; + + part= fopen(filename,"r"); + if (!part) ohshite("cannot open archive part file `%.250s'",filename); + if (!read_info(part,filename,ri)) + ohshite("file `%.250s' is not an archive part",filename); + fclose(part); +} + +void print_info(const struct partinfo *pi) { + printf("%s:\n" + " Part format version: %s\n" + " Part of package: %s\n" + " ... version: %s\n" + " ... MD5 checksum: %s\n" + " ... length: %lu bytes\n" + " ... split every: %lu bytes\n" + " Part number: %d/%d\n" + " Part length: %lu bytes\n" + " Part offset: %lu bytes\n" + " Part file size (used portion): %lu bytes\n\n", + pi->filename, + pi->fmtversion, + pi->package, + pi->version, + pi->md5sum, + pi->orglength, + pi->maxpartlen, + pi->thispartn, + pi->maxpartn, + pi->thispartlen, + pi->thispartoffset, + pi->filesize); +} + +void do_info(const char *const *argv) { + const char *thisarg; + struct partinfo *pi, ps; + FILE *part; + + if (!*argv) badusage("--info requires one or more part file arguments"); + + while ((thisarg= *argv++)) { + part= fopen(thisarg,"r"); + if (!part) ohshite("cannot open archive part file `%.250s'",thisarg); + pi= read_info(part,thisarg,&ps); + fclose(part); + if (pi) { + print_info(pi); + } else { + printf("file `%s' is not an archive part\n",thisarg); + } + if (ferror(stdout)) werr("stdout"); + } +} diff --git a/split/join.c b/split/join.c new file mode 100644 index 00000000..9e99f643 --- /dev/null +++ b/split/join.c @@ -0,0 +1,134 @@ +/* + * dpkg-split - splitting and joining of multipart *.deb archives + * join.c - joining + * + * Copyright (C) 1995 Ian Jackson + * + * This 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, + * or (at your option) any later version. + * + * This 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 dpkg; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include + +#include "config.h" +#include "dpkg.h" +#include "dpkg-db.h" +#include "dpkg-split.h" + +void reassemble(struct partinfo **partlist, const char *outputfile) { + FILE *output, *input; + void *buffer; + struct partinfo *pi; + int i,nr; + long buffersize; + + printf("Putting package %s together from %d parts: ", + partlist[0]->package,partlist[0]->maxpartn); + + buffersize= partlist[0]->maxpartlen; + for (i=0; imaxpartn; i++) + if (partlist[0]->headerlen > buffersize) buffersize= partlist[0]->headerlen; + buffer= m_malloc(partlist[0]->maxpartlen); + output= fopen(outputfile,"w"); + if (!output) ohshite("unable to open output file `%.250s'",outputfile); + for (i=0; imaxpartn; i++) { + pi= partlist[i]; + input= fopen(pi->filename,"r"); + if (!input) ohshite("unable to (re)open input part file `%.250s'",pi->filename); + assert(pi->headerlen <= buffersize); + nr= fread(buffer,1,pi->headerlen,input); + if (nr != pi->headerlen) rerreof(input,pi->filename); + assert(pi->thispartlen <= buffersize); + printf("%d ",i+1); + nr= fread(buffer,1,pi->thispartlen,input); + if (nr != pi->thispartlen) rerreof(input,pi->filename); + if (pi->thispartlen & 1) + if (getc(input) == EOF) rerreof(input,pi->filename); + if (ferror(input)) rerr(pi->filename); + fclose(input); + nr= fwrite(buffer,1,pi->thispartlen,output); + if (nr != pi->thispartlen) werr(outputfile); + } + if (fclose(output)) werr(outputfile); + printf("done\n"); +} + + +void addtopartlist(struct partinfo **partlist, + struct partinfo *pi, struct partinfo *refi) { + int i; + + if (strcmp(pi->package,refi->package) || + strcmp(pi->version,refi->version) || + strcmp(pi->md5sum,refi->md5sum) || + pi->orglength != refi->orglength || + pi->maxpartn != refi->maxpartn || + pi->maxpartlen != refi->maxpartlen) { + print_info(pi); + print_info(refi); + ohshit("files `%.250s' and `%.250s' are not parts of the same file", + pi->filename,refi->filename); + } + i= pi->thispartn-1; + if (partlist[i]) + ohshit("there are several versions of part %d - at least `%.250s' and `%.250s'", + pi->thispartn, pi->filename, partlist[i]->filename); + partlist[i]= pi; +} + +void do_join(const char *const *argv) { + char *p; + const char *thisarg; + struct partqueue *pq; + struct partinfo *refi, *pi, **partlist; + int i; + + assert(!queue); + if (!*argv) badusage("--join requires one or more part file arguments"); + while ((thisarg= *argv++)) { + pq= nfmalloc(sizeof(struct partqueue)); + + mustgetpartinfo(thisarg,&pq->info); + + + pq->nextinqueue= queue; + queue= pq; + } + refi= 0; + for (pq= queue; pq; pq= pq->nextinqueue) + if (!refi || pq->info.thispartn < refi->thispartn) refi= &pq->info; + assert(refi); + partlist= nfmalloc(sizeof(struct partinfo*)*refi->maxpartn); + for (i=0; imaxpartn; i++) partlist[i]= 0; + for (pq= queue; pq; pq= pq->nextinqueue) { + pi= &pq->info; + addtopartlist(partlist,pi,refi); + } + for (i=0; imaxpartn; i++) { + if (!partlist[i]) ohshit("part %d is missing",i+1); + } + if (!outputfile) { + p= nfmalloc(strlen(refi->package)+1+strlen(refi->version)+sizeof(DEBEXT)); + strcpy(p,refi->package); + strcat(p,"-"); + strcat(p,refi->version); + strcat(p,DEBEXT); + outputfile= p; + } + reassemble(partlist,outputfile); +} diff --git a/split/junk b/split/junk new file mode 100644 index 00000000..d71a8cbb --- /dev/null +++ b/split/junk @@ -0,0 +1,36 @@ + struct partqueue *backinpackage; /* circular doubly linked list of */ + struct partqueue *nextinpackage; /* parts of a particular split file */ + /* in ascending order of part number */ + /* singly linked list of all files in depot */ + pq->nextinpackage= pq->backinpackage= 0; + + for (search= queue; + search && !(!strcmp(pq->info.md5sum,search->info.md5sum) && + pq->info.maxpartlen == search->info.maxpartlen); + search= search->next); + if (search) { + /* insert after search */ + while (!(search == search->nextinpackage || + (search->info.thispartn < search->nextinpackage->info.thispartn ? + (search->info.thispartn <= pq->info.thispartn && + pq->info.thispartn < search->nextinpackage->info.thispartn) : + (search->info.thispartn <= pq->info.thispartn || + search->nextinpackage->info.thispartn > pq->info.thispartn)))) + search= search->nextinpackage; + if (search->info.maxpartn != pq->info.maxpartn) + ohshit("inconsistency in parts depot - " + "md5sum `%s' part length %lu has both %d and %d parts", + pq->info.md5sum, pq->info.maxpartlen, pq->info.maxpartn, + search->info.maxpartn); + if (search->info.thispartn == pq->info.thispartn) + ohshit("inconsistency in parts depot - two instances of " + "md5sum `%s' part length %ld part %d/%d", + pq->info.md5sum, pq->info.maxpartlen, pq->info.thispartn, + pq->info.maxpartn); + pq->nextinpackage= search->nextinpackage; + pq->backinpackage= search; + pq->nextinpackage->backinpackage= pq; + pq->backinpackage->nextinpackage= pq; + } else { + pq->nextinpackage= pq->backinpackage= pq; + } diff --git a/split/magic b/split/magic new file mode 100644 index 00000000..b4819a65 --- /dev/null +++ b/split/magic @@ -0,0 +1,5 @@ +0 string ! archive +>8 string debian-binary - Debian binary package +>8 string debian-split - part of multipart Debian package +>8 string __.SYMDEF random library +>8 string diff --git a/split/main.c b/split/main.c new file mode 100644 index 00000000..08645350 --- /dev/null +++ b/split/main.c @@ -0,0 +1,176 @@ +/* + * dpkg-split - splitting and joining of multipart *.deb archives + * main.c - main program + * + * Copyright (C) 1995 Ian Jackson + * + * This 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, + * or (at your option) any later version. + * + * This 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 dpkg; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include + +#include "config.h" +#include "dpkg.h" +#include "dpkg-db.h" +#include "version.h" +#include "myopt.h" + +#include "dpkg-split.h" + +static void printversion(void) { + if (!fputs + ("Debian GNU/Linux `" SPLITTER "' package archive split/join tool;\n" + "version " DPKG_VERSION_ARCH + ". Copyright (C) 1994,1995 Ian Jackson. This is free\n" + "software; see the GNU General Public Licence version 2 or later for copying\n" + "conditions. There is NO warranty. See dpkg-split --licence for details.\n", + stderr)) werr("stderr"); +} + +static void usage(void) { + if (!fputs("\ +Usage: " SPLITTER " -s|--split [] Split an archive.\n\ + " SPLITTER " -j|--join ... Join parts together.\n\ + " SPLITTER " -I|--info ... Display info about a part.\n\ + " SPLITTER " -h|--help|--version|--licence Show help/version/licence.\n\ +\n\ + " SPLITTER " -a|--auto -o Auto-accumulate parts.\n\ + " SPLITTER " -l|--listq List unmatched pieces.\n\ + " SPLITTER " -d|--discard [ ...] Discard unmatched pieces.\n\ +\n\ +Options: --depotdir (default is /var/lib/dpkg/parts)\n\ + -S|--partsize (in Kb, for -s, default is 450)\n\ + -o|--output (for -j, default is -.deb)\n\ + -Q|--npquiet (be quiet when -a is not a part)\n\ + --msdos (generate 8.3 filenames)\n\ +\n\ +Exit status: 0 = OK; 1 = -a is not a part; 2 = trouble!\n", + stderr)) werr("stderr"); +} + +const char thisname[]= SPLITTER; +const char printforhelp[]= "Type " SPLITTER " --help for help."; + +dofunction *action=0; +const struct cmdinfo *cipaction=0; +long maxpartsize= SPLITPARTDEFMAX; +const char *depotdir= ADMINDIR "/" PARTSDIR, *outputfile= 0; +struct partqueue *queue= 0; +int npquiet= 0, msdos= 0; + +void rerr(const char *fn) { + ohshite("error reading %s",fn); +} + +void rerreof(FILE *f, const char *fn) { + if (ferror(f)) ohshite("error reading %.250s",fn); + ohshit("unexpected end of file in %.250s",fn); +} + +static void helponly(const struct cmdinfo *cip, const char *value) { + usage(); exit(0); +} +static void versiononly(const struct cmdinfo *cip, const char *value) { + printversion(); exit(0); +} + +static void setaction(const struct cmdinfo *cip, const char *value); + +static void setpartsize(const struct cmdinfo *cip, const char *value) { + long newpartsize; + char *endp; + + newpartsize= strtol(value,&endp,10); + if (newpartsize <= 0 || newpartsize > (INT_MAX >> 10)) + badusage("part size is far too large or is not positive"); + + maxpartsize= newpartsize << 10; + if (maxpartsize <= HEADERALLOWANCE) + badusage("part size must be at least %dk (to allow for header)", + (HEADERALLOWANCE >> 10) + 1); +} + +static dofunction *const dofunctions[]= { + do_split, + do_join, + do_info, + do_auto, + do_queue, + do_discard, +}; + +/* NB: the entries using setaction must appear first and be in the + * same order as dofunctions: + */ +static const struct cmdinfo cmdinfos[]= { + { "split", 's', 0, 0, 0, setaction }, + { "join", 'j', 0, 0, 0, setaction }, + { "info", 'I', 0, 0, 0, setaction }, + { "auto", 'a', 0, 0, 0, setaction }, + { "listq", 'l', 0, 0, 0, setaction }, + { "discard", 'd', 0, 0, 0, setaction }, + { "help", 'h', 0, 0, 0, helponly }, + { "version", 0, 0, 0, 0, versiononly }, + { "licence", 0, 0, 0, 0, showcopyright }, /* UK spelling */ + { "license", 0, 0, 0, 0, showcopyright }, /* US spelling */ + { "depotdir", 0, 1, 0, &depotdir, 0 }, + { "partsize", 'S', 1, 0, 0, setpartsize }, + { "output", 'o', 1, 0, &outputfile, 0 }, + { "npquiet", 'Q', 0, &npquiet, 0, 0, 1 }, + { "msdos", 0, 0, &msdos, 0, 0, 1 }, + { 0, 0 } +}; + +static void setaction(const struct cmdinfo *cip, const char *value) { + if (cipaction) + badusage("conflicting actions --%s and --%s",cip->olong,cipaction->olong); + cipaction= cip; + assert(cip-cmdinfos < sizeof(dofunctions)*sizeof(dofunction*)); + action= dofunctions[cip-cmdinfos]; +} + +int main(int argc, const char *const *argv) { + jmp_buf ejbuf; + int l; + char *p; + + if (setjmp(ejbuf)) { /* expect warning about possible clobbering of argv */ + error_unwind(ehflag_bombout); exit(2); + } + push_error_handler(&ejbuf,print_error_fatal,0); + + myopt(&argv,cmdinfos); + if (!cipaction) badusage("need an action option"); + + l= strlen(depotdir); + if (l && depotdir[l-1] != '/') { + p= nfmalloc(l+2); + strcpy(p,depotdir); + strcpy(p+l,"/"); + depotdir= p; + } + + setvbuf(stdout,0,_IONBF,0); + action(argv); + + if (ferror(stderr)) werr("stderr"); + + set_error_display(0,0); + error_unwind(ehflag_normaltidy); + exit(0); +} diff --git a/split/mksplit b/split/mksplit new file mode 100755 index 00000000..2ed17704 --- /dev/null +++ b/split/mksplit @@ -0,0 +1,88 @@ +#!/usr/bin/perl -- +# This script is only supposed to be called by dpkg-split. +# Its arguments are: +# +# Stdin is also redirected from the source archive by dpkg-split. + +# Copyright (C) 1995 Ian Jackson +# +# This 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, +# or (at your option) any later version. +# +# This 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 dpkg; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +@ARGV == 6 || die "mksplit: bad invocation\n"; + +($sourcefile,$partsize,$prefix,$orgsize,$partsizeallow,$msdos) = @ARGV; + +sub output { + $!=0; $rv= `$_[0]`; $? && die "mksplit $_[0]: $! $?\n"; + $rv =~ s/\n$//; $rv =~ s/\s+$//; $rv =~ s/^\s+//; + $rv; +} + +$myversion='2.1'; +$csum= &output("md5sum <\"$sourcefile\""); +$package= &output("dpkg-deb --field \"$sourcefile\" Package"); +$version= &output("dpkg-deb --field \"$sourcefile\" Version"); +$revision= &output("dpkg-deb --field \"$sourcefile\" Package_Revision"); +$version.= "-$revision" if length($revision); +$nparts=int(($orgsize+$partsize-1)/$partsize); +$startat=0; +$showpartnum=1; + +$|=1; +print("Splitting package $package into $nparts parts: "); + +$msdos= ($msdos eq 'yes'); +if ($msdos) { + $prefixdir= $prefix; $prefixdir =~ s:(/?)/*[^/]+$:$1:; + $cleanprefix= $prefix; $cleanprefix =~ s:^.*/+::; + $cleanprefix =~ y/-A-Za-z0-9+/-a-za-z0-9x/d; +} + +sub add { + $data .= + sprintf("%-16s%-12d0 0 100644 %-10d%c\n%s%s", + $_[0], time, length($_[1]), 0140, $_[1], + (length($_[1]) & 1) ? "\n" : ""); +} + +while ($startat < $orgsize) { + $dsp= "$myversion\n$package\n$version\n$csum\n$orgsize\n$partsize\n". + "$showpartnum/$nparts\n"; + defined($thispartreallen= read(STDIN,$pd,$partsize)) || die "mksplit: read: $!\n"; + $data= "!\n"; + print("$showpartnum "); + &add('debian-split',$dsp); + &add("data.$showpartnum",$pd); + if ($thispartreallen > $partsizeallow) { + die "Header is too long, making part too long. Your package name or version\n". + "numbers must be extraordinarily long, or something. Giving up.\n"; + } + if ($msdos) { + $basename= "${showpartnum}of$nparts.$cleanprefix"; + $basename= substr($basename,0,9); + $basename =~ s/^([^.]*)\.(.*)$/$2$1/; + $basename= "$prefixdir$basename"; + } else { + $basename= "$prefix.${showpartnum}of$nparts"; + } + open(O,"> $basename.deb") || die("mksplit: open $basename.deb: $!\n"); + print(O $data) || die("mksplit: write $basename.deb: $!\n"); + close(O) || die("mksplit: close $basename.deb: $!\n"); + $startat += $partsize; + $showpartnum++; +} +print("done\n"); + +exit(0); diff --git a/split/mksplit.pl b/split/mksplit.pl new file mode 100644 index 00000000..2ed17704 --- /dev/null +++ b/split/mksplit.pl @@ -0,0 +1,88 @@ +#!/usr/bin/perl -- +# This script is only supposed to be called by dpkg-split. +# Its arguments are: +# +# Stdin is also redirected from the source archive by dpkg-split. + +# Copyright (C) 1995 Ian Jackson +# +# This 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, +# or (at your option) any later version. +# +# This 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 dpkg; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +@ARGV == 6 || die "mksplit: bad invocation\n"; + +($sourcefile,$partsize,$prefix,$orgsize,$partsizeallow,$msdos) = @ARGV; + +sub output { + $!=0; $rv= `$_[0]`; $? && die "mksplit $_[0]: $! $?\n"; + $rv =~ s/\n$//; $rv =~ s/\s+$//; $rv =~ s/^\s+//; + $rv; +} + +$myversion='2.1'; +$csum= &output("md5sum <\"$sourcefile\""); +$package= &output("dpkg-deb --field \"$sourcefile\" Package"); +$version= &output("dpkg-deb --field \"$sourcefile\" Version"); +$revision= &output("dpkg-deb --field \"$sourcefile\" Package_Revision"); +$version.= "-$revision" if length($revision); +$nparts=int(($orgsize+$partsize-1)/$partsize); +$startat=0; +$showpartnum=1; + +$|=1; +print("Splitting package $package into $nparts parts: "); + +$msdos= ($msdos eq 'yes'); +if ($msdos) { + $prefixdir= $prefix; $prefixdir =~ s:(/?)/*[^/]+$:$1:; + $cleanprefix= $prefix; $cleanprefix =~ s:^.*/+::; + $cleanprefix =~ y/-A-Za-z0-9+/-a-za-z0-9x/d; +} + +sub add { + $data .= + sprintf("%-16s%-12d0 0 100644 %-10d%c\n%s%s", + $_[0], time, length($_[1]), 0140, $_[1], + (length($_[1]) & 1) ? "\n" : ""); +} + +while ($startat < $orgsize) { + $dsp= "$myversion\n$package\n$version\n$csum\n$orgsize\n$partsize\n". + "$showpartnum/$nparts\n"; + defined($thispartreallen= read(STDIN,$pd,$partsize)) || die "mksplit: read: $!\n"; + $data= "!\n"; + print("$showpartnum "); + &add('debian-split',$dsp); + &add("data.$showpartnum",$pd); + if ($thispartreallen > $partsizeallow) { + die "Header is too long, making part too long. Your package name or version\n". + "numbers must be extraordinarily long, or something. Giving up.\n"; + } + if ($msdos) { + $basename= "${showpartnum}of$nparts.$cleanprefix"; + $basename= substr($basename,0,9); + $basename =~ s/^([^.]*)\.(.*)$/$2$1/; + $basename= "$prefixdir$basename"; + } else { + $basename= "$prefix.${showpartnum}of$nparts"; + } + open(O,"> $basename.deb") || die("mksplit: open $basename.deb: $!\n"); + print(O $data) || die("mksplit: write $basename.deb: $!\n"); + close(O) || die("mksplit: close $basename.deb: $!\n"); + $startat += $partsize; + $showpartnum++; +} +print("done\n"); + +exit(0); diff --git a/split/old-mksplit.sh b/split/old-mksplit.sh new file mode 100644 index 00000000..8743f944 --- /dev/null +++ b/split/old-mksplit.sh @@ -0,0 +1,113 @@ +#!/bin/bash +# This script is only supposed to be called by dpkg-split. +# Its arguments are: +# +# Stdin is also redirected from the source archive by dpkg-split. + +# Copyright (C) 1995 Ian Jackson +# +# This 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, +# or (at your option) any later version. +# +# This 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 dpkg; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +set -ex + +if [ "$#" != 6 ]; then echo >&2 'Bad invocation of mksplit.sh.'; exit 1; fi + +sourcefile="$1" +partsize="$2" +prefix="$3" +orgsize="$4" +partsizeallow="$5" +msdos="$6" + +myversion=2.1 +csum=`md5sum <"$sourcefile"` +package="`dpkg-deb --field \"$sourcefile\" Package`" +version="`dpkg-deb --field \"$sourcefile\" Version`" +revision="`dpkg-deb --field \"$sourcefile\" Package_Revision`" +if [ "x$revision" != x ]; then version="$version-$revision"; fi +nparts=$[($orgsize+$partsize-1)/$partsize] +startat=0 +partnum=0 + +td=/tmp/ds$$ +mkdir $td +ec=1 +trap "rm -r $td; exit $ec" 0 +dsp=$td/debian-split + +echo -n "Splitting package $package into $nparts parts: " + +if [ yes = "$msdos" ] +then + prefixdir="`dirname \"$prefix\"`" + cleanprefix="`basename \"$prefix\" | tr A-Z+ a-zx | tr -dc 0-9a-z-`" +fi + +ar-include () { + perl -e ' + $f= $ARGV[0]; + (@s= stat(STDIN)) || die "$f: $!"; + undef $/; read(STDIN,$d,$s[7]) == $s[7] || die "$f: $!"; + printf("%-16s%-12d0 0 100644 %-10d%c\n%s%s", + $f, time, $s[7], 0140, $d, + ($s[7] & 1) ? "\n" : "") || die "$f: $!"; + close(STDOUT) || die "$f: $!"; + ' "$1" <"$td/$1" +} + +while [ $startat -lt $orgsize ] +do + showpartnum=$[$partnum+1] + echo $myversion >$dsp + echo $package >>$dsp + echo $version >>$dsp + echo $csum >>$dsp + echo $orgsize >>$dsp + echo $partsize >>$dsp + echo $showpartnum/$nparts >>$dsp + dd bs=$partsize skip=$partnum count=1 \ + of=$td/data.$showpartnum \ + 2>&1 | (egrep -v '.* records (in|out)' || true) + rm -f $td/part + echo -n "$showpartnum " + echo '!' >$td/part + ar-include debian-split >>$td/part + ar-include data.$showpartnum >>$td/part + thispartreallen="`ls -l $td/part | awk '{print $5}'`" + if ! [ "$thispartreallen" -le "$partsizeallow" ] + then + cat >&2 </dev/null | \ + sed -e 's/^\([^.]*\)\.\(.*\)$/\2\1/'`" + basename="$prefixdir/$basename" + else + basename="$prefix.${showpartnum}of$nparts" + fi + mv $td/part $basename.deb + startat=$[$startat+$partsize] + partnum=$showpartnum +done +echo "done" + +ec=0 diff --git a/split/queue.c b/split/queue.c new file mode 100644 index 00000000..35d0418d --- /dev/null +++ b/split/queue.c @@ -0,0 +1,273 @@ +/* + * dpkg-split - splitting and joining of multipart *.deb archives + * queue.c - queue management + * + * Copyright (C) 1995 Ian Jackson + * + * This 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, + * or (at your option) any later version. + * + * This 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 dpkg; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* + * Queue, in /var/lib/dpkg/parts, is a plain directory with one + * file per part. + * + * parts are named + * ... + * all numbers in hex + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "dpkg.h" +#include "dpkg-db.h" +#include "dpkg-split.h" + +static int decompose_filename(const char *filename, struct partqueue *pq) { + const char *p; + char *q; + + if (strspn(filename,"0123456789abcdef") != 32 || filename[32] != '.') return 0; + q= nfmalloc(33); + memcpy(q,filename,32); q[32]= 0; + pq->info.md5sum= q; + p= filename+33; + pq->info.maxpartlen= strtol(p,&q,16); if (q==p || *q++ != '.') return 0; + p=q; pq->info.thispartn= (int)strtol(p,&q,16); if (q==p || *q++ != '.') return 0; + p=q; pq->info.maxpartn= (int)strtol(p,&q,16); if (q==p || *q) return 0; + return 1; +} + +void scandepot(void) { + DIR *depot; + struct dirent *de; + struct partqueue *pq; + char *p; + + assert(!queue); + depot= opendir(depotdir); + if (!depot) ohshite("unable to read depot directory `%.250s'",depotdir); + while ((de= readdir(depot))) { + if (de->d_name[0] == '.') continue; + pq= nfmalloc(sizeof(struct partqueue)); + pq->info.fmtversion= pq->info.package= pq->info.version= 0; + pq->info.orglength= pq->info.thispartoffset= pq->info.thispartlen= 0; + pq->info.headerlen= 0; + p= nfmalloc(strlen(depotdir)+strlen(de->d_name)+1); + strcpy(p,depotdir); + strcat(p,de->d_name); + pq->info.filename= p; + if (!decompose_filename(de->d_name,pq)) { + pq->info.md5sum= 0; + pq->info.maxpartlen= pq->info.thispartn= pq->info.maxpartn= 0; + } + pq->nextinqueue= queue; + queue= pq; + } +} + +static int partmatches(struct partinfo *pi, struct partinfo *refi) { + return (pi->md5sum && + !strcmp(pi->md5sum,refi->md5sum) && + pi->maxpartn == refi->maxpartn && + pi->maxpartlen == refi->maxpartlen); +} + +void do_auto(const char *const *argv) { + const char *partfile; + struct partinfo *pi, *refi, *npi, **partlist, *otherthispart; + struct partqueue *pq; + int i, nr, j, ap; + FILE *part; + void *buffer; + char *p, *q; + + if (!outputfile) badusage("--auto requires the use of the --output option"); + if (!(partfile= *argv++) || *argv) + badusage("--auto requires exactly one part file arguments"); + + refi= nfmalloc(sizeof(struct partqueue)); + part= fopen(partfile,"r"); + if (!part) ohshite("unable to read part file `%.250s'",partfile); + if (!read_info(part,partfile,refi)) { + if (!npquiet) + printf("File `%.250s' is not part of a multipart archive.\n",partfile); + if (fclose(stdout)) werr("stdout"); + exit(1); + } + fclose(part); + scandepot(); + partlist= nfmalloc(sizeof(struct partinfo*)*refi->maxpartn); + for (i=0; imaxpartn; i++) partlist[i]= 0; + for (pq= queue; pq; pq= pq->nextinqueue) { + pi= &pq->info; + if (!partmatches(pi,refi)) continue; + npi= nfmalloc(sizeof(struct partinfo)); + mustgetpartinfo(pi->filename,npi); + addtopartlist(partlist,npi,refi); + } + /* If we already have a copy of this version we ignore it and prefer the + * new one, but we still want to delete the one in the depot, so we + * save its partinfo (with the filename) for later. This also prevents + * us from accidentally deleting the source file. + */ + otherthispart= partlist[refi->thispartn-1]; + partlist[refi->thispartn-1]= refi; + for (j=refi->maxpartn-1; j>=0 && partlist[j]; j--); + + if (j>=0) { + + part= fopen(partfile,"r"); + if (!part) ohshite("unable to reopen part file `%.250s'",partfile); + buffer= nfmalloc(refi->filesize); + nr= fread(buffer,1,refi->filesize,part); + if (nr != refi->filesize) rerreof(part,partfile); + if (getc(part) != EOF) ohshit("part file `%.250s' has trailing garbage",partfile); + if (ferror(part)) rerr(partfile); + fclose(part); + p= nfmalloc(strlen(depotdir)+50); + q= nfmalloc(strlen(depotdir)+200); + sprintf(p,"%st.%lx",depotdir,(long)getpid()); + sprintf(q,"%s%s.%lx.%x.%x",depotdir,refi->md5sum, + refi->maxpartlen,refi->thispartn,refi->maxpartn); + part= fopen(p,"w"); + if (!part) ohshite("unable to open new depot file `%.250s'",p); + nr= fwrite(buffer,1,refi->filesize,part); + if (nr != refi->filesize) werr(p); + if (fclose(part)) werr(p); + if (rename(p,q)) ohshite("unable to rename new depot file `%.250s' to `%.250s'",p,q); + + printf("Part %d of package %s filed (still want ",refi->thispartn,refi->package); + /* There are still some parts missing. */ + for (i=0, ap=0; imaxpartn; i++) + if (!partlist[i]) + printf("%s%d", !ap++ ? "" : i==j ? " and " : ", ", i+1); + printf(").\n"); + + } else { + + /* We have all the parts. */ + reassemble(partlist,outputfile); + + /* OK, delete all the parts (except the new one, which we never copied). */ + partlist[refi->thispartn-1]= otherthispart; + for (i=0; imaxpartn; i++) + if (partlist[i]) + if (unlink(partlist[i]->filename)) + ohshite("unable to delete used-up depot file `%.250s'",partlist[i]->filename); + + } + + if (ferror(stderr)) werr("stderr"); +} + +void do_queue(const char *const *argv) { + struct partqueue *pq, *qq; + struct partinfo ti; + const char *head; + struct stat stab; + unsigned long bytes; + int i; + + if (*argv) badusage("--listq does not take any arguments"); + scandepot(); + + head= "Junk files left around in the depot directory:\n"; + for (pq= queue; pq; pq= pq->nextinqueue) { + if (pq->info.md5sum) continue; + fputs(head,stdout); head= ""; + if (lstat(pq->info.filename,&stab)) + ohshit("unable to stat `%.250s'",pq->info.filename); + if (S_ISREG(stab.st_mode)) { + bytes= stab.st_size; + printf(" %s (%lu bytes)\n",pq->info.filename,bytes); + } else { + printf(" %s (not a plain file)\n",pq->info.filename); + } + } + if (!*head) putchar('\n'); + + head= "Packages not yet reassembled:\n"; + for (pq= queue; pq; pq= pq->nextinqueue) { + if (!pq->info.md5sum) continue; + mustgetpartinfo(pq->info.filename,&ti); + fputs(head,stdout); head= ""; + printf(" Package %s: part(s) ",ti.package); + bytes= 0; + for (i=0; iinfo,&ti) && qq->info.thispartn == i+1); + qq= qq->nextinqueue); + if (qq) { + printf("%d ",i+1); + if (lstat(qq->info.filename,&stab)) + ohshite("unable to stat `%.250s'",qq->info.filename); + if (!S_ISREG(stab.st_mode)) + ohshit("part file `%.250s' is not a plain file",qq->info.filename); + bytes+= stab.st_size; + qq->info.md5sum= 0; /* don't find this package again */ + } + } + printf("(total %lu bytes)\n",bytes); + } + if (fclose(stdout)) werr("stdout"); +} + +enum discardwhich { ds_junk, ds_package, ds_all }; + +static void discardsome(enum discardwhich which, const char *package) { + struct partqueue *pq; + + for (pq= queue; pq; pq= pq->nextinqueue) { + switch (which) { + case ds_junk: + if (pq->info.md5sum) continue; + break; + case ds_package: + if (!pq->info.md5sum || strcasecmp(pq->info.package,package)) continue; + break; + case ds_all: + break; + default: internerr("bad discardsome which"); + } + if (unlink(pq->info.filename)) + ohshite("unable to discard `%.250s'",pq->info.filename); + printf("Deleted %s.\n",pq->info.filename); + } +} + +void do_discard(const char *const *argv) { + const char *thisarg; + struct partqueue *pq; + + scandepot(); + if (*argv) { + for (pq= queue; pq; pq= pq->nextinqueue) + if (pq->info.md5sum) + mustgetpartinfo(pq->info.filename,&pq->info); + discardsome(ds_junk,0); + while ((thisarg= *argv++)) discardsome(ds_package,thisarg); + } else { + discardsome(ds_all,0); + } +} diff --git a/split/split.c b/split/split.c new file mode 100644 index 00000000..facdeb0e --- /dev/null +++ b/split/split.c @@ -0,0 +1,71 @@ +/* + * dpkg-split - splitting and joining of multipart *.deb archives + * split.c - splitting archives + * + * Copyright (C) 1995 Ian Jackson + * + * This 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, + * or (at your option) any later version. + * + * This 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 dpkg; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "dpkg.h" +#include "dpkg-db.h" +#include "dpkg-split.h" + +void do_split(const char *const *argv) { + const char *sourcefile, *prefix; + char *palloc; + int l, fd; + char partsizebuf[30], lengthbuf[30], partsizeallowbuf[30]; + struct stat stab; + + sourcefile= *argv++; + if (!sourcefile) + badusage("--split needs a source filename argument"); + prefix= *argv++; + if (prefix && *argv) + badusage("--split takes at most a source filename and destination prefix"); + if (!prefix) { + l= strlen(sourcefile); + palloc= nfmalloc(l+1); + strcpy(palloc,sourcefile); + if (!strcmp(palloc+l-(sizeof(DEBEXT)-1),DEBEXT)) { + l -= (sizeof(DEBEXT)-1); + palloc[l]= 0; + } + prefix= palloc; + } + sprintf(partsizebuf,"%ld",maxpartsize-HEADERALLOWANCE); + sprintf(partsizeallowbuf,"%ld",maxpartsize); + fd= open(sourcefile,O_RDONLY); + if (!fd) ohshite("unable to open source file `%.250s'",sourcefile); + if (fstat(fd,&stab)) ohshite("unable to fstat source file"); + if (!S_ISREG(stab.st_mode)) ohshit("source file `%.250s' not a plain file",sourcefile); + sprintf(lengthbuf,"%lu",(unsigned long)stab.st_size); + m_dup2(fd,0); + execl(MKSPLITSCRIPT,MKSPLITSCRIPT, + sourcefile,partsizebuf,prefix,lengthbuf,partsizeallowbuf,msdos?"yes":"no", + (char*)0); + ohshite("unable to exec " MKSPLITSCRIPT); +} diff --git a/version.h b/version.h new file mode 100644 index 00000000..ce5a0ea2 --- /dev/null +++ b/version.h @@ -0,0 +1 @@ +#define DPKG_VERSION "1.1.4" /* This line modified by Makefile */ -- 2.39.5