Correcting branch name to be like previous ones

1_3_stable
Denis Vlasenko 2006-12-27 04:35:04 +00:00
commit 8d42f86b14
909 changed files with 222869 additions and 0 deletions

33
.indent.pro vendored Normal file
View File

@ -0,0 +1,33 @@
--blank-lines-after-declarations
--blank-lines-after-procedures
--break-before-boolean-operator
--no-blank-lines-after-commas
--braces-on-if-line
--braces-on-struct-decl-line
--comment-indentation25
--declaration-comment-column25
--no-comment-delimiters-on-blank-lines
--cuddle-else
--continuation-indentation4
--case-indentation0
--else-endif-column33
--space-after-cast
--line-comments-indentation0
--declaration-indentation1
--dont-format-first-column-comments
--dont-format-comments
--honour-newlines
--indent-level4
/* changed from 0 to 4 */
--parameter-indentation4
--line-length78 /* changed from 75 */
--continue-at-parentheses
--no-space-after-function-call-names
--dont-break-procedure-type
--dont-star-comments
--leave-optional-blank-lines
--dont-space-special-semicolon
--tab-size4
/* additions by Mark */
--case-brace-indentation0
--leave-preprocessor-space

173
AUTHORS Normal file
View File

@ -0,0 +1,173 @@
List of the authors of code contained in BusyBox.
If you have code in BusyBox, you should be listed here. If you should be
listed, or the description of what you have done needs more detail, or is
incorrect, _please_ let me know.
-Erik
-----------
Peter Willis <psyphreak@phreaker.net>
eject
Emanuele Aina <emanuele.aina@tiscali.it>
run-parts
Erik Andersen <andersen@codepoet.org>
Tons of new stuff, major rewrite of most of the
core apps, tons of new apps as noted in header files.
Lots of tedious effort writing these boring docs that
nobody is going to actually read.
Laurence Anderson <l.d.anderson@warwick.ac.uk>
rpm2cpio, unzip, get_header_cpio, read_gz interface, rpm
Jeff Angielski <jeff@theptrgroup.com>
ftpput, ftpget
Enrik Berkhan <Enrik.Berkhan@inka.de>
setconsole
Jim Bauer <jfbauer@nfr.com>
modprobe shell dependency
Edward Betts <edward@debian.org>
expr, hostid, logname, whoami
John Beppu <beppu@codepoet.org>
du, nslookup, sort
David Brownell <dbrownell@users.sourceforge.net>
zcip
Brian Candler <B.Candler@pobox.com>
tiny-ls(ls)
Randolph Chung <tausq@debian.org>
fbset, ping, hostname
Dave Cinege <dcinege@psychosis.com>
more(v2), makedevs, dutmp, modularization, auto links file,
various fixes, Linux Router Project maintenance
Jordan Crouse <jordan@cosmicpenguin.net>
ipcalc
Magnus Damm <damm@opensource.se>
tftp client
insmod powerpc support
Larry Doolittle <ldoolitt@recycle.lbl.gov>
pristine source directory compilation, lots of patches and fixes.
Glenn Engel <glenne@engel.org>
httpd
Gennady Feldman <gfeldman@gena01.com>
Sysklogd (single threaded syslogd, IPC Circular buffer support,
logread), various fixes.
Robert Griebl <sandman@handhelds.org>
modprobe, hwclock, suid/sgid handling, tinylogin integration
many bugfixes and enhancements
Karl M. Hegbloom <karlheg@debian.org>
cp_mv.c, the test suite, various fixes to utility.c, &c.
Daniel Jacobowitz <dan@debian.org>
mktemp.c
Matt Kraai <kraai@alumni.cmu.edu>
documentation, bugfixes, test suite
Rob Landley <rob@landley.net>
Became busybox maintainer in 2006.
sed (major rewrite in 2003, and I now maintain the thing)
bunzip2 (complete from-scratch rewrite, then mjn3 optimized the result)
sort (more or less from scratch rewrite in 2004, I now maintain it)
mount (rewrite in 2005, I maintain the new one)
Stephan Linz <linz@li-pro.net>
ipcalc, Red Hat equivalence
John Lombardo <john@deltanet.com>
tr
Glenn McGrath <bug1@iinet.net.au>
Common unarchiving code and unarchiving applets, ifupdown, ftpgetput,
nameif, sed, patch, fold, install, uudecode.
Various bugfixes, review and apply numerous patches.
Manuel Novoa III <mjn3@codepoet.org>
cat, head, mkfifo, mknod, rmdir, sleep, tee, tty, uniq, usleep, wc, yes,
mesg, vconfig, nice, renice,
make_directory, parse_mode, dirname, mode_string,
get_last_path_component, simplify_path, and a number trivial libbb routines
also bug fixes, partial rewrites, and size optimizations in
ash, basename, cal, cmp, cp, df, du, echo, env, ln, logname, md5sum, mkdir,
mv, realpath, rm, sort, tail, touch, uname, watch, arith, human_readable,
interface, dutmp, ifconfig, route
Vladimir Oleynik <dzo@simtreas.ru>
cmdedit; bb_mkdep, xargs(current), httpd(current);
ports: ash, crond, fdisk (initial, unmaintained now), inetd, stty, traceroute,
top;
locale, various fixes
and irreconcilable critic of everything not perfect.
Bruce Perens <bruce@pixar.com>
Original author of BusyBox in 1995, 1996. Some of his code can
still be found hiding here and there...
Rodney Radford <rradford@mindspring.com>
ipcs, ipcrm
Tim Riker <Tim@Rikers.org>
bug fixes, member of fan club
Kent Robotti <robotti@metconnect.com>
reset, tons and tons of bug reports and patches.
Chip Rosenthal <chip@unicom.com>, <crosenth@covad.com>
wget - Contributed by permission of Covad Communications
Pavel Roskin <proski@gnu.org>
Lots of bugs fixes and patches.
Gyepi Sam <gyepi@praxis-sw.com>
Remote logging feature for syslogd
Rob Sullivan <cogito.ergo.cogito@gmail.com>
comm
Linus Torvalds
mkswap, fsck.minix, mkfs.minix
Mark Whitley <markw@codepoet.org>
grep, sed, cut, xargs(previous),
style-guide, new-applet-HOWTO, bug fixes, etc.
Charles P. Wright <cpwright@villagenet.com>
gzip, mini-netcat(nc)
Enrique Zanardi <ezanardi@ull.es>
tarcat (since removed), loadkmap, various fixes, Debian maintenance
Tito Ragusa <farmatito@tiscali.it>
devfsd and size optimizations in strings, openvt, chvt, deallocvt, hdparm,
fdformat, lsattr, chattr, id and eject.
Paul Fox <pgf@foxharp.boston.ma.us>
vi editing mode for ash, various other patches/fixes
Roberto A. Foglietta <me@roberto.foglietta.name>
port: dnsd
Bernhard Fischer <rep.nop@aon.at>
misc
Mike Frysinger <vapier@gentoo.org>
initial e2fsprogs, printenv, setarch, sum, misc

479
Config.in Normal file
View File

@ -0,0 +1,479 @@
#
# For a description of the syntax of this configuration file,
# see scripts/kbuild/config-language.txt.
#
mainmenu "BusyBox Configuration"
config HAVE_DOT_CONFIG
bool
default y
menu "Busybox Settings"
menu "General Configuration"
config NITPICK
bool "See lots more (probably unnecessary) configuration options."
default n
help
Some BusyBox applets have more configuration options than anyone
will ever care about. To avoid drowining people in complexity, most
of the applet features that can be set to a sane default value are
hidden, unless you hit the above switch.
This is better than to telling people to edit the busybox source
code, but not by much.
See http://en.wikipedia.org/wiki/Fibber_McGee_and_Molly#The_Closet
You have been warned.
config DESKTOP
bool "Enable options for full-blown desktop systems"
default n
help
Enable options and features which are not essential.
Select this only if you plan to use busybox on full-blown
desktop machine with common Linux distro, not on an embedded box.
choice
prompt "Buffer allocation policy"
default FEATURE_BUFFERS_USE_MALLOC
depends on NITPICK
help
There are 3 ways BusyBox can handle buffer allocations:
- Use malloc. This costs code size for the call to xmalloc.
- Put them on stack. For some very small machines with limited stack
space, this can be deadly. For most folks, this works just fine.
- Put them in BSS. This works beautifully for computers with a real
MMU (and OS support), but wastes runtime RAM for uCLinux. This
behavior was the only one available for BusyBox versions 0.48 and
earlier.
config FEATURE_BUFFERS_USE_MALLOC
bool "Allocate with Malloc"
config FEATURE_BUFFERS_GO_ON_STACK
bool "Allocate on the Stack"
config FEATURE_BUFFERS_GO_IN_BSS
bool "Allocate in the .bss section"
endchoice
config SHOW_USAGE
bool "Show terse applet usage messages"
default y
help
All BusyBox applets will show help messages when invoked with
wrong arguments. You can turn off printing these terse usage
messages if you say no here.
This will save you up to 7k.
config FEATURE_VERBOSE_USAGE
bool "Show verbose applet usage messages"
default n
select SHOW_USAGE
help
All BusyBox applets will show more verbose help messages when
busybox is invoked with --help. This will add a lot of text to the
busybox binary. In the default configuration, this will add about
13k, but it can add much more depending on your configuration.
config FEATURE_COMPRESS_USAGE
bool "Store applet usage messages in compressed form"
default y
depends on SHOW_USAGE
help
Store usage messages in compressed form, uncompress them on-the-fly
when <applet> --help is called.
If you have a really tiny busybox with few applets enabled (and
bunzip2 isn't one of them), the overhead of the decompressor might
be noticeable. Also, if you run executables directly from ROM
and have very little memory, this might not be a win. Otherwise,
you probably want this.
config FEATURE_INSTALLER
bool "Support --install [-s] to install applet links at runtime"
default n
help
Enable 'busybox --install [-s]' support. This will allow you to use
busybox at runtime to create hard links or symlinks for all the
applets that are compiled into busybox. This feature requires the
/proc filesystem.
config LOCALE_SUPPORT
bool "Enable locale support (system needs locale for this to work)"
default n
help
Enable this if your system has locale support and you would like
busybox to support locale settings.
config GETOPT_LONG
bool "Enable support for --long-options"
default y
help
Enable this if you want busybox applets to use the gnu --long-option
style, in addition to single character -a -b -c style options.
config FEATURE_DEVPTS
bool "Use the devpts filesystem for Unix98 PTYs"
default y
help
Enable if you want BusyBox to use Unix98 PTY support. If enabled,
busybox will use /dev/ptmx for the master side of the pseudoterminal
and /dev/pts/<number> for the slave side. Otherwise, BSD style
/dev/ttyp<number> will be used. To use this option, you should have
devpts mounted.
config FEATURE_CLEAN_UP
bool "Clean up all memory before exiting (usually not needed)"
default n
depends on NITPICK
help
As a size optimization, busybox normally exits without explicitly
freeing dynamically allocated memory or closing files. This saves
space since the OS will clean up for us, but it can confuse debuggers
like valgrind, which report tons of memory and resource leaks.
Don't enable this unless you have a really good reason to clean
things up manually.
config FEATURE_SUID
bool "Support for SUID/SGID handling"
default n
help
With this option you can install the busybox binary belonging
to root with the suid bit set, and it'll and it'll automatically drop
priviledges for applets that don't need root access.
If you're really paranoid and don't want to do this, build two
busybox binaries with different applets in them (and the appropriate
symlinks pointing to each binary), and only set the suid bit on the
one that needs it. The applets currently marked to need the suid bit
are login, passwd, su, ping, traceroute, crontab, dnsd, ipcrm, ipcs,
and vlock.
config FEATURE_SYSLOG
bool "Support for syslog"
default n
help
This option is auto-selected when you select any applet which may
send its output to syslog. You do not need to select it manually.
config FEATURE_SUID_CONFIG
bool "Runtime SUID/SGID configuration via /etc/busybox.conf"
default n if FEATURE_SUID
depends on FEATURE_SUID
help
Allow the SUID / SGID state of an applet to be determined at runtime
by checking /etc/busybox.conf. (This is sort of a poor man's sudo.)
The format of this file is as follows:
<applet> = [Ssx-][Ssx-][x-] (<username>|<uid>).(<groupname>|<gid>)
An example might help:
[SUID]
su = ssx root.0 # applet su can be run by anyone and runs with euid=0/egid=0
su = ssx # exactly the same
mount = sx- root.disk # applet mount can be run by root and members of group disk
# and runs with euid=0
cp = --- # disable applet cp for everyone
The file has to be owned by user root, group root and has to be
writeable only by root:
(chown 0.0 /etc/busybox.conf; chmod 600 /etc/busybox.conf)
The busybox executable has to be owned by user root, group
root and has to be setuid root for this to work:
(chown 0.0 /bin/busybox; chmod 4755 /bin/busybox)
Robert 'sandman' Griebl has more information here:
<url: http://www.softforge.de/bb/suid.html >.
config FEATURE_SUID_CONFIG_QUIET
bool "Suppress warning message if /etc/busybox.conf is not readable"
default y
depends on FEATURE_SUID_CONFIG
help
/etc/busybox.conf should be readable by the user needing the SUID, check
this option to avoid users to be notified about missing permissions.
config FEATURE_HAVE_RPC
bool "RPC support"
default y
help
Select this if you have rpc support.
This automatically turns off all configuration options that rely
on RPC.
config SELINUX
bool "Support NSA Security Enhanced Linux"
default n
help
Enable support for SELinux in applets ls, ps, and id. Also provide
the option of compiling in SELinux applets.
If you do not have a complete SELinux userland installed, this stuff
will not compile. Go visit
http://www.nsa.gov/selinux/index.html
to download the necessary stuff to allow busybox to compile with
this option enabled. Specifially, libselinux 1.28 or better is
directly required by busybox. If the installation is located in a
non-standard directory, provide it by invoking make as follows:
CFLAGS=-I<libselinux-include-path> \
LDFLAGS=-L<libselinux-lib-path> \
make
Most people will leave this set to 'N'.
config BUSYBOX_EXEC_PATH
string "Path to BusyBox executable"
default "/proc/self/exe"
help
When Busybox applets need to run other busybox applets, BusyBox
sometimes needs to exec() itself. When the /proc filesystem is
mounted, /proc/self/exe always points to the currently running
executable. If you haven't got /proc, set this to wherever you
want to run BusyBox from.
endmenu
menu 'Build Options'
config STATIC
bool "Build BusyBox as a static binary (no shared libs)"
default n
help
If you want to build a static BusyBox binary, which does not
use or require any shared libraries, then enable this option.
This can cause BusyBox to be considerably larger, so you should
leave this option false unless you have a good reason (i.e.
your target platform does not support shared libraries, or
you are building an initrd which doesn't need anything but
BusyBox, etc).
Most people will leave this set to 'N'.
config BUILD_LIBBUSYBOX
bool "Build shared libbusybox"
default n
help
Build a shared library libbusybox.so which contains all
libraries used inside busybox.
This is an experimental feature intended to support the upcoming
"make standalone" mode. Enabling it against the one big busybox
binary serves no purpose (and increases the size). You should
almost certainly say "no" to this right now.
config FEATURE_FULL_LIBBUSYBOX
bool "Feature-complete libbusybox"
default n if !FEATURE_SHARED_BUSYBOX
depends on BUILD_LIBBUSYBOX
help
Build a libbusybox with the complete feature-set, disregarding
the actually selected config.
Normally, libbusybox will only contain the features which are
used by busybox itself. If you plan to write a separate
standalone application which uses libbusybox say 'Y'.
Note: libbusybox is GPL, not LGPL, and exports no stable API that
might act as a copyright barrier. We can and will modify the
exported function set between releases (even minor version number
changes), and happily break out-of-tree features.
Say 'N' if in doubt.
config FEATURE_SHARED_BUSYBOX
bool "Use shared libbusybox for busybox"
default y if BUILD_LIBBUSYBOX
depends on !STATIC && BUILD_LIBBUSYBOX
help
Use libbusybox.so also for busybox itself.
You need to have a working dynamic linker to use this variant.
config LFS
bool "Build with Large File Support (for accessing files > 2 GB)"
default n
select FDISK_SUPPORT_LARGE_DISKS
help
If you want to build BusyBox with large file support, then enable
this option. This will have no effect if your kernel or your C
library lacks large file support for large files. Some of the
programs that can benefit from large file support include dd, gzip,
cp, mount, tar, and many others. If you want to access files larger
than 2 Gigabytes, enable this option. Otherwise, leave it set to 'N'.
config BUILD_AT_ONCE
bool "Compile all sources at once"
default n
help
Normally each source-file is compiled with one invocation of
the compiler.
If you set this option, all sources are compiled at once.
This gives the compiler more opportunities to optimize which can
result in smaller and/or faster binaries.
Setting this option will consume alot of memory, e.g. if you
enable all applets with all features, gcc uses more than 300MB
RAM during compilation of busybox.
This option is most likely only beneficial for newer compilers
such as gcc-4.1 and above.
Say 'N' unless you know what you are doing.
endmenu
menu 'Debugging Options'
config DEBUG
bool "Build BusyBox with extra Debugging symbols"
default n
help
Say Y here if you wish to examine BusyBox internals while applets are
running. This increases the size of the binary considerably, and
should only be used when doing development. If you are doing
development and want to debug BusyBox, answer Y.
Most people should answer N.
config DEBUG_PESSIMIZE
bool "Disable compiler optimizations."
default n
depends on DEBUG
help
The compiler's optimization of source code can eliminate and reorder
code, resulting in an executable that's hard to understand when
stepping through it with a debugger. This switches it off, resulting
in a much bigger executable that more closely matches the source
code.
choice
prompt "Additional debugging library"
default NO_DEBUG_LIB
depends on DEBUG
help
Using an additional debugging library will make BusyBox become
considerable larger and will cause it to run more slowly. You
should always leave this option disabled for production use.
dmalloc support:
----------------
This enables compiling with dmalloc ( http://dmalloc.com/ )
which is an excellent public domain mem leak and malloc problem
detector. To enable dmalloc, before running busybox you will
want to properly set your environment, for example:
export DMALLOC_OPTIONS=debug=0x34f47d83,inter=100,log=logfile
The 'debug=' value is generated using the following command
dmalloc -p log-stats -p log-non-free -p log-bad-space -p log-elapsed-time \
-p check-fence -p check-heap -p check-lists -p check-blank \
-p check-funcs -p realloc-copy -p allow-free-null
Electric-fence support:
-----------------------
This enables compiling with Electric-fence support. Electric
fence is another very useful malloc debugging library which uses
your computer's virtual memory hardware to detect illegal memory
accesses. This support will make BusyBox be considerable larger
and run slower, so you should leave this option disabled unless
you are hunting a hard to find memory problem.
config NO_DEBUG_LIB
bool "None"
config DMALLOC
bool "Dmalloc"
config EFENCE
bool "Electric-fence"
endchoice
config DEBUG_YANK_SUSv2
bool "Disable obsolete features removed before SUSv3?"
default y
help
This option will disable backwards compatibility with SuSv2,
specifically, old-style numeric options ('command -1 <file>')
will not be supported in head, tail, and fold. (Note: should
yank from renice too.)
endmenu
menu 'Installation Options'
config INSTALL_NO_USR
bool "Don't use /usr"
default n
help
Disable use of /usr. Don't activate this option if you don't know
that you really want this behaviour.
choice
prompt "Applets links"
default INSTALL_APPLET_SYMLINKS
help
Choose how you install applets links.
config INSTALL_APPLET_SYMLINKS
bool "as soft-links"
help
Install applets as soft-links to the busybox binary. This needs some
free inodes on the filesystem, but might help with filesystem
generators that can't cope with hard-links.
config INSTALL_APPLET_HARDLINKS
bool "as hard-links"
help
Install applets as hard-links to the busybox binary. This might count
on a filesystem with few inodes.
config INSTALL_APPLET_DONT
bool
prompt "not installed"
depends on FEATURE_INSTALLER || FEATURE_SH_STANDALONE_SHELL
help
Do not install applets links. Usefull when using the -install feature
or a standalone shell for rescue pruposes.
endchoice
config PREFIX
string "BusyBox installation prefix"
default "./_install"
help
Define your directory to install BusyBox files/subdirs in.
endmenu
source libbb/Config.in
endmenu
comment "Applets"
source archival/Config.in
source coreutils/Config.in
source console-tools/Config.in
source debianutils/Config.in
source editors/Config.in
source findutils/Config.in
source init/Config.in
source loginutils/Config.in
source e2fsprogs/Config.in
source modutils/Config.in
source util-linux/Config.in
source miscutils/Config.in
source networking/Config.in
source procps/Config.in
source shell/Config.in
source sysklogd/Config.in
source runit/Config.in

125
INSTALL Normal file
View File

@ -0,0 +1,125 @@
Building:
=========
The BusyBox build process is similar to the Linux kernel build:
make menuconfig # This creates a file called ".config"
make # This creates the "busybox" executable
make install # or make PREFIX=/path/from/root install
The full list of configuration and install options is available by typing:
make help
Quick Start:
============
The easy way to try out BusyBox for the first time, without having to install
it, is to enable all features and then use "standalone shell" mode with a
blank command $PATH.
To enable all features, use "make defconfig", which produces the largest
general-purpose configuration. (It's allyesconfig minus debugging options,
optional packaging choices, and a few special-purpose features requiring
extra configuration to use.)
make defconfig
make
PATH= ./busybox ash
Standalone shell mode causes busybox's built-in command shell to run
any built-in busybox applets directly, without looking for external
programs by that name. Supplying an empty command path (as above) means
the only commands busybox can find are the built-in ones.
Note that the standalone shell requires CONFIG_BUSYBOX_EXEC_PATH
to be set appropriately, depending on whether or not /proc/self/exe is
available or not. If you do not have /proc, then point that config option
to the location of your busybox binary, usually /bin/busybox.
Configuring Busybox:
====================
Busybox is optimized for size, but enabling the full set of functionality
still results in a fairly large executable -- more than 1 megabyte when
statically linked. To save space, busybox can be configured with only the
set of applets needed for each environment. The minimal configuration, with
all applets disabled, produces a 4k executable. (It's useless, but very small.)
The manual configurator "make menuconfig" modifies the existing configuration.
(For systems without ncurses, try "make config" instead.) The two most
interesting starting configurations are "make allnoconfig" (to start with
everything disabled and add just what you need), and "make defconfig" (to
start with everything enabled and remove what you don't need). If menuconfig
is run without an existing configuration, make defconfig will run first to
create a known starting point.
Other starting configurations (mostly used for testing purposes) include
"make allbareconfig" (enables all applets but disables all optional features),
"make allyesconfig" (enables absolutely everything including debug features),
and "make randconfig" (produce a random configuration).
Configuring BusyBox produces a file ".config", which can be saved for future
use. Run "make oldconfig" to bring a .config file from an older version of
busybox up to date.
Installing Busybox:
===================
Busybox is a single executable that can behave like many different commands,
and BusyBox uses the name it was invoked under to determine the desired
behavior. (Try "mv busybox ls" and then "./ls -l".)
Installing busybox consists of creating symlinks (or hardlinks) to the busybox
binary for each applet enabled in busybox, and making sure these symlinks are
in the shell's command $PATH. Running "make install" creates these symlinks,
or "make install-hardlinks" creates hardlinks instead (useful on systems with
a limited number of inodes). This install process uses the file
"busybox.links" (created by make), which contains the list of enabled applets
and the path at which to install them.
Installing links to busybox is not always necessary. The special applet name
"busybox" (or with any optional suffix, such as "busybox-static") uses the
first argument to determine which applet to behave as, for example
"./busybox cat LICENSE". (Running the busybox applet with no arguments gives
a list of all enabled applets.) The standalone shell can also call busybox
applets without links to busybox under other names in the filesystem. You can
also configure a standaone install capability into the busybox base applet,
and then install such links at runtime with one of "busybox --install" (for
hardlinks) or "busybox --install -s" (for symlinks).
If you enabled the busybox shared library feature (libbusybox.so) and want
to run tests without installing, set your LD_LIBRARY_PATH accordingly when
running the executable:
LD_LIBRARY_PATH=`pwd` ./busybox
Building out-of-tree:
=====================
By default, the BusyBox build puts its temporary files in the source tree.
Building from a read-only source tree, or building multiple configurations from
the same source directory, requires the ability to put the temporary files
somewhere else.
To build out of tree, cd to an empty directory and configure busybox from there:
make -f /path/to/source/Makefile defconfig
make
make install
Alternately, use the O=$BUILDPATH option (with an absolute path) during the
configuration step, as in:
make O=/some/empty/directory allyesconfig
cd /some/empty/directory
make
make PREFIX=. install
More Information:
=================
Se also the busybox FAQ, under the questions "How can I get started using
BusyBox" and "How do I build a BusyBox-based system?" The BusyBox FAQ is
available from http://www.busybox.net/FAQ.html or as the file
docs/busybox.net/FAQ.html in this tarball.

348
LICENSE Normal file
View File

@ -0,0 +1,348 @@
--- A note on GPL versions
BusyBox is distributed under version 2 of the General Public License (included
in its entirety, below). Version 2 is the only version of this license which
this version of BusyBox (or modified versions derived from this one) may be
distributed under.
------------------------------------------------------------------------
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Library General
Public License instead of this License.

1289
Makefile Normal file

File diff suppressed because it is too large Load Diff

145
Makefile.custom Normal file
View File

@ -0,0 +1,145 @@
# ==========================================================================
# Build system
# ==========================================================================
%.bflt: %_unstripped
$(CROSS_COMPILE)elf2flt $(ELF2FLTFLAGS) $< -o $@
busybox.links: $(srctree)/applets/busybox.mkll $(objtree)/include/autoconf.h $(srctree)/include/applets.h
$(Q)-$(SHELL) $^ >$@
.PHONY: install
ifeq ($(CONFIG_INSTALL_APPLET_SYMLINKS),y)
INSTALL_OPTS:= --symlinks
endif
ifeq ($(CONFIG_INSTALL_APPLET_HARDLINKS),y)
INSTALL_OPTS:= --hardlinks
endif
install: $(srctree)/applets/install.sh busybox busybox.links
$(Q)DO_INSTALL_LIBS="$(strip $(LIBBUSYBOX_SONAME) $(DO_INSTALL_LIBS))" \
$(SHELL) $< $(CONFIG_PREFIX) $(INSTALL_OPTS)
ifeq ($(strip $(CONFIG_FEATURE_SUID)),y)
@echo
@echo
@echo --------------------------------------------------
@echo You will probably need to make your busybox binary
@echo setuid root to ensure all configured applets will
@echo work properly.
@echo --------------------------------------------------
@echo
endif
uninstall: busybox.links
rm -f $(CONFIG_PREFIX)/bin/busybox
for i in `cat busybox.links` ; do rm -f $(CONFIG_PREFIX)$$i; done
ifneq ($(strip $(DO_INSTALL_LIBS)),n)
for i in $(LIBBUSYBOX_SONAME) $(DO_INSTALL_LIBS); do \
rm -f $(CONFIG_PREFIX)$$i; \
done
endif
check test: busybox
bindir=$(objtree) srcdir=$(srctree)/testsuite SED="$(SED)" \
$(SHELL) $(srctree)/testsuite/runtest $(if $(KBUILD_VERBOSE:1=),-v)
.PHONY: release
release: distclean
cd ..; \
rm -r -f busybox-$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION); \
cp -a busybox busybox-$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION) && { \
find busybox-$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)/ -type d \
-name .svn \
-print \
-exec rm -r -f {} \; ; \
find busybox-$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)/ -type f \
-name .\#* \
-print \
-exec rm -f {} \; ; \
tar -czf busybox-$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION).tar.gz \
busybox-$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)/ ; }
.PHONY: checkhelp
checkhelp:
$(Q)$(srctree)/scripts/checkhelp.awk \
$(patsubst %,$(srctree)/%,$(wildcard $(patsubst %,%/Config.in,$(busybox-dirs) ./)))
.PHONY: sizes
sizes: busybox_unstripped
$(NM) --size-sort $(<)
.PHONY: bloatcheck
bloatcheck: busybox_old busybox_unstripped
@$(srctree)/scripts/bloat-o-meter busybox_old busybox_unstripped
.PHONY: baseline
baseline: busybox_unstripped
@mv busybox_unstripped busybox_old
.PHONY: objsizes
objsizes: busybox_unstripped
$(srctree)/scripts/objsizes
.PHONY: bigdata
bigdata: busybox_unstripped
nm --size-sort busybox_unstripped | grep -vi ' [tr] ' | tail -20
# Documentation Targets
.PHONY: doc
doc: docs/busybox.pod docs/BusyBox.txt docs/BusyBox.1 docs/BusyBox.html
docs/busybox.pod: $(srctree)/docs/busybox_header.pod \
$(srctree)/include/usage.h \
$(srctree)/docs/busybox_footer.pod \
$(srctree)/docs/autodocifier.pl
$(disp_doc)
$(Q)-mkdir -p docs
$(Q)-( cat $(srctree)/docs/busybox_header.pod ; \
$(srctree)/docs/autodocifier.pl $(srctree)/include/usage.h ; \
cat $(srctree)/docs/busybox_footer.pod ; ) > docs/busybox.pod
docs/BusyBox.txt: docs/busybox.pod
$(disp_doc)
$(Q)-mkdir -p docs
$(Q)-pod2text $< > $@
docs/BusyBox.1: docs/busybox.pod
$(disp_doc)
$(Q)-mkdir -p docs
$(Q)-pod2man --center=BusyBox --release="version $(VERSION)" \
$< > $@
docs/BusyBox.html: docs/busybox.net/BusyBox.html
$(disp_doc)
$(Q)-mkdir -p docs
$(Q)-rm -f docs/BusyBox.html
$(Q)-cp docs/busybox.net/BusyBox.html docs/BusyBox.html
docs/busybox.net/BusyBox.html: docs/busybox.pod
$(Q)-mkdir -p docs/busybox.net
$(Q)-pod2html --noindex $< > \
docs/busybox.net/BusyBox.html
$(Q)-rm -f pod2htm*
# documentation, cross-reference
# Modern distributions already ship synopsis packages (e.g. debian)
# If you have an old distribution go to http://synopsis.fresco.org/
syn_tgt = $(wildcard $(patsubst %,%/*.c,$(busybox-alldirs)))
syn = $(patsubst %.c, %.syn, $(syn_tgt))
comma:= ,
brace_open:= (
brace_close:= )
SYN_CPPFLAGS := $(strip $(CPPFLAGS) $(EXTRA_CPPFLAGS))
SYN_CPPFLAGS := $(subst $(brace_open),\$(brace_open),$(SYN_CPPFLAGS))
SYN_CPPFLAGS := $(subst $(brace_close),\$(brace_close),$(SYN_CPPFLAGS))
#SYN_CPPFLAGS := $(subst ",\",$(SYN_CPPFLAGS))
#")
#SYN_CPPFLAGS := [$(patsubst %,'%'$(comma),$(SYN_CPPFLAGS))'']
%.syn: %.c
synopsis -p C -l Comments.SSDFilter,Comments.Previous -Wp,preprocess=True,cppflags="'$(SYN_CPPFLAGS)'" -o $@ $<
.PHONY: html
html: $(syn)
synopsis -f HTML -Wf,title="'BusyBox Documentation'" -o $@ $^

28
Makefile.flags Normal file
View File

@ -0,0 +1,28 @@
# ==========================================================================
# Build system
# ==========================================================================
BB_VER = $(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
# -std=gnu99 needed for [U]LLONG_MAX on some systems
CPPFLAGS += \
-std=gnu99 \
-Iinclude -Ilibbb \
$(if $(KBUILD_SRC),-Iinclude2 -I$(srctree)/include) -I$(srctree)/libbb \
-include include/autoconf.h \
-D_GNU_SOURCE -DNDEBUG \
$(if $(CONFIG_LFS),-D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64) \
-D"BB_VER=KBUILD_STR($(BB_VER))" -DBB_BT=AUTOCONF_TIMESTAMP
CFLAGS += \
-Wall -Wstrict-prototypes -Wshadow -Werror -Wundef \
-funsigned-char -fno-builtin-strlen -finline-limit=0 -static-libgcc \
-Os -falign-functions=1 -falign-jumps=1 -falign-loops=1 \
-fomit-frame-pointer -ffunction-sections -fdata-sections
ifeq ($(CONFIG_DEBUG),y)
CFLAGS += -g
LDFLAGS += -g
endif

42
Makefile.help Normal file
View File

@ -0,0 +1,42 @@
# ==========================================================================
# Build system
# ==========================================================================
help:
@echo 'Cleaning:'
@echo ' clean - delete temporary files created by build'
@echo ' distclean - delete all non-source files (including .config)'
@echo
@echo 'Build:'
@echo ' all - Executable and documentation'
@echo ' busybox - the swiss-army executable'
@echo ' doc - docs/BusyBox.{txt,html,1}'
@echo ' html - create html-based cross-reference'
@echo
@echo 'Configuration:'
@echo ' allnoconfig - disable all symbols in .config'
@echo ' allyesconfig - enable all symbols in .config (see defconfig)'
@echo ' allbareconfig - enable all applets without any sub-features'
@echo ' config - text based configurator (of last resort)'
@echo ' defconfig - set .config to largest generic configuration'
@echo ' menuconfig - interactive curses-based configurator'
@echo ' oldconfig - resolve any unresolved symbols in .config'
@echo ' hosttools - build sed for the host.'
@echo ' You can use these commands if the commands on the host'
@echo ' is unusable. Afterwards use it like:'
@echo ' make SED="$(objtree)/sed"'
@echo
@echo 'Installation:'
@echo ' install - install busybox into $(PREFIX)'
@echo ' uninstall'
@echo
@echo 'Development:'
@echo ' baseline - create busybox_old for bloatcheck.'
@echo ' bloatcheck - show size difference between old and new versions'
@echo ' check - run the test suite for all applets'
@echo ' checkhelp - check for missing help-entries in Config.in'
@echo ' randconfig - generate a random configuration'
@echo ' release - create a distribution tarball'
@echo ' sizes - show size of all enabled busybox symbols'
@echo ' objsizes - show size of each .o object built'
@echo

198
README Normal file
View File

@ -0,0 +1,198 @@
Please see the LICENSE file for details on copying and usage.
Please refer to the INSTALL file for instructions on how to build.
What is busybox:
BusyBox combines tiny versions of many common UNIX utilities into a single
small executable. It provides minimalist replacements for most of the
utilities you usually find in bzip2, coreutils, dhcp, diffutils, e2fsprogs,
file, findutils, gawk, grep, inetutils, less, modutils, net-tools, procps,
sed, shadow, sysklogd, sysvinit, tar, util-linux, and vim. The utilities
in BusyBox often have fewer options than their full-featured cousins;
however, the options that are included provide the expected functionality
and behave very much like their larger counterparts.
BusyBox has been written with size-optimization and limited resources in
mind, both to produce small binaries and to reduce run-time memory usage.
Busybox is also extremely modular so you can easily include or exclude
commands (or features) at compile time. This makes it easy to customize
embedded systems; to create a working system, just add /dev, /etc, and a
Linux kernel. Busybox (usually together with uClibc) has also been used as
a component of "thin client" desktop systems, live-CD distributions, rescue
disks, installers, and so on.
BusyBox provides a fairly complete POSIX environment for any small system,
both embedded environments and more full featured systems concerned about
space. Busybox is slowly working towards implementing the full Single Unix
Specification V3 (http://www.opengroup.org/onlinepubs/009695399/), but isn't
there yet (and for size reasons will probably support at most UTF-8 for
internationalization). We are also interested in passing the Linux Test
Project (http://ltp.sourceforge.net).
----------------
Using busybox:
BusyBox is extremely configurable. This allows you to include only the
components and options you need, thereby reducing binary size. Run 'make
config' or 'make menuconfig' to select the functionality that you wish to
enable. (See 'make help' for more commands.)
The behavior of busybox is determined by the name it's called under: as
"cp" it behaves like cp, as "sed" it behaves like sed, and so on. Called
as "busybox" it takes the second argument as the name of the applet to
run (I.E. "./busybox ls -l /proc").
The "standalone shell" mode is an easy way to try out busybox; this is a
command shell that calls the builtin applets without needing them to be
installed in the path. (Note that this requires /proc to be mounted, if
testing from a boot floppy or in a chroot environment.)
The build automatically generates a file "busybox.links", which is used by
'make install' to create symlinks to the BusyBox binary for all compiled in
commands. This uses the PREFIX environment variable to specify where to
install, and installs hardlinks or symlinks depending on the configuration
preferences. (You can also manually run the install script at
"applets/install.sh").
----------------
Downloading the current source code:
Source for the latest released version, as well as daily snapshots, can always
be downloaded from
http://busybox.net/downloads/
You can browse the up to the minute source code and change history online.
http://www.busybox.net/cgi-bin/viewcvs.cgi/trunk/busybox/
Anonymous SVN access is available. For instructions, check out:
http://busybox.net/subversion.html
For those that are actively contributing and would like to check files in,
see:
http://busybox.net/developer.html
The developers also have a bug and patch tracking system
(http://bugs.busybox.net) although posting a bug/patch to the mailing list
is generally a faster way of getting it fixed, and the complete archive of
what happened is the subversion changelog.
----------------
getting help:
when you find you need help, you can check out the busybox mailing list
archives at http://busybox.net/lists/busybox/ or even join
the mailing list if you are interested.
----------------
bugs:
if you find bugs, please submit a detailed bug report to the busybox mailing
list at busybox@busybox.net. a well-written bug report should include a
transcript of a shell session that demonstrates the bad behavior and enables
anyone else to duplicate the bug on their own machine. the following is such
an example:
to: busybox@busybox.net
from: diligent@testing.linux.org
subject: /bin/date doesn't work
package: busybox
version: 1.00
when i execute busybox 'date' it produces unexpected results.
with gnu date i get the following output:
$ date
fri oct 8 14:19:41 mdt 2004
but when i use busybox date i get this instead:
$ date
illegal instruction
i am using debian unstable, kernel version 2.4.25-vrs2 on a netwinder,
and the latest uclibc from cvs. thanks for the wonderful program!
-diligent
note the careful description and use of examples showing not only what
busybox does, but also a counter example showing what an equivalent app
does (or pointing to the text of a relevant standard). Bug reports lacking
such detail may never be fixed... Thanks for understanding.
----------------
Portability:
Busybox is developed and tested on Linux 2.4 and 2.6 kernels, compiled
with gcc (the unit-at-a-time optimizations in version 3.4 and later are
worth upgrading to get, but older versions should work), and linked against
uClibc (0.9.27 or greater) or glibc (2.2 or greater). In such an
environment, the full set of busybox features should work, and if
anything doesn't we want to know about it so we can fix it.
There are many other environments out there, in which busybox may build
and run just fine. We just don't test them. Since busybox consists of a
large number of more or less independent applets, portability is a question
of which features work where. Some busybox applets (such as cat and rm) are
highly portable and likely to work just about anywhere, while others (such as
insmod and losetup) require recent Linux kernels with recent C libraries.
Earlier versions of Linux and glibc may or may not work, for any given
configuration. Linux 2.2 or earlier should mostly work (there's still
some support code in things like mount.c) but this is no longer regularly
tested, and inherently won't support certain features (such as long files
and --bind mounts). The same is true for glibc 2.0 and 2.1: expect a higher
testing and debugging burden using such old infrastructure. (The busybox
developers are not very interested in supporting these older versions, but
will probably accept small self-contained patches to fix simple problems.)
Some environments are not recommended. Early versions of uClibc were buggy
and missing many features: upgrade. Linking against libc5 or dietlibc is
not supported and not interesting to the busybox developers. (The first is
obsolete and has no known size or feature advantages over uClibc, the second
has known bugs that its developers have actively refused to fix.) Ancient
Linux kernels (2.0.x and earlier) are similarly uninteresting.
In theory it's possible to use Busybox under other operating systems (such as
MacOS X, Solaris, Cygwin, or the BSD Fork Du Jour). This generally involves
a different kernel and a different C library at the same time. While it
should be possible to port the majority of the code to work in one of
these environments, don't be suprised if it doesn't work out of the box. If
you're into that sort of thing, start small (selecting just a few applets)
and work your way up.
Shaun Jackman has recently (2005) ported busybox to a combination of newlib
and libgloss, and some of his patches have been integrated. This platform
may join glibc/uclibc and Linux as a supported combination with the 1.1
release, but is not supported in 1.0.
Supported hardware:
BusyBox in general will build on any architecture supported by gcc. We
support both 32 and 64 bit platforms, and both big and little endian
systems.
Under 2.4 Linux kernels, kernel module loading was implemented in a
platform-specific manner. Busybox's insmod utility has been reported to
work under ARM, CRIS, H8/300, x86, ia64, x86_64, m68k, MIPS, PowerPC, S390,
SH3/4/5, Sparc, v850e, and x86_64. Anything else probably won't work.
The module loading mechanism for the 2.6 kernel is much more generic, and
we believe 2.6.x kernel module loading support should work on all
architectures supported by the kernel.
----------------
Please feed suggestions, bug reports, insults, and bribes back to the busybox
maintainer:
Denis Vlasenko
<vda.linux@googlemail.com>

309
TODO Normal file
View File

@ -0,0 +1,309 @@
Busybox TODO
Stuff that needs to be done. This is organized by who plans to get around to
doing it eventually, but that doesn't mean they "own" the item. If you want to
do one of these bounce an email off the person it's listed under to see if they
have any suggestions how they plan to go about it, and to minimize conflicts
between your work and theirs. But otherwise, all of these are fair game.
Rob Landley <rob@landley.net>:
Add BB_NOMMU to platform.h and migrate __uClinux__ tests to that.
#if defined __UCLIBC__ && !defined __ARCH_USE_MMU__
Add a libbb/platform.c
Implement fdprintf() for platforms that haven't got one.
Implement bb_realpath() that can handle NULL on non-glibc.
Cleanup bb_asprintf()
Migrate calloc() and bb_calloc() occurrences to bb_xzalloc().
Remove obsolete _() wrapper crud for internationalization we don't do.
Figure out where we need utf8 support, and add it.
sh
The command shell situation is a big mess. We have three or four different
shells that don't really share any code, and the "standalone shell" doesn't
work all that well (especially not in a chroot environment), due to apps not
being reentrant. I'm writing a new shell (bbsh) to unify the various
shells and configurably add the minimal set of bash features people
actually use. The hardest part is it has to configure down as small as
lash while providing lash's features. The rest is easy in comparison.
bzip2
Compression-side support.
init
General cleanup (should use ENABLE_FEATURE_INIT_SYSLOG and ENABLE_FEATURE_INIT_DEBUG).
depmod
busybox lacks a way to update module deps when running from firmware without the
use of the depmod.pl (perl is to bloated for most embedded setups) and or orig
modutils. The orig depmod is rather pointless to have to add to a firmware image
in when we already have a insmod/rmmod and friends.
Unify base64 handling.
There's base64 encoding and decoding going on in:
networking/wget.c:base64enc()
coreutils/uudecode.c:read_base64()
coreutils/uuencode.c:tbl_base64[]
networking/httpd.c:decodeBase64()
And probably elsewhere. That needs to be unified into libbb functions.
Do a SUSv3 audit
Look at the full Single Unix Specification version 3 (available online at
"http://www.opengroup.org/onlinepubs/009695399/nfindex.html") and
figure out which of our apps are compliant, and what we're missing that
we might actually care about.
Even better would be some kind of automated compliance test harness that
exercises each command line option and the various corner cases.
Internationalization
How much internationalization should we do?
The low hanging fruit is UTF-8 character set support. We should do this.
(Vodz pointed out the shell's cmdedit as needing work here. What else?)
We also have lots of hardwired english text messages. Consolidating this
into some kind of message table not only makes translation easier, but
also allows us to consolidate redundant (or close) strings.
We probably don't want to be bloated with locale support. (Not unless we
can cleanly export it from our underlying C library without having to
concern ourselves with it directly. Perhaps a few specific things like a
config option for "date" are low hanging fruit here?)
What level should things happen at? How much do we care about
internationalizing the text console when X11 and xterms are so much better
at it? (There's some infrastructure here we don't implement: The
"unicode_start" and "unicode_stop" shell scripts need "vt-is-UTF8" and a
--unicode option to loadkeys. That implies a real loadkeys/dumpkeys
implementation to replace loadkmap/dumpkmap. Plus messing with console font
loading. Is it worth it, or do we just say "use X"?)
Individual compilation of applets.
It would be nice if busybox had the option to compile to individual applets,
for people who want an alternate implementation less bloated than the gnu
utils (or simply with less political baggage), but without it being one big
executable.
Turning libbb into a real dll is another possibility, especially if libbb
could export some of the other library interfaces we've already more or less
got the code for (like zlib).
buildroot - Make a "dogfood" option
Busybox 1.1 will be capable of replacing most gnu packages for real world
use, such as developing software or in a live CD. It needs wider testing.
Busybox should now be able to replace bzip2, coreutils, e2fsprogs, file,
findutils, gawk, grep, inetutils, less, modutils, net-tools, patch, procps,
sed, shadow, sysklogd, sysvinit, tar, util-linux, and vim. The resulting
system should be self-hosting (I.E. able to rebuild itself from source
code). This means it would need (at least) binutils, gcc, and make, or
equivalents.
It would be a good "eating our own dogfood" test if buildroot had the option
of using a "make allyesconfig" busybox instead of the all of the above
packages. Anything that's wrong with the resulting system, we can fix. (It
would be nice to be able to upgrade busybox to be able to replace bash and
diffutils as well, but we're not there yet.)
One example of an existing system that does this already is Firmware Linux:
http://www.landley.net/code/firmware
initramfs
Busybox should have a sample initramfs build script. This depends on
bbsh, mdev, and switch_root.
mkdep
Write a mkdep that doesn't segfault if there's a directory it doesn't
have permission to read, isn't based on manually editing the output of
lexx and yacc, doesn't make such a mess under include/config, etc.
Group globals into unions of structures.
Go through and turn all the global and static variables into structures,
and have all those structures be in a big union shared between processes,
so busybox uses less bss. (This is a big win on nommu machines.) See
sed.c and mdev.c for examples.
Go through bugs.busybox.net and close out all of that somehow.
This one's open to everybody, but I'll wind up doing it...
Bernhard Fischer <busybox@busybox.net> suggests to look at these:
New debug options:
-Wlarger-than-127
Cleanup any big users
-Wunused-parameter
Facilitate applet PROTOTYPES to provide means for having applets that
do a) not take any arguments b) need only one of argc or argv c) need
both argc and argv. All of these three options should go for the most
feature complete denominator.
Collate BUFSIZ IOBUF_SIZE MY_BUF_SIZE PIPE_PROGRESS_SIZE BUFSIZE PIPESIZE
make bb_common_bufsiz1 configurable, size wise.
make pipesize configurable, size wise.
Use bb_common_bufsiz1 throughout applets!
Add chrt applet. Please CC Bernhard if you suggest a patch.
As yet unclaimed:
----
find
doesn't understand (), lots of susv3 stuff.
----
diff
Make sure we handle empty files properly:
From the patch man page:
you can remove a file by sending out a context diff that compares
the file to be deleted with an empty file dated the Epoch. The
file will be removed unless patch is conforming to POSIX and the
-E or --remove-empty-files option is not given.
---
patch
Should have simple fuzz factor support to apply patches at an offset which
shouldn't take up too much space.
And while we're at it, a new patch filename quoting format is apparently
coming soon: http://marc.theaimsgroup.com/?l=git&m=112927316408690&w=2
---
man
It would be nice to have a man command. Not one that handles troff or
anything, just one that can handle preformatted ascii man pages, possibly
compressed. This could probably be a script in the extras directory that
calls cat/zcat/bzcat | less
(How doclifter might work into this is anybody's guess.)
---
ar
Write support?
---
Architectural issues:
bb_close() with fsync()
We should have a bb_close() in place of normal close, with a CONFIG_ option
to not just check the return value of close() for an error, but fsync().
Close can't reliably report anything useful because if write() accepted the
data then it either went out to the network or it's in cache or a pipe
buffer. Either way, there's no guarantee it'll make it to its final
destination before close() gets called, so there's no guarantee that any
error will be reported.
You need to call fsync() if you care about errors that occur after write(),
but that can have a big performance impact. So make it a config option.
---
Unify archivers
Lots of archivers have the same general infrastructure. The directory
traversal code should be factored out, and the guts of each archiver could
be some setup code and a series of callbacks for "add this file",
"add this directory", "add this symlink" and so on.
This could clean up tar and zip, and make it cheaper to add cpio and ar
write support, and possibly even cheaply add things like mkisofs or
mksquashfs someday, if they become relevant.
---
Text buffer support.
Several existing applets (sort, vi, less...) read
a whole file into memory and act on it. There might be an opportunity
for shared code in there that could be moved into libbb...
---
Memory Allocation
We have a CONFIG_BUFFER mechanism that lets us select whether to do memory
allocation on the stack or the heap. Unfortunately, we're not using it much.
We need to audit our memory allocations and turn a lot of malloc/free calls
into RESERVE_CONFIG_BUFFER/RELEASE_CONFIG_BUFFER.
For a start, see e.g. make EXTRA_CFLAGS=-Wlarger-than-64
And while we're at it, many of the CONFIG_FEATURE_CLEAN_UP #ifdefs will be
optimized out by the compiler in the stack allocation case (since there's no
free for an alloca()), and this means that various cleanup loops that just
call free might also be optimized out by the compiler if written right, so
we can yank those #ifdefs too, and generally clean up the code.
---
Switch CONFIG_SYMBOLS to ENABLE_SYMBOLS
In busybox 1.0 and earlier, configuration was done by CONFIG_SYMBOLS
that were either defined or undefined to indicate whether the symbol was
selected in the .config file. They were used with #ifdefs, ala:
#ifdef CONFIG_SYMBOL
if (other_test) {
do_code();
}
#endif
In 1.1, we have new ENABLE_SYMBOLS which are always defined (as 0 or 1),
meaning you can still use them for preprocessor tests by replacing
"#ifdef CONFIG_SYMBOL" with "#if ENABLE_SYMBOL". But more importantly, we
can use them as a true or false test in normal C code:
if (ENABLE_SYMBOL && other_test) {
do_code();
}
(Optimizing away if() statements that resolve to a constant value
is known as "dead code elimination", an optimization so old and simple that
Turbo Pascal for DOS did it twenty years ago. Even modern mini-compilers
like the Tiny C Compiler (tcc) and the Small Device C Compiler (SDCC)
perform dead code elimination.)
Right now, busybox.h is #including both "config.h" (defining the
CONFIG_SYMBOLS) and "bb_config.h" (defining the ENABLE_SYMBOLS). At some
point in the future, it would be nice to wean ourselves off of the
CONFIG versions. (Among other things, some defective build environments
leak the Linux kernel's CONFIG_SYMBOLS into the system's standard #include
files. We've experienced collisions before.)
---
FEATURE_CLEAN_UP
This is more an unresolved issue than a to-do item. More thought is needed.
Normally we rely on exit() to free memory, close files, and unmap segments
for us. This makes most calls to free(), close(), and unmap() optional in
busybox applets that don't intend to run for very long, and optional stuff
can be omitted to save size.
The idea was raised that we could simulate fork/exit with setjmp/longjmp
for _really_ brainless embedded systems, or speed up the standalone shell
by not forking. Doing so would require a reliable FEATURE_CLEAN_UP.
Unfortunately, this isn't as easy as it sounds.
The problem is, lots of things exit(), sometimes unexpectedly (xmalloc())
and sometimes reliably (bb_perror_msg_and_die() or show_usage()). This
jumps out of the normal flow control and bypasses any cleanup code we
put at the end of our applets.
It's possible to add hooks to libbb functions like xmalloc() and xopen()
to add their entries to a linked list, which could be traversed and
freed/closed automatically. (This would need to be able to free just the
entries after a checkpoint to be usable for a forkless standalone shell.
You don't want to free the shell's own resources.)
Right now, FEATURE_CLEAN_UP is more or less a debugging aid, to make things
like valgrind happy. It's also documentation of _what_ we're trusting
exit() to clean up for us. But new infrastructure to auto-free stuff would
render the existing FEATURE_CLEAN_UP code redundant.
For right now, exit() handles it just fine.
Minor stuff:
watchdog.c could autodetect the timer duration via:
if(!ioctl (fd, WDIOC_GETTIMEOUT, &tmo)) timer_duration = 1 + (tmo / 2);
Unfortunately, that needs linux/watchdog.h and that contains unfiltered
kernel types on some distros, which breaks the build.
---
use bb_error_msg where appropriate: See
egrep "(printf.*\([[:space:]]*(stderr|2)|[^_]write.*\([[:space:]]*(stderr|2))"
---
use bb_perror_msg where appropriate: See
egrep "[^_]perror"
---
Remove superfluous fmt occurances: e.g.
fprintf(stderr, "%s: %s not found\n", "unalias", *argptr);
-> fprintf(stderr, "unalias: %s not found\n", *argptr);
---
possible code duplication ingroup() and is_a_group_member()
---
Move __get_hz() to a better place and (re)use it in route.c, ash.c, msh.c
---
Code cleanup:
Replace deprecated functions.
bzero() -> memset()
---
sigblock(), siggetmask(), sigsetmask(), sigmask() -> sigprocmask et al
---
vdprintf() -> similar sized functionality
---

21
applets/Kbuild Normal file
View File

@ -0,0 +1,21 @@
# Makefile for busybox
#
# Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
#
# Licensed under the GPL v2, see the file LICENSE in this tarball.
obj-y:=
obj-y += applets.o
obj-y += busybox.o
# Generated file needs additional love
applets/applets.o: include/usage_compressed.h
hostprogs-y += usage
always := $(hostprogs-y)
HOSTCFLAGS_usage.o = -I$(srctree)/include
include/usage_compressed.h: $(srctree)/include/usage.h applets/usage
@echo ' GEN include/usage_compressed.h'
@$(srctree)/applets/usage_compressed include/usage_compressed.h applets

483
applets/applets.c Normal file
View File

@ -0,0 +1,483 @@
/* vi: set sw=4 ts=4: */
/*
* Utility routines.
*
* Copyright (C) tons of folks. Tracking down who wrote what
* isn't something I'm going to worry about... If you wrote something
* here, please feel free to acknowledge your work.
*
* Based in part on code from sash, Copyright (c) 1999 by David I. Bell
* Permission has been granted to redistribute this code under the GPL.
*
* Licensed under GPLv2 or later, see file License in this tarball for details.
*/
#include "busybox.h"
#include <unistd.h>
#include <string.h>
#include <assert.h>
/* Apparently uclibc defines __GLIBC__ (compat trick?). Oh well. */
#if ENABLE_STATIC && defined(__GLIBC__) && !defined(__UCLIBC__)
#warning Static linking against glibc produces buggy executables
#warning (glibc does not cope well with ld --gc-sections).
#warning See sources.redhat.com/bugzilla/show_bug.cgi?id=3400
#warning Note that glibc is utterly unsuitable for static linking anyway.
#endif
#if ENABLE_SHOW_USAGE && !ENABLE_FEATURE_COMPRESS_USAGE
static const char usage_messages[] =
#define MAKE_USAGE
#include "usage.h"
#include "applets.h"
;
#undef MAKE_USAGE
#else
#define usage_messages 0
#endif /* ENABLE_SHOW_USAGE */
#undef APPLET
#undef APPLET_NOUSAGE
#undef PROTOTYPES
#include "applets.h"
static struct BB_applet *applet_using;
/* The -1 arises because of the {0,NULL,0,-1} entry above. */
const size_t NUM_APPLETS = (sizeof (applets) / sizeof (struct BB_applet) - 1);
#ifdef CONFIG_FEATURE_SUID_CONFIG
#include <ctype.h>
#define CONFIG_FILE "/etc/busybox.conf"
/* applets [] is const, so we have to define this "override" structure */
static struct BB_suid_config
{
struct BB_applet *m_applet;
uid_t m_uid;
gid_t m_gid;
mode_t m_mode;
struct BB_suid_config *m_next;
} *suid_config;
static int suid_cfg_readable;
/* check if u is member of group g */
static int ingroup(uid_t u, gid_t g)
{
struct group *grp = getgrgid(g);
if (grp) {
char **mem;
for (mem = grp->gr_mem; *mem; mem++) {
struct passwd *pwd = getpwnam(*mem);
if (pwd && (pwd->pw_uid == u))
return 1;
}
}
return 0;
}
/* This should probably be a libbb routine. In that case,
* I'd probably rename it to something like bb_trimmed_slice.
*/
static char *get_trimmed_slice(char *s, char *e)
{
/* First, consider the value at e to be nul and back up until we
* reach a non-space char. Set the char after that (possibly at
* the original e) to nul. */
while (e-- > s) {
if (!isspace(*e)) {
break;
}
}
e[1] = 0;
/* Next, advance past all leading space and return a ptr to the
* first non-space char; possibly the terminating nul. */
return skip_whitespace(s);
}
#define parse_error(x) { err=x; goto pe_label; }
/* Don't depend on the tools to combine strings. */
static const char config_file[] = CONFIG_FILE;
/* There are 4 chars + 1 nul for each of user/group/other. */
static const char mode_chars[] = "Ssx-\0Ssx-\0Ttx-";
/* We don't supply a value for the nul, so an index adjustment is
* necessary below. Also, we use unsigned short here to save some
* space even though these are really mode_t values. */
static const unsigned short mode_mask[] = {
/* SST sst xxx --- */
S_ISUID, S_ISUID|S_IXUSR, S_IXUSR, 0, /* user */
S_ISGID, S_ISGID|S_IXGRP, S_IXGRP, 0, /* group */
0, S_IXOTH, S_IXOTH, 0 /* other */
};
static void parse_config_file(void)
{
struct BB_suid_config *sct_head;
struct BB_suid_config *sct;
struct BB_applet *applet;
FILE *f;
char *err;
char *s;
char *e;
int i, lc, section;
char buffer[256];
struct stat st;
assert(!suid_config); /* Should be set to NULL by bss init. */
if ((stat(config_file, &st) != 0) /* No config file? */
|| !S_ISREG(st.st_mode) /* Not a regular file? */
|| (st.st_uid != 0) /* Not owned by root? */
|| (st.st_mode & (S_IWGRP | S_IWOTH)) /* Writable by non-root? */
|| !(f = fopen(config_file, "r")) /* Cannot open? */
) {
return;
}
suid_cfg_readable = 1;
sct_head = NULL;
section = lc = 0;
do {
s = buffer;
if (!fgets(s, sizeof(buffer), f)) { /* Are we done? */
if (ferror(f)) { /* Make sure it wasn't a read error. */
parse_error("reading");
}
fclose(f);
suid_config = sct_head; /* Success, so set the pointer. */
return;
}
lc++; /* Got a (partial) line. */
/* If a line is too long for our buffer, we consider it an error.
* The following test does mistreat one corner case though.
* If the final line of the file does not end with a newline and
* yet exactly fills the buffer, it will be treated as too long
* even though there isn't really a problem. But it isn't really
* worth adding code to deal with such an unlikely situation, and
* we do err on the side of caution. Besides, the line would be
* too long if it did end with a newline. */
if (!strchr(s, '\n') && !feof(f)) {
parse_error("line too long");
}
/* Trim leading and trailing whitespace, ignoring comments, and
* check if the resulting string is empty. */
if (!*(s = get_trimmed_slice(s, strchrnul(s, '#')))) {
continue;
}
/* Check for a section header. */
if (*s == '[') {
/* Unlike the old code, we ignore leading and trailing
* whitespace for the section name. We also require that
* there are no stray characters after the closing bracket. */
if (!(e = strchr(s, ']')) /* Missing right bracket? */
|| e[1] /* Trailing characters? */
|| !*(s = get_trimmed_slice(s+1, e)) /* Missing name? */
) {
parse_error("section header");
}
/* Right now we only have one section so just check it.
* If more sections are added in the future, please don't
* resort to cascading ifs with multiple strcasecmp calls.
* That kind of bloated code is all too common. A loop
* and a string table would be a better choice unless the
* number of sections is very small. */
if (strcasecmp(s, "SUID") == 0) {
section = 1;
continue;
}
section = -1; /* Unknown section so set to skip. */
continue;
}
/* Process sections. */
if (section == 1) { /* SUID */
/* Since we trimmed leading and trailing space above, we're
* now looking for strings of the form
* <key>[::space::]*=[::space::]*<value>
* where both key and value could contain inner whitespace. */
/* First get the key (an applet name in our case). */
if (!!(e = strchr(s, '='))) {
s = get_trimmed_slice(s, e);
}
if (!e || !*s) { /* Missing '=' or empty key. */
parse_error("keyword");
}
/* Ok, we have an applet name. Process the rhs if this
* applet is currently built in and ignore it otherwise.
* Note: This can hide config file bugs which only pop
* up when the busybox configuration is changed. */
if ((applet = find_applet_by_name(s))) {
/* Note: We currently don't check for duplicates!
* The last config line for each applet will be the
* one used since we insert at the head of the list.
* I suppose this could be considered a feature. */
sct = xmalloc(sizeof(struct BB_suid_config));
sct->m_applet = applet;
sct->m_mode = 0;
sct->m_next = sct_head;
sct_head = sct;
/* Get the specified mode. */
e = skip_whitespace(e+1);
for (i=0 ; i < 3 ; i++) {
const char *q;
if (!*(q = strchrnul(mode_chars + 5*i, *e++))) {
parse_error("mode");
}
/* Adjust by -i to account for nul. */
sct->m_mode |= mode_mask[(q - mode_chars) - i];
}
/* Now get the the user/group info. */
s = skip_whitespace(e);
/* Note: We require whitespace between the mode and the
* user/group info. */
if ((s == e) || !(e = strchr(s, '.'))) {
parse_error("<uid>.<gid>");
}
*e++ = 0;
/* We can't use get_ug_id here since it would exit()
* if a uid or gid was not found. Oh well... */
{
char *e2;
sct->m_uid = strtoul(s, &e2, 10);
if (*e2 || (s == e2)) {
struct passwd *pwd = getpwnam(s);
if (!pwd) {
parse_error("user");
}
sct->m_uid = pwd->pw_uid;
}
sct->m_gid = strtoul(e, &e2, 10);
if (*e2 || (e == e2)) {
struct group *grp;
if (!(grp = getgrnam(e))) {
parse_error("group");
}
sct->m_gid = grp->gr_gid;
}
}
}
continue;
}
/* Unknown sections are ignored. */
/* Encountering configuration lines prior to seeing a
* section header is treated as an error. This is how
* the old code worked, but it may not be desirable.
* We may want to simply ignore such lines in case they
* are used in some future version of busybox. */
if (!section) {
parse_error("keyword outside section");
}
} while (1);
pe_label:
fprintf(stderr, "Parse error in %s, line %d: %s\n",
config_file, lc, err);
fclose(f);
/* Release any allocated memory before returning. */
while (sct_head) {
sct = sct_head->m_next;
free(sct_head);
sct_head = sct;
}
return;
}
#else
#define parse_config_file()
#endif /* CONFIG_FEATURE_SUID_CONFIG */
#ifdef CONFIG_FEATURE_SUID
static void check_suid(struct BB_applet *applet)
{
uid_t ruid = getuid(); /* real [ug]id */
uid_t rgid = getgid();
#ifdef CONFIG_FEATURE_SUID_CONFIG
if (suid_cfg_readable) {
struct BB_suid_config *sct;
for (sct = suid_config; sct; sct = sct->m_next) {
if (sct->m_applet == applet)
break;
}
if (sct) {
mode_t m = sct->m_mode;
if (sct->m_uid == ruid) /* same uid */
m >>= 6;
else if ((sct->m_gid == rgid) || ingroup(ruid, sct->m_gid)) /* same group / in group */
m >>= 3;
if (!(m & S_IXOTH)) /* is x bit not set ? */
bb_error_msg_and_die("you have no permission to run this applet!");
if ((sct->m_mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) { /* *both* have to be set for sgid */
xsetgid(sct->m_gid);
} else xsetgid(rgid); /* no sgid -> drop */
if (sct->m_mode & S_ISUID) xsetuid(sct->m_uid);
else xsetuid(ruid); /* no suid -> drop */
} else {
/* default: drop all privileges */
xsetgid(rgid);
xsetuid(ruid);
}
return;
} else {
#ifndef CONFIG_FEATURE_SUID_CONFIG_QUIET
static int onetime = 0;
if (!onetime) {
onetime = 1;
fprintf(stderr, "Using fallback suid method\n");
}
#endif
}
#endif
if (applet->need_suid == _BB_SUID_ALWAYS) {
if (geteuid()) bb_error_msg_and_die("applet requires root privileges!");
} else if (applet->need_suid == _BB_SUID_NEVER) {
xsetgid(rgid); /* drop all privileges */
xsetuid(ruid);
}
}
#else
#define check_suid(x)
#endif /* CONFIG_FEATURE_SUID */
#ifdef CONFIG_FEATURE_COMPRESS_USAGE
#include "usage_compressed.h"
#include "unarchive.h"
static const char *unpack_usage_messages(void)
{
int input[2], output[2], pid;
char *buf;
if(pipe(input) < 0 || pipe(output) < 0)
exit(1);
pid = fork();
switch (pid) {
case -1: /* error */
exit(1);
case 0: /* child */
close(input[1]);
close(output[0]);
uncompressStream(input[0], output[1]);
exit(0);
}
/* parent */
close(input[0]);
close(output[1]);
pid = fork();
switch (pid) {
case -1: /* error */
exit(1);
case 0: /* child */
full_write(input[1], packed_usage, sizeof(packed_usage));
exit(0);
}
/* parent */
close(input[1]);
buf = xmalloc(SIZEOF_usage_messages);
full_read(output[0], buf, SIZEOF_usage_messages);
return buf;
}
#else
#define unpack_usage_messages() usage_messages
#endif /* ENABLE_FEATURE_COMPRESS_USAGE */
void bb_show_usage(void)
{
if (ENABLE_SHOW_USAGE) {
const char *format_string;
const char *usage_string = unpack_usage_messages();
int i;
for (i = applet_using - applets; i > 0;)
if (!*usage_string++) --i;
format_string = "%s\n\nUsage: %s %s\n\n";
if (*usage_string == '\b')
format_string = "%s\n\nNo help available.\n\n";
fprintf(stderr, format_string, bb_msg_full_version,
applet_using->name, usage_string);
}
exit(xfunc_error_retval);
}
static int applet_name_compare(const void *name, const void *vapplet)
{
const struct BB_applet *applet = vapplet;
return strcmp(name, applet->name);
}
extern const size_t NUM_APPLETS;
struct BB_applet *find_applet_by_name(const char *name)
{
return bsearch(name, applets, NUM_APPLETS, sizeof(struct BB_applet),
applet_name_compare);
}
void run_applet_by_name(const char *name, int argc, char **argv)
{
if (ENABLE_FEATURE_SUID_CONFIG) parse_config_file();
if (!strncmp(name, "busybox", 7)) busybox_main(argc, argv);
/* Do a binary search to find the applet entry given the name. */
applet_using = find_applet_by_name(name);
if (applet_using) {
applet_name = applet_using->name;
if(argc==2 && !strcmp(argv[1], "--help")) bb_show_usage();
if(ENABLE_FEATURE_SUID) check_suid(applet_using);
exit((*(applet_using->main))(argc, argv));
}
}

148
applets/busybox.c Normal file
View File

@ -0,0 +1,148 @@
/* vi: set sw=4 ts=4: */
/*
* BusyBox' main applet dispatcher.
*
* Licensed under GPLv2, see file LICENSE in this tarball for details.
*/
#include "busybox.h"
const char *applet_name ATTRIBUTE_EXTERNALLY_VISIBLE;
#ifdef CONFIG_FEATURE_INSTALLER
/*
* directory table
* this should be consistent w/ the enum, busybox.h::Location,
* or else...
*/
static const char usr_bin [] ="/usr/bin";
static const char usr_sbin[] ="/usr/sbin";
static const char* const install_dir[] = {
&usr_bin [8], /* "", equivalent to "/" for concat_path_file() */
&usr_bin [4], /* "/bin" */
&usr_sbin[4], /* "/sbin" */
usr_bin,
usr_sbin
};
/* abstract link() */
typedef int (*__link_f)(const char *, const char *);
/* create (sym)links for each applet */
static void install_links(const char *busybox, int use_symbolic_links)
{
__link_f Link = link;
char *fpc;
int i;
int rc;
if (use_symbolic_links)
Link = symlink;
for (i = 0; applets[i].name != NULL; i++) {
fpc = concat_path_file(
install_dir[applets[i].location], applets[i].name);
rc = Link(busybox, fpc);
if (rc!=0 && errno!=EEXIST) {
bb_perror_msg("%s", fpc);
}
free(fpc);
}
}
#else
#define install_links(x,y)
#endif /* CONFIG_FEATURE_INSTALLER */
int main(int argc, char **argv)
{
const char *s;
applet_name=argv[0];
if (*applet_name == '-') applet_name++;
for (s = applet_name; *s ;)
if (*(s++) == '/') applet_name = s;
/* Set locale for everybody except 'init' */
if (ENABLE_LOCALE_SUPPORT && getpid() != 1)
setlocale(LC_ALL, "");
run_applet_by_name(applet_name, argc, argv);
bb_error_msg_and_die("applet not found");
}
int busybox_main(int argc, char **argv)
{
/*
* This style of argument parsing doesn't scale well
* in the event that busybox starts wanting more --options.
* If someone has a cleaner approach, by all means implement it.
*/
if (ENABLE_FEATURE_INSTALLER && argc > 1 && !strcmp(argv[1], "--install")) {
int use_symbolic_links = 0;
int rc = 0;
char *busybox;
/* to use symlinks, or not to use symlinks... */
if (argc > 2) {
if ((strcmp(argv[2], "-s") == 0)) {
use_symbolic_links = 1;
}
}
/* link */
// XXX: FIXME: this is broken. Why not just use argv[0] ?
busybox = xreadlink("/proc/self/exe");
if (busybox) {
install_links(busybox, use_symbolic_links);
free(busybox);
} else {
rc = 1;
}
return rc;
}
/* Deal with --help. (Also print help when called with no arguments) */
if (argc==1 || !strcmp(argv[1],"--help") ) {
if (argc>2) {
applet_name = argv[2];
run_applet_by_name(applet_name, 2, argv);
} else {
const struct BB_applet *a;
int col, output_width;
if (ENABLE_FEATURE_AUTOWIDTH) {
/* Obtain the terminal width. */
get_terminal_width_height(0, &output_width, NULL);
/* leading tab and room to wrap */
output_width -= sizeof("start-stop-daemon, ") + 8;
} else output_width = 80 - sizeof("start-stop-daemon, ") - 8;
printf("%s\n"
"Copyright (C) 1998-2006  Erik Andersen, Rob Landley, and others.\n"
"Licensed under GPLv2.  See source distribution for full notice.\n\n"
"Usage: busybox [function] [arguments]...\n"
" or: [function] [arguments]...\n\n"
"\tBusyBox is a multi-call binary that combines many common Unix\n"
"\tutilities into a single executable. Most people will create a\n"
"\tlink to busybox for each function they wish to use and BusyBox\n"
"\twill act like whatever it was invoked as!\n"
"\nCurrently defined functions:\n", bb_msg_full_version);
col=0;
for(a = applets; a->name;) {
col += printf("%s%s", (col ? ", " : "\t"), (a++)->name);
if (col > output_width && a->name) {
printf(",\n");
col = 0;
}
}
printf("\n\n");
exit(0);
}
} else run_applet_by_name(argv[1], argc-1, argv+1);
bb_error_msg_and_die("applet not found");
}

24
applets/busybox.mkll Executable file
View File

@ -0,0 +1,24 @@
#!/bin/sh
# Make busybox links list file.
# input $1: full path to Config.h
# input $2: full path to applets.h
# output (stdout): list of pathnames that should be linked to busybox
# Maintainer: Larry Doolittle <ldoolitt@recycle.lbl.gov>
export LC_ALL=POSIX
export LC_CTYPE=POSIX
CONFIG_H=${1:-include/autoconf.h}
APPLETS_H=${2:-include/applets.h}
$HOSTCC -E -DMAKE_LINKS -include $CONFIG_H $APPLETS_H |
awk '/^[ \t]*LINK/{
dir=substr($2,8)
gsub("_","/",dir)
if(dir=="/ROOT") dir=""
file=$3
gsub("\"","",file)
if (file=="busybox") next
print tolower(dir) "/" file
}'

27
applets/individual.c Normal file
View File

@ -0,0 +1,27 @@
/* Minimal wrapper to build an individual busybox applet.
*
* Copyright 2005 Rob Landley <rob@landley.net
*
* Licensed under GPL version 2, see file LICENSE in this tarball for details
*/
const char *applet_name;
#include <stdio.h>
#include <stdlib.h>
//Ok to remove? #include "bb_config.h"
#include "usage.h"
int main(int argc, char *argv[])
{
applet_name=argv[0];
return APPLET_main(argc,argv);
}
void bb_show_usage(void)
{
printf(APPLET_full_usage "\n");
exit(1);
}

94
applets/install.sh Executable file
View File

@ -0,0 +1,94 @@
#!/bin/sh
export LC_ALL=POSIX
export LC_CTYPE=POSIX
prefix=${1}
if [ -z "$prefix" ]; then
echo "usage: applets/install.sh DESTINATION [--symlinks/--hardlinks]"
exit 1;
fi
h=`sort busybox.links | uniq`
cleanup="0"
noclobber="0"
case "$2" in
--hardlinks) linkopts="-f";;
--symlinks) linkopts="-fs";;
--cleanup) cleanup="1";;
--noclobber) noclobber="1";;
"") h="";;
*) echo "Unknown install option: $2"; exit 1;;
esac
if [ -n "$DO_INSTALL_LIBS" ] && [ "$DO_INSTALL_LIBS" != "n" ]; then
# get the target dir for the libs
# assume it starts with lib
libdir=$($CC -print-file-name=libc.so | \
sed -n 's%^.*\(/lib[^\/]*\)/libc.so%\1%p')
if test -z "$libdir"; then
libdir=/lib
fi
mkdir -p $prefix/$libdir || exit 1
for i in $DO_INSTALL_LIBS; do
rm -f $prefix/$libdir/$i || exit 1
if [ -f $i ]; then
cp -a $i $prefix/$libdir/ || exit 1
chmod 0644 $prefix/$libdir/$i || exit 1
fi
done
fi
if [ "$cleanup" = "1" ] && [ -e "$prefix/bin/busybox" ]; then
inode=`ls -i "$prefix/bin/busybox" | awk '{print $1}'`
sub_shell_it=`
cd "$prefix"
for d in usr/sbin usr/bin sbin bin ; do
pd=$PWD
if [ -d "$d" ]; then
cd $d
ls -iL . | grep "^ *$inode" | awk '{print $2}' | env -i xargs rm -f
fi
cd "$pd"
done
`
fi
rm -f $prefix/bin/busybox || exit 1
mkdir -p $prefix/bin || exit 1
install -m 755 busybox $prefix/bin/busybox || exit 1
for i in $h ; do
appdir=`dirname $i`
mkdir -p $prefix/$appdir || exit 1
if [ "$2" = "--hardlinks" ]; then
bb_path="$prefix/bin/busybox"
else
case "$appdir" in
/)
bb_path="bin/busybox"
;;
/bin)
bb_path="busybox"
;;
/sbin)
bb_path="../bin/busybox"
;;
/usr/bin|/usr/sbin)
bb_path="../../bin/busybox"
;;
*)
echo "Unknown installation directory: $appdir"
exit 1
;;
esac
fi
if [ "$noclobber" = "0" ] || [ ! -e "$prefix$i" ]; then
echo " $prefix$i -> $bb_path"
ln $linkopts $bb_path $prefix$i || exit 1
else
echo " $prefix$i already exists"
fi
done
exit 0

17
applets/usage.c Normal file
View File

@ -0,0 +1,17 @@
/* vi: set sw=4 ts=4: */
#include <unistd.h>
#include "../include/autoconf.h"
#include "../include/busybox.h"
static const char usage_messages[] =
#define MAKE_USAGE
#include "usage.h"
#include "applets.h"
;
int main(void)
{
write(1, usage_messages, sizeof(usage_messages));
return 0;
}

19
applets/usage_compressed Executable file
View File

@ -0,0 +1,19 @@
#!/bin/sh
target="$1"
loc="$2"
test "$target" || exit 1
test "$loc" || loc=.
test -x "$loc/usage" || exit 1
test "$SED" || SED=sed
sz=`"$loc/usage" | wc -c` || exit 1
exec >"$target"
echo 'static const char packed_usage[] = '
"$loc/usage" | bzip2 -1 | od -v -t x1 \
| $SED -e 's/^[^ ]*//' -e 's/ \(..\)/\\x\1/g' -e 's/^\(.*\)$/"\1"/'
echo ';'
echo '#define SIZEOF_usage_messages' `expr 0 + $sz`

5
arch/i386/Makefile Normal file
View File

@ -0,0 +1,5 @@
# ==========================================================================
# Build system
# ==========================================================================
CFLAGS += -march=i386 -mpreferred-stack-boundary=2

300
archival/Config.in Normal file
View File

@ -0,0 +1,300 @@
#
# For a description of the syntax of this configuration file,
# see scripts/kbuild/config-language.txt.
#
menu "Archival Utilities"
config AR
bool "ar"
default n
help
ar is an archival utility program used to create, modify, and
extract contents from archives. An archive is a single file holding
a collection of other files in a structure that makes it possible to
retrieve the original individual files (called archive members).
The original files' contents, mode (permissions), timestamp, owner,
and group are preserved in the archive, and can be restored on
extraction.
The stored filename is limited to 15 characters. (for more information
see long filename support).
ar has 60 bytes of overheads for every stored file.
This implementation of ar can extract archives, it cannot create or
modify them.
On an x86 system, the ar applet adds about 1K.
Unless you have a specific application which requires ar, you should
probably say N here.
config FEATURE_AR_LONG_FILENAMES
bool "Enable support for long filenames (not need for debs)"
default n
depends on AR
help
By default the ar format can only store the first 15 characters of the
filename, this option removes that limitation.
It supports the GNU ar long filename method which moves multiple long
filenames into a the data section of a new ar entry.
config BUNZIP2
bool "bunzip2"
default n
help
bunzip2 is a compression utility using the Burrows-Wheeler block
sorting text compression algorithm, and Huffman coding. Compression
is generally considerably better than that achieved by more
conventional LZ77/LZ78-based compressors, and approaches the
performance of the PPM family of statistical compressors.
The BusyBox bunzip2 applet is limited to de-compression only.
On an x86 system, this applet adds about 11K.
Unless you have a specific application which requires bunzip2, you
should probably say N here.
config CPIO
bool "cpio"
default n
help
cpio is an archival utility program used to create, modify, and extract
contents from archives.
cpio has 110 bytes of overheads for every stored file.
This implementation of cpio can extract cpio archives created in the
"newc" or "crc" format, it cannot create or modify them.
Unless you have a specific application which requires cpio, you should
probably say N here.
config DPKG
bool "dpkg"
default n
help
dpkg is a medium-level tool to install, build, remove and manage Debian packages.
This implementation of dpkg has a number of limitations, you should use the
official dpkg if possible.
config DPKG_DEB
bool "dpkg_deb"
default n
help
dpkg-deb packs, unpacks and provides information about Debian archives.
This implementation of dpkg-deb cannot pack archives.
Unless you have a specific application which requires dpkg-deb, you should
probably say N here.
config FEATURE_DPKG_DEB_EXTRACT_ONLY
bool "extract only (-x)"
default n
depends on DPKG_DEB
help
This reduces dpkg-deb to the equivalent of "ar -p <deb> data.tar.gz | tar -zx".
However it saves space as none of the extra dpkg-deb, ar or tar options are
needed, they are linked to internally.
config GUNZIP
bool "gunzip"
default n
help
gunzip is used to decompress archives created by gzip.
You can use the `-t' option to test the integrity of
an archive, without decompressing it.
config FEATURE_GUNZIP_UNCOMPRESS
bool "Uncompress support"
default n
depends on GUNZIP
help
Enable if you want gunzip to have the ability to decompress
archives created by the program compress (not much
used anymore).
config GZIP
bool "gzip"
default n
help
gzip is used to compress files.
It's probably the most widely used UNIX compression program.
config RPM2CPIO
bool "rpm2cpio"
default n
help
Converts an RPM file into a CPIO archive.
config RPM
bool "rpm"
default n
help
Mini RPM applet - queries and extracts
config TAR
bool "tar"
default n
help
tar is an archiving program. It's commonly used with gzip to
create compressed archives. It's probably the most widely used
UNIX archive program.
config FEATURE_TAR_CREATE
bool "Enable archive creation"
default y
depends on TAR
help
If you enable this option you'll be able to create
tar archives using the `-c' option.
config FEATURE_TAR_BZIP2
bool "Enable -j option to handle .tar.bz2 files"
default n
depends on TAR
help
If you enable this option you'll be able to extract
archives compressed with bzip2.
config FEATURE_TAR_LZMA
bool "Enable -a option to handle .tar.lzma files"
default n
depends on TAR
help
If you enable this option you'll be able to extract
archives compressed with lzma.
config FEATURE_TAR_FROM
bool "Enable -X (exclude from) and -T (include from) options)"
default n
depends on TAR
help
If you enable this option you'll be able to specify
a list of files to include or exclude from an archive.
config FEATURE_TAR_GZIP
bool "Enable -z option"
default y
depends on TAR
help
If you enable this option tar will be able to call gzip,
when creating or extracting tar gziped archives.
config FEATURE_TAR_COMPRESS
bool "Enable -Z option"
default n
depends on TAR
help
If you enable this option tar will be able to call uncompress,
when extracting .tar.Z archives.
config FEATURE_TAR_OLDGNU_COMPATIBILITY
bool "Enable support for old tar header format"
default N
depends on TAR
help
This option is required to unpack archives created in
the old GNU format; help to kill this old format by
repacking your ancient archives with the new format.
config FEATURE_TAR_GNU_EXTENSIONS
bool "Enable support for some GNU tar extensions"
default y
depends on TAR
help
With this option busybox supports GNU long filenames and
linknames.
config FEATURE_TAR_LONG_OPTIONS
bool "Enable long options"
default n
depends on TAR && GETOPT_LONG
help
Enable use of long options, increases size by about 400 Bytes
config UNCOMPRESS
bool "uncompress"
default n
help
uncompress is used to decompress archives created by compress.
Not much used anymore, replaced by gzip/gunzip.
config UNLZMA
bool "unlzma"
default n
help
unlzma is a compression utility using the Lempel-Ziv-Markov chain
compression algorithm, and range coding. Compression
is generally considerably better than that achieved by the bzip2
compressors.
The BusyBox unlzma applet is limited to de-compression only.
On an x86 system, this applet adds about 4K.
Unless you have a specific application which requires unlzma, you
should probably say N here.
config FEATURE_LZMA_FAST
bool "Optimze unlzma for speed"
default n
depends on UNLZMA
help
This option reduces decompression time by about 33% at the cost of
a 2K bigger binary.
config UNZIP
bool "unzip"
default n
help
unzip will list or extract files from a ZIP archive,
commonly found on DOS/WIN systems. The default behavior
(with no options) is to extract the archive into the
current directory. Use the `-d' option to extract to a
directory of your choice.
comment "Common options for cpio and tar"
depends on CPIO || TAR
config FEATURE_UNARCHIVE_TAPE
bool "Enable tape drive support"
default n
depends on CPIO || TAR
help
I don't think this is needed anymore.
comment "Common options for dpkg and dpkg_deb"
depends on DPKG || DPKG_DEB
config FEATURE_DEB_TAR_GZ
bool "gzip debian packages (normal)"
default y if DPKG || DPKG_DEB
depends on DPKG || DPKG_DEB
help
This is the default compression method inside the debian ar file.
If you want compatibility with standard .deb's you should say yes here.
config FEATURE_DEB_TAR_BZ2
bool "bzip2 debian packages"
default n
depends on DPKG || DPKG_DEB
help
This allows dpkg and dpkg-deb to extract deb's that are compressed internally
with bzip2 instead of gzip.
You only want this if you are creating your own custom debian packages that
use an internal control.tar.bz2 or data.tar.bz2.
config FEATURE_DEB_TAR_LZMA
bool "lzma debian packages"
default n
depends on DPKG || DPKG_DEB
help
This allows dpkg and dpkg-deb to extract deb's that are compressed
internally with lzma instead of gzip.
You only want this if you are creating your own custom debian
packages that use an internal control.tar.lzma or data.tar.lzma.
endmenu

22
archival/Kbuild Normal file
View File

@ -0,0 +1,22 @@
# Makefile for busybox
#
# Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
#
# Licensed under the GPL v2, see the file LICENSE in this tarball.
libs-y += libunarchive/
lib-y:=
lib-$(CONFIG_AR) += ar.o
lib-$(CONFIG_BUNZIP2) += bunzip2.o
lib-$(CONFIG_UNLZMA) += unlzma.o
lib-$(CONFIG_CPIO) += cpio.o
lib-$(CONFIG_DPKG) += dpkg.o
lib-$(CONFIG_DPKG_DEB) += dpkg_deb.o
lib-$(CONFIG_GUNZIP) += gunzip.o
lib-$(CONFIG_GZIP) += gzip.o
lib-$(CONFIG_RPM2CPIO) += rpm2cpio.o
lib-$(CONFIG_RPM) += rpm.o
lib-$(CONFIG_TAR) += tar.o
lib-$(CONFIG_UNCOMPRESS) += uncompress.o
lib-$(CONFIG_UNZIP) += unzip.o

93
archival/ar.c Normal file
View File

@ -0,0 +1,93 @@
/* vi: set sw=4 ts=4: */
/*
* Mini ar implementation for busybox
*
* Copyright (C) 2000 by Glenn McGrath
* Written by Glenn McGrath <bug1@iinet.net.au> 1 June 2000
*
* Based in part on BusyBox tar, Debian dpkg-deb and GNU ar.
*
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
*
* There is no single standard to adhere to so ar may not portable
* between different systems
* http://www.unix-systems.org/single_unix_specification_v2/xcu/ar.html
*/
#include "busybox.h"
#include "unarchive.h"
static void header_verbose_list_ar(const file_header_t *file_header)
{
const char *mode = bb_mode_string(file_header->mode);
char *mtime;
mtime = ctime(&file_header->mtime);
mtime[16] = ' ';
memmove(&mtime[17], &mtime[20], 4);
mtime[21] = '\0';
printf("%s %d/%d%7d %s %s\n", &mode[1], file_header->uid, file_header->gid,
(int) file_header->size, &mtime[4], file_header->name);
}
#define AR_CTX_PRINT 0x01
#define AR_CTX_LIST 0x02
#define AR_CTX_EXTRACT 0x04
#define AR_OPT_PRESERVE_DATE 0x08
#define AR_OPT_VERBOSE 0x10
#define AR_OPT_CREATE 0x20
#define AR_OPT_INSERT 0x40
int ar_main(int argc, char **argv)
{
archive_handle_t *archive_handle;
unsigned opt;
static const char msg_unsupported_err[] =
"Archive %s not supported. Install binutils 'ar'.";
char magic[8];
archive_handle = init_handle();
/* Prepend '-' to the first argument if required */
opt_complementary = "--:p:t:x:-1:?:p--tx:t--px:x--pt";
opt = getopt32(argc, argv, "ptxovcr");
if (opt & AR_CTX_PRINT) {
archive_handle->action_data = data_extract_to_stdout;
}
if (opt & AR_CTX_LIST) {
archive_handle->action_header = header_list;
}
if (opt & AR_CTX_EXTRACT) {
archive_handle->action_data = data_extract_all;
}
if (opt & AR_OPT_PRESERVE_DATE) {
archive_handle->flags |= ARCHIVE_PRESERVE_DATE;
}
if (opt & AR_OPT_VERBOSE) {
archive_handle->action_header = header_verbose_list_ar;
}
if (opt & AR_OPT_CREATE) {
bb_error_msg_and_die(msg_unsupported_err, "creation");
}
if (opt & AR_OPT_INSERT) {
bb_error_msg_and_die(msg_unsupported_err, "insertion");
}
archive_handle->src_fd = xopen(argv[optind++], O_RDONLY);
while (optind < argc) {
archive_handle->filter = filter_accept_list;
llist_add_to(&(archive_handle->accept), argv[optind++]);
}
xread(archive_handle->src_fd, magic, 7);
if (strncmp(magic, "!<arch>", 7) != 0) {
bb_error_msg_and_die("invalid ar magic");
}
archive_handle->offset += 7;
while (get_header_ar(archive_handle) == EXIT_SUCCESS);
return EXIT_SUCCESS;
}

66
archival/bunzip2.c Normal file
View File

@ -0,0 +1,66 @@
/* vi: set sw=4 ts=4: */
/*
* Modified for busybox by Glenn McGrath <bug1@iinet.net.au>
* Added support output to stdout by Thomas Lundquist <thomasez@zelow.no>
*
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
*/
#include "busybox.h"
#include "unarchive.h"
#define BUNZIP2_OPT_STDOUT 1
#define BUNZIP2_OPT_FORCE 2
int bunzip2_main(int argc, char **argv)
{
USE_DESKTOP(long long) int status;
char *filename;
unsigned opt;
int src_fd, dst_fd;
opt = getopt32(argc, argv, "cf");
/* Set input filename and number */
filename = argv[optind];
if ((filename) && (filename[0] != '-') && (filename[1] != '\0')) {
/* Open input file */
src_fd = xopen(filename, O_RDONLY);
} else {
src_fd = STDIN_FILENO;
filename = 0;
}
/* if called as bzcat force the stdout flag */
if ((opt & BUNZIP2_OPT_STDOUT) || applet_name[2] == 'c')
filename = 0;
/* Check that the input is sane. */
if (isatty(src_fd) && (opt & BUNZIP2_OPT_FORCE) == 0) {
bb_error_msg_and_die("compressed data not read from terminal, "
"use -f to force it");
}
if (filename) {
struct stat stat_buf;
/* extension = filename+strlen(filename)-4 is buggy:
* strlen may be less than 4 */
char *extension = strrchr(filename, '.');
if (!extension || strcmp(extension, ".bz2") != 0) {
bb_error_msg_and_die("invalid extension");
}
xstat(filename, &stat_buf);
*extension = '\0';
dst_fd = xopen3(filename, O_WRONLY | O_CREAT | O_TRUNC,
stat_buf.st_mode);
} else dst_fd = STDOUT_FILENO;
status = uncompressStream(src_fd, dst_fd);
if (filename) {
if (status >= 0) filename[strlen(filename)] = '.';
if (unlink(filename) < 0) {
bb_error_msg_and_die("cannot remove %s", filename);
}
}
return status;
}

86
archival/cpio.c Normal file
View File

@ -0,0 +1,86 @@
/* vi: set sw=4 ts=4: */
/*
* Mini cpio implementation for busybox
*
* Copyright (C) 2001 by Glenn McGrath
*
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
*
* Limitations:
* Doesn't check CRC's
* Only supports new ASCII and CRC formats
*
*/
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "unarchive.h"
#include "busybox.h"
#define CPIO_OPT_EXTRACT 0x01
#define CPIO_OPT_TEST 0x02
#define CPIO_OPT_UNCONDITIONAL 0x04
#define CPIO_OPT_VERBOSE 0x08
#define CPIO_OPT_FILE 0x10
#define CPIO_OPT_CREATE_LEADING_DIR 0x20
#define CPIO_OPT_PRESERVE_MTIME 0x40
int cpio_main(int argc, char **argv)
{
archive_handle_t *archive_handle;
char *cpio_filename = NULL;
unsigned opt;
/* Initialise */
archive_handle = init_handle();
archive_handle->src_fd = STDIN_FILENO;
archive_handle->seek = seek_by_read;
archive_handle->flags = ARCHIVE_EXTRACT_NEWER | ARCHIVE_PRESERVE_DATE;
opt = getopt32(argc, argv, "ituvF:dm", &cpio_filename);
/* One of either extract or test options must be given */
if ((opt & (CPIO_OPT_TEST | CPIO_OPT_EXTRACT)) == 0) {
bb_show_usage();
}
if (opt & CPIO_OPT_TEST) {
/* if both extract and test options are given, ignore extract option */
if (opt & CPIO_OPT_EXTRACT) {
opt &= ~CPIO_OPT_EXTRACT;
}
archive_handle->action_header = header_list;
}
if (opt & CPIO_OPT_EXTRACT) {
archive_handle->action_data = data_extract_all;
}
if (opt & CPIO_OPT_UNCONDITIONAL) {
archive_handle->flags |= ARCHIVE_EXTRACT_UNCONDITIONAL;
archive_handle->flags &= ~ARCHIVE_EXTRACT_NEWER;
}
if (opt & CPIO_OPT_VERBOSE) {
if (archive_handle->action_header == header_list) {
archive_handle->action_header = header_verbose_list;
} else {
archive_handle->action_header = header_list;
}
}
if (cpio_filename) { /* CPIO_OPT_FILE */
archive_handle->src_fd = xopen(cpio_filename, O_RDONLY);
archive_handle->seek = seek_by_jump;
}
if (opt & CPIO_OPT_CREATE_LEADING_DIR) {
archive_handle->flags |= ARCHIVE_CREATE_LEADING_DIRS;
}
while (optind < argc) {
archive_handle->filter = filter_accept_list;
llist_add_to(&(archive_handle->accept), argv[optind]);
optind++;
}
while (get_header_cpio(archive_handle) == EXIT_SUCCESS);
return EXIT_SUCCESS;
}

1819
archival/dpkg.c Normal file

File diff suppressed because it is too large Load Diff

96
archival/dpkg_deb.c Normal file
View File

@ -0,0 +1,96 @@
/* vi: set sw=4 ts=4: */
/*
* dpkg-deb packs, unpacks and provides information about Debian archives.
*
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
*/
#include "busybox.h"
#include "unarchive.h"
#define DPKG_DEB_OPT_CONTENTS 1
#define DPKG_DEB_OPT_CONTROL 2
#define DPKG_DEB_OPT_FIELD 4
#define DPKG_DEB_OPT_EXTRACT 8
#define DPKG_DEB_OPT_EXTRACT_VERBOSE 16
int dpkg_deb_main(int argc, char **argv)
{
archive_handle_t *ar_archive;
archive_handle_t *tar_archive;
llist_t *control_tar_llist = NULL;
unsigned opt;
char *extract_dir = NULL;
short argcount = 1;
/* Setup the tar archive handle */
tar_archive = init_handle();
/* Setup an ar archive handle that refers to the gzip sub archive */
ar_archive = init_handle();
ar_archive->sub_archive = tar_archive;
ar_archive->filter = filter_accept_list_reassign;
#ifdef CONFIG_FEATURE_DEB_TAR_GZ
llist_add_to(&(ar_archive->accept), "data.tar.gz");
llist_add_to(&control_tar_llist, "control.tar.gz");
#endif
#ifdef CONFIG_FEATURE_DEB_TAR_BZ2
llist_add_to(&(ar_archive->accept), "data.tar.bz2");
llist_add_to(&control_tar_llist, "control.tar.bz2");
#endif
opt_complementary = "?c--efXx:e--cfXx:f--ceXx:X--cefx:x--cefX";
opt = getopt32(argc, argv, "cefXx");
if (opt & DPKG_DEB_OPT_CONTENTS) {
tar_archive->action_header = header_verbose_list;
}
if (opt & DPKG_DEB_OPT_CONTROL) {
ar_archive->accept = control_tar_llist;
tar_archive->action_data = data_extract_all;
if (optind + 1 == argc) {
extract_dir = "./DEBIAN";
} else {
argcount++;
}
}
if (opt & DPKG_DEB_OPT_FIELD) {
/* Print the entire control file
* it should accept a second argument which specifies a
* specific field to print */
ar_archive->accept = control_tar_llist;
llist_add_to(&(tar_archive->accept), "./control");
tar_archive->filter = filter_accept_list;
tar_archive->action_data = data_extract_to_stdout;
}
if (opt & DPKG_DEB_OPT_EXTRACT) {
tar_archive->action_header = header_list;
}
if (opt & (DPKG_DEB_OPT_EXTRACT_VERBOSE | DPKG_DEB_OPT_EXTRACT)) {
tar_archive->action_data = data_extract_all;
argcount = 2;
}
if ((optind + argcount) != argc) {
bb_show_usage();
}
tar_archive->src_fd = ar_archive->src_fd = xopen(argv[optind++], O_RDONLY);
/* Workout where to extract the files */
/* 2nd argument is a dir name */
if (argv[optind]) {
extract_dir = argv[optind];
}
if (extract_dir) {
mkdir(extract_dir, 0777); /* bb_make_directory(extract_dir, 0777, 0) */
xchdir(extract_dir);
}
unpack_ar_archive(ar_archive);
/* Cleanup */
close(ar_archive->src_fd);
return EXIT_SUCCESS;
}

162
archival/gunzip.c Normal file
View File

@ -0,0 +1,162 @@
/* vi: set sw=4 ts=4: */
/*
* Gzip implementation for busybox
*
* Based on GNU gzip v1.2.4 Copyright (C) 1992-1993 Jean-loup Gailly.
*
* Originally adjusted for busybox by Sven Rudolph <sr1@inf.tu-dresden.de>
* based on gzip sources
*
* Adjusted further by Erik Andersen <andersen@codepoet.org> to support files as
* well as stdin/stdout, and to generally behave itself wrt command line
* handling.
*
* General cleanup to better adhere to the style guide and make use of standard
* busybox functions by Glenn McGrath <bug1@iinet.net.au>
*
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
*
* gzip (GNU zip) -- compress files with zip algorithm and 'compress' interface
* Copyright (C) 1992-1993 Jean-loup Gailly
* The unzip code was written and put in the public domain by Mark Adler.
* Portions of the lzw code are derived from the public domain 'compress'
* written by Spencer Thomas, Joe Orost, James Woods, Jim McKie, Steve Davies,
* Ken Turkowski, Dave Mack and Peter Jannesen.
*
* See the license_msg below and the file COPYING for the software license.
* See the file algorithm.doc for the compression algorithms and file formats.
*/
#include "busybox.h"
#include "unarchive.h"
#define GUNZIP_OPT_STDOUT 1
#define GUNZIP_OPT_FORCE 2
#define GUNZIP_OPT_TEST 4
#define GUNZIP_OPT_DECOMPRESS 8
#define GUNZIP_OPT_VERBOSE 0x10
int gunzip_main(int argc, char **argv)
{
USE_DESKTOP(long long) int status;
int exitcode = 0;
unsigned opt;
opt = getopt32(argc, argv, "cftdv");
/* if called as zcat */
if (strcmp(applet_name, "zcat") == 0) {
opt |= GUNZIP_OPT_STDOUT;
}
do {
struct stat stat_buf;
char *old_path = argv[optind];
char *delete_path = NULL;
char *new_path = NULL;
int src_fd;
int dst_fd;
optind++;
if (old_path == NULL || strcmp(old_path, "-") == 0) {
src_fd = STDIN_FILENO;
opt |= GUNZIP_OPT_STDOUT;
USE_DESKTOP(opt &= ~GUNZIP_OPT_VERBOSE;)
optind = argc; /* we don't handle "gunzip - a.gz b.gz" */
} else {
src_fd = xopen(old_path, O_RDONLY);
/* Get the time stamp on the input file. */
fstat(src_fd, &stat_buf);
}
/* Check that the input is sane. */
if (isatty(src_fd) && !(opt & GUNZIP_OPT_FORCE)) {
bb_error_msg_and_die
("compressed data not read from terminal, use -f to force it");
}
/* Set output filename and number */
if (opt & GUNZIP_OPT_TEST) {
dst_fd = xopen(bb_dev_null, O_WRONLY); /* why does test use filenum 2 ? */
} else if (opt & GUNZIP_OPT_STDOUT) {
dst_fd = STDOUT_FILENO;
} else {
char *extension;
new_path = xstrdup(old_path);
extension = strrchr(new_path, '.');
#ifdef CONFIG_FEATURE_GUNZIP_UNCOMPRESS
if (extension && (strcmp(extension, ".Z") == 0)) {
*extension = '\0';
} else
#endif
if (extension && (strcmp(extension, ".gz") == 0)) {
*extension = '\0';
} else if (extension && (strcmp(extension, ".tgz") == 0)) {
extension[2] = 'a';
extension[3] = 'r';
} else {
// FIXME: should we die or just skip to next?
bb_error_msg_and_die("invalid extension");
}
/* Open output file (with correct permissions) */
dst_fd = xopen3(new_path, O_WRONLY | O_CREAT | O_TRUNC,
stat_buf.st_mode);
/* If unzip succeeds remove the old file */
delete_path = old_path;
}
status = -1;
/* do the decompression, and cleanup */
if (xread_char(src_fd) == 0x1f) {
unsigned char magic2;
magic2 = xread_char(src_fd);
if (ENABLE_FEATURE_GUNZIP_UNCOMPRESS && magic2 == 0x9d) {
status = uncompress(src_fd, dst_fd);
} else if (magic2 == 0x8b) {
check_header_gzip(src_fd); // FIXME: xfunc? _or_die?
status = inflate_gunzip(src_fd, dst_fd);
} else {
bb_error_msg("invalid magic");
exitcode = 1;
}
if (status < 0) {
bb_error_msg("error inflating");
exitcode = 1;
}
else if (ENABLE_DESKTOP && (opt & GUNZIP_OPT_VERBOSE)) {
fprintf(stderr, "%s: %u%% - replaced with %s\n",
old_path, (unsigned)(stat_buf.st_size*100 / (status+1)), new_path);
}
} else {
bb_error_msg("invalid magic");
exitcode = 1;
}
if (status < 0 && new_path) {
/* Unzip failed, remove new path instead of old path */
delete_path = new_path;
}
if (dst_fd != STDOUT_FILENO) {
close(dst_fd);
}
if (src_fd != STDIN_FILENO) {
close(src_fd);
}
/* delete_path will be NULL if in test mode or from stdin */
if (delete_path && (unlink(delete_path) == -1)) {
bb_error_msg("cannot remove %s", delete_path);
exitcode = 1;
}
free(new_path);
} while (optind < argc);
return exitcode;
}

2473
archival/gzip.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,59 @@
# Makefile for busybox
#
# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
#
# Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
lib-y:= \
\
data_skip.o \
data_extract_all.o \
data_extract_to_stdout.o \
data_extract_to_buffer.o \
\
filter_accept_all.o \
filter_accept_list.o \
filter_accept_reject_list.o \
\
header_skip.o \
header_list.o \
header_verbose_list.o \
\
archive_xread_all_eof.o \
\
seek_by_read.o \
seek_by_jump.o \
\
data_align.o \
find_list_entry.o \
open_transformer.o \
init_handle.o
GUNZIP_FILES:= check_header_gzip.o decompress_unzip.o
DPKG_FILES:= \
get_header_ar.o \
unpack_ar_archive.o \
get_header_tar.o \
filter_accept_list_reassign.o
lib-$(CONFIG_AR) += get_header_ar.o unpack_ar_archive.o
lib-$(CONFIG_BUNZIP2) += decompress_bunzip2.o
lib-$(CONFIG_UNLZMA) += decompress_unlzma.o
lib-$(CONFIG_CPIO) += get_header_cpio.o
lib-$(CONFIG_DPKG) += $(DPKG_FILES)
lib-$(CONFIG_DPKG_DEB) += $(DPKG_FILES)
lib-$(CONFIG_FEATURE_DEB_TAR_GZ) += $(GUNZIP_FILES) get_header_tar_gz.o
lib-$(CONFIG_FEATURE_DEB_TAR_BZ2) += decompress_bunzip2.o get_header_tar_bz2.o
lib-$(CONFIG_FEATURE_DEB_TAR_LZMA) += decompress_unlzma.o get_header_tar_lzma.o
lib-$(CONFIG_GUNZIP) += $(GUNZIP_FILES)
lib-$(CONFIG_FEATURE_GUNZIP_UNCOMPRESS) += decompress_uncompress.o
lib-$(CONFIG_RPM2CPIO) += $(GUNZIP_FILES) get_header_cpio.o
lib-$(CONFIG_RPM) += $(GUNZIP_FILES) get_header_cpio.o
lib-$(CONFIG_TAR) += get_header_tar.o
lib-$(CONFIG_FEATURE_TAR_BZIP2) += decompress_bunzip2.o get_header_tar_bz2.o
lib-$(CONFIG_FEATURE_TAR_LZMA) += decompress_unlzma.o get_header_tar_lzma.o
lib-$(CONFIG_FEATURE_TAR_GZIP) += $(GUNZIP_FILES) get_header_tar_gz.o
lib-$(CONFIG_FEATURE_TAR_COMPRESS) += decompress_uncompress.o
lib-$(CONFIG_UNCOMPRESS) += decompress_uncompress.o
lib-$(CONFIG_UNZIP) += $(GUNZIP_FILES)
lib-$(CONFIG_FEATURE_COMPRESS_USAGE) += decompress_bunzip2.o

View File

@ -0,0 +1,20 @@
/* vi: set sw=4 ts=4: */
/*
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
*/
#include "unarchive.h"
#include "libbb.h"
ssize_t archive_xread_all_eof(archive_handle_t *archive_handle,
unsigned char *buf, size_t count)
{
ssize_t size;
size = full_read(archive_handle->src_fd, buf, count);
if (size != 0 && size != count) {
bb_error_msg_and_die("short read: %u of %u",
(unsigned)size, (unsigned)count);
}
return size;
}

View File

@ -0,0 +1,62 @@
/* vi: set sw=4 ts=4: */
/*
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
*/
#include <stdlib.h>
#include <unistd.h>
#include "libbb.h"
#include "unarchive.h" /* for external decl of check_header_gzip */
void check_header_gzip(int src_fd)
{
union {
unsigned char raw[8];
struct {
unsigned char method;
unsigned char flags;
unsigned int mtime;
unsigned char xtra_flags;
unsigned char os_flags;
} formatted;
} header;
xread(src_fd, header.raw, 8);
/* Check the compression method */
if (header.formatted.method != 8) {
bb_error_msg_and_die("unknown compression method %d",
header.formatted.method);
}
if (header.formatted.flags & 0x04) {
/* bit 2 set: extra field present */
unsigned char extra_short;
extra_short = xread_char(src_fd) + (xread_char(src_fd) << 8);
while (extra_short > 0) {
/* Ignore extra field */
xread_char(src_fd);
extra_short--;
}
}
/* Discard original name if any */
if (header.formatted.flags & 0x08) {
/* bit 3 set: original file name present */
while(xread_char(src_fd) != 0);
}
/* Discard file comment if any */
if (header.formatted.flags & 0x10) {
/* bit 4 set: file comment present */
while(xread_char(src_fd) != 0);
}
/* Read the header checksum */
if (header.formatted.flags & 0x02) {
xread_char(src_fd);
xread_char(src_fd);
}
return;
}

View File

@ -0,0 +1,22 @@
/* vi: set sw=4 ts=4: */
/*
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
*/
#include <sys/types.h>
#include <errno.h>
#include <unistd.h>
#include "libbb.h"
#include "unarchive.h"
void data_align(archive_handle_t *archive_handle, const unsigned short boundary)
{
const unsigned short skip_amount = (boundary - (archive_handle->offset % boundary)) % boundary;
archive_handle->seek(archive_handle, skip_amount);
archive_handle->offset += skip_amount;
return;
}

View File

@ -0,0 +1,119 @@
/* vi: set sw=4 ts=4: */
/*
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
*/
#include "libbb.h"
#include "unarchive.h"
void data_extract_all(archive_handle_t *archive_handle)
{
file_header_t *file_header = archive_handle->file_header;
int dst_fd;
int res;
if (archive_handle->flags & ARCHIVE_CREATE_LEADING_DIRS) {
char *name = xstrdup(file_header->name);
bb_make_directory(dirname(name), -1, FILEUTILS_RECUR);
free(name);
}
/* Check if the file already exists */
if (archive_handle->flags & ARCHIVE_EXTRACT_UNCONDITIONAL) {
/* Remove the existing entry if it exists */
if (((file_header->mode & S_IFMT) != S_IFDIR)
&& (unlink(file_header->name) == -1)
&& (errno != ENOENT)
) {
bb_perror_msg_and_die("cannot remove old file");
}
}
else if (archive_handle->flags & ARCHIVE_EXTRACT_NEWER) {
/* Remove the existing entry if its older than the extracted entry */
struct stat statbuf;
if (lstat(file_header->name, &statbuf) == -1) {
if (errno != ENOENT) {
bb_perror_msg_and_die("cannot stat old file");
}
}
else if (statbuf.st_mtime <= file_header->mtime) {
if (!(archive_handle->flags & ARCHIVE_EXTRACT_QUIET)) {
bb_error_msg("%s not created: newer or "
"same age file exists", file_header->name);
}
data_skip(archive_handle);
return;
}
else if ((unlink(file_header->name) == -1) && (errno != EISDIR)) {
bb_perror_msg_and_die("cannot remove old file %s",
file_header->name);
}
}
/* Handle hard links separately
* We identified hard links as regular files of size 0 with a symlink */
if (S_ISREG(file_header->mode) && (file_header->link_name)
&& (file_header->size == 0)
) {
/* hard link */
res = link(file_header->link_name, file_header->name);
if ((res == -1) && !(archive_handle->flags & ARCHIVE_EXTRACT_QUIET)) {
bb_perror_msg("cannot create hard link");
}
} else {
/* Create the filesystem entry */
switch (file_header->mode & S_IFMT) {
case S_IFREG: {
/* Regular file */
dst_fd = xopen3(file_header->name, O_WRONLY | O_CREAT | O_EXCL,
file_header->mode);
bb_copyfd_size(archive_handle->src_fd, dst_fd, file_header->size);
close(dst_fd);
break;
}
case S_IFDIR:
res = mkdir(file_header->name, file_header->mode);
if ((errno != EISDIR) && (res == -1)
&& !(archive_handle->flags & ARCHIVE_EXTRACT_QUIET)
) {
bb_perror_msg("extract_archive: %s", file_header->name);
}
break;
case S_IFLNK:
/* Symlink */
res = symlink(file_header->link_name, file_header->name);
if ((res == -1)
&& !(archive_handle->flags & ARCHIVE_EXTRACT_QUIET)
) {
bb_perror_msg("cannot create symlink "
"from %s to '%s'",
file_header->name,
file_header->link_name);
}
break;
case S_IFSOCK:
case S_IFBLK:
case S_IFCHR:
case S_IFIFO:
res = mknod(file_header->name, file_header->mode, file_header->device);
if ((res == -1)
&& !(archive_handle->flags & ARCHIVE_EXTRACT_QUIET)
) {
bb_perror_msg("cannot create node %s", file_header->name);
}
break;
default:
bb_error_msg_and_die("unrecognized file type");
}
}
if (!(archive_handle->flags & ARCHIVE_NOPRESERVE_OWN)) {
lchown(file_header->name, file_header->uid, file_header->gid);
}
if (archive_handle->flags & ARCHIVE_PRESERVE_DATE) {
struct utimbuf t;
t.actime = t.modtime = file_header->mtime;
utime(file_header->name, &t);
}
}

View File

@ -0,0 +1,18 @@
/* vi: set sw=4 ts=4: */
/*
* Copyright 2002 Glenn McGrath
*
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
*/
#include "libbb.h"
#include "unarchive.h"
void data_extract_to_buffer(archive_handle_t *archive_handle)
{
const unsigned int size = archive_handle->file_header->size;
archive_handle->buffer = xzalloc(size + 1);
xread(archive_handle->src_fd, archive_handle->buffer, size);
}

View File

@ -0,0 +1,12 @@
/* vi: set sw=4 ts=4: */
/*
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
*/
#include "unarchive.h"
#include <unistd.h>
void data_extract_to_stdout(archive_handle_t *archive_handle)
{
bb_copyfd_size(archive_handle->src_fd, STDOUT_FILENO, archive_handle->file_header->size);
}

View File

@ -0,0 +1,16 @@
/* vi: set sw=4 ts=4: */
/*
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
*/
#include <sys/types.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include "unarchive.h"
#include "libbb.h"
void data_skip(archive_handle_t *archive_handle)
{
archive_handle->seek(archive_handle, archive_handle->file_header->size);
}

View File

@ -0,0 +1,731 @@
/* vi: set sw=4 ts=4: */
/* Small bzip2 deflate implementation, by Rob Landley (rob@landley.net).
Based on bzip2 decompression code by Julian R Seward (jseward@acm.org),
which also acknowledges contributions by Mike Burrows, David Wheeler,
Peter Fenwick, Alistair Moffat, Radford Neal, Ian H. Witten,
Robert Sedgewick, and Jon L. Bentley.
Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
*/
/*
Size and speed optimizations by Manuel Novoa III (mjn3@codepoet.org).
More efficient reading of Huffman codes, a streamlined read_bunzip()
function, and various other tweaks. In (limited) tests, approximately
20% faster than bzcat on x86 and about 10% faster on arm.
Note that about 2/3 of the time is spent in read_unzip() reversing
the Burrows-Wheeler transformation. Much of that time is delay
resulting from cache misses.
I would ask that anyone benefiting from this work, especially those
using it in commercial products, consider making a donation to my local
non-profit hospice organization (www.hospiceacadiana.com) in the name of
the woman I loved, Toni W. Hagan, who passed away Feb. 12, 2003.
Manuel
*/
#include "libbb.h"
#include "unarchive.h"
/* Constants for Huffman coding */
#define MAX_GROUPS 6
#define GROUP_SIZE 50 /* 64 would have been more efficient */
#define MAX_HUFCODE_BITS 20 /* Longest Huffman code allowed */
#define MAX_SYMBOLS 258 /* 256 literals + RUNA + RUNB */
#define SYMBOL_RUNA 0
#define SYMBOL_RUNB 1
/* Status return values */
#define RETVAL_OK 0
#define RETVAL_LAST_BLOCK (-1)
#define RETVAL_NOT_BZIP_DATA (-2)
#define RETVAL_UNEXPECTED_INPUT_EOF (-3)
#define RETVAL_UNEXPECTED_OUTPUT_EOF (-4)
#define RETVAL_DATA_ERROR (-5)
#define RETVAL_OUT_OF_MEMORY (-6)
#define RETVAL_OBSOLETE_INPUT (-7)
/* Other housekeeping constants */
#define IOBUF_SIZE 4096
/* This is what we know about each Huffman coding group */
struct group_data {
/* We have an extra slot at the end of limit[] for a sentinal value. */
int limit[MAX_HUFCODE_BITS+1],base[MAX_HUFCODE_BITS],permute[MAX_SYMBOLS];
int minLen, maxLen;
};
/* Structure holding all the housekeeping data, including IO buffers and
memory that persists between calls to bunzip */
typedef struct {
/* State for interrupting output loop */
int writeCopies,writePos,writeRunCountdown,writeCount,writeCurrent;
/* I/O tracking data (file handles, buffers, positions, etc.) */
int in_fd,out_fd,inbufCount,inbufPos /*,outbufPos*/;
unsigned char *inbuf /*,*outbuf*/;
unsigned int inbufBitCount, inbufBits;
/* The CRC values stored in the block header and calculated from the data */
uint32_t headerCRC, totalCRC, writeCRC;
uint32_t *crc32Table;
/* Intermediate buffer and its size (in bytes) */
unsigned int *dbuf, dbufSize;
/* These things are a bit too big to go on the stack */
unsigned char selectors[32768]; /* nSelectors=15 bits */
struct group_data groups[MAX_GROUPS]; /* Huffman coding tables */
/* For I/O error handling */
jmp_buf jmpbuf;
} bunzip_data;
/* Return the next nnn bits of input. All reads from the compressed input
are done through this function. All reads are big endian */
static unsigned int get_bits(bunzip_data *bd, char bits_wanted)
{
unsigned int bits=0;
/* If we need to get more data from the byte buffer, do so. (Loop getting
one byte at a time to enforce endianness and avoid unaligned access.) */
while (bd->inbufBitCount<bits_wanted) {
/* If we need to read more data from file into byte buffer, do so */
if(bd->inbufPos==bd->inbufCount) {
if((bd->inbufCount = read(bd->in_fd, bd->inbuf, IOBUF_SIZE)) <= 0)
longjmp(bd->jmpbuf,RETVAL_UNEXPECTED_INPUT_EOF);
bd->inbufPos=0;
}
/* Avoid 32-bit overflow (dump bit buffer to top of output) */
if(bd->inbufBitCount>=24) {
bits=bd->inbufBits&((1<<bd->inbufBitCount)-1);
bits_wanted-=bd->inbufBitCount;
bits<<=bits_wanted;
bd->inbufBitCount=0;
}
/* Grab next 8 bits of input from buffer. */
bd->inbufBits=(bd->inbufBits<<8)|bd->inbuf[bd->inbufPos++];
bd->inbufBitCount+=8;
}
/* Calculate result */
bd->inbufBitCount-=bits_wanted;
bits|=(bd->inbufBits>>bd->inbufBitCount)&((1<<bits_wanted)-1);
return bits;
}
/* Unpacks the next block and sets up for the inverse burrows-wheeler step. */
static int get_next_block(bunzip_data *bd)
{
struct group_data *hufGroup;
int dbufCount,nextSym,dbufSize,groupCount,*base,*limit,selector,
i,j,k,t,runPos,symCount,symTotal,nSelectors,byteCount[256];
unsigned char uc, symToByte[256], mtfSymbol[256], *selectors;
unsigned int *dbuf,origPtr;
dbuf=bd->dbuf;
dbufSize=bd->dbufSize;
selectors=bd->selectors;
/* Reset longjmp I/O error handling */
i=setjmp(bd->jmpbuf);
if(i) return i;
/* Read in header signature and CRC, then validate signature.
(last block signature means CRC is for whole file, return now) */
i = get_bits(bd,24);
j = get_bits(bd,24);
bd->headerCRC=get_bits(bd,32);
if ((i == 0x177245) && (j == 0x385090)) return RETVAL_LAST_BLOCK;
if ((i != 0x314159) || (j != 0x265359)) return RETVAL_NOT_BZIP_DATA;
/* We can add support for blockRandomised if anybody complains. There was
some code for this in busybox 1.0.0-pre3, but nobody ever noticed that
it didn't actually work. */
if(get_bits(bd,1)) return RETVAL_OBSOLETE_INPUT;
if((origPtr=get_bits(bd,24)) > dbufSize) return RETVAL_DATA_ERROR;
/* mapping table: if some byte values are never used (encoding things
like ascii text), the compression code removes the gaps to have fewer
symbols to deal with, and writes a sparse bitfield indicating which
values were present. We make a translation table to convert the symbols
back to the corresponding bytes. */
t=get_bits(bd, 16);
symTotal=0;
for (i=0;i<16;i++) {
if(t&(1<<(15-i))) {
k=get_bits(bd,16);
for(j=0;j<16;j++)
if(k&(1<<(15-j))) symToByte[symTotal++]=(16*i)+j;
}
}
/* How many different Huffman coding groups does this block use? */
groupCount=get_bits(bd,3);
if (groupCount<2 || groupCount>MAX_GROUPS) return RETVAL_DATA_ERROR;
/* nSelectors: Every GROUP_SIZE many symbols we select a new Huffman coding
group. Read in the group selector list, which is stored as MTF encoded
bit runs. (MTF=Move To Front, as each value is used it's moved to the
start of the list.) */
if(!(nSelectors=get_bits(bd, 15))) return RETVAL_DATA_ERROR;
for(i=0; i<groupCount; i++) mtfSymbol[i] = i;
for(i=0; i<nSelectors; i++) {
/* Get next value */
for(j=0;get_bits(bd,1);j++) if (j>=groupCount) return RETVAL_DATA_ERROR;
/* Decode MTF to get the next selector */
uc = mtfSymbol[j];
for(;j;j--) mtfSymbol[j] = mtfSymbol[j-1];
mtfSymbol[0]=selectors[i]=uc;
}
/* Read the Huffman coding tables for each group, which code for symTotal
literal symbols, plus two run symbols (RUNA, RUNB) */
symCount=symTotal+2;
for (j=0; j<groupCount; j++) {
unsigned char length[MAX_SYMBOLS],temp[MAX_HUFCODE_BITS+1];
int minLen, maxLen, pp;
/* Read Huffman code lengths for each symbol. They're stored in
a way similar to mtf; record a starting value for the first symbol,
and an offset from the previous value for everys symbol after that.
(Subtracting 1 before the loop and then adding it back at the end is
an optimization that makes the test inside the loop simpler: symbol
length 0 becomes negative, so an unsigned inequality catches it.) */
t=get_bits(bd, 5)-1;
for (i = 0; i < symCount; i++) {
for(;;) {
if (((unsigned)t) > (MAX_HUFCODE_BITS-1))
return RETVAL_DATA_ERROR;
/* If first bit is 0, stop. Else second bit indicates whether
to increment or decrement the value. Optimization: grab 2
bits and unget the second if the first was 0. */
k = get_bits(bd,2);
if (k < 2) {
bd->inbufBitCount++;
break;
}
/* Add one if second bit 1, else subtract 1. Avoids if/else */
t+=(((k+1)&2)-1);
}
/* Correct for the initial -1, to get the final symbol length */
length[i]=t+1;
}
/* Find largest and smallest lengths in this group */
minLen=maxLen=length[0];
for(i = 1; i < symCount; i++) {
if(length[i] > maxLen) maxLen = length[i];
else if(length[i] < minLen) minLen = length[i];
}
/* Calculate permute[], base[], and limit[] tables from length[].
*
* permute[] is the lookup table for converting Huffman coded symbols
* into decoded symbols. base[] is the amount to subtract from the
* value of a Huffman symbol of a given length when using permute[].
*
* limit[] indicates the largest numerical value a symbol with a given
* number of bits can have. This is how the Huffman codes can vary in
* length: each code with a value>limit[length] needs another bit.
*/
hufGroup=bd->groups+j;
hufGroup->minLen = minLen;
hufGroup->maxLen = maxLen;
/* Note that minLen can't be smaller than 1, so we adjust the base
and limit array pointers so we're not always wasting the first
entry. We do this again when using them (during symbol decoding).*/
base=hufGroup->base-1;
limit=hufGroup->limit-1;
/* Calculate permute[]. Concurently, initialize temp[] and limit[]. */
pp=0;
for(i=minLen;i<=maxLen;i++) {
temp[i]=limit[i]=0;
for(t=0;t<symCount;t++)
if(length[t]==i) hufGroup->permute[pp++] = t;
}
/* Count symbols coded for at each bit length */
for (i=0;i<symCount;i++) temp[length[i]]++;
/* Calculate limit[] (the largest symbol-coding value at each bit
* length, which is (previous limit<<1)+symbols at this level), and
* base[] (number of symbols to ignore at each bit length, which is
* limit minus the cumulative count of symbols coded for already). */
pp=t=0;
for (i=minLen; i<maxLen; i++) {
pp+=temp[i];
/* We read the largest possible symbol size and then unget bits
after determining how many we need, and those extra bits could
be set to anything. (They're noise from future symbols.) At
each level we're really only interested in the first few bits,
so here we set all the trailing to-be-ignored bits to 1 so they
don't affect the value>limit[length] comparison. */
limit[i]= (pp << (maxLen - i)) - 1;
pp<<=1;
base[i+1]=pp-(t+=temp[i]);
}
limit[maxLen+1] = INT_MAX; /* Sentinal value for reading next sym. */
limit[maxLen]=pp+temp[maxLen]-1;
base[minLen]=0;
}
/* We've finished reading and digesting the block header. Now read this
block's Huffman coded symbols from the file and undo the Huffman coding
and run length encoding, saving the result into dbuf[dbufCount++]=uc */
/* Initialize symbol occurrence counters and symbol Move To Front table */
for(i=0;i<256;i++) {
byteCount[i] = 0;
mtfSymbol[i]=(unsigned char)i;
}
/* Loop through compressed symbols. */
runPos=dbufCount=selector=0;
for(;;) {
/* fetch next Huffman coding group from list. */
symCount=GROUP_SIZE-1;
if(selector>=nSelectors) return RETVAL_DATA_ERROR;
hufGroup=bd->groups+selectors[selector++];
base=hufGroup->base-1;
limit=hufGroup->limit-1;
continue_this_group:
/* Read next Huffman-coded symbol. */
/* Note: It is far cheaper to read maxLen bits and back up than it is
to read minLen bits and then an additional bit at a time, testing
as we go. Because there is a trailing last block (with file CRC),
there is no danger of the overread causing an unexpected EOF for a
valid compressed file. As a further optimization, we do the read
inline (falling back to a call to get_bits if the buffer runs
dry). The following (up to got_huff_bits:) is equivalent to
j=get_bits(bd,hufGroup->maxLen);
*/
while (bd->inbufBitCount<hufGroup->maxLen) {
if(bd->inbufPos==bd->inbufCount) {
j = get_bits(bd,hufGroup->maxLen);
goto got_huff_bits;
}
bd->inbufBits=(bd->inbufBits<<8)|bd->inbuf[bd->inbufPos++];
bd->inbufBitCount+=8;
};
bd->inbufBitCount-=hufGroup->maxLen;
j = (bd->inbufBits>>bd->inbufBitCount)&((1<<hufGroup->maxLen)-1);
got_huff_bits:
/* Figure how how many bits are in next symbol and unget extras */
i=hufGroup->minLen;
while(j>limit[i]) ++i;
bd->inbufBitCount += (hufGroup->maxLen - i);
/* Huffman decode value to get nextSym (with bounds checking) */
if ((i > hufGroup->maxLen)
|| (((unsigned)(j=(j>>(hufGroup->maxLen-i))-base[i]))
>= MAX_SYMBOLS))
return RETVAL_DATA_ERROR;
nextSym = hufGroup->permute[j];
/* We have now decoded the symbol, which indicates either a new literal
byte, or a repeated run of the most recent literal byte. First,
check if nextSym indicates a repeated run, and if so loop collecting
how many times to repeat the last literal. */
if (((unsigned)nextSym) <= SYMBOL_RUNB) { /* RUNA or RUNB */
/* If this is the start of a new run, zero out counter */
if(!runPos) {
runPos = 1;
t = 0;
}
/* Neat trick that saves 1 symbol: instead of or-ing 0 or 1 at
each bit position, add 1 or 2 instead. For example,
1011 is 1<<0 + 1<<1 + 2<<2. 1010 is 2<<0 + 2<<1 + 1<<2.
You can make any bit pattern that way using 1 less symbol than
the basic or 0/1 method (except all bits 0, which would use no
symbols, but a run of length 0 doesn't mean anything in this
context). Thus space is saved. */
t += (runPos << nextSym); /* +runPos if RUNA; +2*runPos if RUNB */
if(runPos < dbufSize) runPos <<= 1;
goto end_of_huffman_loop;
}
/* When we hit the first non-run symbol after a run, we now know
how many times to repeat the last literal, so append that many
copies to our buffer of decoded symbols (dbuf) now. (The last
literal used is the one at the head of the mtfSymbol array.) */
if(runPos) {
runPos=0;
if(dbufCount+t>=dbufSize) return RETVAL_DATA_ERROR;
uc = symToByte[mtfSymbol[0]];
byteCount[uc] += t;
while(t--) dbuf[dbufCount++]=uc;
}
/* Is this the terminating symbol? */
if(nextSym>symTotal) break;
/* At this point, nextSym indicates a new literal character. Subtract
one to get the position in the MTF array at which this literal is
currently to be found. (Note that the result can't be -1 or 0,
because 0 and 1 are RUNA and RUNB. But another instance of the
first symbol in the mtf array, position 0, would have been handled
as part of a run above. Therefore 1 unused mtf position minus
2 non-literal nextSym values equals -1.) */
if(dbufCount>=dbufSize) return RETVAL_DATA_ERROR;
i = nextSym - 1;
uc = mtfSymbol[i];
/* Adjust the MTF array. Since we typically expect to move only a
* small number of symbols, and are bound by 256 in any case, using
* memmove here would typically be bigger and slower due to function
* call overhead and other assorted setup costs. */
do {
mtfSymbol[i] = mtfSymbol[i-1];
} while (--i);
mtfSymbol[0] = uc;
uc=symToByte[uc];
/* We have our literal byte. Save it into dbuf. */
byteCount[uc]++;
dbuf[dbufCount++] = (unsigned int)uc;
/* Skip group initialization if we're not done with this group. Done
* this way to avoid compiler warning. */
end_of_huffman_loop:
if(symCount--) goto continue_this_group;
}
/* At this point, we've read all the Huffman-coded symbols (and repeated
runs) for this block from the input stream, and decoded them into the
intermediate buffer. There are dbufCount many decoded bytes in dbuf[].
Now undo the Burrows-Wheeler transform on dbuf.
See http://dogma.net/markn/articles/bwt/bwt.htm
*/
/* Turn byteCount into cumulative occurrence counts of 0 to n-1. */
j=0;
for(i=0;i<256;i++) {
k=j+byteCount[i];
byteCount[i] = j;
j=k;
}
/* Figure out what order dbuf would be in if we sorted it. */
for (i=0;i<dbufCount;i++) {
uc=(unsigned char)(dbuf[i] & 0xff);
dbuf[byteCount[uc]] |= (i << 8);
byteCount[uc]++;
}
/* Decode first byte by hand to initialize "previous" byte. Note that it
doesn't get output, and if the first three characters are identical
it doesn't qualify as a run (hence writeRunCountdown=5). */
if(dbufCount) {
if(origPtr>=dbufCount) return RETVAL_DATA_ERROR;
bd->writePos=dbuf[origPtr];
bd->writeCurrent=(unsigned char)(bd->writePos&0xff);
bd->writePos>>=8;
bd->writeRunCountdown=5;
}
bd->writeCount=dbufCount;
return RETVAL_OK;
}
/* Undo burrows-wheeler transform on intermediate buffer to produce output.
If start_bunzip was initialized with out_fd=-1, then up to len bytes of
data are written to outbuf. Return value is number of bytes written or
error (all errors are negative numbers). If out_fd!=-1, outbuf and len
are ignored, data is written to out_fd and return is RETVAL_OK or error.
*/
static int read_bunzip(bunzip_data *bd, char *outbuf, int len)
{
const unsigned int *dbuf;
int pos,current,previous,gotcount;
/* If last read was short due to end of file, return last block now */
if(bd->writeCount<0) return bd->writeCount;
gotcount = 0;
dbuf=bd->dbuf;
pos=bd->writePos;
current=bd->writeCurrent;
/* We will always have pending decoded data to write into the output
buffer unless this is the very first call (in which case we haven't
Huffman-decoded a block into the intermediate buffer yet). */
if (bd->writeCopies) {
/* Inside the loop, writeCopies means extra copies (beyond 1) */
--bd->writeCopies;
/* Loop outputting bytes */
for(;;) {
/* If the output buffer is full, snapshot state and return */
if(gotcount >= len) {
bd->writePos=pos;
bd->writeCurrent=current;
bd->writeCopies++;
return len;
}
/* Write next byte into output buffer, updating CRC */
outbuf[gotcount++] = current;
bd->writeCRC=(((bd->writeCRC)<<8)
^bd->crc32Table[((bd->writeCRC)>>24)^current]);
/* Loop now if we're outputting multiple copies of this byte */
if (bd->writeCopies) {
--bd->writeCopies;
continue;
}
decode_next_byte:
if (!bd->writeCount--) break;
/* Follow sequence vector to undo Burrows-Wheeler transform */
previous=current;
pos=dbuf[pos];
current=pos&0xff;
pos>>=8;
/* After 3 consecutive copies of the same byte, the 4th is a repeat
count. We count down from 4 instead
* of counting up because testing for non-zero is faster */
if(--bd->writeRunCountdown) {
if(current!=previous) bd->writeRunCountdown=4;
} else {
/* We have a repeated run, this byte indicates the count */
bd->writeCopies=current;
current=previous;
bd->writeRunCountdown=5;
/* Sometimes there are just 3 bytes (run length 0) */
if(!bd->writeCopies) goto decode_next_byte;
/* Subtract the 1 copy we'd output anyway to get extras */
--bd->writeCopies;
}
}
/* Decompression of this block completed successfully */
bd->writeCRC=~bd->writeCRC;
bd->totalCRC=((bd->totalCRC<<1) | (bd->totalCRC>>31)) ^ bd->writeCRC;
/* If this block had a CRC error, force file level CRC error. */
if(bd->writeCRC!=bd->headerCRC) {
bd->totalCRC=bd->headerCRC+1;
return RETVAL_LAST_BLOCK;
}
}
/* Refill the intermediate buffer by Huffman-decoding next block of input */
/* (previous is just a convenient unused temp variable here) */
previous=get_next_block(bd);
if(previous) {
bd->writeCount=previous;
return (previous!=RETVAL_LAST_BLOCK) ? previous : gotcount;
}
bd->writeCRC=~0;
pos=bd->writePos;
current=bd->writeCurrent;
goto decode_next_byte;
}
/* Allocate the structure, read file header. If in_fd==-1, inbuf must contain
a complete bunzip file (len bytes long). If in_fd!=-1, inbuf and len are
ignored, and data is read from file handle into temporary buffer. */
static int start_bunzip(bunzip_data **bdp, int in_fd, unsigned char *inbuf,
int len)
{
bunzip_data *bd;
unsigned int i;
const unsigned int BZh0=(((unsigned int)'B')<<24)+(((unsigned int)'Z')<<16)
+(((unsigned int)'h')<<8)+(unsigned int)'0';
/* Figure out how much data to allocate */
i=sizeof(bunzip_data);
if(in_fd!=-1) i+=IOBUF_SIZE;
/* Allocate bunzip_data. Most fields initialize to zero. */
bd=*bdp=xzalloc(i);
/* Setup input buffer */
if(-1==(bd->in_fd=in_fd)) {
bd->inbuf=inbuf;
bd->inbufCount=len;
} else bd->inbuf=(unsigned char *)(bd+1);
/* Init the CRC32 table (big endian) */
bd->crc32Table = crc32_filltable(1);
/* Setup for I/O error handling via longjmp */
i=setjmp(bd->jmpbuf);
if(i) return i;
/* Ensure that file starts with "BZh['1'-'9']." */
i = get_bits(bd,32);
if (((unsigned int)(i-BZh0-1)) >= 9) return RETVAL_NOT_BZIP_DATA;
/* Fourth byte (ascii '1'-'9'), indicates block size in units of 100k of
uncompressed data. Allocate intermediate buffer for block. */
bd->dbufSize=100000*(i-BZh0);
bd->dbuf=xmalloc(bd->dbufSize * sizeof(int));
return RETVAL_OK;
}
/* Example usage: decompress src_fd to dst_fd. (Stops at end of bzip data,
not end of file.) */
USE_DESKTOP(long long) int
uncompressStream(int src_fd, int dst_fd)
{
USE_DESKTOP(long long total_written = 0;)
char *outbuf;
bunzip_data *bd;
int i;
outbuf=xmalloc(IOBUF_SIZE);
i=start_bunzip(&bd,src_fd,0,0);
if(!i) {
for(;;) {
if((i=read_bunzip(bd,outbuf,IOBUF_SIZE)) <= 0) break;
if(i!=write(dst_fd,outbuf,i)) {
i=RETVAL_UNEXPECTED_OUTPUT_EOF;
break;
}
USE_DESKTOP(total_written += i;)
}
}
/* Check CRC and release memory */
if(i==RETVAL_LAST_BLOCK) {
if (bd->headerCRC!=bd->totalCRC) {
bb_error_msg("data integrity error when decompressing");
} else {
i=RETVAL_OK;
}
} else if (i==RETVAL_UNEXPECTED_OUTPUT_EOF) {
bb_error_msg("compressed file ends unexpectedly");
} else {
bb_error_msg("decompression failed");
}
free(bd->dbuf);
free(bd);
free(outbuf);
return i ? i : USE_DESKTOP(total_written) + 0;
}
#ifdef TESTING
static char * const bunzip_errors[]={NULL,"Bad file checksum","Not bzip data",
"Unexpected input EOF","Unexpected output EOF","Data error",
"Out of memory","Obsolete (pre 0.9.5) bzip format not supported."};
/* Dumb little test thing, decompress stdin to stdout */
int main(int argc, char *argv[])
{
int i=uncompressStream(0,1);
char c;
if(i<0) fprintf(stderr,"%s\n", bunzip_errors[-i]);
else if(read(0,&c,1)) fprintf(stderr,"Trailing garbage ignored\n");
return -i;
}
#endif

View File

@ -0,0 +1,292 @@
/* vi: set sw=4 ts=4: */
#include "libbb.h"
/* uncompress for busybox -- (c) 2002 Robert Griebl
*
* based on the original compress42.c source
* (see disclaimer below)
*/
/* (N)compress42.c - File compression ala IEEE Computer, Mar 1992.
*
* Authors:
* Spencer W. Thomas (decvax!harpo!utah-cs!utah-gr!thomas)
* Jim McKie (decvax!mcvax!jim)
* Steve Davies (decvax!vax135!petsd!peora!srd)
* Ken Turkowski (decvax!decwrl!turtlevax!ken)
* James A. Woods (decvax!ihnp4!ames!jaw)
* Joe Orost (decvax!vax135!petsd!joe)
* Dave Mack (csu@alembic.acs.com)
* Peter Jannesen, Network Communication Systems
* (peter@ncs.nl)
*
* marc@suse.de : a small security fix for a buffer overflow
*
* [... History snipped ...]
*
*/
#include <stdio.h>
#include <string.h>
#include <unistd.h>
/* Default input buffer size */
#define IBUFSIZ 2048
/* Default output buffer size */
#define OBUFSIZ 2048
/* Defines for third byte of header */
#define MAGIC_1 (char_type)'\037' /* First byte of compressed file */
#define MAGIC_2 (char_type)'\235' /* Second byte of compressed file */
#define BIT_MASK 0x1f /* Mask for 'number of compresssion bits' */
/* Masks 0x20 and 0x40 are free. */
/* I think 0x20 should mean that there is */
/* a fourth header byte (for expansion). */
#define BLOCK_MODE 0x80 /* Block compresssion if table is full and */
/* compression rate is dropping flush tables */
/* the next two codes should not be changed lightly, as they must not */
/* lie within the contiguous general code space. */
#define FIRST 257 /* first free entry */
#define CLEAR 256 /* table clear output code */
#define INIT_BITS 9 /* initial number of bits/code */
/* machine variants which require cc -Dmachine: pdp11, z8000, DOS */
#define FAST
#define HBITS 17 /* 50% occupancy */
#define HSIZE (1<<HBITS)
#define HMASK (HSIZE-1)
#define HPRIME 9941
#define BITS 16
#undef MAXSEG_64K
#define MAXCODE(n) (1L << (n))
/* Block compress mode -C compatible with 2.0 */
static int block_mode = BLOCK_MODE;
/* user settable max # bits/code */
static int maxbits = BITS;
#define htabof(i) htab[i]
#define codetabof(i) codetab[i]
#define tab_prefixof(i) codetabof(i)
#define tab_suffixof(i) ((unsigned char *)(htab))[i]
#define de_stack ((unsigned char *)&(htab[HSIZE-1]))
#define clear_htab() memset(htab, -1, HSIZE)
#define clear_tab_prefixof() memset(codetab, 0, 256);
/*
* Decompress stdin to stdout. This routine adapts to the codes in the
* file building the "string" table on-the-fly; requiring no table to
* be stored in the compressed file. The tables used herein are shared
* with those of the compress() routine. See the definitions above.
*/
USE_DESKTOP(long long) int
uncompress(int fd_in, int fd_out)
{
USE_DESKTOP(long long total_written = 0;)
unsigned char *stackp;
long int code;
int finchar;
long int oldcode;
long int incode;
int inbits;
int posbits;
int outpos;
int insize;
int bitmask;
long int free_ent;
long int maxcode;
long int maxmaxcode;
int n_bits;
int rsize = 0;
RESERVE_CONFIG_UBUFFER(inbuf, IBUFSIZ + 64);
RESERVE_CONFIG_UBUFFER(outbuf, OBUFSIZ + 2048);
unsigned char htab[HSIZE];
unsigned short codetab[HSIZE];
memset(inbuf, 0, IBUFSIZ + 64);
memset(outbuf, 0, OBUFSIZ + 2048);
insize = 0;
inbuf[0] = xread_char(fd_in);
maxbits = inbuf[0] & BIT_MASK;
block_mode = inbuf[0] & BLOCK_MODE;
maxmaxcode = MAXCODE(maxbits);
if (maxbits > BITS) {
bb_error_msg("compressed with %d bits, can only handle %d bits", maxbits,
BITS);
return -1;
}
maxcode = MAXCODE(n_bits = INIT_BITS) - 1;
bitmask = (1 << n_bits) - 1;
oldcode = -1;
finchar = 0;
outpos = 0;
posbits = 0 << 3;
free_ent = ((block_mode) ? FIRST : 256);
/* As above, initialize the first 256 entries in the table. */
clear_tab_prefixof();
for (code = 255; code >= 0; --code) {
tab_suffixof(code) = (unsigned char) code;
}
do {
resetbuf:;
{
int i;
int e;
int o;
e = insize - (o = (posbits >> 3));
for (i = 0; i < e; ++i)
inbuf[i] = inbuf[i + o];
insize = e;
posbits = 0;
}
if (insize < (int) (IBUFSIZ + 64) - IBUFSIZ) {
rsize = safe_read(fd_in, inbuf + insize, IBUFSIZ);
insize += rsize;
}
inbits = ((rsize > 0) ? (insize - insize % n_bits) << 3 :
(insize << 3) - (n_bits - 1));
while (inbits > posbits) {
if (free_ent > maxcode) {
posbits =
((posbits - 1) +
((n_bits << 3) -
(posbits - 1 + (n_bits << 3)) % (n_bits << 3)));
++n_bits;
if (n_bits == maxbits) {
maxcode = maxmaxcode;
} else {
maxcode = MAXCODE(n_bits) - 1;
}
bitmask = (1 << n_bits) - 1;
goto resetbuf;
}
{
unsigned char *p = &inbuf[posbits >> 3];
code = ((((long) (p[0])) | ((long) (p[1]) << 8) |
((long) (p[2]) << 16)) >> (posbits & 0x7)) & bitmask;
}
posbits += n_bits;
if (oldcode == -1) {
oldcode = code;
finchar = (int) oldcode;
outbuf[outpos++] = (unsigned char) finchar;
continue;
}
if (code == CLEAR && block_mode) {
clear_tab_prefixof();
free_ent = FIRST - 1;
posbits =
((posbits - 1) +
((n_bits << 3) -
(posbits - 1 + (n_bits << 3)) % (n_bits << 3)));
maxcode = MAXCODE(n_bits = INIT_BITS) - 1;
bitmask = (1 << n_bits) - 1;
goto resetbuf;
}
incode = code;
stackp = de_stack;
/* Special case for KwKwK string. */
if (code >= free_ent) {
if (code > free_ent) {
unsigned char *p;
posbits -= n_bits;
p = &inbuf[posbits >> 3];
bb_error_msg
("insize:%d posbits:%d inbuf:%02X %02X %02X %02X %02X (%d)",
insize, posbits, p[-1], p[0], p[1], p[2], p[3],
(posbits & 07));
bb_error_msg("uncompress: corrupt input");
return -1;
}
*--stackp = (unsigned char) finchar;
code = oldcode;
}
/* Generate output characters in reverse order */
while ((long int) code >= (long int) 256) {
*--stackp = tab_suffixof(code);
code = tab_prefixof(code);
}
*--stackp = (unsigned char) (finchar = tab_suffixof(code));
/* And put them out in forward order */
{
int i;
if (outpos + (i = (de_stack - stackp)) >= OBUFSIZ) {
do {
if (i > OBUFSIZ - outpos) {
i = OBUFSIZ - outpos;
}
if (i > 0) {
memcpy(outbuf + outpos, stackp, i);
outpos += i;
}
if (outpos >= OBUFSIZ) {
write(fd_out, outbuf, outpos);
USE_DESKTOP(total_written += outpos;)
outpos = 0;
}
stackp += i;
} while ((i = (de_stack - stackp)) > 0);
} else {
memcpy(outbuf + outpos, stackp, i);
outpos += i;
}
}
/* Generate the new entry. */
if ((code = free_ent) < maxmaxcode) {
tab_prefixof(code) = (unsigned short) oldcode;
tab_suffixof(code) = (unsigned char) finchar;
free_ent = code + 1;
}
/* Remember previous code. */
oldcode = incode;
}
} while (rsize > 0);
if (outpos > 0) {
write(fd_out, outbuf, outpos);
USE_DESKTOP(total_written += outpos;)
}
RELEASE_CONFIG_BUFFER(inbuf);
RELEASE_CONFIG_BUFFER(outbuf);
return USE_DESKTOP(total_written) + 0;
}

View File

@ -0,0 +1,478 @@
/* vi: set sw=4 ts=4: */
/*
* Small lzma deflate implementation.
* Copyright (C) 2006 Aurelien Jacobs <aurel@gnuage.org>
*
* Based on LzmaDecode.c from the LZMA SDK 4.22 (http://www.7-zip.org/)
* Copyright (C) 1999-2005 Igor Pavlov
*
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
*/
#include "libbb.h"
#include "unarchive.h"
#ifdef CONFIG_FEATURE_LZMA_FAST
# define speed_inline ATTRIBUTE_ALWAYS_INLINE
#else
# define speed_inline
#endif
typedef struct {
int fd;
uint8_t *ptr;
uint8_t *buffer;
uint8_t *buffer_end;
int buffer_size;
uint32_t code;
uint32_t range;
uint32_t bound;
} rc_t;
#define RC_TOP_BITS 24
#define RC_MOVE_BITS 5
#define RC_MODEL_TOTAL_BITS 11
/* Called twice: once at startup and once in rc_normalize() */
static void rc_read(rc_t * rc)
{
rc->buffer_size = read(rc->fd, rc->buffer, rc->buffer_size);
if (rc->buffer_size <= 0)
bb_error_msg_and_die("unexpected EOF");
rc->ptr = rc->buffer;
rc->buffer_end = rc->buffer + rc->buffer_size;
}
/* Called once */
static void rc_init(rc_t * rc, int fd, int buffer_size)
{
int i;
rc->fd = fd;
rc->buffer = xmalloc(buffer_size);
rc->buffer_size = buffer_size;
rc->buffer_end = rc->buffer + rc->buffer_size;
rc->ptr = rc->buffer_end;
rc->code = 0;
rc->range = 0xFFFFFFFF;
for (i = 0; i < 5; i++) {
if (rc->ptr >= rc->buffer_end)
rc_read(rc);
rc->code = (rc->code << 8) | *rc->ptr++;
}
}
/* Called once. TODO: bb_maybe_free() */
static ATTRIBUTE_ALWAYS_INLINE void rc_free(rc_t * rc)
{
if (ENABLE_FEATURE_CLEAN_UP)
free(rc->buffer);
}
/* Called twice, but one callsite is in speed_inline'd rc_is_bit_0_helper() */
static void rc_do_normalize(rc_t * rc)
{
if (rc->ptr >= rc->buffer_end)
rc_read(rc);
rc->range <<= 8;
rc->code = (rc->code << 8) | *rc->ptr++;
}
static ATTRIBUTE_ALWAYS_INLINE void rc_normalize(rc_t * rc)
{
if (rc->range < (1 << RC_TOP_BITS)) {
rc_do_normalize(rc);
}
}
/* Called 9 times */
/* Why rc_is_bit_0_helper exists?
* Because we want to always expose (rc->code < rc->bound) to optimizer
*/
static speed_inline uint32_t rc_is_bit_0_helper(rc_t * rc, uint16_t * p)
{
rc_normalize(rc);
rc->bound = *p * (rc->range >> RC_MODEL_TOTAL_BITS);
return rc->bound;
}
static ATTRIBUTE_ALWAYS_INLINE int rc_is_bit_0(rc_t * rc, uint16_t * p)
{
uint32_t t = rc_is_bit_0_helper(rc, p);
return rc->code < t;
}
/* Called ~10 times, but very small, thus inlined */
static speed_inline void rc_update_bit_0(rc_t * rc, uint16_t * p)
{
rc->range = rc->bound;
*p += ((1 << RC_MODEL_TOTAL_BITS) - *p) >> RC_MOVE_BITS;
}
static speed_inline void rc_update_bit_1(rc_t * rc, uint16_t * p)
{
rc->range -= rc->bound;
rc->code -= rc->bound;
*p -= *p >> RC_MOVE_BITS;
}
/* Called 4 times in unlzma loop */
static int rc_get_bit(rc_t * rc, uint16_t * p, int *symbol)
{
if (rc_is_bit_0(rc, p)) {
rc_update_bit_0(rc, p);
*symbol *= 2;
return 0;
} else {
rc_update_bit_1(rc, p);
*symbol = *symbol * 2 + 1;
return 1;
}
}
/* Called once */
static ATTRIBUTE_ALWAYS_INLINE int rc_direct_bit(rc_t * rc)
{
rc_normalize(rc);
rc->range >>= 1;
if (rc->code >= rc->range) {
rc->code -= rc->range;
return 1;
}
return 0;
}
/* Called twice */
static speed_inline void
rc_bit_tree_decode(rc_t * rc, uint16_t * p, int num_levels, int *symbol)
{
int i = num_levels;
*symbol = 1;
while (i--)
rc_get_bit(rc, p + *symbol, symbol);
*symbol -= 1 << num_levels;
}
typedef struct {
uint8_t pos;
uint32_t dict_size;
uint64_t dst_size;
} __attribute__ ((packed)) lzma_header_t;
#define LZMA_BASE_SIZE 1846
#define LZMA_LIT_SIZE 768
#define LZMA_NUM_POS_BITS_MAX 4
#define LZMA_LEN_NUM_LOW_BITS 3
#define LZMA_LEN_NUM_MID_BITS 3
#define LZMA_LEN_NUM_HIGH_BITS 8
#define LZMA_LEN_CHOICE 0
#define LZMA_LEN_CHOICE_2 (LZMA_LEN_CHOICE + 1)
#define LZMA_LEN_LOW (LZMA_LEN_CHOICE_2 + 1)
#define LZMA_LEN_MID (LZMA_LEN_LOW \
+ (1 << (LZMA_NUM_POS_BITS_MAX + LZMA_LEN_NUM_LOW_BITS)))
#define LZMA_LEN_HIGH (LZMA_LEN_MID \
+(1 << (LZMA_NUM_POS_BITS_MAX + LZMA_LEN_NUM_MID_BITS)))
#define LZMA_NUM_LEN_PROBS (LZMA_LEN_HIGH + (1 << LZMA_LEN_NUM_HIGH_BITS))
#define LZMA_NUM_STATES 12
#define LZMA_NUM_LIT_STATES 7
#define LZMA_START_POS_MODEL_INDEX 4
#define LZMA_END_POS_MODEL_INDEX 14
#define LZMA_NUM_FULL_DISTANCES (1 << (LZMA_END_POS_MODEL_INDEX >> 1))
#define LZMA_NUM_POS_SLOT_BITS 6
#define LZMA_NUM_LEN_TO_POS_STATES 4
#define LZMA_NUM_ALIGN_BITS 4
#define LZMA_MATCH_MIN_LEN 2
#define LZMA_IS_MATCH 0
#define LZMA_IS_REP (LZMA_IS_MATCH + (LZMA_NUM_STATES <<LZMA_NUM_POS_BITS_MAX))
#define LZMA_IS_REP_G0 (LZMA_IS_REP + LZMA_NUM_STATES)
#define LZMA_IS_REP_G1 (LZMA_IS_REP_G0 + LZMA_NUM_STATES)
#define LZMA_IS_REP_G2 (LZMA_IS_REP_G1 + LZMA_NUM_STATES)
#define LZMA_IS_REP_0_LONG (LZMA_IS_REP_G2 + LZMA_NUM_STATES)
#define LZMA_POS_SLOT (LZMA_IS_REP_0_LONG \
+ (LZMA_NUM_STATES << LZMA_NUM_POS_BITS_MAX))
#define LZMA_SPEC_POS (LZMA_POS_SLOT \
+(LZMA_NUM_LEN_TO_POS_STATES << LZMA_NUM_POS_SLOT_BITS))
#define LZMA_ALIGN (LZMA_SPEC_POS \
+ LZMA_NUM_FULL_DISTANCES - LZMA_END_POS_MODEL_INDEX)
#define LZMA_LEN_CODER (LZMA_ALIGN + (1 << LZMA_NUM_ALIGN_BITS))
#define LZMA_REP_LEN_CODER (LZMA_LEN_CODER + LZMA_NUM_LEN_PROBS)
#define LZMA_LITERAL (LZMA_REP_LEN_CODER + LZMA_NUM_LEN_PROBS)
USE_DESKTOP(long long) int
unlzma(int src_fd, int dst_fd)
{
USE_DESKTOP(long long total_written = 0;)
lzma_header_t header;
int lc, pb, lp;
uint32_t pos_state_mask;
uint32_t literal_pos_mask;
uint32_t pos;
uint16_t *p;
uint16_t *prob;
uint16_t *prob_lit;
int num_bits;
int num_probs;
rc_t rc;
int i, mi;
uint8_t *buffer;
uint8_t previous_byte = 0;
size_t buffer_pos = 0, global_pos = 0;
int len = 0;
int state = 0;
uint32_t rep0 = 1, rep1 = 1, rep2 = 1, rep3 = 1;
if (read(src_fd, &header, sizeof(header)) != sizeof(header))
bb_error_msg_and_die("can't read header");
if (header.pos >= (9 * 5 * 5))
bb_error_msg_and_die("bad header");
mi = header.pos / 9;
lc = header.pos % 9;
pb = mi / 5;
lp = mi % 5;
pos_state_mask = (1 << pb) - 1;
literal_pos_mask = (1 << lp) - 1;
header.dict_size = SWAP_LE32(header.dict_size);
header.dst_size = SWAP_LE64(header.dst_size);
if (header.dict_size == 0)
header.dict_size = 1;
buffer = xmalloc(MIN(header.dst_size, header.dict_size));
num_probs = LZMA_BASE_SIZE + (LZMA_LIT_SIZE << (lc + lp));
p = xmalloc(num_probs * sizeof(*p));
num_probs = LZMA_LITERAL + (LZMA_LIT_SIZE << (lc + lp));
for (i = 0; i < num_probs; i++)
p[i] = (1 << RC_MODEL_TOTAL_BITS) >> 1;
rc_init(&rc, src_fd, 0x10000);
while (global_pos + buffer_pos < header.dst_size) {
int pos_state = (buffer_pos + global_pos) & pos_state_mask;
prob =
p + LZMA_IS_MATCH + (state << LZMA_NUM_POS_BITS_MAX) + pos_state;
if (rc_is_bit_0(&rc, prob)) {
mi = 1;
rc_update_bit_0(&rc, prob);
prob = (p + LZMA_LITERAL + (LZMA_LIT_SIZE
* ((((buffer_pos + global_pos) & literal_pos_mask) << lc)
+ (previous_byte >> (8 - lc)))));
if (state >= LZMA_NUM_LIT_STATES) {
int match_byte;
pos = buffer_pos - rep0;
while (pos >= header.dict_size)
pos += header.dict_size;
match_byte = buffer[pos];
do {
int bit;
match_byte <<= 1;
bit = match_byte & 0x100;
prob_lit = prob + 0x100 + bit + mi;
if (rc_get_bit(&rc, prob_lit, &mi)) {
if (!bit)
break;
} else {
if (bit)
break;
}
} while (mi < 0x100);
}
while (mi < 0x100) {
prob_lit = prob + mi;
rc_get_bit(&rc, prob_lit, &mi);
}
previous_byte = (uint8_t) mi;
buffer[buffer_pos++] = previous_byte;
if (buffer_pos == header.dict_size) {
buffer_pos = 0;
global_pos += header.dict_size;
// FIXME: error check
write(dst_fd, buffer, header.dict_size);
USE_DESKTOP(total_written += header.dict_size;)
}
if (state < 4)
state = 0;
else if (state < 10)
state -= 3;
else
state -= 6;
} else {
int offset;
uint16_t *prob_len;
rc_update_bit_1(&rc, prob);
prob = p + LZMA_IS_REP + state;
if (rc_is_bit_0(&rc, prob)) {
rc_update_bit_0(&rc, prob);
rep3 = rep2;
rep2 = rep1;
rep1 = rep0;
state = state < LZMA_NUM_LIT_STATES ? 0 : 3;
prob = p + LZMA_LEN_CODER;
} else {
rc_update_bit_1(&rc, prob);
prob = p + LZMA_IS_REP_G0 + state;
if (rc_is_bit_0(&rc, prob)) {
rc_update_bit_0(&rc, prob);
prob = (p + LZMA_IS_REP_0_LONG
+ (state << LZMA_NUM_POS_BITS_MAX) + pos_state);
if (rc_is_bit_0(&rc, prob)) {
rc_update_bit_0(&rc, prob);
state = state < LZMA_NUM_LIT_STATES ? 9 : 11;
pos = buffer_pos - rep0;
while (pos >= header.dict_size)
pos += header.dict_size;
previous_byte = buffer[pos];
buffer[buffer_pos++] = previous_byte;
if (buffer_pos == header.dict_size) {
buffer_pos = 0;
global_pos += header.dict_size;
// FIXME: error check
write(dst_fd, buffer, header.dict_size);
USE_DESKTOP(total_written += header.dict_size;)
}
continue;
} else {
rc_update_bit_1(&rc, prob);
}
} else {
uint32_t distance;
rc_update_bit_1(&rc, prob);
prob = p + LZMA_IS_REP_G1 + state;
if (rc_is_bit_0(&rc, prob)) {
rc_update_bit_0(&rc, prob);
distance = rep1;
} else {
rc_update_bit_1(&rc, prob);
prob = p + LZMA_IS_REP_G2 + state;
if (rc_is_bit_0(&rc, prob)) {
rc_update_bit_0(&rc, prob);
distance = rep2;
} else {
rc_update_bit_1(&rc, prob);
distance = rep3;
rep3 = rep2;
}
rep2 = rep1;
}
rep1 = rep0;
rep0 = distance;
}
state = state < LZMA_NUM_LIT_STATES ? 8 : 11;
prob = p + LZMA_REP_LEN_CODER;
}
prob_len = prob + LZMA_LEN_CHOICE;
if (rc_is_bit_0(&rc, prob_len)) {
rc_update_bit_0(&rc, prob_len);
prob_len = (prob + LZMA_LEN_LOW
+ (pos_state << LZMA_LEN_NUM_LOW_BITS));
offset = 0;
num_bits = LZMA_LEN_NUM_LOW_BITS;
} else {
rc_update_bit_1(&rc, prob_len);
prob_len = prob + LZMA_LEN_CHOICE_2;
if (rc_is_bit_0(&rc, prob_len)) {
rc_update_bit_0(&rc, prob_len);
prob_len = (prob + LZMA_LEN_MID
+ (pos_state << LZMA_LEN_NUM_MID_BITS));
offset = 1 << LZMA_LEN_NUM_LOW_BITS;
num_bits = LZMA_LEN_NUM_MID_BITS;
} else {
rc_update_bit_1(&rc, prob_len);
prob_len = prob + LZMA_LEN_HIGH;
offset = ((1 << LZMA_LEN_NUM_LOW_BITS)
+ (1 << LZMA_LEN_NUM_MID_BITS));
num_bits = LZMA_LEN_NUM_HIGH_BITS;
}
}
rc_bit_tree_decode(&rc, prob_len, num_bits, &len);
len += offset;
if (state < 4) {
int pos_slot;
state += LZMA_NUM_LIT_STATES;
prob =
p + LZMA_POS_SLOT +
((len <
LZMA_NUM_LEN_TO_POS_STATES ? len :
LZMA_NUM_LEN_TO_POS_STATES - 1)
<< LZMA_NUM_POS_SLOT_BITS);
rc_bit_tree_decode(&rc, prob, LZMA_NUM_POS_SLOT_BITS,
&pos_slot);
if (pos_slot >= LZMA_START_POS_MODEL_INDEX) {
num_bits = (pos_slot >> 1) - 1;
rep0 = 2 | (pos_slot & 1);
if (pos_slot < LZMA_END_POS_MODEL_INDEX) {
rep0 <<= num_bits;
prob = p + LZMA_SPEC_POS + rep0 - pos_slot - 1;
} else {
num_bits -= LZMA_NUM_ALIGN_BITS;
while (num_bits--)
rep0 = (rep0 << 1) | rc_direct_bit(&rc);
prob = p + LZMA_ALIGN;
rep0 <<= LZMA_NUM_ALIGN_BITS;
num_bits = LZMA_NUM_ALIGN_BITS;
}
i = 1;
mi = 1;
while (num_bits--) {
if (rc_get_bit(&rc, prob + mi, &mi))
rep0 |= i;
i <<= 1;
}
} else
rep0 = pos_slot;
if (++rep0 == 0)
break;
}
len += LZMA_MATCH_MIN_LEN;
do {
pos = buffer_pos - rep0;
while (pos >= header.dict_size)
pos += header.dict_size;
previous_byte = buffer[pos];
buffer[buffer_pos++] = previous_byte;
if (buffer_pos == header.dict_size) {
buffer_pos = 0;
global_pos += header.dict_size;
// FIXME: error check
write(dst_fd, buffer, header.dict_size);
USE_DESKTOP(total_written += header.dict_size;)
}
len--;
} while (len != 0 && buffer_pos < header.dst_size);
}
}
// FIXME: error check
write(dst_fd, buffer, buffer_pos);
USE_DESKTOP(total_written += buffer_pos;)
rc_free(&rc);
return USE_DESKTOP(total_written) + 0;
}

View File

@ -0,0 +1,924 @@
/* vi: set sw=4 ts=4: */
/*
* gunzip implementation for busybox
*
* Based on GNU gzip v1.2.4 Copyright (C) 1992-1993 Jean-loup Gailly.
*
* Originally adjusted for busybox by Sven Rudolph <sr1@inf.tu-dresden.de>
* based on gzip sources
*
* Adjusted further by Erik Andersen <andersen@codepoet.org> to support
* files as well as stdin/stdout, and to generally behave itself wrt
* command line handling.
*
* General cleanup to better adhere to the style guide and make use of standard
* busybox functions by Glenn McGrath <bug1@iinet.net.au>
*
* read_gz interface + associated hacking by Laurence Anderson
*
* Fixed huft_build() so decoding end-of-block code does not grab more bits
* than necessary (this is required by unzip applet), added inflate_cleanup()
* to free leaked bytebuffer memory (used in unzip.c), and some minor style
* guide cleanups by Ed Clark
*
* gzip (GNU zip) -- compress files with zip algorithm and 'compress' interface
* Copyright (C) 1992-1993 Jean-loup Gailly
* The unzip code was written and put in the public domain by Mark Adler.
* Portions of the lzw code are derived from the public domain 'compress'
* written by Spencer Thomas, Joe Orost, James Woods, Jim McKie, Steve Davies,
* Ken Turkowski, Dave Mack and Peter Jannesen.
*
* See the file algorithm.doc for the compression algorithms and file formats.
*
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
*/
#include "libbb.h"
#include "unarchive.h"
typedef struct huft_s {
unsigned char e; /* number of extra bits or operation */
unsigned char b; /* number of bits in this code or subcode */
union {
unsigned short n; /* literal, length base, or distance base */
struct huft_s *t; /* pointer to next level of table */
} v;
} huft_t;
static int gunzip_src_fd;
unsigned int gunzip_bytes_out; /* number of output bytes */
static unsigned int gunzip_outbuf_count; /* bytes in output buffer */
/* gunzip_window size--must be a power of two, and
* at least 32K for zip's deflate method */
enum { gunzip_wsize = 0x8000 };
static unsigned char *gunzip_window;
static uint32_t *gunzip_crc_table;
uint32_t gunzip_crc;
/* If BMAX needs to be larger than 16, then h and x[] should be ulg. */
#define BMAX 16 /* maximum bit length of any code (16 for explode) */
#define N_MAX 288 /* maximum number of codes in any set */
/* bitbuffer */
static unsigned int gunzip_bb; /* bit buffer */
static unsigned char gunzip_bk; /* bits in bit buffer */
/* These control the size of the bytebuffer */
static unsigned int bytebuffer_max = 0x8000;
static unsigned char *bytebuffer = NULL;
static unsigned int bytebuffer_offset = 0;
static unsigned int bytebuffer_size = 0;
static const unsigned short mask_bits[] = {
0x0000, 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff,
0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff
};
/* Copy lengths for literal codes 257..285 */
static const unsigned short cplens[] = {
3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59,
67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0
};
/* note: see note #13 above about the 258 in this list. */
/* Extra bits for literal codes 257..285 */
static const unsigned char cplext[] = {
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5,
5, 5, 5, 0, 99, 99
}; /* 99==invalid */
/* Copy offsets for distance codes 0..29 */
static const unsigned short cpdist[] = {
1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513,
769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577
};
/* Extra bits for distance codes */
static const unsigned char cpdext[] = {
0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10,
11, 11, 12, 12, 13, 13
};
/* Tables for deflate from PKZIP's appnote.txt. */
/* Order of the bit length code lengths */
static const unsigned char border[] = {
16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15
};
static unsigned int fill_bitbuffer(unsigned int bitbuffer, unsigned int *current, const unsigned int required)
{
while (*current < required) {
if (bytebuffer_offset >= bytebuffer_size) {
/* Leave the first 4 bytes empty so we can always unwind the bitbuffer
* to the front of the bytebuffer, leave 4 bytes free at end of tail
* so we can easily top up buffer in check_trailer_gzip() */
if (1 > (bytebuffer_size = safe_read(gunzip_src_fd, &bytebuffer[4], bytebuffer_max - 8)))
bb_error_msg_and_die("unexpected end of file");
bytebuffer_size += 4;
bytebuffer_offset = 4;
}
bitbuffer |= ((unsigned int) bytebuffer[bytebuffer_offset]) << *current;
bytebuffer_offset++;
*current += 8;
}
return bitbuffer;
}
/*
* Free the malloc'ed tables built by huft_build(), which makes a linked
* list of the tables it made, with the links in a dummy first entry of
* each table.
* t: table to free
*/
static int huft_free(huft_t * t)
{
huft_t *p;
huft_t *q;
/* Go through linked list, freeing from the malloced (t[-1]) address. */
p = t;
while (p != (huft_t *) NULL) {
q = (--p)->v.t;
free((char *) p);
p = q;
}
return 0;
}
/* Given a list of code lengths and a maximum table size, make a set of
* tables to decode that set of codes. Return zero on success, one if
* the given code set is incomplete (the tables are still built in this
* case), two if the input is invalid (all zero length codes or an
* oversubscribed set of lengths), and three if not enough memory.
*
* b: code lengths in bits (all assumed <= BMAX)
* n: number of codes (assumed <= N_MAX)
* s: number of simple-valued codes (0..s-1)
* d: list of base values for non-simple codes
* e: list of extra bits for non-simple codes
* t: result: starting table
* m: maximum lookup bits, returns actual
*/
static
int huft_build(unsigned int *b, const unsigned int n,
const unsigned int s, const unsigned short *d,
const unsigned char *e, huft_t ** t, unsigned int *m)
{
unsigned a; /* counter for codes of length k */
unsigned c[BMAX + 1]; /* bit length count table */
unsigned eob_len; /* length of end-of-block code (value 256) */
unsigned f; /* i repeats in table every f entries */
int g; /* maximum code length */
int htl; /* table level */
unsigned i; /* counter, current code */
unsigned j; /* counter */
int k; /* number of bits in current code */
unsigned *p; /* pointer into c[], b[], or v[] */
huft_t *q; /* points to current table */
huft_t r; /* table entry for structure assignment */
huft_t *u[BMAX]; /* table stack */
unsigned v[N_MAX]; /* values in order of bit length */
int ws[BMAX+1]; /* bits decoded stack */
int w; /* bits decoded */
unsigned x[BMAX + 1]; /* bit offsets, then code stack */
unsigned *xp; /* pointer into x */
int y; /* number of dummy codes added */
unsigned z; /* number of entries in current table */
/* Length of EOB code, if any */
eob_len = n > 256 ? b[256] : BMAX;
/* Generate counts for each bit length */
memset((void *)c, 0, sizeof(c));
p = b;
i = n;
do {
c[*p]++; /* assume all entries <= BMAX */
p++; /* Can't combine with above line (Solaris bug) */
} while (--i);
if (c[0] == n) { /* null input--all zero length codes */
*t = (huft_t *) NULL;
*m = 0;
return 2;
}
/* Find minimum and maximum length, bound *m by those */
for (j = 1; (c[j] == 0) && (j <= BMAX); j++);
k = j; /* minimum code length */
for (i = BMAX; (c[i] == 0) && i; i--);
g = i; /* maximum code length */
*m = (*m < j) ? j : ((*m > i) ? i : *m);
/* Adjust last length count to fill out codes, if needed */
for (y = 1 << j; j < i; j++, y <<= 1) {
if ((y -= c[j]) < 0) {
return 2; /* bad input: more codes than bits */
}
}
if ((y -= c[i]) < 0) {
return 2;
}
c[i] += y;
/* Generate starting offsets into the value table for each length */
x[1] = j = 0;
p = c + 1;
xp = x + 2;
while (--i) { /* note that i == g from above */
*xp++ = (j += *p++);
}
/* Make a table of values in order of bit lengths */
p = b;
i = 0;
do {
if ((j = *p++) != 0) {
v[x[j]++] = i;
}
} while (++i < n);
/* Generate the Huffman codes and for each, make the table entries */
x[0] = i = 0; /* first Huffman code is zero */
p = v; /* grab values in bit order */
htl = -1; /* no tables yet--level -1 */
w = ws[0] = 0; /* bits decoded */
u[0] = (huft_t *) NULL; /* just to keep compilers happy */
q = (huft_t *) NULL; /* ditto */
z = 0; /* ditto */
/* go through the bit lengths (k already is bits in shortest code) */
for (; k <= g; k++) {
a = c[k];
while (a--) {
/* here i is the Huffman code of length k bits for value *p */
/* make tables up to required level */
while (k > ws[htl + 1]) {
w = ws[++htl];
/* compute minimum size table less than or equal to *m bits */
z = (z = g - w) > *m ? *m : z; /* upper limit on table size */
if ((f = 1 << (j = k - w)) > a + 1) { /* try a k-w bit table */
/* too few codes for k-w bit table */
f -= a + 1; /* deduct codes from patterns left */
xp = c + k;
while (++j < z) { /* try smaller tables up to z bits */
if ((f <<= 1) <= *++xp) {
break; /* enough codes to use up j bits */
}
f -= *xp; /* else deduct codes from patterns */
}
}
j = (w + j > eob_len && w < eob_len) ? eob_len - w : j; /* make EOB code end at table */
z = 1 << j; /* table entries for j-bit table */
ws[htl+1] = w + j; /* set bits decoded in stack */
/* allocate and link in new table */
q = (huft_t *) xzalloc((z + 1) * sizeof(huft_t));
*t = q + 1; /* link to list for huft_free() */
t = &(q->v.t);
u[htl] = ++q; /* table starts after link */
/* connect to last table, if there is one */
if (htl) {
x[htl] = i; /* save pattern for backing up */
r.b = (unsigned char) (w - ws[htl - 1]); /* bits to dump before this table */
r.e = (unsigned char) (16 + j); /* bits in this table */
r.v.t = q; /* pointer to this table */
j = (i & ((1 << w) - 1)) >> ws[htl - 1];
u[htl - 1][j] = r; /* connect to last table */
}
}
/* set up table entry in r */
r.b = (unsigned char) (k - w);
if (p >= v + n) {
r.e = 99; /* out of values--invalid code */
} else if (*p < s) {
r.e = (unsigned char) (*p < 256 ? 16 : 15); /* 256 is EOB code */
r.v.n = (unsigned short) (*p++); /* simple code is just the value */
} else {
r.e = (unsigned char) e[*p - s]; /* non-simple--look up in lists */
r.v.n = d[*p++ - s];
}
/* fill code-like entries with r */
f = 1 << (k - w);
for (j = i >> w; j < z; j += f) {
q[j] = r;
}
/* backwards increment the k-bit code i */
for (j = 1 << (k - 1); i & j; j >>= 1) {
i ^= j;
}
i ^= j;
/* backup over finished tables */
while ((i & ((1 << w) - 1)) != x[htl]) {
w = ws[--htl];
}
}
}
/* return actual size of base table */
*m = ws[1];
/* Return true (1) if we were given an incomplete table */
return y != 0 && g != 1;
}
/*
* inflate (decompress) the codes in a deflated (compressed) block.
* Return an error code or zero if it all goes ok.
*
* tl, td: literal/length and distance decoder tables
* bl, bd: number of bits decoded by tl[] and td[]
*/
static int inflate_codes(huft_t * my_tl, huft_t * my_td, const unsigned int my_bl, const unsigned int my_bd, int setup)
{
static unsigned int e; /* table entry flag/number of extra bits */
static unsigned int n, d; /* length and index for copy */
static unsigned int w; /* current gunzip_window position */
static huft_t *t; /* pointer to table entry */
static unsigned int ml, md; /* masks for bl and bd bits */
static unsigned int b; /* bit buffer */
static unsigned int k; /* number of bits in bit buffer */
static huft_t *tl, *td;
static unsigned int bl, bd;
static int resumeCopy = 0;
if (setup) { // 1st time we are called, copy in variables
tl = my_tl;
td = my_td;
bl = my_bl;
bd = my_bd;
/* make local copies of globals */
b = gunzip_bb; /* initialize bit buffer */
k = gunzip_bk;
w = gunzip_outbuf_count; /* initialize gunzip_window position */
/* inflate the coded data */
ml = mask_bits[bl]; /* precompute masks for speed */
md = mask_bits[bd];
return 0; // Don't actually do anything the first time
}
if (resumeCopy) goto do_copy;
while (1) { /* do until end of block */
b = fill_bitbuffer(b, &k, bl);
if ((e = (t = tl + ((unsigned) b & ml))->e) > 16)
do {
if (e == 99) {
bb_error_msg_and_die("inflate_codes error 1");
}
b >>= t->b;
k -= t->b;
e -= 16;
b = fill_bitbuffer(b, &k, e);
} while ((e =
(t = t->v.t + ((unsigned) b & mask_bits[e]))->e) > 16);
b >>= t->b;
k -= t->b;
if (e == 16) { /* then it's a literal */
gunzip_window[w++] = (unsigned char) t->v.n;
if (w == gunzip_wsize) {
gunzip_outbuf_count = (w);
//flush_gunzip_window();
w = 0;
return 1; // We have a block to read
}
} else { /* it's an EOB or a length */
/* exit if end of block */
if (e == 15) {
break;
}
/* get length of block to copy */
b = fill_bitbuffer(b, &k, e);
n = t->v.n + ((unsigned) b & mask_bits[e]);
b >>= e;
k -= e;
/* decode distance of block to copy */
b = fill_bitbuffer(b, &k, bd);
if ((e = (t = td + ((unsigned) b & md))->e) > 16)
do {
if (e == 99)
bb_error_msg_and_die("inflate_codes error 2");
b >>= t->b;
k -= t->b;
e -= 16;
b = fill_bitbuffer(b, &k, e);
} while ((e =
(t =
t->v.t + ((unsigned) b & mask_bits[e]))->e) > 16);
b >>= t->b;
k -= t->b;
b = fill_bitbuffer(b, &k, e);
d = w - t->v.n - ((unsigned) b & mask_bits[e]);
b >>= e;
k -= e;
/* do the copy */
do_copy: do {
n -= (e =
(e =
gunzip_wsize - ((d &= gunzip_wsize - 1) > w ? d : w)) > n ? n : e);
/* copy to new buffer to prevent possible overwrite */
if (w - d >= e) { /* (this test assumes unsigned comparison) */
memcpy(gunzip_window + w, gunzip_window + d, e);
w += e;
d += e;
} else {
/* do it slow to avoid memcpy() overlap */
/* !NOMEMCPY */
do {
gunzip_window[w++] = gunzip_window[d++];
} while (--e);
}
if (w == gunzip_wsize) {
gunzip_outbuf_count = (w);
if (n) resumeCopy = 1;
else resumeCopy = 0;
//flush_gunzip_window();
w = 0;
return 1;
}
} while (n);
resumeCopy = 0;
}
}
/* restore the globals from the locals */
gunzip_outbuf_count = w; /* restore global gunzip_window pointer */
gunzip_bb = b; /* restore global bit buffer */
gunzip_bk = k;
/* normally just after call to inflate_codes, but save code by putting it here */
/* free the decoding tables, return */
huft_free(tl);
huft_free(td);
/* done */
return 0;
}
static int inflate_stored(int my_n, int my_b_stored, int my_k_stored, int setup)
{
static unsigned int n, b_stored, k_stored, w;
if (setup) {
n = my_n;
b_stored = my_b_stored;
k_stored = my_k_stored;
w = gunzip_outbuf_count; /* initialize gunzip_window position */
return 0; // Don't do anything first time
}
/* read and output the compressed data */
while (n--) {
b_stored = fill_bitbuffer(b_stored, &k_stored, 8);
gunzip_window[w++] = (unsigned char) b_stored;
if (w == gunzip_wsize) {
gunzip_outbuf_count = (w);
//flush_gunzip_window();
w = 0;
b_stored >>= 8;
k_stored -= 8;
return 1; // We have a block
}
b_stored >>= 8;
k_stored -= 8;
}
/* restore the globals from the locals */
gunzip_outbuf_count = w; /* restore global gunzip_window pointer */
gunzip_bb = b_stored; /* restore global bit buffer */
gunzip_bk = k_stored;
return 0; // Finished
}
/*
* decompress an inflated block
* e: last block flag
*
* GLOBAL VARIABLES: bb, kk,
*/
// Return values: -1 = inflate_stored, -2 = inflate_codes
static int inflate_block(int *e)
{
unsigned t; /* block type */
unsigned int b; /* bit buffer */
unsigned int k; /* number of bits in bit buffer */
/* make local bit buffer */
b = gunzip_bb;
k = gunzip_bk;
/* read in last block bit */
b = fill_bitbuffer(b, &k, 1);
*e = (int) b & 1;
b >>= 1;
k -= 1;
/* read in block type */
b = fill_bitbuffer(b, &k, 2);
t = (unsigned) b & 3;
b >>= 2;
k -= 2;
/* restore the global bit buffer */
gunzip_bb = b;
gunzip_bk = k;
/* inflate that block type */
switch (t) {
case 0: /* Inflate stored */
{
unsigned int n; /* number of bytes in block */
unsigned int b_stored; /* bit buffer */
unsigned int k_stored; /* number of bits in bit buffer */
/* make local copies of globals */
b_stored = gunzip_bb; /* initialize bit buffer */
k_stored = gunzip_bk;
/* go to byte boundary */
n = k_stored & 7;
b_stored >>= n;
k_stored -= n;
/* get the length and its complement */
b_stored = fill_bitbuffer(b_stored, &k_stored, 16);
n = ((unsigned) b_stored & 0xffff);
b_stored >>= 16;
k_stored -= 16;
b_stored = fill_bitbuffer(b_stored, &k_stored, 16);
if (n != (unsigned) ((~b_stored) & 0xffff)) {
return 1; /* error in compressed data */
}
b_stored >>= 16;
k_stored -= 16;
inflate_stored(n, b_stored, k_stored, 1); // Setup inflate_stored
return -1;
}
case 1: /* Inflate fixed
* decompress an inflated type 1 (fixed Huffman codes) block. We should
* either replace this with a custom decoder, or at least precompute the
* Huffman tables.
*/
{
int i; /* temporary variable */
huft_t *tl; /* literal/length code table */
huft_t *td; /* distance code table */
unsigned int bl; /* lookup bits for tl */
unsigned int bd; /* lookup bits for td */
unsigned int l[288]; /* length list for huft_build */
/* set up literal table */
for (i = 0; i < 144; i++) {
l[i] = 8;
}
for (; i < 256; i++) {
l[i] = 9;
}
for (; i < 280; i++) {
l[i] = 7;
}
for (; i < 288; i++) { /* make a complete, but wrong code set */
l[i] = 8;
}
bl = 7;
if ((i = huft_build(l, 288, 257, cplens, cplext, &tl, &bl)) != 0) {
return i;
}
/* set up distance table */
for (i = 0; i < 30; i++) { /* make an incomplete code set */
l[i] = 5;
}
bd = 5;
if ((i = huft_build(l, 30, 0, cpdist, cpdext, &td, &bd)) > 1) {
huft_free(tl);
return i;
}
/* decompress until an end-of-block code */
inflate_codes(tl, td, bl, bd, 1); // Setup inflate_codes
/* huft_free code moved into inflate_codes */
return -2;
}
case 2: /* Inflate dynamic */
{
const int dbits = 6; /* bits in base distance lookup table */
const int lbits = 9; /* bits in base literal/length lookup table */
huft_t *tl; /* literal/length code table */
huft_t *td; /* distance code table */
unsigned int i; /* temporary variables */
unsigned int j;
unsigned int l; /* last length */
unsigned int m; /* mask for bit lengths table */
unsigned int n; /* number of lengths to get */
unsigned int bl; /* lookup bits for tl */
unsigned int bd; /* lookup bits for td */
unsigned int nb; /* number of bit length codes */
unsigned int nl; /* number of literal/length codes */
unsigned int nd; /* number of distance codes */
unsigned int ll[286 + 30]; /* literal/length and distance code lengths */
unsigned int b_dynamic; /* bit buffer */
unsigned int k_dynamic; /* number of bits in bit buffer */
/* make local bit buffer */
b_dynamic = gunzip_bb;
k_dynamic = gunzip_bk;
/* read in table lengths */
b_dynamic = fill_bitbuffer(b_dynamic, &k_dynamic, 5);
nl = 257 + ((unsigned int) b_dynamic & 0x1f); /* number of literal/length codes */
b_dynamic >>= 5;
k_dynamic -= 5;
b_dynamic = fill_bitbuffer(b_dynamic, &k_dynamic, 5);
nd = 1 + ((unsigned int) b_dynamic & 0x1f); /* number of distance codes */
b_dynamic >>= 5;
k_dynamic -= 5;
b_dynamic = fill_bitbuffer(b_dynamic, &k_dynamic, 4);
nb = 4 + ((unsigned int) b_dynamic & 0xf); /* number of bit length codes */
b_dynamic >>= 4;
k_dynamic -= 4;
if (nl > 286 || nd > 30) {
return 1; /* bad lengths */
}
/* read in bit-length-code lengths */
for (j = 0; j < nb; j++) {
b_dynamic = fill_bitbuffer(b_dynamic, &k_dynamic, 3);
ll[border[j]] = (unsigned int) b_dynamic & 7;
b_dynamic >>= 3;
k_dynamic -= 3;
}
for (; j < 19; j++) {
ll[border[j]] = 0;
}
/* build decoding table for trees--single level, 7 bit lookup */
bl = 7;
i = huft_build(ll, 19, 19, NULL, NULL, &tl, &bl);
if (i != 0) {
if (i == 1) {
huft_free(tl);
}
return i; /* incomplete code set */
}
/* read in literal and distance code lengths */
n = nl + nd;
m = mask_bits[bl];
i = l = 0;
while ((unsigned int) i < n) {
b_dynamic = fill_bitbuffer(b_dynamic, &k_dynamic, (unsigned int)bl);
j = (td = tl + ((unsigned int) b_dynamic & m))->b;
b_dynamic >>= j;
k_dynamic -= j;
j = td->v.n;
if (j < 16) { /* length of code in bits (0..15) */
ll[i++] = l = j; /* save last length in l */
} else if (j == 16) { /* repeat last length 3 to 6 times */
b_dynamic = fill_bitbuffer(b_dynamic, &k_dynamic, 2);
j = 3 + ((unsigned int) b_dynamic & 3);
b_dynamic >>= 2;
k_dynamic -= 2;
if ((unsigned int) i + j > n) {
return 1;
}
while (j--) {
ll[i++] = l;
}
} else if (j == 17) { /* 3 to 10 zero length codes */
b_dynamic = fill_bitbuffer(b_dynamic, &k_dynamic, 3);
j = 3 + ((unsigned int) b_dynamic & 7);
b_dynamic >>= 3;
k_dynamic -= 3;
if ((unsigned int) i + j > n) {
return 1;
}
while (j--) {
ll[i++] = 0;
}
l = 0;
} else { /* j == 18: 11 to 138 zero length codes */
b_dynamic = fill_bitbuffer(b_dynamic, &k_dynamic, 7);
j = 11 + ((unsigned int) b_dynamic & 0x7f);
b_dynamic >>= 7;
k_dynamic -= 7;
if ((unsigned int) i + j > n) {
return 1;
}
while (j--) {
ll[i++] = 0;
}
l = 0;
}
}
/* free decoding table for trees */
huft_free(tl);
/* restore the global bit buffer */
gunzip_bb = b_dynamic;
gunzip_bk = k_dynamic;
/* build the decoding tables for literal/length and distance codes */
bl = lbits;
if ((i = huft_build(ll, nl, 257, cplens, cplext, &tl, &bl)) != 0) {
if (i == 1) {
bb_error_msg_and_die("incomplete literal tree");
huft_free(tl);
}
return i; /* incomplete code set */
}
bd = dbits;
if ((i = huft_build(ll + nl, nd, 0, cpdist, cpdext, &td, &bd)) != 0) {
if (i == 1) {
bb_error_msg_and_die("incomplete distance tree");
huft_free(td);
}
huft_free(tl);
return i; /* incomplete code set */
}
/* decompress until an end-of-block code */
inflate_codes(tl, td, bl, bd, 1); // Setup inflate_codes
/* huft_free code moved into inflate_codes */
return -2;
}
default:
/* bad block type */
bb_error_msg_and_die("bad block type %d", t);
}
}
static void calculate_gunzip_crc(void)
{
int n;
for (n = 0; n < gunzip_outbuf_count; n++) {
gunzip_crc = gunzip_crc_table[((int) gunzip_crc ^ (gunzip_window[n])) & 0xff] ^ (gunzip_crc >> 8);
}
gunzip_bytes_out += gunzip_outbuf_count;
}
static int inflate_get_next_window(void)
{
static int method = -1; // Method == -1 for stored, -2 for codes
static int e = 0;
static int needAnotherBlock = 1;
gunzip_outbuf_count = 0;
while(1) {
int ret;
if (needAnotherBlock) {
if(e) {
calculate_gunzip_crc();
e = 0;
needAnotherBlock = 1;
return 0;
} // Last block
method = inflate_block(&e);
needAnotherBlock = 0;
}
switch (method) {
case -1: ret = inflate_stored(0,0,0,0);
break;
case -2: ret = inflate_codes(0,0,0,0,0);
break;
default: bb_error_msg_and_die("inflate error %d", method);
}
if (ret == 1) {
calculate_gunzip_crc();
return 1; // More data left
} else needAnotherBlock = 1; // End of that block
}
/* Doesnt get here */
}
/* Initialise bytebuffer, be careful not to overfill the buffer */
void inflate_init(unsigned int bufsize)
{
/* Set the bytebuffer size, default is same as gunzip_wsize */
bytebuffer_max = bufsize + 8;
bytebuffer_offset = 4;
bytebuffer_size = 0;
}
void inflate_cleanup(void)
{
free(bytebuffer);
}
USE_DESKTOP(long long) int
inflate_unzip(int in, int out)
{
USE_DESKTOP(long long total = 0;)
ssize_t nwrote;
typedef void (*sig_type) (int);
/* Allocate all global buffers (for DYN_ALLOC option) */
gunzip_window = xmalloc(gunzip_wsize);
gunzip_outbuf_count = 0;
gunzip_bytes_out = 0;
gunzip_src_fd = in;
/* initialize gunzip_window, bit buffer */
gunzip_bk = 0;
gunzip_bb = 0;
/* Create the crc table */
gunzip_crc_table = crc32_filltable(0);
gunzip_crc = ~0;
/* Allocate space for buffer */
bytebuffer = xmalloc(bytebuffer_max);
while(1) {
int ret = inflate_get_next_window();
nwrote = full_write(out, gunzip_window, gunzip_outbuf_count);
if (nwrote == -1) {
bb_perror_msg("write");
return -1;
}
USE_DESKTOP(total += nwrote;)
if (ret == 0) break;
}
/* Cleanup */
free(gunzip_window);
free(gunzip_crc_table);
/* Store unused bytes in a global buffer so calling applets can access it */
if (gunzip_bk >= 8) {
/* Undo too much lookahead. The next read will be byte aligned
* so we can discard unused bits in the last meaningful byte. */
bytebuffer_offset--;
bytebuffer[bytebuffer_offset] = gunzip_bb & 0xff;
gunzip_bb >>= 8;
gunzip_bk -= 8;
}
return USE_DESKTOP(total) + 0;
}
USE_DESKTOP(long long) int
inflate_gunzip(int in, int out)
{
uint32_t stored_crc = 0;
unsigned int count;
USE_DESKTOP(long long total = )inflate_unzip(in, out);
USE_DESKTOP(if (total < 0) return total;)
/* top up the input buffer with the rest of the trailer */
count = bytebuffer_size - bytebuffer_offset;
if (count < 8) {
xread(in, &bytebuffer[bytebuffer_size], 8 - count);
bytebuffer_size += 8 - count;
}
for (count = 0; count != 4; count++) {
stored_crc |= (bytebuffer[bytebuffer_offset] << (count * 8));
bytebuffer_offset++;
}
/* Validate decompression - crc */
if (stored_crc != (~gunzip_crc)) {
bb_error_msg("crc error");
return -1;
}
/* Validate decompression - size */
if (gunzip_bytes_out !=
(bytebuffer[bytebuffer_offset] | (bytebuffer[bytebuffer_offset+1] << 8) |
(bytebuffer[bytebuffer_offset+2] << 16) | (bytebuffer[bytebuffer_offset+3] << 24))) {
bb_error_msg("incorrect length");
return -1;
}
return USE_DESKTOP(total) + 0;
}

View File

@ -0,0 +1,17 @@
/* vi: set sw=4 ts=4: */
/*
* Copyright (C) 2002 by Glenn McGrath
*
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
*/
#include <stdlib.h>
#include "unarchive.h"
/* Accept any non-null name, its not really a filter at all */
char filter_accept_all(archive_handle_t *archive_handle)
{
if (archive_handle->file_header->name)
return EXIT_SUCCESS;
return EXIT_FAILURE;
}

View File

@ -0,0 +1,19 @@
/* vi: set sw=4 ts=4: */
/*
* Copyright (C) 2002 by Glenn McGrath
*
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
*/
#include <stdlib.h>
#include "unarchive.h"
/*
* Accept names that are in the accept list, ignoring reject list.
*/
char filter_accept_list(archive_handle_t *archive_handle)
{
if (find_list_entry(archive_handle->accept, archive_handle->file_header->name))
return EXIT_SUCCESS;
return EXIT_FAILURE;
}

View File

@ -0,0 +1,48 @@
/* vi: set sw=4 ts=4: */
/*
* Copyright (C) 2002 by Glenn McGrath
*
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
*/
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "libbb.h"
#include "unarchive.h"
/*
* Reassign the subarchive metadata parser based on the filename extension
* e.g. if its a .tar.gz modify archive_handle->sub_archive to process a .tar.gz
* or if its a .tar.bz2 make archive_handle->sub_archive handle that
*/
char filter_accept_list_reassign(archive_handle_t *archive_handle)
{
/* Check the file entry is in the accept list */
if (find_list_entry(archive_handle->accept, archive_handle->file_header->name)) {
const char *name_ptr;
/* Extract the last 2 extensions */
name_ptr = strrchr(archive_handle->file_header->name, '.');
/* Modify the subarchive handler based on the extension */
#ifdef CONFIG_FEATURE_DEB_TAR_GZ
if (strcmp(name_ptr, ".gz") == 0) {
archive_handle->action_data_subarchive = get_header_tar_gz;
return EXIT_SUCCESS;
}
#endif
#ifdef CONFIG_FEATURE_DEB_TAR_BZ2
if (strcmp(name_ptr, ".bz2") == 0) {
archive_handle->action_data_subarchive = get_header_tar_bz2;
return EXIT_SUCCESS;
}
#endif
if (ENABLE_FEATURE_DEB_TAR_LZMA && !strcmp(name_ptr, ".lzma")) {
archive_handle->action_data_subarchive = get_header_tar_lzma;
return EXIT_SUCCESS;
}
}
return EXIT_FAILURE;
}

View File

@ -0,0 +1,33 @@
/* vi: set sw=4 ts=4: */
/*
* Copyright (C) 2002 by Glenn McGrath
*
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
*/
#include <stdlib.h>
#include "unarchive.h"
/*
* Accept names that are in the accept list and not in the reject list
*/
char filter_accept_reject_list(archive_handle_t *archive_handle)
{
const char *key = archive_handle->file_header->name;
const llist_t *reject_entry = find_list_entry2(archive_handle->reject, key);
const llist_t *accept_entry;
/* If the key is in a reject list fail */
if (reject_entry) {
return EXIT_FAILURE;
}
accept_entry = find_list_entry2(archive_handle->accept, key);
/* Fail if an accept list was specified and the key wasnt in there */
if ((accept_entry == NULL) && archive_handle->accept) {
return EXIT_FAILURE;
}
/* Accepted */
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,54 @@
/* vi: set sw=4 ts=4: */
/*
* Copyright (C) 2002 by Glenn McGrath
*
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
*/
#include <fnmatch.h>
#include <stdlib.h>
#include "unarchive.h"
/* Find a string in a shell pattern list */
const llist_t *find_list_entry(const llist_t *list, const char *filename)
{
while (list) {
if (fnmatch(list->data, filename, 0) == 0) {
return list;
}
list = list->link;
}
return NULL;
}
/* Same, but compares only path components present in pattern
* (extra trailing path components in filename are assumed to match)
*/
const llist_t *find_list_entry2(const llist_t *list, const char *filename)
{
char buf[PATH_MAX];
int pattern_slash_cnt;
const char *c;
char *d;
while (list) {
c = list->data;
pattern_slash_cnt = 0;
while (*c)
if (*c++ == '/') pattern_slash_cnt++;
c = filename;
d = buf;
/* paranoia is better that buffer overflows */
while (*c && d != buf + sizeof(buf)-1) {
if (*c == '/' && --pattern_slash_cnt < 0)
break;
*d++ = *c++;
}
*d = '\0';
if (fnmatch(list->data, buf, 0) == 0) {
return list;
}
list = list->link;
}
return NULL;
}

View File

@ -0,0 +1,112 @@
/* vi: set sw=4 ts=4: */
/* Copyright 2001 Glenn McGrath.
*
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
*/
#include "libbb.h"
#include "unarchive.h"
char get_header_ar(archive_handle_t *archive_handle)
{
file_header_t *typed = archive_handle->file_header;
union {
char raw[60];
struct {
char name[16];
char date[12];
char uid[6];
char gid[6];
char mode[8];
char size[10];
char magic[2];
} formatted;
} ar;
#ifdef CONFIG_FEATURE_AR_LONG_FILENAMES
static char *ar_long_names;
static unsigned int ar_long_name_size;
#endif
/* dont use xread as we want to handle the error ourself */
if (read(archive_handle->src_fd, ar.raw, 60) != 60) {
/* End Of File */
return EXIT_FAILURE;
}
/* ar header starts on an even byte (2 byte aligned)
* '\n' is used for padding
*/
if (ar.raw[0] == '\n') {
/* fix up the header, we started reading 1 byte too early */
memmove(ar.raw, &ar.raw[1], 59);
ar.raw[59] = xread_char(archive_handle->src_fd);
archive_handle->offset++;
}
archive_handle->offset += 60;
/* align the headers based on the header magic */
if ((ar.formatted.magic[0] != '`') || (ar.formatted.magic[1] != '\n')) {
bb_error_msg_and_die("invalid ar header");
}
typed->mode = xstrtoul(ar.formatted.mode, 8);
typed->mtime = xatou(ar.formatted.date);
typed->uid = xatou(ar.formatted.uid);
typed->gid = xatou(ar.formatted.gid);
typed->size = xatoul(ar.formatted.size);
/* long filenames have '/' as the first character */
if (ar.formatted.name[0] == '/') {
#ifdef CONFIG_FEATURE_AR_LONG_FILENAMES
if (ar.formatted.name[1] == '/') {
/* If the second char is a '/' then this entries data section
* stores long filename for multiple entries, they are stored
* in static variable long_names for use in future entries */
ar_long_name_size = typed->size;
ar_long_names = xmalloc(ar_long_name_size);
xread(archive_handle->src_fd, ar_long_names, ar_long_name_size);
archive_handle->offset += ar_long_name_size;
/* This ar entries data section only contained filenames for other records
* they are stored in the static ar_long_names for future reference */
return get_header_ar(archive_handle); /* Return next header */
} else if (ar.formatted.name[1] == ' ') {
/* This is the index of symbols in the file for compilers */
data_skip(archive_handle);
archive_handle->offset += typed->size;
return get_header_ar(archive_handle); /* Return next header */
} else {
/* The number after the '/' indicates the offset in the ar data section
(saved in variable long_name) that conatains the real filename */
const unsigned int long_offset = atoi(&ar.formatted.name[1]);
if (long_offset >= ar_long_name_size) {
bb_error_msg_and_die("can't resolve long filename");
}
typed->name = xstrdup(ar_long_names + long_offset);
}
#else
bb_error_msg_and_die("long filenames not supported");
#endif
} else {
/* short filenames */
typed->name = xstrndup(ar.formatted.name, 16);
}
typed->name[strcspn(typed->name, " /")] = '\0';
if (archive_handle->filter(archive_handle) == EXIT_SUCCESS) {
archive_handle->action_header(typed);
if (archive_handle->sub_archive) {
while (archive_handle->action_data_subarchive(archive_handle->sub_archive) == EXIT_SUCCESS);
} else {
archive_handle->action_data(archive_handle);
}
} else {
data_skip(archive_handle);
}
archive_handle->offset += typed->size;
/* Set the file pointer to the correct spot, we may have been reading a compressed file */
lseek(archive_handle->src_fd, archive_handle->offset, SEEK_SET);
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,160 @@
/* vi: set sw=4 ts=4: */
/* Copyright 2002 Laurence Anderson
*
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
*/
#include "libbb.h"
#include "unarchive.h"
typedef struct hardlinks_s {
char *name;
int inode;
struct hardlinks_s *next;
} hardlinks_t;
char get_header_cpio(archive_handle_t *archive_handle)
{
static hardlinks_t *saved_hardlinks = NULL;
static unsigned short pending_hardlinks = 0;
static int inode;
file_header_t *file_header = archive_handle->file_header;
char cpio_header[110];
int namesize;
char dummy[16];
int major, minor, nlink;
if (pending_hardlinks) { /* Deal with any pending hardlinks */
hardlinks_t *tmp, *oldtmp;
tmp = saved_hardlinks;
oldtmp = NULL;
file_header->link_name = file_header->name;
file_header->size = 0;
while (tmp) {
if (tmp->inode != inode) {
tmp = tmp->next;
continue;
}
file_header->name = tmp->name;
if (archive_handle->filter(archive_handle) == EXIT_SUCCESS) {
archive_handle->action_data(archive_handle);
archive_handle->action_header(archive_handle->file_header);
}
pending_hardlinks--;
oldtmp = tmp;
tmp = tmp->next;
free(oldtmp->name);
free(oldtmp);
if (oldtmp == saved_hardlinks)
saved_hardlinks = tmp;
}
file_header->name = file_header->link_name;
if (pending_hardlinks > 1) {
bb_error_msg("error resolving hardlink: archive made by GNU cpio 2.0-2.2?");
}
/* No more pending hardlinks, read next file entry */
pending_hardlinks = 0;
}
/* There can be padding before archive header */
data_align(archive_handle, 4);
if (archive_xread_all_eof(archive_handle, (unsigned char*)cpio_header, 110) == 0) {
return EXIT_FAILURE;
}
archive_handle->offset += 110;
if (strncmp(&cpio_header[0], "07070", 5) != 0
|| (cpio_header[5] != '1' && cpio_header[5] != '2')
) {
bb_error_msg_and_die("unsupported cpio format, use newc or crc");
}
{
unsigned long tmpsize;
sscanf(cpio_header, "%6c%8x%8x%8x%8x%8x%8lx%8lx%16c%8x%8x%8x%8c",
dummy, &inode, (unsigned int*)&file_header->mode,
(unsigned int*)&file_header->uid, (unsigned int*)&file_header->gid,
&nlink, &file_header->mtime, &tmpsize,
dummy, &major, &minor, &namesize, dummy);
file_header->size = tmpsize;
}
free(file_header->name);
file_header->name = xzalloc(namesize + 1);
/* Read in filename */
xread(archive_handle->src_fd, file_header->name, namesize);
archive_handle->offset += namesize;
/* Update offset amount and skip padding before file contents */
data_align(archive_handle, 4);
if (strcmp(file_header->name, "TRAILER!!!") == 0) {
/* Always round up */
printf("%d blocks\n", (int) (archive_handle->offset % 512 ?
archive_handle->offset / 512 + 1 :
archive_handle->offset / 512
));
if (saved_hardlinks) { /* Bummer - we still have unresolved hardlinks */
hardlinks_t *tmp = saved_hardlinks;
hardlinks_t *oldtmp = NULL;
while (tmp) {
bb_error_msg("%s not created: cannot resolve hardlink", tmp->name);
oldtmp = tmp;
tmp = tmp->next;
free(oldtmp->name);
free(oldtmp);
}
saved_hardlinks = NULL;
pending_hardlinks = 0;
}
return EXIT_FAILURE;
}
if (S_ISLNK(file_header->mode)) {
file_header->link_name = xzalloc(file_header->size + 1);
xread(archive_handle->src_fd, file_header->link_name, file_header->size);
archive_handle->offset += file_header->size;
file_header->size = 0; /* Stop possible seeks in future */
} else {
file_header->link_name = NULL;
}
if (nlink > 1 && !S_ISDIR(file_header->mode)) {
if (file_header->size == 0) { /* Put file on a linked list for later */
hardlinks_t *new = xmalloc(sizeof(hardlinks_t));
new->next = saved_hardlinks;
new->inode = inode;
/* name current allocated, freed later */
new->name = file_header->name;
file_header->name = NULL;
saved_hardlinks = new;
return EXIT_SUCCESS; /* Skip this one */
}
/* Found the file with data in */
pending_hardlinks = nlink;
}
file_header->device = makedev(major, minor);
if (archive_handle->filter(archive_handle) == EXIT_SUCCESS) {
archive_handle->action_data(archive_handle);
archive_handle->action_header(archive_handle->file_header);
} else {
data_skip(archive_handle);
}
archive_handle->offset += file_header->size;
free(file_header->link_name);
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,275 @@
/* vi: set sw=4 ts=4: */
/* Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
*
* FIXME:
* In privileged mode if uname and gname map to a uid and gid then use the
* mapped value instead of the uid/gid values in tar header
*
* References:
* GNU tar and star man pages,
* Opengroup's ustar interchange format,
* http://www.opengroup.org/onlinepubs/007904975/utilities/pax.html
*/
#include "libbb.h"
#include "unarchive.h"
#if ENABLE_FEATURE_TAR_GNU_EXTENSIONS
static char *longname;
static char *linkname;
#else
enum {
longname = 0,
linkname = 0,
};
#endif
/* NB: _DESTROYS_ str[len] character! */
static unsigned long long getOctal(char *str, int len)
{
unsigned long long v;
/* Actually, tar header allows leading spaces also.
* Oh well, we will be liberal and skip this...
* The only downside probably is that we allow "-123" too :)
if (*str < '0' || *str > '7')
bb_error_msg_and_die("corrupted octal value in tar header");
*/
str[len] = '\0';
v = strtoull(str, &str, 8);
if (*str)
bb_error_msg_and_die("corrupted octal value in tar header");
return v;
}
#define GET_OCTAL(a) getOctal((a), sizeof(a))
void BUG_tar_header_size(void);
char get_header_tar(archive_handle_t *archive_handle)
{
static int end;
file_header_t *file_header = archive_handle->file_header;
struct {
/* ustar header, Posix 1003.1 */
char name[100]; /* 0-99 */
char mode[8]; /* 100-107 */
char uid[8]; /* 108-115 */
char gid[8]; /* 116-123 */
char size[12]; /* 124-135 */
char mtime[12]; /* 136-147 */
char chksum[8]; /* 148-155 */
char typeflag; /* 156-156 */
char linkname[100]; /* 157-256 */
char magic[6]; /* 257-262 */
char version[2]; /* 263-264 */
char uname[32]; /* 265-296 */
char gname[32]; /* 297-328 */
char devmajor[8]; /* 329-336 */
char devminor[8]; /* 337-344 */
char prefix[155]; /* 345-499 */
char padding[12]; /* 500-512 */
} tar;
char *cp;
int sum, i;
int parse_names;
if (sizeof(tar) != 512)
BUG_tar_header_size();
again:
/* Align header */
data_align(archive_handle, 512);
again_after_align:
xread(archive_handle->src_fd, &tar, 512);
archive_handle->offset += 512;
/* If there is no filename its an empty header */
if (tar.name[0] == 0) {
if (end) {
/* This is the second consecutive empty header! End of archive!
* Read until the end to empty the pipe from gz or bz2
*/
while (full_read(archive_handle->src_fd, &tar, 512) == 512)
/* repeat */;
return EXIT_FAILURE;
}
end = 1;
return EXIT_SUCCESS;
}
end = 0;
/* Check header has valid magic, "ustar" is for the proper tar
* 0's are for the old tar format
*/
if (strncmp(tar.magic, "ustar", 5) != 0) {
#if ENABLE_FEATURE_TAR_OLDGNU_COMPATIBILITY
if (memcmp(tar.magic, "\0\0\0\0", 5) != 0)
#endif
bb_error_msg_and_die("invalid tar magic");
}
/* Do checksum on headers */
sum = ' ' * sizeof(tar.chksum);
for (i = 0; i < 148 ; i++) {
sum += ((char*)&tar)[i];
}
for (i = 156; i < 512 ; i++) {
sum += ((char*)&tar)[i];
}
/* This field does not need special treatment (getOctal) */
if (sum != xstrtoul(tar.chksum, 8)) {
bb_error_msg_and_die("invalid tar header checksum");
}
/* 0 is reserved for high perf file, treat as normal file */
if (!tar.typeflag) tar.typeflag = '0';
parse_names = (tar.typeflag >= '0' && tar.typeflag <= '7');
/* getOctal trashes subsequent field, therefore we call it
* on fields in reverse order */
if (tar.devmajor[0]) {
unsigned minor = GET_OCTAL(tar.devminor);
unsigned major = GET_OCTAL(tar.devmajor);
file_header->device = makedev(major, minor);
}
file_header->link_name = NULL;
if (!linkname && parse_names && tar.linkname[0]) {
/* we trash magic[0] here, it's ok */
tar.linkname[sizeof(tar.linkname)] = '\0';
file_header->link_name = xstrdup(tar.linkname);
/* FIXME: what if we have non-link object with link_name? */
/* Will link_name be free()ed? */
}
file_header->mtime = GET_OCTAL(tar.mtime);
file_header->size = GET_OCTAL(tar.size);
file_header->gid = GET_OCTAL(tar.gid);
file_header->uid = GET_OCTAL(tar.uid);
/* Set bits 0-11 of the files mode */
file_header->mode = 07777 & GET_OCTAL(tar.mode);
file_header->name = NULL;
if (!longname && parse_names) {
/* we trash mode[0] here, it's ok */
tar.name[sizeof(tar.name)] = '\0';
if (tar.prefix[0]) {
/* and padding[0] */
tar.prefix[sizeof(tar.prefix)] = '\0';
file_header->name = concat_path_file(tar.prefix, tar.name);
} else
file_header->name = xstrdup(tar.name);
}
/* Set bits 12-15 of the files mode */
/* (typeflag was not trashed because chksum does not use getOctal) */
switch (tar.typeflag) {
/* busybox identifies hard links as being regular files with 0 size and a link name */
case '1':
file_header->mode |= S_IFREG;
break;
case '7':
/* case 0: */
case '0':
#if ENABLE_FEATURE_TAR_OLDGNU_COMPATIBILITY
if (last_char_is(file_header->name, '/')) {
file_header->mode |= S_IFDIR;
} else
#endif
file_header->mode |= S_IFREG;
break;
case '2':
file_header->mode |= S_IFLNK;
break;
case '3':
file_header->mode |= S_IFCHR;
break;
case '4':
file_header->mode |= S_IFBLK;
break;
case '5':
file_header->mode |= S_IFDIR;
break;
case '6':
file_header->mode |= S_IFIFO;
break;
#if ENABLE_FEATURE_TAR_GNU_EXTENSIONS
case 'L':
/* free: paranoia: tar with several consecutive longnames */
free(longname);
/* For paranoia reasons we allocate extra NUL char */
longname = xzalloc(file_header->size + 1);
/* We read ASCIZ string, including NUL */
xread(archive_handle->src_fd, longname, file_header->size);
archive_handle->offset += file_header->size;
/* return get_header_tar(archive_handle); */
/* gcc 4.1.1 didn't optimize it into jump */
/* so we will do it ourself, this also saves stack */
goto again;
case 'K':
free(linkname);
linkname = xzalloc(file_header->size + 1);
xread(archive_handle->src_fd, linkname, file_header->size);
archive_handle->offset += file_header->size;
/* return get_header_tar(archive_handle); */
goto again;
case 'D': /* GNU dump dir */
case 'M': /* Continuation of multi volume archive */
case 'N': /* Old GNU for names > 100 characters */
case 'S': /* Sparse file */
case 'V': /* Volume header */
#endif
case 'g': /* pax global header */
case 'x': { /* pax extended header */
off_t sz;
bb_error_msg("warning: skipping header '%c'", tar.typeflag);
sz = (file_header->size + 511) & ~(off_t)511;
archive_handle->offset += sz;
sz >>= 9; /* sz /= 512 but w/o contortions for signed div */
while (sz--)
xread(archive_handle->src_fd, &tar, 512);
/* return get_header_tar(archive_handle); */
goto again_after_align;
}
default:
bb_error_msg_and_die("unknown typeflag: 0x%x", tar.typeflag);
}
#if ENABLE_FEATURE_TAR_GNU_EXTENSIONS
if (longname) {
file_header->name = longname;
longname = NULL;
}
if (linkname) {
file_header->link_name = linkname;
linkname = NULL;
}
#endif
if (!strncmp(file_header->name, "/../"+1, 3)
|| strstr(file_header->name, "/../")
) {
bb_error_msg_and_die("name with '..' encountered: '%s'",
file_header->name);
}
/* Strip trailing '/' in directories */
/* Must be done after mode is set as '/' is used to check if its a directory */
cp = last_char_is(file_header->name, '/');
if (archive_handle->filter(archive_handle) == EXIT_SUCCESS) {
archive_handle->action_header(archive_handle->file_header);
/* Note that we kill the '/' only after action_header() */
/* (like GNU tar 1.15.1: verbose mode outputs "dir/dir/") */
if (cp) *cp = '\0';
archive_handle->flags |= ARCHIVE_EXTRACT_QUIET;
archive_handle->action_data(archive_handle);
llist_add_to(&(archive_handle->passed), file_header->name);
} else {
data_skip(archive_handle);
free(file_header->name);
}
archive_handle->offset += file_header->size;
free(file_header->link_name);
/* Do not free(file_header->name)! */
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,27 @@
/* vi: set sw=4 ts=4: */
/*
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
*/
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "libbb.h"
#include "unarchive.h"
char get_header_tar_bz2(archive_handle_t *archive_handle)
{
/* Can't lseek over pipes */
archive_handle->seek = seek_by_read;
archive_handle->src_fd = open_transformer(archive_handle->src_fd, uncompressStream);
archive_handle->offset = 0;
while (get_header_tar(archive_handle) == EXIT_SUCCESS) /**/;
/* Can only do one file at a time */
return EXIT_FAILURE;
}

View File

@ -0,0 +1,31 @@
/* vi: set sw=4 ts=4: */
/*
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
*/
#include <stdlib.h>
#include "libbb.h"
#include "unarchive.h"
char get_header_tar_gz(archive_handle_t *archive_handle)
{
unsigned char magic[2];
/* Can't lseek over pipes */
archive_handle->seek = seek_by_read;
xread(archive_handle->src_fd, &magic, 2);
if ((magic[0] != 0x1f) || (magic[1] != 0x8b)) {
bb_error_msg_and_die("invalid gzip magic");
}
check_header_gzip(archive_handle->src_fd);
archive_handle->src_fd = open_transformer(archive_handle->src_fd, inflate_gunzip);
archive_handle->offset = 0;
while (get_header_tar(archive_handle) == EXIT_SUCCESS) /**/;
/* Can only do one file at a time */
return EXIT_FAILURE;
}

View File

@ -0,0 +1,22 @@
/* vi: set sw=4 ts=4: */
/*
* Small lzma deflate implementation.
* Copyright (C) 2006 Aurelien Jacobs <aurel@gnuage.org>
*
* Licensed under GPL v2, see file LICENSE in this tarball for details.
*/
#include "unarchive.h"
char get_header_tar_lzma(archive_handle_t * archive_handle)
{
/* Can't lseek over pipes */
archive_handle->seek = seek_by_read;
archive_handle->src_fd = open_transformer(archive_handle->src_fd, unlzma);
archive_handle->offset = 0;
while (get_header_tar(archive_handle) == EXIT_SUCCESS) /**/;
/* Can only do one file at a time */
return EXIT_FAILURE;
}

View File

@ -0,0 +1,11 @@
/* vi: set sw=4 ts=4: */
/*
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
*/
#include <stdio.h>
#include "unarchive.h"
void header_list(const file_header_t *file_header)
{
puts(file_header->name);
}

View File

@ -0,0 +1,10 @@
/* vi: set sw=4 ts=4: */
/*
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
*/
#include <stdio.h>
#include "unarchive.h"
void header_skip(const file_header_t *file_header ATTRIBUTE_UNUSED)
{
}

View File

@ -0,0 +1,31 @@
/* vi: set sw=4 ts=4: */
/*
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
*/
#include "libbb.h"
#include "unarchive.h"
void header_verbose_list(const file_header_t *file_header)
{
struct tm *mtime = localtime(&(file_header->mtime));
printf("%s %d/%d %9"OFF_FMT"u %4u-%02u-%02u %02u:%02u:%02u %s",
bb_mode_string(file_header->mode),
file_header->uid,
file_header->gid,
file_header->size,
1900 + mtime->tm_year,
1 + mtime->tm_mon,
mtime->tm_mday,
mtime->tm_hour,
mtime->tm_min,
mtime->tm_sec,
file_header->name);
if (file_header->link_name) {
printf(" -> %s", file_header->link_name);
}
/* putchar isnt used anywhere else i dont think */
puts("");
}

View File

@ -0,0 +1,22 @@
/* vi: set sw=4 ts=4: */
/*
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
*/
#include "libbb.h"
#include "unarchive.h"
archive_handle_t *init_handle(void)
{
archive_handle_t *archive_handle;
/* Initialize default values */
archive_handle = xzalloc(sizeof(archive_handle_t));
archive_handle->file_header = xzalloc(sizeof(file_header_t));
archive_handle->action_header = header_skip;
archive_handle->action_data = data_skip;
archive_handle->filter = filter_accept_all;
archive_handle->seek = seek_by_jump;
return archive_handle;
}

View File

@ -0,0 +1,44 @@
/* vi: set sw=4 ts=4: */
/*
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
*/
#include <stdlib.h>
#include <unistd.h>
#include "libbb.h"
#include "unarchive.h"
/* transformer(), more than meets the eye */
int open_transformer(int src_fd,
USE_DESKTOP(long long) int (*transformer)(int src_fd, int dst_fd))
{
int fd_pipe[2];
int pid;
if (pipe(fd_pipe) != 0) {
bb_perror_msg_and_die("can't create pipe");
}
pid = fork();
if (pid == -1) {
bb_perror_msg_and_die("fork failed");
}
if (pid == 0) {
/* child process */
close(fd_pipe[0]); /* We don't wan't to read from the parent */
// FIXME: error check?
transformer(src_fd, fd_pipe[1]);
close(fd_pipe[1]); /* Send EOF */
close(src_fd);
exit(0);
/* notreached */
}
/* parent process */
close(fd_pipe[1]); /* Don't want to write to the child */
return fd_pipe[0];
}

View File

@ -0,0 +1,24 @@
/* vi: set sw=4 ts=4: */
/*
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
*/
#include <sys/types.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include "libbb.h"
#include "unarchive.h"
void seek_by_jump(const archive_handle_t *archive_handle, const unsigned int amount)
{
if (lseek(archive_handle->src_fd, (off_t) amount, SEEK_CUR) == (off_t) -1) {
#ifdef CONFIG_FEATURE_UNARCHIVE_TAPE
if (errno == ESPIPE) {
seek_by_read(archive_handle, amount);
} else
#endif
bb_perror_msg_and_die("seek failure");
}
}

View File

@ -0,0 +1,19 @@
/* vi: set sw=4 ts=4: */
/*
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
*/
#include <stdlib.h>
#include "unarchive.h"
#include "libbb.h"
/* If we are reading through a pipe(), or from stdin then we can't lseek,
* we must read and discard the data to skip over it.
*/
void seek_by_read(const archive_handle_t *archive_handle, const unsigned int jump_size)
{
if (jump_size) {
bb_copyfd_size(archive_handle->src_fd, -1, jump_size);
}
}

View File

@ -0,0 +1,22 @@
/* vi: set sw=4 ts=4: */
/*
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
*/
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include "unarchive.h"
#include "libbb.h"
void unpack_ar_archive(archive_handle_t *ar_archive)
{
char magic[7];
xread(ar_archive->src_fd, magic, 7);
if (strncmp(magic, "!<arch>", 7) != 0) {
bb_error_msg_and_die("invalid ar magic");
}
ar_archive->offset += 7;
while (get_header_ar(ar_archive) == EXIT_SUCCESS);
}

319
archival/rpm.c Normal file
View File

@ -0,0 +1,319 @@
/* vi: set sw=4 ts=4: */
/*
* Mini rpm applet for busybox
*
* Copyright (C) 2001,2002 by Laurence Anderson
*
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
*/
#include "busybox.h"
#include "unarchive.h"
#define RPM_HEADER_MAGIC "\216\255\350"
#define RPM_CHAR_TYPE 1
#define RPM_INT8_TYPE 2
#define RPM_INT16_TYPE 3
#define RPM_INT32_TYPE 4
/* #define RPM_INT64_TYPE 5 ---- These aren't supported (yet) */
#define RPM_STRING_TYPE 6
#define RPM_BIN_TYPE 7
#define RPM_STRING_ARRAY_TYPE 8
#define RPM_I18NSTRING_TYPE 9
#define RPMTAG_NAME 1000
#define RPMTAG_VERSION 1001
#define RPMTAG_RELEASE 1002
#define RPMTAG_SUMMARY 1004
#define RPMTAG_DESCRIPTION 1005
#define RPMTAG_BUILDTIME 1006
#define RPMTAG_BUILDHOST 1007
#define RPMTAG_SIZE 1009
#define RPMTAG_VENDOR 1011
#define RPMTAG_LICENSE 1014
#define RPMTAG_PACKAGER 1015
#define RPMTAG_GROUP 1016
#define RPMTAG_URL 1020
#define RPMTAG_PREIN 1023
#define RPMTAG_POSTIN 1024
#define RPMTAG_FILEFLAGS 1037
#define RPMTAG_FILEUSERNAME 1039
#define RPMTAG_FILEGROUPNAME 1040
#define RPMTAG_SOURCERPM 1044
#define RPMTAG_PREINPROG 1085
#define RPMTAG_POSTINPROG 1086
#define RPMTAG_PREFIXS 1098
#define RPMTAG_DIRINDEXES 1116
#define RPMTAG_BASENAMES 1117
#define RPMTAG_DIRNAMES 1118
#define RPMFILE_CONFIG (1 << 0)
#define RPMFILE_DOC (1 << 1)
enum rpm_functions_e {
rpm_query = 1,
rpm_install = 2,
rpm_query_info = 4,
rpm_query_package = 8,
rpm_query_list = 16,
rpm_query_list_doc = 32,
rpm_query_list_config = 64
};
typedef struct {
uint32_t tag; /* 4 byte tag */
uint32_t type; /* 4 byte type */
uint32_t offset; /* 4 byte offset */
uint32_t count; /* 4 byte count */
} rpm_index;
static void *map;
static rpm_index **mytags;
static int tagcount;
static void extract_cpio_gz(int fd);
static rpm_index **rpm_gettags(int fd, int *num_tags);
static int bsearch_rpmtag(const void *key, const void *item);
static char *rpm_getstring(int tag, int itemindex);
static int rpm_getint(int tag, int itemindex);
static int rpm_getcount(int tag);
static void fileaction_dobackup(char *filename, int fileref);
static void fileaction_setowngrp(char *filename, int fileref);
static void loop_through_files(int filetag, void (*fileaction)(char *filename, int fileref));
int rpm_main(int argc, char **argv)
{
int opt = 0, func = 0, rpm_fd, offset;
while ((opt = getopt(argc, argv, "iqpldc")) != -1) {
switch (opt) {
case 'i': // First arg: Install mode, with q: Information
if (!func) func |= rpm_install;
else func |= rpm_query_info;
break;
case 'q': // First arg: Query mode
if (!func) func |= rpm_query;
else bb_show_usage();
break;
case 'p': // Query a package
func |= rpm_query_package;
break;
case 'l': // List files in a package
func |= rpm_query_list;
break;
case 'd': // List doc files in a package (implies list)
func |= rpm_query_list;
func |= rpm_query_list_doc;
break;
case 'c': // List config files in a package (implies list)
func |= rpm_query_list;
func |= rpm_query_list_config;
break;
default:
bb_show_usage();
}
}
if (optind == argc) bb_show_usage();
while (optind < argc) {
rpm_fd = xopen(argv[optind], O_RDONLY);
mytags = rpm_gettags(rpm_fd, (int *) &tagcount);
offset = lseek(rpm_fd, 0, SEEK_CUR);
if (!mytags) { printf("Error reading rpm header\n"); exit(-1); }
map = mmap(0, offset > getpagesize() ? (offset + offset % getpagesize()) : getpagesize(), PROT_READ, MAP_PRIVATE, rpm_fd, 0); // Mimimum is one page
if (func & rpm_install) {
loop_through_files(RPMTAG_BASENAMES, fileaction_dobackup); /* Backup any config files */
extract_cpio_gz(rpm_fd); // Extact the archive
loop_through_files(RPMTAG_BASENAMES, fileaction_setowngrp); /* Set the correct file uid/gid's */
} else if (func & rpm_query && func & rpm_query_package) {
if (!((func & rpm_query_info) || (func & rpm_query_list))) { // If just a straight query, just give package name
printf("%s-%s-%s\n", rpm_getstring(RPMTAG_NAME, 0), rpm_getstring(RPMTAG_VERSION, 0), rpm_getstring(RPMTAG_RELEASE, 0));
}
if (func & rpm_query_info) {
/* Do the nice printout */
time_t bdate_time;
struct tm *bdate;
char bdatestring[50];
printf("Name : %-29sRelocations: %s\n", rpm_getstring(RPMTAG_NAME, 0), rpm_getstring(RPMTAG_PREFIXS, 0) ? rpm_getstring(RPMTAG_PREFIXS, 0) : "(not relocateable)");
printf("Version : %-34sVendor: %s\n", rpm_getstring(RPMTAG_VERSION, 0), rpm_getstring(RPMTAG_VENDOR, 0) ? rpm_getstring(RPMTAG_VENDOR, 0) : "(none)");
bdate_time = rpm_getint(RPMTAG_BUILDTIME, 0);
bdate = localtime((time_t *) &bdate_time);
strftime(bdatestring, 50, "%a %d %b %Y %T %Z", bdate);
printf("Release : %-30sBuild Date: %s\n", rpm_getstring(RPMTAG_RELEASE, 0), bdatestring);
printf("Install date: %-30sBuild Host: %s\n", "(not installed)", rpm_getstring(RPMTAG_BUILDHOST, 0));
printf("Group : %-30sSource RPM: %s\n", rpm_getstring(RPMTAG_GROUP, 0), rpm_getstring(RPMTAG_SOURCERPM, 0));
printf("Size : %-33dLicense: %s\n", rpm_getint(RPMTAG_SIZE, 0), rpm_getstring(RPMTAG_LICENSE, 0));
printf("URL : %s\n", rpm_getstring(RPMTAG_URL, 0));
printf("Summary : %s\n", rpm_getstring(RPMTAG_SUMMARY, 0));
printf("Description :\n%s\n", rpm_getstring(RPMTAG_DESCRIPTION, 0));
}
if (func & rpm_query_list) {
int count, it, flags;
count = rpm_getcount(RPMTAG_BASENAMES);
for (it = 0; it < count; it++) {
flags = rpm_getint(RPMTAG_FILEFLAGS, it);
switch ((func & rpm_query_list_doc) + (func & rpm_query_list_config))
{
case rpm_query_list_doc: if (!(flags & RPMFILE_DOC)) continue; break;
case rpm_query_list_config: if (!(flags & RPMFILE_CONFIG)) continue; break;
case rpm_query_list_doc + rpm_query_list_config: if (!((flags & RPMFILE_CONFIG) || (flags & RPMFILE_DOC))) continue; break;
}
printf("%s%s\n", rpm_getstring(RPMTAG_DIRNAMES, rpm_getint(RPMTAG_DIRINDEXES, it)), rpm_getstring(RPMTAG_BASENAMES, it));
}
}
}
optind++;
free (mytags);
}
return 0;
}
static void extract_cpio_gz(int fd) {
archive_handle_t *archive_handle;
unsigned char magic[2];
/* Initialise */
archive_handle = init_handle();
archive_handle->seek = seek_by_read;
//archive_handle->action_header = header_list;
archive_handle->action_data = data_extract_all;
archive_handle->flags |= ARCHIVE_PRESERVE_DATE;
archive_handle->flags |= ARCHIVE_CREATE_LEADING_DIRS;
archive_handle->src_fd = fd;
archive_handle->offset = 0;
xread(archive_handle->src_fd, &magic, 2);
if ((magic[0] != 0x1f) || (magic[1] != 0x8b)) {
bb_error_msg_and_die("invalid gzip magic");
}
check_header_gzip(archive_handle->src_fd);
xchdir("/"); // Install RPM's to root
archive_handle->src_fd = open_transformer(archive_handle->src_fd, inflate_gunzip);
archive_handle->offset = 0;
while (get_header_cpio(archive_handle) == EXIT_SUCCESS);
}
static rpm_index **rpm_gettags(int fd, int *num_tags)
{
rpm_index **tags = xzalloc(200 * sizeof(struct rpmtag *)); /* We should never need mode than 200, and realloc later */
int pass, tagindex = 0;
lseek(fd, 96, SEEK_CUR); /* Seek past the unused lead */
for (pass = 0; pass < 2; pass++) { /* 1st pass is the signature headers, 2nd is the main stuff */
struct {
char magic[3]; /* 3 byte magic: 0x8e 0xad 0xe8 */
uint8_t version; /* 1 byte version number */
uint32_t reserved; /* 4 bytes reserved */
uint32_t entries; /* Number of entries in header (4 bytes) */
uint32_t size; /* Size of store (4 bytes) */
} header;
rpm_index *tmpindex;
int storepos;
read(fd, &header, sizeof(header));
if (strncmp((char *) &header.magic, RPM_HEADER_MAGIC, 3) != 0) return NULL; /* Invalid magic */
if (header.version != 1) return NULL; /* This program only supports v1 headers */
header.size = ntohl(header.size);
header.entries = ntohl(header.entries);
storepos = lseek(fd,0,SEEK_CUR) + header.entries * 16;
while (header.entries--) {
tmpindex = tags[tagindex++] = xmalloc(sizeof(rpm_index));
read(fd, tmpindex, sizeof(rpm_index));
tmpindex->tag = ntohl(tmpindex->tag); tmpindex->type = ntohl(tmpindex->type); tmpindex->count = ntohl(tmpindex->count);
tmpindex->offset = storepos + ntohl(tmpindex->offset);
if (pass==0) tmpindex->tag -= 743;
}
lseek(fd, header.size, SEEK_CUR); /* Seek past store */
if (pass==0) lseek(fd, (8 - (lseek(fd,0,SEEK_CUR) % 8)) % 8, SEEK_CUR); /* Skip padding to 8 byte boundary after reading signature headers */
}
tags = realloc(tags, tagindex * sizeof(struct rpmtag *)); /* realloc tags to save space */
*num_tags = tagindex;
return tags; /* All done, leave the file at the start of the gzipped cpio archive */
}
static int bsearch_rpmtag(const void *key, const void *item)
{
int *tag = (int *)key;
rpm_index **tmp = (rpm_index **) item;
return (*tag - tmp[0]->tag);
}
static int rpm_getcount(int tag)
{
rpm_index **found;
found = bsearch(&tag, mytags, tagcount, sizeof(struct rpmtag *), bsearch_rpmtag);
if (!found) return 0;
else return found[0]->count;
}
static char *rpm_getstring(int tag, int itemindex)
{
rpm_index **found;
found = bsearch(&tag, mytags, tagcount, sizeof(struct rpmtag *), bsearch_rpmtag);
if (!found || itemindex >= found[0]->count) return NULL;
if (found[0]->type == RPM_STRING_TYPE || found[0]->type == RPM_I18NSTRING_TYPE || found[0]->type == RPM_STRING_ARRAY_TYPE) {
int n;
char *tmpstr = (char *) (map + found[0]->offset);
for (n=0; n < itemindex; n++) tmpstr = tmpstr + strlen(tmpstr) + 1;
return tmpstr;
} else return NULL;
}
static int rpm_getint(int tag, int itemindex)
{
rpm_index **found;
int n, *tmpint;
/* gcc throws warnings here when sizeof(void*)!=sizeof(int) ...
* it's ok to ignore it because tag won't be used as a pointer */
found = bsearch(&tag, mytags, tagcount, sizeof(struct rpmtag *), bsearch_rpmtag);
if (!found || itemindex >= found[0]->count) return -1;
tmpint = (int *) (map + found[0]->offset);
if (found[0]->type == RPM_INT32_TYPE) {
for (n=0; n<itemindex; n++) tmpint = (int *) ((void *) tmpint + 4);
return ntohl(*tmpint);
} else if (found[0]->type == RPM_INT16_TYPE) {
for (n=0; n<itemindex; n++) tmpint = (int *) ((void *) tmpint + 2);
return ntohs(*tmpint);
} else if (found[0]->type == RPM_INT8_TYPE) {
for (n=0; n<itemindex; n++) tmpint = (int *) ((void *) tmpint + 1);
return ntohs(*tmpint);
} else return -1;
}
static void fileaction_dobackup(char *filename, int fileref)
{
struct stat oldfile;
int stat_res;
char *newname;
if (rpm_getint(RPMTAG_FILEFLAGS, fileref) & RPMFILE_CONFIG) { /* Only need to backup config files */
stat_res = lstat (filename, &oldfile);
if (stat_res == 0 && S_ISREG(oldfile.st_mode)) { /* File already exists - really should check MD5's etc to see if different */
newname = xasprintf("%s.rpmorig", filename);
copy_file(filename, newname, FILEUTILS_RECUR | FILEUTILS_PRESERVE_STATUS);
remove_file(filename, FILEUTILS_RECUR | FILEUTILS_FORCE);
free(newname);
}
}
}
static void fileaction_setowngrp(char *filename, int fileref)
{
int uid, gid;
uid = bb_xgetpwnam(rpm_getstring(RPMTAG_FILEUSERNAME, fileref));
gid = bb_xgetgrnam(rpm_getstring(RPMTAG_FILEGROUPNAME, fileref));
chown(filename, uid, gid);
}
static void loop_through_files(int filetag, void (*fileaction)(char *filename, int fileref))
{
int count = 0;
while (rpm_getstring(filetag, count)) {
char * filename = xasprintf("%s%s",
rpm_getstring(RPMTAG_DIRNAMES, rpm_getint(RPMTAG_DIRINDEXES,
count)), rpm_getstring(RPMTAG_BASENAMES, count));
fileaction(filename, count++);
free(filename);
}
}

89
archival/rpm2cpio.c Normal file
View File

@ -0,0 +1,89 @@
/* vi: set sw=4 ts=4: */
/*
* Mini rpm2cpio implementation for busybox
*
* Copyright (C) 2001 by Laurence Anderson
*
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
*/
#include "busybox.h"
#include "unarchive.h"
#define RPM_MAGIC "\355\253\356\333"
#define RPM_HEADER_MAGIC "\216\255\350"
struct rpm_lead {
unsigned char magic[4];
uint8_t major, minor;
uint16_t type;
uint16_t archnum;
char name[66];
uint16_t osnum;
uint16_t signature_type;
char reserved[16];
};
struct rpm_header {
char magic[3]; /* 3 byte magic: 0x8e 0xad 0xe8 */
uint8_t version; /* 1 byte version number */
uint32_t reserved; /* 4 bytes reserved */
uint32_t entries; /* Number of entries in header (4 bytes) */
uint32_t size; /* Size of store (4 bytes) */
};
static void skip_header(int rpm_fd)
{
struct rpm_header header;
xread(rpm_fd, &header, sizeof(struct rpm_header));
if (strncmp((char *) &header.magic, RPM_HEADER_MAGIC, 3) != 0) {
bb_error_msg_and_die("invalid RPM header magic"); /* Invalid magic */
}
if (header.version != 1) {
bb_error_msg_and_die("unsupported RPM header version"); /* This program only supports v1 headers */
}
header.entries = ntohl(header.entries);
header.size = ntohl(header.size);
lseek (rpm_fd, 16 * header.entries, SEEK_CUR); /* Seek past index entries */
lseek (rpm_fd, header.size, SEEK_CUR); /* Seek past store */
}
/* No getopt required */
int rpm2cpio_main(int argc, char **argv)
{
struct rpm_lead lead;
int rpm_fd;
unsigned char magic[2];
if (argc == 1) {
rpm_fd = STDIN_FILENO;
} else {
rpm_fd = xopen(argv[1], O_RDONLY);
}
xread(rpm_fd, &lead, sizeof(struct rpm_lead));
if (strncmp((char *) &lead.magic, RPM_MAGIC, 4) != 0) {
bb_error_msg_and_die("invalid RPM magic"); /* Just check the magic, the rest is irrelevant */
}
/* Skip the signature header */
skip_header(rpm_fd);
lseek(rpm_fd, (8 - (lseek(rpm_fd, 0, SEEK_CUR) % 8)) % 8, SEEK_CUR);
/* Skip the main header */
skip_header(rpm_fd);
xread(rpm_fd, &magic, 2);
if ((magic[0] != 0x1f) || (magic[1] != 0x8b)) {
bb_error_msg_and_die("invalid gzip magic");
}
check_header_gzip(rpm_fd);
if (inflate_gunzip(rpm_fd, STDOUT_FILENO) < 0) {
bb_error_msg("error inflating");
}
close(rpm_fd);
return 0;
}

896
archival/tar.c Normal file
View File

@ -0,0 +1,896 @@
/* vi: set sw=4 ts=4: */
/*
* Mini tar implementation for busybox
*
* Modified to use common extraction code used by ar, cpio, dpkg-deb, dpkg
* Glenn McGrath <bug1@iinet.net.au>
*
* Note, that as of BusyBox-0.43, tar has been completely rewritten from the
* ground up. It still has remnants of the old code lying about, but it is
* very different now (i.e., cleaner, less global variables, etc.)
*
* Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
*
* Based in part in the tar implementation in sash
* Copyright (c) 1999 by David I. Bell
* Permission is granted to use, distribute, or modify this source,
* provided that this copyright notice remains intact.
* Permission to distribute sash derived code under the GPL has been granted.
*
* Based in part on the tar implementation from busybox-0.28
* Copyright (C) 1995 Bruce Perens
*
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
*/
#include "busybox.h"
#include "unarchive.h"
#include <fnmatch.h>
#include <getopt.h>
#if ENABLE_FEATURE_TAR_CREATE
/* Tar file constants */
#define TAR_BLOCK_SIZE 512
/* POSIX tar Header Block, from POSIX 1003.1-1990 */
#define NAME_SIZE 100
#define NAME_SIZE_STR "100"
struct TarHeader { /* byte offset */
char name[NAME_SIZE]; /* 0-99 */
char mode[8]; /* 100-107 */
char uid[8]; /* 108-115 */
char gid[8]; /* 116-123 */
char size[12]; /* 124-135 */
char mtime[12]; /* 136-147 */
char chksum[8]; /* 148-155 */
char typeflag; /* 156-156 */
char linkname[NAME_SIZE]; /* 157-256 */
char magic[6]; /* 257-262 */
char version[2]; /* 263-264 */
char uname[32]; /* 265-296 */
char gname[32]; /* 297-328 */
char devmajor[8]; /* 329-336 */
char devminor[8]; /* 337-344 */
char prefix[155]; /* 345-499 */
char padding[12]; /* 500-512 (pad to exactly the TAR_BLOCK_SIZE) */
};
typedef struct TarHeader TarHeader;
/*
** writeTarFile(), writeFileToTarball(), and writeTarHeader() are
** the only functions that deal with the HardLinkInfo structure.
** Even these functions use the xxxHardLinkInfo() functions.
*/
typedef struct HardLinkInfo HardLinkInfo;
struct HardLinkInfo {
HardLinkInfo *next; /* Next entry in list */
dev_t dev; /* Device number */
ino_t ino; /* Inode number */
short linkCount; /* (Hard) Link Count */
char name[1]; /* Start of filename (must be last) */
};
/* Some info to be carried along when creating a new tarball */
struct TarBallInfo {
int tarFd; /* Open-for-write file descriptor
for the tarball */
struct stat statBuf; /* Stat info for the tarball, letting
us know the inode and device that the
tarball lives, so we can avoid trying
to include the tarball into itself */
int verboseFlag; /* Whether to print extra stuff or not */
const llist_t *excludeList; /* List of files to not include */
HardLinkInfo *hlInfoHead; /* Hard Link Tracking Information */
HardLinkInfo *hlInfo; /* Hard Link Info for the current file */
};
typedef struct TarBallInfo TarBallInfo;
/* A nice enum with all the possible tar file content types */
enum TarFileType {
REGTYPE = '0', /* regular file */
REGTYPE0 = '\0', /* regular file (ancient bug compat) */
LNKTYPE = '1', /* hard link */
SYMTYPE = '2', /* symbolic link */
CHRTYPE = '3', /* character special */
BLKTYPE = '4', /* block special */
DIRTYPE = '5', /* directory */
FIFOTYPE = '6', /* FIFO special */
CONTTYPE = '7', /* reserved */
GNULONGLINK = 'K', /* GNU long (>100 chars) link name */
GNULONGNAME = 'L', /* GNU long (>100 chars) file name */
};
typedef enum TarFileType TarFileType;
/* Might be faster (and bigger) if the dev/ino were stored in numeric order;) */
static void addHardLinkInfo(HardLinkInfo ** hlInfoHeadPtr,
struct stat *statbuf,
const char *fileName)
{
/* Note: hlInfoHeadPtr can never be NULL! */
HardLinkInfo *hlInfo;
hlInfo = xmalloc(sizeof(HardLinkInfo) + strlen(fileName));
hlInfo->next = *hlInfoHeadPtr;
*hlInfoHeadPtr = hlInfo;
hlInfo->dev = statbuf->st_dev;
hlInfo->ino = statbuf->st_ino;
hlInfo->linkCount = statbuf->st_nlink;
strcpy(hlInfo->name, fileName);
}
static void freeHardLinkInfo(HardLinkInfo ** hlInfoHeadPtr)
{
HardLinkInfo *hlInfo;
HardLinkInfo *hlInfoNext;
if (hlInfoHeadPtr) {
hlInfo = *hlInfoHeadPtr;
while (hlInfo) {
hlInfoNext = hlInfo->next;
free(hlInfo);
hlInfo = hlInfoNext;
}
*hlInfoHeadPtr = NULL;
}
return;
}
/* Might be faster (and bigger) if the dev/ino were stored in numeric order;) */
static HardLinkInfo *findHardLinkInfo(HardLinkInfo * hlInfo, struct stat *statbuf)
{
while (hlInfo) {
if ((statbuf->st_ino == hlInfo->ino) && (statbuf->st_dev == hlInfo->dev))
break;
hlInfo = hlInfo->next;
}
return hlInfo;
}
/* Put an octal string into the specified buffer.
* The number is zero padded and possibly null terminated.
* Stores low-order bits only if whole value does not fit. */
static void putOctal(char *cp, int len, off_t value)
{
char tempBuffer[sizeof(off_t)*3+1];
char *tempString = tempBuffer;
int width;
width = sprintf(tempBuffer, "%0*"OFF_FMT"o", len, value);
tempString += (width - len);
/* If string has leading zeroes, we can drop one */
/* and field will have trailing '\0' */
/* (increases chances of compat with other tars) */
if (tempString[0] == '0')
tempString++;
/* Copy the string to the field */
memcpy(cp, tempString, len);
}
#define PUT_OCTAL(a, b) putOctal((a), sizeof(a), (b))
static void chksum_and_xwrite(int fd, struct TarHeader* hp)
{
const unsigned char *cp;
int chksum, size;
strcpy(hp->magic, "ustar ");
/* Calculate and store the checksum (i.e., the sum of all of the bytes of
* the header). The checksum field must be filled with blanks for the
* calculation. The checksum field is formatted differently from the
* other fields: it has 6 digits, a null, then a space -- rather than
* digits, followed by a null like the other fields... */
memset(hp->chksum, ' ', sizeof(hp->chksum));
cp = (const unsigned char *) hp;
chksum = 0;
size = sizeof(*hp);
do { chksum += *cp++; } while (--size);
putOctal(hp->chksum, sizeof(hp->chksum)-1, chksum);
/* Now write the header out to disk */
xwrite(fd, hp, sizeof(*hp));
}
#if ENABLE_FEATURE_TAR_GNU_EXTENSIONS
static void writeLongname(int fd, int type, const char *name, int dir)
{
static const struct {
char mode[8]; /* 100-107 */
char uid[8]; /* 108-115 */
char gid[8]; /* 116-123 */
char size[12]; /* 124-135 */
char mtime[12]; /* 136-147 */
} prefilled = {
"0000000",
"0000000",
"0000000",
"00000000000",
"00000000000",
};
struct TarHeader header;
int size;
dir = !!dir; /* normalize: 0/1 */
size = strlen(name) + 1 + dir; /* GNU tar uses strlen+1 */
/* + dir: account for possible '/' */
memset(&header, 0, sizeof(header));
strcpy(header.name, "././@LongLink");
memcpy(header.mode, prefilled.mode, sizeof(prefilled));
PUT_OCTAL(header.size, size);
header.typeflag = type;
chksum_and_xwrite(fd, &header);
/* Write filename[/] and pad the block. */
/* dir=0: writes 'name<NUL>', pads */
/* dir=1: writes 'name', writes '/<NUL>', pads */
dir *= 2;
xwrite(fd, name, size - dir);
xwrite(fd, "/", dir);
size = (-size) & (TAR_BLOCK_SIZE-1);
memset(&header, 0, size);
xwrite(fd, &header, size);
}
#endif
/* Write out a tar header for the specified file/directory/whatever */
void BUG_tar_header_size(void);
static int writeTarHeader(struct TarBallInfo *tbInfo,
const char *header_name, const char *fileName, struct stat *statbuf)
{
struct TarHeader header;
if (sizeof(header) != 512)
BUG_tar_header_size();
memset(&header, 0, sizeof(struct TarHeader));
strncpy(header.name, header_name, sizeof(header.name));
/* POSIX says to mask mode with 07777. */
PUT_OCTAL(header.mode, statbuf->st_mode & 07777);
PUT_OCTAL(header.uid, statbuf->st_uid);
PUT_OCTAL(header.gid, statbuf->st_gid);
memset(header.size, '0', sizeof(header.size)-1); /* Regular file size is handled later */
PUT_OCTAL(header.mtime, statbuf->st_mtime);
/* Enter the user and group names */
safe_strncpy(header.uname, get_cached_username(statbuf->st_uid), sizeof(header.uname));
safe_strncpy(header.gname, get_cached_groupname(statbuf->st_gid), sizeof(header.gname));
if (tbInfo->hlInfo) {
/* This is a hard link */
header.typeflag = LNKTYPE;
strncpy(header.linkname, tbInfo->hlInfo->name,
sizeof(header.linkname));
#if ENABLE_FEATURE_TAR_GNU_EXTENSIONS
/* Write out long linkname if needed */
if (header.linkname[sizeof(header.linkname)-1])
writeLongname(tbInfo->tarFd, GNULONGLINK,
tbInfo->hlInfo->name, 0);
#endif
} else if (S_ISLNK(statbuf->st_mode)) {
char *lpath = xreadlink(fileName);
if (!lpath) /* Already printed err msg inside xreadlink() */
return FALSE;
header.typeflag = SYMTYPE;
strncpy(header.linkname, lpath, sizeof(header.linkname));
#if ENABLE_FEATURE_TAR_GNU_EXTENSIONS
/* Write out long linkname if needed */
if (header.linkname[sizeof(header.linkname)-1])
writeLongname(tbInfo->tarFd, GNULONGLINK, lpath, 0);
#else
/* If it is larger than 100 bytes, bail out */
if (header.linkname[sizeof(header.linkname)-1]) {
free(lpath);
bb_error_msg("names longer than "NAME_SIZE_STR" chars not supported");
return FALSE;
}
#endif
free(lpath);
} else if (S_ISDIR(statbuf->st_mode)) {
header.typeflag = DIRTYPE;
/* Append '/' only if there is a space for it */
if (!header.name[sizeof(header.name)-1])
header.name[strlen(header.name)] = '/';
} else if (S_ISCHR(statbuf->st_mode)) {
header.typeflag = CHRTYPE;
PUT_OCTAL(header.devmajor, major(statbuf->st_rdev));
PUT_OCTAL(header.devminor, minor(statbuf->st_rdev));
} else if (S_ISBLK(statbuf->st_mode)) {
header.typeflag = BLKTYPE;
PUT_OCTAL(header.devmajor, major(statbuf->st_rdev));
PUT_OCTAL(header.devminor, minor(statbuf->st_rdev));
} else if (S_ISFIFO(statbuf->st_mode)) {
header.typeflag = FIFOTYPE;
} else if (S_ISREG(statbuf->st_mode)) {
if (sizeof(statbuf->st_size) > 4
&& statbuf->st_size > (off_t)0777777777777LL
) {
bb_error_msg_and_die("cannot store file '%s' "
"of size %"OFF_FMT"d, aborting",
fileName, statbuf->st_size);
}
header.typeflag = REGTYPE;
PUT_OCTAL(header.size, statbuf->st_size);
} else {
bb_error_msg("%s: unknown file type", fileName);
return FALSE;
}
#if ENABLE_FEATURE_TAR_GNU_EXTENSIONS
/* Write out long name if needed */
/* (we, like GNU tar, output long linkname *before* long name) */
if (header.name[sizeof(header.name)-1])
writeLongname(tbInfo->tarFd, GNULONGNAME,
header_name, S_ISDIR(statbuf->st_mode));
#endif
/* Now write the header out to disk */
chksum_and_xwrite(tbInfo->tarFd, &header);
/* Now do the verbose thing (or not) */
if (tbInfo->verboseFlag) {
FILE *vbFd = stdout;
if (tbInfo->tarFd == STDOUT_FILENO) /* If the archive goes to stdout, verbose to stderr */
vbFd = stderr;
/* GNU "tar cvvf" prints "extended" listing a-la "ls -l" */
/* We don't have such excesses here: for us "v" == "vv" */
/* '/' is probably a GNUism */
fprintf(vbFd, "%s%s\n", header_name,
S_ISDIR(statbuf->st_mode) ? "/" : "");
}
return TRUE;
}
# if ENABLE_FEATURE_TAR_FROM
static int exclude_file(const llist_t *excluded_files, const char *file)
{
while (excluded_files) {
if (excluded_files->data[0] == '/') {
if (fnmatch(excluded_files->data, file,
FNM_PATHNAME | FNM_LEADING_DIR) == 0)
return 1;
} else {
const char *p;
for (p = file; p[0] != '\0'; p++) {
if ((p == file || p[-1] == '/') && p[0] != '/' &&
fnmatch(excluded_files->data, p,
FNM_PATHNAME | FNM_LEADING_DIR) == 0)
return 1;
}
}
excluded_files = excluded_files->link;
}
return 0;
}
# else
#define exclude_file(excluded_files, file) 0
# endif
static int writeFileToTarball(const char *fileName, struct stat *statbuf,
void *userData, int depth)
{
struct TarBallInfo *tbInfo = (struct TarBallInfo *) userData;
const char *header_name;
int inputFileFd = -1;
/*
* Check to see if we are dealing with a hard link.
* If so -
* Treat the first occurance of a given dev/inode as a file while
* treating any additional occurances as hard links. This is done
* by adding the file information to the HardLinkInfo linked list.
*/
tbInfo->hlInfo = NULL;
if (statbuf->st_nlink > 1) {
tbInfo->hlInfo = findHardLinkInfo(tbInfo->hlInfoHead, statbuf);
if (tbInfo->hlInfo == NULL)
addHardLinkInfo(&tbInfo->hlInfoHead, statbuf, fileName);
}
/* It is against the rules to archive a socket */
if (S_ISSOCK(statbuf->st_mode)) {
bb_error_msg("%s: socket ignored", fileName);
return TRUE;
}
/* It is a bad idea to store the archive we are in the process of creating,
* so check the device and inode to be sure that this particular file isn't
* the new tarball */
if (tbInfo->statBuf.st_dev == statbuf->st_dev &&
tbInfo->statBuf.st_ino == statbuf->st_ino) {
bb_error_msg("%s: file is the archive; skipping", fileName);
return TRUE;
}
header_name = fileName;
while (header_name[0] == '/') {
static int alreadyWarned = FALSE;
if (alreadyWarned == FALSE) {
bb_error_msg("removing leading '/' from member names");
alreadyWarned = TRUE;
}
header_name++;
}
#if !ENABLE_FEATURE_TAR_GNU_EXTENSIONS
if (strlen(fileName) >= NAME_SIZE) {
bb_error_msg("names longer than "NAME_SIZE_STR" chars not supported");
return TRUE;
}
#endif
if (header_name[0] == '\0')
return TRUE;
if (exclude_file(tbInfo->excludeList, header_name))
return SKIP;
/* Is this a regular file? */
if (tbInfo->hlInfo == NULL && S_ISREG(statbuf->st_mode)) {
/* open the file we want to archive, and make sure all is well */
inputFileFd = open(fileName, O_RDONLY);
if (inputFileFd < 0) {
bb_perror_msg("%s: cannot open", fileName);
return FALSE;
}
}
/* Add an entry to the tarball */
if (writeTarHeader(tbInfo, header_name, fileName, statbuf) == FALSE) {
return FALSE;
}
/* If it was a regular file, write out the body */
if (inputFileFd >= 0) {
off_t readSize = 0;
/* write the file to the archive */
readSize = bb_copyfd_size(inputFileFd, tbInfo->tarFd, statbuf->st_size);
/* readSize < 0 means that error was already reported */
if (readSize != statbuf->st_size && readSize >= 0) {
/* Deadly. We record size into header first, */
/* and then write out file. If file shrinks in between, */
/* tar will be corrupted. So bail out. */
/* NB: GNU tar 1.16 warns and pads with zeroes */
/* or even seeks back and updates header */
bb_error_msg_and_die("short read from %s, aborting", fileName);
}
/* Check that file did not grow in between? */
/* if (safe_read(inputFileFd,1) == 1) warn but continue? */
close(inputFileFd);
/* Pad the file up to the tar block size */
/* (a few tricks here in the name of code size) */
readSize = (-(int)readSize) & (TAR_BLOCK_SIZE-1);
memset(bb_common_bufsiz1, 0, readSize);
xwrite(tbInfo->tarFd, bb_common_bufsiz1, readSize);
}
return TRUE;
}
static int writeTarFile(const int tar_fd, const int verboseFlag,
const unsigned long dereferenceFlag, const llist_t *include,
const llist_t *exclude, const int gzip)
{
pid_t gzipPid = 0;
int errorFlag = FALSE;
struct TarBallInfo tbInfo;
tbInfo.hlInfoHead = NULL;
fchmod(tar_fd, 0644);
tbInfo.tarFd = tar_fd;
tbInfo.verboseFlag = verboseFlag;
/* Store the stat info for the tarball's file, so
* can avoid including the tarball into itself.... */
if (fstat(tbInfo.tarFd, &tbInfo.statBuf) < 0)
bb_perror_msg_and_die("cannot stat tar file");
if ((ENABLE_FEATURE_TAR_GZIP || ENABLE_FEATURE_TAR_BZIP2) && gzip) {
int gzipDataPipe[2] = { -1, -1 };
int gzipStatusPipe[2] = { -1, -1 };
volatile int vfork_exec_errno = 0;
char *zip_exec = (gzip == 1) ? "gzip" : "bzip2";
if (pipe(gzipDataPipe) < 0 || pipe(gzipStatusPipe) < 0)
bb_perror_msg_and_die("pipe");
signal(SIGPIPE, SIG_IGN); /* we only want EPIPE on errors */
# if __GNUC__
/* Avoid vfork clobbering */
(void) &include;
(void) &errorFlag;
(void) &zip_exec;
# endif
gzipPid = vfork();
if (gzipPid == 0) {
dup2(gzipDataPipe[0], 0);
close(gzipDataPipe[1]);
dup2(tbInfo.tarFd, 1);
close(gzipStatusPipe[0]);
fcntl(gzipStatusPipe[1], F_SETFD, FD_CLOEXEC); /* close on exec shows success */
execlp(zip_exec, zip_exec, "-f", NULL);
vfork_exec_errno = errno;
close(gzipStatusPipe[1]);
exit(-1);
} else if (gzipPid > 0) {
close(gzipDataPipe[0]);
close(gzipStatusPipe[1]);
while (1) {
char buf;
int n = full_read(gzipStatusPipe[0], &buf, 1);
if (n == 0 && vfork_exec_errno != 0) {
errno = vfork_exec_errno;
bb_perror_msg_and_die("cannot exec %s", zip_exec);
} else if ((n < 0) && (errno == EAGAIN || errno == EINTR))
continue; /* try it again */
break;
}
close(gzipStatusPipe[0]);
tbInfo.tarFd = gzipDataPipe[1];
} else bb_perror_msg_and_die("vfork gzip");
}
tbInfo.excludeList = exclude;
/* Read the directory/files and iterate over them one at a time */
while (include) {
if (!recursive_action(include->data, TRUE, dereferenceFlag,
FALSE, writeFileToTarball, writeFileToTarball, &tbInfo, 0))
{
errorFlag = TRUE;
}
include = include->link;
}
/* Write two empty blocks to the end of the archive */
memset(bb_common_bufsiz1, 0, 2*TAR_BLOCK_SIZE);
xwrite(tbInfo.tarFd, bb_common_bufsiz1, 2*TAR_BLOCK_SIZE);
/* To be pedantically correct, we would check if the tarball
* is smaller than 20 tar blocks, and pad it if it was smaller,
* but that isn't necessary for GNU tar interoperability, and
* so is considered a waste of space */
/* Close so the child process (if any) will exit */
close(tbInfo.tarFd);
/* Hang up the tools, close up shop, head home */
if (ENABLE_FEATURE_CLEAN_UP)
freeHardLinkInfo(&tbInfo.hlInfoHead);
if (errorFlag)
bb_error_msg("error exit delayed from previous errors");
if (gzipPid && waitpid(gzipPid, NULL, 0) == -1)
bb_error_msg("waitpid failed");
return !errorFlag;
}
#else
int writeTarFile(const int tar_fd, const int verboseFlag,
const unsigned long dereferenceFlag, const llist_t *include,
const llist_t *exclude, const int gzip);
#endif /* tar_create */
#if ENABLE_FEATURE_TAR_FROM
static llist_t *append_file_list_to_list(llist_t *list)
{
FILE *src_stream;
llist_t *cur = list;
llist_t *tmp;
char *line;
llist_t *newlist = NULL;
while (cur) {
src_stream = xfopen(cur->data, "r");
tmp = cur;
cur = cur->link;
free(tmp);
while ((line = xmalloc_getline(src_stream)) != NULL) {
/* kill trailing '/' unless the string is just "/" */
char *cp = last_char_is(line, '/');
if (cp > line)
*cp = '\0';
llist_add_to(&newlist, line);
}
fclose(src_stream);
}
return newlist;
}
#else
#define append_file_list_to_list(x) 0
#endif
#if ENABLE_FEATURE_TAR_COMPRESS
static char get_header_tar_Z(archive_handle_t *archive_handle)
{
/* Can't lseek over pipes */
archive_handle->seek = seek_by_read;
/* do the decompression, and cleanup */
if (xread_char(archive_handle->src_fd) != 0x1f
|| xread_char(archive_handle->src_fd) != 0x9d
) {
bb_error_msg_and_die("invalid magic");
}
archive_handle->src_fd = open_transformer(archive_handle->src_fd, uncompress);
archive_handle->offset = 0;
while (get_header_tar(archive_handle) == EXIT_SUCCESS)
/* nothing */;
/* Can only do one file at a time */
return EXIT_FAILURE;
}
#else
#define get_header_tar_Z 0
#endif
enum {
OPTBIT_KEEP_OLD = 7,
USE_FEATURE_TAR_CREATE( OPTBIT_CREATE ,)
USE_FEATURE_TAR_CREATE( OPTBIT_DEREFERENCE ,)
USE_FEATURE_TAR_BZIP2( OPTBIT_BZIP2 ,)
USE_FEATURE_TAR_LZMA( OPTBIT_LZMA ,)
USE_FEATURE_TAR_FROM( OPTBIT_INCLUDE_FROM,)
USE_FEATURE_TAR_FROM( OPTBIT_EXCLUDE_FROM,)
USE_FEATURE_TAR_GZIP( OPTBIT_GZIP ,)
USE_FEATURE_TAR_COMPRESS(OPTBIT_COMPRESS ,)
OPTBIT_NOPRESERVE_OWN,
OPTBIT_NOPRESERVE_PERM,
OPT_TEST = 1 << 0, // t
OPT_EXTRACT = 1 << 1, // x
OPT_BASEDIR = 1 << 2, // C
OPT_TARNAME = 1 << 3, // f
OPT_2STDOUT = 1 << 4, // O
OPT_P = 1 << 5, // p
OPT_VERBOSE = 1 << 6, // v
OPT_KEEP_OLD = 1 << 7, // k
OPT_CREATE = USE_FEATURE_TAR_CREATE( (1<<OPTBIT_CREATE )) + 0, // c
OPT_DEREFERENCE = USE_FEATURE_TAR_CREATE( (1<<OPTBIT_DEREFERENCE )) + 0, // h
OPT_BZIP2 = USE_FEATURE_TAR_BZIP2( (1<<OPTBIT_BZIP2 )) + 0, // j
OPT_LZMA = USE_FEATURE_TAR_LZMA( (1<<OPTBIT_LZMA )) + 0, // a
OPT_INCLUDE_FROM = USE_FEATURE_TAR_FROM( (1<<OPTBIT_INCLUDE_FROM)) + 0, // T
OPT_EXCLUDE_FROM = USE_FEATURE_TAR_FROM( (1<<OPTBIT_EXCLUDE_FROM)) + 0, // X
OPT_GZIP = USE_FEATURE_TAR_GZIP( (1<<OPTBIT_GZIP )) + 0, // z
OPT_COMPRESS = USE_FEATURE_TAR_COMPRESS((1<<OPTBIT_COMPRESS )) + 0, // Z
OPT_NOPRESERVE_OWN = 1 << OPTBIT_NOPRESERVE_OWN , // no-same-owner
OPT_NOPRESERVE_PERM = 1 << OPTBIT_NOPRESERVE_PERM, // no-same-permissions
};
#if ENABLE_FEATURE_TAR_LONG_OPTIONS
static const struct option tar_long_options[] = {
{ "list", 0, NULL, 't' },
{ "extract", 0, NULL, 'x' },
{ "directory", 1, NULL, 'C' },
{ "file", 1, NULL, 'f' },
{ "to-stdout", 0, NULL, 'O' },
{ "same-permissions", 0, NULL, 'p' },
{ "verbose", 0, NULL, 'v' },
{ "keep-old", 0, NULL, 'k' },
# if ENABLE_FEATURE_TAR_CREATE
{ "create", 0, NULL, 'c' },
{ "dereference", 0, NULL, 'h' },
# endif
# if ENABLE_FEATURE_TAR_BZIP2
{ "bzip2", 0, NULL, 'j' },
# endif
# if ENABLE_FEATURE_TAR_LZMA
{ "lzma", 0, NULL, 'a' },
# endif
# if ENABLE_FEATURE_TAR_FROM
{ "files-from", 1, NULL, 'T' },
{ "exclude-from", 1, NULL, 'X' },
# endif
# if ENABLE_FEATURE_TAR_GZIP
{ "gzip", 0, NULL, 'z' },
# endif
# if ENABLE_FEATURE_TAR_COMPRESS
{ "compress", 0, NULL, 'Z' },
# endif
{ "no-same-owner", 0, NULL, 0xfd },
{ "no-same-permissions",0, NULL, 0xfe },
/* --exclude takes next bit position in option mask, */
/* therefore we have to either put it _after_ --no-same-perm */
/* or add OPT[BIT]_EXCLUDE before OPT[BIT]_NOPRESERVE_OWN */
# if ENABLE_FEATURE_TAR_FROM
{ "exclude", 1, NULL, 0xff },
# endif
{ 0, 0, 0, 0 }
};
#endif
int tar_main(int argc, char **argv)
{
char (*get_header_ptr)(archive_handle_t *) = get_header_tar;
archive_handle_t *tar_handle;
char *base_dir = NULL;
const char *tar_filename = "-";
unsigned opt;
int verboseFlag = 0;
llist_t *excludes = NULL;
/* Initialise default values */
tar_handle = init_handle();
tar_handle->flags = ARCHIVE_CREATE_LEADING_DIRS
| ARCHIVE_PRESERVE_DATE
| ARCHIVE_EXTRACT_UNCONDITIONAL;
/* Prepend '-' to the first argument if required */
opt_complementary = "--:" // first arg is options
"tt:vv:" // count -t,-v
"?:" // bail out with usage instead of error return
"X::T::" // cumulative lists
"\xff::" // cumulative lists for --exclude
USE_FEATURE_TAR_CREATE("c:") "t:x:" // at least one of these is reqd
USE_FEATURE_TAR_CREATE("c--tx:t--cx:x--ct") // mutually exclusive
SKIP_FEATURE_TAR_CREATE("t--x:x--t"); // mutually exclusive
#if ENABLE_FEATURE_TAR_LONG_OPTIONS
applet_long_options = tar_long_options;
#endif
opt = getopt32(argc, argv,
"txC:f:Opvk"
USE_FEATURE_TAR_CREATE( "ch" )
USE_FEATURE_TAR_BZIP2( "j" )
USE_FEATURE_TAR_LZMA( "a" )
USE_FEATURE_TAR_FROM( "T:X:")
USE_FEATURE_TAR_GZIP( "z" )
USE_FEATURE_TAR_COMPRESS("Z" )
,
&base_dir, // -C dir
&tar_filename, // -f filename
USE_FEATURE_TAR_FROM(&(tar_handle->accept),) // T
USE_FEATURE_TAR_FROM(&(tar_handle->reject),) // X
USE_FEATURE_TAR_FROM(&excludes ,) // --exclude
&verboseFlag, // combined count for -t and -v
&verboseFlag // combined count for -t and -v
);
if (verboseFlag) tar_handle->action_header = header_verbose_list;
if (verboseFlag == 1) tar_handle->action_header = header_list;
if (opt & OPT_EXTRACT)
tar_handle->action_data = data_extract_all;
if (opt & OPT_2STDOUT)
tar_handle->action_data = data_extract_to_stdout;
if (opt & OPT_KEEP_OLD)
tar_handle->flags &= ~ARCHIVE_EXTRACT_UNCONDITIONAL;
if (opt & OPT_NOPRESERVE_OWN)
tar_handle->flags |= ARCHIVE_NOPRESERVE_OWN;
if (opt & OPT_NOPRESERVE_PERM)
tar_handle->flags |= ARCHIVE_NOPRESERVE_PERM;
if (opt & OPT_GZIP)
get_header_ptr = get_header_tar_gz;
if (opt & OPT_BZIP2)
get_header_ptr = get_header_tar_bz2;
if (opt & OPT_LZMA)
get_header_ptr = get_header_tar_lzma;
if (opt & OPT_COMPRESS)
get_header_ptr = get_header_tar_Z;
if (ENABLE_FEATURE_TAR_FROM) {
tar_handle->reject = append_file_list_to_list(tar_handle->reject);
/* Append excludes to reject */
while (excludes) {
llist_t *temp = excludes->link;
excludes->link = tar_handle->reject;
tar_handle->reject = excludes;
excludes = temp;
}
tar_handle->accept = append_file_list_to_list(tar_handle->accept);
}
/* Check if we are reading from stdin */
if (argv[optind] && *argv[optind] == '-') {
/* Default is to read from stdin, so just skip to next arg */
optind++;
}
/* Setup an array of filenames to work with */
/* TODO: This is the same as in ar, separate function ? */
while (optind < argc) {
/* kill trailing '/' unless the string is just "/" */
char *cp = last_char_is(argv[optind], '/');
if (cp > argv[optind])
*cp = '\0';
llist_add_to(&tar_handle->accept, argv[optind]);
optind++;
}
tar_handle->accept = rev_llist(tar_handle->accept);
if (tar_handle->accept || tar_handle->reject)
tar_handle->filter = filter_accept_reject_list;
/* Open the tar file */
{
FILE *tar_stream;
int flags;
if (opt & OPT_CREATE) {
/* Make sure there is at least one file to tar up. */
if (tar_handle->accept == NULL)
bb_error_msg_and_die("empty archive");
tar_stream = stdout;
/* Mimicking GNU tar 1.15.1: */
flags = O_WRONLY|O_CREAT|O_TRUNC;
/* was doing unlink; open(O_WRONLY|O_CREAT|O_EXCL); why? */
} else {
tar_stream = stdin;
flags = O_RDONLY;
}
if (tar_filename[0] == '-' && !tar_filename[1]) {
tar_handle->src_fd = fileno(tar_stream);
tar_handle->seek = seek_by_read;
} else {
tar_handle->src_fd = xopen(tar_filename, flags);
}
}
if (base_dir)
xchdir(base_dir);
/* create an archive */
if (opt & OPT_CREATE) {
int zipMode = 0;
if (ENABLE_FEATURE_TAR_GZIP && get_header_ptr == get_header_tar_gz)
zipMode = 1;
if (ENABLE_FEATURE_TAR_BZIP2 && get_header_ptr == get_header_tar_bz2)
zipMode = 2;
writeTarFile(tar_handle->src_fd, verboseFlag, opt & OPT_DEREFERENCE,
tar_handle->accept,
tar_handle->reject, zipMode);
/* NB: writeTarFile() closes tar_handle->src_fd */
return EXIT_SUCCESS;
}
while (get_header_ptr(tar_handle) == EXIT_SUCCESS)
/* nothing */;
/* Check that every file that should have been extracted was */
while (tar_handle->accept) {
if (!find_list_entry(tar_handle->reject, tar_handle->accept->data)
&& !find_list_entry(tar_handle->passed, tar_handle->accept->data)
) {
bb_error_msg_and_die("%s: not found in archive",
tar_handle->accept->data);
}
tar_handle->accept = tar_handle->accept->link;
}
if (ENABLE_FEATURE_CLEAN_UP /* && tar_handle->src_fd != STDIN_FILENO */)
close(tar_handle->src_fd);
return EXIT_SUCCESS;
}

94
archival/uncompress.c Normal file
View File

@ -0,0 +1,94 @@
/* vi: set sw=4 ts=4: */
/*
* Uncompress applet for busybox (c) 2002 Glenn McGrath
*
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
*/
#include "busybox.h"
#include "unarchive.h"
#define GUNZIP_TO_STDOUT 1
#define GUNZIP_FORCE 2
int uncompress_main(int argc, char **argv)
{
int status = EXIT_SUCCESS;
unsigned long flags;
flags = getopt32(argc, argv, "cf");
while (optind < argc) {
char *compressed_file = argv[optind++];
char *delete_path = NULL;
char *uncompressed_file = NULL;
int src_fd;
int dst_fd;
if (strcmp(compressed_file, "-") == 0) {
src_fd = STDIN_FILENO;
flags |= GUNZIP_TO_STDOUT;
} else {
src_fd = xopen(compressed_file, O_RDONLY);
}
/* Check that the input is sane. */
if (isatty(src_fd) && ((flags & GUNZIP_FORCE) == 0)) {
bb_error_msg_and_die
("compressed data not read from terminal. Use -f to force it.");
}
/* Set output filename and number */
if (flags & GUNZIP_TO_STDOUT) {
dst_fd = STDOUT_FILENO;
} else {
struct stat stat_buf;
char *extension;
uncompressed_file = xstrdup(compressed_file);
extension = strrchr(uncompressed_file, '.');
if (!extension || (strcmp(extension, ".Z") != 0)) {
bb_error_msg_and_die("invalid extension");
}
*extension = '\0';
/* Open output file */
xstat(compressed_file, &stat_buf);
dst_fd = xopen3(uncompressed_file,
O_WRONLY | O_CREAT | O_TRUNC,
stat_buf.st_mode);
/* If unzip succeeds remove the old file */
delete_path = compressed_file;
}
/* do the decompression, and cleanup */
if ((xread_char(src_fd) != 0x1f) || (xread_char(src_fd) != 0x9d)) {
bb_error_msg_and_die("invalid magic");
}
status = uncompress(src_fd, dst_fd);
if ((status != EXIT_SUCCESS) && (uncompressed_file)) {
/* Unzip failed, remove the uncomressed file instead of compressed file */
delete_path = uncompressed_file;
}
if (dst_fd != STDOUT_FILENO) {
close(dst_fd);
}
if (src_fd != STDIN_FILENO) {
close(src_fd);
}
/* delete_path will be NULL if in test mode or from stdin */
if (delete_path && (unlink(delete_path) == -1)) {
bb_error_msg_and_die("cannot remove %s", delete_path);
}
free(uncompressed_file);
}
return status;
}

64
archival/unlzma.c Normal file
View File

@ -0,0 +1,64 @@
/* vi: set sw=4 ts=4: */
/*
* Small lzma deflate implementation.
* Copyright (C) 2006 Aurelien Jacobs <aurel@gnuage.org>
*
* Based on bunzip.c from busybox
*
* Licensed under GPL v2, see file LICENSE in this tarball for details.
*/
/* Why our g[un]zip/bunzip2 are so ugly compared to this beauty? */
#include "busybox.h"
#include "unarchive.h"
#define UNLZMA_OPT_STDOUT 1
int unlzma_main(int argc, char **argv)
{
USE_DESKTOP(long long) int status;
char *filename;
unsigned opt;
int src_fd, dst_fd;
opt = getopt32(argc, argv, "c");
/* Set input filename and number */
filename = argv[optind];
if (filename && (filename[0] != '-') && (filename[1] != '\0')) {
/* Open input file */
src_fd = xopen(filename, O_RDONLY);
} else {
src_fd = STDIN_FILENO;
filename = 0;
}
/* if called as lzmacat force the stdout flag */
if ((opt & UNLZMA_OPT_STDOUT) || applet_name[4] == 'c')
filename = 0;
if (filename) {
struct stat stat_buf;
/* bug: char *extension = filename + strlen(filename) - 5; */
char *extension = strrchr(filename, '.');
if (!extension || strcmp(extension, ".lzma") != 0) {
bb_error_msg_and_die("invalid extension");
}
xstat(filename, &stat_buf);
*extension = '\0';
dst_fd = xopen3(filename, O_WRONLY | O_CREAT | O_TRUNC,
stat_buf.st_mode);
} else
dst_fd = STDOUT_FILENO;
status = unlzma(src_fd, dst_fd);
if (filename) {
if (status >= 0) /* if success delete src, else delete dst */
filename[strlen(filename)] = '.';
if (unlink(filename) < 0) {
bb_error_msg_and_die("cannot remove %s", filename);
}
}
return (status < 0);
}

386
archival/unzip.c Normal file
View File

@ -0,0 +1,386 @@
/* vi: set sw=4 ts=4: */
/*
* Mini unzip implementation for busybox
*
* Copyright (C) 2004 by Ed Clark
*
* Loosely based on original busybox unzip applet by Laurence Anderson.
* All options and features should work in this version.
*
* Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
*/
/* For reference see
* http://www.pkware.com/company/standards/appnote/
* http://www.info-zip.org/pub/infozip/doc/appnote-iz-latest.zip
*/
/* TODO
* Endian issues
* Zip64 + other methods
* Improve handling of zip format, ie.
* - deferred CRC, comp. & uncomp. lengths (zip header flags bit 3)
* - unix file permissions, etc.
* - central directory
*/
#include "busybox.h"
#include "unarchive.h"
#define ZIP_FILEHEADER_MAGIC SWAP_LE32(0x04034b50)
#define ZIP_CDS_MAGIC SWAP_LE32(0x02014b50)
#define ZIP_CDS_END_MAGIC SWAP_LE32(0x06054b50)
#define ZIP_DD_MAGIC SWAP_LE32(0x08074b50)
extern unsigned int gunzip_crc;
extern unsigned int gunzip_bytes_out;
typedef union {
unsigned char raw[26];
struct {
unsigned short version; /* 0-1 */
unsigned short flags; /* 2-3 */
unsigned short method; /* 4-5 */
unsigned short modtime; /* 6-7 */
unsigned short moddate; /* 8-9 */
unsigned int crc32 ATTRIBUTE_PACKED; /* 10-13 */
unsigned int cmpsize ATTRIBUTE_PACKED; /* 14-17 */
unsigned int ucmpsize ATTRIBUTE_PACKED; /* 18-21 */
unsigned short filename_len; /* 22-23 */
unsigned short extra_len; /* 24-25 */
} formatted ATTRIBUTE_PACKED;
} zip_header_t;
static void unzip_skip(int fd, off_t skip)
{
if (lseek(fd, skip, SEEK_CUR) == (off_t)-1) {
if ((errno != ESPIPE) || (bb_copyfd_size(fd, -1, skip) != skip)) {
bb_error_msg_and_die("seek failure");
}
}
}
static void unzip_create_leading_dirs(char *fn)
{
/* Create all leading directories */
char *name = xstrdup(fn);
if (bb_make_directory(dirname(name), 0777, FILEUTILS_RECUR)) {
bb_error_msg_and_die("exiting"); /* bb_make_directory is noisy */
}
free(name);
}
static int unzip_extract(zip_header_t *zip_header, int src_fd, int dst_fd)
{
if (zip_header->formatted.method == 0) {
/* Method 0 - stored (not compressed) */
off_t size = zip_header->formatted.ucmpsize;
if (size && (bb_copyfd_size(src_fd, dst_fd, size) != size)) {
bb_error_msg_and_die("cannot complete extraction");
}
} else {
/* Method 8 - inflate */
inflate_init(zip_header->formatted.cmpsize);
inflate_unzip(src_fd, dst_fd);
inflate_cleanup();
/* Validate decompression - crc */
if (zip_header->formatted.crc32 != (gunzip_crc ^ 0xffffffffL)) {
bb_error_msg("invalid compressed data--%s error", "crc");
return 1;
}
/* Validate decompression - size */
if (zip_header->formatted.ucmpsize != gunzip_bytes_out) {
bb_error_msg("invalid compressed data--%s error", "length");
return 1;
}
}
return 0;
}
int unzip_main(int argc, char **argv)
{
zip_header_t zip_header;
enum {v_silent, v_normal, v_list} verbosity = v_normal;
enum {o_prompt, o_never, o_always} overwrite = o_prompt;
unsigned int total_size = 0;
unsigned int total_entries = 0;
int src_fd = -1, dst_fd = -1;
char *src_fn = NULL, *dst_fn = NULL;
llist_t *zaccept = NULL;
llist_t *zreject = NULL;
char *base_dir = NULL;
int failed, i, opt, opt_range = 0, list_header_done = 0;
char key_buf[512];
struct stat stat_buf;
while((opt = getopt(argc, argv, "-d:lnopqx")) != -1) {
switch (opt_range) {
case 0: /* Options */
switch (opt) {
case 'l': /* List */
verbosity = v_list;
break;
case 'n': /* Never overwrite existing files */
overwrite = o_never;
break;
case 'o': /* Always overwrite existing files */
overwrite = o_always;
break;
case 'p': /* Extract files to stdout and fall through to set verbosity */
dst_fd = STDOUT_FILENO;
case 'q': /* Be quiet */
verbosity = (verbosity == v_normal) ? v_silent : verbosity;
break;
case 1 : /* The zip file */
src_fn = xstrndup(optarg, strlen(optarg)+4);
opt_range++;
break;
default:
bb_show_usage();
}
break;
case 1: /* Include files */
if (opt == 1) {
llist_add_to(&zaccept, optarg);
} else if (opt == 'd') {
base_dir = optarg;
opt_range += 2;
} else if (opt == 'x') {
opt_range++;
} else {
bb_show_usage();
}
break;
case 2 : /* Exclude files */
if (opt == 1) {
llist_add_to(&zreject, optarg);
} else if (opt == 'd') { /* Extract to base directory */
base_dir = optarg;
opt_range++;
} else {
bb_show_usage();
}
break;
default:
bb_show_usage();
}
}
if (src_fn == NULL) {
bb_show_usage();
}
/* Open input file */
if (strcmp("-", src_fn) == 0) {
src_fd = STDIN_FILENO;
/* Cannot use prompt mode since zip data is arriving on STDIN */
overwrite = (overwrite == o_prompt) ? o_never : overwrite;
} else {
static const char *const extn[] = {"", ".zip", ".ZIP"};
int orig_src_fn_len = strlen(src_fn);
for(i = 0; (i < 3) && (src_fd == -1); i++) {
strcpy(src_fn + orig_src_fn_len, extn[i]);
src_fd = open(src_fn, O_RDONLY);
}
if (src_fd == -1) {
src_fn[orig_src_fn_len] = 0;
bb_error_msg_and_die("cannot open %s, %s.zip, %s.ZIP", src_fn, src_fn, src_fn);
}
}
/* Change dir if necessary */
if (base_dir)
xchdir(base_dir);
if (verbosity != v_silent)
printf("Archive: %s\n", src_fn);
failed = 0;
while (1) {
unsigned int magic;
/* Check magic number */
xread(src_fd, &magic, 4);
if (magic == ZIP_CDS_MAGIC) {
break;
} else if (magic != ZIP_FILEHEADER_MAGIC) {
bb_error_msg_and_die("invalid zip magic %08X", magic);
}
/* Read the file header */
xread(src_fd, zip_header.raw, 26);
zip_header.formatted.version = SWAP_LE32(zip_header.formatted.version);
zip_header.formatted.flags = SWAP_LE32(zip_header.formatted.flags);
zip_header.formatted.method = SWAP_LE32(zip_header.formatted.method);
zip_header.formatted.modtime = SWAP_LE32(zip_header.formatted.modtime);
zip_header.formatted.moddate = SWAP_LE32(zip_header.formatted.moddate);
zip_header.formatted.crc32 = SWAP_LE32(zip_header.formatted.crc32);
zip_header.formatted.cmpsize = SWAP_LE32(zip_header.formatted.cmpsize);
zip_header.formatted.ucmpsize = SWAP_LE32(zip_header.formatted.ucmpsize);
zip_header.formatted.filename_len = SWAP_LE32(zip_header.formatted.filename_len);
zip_header.formatted.extra_len = SWAP_LE32(zip_header.formatted.extra_len);
if ((zip_header.formatted.method != 0) && (zip_header.formatted.method != 8)) {
bb_error_msg_and_die("unsupported compression method %d", zip_header.formatted.method);
}
/* Read filename */
free(dst_fn);
dst_fn = xzalloc(zip_header.formatted.filename_len + 1);
xread(src_fd, dst_fn, zip_header.formatted.filename_len);
/* Skip extra header bytes */
unzip_skip(src_fd, zip_header.formatted.extra_len);
if ((verbosity == v_list) && !list_header_done){
printf(" Length Date Time Name\n"
" -------- ---- ---- ----\n");
list_header_done = 1;
}
/* Filter zip entries */
if (find_list_entry(zreject, dst_fn) ||
(zaccept && !find_list_entry(zaccept, dst_fn))) { /* Skip entry */
i = 'n';
} else { /* Extract entry */
total_size += zip_header.formatted.ucmpsize;
if (verbosity == v_list) { /* List entry */
unsigned int dostime = zip_header.formatted.modtime | (zip_header.formatted.moddate << 16);
printf("%9u %02u-%02u-%02u %02u:%02u %s\n",
zip_header.formatted.ucmpsize,
(dostime & 0x01e00000) >> 21,
(dostime & 0x001f0000) >> 16,
(((dostime & 0xfe000000) >> 25) + 1980) % 100,
(dostime & 0x0000f800) >> 11,
(dostime & 0x000007e0) >> 5,
dst_fn);
total_entries++;
i = 'n';
} else if (dst_fd == STDOUT_FILENO) { /* Extracting to STDOUT */
i = -1;
} else if (last_char_is(dst_fn, '/')) { /* Extract directory */
if (stat(dst_fn, &stat_buf) == -1) {
if (errno != ENOENT) {
bb_perror_msg_and_die("cannot stat '%s'",dst_fn);
}
if (verbosity == v_normal) {
printf(" creating: %s\n", dst_fn);
}
unzip_create_leading_dirs(dst_fn);
if (bb_make_directory(dst_fn, 0777, 0)) {
bb_error_msg_and_die("exiting");
}
} else {
if (!S_ISDIR(stat_buf.st_mode)) {
bb_error_msg_and_die("'%s' exists but is not directory", dst_fn);
}
}
i = 'n';
} else { /* Extract file */
_check_file:
if (stat(dst_fn, &stat_buf) == -1) { /* File does not exist */
if (errno != ENOENT) {
bb_perror_msg_and_die("cannot stat '%s'",dst_fn);
}
i = 'y';
} else { /* File already exists */
if (overwrite == o_never) {
i = 'n';
} else if (S_ISREG(stat_buf.st_mode)) { /* File is regular file */
if (overwrite == o_always) {
i = 'y';
} else {
printf("replace %s? [y]es, [n]o, [A]ll, [N]one, [r]ename: ", dst_fn);
if (!fgets(key_buf, 512, stdin)) {
bb_perror_msg_and_die("cannot read input");
}
i = key_buf[0];
}
} else { /* File is not regular file */
bb_error_msg_and_die("'%s' exists but is not regular file",dst_fn);
}
}
}
}
switch (i) {
case 'A':
overwrite = o_always;
case 'y': /* Open file and fall into unzip */
unzip_create_leading_dirs(dst_fn);
dst_fd = xopen(dst_fn, O_WRONLY | O_CREAT | O_TRUNC);
case -1: /* Unzip */
if (verbosity == v_normal) {
printf(" inflating: %s\n", dst_fn);
}
if (unzip_extract(&zip_header, src_fd, dst_fd)) {
failed = 1;
}
if (dst_fd != STDOUT_FILENO) {
/* closing STDOUT is potentially bad for future business */
close(dst_fd);
}
break;
case 'N':
overwrite = o_never;
case 'n':
/* Skip entry data */
unzip_skip(src_fd, zip_header.formatted.cmpsize);
break;
case 'r':
/* Prompt for new name */
printf("new name: ");
if (!fgets(key_buf, 512, stdin)) {
bb_perror_msg_and_die("cannot read input");
}
free(dst_fn);
dst_fn = xstrdup(key_buf);
chomp(dst_fn);
goto _check_file;
default:
printf("error: invalid response [%c]\n",(char)i);
goto _check_file;
}
/* Data descriptor section */
if (zip_header.formatted.flags & 4) {
/* skip over duplicate crc, compressed size and uncompressed size */
unzip_skip(src_fd, 12);
}
}
if (verbosity == v_list) {
printf(" -------- -------\n"
"%9d %d files\n", total_size, total_entries);
}
return failed;
}

105
console-tools/Config.in Normal file
View File

@ -0,0 +1,105 @@
#
# For a description of the syntax of this configuration file,
# see scripts/kbuild/config-language.txt.
#
menu "Console Utilities"
config CHVT
bool "chvt"
default n
help
This program is used to change to another terminal.
Example: chvt 4 (change to terminal /dev/tty4)
config CLEAR
bool "clear"
default n
help
This program clears the terminal screen.
config DEALLOCVT
bool "deallocvt"
default n
help
This program deallocates unused virtual consoles.
config DUMPKMAP
bool "dumpkmap"
default n
help
This program dumps the kernel's keyboard translation table to
stdout, in binary format. You can then use loadkmap to load it.
config LOADFONT
bool "loadfont"
default n
help
This program loads a console font from standard input.
config LOADKMAP
bool "loadkmap"
default n
help
This program loads a keyboard translation table from
standard input.
config OPENVT
bool "openvt"
default n
help
This program is used to start a command on an unused
virtual terminal.
config RESET
bool "reset"
default n
help
This program is used to reset the terminal screen, if it
gets messed up.
config RESIZE
bool "resize"
default n
help
This program is used to (re)set the width and height of your current
terminal.
config FEATURE_RESIZE_PRINT
bool "print environment variables"
default n
depends on RESIZE
help
Prints the newly set size (number of columns and rows) of
the terminal.
E.g.:
COLUMNS=80;LINES=44;export COLUMNS LINES;
config SETCONSOLE
bool "setconsole"
default n
help
This program redirects the system console to another device,
like the current tty while logged in via telnet.
config FEATURE_SETCONSOLE_LONG_OPTIONS
bool "Enable long options"
default n
depends on SET_CONSOLE && GETOPT_LONG
help
Support long options for the setconsole applet.
config SETKEYCODES
bool "setkeycodes"
default n
help
This program loads entries into the kernel's scancode-to-keycode
map, allowing unusual keyboards to generate usable keycodes.
config SETLOGCONS
bool "setlogcons"
default n
help
This program redirects the output console of kernel messages.
endmenu

19
console-tools/Kbuild Normal file
View File

@ -0,0 +1,19 @@
# Makefile for busybox
#
# Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
#
# Licensed under the GPL v2, see the file LICENSE in this tarball.
lib-y:=
lib-$(CONFIG_CHVT) += chvt.o
lib-$(CONFIG_CLEAR) += clear.o
lib-$(CONFIG_DEALLOCVT) += deallocvt.o
lib-$(CONFIG_DUMPKMAP) += dumpkmap.o
lib-$(CONFIG_SETCONSOLE) += setconsole.o
lib-$(CONFIG_LOADFONT) += loadfont.o
lib-$(CONFIG_LOADKMAP) += loadkmap.o
lib-$(CONFIG_OPENVT) += openvt.o
lib-$(CONFIG_RESET) += reset.o
lib-$(CONFIG_RESIZE) += resize.o
lib-$(CONFIG_SETKEYCODES) += setkeycodes.o
lib-$(CONFIG_SETLOGCONS) += setlogcons.o

33
console-tools/chvt.c Normal file
View File

@ -0,0 +1,33 @@
/* vi: set sw=4 ts=4: */
/*
* Mini chvt implementation for busybox
*
* Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
*
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
*/
#include "busybox.h"
/* From <linux/vt.h> */
enum {
VT_ACTIVATE = 0x5606, /* make vt active */
VT_WAITACTIVE = 0x5607 /* wait for vt active */
};
int chvt_main(int argc, char **argv)
{
int fd, num;
if (argc != 2) {
bb_show_usage();
}
fd = get_console_fd();
num = xatoul_range(argv[1], 1, 63);
if ((-1 == ioctl(fd, VT_ACTIVATE, num))
|| (-1 == ioctl(fd, VT_WAITACTIVE, num))) {
bb_perror_msg_and_die("ioctl");
}
return EXIT_SUCCESS;
}

21
console-tools/clear.c Normal file
View File

@ -0,0 +1,21 @@
/* vi: set sw=4 ts=4: */
/*
* Mini clear implementation for busybox
*
* Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
*
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
*
*/
/* no options, no getopt */
#include <stdio.h>
#include <stdlib.h>
#include "busybox.h"
int clear_main(int argc, char **argv)
{
return printf("\033[H\033[J") != 6;
}

37
console-tools/deallocvt.c Normal file
View File

@ -0,0 +1,37 @@
/* vi: set sw=4 ts=4: */
/*
* Disallocate virtual terminal(s)
*
* Copyright (C) 2003 by Tito Ragusa <farmatito@tiscali.it>
* Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
*
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
*/
/* no options, no getopt */
#include "busybox.h"
/* From <linux/vt.h> */
enum { VT_DISALLOCATE = 0x5608 }; /* free memory associated to vt */
int deallocvt_main(int argc, char *argv[])
{
/* num = 0 deallocate all unused consoles */
int num = 0;
switch (argc) {
case 2:
num = xatoul_range(argv[1], 1, 63);
/* Fallthrough */
case 1:
break;
default:
bb_show_usage();
}
if (-1 == ioctl(get_console_fd(), VT_DISALLOCATE, num)) {
bb_perror_msg_and_die("VT_DISALLOCATE");
}
return EXIT_SUCCESS;
}

65
console-tools/dumpkmap.c Normal file
View File

@ -0,0 +1,65 @@
/* vi: set sw=4 ts=4: */
/*
* Mini dumpkmap implementation for busybox
*
* Copyright (C) Arne Bernin <arne@matrix.loopback.org>
*
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
*
*/
#include "busybox.h"
/* From <linux/kd.h> */
struct kbentry {
unsigned char kb_table;
unsigned char kb_index;
unsigned short kb_value;
};
#define KDGKBENT 0x4B46 /* gets one entry in translation table */
/* From <linux/keyboard.h> */
#define NR_KEYS 128
#define MAX_NR_KEYMAPS 256
int dumpkmap_main(int argc, char **argv)
{
struct kbentry ke;
int i, j, fd;
char flags[MAX_NR_KEYMAPS], magic[] = "bkeymap";
if (argc >= 2 && *argv[1] == '-')
bb_show_usage();
fd = xopen(CURRENT_VC, O_RDWR);
write(1, magic, 7);
/* Here we want to set everything to 0 except for indexes:
* [0-2] [4-6] [8-10] [12] */
memset(flags, 0x00, MAX_NR_KEYMAPS);
memset(flags, 0x01, 13);
flags[3] = flags[7] = flags[11] = 0;
/* dump flags */
write(1, flags, MAX_NR_KEYMAPS);
for (i = 0; i < MAX_NR_KEYMAPS; i++) {
if (flags[i] == 1) {
for (j = 0; j < NR_KEYS; j++) {
ke.kb_index = j;
ke.kb_table = i;
if (ioctl(fd, KDGKBENT, &ke) < 0) {
bb_perror_msg("ioctl failed with %s, %s, %p",
(char *)&ke.kb_index,
(char *)&ke.kb_table,
&ke.kb_value);
} else {
write(1, (void*)&ke.kb_value, 2);
}
}
}
}
close(fd);
return EXIT_SUCCESS;
}

193
console-tools/loadfont.c Normal file
View File

@ -0,0 +1,193 @@
/* vi: set sw=4 ts=4: */
/*
* loadfont.c - Eugene Crosser & Andries Brouwer
*
* Version 0.96bb
*
* Loads the console font, and possibly the corresponding screen map(s).
* (Adapted for busybox by Matej Vela.)
*/
#include "busybox.h"
#include <sys/kd.h>
enum {
PSF_MAGIC1 = 0x36,
PSF_MAGIC2 = 0x04,
PSF_MODE512 = 0x01,
PSF_MODEHASTAB = 0x02,
PSF_MAXMODE = 0x03,
PSF_SEPARATOR = 0xFFFF
};
struct psf_header {
unsigned char magic1, magic2; /* Magic number */
unsigned char mode; /* PSF font mode */
unsigned char charsize; /* Character size */
};
#define PSF_MAGIC_OK(x) ((x).magic1 == PSF_MAGIC1 && (x).magic2 == PSF_MAGIC2)
static void loadnewfont(int fd);
int loadfont_main(int argc, char **argv)
{
int fd;
if (argc != 1)
bb_show_usage();
fd = xopen(CURRENT_VC, O_RDWR);
loadnewfont(fd);
return EXIT_SUCCESS;
}
static void do_loadfont(int fd, unsigned char *inbuf, int unit, int fontsize)
{
char buf[16384];
int i;
memset(buf, 0, sizeof(buf));
if (unit < 1 || unit > 32)
bb_error_msg_and_die("bad character size %d", unit);
for (i = 0; i < fontsize; i++)
memcpy(buf + (32 * i), inbuf + (unit * i), unit);
#if defined( PIO_FONTX ) && !defined( __sparc__ )
{
struct consolefontdesc cfd;
cfd.charcount = fontsize;
cfd.charheight = unit;
cfd.chardata = buf;
if (ioctl(fd, PIO_FONTX, &cfd) == 0)
return; /* success */
bb_perror_msg("PIO_FONTX ioctl error (trying PIO_FONT)");
}
#endif
if (ioctl(fd, PIO_FONT, buf))
bb_perror_msg_and_die("PIO_FONT ioctl error");
}
static void
do_loadtable(int fd, unsigned char *inbuf, int tailsz, int fontsize)
{
struct unimapinit advice;
struct unimapdesc ud;
struct unipair *up;
int ct = 0, maxct;
int glyph;
u_short unicode;
maxct = tailsz; /* more than enough */
up = (struct unipair *) xmalloc(maxct * sizeof(struct unipair));
for (glyph = 0; glyph < fontsize; glyph++) {
while (tailsz >= 2) {
unicode = (((u_short) inbuf[1]) << 8) + inbuf[0];
tailsz -= 2;
inbuf += 2;
if (unicode == PSF_SEPARATOR)
break;
up[ct].unicode = unicode;
up[ct].fontpos = glyph;
ct++;
}
}
/* Note: after PIO_UNIMAPCLR and before PIO_UNIMAP
this printf did not work on many kernels */
advice.advised_hashsize = 0;
advice.advised_hashstep = 0;
advice.advised_hashlevel = 0;
if (ioctl(fd, PIO_UNIMAPCLR, &advice)) {
#ifdef ENOIOCTLCMD
if (errno == ENOIOCTLCMD) {
bb_error_msg("it seems this kernel is older than 1.1.92");
bb_error_msg_and_die("no Unicode mapping table loaded");
} else
#endif
bb_perror_msg_and_die("PIO_UNIMAPCLR");
}
ud.entry_ct = ct;
ud.entries = up;
if (ioctl(fd, PIO_UNIMAP, &ud)) {
bb_perror_msg_and_die("PIO_UNIMAP");
}
}
static void loadnewfont(int fd)
{
int unit;
unsigned char inbuf[32768]; /* primitive */
unsigned int inputlth, offset;
/*
* We used to look at the length of the input file
* with stat(); now that we accept compressed files,
* just read the entire file.
*/
inputlth = fread(inbuf, 1, sizeof(inbuf), stdin);
if (ferror(stdin))
bb_perror_msg_and_die("error reading input font");
/* use malloc/realloc in case of giant files;
maybe these do not occur: 16kB for the font,
and 16kB for the map leaves 32 unicode values
for each font position */
if (!feof(stdin))
bb_perror_msg_and_die("font too large");
/* test for psf first */
{
struct psf_header psfhdr;
int fontsize;
int hastable;
unsigned int head0, head;
if (inputlth < sizeof(struct psf_header))
goto no_psf;
psfhdr = *(struct psf_header *) &inbuf[0];
if (!PSF_MAGIC_OK(psfhdr))
goto no_psf;
if (psfhdr.mode > PSF_MAXMODE)
bb_error_msg_and_die("unsupported psf file mode");
fontsize = ((psfhdr.mode & PSF_MODE512) ? 512 : 256);
#if !defined( PIO_FONTX ) || defined( __sparc__ )
if (fontsize != 256)
bb_error_msg_and_die("only fontsize 256 supported");
#endif
hastable = (psfhdr.mode & PSF_MODEHASTAB);
unit = psfhdr.charsize;
head0 = sizeof(struct psf_header);
head = head0 + fontsize * unit;
if (head > inputlth || (!hastable && head != inputlth))
bb_error_msg_and_die("input file: bad length");
do_loadfont(fd, inbuf + head0, unit, fontsize);
if (hastable)
do_loadtable(fd, inbuf + head, inputlth - head, fontsize);
return;
}
no_psf:
/* file with three code pages? */
if (inputlth == 9780) {
offset = 40;
unit = 16;
} else {
/* bare font */
if (inputlth & 0377)
bb_error_msg_and_die("bad input file size");
offset = 0;
unit = inputlth / 256;
}
do_loadfont(fd, inbuf + offset, unit, 256);
}

61
console-tools/loadkmap.c Normal file
View File

@ -0,0 +1,61 @@
/* vi: set sw=4 ts=4: */
/*
* Mini loadkmap implementation for busybox
*
* Copyright (C) 1998 Enrique Zanardi <ezanardi@ull.es>
*
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
*
*/
#include "busybox.h"
#define BINARY_KEYMAP_MAGIC "bkeymap"
/* From <linux/kd.h> */
struct kbentry {
unsigned char kb_table;
unsigned char kb_index;
unsigned short kb_value;
};
/* sets one entry in translation table */
#define KDSKBENT 0x4B47
/* From <linux/keyboard.h> */
#define NR_KEYS 128
#define MAX_NR_KEYMAPS 256
int loadkmap_main(int argc, char **argv)
{
struct kbentry ke;
int i, j, fd;
u_short ibuff[NR_KEYS];
char flags[MAX_NR_KEYMAPS];
char buff[7];
if (argc != 1)
bb_show_usage();
fd = xopen(CURRENT_VC, O_RDWR);
xread(0, buff, 7);
if (strncmp(buff, BINARY_KEYMAP_MAGIC, 7))
bb_error_msg_and_die("this is not a valid binary keymap");
xread(0, flags, MAX_NR_KEYMAPS);
for (i = 0; i < MAX_NR_KEYMAPS; i++) {
if (flags[i] == 1) {
xread(0, ibuff, NR_KEYS * sizeof(u_short));
for (j = 0; j < NR_KEYS; j++) {
ke.kb_index = j;
ke.kb_table = i;
ke.kb_value = ibuff[j];
ioctl(fd, KDSKBENT, &ke);
}
}
}
if (ENABLE_FEATURE_CLEAN_UP) close(fd);
return 0;
}

45
console-tools/openvt.c Normal file
View File

@ -0,0 +1,45 @@
/* vi: set sw=4 ts=4: */
/*
* openvt.c - open a vt to run a command.
*
* busyboxed by Quy Tonthat <quy@signal3.com>
* hacked by Tito <farmatito@tiscali.it>
*
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
*/
/* getopt not needed */
#include "busybox.h"
int openvt_main(int argc, char **argv)
{
int fd;
char vtname[sizeof(VC_FORMAT) + 2];
if (argc < 3) {
bb_show_usage();
}
/* check for illegal vt number: < 1 or > 63 */
sprintf(vtname, VC_FORMAT, (int)xatoul_range(argv[1], 1, 63));
if (fork() == 0) {
/* leave current vt */
if (setsid() < 0) {
bb_perror_msg_and_die("setsid");
}
close(0); /* so that new vt becomes stdin */
/* and grab new one */
fd = xopen(vtname, O_RDWR);
/* Reassign stdout and sterr */
dup2(fd, STDOUT_FILENO);
dup2(fd, STDERR_FILENO);
execvp(argv[2], &argv[2]);
_exit(1);
}
return EXIT_SUCCESS;
}

32
console-tools/reset.c Normal file
View File

@ -0,0 +1,32 @@
/* vi: set sw=4 ts=4: */
/*
* Mini reset implementation for busybox
*
* Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
* Written by Erik Andersen and Kent Robotti <robotti@metconnect.com>
*
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
*/
/* no options, no getopt */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "busybox.h"
int reset_main(int argc, char **argv)
{
if (isatty(1)) {
/* See 'man 4 console_codes' for details:
* "ESC c" -- Reset
* "ESC ( K" -- Select user mapping
* "ESC [ J" -- Erase display
* "ESC [ 0 m" -- Reset all display attributes
* "ESC [ ? 25 h" -- Make cursor visible.
*/
printf("\033c\033(K\033[J\033[0m\033[?25h");
}
return EXIT_SUCCESS;
}

38
console-tools/resize.c Normal file
View File

@ -0,0 +1,38 @@
/* vi: set sw=4 ts=4: */
/*
* resize - set terminal width and height.
*
* Copyright 2006 Bernhard Fischer
*
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
*/
/* no options, no getopt */
#include "busybox.h"
int resize_main(int argc, char **argv)
{
struct termios old, new;
struct winsize w = {0,0,0,0};
int ret;
tcgetattr(STDOUT_FILENO, &old); /* fiddle echo */
new = old;
new.c_cflag |= (CLOCAL | CREAD);
new.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
tcsetattr(STDOUT_FILENO, TCSANOW, &new);
/* save_cursor_pos 7
* scroll_whole_screen [r
* put_cursor_waaaay_off [$x;$yH
* get_cursor_pos [6n
* restore_cursor_pos 8
*/
printf("\0337\033[r\033[999;999H\033[6n");
scanf("\033[%hu;%huR", &w.ws_row, &w.ws_col);
ret = ioctl(STDOUT_FILENO, TIOCSWINSZ, &w);
printf("\0338");
tcsetattr(STDOUT_FILENO, TCSANOW, &old);
if (ENABLE_FEATURE_RESIZE_PRINT)
printf("COLUMNS=%d;LINES=%d;export COLUMNS LINES;\n",
w.ws_col, w.ws_row);
return ret;
}

View File

@ -0,0 +1,47 @@
/* vi: set sw=4 ts=4: */
/*
* setconsole.c - redirect system console output
*
* Copyright (C) 2004,2005 Enrik Berkhan <Enrik.Berkhan@inka.de>
*
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
*/
#include "busybox.h"
#if ENABLE_FEATURE_SETCONSOLE_LONG_OPTIONS
static const struct option setconsole_long_options[] = {
{ "reset", 0, NULL, 'r' },
{ 0, 0, 0, 0 }
};
#endif
#define OPT_SETCONS_RESET 1
int setconsole_main(int argc, char **argv)
{
unsigned long flags;
const char *device = CURRENT_TTY;
#if ENABLE_FEATURE_SETCONSOLE_LONG_OPTIONS
applet_long_options = setconsole_long_options;
#endif
flags = getopt32(argc, argv, "r");
if (argc - optind > 1)
bb_show_usage();
if (argc - optind == 1) {
if (flags & OPT_SETCONS_RESET)
bb_show_usage();
device = argv[optind];
} else {
if (flags & OPT_SETCONS_RESET)
device = CONSOLE_DEV;
}
if (-1 == ioctl(xopen(device, O_RDONLY), TIOCCONS)) {
bb_perror_msg_and_die("TIOCCONS");
}
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,53 @@
/* vi: set sw=4 ts=4: */
/*
* setkeycodes
*
* Copyright (C) 1994-1998 Andries E. Brouwer <aeb@cwi.nl>
*
* Adjusted for BusyBox by Erik Andersen <andersen@codepoet.org>
*
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
*/
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include "busybox.h"
/* From <linux/kd.h> */
struct kbkeycode {
unsigned int scancode, keycode;
};
enum {
KDSETKEYCODE = 0x4B4D /* write kernel keycode table entry */
};
extern int
setkeycodes_main(int argc, char** argv)
{
int fd, sc;
struct kbkeycode a;
if (argc % 2 != 1 || argc < 2) {
bb_show_usage();
}
fd = get_console_fd();
while (argc > 2) {
a.keycode = xatoul_range(argv[2], 0, 127);
a.scancode = sc = xstrtoul_range(argv[1], 16, 0, 255);
if (a.scancode > 127) {
a.scancode -= 0xe000;
a.scancode += 128;
}
if (ioctl(fd, KDSETKEYCODE, &a)) {
bb_perror_msg_and_die("failed to set SCANCODE %x to KEYCODE %d", sc, a.keycode);
}
argc -= 2;
argv += 2;
}
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,31 @@
/* vi: set sw=4 ts=4: */
/*
* setlogcons: Send kernel messages to the current console or to console N
*
* Copyright (C) 2006 by Jan Kiszka <jan.kiszka@web.de>
*
* Based on setlogcons (kbd-1.12) by Andries E. Brouwer
*
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
*/
#include "busybox.h"
extern int setlogcons_main(int argc, char **argv)
{
struct {
char fn;
char subarg;
} arg;
arg.fn = 11; /* redirect kernel messages */
arg.subarg = 0; /* to specified console (current as default) */
if (argc == 2)
arg.subarg = xatoul_range(argv[1], 0, 63);
if (ioctl(xopen(VC_1, O_RDONLY), TIOCLINUX, &arg))
bb_perror_msg_and_die("TIOCLINUX");
return 0;
}

782
coreutils/Config.in Normal file
View File

@ -0,0 +1,782 @@
#
# For a description of the syntax of this configuration file,
# see scripts/kbuild/config-language.txt.
#
menu "Coreutils"
config BASENAME
bool "basename"
default n
help
basename is used to strip the directory and suffix from filenames,
leaving just the filename itself. Enable this option if you wish
to enable the 'basename' utility.
config CAL
bool "cal"
default n
help
cal is used to display a monthly calender.
config CAT
bool "cat"
default n
help
cat is used to concatenate files and print them to the standard
output. Enable this option if you wish to enable the 'cat' utility.
config CATV
bool "catv"
default n
help
Display nonprinting characters as escape sequences (like some
implementations' cat -v option).
config CHGRP
bool "chgrp"
default n
help
chgrp is used to change the group ownership of files.
config CHMOD
bool "chmod"
default n
help
chmod is used to change the access permission of files.
config CHOWN
bool "chown"
default n
help
chown is used to change the user and/or group ownership
of files.
config CHROOT
bool "chroot"
default n
help
chroot is used to change the root directory and run a command.
The default command is `/bin/sh'.
config CKSUM
bool "cksum"
default n
help
cksum is used to calculate the CRC32 checksum of a file.
config CMP
bool "cmp"
default n
help
cmp is used to compare two files and returns the result
to standard output.
config COMM
bool "comm"
default n
help
comm is used to compare two files line by line and return
a three-column output.
config CP
bool "cp"
default n
help
cp is used to copy files and directories.
config CUT
bool "cut"
default n
help
cut is used to print selected parts of lines from
each file to stdout.
config DATE
bool "date"
default n
help
date is used to set the system date or display the
current time in the given format.
config FEATURE_DATE_ISOFMT
bool "Enable ISO date format output (-I)"
default y
depends on DATE
help
Enable option (-I) to output an ISO-8601 compliant
date/time string.
config DD
bool "dd"
default n
help
dd copies a file (from standard input to standard output,
by default) using specific input and output blocksizes,
while optionally performing conversions on it.
config FEATURE_DD_SIGNAL_HANDLING
bool "Enable DD signal handling for status reporting"
default y
depends on DD
help
sending a SIGUSR1 signal to a running `dd' process makes it
print to standard error the number of records read and written
so far, then to resume copying.
$ dd if=/dev/zero of=/dev/null& pid=$! $ kill -USR1 $pid; sleep 1; kill $pid
10899206+0 records in 10899206+0 records out
config FEATURE_DD_IBS_OBS
bool "Enable ibs, obs and conv options"
default n
depends on DD
help
Enables support for writing a certain number of bytes in and out,
at a time, and performing conversions on the data stream.
config DF
bool "df"
default n
help
df reports the amount of disk space used and available
on filesystems.
config DIFF
bool "diff"
default n
help
diff compares two files or directories and outputs the
differences between them in a form that can be given to
the patch command.
config FEATURE_DIFF_BINARY
bool "Enable checks for binary files"
default y
depends on DIFF
help
This option enables support for checking for binary files
before a comparison is carried out.
config FEATURE_DIFF_DIR
bool "Enable directory support"
default y
depends on DIFF
help
This option enables support for directory and subdirectory
comparison.
config FEATURE_DIFF_MINIMAL
bool "Enable -d option to find smaller sets of changes"
default n
depends on DIFF
help
Enabling this option allows the use of -d to make diff
try hard to find the smallest possible set of changes.
config DIRNAME
bool "dirname"
default n
help
dirname is used to strip a non-directory suffix from
a file name.
config DOS2UNIX
bool "dos2unix/unix2dos"
default n
help
dos2unix is used to convert a text file from DOS format to
UNIX format, and vice versa.
config UNIX2DOS
bool
default y
depends on DOS2UNIX
help
unix2dos is used to convert a text file from UNIX format to
DOS format, and vice versa.
config DU
bool "du (default blocksize of 512 bytes)"
default n
help
du is used to report the amount of disk space used
for specified files.
config FEATURE_DU_DEFAULT_BLOCKSIZE_1K
bool "Use a default blocksize of 1024 bytes (1K)"
default y
depends on DU
help
Use a blocksize of (1K) instead of the default 512b.
config ECHO
bool "echo (basic SuSv3 version taking no options)"
default n
help
echo is used to print a specified string to stdout.
# this entry also appears in shell/Config.in, next to the echo builtin
config FEATURE_FANCY_ECHO
bool "Enable echo options (-n and -e)"
default y
depends on ECHO
help
This adds options (-n and -e) to echo.
config ENV
bool "env"
default n
help
env is used to set an environment variable and run
a command; without options it displays the current
environment.
config FEATURE_ENV_LONG_OPTIONS
bool "Enable long options"
default n
depends on ENV && GETOPT_LONG
help
Support long options for the env applet.
config EXPR
bool "expr"
default n
help
expr is used to calculate numbers and print the result
to standard output.
config EXPR_MATH_SUPPORT_64
bool "Extend Posix numbers support to 64 bit"
default n
depends on EXPR
help
Enable 64-bit math support in the expr applet. This will make
the applet slightly larger, but will allow computation with very
large numbers.
config FALSE
bool "false"
default n
help
false returns an exit code of FALSE (1).
config FOLD
bool "fold"
default n
help
Wrap text to fit a specific width.
config HEAD
bool "head"
default n
help
head is used to print the first specified number of lines
from files.
config FEATURE_FANCY_HEAD
bool "Enable head options (-c, -q, and -v)"
default n
depends on HEAD
help
This enables the head options (-c, -q, and -v).
config HOSTID
bool "hostid"
default n
help
hostid prints the numeric identifier (in hexadecimal) for
the current host.
config ID
bool "id"
default n
help
id displays the current user and group ID names.
config INSTALL
bool "install"
default n
help
Copy files and set attributes.
config FEATURE_INSTALL_LONG_OPTIONS
bool "Enable long options"
default n
depends on INSTALL && GETOPT_LONG
help
Support long options for the install applet.
config LENGTH
bool "length"
default n
help
length is used to print out the length of a specified string.
config LN
bool "ln"
default n
help
ln is used to create hard or soft links between files.
config LOGNAME
bool "logname"
default n
help
logname is used to print the current user's login name.
config LS
bool "ls"
default n
help
ls is used to list the contents of directories.
config FEATURE_LS_FILETYPES
bool "Enable filetyping options (-p and -F)"
default y
depends on LS
help
Enable the ls options (-p and -F).
config FEATURE_LS_FOLLOWLINKS
bool "Enable symlinks dereferencing (-L)"
default y
depends on LS
help
Enable the ls option (-L).
config FEATURE_LS_RECURSIVE
bool "Enable recursion (-R)"
default y
depends on LS
help
Enable the ls option (-R).
config FEATURE_LS_SORTFILES
bool "Sort the file names"
default y
depends on LS
help
Allow ls to sort file names alphabetically.
config FEATURE_LS_TIMESTAMPS
bool "Show file timestamps"
default y
depends on LS
help
Allow ls to display timestamps for files.
config FEATURE_LS_USERNAME
bool "Show username/groupnames"
default y
depends on LS
help
Allow ls to display username/groupname for files.
config FEATURE_LS_COLOR
bool "Allow use of color to identify file types"
default y
depends on LS && GETOPT_LONG
help
This enables the --color option to ls.
config FEATURE_LS_COLOR_IS_DEFAULT
bool "Produce colored ls output by default"
default n
depends on FEATURE_LS_COLOR
help
Saying yes here will turn coloring on by default,
even if no "--color" option is given to the ls command.
This is not recommended, since the colors are not
configurable, and the output may not be legible on
many output screens.
config MD5SUM
bool "md5sum"
default n
help
md5sum is used to print or check MD5 checksums.
config MKDIR
bool "mkdir"
default n
help
mkdir is used to create directories with the specified names.
config FEATURE_MKDIR_LONG_OPTIONS
bool "Enable long options"
default n
depends on MKDIR && GETOPT_LONG
help
Support long options for the mkdir applet.
config MKFIFO
bool "mkfifo"
default n
help
mkfifo is used to create FIFOs (named pipes).
The `mknod' program can also create FIFOs.
config MKNOD
bool "mknod"
default n
help
mknod is used to create FIFOs or block/character special
files with the specified names.
config MV
bool "mv"
default n
help
mv is used to move or rename files or directories.
config FEATURE_MV_LONG_OPTIONS
bool "Enable long options"
default n
depends on MV && GETOPT_LONG
help
Support long options for the mv applet.
config NICE
bool "nice"
default n
help
nice runs a program with modified scheduling priority.
config NOHUP
bool "nohup"
default n
help
run a command immune to hangups, with output to a non-tty.
config OD
bool "od"
default n
help
od is used to dump binary files in octal and other formats.
config PRINTENV
bool "printenv"
default n
help
printenv is used to print all or part of environment.
config PRINTF
bool "printf"
default n
help
printf is used to format and print specified strings.
It's similar to `echo' except it has more options.
config PWD
bool "pwd"
default n
help
pwd is used to print the current directory.
config REALPATH
bool "realpath"
default n
help
Return the canonicalized absolute pathname.
This isn't provided by GNU shellutils, but where else does it belong.
config RM
bool "rm"
default n
help
rm is used to remove files or directories.
config RMDIR
bool "rmdir"
default n
help
rmdir is used to remove empty directories.
config SEQ
bool "seq"
default n
help
print a sequence of numbers
config SHA1SUM
bool "sha1sum"
default n
help
Compute and check SHA1 message digest
config SLEEP
bool "sleep (single integer arg with no suffix)"
default n
help
sleep is used to pause for a specified number of seconds,
config FEATURE_FANCY_SLEEP
bool "Enable multiple integer args and optional time suffixes"
default n
depends on SLEEP
help
Allow sleep to pause for specified minutes, hours, and days.
config SORT
bool "sort"
default n
help
sort is used to sort lines of text in specified files.
config FEATURE_SORT_BIG
bool "full SuSv3 compliant sort (Support -ktcsbdfiozgM)"
default y
depends on SORT
help
Without this, sort only supports -r, -u, and an integer version
of -n. Selecting this adds sort keys, floating point support, and
more. This adds a little over 3k to a nonstatic build on x86.
The SuSv3 sort standard is available at:
http://www.opengroup.org/onlinepubs/007904975/utilities/sort.html
config STAT
bool "stat"
default n
help
display file or filesystem status.
config FEATURE_STAT_FORMAT
bool "Enable custom formats (-c)"
default n
depends on STAT
help
Without this, stat will not support the '-c format' option where
users can pass a custom format string for output. This adds about
7k to a nonstatic build on amd64.
config STTY
bool "stty"
default n
help
stty is used to change and print terminal line settings.
config SUM
bool "sum"
default n
help
checksum and count the blocks in a file
config SYNC
bool "sync"
default n
help
sync is used to flush filesystem buffers.
config TAIL
bool "tail"
default n
help
tail is used to print the last specified number of lines
from files.
config FEATURE_FANCY_TAIL
bool "Enable extra tail options (-q, -s, and -v)"
default y
depends on TAIL
help
The options (-q, -s, and -v) are provided by GNU tail, but
are not specific in the SUSv3 standard.
config TEE
bool "tee"
default n
help
tee is used to read from standard input and write
to standard output and files.
config FEATURE_TEE_USE_BLOCK_IO
bool "Enable block i/o (larger/faster) instead of byte i/o."
default n
depends on TEE
help
Enable this option for a faster tee, at expense of size.
config TEST
bool "test"
default n
help
test is used to check file types and compare values,
returning an appropriate exit code. The bash shell
has test built in, ash can build it in optionally.
config FEATURE_TEST_64
bool "Extend test to 64 bit"
default n
depends on TEST
help
Enable 64-bit support in test.
config TOUCH
bool "touch"
default n
help
touch is used to create or change the access and/or
modification timestamp of specified files.
config TR
bool "tr"
default n
help
tr is used to squeeze, and/or delete characters from standard
input, writing to standard output.
config FEATURE_TR_CLASSES
bool "Enable character classes (such as [:upper:])"
default n
depends on TR
help
Enable character classes, enabling commands such as:
tr [:upper:] [:lower:] to convert input into lowercase.
config FEATURE_TR_EQUIV
bool "Enable equivalence classes"
default n
depends on TR
help
Enable equivalence classes, which essentially add the enclosed
character to the current set. For instance, tr [=a=] xyz would
replace all instances of 'a' with 'xyz'. This option is mainly
useful for cases when no other way of expressing a character
is possible.
config TRUE
bool "true"
default n
help
true returns an exit code of TRUE (0).
config TTY
bool "tty"
default n
help
tty is used to print the name of the current terminal to
standard output.
config UNAME
bool "uname"
default n
help
uname is used to print system information.
config UNIQ
bool "uniq"
default n
help
uniq is used to remove duplicate lines from a sorted file.
config USLEEP
bool "usleep"
default n
help
usleep is used to pause for a specified number of microseconds.
config UUDECODE
bool "uudecode"
default n
help
uudecode is used to decode a uuencoded file.
config UUENCODE
bool "uuencode"
default n
help
uuencode is used to uuencode a file.
config WATCH
bool "watch"
default n
select DATE
help
watch is used to execute a program periodically, showing
output to the screen.
config WC
bool "wc"
default n
help
wc is used to print the number of bytes, words, and lines,
in specified files.
config FEATURE_WC_LARGE
bool "Support very large files in wc"
default n
depends on WC
help
Use "unsigned long long" in wc for count variables
config WHO
bool "who"
default n
select FEATURE_UTMP
help
who is used to show who is logged on.
config WHOAMI
bool "whoami"
default n
help
whoami is used to print the username of the current
user id (same as id -un).
config YES
bool "yes"
default n
help
yes is used to repeatedly output a specific string, or
the default string `y'.
comment "Common options for cp and mv"
depends on CP || MV
config FEATURE_PRESERVE_HARDLINKS
bool "Preserve hard links"
default n
depends on CP || MV
help
Allow cp and mv to preserve hard links.
comment "Common options for ls, more and telnet"
depends on LS || MORE || TELNET
config FEATURE_AUTOWIDTH
bool "Calculate terminal & column widths"
default y
depends on LS || MORE || TELNET
help
This option allows utilities such as 'ls', 'more' and 'telnet'
to determine the width of the screen, which can allow them to
display additional text or avoid wrapping text onto the next line.
If you leave this disabled, your utilities will be especially
primitive and will be unable to determine the current screen width.
comment "Common options for df, du, ls"
depends on DF || DU || LS
config FEATURE_HUMAN_READABLE
bool "Support for human readable output (example 13k, 23M, 235G)"
default n
depends on DF || DU || LS
help
Allow df, du, and ls to have human readable output.
comment "Common options for md5sum, sha1sum"
depends on MD5SUM || SHA1SUM
config FEATURE_MD5_SHA1_SUM_CHECK
bool "Enable -c, -s and -w options"
default n
depends on MD5SUM || SHA1SUM
help
Enabling the -c options allows files to be checked
against pre-calculated hash values.
-s and -w are useful options when verifying checksums.
endmenu

81
coreutils/Kbuild Normal file
View File

@ -0,0 +1,81 @@
# Makefile for busybox
#
# Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
#
# Licensed under the GPL v2, see the file LICENSE in this tarball.
libs-y += libcoreutils/
lib-y:=
lib-$(CONFIG_BASENAME) += basename.o
lib-$(CONFIG_CAL) += cal.o
lib-$(CONFIG_CAT) += cat.o
lib-$(CONFIG_CATV) += catv.o
lib-$(CONFIG_CHGRP) += chgrp.o chown.o
lib-$(CONFIG_CHMOD) += chmod.o
lib-$(CONFIG_CHOWN) += chown.o
lib-$(CONFIG_CHROOT) += chroot.o
lib-$(CONFIG_CKSUM) += cksum.o
lib-$(CONFIG_CMP) += cmp.o
lib-$(CONFIG_COMM) += comm.o
lib-$(CONFIG_CP) += cp.o
lib-$(CONFIG_CUT) += cut.o
lib-$(CONFIG_DATE) += date.o
lib-$(CONFIG_DD) += dd.o
lib-$(CONFIG_DF) += df.o
lib-$(CONFIG_DIFF) += diff.o
lib-$(CONFIG_DIRNAME) += dirname.o
lib-$(CONFIG_DOS2UNIX) += dos2unix.o
lib-$(CONFIG_DU) += du.o
lib-$(CONFIG_ECHO) += echo.o
lib-$(CONFIG_ENV) += env.o
lib-$(CONFIG_EXPR) += expr.o
lib-$(CONFIG_FALSE) += false.o
lib-$(CONFIG_FOLD) += fold.o
lib-$(CONFIG_HEAD) += head.o
lib-$(CONFIG_HOSTID) += hostid.o
lib-$(CONFIG_ID) += id.o
lib-$(CONFIG_INSTALL) += install.o
lib-$(CONFIG_LENGTH) += length.o
lib-$(CONFIG_LN) += ln.o
lib-$(CONFIG_LOGNAME) += logname.o
lib-$(CONFIG_LS) += ls.o
lib-$(CONFIG_MD5SUM) += md5_sha1_sum.o
lib-$(CONFIG_MKDIR) += mkdir.o
lib-$(CONFIG_MKFIFO) += mkfifo.o
lib-$(CONFIG_MKNOD) += mknod.o
lib-$(CONFIG_MV) += mv.o
lib-$(CONFIG_NICE) += nice.o
lib-$(CONFIG_NOHUP) += nohup.o
lib-$(CONFIG_OD) += od.o
lib-$(CONFIG_PRINTENV) += printenv.o
lib-$(CONFIG_PRINTF) += printf.o
lib-$(CONFIG_PWD) += pwd.o
lib-$(CONFIG_REALPATH) += realpath.o
lib-$(CONFIG_RM) += rm.o
lib-$(CONFIG_RMDIR) += rmdir.o
lib-$(CONFIG_SEQ) += seq.o
lib-$(CONFIG_SHA1SUM) += md5_sha1_sum.o
lib-$(CONFIG_SLEEP) += sleep.o
lib-$(CONFIG_SORT) += sort.o
lib-$(CONFIG_STAT) += stat.o
lib-$(CONFIG_STTY) += stty.o
lib-$(CONFIG_SUM) += sum.o
lib-$(CONFIG_SYNC) += sync.o
lib-$(CONFIG_TAIL) += tail.o
lib-$(CONFIG_TEE) += tee.o
lib-$(CONFIG_TEST) += test.o
lib-$(CONFIG_TOUCH) += touch.o
lib-$(CONFIG_TR) += tr.o
lib-$(CONFIG_TRUE) += true.o
lib-$(CONFIG_TTY) += tty.o
lib-$(CONFIG_UNAME) += uname.o
lib-$(CONFIG_UNIQ) += uniq.o
lib-$(CONFIG_USLEEP) += usleep.o
lib-$(CONFIG_UUDECODE) += uudecode.o
lib-$(CONFIG_UUENCODE) += uuencode.o
lib-$(CONFIG_WATCH) += watch.o
lib-$(CONFIG_WC) += wc.o
lib-$(CONFIG_WHO) += who.o
lib-$(CONFIG_WHOAMI) += whoami.o
lib-$(CONFIG_YES) += yes.o

50
coreutils/basename.c Normal file
View File

@ -0,0 +1,50 @@
/* vi: set sw=4 ts=4: */
/*
* Mini basename implementation for busybox
*
* Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
*
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
*
*/
/* BB_AUDIT SUSv3 compliant */
/* http://www.opengroup.org/onlinepubs/007904975/utilities/basename.html */
/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org)
*
* Changes:
* 1) Now checks for too many args. Need at least one and at most two.
* 2) Don't check for options, as per SUSv3.
* 3) Save some space by using strcmp(). Calling strncmp() here was silly.
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "busybox.h"
int basename_main(int argc, char **argv)
{
size_t m, n;
char *s;
if (((unsigned int)(argc-2)) >= 2) {
bb_show_usage();
}
s = bb_get_last_path_component(*++argv);
if (*++argv) {
n = strlen(*argv);
m = strlen(s);
if ((m > n) && ((strcmp)(s+m-n, *argv) == 0)) {
s[m-n] = '\0';
}
}
puts(s);
fflush_stdout_and_exit(EXIT_SUCCESS);
}

356
coreutils/cal.c Normal file
View File

@ -0,0 +1,356 @@
/* vi: set sw=4 ts=4: */
/*
* Calendar implementation for busybox
*
* See original copyright at the end of this file
*
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
*/
/* BB_AUDIT SUSv3 compliant with -j and -y extensions (from util-linux). */
/* BB_AUDIT BUG: The output of 'cal -j 1752' is incorrect. The upstream
* BB_AUDIT BUG: version in util-linux seems to be broken as well. */
/* http://www.opengroup.org/onlinepubs/007904975/utilities/cal.html */
/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org)
*
* Major size reduction... over 50% (>1.5k) on i386.
*/
#include "busybox.h"
#define THURSDAY 4 /* for reformation */
#define SATURDAY 6 /* 1 Jan 1 was a Saturday */
#define FIRST_MISSING_DAY 639787 /* 3 Sep 1752 */
#define NUMBER_MISSING_DAYS 11 /* 11 day correction */
#define MAXDAYS 42 /* max slots in a month array */
#define SPACE -1 /* used in day array */
static const char days_in_month[] = {
0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};
static const char sep1752[] = {
1, 2, 14, 15, 16,
17, 18, 19, 20, 21, 22, 23,
24, 25, 26, 27, 28, 29, 30
};
static int julian;
/* leap year -- account for Gregorian reformation in 1752 */
#define leap_year(yr) \
((yr) <= 1752 ? !((yr) % 4) : \
(!((yr) % 4) && ((yr) % 100)) || !((yr) % 400))
static int is_leap_year(int year)
{
return leap_year(year);
}
#undef leap_year
#define leap_year(yr) is_leap_year(yr)
/* number of centuries since 1700, not inclusive */
#define centuries_since_1700(yr) \
((yr) > 1700 ? (yr) / 100 - 17 : 0)
/* number of centuries since 1700 whose modulo of 400 is 0 */
#define quad_centuries_since_1700(yr) \
((yr) > 1600 ? ((yr) - 1600) / 400 : 0)
/* number of leap years between year 1 and this year, not inclusive */
#define leap_years_since_year_1(yr) \
((yr) / 4 - centuries_since_1700(yr) + quad_centuries_since_1700(yr))
static void center (char *, int, int);
static void day_array (int, int, int *);
static void trim_trailing_spaces_and_print (char *);
static void blank_string(char *buf, size_t buflen);
static char *build_row(char *p, int *dp);
#define DAY_LEN 3 /* 3 spaces per day */
#define J_DAY_LEN (DAY_LEN + 1)
#define WEEK_LEN 20 /* 7 * 3 - one space at the end */
#define J_WEEK_LEN (WEEK_LEN + 7)
#define HEAD_SEP 2 /* spaces between day headings */
int cal_main(int argc, char **argv)
{
struct tm *local_time;
struct tm zero_tm;
time_t now;
int month, year, flags, i;
char *month_names[12];
char day_headings[28]; /* 28 for julian, 21 for nonjulian */
char buf[40];
// Done in busybox.c, ok to remove?
//#ifdef CONFIG_LOCALE_SUPPORT
// setlocale(LC_TIME, "");
//#endif
flags = getopt32(argc, argv, "jy");
julian = flags & 1;
argv += optind;
month = 0;
if ((argc -= optind) > 2) {
bb_show_usage();
}
if (!argc) {
time(&now);
local_time = localtime(&now);
year = local_time->tm_year + 1900;
if (!(flags & 2)) {
month = local_time->tm_mon + 1;
}
} else {
if (argc == 2) {
month = xatoul_range(*argv++, 1, 12);
}
year = xatoul_range(*argv, 1, 9999);
}
blank_string(day_headings, sizeof(day_headings) - 7 + 7*julian);
i = 0;
do {
zero_tm.tm_mon = i;
strftime(buf, sizeof(buf), "%B", &zero_tm);
month_names[i] = xstrdup(buf);
if (i < 7) {
zero_tm.tm_wday = i;
strftime(buf, sizeof(buf), "%a", &zero_tm);
strncpy(day_headings + i * (3+julian) + julian, buf, 2);
}
} while (++i < 12);
if (month) {
int row, len, days[MAXDAYS];
int *dp = days;
char lineout[30];
day_array(month, year, dp);
len = sprintf(lineout, "%s %d", month_names[month - 1], year);
printf("%*s%s\n%s\n",
((7*julian + WEEK_LEN) - len) / 2, "",
lineout, day_headings);
for (row = 0; row < 6; row++) {
build_row(lineout, dp)[0] = '\0';
dp += 7;
trim_trailing_spaces_and_print(lineout);
}
} else {
int row, which_cal, week_len, days[12][MAXDAYS];
int *dp;
char lineout[80];
sprintf(lineout, "%d", year);
center(lineout,
(WEEK_LEN * 3 + HEAD_SEP * 2)
+ julian * (J_WEEK_LEN * 2 + HEAD_SEP
- (WEEK_LEN * 3 + HEAD_SEP * 2)),
0);
puts("\n"); /* two \n's */
for (i = 0; i < 12; i++) {
day_array(i + 1, year, days[i]);
}
blank_string(lineout, sizeof(lineout));
week_len = WEEK_LEN + julian * (J_WEEK_LEN - WEEK_LEN);
for (month = 0; month < 12; month += 3-julian) {
center(month_names[month], week_len, HEAD_SEP);
if (!julian) {
center(month_names[month + 1], week_len, HEAD_SEP);
}
center(month_names[month + 2 - julian], week_len, 0);
printf("\n%s%*s%s", day_headings, HEAD_SEP, "", day_headings);
if (!julian) {
printf("%*s%s", HEAD_SEP, "", day_headings);
}
putchar('\n');
for (row = 0; row < (6*7); row += 7) {
for (which_cal = 0; which_cal < 3-julian; which_cal++) {
dp = days[month + which_cal] + row;
build_row(lineout + which_cal * (week_len + 2), dp);
}
/* blank_string took care of nul termination. */
trim_trailing_spaces_and_print(lineout);
}
}
}
fflush_stdout_and_exit(0);
}
/*
* day_array --
* Fill in an array of 42 integers with a calendar. Assume for a moment
* that you took the (maximum) 6 rows in a calendar and stretched them
* out end to end. You would have 42 numbers or spaces. This routine
* builds that array for any month from Jan. 1 through Dec. 9999.
*/
static void day_array(int month, int year, int *days)
{
long temp;
int i;
int j_offset;
int day, dw, dm;
memset(days, SPACE, MAXDAYS * sizeof(int));
if ((month == 9) && (year == 1752)) {
size_t oday = 0;
j_offset = julian * 244;
do {
days[oday+2] = sep1752[oday] + j_offset;
} while (++oday < sizeof(sep1752));
return;
}
/* day_in_year
* return the 1 based day number within the year
*/
day = 1;
if ((month > 2) && leap_year(year)) {
++day;
}
i = month;
while (i) {
day += days_in_month[--i];
}
/* day_in_week
* return the 0 based day number for any date from 1 Jan. 1 to
* 31 Dec. 9999. Assumes the Gregorian reformation eliminates
* 3 Sep. 1752 through 13 Sep. 1752. Returns Thursday for all
* missing days.
*/
dw = THURSDAY;
temp = (long)(year - 1) * 365 + leap_years_since_year_1(year - 1)
+ day;
if (temp < FIRST_MISSING_DAY) {
dw = ((temp - 1 + SATURDAY) % 7);
} else if (temp >= (FIRST_MISSING_DAY + NUMBER_MISSING_DAYS)) {
dw = (((temp - 1 + SATURDAY) - NUMBER_MISSING_DAYS) % 7);
}
if (!julian) {
day = 1;
}
dm = days_in_month[month];
if ((month == 2) && leap_year(year)) {
++dm;
}
while (dm) {
days[dw++] = day++;
--dm;
}
}
static void trim_trailing_spaces_and_print(char *s)
{
char *p = s;
while (*p) {
++p;
}
while (p > s) {
--p;
if (!(isspace)(*p)) { /* We want the function... not the inline. */
p[1] = '\0';
break;
}
}
puts(s);
}
static void center(char *str, int len, int separate)
{
int n = strlen(str);
len -= n;
printf("%*s%*s", (len/2) + n, str, (len/2) + (len % 2) + separate, "");
}
static void blank_string(char *buf, size_t buflen)
{
memset(buf, ' ', buflen);
buf[buflen-1] = '\0';
}
static char *build_row(char *p, int *dp)
{
int col, val, day;
memset(p, ' ', (julian + DAY_LEN) * 7);
col = 0;
do {
if ((day = *dp++) != SPACE) {
if (julian) {
++p;
if (day >= 100) {
*p = '0';
p[-1] = (day / 100) + '0';
day %= 100;
}
}
if ((val = day / 10) > 0) {
*p = val + '0';
}
*++p = day % 10 + '0';
p += 2;
} else {
p += DAY_LEN + julian;
}
} while (++col < 7);
return p;
}
/*
* Copyright (c) 1989, 1993, 1994
* The Regents of the University of California. All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Kim Letkeman.
*
* 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 REGENTS AND CONTRIBUTORS ``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.
*/

41
coreutils/cat.c Normal file
View File

@ -0,0 +1,41 @@
/* vi: set sw=4 ts=4: */
/*
* cat implementation for busybox
*
* Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org>
*
* Licensed under GPLv2, see file License in this tarball for details.
*/
/* BB_AUDIT SUSv3 compliant */
/* http://www.opengroup.org/onlinepubs/007904975/utilities/cat.html */
#include "busybox.h"
#include <unistd.h>
int cat_main(int argc, char **argv)
{
FILE *f;
int retval = EXIT_SUCCESS;
getopt32(argc, argv, "u");
argv += optind;
if (!*argv) {
*--argv = "-";
}
do {
f = fopen_or_warn_stdin(*argv);
if (f) {
off_t r = bb_copyfd_eof(fileno(f), STDOUT_FILENO);
fclose_if_not_stdin(f);
if (r >= 0) {
continue;
}
}
retval = EXIT_FAILURE;
} while (*++argv);
return retval;
}

69
coreutils/catv.c Normal file
View File

@ -0,0 +1,69 @@
/* vi: set sw=4 ts=4: */
/*
* cat -v implementation for busybox
*
* Copyright (C) 2006 Rob Landley <rob@landley.net>
*
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
*/
/* See "Cat -v considered harmful" at
* http://cm.bell-labs.com/cm/cs/doc/84/kp.ps.gz */
#include "busybox.h"
int catv_main(int argc, char **argv)
{
int retval = EXIT_SUCCESS, fd;
unsigned flags;
flags = getopt32(argc, argv, "etv");
#define CATV_OPT_e (1<<0)
#define CATV_OPT_t (1<<1)
#define CATV_OPT_v (1<<2)
flags ^= CATV_OPT_v;
argv += optind;
do {
/* Read from stdin if there's nothing else to do. */
fd = 0;
if (*argv && 0 > (fd = xopen(*argv, O_RDONLY)))
retval = EXIT_FAILURE;
else for (;;) {
int i, res;
res = read(fd, bb_common_bufsiz1, sizeof(bb_common_bufsiz1));
if (res < 0)
retval = EXIT_FAILURE;
if (res < 1)
break;
for (i = 0; i < res; i++) {
char c = bb_common_bufsiz1[i];
if (c > 126 && (flags & CATV_OPT_v)) {
if (c == 127) {
printf("^?");
continue;
} else {
printf("M-");
c -= 128;
}
}
if (c < 32) {
if (c == 10) {
if (flags & CATV_OPT_e)
putchar('$');
} else if (flags & (c==9 ? CATV_OPT_t : CATV_OPT_v)) {
printf("^%c", c+'@');
continue;
}
}
putchar(c);
}
}
if (ENABLE_FEATURE_CLEAN_UP && fd)
close(fd);
} while (*++argv);
fflush_stdout_and_exit(retval);
}

27
coreutils/chgrp.c Normal file
View File

@ -0,0 +1,27 @@
/* vi: set sw=4 ts=4: */
/*
* Mini chgrp implementation for busybox
*
* Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
*
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
*/
/* BB_AUDIT SUSv3 defects - unsupported options -H, -L, and -P. */
/* BB_AUDIT GNU defects - unsupported long options. */
/* http://www.opengroup.org/onlinepubs/007904975/utilities/chgrp.html */
#include "busybox.h"
int chgrp_main(int argc, char **argv)
{
/* "chgrp [opts] abc file(s)" == "chown [opts] :abc file(s)" */
char **p = argv;
while (*++p) {
if (p[0][0] != '-') {
p[0] = xasprintf(":%s", p[0]);
break;
}
}
return chown_main(argc, argv);
}

157
coreutils/chmod.c Normal file
View File

@ -0,0 +1,157 @@
/* vi: set sw=4 ts=4: */
/*
* Mini chmod implementation for busybox
*
* Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
*
* Reworked by (C) 2002 Vladimir Oleynik <dzo@simtreas.ru>
* to correctly parse '-rwxgoa'
*
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
*/
/* BB_AUDIT SUSv3 compliant */
/* BB_AUDIT GNU defects - unsupported long options. */
/* http://www.opengroup.org/onlinepubs/007904975/utilities/chmod.html */
#include "busybox.h"
#define OPT_RECURSE (option_mask32 & 1)
#define OPT_VERBOSE (USE_DESKTOP(option_mask32 & 2) SKIP_DESKTOP(0))
#define OPT_CHANGED (USE_DESKTOP(option_mask32 & 4) SKIP_DESKTOP(0))
#define OPT_QUIET (USE_DESKTOP(option_mask32 & 8) SKIP_DESKTOP(0))
#define OPT_STR "R" USE_DESKTOP("vcf")
/* coreutils:
* chmod never changes the permissions of symbolic links; the chmod
* system call cannot change their permissions. This is not a problem
* since the permissions of symbolic links are never used.
* However, for each symbolic link listed on the command line, chmod changes
* the permissions of the pointed-to file. In contrast, chmod ignores
* symbolic links encountered during recursive directory traversals.
*/
static int fileAction(const char *fileName, struct stat *statbuf, void* junk, int depth)
{
mode_t newmode;
/* match coreutils behavior */
if (depth == 0) {
/* statbuf holds lstat result, but we need stat (follow link) */
if (stat(fileName, statbuf))
goto err;
} else { /* depth > 0: skip links */
if (S_ISLNK(statbuf->st_mode))
return TRUE;
}
newmode = statbuf->st_mode;
if (!bb_parse_mode((char *)junk, &newmode))
bb_error_msg_and_die("invalid mode: %s", (char *)junk);
if (chmod(fileName, newmode) == 0) {
if (OPT_VERBOSE
|| (OPT_CHANGED && statbuf->st_mode != newmode)
) {
printf("mode of '%s' changed to %04o (%s)\n", fileName,
newmode & 07777, bb_mode_string(newmode)+1);
}
return TRUE;
}
err:
if (!OPT_QUIET)
bb_perror_msg("%s", fileName);
return FALSE;
}
int chmod_main(int argc, char **argv)
{
int retval = EXIT_SUCCESS;
char *arg, **argp;
char *smode;
/* Convert first encountered -r into ar, -w into aw etc
* so that getopt would not eat it */
argp = argv;
while ((arg = *++argp)) {
/* Mode spec must be the first arg (sans -R etc) */
/* (protect against mishandling e.g. "chmod 644 -r") */
if (arg[0] != '-') {
arg = NULL;
break;
}
/* An option. Not a -- or valid option? */
if (arg[1] && !strchr("-"OPT_STR, arg[1])) {
arg[0] = 'a';
break;
}
}
/* Parse options */
opt_complementary = "-2";
getopt32(argc, argv, ("-"OPT_STR) + 1); /* Reuse string */
argv += optind;
/* Restore option-like mode if needed */
if (arg) arg[0] = '-';
/* Ok, ready to do the deed now */
smode = *argv++;
do {
if (!recursive_action(*argv,
OPT_RECURSE, // recurse
FALSE, // follow links: coreutils doesn't
FALSE, // depth first
fileAction, // file action
fileAction, // dir action
smode, // user data
0) // depth
) {
retval = EXIT_FAILURE;
}
} while (*++argv);
return retval;
}
/*
Security: chmod is too important and too subtle.
This is a test script (busybox chmod versus coreutils).
Run it in empty dir. Probably requires bash.
#!/bin/sh
function create() {
rm -rf $1; mkdir $1
(
cd $1 || exit 1
mkdir dir
>up
>file
>dir/file
ln -s dir linkdir
ln -s file linkfile
ln -s ../up dir/up
)
}
function tst() {
(cd test1; $t1 $1)
(cd test2; $t2 $1)
(cd test1; ls -lR) >out1
(cd test2; ls -lR) >out2
echo "chmod $1" >out.diff
if ! diff -u out1 out2 >>out.diff; then exit 1; fi
mv out.diff out1.diff
}
t1="/tmp/busybox chmod"
t2="/usr/bin/chmod"
create test1; create test2
tst "a+w file"
tst "a-w dir"
tst "a+w linkfile"
tst "a-w linkdir"
tst "-R a+w file"
tst "-R a-w dir"
tst "-R a+w linkfile"
tst "-R a-w linkdir"
tst "a-r,a+x linkfile"
*/

98
coreutils/chown.c Normal file
View File

@ -0,0 +1,98 @@
/* vi: set sw=4 ts=4: */
/*
* Mini chown implementation for busybox
*
* Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
*
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
*/
/* BB_AUDIT SUSv3 defects - unsupported options -H, -L, and -P. */
/* BB_AUDIT GNU defects - unsupported long options. */
/* http://www.opengroup.org/onlinepubs/007904975/utilities/chown.html */
#include "busybox.h"
static uid_t uid = -1;
static gid_t gid = -1;
static int (*chown_func)(const char *, uid_t, gid_t) = chown;
#define OPT_RECURSE (option_mask32 & 1)
#define OPT_NODEREF (option_mask32 & 2)
#define OPT_VERBOSE (USE_DESKTOP(option_mask32 & 4) SKIP_DESKTOP(0))
#define OPT_CHANGED (USE_DESKTOP(option_mask32 & 8) SKIP_DESKTOP(0))
#define OPT_QUIET (USE_DESKTOP(option_mask32 & 0x10) SKIP_DESKTOP(0))
#define OPT_STR ("Rh" USE_DESKTOP("vcf"))
/* TODO:
* -H if a command line argument is a symbolic link to a directory, traverse it
* -L traverse every symbolic link to a directory encountered
* -P do not traverse any symbolic links (default)
*/
static int fileAction(const char *fileName, struct stat *statbuf,
void ATTRIBUTE_UNUSED *junk, int depth)
{
// TODO: -H/-L/-P
// if (depth ... && S_ISLNK(statbuf->st_mode)) ....
if (!chown_func(fileName,
(uid == (uid_t)-1) ? statbuf->st_uid : uid,
(gid == (gid_t)-1) ? statbuf->st_gid : gid)) {
if (OPT_VERBOSE
|| (OPT_CHANGED && (statbuf->st_uid != uid || statbuf->st_gid != gid))
) {
printf("changed ownership of '%s' to %u:%u\n", fileName, uid, gid);
}
return TRUE;
}
if (!OPT_QUIET)
bb_perror_msg("%s", fileName); /* A filename can have % in it... */
return FALSE;
}
int chown_main(int argc, char **argv)
{
int retval = EXIT_SUCCESS;
char *groupName;
opt_complementary = "-2";
getopt32(argc, argv, OPT_STR);
if (OPT_NODEREF) chown_func = lchown;
argv += optind;
/* First, check if there is a group name here */
groupName = strchr(*argv, '.');
if (!groupName) {
groupName = strchr(*argv, ':');
}
/* Check for the username and groupname */
if (groupName) {
*groupName++ = '\0';
gid = get_ug_id(groupName, bb_xgetgrnam);
}
if (--groupName != *argv)
uid = get_ug_id(*argv, bb_xgetpwnam);
++argv;
/* Ok, ready to do the deed now */
do {
if (!recursive_action(*argv,
OPT_RECURSE, // recurse
FALSE, // follow links: TODO: -H/-L/-P
FALSE, // depth first
fileAction, // file action
fileAction, // dir action
NULL, // user data
0) // depth
) {
retval = EXIT_FAILURE;
}
} while (*++argv);
return retval;
}

37
coreutils/chroot.c Normal file
View File

@ -0,0 +1,37 @@
/* vi: set sw=4 ts=4: */
/*
* Mini chroot implementation for busybox
*
* Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
*
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
*/
/* BB_AUDIT SUSv3 N/A -- Matches GNU behavior. */
#include "busybox.h"
int chroot_main(int argc, char **argv)
{
if (argc < 2) {
bb_show_usage();
}
++argv;
if (chroot(*argv)) {
bb_perror_msg_and_die("cannot change root directory to %s", *argv);
}
xchdir("/");
++argv;
if (argc == 2) {
argv -= 2;
if (!(*argv = getenv("SHELL"))) {
*argv = (char *) DEFAULT_SHELL;
}
argv[1] = (char *) "-i";
}
execvp(*argv, argv);
bb_perror_msg_and_die("cannot execute %s", *argv);
}

53
coreutils/cksum.c Normal file
View File

@ -0,0 +1,53 @@
/* vi: set sw=4 ts=4: */
/*
* cksum - calculate the CRC32 checksum of a file
*
* Copyright (C) 2006 by Rob Sullivan, with ideas from code by Walter Harms
*
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details. */
#include "busybox.h"
int cksum_main(int argc, char **argv)
{
uint32_t *crc32_table = crc32_filltable(1);
FILE *fp;
uint32_t crc;
long length, filesize;
int bytes_read;
char *cp;
int inp_stdin = (argc == optind) ? 1 : 0;
do {
fp = fopen_or_warn_stdin((inp_stdin) ? bb_msg_standard_input : *++argv);
crc = 0;
length = 0;
while ((bytes_read = fread(bb_common_bufsiz1, 1, BUFSIZ, fp)) > 0) {
cp = bb_common_bufsiz1;
length += bytes_read;
while (bytes_read--)
crc = (crc << 8) ^ crc32_table[((crc >> 24) ^ (*cp++)) & 0xffL];
}
filesize = length;
for (; length; length >>= 8)
crc = (crc << 8) ^ crc32_table[((crc >> 24) ^ length) & 0xffL];
crc ^= 0xffffffffL;
if (inp_stdin) {
printf("%" PRIu32 " %li\n", crc, filesize);
break;
}
printf("%" PRIu32 " %li %s\n", crc, filesize, *argv);
fclose(fp);
} while (*(argv + 1));
fflush_stdout_and_exit(EXIT_SUCCESS);
}

127
coreutils/cmp.c Normal file
View File

@ -0,0 +1,127 @@
/* vi: set sw=4 ts=4: */
/*
* Mini cmp implementation for busybox
*
* Copyright (C) 2000,2001 by Matt Kraai <kraai@alumni.carnegiemellon.edu>
*
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
*/
/* BB_AUDIT SUSv3 (virtually) compliant -- uses nicer GNU format for -l. */
/* http://www.opengroup.org/onlinepubs/007904975/utilities/cmp.html */
/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org)
*
* Original version majorly reworked for SUSv3 compliance, bug fixes, and
* size optimizations. Changes include:
* 1) Now correctly distinguishes between errors and actual file differences.
* 2) Proper handling of '-' args.
* 3) Actual error checking of i/o.
* 4) Accept SUSv3 -l option. Note that we use the slightly nicer gnu format
* in the '-l' case.
*/
#include "busybox.h"
static FILE *cmp_xfopen_input(const char * const filename)
{
FILE *fp;
fp = fopen_or_warn_stdin(filename);
if (fp)
return fp;
exit(xfunc_error_retval); /* We already output an error message. */
}
static const char fmt_eof[] = "cmp: EOF on %s\n";
static const char fmt_differ[] = "%s %s differ: char %d, line %d\n";
// This fmt_l_opt uses gnu-isms. SUSv3 would be "%.0s%.0s%d %o %o\n"
static const char fmt_l_opt[] = "%.0s%.0s%d %3o %3o\n";
static const char opt_chars[] = "sl";
#define CMP_OPT_s (1<<0)
#define CMP_OPT_l (1<<1)
int cmp_main(int argc, char **argv)
{
FILE *fp1, *fp2, *outfile = stdout;
const char *filename1, *filename2 = "-";
const char *fmt;
int c1, c2, char_pos = 0, line_pos = 1;
unsigned opt;
int retval = 0;
xfunc_error_retval = 2; /* 1 is returned if files are different. */
opt = getopt32(argc, argv, opt_chars);
if (((opt & (CMP_OPT_s|CMP_OPT_l)) == (CMP_OPT_s|CMP_OPT_l))
|| (((unsigned int)(--argc - optind)) > 1))
bb_show_usage();
fp1 = cmp_xfopen_input(filename1 = *(argv += optind));
if (*++argv) {
filename2 = *argv;
}
fp2 = cmp_xfopen_input(filename2);
if (fp1 == fp2) { /* Paranioa check... stdin == stdin? */
/* Note that we don't bother reading stdin. Neither does gnu wc.
* But perhaps we should, so that other apps down the chain don't
* get the input. Consider 'echo hello | (cmp - - && cat -)'.
*/
return 0;
}
if (opt & CMP_OPT_l)
fmt = fmt_l_opt;
else
fmt = fmt_differ;
do {
c1 = getc(fp1);
c2 = getc(fp2);
++char_pos;
if (c1 != c2) { /* Remember: a read error may have occurred. */
retval = 1; /* But assume the files are different for now. */
if (c2 == EOF) {
/* We know that fp1 isn't at EOF or in an error state. But to
* save space below, things are setup to expect an EOF in fp1
* if an EOF occurred. So, swap things around.
*/
fp1 = fp2;
filename1 = filename2;
c1 = c2;
}
if (c1 == EOF) {
die_if_ferror(fp1, filename1);
fmt = fmt_eof; /* Well, no error, so it must really be EOF. */
outfile = stderr;
/* There may have been output to stdout (option -l), so
* make sure we fflush before writing to stderr. */
xfflush_stdout();
}
if (!(opt & CMP_OPT_s)) {
if (opt & CMP_OPT_l) {
line_pos = c1; /* line_pos is unused in the -l case. */
}
fprintf(outfile, fmt, filename1, filename2, char_pos, line_pos, c2);
if (opt) { /* This must be -l since not -s. */
/* If we encountered an EOF,
* the while check will catch it. */
continue;
}
}
break;
}
if (c1 == '\n') {
++line_pos;
}
} while (c1 != EOF);
die_if_ferror(fp1, filename1);
die_if_ferror(fp2, filename2);
fflush_stdout_and_exit(retval);
}

126
coreutils/comm.c Normal file
View File

@ -0,0 +1,126 @@
/* vi: set sw=4 ts=4: */
/*
* Mini comm implementation for busybox
*
* Copyright (C) 2005 by Robert Sullivan <cogito.ergo.cogito@gmail.com>
*
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
*/
#include "busybox.h"
#define COMM_OPT_1 0x01
#define COMM_OPT_2 0x02
#define COMM_OPT_3 0x04
/* These three variables control behaviour if non-zero */
static int only_file_1;
static int only_file_2;
static int both;
/* writeline outputs the input given, appropriately aligned according to class */
static void writeline(char *line, int class)
{
if (class == 0) {
if (!only_file_1)
return;
} else if (class == 1) {
if (!only_file_2)
return;
if (only_file_1)
putchar('\t');
}
else /*if (class == 2)*/ {
if (!both)
return;
if (only_file_1)
putchar('\t');
if (only_file_2)
putchar('\t');
}
fputs(line, stdout);
}
/* This is the real core of the program - lines are compared here */
static void cmp_files(char **infiles)
{
#define LINE_LEN 100
#define BB_EOF_0 0x1
#define BB_EOF_1 0x2
char thisline[2][LINE_LEN];
FILE *streams[2];
int i;
for (i = 0; i < 2; ++i) {
streams[i] = ((infiles[i][0] == '=' && infiles[i][1]) ? stdin : xfopen(infiles[i], "r"));
fgets(thisline[i], LINE_LEN, streams[i]);
}
while (*thisline[0] || *thisline[1]) {
int order = 0;
i = 0;
if (feof(streams[0])) i |= BB_EOF_0;
if (feof(streams[1])) i |= BB_EOF_1;
if (!*thisline[0])
order = 1;
else if (!*thisline[1])
order = -1;
else {
int tl0_len, tl1_len;
tl0_len = strlen(thisline[0]);
tl1_len = strlen(thisline[1]);
order = memcmp(thisline[0], thisline[1], tl0_len < tl1_len ? tl0_len : tl1_len);
if (!order)
order = tl0_len < tl1_len ? -1 : tl0_len != tl1_len;
}
if (order == 0 && !i)
writeline(thisline[1], 2);
else if (order > 0 && !(i & BB_EOF_1))
writeline(thisline[1], 1);
else if (order < 0 && !(i & BB_EOF_0))
writeline(thisline[0], 0);
if (i & BB_EOF_0 & BB_EOF_1) {
break;
} else if (i) {
i = (i & BB_EOF_0 ? 1 : 0);
while (!feof(streams[i])) {
if ((order < 0 && i) || (order > 0 && !i))
writeline(thisline[i], i);
fgets(thisline[i], LINE_LEN, streams[i]);
}
break;
} else {
if (order >= 0)
fgets(thisline[1], LINE_LEN, streams[1]);
if (order <= 0)
fgets(thisline[0], LINE_LEN, streams[0]);
}
}
fclose(streams[0]);
fclose(streams[1]);
}
int comm_main(int argc, char **argv)
{
unsigned long flags;
flags = getopt32(argc, argv, "123");
if (optind + 2 != argc)
bb_show_usage();
only_file_1 = !(flags & COMM_OPT_1);
only_file_2 = !(flags & COMM_OPT_2);
both = !(flags & COMM_OPT_3);
cmp_files(argv + optind);
exit(EXIT_SUCCESS);
}

92
coreutils/cp.c Normal file
View File

@ -0,0 +1,92 @@
/* vi: set sw=4 ts=4: */
/*
* Mini cp implementation for busybox
*
* Copyright (C) 2000 by Matt Kraai <kraai@alumni.carnegiemellon.edu>
*
* Licensed under GPL v2 or later, see file LICENSE in this tarball for details.
*/
/* http://www.opengroup.org/onlinepubs/007904975/utilities/cp.html */
/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org)
*
* Size reduction.
*/
#include "busybox.h"
#include "libcoreutils/coreutils.h"
int cp_main(int argc, char **argv)
{
struct stat source_stat;
struct stat dest_stat;
const char *last;
const char *dest;
int s_flags;
int d_flags;
int flags;
int status = 0;
enum {
OPT_a = 1 << (sizeof(FILEUTILS_CP_OPTSTR)-1),
OPT_r = 1 << (sizeof(FILEUTILS_CP_OPTSTR)),
OPT_P = 1 << (sizeof(FILEUTILS_CP_OPTSTR)+1),
OPT_H = 1 << (sizeof(FILEUTILS_CP_OPTSTR)+2),
OPT_L = 1 << (sizeof(FILEUTILS_CP_OPTSTR)+3),
};
// Soft- and hardlinking don't mix
// -P and -d are the same (-P is POSIX, -d is GNU)
// -r and -R are the same
// -a = -pdR
opt_complementary = "?:l--s:s--l:Pd:rR:apdR";
flags = getopt32(argc, argv, FILEUTILS_CP_OPTSTR "arPHL");
/* Default behavior of cp is to dereference, so we don't have to do
* anything special when we are given -L.
* The behavior of -H is *almost* like -L, but not quite, so let's
* just ignore it too for fun.
if (flags & OPT_L) ...
if (flags & OPT_H) ... // deref command-line params only
*/
flags ^= FILEUTILS_DEREFERENCE; /* The sense of this flag was reversed. */
if (optind + 2 > argc) {
bb_show_usage();
}
last = argv[argc - 1];
argv += optind;
/* If there are only two arguments and... */
if (optind + 2 == argc) {
s_flags = cp_mv_stat2(*argv, &source_stat,
(flags & FILEUTILS_DEREFERENCE) ? stat : lstat);
if (s_flags < 0)
return EXIT_FAILURE;
d_flags = cp_mv_stat(last, &dest_stat);
if (d_flags < 0)
return EXIT_FAILURE;
/* ...if neither is a directory or... */
if ( !((s_flags | d_flags) & 2) ||
/* ...recursing, the 1st is a directory, and the 2nd doesn't exist... */
((flags & FILEUTILS_RECUR) && (s_flags & 2) && !d_flags)
) {
/* ...do a simple copy. */
dest = xstrdup(last);
goto DO_COPY; /* Note: optind+2==argc implies argv[1]==last below. */
}
}
do {
dest = concat_path_file(last, bb_get_last_path_component(*argv));
DO_COPY:
if (copy_file(*argv, dest, flags) < 0) {
status = 1;
}
free((void*)dest);
} while (*++argv != last);
return status;
}

285
coreutils/cut.c Normal file
View File

@ -0,0 +1,285 @@
/* vi: set sw=4 ts=4: */
/*
* cut.c - minimalist version of cut
*
* Copyright (C) 1999,2000,2001 by Lineo, inc.
* Written by Mark Whitley <markw@codepoet.org>
* debloated by Bernhard Fischer
*
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
*/
#include "busybox.h"
/* option vars */
static const char optstring[] = "b:c:f:d:sn";
#define CUT_OPT_BYTE_FLGS (1<<0)
#define CUT_OPT_CHAR_FLGS (1<<1)
#define CUT_OPT_FIELDS_FLGS (1<<2)
#define CUT_OPT_DELIM_FLGS (1<<3)
#define CUT_OPT_SUPPRESS_FLGS (1<<4)
static char delim = '\t'; /* delimiter, default is tab */
struct cut_list {
int startpos;
int endpos;
};
enum {
BOL = 0,
EOL = INT_MAX,
NON_RANGE = -1
};
/* growable array holding a series of lists */
static struct cut_list *cut_lists;
static unsigned int nlists; /* number of elements in above list */
static int cmpfunc(const void *a, const void *b)
{
return (((struct cut_list *) a)->startpos -
((struct cut_list *) b)->startpos);
}
static void cut_file(FILE * file)
{
char *line = NULL;
unsigned int linenum = 0; /* keep these zero-based to be consistent */
/* go through every line in the file */
while ((line = xmalloc_getline(file)) != NULL) {
/* set up a list so we can keep track of what's been printed */
char * printed = xzalloc(strlen(line) * sizeof(char));
char * orig_line = line;
unsigned int cl_pos = 0;
int spos;
/* cut based on chars/bytes XXX: only works when sizeof(char) == byte */
if (option_mask32 & (CUT_OPT_CHAR_FLGS | CUT_OPT_BYTE_FLGS)) {
/* print the chars specified in each cut list */
for (; cl_pos < nlists; cl_pos++) {
spos = cut_lists[cl_pos].startpos;
while (spos < strlen(line)) {
if (!printed[spos]) {
printed[spos] = 'X';
putchar(line[spos]);
}
spos++;
if (spos > cut_lists[cl_pos].endpos
|| cut_lists[cl_pos].endpos == NON_RANGE)
break;
}
}
} else if (delim == '\n') { /* cut by lines */
spos = cut_lists[cl_pos].startpos;
/* get out if we have no more lists to process or if the lines
* are lower than what we're interested in */
if (linenum < spos || cl_pos >= nlists)
goto next_line;
/* if the line we're looking for is lower than the one we were
* passed, it means we displayed it already, so move on */
while (spos < linenum) {
spos++;
/* go to the next list if we're at the end of this one */
if (spos > cut_lists[cl_pos].endpos
|| cut_lists[cl_pos].endpos == NON_RANGE) {
cl_pos++;
/* get out if there's no more lists to process */
if (cl_pos >= nlists)
goto next_line;
spos = cut_lists[cl_pos].startpos;
/* get out if the current line is lower than the one
* we just became interested in */
if (linenum < spos)
goto next_line;
}
}
/* If we made it here, it means we've found the line we're
* looking for, so print it */
puts(line);
goto next_line;
} else { /* cut by fields */
int ndelim = -1; /* zero-based / one-based problem */
int nfields_printed = 0;
char *field = NULL;
const char delimiter[2] = { delim, 0 };
/* does this line contain any delimiters? */
if (strchr(line, delim) == NULL) {
if (!(option_mask32 & CUT_OPT_SUPPRESS_FLGS))
puts(line);
goto next_line;
}
/* process each list on this line, for as long as we've got
* a line to process */
for (; cl_pos < nlists && line; cl_pos++) {
spos = cut_lists[cl_pos].startpos;
do {
/* find the field we're looking for */
while (line && ndelim < spos) {
field = strsep(&line, delimiter);
ndelim++;
}
/* we found it, and it hasn't been printed yet */
if (field && ndelim == spos && !printed[ndelim]) {
/* if this isn't our first time through, we need to
* print the delimiter after the last field that was
* printed */
if (nfields_printed > 0)
putchar(delim);
fputs(field, stdout);
printed[ndelim] = 'X';
nfields_printed++; /* shouldn't overflow.. */
}
spos++;
/* keep going as long as we have a line to work with,
* this is a list, and we're not at the end of that
* list */
} while (spos <= cut_lists[cl_pos].endpos && line
&& cut_lists[cl_pos].endpos != NON_RANGE);
}
}
/* if we printed anything at all, we need to finish it with a
* newline cuz we were handed a chomped line */
putchar('\n');
next_line:
linenum++;
free(printed);
free(orig_line);
}
}
static const char _op_on_field[] = " only when operating on fields";
int cut_main(int argc, char **argv)
{
char *sopt, *ltok;
opt_complementary = "b--bcf:c--bcf:f--bcf";
getopt32(argc, argv, optstring, &sopt, &sopt, &sopt, &ltok);
if (!(option_mask32 & (CUT_OPT_BYTE_FLGS | CUT_OPT_CHAR_FLGS | CUT_OPT_FIELDS_FLGS)))
bb_error_msg_and_die("expected a list of bytes, characters, or fields");
if (option_mask32 & BB_GETOPT_ERROR)
bb_error_msg_and_die("only one type of list may be specified");
if (option_mask32 & CUT_OPT_DELIM_FLGS) {
if (strlen(ltok) > 1) {
bb_error_msg_and_die("the delimiter must be a single character");
}
delim = ltok[0];
}
/* non-field (char or byte) cutting has some special handling */
if (!(option_mask32 & CUT_OPT_FIELDS_FLGS)) {
if (option_mask32 & CUT_OPT_SUPPRESS_FLGS) {
bb_error_msg_and_die
("suppressing non-delimited lines makes sense%s",
_op_on_field);
}
if (delim != '\t') {
bb_error_msg_and_die
("a delimiter may be specified%s", _op_on_field);
}
}
/*
* parse list and put values into startpos and endpos.
* valid list formats: N, N-, N-M, -M
* more than one list can be separated by commas
*/
{
char *ntok;
int s = 0, e = 0;
/* take apart the lists, one by one (they are separated with commas */
while ((ltok = strsep(&sopt, ",")) != NULL) {
/* it's actually legal to pass an empty list */
if (strlen(ltok) == 0)
continue;
/* get the start pos */
ntok = strsep(&ltok, "-");
if (ntok == NULL) {
bb_error_msg
("internal error: ntok is null for start pos!?\n");
} else if (strlen(ntok) == 0) {
s = BOL;
} else {
s = xatoi_u(ntok);
/* account for the fact that arrays are zero based, while
* the user expects the first char on the line to be char #1 */
if (s != 0)
s--;
}
/* get the end pos */
ntok = strsep(&ltok, "-");
if (ntok == NULL) {
e = NON_RANGE;
} else if (strlen(ntok) == 0) {
e = EOL;
} else {
e = xatoi_u(ntok);
/* if the user specified and end position of 0, that means "til the
* end of the line */
if (e == 0)
e = EOL;
e--; /* again, arrays are zero based, lines are 1 based */
if (e == s)
e = NON_RANGE;
}
/* if there's something left to tokenize, the user passed
* an invalid list */
if (ltok)
bb_error_msg_and_die("invalid byte or field list");
/* add the new list */
cut_lists = xrealloc(cut_lists, sizeof(struct cut_list) * (++nlists));
cut_lists[nlists-1].startpos = s;
cut_lists[nlists-1].endpos = e;
}
/* make sure we got some cut positions out of all that */
if (nlists == 0)
bb_error_msg_and_die("missing list of positions");
/* now that the lists are parsed, we need to sort them to make life
* easier on us when it comes time to print the chars / fields / lines
*/
qsort(cut_lists, nlists, sizeof(struct cut_list), cmpfunc);
}
/* argv[(optind)..(argc-1)] should be names of file to process. If no
* files were specified or '-' was specified, take input from stdin.
* Otherwise, we process all the files specified. */
if (argv[optind] == NULL
|| (argv[optind][0] == '-' && argv[optind][1] == '\0')) {
cut_file(stdin);
} else {
FILE *file;
for (; optind < argc; optind++) {
file = fopen_or_warn(argv[optind], "r");
if (file) {
cut_file(file);
fclose(file);
}
}
}
if (ENABLE_FEATURE_CLEAN_UP)
free(cut_lists);
return EXIT_SUCCESS;
}

239
coreutils/date.c Normal file
View File

@ -0,0 +1,239 @@
/* vi: set sw=4 ts=4: */
/*
* Mini date implementation for busybox
*
* by Matthew Grant <grantma@anathoth.gen.nz>
*
* iso-format handling added by Robert Griebl <griebl@gmx.de>
* bugfixes and cleanup by Bernhard Fischer
*
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
*/
#include "busybox.h"
/* This 'date' command supports only 2 time setting formats,
all the GNU strftime stuff (its in libc, lets use it),
setting time using UTC and displaying it, as well as
an RFC 2822 compliant date output for shell scripting
mail commands */
/* Input parsing code is always bulky - used heavy duty libc stuff as
much as possible, missed out a lot of bounds checking */
/* Default input handling to save surprising some people */
#define DATE_OPT_RFC2822 0x01
#define DATE_OPT_SET 0x02
#define DATE_OPT_UTC 0x04
#define DATE_OPT_DATE 0x08
#define DATE_OPT_REFERENCE 0x10
#define DATE_OPT_TIMESPEC 0x20
#define DATE_OPT_HINT 0x40
static void xputenv(char *s)
{
if (putenv(s) != 0)
bb_error_msg_and_die(bb_msg_memory_exhausted);
}
static void maybe_set_utc(int opt)
{
if (opt & DATE_OPT_UTC)
xputenv("TZ=UTC0");
}
int date_main(int argc, char **argv)
{
time_t tm;
struct tm tm_time;
unsigned opt;
int ifmt = -1;
char *date_str = NULL;
char *date_fmt = NULL;
char *filename = NULL;
char *isofmt_arg;
char *hintfmt_arg;
opt_complementary = "?:d--s:s--d"
USE_FEATURE_DATE_ISOFMT(":R--I:I--R");
opt = getopt32(argc, argv, "Rs:ud:r:"
USE_FEATURE_DATE_ISOFMT("I::D:"),
&date_str, &date_str, &filename
USE_FEATURE_DATE_ISOFMT(, &isofmt_arg, &hintfmt_arg));
maybe_set_utc(opt);
if (ENABLE_FEATURE_DATE_ISOFMT && (opt & DATE_OPT_TIMESPEC)) {
if (!isofmt_arg) {
ifmt = 0; /* default is date */
} else {
const char * const isoformats[] =
{"date", "hours", "minutes", "seconds"};
for (ifmt = 0; ifmt < 4; ifmt++)
if (!strcmp(isofmt_arg, isoformats[ifmt])) {
break;
}
if (ifmt == 4) /* parse error */
bb_show_usage();
}
}
/* XXX, date_fmt == NULL from this always */
if ((date_fmt == NULL) && (optind < argc) && (argv[optind][0] == '+')) {
date_fmt = &argv[optind][1]; /* Skip over the '+' */
} else if (date_str == NULL) {
opt |= DATE_OPT_SET;
date_str = argv[optind];
}
/* Now we have parsed all the information except the date format
which depends on whether the clock is being set or read */
if (filename) {
struct stat statbuf;
xstat(filename, &statbuf);
tm = statbuf.st_mtime;
} else
time(&tm);
memcpy(&tm_time, localtime(&tm), sizeof(tm_time));
/* Zero out fields - take her back to midnight! */
if (date_str != NULL) {
tm_time.tm_sec = 0;
tm_time.tm_min = 0;
tm_time.tm_hour = 0;
/* Process any date input to UNIX time since 1 Jan 1970 */
if (ENABLE_FEATURE_DATE_ISOFMT && (opt & DATE_OPT_HINT)) {
strptime(date_str, hintfmt_arg, &tm_time);
} else if (strchr(date_str, ':') != NULL) {
/* Parse input and assign appropriately to tm_time */
if (sscanf(date_str, "%d:%d:%d", &tm_time.tm_hour, &tm_time.tm_min,
&tm_time.tm_sec) == 3) {
/* no adjustments needed */
} else if (sscanf(date_str, "%d:%d", &tm_time.tm_hour,
&tm_time.tm_min) == 2) {
/* no adjustments needed */
} else if (sscanf(date_str, "%d.%d-%d:%d:%d", &tm_time.tm_mon,
&tm_time.tm_mday, &tm_time.tm_hour,
&tm_time.tm_min, &tm_time.tm_sec) == 5) {
/* Adjust dates from 1-12 to 0-11 */
tm_time.tm_mon -= 1;
} else if (sscanf(date_str, "%d.%d-%d:%d", &tm_time.tm_mon,
&tm_time.tm_mday,
&tm_time.tm_hour, &tm_time.tm_min) == 4) {
/* Adjust dates from 1-12 to 0-11 */
tm_time.tm_mon -= 1;
} else if (sscanf(date_str, "%d.%d.%d-%d:%d:%d", &tm_time.tm_year,
&tm_time.tm_mon, &tm_time.tm_mday,
&tm_time.tm_hour, &tm_time.tm_min,
&tm_time.tm_sec) == 6) {
tm_time.tm_year -= 1900; /* Adjust years */
tm_time.tm_mon -= 1; /* Adjust dates from 1-12 to 0-11 */
} else if (sscanf(date_str, "%d.%d.%d-%d:%d", &tm_time.tm_year,
&tm_time.tm_mon, &tm_time.tm_mday,
&tm_time.tm_hour, &tm_time.tm_min) == 5) {
tm_time.tm_year -= 1900; /* Adjust years */
tm_time.tm_mon -= 1; /* Adjust dates from 1-12 to 0-11 */
} else {
bb_error_msg_and_die(bb_msg_invalid_date, date_str);
}
} else {
int nr;
char *cp;
nr = sscanf(date_str, "%2d%2d%2d%2d%d", &tm_time.tm_mon,
&tm_time.tm_mday, &tm_time.tm_hour, &tm_time.tm_min,
&tm_time.tm_year);
if (nr < 4 || nr > 5) {
bb_error_msg_and_die(bb_msg_invalid_date, date_str);
}
cp = strchr(date_str, '.');
if (cp) {
nr = sscanf(cp + 1, "%2d", &tm_time.tm_sec);
if (nr != 1) {
bb_error_msg_and_die(bb_msg_invalid_date, date_str);
}
}
/* correct for century - minor Y2K problem here? */
if (tm_time.tm_year >= 1900) {
tm_time.tm_year -= 1900;
}
/* adjust date */
tm_time.tm_mon -= 1;
}
/* Correct any day of week and day of year etc. fields */
tm_time.tm_isdst = -1; /* Be sure to recheck dst. */
tm = mktime(&tm_time);
if (tm < 0) {
bb_error_msg_and_die(bb_msg_invalid_date, date_str);
}
maybe_set_utc(opt);
/* if setting time, set it */
if ((opt & DATE_OPT_SET) && stime(&tm) < 0) {
bb_perror_msg("cannot set date");
}
}
/* Display output */
/* Deal with format string */
if (date_fmt == NULL) {
int i;
date_fmt = xzalloc(32);
if (ENABLE_FEATURE_DATE_ISOFMT && ifmt >= 0) {
strcpy(date_fmt, "%Y-%m-%d");
if (ifmt > 0) {
i = 8;
date_fmt[i++] = 'T';
date_fmt[i++] = '%';
date_fmt[i++] = 'H';
if (ifmt > 1) {
date_fmt[i++] = ':';
date_fmt[i++] = '%';
date_fmt[i++] = 'M';
}
if (ifmt > 2) {
date_fmt[i++] = ':';
date_fmt[i++] = '%';
date_fmt[i++] = 'S';
}
format_utc:
date_fmt[i++] = '%';
date_fmt[i] = (opt & DATE_OPT_UTC) ? 'Z' : 'z';
}
} else if (opt & DATE_OPT_RFC2822) {
/* Undo busybox.c for date -R */
setlocale(LC_TIME, "C");
strcpy(date_fmt, "%a, %d %b %Y %H:%M:%S ");
i = 22;
goto format_utc;
} else /* default case */
date_fmt = "%a %b %e %H:%M:%S %Z %Y";
}
if (*date_fmt == '\0') {
/* With no format string, just print a blank line */
*bb_common_bufsiz1 = 0;
} else {
/* Handle special conversions */
if (strncmp(date_fmt, "%f", 2) == 0) {
date_fmt = "%Y.%m.%d-%H:%M:%S";
}
/* Generate output string */
strftime(bb_common_bufsiz1, 200, date_fmt, &tm_time);
}
puts(bb_common_bufsiz1);
return EXIT_SUCCESS;
}

248
coreutils/dd.c Normal file
View File

@ -0,0 +1,248 @@
/* vi: set sw=4 ts=4: */
/*
* Mini dd implementation for busybox
*
*
* Copyright (C) 2000,2001 Matt Kraai
*
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
*/
#include "busybox.h"
#include <signal.h> /* For FEATURE_DD_SIGNAL_HANDLING */
static const struct suffix_mult dd_suffixes[] = {
{ "c", 1 },
{ "w", 2 },
{ "b", 512 },
{ "kD", 1000 },
{ "k", 1024 },
{ "K", 1024 }, // compat with coreutils dd
{ "MD", 1000000 },
{ "M", 1048576 },
{ "GD", 1000000000 },
{ "G", 1073741824 },
{ NULL, 0 }
};
static off_t out_full, out_part, in_full, in_part;
static void dd_output_status(int ATTRIBUTE_UNUSED cur_signal)
{
fprintf(stderr, "%"OFF_FMT"d+%"OFF_FMT"d records in\n"
"%"OFF_FMT"d+%"OFF_FMT"d records out\n",
in_full, in_part,
out_full, out_part);
}
static ssize_t full_write_or_warn(int fd, const void *buf, size_t len,
const char* filename)
{
ssize_t n = full_write(fd, buf, len);
if (n < 0)
bb_perror_msg("writing '%s'", filename);
return n;
}
#if ENABLE_LFS
#define XATOU_SFX xatoull_sfx
#else
#define XATOU_SFX xatoul_sfx
#endif
int dd_main(int argc, char **argv)
{
enum {
sync_flag = 1 << 0,
noerror = 1 << 1,
trunc_flag = 1 << 2,
twobufs_flag = 1 << 3,
};
int flags = trunc_flag;
size_t oc = 0, ibs = 512, obs = 512;
ssize_t n, w;
off_t seek = 0, skip = 0, count = OFF_T_MAX;
int oflag, ifd, ofd;
const char *infile = NULL, *outfile = NULL;
char *ibuf, *obuf;
if (ENABLE_FEATURE_DD_SIGNAL_HANDLING) {
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_handler = dd_output_status;
sa.sa_flags = SA_RESTART;
sigemptyset(&sa.sa_mask);
sigaction(SIGUSR1, &sa, 0);
}
for (n = 1; n < argc; n++) {
char *arg = argv[n];
/* Must fit into positive ssize_t */
if (ENABLE_FEATURE_DD_IBS_OBS && !strncmp("ibs=", arg, 4))
ibs = xatoul_range_sfx(arg+4, 0, ((size_t)-1L)/2, dd_suffixes);
else if (ENABLE_FEATURE_DD_IBS_OBS && !strncmp("obs=", arg, 4))
obs = xatoul_range_sfx(arg+4, 0, ((size_t)-1L)/2, dd_suffixes);
else if (!strncmp("bs=", arg, 3))
ibs = obs = xatoul_range_sfx(arg+3, 0, ((size_t)-1L)/2, dd_suffixes);
/* These can be large: */
else if (!strncmp("count=", arg, 6))
count = XATOU_SFX(arg+6, dd_suffixes);
else if (!strncmp("seek=", arg, 5))
seek = XATOU_SFX(arg+5, dd_suffixes);
else if (!strncmp("skip=", arg, 5))
skip = XATOU_SFX(arg+5, dd_suffixes);
else if (!strncmp("if=", arg, 3))
infile = arg+3;
else if (!strncmp("of=", arg, 3))
outfile = arg+3;
else if (ENABLE_FEATURE_DD_IBS_OBS && !strncmp("conv=", arg, 5)) {
arg += 5;
while (1) {
if (!strncmp("notrunc", arg, 7)) {
flags &= ~trunc_flag;
arg += 7;
} else if (!strncmp("sync", arg, 4)) {
flags |= sync_flag;
arg += 4;
} else if (!strncmp("noerror", arg, 7)) {
flags |= noerror;
arg += 7;
} else {
bb_error_msg_and_die(bb_msg_invalid_arg, arg, "conv");
}
if (arg[0] == '\0') break;
if (*arg++ != ',') bb_show_usage();
}
} else
bb_show_usage();
}
ibuf = obuf = xmalloc(ibs);
if (ibs != obs) {
flags |= twobufs_flag;
obuf = xmalloc(obs);
}
if (infile != NULL)
ifd = xopen(infile, O_RDONLY);
else {
ifd = STDIN_FILENO;
infile = bb_msg_standard_input;
}
if (outfile != NULL) {
oflag = O_WRONLY | O_CREAT;
if (!seek && (flags & trunc_flag))
oflag |= O_TRUNC;
ofd = xopen(outfile, oflag);
if (seek && (flags & trunc_flag)) {
if (ftruncate(ofd, seek * obs) < 0) {
struct stat st;
if (fstat(ofd, &st) < 0 || S_ISREG(st.st_mode) ||
S_ISDIR(st.st_mode))
goto die_outfile;
}
}
} else {
ofd = STDOUT_FILENO;
outfile = bb_msg_standard_output;
}
if (skip) {
if (lseek(ifd, skip * ibs, SEEK_CUR) < 0) {
while (skip-- > 0) {
n = safe_read(ifd, ibuf, ibs);
if (n < 0)
bb_perror_msg_and_die("%s", infile);
if (n == 0)
break;
}
}
}
if (seek) {
if (lseek(ofd, seek * obs, SEEK_CUR) < 0)
goto die_outfile;
}
while (in_full + in_part != count) {
if (flags & noerror) {
/* Pre-zero the buffer when doing the noerror thing */
memset(ibuf, '\0', ibs);
}
n = safe_read(ifd, ibuf, ibs);
if (n == 0)
break;
if (n < 0) {
if (flags & noerror) {
n = ibs;
bb_perror_msg("%s", infile);
} else
bb_perror_msg_and_die("%s", infile);
}
if ((size_t)n == ibs)
in_full++;
else {
in_part++;
if (flags & sync_flag) {
memset(ibuf + n, '\0', ibs - n);
n = ibs;
}
}
if (flags & twobufs_flag) {
char *tmp = ibuf;
while (n) {
size_t d = obs - oc;
if (d > n)
d = n;
memcpy(obuf + oc, tmp, d);
n -= d;
tmp += d;
oc += d;
if (oc == obs) {
w = full_write_or_warn(ofd, obuf, obs, outfile);
if (w < 0) goto out_status;
if (w == obs)
out_full++;
else if (w > 0)
out_part++;
oc = 0;
}
}
} else {
w = full_write_or_warn(ofd, ibuf, n, outfile);
if (w < 0) goto out_status;
if (w == obs)
out_full++;
else if (w > 0)
out_part++;
}
}
if (ENABLE_FEATURE_DD_IBS_OBS && oc) {
w = full_write_or_warn(ofd, obuf, oc, outfile);
if (w < 0) goto out_status;
if (w > 0)
out_part++;
}
if (close(ifd) < 0) {
bb_perror_msg_and_die("%s", infile);
}
if (close(ofd) < 0) {
die_outfile:
bb_perror_msg_and_die("%s", outfile);
}
out_status:
dd_output_status(0);
return EXIT_SUCCESS;
}

154
coreutils/df.c Normal file
View File

@ -0,0 +1,154 @@
/* vi: set sw=4 ts=4: */
/*
* Mini df implementation for busybox
*
* Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
* based on original code by (I think) Bruce Perens <bruce@pixar.com>.
*
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
*/
/* BB_AUDIT SUSv3 _NOT_ compliant -- options -P and -t missing. Also blocksize. */
/* http://www.opengroup.org/onlinepubs/007904975/utilities/df.html */
/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org)
*
* Size reduction. Removed floating point dependency. Added error checking
* on output. Output stats on 0-sized filesystems if specifically listed on
* the command line. Properly round *-blocks, Used, and Available quantities.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <mntent.h>
#include <sys/vfs.h>
#include "busybox.h"
#ifndef CONFIG_FEATURE_HUMAN_READABLE
static long kscale(long b, long bs)
{
return ( b * (long long) bs + 1024/2 ) / 1024;
}
#endif
int df_main(int argc, char **argv)
{
long blocks_used;
long blocks_percent_used;
#ifdef CONFIG_FEATURE_HUMAN_READABLE
unsigned long df_disp_hr = 1024;
#endif
int status = EXIT_SUCCESS;
unsigned opt;
FILE *mount_table;
struct mntent *mount_entry;
struct statfs s;
static const char hdr_1k[] = "1k-blocks"; /* default display is kilobytes */
const char *disp_units_hdr = hdr_1k;
#ifdef CONFIG_FEATURE_HUMAN_READABLE
opt_complementary = "h-km:k-hm:m-hk";
opt = getopt32(argc, argv, "hmk");
if (opt & 1) {
df_disp_hr = 0;
disp_units_hdr = " Size";
}
if (opt & 2) {
df_disp_hr = 1024*1024;
disp_units_hdr = "1M-blocks";
}
#else
opt = getopt32(argc, argv, "k");
#endif
printf("Filesystem%11s%-15sUsed Available Use%% Mounted on\n",
"", disp_units_hdr);
mount_table = NULL;
argv += optind;
if (optind >= argc) {
mount_table = setmntent(bb_path_mtab_file, "r");
if (!mount_table) {
bb_perror_msg_and_die(bb_path_mtab_file);
}
}
do {
const char *device;
const char *mount_point;
if (mount_table) {
mount_entry = getmntent(mount_table);
if (!mount_entry) {
endmntent(mount_table);
break;
}
} else {
mount_point = *argv++;
if (!mount_point) {
break;
}
mount_entry = find_mount_point(mount_point, bb_path_mtab_file);
if (!mount_entry) {
bb_error_msg("%s: can't find mount point", mount_point);
SET_ERROR:
status = EXIT_FAILURE;
continue;
}
}
device = mount_entry->mnt_fsname;
mount_point = mount_entry->mnt_dir;
if (statfs(mount_point, &s) != 0) {
bb_perror_msg("%s", mount_point);
goto SET_ERROR;
}
if ((s.f_blocks > 0) || !mount_table){
blocks_used = s.f_blocks - s.f_bfree;
blocks_percent_used = 0;
if (blocks_used + s.f_bavail) {
blocks_percent_used = (((long long) blocks_used) * 100
+ (blocks_used + s.f_bavail)/2
) / (blocks_used + s.f_bavail);
}
if (strcmp(device, "rootfs") == 0) {
continue;
} else if (strcmp(device, "/dev/root") == 0) {
/* Adjusts device to be the real root device,
* or leaves device alone if it can't find it */
device = find_block_device("/");
if (!device) {
goto SET_ERROR;
}
}
#ifdef CONFIG_FEATURE_HUMAN_READABLE
printf("%-20s %9s ", device,
make_human_readable_str(s.f_blocks, s.f_bsize, df_disp_hr));
printf("%9s ",
make_human_readable_str( (s.f_blocks - s.f_bfree),
s.f_bsize, df_disp_hr));
printf("%9s %3ld%% %s\n",
make_human_readable_str(s.f_bavail, s.f_bsize, df_disp_hr),
blocks_percent_used, mount_point);
#else
printf("%-20s %9ld %9ld %9ld %3ld%% %s\n",
device,
kscale(s.f_blocks, s.f_bsize),
kscale(s.f_blocks-s.f_bfree, s.f_bsize),
kscale(s.f_bavail, s.f_bsize),
blocks_percent_used, mount_point);
#endif
}
} while (1);
fflush_stdout_and_exit(status);
}

1243
coreutils/diff.c Normal file

File diff suppressed because it is too large Load Diff

26
coreutils/dirname.c Normal file
View File

@ -0,0 +1,26 @@
/* vi: set sw=4 ts=4: */
/*
* Mini dirname implementation for busybox
*
* Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
*
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
*/
/* BB_AUDIT SUSv3 compliant */
/* http://www.opengroup.org/onlinepubs/007904975/utilities/dirname.html */
#include <stdio.h>
#include <stdlib.h>
#include "busybox.h"
int dirname_main(int argc, char **argv)
{
if (argc != 2) {
bb_show_usage();
}
puts(dirname(argv[1]));
fflush_stdout_and_exit(EXIT_SUCCESS);
}

Some files were not shown because too many files have changed in this diff Show More