Initial git import v0.5
authorSteve Youngs <steve@sxemacs.org>
Tue, 30 Nov 2010 15:25:40 +0000 (01:25 +1000)
committerSteve Youngs <steve@sxemacs.org>
Tue, 30 Nov 2010 15:25:40 +0000 (01:25 +1000)
Signed-off-by: Steve Youngs <steve@sxemacs.org>
35 files changed:
LFS-pkgusr-hint.txt [new file with mode: 0644]
README [new file with mode: 0644]
bin/mail [new file with mode: 0755]
bin/which [new file with mode: 0755]
etc/pkgusr/bash_profile [new file with mode: 0644]
etc/pkgusr/bashrc [new file with mode: 0644]
etc/pkgusr/handy_funcs [new file with mode: 0644]
etc/pkgusr/skel-package/.bash_profile [new symlink]
etc/pkgusr/skel-package/.bashrc [new symlink]
etc/pkgusr/skel-package/.project [new file with mode: 0644]
etc/pkgusr/skel-package/build [new file with mode: 0755]
etc/pkgusr/zsh/_zsh-pkgtools [new file with mode: 0644]
etc/pkgusr/zsh/zsh-pkgtools [new file with mode: 0644]
installdir.lst [new file with mode: 0644]
lisp/.arch-inventory [new file with mode: 0644]
lisp/pkgusr.el [new file with mode: 0644]
usr/bin/forall_direntries_from [new file with mode: 0755]
usr/bin/grep_all_regular_files_for [new file with mode: 0755]
usr/bin/header-symbol-search [new file with mode: 0755]
usr/bin/lesspipe.sh [new file with mode: 0755]
usr/bin/library-symbol-search [new file with mode: 0755]
usr/bin/list_package [new file with mode: 0755]
usr/bin/list_suspicious_files [new file with mode: 0755]
usr/bin/list_suspicious_files_from [new file with mode: 0755]
usr/bin/uninstall_package [new file with mode: 0755]
usr/lib/pkgusr/chgrp [new file with mode: 0755]
usr/lib/pkgusr/chmod [new file with mode: 0755]
usr/lib/pkgusr/chown [new file with mode: 0755]
usr/lib/pkgusr/install [new file with mode: 0755]
usr/lib/pkgusr/mkdir [new file with mode: 0755]
usr/lib/pkgusr/update-pkg-project [new file with mode: 0755]
usr/sbin/add_package_user [new file with mode: 0755]
usr/sbin/groupadd [new file with mode: 0755]
usr/sbin/install_package [new file with mode: 0755]
usr/sbin/useradd [new file with mode: 0755]

diff --git a/LFS-pkgusr-hint.txt b/LFS-pkgusr-hint.txt
new file mode 100644 (file)
index 0000000..49c7356
--- /dev/null
@@ -0,0 +1,1790 @@
+AUTHOR: Matthias S. Benkmann <article at winterdrache dot de>
+
+DATE: 2005-11-13
+
+LICENSE: Creative Commons Attribution-NoDerivs 2.0 
+         (http://creativecommons.org/licenses/by-nd/2.0/)
+
+SYNOPSIS: More Control and Package Management using Package Users (v1.2)
+
+DESCRIPTION:
+-You want to know which packages your files belong to ?                 
+-You want to deinstall software that doesn't have make uninstall ?      
+-You are bothered by programs installed setuid root behind your back ?  
+-You don't like packages quietly overwriting files from other packages ?
+-You don't like package managers like RPM ?                             
+-YOU WANT TOTAL CONTROL USING ONLY UNIX BUILTINS ?                      
+
+ATTACHMENTS:
+http://www.linuxfromscratch.org/hints/downloads/attachments/more_control_and_pkg_man/more_control_helpers.tar.bz2
+
+PREREQUISITES:
+For use with LFS book 6.0: Brain.
+For use with LFS book later than 6.0: Brain (awake, good working condition).
+
+HINT:
+
+########################################################################### 
+ Contents
+###########################################################################
+
+1. Preface
+2. Overview
+
+- PART 1: General Information -
+
+3. Package Users
+  3.1 Introduction
+  3.2 User Name
+  3.3 Groups
+  3.4 Home Directory
+4. Common Problems
+  4.1 Introduction
+  4.2 General Procedure
+  4.3 Permission Changes
+  4.4 Ownership Changes
+  4.5 Write to Non-Install Directory
+  4.6 Delete or Overwrite File
+  4.7 /sbin/ldconfig
+5. The more_control_helpers Archive
+  5.1 Overview
+  5.2 The Wrappers
+  5.3 add_package_user/install_package
+  5.4 forall_direntries_from
+  5.5 uninstall_package
+  5.6 list_suspicious_files/list_suspicious_files_from
+  5.7 list_package
+  5.8 grep_all_regular_files_for
+  5.9 The etc Directory
+  5.10 Temporary Files
+  
+- PART 2: LFS Specifics -
+
+6. Pre-Chroot Phase (Chapter 5)
+7. Chroot Phase (Chapter 6)
+  7.1 Preparations
+  7.2 Walkthrough: Installing linux-libc-headers
+  7.3 Known Issues with LFS Packages
+8. Sanity Checks
+  8.1 Suspicious Files
+  8.2 References to Temporary Files
+
+- APPENDICES -
+
+A. Security Issues
+  A.1 NFS
+  A.2 Daemons
+B. Package Categories
+C. Acknowledgements and Changelog
+  
+
+########################################################################### 
+ 1. Preface
+###########################################################################
+
+Let's say I have written a program that you would like to use. To make it
+easier for you I come over to install it for you. Would you give me the root
+account and then leave the room ? No ? Then why do you give it to complete
+strangers who you have never seen in your life, to install software packages
+pulled from some Internet server, that come with no warranty and don't even 
+list their contents in the README, although they will happily spread them all 
+over your system ?
+
+It is a mystery why Unix admins who wouldn't even trust their employer with
+more than a normal user account carelessly execute complex and incomprehensible
+installation scripts with full root rights.
+
+Users and groups are the basic security principle in a Unix system. They have
+been used successfully for a long time to monitor who has created a file and 
+to control who is allowed to delete or change it. But this control has only 
+been imposed on the files of ordinary users. What a waste! I suggest to extend
+this control to all system files. 
+
+The general idea is to create package users, i.e. user accounts with restricted
+rights, to build and install software packages, rather than doing these tasks 
+as root. Not only does this give you more control over what build and install 
+scripts may or may not do, it can also serve as a quite useful package 
+management system.
+
+
+#############################################################################
+ 2. Overview
+#############################################################################
+
+This hint is divided into 3 parts. The first part contains general information
+about the package user method. This part is the most important part of the
+hint. Read it thoroughly. The second part explains how to apply the package 
+user method to the building of an LFS system. 
+Finally, part 3 of this hint is the Appendix with information that would not
+fit anywhere else or that is not of general interest.
+
+It is inevitable that part 2 will become outdated with time as the LFS book 
+changes and new versions of the software packages used with LFS are released. 
+I make no attempt to track these changes. 
+When someone reports an issue with a package I will incorporate
+it into the hint, but larger changes that might be required due to changes in
+the LFS build methodology could take a long time to get included. The reason 
+for this (aside from lack of time) is that I consider part 2 as bonus material 
+that helps people get started but is not essential. Part 1 describes the 
+concepts, which are independent of package versions or the LFS book, and you 
+will have to rely on this information whenever part 2 fails. Don't forget 
+that part 2 only deals with the packages used by the LFS book. For all the 
+other packages you install on your system after that even an up-to-date 
+part 2 would offer no aid anyway.
+
+The previous paragraph might sound discouraging, and as you read more from the
+hint it is possible that you get the impression that the package user
+method is complicated, causes lots of difficult problems and is overall too
+much trouble for anyone but a real hardcore admin with programming experience.
+But you would be mistaken. 
+First of all, many things experienced as installation problems when working
+with the package user system are in fact desirable features. 
+If `make install' fails for some package, because it attempts to install a
+file with the same name as a pre-existing file from another package, you
+should not curse the fact that you have to spend additional time to resolve
+this issue. Instead you should be happy that you have been alerted of this
+collision that, had it gone unnoticed, could have messed up your system in
+more or less subtle ways.
+Secondly, the package user system is not an all-or-nothing approach. It
+works on a per-package basis. If a package gives you too much trouble, you
+can always decide to chicken out and finish the installation as root.
+Finally, the more_control_helpers archive provided with this hint contains
+several useful scripts that automate many aspects of software installation
+as a package user and, together with the tips given in this hint, add a lot 
+of value to the package user system. 
+So do not pass judgement until you have read at least the complete part 1,
+including the description of the more_control_helpers.
+
+
+---------------------- PART 1: General Information --------------------------
+
+
+#############################################################################
+ 3. Package Users
+#############################################################################
+
+ 3.1 Introduction
+ ----------------
+
+The basic idea of this scheme is easily explained. Every package belongs to a 
+certain "package user". When you install a package, you build and install
+the package as this package user, causing all files that are installed to be 
+owned by the package user. As a consequence all the usual package management 
+tasks can be comfortably achieved through the use of standard command line 
+utilities. A simple `ls -l <file>' will tell you, for instance, what package 
+<file> belongs to and a `find -user ...' command allows you to perform an 
+operation on all the files belonging to a certain package, e.g. delete them 
+to uninstall the package.
+
+But package management is not all that package users are good for. Because
+package users do not have root-rights, the installation of a package is
+limited in what it can do. One thing that a package user is not allowed to do,
+for example, is to overwrite files from a different package user. Clashes
+between different packages that want to install a binary, library or header 
+file of the same name are more common than you might think. With package users
+you never run the risk of package B's installation destroying files from 
+package A silently without you noticing. Every attempt of doing this during
+package B's installation will cause a "Permission denied" or
+"Operation not permitted" error so that you have the chance of taking 
+appropriate steps.
+Another thing that package users are not allowed to do is install setuid root 
+binaries. The decision to make a binary setuid root is also something that a 
+prudent admin does not want to leave up to the creator of a software package.
+
+Usually package user accounts have no valid password so that only root can su 
+to a package user, which ensures that package users do not open an additional 
+way into the system and undermine security. But you *may* set passwords 
+anyway to allow a co-admin who you want to be able to install and maintain 
+certain software packages to do so without having access to the actual root 
+account. This co-admin could for instance install, delete, change additional 
+libraries which might be necessary for his workgroup. He would be unable, 
+though, to remove or modify libraries which don't belong to him/her, such as 
+libc.
+
+
+ 3.2 User Name
+ -------------
+
+You don't need to drive yourself nuts trying to come up with 8 character
+names for the package users. I always use the name of the package without 
+the version number, including dashes and possibly exceeding 8 characters in 
+length, e.g. "util-linux", and in the several years that I've been using this 
+scheme I have not encountered any problems, nor has anyone else reported 
+trouble. The 8-character limit on user names seems to be a thing of the past.
+
+TIP:
+  You can use bash's programmable completion feature to save yourself some 
+  typing when entering commands that take a user name as an argument, such as
+  su, finger or pinky. The command 
+  
+      complete -o default -o nospace -A user su finger pinky
+      
+  tells bash to tab-complete words as user names for the commands su,
+  finger and pinky.
+  With this in place you can simply type `su linux-li<TAB>' and bash
+  will complete this to `su linux-libc-headers' (assuming that you have a
+  package user named "linux-libc-headers").
+  "-o default" tells bash that if a suitable user name does not exist, the 
+  default completion shall be attempted. 
+  "-o nospace" prevents the addition of a space after the completed word.
+  
+  This is a very useful command to put into root's .bashrc and .bash_profile.
+  
+  BTW, at http://freshmeat.net/projects/bashcompletion/
+  you can find a project that offers sophisticated completions for many
+  other commands.
+
+
+ 3.3 Groups
+ ----------
+
+Every package user belongs to at least 2 groups. One of these groups is
+the "install" group, which all package users (and only package users) belong
+to. All directories that packages are allowed to install stuff in belong to 
+the install group. This includes directories such as /bin and /usr/bin but 
+excludes directories like /root or /. 
+The directories owned by the install group are always group-writable. 
+This would be enough for the package management aspects, but without further 
+preparation this would not give added security or control because every 
+package could replace the files from a different package (the change would 
+be visible in the output from `ls -l', though).
+For this reason all install directories get the sticky attribute. This
+allows users to create new files and delete or modify their own files in
+the directory, but files from other users can not be modified or removed.
+In the rest of this hint, whenever the term "install directory" is used, it
+refers to a directory that belongs to group install, is group-writable and
+sticky. IOW, to turn <dir> into an install directory you would do
+
+    chgrp install <dir> && chmod g+w,o+t <dir>
+
+Although the install group is crucial for the package user system, it is 
+implemented as a supplementary group, rather than as the primary group for
+package users. This has at least 2 advantages. 
+One advantage is that this makes it easy to get a list of all packages 
+installed on the system with the command
+
+    grep install /etc/group
+
+A more important point, however, is that the primary group is the
+one that files created by the package user will belong to. So it will be 
+printed in the output of `ls -l' and is subject to find's "-group" test. 
+This makes it very useful for organizational purposes. 
+Following are some suggestions for how to use the primary group. 
+
+1. group name = user name
+
+   Under this scheme the package user for the bash package would be
+   bash:bash. `ls -l /bin/bash' would show something like this
+   
+   -rwxr-xr-x    1 bash     bash    1731859 Feb 30  2005 /bin/bash
+   
+   An important advantage of this scheme is that the user information is
+   not lost when you make a file setuid root, which requires changing
+   the file's owner. Because of this advantage, this scheme is the one
+   recommended by this hint. However, the hint's instructions will work
+   fine if you choose a different scheme.
+
+2. group name = package category
+
+   Under this scheme, you would have certain package categories, such as
+   games, system, net,... and bash, being a system program, would possibly
+   belong to the system group, so that `ls -l /bin/bash' would show something 
+   like this
+   
+   -rwxr-xr-x    1 bash     system    1731859 Jul  4  1776 /bin/bash
+   
+   This system is nice, but probably not as useful as #1 above, unless you
+   have a real use for this categorization.
+   For a possible categorization see Appendix B at the end of this hint.
+
+3. group name = identifier for a real group of people
+   
+   Under this scheme, the group would correspond to a real group of people in 
+   meatspace, e.g. the group of admins responsible for the package. 
+   If you need something like this you'll know best what it looks like and how
+   to implement it, so no further discussion of this method will be given here. 
+   
+
+ 3.4 Home Directory
+ ------------------
+Although it is well possible not to have a valid home directory for package
+users or to have just one home directory shared by all package users, that
+would be a wasted opportunity. Having individual home directories for the
+package users offers a nice way to organize tarballs, patches, build scripts,
+notes and all the other per-package information that you accumulate with time.
+
+I suggest to use the home directory /usr/src/<package> for a package user
+called <package> with the contents detailed below. The more_control_helpers 
+archive contains scripts and skeleton files that implement this suggestion.
+
+  .bash_profile: 
+           You will usually want to have the same environment for all package
+           users, so it is a good idea to make .bash_profile a symbolic link
+           to a file in a central location. The more_control_helpers example
+           uses /etc/pkgusr/bash_profile for this purpose.
+           
+  .bashrc: 
+           As for .bash_profile a symlink is a good idea for .bashrc. The
+           more_control_helpers example uses /etc/pkgusr/bashrc as link target.
+           Under normal circumstances package users are not 
+           (and even can not be) used for logging into the system, so there 
+           is little reason to distinguish between login and non-login shells 
+           for package users. Therefore, the example bashrc from 
+           more_control_helpers simply sources .bash_profile.
+           This makes sure that the same environment will be used, regardless
+           of whether `su <package>' or `su - <package>' is used to become
+           the package user.
+           
+  .project: 
+           The contents of this file are printed by the commands
+           `finger -l <user>' and 'pinky -l <user>' so .project is a
+           good place for putting information about a package. You should 
+           keep the contents of the .project files for your package users 
+           up-to-date.
+                       
+  source code:
+           The package user's home directory is the perfect place for storing
+           a package's source code. This includes tarballs for different
+           versions, CVS checkouts, unpacked source trees for building,...
+           
+  build script(s):
+           Package user installations require more careful examination of build 
+           and install messages than installations done as root, because of
+           the package user-specific problems that can occur. Therefore it is
+           unwise to simply copy'n'paste installation instructions from the
+           LFS book. Build scripts allow you to use sophisticated output
+           redirection for logging purposes that is impractical for direct
+           entry on the command line. The build script skeleton included in
+           the more_control_helpers archive demonstrates this.
+                       
+
+############################################################################
+ 4. Common Problems
+############################################################################
+
+ 4.1 Introduction
+ ----------------
+
+Software installation is the crux of the package user system. Because 
+installation scripts are often written under the assumption that they will be 
+executed as root, they sometimes fail when executed by a package user.
+Once this hurdle is passed and a package has been installed, there's usually no 
+difference to a root-installation. A few programs insist that certain 
+security-sensitive files be owned by root and will not execute otherwise,
+but this is the rare exception. 
+This chapter presents some more or less common problems that you will 
+encounter when using package user accounts to install software, together with
+guidelines on how to deal with these issues. 
+Although I've said it before I will say it again: Many of the problems you
+encounter during a package user installation are desirable features of the
+package user system. You want installation to fail rather than have 
+potentially dangerous actions performed behind your back with root rights.
+
+ 4.2 General Procedure
+ ---------------------
+When an installation fails it is almost always due to a "Permission denied"
+or "Operation not permitted" error while executing a command during
+`make install'. The first thing you have to do is identify the command that
+is causing the problem. Usually you will find this in the make output right
+before the error message. Once you have identified the culprit, you have to
+decide whether the action that is attempted is illegitimate, partially
+legitimate or completely legitimate. Illegitimate commands can simply be
+removed from the Makefile. The other 2 possibilities are more difficult to 
+deal with. You either have to change the condition that makes the command fail
+or you have to change or sometimes remove the command and make a note if your
+change suppresses a legitimate action.
+
+After you've made changes to solve a certain problem, you reattempt the
+installation and solve any remaining problems until the installation
+succeeds. Once you've reached that point it is time to perform any remaining
+legitimate actions that you've had to disable, such as make certain binaries
+setuid root.
+
+Note that often Makefiles are generated during the configure step, sometimes
+even later in the build process. If you want to apply changes before the
+configure step you will usually have to edit files called "Makefile.in".
+
+ 4.3 Permission Changes
+ ----------------------
+
+Some unsophisticated build systems that don't use the mkinstalldirs script to 
+create installation target directories are very poorly written. Instead of 
+testing whether a target directory exists, they simply attempt to create 
+it with default permissions. This problem usually manifests as a line such
+as "install -d $(prefix)/bin" in the Makefile. In the common case where 
+prefix=/usr this would attempt to create the /usr/bin directory. If the target
+directory already exists, as in this case, install will attempt to change its
+permissions to the default permissions (or those passed on the command line).
+Of course a package user is not allowed to change the permissions of
+/usr/bin and so the command fails with a message like
+"install: cannot change permissions of `/usr/bin': Operation not permitted"
+This is an example of a completely illegitimate command. Just remove it from
+the Makefile and everything's fine.
+
+ 4.4 Ownership Changes
+ ---------------------
+
+The most common situation when a package wants to change the ownership of
+files during installation is when it wants to install setuid root binaries.
+A common command to do this would be something like
+"install -c -m 4755 -o root name /usr/bin/name" and the error message would
+look like this:
+"install: cannot change ownership of `name': Operation not permitted"
+The change of ownership is hidden in the "-o root" switch to install, which
+tells it to make the target file owned by root.
+The command is at least partially legitimate, because you probably want the 
+binary to be installed. Whether you actually want it to be setuid root is 
+a different matter. The fact that a binary is commonly installed as setuid
+root doesn't mean that you should make it so. You'll have to ask yourself if
+normal users absolutely need to execute that binary. If you think they can
+live without it you're better off not making it setuid root, because every
+setuid root binary is a potential security hole. In any case you will
+have to edit the Makefile and remove the offending switch, "-o root" in this 
+case, so that the installation can succeed. Note that this will cause the
+binary to be installed setuid <package>, which of course makes no sense at all.
+If you don't intend to make the binary setuid root after the installation, it
+is best to change the "-m 4755" to "-m 755", so that it won't be installed
+setuid at all.
+
+TIP:
+  When you make a binary setuid root after the installation, use
+  `chown root /usr/bin/name' and not `chown root:root /usr/bin/name'.
+  This way you can keep original group of the file (i.e. the group of the 
+  package user) intact. With the user name = group name scheme recommended for
+  package users this makes sure that you can identify the source package of
+  the binary even after making it setuid root.
+  Note that as a security measure chown resets the setuid bit,
+  so you will have to do `chmod u+s /usr/bin/name' after the chown.
+
+ 4.5 Write to Non-Install Directory
+ ----------------------------------
+
+Sometimes packages want to create files or directories in non-install 
+directories. 3 situations have to be distinguished in this case. The 1st
+possibility is that the target directory should be an install directory.
+An example of this is /usr/share/aclocal. This directory is not among the
+standard system directories created when building an LFS system. It will be
+created by the first package that has files to install there and will be
+owned by the corresponding package user. The next package that wants to write
+in it will fail to install. The remedy is simple. Just make the directory an
+install directory. You don't even need to be root to do it. The package user
+that owns the directory has the rights to make that change.
+
+The 2nd possible reason for a package wanting to write to a non-install
+directory is that the failing command is only partially legitimate, i.e. you
+do want to have installed whatever it is meant to install, but you want it in
+a different location. For example some packages install binaries that are not
+meant to be called directly. The default location for these binaries is
+sometimes called libexec and with prefix=/usr the package will attempt to 
+create /usr/libexec. In cases such as this you often don't have to change 
+any Makefiles. There is either a configure switch to change the directory in
+question or it is just a matter of overriding a Makefile variable as in
+`make libexecdir=/usr/lib install'.
+
+The 3rd possible reason for an attempt to write to a non-install directory is
+that the command in question is illegitimate, i.e. you don't want to have
+installed whatever the package wants to install. As usual with illegitimate
+commands you can edit the Makefile and just remove them. In the case of
+a whole directory whose installation you want to suppress it could be too
+much effort to remove all of the offending commands that want to install
+files there. In this case an approach similar to that from the previous
+paragraph can be more effective. Either through configure switches or 
+overriding of variables you change the directory in question to something
+like <builddir>/foobar, where <builddir> is the directory in which build 
+commands are run (i.e. usually the top of the unpackaged source
+tree). This will cause the package to create the unwanted directory inside
+the build tree, which doesn't cause any permission problems and has the nice
+side effect that it'll be deleted together with the build directory when you
+clean up after the build.
+
+
+ 4.6 Delete or Overwrite File
+ ----------------------------
+
+In a perfect world one package should not mess with another package's files,
+but in the real world conflicts do happen occasionally. While a normal
+sysadmin installing as root won't notice this until it's too late, an admin
+employing the package user system will have to deal with conflicts right away.
+When a package tries to overwrite or delete a file or directory that is owned
+by another package the attempt will fail. It will fail even inside install
+directories because of the sticky bit.
+Although sometimes difficult to implement, the solution to such a conflict is
+easy to describe. You need to either remove (or rename) the old file or 
+directory before installing, or suppress the installation of the new file or 
+directory. The installation of individual binaries is sometimes easy to
+prevent. If you find a line such as "PROGRAMS=foo bar fubar barfu" in the
+Makefile and "foo" is the name of the conflicting binary, just try removing
+it from that list. That may be sufficient to prevent it from being installed.
+
+ 4.7 /sbin/ldconfig
+ ------------------
+Packages that install libraries sometimes run /sbin/ldconfig as part of their
+installation so that the dynamic libraries are properly registered on the 
+system. Because a package user is not allowed to overwrite /etc/ld.so.cache
+ldconfig fails. This failure is commonly ignored in Makefiles, but you should
+take note of it anyway, because you need to run ldconfig as root after
+the installation.
+############################################################################
+ 5. The more_control_helpers Archive
+############################################################################
+
+ 5.1 Overview
+ ------------
+
+The more_control_helpers archive contains files to help you with building and
+maintaining a system that uses the package user method. One thing that the
+more_control_helpers archive contains are some LFS-specific temporary files 
+that are only needed for the building of your LFS system and will not remain 
+installed in a permanent location. Then there are the previously mentioned 
+example files that demonstrate the suggested use of the package user home 
+directories discussed earlier. Another group of files contained in the archive 
+is a set of scripts that help with package management aspects, such as
+creating new package users and checking which files a particular package has 
+installed. Finally the more_control_helpers archive contains wrapper scripts
+for some commands that handle many of the common problems discussed in the
+previous chapter and make package user installations a lot easier.
+ 5.2 The Wrappers
+ ----------------
+The previous chapter discussed some common problems encountered during
+package user builds and how to solve them. The solution to an installation
+failure usually requires editing of one or more Makefiles. Making such changes
+manually is annoying, even if it happens only occasionally, and whenever you
+reinstall a package you have to make the changes again. Sed scripts and patches
+can help with the latter problem, but they still have to be custom fitted to 
+every package that needs them. There is a better solution, though. While there
+exist countless ways to install files, only very few are commonly used by
+packages. The 5 commands mkdir, chgrp, chown, chmod and install are responsible 
+for many of the common problems that arise during an LFS installation. This
+prompted me to write wrapper scripts for these 5 commands that recognize
+certain problematic patterns and deal with them automatically.
+
+The instructions given in this hint in the LFS-specific part will instruct you
+to install these wrappers in /usr/lib/pkgusr. If you do that and make sure 
+that this directory is the first entry in the PATH of every package user, then
+they will save you a lot of time and effort in dealing with recurring issues.
+Note that if you want to choose a directory other than /usr/lib/pkgusr for
+the wrappers, you need to be careful. Some configure scripts ignore certain
+locations. A subdirectory of /etc would not work, for instance, because /etc
+is one of these locations.  
+
+It is important that you understand the limitations of the wrapper scripts.
+They can fix some problems without user intervention, such as turning
+newly created directories in /usr/share/locale into install directories.
+But other problems by their very nature require manual intervention. When a 
+program tries to install a setuid root binary, for instance, the wrapper 
+scripts will suppress the attempt to change ownership of an installed file to 
+root. While that allows `make install' to complete without error, it is only 
+a partial solution. The wrapper scripts can not (and should not) take away
+your responsibility for deciding whether the program in question should be
+setuid root and to make it so, if necessary. To account for this, the
+wrapper scripts will output warning lines to standard error that start with
+"***" whenever they encounter a situation that needs to be reviewed.
+Following the "***" in the message will be the original command that the
+installation attempted to perform. 
+You *must* check these "***" lines, examine the affected files or directories
+and take appropriate action. Because of this it is imperative that you log
+the messages output during a package installation and check these logs
+religiously. The `build' script contained in the more_control_helpers archive
+demonstrates some useful output redirection tricks to be used for this purpose.
+The following 3 examples shall illustrate what kind of things you will have to
+look for:
+
+Example 1: "*** install -c rsh -o root -m 4775 /usr/bin/rsh"
+   This message is output by the install wrapper during the installation of
+   inetutils. The package wants to install the rsh binary setuid root. The
+   install wrapper removes the "-o root" and changes the "-m 4775" to
+   "-m 755" before passing the command on to the real install program.
+   The important thing here is the "-m 4xxx", because this wants to set the
+   setuid bit. Some install scripts throw in a "-o root" for good measure
+   when installing an otherwise normal binary. In that case it's enough that
+   the install wrapper strips out the "-o root" and you don't have to take
+   further action. But when, as in the case of inetutils, the permissions
+   indicate an attempt to make a binary setuid or setgid, then you will have to
+   investigate. You need to decide if you want rsh to be setuid root and
+   if you decide you do, you need to become root and issue commands like this:
+       
+       chown root /usr/bin/rsh
+       chmod u+s /usr/bin/rsh
+  
+TIP:
+   Be conservative with making binaries setuid. If you're unsure whether you
+   will ever use a program (as non-root), you probably don't want it to be 
+   setuid root. Keep in mind that you can always make the change later when
+   you need it. When you apply this reasoning to rsh, for instance, you'll 
+   probably end up not making it setuid root.
+
+
+Example 2: "*** chgrp tty /usr/bin/write" 
+   This is output by the chgrp wrapper during the util-linux installation.
+   The util-linux package wants to install the write program as setgid tty,
+   so that it is allowed to access other users' terminals. The chgrp wrapper
+   prevents the changing of the group and the chmod wrapper prevents the
+   setting of the setgid bit. You need to decide if you want the
+   program to be setgid and if you decide in favor of this, do as root
+   
+       chgrp tty /usr/bin/write
+       chmod g+s /usr/bin/write
+   
+    
+Example 3: "*** install -d -m 755 /sbin"
+   This is also from the util-linux installation. Util-linux, for no good
+   reason, tries to recreate the /sbin directory. The install wrapper
+   prevents this and you don't have to take any further action.
+
+ 5.3 add_package_user/install_package
+ ------------------------------------
+  
+Whenever you install a new package on your system, you first have to create
+a new user account, possibly create a new group and if you follow the advice
+from this hint about making productive use of a package user's home directory,
+you will have to set up that one, too. If you were to do all of this manually,
+it would be a lot of work. The add_package_user and install_package scripts
+in the more_control_helpers archive were written to automate this.
+
+The install_package script is the one you will normally use to prepare for
+installing a new package. It takes 3 parameters: the description of the 
+package, the name of the package user account to create and the name of the
+package user's primary group. So if you use the user=group scheme recommended 
+by this hint and are as creative with your package descriptions as I am, then 
+the command you'll use to prepare for installing package "foo" will be
+
+    install_package foo foo foo
+
+This command does 2 things. First it calls the add_package_user script with
+the provided name, group and description plus sensible default values for 
+add_package_user's other parameters. Then, after add_package_user has created 
+the package user account, install_package automatically uses the su-command
+to switch to the newly created account. If the default .bashrc and
+.bash_profile scripts you use for package users contain the command "cd" as do
+the examples in the more_control_helpers archive, you will be put right into
+your package user's home directory so that you can start installing right away.
+
+The add_package_user script is responsible for the actual work of creating
+a new package user account. Given a name, a group name and a description, it
+will create a new user account with the provided primary group and the install
+group as supplementary group. The groups will be created if necessary. 
+add_package_user takes several arguments that determine the numeric ranges from
+which it will pick the new user's UID and the GIDs for groups it needs to
+create. add_package_user does not only create the package user account. It
+will set up a home directory for it, too. You can either specify the directory
+or go with the default, which is /usr/src/<name>, where <name> is the name
+provided for the new account. If the home directory already exists, its
+ownership and that of any existing contents will be changed to the new user.
+If it doesn't exist, it will be created. 
+
+The contents of /etc/pkgusr/skel-package will be copied into the new package
+user's home directory (without overwriting pre-existing files). 
+The more_control_helpers archive contains an example of a useful skel-package
+directory. Note that symlinks are copied as symlinks, so skel-package is the
+perfect place to put .bashrc and .bash_profile symlinks to a central location
+that will ensure that all package user accounts have the same environment.
+This is especially useful to make sure that all package users have the
+wrappers directory in their PATH.
+
+  
+ 5.4 forall_direntries_from
+ --------------------------
+
+The forall_direntries_from script is a very useful tool for common package
+management tasks. It can roughly be described as a shortcut for 
+"find / -user <name> -or -group <name>  <commands>", where <name> is the
+first parameter to forall_direntries_from and <commands> are the remaining
+parameters. However, forall_direntries_from takes care of making sure that
+only relevant filesystems are scanned and shields you from certain unpleasant
+surprises such as "Oops, I forgot that -depth negates -prune and have 
+accidentally wiped out my home directory." or "Oops, I forgot to -prune /proc
+and now I'm getting parity errors on my SCSI bus.". 
+
+IMPORTANT NOTE: By default the forall_direntries_from script will only scan
+the / filesystem and will not traverse other filesystems. If you have
+relevant directories that need to be scanned on other filesystems, you will 
+need to edit the script and add the respective mount point(s) to the
+fs_to_scan list at the beginning of the script. The most likely candidate for
+addition is "/usr".
+
+Application examples:
+
+Example 1: Create a tar.gz archive of all files that belong to <package>, e.g.
+           for installing <package> on another machine without having to 
+           recompile it there.
+
+  forall_direntries_from <package> -fprint0 /tmp/files.lst
+  tar --null -P -czf /tmp/archive.tar.gz --files-from=/tmp/files.lst
+
+
+Example 2: Print out all setuid root binaries installed by <package>.
+           (This only works if you use the user=group scheme.)
+  
+  forall_direntries_from <package> -perm +u+s -print
+
+
+Example 3: List all binaries in /bin and /usr/bin belonging to "me" (i.e. the 
+           package user executing the command) in alphabetical order.
+
+  forall_direntries_from $(whoami) -path "*/bin/*" -printf "%f\n" | sort
+
+
+Example 4: Uninstall <package>.
+          
+  See following section about the uninstall_package script.
+                                   
+ 5.5 uninstall_package
+ ---------------------
+
+The uninstall_package script is basically a forall_direntries_from
+application example in script form. The command `uninstall_package foo'
+prints out the forall_direntries_from call that you have to use to delete
+all the files of package "foo" (except for those in directories that 
+forall_direntries_from is instructed not to scan) together with some
+explanations. So in order to delete the files from package foo, you would 
+execute `uninstall_package foo' and then copy'n'paste the command it prints
+to the command line. As a safeguard the forall_direntries_from call has an 
+"echo" in front of the "rm" and "rmdir" calls, so if you execute it, the files 
+will not actually be deleted unless you remove both instances of "echo". 
+It is recommended that you execute the command once with the echos and check 
+the output to make sure that only the files you intend to be deleted are in 
+the list. After you've confirmed that, you can use the shell's history to 
+recall the command, edit out the instances of "echo" and remove the files 
+for real.
+
+
+ 5.6 list_suspicious_files/list_suspicious_files_from
+ ----------------------------------------------------
+
+list_suspicious_files looks for filesystem entries that are out of the ordinary
+in some way and prints a categorized list of them. Things that qualify as
+suspicious include setuid and setgid binaries, world-writable files, symlinks 
+that are possibly broken, hard links, install directories with unusual 
+permissions and other stuff. You should run this script after you've finished 
+your new LFS system and in regular intervals after that. Investigate the
+listing closely.
+
+TIP: 
+  When you check the list of setuid and setgid files, don't forget to
+  look at the actual user or group ownership of the file. It's easy to forget
+  that, especially in the setuid case, because we often equate setuid with
+  setuid root since setuid is seldom used with other user accounts.
+
+list_suspicious_files_from takes a user or group name or a UID/GID as an 
+argument and reports suspicious entries only when they are owned by the given 
+user or group. Usually you would not call this script directly but instead
+use list_package (described in the next section), whose output includes that 
+from list_suspicious_files_from.
+
+IMPORTANT NOTE: By default the list_suspicious_files script will only scan
+the / filesystem and will not traverse other filesystems. If you have
+relevant directories that need to be scanned on other filesystems, you will 
+need to edit the script and add the respective mount point(s) to the
+fs_to_scan list at the beginning of the script. The most likely candidate for
+addition is "/usr".
+
+
+ 5.7 list_package
+ ----------------
+
+list_package tells you everything about a package's installed files. In
+general you will want to execute something like 
+
+   list_package $(whoami) >pkg.lst
+   
+right after installing a package and you can forget about the chronically
+inaccurate content listings in the (B)LFS book. 
+The following (shortened) output for util-linux speaks for itself:
+
+PS1> list_package util-linux
+
+EXECUTABLES (in */bin or */sbin)
+  agetty, arch, blockdev, cal, cfdisk, [...] vidmode(->rdev), whereis, write
+
+EXECUTABLES WITH NO MANPAGE (in */bin or */sbin)
+  fsck.cramfs, mkfs.cramfs
+
+MANPAGE SUMMARIES OF EXECUTABLES (in */bin or */sbin)
+  agetty: alternative Linux getty
+  arch: print machine architecture
+  blockdev: call block device ioctls from the command line
+  cal: displays a calendar
+  cfdisk: Curses based disk partition table manipulator for Linux
+  chkdupexe: find duplicate executables
+  col: filter reverse line feeds from input
+  [...]
+  swapon: enable/disable devices and files for paging and swapping
+  tailf: follow the growth of a log file
+  tunelp: set various parameters for the lp device
+  ul: do underlining
+  umount: unmount file systems
+  vidmode: query/set image root device, RAM disk size, or video mode
+  whereis: locate the binary, source, and manual page files for a command
+  write: send a message to another user
+
+EXTRA MANPAGES
+  /usr/share/man/man5/fstab.5
+  /usr/share/man/man5/nfs.5
+  /usr/share/man/man8/sln.8
+
+EXTRA EXECUTABLES (not in */bin or */sbin)
+  /usr/share/misc/getopt/getopt-parse.bash
+  /usr/share/misc/getopt/getopt-parse.tcsh
+  /usr/share/misc/getopt/getopt-test.bash
+  /usr/share/misc/getopt/getopt-test.tcsh
+
+ALL FILES
+  /etc/fdprm
+  /sbin/agetty
+  /sbin/blockdev
+  /sbin/cfdisk
+  /sbin/ctrlaltdel
+  /sbin/elvtune
+  /sbin/fdisk
+  /sbin/fsck.cramfs
+  /sbin/fsck.minix
+  /sbin/hwclock
+  /sbin/losetup
+  /sbin/mkfs
+  /sbin/mkfs.bfs
+  [...]
+  /usr/share/man/man8/rootflags.8
+  /usr/share/man/man8/setfdprm.8
+  /usr/share/man/man8/setsid.8
+  /usr/share/man/man8/sfdisk.8
+  /usr/share/man/man8/sln.8
+  /usr/share/man/man8/swapoff.8
+  /usr/share/man/man8/swapon.8
+  /usr/share/man/man8/tunelp.8
+  /usr/share/man/man8/umount.8
+  /usr/share/man/man8/vidmode.8
+  /usr/share/misc/getopt
+  /usr/share/misc/getopt/getopt-parse.bash
+  /usr/share/misc/getopt/getopt-parse.tcsh
+  /usr/share/misc/getopt/getopt-test.bash
+  /usr/share/misc/getopt/getopt-test.tcsh
+
+SETUID FILES
+  -rwsr-xr-x "/usr/bin/mount"  root:util-linux
+  -rwsr-xr-x "/usr/bin/umount"  root:util-linux
+
+SETGID FILES
+  -rwxr-sr-x "/usr/bin/write"  util-linux:tty
+
+FILES WITH UNUSUAL PERMISSIONS
+  -rwsr-xr-x "/usr/bin/mount"  root:util-linux
+  -rwsr-xr-x "/usr/bin/umount"  root:util-linux
+  -rwxr-sr-x "/usr/bin/write"  util-linux:tty
+
+
+Note: list_package works regardless of the prefix you've installed the package
+      with, so you can for instance configure with --prefix=/opt/package and
+      list_package will work just fine (provided that /opt is on a
+      filesystem configured to be scanned by forall_direntries_from and
+      list_suspicious_files).
+
+Note: list_package only considers manpages actually owned by the package to 
+      list. It will not consider manpages installed by another package. This
+      means that you may see executables identified as not having a manpage
+      although they do have one courtesy of another package 
+      (usually man-pages).
+
+
+ 5.8 grep_all_regular_files_for
+ ------------------------------
+
+This script is not really related to the package user system, but because of
+its similarity to the other scripts I've included it anyway. The sole purpose
+of this script is to identify files that store references to the build 
+environment, specifically the /tools directory. Such references may point out
+problems, since the /tools directory is supposed to be transient.
+Don't forget that results for unstripped binaries and libraries are not 
+reliable, because debugging information often includes references to the
+build environment. These do not cause trouble (unless you're trying to debug
+the objects in question after deleting /tools).
+  
+IMPORTANT NOTE: By default the grep_all_regular_files_for script will only scan
+the / filesystem and will not traverse other filesystems. If you have
+relevant directories that need to be scanned on other filesystems, you will 
+need to edit the script and add the respective mount point(s) to the
+fs_to_scan list at the beginning of the script. The most likely candidate for
+addition is "/usr".  
+  
+  
+ 5.9 The etc Directory
+ ---------------------
+If you follow the instructions provided in the LFS-specific part of this hint,
+the contents of the etc directory will be installed in /etc/pkgusr. The
+directory contains a bashrc and bash_profile for package users that takes
+care of package user specific details such as putting the wrappers directory
+at the beginning of the PATH and calling cd, so that `su <package>' will
+put you right into the package user's home directory. Also contained in the
+etc directory is a skel-package directory as used by 
+install_package/add_package_user to populate the home directories of newly
+created package users.
+ 5.10 Temporary Files 
+ --------------------
+
+3 files in the more_control_helpers archive are only used during the 
+installation of the base LFS system and are not installed permanently.
+The first of them is the installdirs.lst file that contains a list of 
+directories that should be install directories. 
+The second file is sbin/useradd, which is a very primitive shell script that
+adds a new entry to /etc/passwd. It allows us to add package users before
+we have installed shadow, which provides a real useradd.
+Finally there is groupadd, which is like useradd, only for /etc/group.
+Both scripts, useradd as well as groupadd, do very little error checking and
+only support the syntax needed by install_package/add_package_user. So don't
+try anything funky with them.
+
+  
+------------------------ PART 2: LFS Specifics ------------------------------
+
+
+#############################################################################
+ 6. Pre-Chroot Phase (Chapter 5)
+#############################################################################
+
+Build Chapter 5 exactly as explained by the LFS book. There is only one
+little change you have to make. After running `make install' for the coreutils
+package, issue the following command (still from within the coreutils
+build directory):
+
+    cp src/su /tools/bin
+    
+This installs the su binary. Coreutils doesn't install su when working as
+non-root (which we do in Chapter 5), because su needs to be setuid root for
+normal operation and a non-root user cannot install setuid root binaries. 
+But for our purposes (i.e. su'ing from root to a package user) a non-setuid 
+su is enough, so we just copy coreutils' su to /tools/bin without making it
+setuid root.
+    
+When you have reached the end of Chapter 5, before you begin with Chapter 6 
+you will need to install the helper scripts in the /tools directory so that
+they are available once you've entered the chroot environment. Use the
+following commands to install the more_control_helpers in /tools:
+
+    cd /tools &&
+    tar xjf /path/to/more_control_helpers.tar.bz2 &&
+    cd  more_control_helpers &&
+    cp ./sbin/* /tools/bin
+    
+Note that the target directory is "/tools/bin" in the cp command and 
+*not* "/tools/sbin", although the latter location would be more appropriate.
+The reason for this is simply that the LFS instructions do not add 
+"/tools/sbin" to the PATH (and neither do the instructions in this hint).
+
+
+#############################################################################
+ 7. Chroot Phase (Chapter 6)
+#############################################################################
+
+ 7.1 Preparations
+ ----------------
+
+Enter the chroot environment and follow the instructions from the book up to
+but *not* including the installation of the first package (which at the time of
+this writing is linux-libc-headers). Now install the more_control_helpers
+files in their proper locations on the new LFS system:
+
+    cp -a /tools/more_control_helpers/etc /etc/pkgusr &&
+    chown -R 0:0 /etc/pkgusr &&
+    cp -a /tools/more_control_helpers/lib /usr/lib/pkgusr &&
+    chown -R 0:0 /usr/lib/pkgusr &&
+    cp /tools/more_control_helpers/bin/* /usr/bin &&
+    cp /tools/more_control_helpers/sbin/* /usr/sbin &&
+    rm /usr/sbin/{useradd,groupadd}
+
+Note that the useradd and groupadd scripts are not installed on the new LFS 
+system. These scripts are just temporary workarounds we will use as long as 
+the real useradd and groupadd are not available. Therefore they should only 
+be in /tools/bin.
+
+ATTENTION! If you decide to use a different directory than /usr/lib/pkgusr
+for the wrappers, you have to be careful, because at least the glibc
+configure script ignores certain directories when looking for programs. The
+list of ignored directories for glibc includes, among others, everything that 
+starts with "/etc", "/usr/etc" and "/sbin". Wrappers put into a directory that
+matches any of these patterns would be ineffective.
+
+Now it's time to create the install group:
+
+    groupadd -g 9999 install
+
+The GID 9999 has been chosen because the default range used by 
+add_package_user for package user GIDs starts at 10000. Choose whatever number
+you like.
+
+Once the install group has been created you have to turn all the directories
+that packages will install files in into install directories. To make this
+easier I have compiled a list of install directories that can be found in
+the file /tools/more_control_helpers/installdirs.lst. The following command
+uses this list to assign the necessary directories to the install group.
+Note that you will get several error messages because of non-existent
+directories. This is because the list contains some directories not created
+by LFS.
+
+    chown 0:9999 $(cat /tools/more_control_helpers/installdirs.lst)
+
+To be usable by package users, the directories will have to be group-writable
+and should be sticky as has been explained in the beginning of this hint.
+The following command sets the permissions appropriately.
+You will get the same error messages as for the previous command.
+
+    chmod ug=rwx,o=rxt $(cat /tools/more_control_helpers/installdirs.lst)
+
+
+ 7.2 Walkthrough: Installing linux-libc-headers
+ ----------------------------------------------
+
+At this point everything has been set up for creating the first package
+user. At the time of this writing the first package installed in the LFS
+book is Linux-Libc-Headers, so this package will serve as an example for how 
+things are done. The command
+
+     install_package 'Linux Headers' linux-libc-headers linux-libc-headers
+
+will create a package user with user and group name linux-libc-headers.
+If you don't want to use the user=group scheme, change the last argument to
+the desired group name. The description is arbitrary but needs to meet the
+requirements for the description field of an /etc/passwd entry.
+
+The directory /usr/src/linux-libc-headers will be set up as the home directory
+for the package user, automatically populated with the contents of 
+/etc/pkgusr/skel-package. The install_package command also issues the command
+`su linux-libc-headers' to assume the identity of the newly created package
+user. If you're using the bashrc and bash_profile scripts from the
+more_control_helpers archive, you will be put straight into the directory 
+/usr/src/linux-libc-headers and your prompt will look like this
+
+package linux-libc-headers:/usr/src/linux-libc-headers>
+     
+to show you that you're working as package user linux-libc-headers and
+that your current working directory is /usr/src/linux-libc-headers.
+
+Use the command 
+   
+     echo $PATH
+
+to verify that your PATH starts with "/usr/lib/pkgusr", the directory that 
+contains the wrappers, and ends with "/tools/bin".
+
+Now everything is prepared for installing the package according to the
+instructions in the LFS book. Note that at the time of this writing the 
+LFS book tells you to execute a chown command to make sure that the headers 
+are owned by root. This is just because the packager has made a very common 
+mistake when creating the tarball for the headers: He has archived the files 
+with a non-root user/group assignment. When unpacking such a tarball as root, 
+the files end up being owned by some weird user/group combination, which may 
+open a security hole. When you're working as a package user this can not 
+happen and you don't want to chown the headers to root:root, because that 
+would defeat the whole point of installing the headers with a package user.
+This is one of the small points on which you will have to deviate from the 
+standard LFS instructions when using package users. More package user related
+issues with the current LFS book can be found in the next section.
+
+After you've installed the headers, simply issue the command
+
+    exit
+    
+to become root again. Now would be a good time to think about useful
+customizations for /etc/pkgusr/{bash_profile,bashrc} and/or 
+/etc/pkgusr/skel-package, if you've not already customized them.
+Once you're satisfied with your setup, install the rest of the packages.
+The following section will help you with some problems that you will run into.
+
+
+ 7.3 Known Issues with LFS Packages
+ ----------------------------------
+
+This section has details on the package user related problems you will face
+when building your LFS system. You should copy the information from this
+section to the INSTALL NOTES of the relevant .project files for the packages 
+concerned, together with any of your own notes.
+
+NOTE: If you're building by an LFS book later than 6.0 it is recommended that
+      you read this complete chapter before you start building any packages.
+      If your LFS version is 6.0 then it's fine to read this section package
+      by package as you progress with your build.
+
+
+linux-libc-headers:
+    At the time of this writing the LFS book tells you to execute a chown 
+    command to make sure that the headers are owned by root. This is just 
+    because the packager has made a very common mistake when creating the 
+    tarball for the headers: He has archived the files with a non-root 
+    user/group assignment. When unpacking such a tarball as root, the files 
+    end up being owned by some weird user/group combination, which may open 
+    a security hole. When you're working as a package user this can not happen 
+    and you don't want to chown the headers to root:root, because that would 
+    defeat the whole point of installing the headers with a package user.
+    
+    There's another packaging error in the linux-libc-headers archive.
+    The files are stored with incorrect permissions. They are supposed to
+    be world-readable, but they are not. The book's instructions already
+    tell you how to correct this but I point it out, because this error will 
+    resurface a little later.
+
+
+man-pages:
+    If the name you use for the man-pages package user is not exactly 
+    "man-pages", then you will have to change the variable "manpagesowner"
+    right at the beginning of the wrapper script `install'.
+
+    Recent versions of man-pages contain POSIX manpages that the package
+    tries to install in /usr/share/man/man{0,1,3}p. As /usr/share/man is
+    not an install directory and the LFS book does not have instructions to
+    create these directories at the time of this writing, the installation 
+    will fail and the respective man-pages will not be installed.
+    Possible remedies:
+      1. Make /usr/share/man an install directory. 
+         Consequence: All Packages will be able to create new subdirectories
+         in /usr/share/man. I find this undesirable because there are packages
+         that create directories for manpages in foreign languages that I
+         don't want. YMMV.
+      2. Ignore the problem and live without the POSIX manpages. Unless
+         you are a developer (including script writer) who is interested
+         in writing portable programs/scripts this is a good solution.
+      3. Create the directories /usr/share/man/man{0,1,3}p as root
+         prior to installing man-pages. You'll have to either chown them
+         to the man-pages package user or make them install directories.
+         This is my preferred solution.
+
+
+glibc:
+    The packaging error of libc-linux-headers described earlier also affects
+    the glibc build. Because of the error, the headers in /tools/include
+    are not world-readable. Unfortunately the LFS book (as of this writing)
+    does not correct this in Chapter 5 like it does in Chapter 6. For a
+    standard LFS build this is no problem, because glibc is built as root and
+    root can access everything regardless of permissions.
+    The glibc package user, however, does not have permission to access
+    these headers. This will cause several configure tests to fail, because
+    the respective test programs can not be compiled.
+    The end result is the error message "/lib/cpp fails sanity check", which
+    is completely nonsensical as we don't have a /lib/cpp.
+    
+    This is the perfect opportunity to introduce rule #1 of error diagnostics:
+    
+       NEVER TRUST DIAGNOSTIC MESSAGES !
+       
+    There are 2 kinds of diagnostic messages:
+    
+      1. Those that are unnecessary, because once you see which component has
+         failed, the source of the problem is obvious.
+      2. Those that grossly misdiagnose the source of the problem and lead
+         you to draw the wrong conclusions.
+    
+    No, there is no other kind. Trust me ;-)
+    In this case, /lib/cpp has nothing to do with the problem. It doesn't
+    exist and that's fine. The message just wants to trick you into doing
+    something stupid such as create a symlink /lib/cpp -> /tools/bin/cpp.
+    But that would be totally wrong. Before you jump to any premature 
+    conclusions you should always try to get as much *low-level* information 
+    as you can. Diagnostic messages are *high-level* information. They 
+    represent a filtered view of the problem, which is usually of little help.
+    Fortunately the message (the complete one, not the part quoted above) also
+    points at the source for the necessary low-level information. In this 
+    case that is the file config.log (not to be confused with configure.log, 
+    the file created by the build script included in the more_control_helpers 
+    archive).
+    config.log is created by all autoconf-created configures (not just that 
+    of glibc) and it contains, among other things, the test programs used by 
+    configure and messages output while building and running them. Whenever a 
+    configure script fails or gives weird results, check config.log. And 
+    always remember rule #2 of error diagnostics
+    
+       ALWAYS START AT THE FIRST ERROR
+       
+    This seems pretty obvious, but nevertheless people commonly do the exact
+    opposite. It's just too tempting to start at the point of the final 
+    failure and try to work backwards. In this case many people would open 
+    config.log and scroll to the point of the failed /lib/cpp sanity check.
+    After all, that's what caused configure to abort and so that's what needs
+    to be fixed, right?  WRONG! Someone who takes this approach just sees the 
+    error message "/lib/cpp: No such file or directory" and is even more 
+    convinced that a missing /lib/cpp symlink (or program) is the problem.
+    
+    The correct way to approach such a problem is to start at the beginning
+    of config.log, to scroll down to first error message and to check if it
+    is an issue that needs to be fixed (error messages in config.log are
+    not always signs for a problem). If the issue needs to be fixed, then
+    it needs to be fixed first, because all later errors could be rooted in
+    this issue (even if, no, *especially* if you don't believe this is the 
+    case).
+    If we apply this advice to the problem at hand, we quickly get to the first
+    serious error in config.log: 
+
+      "/tools/include/linux/limits.h: Permission denied"
+    
+    A quick check with ls reveals that indeed the directory with the linux
+    headers is not world-readable, which is obviously wrong. The fix is
+    easy. Just make (as root) the header directories /tools/include/{linux,asm} 
+    world-readable with commands similar to those the LFS book presents 
+    in Chapter 6 for the installation of linux-libc-headers.
+    Once this change has been made, glibc's configure succeeds.
+
+TIP:
+    Even when configure completes successfully, you should still check the
+    output carefully to see if there is anything odd. E.g. if you're using
+    the wrappers, you should check that configure outputs the line
+    
+       checking for a BSD-compatible install... /usr/lib/pkgusr/install -c
+    
+    If configure detects a different install, such as /tools/bin/install,
+    something is wrong. Maybe there's a typo in the PATH for the package
+    user, or you've put the wrappers into a directory that is ignored by
+    configure.
+    
+
+    With the wrappers the glibc build and install should work smoothly. 
+    The wrapper script for install makes sure that the /usr/share/locale/* 
+    directories become install directories so that other programs can install 
+    their localized messages. One thing that the wrappers do not take care of, 
+    however, is the file /usr/share/info/dir. Because in the current LFS build
+    order glibc is the first package that installs info files, dir is owned by 
+    and only writable by glibc. In order to allow other packages to install 
+    info pages, execute the following commands as root:
+    
+       chown root:install /usr/share/info/dir &&
+       chmod ug=rw,o=r /usr/share/info/dir
+
+NOTE:
+    glibc wants to install the program pt_chown as setuid root. If you install
+    as a package user, the program will get installed but not given root
+    privileges (because of the install wrapper). 
+    The following info is from the glibc docs:
+          
+       One auxiliary program, `/usr/libexec/pt_chown', is installed setuid
+       `root'.  This program is invoked by the `grantpt' function; it sets the
+       permissions on a pseudoterminal so it can be used by the calling
+       process.  This means programs like `xterm' and `screen' do not have to
+       be setuid to get a pty.  (There may be other reasons why they need
+       privileges.)  If you are using a 2.1 or newer Linux kernel with the
+       `devptsfs' or `devfs' filesystems providing pty slaves, you don't need
+       this program; otherwise you do.  The source for `pt_chown' is in
+       `login/programs/pt_chown.c'.
+                               
+    So unless you're building a system that does not use devpts (which would
+    be quite unusual), this does not need to concern you.
+
+TIP:
+    In case you were wondering if you should create /etc/nsswitch.conf and
+    /etc/ld.so.conf as root or glibc, I recommend to assign all files that 
+    you manually create or manually edit to the root account. That way you can
+    distinguish between those files that can be regenerated automatically and
+    those that can not. Assigning even automatically generated files to
+    root once you make the first manual edit, ensures that a later 
+    reinstallation of a package won't silently do away with your manual tweaks.
+
+
+binutils:
+    The installation of binutils should complete without problems. 
+    It does however cause minor conflicts with autoconf (see later).
+    
+    
+gcc:
+    Because the /usr/lib/libgcc_s.so.1 symlink created at the beginning of
+    Chapter 6 is owned by root, gcc's installation cannot remove it. 
+    So you will have to remove it as root before `make install'.
+    
+    
+coreutils:
+    Because the /bin/cat, /bin/pwd and /bin/stty symlinks are owned by root,
+    coreutils' installation cannot remove them. So you will have to remove 
+    them manually before `make install'.
+
+NOTE:
+    The man-pages package has already installed manpages for the binaries
+    from coreutils. The install wrapper will prevent coreutils from overwriting
+    those. This is done because the manpages from the man-pages package are 
+    of superior quality. It also prevents errors during `make install' that
+    would otherwise occur because the coreutils package user cannot overwrite 
+    manpages owned by another user.
+    If you don't like the above behaviour and would rather have the original 
+    package manpages (despite them being inferior), you can set the variable 
+    manpagesowner at the beginning of the install wrapper to a string that
+    doesn't correspond to a package user name (it must not be empty, though!).
+    If you do this, you will have to resolve manpage conflicts in another way. 
+    The easiest way to handle this is probably to not install the man-pages 
+    package at the beginning of Chapter 6 but at the end, after all the other
+    packages have already installed their manpages. Then you need only deal 
+    with the conflicts once, when installing man-pages.
+
+
+ncurses:
+    The installation of ncurses (like that of other packages that include 
+    libraries) wants to run /sbin/ldconfig to update /etc/ld.so.cache. 
+    This fails because the package user doesn't have permission to replace 
+    /etc/ld.so.cache.
+    Making /etc/ld.so.cache group-writable by the install group doesn't help, 
+    because the permissions would be reset on the next call to /sbin/ldconfig.
+    This error will usually not abort the installation and you can just
+    run /sbin/ldconfig manually as root afterwards.
+
+
+gettext:
+    The gettext installation creates the directory /usr/share/aclocal, which
+    contains macros for autoconf. Other packages want to install 
+    files into this directory, so you should make it writable by the install 
+    group and sticky. You don't need to do this now. You can wait till you 
+    install a package that wants to write to aclocal.
+
+
+inetutils:
+    This package contains some programs that it wants to be setuid root:
+    rsh, rcp, rlogin and ping
+    The install wrapper prevents these programs from being installed
+    setuid root. You must decide which of these programs you want to be 
+    setuid root and manually make them so. Be conservative. Don't make a
+    binary setuid root unless you *know* that ordinary users can't live
+    without it. Every setuid root binary is a potential security hole.
+
+
+iproute2:
+    This package tries to change the permissions of /usr/sbin. The install
+    wrapper takes care of this.
+
+
+perl:
+    Before you do `make install', you will have to 
+    `chown perl /usr/bin/perl' so that the perl package user is allowed to
+    remove the /usr/bin/perl symlink.
+    
+    If you will install add-on packages for perl as their own package users
+    into /usr/lib/perl5/site_perl, then you will need to turn 
+    /usr/lib/perl5/site_perl/ and its subdirectories into 
+    install directories. You don't need to do this now as you'll notice it
+    anyway when installing a perl add-on fails. 
+
+
+autoconf:
+    The autoconf package wants to install its own copy of standards.info,
+    which fails because binutils has already installed this file. You can 
+    either ignore the error or remove the binutils version of standards.info 
+    before `make install'.
+
+
+bash:
+    Before you can `make install' you need to `chown bash /bin/bash' so
+    that the bash installation can replace the /bin/bash symlink.
+    When running the test suite as a package user, the test "run-test" will 
+    fail with the following output:
+
+      33d32
+      < *** chmod g+s /tmp/test.setgid
+      35c34
+      < 1
+      ---
+      > 0
+      64d62
+      < *** chmod u+s /tmp/test.setuid
+      66c64
+      < 1
+      ---
+      > 0
+      154c152
+      < 1
+      ---
+      > 0
+      160c158
+      < 1
+      ---
+      > 0
+    
+    The first 2 failures are caused by the chmod wrapper which prevents the
+    test from setting the setuid and setgid bits and outputs the *** warning.
+    The failures are harmless and will not occur if you remove the wrappers
+    directory from the PATH before running the tests.
+    
+    The last 2 failures are not specific to package users but will occur 
+    whenever the user running the test is not the user who owns the terminal 
+    as is usually the case when you use the `su' command.
+    Simply ignore these failures. They are harmless. If you insist on getting
+    the tests to succeed, you will have to use chown as root to
+    assign ownership of the tty in which you will run the tests to the 
+    user running the tests. To find out the proper terminal, use the command
+    `ls -la /proc/self/fd/1' in the terminal where you will run the tests. 
+    It will output something like
+      lrwx------  1 bash bash 64 Sep 12 21:29 /proc/self/fd/1 -> /dev/pts/2
+    In this example the tty to be chowned would be /dev/pts/2.
+    
+    
+libtool:
+    The libtool installation wants to add files to /usr/share/aclocal, so
+    if you have not made it an install directory, yet, you will have to do it 
+    now (i.e. make the directory group install, group-writable, sticky).
+      
+    
+grub:
+    The commands to create and populate /boot/grub have to be executed as
+    root.
+
+
+procps:
+    The procps installation wants to execute the command `ldconfig'. This will
+    fail for 2 reasons:
+    
+      1) A package user does not have /sbin in its PATH
+      2) Package users are not allowed to overwrite /etc/ld.so.cache
+    
+    To overcome this problem, install with 
+      
+      make ldconfig='' install
+      
+    and issue the command `/sbin/ldconfig' manually as root after installing.
+
+
+shadow:
+    shadow contains its own version of the `groups' command and accompanying
+    manpage. The installation of these conflicts with the coreutils versions.
+    As of this writing the LFS book deals with this problem in
+    the following way:
+    
+      1) coreutils' groups is installed in /usr/bin and shadow's
+         groups is installed in /bin, so it's enough to delete shadow's groups
+         after installation.
+      2) The manpage issue is simply ignored, meaning that the system will
+         end up having the coreutils version of groups but the shadow version
+         of the groups manpage.
+         
+    Number 1 will not cause trouble with package users, unless you
+    are doing things like symlinking /usr/bin and /bin to be the same. And in
+    that case the "trouble" caused, namely that shadow won't be able to
+    overwrite `groups', is actually a good thing, because it prevents you
+    from unknowingly ending up with a different `groups' command than a 
+    standard-LFS user. Issues like this are exactly what the "more control"
+    part of this hint's title is about. The package user system does not
+    allow things like this to happen behind your back.
+    
+    Number 2 is probably not intentional. It's just one of those things that
+    people who don't use the package user system never become aware of and so
+    it has managed to escape the attention of the LFS testers. So once again 
+    the installation failure caused by the package user system, although 
+    annoying, is a desirable feature.
+    
+    To deal with both groups-issues, simply prevent shadow from installing
+    groups and its manpage. Execute the following commands *after* the
+    configure step, because the Makefiles don't exist until then.
+    
+      sed -i 's/groups.1//' man/Makefile
+      sed -i '/^bin_PROGRAMS/s/groups//' src/Makefile
+
+    By default shadow wants to install non-English manpages. This fails
+    because the /usr/share/man directory is not an install directory and
+    therefore package users are not allowed to create new subdirectories in it.
+    To solve this problem, before you `make install', open the file
+    man/Makefile, find the line 
+    
+      SUBDIRS = cs de es fr hu id it ja ko pl pt_BR ru zh_CN zh_TW
+    
+    and remove all the languages that you don't want to install. For those
+    languages that you do want to install, create directories with the
+    respective names in /usr/share/man as root and make them install
+    directories (i.e. group install, group-writable, sticky).
+    
+    At the time of this writing the coreutils patch used in LFS prevents the 
+    installation of the su binary, but not of its manpage. This is probably
+    another buglet in LFS that is exposed by the package user system. 
+    Whatever the reason, you will have to remove the su.1 manpage manually 
+    as root before shadow can be installed:
+    
+      rm /usr/share/man/man1/su.1
+    
+    There is yet another issue with shadow concerning manpages. The shadow
+    package contains a passwd.5 manpage. Installation of this manpage is
+    automatically suppressed by the install wrapper, because it would 
+    overwrite the passwd.5 manpage provided by the man-pages package. As usual
+    the man-pages version is better, so you can simply ignore this issue.
+
+    shadow wants to install the programs su, chage, chfn, chsh, expiry, 
+    gpasswd, newgrp and passwd as setuid root. You will need to decide which 
+    of these programs you want to be setuid root and manually make them so. 
+
+
+sysklogd:
+    sysklogd's Makefile has /usr/bin/install hardwired as the install
+    program, which circumvents the install wrapper. The wrapper is needed 
+    for sysklogd because it tries to make its manpages owned by root 
+    (which obviously a package user is not allowed to do). 
+    Therefore, install with
+    
+      make INSTALL=install install
+
+
+sysvinit:
+    sysvinit's installation wants to create /dev/initctl if it does not exist, 
+    but a package user does not have permission to do that, so create
+    /dev/initctl manually as root before installing:
+    
+        rm -f /dev/initctl
+        mkfifo /dev/initctl
+        chmod 600 /dev/initctl
+                                                  
+
+udev:
+    udev wants to recreate the /dev directory, although it already exists.
+    Since a package user cannot do that, the installation fails. To fix this,
+    kill the line in the Makefile that's responsible:
+    
+      sed -i '/\$(INSTALL) -d \$(DESTDIR)\$(udevdir)/d' Makefile
+    
+    NOTE: udev's Makefile is read-only, but apparently sed doesn't care about
+    this. If you want to edit the Makefile in another way (or if you're using
+    a sed version that doesn't have this, IMHO buggy, behaviour), you will 
+    have to `chmod u+w Makefile' first.
+
+
+util-linux:
+    util-linux wants to install write as setgid tty and u/mount as
+    setuid root. The wrappers catch this, so it doesn't cause the install to
+    fail, but as usual you'll have to decide if you want these programs to
+    have special privileges and take manual action as root if you do.
+
+
+##########################################################################  
+ 8. Sanity Checks 
+##########################################################################
+
+ 8.1 Suspicious Files
+ --------------------
+You probably ran the `list_package' command for each package and reviewed
+the results which include the suspicious files owned by that package. But even
+if you did that it's still a good idea to run the non-package specific 
+`list_suspicious_files' command once your build is complete. There could be
+something you overlooked the first time, or maybe you created a file as root
+with the wrong permissions. It doesn't hurt to check again and this will also
+give you the opportunity to review any setuid/setgid decisions you made with
+respect to the installed binaries.
+
+TIP: 
+  When you check the list of setuid and setgid files, don't forget to
+  look at the actual user or group ownership of the file. It's easy to forget
+  that, especially in the setuid case, because we often equate setuid with
+  setuid root since setuid is seldom used with other user accounts.
+
+  
+ 8.2 References to Temporary Files
+ ---------------------------------
+
+One big concern when building an LFS system is independence of the new LFS
+system from the files installed in /tools. The /tools directory is intended 
+to be temporary and it should be possible to delete it after building your
+LFS system with no adverse side effects. The `grep_all_regular_files_for'
+script from the more_control_helpers package can help you verify that your
+new LFS system is indeed clean. The command
+
+     grep_all_regular_files_for /tools
+
+will give you a list of all files that contain the string "/tools". Review the
+files in the list to make sure that no dependencies on the temporary files in
+/tools have crept in. But remember that results from binaries and libraries 
+are only meaningful after stripping away the debug information, because
+debug information necessarily includes references to the build environment.
+Of course, if you are a developer who will potentially run gdb on system
+libraries/binaries, your position will be that stripping away debug information
+is the wrong way to do away with /tools references. The other way to deal with
+them is to rebuild packages for which /tools references are reported. The new
+build will not involve any files from /tools and so the new debug information
+will not refer to /tools. Note that the LFS build instructions for glibc
+make glibc compile against /tools/glibc-kernheaders. Unless you copy the
+glibc-kernheaders directory to a location outside of /tools and compile glibc
+against that copy, you won't get rid of the /tools references in glibc's 
+debug information.
+No matter what means you choose to deal with the debug information issue, in 
+the end you should have a system where the above command produces only false 
+positives (such as "perlfaq3.1", which includes the URL
+"http://www.research.att.com/sw/tools/uwin/") and files that legitimately
+refer to /tools (such as a copy of this hint file).
+
+
+----------------------------- APPENDICES ----------------------------------
+
+
+###########################################################################
+ Appendix A: Security Issues
+###########################################################################
+
+ A.1 NFS
+ -------
+
+If you use the network filesystem NFS, there are some things you need to
+look out for when using the package user system. A fundamental security 
+problem with NFS is that it blindly trusts the UID and GID of the client. 
+If an attacker can get access to the root account on a system in your network 
+that is allowed to mount NFS shares from your server, or if the attacker can 
+attach his own computer to your network, then this attacker can pretend to be 
+anyone. NFS will happily allow the attacker to work in the NFS exported 
+directory as any user he wants to be. The only exception is the root account. 
+By default NFS exports directories with the root_squash option that maps all 
+incoming requests from uid 0 to anonuid (65534 unless set in /etc/exports) 
+and gid 0 to anongid (65534 unless set in /etc/exports). This protects files 
+owned by root:root. On a normal system this includes most files in /bin, /etc,
+/lib and most other directories except /home. If you use the package user 
+scheme, however, most of these files are owned by package users. These files 
+are not protected by the root_squash option. In order to make NFS exports 
+secure, you have to add the option "all_squash" to every entry in /etc/exports 
+that exports a directory that contains files owned by package users. Note that 
+all_squash is always a good idea because even systems that don't use package 
+users often have some programs owned by other users or groups, because they 
+need to be setuid or setgid.
+
+
+ A.2 Daemons
+ -----------
+
+It is a common practice to run daemons under special user accounts rather
+than as root as a security measure. If you feel tempted to use a package 
+user account for this purpose, resist the temptation. It would be a very
+stupid idea. Although they are deliberately less powerful than root, package 
+user accounts are still privileged and need to be considered as equivalent to 
+root as far as security is concerned. Do not do anything with a package user 
+that on a system without package users you would not do with the root account.
+
+
+###########################################################################
+ Appendix B: Package Categories
+###########################################################################
+
+Although the user name = group name scheme is recommended by this hint, it is
+not the only possible one. Another scheme that has some appeal is to define
+package categories and to use the group for the purpose of categorizing the
+packages. Following is a suggested set of categories that can serve as a
+guideline for implementing this scheme.
+
+devel: development related stuff, e.g. compilers. This is not restricted to
+       software development. TeX for instance would belong in this group.
+       
+utils: Most software fits into this category, even somewhat essential software 
+       like grep or text editors.
+      
+net: network related stuff such as an ftp daemon or a web browser. This
+     group overlaps with other groups to a large extent. It should be used
+     in preference of the other groups whenever a package is clearly focused
+     towards Internet, LAN, WWW,... A utility like wget for instance would
+     go in net rather than utils. Exceptions from this rule are the groups
+     docs, addons, games and mmedia. If a package fits into one of those 
+     groups, use the respective group instead of net.
+     
+docs: Documentation related packages, such as a tarball with Linux howtos.
+      Note that software to create documentation such as XML processors should
+      probably go in devel and software to view or post-process documentation
+      such as man or groff should probably go in utils.
+      
+system: important system software, such as bash. This group should be used
+        only for really essential packages. Most packages you would put in 
+        this group are better put in "utils". Vi for instance belongs in 
+        utils. 
+        It is unlikely that any package not part of basic LFS belongs in the
+        system group.
+        
+libs: What utils is for executables, libs is for libraries. Libraries that are
+      not strongly related to any of the other categories should go here, such
+      as zlib or libpng.
+      Essential system libraries such as glibc, ncurses or gettext should
+      go in system instead.
+      The libs group is also used for run-time environments such as the
+      Java Virtual Machine, dosemu and wine. Other emulators like MAME for
+      instance should probably go into games instead. 
+     
+games: what do you expect ;-)
+
+mmedia: This is the group for audio and video editors, mp3 players etc.
+
+apps: Applications such as spreadsheets and word processors (not text editors)
+      but also CAD software and graphics software such as Gimp.
+      The apps group is a bit like utils, but apps are usually more user 
+      friendly and more streamlined and feel less nerdish than utils. 
+      
+addons: plugins, filters and similar that are meant to be used in conjunction
+       with another package.
+       
+x: software that relates to the X Window System in general and does not fit
+   into any of the other categories, such as the X server itself or window 
+   managers.
+   Most X software should be put into other more specific groups.
+   A game like xmines would go in games for instance and a text editor for
+   X would go in utils.
+   
+kde: Software that relates to KDE and does not fit into
+     any other category. This group should be used with care. 
+     Do *not* use it for all KDE software. K Office for instance belongs in
+     apps. Konqueror belongs in net.
+     
+gnome: Software that relates to GNOME and does not fit into
+       any other category. This group should be used with care. 
+       Do *not* use it for all GNOME software. Gimp for instance belongs 
+       in apps. A GNOME-aware window manager that works with plain X should
+       go in the x group.
+
+
+###########################################################################
+ Appendix C: Acknowledgements and Changelog
+###########################################################################
+
+ACKNOWLEDGEMENTS:
+  * Tushar Teredesai for suggesting the user=group scheme.
+  * Markus Laire for reporting the 2005-01-01 build bug
+
+CHANGELOG:
+
+2005-11-13
+           -fixed list_suspicious_files and list_package to work with
+            recent more POSIX-conforming versions of GNU find
+           -released version 1.2 
+
+2005-01-01
+            -fixed bug in skel-package/build script that caused it to report
+             all steps as successful, even if they failed 
+            -released version 1.1
+
+2004-11-01
+            -capitalized title
+            -released version 1.0
+            
+2004-10-14
+            -started developing the more_control_helpers utilities
+            
+2004-08-14
+            -started major rewrite (update for new LFS version, new hint 
+             format, textual improvements,...)
+
+2002-04-20
+            -changed LFS VERSION header to be more conservative
+            -added <br> tags to the synopsis for the sake of the hints 
+             index
+            -added group mmedia to the list of suggested groups 
+            -submitted v0.8
+
+2002-03-16  
+            -added note, that on Linux make doesn't need to be setgid kmem
+
+2002-02-18
+            -added section "Security issues with NFS"
+            -submitted v0.7
+
+2002-01-30  -added Changelog
+            -moved "chown 0.10000 `cat /tmp/installdirs`" command up (before
+             glibc package user is created)
+            -add_package_user: create home directory with "mkdir -p"
+                               use $grpfile everywhere instead of /etc/group
+            -improved mammoth sentence in Introduction
+            -added note about possibility to have user name==group name
+            -source bashrc_basic in bashrc_package
+            -minor textual changes
+
+
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..c4b323f
--- /dev/null
+++ b/README
@@ -0,0 +1,201 @@
+-*- mode:text; eval:(footnote-balloons) -*-
+Time-stamp: <Wednesday Dec  1, 2010 01:19:17 steve>
+
+Introduction:
+============
+
+Here are the tools I use for package management on bastard.  It is
+based _very_ heavily on the LFS hint:
+
+  "More control and package management using package users (v1.2)"
+
+Which I have included in this repo (see LFS-pkgusr-hint.txt) and is
+considered essential reading.
+
+The main differences (enhancements?) these tools have over the
+original LFS hint are:
+
+        o A set of convenience shell functions for pkg users, and a
+          pseudo "master installer"[1]
+
+        o Make it easy to give individual settings to each pkg user.
+          For example, some pkg users may need ssh/gnupg or $DISPLAY
+          set. 
+
+        o Better build script and .project templates
+
+        o Better uninstall script.
+
+        o Includes a `which' script.
+
+        o Includes a `/bin/mail' script.
+
+        o Includes a `lesspipe.sh' script (unashamedly stolen from
+          Pat Volkerding and Slackware.  Thanks, Pat!)
+
+        o A more complete list of "install" directories.
+
+        o Includes an elisp library that implements much of the
+          convenience shell functions.[2]
+
+You should understand that not everything here can be used straight
+away.  The /bin/mail script, for example, needs Zsh, and Sendmail
+installed (could probably be rewritten to use bash with very little
+effort); if you want to set yourself up as a "master installer",
+OpenSSL and OpenSSH are needed; and pkgusr.el needs SXEmacs (or
+XEmacs/Emacs) obviously.
+
+Pre-Installation:
+================
+The current version of the LFS book (Version SVN-20100919) is a little
+screwed up in one or two places, so BEFORE you move onto the chroot
+part of the deal you need to do a couple extra things:
+
+  1.  Install bison, flex, nano, and less (in that order) into your
+      /tools dir.  Bison, and flex are needed to build binutils in the
+      chroot.  And nano and less make life for the package user so
+      much easier.
+
+  2.  At the point in the book where it has you mount the devpts
+      filesystem, it neglects to mention that you have to specify a
+      mode and gid:
+          mount -vt devpts devpts ${LFS}/dev/pts -o gid=5,mode=620
+
+  3.  When creating the initial /etc/group file, I don't use the LFS
+      book suggestions.  I have my own set groups that I've been using
+      since 1995, and old habits are so hard to break. :-)
+
+      IMPORTANT:  Please note that my tty group has GID 5 and that
+      group in the LFS book has GID 4... make sure you use the right
+      gid= option when mounting your devpts.
+
+      Anyhoo, here's my initial group file...
+
+cat>/etc/group<<EOF
+root:x:0:
+bin:x:1:
+daemon:x:2:
+sys:x:3:
+adm:x:4:
+tty:x:5:
+disk:x:6:
+lp:x:7:
+mem:x:8:
+kmem:x:9:
+wheel:x:10:
+floppy:x:11:
+mail:x:12:
+news:x:13:
+uucp:x:14:
+man:x:15:
+dialout:x:16:
+audio:x:17:
+video:x:18:
+cdrom:x:19:
+games:x:20:
+utmp:x:22:
+usb:x:23:
+tape:x:26:
+nobody:x:98:
+nogroup:x:99:
+users:x:1000:
+EOF
+
+Installation:
+============
+
+Step 1... Go read that LFS hint.  You'll probably want to read it
+through a couple of times.
+
+Step 2... Don't follow the instructions in the LFS hint for how to set
+things up and where to dump the scripts etc.  Instead see the next
+section, "Pkgusr Specific Installation".
+
+Pkgusr Specific Installation
+============================
+
+In the hint it tells you how where and when to put the helper scripts
+and stuff, which is great, but I have not included the
+"more-control-scripts" tarball in this repo.  I've split them out into
+their respective directories so that I can easily modify and track
+them.  Because of that you may get a little lost in aligning what the
+hint says to the files you have in this repo.
+
+When
+----
+Right _BEFORE_ installing linux libc headers at start of Chap 6 in the
+LFS book.
+
+Where
+-----
+From _OUTSIDE_ the chroot environment
+
+Who
+---
+Do this as root
+
+How
+---
+Copy'n'Paste Fun!
+
+PKGUSR=/path/to/your/pkgusr/working/dir
+cp -v ${PKGUSR}/installdir.lst ${LFS}/root &&
+cp -v ${PKGUSR}/bin/{which,mail} ${LFS}/bin &&
+cp -va ${PKGUSR}/etc/pkgusr ${LFS}/etc &&
+cp -v ${PKGUSR}/usr/bin/* ${LFS}/bin &&
+cp -va ${PKGUSR}/usr/lib/pkgusr ${LFS}/usr/lib &&
+cp -v ${PKGUSR}/usr/sbin/{add_package_user,install_package} \
+  ${LFS}/usr/sbin &&
+cp -v ${PKGUSR}/usr/sbin/{group,user}add ${LFS}/tools/bin &&
+chown -vR 0:0 ${LFS}/etc/pkgusr &&
+chown -vR 0:0 ${LFS}/usr/lib/pkgusr
+unset PKGUSR
+
+From this point on you need to re-enter the chroot environment.  So
+these next (and final) steps are done _INSIDE_ the chroot (as root).
+
+Create the install group:
+------------------------
+
+groupadd -g 9999 install
+
+
+Set up the install directories:
+------------------------------
+
+while read dir; do
+    if [ ! -d ${dir} ]; then
+        mkdir -vp ${dir}
+    fi
+    chown -v 0:9999 ${dir}
+    chmod -v 1775 ${dir}
+done < /root/installdir.lst
+
+One last symlink:
+----------------
+
+  ln -sv /tools/lib/libncurses.so.5 /usr/lib/libncursesw.so.5
+
+(don't forget you'll have to remove that before you install ncurses
+later)
+
+Alright, where are we up to now?  Well, in the LFS hint you are up to:
+
+  7.2 Walkthrough: Installing linux-libc-headers
+
+And in the LFS book, you are up to:
+
+  6.7. Linux-2.6.35.4 API Headers
+
+So have fun building the rest of your LFS system!
+
+
+Footnotes: 
+[1]  The "master installer" is just someone who can change their
+     identity to a pkg user.  It _could_ be root, though I don't
+     recommend it.  I use a ssh->root->su->pkgusr from my personal A/C
+     which is a single "step" (I'm not stopping in a root shell).
+
+[2]  pkgusr.el is probably fairly SXEmacs-centric, but should work
+     with perhaps minor changes on XEmacs or even GNU/Emacs.
+
diff --git a/bin/mail b/bin/mail
new file mode 100755 (executable)
index 0000000..3ac0bfb
--- /dev/null
+++ b/bin/mail
@@ -0,0 +1,188 @@
+#!/bin/zsh
+
+## Copyright (C) 2007 - 2010 Steve Youngs
+
+## Time-stamp: <Friday Oct 15, 2010 22:17:02 steve>
+
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted provided that the following conditions
+## are met:
+##
+## 1. Redistributions of source code must retain the above copyright
+##    notice, this list of conditions and the following disclaimer.
+## 2. Redistributions in binary form must reproduce the above copyright
+##    notice, this list of conditions and the following disclaimer in the
+##    documentation and/or other materials provided with the distribution.
+## 3. Neither the name of the University nor the names of its contributors
+##    may be used to endorse or promote products derived from this software
+##    without specific prior written permission.
+
+## THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+## IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+## WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+## DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+## FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+## CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+## SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+## BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+## WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+## OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+## IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+## Commentary:
+#
+#  This is a simple wrapper around sendmail that serves as a command
+#  line mailer, along the lines of mailx or nail.
+
+## Code:
+ourname=${0##*/}
+
+# Version info
+VERSION=0.3
+COPYRIGHT="Copyright (C) 2007 - 2010 Steve Youngs <steve@steveyoungs.com>"
+version_str="${ourname}: ${VERSION}
+${COPYRIGHT}"
+
+_version () { echo $version_str }
+
+# Help/Usage
+usage ()
+{
+    cat<<EOF
+                                  $ourname
+
+    ${VERSION}
+
+Synopsis:
+--------
+
+  $ourname
+        [ -F FROM | --from=FROM ] [ -s SUBJECT | --subject=SUBJECT ]
+       [ -c CC[,...CCn] | --cc=CC[,...CCn] ] [ -f FILE | --file=FILE ]
+       RECIPIENT[,...RECIPIENTn]
+  $ourname
+        [ -v | --version ] [ -h | --help | --usage ]
+
+Description:
+-----------
+
+  $ourname is a simple command line mailer.  It is not really meant as
+  a replacement for something better like mailx or nail, but it should
+  suffice until you install a more feature rich mailer.  The author
+  uses SXEmacs/Gnus for day to day email, and this script for any
+  command line mailing.
+
+Options:
+-------
+
+  -F FROM
+  --from=FROM  
+        Specify an alternate From address.  If omitted, try the
+        environment variable EMAIL, fall back to:
+         $USER@$(hostname -f).
+
+  -s SUBJECT
+  --subject=SUBJECT
+        Specify a Subject header.  If omitted, "No Subject given" will
+        be used.
+
+  -c CC[,...CCn]
+  --cc=CC[,...CCn]
+        Optional addresses to include on a Cc header.
+
+  -f FILE
+  --file=FILE
+        A plain text file containing the body of the email message.
+        If this option is omitted, stdin is used.  This option should
+        always be the last on the command line.
+
+  -v
+  --version
+        Display version.
+
+  -h
+  --help
+  --usage
+        Display this help.
+
+Environment Variables:
+---------------------
+
+  EMAIL -- To set a From address.  Maybe necessary if your publically
+           known email address is different from your system default.
+
+${COPYRIGHT}
+
+EOF
+    return 0
+}
+
+# Process the mail's headers
+process_mail ()
+{
+    local MTA=/usr/sbin/sendmail
+
+    [[ -n ${FROM} ]] ||
+       { FROM=${EMAIL} && [[ -n ${FROM} ]] || FROM=${USER}@$(hostname -f) }
+
+    [[ -n ${SUBJECT} ]] || SUBJECT="No Subject given"
+
+    (
+       echo "To: ${RCPT}"
+       [[ -n ${CC} ]] && echo "Cc: ${CC}"
+       echo "From: <${FROM}>"
+       echo "Date: $(date --rfc-2822)"
+       echo "Subject: ${SUBJECT}"
+       echo "User-Agent: bastard mailer, ${VERSION}"
+       echo
+       cat ${FILE}
+    ) | ${MTA} "${RCPT}"
+}
+
+
+# Parse the command line
+args=vh-:F:s:c:f:
+rv=0
+
+while getopts $args opts; do
+    case $opts in
+       (-)
+           case $OPTARG in
+               (from?*)     FROM=${OPTARG/from=/} ;;
+               (subject?*)  SUBJECT=${OPTARG/subject=/} ;;
+               (cc?*)       CC=${OPTARG/cc=/} ;;
+               (file?*)     FILE=${OPTARG/file=/} ;;
+               (version)    _version; exit 0 ;;
+               (help|usage) usage; exit 0 ;;
+               (*)
+                   print Unrecognised option: --$OPTARG >&2
+                   print See $ourname --help >&2
+                   rv=1
+                   ;;
+           esac
+           ;;
+       (F) FROM=${OPTARG} ;;
+       (s) SUBJECT=${OPTARG} ;;
+       (c) CC=${OPTARG} ;;
+       (f) FILE=${OPTARG} ;;
+       (v) _version; exit 0 ;;
+       (h) usage; exit 0 ;;
+       (*)
+           print Unrecognised option -$OPTARG >&2
+           print see $ourname --help >&2
+           rv=1
+           ;;
+    esac
+done
+shift $(( $OPTIND - 1 ))
+
+# What's left on the command line _should_ be the recipients
+RCPT=$argv
+
+if [[ $rv -eq 0 ]]; then
+    process_mail
+    exit $?
+else
+    exit $rv
+fi
diff --git a/bin/which b/bin/which
new file mode 100755 (executable)
index 0000000..2a8e4b9
--- /dev/null
+++ b/bin/which
@@ -0,0 +1,7 @@
+#!/bin/bash
+EXIT_STATUS=0
+for i in "$@"; do
+        type -Pp $i 2>/dev/null||EXIT_STATUS=1
+done
+exit $EXIT_STATUS
+
diff --git a/etc/pkgusr/bash_profile b/etc/pkgusr/bash_profile
new file mode 100644 (file)
index 0000000..58d3c4d
--- /dev/null
@@ -0,0 +1,40 @@
+# couple of environment settings to ensure sanity
+set +h
+umask 022
+LC_ALL=POSIX
+
+## PATH
+# The wrappers directory must be the first entry in the PATH.  Once
+# you are no longer needing or using the tools dir you can remove it
+# from the PATH if you want, but leaving it there won't hurt
+PATH=/usr/lib/pkgusr:/usr/bin:/bin:/usr/X11R6/bin:/tools/bin
+
+## A couple things to make less(1) nicer.
+LESS=-MRgisw
+LESSCHARSET=latin1
+LESSOPEN='|lesspipe.sh %s'
+
+## Timezone -- set to your local zone
+TZ='Australia/Brisbane'
+
+## pkg-config
+PKG_CONFIG_PATH=/usr/lib/pkgconfig:/usr/X11R6/lib/pkgconfig
+
+### export everything
+export LC_ALL PATH LESS LESSCHARSET LESSOPEN TZ PKG_CONFIG_DIR
+
+
+# Make prompt reflect that we are a package user.
+export PROMPT_COMMAND='PS1="[pkgusr (\u)] \w> "'
+
+# Suck in some handy shell functions
+. /etc/pkgusr/handy_funcs
+
+# If they exist, load any private settings.  This comes last so that
+# we can override any system defaults if need be
+if [ -f ${HOME}/.pkgusrrc ]; then
+        . ${HOME}/.pkgusrrc
+fi
+
+# Go to the home directory whenever we su to a package user.
+cd
diff --git a/etc/pkgusr/bashrc b/etc/pkgusr/bashrc
new file mode 100644 (file)
index 0000000..ccfa381
--- /dev/null
@@ -0,0 +1,3 @@
+#Use the same environment regardless of whether we use 
+#`su <package>' or 'su - <package>' to become the package user.
+source ~/.bash_profile
diff --git a/etc/pkgusr/handy_funcs b/etc/pkgusr/handy_funcs
new file mode 100644 (file)
index 0000000..28d90a6
--- /dev/null
@@ -0,0 +1,260 @@
+# -*- shell-script -*-
+# Copyright (C) 2007 Steve Youngs <steve@sxemacs.org>
+
+# What lies here is a collection of handy bash shell functions that
+# make life a little easier for pkgusr.
+
+## The build logs
+alogs()
+{
+        ls -l ${HOME}/*.{err,log}
+}
+
+lerr()
+{
+        ls -l ${HOME}/*.err
+}
+
+llog()
+{
+        ls -l ${HOME}/*.log
+}
+
+verr()
+{
+        local arg=$1
+
+        if [ -z "$arg" ]; then
+                arg=all
+        fi
+
+        case $arg in
+                   conf) less ${HOME}/configure.err ;;
+                install) less ${HOME}/install.err ;;
+                  check) less ${HOME}/check.err ;;
+                   make) less ${HOME}/make.err ;;
+                   upd) less ${HOME}/upd.err ;;
+                   ver) less ${HOME}/verupd.err ;;
+                    all) less ${HOME}/*.err ;;
+        esac
+}
+
+vlog()
+{
+        local arg=$1
+
+        if [ -z "$arg" ]; then
+                arg=all
+        fi
+
+        case $arg in
+                   conf) less ${HOME}/configure.log ;;
+                install) less ${HOME}/install.log ;;
+                  check) less ${HOME}/check.log ;;
+                   make) less ${HOME}/make.log ;;
+                   upd) less ${HOME}/upd.log ;;
+                   ver) less ${HOME}/verupd.log ;;
+                    all) less ${HOME}/*.log ;;
+        esac
+}
+
+verrlog()
+{
+        local arg=$1
+
+        if [ -z "$arg" ]; then
+                arg=all
+        fi
+
+        case $arg in
+                   conf) less ${HOME}/configure.{err,log} ;;
+                install) less ${HOME}/install.{err,log} ;;
+                  check) less ${HOME}/check.{err,log} ;;
+                   make) less ${HOME}/make.{err,log} ;;
+                   upd) less ${HOME}/upd.{err,log} ;;
+                   ver) less ${HOME}/verupd.{err,log} ;;
+                    all) less ${HOME}/*.{err,log} ;;
+        esac
+}
+
+dlog()
+{
+    for log in configure make check install upd verupd; do
+           [[ -f ${HOME}/${log}.err ]] && rm -v ${HOME}/${log}.err
+           [[ -f ${HOME}/${log}.log ]] && rm -v ${HOME}/${log}.log
+    done
+}
+
+updver()
+{
+        local arg=${1}
+        sed -i "s|\(Version: \).*$|\1${arg}|" ${HOME}/.project
+        echo -n "Version updated... "
+        grep --colour Version:.*$ ${HOME}/.project
+}
+
+showinst()
+{
+        local top=$(pinky -l $(whoami)|grep -n "^Install Notes:$"|cut -d: -f1)
+        local bot=$(pinky -l $(whoami)|grep -n "^General Notes:$"|cut -d: -f1)
+
+        pinky -l $(whoami)|sed -n ${top},${bot}p
+}
+
+showgen()
+{
+        local top=$(pinky -l $(whoami)|grep -n "^General Notes:$"|cut -d: -f1)
+        local bot=$(pinky -l $(whoami)|grep -n "^CONTENTS:$"|cut -d: -f1)
+
+        pinky -l $(whoami)|sed -n ${top},${bot}p
+}
+
+listp()
+{
+        pinky -l $(whoami)|less
+}
+
+srepo()
+{
+        pinky -l $(whoami)|grep --colour Repo_Location:.*$
+}
+
+rawrepo()
+{
+    srepo|awk '{print $2;}'|tr -d '<>'
+}
+
+trepo()
+{
+        pinky -l $(whoami)|grep --colour Repo_Type:.*$
+}
+
+web()
+{
+        pinky -l $(whoami)|grep --colour Web_Site:.*$
+}
+
+rawweb()
+{
+    web|awk '{print $2;}'|tr -d '<>'
+}
+
+xtar()
+{
+        local opts
+        local type
+        local fname=$1
+
+        if [ -z "${fname}" ]; then
+                echo No filename specified >&2
+                return 1
+        fi
+
+        type=$(file ${fname}|cut -d' ' -f2)
+
+        case $type in
+                tar)    opts=xf ;;
+                gzip)   opts=zxf ;;
+                bzip2)  opts=jxf ;;
+               xz)     opts=Jxf ;;
+                *)
+                       # try lzma
+                       if lzmainfo ${fname} &>/dev/null; then
+                               opts="--lzma -xf"
+                       else
+                               printf "Unknown file type: %s\n" $type >&2
+                               return 2
+                       fi
+                       ;;
+        esac
+
+        tar ${opts} ${fname}
+}
+
+vtar()
+{
+        local opts
+        local type
+        local fname=$1
+
+        if [ -z "${fname}" ]; then
+                echo No filename specified >&2
+                return 1
+        fi
+
+        type=$(file ${fname}|cut -d' ' -f2)
+
+        case $type in
+                tar)    opts=tvvvf ;;
+                gzip)   opts=ztvvvf ;;
+                bzip2)  opts=jtvvvf ;;
+               xz)     opts=Jtvvvf ;;
+                *)
+                       # lzma.  Here because lzmainfo is too stupid
+                       if lzmainfo ${fname} &>/dev/null; then
+                               opts="--lzma -tvvvf"
+                       else
+                               printf "Unknown file type: %s\n" $type >&2
+                               return 2
+                       fi
+                        ;;
+        esac
+
+        tar ${opts} ${fname}|less
+}
+
+ebld()
+{
+        nano -w ${HOME}/build
+}
+
+epro()
+{
+        nano -w ${HOME}/.project
+}
+
+ebp()
+{
+        nano -w ${HOME}/{build,.project}
+}
+
+H-pkg()
+{
+        cat<<EOF
+
+        alogs           List of build logs (showing size)
+        lerr            List just the error logs
+        llog            List just the .log logs
+        verr [LOG]      Display LOG, which can be:
+                               \`conf' -- configure.err
+                              \`check' -- check.err
+                            \`install' -- install.err
+                               \`make' -- make.err
+                               \`upd' -- upd.err
+                               \`ver' -- verupd.err
+                                \`all' -- all error logs (default)
+        vlog [LOG]      Same as for \`verr', but for the .log files.
+        verrlog [LOG]   Same as for \`verr', but displays both the .err,
+                        and the .log files.
+        dlog            Removes all build logs
+        updver [NEWVER]
+                        Updates the version in the .project.  It MUST be
+                        quoted to protect it from shell expansion.
+        showinst        Displays the \`Install Notes'.
+        showgen         Displays the \`General Notes'.
+        listp           Displays the entire package info (piped through less(1))
+        srepo           Display the package's source repo location.
+       rawrepo         Output just the repo URL (to use with lynx, curl, etc)
+        trepo           Display the type of repo (tla, git, svn, mercurial etc)
+        web             Display the package's homepage URL.
+       rawweb          Output just the web URL (to use with lynx etc)
+        xtar [TARBALL]  Extract TARBALL, automatically choosing the appropriate
+                        tar(1) options.
+        vtar [TARBALL]  List the contents of TARBALL, automatically choosing 
+                        appropriate options and piping through less(1).
+        ebld            Edit the build script.
+        epro            Edit the .project file.
+        ebp             Edit the build script and the .project file.
+
+EOF
+}
diff --git a/etc/pkgusr/skel-package/.bash_profile b/etc/pkgusr/skel-package/.bash_profile
new file mode 120000 (symlink)
index 0000000..1e24bd9
--- /dev/null
@@ -0,0 +1 @@
+/etc/pkgusr/bash_profile
\ No newline at end of file
diff --git a/etc/pkgusr/skel-package/.bashrc b/etc/pkgusr/skel-package/.bashrc
new file mode 120000 (symlink)
index 0000000..3e42811
--- /dev/null
@@ -0,0 +1 @@
+/etc/pkgusr/bashrc
\ No newline at end of file
diff --git a/etc/pkgusr/skel-package/.project b/etc/pkgusr/skel-package/.project
new file mode 100644 (file)
index 0000000..7e36f16
--- /dev/null
@@ -0,0 +1,30 @@
+
+ Last_Updated: <date/time>
+      Version: <version string>
+  Description: <short one liner>
+
+<verbose description here if desired>
+
+    Repo_Type: <tarball|tla|svn|cvs|git|mercurial>
+Repo_Location: <URI>
+     Web_Site: <URL>
+
+Install Notes:
+-------------
+Use the build script.
+
+Configure options used...
+  ./configure --prefix=/usr \
+              --infodir=/usr/share/info \
+              --mandir=/usr/share/man \
+              --sysconfdir=/etc \
+              --libexecdir=/usr/lib \
+              --localstatedir=/var
+
+
+General Notes:
+-------------
+
+
+CONTENTS:
+--------
diff --git a/etc/pkgusr/skel-package/build b/etc/pkgusr/skel-package/build
new file mode 100755 (executable)
index 0000000..5a53927
--- /dev/null
@@ -0,0 +1,407 @@
+#!/bin/bash
+#
+# Build script for <PACKAGE>
+#
+# This build script is meant to be executed from within the source
+# directory created by extracting the tarball.
+#
+# It will create up to 12 log files in the $HOME directory:
+#
+#   configure.log: All messages output during configure
+#   configure.err: Just the errors output during configure
+#        make.log: All messages output during make
+#        make.err: Just the errors output during make
+#       check.log: All messages output during make check/test
+#       check.err: Just the errors output during make check/test
+#     install.log: All messages output during make install
+#     install.err: Just the errors output during make install
+#         upd.log: Any messages from updating the package list
+#                  (usually nothing)
+#         upd.err: Just the errors from updating the package list
+#                  (usually nothing)
+#      verupd.log: Any messages from updating the package version
+#                  (usually nothing)
+#      verupd.err: Just the errors from updating the package version
+#                  (usually nothing)
+#
+# After running the script you should check the *.err files to see
+# if any problems have occurred. If that is the case, use the corresponding
+# *.log files to see the error messages in context.
+#
+# Note: the ":" before the "}" in *_commands() is a no-op that makes sure 
+# that the function remains syntactically valid, even if you remove its
+# contents (e.g. remove the "configure" line, because there's nothing to 
+# configure for the package).
+#
+# Comments throughout the script marked "#*" are places where you may
+# have to change things for individual package circumstances.
+
+## Version info.
+ourname=${0##*/}
+VERSION=1.0
+COPYRIGHT="Copyright (C) 2007 - 2010 Steve Youngs <steve@steveyoungs.com>"
+version_str="${ourname}: ${VERSION}\n${COPYRIGHT}"
+
+show_version()
+{
+    echo -e $version_str
+    exit 0
+}
+
+#*
+# Set this to 1 (one) if the package's version can be updated
+# automatically
+auto_version=0
+
+#*
+# Set the configure commands/options here.  Remove everything except
+# the braces and colon if the package has no configure.
+configure_commands()
+{ :
+    ./configure --prefix=/usr \
+        --infodir=/usr/share/info \
+        --mandir=/usr/share/man \
+        --sysconfdir=/etc \
+        --libexecdir=/usr/lib \
+        --localstatedir=/var
+}
+
+#*
+# Set the make commands/options here.
+make_commands()
+{ :
+    make
+}
+
+#*
+# Set the test suite commands/options here.  Remove everything except
+# the braces and colon if the package has no test suite
+check_commands()
+{ :
+    make check
+}
+
+#*
+# Set the install commands/options here.
+install_commands()
+{ :
+    make install &&
+    # libtool .la files DO NOT need to be executable! (remove if not a
+    # libtool'd package)
+    forall_direntries_from $(whoami) -name \*.la -exec chmod -v 644 {} \;
+}
+
+update_commands()
+{ :
+    sed -i s/"Last_Updated.*"/"Last_Updated: $(date +%c)"/g ${HOME}/.project
+    awk '/^CONTENTS:/ { print; exit; } {print}' ${HOME}/.project > ${HOME}/.projtmp
+    echo "--------" >> ${HOME}/.projtmp
+    list_package $(whoami) >> ${HOME}/.projtmp
+    mv ${HOME}/.projtmp ${HOME}/.project
+}
+
+#*
+#  Set `arg' to command(s) that output just the version number of the
+#  package.
+version_commands()
+{ :
+    # Commands to update the version string in .project
+    arg=''  # replace with something that returns a version number.
+    sed -i "s|\(Version: \).*$|\1${arg}|" ${HOME}/.project
+}
+
+#======================================================================#
+### There shouldn't be anything beyond this point to tweak or change ###
+#======================================================================#
+test_pipe()
+{
+    for i in "${PIPESTATUS[@]}"; do
+        test $i != 0 && { echo FAILED! ; exit 1 ; }
+    done
+    echo successful!
+    return 0
+}
+
+
+run_configure()
+{
+    echo -n "Configuring ($(whoami))... "
+    { configure_commands 3>&1 1>&2 2>&3 | tee "$HOME/configure.err" ;} \
+       &>"$HOME/configure.log"
+    test_pipe
+    [[ ${only} = yes ]] && exit 0 || run_build
+}
+
+run_build()
+{
+    echo -n "Building ($(whoami))... "
+    { make_commands 3>&1 1>&2 2>&3 | tee "$HOME/make.err" ;} \
+       &>"$HOME/make.log"
+    test_pipe
+    [[ ${only} = yes ]] && exit 0 || run_check
+}
+
+run_check()
+{
+    echo -n "Checking ($(whoami))... "
+    { check_commands 3>&1 1>&2 2>&3 | tee "$HOME/check.err" ;} \
+       &>"$HOME/check.log"
+    test_pipe
+    [[ ${only} = yes ]] && exit 0 || run_install
+}
+
+run_install()
+{
+    echo -n "Installing ($(whoami))... "
+    { install_commands 3>&1 1>&2 2>&3 | tee "$HOME/install.err" ;} \
+       &>"$HOME/install.log"
+    test_pipe
+    [[ ${only} = yes ]] && exit 0 || run_update
+}
+
+run_update()
+{
+    echo -n "Updating package list ($(whoami))... "
+    { update_commands 3>&1 1>&2 2>&3 | tee "$HOME/upd.err" ;} \
+       &>"$HOME/upd.log"
+    test_pipe
+    # maybe update the version too
+    if [ $auto_version -eq 1 ];then
+       echo -n "Updating package version ($(whoami))... "
+       { version_commands 3>&1 1>&2 2>&3 | tee "$HOME/verupd.err" ;} \
+           &>"$HOME/verupd.log"
+       test_pipe
+    fi
+}
+
+# Help
+usage()
+{
+    # Look for a pager to display the help with.
+    local _cat
+
+    if [ ${PAGER} ]; then
+       # User has PAGER env var set, use that.
+       _cat=${PAGER}
+    else
+       # No PAGER var set, try most->less->more->cat
+       for pager in most less more cat; do
+           if [ -x "$(which $pager)" ]; then
+               _cat=$pager
+               break
+           else
+               continue
+           fi
+       done
+    fi
+
+    $_cat<<EOF
+                               $ourname
+         ${VERSION}
+
+
+Synopsis:
+--------
+
+  $ourname
+  $ourname
+        [ -c | -C | -b | -B | -k | -K | -i | -I | -u ]
+       [ --conf | --conf_only | --build | --build_only | --check ]
+       [ --check_only | --install | --install_only | --upd_list ]
+  $ourname
+        [ -h | --help ]
+  $ourname
+       [ -v | --version ]
+
+Description:
+-----------
+
+  $ourname is a general purpose build script.  It should fit the bill
+  for most packages out of the box.  However you should ALWAYS check
+  through the script before running it blindly on a package.
+
+  The places in the script that will require your attention each time
+  you set up a new package have been marked with "#*".
+
+Options:
+-------
+
+  Most times you will not need to specify any command line options.
+  They exist mainly for those times when something has gone awry.
+
+  -c
+  --conf
+        Run the script from the configure stage, onwards.  This is
+        synonymous with running the script without any command line
+        options.
+
+  -C
+  --conf_only
+        Run just the configure stage and then exit.
+
+  -b
+  --build
+        Run the script from the build (make) stage, onwards.  Specifying
+        this option forces the script to skip the configure stage.  Do
+        not use this option simply because the package does not have a
+        configure, in that case, you are better off simply removing the
+        contents of the "configure_commands" function.
+
+  -B
+  --build_only
+        Run just the build (make) stage and then exit.
+
+  -k
+  --check
+        Run the script from the check (testsuite) stage, onwards.
+        Specifying this option forces the script to skip the configure
+        and build stages.
+
+  -K
+  --check_only
+        Run just the check (testsuite) stage and then exit.
+
+  -i
+  --install
+        Run the script from the install stage, onwards.  Specifying
+        this option forces the script to skip the configure, build,
+        and check stages.
+
+  -I
+  --install_only
+        Run just the install stage and then exit.
+
+  -u
+  --upd_list
+        Updates the package file list kept in the .project file.  This
+        option also updates the package version in that file too if
+        possible.
+
+  -h
+  --help
+        Display this usage info and exit.
+
+  -v
+  --version
+        Display version and copyright info and exit.
+
+Files:
+-----
+
+  ${HOME}/.project
+        Has information about the package, including (but not limited
+        to), website, repo location and type, version, date last
+        updated, installation notes, and complete file list.
+
+  ${HOME}/configure.log
+        Contains all messages output during configure.
+
+  ${HOME}/configure.err
+        Contains only error messages output during configure.
+
+  ${HOME}/make.log
+        Contains all messages output during make.
+
+  ${HOME}/make.err
+        Contains only error messages output during make.
+
+  ${HOME}/check.log
+        Contains all messages output from running a package testsuite.
+
+  ${HOME}/check.err
+        Contains only error messages output from running a package
+        testsuite.
+
+  ${HOME}/install.log
+        Contains all messages output during install.
+
+  ${HOME}/install.err
+        Contains only error messages output during install.
+
+  ${HOME}/upd.log
+        Contains all messages output during the update of the package
+        file list.  This log is nearly always empty, or at least it
+        should be.
+
+  ${HOME}/upd.err
+        Contains only error messages output during the update of the
+        package file list.  This log is nearly always empty, or at
+        least it should be.
+
+  ${HOME}/updver.log
+        Contains all messages output when updating the package version
+        info.  It should be empty.
+
+  ${HOME}/updver.err
+        Contains only error messages output when updating the package
+        version info.  It should be empty.
+
+Exit Codes:
+----------
+
+  0 -- Successful completion.
+  1 -- Something bad happened.
+  2 -- Bad command line option.
+
+$COPYRIGHT
+
+EOF
+}
+
+# Command line parsing.
+# Yes, it is possible to give more than one option on the command
+# line, but that normally doesn't make much sense.  Consider:
+# '../build --conf_only --install' the --conf_only option will
+# cause the script to exit before the install happens.
+args=cCbBkKiIuhV-:
+only=no
+
+if [ $1 ]; then
+    # We have cmdline args, deal with them.
+    while getopts $args opts; do
+       case $opts in
+           (-)
+                case $OPTARG in
+                   (conf) run_configure ;;
+                   (conf_only) only=yes; run_configure ;;
+                   (build) run_build ;;
+                   (build_only) only=yes; run_build ;;
+                   (check) run_check ;;
+                   (check_only) only=yes; run_check ;;
+                   (install) run_install ;;
+                   (install_only) only=yes; run_install ;;
+                   (upd_list) run_update ;;
+                   (help|usage) usage ;;
+                   (version) show_version ;;
+                   (*)
+                        echo $ouname: error: bad option: --$OPTARG >&2
+                       exit 2
+                       ;;
+               esac
+               ;;
+           (c) run_configure ;;
+           (C) only=yes; run_configure ;;
+           (b) run_build ;;
+           (B) only=yes; run_build ;;
+           (k) run_check ;;
+           (K) only=yes; run_check ;;
+           (i) run_install ;;
+           (I) only=yes; run_install ;;
+           (h) usage ;;
+           (u) run_update ;;
+           (V) show_version ;;
+           (*)
+                echo $ourname: error: bad option: -$OPTARG >&2
+               exit 2
+               ;;
+       esac
+    done
+    shift $(( $OPTIND - 1 ))
+else
+    # There were no cmdline args given, just run from configure onwards
+    only=no                    # this should be "no" already, but make sure
+    run_configure
+fi
+
+## build ends here
diff --git a/etc/pkgusr/zsh/_zsh-pkgtools b/etc/pkgusr/zsh/_zsh-pkgtools
new file mode 100644 (file)
index 0000000..960e968
--- /dev/null
@@ -0,0 +1,9 @@
+#compdef ppkg cpkg upkg vpkg ipkg gpkg wpkg pkgrepo pkgsu xtar vtar
+
+case $service in
+        (cpkg) _command_names -e ;;
+        (?pkg) _wanted file expl 'Pkg User' _users ;;
+        (pkgrepo|pkgsu) _wanted file expl 'Pkg User' _users ;;
+        ([xv]tar) _wanted file expl 'Tarball' _files -g '*.{t{gz,bz{,2},lz,xz},tar.{Z,gz,bz2,lzma,xz}}(-.)' ;;
+esac
+
diff --git a/etc/pkgusr/zsh/zsh-pkgtools b/etc/pkgusr/zsh/zsh-pkgtools
new file mode 100644 (file)
index 0000000..018268d
--- /dev/null
@@ -0,0 +1,270 @@
+# -*- Shell-script -*-
+# Copyright (C) 2007 - 2010 Steve Youngs <steve@steveyoungs.com>
+#
+#  A collection of Zsh functions that I use day to day for maintaining
+#  and administering my machines.  These come in very handy if you set up
+#  one of your users as a "package manager".  There is a corresponding
+#  _zsh-pkgtools file that provides completion for most of what is here.
+H-pkg ()
+{
+        cat<<EOF
+
+        fpkg [REGEXP] -- finds pkg names matching REGEXP.                      
+        ppkg [NAME]   -- print pkg NAME pkg info.  
+        cpkg [CMD]    -- find what package provides CMD.     
+        lpkg          -- list installed packages.
+        Lpkg          -- list installed packages with dates last updated.
+        upkg [PKG]    -- date/time the last time PKG was updated.
+        vpkg [PKG]    -- print the version of PKG.
+        ipkg [PKG]    -- print install notes of PKG.
+        gpkg [PKG]    -- print general notes of PKG.
+        wpkg [PKG]    -- print PKG website URL.
+        pkgrepo [PKG] <t> -- print the source repo location of PKG
+                             with optional 2nd arg non-nil, also print
+                             repo type.
+        pkgsu [PKGUSR] -- switch to user PKGUSR.
+        pkg_install [DESCRIPTION] [USER] [GROUP]
+                -- install a new package, DESCRIPTION must be quoted.
+        pkg_ldconfig  -- Run ldconfig.
+
+        xtar [FILE]   -- extract tarball FILE.  sets tar(1) options
+                         based on file type.
+        vtar [FILE]   -- view the contents of tarball FILE.  sets tar(1)
+                         options based on file type.
+        ctar [FILE] [DIRECTORY]
+                     -- create a tarball FILE of DIRECTORY.  Compression
+                         is automatically chosen from the filename.
+
+
+EOF
+}
+
+fpkg () 
+{
+        if [[ $ARGC -lt 1 || $ARGC -gt 1 ]]; then
+                echo Invalid or missing argument >&2
+                echo "Usage: $0 [REGEXP]" >&2
+                return 1
+        else
+                sed -n '/^install:/,$p' /etc/group|cut -d: -f1| \
+                  grep -i --colour ${argv[1]}
+        fi
+}
+
+ppkg () 
+{
+        if [[ $ARGC -lt 1 || $ARGC -gt 1 ]]; then
+                echo Invalid or missing argument >&2
+                echo "Usage: $0 [NAME]" >&2
+                return 1
+        else
+                pinky -l $argv[1]|less
+        fi
+}
+
+cpkg ()
+{
+        if [[ $ARGC -lt 1 || $ARGC -gt 1 ]]; then
+                echo Invalid or missing argument >&2
+                echo "Usage: $0 [CMD]" >&2
+                return 1
+        else
+                find $path -type f -regex "^.*/$argv[1].*$" \
+                  -printf "%f -- (%u:%g)\n"|grep --colour '(.*)'
+        fi
+}
+
+lpkg () 
+{
+        sed -n '/^install/p' /etc/group|cut -d: -f4|tr ',' '\n'|less
+}
+
+upkg()
+{
+        if [[ $ARGC -lt 1 || $ARGC -gt 1 ]]; then
+                echo Invalid or missing argument >&2
+                echo "Usage: $0 [PKG]" >&2
+                return 1
+        else
+                echo -n "$argv[1]... "
+                pinky -l $argv[1]|grep 'Last_Updated:.*'| \
+                  sed 's/^      //'|grep --colour '.*'
+        fi
+}
+
+Lpkg ()
+{
+        for pkg in $(sed -n '/^install/p' /etc/group|cut -d: -f4|tr ',' '\n'); do
+                printf "${pkg}\t\t\t$(pinky -l ${pkg}|grep Last_Updated|cut -d' ' -f3-)\n"
+        done|less
+}
+
+vpkg()
+{
+        if [[ $ARGC -lt 1 || $ARGC -gt 1 ]]; then
+                echo Invalid or missing argument >&2
+                echo "Usage: $0 [PKG]" >&2
+                return 1
+        else
+                echo -n "$argv[1]... "
+                pinky -l $argv[1]|grep 'Version:.*'| \
+                  sed 's/^      //'|grep --colour '.*'
+        fi
+}
+
+ipkg()
+{
+        if [[ $ARGC -lt 1 || $ARGC -gt 1 ]]; then
+                echo Invalid or missing argument >&2
+                echo "Usage: $0 [PKG]" >&2
+                return 1
+        else
+                local top=$(pinky -l $argv[1]|grep -n "Install Notes"|cut -d: -f1)
+                local bot=$(pinky -l $argv[1]|grep -n "General Notes"|cut -d: -f1)
+                pinky -l $argv[1]|sed -n ${top},${bot}p
+        fi
+}
+               
+gpkg()
+{
+        if [[ $ARGC -lt 1 || $ARGC -gt 1 ]]; then
+                echo Invalid or missing argument >&2
+                echo "Usage: $0 [PKG]" >&2
+                return 1
+        else
+                local top=$(pinky -l $argv[1]|grep -n "General Notes"|cut -d: -f1)
+                local bot=$(pinky -l $argv[1]|grep -n "CONTENTS"|cut -d: -f1)
+                pinky -l $argv[1]|sed -n ${top},${bot}p
+        fi
+}
+
+wpkg()
+{
+        if [[ $ARGC -lt 1 || $ARGC -gt 1 ]]; then
+                echo Invalid or missing argument >&2
+                echo "Usage: $0 [PKG]" >&2
+                return 1
+        else
+                pinky -l $argv[1]|grep --colour 'Web_Site:.*$'
+        fi
+}
+
+pkgrepo()
+{
+        if [[ $ARGC -lt 1 || $ARGC -gt 2 ]]; then
+                echo Invalid or missing argument >&2
+                echo "Usage: $0 [PKG] <t>" >&2
+                return 1
+        else
+                pinky -l $argv[1]|grep --colour 'Repo_Location:.*$'
+                [[ -n "$argv[2]" ]] && pinky -l $argv[1]|grep --colour 'Repo_Type:.*$'
+        fi
+}
+
+xtar()
+{
+        if [[ $ARGC -lt 1 || $ARGC -gt 1 ]]; then
+                echo Invalid or missing argument >&2
+                echo "Usage: $0 [FILE]" >&2
+                return 1
+        fi
+
+        local opts
+        local type
+        local fname=$argv[1]
+
+        type=$(file ${fname}|cut -d' ' -f2)
+
+        case $type in
+               (tar)   opts=xf ;;
+               (gzip)  opts=zxf ;;
+               (bzip2) opts=jxf ;;
+               (xz)    opts=Jxf ;;
+               (*)
+                       # try lzma
+                       if lzmainfo ${fname} &>/dev/null; then
+                               opts=(--lzma -xf)
+                       else
+                               printf "Unknown file type: %s\n" $type >&2
+                               return 2
+                       fi
+                       ;;
+        esac
+
+        tar ${opts} ${fname}
+}
+
+vtar()
+{
+        if [[ $ARGC -lt 1 || $ARGC -gt 1 ]]; then
+                echo Invalid or missing argument >&2
+                echo "Usage: $0 [FILE]" >&2
+                return 1
+        fi
+
+        local opts
+        local type
+        local fname=$argv[1]
+
+        type=$(file ${fname}|cut -d' ' -f2)
+
+        case $type in
+                (tar)   opts=tvvvf ;;
+                (gzip)  opts=ztvvvf ;;
+                (bzip2) opts=jtvvvf ;;
+               (xz)    opts=Jtvvvf ;;
+                (*)
+                       # lzma.  Here because lzmainfo is too stupid
+                       if lzmainfo ${fname} &>/dev/null; then
+                               opts=(--lzma -tvvvf)
+                       else
+                               printf "Unknown file type: %s\n" $type >&2
+                               return 2
+                       fi
+                        ;;
+        esac
+
+        tar ${opts} ${fname}|less
+}
+
+ctar()
+{
+        if [[ $ARGC -ne 2 ]]; then
+               echo Invalid or missing argument >&2
+               echo "Usage: $0 [FILE] [DIRECTORY]" >&2
+               return 1
+        fi
+
+        local opts
+        opts=(--create --owner=0 --group=0 --auto-compress --file)
+
+        tar ${opts} $1 $2
+}
+
+pkgsu()
+{
+        if [[ $ARGC -lt 1 || $ARGC -gt 1 ]]; then
+                echo Invalid or missing argument >&2
+                echo "Usage: $0 [PKGUSR]" >&2
+                return 1
+        else
+                ssh -l root localhost -t su $argv[1]
+        fi
+}
+
+pkg_install()
+{
+        if [[ $ARGC -lt 3 || $ARGC -gt 3 ]]; then
+                echo Invalid or missing argument >&2
+                echo "Usage: $0 [DESCRIPTION] [USER] [GROUP]" >&2
+                return 1
+        else
+                ssh -l root localhost -t install_package \
+                  ${argv[1]} $argv[2] $argv[3]
+        fi
+}
+
+alias pkg_ldconfig='ssh -l root localhost -t ldconfig'
+
+### End
+
diff --git a/installdir.lst b/installdir.lst
new file mode 100644 (file)
index 0000000..a8328df
--- /dev/null
@@ -0,0 +1,306 @@
+/bin
+/boot
+/etc
+/etc/X11
+/etc/avahi/services
+/etc/dbus-1/system.d
+/etc/foomatic
+/etc/gconf/schemas
+/etc/gnome/gconf/gconf.xml.defaults
+/etc/init.d
+/etc/opt
+/etc/profile.d
+/etc/sgml
+/etc/udev
+/etc/udev/rules.d
+/lib
+/lib/firmware
+/opt
+/sbin
+/usr/X11R6
+/usr/X11R6/bin
+/usr/X11R6/include
+/usr/X11R6/include/GL
+/usr/X11R6/include/GL/internal
+/usr/X11R6/include/X11
+/usr/X11R6/lib
+/usr/X11R6/lib/X11/fonts
+/usr/X11R6/lib/pkgconfig
+/usr/X11R6/lib/xorg
+/usr/X11R6/lib/xorg/modules
+/usr/X11R6/share
+/usr/X11R6/share/X11/app-defaults
+/usr/X11R6/share/doc
+/usr/X11R6/share/man
+/usr/X11R6/share/man/man1
+/usr/X11R6/share/man/man3
+/usr/X11R6/share/man/man4
+/usr/X11R6/share/man/man5
+/usr/X11R6/share/man/man7
+/usr/X11R6/share/man/man8
+/usr/bin
+/usr/include
+/usr/include/SDL
+/usr/include/apache
+/usr/include/arpa
+/usr/include/linux
+/usr/include/net
+/usr/include/scsi
+/usr/include/sys
+/usr/lib
+/usr/lib/apache
+/usr/lib/bonobo-2.0/samples
+/usr/lib/bonobo/monikers
+/usr/lib/bonobo/servers
+/usr/lib/enlightenment/modules
+/usr/lib/gimp/2.0/plug-ins
+/usr/lib/gnome-vfs-2.0/modules
+/usr/lib/gtk-2.0/modules
+/usr/lib/libglade/2.0
+/usr/lib/mozilla
+/usr/lib/mozilla/components
+/usr/lib/mozilla/plugins
+/usr/lib/orbit-2.0
+/usr/lib/perl5/site_perl
+/usr/lib/pkgconfig
+/usr/lib/ruby/site_ruby
+/usr/lib/sxemacs
+/usr/sbin
+/usr/share
+/usr/share/aclocal
+/usr/share/application-registry
+/usr/share/applications
+/usr/share/dbus-1
+/usr/share/dbus-1/interfaces
+/usr/share/dbus-1/services
+/usr/share/dbus-1/system-services
+/usr/share/doc
+/usr/share/foomatic
+/usr/share/foomatic/db
+/usr/share/foomatic/db/source
+/usr/share/foomatic/db/source/driver
+/usr/share/foomatic/db/source/opt
+/usr/share/foomatic/db/source/printer
+/usr/share/gnome
+/usr/share/gnome/help
+/usr/share/gnome/wm-properties
+/usr/share/gtk-doc
+/usr/share/gtk-doc/html
+/usr/share/hwdata
+/usr/share/icons
+/usr/share/icons/hicolor
+/usr/share/icons/hicolor/128x128
+/usr/share/icons/hicolor/128x128/actions
+/usr/share/icons/hicolor/128x128/animations
+/usr/share/icons/hicolor/128x128/apps
+/usr/share/icons/hicolor/128x128/categories
+/usr/share/icons/hicolor/128x128/devices
+/usr/share/icons/hicolor/128x128/emblems
+/usr/share/icons/hicolor/128x128/emotes
+/usr/share/icons/hicolor/128x128/filesystems
+/usr/share/icons/hicolor/128x128/intl
+/usr/share/icons/hicolor/128x128/mimetypes
+/usr/share/icons/hicolor/128x128/places
+/usr/share/icons/hicolor/128x128/status
+/usr/share/icons/hicolor/128x128/stock
+/usr/share/icons/hicolor/128x128/stock/chart
+/usr/share/icons/hicolor/128x128/stock/code
+/usr/share/icons/hicolor/128x128/stock/data
+/usr/share/icons/hicolor/128x128/stock/form
+/usr/share/icons/hicolor/128x128/stock/image
+/usr/share/icons/hicolor/128x128/stock/io
+/usr/share/icons/hicolor/128x128/stock/media
+/usr/share/icons/hicolor/128x128/stock/navigation
+/usr/share/icons/hicolor/128x128/stock/net
+/usr/share/icons/hicolor/128x128/stock/object
+/usr/share/icons/hicolor/128x128/stock/table
+/usr/share/icons/hicolor/128x128/stock/text
+/usr/share/icons/hicolor/16x16
+/usr/share/icons/hicolor/16x16/apps
+/usr/share/icons/hicolor/192x192
+/usr/share/icons/hicolor/192x192/actions
+/usr/share/icons/hicolor/192x192/animations
+/usr/share/icons/hicolor/192x192/apps
+/usr/share/icons/hicolor/192x192/categories
+/usr/share/icons/hicolor/192x192/devices
+/usr/share/icons/hicolor/192x192/emblems
+/usr/share/icons/hicolor/192x192/emotes
+/usr/share/icons/hicolor/192x192/filesystems
+/usr/share/icons/hicolor/192x192/intl
+/usr/share/icons/hicolor/192x192/mimetypes
+/usr/share/icons/hicolor/192x192/places
+/usr/share/icons/hicolor/192x192/status
+/usr/share/icons/hicolor/192x192/stock
+/usr/share/icons/hicolor/192x192/stock/chart
+/usr/share/icons/hicolor/192x192/stock/code
+/usr/share/icons/hicolor/192x192/stock/data
+/usr/share/icons/hicolor/192x192/stock/form
+/usr/share/icons/hicolor/192x192/stock/image
+/usr/share/icons/hicolor/192x192/stock/io
+/usr/share/icons/hicolor/192x192/stock/media
+/usr/share/icons/hicolor/192x192/stock/navigation
+/usr/share/icons/hicolor/192x192/stock/net
+/usr/share/icons/hicolor/192x192/stock/object
+/usr/share/icons/hicolor/192x192/stock/table
+/usr/share/icons/hicolor/192x192/stock/text
+/usr/share/icons/hicolor/22x22
+/usr/share/icons/hicolor/22x22/apps
+/usr/share/icons/hicolor/24x24
+/usr/share/icons/hicolor/24x24/apps
+/usr/share/icons/hicolor/32x32
+/usr/share/icons/hicolor/32x32/apps
+/usr/share/icons/hicolor/36x36
+/usr/share/icons/hicolor/36x36/actions
+/usr/share/icons/hicolor/36x36/animations
+/usr/share/icons/hicolor/36x36/apps
+/usr/share/icons/hicolor/36x36/categories
+/usr/share/icons/hicolor/36x36/devices
+/usr/share/icons/hicolor/36x36/emblems
+/usr/share/icons/hicolor/36x36/emotes
+/usr/share/icons/hicolor/36x36/filesystems
+/usr/share/icons/hicolor/36x36/intl
+/usr/share/icons/hicolor/36x36/mimetypes
+/usr/share/icons/hicolor/36x36/places
+/usr/share/icons/hicolor/36x36/status
+/usr/share/icons/hicolor/36x36/stock
+/usr/share/icons/hicolor/36x36/stock/chart
+/usr/share/icons/hicolor/36x36/stock/code
+/usr/share/icons/hicolor/36x36/stock/data
+/usr/share/icons/hicolor/36x36/stock/form
+/usr/share/icons/hicolor/36x36/stock/image
+/usr/share/icons/hicolor/36x36/stock/io
+/usr/share/icons/hicolor/36x36/stock/media
+/usr/share/icons/hicolor/36x36/stock/navigation
+/usr/share/icons/hicolor/36x36/stock/net
+/usr/share/icons/hicolor/36x36/stock/object
+/usr/share/icons/hicolor/36x36/stock/table
+/usr/share/icons/hicolor/36x36/stock/text
+/usr/share/icons/hicolor/48x48
+/usr/share/icons/hicolor/48x48/apps
+/usr/share/icons/hicolor/64x64
+/usr/share/icons/hicolor/64x64/actions
+/usr/share/icons/hicolor/64x64/animations
+/usr/share/icons/hicolor/64x64/apps
+/usr/share/icons/hicolor/64x64/categories
+/usr/share/icons/hicolor/64x64/devices
+/usr/share/icons/hicolor/64x64/emblems
+/usr/share/icons/hicolor/64x64/emotes
+/usr/share/icons/hicolor/64x64/filesystems
+/usr/share/icons/hicolor/64x64/intl
+/usr/share/icons/hicolor/64x64/mimetypes
+/usr/share/icons/hicolor/64x64/places
+/usr/share/icons/hicolor/64x64/status
+/usr/share/icons/hicolor/64x64/stock
+/usr/share/icons/hicolor/64x64/stock/chart
+/usr/share/icons/hicolor/64x64/stock/code
+/usr/share/icons/hicolor/64x64/stock/data
+/usr/share/icons/hicolor/64x64/stock/form
+/usr/share/icons/hicolor/64x64/stock/image
+/usr/share/icons/hicolor/64x64/stock/io
+/usr/share/icons/hicolor/64x64/stock/media
+/usr/share/icons/hicolor/64x64/stock/navigation
+/usr/share/icons/hicolor/64x64/stock/net
+/usr/share/icons/hicolor/64x64/stock/object
+/usr/share/icons/hicolor/64x64/stock/table
+/usr/share/icons/hicolor/64x64/stock/text
+/usr/share/icons/hicolor/72x72
+/usr/share/icons/hicolor/72x72/actions
+/usr/share/icons/hicolor/72x72/animations
+/usr/share/icons/hicolor/72x72/apps
+/usr/share/icons/hicolor/72x72/categories
+/usr/share/icons/hicolor/72x72/devices
+/usr/share/icons/hicolor/72x72/emblems
+/usr/share/icons/hicolor/72x72/emotes
+/usr/share/icons/hicolor/72x72/filesystems
+/usr/share/icons/hicolor/72x72/intl
+/usr/share/icons/hicolor/72x72/mimetypes
+/usr/share/icons/hicolor/72x72/places
+/usr/share/icons/hicolor/72x72/status
+/usr/share/icons/hicolor/72x72/stock
+/usr/share/icons/hicolor/72x72/stock/chart
+/usr/share/icons/hicolor/72x72/stock/code
+/usr/share/icons/hicolor/72x72/stock/data
+/usr/share/icons/hicolor/72x72/stock/form
+/usr/share/icons/hicolor/72x72/stock/image
+/usr/share/icons/hicolor/72x72/stock/io
+/usr/share/icons/hicolor/72x72/stock/media
+/usr/share/icons/hicolor/72x72/stock/navigation
+/usr/share/icons/hicolor/72x72/stock/net
+/usr/share/icons/hicolor/72x72/stock/object
+/usr/share/icons/hicolor/72x72/stock/table
+/usr/share/icons/hicolor/72x72/stock/text
+/usr/share/icons/hicolor/96x96
+/usr/share/icons/hicolor/96x96/actions
+/usr/share/icons/hicolor/96x96/animations
+/usr/share/icons/hicolor/96x96/apps
+/usr/share/icons/hicolor/96x96/categories
+/usr/share/icons/hicolor/96x96/devices
+/usr/share/icons/hicolor/96x96/emblems
+/usr/share/icons/hicolor/96x96/emotes
+/usr/share/icons/hicolor/96x96/filesystems
+/usr/share/icons/hicolor/96x96/intl
+/usr/share/icons/hicolor/96x96/mimetypes
+/usr/share/icons/hicolor/96x96/places
+/usr/share/icons/hicolor/96x96/status
+/usr/share/icons/hicolor/96x96/stock
+/usr/share/icons/hicolor/96x96/stock/chart
+/usr/share/icons/hicolor/96x96/stock/code
+/usr/share/icons/hicolor/96x96/stock/data
+/usr/share/icons/hicolor/96x96/stock/form
+/usr/share/icons/hicolor/96x96/stock/image
+/usr/share/icons/hicolor/96x96/stock/io
+/usr/share/icons/hicolor/96x96/stock/media
+/usr/share/icons/hicolor/96x96/stock/navigation
+/usr/share/icons/hicolor/96x96/stock/net
+/usr/share/icons/hicolor/96x96/stock/object
+/usr/share/icons/hicolor/96x96/stock/table
+/usr/share/icons/hicolor/96x96/stock/text
+/usr/share/icons/hicolor/scalable
+/usr/share/icons/hicolor/scalable/apps
+/usr/share/idl
+/usr/share/info
+/usr/share/kbd/consolefonts
+/usr/share/locale
+/usr/share/man
+/usr/share/man/man1
+/usr/share/man/man2
+/usr/share/man/man3
+/usr/share/man/man4
+/usr/share/man/man5
+/usr/share/man/man6
+/usr/share/man/man7
+/usr/share/man/man8
+/usr/share/mime/packages
+/usr/share/misc
+/usr/share/omf
+/usr/share/pixmaps
+/usr/share/pygtk/2.0
+/usr/share/pygtk/2.0/defs
+/usr/share/sgml
+/usr/share/sgml/docbook
+/usr/share/sounds
+/usr/share/sxemacs
+/usr/share/sxemacs/site-packages/etc
+/usr/share/sxemacs/site-packages/info
+/usr/share/sxemacs/site-packages/lisp
+/usr/share/sxemacs/site-packages/pkginfo
+/usr/share/terminfo
+/usr/share/texinfo
+/usr/share/texmf
+/usr/share/texmf-local
+/usr/share/texmf/tex
+/usr/share/texmf/tex/generic
+/usr/share/texmf/tex/generic/epsf
+/usr/share/texmf/tex/texinfo
+/usr/share/themes
+/usr/share/themes/Default
+/usr/share/themes/Emacs
+/usr/share/themes/Raleigh
+/usr/share/xml
+/usr/share/xml/docbook
+/usr/share/zoneinfo
+/var/cache
+/var/lib
+/var/lib/misc
+/var/opt
+/var/spool
diff --git a/lisp/.arch-inventory b/lisp/.arch-inventory
new file mode 100644 (file)
index 0000000..a7b89e1
--- /dev/null
@@ -0,0 +1 @@
+precious ^(pkgusr\.elc)$
diff --git a/lisp/pkgusr.el b/lisp/pkgusr.el
new file mode 100644 (file)
index 0000000..475c8dc
--- /dev/null
@@ -0,0 +1,353 @@
+;; pkgusr.el --- elisp tools for LFS pkgusr package management   -*- Emacs-Lisp -*-
+
+;; Copyright (C) 2007 Steve Youngs
+
+;; Author:     Steve Youngs <steve@sxemacs.org>
+;; Maintainer: Steve Youngs <steve@sxemacs.org>
+;; Created:    <2007-07-13>
+;; Time-stamp: <Thursday Nov  8, 2007 16:27:01 steve>
+;; Homepage:   N/A
+;; Keywords:   utils package-management
+
+;; This file is part of pkgusr.
+
+;; Redistribution and use in source and binary forms, with or without
+;; modification, are permitted provided that the following conditions
+;; are met:
+;;
+;; 1. Redistributions of source code must retain the above copyright
+;;    notice, this list of conditions and the following disclaimer.
+;;
+;; 2. Redistributions in binary form must reproduce the above copyright
+;;    notice, this list of conditions and the following disclaimer in the
+;;    documentation and/or other materials provided with the distribution.
+;;
+;; 3. Neither the name of the author nor the names of any contributors
+;;    may be used to endorse or promote products derived from this
+;;    software without specific prior written permission.
+;;
+;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
+;; IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+;; DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+;; FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+;; CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+;; SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+;; BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+;; OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+;; IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+;;; Commentary:
+;; 
+;;   This is a collection of tools I use with package management here
+;;   on bastard.  A lot of them are elisp ports of some shell functions
+;;   I use for the same.
+
+;;; Todo:
+;;
+;;     
+
+;;; ChangeLog:
+;;
+;;  This is just a place holder so `pkgusr-commentary' will work
+;;  properly.  See the ChangeLog file for changes.
+
+;;; Code:
+(defvar pkgusr-pkg-history nil
+  "History for pkgusr.")
+
+;; Errors
+(define-error 'pkgusr-unknown-cmd "Can't find command")
+(define-error 'pkgusr-unknown-file "Don't recognise file")
+(define-error 'pkgusr-unknown-pkg "Unknown package")
+
+(defun pkgusr-all-pkgs ()
+  "Return a list of all installed packages."
+  (let ((lst (with-temp-buffer
+              (erase-buffer)
+              (insert-file-contents "/etc/group")
+              (re-search-forward "^install:x:9999:" nil t)
+              (narrow-to-region (point) (point-at-eol))
+              (split-string-by-char (buffer-string) ?,))))
+    lst))
+
+(defun pkgusr-pkgs-count ()
+  "Return the number of installed packages."
+  (length (pkgusr-all-pkgs)))
+
+(defun pkgusr-list-pkgs-regexp (regexp)
+  "Return a list of packages matching REGEXP."
+  (interactive "sRegexp: ")
+  (let ((pkgs (pkgusr-all-pkgs))
+       (case-fold-search t)
+       res)
+    (mapcar
+     #'(lambda (pkg)
+        (when (string-match regexp pkg)
+          (push pkg res)))
+     pkgs)
+    (if (interactive-p)
+       (message "%S" (nreverse res))
+      (nreverse res))))
+
+(defconst pkgusr-url-regexp
+  (concat
+   #r"\(\(https?\|ftp\|gopher\|telnet\|wais\)://\|file:/\|s?news:\|mailto:\)"
+   "[^]\t\n \"'()<>[^`{}]*[^]\t\n \"'()<>[^`{}.,;]+")
+  "A regular expression matching URLs.")
+
+(defun pkgusr-url-at-point ()
+  "Browse to a URL from the `pkgusr-show-pkg' buffer."
+  (interactive)
+  (when (extentp (extent-at (point)))
+    (browse-url (extent-string (extent-at (point))))))
+
+(defun pkgusr-url-at-mouse (event)
+  "Browse to a URL at EVENT via the mouse from the `pkgusr-show-pkg' buffer."
+  (interactive "e")
+  (when (extentp (extent-at-event event))
+    (browse-url (extent-string (extent-at-event event)))))
+
+(defconst pkgusr-ext-map
+  (let* ((map (make-sparse-keymap 'pkgusr-ext-map)))
+    (define-key map [button2] 'pkgusr-url-at-mouse)
+    (define-key map [return] 'pkgusr-url-at-point)
+    map)
+  "A keymap for the extents in the `pkgusr-show-pkg' buffer.")
+
+(defun pkgusr-make-url-extents ()
+  "Create extent objects for all the URLs in the buffer."
+  (goto-char (point-min))
+  (save-excursion
+    (while (re-search-forward pkgusr-url-regexp nil t)
+      (let ((extent (make-extent (match-beginning 0) (match-end 0)))
+           (echo "RET or Button2 to visit this URL."))
+       (set-extent-property extent 'face 'bold)
+       (set-extent-property extent 'mouse-face 'highlight)
+       (set-extent-property extent 'keymap pkgusr-ext-map)
+       (set-extent-property extent 'help-echo echo)
+       (set-extent-property extent 'balloon-help echo)
+       (set-extent-property extent 'duplicable t)))))
+
+(defun pkgusr-show-pkg (&optional pkg)
+  "Display a buffer of package details for PKG."
+  (interactive)
+  (let* ((allpkgs (pkgusr-all-pkgs))
+        (pkg (or pkg (completing-read "Show Package: "
+                                      (mapcar #'list allpkgs)
+                                      nil t nil pkgusr-pkg-history)))
+        (buf (get-buffer-create (format "*Package Details: %s*" pkg)))
+        (detail (shell-command-to-string (format "pinky -l %s" pkg))))
+    (with-current-buffer buf
+      (erase-buffer)
+      (insert detail)
+      (pkgusr-make-url-extents))
+    (push-window-configuration)
+    (pop-to-buffer buf)
+    (view-mode nil
+              #'(lambda (b)
+                  (kill-buffer b)
+                  (pop-window-configuration)))))
+
+(defun pkgusr-pkg-install-notes (&optional pkg)
+  "Display the install notes of a PKG."
+  (interactive)
+  (let* ((allpkgs (pkgusr-all-pkgs))
+        (pkg (or pkg (completing-read "Show Package: "
+                                      (mapcar #'list allpkgs)
+                                      nil t nil pkgusr-pkg-history)))
+        start end)
+    (pkgusr-show-pkg pkg)
+    (re-search-forward "^Install Notes:$" nil t)
+    (setq start (point-at-bol))
+    (re-search-forward "^General Notes:$" nil t)
+    (setq end (point-at-bol))
+    (narrow-to-region start end)
+    (goto-char (point-min))))
+
+(defun pkgusr-pkg-general-notes (&optional pkg)
+  "Display the general notes of a PKG."
+  (interactive)
+  (let* ((allpkgs (pkgusr-all-pkgs))
+        (pkg (or pkg (completing-read "Show Package: "
+                                      (mapcar #'list allpkgs)
+                                      nil t nil pkgusr-pkg-history)))
+        start end)
+    (pkgusr-show-pkg pkg)
+    (re-search-forward "^General Notes:$" nil t)
+    (setq start (point-at-bol))
+    (re-search-forward "^CONTENTS:$" nil t)
+    (setq end (point-at-bol))
+    (narrow-to-region start end)
+    (goto-char (point-min))))
+
+(defun pkgusr-cmd-pkg (cmd)
+  "Display the package name \(user:group\) which contains CMD.
+
+If non-interactive, return a list whose car is user and cdr is group."
+  (interactive "sCommand: ")
+  (unless (executable-find cmd)
+    (error 'pkgusr-unknown-cmd cmd))
+  (let* ((cmd (executable-find cmd))
+        (user (user-login-name
+               (nth 2 (file-attributes cmd))))
+        (group (user-login-name
+                (nth 3 (file-attributes cmd)))))
+    (if (interactive-p)
+       (message "Command: %s is from the \"%s\" package \(%s:%2$s\)"
+                cmd group user)
+      (list user group))))
+
+(defun pkgusr-file-pkg (file)
+  "Display the pkg name \(user:group\) which contains FILE.
+
+If non-interactive, return a list whose car is user and cdr is group."
+  (interactive "fFile: ")
+  (let* ((user (user-login-name
+               (nth 2 (file-attributes file))))
+        (group (user-login-name
+                (nth 3 (file-attributes file)))))
+    (if (member user (pkgusr-all-pkgs))
+       (if (interactive-p)
+           (message "File: %s is from the \"%s\" package \(%s:%2$s\)"
+                    file group user)
+         (list user group))
+      (error 'pkgusr-unknown-file file))))
+
+(defun pkgusr-pkg-url (&optional pkg)
+  "Return the URL of PKG as a string."
+  (interactive)
+  (let* ((allpkgs (pkgusr-all-pkgs))
+        (pkg (or pkg (completing-read "Package: "
+                                      (mapcar #'list allpkgs)
+                                      nil t nil pkgusr-pkg-history))))
+    (when (member pkg allpkgs)
+      (with-temp-buffer
+       (erase-buffer)
+       (insert (shell-command-to-string (format "pinky -l %s" pkg)))
+       (goto-char (point-min))
+       (re-search-forward "Web_Site: <\\(.*\\)>$" nil t)
+       (if (interactive-p)
+           (message "[%s URL] %s" pkg (match-string 1))
+         (match-string 1))))))
+
+(defun pkgusr-pkg-repo (&optional pkg)
+  "Return the repo URI of PKG as a string."
+  (interactive)
+  (let* ((allpkgs (pkgusr-all-pkgs))
+        (pkg (or pkg (completing-read "Package: "
+                                      (mapcar #'list allpkgs)
+                                      nil t nil pkgusr-pkg-history)))
+        repo type)
+    (when (member pkg allpkgs)
+      (with-temp-buffer
+       (erase-buffer)
+       (insert (shell-command-to-string (format "pinky -l %s" pkg)))
+       (goto-char (point-min))
+       (re-search-forward "Repo_Type: <?\\(.*\\)>?$" nil t)
+       (setq type (match-string 1))
+       (re-search-forward "Repo_Location: <\\(.*\\)>" nil t)
+       (setq repo (match-string 1)))
+      (if (interactive-p)
+         (message "[%s Repo] %s (%s)" pkg repo type)
+       repo))))
+
+(defun pkgusr-pkg-version (&optional pkg)
+  "Return the version of PKG as a string."
+  (interactive)
+  (let* ((allpkgs (pkgusr-all-pkgs))
+        (pkg (or pkg (completing-read "Package: "
+                                      (mapcar #'list allpkgs)
+                                      nil t nil pkgusr-pkg-history))))
+    (if (member pkg allpkgs)
+       (with-temp-buffer
+         (erase-buffer)
+         (insert (shell-command-to-string (format "pinky -l %s" pkg)))
+         (goto-char (point-min))
+         (re-search-forward "Version: \\(.*$\\)" nil t)
+         (if (interactive-p)
+             (message "[%s Ver] %s" pkg (match-string 1))
+           (match-string 1)))
+      (error 'pkgusr-unknown-pkg pkg))))
+
+(defun pkgusr-pkg-description (&optional pkg)
+  "Return the description of PKG as a string."
+  (interactive)
+  (let* ((allpkgs (pkgusr-all-pkgs))
+        (pkg (or pkg (completing-read "Package: "
+                                      (mapcar #'list allpkgs)
+                                      nil t nil pkgusr-pkg-history))))
+    (if (member pkg allpkgs)
+       (with-temp-buffer
+         (erase-buffer)
+         (insert (shell-command-to-string (format "pinky -l %s" pkg)))
+         (goto-char (point-min))
+         (re-search-forward "Description: \\(.*$\\)" nil t)
+         (if (interactive-p)
+             (message "[%s Desc] %s" pkg (match-string 1))
+           (match-string 1)))
+      (error 'pkgusr-unknown-pkg pkg))))
+
+;; A little bogus perhaps, but it works.  `pkgusr-find-file' is
+;; something that only I can use because it ssh's through root to
+;; get to the pkgusr. And nobody but me would have a need for
+;; `pkgusr-file-history' --SY.
+(defconst pkgusr-pkgmgr "steve"
+  "The Package Manager.
+
+This is a defconst for a reason... to make it a bit harder to customise.
+Just setq'ing this in your init.el won't work if you load pkgusr.el
+after the setq.  Be bold and hard code it in pkgusr.el itself.")
+
+(defmacro defun-when-pkgmgr (&rest args)
+  "Define a function only if you are the right user."
+  `(when (equal (user-login-name) pkgusr-pkgmgr)
+     (defun ,@args)))
+
+(defmacro defvar-when-pkgmgr (&rest args)
+  "Define a variable only if you are the right user."
+  `(when (equal (user-login-name) pkgusr-pkgmgr)
+     (defvar ,@args)))
+
+(defvar-when-pkgmgr pkgusr-file-history nil
+  "History for pkgusr-find-file.")
+
+(defun-when-pkgmgr pkgusr-find-file (pkgusr file)
+  "Using Tramp, find PKGUSR's FILE."
+  (interactive "i\ni")
+  (unless (interactive-p)
+    (error 'invalid-operation "Trying to call interactive-only command"))
+  (let* ((allpkgs (pkgusr-all-pkgs))
+        (puser (completing-read "Package User: "
+                                (mapcar #'list allpkgs)
+                                nil t nil pkgusr-pkg-history))
+        (file (read-file-name (format "[%s] find file: " puser)
+                              (file-name-as-directory
+                               (expand-file-name puser "/usr/src"))
+                              (file-name-as-directory
+                               (expand-file-name puser "/usr/src"))
+                              nil nil pkgusr-file-history))
+        (tpath (format "[multi/ssh:root@localhost/su:%s@localhost]%s"
+                       puser file))
+        (default-directory "/"))
+    (find-file tpath)))
+
+;; Some key bindings
+(global-set-key [(hyper c) c] #'pkgusr-cmd-pkg)
+(global-set-key [(hyper c) f] #'pkgusr-file-pkg)
+(global-set-key [(hyper c) (hyper r)] #'pkgusr-list-pkgs-regexp)
+(global-set-key [(hyper c) d] #'pkgusr-pkg-description)
+(global-set-key [(hyper c) g] #'pkgusr-pkg-general-notes)
+(global-set-key [(hyper c) i] #'pkgusr-pkg-install-notes)
+(global-set-key [(hyper c) r] #'pkgusr-pkg-repo)
+(global-set-key [(hyper c) u] #'pkgusr-pkg-url)
+(global-set-key [(hyper c) v] #'pkgusr-pkg-version)
+(global-set-key [(hyper c) s] #'pkgusr-show-pkg)
+
+(eval-and-compile
+  (when (equal (user-login-name) pkgusr-pkgmgr)
+    (global-set-key [(hyper x) (hyper f)] #'pkgusr-find-file)))
+
+(provide 'pkgusr)
+;;; pkgusr.el ends here
diff --git a/usr/bin/forall_direntries_from b/usr/bin/forall_direntries_from
new file mode 100755 (executable)
index 0000000..23024f5
--- /dev/null
@@ -0,0 +1,124 @@
+#!/bin/bash
+# Copyright (c) 2004 Matthias S. Benkmann <article AT winterdrache DOT de>
+# You may do everything with this code except misrepresent its origin.
+# PROVIDED `AS IS' WITH ABSOLUTELY NO WARRANTY OF ANY KIND!
+
+#The following list should contain the mount points of all filesystems
+#that are to be scanned as a space-separated list within parentheses. 
+#/ will usually be in this list and if you have /usr
+#on a separate partition, it will also be in this list. Other non-special
+#filesystems where package users could own files should also be put in this
+#list.
+#Mount points whose filesystems are special, such as procfs or sysfs must
+#not be in this list. While a simple find on those special filesystems should 
+#be harmless, operations such as "-exec grep something" are NOT SAFE and may 
+#have HARMFUL SIDE-EFFECTS, especially when performed as root. 
+
+## Bastard settings
+# fs_to_scan=(/ /opt /usr /usr/local /var)
+
+fs_to_scan=(/)
+
+#Files with a path prefix found in the following list are ignored.
+#This list will usually contain the parent directory of your package users'
+#home directories, because normally you don't want to scan those. You can
+#also add other directories that will never contain package user files, such
+#as /home. This reduces scan time.
+#NOTE: The LFS-6.0 book uses a ramfs mounted on /dev and with that setup
+#/dev does not need to be in the prune list. But since there is no requirement
+#that /dev have its on filesystem it's better to prune it explicitly.
+
+## Bastard settings
+# prune_prefixes=(\
+#      /tools \
+#      /usr/local/LFS/tools \
+#      /home \
+#      /usr/src \
+#      /dev \
+#      /mnt \
+#      /tmp \
+#      /sys \
+#      /etc/apache/ssl.key \
+#      /etc/cups/ssl \
+#      /etc/firewall \
+#      /etc/skel \
+#      /etc/ssl/private \
+#      /lost+found \
+#      /**/lost+found \
+#      /root \
+#      /usr/local/lost+found \
+#      /**/.{mc,ssh,mozilla,spamassassin} \
+#      /usr/local/media/pr0n \
+#      /usr/local/LFS \
+#      /usr/share/mailman \
+#      /var/{cache,chroot,run,snmp,spool} \
+#      /var/lib/{sshd,nfs,spamassassin,pulse} \
+#      /var/www/htdocs/SXEmacs-issues{,.old} \
+#      /var/lost+found) #NO TRAILING SLASHES!!!!
+
+prune_prefixes=(/home /usr/src /dev /tools) #NO TRAILING SLASHES!!!!
+
+if [ $# -lt 1 -o "$1" = "--help" ]; then
+    echo 1>&2
+    echo 1>&2 'USAGE: '"${0##*/}"' <user_or_group_name> [<find-commands>]'
+    echo 1>&2
+    echo 1>&2 '  If <find-commands> contains no action other than -prune, -print will be'
+    echo 1>&2 '    executed for all matching files.'
+    echo 1>&2 '  Entries will be matched if group and/or user equals <user_or_group_name>'
+    echo 1>&2 '    (numeric UID/GID allowed).'
+    echo 1>&2 '  All matching entries will be acted on, including device special files, so'
+    echo 1>&2 '    you should be extra careful with the <find-commands> you provide!'
+    echo 1>&2
+    exit 1
+fi
+
+#suppress ugly debug output from shell
+trap ':' SIGPIPE
+
+ugname="$1"
+shift 1  #remove user_or_group_name from argument list
+
+ugmatcher=(-false)
+#test if find accepts ugname as a user, and append to ugmatcher if it does
+if find / -maxdepth 0 -user "$ugname" >/dev/null 2>&1 ; then
+    ugmatcher[${#ugmatcher[@]}]="-or"
+    ugmatcher[${#ugmatcher[@]}]="-user"
+    ugmatcher[${#ugmatcher[@]}]="$ugname"
+fi
+#test if find accepts ugname as a group, and append to ugmatcher if it does
+if find / -maxdepth 0 -group "$ugname" >/dev/null 2>&1 ; then
+    ugmatcher[${#ugmatcher[@]}]="-or"
+    ugmatcher[${#ugmatcher[@]}]="-group"
+    ugmatcher[${#ugmatcher[@]}]="$ugname"
+fi
+
+#if find accepted ugname as neither user nor group, then exit
+if [ "${#ugmatcher[@]}" = 1 ]; then
+    echo 1>&2 'find does not accept `'"$ugname'"' as group or user name'
+    exit 1
+fi
+
+#construct find commands that match the prune_prefixes. Each prefix will be
+#matched as -path <prefix> -or -path <prefix>/*
+#so that the directory itself and all subdirectories are matched.
+y=(\( -false)
+for ((i=0; $i<${#prune_prefixes[@]}; i=$i+1))
+do
+    y[${#y[@]}]='-or'
+    y[${#y[@]}]=-path
+    y[${#y[@]}]="${prune_prefixes[$i]}"
+    y[${#y[@]}]='-or'
+    y[${#y[@]}]=-path
+    y[${#y[@]}]="${prune_prefixes[$i]}/*"
+done
+y[${#y[@]}]=')'
+
+#In the following find command, the part
+# -not ( ( "${y[@]}" -prune ) -or "${y[@]}" )
+#is responsible for preventing the files that match prune_prefixes from
+#being processed. The 2nd "${y[@]}" may seem redundant, but it isn't, because
+#-prune has no effect and is always false when -depth is used.
+#The -true before "$@" ensures that -depth can be passed as only parameter.
+find "${fs_to_scan[@]}" -xdev -noleaf \
+    -not \( \( "${y[@]}" -prune \) -or "${y[@]}" \) \
+    -and \( "${ugmatcher[@]}" \) -and \( -true "$@" \)
diff --git a/usr/bin/grep_all_regular_files_for b/usr/bin/grep_all_regular_files_for
new file mode 100755 (executable)
index 0000000..5f741d7
--- /dev/null
@@ -0,0 +1,78 @@
+#!/bin/bash
+# Copyright (c) 2004 Matthias S. Benkmann <article AT winterdrache DOT de>
+# You may do everything with this code except misrepresent its origin.
+# PROVIDED `AS IS' WITH ABSOLUTELY NO WARRANTY OF ANY KIND!
+
+#The following list should contain the mount points of all filesystems
+#that are to be scanned as a space-separated list within parentheses. 
+#/ will usually be in this list and if you have /usr
+#on a separate partition, it will also be in this list. 
+#Mount points whose filesystems are special, such as procfs or sysfs must
+#not be in this list. While a simple find on those special filesystems should 
+#be harmless, operations such as "-exec grep something" are NOT SAFE and may 
+#have HARMFUL SIDE-EFFECTS, especially when performed as root. 
+
+## Bastard settings
+# fs_to_scan=(/ /opt /usr /usr/local /var)
+
+fs_to_scan=(/)
+
+#Files with a path prefix found in the following list are ignored. As the
+#main function of this script is to help you find files that contain
+#hardwired paths to /tools or other unwanted references to
+#your build system, you will usually prune any directories that don't contain
+#files of interest, such as /tools (whose files naturally refer to /tools)
+#and your package users' home directories (which may also test positive if
+#you have unpacked and configured sources lying around).
+#NOTE: The LFS-6.0 book uses a ramfs mounted on /dev and with that setup
+#/dev does not need to be in the prune list. But since there is no requirement
+#that /dev have its on filesystem it's better to prune it explicitly.
+prune_prefixes=(/home /usr/src /dev /tools) #NO TRAILING SLASHES!!!
+
+if [ $# -lt 1 -o "$1" = "--help" ]; then
+    echo 1>&2 
+    echo 1>&2 'USAGE: '"${0##*/}"' <grep-commands>'
+    echo 1>&2 
+    echo 1>&2 '  grep -l <grep-commands> -- <file>'
+    echo 1>&2 '  will be executed for each *regular file* <file>'
+    echo 1>&2 '  ATTENTION! If you override the -l switch with a switch that makes grep'
+    echo 1>&2 '  output all individual matches rather than just the matching files,'
+    echo 1>&2 '  then DO NOT redirect output to a file that is in a directory that will be'
+    echo 1>&2 '  scanned, or you risk creating an endless loop that will cause your'
+    echo 1>&2 '  output file to grow till your disk is full.'
+    echo 1>&2 
+    exit 1
+fi
+
+#suppress ugly debug output from shell
+trap ':' SIGPIPE
+
+#construct find commands that match the prune_prefixes. Each prefix will be
+#matched as -path <prefix> -or -path <prefix>/*
+#so that the directory itself and all subdirectories are matched.
+y=(\( -false)
+for ((i=0; $i<${#prune_prefixes[@]}; i=$i+1)) 
+do
+    y[${#y[@]}]='-or'
+    y[${#y[@]}]=-path
+    y[${#y[@]}]="${prune_prefixes[$i]}"
+    y[${#y[@]}]='-or'
+    y[${#y[@]}]=-path
+    y[${#y[@]}]="${prune_prefixes[$i]}/*"
+done
+y[${#y[@]}]=')'
+
+cmd_pre=(-type f -exec grep -l)
+cmd_post=(-- {} \;)
+
+#In the following find command, the part
+# -not ( ( "${y[@]}" -prune ) -or "${y[@]}" )
+#is responsible for preventing the files that match prune_prefixes from
+#being processed. The 2nd "${y[@]}" may seem redundant, but it isn't, because
+#-prune has no effect and is always false when -depth is used (which someone
+#might do in the future).
+#The -true before "$@" ensures that -depth can be passed as 1st parameter
+#of $cmd_pre (should someone change it in the future).
+find "${fs_to_scan[@]}" -xdev -noleaf \
+    -not \( \( "${y[@]}" -prune \) -or "${y[@]}" \) \
+    -and \( -true "${cmd_pre[@]}" "$@" "${cmd_post[@]}" \)
diff --git a/usr/bin/header-symbol-search b/usr/bin/header-symbol-search
new file mode 100755 (executable)
index 0000000..a8b5dc8
--- /dev/null
@@ -0,0 +1,79 @@
+#!/bin/bash
+
+# Copyright (C) 2008 Steve Youngs
+
+# Author:     Steve Youngs <steve@sxemacs.org>
+# Maintainer: Steve Youngs <steve@sxemacs.org>
+# Created:    <2008-03-10>
+# Time-stamp: <Tuesday Mar 11, 2008 00:23:45 steve>
+# Homepage:   N/A
+# Keywords:   utils package-management
+
+# This file is part of pkgusr.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright
+#    notice, this list of conditions and the following disclaimer in the
+#    documentation and/or other materials provided with the distribution.
+#
+# 3. Neither the name of the author nor the names of any contributors
+#    may be used to endorse or promote products derived from this
+#    software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+# BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+# OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+# IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+## Commentary:
+# 
+#  This script is used to locate headers that declare symbol.  symbol
+#  being the thing you are searching for.  This is good for those
+#  "unknown reference" errors.
+
+## Code:
+ourname=${0##*/}
+
+# Usage
+if [ $# -lt 1 -o "$1" = "--help" -o "$1" = "--usage" -o "$1" = "-h" ]; then
+       echo 1>&2
+       echo 1>&2 'USAGE:  ${ourname} <symbol_regexp>'
+       echo 1>&2
+       echo 1>&2 '  Find headers that declare symbol matching <symbol_regexp>.'
+       echo 1>&2
+       echo 1>&2 '  See grep(1), '"'REGULAR EXPRESSIONS'"' for the syntax of regexps'
+       echo 1>&2 '  used by this script.'
+       echo 1>&2
+       exit 1
+fi
+
+# Header directories.
+# These are the directories we search.  If you have other header directories
+# such as /usr/local/include, /opt/include, add them here.
+header_dirs=(/usr/include /usr/X11/include)
+
+sym=${1}
+
+all_headers=$(find -H -L ${header_dirs[*]} -type f -name "*.h" -print)
+
+### FIXME: This is totally brain dead, it'll match on shit you don't
+#          want like comments.
+#    A possible solution might be to use etags in some way.
+for header in ${all_headers} ; do
+    grep -E -l --mmap --colour ${sym} ${header} 2>/dev/null
+done
+
+## header-symbol-search ends here
diff --git a/usr/bin/lesspipe.sh b/usr/bin/lesspipe.sh
new file mode 100755 (executable)
index 0000000..7850a48
--- /dev/null
@@ -0,0 +1,68 @@
+#!/bin/sh
+#
+# Copyright 1997, 1998, 1999, 2000  Patrick Volkerding, Moorhead, MN, USA
+# Copyright 2001, 2002  Slackware Linux, Inc, Concord, CA, USA
+# Copyright 2006, 2009  Patrick Volkerding, Sebeka, MN, USA
+# All rights reserved.
+#
+# Redistribution and use of this script, with or without modification, is
+# permitted provided that the following conditions are met:
+#
+# 1. Redistributions of this script must retain the above copyright
+#    notice, this list of conditions and the following disclaimer.
+#
+#  THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+#  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+#  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO
+#  EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+#  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+#  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+#  OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+#  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+#  OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+#  ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+# This is a preprocessor for 'less'.  It is used when this environment
+# variable is set:   LESSOPEN="|lesspipe.sh %s"
+
+lesspipe() {
+  case "$1" in
+  *.tar) tar tvvf "$1" 2>/dev/null ;;
+  *.tgz | *.tar.gz | *.tar.Z | *.tar.z | *.tar.bz2 | *.tbz ) tar tvvf "$1" 2>/dev/null ;;
+  *.tlz | *.tar.lzma ) lzma -dc "$1" 2> /dev/null | tar tvvf - 2> /dev/null ;;
+  *.txz | *.tar.xz ) xz -dc "$1" 2> /dev/null | tar tvvf - 2> /dev/null ;;
+  *.zip) unzip -l "$1" 2>/dev/null ;;
+  *.rpm) rpm -qpvl "$1" 2>/dev/null ;;
+  *.rar) # check if rar is installed first
+    if which rar 1> /dev/null ; then
+      `which rar` t "$1" 
+    fi ;;
+  *.1|*.2|*.3|*.4|*.5|*.6|*.7|*.8|*.9|*.n|*.man) # *roff src?
+    if file -L "$1" | grep roff 1> /dev/null ; then
+      nroff -S -mandoc "$1"
+    fi ;;
+  *.1.gz|*.2.gz|*.3.gz|*.4.gz|*.5.gz|*.6.gz|*.7.gz|*.8.gz|*.9.gz|*.n.gz|*.man.gz) # compressed *roff src?
+    if gzip -dc "$1" | file - | grep roff 1> /dev/null ; then
+      gzip -dc "$1" | nroff -S -mandoc -
+    else gzip -dc "$1"  2>/dev/null
+    fi ;;
+  *.1.bz2|*.2.bz2|*.3.bz2|*.4.bz2|*.5.bz2|*.6.bz2|*.7.bz2|*.8.bz2|*.9.bz2|*.n.bz2|*.man.bz2) # compressed *roff src?
+    if bzip2 -dc "$1" | file - | grep roff 1> /dev/null ; then
+      bzip2 -dc "$1" | nroff -S -mandoc -
+    fi ;;
+  *.gz) gzip -dc "$1"  2>/dev/null ;;
+  *.bz2) bzip2 -dc "$1" 2>/dev/null ;;
+  *.lzma) lzma -dc "$1" 2>/dev/null ;;
+  *.xz) xz -dc "$1" 2>/dev/null ;;
+#  *) FILE=`file -L "$1"` ; # Check to see if binary, if so -- view with 'strings'
+#    FILE1=`echo $FILE | cut -d ' ' -f 2`
+#    FILE2=`echo $FILE | cut -d ' ' -f 3`
+#    if [ "$FILE1" = "Linux/i386" -o "$FILE2" = "Linux/i386" \
+#         -o "$FILE1" = "ELF" -o "$FILE2" = "ELF" ]; then
+#      strings "$1"
+#    fi ;;
+  esac
+}
+
+lesspipe "$1"
diff --git a/usr/bin/library-symbol-search b/usr/bin/library-symbol-search
new file mode 100755 (executable)
index 0000000..aa9123a
--- /dev/null
@@ -0,0 +1,104 @@
+#!/bin/bash
+
+# Copyright (C) 2008 Steve Youngs
+
+# Author:     Steve Youngs <steve@sxemacs.org>
+# Maintainer: Steve Youngs <steve@sxemacs.org>
+# Created:    <2008-03-10>
+# Time-stamp: <Tuesday Apr  1, 2008 00:52:35 steve>
+# Homepage:   N/A
+# Keywords:   utils package-management
+
+# This file is part of pkgusr.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright
+#    notice, this list of conditions and the following disclaimer in the
+#    documentation and/or other materials provided with the distribution.
+#
+# 3. Neither the name of the author nor the names of any contributors
+#    may be used to endorse or promote products derived from this
+#    software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+# BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+# OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+# IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+## Commentary:
+# 
+#  This script is used to locate libraries that define a symbol
+#  (function).  It comes in very handy when package builds fail with
+#  "undefined reference..." errors.  By default, only defined syms
+#  are searched for, but with -a option, all syms, both defined and
+#  undefined are searched.
+#
+#  First, it tries with nm(1), and if that gets nowhere (like in the
+#  case of stripped libraries), fall back on objdump(1).
+
+## Code:
+ourname=${0##*/}
+
+# Usage
+if [ $# -lt 1 -o "$1" = "--help" -o "$1" = "--usage" -o "$1" = "-h" ]; then
+       echo 1>&2
+       echo 1>&2 'USAGE:  ${ourname} [ -a ] <symbol_regexp>'
+       echo 1>&2
+       echo 1>&2 '  Find libraries that export symbol matching <symbol_regexp>.'
+       echo 1>&2 '  By default, only libraries that define the symbol are reported.'
+       echo 1>&2 '  With the '"'-a'"' option, libraries that have the symbol, but'
+       echo 1>&2 '  it is undefined, or a debugging symbol, are also listed.'
+       echo 1>&2
+       echo 1>&2 '  See grep(1), '"'REGULAR EXPRESSIONS'"' for the syntax of regexps'
+       echo 1>&2 '  used by this script.'
+       echo 1>&2
+       exit 1
+fi
+
+# Library directories.
+# These are the directories we search.  If you have other lib directories
+# such as /usr/local/lib, /opt/lib, add them here.
+lib_dirs=(/usr/lib /usr/X11/lib /lib)
+
+if [ "$1" = "-a" ]; then
+    nm_opts="--demangle=gnu-v3 --debug-syms"
+    shift
+else
+    nm_opts="--demangle=gnu-v3 --defined-only --dynamic"
+fi
+obj_opts="--demangle=gnu-v3 --reloc --dynamic-reloc --syms --dynamic-syms"
+
+# $1 should now be the symbol (or symbol regexp) to search for
+sym=${1}
+
+all_libs=$(find -H -L ${lib_dirs[*]} -type f \( -name "lib*.so" -o -name "lib*.a" \) -print)
+
+for lib in ${all_libs} ; do
+    unset good_file lib_syms
+    good_file="$(file ${lib}|grep \(\(shared object\)\|\(ar archive\)\) 2>/dev/null)"
+    if [ -n "${good_file}" ]; then
+       lib_syms=$(nm ${nm_opts} ${lib} 2>/dev/null)
+       if [ -n "${lib_syms}" ]; then
+           echo ${lib_syms}|grep -E ${sym} 1>/dev/null && echo ${lib}
+       else
+           objdump ${obj_opts} ${lib} 2>/dev/null |
+               grep -E ${sym} 1>/dev/null &&
+               echo ${lib}
+       fi
+    fi
+done
+
+## library-symbol-search ends here
diff --git a/usr/bin/list_package b/usr/bin/list_package
new file mode 100755 (executable)
index 0000000..2269573
--- /dev/null
@@ -0,0 +1,276 @@
+#!/bin/bash
+# Copyright (c) 2004 Matthias S. Benkmann <article AT winterdrache DOT de>
+# You may do everything with this code except misrepresent its origin.
+# PROVIDED `AS IS' WITH ABSOLUTELY NO WARRANTY OF ANY KIND!
+
+if [ $# -lt 1 -o \( $# -gt 1 -a "z$1" != "z:man" -a "z$1" != "z:mani" -a "z$1" != "z:lib" \) -o "$1" = "--help" ]; then
+    echo 1>&2
+    echo 1>&2 'USAGE: '"${0##*/}"' <user_or_group_name>'
+    echo 1>&2
+    echo 1>&2 '  Entries will be matched if group and/or user equals <user_or_group_name>'
+    echo 1>&2 '    (numeric UID/GID allowed).'
+    echo 1>&2 '  This script uses `forall_direntries_from'"'"' and `list_suspicious_files_from'"'."
+    echo 1>&2
+    echo 1>&2 '  NOTE: Several minutes may pass before you see the first output.'
+    echo 1>&2 '  You should probably redirect output to a file for later reference.'
+    echo 1>&2
+    echo 1>&2 '  WARNING! This program is for listing files from package users only!'
+    echo 1>&2 '           Do NOT use it to list files from untrusted users!'
+    echo 1>&2 '           An untrusted user could set up a manipulated manpage to exploit'
+    echo 1>&2 '           a bug in man when it is used to extract the summary!'
+    exit 1
+fi
+
+# KNOWN BUGS:
+#  - when extracting summaries from manpages, candidate manpages are considered
+#    in alphabetic order rather than the order used by the man command.
+#    The problem with this is that section 8, which contains manpages for
+#    admin commands, will be considered after lower-numbered sections.
+#    In the rare case that an admin command has the same name as a topic from
+#    a lower-numbered manpage installed by the same package, the summary will
+#    be taken from the wrong manpage.
+#    An example for such a clash are the faillog.5 and faillog.8 manpages from
+#    the shadow package.
+#    Because this problem is difficult to fix, rare and easily spotted (since
+#    the manpage that should have provided the summary will be listed under
+#    EXTRA MANPAGES) I won't fix it.
+
+ugname="$1"
+
+#suppress ugly debug output from shell
+trap ':' SIGPIPE
+
+if [ $# -gt 1 ]; then
+    name="${2##*/}"
+    case "$1" in
+        :man) 
+            name=${name%.gz}
+           name=${name%.bz2}
+           name=${name%.*}
+           echo $'command\2'"$name"$'\2man\2'"$2" ;;
+        :mani) 
+            name=${name%.gz}
+           name=${name%.bz2}
+           name=${name%.*}
+           echo $'command\2'"$name"$'\2mani\2'"$2" ;;
+        :lib) 
+            name=${name%.a}
+           name=${name%%.so*}
+           echo "lib $name" 
+           ;;
+    esac
+    exit 0
+fi
+
+sanitize() { tr -c '[:print:]' '?' ; }
+
+# $1: <commandname>
+# $2: command\2<commandname>\2cmd\2(-><linktarget>)
+# $3: command\2<commandname>\2man[i]\2<manpage_path>  or  <empty>
+expand_command()
+{
+    sep=$'\2'
+    cmdname="$1"
+    cmdline="$2"
+    manline="$3"
+    linktarget="${cmdline##*${sep}}"
+  
+    if [ -z "$manline" ]; then
+       description='No manpage'
+        #the "l" at the beginning is just to make it sort after "lib"
+       echo -n "lmanlessbin $cmdname" | sanitize
+       echo 
+    
+    else # if [ ! -z "$manline" ]; then
+       manpage=${manline##*${sep}}
+       wsc='[[:space:],]\+'
+        # The `t l;d;:l;n;b l' in the sed command is voodoo magic to make sed
+        # output only the first match but to keep eating up all input. I use this
+        # instead of  `| head -n 1', because head breaks the pipe after doing
+        # its 1 line output, which (if it happens before sed has processed the
+        # complete input) freaks out man and causes it to emit a totally
+        # silly error message including "No such file or directory", which is
+        # annoying when you do testing without suppressing man's errors.
+        # The $'s/.\b\\(.\\)/\\1/g;s/\x1b[^m]\\+m//g' removes the backspace-based 
+        # as well as ESC-based formatting from man's output. 
+       description="$( { COLUMNS=300 man "$manpage" 2>/dev/null || 
+                                      echo " $name - Broken manpage" ; } | 
+        sed $'s/.\b\\(.\\)/\\1/g;s/\x1b[^m]\\+m//g' |
+        sed -n "/^NAME/,/^[A-Z]/s/^.*${wsc}${cmdname}${wsc}.*-\+${wsc}\(.*\)/\1/p;t l;d;:l;n;b l" )"
+        if [ -z "$description" ]; then
+           description="$( COLUMNS=300 man "$manpage" 2>/dev/null |
+            sed $'s/.\b\\(.\\)/\\1/g;s/\x1b[^m]\\+m//g' |
+            sed -n "s/^.*${wsc}..*${wsc}.*-\+${wsc}\(.*\)/\1/p;t l;d;:l;n;b l" )"
+        fi
+       test -z "$description" && description="Weird manpage"
+    fi
+
+    echo -n "binexe $cmdname" | sanitize
+    test "$linktarget" != '(->)' && echo -n "$linktarget" | sanitize
+    echo 
+    #the "lx" in "lxdescription" is just to make sure it sorts after "lmanlessbin"
+    echo -n "lxdescription $cmdname: $description" | sanitize  
+    echo 
+}
+
+# NOTE: The -path and -lname stuff at the beginning of the following is
+# there to make sure that none of the lines output by find contains
+# a) \n or \r, because that would mess up post-processing the output 
+#    line-by-line.
+# b) \x7f, because this character triggers one of the nastier bash-bugs
+#    wrt string handling
+# c) \2, because I use this as separator within the lines
+#    (Why \2 and not \0 or \1 ? Because bash can't cope with \0 at all and has
+#    bugs related to \1.)
+#
+# Because of this, having the final section called "ALL FILES" is technically
+# a lie, because files with a path containing one of the abovementioned
+# characters will not appear in output.
+# However, a) no sane package contains such files
+#          b) they will be listed in the output from list_suspicious_files
+cmd=(\( -path $'*\n*' -or -path $'*\r*' -or -path $'*\x7f*' 
+        -or -path $'*\2*'
+        -or -lname $'*\n*' -or -lname $'*\r*' -or -lname $'*\x7f*' 
+        -or -lname $'*\2*' 
+     \) 
+     -or
+     \(
+       \( -printf "zall %p\n" \) ,
+       \(
+         \( -type f -or -xtype f \) -and
+         \(
+           \( -perm -u+x \( -path "*/bin/*" -or -path "*/sbin/*" \) -printf 'command\2%f\2cmd\2(->%l)\n' \)
+       -or \( -path "*/man/man*/*" -exec "$0" ":man" {} \; \)
+       -or \( -path "*/man/*/man*/*" -exec "$0" ":mani" {} \; \)
+       -or \( \( -name "lib*.so" -or -name "lib*.a" -or -name "lib*.so.*" \) -path "*/lib/*" -exec "$0" ":lib" {} \; \)
+       -or \( -type f -perm -u+x -not \( \( -name "lib*.so" -or -name "lib*.a" -or -name "lib*.so.*" \) -path "*/lib/*" \)  -printf "nobinexe %p\n" \)
+         \)
+       \)  
+     \)
+    )
+
+forall_direntries_from "$ugname" "${cmd[@]}" | sort -u |
+{
+    sep=$'\2'
+    hold=''
+    for((;;)); do
+       if [ -z "$hold" ]; then
+           read -r line || break
+       else
+           line="$hold"
+           hold=''
+       fi
+    
+       case "z$line" in
+           zcommand${sep}*${sep}cmd${sep}*) 
+            cmdname=${line#command${sep}}
+           cmdname=${cmdname%%${sep}*}
+           read -r hold
+               case "z$hold" in
+                   zcommand${sep}${cmdname}${sep}man${sep}*|zcommand${sep}${cmdname}${sep}mani${sep}*) 
+                    expand_command "$cmdname" "$line" "$hold"
+                   hold=''
+                   ;;
+          
+                   z*) 
+                        expand_command "$cmdname" "$line" ""
+                       ;;
+                esac
+               ;;
+
+               zcommand${sep}*${sep}man${sep}*|command${sep}*${sep}mani${sep}*) 
+         
+               echo -n "manextra ${line##*${sep}}" | sanitize
+               echo
+               ;;
+
+            z*) 
+                echo -n "$line" | sanitize
+               echo
+               ;;
+       esac
+    
+  done
+} | sort |  #no -u here, bc. the above processing may equalize different files
+{
+# (1) binexe: Executables (in *bin/)
+# (2) lib: Libraries (in *lib/*)
+# (3) lmanlessbin: Executables (in *bin/) without manpages
+# (4) lxdescription: Summaries for executables (in *bin/)
+# (5) manextra: Extra manpages
+#     full paths, no perms
+# (6) nobinexe: Executables not in *bin/ (excluding *lib/*.so and *lib/*.so.*)
+#     full paths, no perms
+# (7) zall: All files
+#     full paths, no perms
+
+    curstate=''
+    while read -r line ; do
+       newstate="${line%% *}"
+       if [ "$newstate" != "$curstate" ]; then
+           curstate="$newstate"
+           case "$curstate" in
+               binexe)
+                    echo 'EXECUTABLES (in */bin or */sbin)'
+                   echo -n "  ${line#binexe }"
+                   ;;
+                lib)
+                    echo
+                   echo
+                   echo 'LIBRARIES (lib*.a or lib*.so)'
+                   echo -n "  ${line#lib }"
+                   ;;
+                lmanlessbin)
+                    echo
+                   echo
+                   echo 'EXECUTABLES WITH NO MANPAGE (in */bin or */sbin)'
+                   echo -n "  ${line#lmanlessbin }"
+                   ;;
+                lxdescription)
+                    echo
+                   echo
+                   echo 'MANPAGE SUMMARIES OF EXECUTABLES (in */bin or */sbin)'
+                   echo "  ${line#lxdescription }"
+                   ;;
+                manextra)
+                    echo
+                   echo 'EXTRA MANPAGES'
+                   echo "  ${line#manextra }"
+                   ;;
+                nobinexe)
+                    echo
+                   echo 'EXTRA EXECUTABLES (not in */bin or */sbin)'
+                   echo "  ${line#nobinexe }"
+                   ;;
+                zall)
+                    echo
+                   echo 'ALL FILES'
+                   echo "  ${line#zall }"
+                   ;;
+                *) 
+                    echo
+                   echo
+                   echo 'UNEXPECTED LINE'
+                   echo "  $line"
+                   ;;
+            esac
+       else
+           case "$curstate" in
+               binexe) echo -n ", ${line#binexe }" ;;
+               lib) echo -n ", ${line#lib }" ;;
+               lmanlessbin) echo -n ", ${line#lmanlessbin }" ;;
+               lxdescription) echo "  ${line#lxdescription }" ;;
+               manextra) echo "  ${line#manextra }" ;;
+               nobinexe) echo "  ${line#nobinexe }" ;;
+               zall) echo "  ${line#zall }" ;;
+               *) 
+                   echo
+                   echo 'UNEXPECTED LINE'
+                   echo "  $line"
+                   ;;
+           esac
+       fi
+    done
+} 
+
+list_suspicious_files_from "$ugname"
diff --git a/usr/bin/list_suspicious_files b/usr/bin/list_suspicious_files
new file mode 100755 (executable)
index 0000000..3697b79
--- /dev/null
@@ -0,0 +1,226 @@
+#!/bin/bash
+# Copyright (c) 2004 Matthias S. Benkmann <article AT winterdrache DOT de>
+# You may do everything with this code except misrepresent its origin.
+# PROVIDED `AS IS' WITH ABSOLUTELY NO WARRANTY OF ANY KIND!
+
+#The following list should contain the mount points of all filesystems
+#that are to be scanned as a space-separated list within parentheses. 
+#/ will usually be in this list and if you have /usr
+#on a separate partition, it will also be in this list. Other non-special
+#filesystems where suspicious files could be located should also be put in 
+#this list.
+#Mount points whose filesystems are special, such as procfs or sysfs should
+#not be in this list. 
+
+## Bastard settings
+# fs_to_scan=(/ /opt /usr /usr/local /var)
+
+fs_to_scan=(/)
+
+#Files with a path prefix found in the following list are ignored.
+#DO !!!!NOT!!! PUT /usr/src OR WHATEVER THE HOME DIRECTORY prefix is for your
+#package users into this list!!! You DO want to scan those directories in
+#order to spot e.g. world-writable tarballs and other abominations that
+#may have crept in.
+#Ideally, this list should be empty.
+
+## Bastard settings
+# prune_prefixes=(/*/\{arch\}) #NO TRAILING SLASHES!!!
+
+prune_prefixes=()
+
+#If the following variable is set to "yes", then files that contain
+#control characters or other non-printable characters (except for space)
+#will be reported as suspicious. 
+#This test slows down the search considerably!
+enable_illchars=yes
+
+
+#suppress ugly debug output from shell
+trap ':' SIGPIPE
+
+#"-false" as 1st argument is used when called by list_suspicious_files_from
+if [ $# -ge 1 -a "$1" != "-false" ]; then
+    echo 1>&2 
+    echo 1>&2 "USAGE: ${0##*/}"
+    echo 1>&2 
+    echo 1>&2 '  Outputs a categorized list of files and directories with properties'
+    echo 1>&2 '  that could mean trouble and should be investigated.'
+    echo 1>&2
+    exit 1
+fi
+
+
+usergroupmatch=(-true)
+if [ "$1" = "-false" ]; then
+    usergroupmatch=(\( "$@" \))
+fi
+
+#construct find commands that match the prune_prefixes. Each prefix will be
+#matched as -path <prefix> -or -path <prefix>/*
+#so that the directory itself and all subdirectories are matched.
+y=(\( -false)
+for ((i=0; $i<${#prune_prefixes[@]}; i=$i+1)) 
+do
+    y[${#y[@]}]='-or'
+    y[${#y[@]}]=-path
+    y[${#y[@]}]="${prune_prefixes[$i]}"
+    y[${#y[@]}]='-or'
+    y[${#y[@]}]=-path
+    y[${#y[@]}]="${prune_prefixes[$i]}/*"
+done
+y[${#y[@]}]=')'
+
+illchars=( $'\x1'  $'\x2'  $'\x3'  $'\x4'  $'\x5'  $'\x6'  $'\x7'  $'\x8'  
+  $'\x9'  $'\xA'  $'\xB'  $'\xC'  $'\xD'  $'\xE'  $'\xF'  $'\x10'  $'\x11' 
+  $'\x12'  $'\x13'  $'\x14'  $'\x15'  $'\x16'  $'\x17'  $'\x18'  $'\x19'  
+  $'\x1A'  $'\x1B'  $'\x1C'  $'\x1D'  $'\x1E'  $'\x1F'  $'\x7f' $'\x80' 
+  $'\x81'  $'\x82'  $'\x83'  $'\x84'  $'\x85'  $'\x86'  $'\x87'  $'\x88'  
+  $'\x89'  $'\x8A'  $'\x8B'  $'\x8C'  $'\x8D'  $'\x8E'  $'\x8F'  $'\x90'  
+  $'\x91'  $'\x92'  $'\x93'  $'\x94'  $'\x95'  $'\x96'  $'\x97'  $'\x98'  
+  $'\x99'  $'\x9A'  $'\x9B'  $'\x9C'  $'\x9D'  $'\x9E'  $'\x9F' ) 
+
+
+if [ "$enable_illchars" = yes ]; then
+
+    illname=(\( -false)
+    for ((i=0; $i<${#illchars[@]}; i=$i+1)) 
+    do
+        #handle bash \x7f error
+        if [ "*${illchars[$i]}*" = "**" ]; then
+           illchars[$i]=$'\x80'  #'
+       fi
+       illname[${#illname[@]}]='-or'
+       illname[${#illname[@]}]=-name
+        illname[${#illname[@]}]="*${illchars[$i]}*"
+    done
+    illname[${#illname[@]}]=')'
+
+    illlink=(\( -false)
+    for ((i=0; $i<${#illchars[@]}; i=$i+1)) 
+    do
+        illlink[${#illlink[@]}]='-or'
+        illlink[${#illlink[@]}]=-lname
+        illlink[${#illlink[@]}]="*${illchars[$i]}*"
+    done
+    illlink[${#illlink[@]}]=')'
+else #if [ "$enable_illchars" = no ]
+    illlink=(-false)
+    illname=(-false)
+fi
+
+# $1=section heading
+# $2=inode message
+report()
+{
+    echo -printf "increment_code_here"
+    echo -printf 
+    echo "1 ${1}\\n" | sed 's/ /\\040/g'
+    echo -printf "insert_code_here"
+  
+    if [ -n "$2" ]; then
+       echo -printf 
+       echo "2 %i 1 ${2}\\n" | sed 's/ /\\040/g'
+       echo -printf "insert_code_here"
+       echo -printf 
+       echo "2 %i 2 " | sed 's/ /\\040/g'
+    else
+       echo -printf "2\\040" 
+    fi
+  
+    echo -exec ls -T 0 -ladQ {} \;
+}
+
+
+filegoodperm=(\( -perm 644 -or -perm 755 -or -perm 555 -or -perm 444 -or -perm 600 -or -perm 700 -or -perm 640 \))
+dirgoodperm=(\( -perm 755 -or -perm 555 -or -perm 700 -or -perm 750 \))
+
+good=( \( 
+          -not \( -not -type d -links +1 \)
+          -not -nouser -not -nogroup 
+          -not \( "${illname[@]}" \) 
+          -not \( "${illlink[@]}" \) 
+       \) 
+       -and
+\(
+      \( -type f -not -group install "${filegoodperm[@]}" \) 
+  -or \( -type d -not -group install "${dirgoodperm[@]}" \)
+  -or \( -type d -group install \( -perm 1775 \) \)
+  -or \( -type d -group root -user root -path "/tmp" \( -perm 1777 \) \)
+  -or \( -type d -group root -user root -path "/var/tmp" \( -perm 1777 \) \)
+  -or \( -not -type d -not -type f -not -type l -path "/dev/*" \)
+  -or \( -type l \( -xtype b -or -xtype c -or -xtype d -or -xtype p -or -xtype f \) \)
+\)
+)
+
+bad=(
+    \( "${illname[@]}" $(report  "NON-PRINTABLE CHAR IN NAME")  \)
+ OP \( "${illlink[@]}" $(report  "NON-PRINTABLE-CHAR IN LINK-TARGET")  \)
+ OP \( -type f -perm -4000 $(report  "SETUID FILES")  \)
+ OP \( -type f -perm -2000 $(report  "SETGID FILES")  \)
+ OP \( -type f -perm -1000 $(report  "STICKY FILES")  \)
+ OP \( -type d -perm -2000 $(report  "GROUP-KEEPING DIRECTORIES")  \)
+ OP \( -type d -not -group install -perm -1000  $(report  "STICKY DIRECTORIES")  \)
+ OP \( -type f -perm -g+w  $(report  "GROUP-WRITABLE FILES")  \)
+ OP \( -type f -perm -o+w  $(report  "WORLD-WRITABLE FILES")  \)
+ OP \( -type d -perm -g+w  $(report  "GROUP-WRITABLE DIRECTORIES")  \)
+ OP \( -type d -perm -o+w  $(report  "WORLD-WRITABLE DIRECTORIES")  \)
+ OP \( -not \( -type f -or -type l -or -type d \) -not -path "/dev/*"  $(report  "SPECIAL FILES OUTSIDE /dev")  \)
+ OP \( -type d -group install -not -perm 1755  $(report  "INSTALL DIRECTORIES WITH UNUSUAL PERMISSIONS")  \)
+ OP \( -type f -group install $(report  "FILES ASSIGNED TO GROUP INSTALL")  \)
+ OP \( -type l -not \( -xtype b -or -xtype c -or -xtype d -or -xtype p -or -xtype f \) $(report  "SYMLINKS POSSIBLY BROKEN OR LOOP")  \)
+ OP \( -not -type d -links +1 $(report "HARDLINKED FILES" "Inode %i is shared by %n files, including") \)
+ OP \( -nouser  $(report  "THINGS HAVING UID WITH NO ASSIGNED USER NAME")  \)
+ OP \( -nogroup  $(report  "THINGS HAVING GID WITH NO ASSIGNED GROUP NAME")  \)
+ OP \( -type f -not -group install -not "${filegoodperm[@]}"  $(report  "FILES WITH UNUSUAL PERMISSIONS")  \)
+ OP \( -type d -not -group install -not "${dirgoodperm[@]}"  $(report  "DIRECTORIES WITH UNUSUAL PERMISSIONS")  \)
+)
+
+#insert unique codes for the messages
+code=100
+for ((i=0; $i<${#bad[@]}; i=$i+1)) 
+do
+    if [ "${bad[$i]}" = "increment_code_here" ]; then
+       code=$(($code + 1))
+       bad[$i]=$code
+    elif [ "${bad[$i]}" = "insert_code_here" ]; then
+       bad[$i]=$code
+    fi
+done
+
+allbad=()  #all bad matches are reported
+onebad=()  #only the first bad match is reported
+for ((i=0; $i<${#bad[@]}; i=$i+1)) 
+do
+    if [ "${bad[$i]}" = "OP" ]; then
+       allbad[$i]=","
+       onebad[$i]="-or"
+    else
+       allbad[$i]="${bad[$i]}"
+       onebad[$i]="${bad[$i]}"
+    fi
+done
+
+#Add a default case to onebad.
+#This should never be hit, because the explicit cases should catch all
+#files, but just in case I've missed something, this will catch it.
+onebad=("${onebad[@]}" -or  $(report  "WEIRD SHIT") )
+
+#make allbad always return false
+allbad=("${allbad[@]}" , -false)
+
+cmd=( "${usergroupmatch[@]}" -and
+     \( \( "${good[@]}" \) -or \( "${allbad[@]}" \) -or \( "${onebad[@]}" \) \)
+    )
+
+#In the following find command, the part
+# -not ( ( "${y[@]}" -prune ) -or "${y[@]}" )
+#is responsible for preventing the files that match prune_prefixes from
+#being processed. The 2nd "${y[@]}" may seem redundant, but it isn't, because
+#-prune has no effect and is always false when -depth is used.
+find "${fs_to_scan[@]}" -xdev -noleaf \
+    -not \( \( "${y[@]}" -prune \) -or "${y[@]}" \) \
+    -and \( "${cmd[@]}" \) 2>/dev/null | 
+sed 's/^\(...2\) \([0-9]\+ 2 \)\?\([^ ]\+\) \+[^ ]\+ \+\([^ ]\+\) \+\([^ ]\+\) \+[^"]\+\(".\+\)/\1 \2\3 \6  \4:\5/' |
+sort -u | 
+sed 's/^...1 /\'$'\n''/;s/^...2 [0-9]\+ 1 /\'$'\n''  /;s/^...2 [0-9]\+ 2 /    /;s/^...2 /  /'
diff --git a/usr/bin/list_suspicious_files_from b/usr/bin/list_suspicious_files_from
new file mode 100755 (executable)
index 0000000..3bf4d3a
--- /dev/null
@@ -0,0 +1,41 @@
+#!/bin/bash
+# Copyright (c) 2004 Matthias S. Benkmann <article AT winterdrache DOT de>
+# You may do everything with this code except misrepresent its origin.
+# PROVIDED `AS IS' WITH ABSOLUTELY NO WARRANTY OF ANY KIND!
+
+if [ $# != 1 -o "$1" = "--help" ]; then
+    echo 1>&2
+    echo 1>&2 'USAGE: '"${0##*/}"' <user_or_group>'
+    echo 1>&2
+    echo 1>&2 '  Outputs a categorized list of files and directories with properties'
+    echo 1>&2 '  that could mean trouble and should be investigated.'
+    echo 1>&2 '  Suspicious objects will be reported only if group and/or user equals'
+    echo 1>&2 '  <user_or_group> (numeric UID/GID allowed).'
+    echo 1>&2 '  This script calls `'"${0%_*}'"' for the real work.'
+    echo 1>&2
+    exit 1
+fi
+
+ugname="$1"
+
+ugmatcher=(-false)
+#test if find accepts ugname as a user, and append to ugmatcher if it does
+if find / -maxdepth 0 -user "$ugname" >/dev/null 2>&1 ; then
+    ugmatcher[${#ugmatcher[@]}]="-or"
+    ugmatcher[${#ugmatcher[@]}]="-user"
+    ugmatcher[${#ugmatcher[@]}]="$ugname"
+fi
+#test if find accepts ugname as a group, and append to ugmatcher if it does
+if find / -maxdepth 0 -group "$ugname" >/dev/null 2>&1 ; then
+    ugmatcher[${#ugmatcher[@]}]="-or"
+    ugmatcher[${#ugmatcher[@]}]="-group"
+    ugmatcher[${#ugmatcher[@]}]="$ugname"
+fi
+
+#if find accepted ugname as neither user nor group, then exit
+if [ "${#ugmatcher[@]}" = 1 ]; then
+    echo 1>&2 'find does not accept `'"$ugname'"' as group or user name'
+    exit 1
+fi
+
+"${0%_*}" "${ugmatcher[@]}"
diff --git a/usr/bin/uninstall_package b/usr/bin/uninstall_package
new file mode 100755 (executable)
index 0000000..6c1aae2
--- /dev/null
@@ -0,0 +1,80 @@
+#!/bin/sh
+# Copyright (c) 2004 Matthias S. Benkmann <article AT winterdrache DOT de>
+# You may do everything with this code except misrepresent its origin.
+# PROVIDED `AS IS' WITH ABSOLUTELY NO WARRANTY OF ANY KIND!
+
+# Copyright (c) 2007 Steve Youngs <steve@sxemacs.org>
+# Originally, all this script did was to echo a command to stdout.  It didn't
+# actually do any deleting.  To remove the package you had to kill/yank that
+# command and then remove the "echo"s in it to get the job done.
+
+# Rewritten to eliminate the need for kill/yank and echo removal.  The default
+# is still a dry-run, but now the "wet" run is achieved by adding a 2nd argument
+# to the command line: `now'.  The `dry_run' is piped through less(1) for easier
+# inspection.  And errors are redirected to /tmp/<pkgname>.err during the real
+# uninstall.
+
+if [ $# = 0 -o "$1" = '--help' ]; then
+    echo 1>&2 'USAGE: uninstall_package <package-name> [now]'
+    echo
+    echo 1>&2 'Unless you specify "now" as the 2nd arg, nothing will actually'
+    echo 1>&2 'be deleted.'
+    exit 1
+fi
+
+pkg=$1
+
+dry_run()
+{
+    forall_direntries_from ${pkg} -type d -exec echo rm -rvf {} 2>/dev/null \;
+    forall_direntries_from ${pkg} -not -type d -exec echo rm -vf {} 2>/dev/null \;
+    suid=$(forall_direntries_from ${pkg} -not -user ${pkg})
+    if [ -n "${suid}" ]; then
+       echo '###'
+       echo '# There were setuid binaries found.  You _could_ use root'
+       echo '# to delete this package, but you run a very real risk of'
+       echo '# completely FUCKING UP your system.  Instead, run this:'
+       echo '#'
+       echo '#  forall_direntries_from ${pkg} -not -user ${pkg}'
+       echo '#'
+       echo '# and delete those files manually and individually'
+       echo '###'
+    else
+       echo
+       echo User \"${pkg}\", or \"root\" can safely delete this package.
+    fi
+    echo
+    echo Use: \"uninstall_package ${pkg} now\" to really remove this package.
+    echo Any errors will be redirected to /tmp/${pkg}.err
+}
+
+run()
+{
+    # We have to do it twice to actually get the job done properly.
+    for (( i=1; i<=2; ++i )); do
+       forall_direntries_from ${pkg} -type d -exec rm -rvf {} 2>>/tmp/${pkg}.err \;
+       forall_direntries_from ${pkg} -not -type d -exec rm -vf {} 2>>/tmp/${pkg}.err \;
+    done
+
+    leftovers=$(forall_direntries_from ${pkg})
+    if [ -s /tmp/${pkg}.err -a -n "${leftovers}" ]; then
+       echo Errors were reported.  Please inspect /tmp/${pkg}.err
+    else
+        # Bring ~/.project inline with reality
+        sed -i -e 's/\(Last_Updated: \).*$/\1Not Installed/' \
+                -e 's/\(Version: \).*$/\1Not Installed/' ${HOME}/.project
+        awk '/^CONTENTS:/ { print; exit; } {print}' ${HOME}/.project > ${HOME}/.projtmp
+        echo "--------" >> ${HOME}/.projtmp
+        mv ${HOME}/.projtmp ${HOME}/.project
+        # We should be done.
+       echo Package: ${pkg} successfully removed
+       rm -f /tmp/${pkg}.err
+    fi
+}
+
+case $2 in
+        now) run ;;
+        *) dry_run|less ;;
+esac
+
+exit 0
diff --git a/usr/lib/pkgusr/chgrp b/usr/lib/pkgusr/chgrp
new file mode 100755 (executable)
index 0000000..74ccd1c
--- /dev/null
@@ -0,0 +1,30 @@
+#!/bin/bash
+# Copyright (c) 2000,2004 Matthias S. Benkmann <article AT winterdrache DOT de>
+# You may do everything with this code except misrepresent its origin.
+# PROVIDED `AS IS' WITH ABSOLUTELY NO WARRANTY OF ANY KIND!
+
+DAISY_CHAIN=""
+
+for p in $(type -ap chgrp) ; do
+    if [ ! $p -ef $0 ]; then
+       DAISY_CHAIN=$p
+       break
+    fi
+done
+
+if [ ! -n "$DAISY_CHAIN" ]; then
+    echo Cannot find real ${0##*/} command 
+    exit 1
+fi
+
+if [ $UID == 0 ]; then
+    exec $DAISY_CHAIN "$@"
+fi
+
+if [ "$1" == "tty" ]; then
+    echo 1>&2 '***' chgrp "$@"  
+else
+    $DAISY_CHAIN "$@" || exit $?
+fi
+
+exit 0
diff --git a/usr/lib/pkgusr/chmod b/usr/lib/pkgusr/chmod
new file mode 100755 (executable)
index 0000000..5fdb525
--- /dev/null
@@ -0,0 +1,43 @@
+#!/bin/bash
+# Copyright (c) 2004 Matthias S. Benkmann <article AT winterdrache DOT de>
+# You may do everything with this code except misrepresent its origin.
+# PROVIDED `AS IS' WITH ABSOLUTELY NO WARRANTY OF ANY KIND!
+
+DAISY_CHAIN=""
+
+for p in $(type -ap chmod) ; do
+    if [ ! $p -ef $0 ]; then
+       DAISY_CHAIN=$p
+       break
+    fi
+done
+
+if [ ! -n "$DAISY_CHAIN" ]; then
+    echo Cannot find real ${0##*/} command 
+    exit 1
+fi
+
+if [ $UID == 0 ]; then
+    exec $DAISY_CHAIN "$@"
+fi
+
+report=0
+doit=1
+reportmsg="*** chmod $@"
+
+case "$1" in
+    g+s|u+s) report=1; doit=0  ;;
+    4755) shift 1 ; set -- 755 "$@" ; report=1; doit=1 ;;
+    4555) shift 1 ; set -- 555 "$@" ; report=1; doit=1 ;;
+    *) ;;
+esac
+
+if [ "$report" = 1 ]; then
+    echo 1>&2 "$reportmsg"
+fi
+
+if [ "$doit" = 1 ]; then  
+    exec $DAISY_CHAIN "$@"
+fi
+
+exit 0
diff --git a/usr/lib/pkgusr/chown b/usr/lib/pkgusr/chown
new file mode 100755 (executable)
index 0000000..8dfddb4
--- /dev/null
@@ -0,0 +1,30 @@
+#!/bin/bash
+# Copyright (c) 2000,2004 Matthias S. Benkmann <article AT winterdrache DOT de>
+# You may do everything with this code except misrepresent its origin.
+# PROVIDED `AS IS' WITH ABSOLUTELY NO WARRANTY OF ANY KIND!
+
+DAISY_CHAIN=""
+
+for p in $(type -ap chown) ; do
+    if [ ! $p -ef $0 ]; then
+       DAISY_CHAIN=$p
+       break
+    fi
+done
+
+if [ ! -n "$DAISY_CHAIN" ]; then
+    echo Cannot find real ${0##*/} command 
+    exit 1
+fi
+
+if [ $UID == 0 ]; then
+    exec $DAISY_CHAIN "$@"
+fi
+
+if [ "$1" == "root.root" ]; then
+    echo 1>&2 '***' chown "$@"  
+else
+    $DAISY_CHAIN "$@" || exit $?
+fi
+
+exit 0
diff --git a/usr/lib/pkgusr/install b/usr/lib/pkgusr/install
new file mode 100755 (executable)
index 0000000..78f7aeb
--- /dev/null
@@ -0,0 +1,135 @@
+#!/bin/bash
+# Copyright (c) 2000,2004 Matthias S. Benkmann <article AT winterdrache DOT de>
+# You may do everything with this code except misrepresent its origin.
+# PROVIDED `AS IS' WITH ABSOLUTELY NO WARRANTY OF ANY KIND!
+
+manpagesowner=man-pages
+localedir=/usr/share/locale
+cmdline="$@"
+
+DAISY_CHAIN=""
+
+for p in $(type -ap install) ; do
+    if [ ! $p -ef $0 ]; then
+       DAISY_CHAIN=$p
+       break
+    fi
+done
+
+if [ ! -n "$DAISY_CHAIN" ]; then
+    echo Cannot find real ${0##*/} command 
+    exit 1
+fi
+
+if [ $UID == 0 ]; then
+    exec $DAISY_CHAIN "$@"
+fi
+
+#kill unused -c parameter if we get it
+if [ z"$1" = z"-c" ]; then shift 1 ; fi
+
+       #********** test if we create directories ********************
+if [ \( z"$1" = z"-d" \) -o \( z"$1" = z"-m" -a z"$3" = z"-d" \) ]; then  
+    locdirs=""
+    notify=0
+    havedir=0
+    for((i=$#; $i>0; ))
+    do
+        a="$1"
+       shift 1; i=$(($i-1))
+       case "$a" in
+           -o|-g|--owner|--group)
+                 notify=1 
+               shift 1; i=$(($i-1))
+               set -- "$@" 
+               ;;
+           $localedir/*)
+                if [ ! -d "$a" ]; then
+                   locdirs="$locdirs ""$(expr $a : "$localedir/\(.*\)")" 
+                   set -- "$@" "$a"
+                   havedir=1
+               else
+                   notify=1
+                   set -- "$@"
+               fi  
+               ;;
+            */*|/sbin)
+                if [ ! -d "$a" ]; then 
+                   set -- "$@" "$a" 
+                   havedir=1
+               else
+                   notify=1
+                   set -- "$@"
+               fi             
+               ;;
+           *) set -- "$@" "$a" ;;
+        esac
+    done
+  
+    test $notify -eq 1 -o z"$locdirs" != z && \
+       echo 1>&2 '***' install "$cmdline"
+
+    test $havedir -eq 0 && exit 0
+
+    $DAISY_CHAIN "$@" || exit $?
+  
+    test z"$locdirs" != z &&
+    for dir in $locdirs ; do
+       cumuldir=""
+       for d in $(echo $locdirs | sed 's#/# #g' -) ; do
+           cumuldir=$cumuldir$d/
+           if [ -d $localedir/$cumuldir ]; then
+               chgrp install $localedir/$cumuldir
+               chmod g+w,o+t $localedir/$cumuldir
+           fi  
+       done  
+    done
+
+else  #if "$1" != "-d"  ,i.e. we do not create directories *****************
+    notify=0
+    for((i=$# ; $i>0; ))
+    do
+        a="$1"
+       shift 1; i=$(($i-1))
+       case "$a" in
+            -m)
+               set -- "$@" "$a" 
+               a="$1"
+               shift 1; i=$(($i-1))
+               case "$a" in
+                   4755) notify=1 ; set -- "$@" "755" ;;
+                   4775) notify=1 ; set -- "$@" "755" ;;
+                   4711) notify=1 ; set -- "$@" "711" ;;
+                   *) set -- "$@" "$a"  ;;
+               esac
+               ;;
+            -m4755) notify=1 ; set -- "$@" "-m755" ;;
+           -m4775) notify=1 ; set -- "$@" "-m755" ;;
+            -m4711) notify=1 ; set -- "$@" "-m711" ;;
+            -o|-g|--owner|--group)
+               notify=1 
+               shift 1; i=$(($i-1))
+               set -- "$@" 
+               ;;
+            */man/man?/*) 
+                if [ -e "$a" -a ! -O "$a" ]; then
+                   if [ $(find "$a" -printf \%u) = $manpagesowner ]; then
+                       notify=1
+                       set -- "$@" not_installed
+                   else
+                       set -- "$@" "$a"
+                   fi  
+                else
+                   set -- "$@" "$a"
+                fi
+               ;;    
+            *) set -- "$@" "$a" ;;
+        esac
+    done
+
+    test $notify -eq 1 && echo 1>&2 '***' install "$cmdline"
+
+    $DAISY_CHAIN "$@" || exit $?
+fi
+
+exit 0
diff --git a/usr/lib/pkgusr/mkdir b/usr/lib/pkgusr/mkdir
new file mode 100755 (executable)
index 0000000..e575adc
--- /dev/null
@@ -0,0 +1,54 @@
+#!/bin/bash
+# Copyright (c) 2000 Matthias S. Benkmann <article AT winterdrache DOT de>
+# You may do everything with this code except misrepresent its origin.
+# PROVIDED `AS IS' WITH ABSOLUTELY NO WARRANTY OF ANY KIND!
+
+watchdir=/usr/share/locale
+
+DAISY_CHAIN=""
+
+for p in $(type -ap mkdir) ; do
+    if [ ! $p -ef $0 ]; then
+       DAISY_CHAIN=$p
+       break
+    fi
+done
+
+if [ ! -n "$DAISY_CHAIN" ]; then
+    echo Cannot find real ${0##*/} command 
+    exit 1
+fi
+
+if [ $UID == 0 ]; then
+    exec $DAISY_CHAIN "$@"
+fi
+
+cmdline="$@"
+
+dirs=""
+for((i=$#; $i>0;))
+do
+    a="$1"
+    shift 1; i=$(($i-1))
+    case "$a" in
+       $watchdir/*)
+            dirs="$dirs ""$(expr $a : "$watchdir/\(.*\)")" 
+           set -- "$@" "$a" 
+           ;;
+        *) set -- "$@" "$a" ;;
+    esac
+done
+
+$DAISY_CHAIN "$@" || exit $?
+
+test z"$dirs" != z &&
+echo 1>&2 '***' mkdir "$cmdline"
+for dir in $dirs ; do
+    cumuldir=""
+    for d in $(echo $dirs | sed 's#/# #g' -) ; do
+       cumuldir=$cumuldir$d/
+       chgrp install $watchdir/$cumuldir
+       test -k $watchdir/$cumuldir || chmod g+w,o+t $watchdir/$cumuldir
+    done  
+done
+exit 0
diff --git a/usr/lib/pkgusr/update-pkg-project b/usr/lib/pkgusr/update-pkg-project
new file mode 100755 (executable)
index 0000000..0cd7cf6
--- /dev/null
@@ -0,0 +1,21 @@
+#!/bin/bash
+
+# Updates the timestamp and contents in a package's .project
+# if $1 is null, update pkgusr's pkg
+
+pkg="$1"
+
+if [ -z "${pkg}" ]; then
+        pkg=$(whoami)
+fi
+
+pkgdir=/usr/src/${pkg}
+
+sed -i "s/\(Last_Updated: \).*$/\1$(date +%c)/" ${pkgdir}/.project
+awk '/^CONTENTS:/ { print; exit; } {print}' ${pkgdir}/.project > ${pkgdir}/.projtmp
+echo "--------" >> ${pkgdir}/.projtmp
+list_package ${pkg} >> ${pkgdir}/.projtmp
+mv ${pkgdir}/.projtmp ${pkgdir}/.project
+
+# If we're root, chown the .project file
+[[ $(id -u) -eq 0 ]] && chown -v ${pkg}:${pkg} ${pkgdir}/.project
diff --git a/usr/sbin/add_package_user b/usr/sbin/add_package_user
new file mode 100755 (executable)
index 0000000..c592237
--- /dev/null
@@ -0,0 +1,268 @@
+#!/bin/bash
+# Copyright (c) 2004 Matthias S. Benkmann <article AT winterdrache DOT de>
+# You may do everything with this code except misrepresent its origin.
+# PROVIDED `AS IS' WITH ABSOLUTELY NO WARRANTY OF ANY KIND!
+
+#Package user home directories will be located under this directory
+homebase=/usr/src
+
+#Contents of following directory are copied into home directory when creating 
+#a new package user (existing files will not be overwritten)
+skel=/etc/pkgusr/skel-package
+
+if [ $# -lt 7 ]; then
+    echo 1>&2 'USAGE: '
+    echo 1>&2 'add_package_user <description> <name> <minuid> <maxuid>'
+    echo 1>&2 '                              <group> <mingid> <maxgid> [-d <home>]'
+    echo 1>&2
+    echo 1>&2 'If a user account called <name> exists, a message will be printed and'
+    echo 1>&2 'everything will be left as-is. If a user account called <name> does not'
+    echo 1>&2 'exist, one will be created.'
+    echo 1>&2 'The account'"'"'s primary group will be <group> and the /etc/passwd'
+    echo 1>&2 'description field will be set to <description>. If a group called <group>'
+    echo 1>&2 'does not already exist, one will be created.'
+    echo 1>&2 'The new account will get the "install" group as a supplementary group. If'
+    echo 1>&2 'a group named "install" does not exist, one will be created.'
+    echo 1>&2
+    echo 1>&2 '<description> needs to be a valid string for the /etc/passwd description'
+    echo 1>&2 '  field. This means, among other things, that it must not contain ":".'
+    echo 1>&2 '  Don'"'"'t forget to properly quote <description> if it contains spaces or'
+    echo 1>&2 '  other characters interpreted by the shell!'
+    echo 1>&2
+    echo 1>&2 '<minuid>(incl.) and <maxuid>(excl.) determine the numeric range from which'
+    echo 1>&2 '  the new account'"'"'s UID will be picked in the following way:'
+    echo 1>&2
+    echo 1>&2 '  1. If the range contains no unused UID => Exit with error.'
+    echo 1>&2 '  2. If <maxuid>-1 is still unused, find the greatest UID from the range'
+    echo 1>&2 '     that is used and pick the number after that.'
+    echo 1>&2 '  3. If <maxuid>-1 is in use, pick the first unused number from the range.'
+    echo 1>&2
+    echo 1>&2 '<mingid>(incl.) and <maxgid>(excl.) determine the numeric range from which'
+    echo 1>&2 '  to pick the GID for group <group> and/or group "install", if it needs to be'
+    echo 1>&2 '  created. The process for picking the GID is the same as that for the UID.'
+    echo 1>&2 ''
+    echo 1>&2 '<home> specifies the new user'"'"'s home directory. If it is not provided,'
+    echo 1>&2 '  it will default to '"$homebase/<name> ."
+    echo 1>&2 '  If the home directory does not exist yet it will be created, otherwise'
+    echo 1>&2 '  the existing directory will be recursively chown'"'"'ed to the new user.'
+    echo 1>&2 '  The home directory will be populated with a copy of the contents of'
+    echo 1>&2 "  $skel, but pre-existing files in the home directory"
+    echo 1>&2 '  will not be overwritten. Note that symlinks will be copied as symlinks!'
+    echo 1>&2 ''
+    exit 1
+fi
+
+grpfile=/etc/group
+passwd=/etc/passwd
+
+
+
+description=$1
+name=$2
+minuid=$3
+maxuid=$4
+gname=$5
+mingid=$6
+maxgid=$7
+home=$homebase/$name
+
+set -- "$@" _eNd_OF_lisT_
+while [ "$1" != "_eNd_OF_lisT_" ]; do
+    case "$1" in
+       -d)
+            shift 1
+           if [ "$1" = "_eNd_OF_lisT_" ]; then
+               echo 1>&2 "-d directory name missing!"
+               exit 1
+           fi
+           home="$1"
+           shift 1
+           ;;
+        *)
+            temp="$1" 
+           shift 1
+           set -- "$@" "$temp"
+           ;;
+    esac     
+done
+shift 1 #throw away _eNd_OF_lisT_
+
+if [ $UID -ne 0 ]; then
+    echo Please run this script as root.
+    exit 1
+fi
+
+#test if user already exists
+grep "^$name:.*" $passwd
+if [ $? -eq 0 ]; then 
+    echo 'Package user does already exist! Do su '$name' to do maintenance work.'
+    exit 1
+fi 
+
+#test if minuid, maxuid, mingid and maxgid are integers, otherwise error
+error=0
+expr ${minuid} + 1 2>/dev/null 1>&2 || error=1
+expr ${maxuid} + 1 2>/dev/null 1>&2 || error=1
+expr ${mingid} + 1 2>/dev/null 1>&2 || error=1
+expr ${maxgid} + 1 2>/dev/null 1>&2 || error=1
+
+if [ $error -eq 1 ]; then
+    echo Error: Illegal numeric value!
+    exit 1
+fi
+
+if [ $minuid -ge $maxuid ]; then
+    echo 'Error: minuid must be less than maxuid !' 
+    exit 1
+fi
+
+if [ $mingid -ge $maxgid ]; then
+    echo 'Error: mingid must be less than maxgid !' 
+    exit 1
+fi
+
+
+uidlist=`cut -d : -f 3 $passwd | sort -n`
+
+#find last used UID within range
+u=0
+for i in $uidlist
+do
+    if [ $i -ge $maxuid ]; then break; fi
+    if [ $i -ge $minuid ]; then u=$i; fi 
+done
+
+#if no UID from the range is used, pick the first, otherwise pick the one
+#immediately following the last UID in use.
+if [ $u -eq 0 ]; then
+    u=$minuid
+else
+    u=`expr $u + 1`
+fi
+
+#if the UID determined above is >= maxuid (i.e. illegal)
+#then we look for the first unused uid in the range.
+if [ $u -ge $maxuid ]; then
+    u=$minuid
+    for i in $uidlist
+    do
+        if [ $u -eq $i ]; then
+           u=`expr $u + 1`
+       fi
+       if [ $i -ge $maxuid ]; then
+           break
+       fi
+    done  
+
+    if [ $u -ge $maxuid ]; then
+       echo Error: UID range is full!
+       exit 1
+    fi
+fi
+
+echo Will create user $name with uid: $u
+
+unset uidlist
+
+#############################################################################
+#                                 group
+#############################################################################
+
+#execute the following for gname and "install" to get gids for those 2 groups
+
+g=0
+creategroup=0
+for group in install $gname
+do
+    oldg=$g #save gid from previous run
+    createinstall=$creategroup
+    creategroup=0
+    #test if group already exists and extract gid if so
+    g=`grep ^${group}:.\* $grpfile | cut -d : -f 3 -`
+
+    #if group does not exist, then check range for a free gid
+    if [ z$g = z ]; then 
+       creategroup=1
+    
+       gidlist=`cut -d : -f 3 $grpfile | sort -n`
+
+        #find last used GID within range
+       g=0
+       for i in $gidlist; do
+           if [ $i -ge $maxgid ]; then break; fi
+           if [ $i -ge $mingid ]; then g=$i; fi
+       done
+
+        #if no GID from the range is used, pick the first, otherwise pick the one
+        #immediately following the last GID in use.
+       if [ $g -eq 0 ]; then
+           g=$mingid
+       else
+           g=`expr $g + 1`
+       fi
+    
+        #don't reuse gid from previous run 
+       if [ $g -eq $oldg ]; then
+           g=`expr $g + 1`
+       fi
+
+        #if the GID determined above is >= maxgid (i.e. illegal)
+        #then we look for the first unused gid in the range.
+       if [ $g -ge $maxgid ]; then
+           g=$mingid
+           for i in $gidlist; do
+               if [ $g -eq $i ]; then g=`expr $g + 1` ; fi
+               if [ $g -eq $oldg ]; then g=`expr $g + 1` ; fi
+               if [ $i -ge $maxgid ]; then break; fi
+           done  
+
+           if [ $g -ge $maxgid ]; then
+               echo Error: GID range is full!
+               exit 1
+           fi
+       fi
+    fi
+done
+
+unset gidlist
+
+if [ $createinstall -eq 1 ]; then
+    echo Creating group install with gid $oldg
+    groupadd -g $oldg install || exit 1
+else
+    echo Group install has gid $oldg
+fi
+if [ $creategroup -eq 1 ]; then
+    echo Creating group $gname with gid $g
+    groupadd -g $g $gname || exit 1
+else 
+    echo Group $gname has gid $g
+fi
+
+
+## Bastard Note:
+#  Once you have the shadow package installed, uncomment this section
+#  and comment out the section below it.
+##
+#useradd -c "${description}" -d ${home} -g ${gname} -G install \
+#    -m -k /etc/pkgusr/skel-package \
+#    -s /bin/bash -u ${u} ${name}
+
+## Bastard Note:
+#  Once the shadow package is installed, comment this section out and
+#  use the above section.
+##
+useradd -c "${description}" -d ${home} -g ${gname} -G install \
+        -s /bin/bash -u ${u} ${name}  || exit 1
+
+mkdir -p $home || exit 1
+
+yes n|cp -ai -R ${skel}/{[^.],.[^.],..?}* ${home} 2>/dev/null >/dev/null
+
+cd ${home}
+chown --recursive ${u}:${g} .
+
+
+
+exit 0
diff --git a/usr/sbin/groupadd b/usr/sbin/groupadd
new file mode 100755 (executable)
index 0000000..963d497
--- /dev/null
@@ -0,0 +1,26 @@
+#!/bin/bash
+# Copyright (c) 2000,2004 Matthias S. Benkmann <article AT winterdrache DOT de>
+# You may do everything with this code except misrepresent its origin.
+# PROVIDED `AS IS' WITH ABSOLUTELY NO WARRANTY OF ANY KIND!
+
+#
+# This is a primitive script to serve as groupadd until the real groupadd
+# has been installed. It has little error checking, so don't pass it anything
+# stupid or it'll mess up your /etc/group file.
+#
+
+if [ $# -ne 3 -o z$1 != z-g ]; then
+    echo 1>&2 USAGE: groupadd -g gid groupname
+    exit 1
+fi
+
+#test if group already exists
+grep "^${3}:.*" /etc/group 
+if [ $? -eq 0 ]; then
+    echo 1>&2 $0: Group does already exist
+    exit 1
+fi       
+
+cp /etc/group /tmp/group123456
+echo ${3}:x:${2}: | sort -t : -k3,3n -m /tmp/group123456 - > /etc/group
+
diff --git a/usr/sbin/install_package b/usr/sbin/install_package
new file mode 100755 (executable)
index 0000000..7b8505e
--- /dev/null
@@ -0,0 +1,32 @@
+#!/bin/bash
+# Copyright (c) 2000,2004 Matthias S. Benkmann <article AT winterdrache DOT de>
+# You may do everything with this code except misrepresent its origin.
+# PROVIDED `AS IS' WITH ABSOLUTELY NO WARRANTY OF ANY KIND!
+
+if [ $# -ne 3 ]; then
+    echo 1>&2
+    echo 1>&2 'USAGE: install_package <description> <name> <group>'
+    echo 1>&2
+    echo 1>&2 'Creates a new package user called <name> with primary group <group>'
+    echo 1>&2 'and description <description>.'
+    echo 1>&2 'If the user account has been created successfully, `su <name>'"'"' will be'
+    echo 1>&2 'executed so that you can start working with the new account right away.'
+    echo 1>&2
+    echo 1>&2 '<description> needs to be a valid string for the /etc/passwd description'
+    echo 1>&2 '  field. This means, among other things, that it must not contain ":".'
+    echo 1>&2 '  Don'"'"'t forget to properly quote <description> if it contains spaces or'
+    echo 1>&2 '  other characters interpreted by the shell!'
+    echo 1>&2
+    echo 1>&2 'This script leaves the actual creation of the new account to the'
+    echo 1>&2 'add_package_user script. Check out its documentation for details.'
+    echo 1>&2
+    exit 1
+fi
+
+if [ $UID -ne 0 ]; then
+    echo Please run this script as root.
+    exit 1
+fi
+add_package_user "${1}" $2 10000 20000 $3 10000 20000 || exit 1
+rm -f /var/mail/$2
+su $2
diff --git a/usr/sbin/useradd b/usr/sbin/useradd
new file mode 100755 (executable)
index 0000000..4d19a97
--- /dev/null
@@ -0,0 +1,45 @@
+#!/bin/bash
+# Copyright (c) 2000,2004 Matthias S. Benkmann <article AT winterdrache DOT de>
+# You may do everything with this code except misrepresent its origin.
+# PROVIDED `AS IS' WITH ABSOLUTELY NO WARRANTY OF ANY KIND!
+
+#
+# This is a primitive script to serve as useradd until the real useradd
+# has been installed. It has little error checking, so don't pass it anything
+# stupid or it'll mess up your /etc/passwd and/or /etc/group file.
+#
+
+if [ $# -ne 13 -o z$1 != z-c -o z$3 != z-d -o z$5 != z-g -o z$7 != z-G -o z$9 != z-s -o z${11} != z-u ]; then
+    echo 1>&2 USAGE: useradd -c description -d home -g maingroup -G addgroup -s shell -u uid login 
+    exit 1
+fi
+
+#test if user already exists
+grep "^${13}:.*" /etc/passwd 
+if [ $? -eq 0 ]; then
+    echo 1>&2 $0: User does already exist
+    exit 1
+fi       
+
+g=`grep ^${6}:.\* /etc/group | cut -d : -f 3 -`
+if [ z${g} = z ]; then
+    echo 1>&2 $0: Group ${6} does not exist!
+    exit 1
+fi
+
+grep ^${8}:.\* /etc/group >/dev/null || \
+{
+    echo 1>&2 $0: Group ${8} does not exist!
+    exit 1
+}
+
+
+cp /etc/passwd /tmp/passwd123456
+echo "${13}:x:${12}:$g:$2:$4:/bin/bash" \
+    | sort -t : -k3,3n -m /tmp/passwd123456 - > /etc/passwd
+
+
+cp /etc/group /tmp/group123456
+sed  -e 's/^\('"${8}"':[^:]*:[0-9]*:..*\)$/\1,'"${13}"'/' \
+     -e 's/^\('"${8}"':[^:]*:[0-9]*\):$/\1:'"${13}"'/' \
+    /tmp/group123456 >/etc/group