mirror of https://github.com/mirror/busybox.git
Correcting branch name to be like previous ones
commit
8d42f86b14
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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.
|
|
@ -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.
|
|
@ -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 $@ $^
|
|
@ -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
|
||||
|
|
@ -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
|
|
@ -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>
|
|
@ -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
|
||||
---
|
|
@ -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
|
|
@ -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));
|
||||
}
|
||||
}
|
|
@ -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");
|
||||
}
|
|
@ -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
|
||||
}'
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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`
|
|
@ -0,0 +1,5 @@
|
|||
# ==========================================================================
|
||||
# Build system
|
||||
# ==========================================================================
|
||||
|
||||
CFLAGS += -march=i386 -mpreferred-stack-boundary=2
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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)
|
||||
{
|
||||
}
|
|
@ -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("");
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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];
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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.
|
||||
*/
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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"
|
||||
*/
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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, <ok);
|
||||
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(<ok, "-");
|
||||
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(<ok, "-");
|
||||
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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -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
Loading…
Reference in New Issue