From: Ian Jackson Date: Thu, 4 Apr 1996 00:58:40 +0000 (+0100) Subject: dpkg (1.1.4); priority=MEDIUM X-Git-Url: https://err.no/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=1b80fb16c22db72457d7a456ffbf1f70a8dfc0a5;p=dpkg 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 --- 1b80fb16c22db72457d7a456ffbf1f70a8dfc0a5 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 */