From 73b16af8feec390afbabd9356d6e5e83c0390838 Mon Sep 17 00:00:00 2001 From: Bjørn Mork Date: Fri, 15 May 2015 10:20:47 +0200 Subject: busybox: imported from http://www.busybox.net/downloads/busybox-1.13.3.tar.bz2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Bjørn Mork --- coreutils/Config.in | 827 +++++++++++++++ coreutils/Kbuild | 92 ++ coreutils/basename.c | 53 + coreutils/cal.c | 349 +++++++ coreutils/cat.c | 48 + coreutils/catv.c | 75 ++ coreutils/chgrp.c | 31 + coreutils/chmod.c | 160 +++ coreutils/chown.c | 180 ++++ coreutils/chroot.c | 37 + coreutils/cksum.c | 64 ++ coreutils/comm.c | 100 ++ coreutils/cp.c | 107 ++ coreutils/cut.c | 287 ++++++ coreutils/date.c | 239 +++++ coreutils/dd.c | 356 +++++++ coreutils/df.c | 198 ++++ coreutils/dirname.c | 27 + coreutils/dos2unix.c | 98 ++ coreutils/du.c | 240 +++++ coreutils/echo.c | 303 ++++++ coreutils/env.c | 123 +++ coreutils/expand.c | 200 ++++ coreutils/expr.c | 504 ++++++++++ coreutils/false.c | 21 + coreutils/fold.c | 151 +++ coreutils/head.c | 140 +++ coreutils/hostid.c | 26 + coreutils/id.c | 220 ++++ coreutils/id_test.sh | 244 +++++ coreutils/install.c | 210 ++++ coreutils/length.c | 19 + coreutils/libcoreutils/Kbuild | 12 + coreutils/libcoreutils/coreutils.h | 24 + coreutils/libcoreutils/cp_mv_stat.c | 50 + coreutils/libcoreutils/getopt_mk_fifo_nod.c | 48 + coreutils/ln.c | 109 ++ coreutils/logname.c | 43 + coreutils/ls.c | 980 ++++++++++++++++++ coreutils/md5_sha1_sum.c | 175 ++++ coreutils/mkdir.c | 80 ++ coreutils/mkfifo.c | 37 + coreutils/mknod.c | 57 ++ coreutils/mv.c | 135 +++ coreutils/nice.c | 55 + coreutils/nohup.c | 78 ++ coreutils/od.c | 225 +++++ coreutils/od_bloaty.c | 1428 ++++++++++++++++++++++++++ coreutils/printenv.c | 33 + coreutils/printf.c | 386 +++++++ coreutils/pwd.c | 27 + coreutils/readlink.c | 49 + coreutils/realpath.c | 46 + coreutils/rm.c | 55 + coreutils/rmdir.c | 70 ++ coreutils/seq.c | 40 + coreutils/sleep.c | 104 ++ coreutils/sort.c | 406 ++++++++ coreutils/split.c | 139 +++ coreutils/stat.c | 654 ++++++++++++ coreutils/stty.c | 1439 +++++++++++++++++++++++++++ coreutils/sum.c | 99 ++ coreutils/sync.c | 25 + coreutils/tac.c | 106 ++ coreutils/tail.c | 284 ++++++ coreutils/tee.c | 107 ++ coreutils/test.c | 770 ++++++++++++++ coreutils/test_ptr_hack.c | 23 + coreutils/touch.c | 91 ++ coreutils/tr.c | 250 +++++ coreutils/true.c | 21 + coreutils/tty.c | 44 + coreutils/uname.c | 99 ++ coreutils/uniq.c | 102 ++ coreutils/usleep.c | 28 + coreutils/uudecode.c | 224 +++++ coreutils/uuencode.c | 61 ++ coreutils/wc.c | 205 ++++ coreutils/who.c | 80 ++ coreutils/whoami.c | 26 + coreutils/yes.c | 42 + 81 files changed, 15400 insertions(+) create mode 100644 coreutils/Config.in create mode 100644 coreutils/Kbuild create mode 100644 coreutils/basename.c create mode 100644 coreutils/cal.c create mode 100644 coreutils/cat.c create mode 100644 coreutils/catv.c create mode 100644 coreutils/chgrp.c create mode 100644 coreutils/chmod.c create mode 100644 coreutils/chown.c create mode 100644 coreutils/chroot.c create mode 100644 coreutils/cksum.c create mode 100644 coreutils/comm.c create mode 100644 coreutils/cp.c create mode 100644 coreutils/cut.c create mode 100644 coreutils/date.c create mode 100644 coreutils/dd.c create mode 100644 coreutils/df.c create mode 100644 coreutils/dirname.c create mode 100644 coreutils/dos2unix.c create mode 100644 coreutils/du.c create mode 100644 coreutils/echo.c create mode 100644 coreutils/env.c create mode 100644 coreutils/expand.c create mode 100644 coreutils/expr.c create mode 100644 coreutils/false.c create mode 100644 coreutils/fold.c create mode 100644 coreutils/head.c create mode 100644 coreutils/hostid.c create mode 100644 coreutils/id.c create mode 100755 coreutils/id_test.sh create mode 100644 coreutils/install.c create mode 100644 coreutils/length.c create mode 100644 coreutils/libcoreutils/Kbuild create mode 100644 coreutils/libcoreutils/coreutils.h create mode 100644 coreutils/libcoreutils/cp_mv_stat.c create mode 100644 coreutils/libcoreutils/getopt_mk_fifo_nod.c create mode 100644 coreutils/ln.c create mode 100644 coreutils/logname.c create mode 100644 coreutils/ls.c create mode 100644 coreutils/md5_sha1_sum.c create mode 100644 coreutils/mkdir.c create mode 100644 coreutils/mkfifo.c create mode 100644 coreutils/mknod.c create mode 100644 coreutils/mv.c create mode 100644 coreutils/nice.c create mode 100644 coreutils/nohup.c create mode 100644 coreutils/od.c create mode 100644 coreutils/od_bloaty.c create mode 100644 coreutils/printenv.c create mode 100644 coreutils/printf.c create mode 100644 coreutils/pwd.c create mode 100644 coreutils/readlink.c create mode 100644 coreutils/realpath.c create mode 100644 coreutils/rm.c create mode 100644 coreutils/rmdir.c create mode 100644 coreutils/seq.c create mode 100644 coreutils/sleep.c create mode 100644 coreutils/sort.c create mode 100644 coreutils/split.c create mode 100644 coreutils/stat.c create mode 100644 coreutils/stty.c create mode 100644 coreutils/sum.c create mode 100644 coreutils/sync.c create mode 100644 coreutils/tac.c create mode 100644 coreutils/tail.c create mode 100644 coreutils/tee.c create mode 100644 coreutils/test.c create mode 100644 coreutils/test_ptr_hack.c create mode 100644 coreutils/touch.c create mode 100644 coreutils/tr.c create mode 100644 coreutils/true.c create mode 100644 coreutils/tty.c create mode 100644 coreutils/uname.c create mode 100644 coreutils/uniq.c create mode 100644 coreutils/usleep.c create mode 100644 coreutils/uudecode.c create mode 100644 coreutils/uuencode.c create mode 100644 coreutils/wc.c create mode 100644 coreutils/who.c create mode 100644 coreutils/whoami.c create mode 100644 coreutils/yes.c (limited to 'coreutils') diff --git a/coreutils/Config.in b/coreutils/Config.in new file mode 100644 index 0000000..8cbc92f --- /dev/null +++ b/coreutils/Config.in @@ -0,0 +1,827 @@ +# +# 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 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 FEATURE_DF_FANCY + bool "Enable -a, -i, -B" + default n + depends on DF + help + This option enables -a, -i and -B. + +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 || ASH_BUILTIN_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 EXPAND + bool "expand" + default n + help + By default, convert all tabs to spaces. + +config FEATURE_EXPAND_LONG_OPTIONS + bool "Enable long options" + default n + depends on EXPAND && GETOPT_LONG + help + Support long options for the expand 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 READLINK + bool "readlink" + default n + help + This program reads a symbolic link and returns the name + of the file it points to + +config FEATURE_READLINK_FOLLOW + bool "Enable canonicalization by following all symlinks (-f)" + default n + depends on READLINK + help + Enable the readlink option (-f). + +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 FEATURE_RMDIR_LONG_OPTIONS + bool "Enable long options" + default n + depends on RMDIR && GETOPT_LONG + help + Support long options for the rmdir applet, including + --ignore-fail-on-non-empty for compatibility with GNU rmdir. + +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" + default n + help + sleep is used to pause for a specified number of seconds. + It comes in 3 versions: + - small: takes one integer parameter + - fancy: takes multiple integer arguments with suffixes: + sleep 1d 2h 3m 15s + - fancy with fractional numbers: + sleep 2.3s 4.5h sleeps for 16202.3 seconds + Last one is "the most compatible" with coreutils sleep, + but it adds around 1k of code. + +config FEATURE_FANCY_SLEEP + bool "Enable multiple arguments and s/m/h/d suffixes" + default n + depends on SLEEP + help + Allow sleep to pause for specified minutes, hours, and days. + +config FEATURE_FLOAT_SLEEP + bool "Enable fractional arguments" + default n + depends on FEATURE_FANCY_SLEEP + help + Allow for fractional numeric parameters. + +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 SPLIT + bool "split" + default n + help + split a file into pieces. + +config FEATURE_SPLIT_FANCY + bool "Fancy extensions" + default n + depends on SPLIT + help + Add support for features not required by SUSv3. + Supports additional suffixes 'b' for 512 bytes, + 'g' for 1GiB for the -b option. + +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 TAC + bool "tac" + default n + help + tac is used to concatenate and print files in reverse. + +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 || ASH_BUILTIN_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 UNEXPAND + bool "unexpand" + default n + help + By default, convert only leading sequences of blanks to tabs. + +config FEATURE_UNEXPAND_LONG_OPTIONS + bool "Enable long options" + default n + depends on UNEXPAND && GETOPT_LONG + help + Support long options for the unexpand applet. + +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 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 diff --git a/coreutils/Kbuild b/coreutils/Kbuild new file mode 100644 index 0000000..a5a2d4c --- /dev/null +++ b/coreutils/Kbuild @@ -0,0 +1,92 @@ +# Makefile for busybox +# +# Copyright (C) 1999-2005 by Erik Andersen +# +# 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_MORE) += cat.o # more uses it if stdout isn't a tty +lib-$(CONFIG_LESS) += cat.o # less too +lib-$(CONFIG_CRONTAB) += cat.o # crontab -l +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_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_DIRNAME) += dirname.o +lib-$(CONFIG_DOS2UNIX) += dos2unix.o +lib-$(CONFIG_DU) += du.o +lib-$(CONFIG_ECHO) += echo.o +lib-$(CONFIG_ASH) += echo.o # used by ash +lib-$(CONFIG_HUSH) += echo.o # used by hush +lib-$(CONFIG_ENV) += env.o +lib-$(CONFIG_EXPR) += expr.o +lib-$(CONFIG_EXPAND) += expand.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_ASH_BUILTIN_PRINTF) += printf.o +lib-$(CONFIG_PWD) += pwd.o +lib-$(CONFIG_READLINK) += readlink.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_SPLIT) += split.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_TAC) += tac.o +lib-$(CONFIG_TAIL) += tail.o +lib-$(CONFIG_TEE) += tee.o +lib-$(CONFIG_TEST) += test.o test_ptr_hack.o +lib-$(CONFIG_ASH) += test.o test_ptr_hack.o # used by ash +lib-$(CONFIG_HUSH) += test.o test_ptr_hack.o # used by hush +lib-$(CONFIG_MSH) += test.o test_ptr_hack.o # used by msh +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_UNEXPAND) += expand.o +lib-$(CONFIG_UNIQ) += uniq.o +lib-$(CONFIG_USLEEP) += usleep.o +lib-$(CONFIG_UUDECODE) += uudecode.o +lib-$(CONFIG_UUENCODE) += uuencode.o +lib-$(CONFIG_WC) += wc.o +lib-$(CONFIG_WHO) += who.o +lib-$(CONFIG_WHOAMI) += whoami.o +lib-$(CONFIG_YES) += yes.o diff --git a/coreutils/basename.c b/coreutils/basename.c new file mode 100644 index 0000000..8a5597e --- /dev/null +++ b/coreutils/basename.c @@ -0,0 +1,53 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini basename implementation for busybox + * + * Copyright (C) 1999-2004 by Erik Andersen + * + * 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 "libbb.h" + +/* This is a NOFORK applet. Be very careful! */ + +int basename_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int basename_main(int argc, char **argv) +{ + size_t m, n; + char *s; + + if (((unsigned int)(argc-2)) >= 2) { + bb_show_usage(); + } + + /* It should strip slash: /abc/def/ -> def */ + s = bb_get_last_path_component_strip(*++argv); + + m = strlen(s); + if (*++argv) { + n = strlen(*argv); + if ((m > n) && ((strcmp)(s+m-n, *argv) == 0)) { + m -= n; + /*s[m] = '\0'; - redundant */ + } + } + + /* puts(s) will do, but we can do without stdio this way: */ + s[m++] = '\n'; + /* NB: != is correct here: */ + return full_write(STDOUT_FILENO, s, m) != (ssize_t)m; +} diff --git a/coreutils/cal.c b/coreutils/cal.c new file mode 100644 index 0000000..9b59777 --- /dev/null +++ b/coreutils/cal.c @@ -0,0 +1,349 @@ +/* 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 "libbb.h" + +/* We often use "unsigned" intead of "int", it's easier to div on most CPUs */ + +#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 unsigned char days_in_month[] ALIGN1 = { + 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 +}; + +static const unsigned char sep1752[] ALIGN1 = { + 1, 2, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30 +}; + +/* Set to 0 or 1 in main */ +#define julian ((unsigned)option_mask32) + +/* leap year -- account for Gregorian reformation in 1752 */ +static int leap_year(unsigned yr) +{ + if (yr <= 1752) + return !(yr % 4); + return (!(yr % 4) && (yr % 100)) || !(yr % 400); +} + +/* 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 *, unsigned, unsigned); +static void day_array(unsigned, unsigned, unsigned *); +static void trim_trailing_spaces_and_print(char *); + +static void blank_string(char *buf, size_t buflen); +static char *build_row(char *p, unsigned *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) MAIN_EXTERNALLY_VISIBLE; +int cal_main(int argc, char **argv) +{ + struct tm *local_time; + struct tm zero_tm; + time_t now; + unsigned month, year, flags, i; + char *month_names[12]; + char day_headings[28]; /* 28 for julian, 21 for nonjulian */ + char buf[40]; + + flags = getopt32(argv, "jy"); + /* This sets julian = flags & 1: */ + option_mask32 &= 1; + month = 0; + argv += optind; + argc -= optind; + + if (argc > 2) { + bb_show_usage(); + } + + if (!argc) { + time(&now); + local_time = localtime(&now); + year = local_time->tm_year + 1900; + if (!(flags & 2)) { /* no -y */ + month = local_time->tm_mon + 1; + } + } else { + if (argc == 2) { + month = xatou_range(*argv++, 1, 12); + } + year = xatou_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) { + unsigned row, len, days[MAXDAYS]; + unsigned *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 { + unsigned row, which_cal, week_len, days[12][MAXDAYS]; + unsigned *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); + } + bb_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(EXIT_SUCCESS); +} + +/* + * 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(unsigned month, unsigned year, unsigned *days) +{ + unsigned long temp; + unsigned i; + unsigned day, dw, dm; + + memset(days, SPACE, MAXDAYS * sizeof(int)); + + if ((month == 9) && (year == 1752)) { + /* Assumes the Gregorian reformation eliminates + * 3 Sep. 1752 through 13 Sep. 1752. + */ + unsigned j_offset = julian * 244; + size_t oday = 0; + + 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. + */ + temp = (long)(year - 1) * 365 + leap_years_since_year_1(year - 1) + day; + if (temp < FIRST_MISSING_DAY) { + dw = ((temp - 1 + SATURDAY) % 7); + } else { + dw = (((temp - 1 + SATURDAY) - NUMBER_MISSING_DAYS) % 7); + } + + if (!julian) { + day = 1; + } + + dm = days_in_month[month]; + if ((month == 2) && leap_year(year)) { + ++dm; + } + + do { + days[dw++] = day++; + } while (--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, unsigned len, unsigned separate) +{ + unsigned 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, unsigned *dp) +{ + unsigned col, val, day; + + memset(p, ' ', (julian + DAY_LEN) * 7); + + col = 0; + do { + day = *dp++; + if (day != SPACE) { + if (julian) { + ++p; + if (day >= 100) { + *p = '0'; + p[-1] = (day / 100) + '0'; + day %= 100; + } + } + val = day / 10; + if (val > 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. + */ diff --git a/coreutils/cat.c b/coreutils/cat.c new file mode 100644 index 0000000..0024eb8 --- /dev/null +++ b/coreutils/cat.c @@ -0,0 +1,48 @@ +/* vi: set sw=4 ts=4: */ +/* + * cat implementation for busybox + * + * Copyright (C) 2003 Manuel Novoa III + * + * 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 "libbb.h" + +/* This is a NOFORK applet. Be very careful! */ + + +int bb_cat(char **argv) +{ + int fd; + int retval = EXIT_SUCCESS; + + if (!*argv) + argv = (char**) &bb_argv_dash; + + do { + fd = open_or_warn_stdin(*argv); + if (fd >= 0) { + /* This is not a xfunc - never exits */ + off_t r = bb_copyfd_eof(fd, STDOUT_FILENO); + if (fd != STDIN_FILENO) + close(fd); + if (r >= 0) + continue; + } + retval = EXIT_FAILURE; + } while (*++argv); + + return retval; +} + +int cat_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int cat_main(int argc UNUSED_PARAM, char **argv) +{ + getopt32(argv, "u"); + argv += optind; + return bb_cat(argv); +} diff --git a/coreutils/catv.c b/coreutils/catv.c new file mode 100644 index 0000000..ff3139c --- /dev/null +++ b/coreutils/catv.c @@ -0,0 +1,75 @@ +/* vi: set sw=4 ts=4: */ +/* + * cat -v implementation for busybox + * + * Copyright (C) 2006 Rob Landley + * + * 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 "libbb.h" + +int catv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int catv_main(int argc UNUSED_PARAM, char **argv) +{ + int retval = EXIT_SUCCESS; + int fd; + unsigned flags; + + flags = getopt32(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; + + /* Read from stdin if there's nothing else to do. */ + if (!argv[0]) + *--argv = (char*)"-"; + do { + fd = open_or_warn_stdin(*argv); + if (fd < 0) { + retval = EXIT_FAILURE; + continue; + } + for (;;) { + int i, res; + +#define read_buf bb_common_bufsiz1 + res = read(fd, read_buf, COMMON_BUFSIZE); + if (res < 0) + retval = EXIT_FAILURE; + if (res < 1) + break; + for (i = 0; i < res; i++) { + unsigned char c = read_buf[i]; + + if (c > 126 && (flags & CATV_OPT_v)) { + if (c == 127) { + printf("^?"); + continue; + } + printf("M-"); + c -= 128; + } + if (c < 32) { + if (c == 10) { + if (flags & CATV_OPT_e) + bb_putchar('$'); + } else if (flags & (c==9 ? CATV_OPT_t : CATV_OPT_v)) { + printf("^%c", c+'@'); + continue; + } + } + bb_putchar(c); + } + } + if (ENABLE_FEATURE_CLEAN_UP && fd) + close(fd); + } while (*++argv); + + fflush_stdout_and_exit(retval); +} diff --git a/coreutils/chgrp.c b/coreutils/chgrp.c new file mode 100644 index 0000000..7f39048 --- /dev/null +++ b/coreutils/chgrp.c @@ -0,0 +1,31 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini chgrp implementation for busybox + * + * Copyright (C) 1999-2004 by Erik Andersen + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +/* BB_AUDIT SUSv3 defects - none? */ +/* BB_AUDIT GNU defects - unsupported long options. */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/chgrp.html */ + +#include "libbb.h" + +/* This is a NOEXEC applet. Be very careful! */ + + +int chgrp_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +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); +} diff --git a/coreutils/chmod.c b/coreutils/chmod.c new file mode 100644 index 0000000..40f681f --- /dev/null +++ b/coreutils/chmod.c @@ -0,0 +1,160 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini chmod implementation for busybox + * + * Copyright (C) 1999-2004 by Erik Andersen + * + * Reworked by (C) 2002 Vladimir Oleynik + * 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 "libbb.h" + +/* This is a NOEXEC applet. Be very careful! */ + + +#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 FAST_FUNC fileAction(const char *fileName, struct stat *statbuf, void* param, 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 *)param, &newmode)) + bb_error_msg_and_die("invalid mode: %s", (char *)param); + + 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_simple_perror_msg(fileName); + return FALSE; +} + +int chmod_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int chmod_main(int argc UNUSED_PARAM, 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(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 + 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 directory. + +#!/bin/sh +t1="/tmp/busybox chmod" +t2="/usr/bin/chmod" +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 + ) +} +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 + rm out.diff +} +echo "If script produced 'out.diff' file, then at least one testcase failed" +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" +*/ diff --git a/coreutils/chown.c b/coreutils/chown.c new file mode 100644 index 0000000..3452492 --- /dev/null +++ b/coreutils/chown.c @@ -0,0 +1,180 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini chown implementation for busybox + * + * Copyright (C) 1999-2004 by Erik Andersen + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +/* BB_AUDIT SUSv3 defects - none? */ +/* BB_AUDIT GNU defects - unsupported long options. */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/chown.html */ + +#include "libbb.h" + +/* This is a NOEXEC applet. Be very careful! */ + + +#define OPT_STR ("Rh" USE_DESKTOP("vcfLHP")) +#define BIT_RECURSE 1 +#define OPT_RECURSE (opt & 1) +#define OPT_NODEREF (opt & 2) +#define OPT_VERBOSE (USE_DESKTOP(opt & 0x04) SKIP_DESKTOP(0)) +#define OPT_CHANGED (USE_DESKTOP(opt & 0x08) SKIP_DESKTOP(0)) +#define OPT_QUIET (USE_DESKTOP(opt & 0x10) SKIP_DESKTOP(0)) +/* POSIX options + * -L traverse every symbolic link to a directory encountered + * -H if a command line argument is a symbolic link to a directory, traverse it + * -P do not traverse any symbolic links (default) + * We do not conform to the following: + * "Specifying more than one of -H, -L, and -P is not an error. + * The last option specified shall determine the behavior of the utility." */ +/* -L */ +#define BIT_TRAVERSE 0x20 +#define OPT_TRAVERSE (USE_DESKTOP(opt & BIT_TRAVERSE) SKIP_DESKTOP(0)) +/* -H or -L */ +#define BIT_TRAVERSE_TOP (0x20|0x40) +#define OPT_TRAVERSE_TOP (USE_DESKTOP(opt & BIT_TRAVERSE_TOP) SKIP_DESKTOP(0)) + +typedef int (*chown_fptr)(const char *, uid_t, gid_t); + +struct param_t { + struct bb_uidgid_t ugid; + chown_fptr chown_func; +}; + +static int FAST_FUNC fileAction(const char *fileName, struct stat *statbuf, + void *vparam, int depth UNUSED_PARAM) +{ +#define param (*(struct param_t*)vparam) +#define opt option_mask32 + uid_t u = (param.ugid.uid == (uid_t)-1) ? statbuf->st_uid : param.ugid.uid; + gid_t g = (param.ugid.gid == (gid_t)-1) ? statbuf->st_gid : param.ugid.gid; + + if (param.chown_func(fileName, u, g) == 0) { + if (OPT_VERBOSE + || (OPT_CHANGED && (statbuf->st_uid != u || statbuf->st_gid != g)) + ) { + printf("changed ownership of '%s' to %u:%u\n", + fileName, (unsigned)u, (unsigned)g); + } + return TRUE; + } + if (!OPT_QUIET) + bb_simple_perror_msg(fileName); /* A filename can have % in it... */ + return FALSE; +#undef opt +#undef param +} + +int chown_main(int argc UNUSED_PARAM, char **argv) +{ + int retval = EXIT_SUCCESS; + int opt, flags; + struct param_t param; + + param.ugid.uid = -1; + param.ugid.gid = -1; + param.chown_func = chown; + + opt_complementary = "-2"; + opt = getopt32(argv, OPT_STR); + argv += optind; + + /* This matches coreutils behavior (almost - see below) */ + if (OPT_NODEREF + /* || (OPT_RECURSE && !OPT_TRAVERSE_TOP): */ + USE_DESKTOP( || (opt & (BIT_RECURSE|BIT_TRAVERSE_TOP)) == BIT_RECURSE) + ) { + param.chown_func = lchown; + } + + flags = ACTION_DEPTHFIRST; /* match coreutils order */ + if (OPT_RECURSE) + flags |= ACTION_RECURSE; + if (OPT_TRAVERSE_TOP) + flags |= ACTION_FOLLOWLINKS_L0; /* -H/-L: follow links on depth 0 */ + if (OPT_TRAVERSE) + flags |= ACTION_FOLLOWLINKS; /* follow links if -L */ + + parse_chown_usergroup_or_die(¶m.ugid, argv[0]); + + /* Ok, ready to do the deed now */ + argv++; + do { + if (!recursive_action(*argv, + flags, /* flags */ + fileAction, /* file action */ + fileAction, /* dir action */ + ¶m, /* user data */ + 0) /* depth */ + ) { + retval = EXIT_FAILURE; + } + } while (*++argv); + + return retval; +} + +/* +Testcase. Run in empty directory. + +#!/bin/sh +t1="/tmp/busybox chown" +t2="/usr/bin/chown" +create() { + rm -rf $1; mkdir $1 + ( + cd $1 || exit 1 + mkdir dir dir2 + >up + >file + >dir/file + >dir2/file + ln -s dir linkdir + ln -s file linkfile + ln -s ../up dir/linkup + ln -s ../dir2 dir/linkupdir2 + ) + chown -R 0:0 $1 +} +tst() { + create test1 + create test2 + echo "[$1]" >>test1.out + echo "[$1]" >>test2.out + (cd test1; $t1 $1) >>test1.out 2>&1 + (cd test2; $t2 $1) >>test2.out 2>&1 + (cd test1; ls -lnR) >out1 + (cd test2; ls -lnR) >out2 + echo "chown $1" >out.diff + if ! diff -u out1 out2 >>out.diff; then exit 1; fi + rm out.diff +} +tst_for_each() { + tst "$1 1:1 file" + tst "$1 1:1 dir" + tst "$1 1:1 linkdir" + tst "$1 1:1 linkfile" +} +echo "If script produced 'out.diff' file, then at least one testcase failed" +>test1.out +>test2.out +# These match coreutils 6.8: +tst_for_each "-v" +tst_for_each "-vR" +tst_for_each "-vRP" +tst_for_each "-vRL" +tst_for_each "-vRH" +tst_for_each "-vh" +tst_for_each "-vhR" +tst_for_each "-vhRP" +tst_for_each "-vhRL" +tst_for_each "-vhRH" +# Fix `name' in coreutils output +sed 's/`/'"'"'/g' -i test2.out +# Compare us with coreutils output +diff -u test1.out test2.out + +*/ diff --git a/coreutils/chroot.c b/coreutils/chroot.c new file mode 100644 index 0000000..1198a41 --- /dev/null +++ b/coreutils/chroot.c @@ -0,0 +1,37 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini chroot implementation for busybox + * + * Copyright (C) 1999-2004 by Erik Andersen + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +/* BB_AUDIT SUSv3 N/A -- Matches GNU behavior. */ + +#include "libbb.h" + +int chroot_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int chroot_main(int argc, char **argv) +{ + if (argc < 2) { + bb_show_usage(); + } + + ++argv; + xchroot(*argv); + xchdir("/"); + + ++argv; + if (argc == 2) { + argv -= 2; + argv[0] = getenv("SHELL"); + if (!argv[0]) { + argv[0] = (char *) DEFAULT_SHELL; + } + argv[1] = (char *) "-i"; + } + + BB_EXECVP(*argv, argv); + bb_perror_msg_and_die("cannot execute %s", *argv); +} diff --git a/coreutils/cksum.c b/coreutils/cksum.c new file mode 100644 index 0000000..546532c --- /dev/null +++ b/coreutils/cksum.c @@ -0,0 +1,64 @@ +/* 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 "libbb.h" + +int cksum_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int cksum_main(int argc UNUSED_PARAM, char **argv) +{ + uint32_t *crc32_table = crc32_filltable(NULL, 1); + uint32_t crc; + off_t length, filesize; + int bytes_read; + uint8_t *cp; + +#if ENABLE_DESKTOP + getopt32(argv, ""); /* coreutils 6.9 compat */ + argv += optind; +#else + argv++; +#endif + + do { + int fd = open_or_warn_stdin(*argv ? *argv : bb_msg_standard_input); + + if (fd < 0) + continue; + crc = 0; + length = 0; + +#define read_buf bb_common_bufsiz1 + while ((bytes_read = safe_read(fd, read_buf, sizeof(read_buf))) > 0) { + cp = (uint8_t *) read_buf; + length += bytes_read; + do { + crc = (crc << 8) ^ crc32_table[(crc >> 24) ^ *cp++]; + } while (--bytes_read); + } + close(fd); + + filesize = length; + + while (length) { + crc = (crc << 8) ^ crc32_table[(uint8_t)(crc >> 24) ^ (uint8_t)length]; + /* must ensure that shift is unsigned! */ + if (sizeof(length) <= sizeof(unsigned)) + length = (unsigned)length >> 8; + else if (sizeof(length) <= sizeof(unsigned long)) + length = (unsigned long)length >> 8; + else + length = (unsigned long long)length >> 8; + } + crc = ~crc; + + printf((*argv ? "%"PRIu32" %"OFF_FMT"i %s\n" : "%"PRIu32" %"OFF_FMT"i\n"), + crc, filesize, *argv); + } while (*argv && *++argv); + + fflush_stdout_and_exit(EXIT_SUCCESS); +} diff --git a/coreutils/comm.c b/coreutils/comm.c new file mode 100644 index 0000000..221cbfb --- /dev/null +++ b/coreutils/comm.c @@ -0,0 +1,100 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini comm implementation for busybox + * + * Copyright (C) 2005 by Robert Sullivan + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +#include "libbb.h" + +#define COMM_OPT_1 (1 << 0) +#define COMM_OPT_2 (1 << 1) +#define COMM_OPT_3 (1 << 2) + +/* writeline outputs the input given, appropriately aligned according to class */ +static void writeline(char *line, int class) +{ + int flags = option_mask32; + if (class == 0) { + if (flags & COMM_OPT_1) + return; + } else if (class == 1) { + if (flags & COMM_OPT_2) + return; + if (!(flags & COMM_OPT_1)) + putchar('\t'); + } else /*if (class == 2)*/ { + if (flags & COMM_OPT_3) + return; + if (!(flags & COMM_OPT_1)) + putchar('\t'); + if (!(flags & COMM_OPT_2)) + putchar('\t'); + } + puts(line); +} + +int comm_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int comm_main(int argc UNUSED_PARAM, char **argv) +{ + char *thisline[2]; + FILE *stream[2]; + int i; + int order; + + opt_complementary = "=2"; + getopt32(argv, "123"); + argv += optind; + + for (i = 0; i < 2; ++i) { + stream[i] = xfopen_stdin(argv[i]); + } + + order = 0; + thisline[1] = thisline[0] = NULL; + while (1) { + if (order <= 0) { + free(thisline[0]); + thisline[0] = xmalloc_fgetline(stream[0]); + } + if (order >= 0) { + free(thisline[1]); + thisline[1] = xmalloc_fgetline(stream[1]); + } + + i = !thisline[0] + (!thisline[1] << 1); + if (i) + break; + order = strcmp(thisline[0], thisline[1]); + + if (order >= 0) + writeline(thisline[1], order ? 1 : 2); + else + writeline(thisline[0], 0); + } + + /* EOF at least on one of the streams */ + i &= 1; + if (thisline[i]) { + /* stream[i] is not at EOF yet */ + /* we did not print thisline[i] yet */ + char *p = thisline[i]; + writeline(p, i); + while (1) { + free(p); + p = xmalloc_fgetline(stream[i]); + if (!p) + break; + writeline(p, i); + } + } + + if (ENABLE_FEATURE_CLEAN_UP) { + fclose(stream[0]); + fclose(stream[1]); + } + + return EXIT_SUCCESS; +} diff --git a/coreutils/cp.c b/coreutils/cp.c new file mode 100644 index 0000000..40d3625 --- /dev/null +++ b/coreutils/cp.c @@ -0,0 +1,107 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini cp implementation for busybox + * + * Copyright (C) 2000 by Matt Kraai + * SELinux support by Yuichi Nakamura + * + * 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 "libbb.h" +#include "libcoreutils/coreutils.h" + +/* This is a NOEXEC applet. Be very careful! */ + + +int cp_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +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), + }; + + // Need at least two arguments + // Soft- and hardlinking doesn't mix + // -P and -d are the same (-P is POSIX, -d is GNU) + // -r and -R are the same + // -R (and therefore -r) turns on -d (coreutils does this) + // -a = -pdR + opt_complementary = "-2:l--s:s--l:Pd:rRd:Rd:apdR:HL"; + flags = getopt32(argv, FILEUTILS_CP_OPTSTR "arPH"); + argc -= optind; + argv += optind; + flags ^= FILEUTILS_DEREFERENCE; /* the sense of this flag was reversed */ + /* coreutils 6.9 compat: + * by default, "cp" derefs symlinks (creates regular dest files), + * but "cp -R" does not. We switch off deref if -r or -R (see above). + * However, "cp -RL" must still deref symlinks: */ + if (flags & FILEUTILS_DEREF_SOFTLINK) /* -L */ + flags |= FILEUTILS_DEREFERENCE; + /* The behavior of -H is *almost* like -L, but not quite, so let's + * just ignore it too for fun. TODO. + if (flags & OPT_H) ... // deref command-line params only + */ + +#if ENABLE_SELINUX + if (flags & FILEUTILS_PRESERVE_SECURITY_CONTEXT) { + selinux_or_die(); + } +#endif + + last = argv[argc - 1]; + /* If there are only two arguments and... */ + if (argc == 2) { + 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 = last; + goto DO_COPY; /* NB: argc==2 -> *++argv==last */ + } + } + + while (1) { + dest = concat_path_file(last, bb_get_last_path_component_strip(*argv)); + DO_COPY: + if (copy_file(*argv, dest, flags) < 0) { + status = 1; + } + if (*++argv == last) { + /* possibly leaking dest... */ + break; + } + free((void*)dest); + } + + /* Exit. We are NOEXEC, not NOFORK. We do exit at the end of main() */ + return status; +} diff --git a/coreutils/cut.c b/coreutils/cut.c new file mode 100644 index 0000000..9cc22be --- /dev/null +++ b/coreutils/cut.c @@ -0,0 +1,287 @@ +/* vi: set sw=4 ts=4: */ +/* + * cut.c - minimalist version of cut + * + * Copyright (C) 1999,2000,2001 by Lineo, inc. + * Written by Mark Whitley + * debloated by Bernhard Reutner-Fischer + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +#include "libbb.h" + +/* This is a NOEXEC applet. Be very careful! */ + + +/* option vars */ +static const char optstring[] ALIGN1 = "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) + +struct cut_list { + int startpos; + int endpos; +}; + +enum { + BOL = 0, + EOL = INT_MAX, + NON_RANGE = -1 +}; + +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 delim, const struct cut_list *cut_lists, unsigned nlists) +{ + char *line; + unsigned linenum = 0; /* keep these zero-based to be consistent */ + + /* go through every line in the file */ + while ((line = xmalloc_fgetline(file)) != NULL) { + + /* set up a list so we can keep track of what's been printed */ + int linelen = strlen(line); + char *printed = xzalloc(linelen + 1); + char *orig_line = line; + unsigned 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 < linelen) { + if (!printed[spos]) { + printed[spos] = 'X'; + putchar(line[spos]); + } + spos++; + if (spos > cut_lists[cl_pos].endpos + /* NON_RANGE is -1, so if below is true, + * the above was true too (spos is >= 0) */ + /* || 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 (((int)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 < (int)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 ((int)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); + } +} + +int cut_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int cut_main(int argc UNUSED_PARAM, char **argv) +{ + /* growable array holding a series of lists */ + struct cut_list *cut_lists = NULL; + unsigned nlists = 0; /* number of elements in above list */ + char delim = '\t'; /* delimiter, default is tab */ + char *sopt, *ltok; + unsigned opt; + + opt_complementary = "b--bcf:c--bcf:f--bcf"; + opt = getopt32(argv, optstring, &sopt, &sopt, &sopt, <ok); +// argc -= optind; + argv += optind; + if (!(opt & (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 (opt & CUT_OPT_DELIM_FLGS) { + if (ltok[0] && ltok[1]) { /* more than 1 char? */ + 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 (!(opt & CUT_OPT_FIELDS_FLGS)) { + static const char _op_on_field[] ALIGN1 = " only when operating on fields"; + + if (opt & 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 (!ltok[0]) + continue; + + /* get the start pos */ + ntok = strsep(<ok, "-"); + if (!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 */ + if (ltok == NULL) { + e = NON_RANGE; + } else if (!ltok[0]) { + e = EOL; + } else { + e = xatoi_u(ltok); + /* 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; + } + + /* add the new list */ + cut_lists = xrealloc_vector(cut_lists, 4, nlists); + /* NB: startpos is always >= 0, + * while endpos may be = NON_RANGE (-1) */ + cut_lists[nlists].startpos = s; + cut_lists[nlists].endpos = e; + nlists++; + } + + /* 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); + } + + { + int retval = EXIT_SUCCESS; + + if (!*argv) + *--argv = (char *)"-"; + + do { + FILE *file = fopen_or_warn_stdin(*argv); + if (!file) { + retval = EXIT_FAILURE; + continue; + } + cut_file(file, delim, cut_lists, nlists); + fclose_if_not_stdin(file); + } while (*++argv); + + if (ENABLE_FEATURE_CLEAN_UP) + free(cut_lists); + fflush_stdout_and_exit(retval); + } +} diff --git a/coreutils/date.c b/coreutils/date.c new file mode 100644 index 0000000..177b7d0 --- /dev/null +++ b/coreutils/date.c @@ -0,0 +1,239 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini date implementation for busybox + * + * by Matthew Grant + * + * iso-format handling added by Robert Griebl + * bugfixes and cleanup by Bernhard Reutner-Fischer + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. +*/ + +#include "libbb.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 maybe_set_utc(int opt) +{ + if (opt & DATE_OPT_UTC) + putenv((char*)"TZ=UTC0"); +} + +int date_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int date_main(int argc UNUSED_PARAM, char **argv) +{ + struct tm tm_time; + time_t tm; + unsigned opt; + int ifmt = -1; + char *date_str; + char *fmt_dt2str; + char *fmt_str2dt; + char *filename; + char *isofmt_arg = NULL; + + opt_complementary = "d--s:s--d" + USE_FEATURE_DATE_ISOFMT(":R--I:I--R"); + opt = getopt32(argv, "Rs:ud:r:" + USE_FEATURE_DATE_ISOFMT("I::D:"), + &date_str, &date_str, &filename + USE_FEATURE_DATE_ISOFMT(, &isofmt_arg, &fmt_str2dt)); + argv += optind; + maybe_set_utc(opt); + + if (ENABLE_FEATURE_DATE_ISOFMT && (opt & DATE_OPT_TIMESPEC)) { + ifmt = 0; /* default is date */ + if (isofmt_arg) { + static const char isoformats[] ALIGN1 = + "date\0""hours\0""minutes\0""seconds\0"; + ifmt = index_in_strings(isoformats, isofmt_arg); + if (ifmt < 0) + bb_show_usage(); + } + } + + fmt_dt2str = NULL; + if (argv[0] && argv[0][0] == '+') { + fmt_dt2str = &argv[0][1]; /* Skip over the '+' */ + argv++; + } + if (!(opt & (DATE_OPT_SET | DATE_OPT_DATE))) { + opt |= DATE_OPT_SET; + date_str = argv[0]; /* can be NULL */ + if (date_str) + argv++; + } + if (*argv) + bb_show_usage(); + + /* Now we have parsed all the information except the date format + which depends on whether the clock is being set or read */ + + if (opt & DATE_OPT_REFERENCE) { + struct stat statbuf; + xstat(filename, &statbuf); + tm = statbuf.st_mtime; + } else + time(&tm); + memcpy(&tm_time, localtime(&tm), sizeof(tm_time)); + + /* If date string is given, update tm_time, and maybe set date */ + if (date_str != NULL) { + /* Zero out fields - take her back to midnight! */ + 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)) { + if (strptime(date_str, fmt_str2dt, &tm_time) == NULL) + bb_error_msg_and_die(bb_msg_invalid_date, date_str); + } else { + char end = '\0'; + const char *last_colon = strrchr(date_str, ':'); + + if (last_colon != NULL) { + /* Parse input and assign appropriately to tm_time */ + + if (sscanf(date_str, "%u:%u%c", + &tm_time.tm_hour, + &tm_time.tm_min, + &end) >= 2) { + /* no adjustments needed */ + } else if (sscanf(date_str, "%u.%u-%u:%u%c", + &tm_time.tm_mon, &tm_time.tm_mday, + &tm_time.tm_hour, &tm_time.tm_min, + &end) >= 4) { + /* Adjust dates from 1-12 to 0-11 */ + tm_time.tm_mon -= 1; + } else if (sscanf(date_str, "%u.%u.%u-%u:%u%c", &tm_time.tm_year, + &tm_time.tm_mon, &tm_time.tm_mday, + &tm_time.tm_hour, &tm_time.tm_min, + &end) >= 5) { + 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, "%u-%u-%u %u:%u%c", &tm_time.tm_year, + &tm_time.tm_mon, &tm_time.tm_mday, + &tm_time.tm_hour, &tm_time.tm_min, + &end) >= 5) { + tm_time.tm_year -= 1900; /* Adjust years */ + tm_time.tm_mon -= 1; /* Adjust dates from 1-12 to 0-11 */ +//TODO: coreutils 6.9 also accepts "YYYY-MM-DD HH" (no minutes) + } else { + bb_error_msg_and_die(bb_msg_invalid_date, date_str); + } + if (end == ':') { + if (sscanf(last_colon + 1, "%u%c", &tm_time.tm_sec, &end) == 1) + end = '\0'; + /* else end != NUL and we error out */ + } + } else { + if (sscanf(date_str, "%2u%2u%2u%2u%u%c", &tm_time.tm_mon, + &tm_time.tm_mday, &tm_time.tm_hour, &tm_time.tm_min, + &tm_time.tm_year, &end) < 4) + 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; + if (end == '.') { + if (sscanf(strchr(date_str, '.') + 1, "%u%c", + &tm_time.tm_sec, &end) == 1) + end = '\0'; + /* else end != NUL and we error out */ + } + } + if (end != '\0') { + bb_error_msg_and_die(bb_msg_invalid_date, date_str); + } + } + /* 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 (fmt_dt2str == NULL) { + int i; + fmt_dt2str = xzalloc(32); + if (ENABLE_FEATURE_DATE_ISOFMT && ifmt >= 0) { + strcpy(fmt_dt2str, "%Y-%m-%d"); + if (ifmt > 0) { + i = 8; + fmt_dt2str[i++] = 'T'; + fmt_dt2str[i++] = '%'; + fmt_dt2str[i++] = 'H'; + if (ifmt > 1) { + fmt_dt2str[i++] = ':'; + fmt_dt2str[i++] = '%'; + fmt_dt2str[i++] = 'M'; + if (ifmt > 2) { + fmt_dt2str[i++] = ':'; + fmt_dt2str[i++] = '%'; + fmt_dt2str[i++] = 'S'; + } + } + format_utc: + fmt_dt2str[i++] = '%'; + fmt_dt2str[i] = (opt & DATE_OPT_UTC) ? 'Z' : 'z'; + } + } else if (opt & DATE_OPT_RFC2822) { + /* Undo busybox.c for date -R */ + if (ENABLE_LOCALE_SUPPORT) + setlocale(LC_TIME, "C"); + strcpy(fmt_dt2str, "%a, %d %b %Y %H:%M:%S "); + i = 22; + goto format_utc; + } else /* default case */ + fmt_dt2str = (char*)"%a %b %e %H:%M:%S %Z %Y"; + } + +#define date_buf bb_common_bufsiz1 + if (*fmt_dt2str == '\0') { + /* With no format string, just print a blank line */ + date_buf[0] = '\0'; + } else { + /* Handle special conversions */ + if (strncmp(fmt_dt2str, "%f", 2) == 0) { + fmt_dt2str = (char*)"%Y.%m.%d-%H:%M:%S"; + } + + /* Generate output string */ + strftime(date_buf, sizeof(date_buf), fmt_dt2str, &tm_time); + } + puts(date_buf); + + return EXIT_SUCCESS; +} diff --git a/coreutils/dd.c b/coreutils/dd.c new file mode 100644 index 0000000..8a40aa7 --- /dev/null +++ b/coreutils/dd.c @@ -0,0 +1,356 @@ +/* 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 /* For FEATURE_DD_SIGNAL_HANDLING */ +#include "libbb.h" + +/* This is a NOEXEC applet. Be very careful! */ + + +enum { + ifd = STDIN_FILENO, + ofd = STDOUT_FILENO, +}; + +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 }, + { } +}; + +struct globals { + off_t out_full, out_part, in_full, in_part; +}; +#define G (*(struct globals*)&bb_common_bufsiz1) +/* We have to zero it out because of NOEXEC */ +#define INIT_G() memset(&G, 0, sizeof(G)) + + +static void dd_output_status(int UNUSED_PARAM cur_signal) +{ + /* Deliberately using %u, not %d */ + fprintf(stderr, "%"OFF_FMT"u+%"OFF_FMT"u records in\n" + "%"OFF_FMT"u+%"OFF_FMT"u records out\n", + G.in_full, G.in_part, + G.out_full, G.out_part); +} + +static ssize_t full_write_or_warn(const void *buf, size_t len, + const char *const filename) +{ + ssize_t n = full_write(ofd, buf, len); + if (n < 0) + bb_perror_msg("writing '%s'", filename); + return n; +} + +static bool write_and_stats(const void *buf, size_t len, size_t obs, + const char *filename) +{ + ssize_t n = full_write_or_warn(buf, len, filename); + if (n < 0) + return 1; + if ((size_t)n == obs) + G.out_full++; + else if (n) /* > 0 */ + G.out_part++; + return 0; +} + +#if ENABLE_LFS +#define XATOU_SFX xatoull_sfx +#else +#define XATOU_SFX xatoul_sfx +#endif + +int dd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int dd_main(int argc UNUSED_PARAM, char **argv) +{ + enum { + /* Must be in the same order as OP_conv_XXX! */ + /* (see "flags |= (1 << what)" below) */ + FLAG_NOTRUNC = 1 << 0, + FLAG_SYNC = 1 << 1, + FLAG_NOERROR = 1 << 2, + FLAG_FSYNC = 1 << 3, + /* end of conv flags */ + FLAG_TWOBUFS = 1 << 4, + FLAG_COUNT = 1 << 5, + }; + static const char keywords[] ALIGN1 = + "bs\0""count\0""seek\0""skip\0""if\0""of\0" +#if ENABLE_FEATURE_DD_IBS_OBS + "ibs\0""obs\0""conv\0" +#endif + ; +#if ENABLE_FEATURE_DD_IBS_OBS + static const char conv_words[] ALIGN1 = + "notrunc\0""sync\0""noerror\0""fsync\0"; +#endif + enum { + OP_bs = 0, + OP_count, + OP_seek, + OP_skip, + OP_if, + OP_of, +#if ENABLE_FEATURE_DD_IBS_OBS + OP_ibs, + OP_obs, + OP_conv, + /* Must be in the same order as FLAG_XXX! */ + OP_conv_notrunc = 0, + OP_conv_sync, + OP_conv_noerror, + OP_conv_fsync, + /* Unimplemented conv=XXX: */ + //nocreat do not create the output file + //excl fail if the output file already exists + //fdatasync physically write output file data before finishing + //swab swap every pair of input bytes + //lcase change upper case to lower case + //ucase change lower case to upper case + //block pad newline-terminated records with spaces to cbs-size + //unblock replace trailing spaces in cbs-size records with newline + //ascii from EBCDIC to ASCII + //ebcdic from ASCII to EBCDIC + //ibm from ASCII to alternate EBCDIC +#endif + }; + int exitcode = EXIT_FAILURE; + size_t ibs = 512, obs = 512; + ssize_t n, w; + char *ibuf, *obuf; + /* And these are all zeroed at once! */ + struct { + int flags; + size_t oc; + off_t count; + off_t seek, skip; + const char *infile, *outfile; + } Z; +#define flags (Z.flags ) +#define oc (Z.oc ) +#define count (Z.count ) +#define seek (Z.seek ) +#define skip (Z.skip ) +#define infile (Z.infile ) +#define outfile (Z.outfile) + + memset(&Z, 0, sizeof(Z)); + INIT_G(); + //fflush(NULL); - is this needed because of NOEXEC? + +#if ENABLE_FEATURE_DD_SIGNAL_HANDLING + signal_SA_RESTART_empty_mask(SIGUSR1, dd_output_status); +#endif + + for (n = 1; argv[n]; n++) { + int what; + char *val; + char *arg = argv[n]; + +#if ENABLE_DESKTOP + /* "dd --". NB: coreutils 6.9 will complain if they see + * more than one of them. We wouldn't. */ + if (arg[0] == '-' && arg[1] == '-' && arg[2] == '\0') + continue; +#endif + val = strchr(arg, '='); + if (val == NULL) + bb_show_usage(); + *val = '\0'; + what = index_in_strings(keywords, arg); + if (what < 0) + bb_show_usage(); + /* *val = '='; - to preserve ps listing? */ + val++; +#if ENABLE_FEATURE_DD_IBS_OBS + if (what == OP_ibs) { + /* Must fit into positive ssize_t */ + ibs = xatoul_range_sfx(val, 1, ((size_t)-1L)/2, dd_suffixes); + /*continue;*/ + } + if (what == OP_obs) { + obs = xatoul_range_sfx(val, 1, ((size_t)-1L)/2, dd_suffixes); + /*continue;*/ + } + if (what == OP_conv) { + while (1) { + /* find ',', replace them with NUL so we can use val for + * index_in_strings() without copying. + * We rely on val being non-null, else strchr would fault. + */ + arg = strchr(val, ','); + if (arg) + *arg = '\0'; + what = index_in_strings(conv_words, val); + if (what < 0) + bb_error_msg_and_die(bb_msg_invalid_arg, val, "conv"); + flags |= (1 << what); + if (!arg) /* no ',' left, so this was the last specifier */ + break; + /* *arg = ','; - to preserve ps listing? */ + val = arg + 1; /* skip this keyword and ',' */ + } + continue; /* we trashed 'what', can't fall through */ + } +#endif + if (what == OP_bs) { + ibs = obs = xatoul_range_sfx(val, 1, ((size_t)-1L)/2, dd_suffixes); + /*continue;*/ + } + /* These can be large: */ + if (what == OP_count) { + flags |= FLAG_COUNT; + count = XATOU_SFX(val, dd_suffixes); + /*continue;*/ + } + if (what == OP_seek) { + seek = XATOU_SFX(val, dd_suffixes); + /*continue;*/ + } + if (what == OP_skip) { + skip = XATOU_SFX(val, dd_suffixes); + /*continue;*/ + } + if (what == OP_if) { + infile = val; + /*continue;*/ + } + if (what == OP_of) { + outfile = val; + /*continue;*/ + } + } /* end of "for (argv[n])" */ + +//XXX:FIXME for huge ibs or obs, malloc'ing them isn't the brightest idea ever + ibuf = obuf = xmalloc(ibs); + if (ibs != obs) { + flags |= FLAG_TWOBUFS; + obuf = xmalloc(obs); + } + if (infile != NULL) + xmove_fd(xopen(infile, O_RDONLY), ifd); + else { + infile = bb_msg_standard_input; + } + if (outfile != NULL) { + int oflag = O_WRONLY | O_CREAT; + + if (!seek && !(flags & FLAG_NOTRUNC)) + oflag |= O_TRUNC; + + xmove_fd(xopen(outfile, oflag), ofd); + + if (seek && !(flags & FLAG_NOTRUNC)) { + 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 { + 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) + goto die_infile; + if (n == 0) + break; + } + } + } + if (seek) { + if (lseek(ofd, seek * obs, SEEK_CUR) < 0) + goto die_outfile; + } + + while (!(flags & FLAG_COUNT) || (G.in_full + G.in_part != count)) { + if (flags & FLAG_NOERROR) /* Pre-zero the buffer if conv=noerror */ + memset(ibuf, 0, ibs); + n = safe_read(ifd, ibuf, ibs); + if (n == 0) + break; + if (n < 0) { + if (!(flags & FLAG_NOERROR)) + goto die_infile; + n = ibs; + bb_simple_perror_msg(infile); + } + if ((size_t)n == ibs) + G.in_full++; + else { + G.in_part++; + if (flags & FLAG_SYNC) { + memset(ibuf + n, '\0', ibs - n); + n = ibs; + } + } + if (flags & FLAG_TWOBUFS) { + char *tmp = ibuf; + while (n) { + size_t d = obs - oc; + + if (d > (size_t)n) + d = n; + memcpy(obuf + oc, tmp, d); + n -= d; + tmp += d; + oc += d; + if (oc == obs) { + if (write_and_stats(obuf, obs, obs, outfile)) + goto out_status; + oc = 0; + } + } + } else if (write_and_stats(ibuf, n, obs, outfile)) + goto out_status; + + if (flags & FLAG_FSYNC) { + if (fsync(ofd) < 0) + goto die_outfile; + } + } + + if (ENABLE_FEATURE_DD_IBS_OBS && oc) { + w = full_write_or_warn(obuf, oc, outfile); + if (w < 0) goto out_status; + if (w > 0) G.out_part++; + } + if (close(ifd) < 0) { + die_infile: + bb_simple_perror_msg_and_die(infile); + } + + if (close(ofd) < 0) { + die_outfile: + bb_simple_perror_msg_and_die(outfile); + } + + exitcode = EXIT_SUCCESS; + out_status: + dd_output_status(0); + + return exitcode; +} diff --git a/coreutils/df.c b/coreutils/df.c new file mode 100644 index 0000000..1c7d6cb --- /dev/null +++ b/coreutils/df.c @@ -0,0 +1,198 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini df implementation for busybox + * + * Copyright (C) 1999-2004 by Erik Andersen + * based on original code by (I think) Bruce Perens . + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +/* BB_AUDIT SUSv3 _NOT_ compliant -- option -t missing. */ +/* 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. + * + * Aug 28, 2008 Bernhard Reutner-Fischer + * + * Implement -P and -B; better coreutils compat; cleanup + */ + +#include +#include +#include "libbb.h" + +#if !ENABLE_FEATURE_HUMAN_READABLE +static unsigned long kscale(unsigned long b, unsigned long bs) +{ + return (b * (unsigned long long) bs + 1024/2) / 1024; +} +#endif + +int df_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int df_main(int argc, char **argv) +{ + unsigned long blocks_used; + unsigned blocks_percent_used; + unsigned long df_disp_hr = 1024; + int status = EXIT_SUCCESS; + unsigned opt; + FILE *mount_table; + struct mntent *mount_entry; + struct statfs s; + static const char ignored_mounts[] ALIGN1 = + "rootfs\0"; + + enum { + OPT_KILO = (1 << 0), + OPT_POSIX = (1 << 1), + OPT_ALL = (1 << 2) * ENABLE_FEATURE_DF_FANCY, + OPT_INODE = (1 << 3) * ENABLE_FEATURE_DF_FANCY, + OPT_BSIZE = (1 << 4) * ENABLE_FEATURE_DF_FANCY, + OPT_HUMAN = (1 << 5) * ENABLE_FEATURE_HUMAN_READABLE, + OPT_MEGA = (1 << 6) * ENABLE_FEATURE_HUMAN_READABLE, + }; + const char *disp_units_hdr = NULL; + char *chp; + +#if ENABLE_FEATURE_HUMAN_READABLE && ENABLE_FEATURE_DF_FANCY + opt_complementary = "k-mB:m-Bk:B-km"; +#elif ENABLE_FEATURE_HUMAN_READABLE + opt_complementary = "k-m:m-k"; +#endif + opt = getopt32(argv, "kP" + USE_FEATURE_DF_FANCY("aiB:") + USE_FEATURE_HUMAN_READABLE("hm") + USE_FEATURE_DF_FANCY(, &chp)); + if (opt & OPT_MEGA) + df_disp_hr = 1024*1024; + + if (opt & OPT_BSIZE) + df_disp_hr = xatoul_range(chp, 1, ULONG_MAX); /* disallow 0 */ + + /* From the manpage of df from coreutils-6.10: + Disk space is shown in 1K blocks by default, unless the environment + variable POSIXLY_CORRECT is set, in which case 512-byte blocks are used. + */ + if (getenv("POSIXLY_CORRECT")) /* TODO - a new libbb function? */ + df_disp_hr = 512; + + if (opt & OPT_HUMAN) { + df_disp_hr = 0; + disp_units_hdr = " Size"; + } + if (opt & OPT_INODE) + disp_units_hdr = " Inodes"; + + if (disp_units_hdr == NULL) { +#if ENABLE_FEATURE_HUMAN_READABLE + disp_units_hdr = xasprintf("%s-blocks", + make_human_readable_str(df_disp_hr, 0, !!(opt & OPT_POSIX))); +#else + disp_units_hdr = xasprintf("%lu-blocks", df_disp_hr); +#endif + } + printf("Filesystem %-15sUsed Available %s Mounted on\n", + disp_units_hdr, (opt & OPT_POSIX) ? "Capacity" : "Use%"); + + 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); + } + + while (1) { + 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_simple_perror_msg(mount_point); + goto SET_ERROR; + } + + if ((s.f_blocks > 0) || !mount_table || (opt & OPT_ALL)) { + if (opt & OPT_INODE) { + s.f_blocks = s.f_files; + s.f_bavail = s.f_bfree = s.f_ffree; + s.f_bsize = 1; + + if (df_disp_hr) + df_disp_hr = 1; + } + blocks_used = s.f_blocks - s.f_bfree; + blocks_percent_used = 0; + if (blocks_used + s.f_bavail) { + blocks_percent_used = (blocks_used * 100ULL + + (blocks_used + s.f_bavail)/2 + ) / (blocks_used + s.f_bavail); + } + + /* GNU coreutils 6.10 skip certain mounts, try to be compatible. */ + if (index_in_strings(device, ignored_mounts) != -1) + continue; + +#ifdef WHY_WE_DO_IT_FOR_DEV_ROOT_ONLY +/* ... and also this is the only user of find_block_device */ + 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; + } + } +#endif + + if (printf("\n%-20s" + 1, device) > 20) + printf("\n%-20s", ""); +#if ENABLE_FEATURE_HUMAN_READABLE + printf(" %9s ", + make_human_readable_str(s.f_blocks, s.f_bsize, df_disp_hr)); + + printf(" %9s " + 1, + make_human_readable_str((s.f_blocks - s.f_bfree), + s.f_bsize, df_disp_hr)); + + printf("%9s %3u%% %s\n", + make_human_readable_str(s.f_bavail, s.f_bsize, df_disp_hr), + blocks_percent_used, mount_point); +#else + printf(" %9lu %9lu %9lu %3u%% %s\n", + 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 + } + } + + return status; +} diff --git a/coreutils/dirname.c b/coreutils/dirname.c new file mode 100644 index 0000000..c0c0925 --- /dev/null +++ b/coreutils/dirname.c @@ -0,0 +1,27 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini dirname implementation for busybox + * + * Copyright (C) 1999-2004 by Erik Andersen + * + * 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 "libbb.h" + +/* This is a NOFORK applet. Be very careful! */ + +int dirname_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int dirname_main(int argc, char **argv) +{ + if (argc != 2) { + bb_show_usage(); + } + + puts(dirname(argv[1])); + + return fflush(stdout); +} diff --git a/coreutils/dos2unix.c b/coreutils/dos2unix.c new file mode 100644 index 0000000..309cbc3 --- /dev/null +++ b/coreutils/dos2unix.c @@ -0,0 +1,98 @@ +/* vi: set sw=4 ts=4: */ +/* + * dos2unix for BusyBox + * + * dos2unix '\n' convertor 0.5.0 + * based on Unix2Dos 0.9.0 by Peter Hanecak (made 19.2.1997) + * Copyright 1997,.. by Peter Hanecak . + * All rights reserved. + * + * dos2unix filters reading input from stdin and writing output to stdout. + * + * Licensed under the GPL v2 or later, see the file LICENSE in this tarball. +*/ + +#include "libbb.h" + +enum { + CT_UNIX2DOS = 1, + CT_DOS2UNIX +}; + +/* if fn is NULL then input is stdin and output is stdout */ +static void convert(char *fn, int conv_type) +{ + FILE *in, *out; + int i; + char *temp_fn = temp_fn; /* for compiler */ + char *resolved_fn = resolved_fn; + + in = stdin; + out = stdout; + if (fn != NULL) { + struct stat st; + + resolved_fn = xmalloc_follow_symlinks(fn); + if (resolved_fn == NULL) + bb_simple_perror_msg_and_die(fn); + in = xfopen_for_read(resolved_fn); + fstat(fileno(in), &st); + + temp_fn = xasprintf("%sXXXXXX", resolved_fn); + i = mkstemp(temp_fn); + if (i == -1 + || fchmod(i, st.st_mode) == -1 + || !(out = fdopen(i, "w+")) + ) { + bb_simple_perror_msg_and_die(temp_fn); + } + } + + while ((i = fgetc(in)) != EOF) { + if (i == '\r') + continue; + if (i == '\n') + if (conv_type == CT_UNIX2DOS) + fputc('\r', out); + fputc(i, out); + } + + if (fn != NULL) { + if (fclose(in) < 0 || fclose(out) < 0) { + unlink(temp_fn); + bb_perror_nomsg_and_die(); + } + xrename(temp_fn, resolved_fn); + free(temp_fn); + free(resolved_fn); + } +} + +int dos2unix_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int dos2unix_main(int argc, char **argv) +{ + int o, conv_type; + + /* See if we are supposed to be doing dos2unix or unix2dos */ + conv_type = CT_UNIX2DOS; + if (applet_name[0] == 'd') { + conv_type = CT_DOS2UNIX; + } + + /* -u convert to unix, -d convert to dos */ + opt_complementary = "u--d:d--u"; /* mutually exclusive */ + o = getopt32(argv, "du"); + + /* Do the conversion requested by an argument else do the default + * conversion depending on our name. */ + if (o) + conv_type = o; + + do { + /* might be convert(NULL) if there is no filename given */ + convert(argv[optind], conv_type); + optind++; + } while (optind < argc); + + return 0; +} diff --git a/coreutils/du.c b/coreutils/du.c new file mode 100644 index 0000000..efc9bb9 --- /dev/null +++ b/coreutils/du.c @@ -0,0 +1,240 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini du implementation for busybox + * + * Copyright (C) 1999,2000,2001 by Lineo, inc. and John Beppu + * Copyright (C) 1999,2000,2001 by John Beppu + * Copyright (C) 2002 Edward Betts + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +/* BB_AUDIT SUSv3 compliant (unless default blocksize set to 1k) */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/du.html */ + +/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org) + * + * Mostly rewritten for SUSv3 compliance and to fix bugs/defects. + * 1) Added support for SUSv3 -a, -H, -L, gnu -c, and (busybox) -d options. + * The -d option allows setting of max depth (similar to gnu --max-depth). + * 2) Fixed incorrect size calculations for links and directories, especially + * when errors occurred. Calculates sizes should now match gnu du output. + * 3) Added error checking of output. + * 4) Fixed busybox bug #1284 involving long overflow with human_readable. + */ + +#include "libbb.h" + +struct globals { +#if ENABLE_FEATURE_HUMAN_READABLE + unsigned long disp_hr; +#else + unsigned disp_k; +#endif + + int max_print_depth; + nlink_t count_hardlinks; + + bool status; + bool one_file_system; + int print_files; + int slink_depth; + int du_depth; + dev_t dir_dev; +}; +#define G (*(struct globals*)&bb_common_bufsiz1) + + +static void print(unsigned long size, const char *filename) +{ + /* TODO - May not want to defer error checking here. */ +#if ENABLE_FEATURE_HUMAN_READABLE + printf("%s\t%s\n", make_human_readable_str(size, 512, G.disp_hr), + filename); +#else + if (G.disp_k) { + size++; + size >>= 1; + } + printf("%ld\t%s\n", size, filename); +#endif +} + +/* tiny recursive du */ +static unsigned long du(const char *filename) +{ + struct stat statbuf; + unsigned long sum; + + if (lstat(filename, &statbuf) != 0) { + bb_simple_perror_msg(filename); + G.status = EXIT_FAILURE; + return 0; + } + + if (G.one_file_system) { + if (G.du_depth == 0) { + G.dir_dev = statbuf.st_dev; + } else if (G.dir_dev != statbuf.st_dev) { + return 0; + } + } + + sum = statbuf.st_blocks; + + if (S_ISLNK(statbuf.st_mode)) { + if (G.slink_depth > G.du_depth) { /* -H or -L */ + if (stat(filename, &statbuf) != 0) { + bb_simple_perror_msg(filename); + G.status = EXIT_FAILURE; + return 0; + } + sum = statbuf.st_blocks; + if (G.slink_depth == 1) { + G.slink_depth = INT_MAX; /* Convert -H to -L. */ + } + } + } + + if (statbuf.st_nlink > G.count_hardlinks) { + /* Add files/directories with links only once */ + if (is_in_ino_dev_hashtable(&statbuf)) { + return 0; + } + add_to_ino_dev_hashtable(&statbuf, NULL); + } + + if (S_ISDIR(statbuf.st_mode)) { + DIR *dir; + struct dirent *entry; + char *newfile; + + dir = warn_opendir(filename); + if (!dir) { + G.status = EXIT_FAILURE; + return sum; + } + + newfile = last_char_is(filename, '/'); + if (newfile) + *newfile = '\0'; + + while ((entry = readdir(dir))) { + char *name = entry->d_name; + + newfile = concat_subpath_file(filename, name); + if (newfile == NULL) + continue; + ++G.du_depth; + sum += du(newfile); + --G.du_depth; + free(newfile); + } + closedir(dir); + } else if (G.du_depth > G.print_files) { + return sum; + } + if (G.du_depth <= G.max_print_depth) { + print(sum, filename); + } + return sum; +} + +int du_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int du_main(int argc UNUSED_PARAM, char **argv) +{ + unsigned long total; + int slink_depth_save; + bool print_final_total; + unsigned opt; + +#if ENABLE_FEATURE_HUMAN_READABLE + USE_FEATURE_DU_DEFAULT_BLOCKSIZE_1K(G.disp_hr = 1024;) + SKIP_FEATURE_DU_DEFAULT_BLOCKSIZE_1K(G.disp_hr = 512;) + if (getenv("POSIXLY_CORRECT")) /* TODO - a new libbb function? */ + G.disp_hr = 512; +#else + USE_FEATURE_DU_DEFAULT_BLOCKSIZE_1K(G.disp_k = 1;) + /* SKIP_FEATURE_DU_DEFAULT_BLOCKSIZE_1K(G.disp_k = 0;) - G is pre-zeroed */ +#endif + G.max_print_depth = INT_MAX; + G.count_hardlinks = 1; + + /* Note: SUSv3 specifies that -a and -s options cannot be used together + * in strictly conforming applications. However, it also says that some + * du implementations may produce output when -a and -s are used together. + * gnu du exits with an error code in this case. We choose to simply + * ignore -a. This is consistent with -s being equivalent to -d 0. + */ +#if ENABLE_FEATURE_HUMAN_READABLE + opt_complementary = "h-km:k-hm:m-hk:H-L:L-H:s-d:d-s:d+"; + opt = getopt32(argv, "aHkLsx" "d:" "lc" "hm", &G.max_print_depth); + argv += optind; + if (opt & (1 << 9)) { + /* -h opt */ + G.disp_hr = 0; + } + if (opt & (1 << 10)) { + /* -m opt */ + G.disp_hr = 1024*1024; + } + if (opt & (1 << 2)) { + /* -k opt */ + G.disp_hr = 1024; + } +#else + opt_complementary = "H-L:L-H:s-d:d-s:d+"; + opt = getopt32(argv, "aHkLsx" "d:" "lc", &G.max_print_depth); + argv += optind; +#if !ENABLE_FEATURE_DU_DEFAULT_BLOCKSIZE_1K + if (opt & (1 << 2)) { + /* -k opt */ + G.disp_k = 1; + } +#endif +#endif + if (opt & (1 << 0)) { + /* -a opt */ + G.print_files = INT_MAX; + } + if (opt & (1 << 1)) { + /* -H opt */ + G.slink_depth = 1; + } + if (opt & (1 << 3)) { + /* -L opt */ + G.slink_depth = INT_MAX; + } + if (opt & (1 << 4)) { + /* -s opt */ + G.max_print_depth = 0; + } + G.one_file_system = opt & (1 << 5); /* -x opt */ + if (opt & (1 << 7)) { + /* -l opt */ + G.count_hardlinks = MAXINT(nlink_t); + } + print_final_total = opt & (1 << 8); /* -c opt */ + + /* go through remaining args (if any) */ + if (!*argv) { + *--argv = (char*)"."; + if (G.slink_depth == 1) { + G.slink_depth = 0; + } + } + + slink_depth_save = G.slink_depth; + total = 0; + do { + total += du(*argv); + G.slink_depth = slink_depth_save; + } while (*++argv); + + if (ENABLE_FEATURE_CLEAN_UP) + reset_ino_dev_hashtable(); + if (print_final_total) + print(total, "total"); + + fflush_stdout_and_exit(G.status); +} diff --git a/coreutils/echo.c b/coreutils/echo.c new file mode 100644 index 0000000..decca09 --- /dev/null +++ b/coreutils/echo.c @@ -0,0 +1,303 @@ +/* vi: set sw=4 ts=4: */ +/* + * echo implementation for busybox + * + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + * + * Original copyright notice is retained at the end of this file. + */ + +/* BB_AUDIT SUSv3 compliant -- unless configured as fancy echo. */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/echo.html */ + +/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org) + * + * Because of behavioral differences, implemented configurable SUSv3 + * or 'fancy' gnu-ish behaviors. Also, reduced size and fixed bugs. + * 1) In handling '\c' escape, the previous version only suppressed the + * trailing newline. SUSv3 specifies _no_ output after '\c'. + * 2) SUSv3 specifies that octal escapes are of the form \0{#{#{#}}}. + * The previous version did not allow 4-digit octals. + */ + +#include "libbb.h" + +/* This is a NOFORK applet. Be very careful! */ + +/* NB: can be used by shell even if not enabled as applet */ + +int echo_main(int argc UNUSED_PARAM, char **argv) +{ + const char *arg; +#if !ENABLE_FEATURE_FANCY_ECHO + enum { + eflag = '\\', + nflag = 1, /* 1 -- print '\n' */ + }; + + /* We must check that stdout is not closed. + * The reason for this is highly non-obvious. + * echo_main is used from shell. Shell must correctly handle "echo foo" + * if stdout is closed. With stdio, output gets shoveled into + * stdout buffer, and even fflush cannot clear it out. It seems that + * even if libc receives EBADF on write attempts, it feels determined + * to output data no matter what. So it will try later, + * and possibly will clobber future output. Not good. */ +// TODO: check fcntl() & O_ACCMODE == O_WRONLY or O_RDWR? + if (fcntl(1, F_GETFL) == -1) + return 1; /* match coreutils 6.10 (sans error msg to stderr) */ + //if (dup2(1, 1) != 1) - old way + // return 1; + + arg = *++argv; + if (!arg) + goto newline_ret; +#else + const char *p; + char nflag = 1; + char eflag = 0; + + /* We must check that stdout is not closed. */ + if (fcntl(1, F_GETFL) == -1) + return 1; + + while (1) { + arg = *++argv; + if (!arg) + goto newline_ret; + if (*arg != '-') + break; + + /* If it appears that we are handling options, then make sure + * that all of the options specified are actually valid. + * Otherwise, the string should just be echoed. + */ + p = arg + 1; + if (!*p) /* A single '-', so echo it. */ + goto just_echo; + + do { + if (!strrchr("neE", *p)) + goto just_echo; + } while (*++p); + + /* All of the options in this arg are valid, so handle them. */ + p = arg + 1; + do { + if (*p == 'n') + nflag = 0; + if (*p == 'e') + eflag = '\\'; + } while (*++p); + } + just_echo: +#endif + while (1) { + /* arg is already == *argv and isn't NULL */ + int c; + + if (!eflag) { + /* optimization for very common case */ + fputs(arg, stdout); + } else while ((c = *arg++)) { + if (c == eflag) { /* Check for escape seq. */ + if (*arg == 'c') { + /* '\c' means cancel newline and + * ignore all subsequent chars. */ + goto ret; + } +#if !ENABLE_FEATURE_FANCY_ECHO + /* SUSv3 specifies that octal escapes must begin with '0'. */ + if ( ((int)(unsigned char)(*arg) - '0') >= 8) /* '8' or bigger */ +#endif + { + /* Since SUSv3 mandates a first digit of 0, 4-digit octals + * of the form \0### are accepted. */ + if (*arg == '0') { + /* NB: don't turn "...\0" into "...\" */ + if (arg[1] && ((unsigned char)(arg[1]) - '0') < 8) { + arg++; + } + } + /* bb_process_escape_sequence handles NUL correctly + * ("...\" case). */ + c = bb_process_escape_sequence(&arg); + } + } + bb_putchar(c); + } + + arg = *++argv; + if (!arg) + break; + bb_putchar(' '); + } + + newline_ret: + if (nflag) { + bb_putchar('\n'); + } + ret: + return fflush(stdout); +} + +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * 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. + * + * California, Berkeley and its contributors. + * 4. 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. + * + * @(#)echo.c 8.1 (Berkeley) 5/31/93 + */ + +#ifdef VERSION_WITH_WRITEV +/* We can't use stdio. + * The reason for this is highly non-obvious. + * echo_main is used from shell. Shell must correctly handle "echo foo" + * if stdout is closed. With stdio, output gets shoveled into + * stdout buffer, and even fflush cannot clear it out. It seems that + * even if libc receives EBADF on write attempts, it feels determined + * to output data no matter what. So it will try later, + * and possibly will clobber future output. Not good. + * + * Using writev instead, with 'direct' conversion of argv vector. + */ + +int echo_main(int argc, char **argv) +{ + struct iovec io[argc]; + struct iovec *cur_io = io; + char *arg; + char *p; +#if !ENABLE_FEATURE_FANCY_ECHO + enum { + eflag = '\\', + nflag = 1, /* 1 -- print '\n' */ + }; + arg = *++argv; + if (!arg) + goto newline_ret; +#else + char nflag = 1; + char eflag = 0; + + while (1) { + arg = *++argv; + if (!arg) + goto newline_ret; + if (*arg != '-') + break; + + /* If it appears that we are handling options, then make sure + * that all of the options specified are actually valid. + * Otherwise, the string should just be echoed. + */ + p = arg + 1; + if (!*p) /* A single '-', so echo it. */ + goto just_echo; + + do { + if (!strrchr("neE", *p)) + goto just_echo; + } while (*++p); + + /* All of the options in this arg are valid, so handle them. */ + p = arg + 1; + do { + if (*p == 'n') + nflag = 0; + if (*p == 'e') + eflag = '\\'; + } while (*++p); + } + just_echo: +#endif + + while (1) { + /* arg is already == *argv and isn't NULL */ + int c; + + cur_io->iov_base = p = arg; + + if (!eflag) { + /* optimization for very common case */ + p += strlen(arg); + } else while ((c = *arg++)) { + if (c == eflag) { /* Check for escape seq. */ + if (*arg == 'c') { + /* '\c' means cancel newline and + * ignore all subsequent chars. */ + cur_io->iov_len = p - (char*)cur_io->iov_base; + cur_io++; + goto ret; + } +#if !ENABLE_FEATURE_FANCY_ECHO + /* SUSv3 specifies that octal escapes must begin with '0'. */ + if ( (((unsigned char)*arg) - '1') >= 7) +#endif + { + /* Since SUSv3 mandates a first digit of 0, 4-digit octals + * of the form \0### are accepted. */ + if (*arg == '0' && ((unsigned char)(arg[1]) - '0') < 8) { + arg++; + } + /* bb_process_escape_sequence can handle nul correctly */ + c = bb_process_escape_sequence( (void*) &arg); + } + } + *p++ = c; + } + + arg = *++argv; + if (arg) + *p++ = ' '; + cur_io->iov_len = p - (char*)cur_io->iov_base; + cur_io++; + if (!arg) + break; + } + + newline_ret: + if (nflag) { + cur_io->iov_base = (char*)"\n"; + cur_io->iov_len = 1; + cur_io++; + } + ret: + /* TODO: implement and use full_writev? */ + return writev(1, io, (cur_io - io)) >= 0; +} +#endif diff --git a/coreutils/env.c b/coreutils/env.c new file mode 100644 index 0000000..2f8c8b7 --- /dev/null +++ b/coreutils/env.c @@ -0,0 +1,123 @@ +/* vi: set sw=4 ts=4: */ +/* + * env implementation for busybox + * + * Copyright (c) 1988, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + * + * Original copyright notice is retained at the end of this file. + * + * Modified for BusyBox by Erik Andersen + */ + +/* BB_AUDIT SUSv3 compliant */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/env.html */ + +/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org) + * + * Fixed bug involving exit return codes if execvp fails. Also added + * output error checking. + */ + +/* + * Modified by Vladimir Oleynik (C) 2003 + * - correct "-" option usage + * - multiple "-u unsetenv" support + * - GNU long option support + * - use xfunc_error_retval + */ + +/* This is a NOEXEC applet. Be very careful! */ + +#include "libbb.h" + +#if ENABLE_FEATURE_ENV_LONG_OPTIONS +static const char env_longopts[] ALIGN1 = + "ignore-environment\0" No_argument "i" + "unset\0" Required_argument "u" + ; +#endif + +int env_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int env_main(int argc UNUSED_PARAM, char **argv) +{ + /* cleanenv was static - why? */ + char *cleanenv[1]; + char **ep; + unsigned opt; + llist_t *unset_env = NULL; + + opt_complementary = "u::"; +#if ENABLE_FEATURE_ENV_LONG_OPTIONS + applet_long_options = env_longopts; +#endif + opt = getopt32(argv, "+iu:", &unset_env); + argv += optind; + if (*argv && LONE_DASH(argv[0])) { + opt |= 1; + ++argv; + } + if (opt & 1) { + cleanenv[0] = NULL; + environ = cleanenv; + } else { + while (unset_env) { + unsetenv(llist_pop(&unset_env)); + } + } + + while (*argv && (strchr(*argv, '=') != NULL)) { + if (putenv(*argv) < 0) { + bb_perror_msg_and_die("putenv"); + } + ++argv; + } + + if (*argv) { + BB_EXECVP(*argv, argv); + /* SUSv3-mandated exit codes. */ + xfunc_error_retval = (errno == ENOENT) ? 127 : 126; + bb_simple_perror_msg_and_die(*argv); + } + + for (ep = environ; *ep; ep++) { + puts(*ep); + } + + fflush_stdout_and_exit(EXIT_SUCCESS); +} + +/* + * Copyright (c) 1988, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * 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. + * + * 4. 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. + */ diff --git a/coreutils/expand.c b/coreutils/expand.c new file mode 100644 index 0000000..ee51c03 --- /dev/null +++ b/coreutils/expand.c @@ -0,0 +1,200 @@ +/* expand - convert tabs to spaces + * unexpand - convert spaces to tabs + * + * Copyright (C) 89, 91, 1995-2006 Free Software Foundation, Inc. + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + * + * David MacKenzie + * + * Options for expand: + * -t num --tabs=NUM Convert tabs to num spaces (default 8 spaces). + * -i --initial Only convert initial tabs on each line to spaces. + * + * Options for unexpand: + * -a --all Convert all blanks, instead of just initial blanks. + * -f --first-only Convert only leading sequences of blanks (default). + * -t num --tabs=NUM Have tabs num characters apart instead of 8. + * + * Busybox version (C) 2007 by Tito Ragusa + * + * Caveat: this versions of expand and unexpand don't accept tab lists. + */ + +#include "libbb.h" + +enum { + OPT_INITIAL = 1 << 0, + OPT_TABS = 1 << 1, + OPT_ALL = 1 << 2, +}; + +static void xputchar(char c) +{ + if (putchar(c) < 0) + bb_error_msg_and_die(bb_msg_write_error); +} + +#if ENABLE_EXPAND +static void expand(FILE *file, unsigned tab_size, unsigned opt) +{ + char *line; + char *ptr; + int convert; + unsigned pos; + + /* Increment tab_size by 1 locally.*/ + tab_size++; + + while ((line = xmalloc_fgets(file)) != NULL) { + convert = 1; + pos = 0; + ptr = line; + while (*line) { + pos++; + if (*line == '\t' && convert) { + for (; pos < tab_size; pos++) { + xputchar(' '); + } + } else { + if ((opt & OPT_INITIAL) && !isblank(*line)) { + convert = 0; + } + xputchar(*line); + } + if (pos == tab_size) { + pos = 0; + } + line++; + } + free(ptr); + } +} +#endif + +#if ENABLE_UNEXPAND +static void unexpand(FILE *file, unsigned int tab_size, unsigned opt) +{ + char *line; + char *ptr; + int convert; + int pos; + int i = 0; + unsigned column = 0; + + while ((line = xmalloc_fgets(file)) != NULL) { + convert = 1; + pos = 0; + ptr = line; + while (*line) { + while ((*line == ' ' || *line == '\t') && convert) { + pos += (*line == ' ') ? 1 : tab_size; + line++; + column++; + if ((opt & OPT_ALL) && column == tab_size) { + column = 0; + goto put_tab; + } + } + if (pos) { + i = pos / tab_size; + if (i) { + for (; i > 0; i--) { + put_tab: + xputchar('\t'); + } + } else { + for (i = pos % tab_size; i > 0; i--) { + xputchar(' '); + } + } + pos = 0; + } else { + if (opt & OPT_INITIAL) { + convert = 0; + } + if (opt & OPT_ALL) { + column++; + } + xputchar(*line); + line++; + } + } + free(ptr); + } +} +#endif + +int expand_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int expand_main(int argc UNUSED_PARAM, char **argv) +{ + /* Default 8 spaces for 1 tab */ + const char *opt_t = "8"; + FILE *file; + unsigned tab_size; + unsigned opt; + int exit_status = EXIT_SUCCESS; + +#if ENABLE_FEATURE_EXPAND_LONG_OPTIONS + static const char expand_longopts[] ALIGN1 = + /* name, has_arg, val */ + "initial\0" No_argument "i" + "tabs\0" Required_argument "t" + ; +#endif +#if ENABLE_FEATURE_UNEXPAND_LONG_OPTIONS + static const char unexpand_longopts[] ALIGN1 = + /* name, has_arg, val */ + "first-only\0" No_argument "i" + "tabs\0" Required_argument "t" + "all\0" No_argument "a" + ; +#endif + + if (ENABLE_EXPAND && (!ENABLE_UNEXPAND || applet_name[0] == 'e')) { + USE_FEATURE_EXPAND_LONG_OPTIONS(applet_long_options = expand_longopts); + opt = getopt32(argv, "it:", &opt_t); + } else { + USE_FEATURE_UNEXPAND_LONG_OPTIONS(applet_long_options = unexpand_longopts); + /* -t NUM sets also -a */ + opt_complementary = "ta"; + opt = getopt32(argv, "ft:a", &opt_t); + /* -f --first-only is the default */ + if (!(opt & OPT_ALL)) opt |= OPT_INITIAL; + } + tab_size = xatou_range(opt_t, 1, UINT_MAX); + + argv += optind; + + if (!*argv) { + *--argv = (char*)bb_msg_standard_input; + } + do { + file = fopen_or_warn_stdin(*argv); + if (!file) { + exit_status = EXIT_FAILURE; + continue; + } + + if (ENABLE_EXPAND && (!ENABLE_UNEXPAND || applet_name[0] == 'e')) + USE_EXPAND(expand(file, tab_size, opt)); + else + USE_UNEXPAND(unexpand(file, tab_size, opt)); + + /* Check and close the file */ + if (fclose_if_not_stdin(file)) { + bb_simple_perror_msg(*argv); + exit_status = EXIT_FAILURE; + } + /* If stdin also clear EOF */ + if (file == stdin) + clearerr(file); + } while (*++argv); + + /* Now close stdin also */ + /* (if we didn't read from it, it's a no-op) */ + if (fclose(stdin)) + bb_perror_msg_and_die(bb_msg_standard_input); + + fflush_stdout_and_exit(exit_status); +} diff --git a/coreutils/expr.c b/coreutils/expr.c new file mode 100644 index 0000000..2f9c5c1 --- /dev/null +++ b/coreutils/expr.c @@ -0,0 +1,504 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini expr implementation for busybox + * + * based on GNU expr Mike Parker. + * Copyright (C) 86, 1991-1997, 1999 Free Software Foundation, Inc. + * + * Busybox modifications + * Copyright (c) 2000 Edward Betts . + * Copyright (C) 2003-2005 Vladimir Oleynik + * - reduced 464 bytes. + * - 64 math support + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +/* This program evaluates expressions. Each token (operator, operand, + * parenthesis) of the expression must be a separate argument. The + * parser used is a reasonably general one, though any incarnation of + * it is language-specific. It is especially nice for expressions. + * + * No parse tree is needed; a new node is evaluated immediately. + * One function can handle multiple operators all of equal precedence, + * provided they all associate ((x op x) op x). */ + +/* no getopt needed */ + +#include "libbb.h" +#include "xregex.h" + +#if ENABLE_EXPR_MATH_SUPPORT_64 +typedef int64_t arith_t; + +#define PF_REZ "ll" +#define PF_REZ_TYPE (long long) +#define STRTOL(s, e, b) strtoll(s, e, b) +#else +typedef long arith_t; + +#define PF_REZ "l" +#define PF_REZ_TYPE (long) +#define STRTOL(s, e, b) strtol(s, e, b) +#endif + +/* TODO: use bb_strtol[l]? It's easier to check for errors... */ + +/* The kinds of value we can have. */ +enum { + INTEGER, + STRING +}; + +/* A value is.... */ +struct valinfo { + smallint type; /* Which kind. */ + union { /* The value itself. */ + arith_t i; + char *s; + } u; +}; +typedef struct valinfo VALUE; + +/* The arguments given to the program, minus the program name. */ +struct globals { + char **args; +}; +#define G (*(struct globals*)&bb_common_bufsiz1) + +/* forward declarations */ +static VALUE *eval(void); + + +/* Return a VALUE for I. */ + +static VALUE *int_value(arith_t i) +{ + VALUE *v; + + v = xzalloc(sizeof(VALUE)); + if (INTEGER) /* otherwise xzaaloc did it already */ + v->type = INTEGER; + v->u.i = i; + return v; +} + +/* Return a VALUE for S. */ + +static VALUE *str_value(const char *s) +{ + VALUE *v; + + v = xzalloc(sizeof(VALUE)); + if (STRING) /* otherwise xzaaloc did it already */ + v->type = STRING; + v->u.s = xstrdup(s); + return v; +} + +/* Free VALUE V, including structure components. */ + +static void freev(VALUE *v) +{ + if (v->type == STRING) + free(v->u.s); + free(v); +} + +/* Return nonzero if V is a null-string or zero-number. */ + +static int null(VALUE *v) +{ + if (v->type == INTEGER) + return v->u.i == 0; + /* STRING: */ + return v->u.s[0] == '\0' || LONE_CHAR(v->u.s, '0'); +} + +/* Coerce V to a STRING value (can't fail). */ + +static void tostring(VALUE *v) +{ + if (v->type == INTEGER) { + v->u.s = xasprintf("%" PF_REZ "d", PF_REZ_TYPE v->u.i); + v->type = STRING; + } +} + +/* Coerce V to an INTEGER value. Return 1 on success, 0 on failure. */ + +static bool toarith(VALUE *v) +{ + if (v->type == STRING) { + arith_t i; + char *e; + + /* Don't interpret the empty string as an integer. */ + /* Currently does not worry about overflow or int/long differences. */ + i = STRTOL(v->u.s, &e, 10); + if ((v->u.s == e) || *e) + return 0; + free(v->u.s); + v->u.i = i; + v->type = INTEGER; + } + return 1; +} + +/* Return str[0]+str[1] if the next token matches STR exactly. + STR must not be NULL. */ + +static int nextarg(const char *str) +{ + if (*G.args == NULL || strcmp(*G.args, str) != 0) + return 0; + return (unsigned char)str[0] + (unsigned char)str[1]; +} + +/* The comparison operator handling functions. */ + +static int cmp_common(VALUE *l, VALUE *r, int op) +{ + arith_t ll, rr; + + ll = l->u.i; + rr = r->u.i; + if (l->type == STRING || r->type == STRING) { + tostring(l); + tostring(r); + ll = strcmp(l->u.s, r->u.s); + rr = 0; + } + /* calculating ll - rr and checking the result is prone to overflows. + * We'll do it differently: */ + if (op == '<') + return ll < rr; + if (op == ('<' + '=')) + return ll <= rr; + if (op == '=' || (op == '=' + '=')) + return ll == rr; + if (op == '!' + '=') + return ll != rr; + if (op == '>') + return ll > rr; + /* >= */ + return ll >= rr; +} + +/* The arithmetic operator handling functions. */ + +static arith_t arithmetic_common(VALUE *l, VALUE *r, int op) +{ + arith_t li, ri; + + if (!toarith(l) || !toarith(r)) + bb_error_msg_and_die("non-numeric argument"); + li = l->u.i; + ri = r->u.i; + if (op == '+') + return li + ri; + if (op == '-') + return li - ri; + if (op == '*') + return li * ri; + if (ri == 0) + bb_error_msg_and_die("division by zero"); + if (op == '/') + return li / ri; + return li % ri; +} + +/* Do the : operator. + SV is the VALUE for the lhs (the string), + PV is the VALUE for the rhs (the pattern). */ + +static VALUE *docolon(VALUE *sv, VALUE *pv) +{ + VALUE *v; + regex_t re_buffer; + const int NMATCH = 2; + regmatch_t re_regs[NMATCH]; + + tostring(sv); + tostring(pv); + + if (pv->u.s[0] == '^') { + bb_error_msg("\ +warning: unportable BRE: `%s': using `^' as the first character\n\ +of a basic regular expression is not portable; it is being ignored", pv->u.s); + } + + memset(&re_buffer, 0, sizeof(re_buffer)); + memset(re_regs, 0, sizeof(*re_regs)); + xregcomp(&re_buffer, pv->u.s, 0); + + /* expr uses an anchored pattern match, so check that there was a + * match and that the match starts at offset 0. */ + if (regexec(&re_buffer, sv->u.s, NMATCH, re_regs, 0) != REG_NOMATCH + && re_regs[0].rm_so == 0 + ) { + /* Were \(...\) used? */ + if (re_buffer.re_nsub > 0) { + sv->u.s[re_regs[1].rm_eo] = '\0'; + v = str_value(sv->u.s + re_regs[1].rm_so); + } else { + v = int_value(re_regs[0].rm_eo); + } + } else { + /* Match failed -- return the right kind of null. */ + if (re_buffer.re_nsub > 0) + v = str_value(""); + else + v = int_value(0); + } +//FIXME: sounds like here is a bit missing: regfree(&re_buffer); + return v; +} + +/* Handle bare operands and ( expr ) syntax. */ + +static VALUE *eval7(void) +{ + VALUE *v; + + if (!*G.args) + bb_error_msg_and_die("syntax error"); + + if (nextarg("(")) { + G.args++; + v = eval(); + if (!nextarg(")")) + bb_error_msg_and_die("syntax error"); + G.args++; + return v; + } + + if (nextarg(")")) + bb_error_msg_and_die("syntax error"); + + return str_value(*G.args++); +} + +/* Handle match, substr, index, length, and quote keywords. */ + +static VALUE *eval6(void) +{ + static const char keywords[] ALIGN1 = + "quote\0""length\0""match\0""index\0""substr\0"; + + VALUE *r, *i1, *i2; + VALUE *l = l; /* silence gcc */ + VALUE *v = v; /* silence gcc */ + int key = *G.args ? index_in_strings(keywords, *G.args) + 1 : 0; + + if (key == 0) /* not a keyword */ + return eval7(); + G.args++; /* We have a valid token, so get the next argument. */ + if (key == 1) { /* quote */ + if (!*G.args) + bb_error_msg_and_die("syntax error"); + return str_value(*G.args++); + } + if (key == 2) { /* length */ + r = eval6(); + tostring(r); + v = int_value(strlen(r->u.s)); + freev(r); + } else + l = eval6(); + + if (key == 3) { /* match */ + r = eval6(); + v = docolon(l, r); + freev(l); + freev(r); + } + if (key == 4) { /* index */ + r = eval6(); + tostring(l); + tostring(r); + v = int_value(strcspn(l->u.s, r->u.s) + 1); + if (v->u.i == (arith_t) strlen(l->u.s) + 1) + v->u.i = 0; + freev(l); + freev(r); + } + if (key == 5) { /* substr */ + i1 = eval6(); + i2 = eval6(); + tostring(l); + if (!toarith(i1) || !toarith(i2) + || i1->u.i > (arith_t) strlen(l->u.s) + || i1->u.i <= 0 || i2->u.i <= 0) + v = str_value(""); + else { + v = xmalloc(sizeof(VALUE)); + v->type = STRING; + v->u.s = xstrndup(l->u.s + i1->u.i - 1, i2->u.i); + } + freev(l); + freev(i1); + freev(i2); + } + return v; + +} + +/* Handle : operator (pattern matching). + Calls docolon to do the real work. */ + +static VALUE *eval5(void) +{ + VALUE *l, *r, *v; + + l = eval6(); + while (nextarg(":")) { + G.args++; + r = eval6(); + v = docolon(l, r); + freev(l); + freev(r); + l = v; + } + return l; +} + +/* Handle *, /, % operators. */ + +static VALUE *eval4(void) +{ + VALUE *l, *r; + int op; + arith_t val; + + l = eval5(); + while (1) { + op = nextarg("*"); + if (!op) { op = nextarg("/"); + if (!op) { op = nextarg("%"); + if (!op) return l; + }} + G.args++; + r = eval5(); + val = arithmetic_common(l, r, op); + freev(l); + freev(r); + l = int_value(val); + } +} + +/* Handle +, - operators. */ + +static VALUE *eval3(void) +{ + VALUE *l, *r; + int op; + arith_t val; + + l = eval4(); + while (1) { + op = nextarg("+"); + if (!op) { + op = nextarg("-"); + if (!op) return l; + } + G.args++; + r = eval4(); + val = arithmetic_common(l, r, op); + freev(l); + freev(r); + l = int_value(val); + } +} + +/* Handle comparisons. */ + +static VALUE *eval2(void) +{ + VALUE *l, *r; + int op; + arith_t val; + + l = eval3(); + while (1) { + op = nextarg("<"); + if (!op) { op = nextarg("<="); + if (!op) { op = nextarg("="); + if (!op) { op = nextarg("=="); + if (!op) { op = nextarg("!="); + if (!op) { op = nextarg(">="); + if (!op) { op = nextarg(">"); + if (!op) return l; + }}}}}} + G.args++; + r = eval3(); + toarith(l); + toarith(r); + val = cmp_common(l, r, op); + freev(l); + freev(r); + l = int_value(val); + } +} + +/* Handle &. */ + +static VALUE *eval1(void) +{ + VALUE *l, *r; + + l = eval2(); + while (nextarg("&")) { + G.args++; + r = eval2(); + if (null(l) || null(r)) { + freev(l); + freev(r); + l = int_value(0); + } else + freev(r); + } + return l; +} + +/* Handle |. */ + +static VALUE *eval(void) +{ + VALUE *l, *r; + + l = eval1(); + while (nextarg("|")) { + G.args++; + r = eval1(); + if (null(l)) { + freev(l); + l = r; + } else + freev(r); + } + return l; +} + +int expr_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int expr_main(int argc, char **argv) +{ + VALUE *v; + + if (argc == 1) { + bb_error_msg_and_die("too few arguments"); + } + + G.args = argv + 1; + + v = eval(); + if (*G.args) + bb_error_msg_and_die("syntax error"); + + if (v->type == INTEGER) + printf("%" PF_REZ "d\n", PF_REZ_TYPE v->u.i); + else + puts(v->u.s); + + fflush_stdout_and_exit(null(v)); +} diff --git a/coreutils/false.c b/coreutils/false.c new file mode 100644 index 0000000..f448ebf --- /dev/null +++ b/coreutils/false.c @@ -0,0 +1,21 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini false implementation for busybox + * + * Copyright (C) 1999-2004 by Erik Andersen + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +/* BB_AUDIT SUSv3 compliant */ +/* http://www.opengroup.org/onlinepubs/000095399/utilities/false.html */ + +#include "libbb.h" + +/* This is a NOFORK applet. Be very careful! */ + +int false_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int false_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) +{ + return EXIT_FAILURE; +} diff --git a/coreutils/fold.c b/coreutils/fold.c new file mode 100644 index 0000000..e2a30d5 --- /dev/null +++ b/coreutils/fold.c @@ -0,0 +1,151 @@ +/* vi: set sw=4 ts=4: */ +/* fold -- wrap each input line to fit in specified width. + + Written by David MacKenzie, djm@gnu.ai.mit.edu. + Copyright (C) 91, 1995-2002 Free Software Foundation, Inc. + + Modified for busybox based on coreutils v 5.0 + Copyright (C) 2003 Glenn McGrath + + Licensed under the GPL v2 or later, see the file LICENSE in this tarball. +*/ + +#include "libbb.h" + +/* Must match getopt32 call */ +#define FLAG_COUNT_BYTES 1 +#define FLAG_BREAK_SPACES 2 +#define FLAG_WIDTH 4 + +/* Assuming the current column is COLUMN, return the column that + printing C will move the cursor to. + The first column is 0. */ +static int adjust_column(int column, char c) +{ + if (!(option_mask32 & FLAG_COUNT_BYTES)) { + if (c == '\b') { + if (column > 0) + column--; + } else if (c == '\r') + column = 0; + else if (c == '\t') + column = column + 8 - column % 8; + else /* if (isprint (c)) */ + column++; + } else + column++; + return column; +} + +int fold_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int fold_main(int argc, char **argv) +{ + char *line_out = NULL; + int allocated_out = 0; + char *w_opt; + int width = 80; + int i; + int errs = 0; + + if (ENABLE_INCLUDE_SUSv2) { + /* Turn any numeric options into -w options. */ + for (i = 1; i < argc; i++) { + char const *a = argv[i]; + + if (*a++ == '-') { + if (*a == '-' && !a[1]) /* "--" */ + break; + if (isdigit(*a)) + argv[i] = xasprintf("-w%s", a); + } + } + } + + getopt32(argv, "bsw:", &w_opt); + if (option_mask32 & FLAG_WIDTH) + width = xatoul_range(w_opt, 1, 10000); + + argv += optind; + if (!*argv) + *--argv = (char*)"-"; + + do { + FILE *istream = fopen_or_warn_stdin(*argv); + int c; + int column = 0; /* Screen column where next char will go. */ + int offset_out = 0; /* Index in 'line_out' for next char. */ + + if (istream == NULL) { + errs |= EXIT_FAILURE; + continue; + } + + while ((c = getc(istream)) != EOF) { + if (offset_out + 1 >= allocated_out) { + allocated_out += 1024; + line_out = xrealloc(line_out, allocated_out); + } + + if (c == '\n') { + line_out[offset_out++] = c; + fwrite(line_out, sizeof(char), (size_t) offset_out, stdout); + column = offset_out = 0; + continue; + } + rescan: + column = adjust_column(column, c); + + if (column > width) { + /* This character would make the line too long. + Print the line plus a newline, and make this character + start the next line. */ + if (option_mask32 & FLAG_BREAK_SPACES) { + /* Look for the last blank. */ + int logical_end; + + for (logical_end = offset_out - 1; logical_end >= 0; logical_end--) { + if (isblank(line_out[logical_end])) { + break; + } + } + if (logical_end >= 0) { + /* Found a blank. Don't output the part after it. */ + logical_end++; + fwrite(line_out, sizeof(char), (size_t) logical_end, stdout); + bb_putchar('\n'); + /* Move the remainder to the beginning of the next line. + The areas being copied here might overlap. */ + memmove(line_out, line_out + logical_end, offset_out - logical_end); + offset_out -= logical_end; + for (column = i = 0; i < offset_out; i++) { + column = adjust_column(column, line_out[i]); + } + goto rescan; + } + } else { + if (offset_out == 0) { + line_out[offset_out++] = c; + continue; + } + } + line_out[offset_out++] = '\n'; + fwrite(line_out, sizeof(char), (size_t) offset_out, stdout); + column = offset_out = 0; + goto rescan; + } + + line_out[offset_out++] = c; + } + + if (offset_out) { + fwrite(line_out, sizeof(char), (size_t) offset_out, stdout); + } + + if (fclose_if_not_stdin(istream)) { + bb_simple_perror_msg(*argv); /* Avoid multibyte problems. */ + errs |= EXIT_FAILURE; + } + } while (*++argv); + + fflush_stdout_and_exit(errs); +} diff --git a/coreutils/head.c b/coreutils/head.c new file mode 100644 index 0000000..570f140 --- /dev/null +++ b/coreutils/head.c @@ -0,0 +1,140 @@ +/* vi: set sw=4 ts=4: */ +/* + * head implementation for busybox + * + * Copyright (C) 2003 Manuel Novoa III + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +/* BB_AUDIT SUSv3 compliant */ +/* BB_AUDIT GNU compatible -c, -q, and -v options in 'fancy' configuration. */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/head.html */ + +#include "libbb.h" + +static const char head_opts[] ALIGN1 = + "n:" +#if ENABLE_FEATURE_FANCY_HEAD + "c:qv" +#endif + ; + +#if ENABLE_FEATURE_FANCY_HEAD +static const struct suffix_mult head_suffixes[] = { + { "b", 512 }, + { "k", 1024 }, + { "m", 1024*1024 }, + { } +}; +#endif + +static const char header_fmt_str[] ALIGN1 = "\n==> %s <==\n"; + +int head_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int head_main(int argc, char **argv) +{ + unsigned long count = 10; + unsigned long i; +#if ENABLE_FEATURE_FANCY_HEAD + int count_bytes = 0; + int header_threshhold = 1; +#endif + + FILE *fp; + const char *fmt; + char *p; + int opt; + int c; + int retval = EXIT_SUCCESS; + +#if ENABLE_INCLUDE_SUSv2 || ENABLE_FEATURE_FANCY_HEAD + /* Allow legacy syntax of an initial numeric option without -n. */ + if (argc > 1 && argv[1][0] == '-' + && isdigit(argv[1][1]) + ) { + --argc; + ++argv; + p = (*argv) + 1; + goto GET_COUNT; + } +#endif + + /* No size benefit in converting this to getopt32 */ + while ((opt = getopt(argc, argv, head_opts)) > 0) { + switch (opt) { +#if ENABLE_FEATURE_FANCY_HEAD + case 'q': + header_threshhold = INT_MAX; + break; + case 'v': + header_threshhold = -1; + break; + case 'c': + count_bytes = 1; + /* fall through */ +#endif + case 'n': + p = optarg; +#if ENABLE_INCLUDE_SUSv2 || ENABLE_FEATURE_FANCY_HEAD + GET_COUNT: +#endif + +#if !ENABLE_FEATURE_FANCY_HEAD + count = xatoul(p); +#else + count = xatoul_sfx(p, head_suffixes); +#endif + break; + default: + bb_show_usage(); + } + } + + argc -= optind; + argv += optind; + if (!*argv) + *--argv = (char*)"-"; + + fmt = header_fmt_str + 1; +#if ENABLE_FEATURE_FANCY_HEAD + if (argc <= header_threshhold) { + header_threshhold = 0; + } +#else + if (argc <= 1) { + fmt += 11; /* "" */ + } + /* Now define some things here to avoid #ifdefs in the code below. + * These should optimize out of the if conditions below. */ +#define header_threshhold 1 +#define count_bytes 0 +#endif + + do { + fp = fopen_or_warn_stdin(*argv); + if (fp) { + if (fp == stdin) { + *argv = (char *) bb_msg_standard_input; + } + if (header_threshhold) { + printf(fmt, *argv); + } + i = count; + while (i && ((c = getc(fp)) != EOF)) { + if (count_bytes || (c == '\n')) { + --i; + } + putchar(c); + } + if (fclose_if_not_stdin(fp)) { + bb_simple_perror_msg(*argv); /* Avoid multibyte problems. */ + retval = EXIT_FAILURE; + } + die_if_ferror_stdout(); + } + fmt = header_fmt_str; + } while (*++argv); + + fflush_stdout_and_exit(retval); +} diff --git a/coreutils/hostid.c b/coreutils/hostid.c new file mode 100644 index 0000000..2794510 --- /dev/null +++ b/coreutils/hostid.c @@ -0,0 +1,26 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini hostid implementation for busybox + * + * Copyright (C) 2000 Edward Betts . + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +/* BB_AUDIT SUSv3 N/A -- Matches GNU behavior. */ + +#include "libbb.h" + +/* This is a NOFORK applet. Be very careful! */ + +int hostid_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int hostid_main(int argc, char **argv UNUSED_PARAM) +{ + if (argc > 1) { + bb_show_usage(); + } + + printf("%lx\n", gethostid()); + + return fflush(stdout); +} diff --git a/coreutils/id.c b/coreutils/id.c new file mode 100644 index 0000000..6ddb236 --- /dev/null +++ b/coreutils/id.c @@ -0,0 +1,220 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini id implementation for busybox + * + * Copyright (C) 2000 by Randolph Chung + * Copyright (C) 2008 by Tito Ragusa + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +/* BB_AUDIT SUSv3 compliant. */ +/* Hacked by Tito Ragusa (C) 2004 to handle usernames of whatever + * length and to be more similar to GNU id. + * -Z option support: by Yuichi Nakamura + * Added -G option Tito Ragusa (C) 2008 for SUSv3. + */ + +#include "libbb.h" + +#if !ENABLE_USE_BB_PWD_GRP +#if defined(__UCLIBC_MAJOR__) && (__UCLIBC_MAJOR__ == 0) +#if (__UCLIBC_MINOR__ < 9) || (__UCLIBC_MINOR__ == 9 && __UCLIBC_SUBLEVEL__ < 30) +#error "Sorry, you need at least uClibc version 0.9.30 for id applet to build" +#endif +#endif +#endif + +enum { + PRINT_REAL = (1 << 0), + NAME_NOT_NUMBER = (1 << 1), + JUST_USER = (1 << 2), + JUST_GROUP = (1 << 3), + JUST_ALL_GROUPS = (1 << 4), +#if ENABLE_SELINUX + JUST_CONTEXT = (1 << 5), +#endif +}; + +static int print_common(unsigned id, + char* FAST_FUNC bb_getXXXid(char *name, int bufsize, long uid), + const char *prefix) +{ + const char *name = bb_getXXXid(NULL, 0, id); + + if (prefix) { + printf("%s", prefix); + } + if (!(option_mask32 & NAME_NOT_NUMBER) || !name) { + printf("%u", id); + } + if (!option_mask32 || (option_mask32 & NAME_NOT_NUMBER)) { + if (name) { + printf(option_mask32 ? "%s" : "(%s)", name); + } else { + /* Don't set error status flag in default mode */ + if (option_mask32) { + if (ENABLE_DESKTOP) + bb_error_msg("unknown ID %u", id); + return EXIT_FAILURE; + } + } + } + return EXIT_SUCCESS; +} + +static int print_group(gid_t id, const char *prefix) +{ + return print_common(id, bb_getgrgid, prefix); +} + +static int print_user(uid_t id, const char *prefix) +{ + return print_common(id, bb_getpwuid, prefix); +} + +/* On error set *n < 0 and return >= 0 + * If *n is too small, update it and return < 0 + * (ok to trash groups[] in both cases) + * Otherwise fill in groups[] and return >= 0 + */ +static int get_groups(const char *username, gid_t rgid, gid_t *groups, int *n) +{ + int m; + + if (username) { + /* If the user is a member of more than + * *n groups, then -1 is returned. Otherwise >= 0. + * (and no defined way of detecting errors?!) */ + m = getgrouplist(username, rgid, groups, n); + /* I guess *n < 0 might indicate error. Anyway, + * malloc'ing -1 bytes won't be good, so: */ + //if (*n < 0) + // return 0; + //return m; + //commented out here, happens below anyway + } else { + /* On error -1 is returned, which ends up in *n */ + int nn = getgroups(*n, groups); + /* 0: nn <= *n, groups[] was big enough; -1 otherwise */ + m = - (nn > *n); + *n = nn; + } + if (*n < 0) + return 0; /* error, don't return < 0! */ + return m; +} + +int id_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int id_main(int argc UNUSED_PARAM, char **argv) +{ + uid_t ruid; + gid_t rgid; + uid_t euid; + gid_t egid; + unsigned opt; + int i; + int status = EXIT_SUCCESS; + const char *prefix; + const char *username; +#if ENABLE_SELINUX + security_context_t scontext = NULL; +#endif + /* Don't allow -n -r -nr -ug -rug -nug -rnug -uZ -gZ -GZ*/ + /* Don't allow more than one username */ + opt_complementary = "?1:u--g:g--u:G--u:u--G:g--G:G--g:r?ugG:n?ugG" + USE_SELINUX(":u--Z:Z--u:g--Z:Z--g:G--Z:Z--G"); + opt = getopt32(argv, "rnugG" USE_SELINUX("Z")); + + username = argv[optind]; + if (username) { + struct passwd *p = getpwnam(username); + if (!p) + bb_error_msg_and_die("unknown user %s", username); + euid = ruid = p->pw_uid; + egid = rgid = p->pw_gid; + } else { + egid = getegid(); + rgid = getgid(); + euid = geteuid(); + ruid = getuid(); + } + /* JUST_ALL_GROUPS ignores -r PRINT_REAL flag even if man page for */ + /* id says: print the real ID instead of the effective ID, with -ugG */ + /* in fact in this case egid is always printed if egid != rgid */ + if (!opt || (opt & JUST_ALL_GROUPS)) { + gid_t *groups; + int n; + + if (!opt) { + /* Default Mode */ + status |= print_user(ruid, "uid="); + status |= print_group(rgid, " gid="); + if (euid != ruid) + status |= print_user(euid, " euid="); + if (egid != rgid) + status |= print_group(egid, " egid="); + } else { + /* JUST_ALL_GROUPS */ + status |= print_group(rgid, NULL); + if (egid != rgid) + status |= print_group(egid, " "); + } + /* We are supplying largish buffer, trying + * to not run get_groups() twice. That might be slow + * ("user database in remote SQL server" case) */ + groups = xmalloc(64 * sizeof(gid_t)); + n = 64; + if (get_groups(username, rgid, groups, &n) < 0) { + /* Need bigger buffer after all */ + groups = xrealloc(groups, n * sizeof(gid_t)); + get_groups(username, rgid, groups, &n); + } + if (n > 0) { + /* Print the list */ + prefix = " groups="; + for (i = 0; i < n; i++) { + if (opt && (groups[i] == rgid || groups[i] == egid)) + continue; + status |= print_group(groups[i], opt ? " " : prefix); + prefix = ","; + } + } else if (n < 0) { /* error in get_groups() */ + if (!ENABLE_DESKTOP) + bb_error_msg_and_die("cannot get groups"); + else + return EXIT_FAILURE; + } + if (ENABLE_FEATURE_CLEAN_UP) + free(groups); +#if ENABLE_SELINUX + if (is_selinux_enabled()) { + if (getcon(&scontext) == 0) + printf(" context=%s", scontext); + } +#endif + } else if (opt & PRINT_REAL) { + euid = ruid; + egid = rgid; + } + + if (opt & JUST_USER) + status |= print_user(euid, NULL); + else if (opt & JUST_GROUP) + status |= print_group(egid, NULL); +#if ENABLE_SELINUX + else if (opt & JUST_CONTEXT) { + selinux_or_die(); + if (username || getcon(&scontext)) { + bb_error_msg_and_die("can't get process context%s", + username ? " for a different user" : ""); + } + fputs(scontext, stdout); + } + /* freecon(NULL) seems to be harmless */ + if (ENABLE_FEATURE_CLEAN_UP) + freecon(scontext); +#endif + bb_putchar('\n'); + fflush_stdout_and_exit(status); +} diff --git a/coreutils/id_test.sh b/coreutils/id_test.sh new file mode 100755 index 0000000..0d65f2a --- /dev/null +++ b/coreutils/id_test.sh @@ -0,0 +1,244 @@ +#!/bin/bash +# Test script for busybox id vs. coreutils id. +# Needs root privileges for some tests. + +cp /usr/bin/id . +BUSYBOX=./busybox +ID=./id +LIST=`awk -F: '{ printf "%s\n", $1 }' /etc/passwd` +FLAG_USER_EXISTS="no" +TEST_USER="f583ca884c1d93458fb61ed137ff44f6" + +echo "test 1: id [options] nousername" +rm -f foo bar +for OPTIONS in "" "-u" "-un" "-unr" "-g" "-gn" "-gnr" "-G" "-Gn" "-Gnr" +do + #echo "$OPTIONS" + $BUSYBOX id $OPTIONS >foo 2>/dev/null + RET1=$? + $ID $OPTIONS >bar 2>/dev/null + RET2=$? + if test "$RET1" != "$RET2"; then + echo "Return Values differ ($RET1 != $RET2): options $OPTIONS" + fi + diff foo bar +done + +echo "test 2: id [options] username" +rm -f foo bar +for OPTIONS in "" "-u" "-un" "-unr" "-g" "-gn" "-gnr" "-G" "-Gn" "-Gnr" +do + #echo "$OPTIONS" + for i in $LIST ; do + if test "$i" = "$TEST_USER"; then + FLAG_USER_EXISTS="yes" + fi + $BUSYBOX id $OPTIONS $i >foo 2>/dev/null + RET1=$? + $ID $OPTIONS $i >bar 2>/dev/null + RET2=$? + if test "$RET1" != "$RET2"; then + echo "Return Values differ ($RET1 != $RET2): options $OPTIONS" + fi + diff foo bar + done +done + +if test $FLAG_USER_EXISTS = "yes"; then + echo "test 3,4,5,6,7,8,9,10,11,12 skipped because test user $TEST_USER already exists" + rm -f foo bar + exit 1 +fi + +adduser -s /bin/true -g "" -H -D "$TEST_USER" || exit 1 + +chown $TEST_USER.$TEST_USER $BUSYBOX +chmod u+s $BUSYBOX 2>&1 /dev/null +chown $TEST_USER.$TEST_USER $ID +chmod u+s $ID 2>&1 /dev/null + +echo "test 3 setuid, existing user: id [options] no username" +rm -f foo bar +for OPTIONS in "" "-u" "-un" "-unr" "-g" "-gn" "-gnr" "-G" "-Gn" "-Gnr" +do + #echo "$OPTIONS" + $BUSYBOX id $OPTIONS >foo 2>/dev/null + RET1=$? + $ID $OPTIONS >bar 2>/dev/null + RET2=$? + if test "$RET1" != "$RET2"; then + echo "Return Values differ ($RET1 != $RET2): options $OPTIONS" + fi + diff foo bar + #done +done + +echo "test 4 setuid, existing user: id [options] username" +rm -f foo bar +for OPTIONS in "" "-u" "-un" "-unr" "-g" "-gn" "-gnr" "-G" "-Gn" "-Gnr" +do + #echo "$OPTIONS" + for i in $LIST ; do + $BUSYBOX id $OPTIONS $i >foo 2>/dev/null + RET1=$? + $ID $OPTIONS $i >bar 2>/dev/null + RET2=$? + if test "$RET1" != "$RET2"; then + echo "Return Values differ ($RET1 != $RET2): options $OPTIONS" + fi + diff foo bar + done +done + +chown $TEST_USER.$TEST_USER $BUSYBOX +chmod g+s $BUSYBOX 2>&1 /dev/null +chown $TEST_USER.$TEST_USER $ID +chmod g+s $ID 2>&1 /dev/null + +echo "test 5 setgid, existing user: id [options] no username" +rm -f foo bar +for OPTIONS in "" "-u" "-un" "-unr" "-g" "-gn" "-gnr" "-G" "-Gn" "-Gnr" +do + #echo "$OPTIONS" + $BUSYBOX id $OPTIONS >foo 2>/dev/null + RET1=$? + $ID $OPTIONS >bar 2>/dev/null + RET2=$? + if test "$RET1" != "$RET2"; then + echo "Return Values differ ($RET1 != $RET2): options $OPTIONS" + fi + diff foo bar + #done +done + +echo "test 6 setgid, existing user: id [options] username" +rm -f foo bar +for OPTIONS in "" "-u" "-un" "-unr" "-g" "-gn" "-gnr" "-G" "-Gn" "-Gnr" +do + #echo "$OPTIONS" + for i in $LIST ; do + $BUSYBOX id $OPTIONS $i >foo 2>/dev/null + RET1=$? + $ID $OPTIONS $i >bar 2>/dev/null + RET2=$? + if test "$RET1" != "$RET2"; then + echo "Return Values differ ($RET1 != $RET2): options $OPTIONS" + fi + diff foo bar + done +done + +chown $TEST_USER.$TEST_USER $BUSYBOX +chmod u+s,g+s $BUSYBOX 2>&1 /dev/null +chown $TEST_USER.$TEST_USER $ID +chmod u+s,g+s $ID 2>&1 /dev/null + +echo "test 7 setuid, setgid, existing user: id [options] no username" +rm -f foo bar +for OPTIONS in "" "-u" "-un" "-unr" "-g" "-gn" "-gnr" "-G" "-Gn" "-Gnr" +do + #echo "$OPTIONS" + $BUSYBOX id $OPTIONS >foo 2>/dev/null + RET1=$? + $ID $OPTIONS >bar 2>/dev/null + RET2=$? + if test "$RET1" != "$RET2"; then + echo "Return Values differ ($RET1 != $RET2): options $OPTIONS" + fi + diff foo bar + #done +done + +echo "test 8 setuid, setgid, existing user: id [options] username" +rm -f foo bar +for OPTIONS in "" "-u" "-un" "-unr" "-g" "-gn" "-gnr" "-G" "-Gn" "-Gnr" +do + #echo "$OPTIONS" + for i in $LIST ; do + $BUSYBOX id $OPTIONS $i >foo 2>/dev/null + RET1=$? + $ID $OPTIONS $i >bar 2>/dev/null + RET2=$? + if test "$RET1" != "$RET2"; then + echo "Return Values differ ($RET1 != $RET2): options $OPTIONS" + fi + diff foo bar + done +done + +deluser $TEST_USER || exit 1 + +echo "test 9 setuid, setgid, not existing user: id [options] no username" +rm -f foo bar +for OPTIONS in "" "-u" "-un" "-unr" "-g" "-gn" "-gnr" "-G" "-Gn" "-Gnr" +do + #echo "$OPTIONS" + $BUSYBOX id $OPTIONS >foo 2>/dev/null + RET1=$? + $ID $OPTIONS >bar 2>/dev/null + RET2=$? + if test "$RET1" != "$RET2"; then + echo "Return Values differ ($RET1 != $RET2): options $OPTIONS" + fi + diff foo bar +done + +echo "test 10 setuid, setgid, not existing user: id [options] username" +rm -f foo bar +for OPTIONS in "" "-u" "-un" "-unr" "-g" "-gn" "-gnr" "-G" "-Gn" "-Gnr" +do + #echo "$OPTIONS" + for i in $LIST ; do + $BUSYBOX id $OPTIONS $i >foo 2>/dev/null + RET1=$? + $ID $OPTIONS $i >bar 2>/dev/null + RET2=$? + if test "$RET1" != "$RET2"; then + echo "Return Values differ ($RET1 != $RET2): options $OPTIONS" + fi + diff foo bar + done +done + +chown .root $BUSYBOX 2>&1 /dev/null +chown .root $ID 2>&1 /dev/null +chmod g+s $BUSYBOX 2>&1 /dev/null +chmod g+s $ID 2>&1 /dev/null + +echo "test 11 setgid, not existing group: id [options] no username" +rm -f foo bar +for OPTIONS in "" "-u" "-un" "-unr" "-g" "-gn" "-gnr" "-G" "-Gn" "-Gnr" +do + #echo "$OPTIONS" + $BUSYBOX id $OPTIONS >foo 2>/dev/null + RET1=$? + $ID $OPTIONS >bar 2>/dev/null + RET2=$? + if test "$RET1" != "$RET2"; then + echo "Return Values differ ($RET1 != $RET2): options $OPTIONS" + fi + diff foo bar + #done +done + +echo "test 12 setgid, not existing group: id [options] username" +rm -f foo bar +for OPTIONS in "" "-u" "-un" "-unr" "-g" "-gn" "-gnr" "-G" "-Gn" "-Gnr" +do + #echo "$OPTIONS" + for i in $LIST ; do + $BUSYBOX id $OPTIONS $i >foo 2>/dev/null + RET1=$? + $ID $OPTIONS $i >bar 2>/dev/null + RET2=$? + if test "$RET1" != "$RET2"; then + echo "Return Values differ ($RET1 != $RET2): options $OPTIONS" + fi + diff foo bar + done +done + +chown root.root $BUSYBOX 2>&1 /dev/null +chown root.root $ID 2>&1 /dev/null +rm -f $ID +rm -f foo bar diff --git a/coreutils/install.c b/coreutils/install.c new file mode 100644 index 0000000..2b796e2 --- /dev/null +++ b/coreutils/install.c @@ -0,0 +1,210 @@ +/* vi: set sw=4 ts=4: */ +/* + * Copyright (C) 2003 by Glenn McGrath + * SELinux support: by Yuichi Nakamura + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +#include "libbb.h" +#include "libcoreutils/coreutils.h" + +#if ENABLE_FEATURE_INSTALL_LONG_OPTIONS +static const char install_longopts[] ALIGN1 = + "directory\0" No_argument "d" + "preserve-timestamps\0" No_argument "p" + "strip\0" No_argument "s" + "group\0" Required_argument "g" + "mode\0" Required_argument "m" + "owner\0" Required_argument "o" +/* autofs build insists of using -b --suffix=.orig */ +/* TODO? (short option for --suffix is -S) */ +#if ENABLE_SELINUX + "context\0" Required_argument "Z" + "preserve_context\0" No_argument "\xff" + "preserve-context\0" No_argument "\xff" +#endif + ; +#endif + + +#if ENABLE_SELINUX +static void setdefaultfilecon(const char *path) +{ + struct stat s; + security_context_t scontext = NULL; + + if (!is_selinux_enabled()) { + return; + } + if (lstat(path, &s) != 0) { + return; + } + + if (matchpathcon(path, s.st_mode, &scontext) < 0) { + goto out; + } + if (strcmp(scontext, "<>") == 0) { + goto out; + } + + if (lsetfilecon(path, scontext) < 0) { + if (errno != ENOTSUP) { + bb_perror_msg("warning: failed to change context" + " of %s to %s", path, scontext); + } + } + + out: + freecon(scontext); +} + +#endif + +int install_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int install_main(int argc, char **argv) +{ + struct stat statbuf; + mode_t mode; + uid_t uid; + gid_t gid; + char *arg, *last; + const char *gid_str; + const char *uid_str; + const char *mode_str; + int copy_flags = FILEUTILS_DEREFERENCE | FILEUTILS_FORCE; + int opts; + int min_args = 1; + int ret = EXIT_SUCCESS; + int isdir = 0; +#if ENABLE_SELINUX + security_context_t scontext; + bool use_default_selinux_context = 1; +#endif + enum { + OPT_c = 1 << 0, + OPT_v = 1 << 1, + OPT_b = 1 << 2, + OPT_MKDIR_LEADING = 1 << 3, + OPT_DIRECTORY = 1 << 4, + OPT_PRESERVE_TIME = 1 << 5, + OPT_STRIP = 1 << 6, + OPT_GROUP = 1 << 7, + OPT_MODE = 1 << 8, + OPT_OWNER = 1 << 9, +#if ENABLE_SELINUX + OPT_SET_SECURITY_CONTEXT = 1 << 10, + OPT_PRESERVE_SECURITY_CONTEXT = 1 << 11, +#endif + }; + +#if ENABLE_FEATURE_INSTALL_LONG_OPTIONS + applet_long_options = install_longopts; +#endif + opt_complementary = "s--d:d--s" USE_SELINUX(":Z--\xff:\xff--Z"); + /* -c exists for backwards compatibility, it's needed */ + /* -v is ignored ("print name of each created directory") */ + /* -b is ignored ("make a backup of each existing destination file") */ + opts = getopt32(argv, "cvb" "Ddpsg:m:o:" USE_SELINUX("Z:"), + &gid_str, &mode_str, &uid_str USE_SELINUX(, &scontext)); + argc -= optind; + argv += optind; + +#if ENABLE_SELINUX + if (opts & (OPT_PRESERVE_SECURITY_CONTEXT|OPT_SET_SECURITY_CONTEXT)) { + selinux_or_die(); + use_default_selinux_context = 0; + if (opts & OPT_PRESERVE_SECURITY_CONTEXT) { + copy_flags |= FILEUTILS_PRESERVE_SECURITY_CONTEXT; + } + if (opts & OPT_SET_SECURITY_CONTEXT) { + setfscreatecon_or_die(scontext); + copy_flags |= FILEUTILS_SET_SECURITY_CONTEXT; + } + } +#endif + + /* preserve access and modification time, this is GNU behaviour, + * BSD only preserves modification time */ + if (opts & OPT_PRESERVE_TIME) { + copy_flags |= FILEUTILS_PRESERVE_STATUS; + } + mode = 0666; + if (opts & OPT_MODE) + bb_parse_mode(mode_str, &mode); + uid = (opts & OPT_OWNER) ? get_ug_id(uid_str, xuname2uid) : getuid(); + gid = (opts & OPT_GROUP) ? get_ug_id(gid_str, xgroup2gid) : getgid(); + + last = argv[argc - 1]; + if (!(opts & OPT_DIRECTORY)) { + argv[argc - 1] = NULL; + min_args++; + + /* coreutils install resolves link in this case, don't use lstat */ + isdir = stat(last, &statbuf) < 0 ? 0 : S_ISDIR(statbuf.st_mode); + } + + if (argc < min_args) + bb_show_usage(); + + while ((arg = *argv++) != NULL) { + char *dest = last; + if (opts & OPT_DIRECTORY) { + dest = arg; + /* GNU coreutils 6.9 does not set uid:gid + * on intermediate created directories + * (only on last one) */ + if (bb_make_directory(dest, 0755, FILEUTILS_RECUR)) { + ret = EXIT_FAILURE; + goto next; + } + } else { + if (opts & OPT_MKDIR_LEADING) { + char *ddir = xstrdup(dest); + bb_make_directory(dirname(ddir), 0755, FILEUTILS_RECUR); + /* errors are not checked. copy_file + * will fail if dir is not created. */ + free(ddir); + } + if (isdir) + dest = concat_path_file(last, basename(arg)); + if (copy_file(arg, dest, copy_flags)) { + /* copy is not made */ + ret = EXIT_FAILURE; + goto next; + } + } + + /* Set the file mode */ + if ((opts & OPT_MODE) && chmod(dest, mode) == -1) { + bb_perror_msg("can't change %s of %s", "permissions", dest); + ret = EXIT_FAILURE; + } +#if ENABLE_SELINUX + if (use_default_selinux_context) + setdefaultfilecon(dest); +#endif + /* Set the user and group id */ + if ((opts & (OPT_OWNER|OPT_GROUP)) + && lchown(dest, uid, gid) == -1 + ) { + bb_perror_msg("can't change %s of %s", "ownership", dest); + ret = EXIT_FAILURE; + } + if (opts & OPT_STRIP) { + char *args[3]; + args[0] = (char*)"strip"; + args[1] = dest; + args[2] = NULL; + if (spawn_and_wait(args)) { + bb_perror_msg("strip"); + ret = EXIT_FAILURE; + } + } + next: + if (ENABLE_FEATURE_CLEAN_UP && isdir) + free(dest); + } + + return ret; +} diff --git a/coreutils/length.c b/coreutils/length.c new file mode 100644 index 0000000..c7523a0 --- /dev/null +++ b/coreutils/length.c @@ -0,0 +1,19 @@ +/* vi: set sw=4 ts=4: */ + +/* BB_AUDIT SUSv3 N/A -- Apparently a busybox (obsolete?) extension. */ + +#include "libbb.h" + +/* This is a NOFORK applet. Be very careful! */ + +int length_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int length_main(int argc, char **argv) +{ + if ((argc != 2) || (**(++argv) == '-')) { + bb_show_usage(); + } + + printf("%u\n", (unsigned)strlen(*argv)); + + return fflush(stdout); +} diff --git a/coreutils/libcoreutils/Kbuild b/coreutils/libcoreutils/Kbuild new file mode 100644 index 0000000..755d01f --- /dev/null +++ b/coreutils/libcoreutils/Kbuild @@ -0,0 +1,12 @@ +# Makefile for busybox +# +# Copyright (C) 1999-2004 by Erik Andersen +# +# Licensed under the GPL v2 or later, see the file LICENSE in this tarball. + +lib-y:= +lib-$(CONFIG_MKFIFO) += getopt_mk_fifo_nod.o +lib-$(CONFIG_MKNOD) += getopt_mk_fifo_nod.o +lib-$(CONFIG_INSTALL) += cp_mv_stat.o +lib-$(CONFIG_CP) += cp_mv_stat.o +lib-$(CONFIG_MV) += cp_mv_stat.o diff --git a/coreutils/libcoreutils/coreutils.h b/coreutils/libcoreutils/coreutils.h new file mode 100644 index 0000000..89cd953 --- /dev/null +++ b/coreutils/libcoreutils/coreutils.h @@ -0,0 +1,24 @@ +/* vi: set sw=4 ts=4: */ +/* + * Licensed under the GPL v2 or later, see the file LICENSE in this tarball. + */ + +#ifndef COREUTILS_H +#define COREUTILS_H 1 + +#if __GNUC_PREREQ(4,1) +# pragma GCC visibility push(hidden) +#endif + +typedef int (*stat_func)(const char *fn, struct stat *ps); + +int cp_mv_stat2(const char *fn, struct stat *fn_stat, stat_func sf) FAST_FUNC; +int cp_mv_stat(const char *fn, struct stat *fn_stat) FAST_FUNC; + +mode_t getopt_mk_fifo_nod(char **argv) FAST_FUNC; + +#if __GNUC_PREREQ(4,1) +# pragma GCC visibility pop +#endif + +#endif diff --git a/coreutils/libcoreutils/cp_mv_stat.c b/coreutils/libcoreutils/cp_mv_stat.c new file mode 100644 index 0000000..0fd467c --- /dev/null +++ b/coreutils/libcoreutils/cp_mv_stat.c @@ -0,0 +1,50 @@ +/* vi: set sw=4 ts=4: */ +/* + * coreutils utility routine + * + * Copyright (C) 2003 Manuel Novoa III + * + * 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 + * + */ + +#include "libbb.h" +#include "coreutils.h" + +int FAST_FUNC cp_mv_stat2(const char *fn, struct stat *fn_stat, stat_func sf) +{ + if (sf(fn, fn_stat) < 0) { + if (errno != ENOENT) { +#if ENABLE_FEATURE_VERBOSE_CP_MESSAGE + if (errno == ENOTDIR) { + bb_error_msg("cannot stat '%s': Path has non-directory component", fn); + return -1; + } +#endif + bb_perror_msg("cannot stat '%s'", fn); + return -1; + } + return 0; + } + if (S_ISDIR(fn_stat->st_mode)) { + return 3; + } + return 1; +} + +int FAST_FUNC cp_mv_stat(const char *fn, struct stat *fn_stat) +{ + return cp_mv_stat2(fn, fn_stat, stat); +} diff --git a/coreutils/libcoreutils/getopt_mk_fifo_nod.c b/coreutils/libcoreutils/getopt_mk_fifo_nod.c new file mode 100644 index 0000000..ba3222e --- /dev/null +++ b/coreutils/libcoreutils/getopt_mk_fifo_nod.c @@ -0,0 +1,48 @@ +/* vi: set sw=4 ts=4: */ +/* + * coreutils utility routine + * + * Copyright (C) 2003 Manuel Novoa III + * + * 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 + * + */ + +#include "libbb.h" +#include "coreutils.h" + +mode_t FAST_FUNC getopt_mk_fifo_nod(char **argv) +{ + mode_t mode = 0666; + char *smode = NULL; +#if ENABLE_SELINUX + security_context_t scontext; +#endif + int opt; + opt = getopt32(argv, "m:" USE_SELINUX("Z:"), &smode USE_SELINUX(,&scontext)); + if (opt & 1) { + if (bb_parse_mode(smode, &mode)) + umask(0); + } + +#if ENABLE_SELINUX + if (opt & 2) { + selinux_or_die(); + setfscreatecon_or_die(scontext); + } +#endif + + return mode; +} diff --git a/coreutils/ln.c b/coreutils/ln.c new file mode 100644 index 0000000..eb71719 --- /dev/null +++ b/coreutils/ln.c @@ -0,0 +1,109 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini ln implementation for busybox + * + * Copyright (C) 1999-2004 by Erik Andersen + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +/* BB_AUDIT SUSv3 compliant */ +/* BB_AUDIT GNU options missing: -d, -F, -i, and -v. */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/ln.html */ + +#include "libbb.h" + +/* This is a NOEXEC applet. Be very careful! */ + + +#define LN_SYMLINK 1 +#define LN_FORCE 2 +#define LN_NODEREFERENCE 4 +#define LN_BACKUP 8 +#define LN_SUFFIX 16 + +int ln_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int ln_main(int argc, char **argv) +{ + int status = EXIT_SUCCESS; + int flag; + char *last; + char *src_name; + char *src; + char *suffix = (char*)"~"; + struct stat statbuf; + int (*link_func)(const char *, const char *); + + flag = getopt32(argv, "sfnbS:", &suffix); + + if (argc == optind) { + bb_show_usage(); + } + + last = argv[argc - 1]; + argv += optind; + + if (argc == optind + 1) { + *--argv = last; + last = bb_get_last_path_component_strip(xstrdup(last)); + } + + do { + src_name = NULL; + src = last; + + if (is_directory(src, + (flag & LN_NODEREFERENCE) ^ LN_NODEREFERENCE, + NULL) + ) { + src_name = xstrdup(*argv); + src = concat_path_file(src, bb_get_last_path_component_strip(src_name)); + free(src_name); + src_name = src; + } + if (!(flag & LN_SYMLINK) && stat(*argv, &statbuf)) { + // coreutils: "ln dangling_symlink new_hardlink" works + if (lstat(*argv, &statbuf) || !S_ISLNK(statbuf.st_mode)) { + bb_simple_perror_msg(*argv); + status = EXIT_FAILURE; + free(src_name); + continue; + } + } + + if (flag & LN_BACKUP) { + char *backup; + backup = xasprintf("%s%s", src, suffix); + if (rename(src, backup) < 0 && errno != ENOENT) { + bb_simple_perror_msg(src); + status = EXIT_FAILURE; + free(backup); + continue; + } + free(backup); + /* + * When the source and dest are both hard links to the same + * inode, a rename may succeed even though nothing happened. + * Therefore, always unlink(). + */ + unlink(src); + } else if (flag & LN_FORCE) { + unlink(src); + } + + link_func = link; + if (flag & LN_SYMLINK) { + link_func = symlink; + } + + if (link_func(*argv, src) != 0) { + bb_simple_perror_msg(src); + status = EXIT_FAILURE; + } + + free(src_name); + + } while ((++argv)[1]); + + return status; +} diff --git a/coreutils/logname.c b/coreutils/logname.c new file mode 100644 index 0000000..3400c30 --- /dev/null +++ b/coreutils/logname.c @@ -0,0 +1,43 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini logname implementation for busybox + * + * Copyright (C) 2000 Edward Betts . + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +/* BB_AUDIT SUSv3 compliant */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/logname.html */ + +/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org) + * + * SUSv3 specifies the string used is that returned from getlogin(). + * The previous implementation used getpwuid() for geteuid(), which + * is _not_ the same. Erik apparently made this change almost 3 years + * ago to avoid failing when no utmp was available. However, the + * correct course of action wrt SUSv3 for a failing getlogin() is + * a diagnostic message and an error return. + */ + +#include "libbb.h" + +/* This is a NOFORK applet. Be very careful! */ + +int logname_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int logname_main(int argc, char **argv UNUSED_PARAM) +{ + char buf[128]; + + if (argc > 1) { + bb_show_usage(); + } + + /* Using _r function - avoid pulling in static buffer from libc */ + if (getlogin_r(buf, sizeof(buf)) == 0) { + puts(buf); + return fflush(stdout); + } + + bb_perror_msg_and_die("getlogin"); +} diff --git a/coreutils/ls.c b/coreutils/ls.c new file mode 100644 index 0000000..f4e71bc --- /dev/null +++ b/coreutils/ls.c @@ -0,0 +1,980 @@ +/* vi: set sw=4 ts=4: */ +/* + * tiny-ls.c version 0.1.0: A minimalist 'ls' + * Copyright (C) 1996 Brian Candler + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +/* + * To achieve a small memory footprint, this version of 'ls' doesn't do any + * file sorting, and only has the most essential command line switches + * (i.e., the ones I couldn't live without :-) All features which involve + * linking in substantial chunks of libc can be disabled. + * + * Although I don't really want to add new features to this program to + * keep it small, I *am* interested to receive bug fixes and ways to make + * it more portable. + * + * KNOWN BUGS: + * 1. ls -l of a directory doesn't give "total " header + * 2. ls of a symlink to a directory doesn't list directory contents + * 3. hidden files can make column width too large + * + * NON-OPTIMAL BEHAVIOUR: + * 1. autowidth reads directories twice + * 2. if you do a short directory listing without filetype characters + * appended, there's no need to stat each one + * PORTABILITY: + * 1. requires lstat (BSD) - how do you do it without? + */ + +#include "libbb.h" + +#if ENABLE_FEATURE_ASSUME_UNICODE +#include +#endif + +/* This is a NOEXEC applet. Be very careful! */ + + +enum { + +TERMINAL_WIDTH = 80, /* use 79 if terminal has linefold bug */ +COLUMN_GAP = 2, /* includes the file type char */ + +/* what is the overall style of the listing */ +STYLE_COLUMNS = 1 << 21, /* fill columns */ +STYLE_LONG = 2 << 21, /* one record per line, extended info */ +STYLE_SINGLE = 3 << 21, /* one record per line */ +STYLE_MASK = STYLE_SINGLE, + +/* 51306 lrwxrwxrwx 1 root root 2 May 11 01:43 /bin/view -> vi* */ +/* what file information will be listed */ +LIST_INO = 1 << 0, +LIST_BLOCKS = 1 << 1, +LIST_MODEBITS = 1 << 2, +LIST_NLINKS = 1 << 3, +LIST_ID_NAME = 1 << 4, +LIST_ID_NUMERIC = 1 << 5, +LIST_CONTEXT = 1 << 6, +LIST_SIZE = 1 << 7, +LIST_DEV = 1 << 8, +LIST_DATE_TIME = 1 << 9, +LIST_FULLTIME = 1 << 10, +LIST_FILENAME = 1 << 11, +LIST_SYMLINK = 1 << 12, +LIST_FILETYPE = 1 << 13, +LIST_EXEC = 1 << 14, +LIST_MASK = (LIST_EXEC << 1) - 1, + +/* what files will be displayed */ +DISP_DIRNAME = 1 << 15, /* 2 or more items? label directories */ +DISP_HIDDEN = 1 << 16, /* show filenames starting with . */ +DISP_DOT = 1 << 17, /* show . and .. */ +DISP_NOLIST = 1 << 18, /* show directory as itself, not contents */ +DISP_RECURSIVE = 1 << 19, /* show directory and everything below it */ +DISP_ROWS = 1 << 20, /* print across rows */ +DISP_MASK = ((DISP_ROWS << 1) - 1) & ~(DISP_DIRNAME - 1), + +/* how will the files be sorted (CONFIG_FEATURE_LS_SORTFILES) */ +SORT_FORWARD = 0, /* sort in reverse order */ +SORT_REVERSE = 1 << 27, /* sort in reverse order */ + +SORT_NAME = 0, /* sort by file name */ +SORT_SIZE = 1 << 28, /* sort by file size */ +SORT_ATIME = 2 << 28, /* sort by last access time */ +SORT_CTIME = 3 << 28, /* sort by last change time */ +SORT_MTIME = 4 << 28, /* sort by last modification time */ +SORT_VERSION = 5 << 28, /* sort by version */ +SORT_EXT = 6 << 28, /* sort by file name extension */ +SORT_DIR = 7 << 28, /* sort by file or directory */ +SORT_MASK = (7 << 28) * ENABLE_FEATURE_LS_SORTFILES, + +/* which of the three times will be used */ +TIME_CHANGE = (1 << 23) * ENABLE_FEATURE_LS_TIMESTAMPS, +TIME_ACCESS = (1 << 24) * ENABLE_FEATURE_LS_TIMESTAMPS, +TIME_MASK = (3 << 23) * ENABLE_FEATURE_LS_TIMESTAMPS, + +FOLLOW_LINKS = (1 << 25) * ENABLE_FEATURE_LS_FOLLOWLINKS, + +LS_DISP_HR = (1 << 26) * ENABLE_FEATURE_HUMAN_READABLE, + +LIST_SHORT = LIST_FILENAME, +LIST_LONG = LIST_MODEBITS | LIST_NLINKS | LIST_ID_NAME | LIST_SIZE | \ + LIST_DATE_TIME | LIST_FILENAME | LIST_SYMLINK, + +SPLIT_DIR = 1, +SPLIT_FILE = 0, +SPLIT_SUBDIR = 2, + +}; + +#define TYPEINDEX(mode) (((mode) >> 12) & 0x0f) +#define TYPECHAR(mode) ("0pcCd?bB-?l?s???" [TYPEINDEX(mode)]) +#define APPCHAR(mode) ("\0|\0\0/\0\0\0\0\0@\0=\0\0\0" [TYPEINDEX(mode)]) +#define COLOR(mode) ("\000\043\043\043\042\000\043\043"\ + "\000\000\044\000\043\000\000\040" [TYPEINDEX(mode)]) +#define ATTR(mode) ("\00\00\01\00\01\00\01\00"\ + "\00\00\01\00\01\00\00\01" [TYPEINDEX(mode)]) + +/* + * a directory entry and its stat info are stored here + */ +struct dnode { /* the basic node */ + const char *name; /* the dir entry name */ + const char *fullname; /* the dir entry name */ + int allocated; + struct stat dstat; /* the file stat info */ + USE_SELINUX(security_context_t sid;) + struct dnode *next; /* point at the next node */ +}; + +static struct dnode **list_dir(const char *); +static struct dnode **dnalloc(int); +static int list_single(const struct dnode *); + + +struct globals { +#if ENABLE_FEATURE_LS_COLOR + smallint show_color; +#endif + smallint exit_code; + unsigned all_fmt; +#if ENABLE_FEATURE_AUTOWIDTH + unsigned tabstops; // = COLUMN_GAP; + unsigned terminal_width; // = TERMINAL_WIDTH; +#endif +#if ENABLE_FEATURE_LS_TIMESTAMPS + /* Do time() just once. Saves one syscall per file for "ls -l" */ + time_t current_time_t; +#endif +}; +#define G (*(struct globals*)&bb_common_bufsiz1) +#if ENABLE_FEATURE_LS_COLOR +#define show_color (G.show_color ) +#else +enum { show_color = 0 }; +#endif +#define exit_code (G.exit_code ) +#define all_fmt (G.all_fmt ) +#if ENABLE_FEATURE_AUTOWIDTH +#define tabstops (G.tabstops ) +#define terminal_width (G.terminal_width) +#else +enum { + tabstops = COLUMN_GAP, + terminal_width = TERMINAL_WIDTH, +}; +#endif +#define current_time_t (G.current_time_t) +/* memset: we have to zero it out because of NOEXEC */ +#define INIT_G() do { \ + memset(&G, 0, sizeof(G)); \ + USE_FEATURE_AUTOWIDTH(tabstops = COLUMN_GAP;) \ + USE_FEATURE_AUTOWIDTH(terminal_width = TERMINAL_WIDTH;) \ + USE_FEATURE_LS_TIMESTAMPS(time(¤t_time_t);) \ +} while (0) + + +#if ENABLE_FEATURE_ASSUME_UNICODE +/* libbb candidate */ +static size_t mbstrlen(const char *string) +{ + size_t width = mbsrtowcs(NULL /*dest*/, &string, + MAXINT(size_t) /*len*/, NULL /*state*/); + if (width == (size_t)-1) + return strlen(string); + return width; +} +#else +#define mbstrlen(string) strlen(string) +#endif + + +static struct dnode *my_stat(const char *fullname, const char *name, int force_follow) +{ + struct stat dstat; + struct dnode *cur; + USE_SELINUX(security_context_t sid = NULL;) + + if ((all_fmt & FOLLOW_LINKS) || force_follow) { +#if ENABLE_SELINUX + if (is_selinux_enabled()) { + getfilecon(fullname, &sid); + } +#endif + if (stat(fullname, &dstat)) { + bb_simple_perror_msg(fullname); + exit_code = EXIT_FAILURE; + return 0; + } + } else { +#if ENABLE_SELINUX + if (is_selinux_enabled()) { + lgetfilecon(fullname, &sid); + } +#endif + if (lstat(fullname, &dstat)) { + bb_simple_perror_msg(fullname); + exit_code = EXIT_FAILURE; + return 0; + } + } + + cur = xmalloc(sizeof(struct dnode)); + cur->fullname = fullname; + cur->name = name; + cur->dstat = dstat; + USE_SELINUX(cur->sid = sid;) + return cur; +} + +#if ENABLE_FEATURE_LS_COLOR +static char fgcolor(mode_t mode) +{ + /* Check wheter the file is existing (if so, color it red!) */ + if (errno == ENOENT) + return '\037'; + if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH))) + return COLOR(0xF000); /* File is executable ... */ + return COLOR(mode); +} + +static char bgcolor(mode_t mode) +{ + if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH))) + return ATTR(0xF000); /* File is executable ... */ + return ATTR(mode); +} +#endif + +#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR +static char append_char(mode_t mode) +{ + if (!(all_fmt & LIST_FILETYPE)) + return '\0'; + if (S_ISDIR(mode)) + return '/'; + if (!(all_fmt & LIST_EXEC)) + return '\0'; + if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH))) + return '*'; + return APPCHAR(mode); +} +#endif + +#define countdirs(A, B) count_dirs((A), (B), 1) +#define countsubdirs(A, B) count_dirs((A), (B), 0) +static int count_dirs(struct dnode **dn, int nfiles, int notsubdirs) +{ + int i, dirs; + + if (!dn) + return 0; + dirs = 0; + for (i = 0; i < nfiles; i++) { + const char *name; + if (!S_ISDIR(dn[i]->dstat.st_mode)) + continue; + name = dn[i]->name; + if (notsubdirs + || name[0]!='.' || (name[1] && (name[1]!='.' || name[2])) + ) { + dirs++; + } + } + return dirs; +} + +static int countfiles(struct dnode **dnp) +{ + int nfiles; + struct dnode *cur; + + if (dnp == NULL) + return 0; + nfiles = 0; + for (cur = dnp[0]; cur->next; cur = cur->next) + nfiles++; + nfiles++; + return nfiles; +} + +/* get memory to hold an array of pointers */ +static struct dnode **dnalloc(int num) +{ + if (num < 1) + return NULL; + + return xzalloc(num * sizeof(struct dnode *)); +} + +#if ENABLE_FEATURE_LS_RECURSIVE +static void dfree(struct dnode **dnp, int nfiles) +{ + int i; + + if (dnp == NULL) + return; + + for (i = 0; i < nfiles; i++) { + struct dnode *cur = dnp[i]; + if (cur->allocated) + free((char*)cur->fullname); /* free the filename */ + free(cur); /* free the dnode */ + } + free(dnp); /* free the array holding the dnode pointers */ +} +#else +#define dfree(...) ((void)0) +#endif + +static struct dnode **splitdnarray(struct dnode **dn, int nfiles, int which) +{ + int dncnt, i, d; + struct dnode **dnp; + + if (dn == NULL || nfiles < 1) + return NULL; + + /* count how many dirs and regular files there are */ + if (which == SPLIT_SUBDIR) + dncnt = countsubdirs(dn, nfiles); + else { + dncnt = countdirs(dn, nfiles); /* assume we are looking for dirs */ + if (which == SPLIT_FILE) + dncnt = nfiles - dncnt; /* looking for files */ + } + + /* allocate a file array and a dir array */ + dnp = dnalloc(dncnt); + + /* copy the entrys into the file or dir array */ + for (d = i = 0; i < nfiles; i++) { + if (S_ISDIR(dn[i]->dstat.st_mode)) { + const char *name; + if (!(which & (SPLIT_DIR|SPLIT_SUBDIR))) + continue; + name = dn[i]->name; + if ((which & SPLIT_DIR) + || name[0]!='.' || (name[1] && (name[1]!='.' || name[2])) + ) { + dnp[d++] = dn[i]; + } + } else if (!(which & (SPLIT_DIR|SPLIT_SUBDIR))) { + dnp[d++] = dn[i]; + } + } + return dnp; +} + +#if ENABLE_FEATURE_LS_SORTFILES +static int sortcmp(const void *a, const void *b) +{ + struct dnode *d1 = *(struct dnode **)a; + struct dnode *d2 = *(struct dnode **)b; + unsigned sort_opts = all_fmt & SORT_MASK; + int dif; + + dif = 0; /* assume SORT_NAME */ + // TODO: use pre-initialized function pointer + // instead of branch forest + if (sort_opts == SORT_SIZE) { + dif = (int) (d2->dstat.st_size - d1->dstat.st_size); + } else if (sort_opts == SORT_ATIME) { + dif = (int) (d2->dstat.st_atime - d1->dstat.st_atime); + } else if (sort_opts == SORT_CTIME) { + dif = (int) (d2->dstat.st_ctime - d1->dstat.st_ctime); + } else if (sort_opts == SORT_MTIME) { + dif = (int) (d2->dstat.st_mtime - d1->dstat.st_mtime); + } else if (sort_opts == SORT_DIR) { + dif = S_ISDIR(d2->dstat.st_mode) - S_ISDIR(d1->dstat.st_mode); + /* } else if (sort_opts == SORT_VERSION) { */ + /* } else if (sort_opts == SORT_EXT) { */ + } + + if (dif == 0) { + /* sort by name - may be a tie_breaker for time or size cmp */ + if (ENABLE_LOCALE_SUPPORT) dif = strcoll(d1->name, d2->name); + else dif = strcmp(d1->name, d2->name); + } + + if (all_fmt & SORT_REVERSE) { + dif = -dif; + } + return dif; +} + +static void dnsort(struct dnode **dn, int size) +{ + qsort(dn, size, sizeof(*dn), sortcmp); +} +#else +#define dnsort(dn, size) ((void)0) +#endif + + +static void showfiles(struct dnode **dn, int nfiles) +{ + int i, ncols, nrows, row, nc; + int column = 0; + int nexttab = 0; + int column_width = 0; /* for STYLE_LONG and STYLE_SINGLE not used */ + + if (dn == NULL || nfiles < 1) + return; + + if (all_fmt & STYLE_LONG) { + ncols = 1; + } else { + /* find the longest file name, use that as the column width */ + for (i = 0; i < nfiles; i++) { + int len = mbstrlen(dn[i]->name); + if (column_width < len) + column_width = len; + } + column_width += tabstops + + USE_SELINUX( ((all_fmt & LIST_CONTEXT) ? 33 : 0) + ) + ((all_fmt & LIST_INO) ? 8 : 0) + + ((all_fmt & LIST_BLOCKS) ? 5 : 0); + ncols = (int) (terminal_width / column_width); + } + + if (ncols > 1) { + nrows = nfiles / ncols; + if (nrows * ncols < nfiles) + nrows++; /* round up fractionals */ + } else { + nrows = nfiles; + ncols = 1; + } + + for (row = 0; row < nrows; row++) { + for (nc = 0; nc < ncols; nc++) { + /* reach into the array based on the column and row */ + i = (nc * nrows) + row; /* assume display by column */ + if (all_fmt & DISP_ROWS) + i = (row * ncols) + nc; /* display across row */ + if (i < nfiles) { + if (column > 0) { + nexttab -= column; + printf("%*s", nexttab, ""); + column += nexttab; + } + nexttab = column + column_width; + column += list_single(dn[i]); + } + } + putchar('\n'); + column = 0; + } +} + + +static void showdirs(struct dnode **dn, int ndirs, int first) +{ + int i, nfiles; + struct dnode **subdnp; + int dndirs; + struct dnode **dnd; + + if (dn == NULL || ndirs < 1) + return; + + for (i = 0; i < ndirs; i++) { + if (all_fmt & (DISP_DIRNAME | DISP_RECURSIVE)) { + if (!first) + bb_putchar('\n'); + first = 0; + printf("%s:\n", dn[i]->fullname); + } + subdnp = list_dir(dn[i]->fullname); + nfiles = countfiles(subdnp); + if (nfiles > 0) { + /* list all files at this level */ + dnsort(subdnp, nfiles); + showfiles(subdnp, nfiles); + if (ENABLE_FEATURE_LS_RECURSIVE) { + if (all_fmt & DISP_RECURSIVE) { + /* recursive- list the sub-dirs */ + dnd = splitdnarray(subdnp, nfiles, SPLIT_SUBDIR); + dndirs = countsubdirs(subdnp, nfiles); + if (dndirs > 0) { + dnsort(dnd, dndirs); + showdirs(dnd, dndirs, 0); + /* free the array of dnode pointers to the dirs */ + free(dnd); + } + } + /* free the dnodes and the fullname mem */ + dfree(subdnp, nfiles); + } + } + } +} + + +static struct dnode **list_dir(const char *path) +{ + struct dnode *dn, *cur, **dnp; + struct dirent *entry; + DIR *dir; + int i, nfiles; + + if (path == NULL) + return NULL; + + dn = NULL; + nfiles = 0; + dir = warn_opendir(path); + if (dir == NULL) { + exit_code = EXIT_FAILURE; + return NULL; /* could not open the dir */ + } + while ((entry = readdir(dir)) != NULL) { + char *fullname; + + /* are we going to list the file- it may be . or .. or a hidden file */ + if (entry->d_name[0] == '.') { + if ((!entry->d_name[1] || (entry->d_name[1] == '.' && !entry->d_name[2])) + && !(all_fmt & DISP_DOT) + ) { + continue; + } + if (!(all_fmt & DISP_HIDDEN)) + continue; + } + fullname = concat_path_file(path, entry->d_name); + cur = my_stat(fullname, bb_basename(fullname), 0); + if (!cur) { + free(fullname); + continue; + } + cur->allocated = 1; + cur->next = dn; + dn = cur; + nfiles++; + } + closedir(dir); + + /* now that we know how many files there are + * allocate memory for an array to hold dnode pointers + */ + if (dn == NULL) + return NULL; + dnp = dnalloc(nfiles); + for (i = 0, cur = dn; i < nfiles; i++) { + dnp[i] = cur; /* save pointer to node in array */ + cur = cur->next; + } + + return dnp; +} + + +static int list_single(const struct dnode *dn) +{ + int i, column = 0; + +#if ENABLE_FEATURE_LS_TIMESTAMPS + char *filetime; + time_t ttime, age; +#endif +#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR + struct stat info; + char append; +#endif + + if (dn->fullname == NULL) + return 0; + +#if ENABLE_FEATURE_LS_TIMESTAMPS + ttime = dn->dstat.st_mtime; /* the default time */ + if (all_fmt & TIME_ACCESS) + ttime = dn->dstat.st_atime; + if (all_fmt & TIME_CHANGE) + ttime = dn->dstat.st_ctime; + filetime = ctime(&ttime); +#endif +#if ENABLE_FEATURE_LS_FILETYPES + append = append_char(dn->dstat.st_mode); +#endif + + for (i = 0; i <= 31; i++) { + switch (all_fmt & (1 << i)) { + case LIST_INO: + column += printf("%7ld ", (long) dn->dstat.st_ino); + break; + case LIST_BLOCKS: + column += printf("%4"OFF_FMT"d ", (off_t) dn->dstat.st_blocks >> 1); + break; + case LIST_MODEBITS: + column += printf("%-10s ", (char *) bb_mode_string(dn->dstat.st_mode)); + break; + case LIST_NLINKS: + column += printf("%4ld ", (long) dn->dstat.st_nlink); + break; + case LIST_ID_NAME: +#if ENABLE_FEATURE_LS_USERNAME + printf("%-8.8s %-8.8s", + get_cached_username(dn->dstat.st_uid), + get_cached_groupname(dn->dstat.st_gid)); + column += 17; + break; +#endif + case LIST_ID_NUMERIC: + column += printf("%-8d %-8d", dn->dstat.st_uid, dn->dstat.st_gid); + break; + case LIST_SIZE: + case LIST_DEV: + if (S_ISBLK(dn->dstat.st_mode) || S_ISCHR(dn->dstat.st_mode)) { + column += printf("%4d, %3d ", (int) major(dn->dstat.st_rdev), + (int) minor(dn->dstat.st_rdev)); + } else { + if (all_fmt & LS_DISP_HR) { + column += printf("%9s ", + make_human_readable_str(dn->dstat.st_size, 1, 0)); + } else { + column += printf("%9"OFF_FMT"d ", (off_t) dn->dstat.st_size); + } + } + break; +#if ENABLE_FEATURE_LS_TIMESTAMPS + case LIST_FULLTIME: + printf("%24.24s ", filetime); + column += 25; + break; + case LIST_DATE_TIME: + if ((all_fmt & LIST_FULLTIME) == 0) { + /* current_time_t ~== time(NULL) */ + age = current_time_t - ttime; + printf("%6.6s ", filetime + 4); + if (age < 3600L * 24 * 365 / 2 && age > -15 * 60) { + /* hh:mm if less than 6 months old */ + printf("%5.5s ", filetime + 11); + } else { + printf(" %4.4s ", filetime + 20); + } + column += 13; + } + break; +#endif +#if ENABLE_SELINUX + case LIST_CONTEXT: + { + char context[80]; + int len = 0; + + if (dn->sid) { + /* I assume sid initilized with NULL */ + len = strlen(dn->sid) + 1; + safe_strncpy(context, dn->sid, len); + freecon(dn->sid); + } else { + safe_strncpy(context, "unknown", 8); + } + printf("%-32s ", context); + column += MAX(33, len); + } + break; +#endif + case LIST_FILENAME: + errno = 0; +#if ENABLE_FEATURE_LS_COLOR + if (show_color && !lstat(dn->fullname, &info)) { + printf("\033[%d;%dm", bgcolor(info.st_mode), + fgcolor(info.st_mode)); + } +#endif +#if ENABLE_FEATURE_ASSUME_UNICODE + printf("%s", dn->name); + column += mbstrlen(dn->name); +#else + column += printf("%s", dn->name); +#endif + if (show_color) { + printf("\033[0m"); + } + break; + case LIST_SYMLINK: + if (S_ISLNK(dn->dstat.st_mode)) { + char *lpath = xmalloc_readlink_or_warn(dn->fullname); + if (!lpath) break; + printf(" -> "); +#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR + if (!stat(dn->fullname, &info)) { + append = append_char(info.st_mode); + } +#endif +#if ENABLE_FEATURE_LS_COLOR + if (show_color) { + errno = 0; + printf("\033[%d;%dm", bgcolor(info.st_mode), + fgcolor(info.st_mode)); + } +#endif + column += printf("%s", lpath) + 4; + if (show_color) { + printf("\033[0m"); + } + free(lpath); + } + break; +#if ENABLE_FEATURE_LS_FILETYPES + case LIST_FILETYPE: + if (append) { + putchar(append); + column++; + } + break; +#endif + } + } + + return column; +} + + +/* "[-]Cadil1", POSIX mandated options, busybox always supports */ +/* "[-]gnsx", POSIX non-mandated options, busybox always supports */ +/* "[-]Ak" GNU options, busybox always supports */ +/* "[-]FLRctur", POSIX mandated options, busybox optionally supports */ +/* "[-]p", POSIX non-mandated options, busybox optionally supports */ +/* "[-]SXvThw", GNU options, busybox optionally supports */ +/* "[-]K", SELinux mandated options, busybox optionally supports */ +/* "[-]e", I think we made this one up */ +static const char ls_options[] ALIGN1 = + "Cadil1gnsxAk" + USE_FEATURE_LS_TIMESTAMPS("cetu") + USE_FEATURE_LS_SORTFILES("SXrv") + USE_FEATURE_LS_FILETYPES("Fp") + USE_FEATURE_LS_FOLLOWLINKS("L") + USE_FEATURE_LS_RECURSIVE("R") + USE_FEATURE_HUMAN_READABLE("h") + USE_SELINUX("K") + USE_FEATURE_AUTOWIDTH("T:w:") + USE_SELINUX("Z"); + +enum { + LIST_MASK_TRIGGER = 0, + STYLE_MASK_TRIGGER = STYLE_MASK, + DISP_MASK_TRIGGER = DISP_ROWS, + SORT_MASK_TRIGGER = SORT_MASK, +}; + +static const unsigned opt_flags[] = { + LIST_SHORT | STYLE_COLUMNS, /* C */ + DISP_HIDDEN | DISP_DOT, /* a */ + DISP_NOLIST, /* d */ + LIST_INO, /* i */ + LIST_LONG | STYLE_LONG, /* l - remember LS_DISP_HR in mask! */ + LIST_SHORT | STYLE_SINGLE, /* 1 */ + 0, /* g - ingored */ + LIST_ID_NUMERIC, /* n */ + LIST_BLOCKS, /* s */ + DISP_ROWS, /* x */ + DISP_HIDDEN, /* A */ + ENABLE_SELINUX * LIST_CONTEXT, /* k (ignored if !SELINUX) */ +#if ENABLE_FEATURE_LS_TIMESTAMPS + TIME_CHANGE | (ENABLE_FEATURE_LS_SORTFILES * SORT_CTIME), /* c */ + LIST_FULLTIME, /* e */ + ENABLE_FEATURE_LS_SORTFILES * SORT_MTIME, /* t */ + TIME_ACCESS | (ENABLE_FEATURE_LS_SORTFILES * SORT_ATIME), /* u */ +#endif +#if ENABLE_FEATURE_LS_SORTFILES + SORT_SIZE, /* S */ + SORT_EXT, /* X */ + SORT_REVERSE, /* r */ + SORT_VERSION, /* v */ +#endif +#if ENABLE_FEATURE_LS_FILETYPES + LIST_FILETYPE | LIST_EXEC, /* F */ + LIST_FILETYPE, /* p */ +#endif +#if ENABLE_FEATURE_LS_FOLLOWLINKS + FOLLOW_LINKS, /* L */ +#endif +#if ENABLE_FEATURE_LS_RECURSIVE + DISP_RECURSIVE, /* R */ +#endif +#if ENABLE_FEATURE_HUMAN_READABLE + LS_DISP_HR, /* h */ +#endif +#if ENABLE_SELINUX + LIST_MODEBITS|LIST_NLINKS|LIST_CONTEXT|LIST_SIZE|LIST_DATE_TIME, /* K */ +#endif +#if ENABLE_FEATURE_AUTOWIDTH + 0, 0, /* T, w - ignored */ +#endif +#if ENABLE_SELINUX + LIST_MODEBITS|LIST_ID_NAME|LIST_CONTEXT, /* Z */ +#endif + (1U<<31) +}; + + +/* colored LS support by JaWi, janwillem.janssen@lxtreme.nl */ +#if ENABLE_FEATURE_LS_COLOR +/* long option entry used only for --color, which has no short option + * equivalent */ +static const char ls_color_opt[] ALIGN1 = + "color\0" Optional_argument "\xff" /* no short equivalent */ + ; +#endif + + +int ls_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int ls_main(int argc UNUSED_PARAM, char **argv) +{ + struct dnode **dnd; + struct dnode **dnf; + struct dnode **dnp; + struct dnode *dn; + struct dnode *cur; + unsigned opt; + int nfiles; + int dnfiles; + int dndirs; + int i; + /* need to initialize since --color has _an optional_ argument */ + USE_FEATURE_LS_COLOR(const char *color_opt = "always";) + + INIT_G(); + + all_fmt = LIST_SHORT | + (ENABLE_FEATURE_LS_SORTFILES * (SORT_NAME | SORT_FORWARD)); + +#if ENABLE_FEATURE_AUTOWIDTH + /* Obtain the terminal width */ + get_terminal_width_height(STDIN_FILENO, &terminal_width, NULL); + /* Go one less... */ + terminal_width--; +#endif + + /* process options */ + USE_FEATURE_LS_COLOR(applet_long_options = ls_color_opt;) +#if ENABLE_FEATURE_AUTOWIDTH + opt_complementary = "T+:w+"; /* -T N, -w N */ + opt = getopt32(argv, ls_options, &tabstops, &terminal_width + USE_FEATURE_LS_COLOR(, &color_opt)); +#else + opt = getopt32(argv, ls_options USE_FEATURE_LS_COLOR(, &color_opt)); +#endif + for (i = 0; opt_flags[i] != (1U<<31); i++) { + if (opt & (1 << i)) { + unsigned flags = opt_flags[i]; + + if (flags & LIST_MASK_TRIGGER) + all_fmt &= ~LIST_MASK; + if (flags & STYLE_MASK_TRIGGER) + all_fmt &= ~STYLE_MASK; + if (flags & SORT_MASK_TRIGGER) + all_fmt &= ~SORT_MASK; + if (flags & DISP_MASK_TRIGGER) + all_fmt &= ~DISP_MASK; + if (flags & TIME_MASK) + all_fmt &= ~TIME_MASK; + if (flags & LIST_CONTEXT) + all_fmt |= STYLE_SINGLE; + /* huh?? opt cannot be 'l' */ + //if (LS_DISP_HR && opt == 'l') + // all_fmt &= ~LS_DISP_HR; + all_fmt |= flags; + } + } + +#if ENABLE_FEATURE_LS_COLOR + /* find color bit value - last position for short getopt */ + if (ENABLE_FEATURE_LS_COLOR_IS_DEFAULT && isatty(STDOUT_FILENO)) { + char *p = getenv("LS_COLORS"); + /* LS_COLORS is unset, or (not empty && not "none") ? */ + if (!p || (p[0] && strcmp(p, "none") != 0)) + show_color = 1; + } + if (opt & (1 << i)) { /* next flag after short options */ + if (strcmp("always", color_opt) == 0) + show_color = 1; + else if (strcmp("never", color_opt) == 0) + show_color = 0; + else if (strcmp("auto", color_opt) == 0 && isatty(STDOUT_FILENO)) + show_color = 1; + } +#endif + + /* sort out which command line options take precedence */ + if (ENABLE_FEATURE_LS_RECURSIVE && (all_fmt & DISP_NOLIST)) + all_fmt &= ~DISP_RECURSIVE; /* no recurse if listing only dir */ + if (ENABLE_FEATURE_LS_TIMESTAMPS && ENABLE_FEATURE_LS_SORTFILES) { + if (all_fmt & TIME_CHANGE) + all_fmt = (all_fmt & ~SORT_MASK) | SORT_CTIME; + if (all_fmt & TIME_ACCESS) + all_fmt = (all_fmt & ~SORT_MASK) | SORT_ATIME; + } + if ((all_fmt & STYLE_MASK) != STYLE_LONG) /* only for long list */ + all_fmt &= ~(LIST_ID_NUMERIC|LIST_FULLTIME|LIST_ID_NAME|LIST_ID_NUMERIC); + if (ENABLE_FEATURE_LS_USERNAME) + if ((all_fmt & STYLE_MASK) == STYLE_LONG && (all_fmt & LIST_ID_NUMERIC)) + all_fmt &= ~LIST_ID_NAME; /* don't list names if numeric uid */ + + /* choose a display format */ + if (!(all_fmt & STYLE_MASK)) + all_fmt |= (isatty(STDOUT_FILENO) ? STYLE_COLUMNS : STYLE_SINGLE); + + argv += optind; + if (!argv[0]) + *--argv = (char*)"."; + + if (argv[1]) + all_fmt |= DISP_DIRNAME; /* 2 or more items? label directories */ + + /* stuff the command line file names into a dnode array */ + dn = NULL; + nfiles = 0; + do { + /* ls w/o -l follows links on command line */ + cur = my_stat(*argv, *argv, !(all_fmt & STYLE_LONG)); + argv++; + if (!cur) + continue; + cur->allocated = 0; + cur->next = dn; + dn = cur; + nfiles++; + } while (*argv); + + /* now that we know how many files there are + * allocate memory for an array to hold dnode pointers + */ + dnp = dnalloc(nfiles); + for (i = 0, cur = dn; i < nfiles; i++) { + dnp[i] = cur; /* save pointer to node in array */ + cur = cur->next; + } + + if (all_fmt & DISP_NOLIST) { + dnsort(dnp, nfiles); + if (nfiles > 0) + showfiles(dnp, nfiles); + } else { + dnd = splitdnarray(dnp, nfiles, SPLIT_DIR); + dnf = splitdnarray(dnp, nfiles, SPLIT_FILE); + dndirs = countdirs(dnp, nfiles); + dnfiles = nfiles - dndirs; + if (dnfiles > 0) { + dnsort(dnf, dnfiles); + showfiles(dnf, dnfiles); + if (ENABLE_FEATURE_CLEAN_UP) + free(dnf); + } + if (dndirs > 0) { + dnsort(dnd, dndirs); + showdirs(dnd, dndirs, dnfiles == 0); + if (ENABLE_FEATURE_CLEAN_UP) + free(dnd); + } + } + if (ENABLE_FEATURE_CLEAN_UP) + dfree(dnp, nfiles); + return exit_code; +} diff --git a/coreutils/md5_sha1_sum.c b/coreutils/md5_sha1_sum.c new file mode 100644 index 0000000..a568158 --- /dev/null +++ b/coreutils/md5_sha1_sum.c @@ -0,0 +1,175 @@ +/* vi: set sw=4 ts=4: */ +/* + * Copyright (C) 2003 Glenn L. McGrath + * Copyright (C) 2003-2004 Erik Andersen + * + * Licensed under the GPL v2 or later, see the file LICENSE in this tarball. + */ + +#include "libbb.h" + +typedef enum { HASH_SHA1, HASH_MD5 } hash_algo_t; + +#define FLAG_SILENT 1 +#define FLAG_CHECK 2 +#define FLAG_WARN 4 + +/* This might be useful elsewhere */ +static unsigned char *hash_bin_to_hex(unsigned char *hash_value, + unsigned hash_length) +{ + /* xzalloc zero-terminates */ + char *hex_value = xzalloc((hash_length * 2) + 1); + bin2hex(hex_value, (char*)hash_value, hash_length); + return (unsigned char *)hex_value; +} + +static uint8_t *hash_file(const char *filename, hash_algo_t hash_algo) +{ + int src_fd, hash_len, count; + union _ctx_ { + sha1_ctx_t sha1; + md5_ctx_t md5; + } context; + uint8_t *hash_value = NULL; + RESERVE_CONFIG_UBUFFER(in_buf, 4096); + void FAST_FUNC (*update)(const void*, size_t, void*); + void FAST_FUNC (*final)(void*, void*); + + src_fd = open_or_warn_stdin(filename); + if (src_fd < 0) { + return NULL; + } + + /* figure specific hash algorithims */ + if (ENABLE_MD5SUM && hash_algo==HASH_MD5) { + md5_begin(&context.md5); + update = (void*)md5_hash; + final = (void*)md5_end; + hash_len = 16; + } else if (ENABLE_SHA1SUM && hash_algo==HASH_SHA1) { + sha1_begin(&context.sha1); + update = (void*)sha1_hash; + final = (void*)sha1_end; + hash_len = 20; + } else { + bb_error_msg_and_die("algorithm not supported"); + } + + while (0 < (count = safe_read(src_fd, in_buf, 4096))) { + update(in_buf, count, &context); + } + + if (count == 0) { + final(in_buf, &context); + hash_value = hash_bin_to_hex(in_buf, hash_len); + } + + RELEASE_CONFIG_BUFFER(in_buf); + + if (src_fd != STDIN_FILENO) { + close(src_fd); + } + + return hash_value; +} + +int md5_sha1_sum_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int md5_sha1_sum_main(int argc UNUSED_PARAM, char **argv) +{ + int return_value = EXIT_SUCCESS; + uint8_t *hash_value; + unsigned flags; + hash_algo_t hash_algo = ENABLE_MD5SUM + ? (ENABLE_SHA1SUM ? (applet_name[0] == 'm' ? HASH_MD5 : HASH_SHA1) : HASH_MD5) + : HASH_SHA1; + + if (ENABLE_FEATURE_MD5_SHA1_SUM_CHECK) + flags = getopt32(argv, "scw"); + else optind = 1; + argv += optind; + //argc -= optind; + if (!*argv) + *--argv = (char*)"-"; + + if (ENABLE_FEATURE_MD5_SHA1_SUM_CHECK && !(flags & FLAG_CHECK)) { + if (flags & FLAG_SILENT) { + bb_error_msg_and_die + ("-%c is meaningful only when verifying checksums", 's'); + } else if (flags & FLAG_WARN) { + bb_error_msg_and_die + ("-%c is meaningful only when verifying checksums", 'w'); + } + } + + if (ENABLE_FEATURE_MD5_SHA1_SUM_CHECK && (flags & FLAG_CHECK)) { + FILE *pre_computed_stream; + int count_total = 0; + int count_failed = 0; + char *line; + + if (argv[1]) { + bb_error_msg_and_die + ("only one argument may be specified when using -c"); + } + + pre_computed_stream = xfopen_stdin(argv[0]); + + while ((line = xmalloc_fgetline(pre_computed_stream)) != NULL) { + char *filename_ptr; + + count_total++; + filename_ptr = strstr(line, " "); + /* handle format for binary checksums */ + if (filename_ptr == NULL) { + filename_ptr = strstr(line, " *"); + } + if (filename_ptr == NULL) { + if (flags & FLAG_WARN) { + bb_error_msg("invalid format"); + } + count_failed++; + return_value = EXIT_FAILURE; + free(line); + continue; + } + *filename_ptr = '\0'; + filename_ptr += 2; + + hash_value = hash_file(filename_ptr, hash_algo); + + if (hash_value && (strcmp((char*)hash_value, line) == 0)) { + if (!(flags & FLAG_SILENT)) + printf("%s: OK\n", filename_ptr); + } else { + if (!(flags & FLAG_SILENT)) + printf("%s: FAILED\n", filename_ptr); + count_failed++; + return_value = EXIT_FAILURE; + } + /* possible free(NULL) */ + free(hash_value); + free(line); + } + if (count_failed && !(flags & FLAG_SILENT)) { + bb_error_msg("WARNING: %d of %d computed checksums did NOT match", + count_failed, count_total); + } + /* + if (fclose_if_not_stdin(pre_computed_stream) == EOF) { + bb_perror_msg_and_die("cannot close file %s", file_ptr); + } + */ + } else { + do { + hash_value = hash_file(*argv, hash_algo); + if (hash_value == NULL) { + return_value = EXIT_FAILURE; + } else { + printf("%s %s\n", hash_value, *argv); + free(hash_value); + } + } while (*++argv); + } + return return_value; +} diff --git a/coreutils/mkdir.c b/coreutils/mkdir.c new file mode 100644 index 0000000..72bd105 --- /dev/null +++ b/coreutils/mkdir.c @@ -0,0 +1,80 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini mkdir implementation for busybox + * + * Copyright (C) 2001 Matt Kraai + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +/* BB_AUDIT SUSv3 compliant */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/mkdir.html */ + +/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org) + * + * Fixed broken permission setting when -p was used; especially in + * conjunction with -m. + */ + +/* Nov 28, 2006 Yoshinori Sato : Add SELinux Support. + */ + +#include "libbb.h" + +/* This is a NOFORK applet. Be very careful! */ + +#if ENABLE_FEATURE_MKDIR_LONG_OPTIONS +static const char mkdir_longopts[] ALIGN1 = + "mode\0" Required_argument "m" + "parents\0" No_argument "p" +#if ENABLE_SELINUX + "context\0" Required_argument "Z" +#endif + ; +#endif + +int mkdir_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int mkdir_main(int argc, char **argv) +{ + mode_t mode = (mode_t)(-1); + int status = EXIT_SUCCESS; + int flags = 0; + unsigned opt; + char *smode; +#if ENABLE_SELINUX + security_context_t scontext; +#endif + +#if ENABLE_FEATURE_MKDIR_LONG_OPTIONS + applet_long_options = mkdir_longopts; +#endif + opt = getopt32(argv, "m:p" USE_SELINUX("Z:"), &smode USE_SELINUX(,&scontext)); + if (opt & 1) { + mode = 0777; + if (!bb_parse_mode(smode, &mode)) { + bb_error_msg_and_die("invalid mode '%s'", smode); + } + } + if (opt & 2) + flags |= FILEUTILS_RECUR; +#if ENABLE_SELINUX + if (opt & 4) { + selinux_or_die(); + setfscreatecon_or_die(scontext); + } +#endif + + if (optind == argc) { + bb_show_usage(); + } + + argv += optind; + + do { + if (bb_make_directory(*argv, mode, flags)) { + status = EXIT_FAILURE; + } + } while (*++argv); + + return status; +} diff --git a/coreutils/mkfifo.c b/coreutils/mkfifo.c new file mode 100644 index 0000000..6549460 --- /dev/null +++ b/coreutils/mkfifo.c @@ -0,0 +1,37 @@ +/* vi: set sw=4 ts=4: */ +/* + * mkfifo implementation for busybox + * + * Copyright (C) 2003 Manuel Novoa III + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +/* BB_AUDIT SUSv3 compliant */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/mkfifo.html */ + +#include "libbb.h" +#include "libcoreutils/coreutils.h" + +int mkfifo_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int mkfifo_main(int argc UNUSED_PARAM, char **argv) +{ + mode_t mode; + int retval = EXIT_SUCCESS; + + mode = getopt_mk_fifo_nod(argv); + + argv += optind; + if (!*argv) { + bb_show_usage(); + } + + do { + if (mkfifo(*argv, mode) < 0) { + bb_simple_perror_msg(*argv); /* Avoid multibyte problems. */ + retval = EXIT_FAILURE; + } + } while (*++argv); + + return retval; +} diff --git a/coreutils/mknod.c b/coreutils/mknod.c new file mode 100644 index 0000000..0c69494 --- /dev/null +++ b/coreutils/mknod.c @@ -0,0 +1,57 @@ +/* vi: set sw=4 ts=4: */ +/* + * mknod implementation for busybox + * + * Copyright (C) 2003 Manuel Novoa III + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +/* BB_AUDIT SUSv3 N/A -- Matches GNU behavior. */ + +#include // For makedev + +#include "libbb.h" +#include "libcoreutils/coreutils.h" + +static const char modes_chars[] ALIGN1 = { 'p', 'c', 'u', 'b', 0, 1, 1, 2 }; +static const mode_t modes_cubp[] = { S_IFIFO, S_IFCHR, S_IFBLK }; + +int mknod_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int mknod_main(int argc, char **argv) +{ + mode_t mode; + dev_t dev; + const char *name; + + mode = getopt_mk_fifo_nod(argv); + argv += optind; + argc -= optind; + + if (argc >= 2) { + name = strchr(modes_chars, argv[1][0]); + if (name != NULL) { + mode |= modes_cubp[(int)(name[4])]; + + dev = 0; + if (*name != 'p') { + argc -= 2; + if (argc == 2) { + /* Autodetect what the system supports; these macros should + * optimize out to two constants. */ + dev = makedev(xatoul_range(argv[2], 0, major(UINT_MAX)), + xatoul_range(argv[3], 0, minor(UINT_MAX))); + } + } + + if (argc == 2) { + name = *argv; + if (mknod(name, mode, dev) == 0) { + return EXIT_SUCCESS; + } + bb_simple_perror_msg_and_die(name); + } + } + } + bb_show_usage(); +} diff --git a/coreutils/mv.c b/coreutils/mv.c new file mode 100644 index 0000000..be10b03 --- /dev/null +++ b/coreutils/mv.c @@ -0,0 +1,135 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini mv implementation for busybox + * + * Copyright (C) 2000 by Matt Kraai + * SELinux support by Yuichi Nakamura + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org) + * + * Size reduction and improved error checking. + */ + +#include "libbb.h" +#include "libcoreutils/coreutils.h" + +#if ENABLE_FEATURE_MV_LONG_OPTIONS +static const char mv_longopts[] ALIGN1 = + "interactive\0" No_argument "i" + "force\0" No_argument "f" + ; +#endif + +#define OPT_FILEUTILS_FORCE 1 +#define OPT_FILEUTILS_INTERACTIVE 2 + +int mv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int mv_main(int argc, char **argv) +{ + struct stat dest_stat; + const char *last; + const char *dest; + unsigned flags; + int dest_exists; + int status = 0; + int copy_flag = 0; + +#if ENABLE_FEATURE_MV_LONG_OPTIONS + applet_long_options = mv_longopts; +#endif + // Need at least two arguments + // -f unsets -i, -i unsets -f + opt_complementary = "-2:f-i:i-f"; + flags = getopt32(argv, "fi"); + argc -= optind; + argv += optind; + last = argv[argc - 1]; + + if (argc == 2) { + dest_exists = cp_mv_stat(last, &dest_stat); + if (dest_exists < 0) { + return EXIT_FAILURE; + } + + if (!(dest_exists & 2)) { /* last is not a directory */ + dest = last; + goto DO_MOVE; + } + } + + do { + dest = concat_path_file(last, bb_get_last_path_component_strip(*argv)); + dest_exists = cp_mv_stat(dest, &dest_stat); + if (dest_exists < 0) { + goto RET_1; + } + + DO_MOVE: + if (dest_exists + && !(flags & OPT_FILEUTILS_FORCE) + && ((access(dest, W_OK) < 0 && isatty(0)) + || (flags & OPT_FILEUTILS_INTERACTIVE)) + ) { + if (fprintf(stderr, "mv: overwrite '%s'? ", dest) < 0) { + goto RET_1; /* Ouch! fprintf failed! */ + } + if (!bb_ask_confirmation()) { + goto RET_0; + } + } + if (rename(*argv, dest) < 0) { + struct stat source_stat; + int source_exists; + + if (errno != EXDEV + || (source_exists = cp_mv_stat2(*argv, &source_stat, lstat)) < 1 + ) { + bb_perror_msg("cannot rename '%s'", *argv); + } else { + static const char fmt[] ALIGN1 = + "cannot overwrite %sdirectory with %sdirectory"; + + if (dest_exists) { + if (dest_exists == 3) { + if (source_exists != 3) { + bb_error_msg(fmt, "", "non-"); + goto RET_1; + } + } else { + if (source_exists == 3) { + bb_error_msg(fmt, "non-", ""); + goto RET_1; + } + } + if (unlink(dest) < 0) { + bb_perror_msg("cannot remove '%s'", dest); + goto RET_1; + } + } + /* FILEUTILS_RECUR also prevents nasties like + * "read from device and write contents to dst" + * instead of "create same device node" */ + copy_flag = FILEUTILS_RECUR | FILEUTILS_PRESERVE_STATUS; +#if ENABLE_SELINUX + copy_flag |= FILEUTILS_PRESERVE_SECURITY_CONTEXT; +#endif + if ((copy_file(*argv, dest, copy_flag) >= 0) + && (remove_file(*argv, FILEUTILS_RECUR | FILEUTILS_FORCE) >= 0) + ) { + goto RET_0; + } + } + RET_1: + status = 1; + } + RET_0: + if (dest != last) { + free((void *) dest); + } + } while (*++argv != last); + + return status; +} diff --git a/coreutils/nice.c b/coreutils/nice.c new file mode 100644 index 0000000..d24a95b --- /dev/null +++ b/coreutils/nice.c @@ -0,0 +1,55 @@ +/* vi: set sw=4 ts=4: */ +/* + * nice implementation for busybox + * + * Copyright (C) 2005 Manuel Novoa III + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +#include +#include "libbb.h" + +int nice_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int nice_main(int argc, char **argv) +{ + int old_priority, adjustment; + + old_priority = getpriority(PRIO_PROCESS, 0); + + if (!*++argv) { /* No args, so (GNU) output current nice value. */ + printf("%d\n", old_priority); + fflush_stdout_and_exit(EXIT_SUCCESS); + } + + adjustment = 10; /* Set default adjustment. */ + + if (argv[0][0] == '-') { + if (argv[0][1] == 'n') { /* -n */ + if (argv[0][2]) { /* -nNNNN (w/o space) */ + argv[0] += 2; argv--; argc++; + } + } else { /* -NNN (NNN may be negative) == -n NNN */ + argv[0] += 1; argv--; argc++; + } + if (argc < 4) { /* Missing priority and/or utility! */ + bb_show_usage(); + } + adjustment = xatoi_range(argv[1], INT_MIN/2, INT_MAX/2); + argv += 2; + } + + { /* Set our priority. */ + int prio = old_priority + adjustment; + + if (setpriority(PRIO_PROCESS, 0, prio) < 0) { + bb_perror_msg_and_die("setpriority(%d)", prio); + } + } + + BB_EXECVP(*argv, argv); /* Now exec the desired program. */ + + /* The exec failed... */ + xfunc_error_retval = (errno == ENOENT) ? 127 : 126; /* SUSv3 */ + bb_simple_perror_msg_and_die(*argv); +} diff --git a/coreutils/nohup.c b/coreutils/nohup.c new file mode 100644 index 0000000..f44e2af --- /dev/null +++ b/coreutils/nohup.c @@ -0,0 +1,78 @@ +/* vi: set sw=4 ts=4: */ +/* nohup - invoke a utility immune to hangups. + * + * Busybox version based on nohup specification at + * http://www.opengroup.org/onlinepubs/007904975/utilities/nohup.html + * + * Copyright 2006 Rob Landley + * Copyright 2006 Bernhard Reutner-Fischer + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +#include "libbb.h" + +/* Compat info: nohup (GNU coreutils 6.8) does this: +# nohup true +nohup: ignoring input and appending output to `nohup.out' +# nohup true 1>/dev/null +nohup: ignoring input and redirecting stderr to stdout +# nohup true 2>zz +# cat zz +nohup: ignoring input and appending output to `nohup.out' +# nohup true 2>zz 1>/dev/null +# cat zz +nohup: ignoring input +# nohup true /dev/null +nohup: redirecting stderr to stdout +# nohup true zz 1>/dev/null +# cat zz + (nothing) +# +*/ + +int nohup_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int nohup_main(int argc, char **argv) +{ + const char *nohupout; + char *home; + + xfunc_error_retval = 127; + + if (argc < 2) bb_show_usage(); + + /* If stdin is a tty, detach from it. */ + if (isatty(STDIN_FILENO)) { + /* bb_error_msg("ignoring input"); */ + close(STDIN_FILENO); + xopen(bb_dev_null, O_RDONLY); /* will be fd 0 (STDIN_FILENO) */ + } + + nohupout = "nohup.out"; + /* Redirect stdout to nohup.out, either in "." or in "$HOME". */ + if (isatty(STDOUT_FILENO)) { + close(STDOUT_FILENO); + if (open(nohupout, O_CREAT|O_WRONLY|O_APPEND, S_IRUSR|S_IWUSR) < 0) { + home = getenv("HOME"); + if (home) { + nohupout = concat_path_file(home, nohupout); + xopen3(nohupout, O_CREAT|O_WRONLY|O_APPEND, S_IRUSR|S_IWUSR); + } else { + xopen(bb_dev_null, O_RDONLY); /* will be fd 1 */ + } + } + bb_error_msg("appending output to %s", nohupout); + } + + /* If we have a tty on stderr, redirect to stdout. */ + if (isatty(STDERR_FILENO)) { + /* if (stdout_wasnt_a_tty) + bb_error_msg("redirecting stderr to stdout"); */ + dup2(STDOUT_FILENO, STDERR_FILENO); + } + + signal(SIGHUP, SIG_IGN); + + BB_EXECVP(argv[1], argv+1); + bb_simple_perror_msg_and_die(argv[1]); +} diff --git a/coreutils/od.c b/coreutils/od.c new file mode 100644 index 0000000..e4179a3 --- /dev/null +++ b/coreutils/od.c @@ -0,0 +1,225 @@ +/* vi: set sw=4 ts=4: */ +/* + * od implementation for busybox + * Based on code from util-linux v 2.11l + * + * Copyright (c) 1990 + * The Regents of the University of California. All rights reserved. + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + * + * Original copyright notice is retained at the end of this file. + */ + + +#include "libbb.h" +#if ENABLE_DESKTOP +/* This one provides -t (busybox's own build script needs it) */ +#include "od_bloaty.c" +#else + +#include "dump.h" + +#define isdecdigit(c) isdigit(c) +#define ishexdigit(c) (isxdigit)(c) + +static void +odoffset(dumper_t *dumper, int argc, char ***argvp) +{ + char *num, *p; + int base; + char *end; + + /* + * The offset syntax of od(1) was genuinely bizarre. First, if + * it started with a plus it had to be an offset. Otherwise, if + * there were at least two arguments, a number or lower-case 'x' + * followed by a number makes it an offset. By default it was + * octal; if it started with 'x' or '0x' it was hex. If it ended + * in a '.', it was decimal. If a 'b' or 'B' was appended, it + * multiplied the number by 512 or 1024 byte units. There was + * no way to assign a block count to a hex offset. + * + * We assumes it's a file if the offset is bad. + */ + p = **argvp; + + if (!p) { + /* hey someone is probably piping to us ... */ + return; + } + + if ((*p != '+') + && (argc < 2 + || (!isdecdigit(p[0]) + && ((p[0] != 'x') || !ishexdigit(p[1]))))) + return; + + base = 0; + /* + * skip over leading '+', 'x[0-9a-fA-f]' or '0x', and + * set base. + */ + if (p[0] == '+') + ++p; + if (p[0] == 'x' && ishexdigit(p[1])) { + ++p; + base = 16; + } else if (p[0] == '0' && p[1] == 'x') { + p += 2; + base = 16; + } + + /* skip over the number */ + if (base == 16) + for (num = p; ishexdigit(*p); ++p) + continue; + else + for (num = p; isdecdigit(*p); ++p) + continue; + + /* check for no number */ + if (num == p) + return; + + /* if terminates with a '.', base is decimal */ + if (*p == '.') { + if (base) + return; + base = 10; + } + + dumper->dump_skip = strtol(num, &end, base ? base : 8); + + /* if end isn't the same as p, we got a non-octal digit */ + if (end != p) + dumper->dump_skip = 0; + else { + if (*p) { + if (*p == 'b') { + dumper->dump_skip *= 512; + ++p; + } else if (*p == 'B') { + dumper->dump_skip *= 1024; + ++p; + } + } + if (*p) + dumper->dump_skip = 0; + else { + ++*argvp; + /* + * If the offset uses a non-octal base, the base of + * the offset is changed as well. This isn't pretty, + * but it's easy. + */ +#define TYPE_OFFSET 7 + { + char x_or_d; + if (base == 16) { + x_or_d = 'x'; + goto DO_X_OR_D; + } + if (base == 10) { + x_or_d = 'd'; + DO_X_OR_D: + dumper->fshead->nextfu->fmt[TYPE_OFFSET] + = dumper->fshead->nextfs->nextfu->fmt[TYPE_OFFSET] + = x_or_d; + } + } + } + } +} + +static const char *const add_strings[] = { + "16/1 \"%3_u \" \"\\n\"", /* a */ + "8/2 \" %06o \" \"\\n\"", /* B, o */ + "16/1 \"%03o \" \"\\n\"", /* b */ + "16/1 \"%3_c \" \"\\n\"", /* c */ + "8/2 \" %05u \" \"\\n\"", /* d */ + "4/4 \" %010u \" \"\\n\"", /* D */ + "2/8 \" %21.14e \" \"\\n\"", /* e (undocumented in od), F */ + "4/4 \" %14.7e \" \"\\n\"", /* f */ + "4/4 \" %08x \" \"\\n\"", /* H, X */ + "8/2 \" %04x \" \"\\n\"", /* h, x */ + "4/4 \" %11d \" \"\\n\"", /* I, L, l */ + "8/2 \" %6d \" \"\\n\"", /* i */ + "4/4 \" %011o \" \"\\n\"", /* O */ +}; + +static const char od_opts[] ALIGN1 = "aBbcDdeFfHhIiLlOoXxv"; + +static const char od_o2si[] ALIGN1 = { + 0, 1, 2, 3, 5, + 4, 6, 6, 7, 8, + 9, 0xa, 0xb, 0xa, 0xa, + 0xb, 1, 8, 9, +}; + +int od_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int od_main(int argc, char **argv) +{ + int ch; + int first = 1; + char *p; + dumper_t *dumper = alloc_dumper(); + + while ((ch = getopt(argc, argv, od_opts)) > 0) { + if (ch == 'v') { + dumper->dump_vflag = ALL; + } else if (((p = strchr(od_opts, ch)) != NULL) && (*p != '\0')) { + if (first) { + first = 0; + bb_dump_add(dumper, "\"%07.7_Ao\n\""); + bb_dump_add(dumper, "\"%07.7_ao \""); + } else { + bb_dump_add(dumper, "\" \""); + } + bb_dump_add(dumper, add_strings[(int)od_o2si[(p - od_opts)]]); + } else { /* P, p, s, w, or other unhandled */ + bb_show_usage(); + } + } + if (!dumper->fshead) { + bb_dump_add(dumper, "\"%07.7_Ao\n\""); + bb_dump_add(dumper, "\"%07.7_ao \" 8/2 \"%06o \" \"\\n\""); + } + + argc -= optind; + argv += optind; + + odoffset(dumper, argc, &argv); + + return bb_dump_dump(dumper, argv); +} +#endif /* ENABLE_DESKTOP */ + +/*- + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * 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. + */ diff --git a/coreutils/od_bloaty.c b/coreutils/od_bloaty.c new file mode 100644 index 0000000..eb45798 --- /dev/null +++ b/coreutils/od_bloaty.c @@ -0,0 +1,1428 @@ +/* od -- dump files in octal and other formats + Copyright (C) 92, 1995-2004 Free Software Foundation, Inc. + + 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, 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. */ + +/* Written by Jim Meyering. */ + +/* Busyboxed by Denys Vlasenko + +Based on od.c from coreutils-5.2.1 +Top bloat sources: + +00000073 t parse_old_offset +0000007b t get_lcm +00000090 r long_options +00000092 t print_named_ascii +000000bf t print_ascii +00000168 t write_block +00000366 t decode_format_string +00000a71 T od_main + +Tested for compat with coreutils 6.3 +using this script. Minor differences fixed. + +#!/bin/sh +echo STD +time /path/to/coreutils/od \ +...params... \ +>std +echo Exit code $? +echo BBOX +time ./busybox od \ +...params... \ +>bbox +echo Exit code $? +diff -u -a std bbox >bbox.diff || { echo Different!; sleep 1; } + +*/ + +#include "libbb.h" + +#define assert(a) ((void)0) + +/* Check for 0x7f is a coreutils 6.3 addition */ +#define ISPRINT(c) (((c)>=' ') && (c) != 0x7f) + +typedef long double longdouble_t; +typedef unsigned long long ulonglong_t; +typedef long long llong; + +#if ENABLE_LFS +# define xstrtooff_sfx xstrtoull_sfx +#else +# define xstrtooff_sfx xstrtoul_sfx +#endif + +/* The default number of input bytes per output line. */ +#define DEFAULT_BYTES_PER_BLOCK 16 + +/* The number of decimal digits of precision in a float. */ +#ifndef FLT_DIG +# define FLT_DIG 7 +#endif + +/* The number of decimal digits of precision in a double. */ +#ifndef DBL_DIG +# define DBL_DIG 15 +#endif + +/* The number of decimal digits of precision in a long double. */ +#ifndef LDBL_DIG +# define LDBL_DIG DBL_DIG +#endif + +enum size_spec { + NO_SIZE, + CHAR, + SHORT, + INT, + LONG, + LONG_LONG, + FLOAT_SINGLE, + FLOAT_DOUBLE, + FLOAT_LONG_DOUBLE, + N_SIZE_SPECS +}; + +enum output_format { + SIGNED_DECIMAL, + UNSIGNED_DECIMAL, + OCTAL, + HEXADECIMAL, + FLOATING_POINT, + NAMED_CHARACTER, + CHARACTER +}; + +/* Each output format specification (from '-t spec' or from + old-style options) is represented by one of these structures. */ +struct tspec { + enum output_format fmt; + enum size_spec size; + void (*print_function) (size_t, const char *, const char *); + char *fmt_string; + int hexl_mode_trailer; + int field_width; +}; + +/* Convert the number of 8-bit bytes of a binary representation to + the number of characters (digits + sign if the type is signed) + required to represent the same quantity in the specified base/type. + For example, a 32-bit (4-byte) quantity may require a field width + as wide as the following for these types: + 11 unsigned octal + 11 signed decimal + 10 unsigned decimal + 8 unsigned hexadecimal */ + +static const uint8_t bytes_to_oct_digits[] ALIGN1 = +{0, 3, 6, 8, 11, 14, 16, 19, 22, 25, 27, 30, 32, 35, 38, 41, 43}; + +static const uint8_t bytes_to_signed_dec_digits[] ALIGN1 = +{1, 4, 6, 8, 11, 13, 16, 18, 20, 23, 25, 28, 30, 33, 35, 37, 40}; + +static const uint8_t bytes_to_unsigned_dec_digits[] ALIGN1 = +{0, 3, 5, 8, 10, 13, 15, 17, 20, 22, 25, 27, 29, 32, 34, 37, 39}; + +static const uint8_t bytes_to_hex_digits[] ALIGN1 = +{0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32}; + +/* Convert enum size_spec to the size of the named type. */ +static const signed char width_bytes[] ALIGN1 = { + -1, + sizeof(char), + sizeof(short), + sizeof(int), + sizeof(long), + sizeof(ulonglong_t), + sizeof(float), + sizeof(double), + sizeof(longdouble_t) +}; +/* Ensure that for each member of 'enum size_spec' there is an + initializer in the width_bytes array. */ +struct ERR_width_bytes_has_bad_size { + char ERR_width_bytes_has_bad_size[ARRAY_SIZE(width_bytes) == N_SIZE_SPECS ? 1 : -1]; +}; + +static smallint flag_dump_strings; +/* Non-zero if an old-style 'pseudo-address' was specified. */ +static smallint flag_pseudo_start; +static smallint limit_bytes_to_format; +/* When zero and two or more consecutive blocks are equal, format + only the first block and output an asterisk alone on the following + line to indicate that identical blocks have been elided. */ +static smallint verbose; +static smallint ioerror; + +static size_t string_min; + +/* An array of specs describing how to format each input block. */ +static size_t n_specs; +static struct tspec *spec; + +/* Function that accepts an address and an optional following char, + and prints the address and char to stdout. */ +static void (*format_address)(off_t, char); +/* The difference between the old-style pseudo starting address and + the number of bytes to skip. */ +static off_t pseudo_offset; +/* When zero, MAX_BYTES_TO_FORMAT and END_OFFSET are ignored, and all + input is formatted. */ + +/* The number of input bytes formatted per output line. It must be + a multiple of the least common multiple of the sizes associated with + the specified output types. It should be as large as possible, but + no larger than 16 -- unless specified with the -w option. */ +static unsigned bytes_per_block = 32; /* have to use unsigned, not size_t */ + +/* A NULL-terminated list of the file-arguments from the command line. */ +static const char *const *file_list; + +/* The input stream associated with the current file. */ +static FILE *in_stream; + +#define MAX_INTEGRAL_TYPE_SIZE sizeof(ulonglong_t) +static const unsigned char integral_type_size[MAX_INTEGRAL_TYPE_SIZE + 1] ALIGN1 = { + [sizeof(char)] = CHAR, +#if USHRT_MAX != UCHAR_MAX + [sizeof(short)] = SHORT, +#endif +#if UINT_MAX != USHRT_MAX + [sizeof(int)] = INT, +#endif +#if ULONG_MAX != UINT_MAX + [sizeof(long)] = LONG, +#endif +#if ULLONG_MAX != ULONG_MAX + [sizeof(ulonglong_t)] = LONG_LONG, +#endif +}; + +#define MAX_FP_TYPE_SIZE sizeof(longdouble_t) +static const unsigned char fp_type_size[MAX_FP_TYPE_SIZE + 1] ALIGN1 = { + /* gcc seems to allow repeated indexes. Last one stays */ + [sizeof(longdouble_t)] = FLOAT_LONG_DOUBLE, + [sizeof(double)] = FLOAT_DOUBLE, + [sizeof(float)] = FLOAT_SINGLE +}; + + +static unsigned +gcd(unsigned u, unsigned v) +{ + unsigned t; + while (v != 0) { + t = u % v; + u = v; + v = t; + } + return u; +} + +/* Compute the least common multiple of U and V. */ +static unsigned +lcm(unsigned u, unsigned v) { + unsigned t = gcd(u, v); + if (t == 0) + return 0; + return u * v / t; +} + +static void +print_s_char(size_t n_bytes, const char *block, const char *fmt_string) +{ + while (n_bytes--) { + int tmp = *(signed char *) block; + printf(fmt_string, tmp); + block += sizeof(unsigned char); + } +} + +static void +print_char(size_t n_bytes, const char *block, const char *fmt_string) +{ + while (n_bytes--) { + unsigned tmp = *(unsigned char *) block; + printf(fmt_string, tmp); + block += sizeof(unsigned char); + } +} + +static void +print_s_short(size_t n_bytes, const char *block, const char *fmt_string) +{ + n_bytes /= sizeof(signed short); + while (n_bytes--) { + int tmp = *(signed short *) block; + printf(fmt_string, tmp); + block += sizeof(unsigned short); + } +} + +static void +print_short(size_t n_bytes, const char *block, const char *fmt_string) +{ + n_bytes /= sizeof(unsigned short); + while (n_bytes--) { + unsigned tmp = *(unsigned short *) block; + printf(fmt_string, tmp); + block += sizeof(unsigned short); + } +} + +static void +print_int(size_t n_bytes, const char *block, const char *fmt_string) +{ + n_bytes /= sizeof(unsigned); + while (n_bytes--) { + unsigned tmp = *(unsigned *) block; + printf(fmt_string, tmp); + block += sizeof(unsigned); + } +} + +#if UINT_MAX == ULONG_MAX +# define print_long print_int +#else +static void +print_long(size_t n_bytes, const char *block, const char *fmt_string) +{ + n_bytes /= sizeof(unsigned long); + while (n_bytes--) { + unsigned long tmp = *(unsigned long *) block; + printf(fmt_string, tmp); + block += sizeof(unsigned long); + } +} +#endif + +#if ULONG_MAX == ULLONG_MAX +# define print_long_long print_long +#else +static void +print_long_long(size_t n_bytes, const char *block, const char *fmt_string) +{ + n_bytes /= sizeof(ulonglong_t); + while (n_bytes--) { + ulonglong_t tmp = *(ulonglong_t *) block; + printf(fmt_string, tmp); + block += sizeof(ulonglong_t); + } +} +#endif + +static void +print_float(size_t n_bytes, const char *block, const char *fmt_string) +{ + n_bytes /= sizeof(float); + while (n_bytes--) { + float tmp = *(float *) block; + printf(fmt_string, tmp); + block += sizeof(float); + } +} + +static void +print_double(size_t n_bytes, const char *block, const char *fmt_string) +{ + n_bytes /= sizeof(double); + while (n_bytes--) { + double tmp = *(double *) block; + printf(fmt_string, tmp); + block += sizeof(double); + } +} + +static void +print_long_double(size_t n_bytes, const char *block, const char *fmt_string) +{ + n_bytes /= sizeof(longdouble_t); + while (n_bytes--) { + longdouble_t tmp = *(longdouble_t *) block; + printf(fmt_string, tmp); + block += sizeof(longdouble_t); + } +} + +/* print_[named]_ascii are optimized for speed. + * Remember, someday you may want to pump gigabytes through this thing. + * Saving a dozen of .text bytes here is counter-productive */ + +static void +print_named_ascii(size_t n_bytes, const char *block, + const char *unused_fmt_string UNUSED_PARAM) +{ + /* Names for some non-printing characters. */ + static const char charname[33][3] ALIGN1 = { + "nul", "soh", "stx", "etx", "eot", "enq", "ack", "bel", + " bs", " ht", " nl", " vt", " ff", " cr", " so", " si", + "dle", "dc1", "dc2", "dc3", "dc4", "nak", "syn", "etb", + "can", " em", "sub", "esc", " fs", " gs", " rs", " us", + " sp" + }; + // buf[N] pos: 01234 56789 + char buf[12] = " x\0 0xx\0"; + // actually " x\0 xxx\0", but I want to share the string with below. + // [12] because we take three 32bit stack slots anyway, and + // gcc is too dumb to initialize with constant stores, + // it copies initializer from rodata. Oh well. + + while (n_bytes--) { + unsigned masked_c = *(unsigned char *) block++; + + masked_c &= 0x7f; + if (masked_c == 0x7f) { + fputs(" del", stdout); + continue; + } + if (masked_c > ' ') { + buf[3] = masked_c; + fputs(buf, stdout); + continue; + } + /* Why? Because printf(" %3.3s") is much slower... */ + buf[6] = charname[masked_c][0]; + buf[7] = charname[masked_c][1]; + buf[8] = charname[masked_c][2]; + fputs(buf+5, stdout); + } +} + +static void +print_ascii(size_t n_bytes, const char *block, + const char *unused_fmt_string UNUSED_PARAM) +{ + // buf[N] pos: 01234 56789 + char buf[12] = " x\0 0xx\0"; + + while (n_bytes--) { + const char *s; + unsigned c = *(unsigned char *) block++; + + if (ISPRINT(c)) { + buf[3] = c; + fputs(buf, stdout); + continue; + } + switch (c) { + case '\0': + s = " \\0"; + break; + case '\007': + s = " \\a"; + break; + case '\b': + s = " \\b"; + break; + case '\f': + s = " \\f"; + break; + case '\n': + s = " \\n"; + break; + case '\r': + s = " \\r"; + break; + case '\t': + s = " \\t"; + break; + case '\v': + s = " \\v"; + break; + case '\x7f': + s = " 177"; + break; + default: /* c is never larger than 040 */ + buf[7] = (c >> 3) + '0'; + buf[8] = (c & 7) + '0'; + s = buf + 5; + } + fputs(s, stdout); + } +} + +/* Given a list of one or more input filenames FILE_LIST, set the global + file pointer IN_STREAM and the global string INPUT_FILENAME to the + first one that can be successfully opened. Modify FILE_LIST to + reference the next filename in the list. A file name of "-" is + interpreted as standard input. If any file open fails, give an error + message and return nonzero. */ + +static void +open_next_file(void) +{ + while (1) { + if (!*file_list) + return; + in_stream = fopen_or_warn_stdin(*file_list++); + if (in_stream) { + break; + } + ioerror = 1; + } + + if (limit_bytes_to_format && !flag_dump_strings) + setbuf(in_stream, NULL); +} + +/* Test whether there have been errors on in_stream, and close it if + it is not standard input. Return nonzero if there has been an error + on in_stream or stdout; return zero otherwise. This function will + report more than one error only if both a read and a write error + have occurred. IN_ERRNO, if nonzero, is the error number + corresponding to the most recent action for IN_STREAM. */ + +static void +check_and_close(void) +{ + if (in_stream) { + if (ferror(in_stream)) { + bb_error_msg("%s: read error", (in_stream == stdin) + ? bb_msg_standard_input + : file_list[-1] + ); + ioerror = 1; + } + fclose_if_not_stdin(in_stream); + in_stream = NULL; + } + + if (ferror(stdout)) { + bb_error_msg("write error"); + ioerror = 1; + } +} + +/* If S points to a single valid modern od format string, put + a description of that format in *TSPEC, return pointer to + character following the just-decoded format. + For example, if S were "d4afL", we will return a rtp to "afL" + and *TSPEC would be + { + fmt = SIGNED_DECIMAL; + size = INT or LONG; (whichever integral_type_size[4] resolves to) + print_function = print_int; (assuming size == INT) + fmt_string = "%011d%c"; + } + S_ORIG is solely for reporting errors. It should be the full format + string argument. */ + +static const char * +decode_one_format(const char *s_orig, const char *s, struct tspec *tspec) +{ + enum size_spec size_spec; + unsigned size; + enum output_format fmt; + const char *p; + char *end; + char *fmt_string = NULL; + void (*print_function) (size_t, const char *, const char *); + unsigned c; + unsigned field_width = 0; + int pos; + + + switch (*s) { + case 'd': + case 'o': + case 'u': + case 'x': { + static const char CSIL[] ALIGN1 = "CSIL"; + + c = *s++; + p = strchr(CSIL, *s); + if (!p) { + size = sizeof(int); + if (isdigit(s[0])) { + size = bb_strtou(s, &end, 0); + if (errno == ERANGE + || MAX_INTEGRAL_TYPE_SIZE < size + || integral_type_size[size] == NO_SIZE + ) { + bb_error_msg_and_die("invalid type string '%s'; " + "%u-byte %s type is not supported", + s_orig, size, "integral"); + } + s = end; + } + } else { + static const uint8_t CSIL_sizeof[4] = { + sizeof(char), + sizeof(short), + sizeof(int), + sizeof(long), + }; + size = CSIL_sizeof[p - CSIL]; + s++; /* skip C/S/I/L */ + } + +#define ISPEC_TO_FORMAT(Spec, Min_format, Long_format, Max_format) \ + ((Spec) == LONG_LONG ? (Max_format) \ + : ((Spec) == LONG ? (Long_format) : (Min_format))) + +#define FMT_BYTES_ALLOCATED 9 + size_spec = integral_type_size[size]; + + { + static const char doux[] ALIGN1 = "doux"; + static const char doux_fmt_letter[][4] = { + "lld", "llo", "llu", "llx" + }; + static const enum output_format doux_fmt[] = { + SIGNED_DECIMAL, + OCTAL, + UNSIGNED_DECIMAL, + HEXADECIMAL, + }; + static const uint8_t *const doux_bytes_to_XXX[] = { + bytes_to_signed_dec_digits, + bytes_to_oct_digits, + bytes_to_unsigned_dec_digits, + bytes_to_hex_digits, + }; + static const char doux_fmtstring[][sizeof(" %%0%u%s")] = { + " %%%u%s", + " %%0%u%s", + " %%%u%s", + " %%0%u%s", + }; + + pos = strchr(doux, c) - doux; + fmt = doux_fmt[pos]; + field_width = doux_bytes_to_XXX[pos][size]; + p = doux_fmt_letter[pos] + 2; + if (size_spec == LONG) p--; + if (size_spec == LONG_LONG) p -= 2; + fmt_string = xasprintf(doux_fmtstring[pos], field_width, p); + } + + switch (size_spec) { + case CHAR: + print_function = (fmt == SIGNED_DECIMAL + ? print_s_char + : print_char); + break; + case SHORT: + print_function = (fmt == SIGNED_DECIMAL + ? print_s_short + : print_short); + break; + case INT: + print_function = print_int; + break; + case LONG: + print_function = print_long; + break; + default: /* case LONG_LONG: */ + print_function = print_long_long; + break; + } + break; + } + + case 'f': { + static const char FDL[] ALIGN1 = "FDL"; + + fmt = FLOATING_POINT; + ++s; + p = strchr(FDL, *s); + if (!p) { + size = sizeof(double); + if (isdigit(s[0])) { + size = bb_strtou(s, &end, 0); + if (errno == ERANGE || size > MAX_FP_TYPE_SIZE + || fp_type_size[size] == NO_SIZE + ) { + bb_error_msg_and_die("invalid type string '%s'; " + "%u-byte %s type is not supported", + s_orig, size, "floating point"); + } + s = end; + } + } else { + static const uint8_t FDL_sizeof[] = { + sizeof(float), + sizeof(double), + sizeof(longdouble_t), + }; + + size = FDL_sizeof[p - FDL]; + } + + size_spec = fp_type_size[size]; + + switch (size_spec) { + case FLOAT_SINGLE: + print_function = print_float; + field_width = FLT_DIG + 8; + /* Don't use %#e; not all systems support it. */ + fmt_string = xasprintf(" %%%d.%de", field_width, FLT_DIG); + break; + case FLOAT_DOUBLE: + print_function = print_double; + field_width = DBL_DIG + 8; + fmt_string = xasprintf(" %%%d.%de", field_width, DBL_DIG); + break; + default: /* case FLOAT_LONG_DOUBLE: */ + print_function = print_long_double; + field_width = LDBL_DIG + 8; + fmt_string = xasprintf(" %%%d.%dLe", field_width, LDBL_DIG); + break; + } + break; + } + + case 'a': + ++s; + fmt = NAMED_CHARACTER; + size_spec = CHAR; + print_function = print_named_ascii; + field_width = 3; + break; + case 'c': + ++s; + fmt = CHARACTER; + size_spec = CHAR; + print_function = print_ascii; + field_width = 3; + break; + default: + bb_error_msg_and_die("invalid character '%c' " + "in type string '%s'", *s, s_orig); + } + + tspec->size = size_spec; + tspec->fmt = fmt; + tspec->print_function = print_function; + tspec->fmt_string = fmt_string; + + tspec->field_width = field_width; + tspec->hexl_mode_trailer = (*s == 'z'); + if (tspec->hexl_mode_trailer) + s++; + + return s; +} + +/* Decode the modern od format string S. Append the decoded + representation to the global array SPEC, reallocating SPEC if + necessary. */ + +static void +decode_format_string(const char *s) +{ + const char *s_orig = s; + + while (*s != '\0') { + struct tspec tspec; + const char *next; + + next = decode_one_format(s_orig, s, &tspec); + + assert(s != next); + s = next; + spec = xrealloc_vector(spec, 4, n_specs); + memcpy(&spec[n_specs], &tspec, sizeof(spec[0])); + n_specs++; + } +} + +/* Given a list of one or more input filenames FILE_LIST, set the global + file pointer IN_STREAM to position N_SKIP in the concatenation of + those files. If any file operation fails or if there are fewer than + N_SKIP bytes in the combined input, give an error message and return + nonzero. When possible, use seek rather than read operations to + advance IN_STREAM. */ + +static void +skip(off_t n_skip) +{ + if (n_skip == 0) + return; + + while (in_stream) { /* !EOF */ + struct stat file_stats; + + /* First try seeking. For large offsets, this extra work is + worthwhile. If the offset is below some threshold it may be + more efficient to move the pointer by reading. There are two + issues when trying to seek: + - the file must be seekable. + - before seeking to the specified position, make sure + that the new position is in the current file. + Try to do that by getting file's size using fstat. + But that will work only for regular files. */ + + /* The st_size field is valid only for regular files + (and for symbolic links, which cannot occur here). + If the number of bytes left to skip is at least + as large as the size of the current file, we can + decrement n_skip and go on to the next file. */ + if (fstat(fileno(in_stream), &file_stats) == 0 + && S_ISREG(file_stats.st_mode) && file_stats.st_size > 0 + ) { + if (file_stats.st_size < n_skip) { + n_skip -= file_stats.st_size; + /* take "check & close / open_next" route */ + } else { + if (fseeko(in_stream, n_skip, SEEK_CUR) != 0) + ioerror = 1; + return; + } + } else { + /* If it's not a regular file with positive size, + position the file pointer by reading. */ + char buf[1024]; + size_t n_bytes_to_read = 1024; + size_t n_bytes_read; + + while (n_skip > 0) { + if (n_skip < n_bytes_to_read) + n_bytes_to_read = n_skip; + n_bytes_read = fread(buf, 1, n_bytes_to_read, in_stream); + n_skip -= n_bytes_read; + if (n_bytes_read != n_bytes_to_read) + break; /* EOF on this file or error */ + } + } + if (n_skip == 0) + return; + + check_and_close(); + open_next_file(); + } + + if (n_skip) + bb_error_msg_and_die("cannot skip past end of combined input"); +} + + +typedef void FN_format_address(off_t address, char c); + +static void +format_address_none(off_t address UNUSED_PARAM, char c UNUSED_PARAM) +{ +} + +static char address_fmt[] ALIGN1 = "%0n"OFF_FMT"xc"; +/* Corresponds to 'x' above */ +#define address_base_char address_fmt[sizeof(address_fmt)-3] +/* Corresponds to 'n' above */ +#define address_pad_len_char address_fmt[2] + +static void +format_address_std(off_t address, char c) +{ + /* Corresponds to 'c' */ + address_fmt[sizeof(address_fmt)-2] = c; + printf(address_fmt, address); +} + +#if ENABLE_GETOPT_LONG +/* only used with --traditional */ +static void +format_address_paren(off_t address, char c) +{ + putchar('('); + format_address_std(address, ')'); + if (c) putchar(c); +} + +static void +format_address_label(off_t address, char c) +{ + format_address_std(address, ' '); + format_address_paren(address + pseudo_offset, c); +} +#endif + +static void +dump_hexl_mode_trailer(size_t n_bytes, const char *block) +{ + fputs(" >", stdout); + while (n_bytes--) { + unsigned c = *(unsigned char *) block++; + c = (ISPRINT(c) ? c : '.'); + putchar(c); + } + putchar('<'); +} + +/* Write N_BYTES bytes from CURR_BLOCK to standard output once for each + of the N_SPEC format specs. CURRENT_OFFSET is the byte address of + CURR_BLOCK in the concatenation of input files, and it is printed + (optionally) only before the output line associated with the first + format spec. When duplicate blocks are being abbreviated, the output + for a sequence of identical input blocks is the output for the first + block followed by an asterisk alone on a line. It is valid to compare + the blocks PREV_BLOCK and CURR_BLOCK only when N_BYTES == BYTES_PER_BLOCK. + That condition may be false only for the last input block -- and then + only when it has not been padded to length BYTES_PER_BLOCK. */ + +static void +write_block(off_t current_offset, size_t n_bytes, + const char *prev_block, const char *curr_block) +{ + static char first = 1; + static char prev_pair_equal = 0; + size_t i; + + if (!verbose && !first + && n_bytes == bytes_per_block + && memcmp(prev_block, curr_block, bytes_per_block) == 0 + ) { + if (prev_pair_equal) { + /* The two preceding blocks were equal, and the current + block is the same as the last one, so print nothing. */ + } else { + puts("*"); + prev_pair_equal = 1; + } + } else { + first = 0; + prev_pair_equal = 0; + for (i = 0; i < n_specs; i++) { + if (i == 0) + format_address(current_offset, '\0'); + else + printf("%*s", address_pad_len_char - '0', ""); + (*spec[i].print_function) (n_bytes, curr_block, spec[i].fmt_string); + if (spec[i].hexl_mode_trailer) { + /* space-pad out to full line width, then dump the trailer */ + int datum_width = width_bytes[spec[i].size]; + int blank_fields = (bytes_per_block - n_bytes) / datum_width; + int field_width = spec[i].field_width + 1; + printf("%*s", blank_fields * field_width, ""); + dump_hexl_mode_trailer(n_bytes, curr_block); + } + putchar('\n'); + } + } +} + +static void +read_block(size_t n, char *block, size_t *n_bytes_in_buffer) +{ + assert(0 < n && n <= bytes_per_block); + + *n_bytes_in_buffer = 0; + + if (n == 0) + return; + + while (in_stream != NULL) { /* EOF. */ + size_t n_needed; + size_t n_read; + + n_needed = n - *n_bytes_in_buffer; + n_read = fread(block + *n_bytes_in_buffer, 1, n_needed, in_stream); + *n_bytes_in_buffer += n_read; + if (n_read == n_needed) + break; + /* error check is done in check_and_close */ + check_and_close(); + open_next_file(); + } +} + +/* Return the least common multiple of the sizes associated + with the format specs. */ + +static int +get_lcm(void) +{ + size_t i; + int l_c_m = 1; + + for (i = 0; i < n_specs; i++) + l_c_m = lcm(l_c_m, width_bytes[(int) spec[i].size]); + return l_c_m; +} + +#if ENABLE_GETOPT_LONG +/* If S is a valid traditional offset specification with an optional + leading '+' return nonzero and set *OFFSET to the offset it denotes. */ + +static int +parse_old_offset(const char *s, off_t *offset) +{ + static const struct suffix_mult Bb[] = { + { "B", 1024 }, + { "b", 512 }, + { } + }; + char *p; + int radix; + + /* Skip over any leading '+'. */ + if (s[0] == '+') ++s; + + /* Determine the radix we'll use to interpret S. If there is a '.', + * it's decimal, otherwise, if the string begins with '0X'or '0x', + * it's hexadecimal, else octal. */ + p = strchr(s, '.'); + radix = 8; + if (p) { + p[0] = '\0'; /* cheating */ + radix = 10; + } else if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) + radix = 16; + + *offset = xstrtooff_sfx(s, radix, Bb); + if (p) p[0] = '.'; + + return (*offset >= 0); +} +#endif + +/* Read a chunk of size BYTES_PER_BLOCK from the input files, write the + formatted block to standard output, and repeat until the specified + maximum number of bytes has been read or until all input has been + processed. If the last block read is smaller than BYTES_PER_BLOCK + and its size is not a multiple of the size associated with a format + spec, extend the input block with zero bytes until its length is a + multiple of all format spec sizes. Write the final block. Finally, + write on a line by itself the offset of the byte after the last byte + read. */ + +static void +dump(off_t current_offset, off_t end_offset) +{ + char *block[2]; + int idx; + size_t n_bytes_read; + + block[0] = xmalloc(2*bytes_per_block); + block[1] = block[0] + bytes_per_block; + + idx = 0; + if (limit_bytes_to_format) { + while (1) { + size_t n_needed; + if (current_offset >= end_offset) { + n_bytes_read = 0; + break; + } + n_needed = MIN(end_offset - current_offset, + (off_t) bytes_per_block); + read_block(n_needed, block[idx], &n_bytes_read); + if (n_bytes_read < bytes_per_block) + break; + assert(n_bytes_read == bytes_per_block); + write_block(current_offset, n_bytes_read, + block[!idx], block[idx]); + current_offset += n_bytes_read; + idx = !idx; + } + } else { + while (1) { + read_block(bytes_per_block, block[idx], &n_bytes_read); + if (n_bytes_read < bytes_per_block) + break; + assert(n_bytes_read == bytes_per_block); + write_block(current_offset, n_bytes_read, + block[!idx], block[idx]); + current_offset += n_bytes_read; + idx = !idx; + } + } + + if (n_bytes_read > 0) { + int l_c_m; + size_t bytes_to_write; + + l_c_m = get_lcm(); + + /* Make bytes_to_write the smallest multiple of l_c_m that + is at least as large as n_bytes_read. */ + bytes_to_write = l_c_m * ((n_bytes_read + l_c_m - 1) / l_c_m); + + memset(block[idx] + n_bytes_read, 0, bytes_to_write - n_bytes_read); + write_block(current_offset, bytes_to_write, + block[!idx], block[idx]); + current_offset += n_bytes_read; + } + + format_address(current_offset, '\n'); + + if (limit_bytes_to_format && current_offset >= end_offset) + check_and_close(); + + free(block[0]); +} + +/* Read a single byte into *C from the concatenation of the input files + named in the global array FILE_LIST. On the first call to this + function, the global variable IN_STREAM is expected to be an open + stream associated with the input file INPUT_FILENAME. If IN_STREAM + is at end-of-file, close it and update the global variables IN_STREAM + and INPUT_FILENAME so they correspond to the next file in the list. + Then try to read a byte from the newly opened file. Repeat if + necessary until EOF is reached for the last file in FILE_LIST, then + set *C to EOF and return. Subsequent calls do likewise. */ + +static void +read_char(int *c) +{ + while (in_stream) { /* !EOF */ + *c = fgetc(in_stream); + if (*c != EOF) + return; + check_and_close(); + open_next_file(); + } + *c = EOF; +} + +/* Read N bytes into BLOCK from the concatenation of the input files + named in the global array FILE_LIST. On the first call to this + function, the global variable IN_STREAM is expected to be an open + stream associated with the input file INPUT_FILENAME. If all N + bytes cannot be read from IN_STREAM, close IN_STREAM and update + the global variables IN_STREAM and INPUT_FILENAME. Then try to + read the remaining bytes from the newly opened file. Repeat if + necessary until EOF is reached for the last file in FILE_LIST. + On subsequent calls, don't modify BLOCK and return zero. Set + *N_BYTES_IN_BUFFER to the number of bytes read. If an error occurs, + it will be detected through ferror when the stream is about to be + closed. If there is an error, give a message but continue reading + as usual and return nonzero. Otherwise return zero. */ + +/* STRINGS mode. Find each "string constant" in the input. + A string constant is a run of at least 'string_min' ASCII + graphic (or formatting) characters terminated by a null. + Based on a function written by Richard Stallman for a + traditional version of od. */ + +static void +dump_strings(off_t address, off_t end_offset) +{ + size_t bufsize = MAX(100, string_min); + char *buf = xmalloc(bufsize); + + while (1) { + size_t i; + int c; + + /* See if the next 'string_min' chars are all printing chars. */ + tryline: + if (limit_bytes_to_format && (end_offset - string_min <= address)) + break; + i = 0; + while (!limit_bytes_to_format || address < end_offset) { + if (i == bufsize) { + bufsize += bufsize/8; + buf = xrealloc(buf, bufsize); + } + read_char(&c); + if (c < 0) { /* EOF */ + free(buf); + return; + } + address++; + if (!c) + break; + if (!ISPRINT(c)) + goto tryline; /* It isn't; give up on this string. */ + buf[i++] = c; /* String continues; store it all. */ + } + + if (i < string_min) /* Too short! */ + goto tryline; + + /* If we get here, the string is all printable and NUL-terminated, + * so print it. It is all in 'buf' and 'i' is its length. */ + buf[i] = 0; + format_address(address - i - 1, ' '); + + for (i = 0; (c = buf[i]); i++) { + switch (c) { + case '\007': fputs("\\a", stdout); break; + case '\b': fputs("\\b", stdout); break; + case '\f': fputs("\\f", stdout); break; + case '\n': fputs("\\n", stdout); break; + case '\r': fputs("\\r", stdout); break; + case '\t': fputs("\\t", stdout); break; + case '\v': fputs("\\v", stdout); break; + default: putchar(c); + } + } + putchar('\n'); + } + + /* We reach this point only if we search through + (max_bytes_to_format - string_min) bytes before reaching EOF. */ + free(buf); + + check_and_close(); +} + +int od_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int od_main(int argc, char **argv) +{ + static const struct suffix_mult bkm[] = { + { "b", 512 }, + { "k", 1024 }, + { "m", 1024*1024 }, + { } + }; + enum { + OPT_A = 1 << 0, + OPT_N = 1 << 1, + OPT_a = 1 << 2, + OPT_b = 1 << 3, + OPT_c = 1 << 4, + OPT_d = 1 << 5, + OPT_f = 1 << 6, + OPT_h = 1 << 7, + OPT_i = 1 << 8, + OPT_j = 1 << 9, + OPT_l = 1 << 10, + OPT_o = 1 << 11, + OPT_t = 1 << 12, + OPT_v = 1 << 13, + OPT_x = 1 << 14, + OPT_s = 1 << 15, + OPT_S = 1 << 16, + OPT_w = 1 << 17, + OPT_traditional = (1 << 18) * ENABLE_GETOPT_LONG, + }; +#if ENABLE_GETOPT_LONG + static const char od_longopts[] ALIGN1 = + "skip-bytes\0" Required_argument "j" + "address-radix\0" Required_argument "A" + "read-bytes\0" Required_argument "N" + "format\0" Required_argument "t" + "output-duplicates\0" No_argument "v" + "strings\0" Optional_argument "S" + "width\0" Optional_argument "w" + "traditional\0" No_argument "\xff" + ; +#endif + char *str_A, *str_N, *str_j, *str_S; + llist_t *lst_t = NULL; + unsigned opt; + int l_c_m; + /* The old-style 'pseudo starting address' to be printed in parentheses + after any true address. */ + off_t pseudo_start = pseudo_start; // for gcc + /* The number of input bytes to skip before formatting and writing. */ + off_t n_bytes_to_skip = 0; + /* The offset of the first byte after the last byte to be formatted. */ + off_t end_offset = 0; + /* The maximum number of bytes that will be formatted. */ + off_t max_bytes_to_format = 0; + + spec = NULL; + format_address = format_address_std; + address_base_char = 'o'; + address_pad_len_char = '7'; + /* flag_dump_strings = 0; - already is */ + + /* Parse command line */ + opt_complementary = "w+:t::"; /* -w N, -t is a list */ +#if ENABLE_GETOPT_LONG + applet_long_options = od_longopts; +#endif + opt = getopt32(argv, "A:N:abcdfhij:lot:vxsS:" + "w::", // -w with optional param + // -S was -s and also had optional parameter + // but in coreutils 6.3 it was renamed and now has + // _mandatory_ parameter + &str_A, &str_N, &str_j, &lst_t, &str_S, &bytes_per_block); + argc -= optind; + argv += optind; + if (opt & OPT_A) { + static const char doxn[] ALIGN1 = "doxn"; + static const char doxn_address_base_char[] ALIGN1 = { + 'u', 'o', 'x', /* '?' fourth one is not important */ + }; + static const uint8_t doxn_address_pad_len_char[] ALIGN1 = { + '7', '7', '6', /* '?' */ + }; + char *p; + int pos; + p = strchr(doxn, str_A[0]); + if (!p) + bb_error_msg_and_die("bad output address radix " + "'%c' (must be [doxn])", str_A[0]); + pos = p - doxn; + if (pos == 3) format_address = format_address_none; + address_base_char = doxn_address_base_char[pos]; + address_pad_len_char = doxn_address_pad_len_char[pos]; + } + if (opt & OPT_N) { + limit_bytes_to_format = 1; + max_bytes_to_format = xstrtooff_sfx(str_N, 0, bkm); + } + if (opt & OPT_a) decode_format_string("a"); + if (opt & OPT_b) decode_format_string("oC"); + if (opt & OPT_c) decode_format_string("c"); + if (opt & OPT_d) decode_format_string("u2"); + if (opt & OPT_f) decode_format_string("fF"); + if (opt & OPT_h) decode_format_string("x2"); + if (opt & OPT_i) decode_format_string("d2"); + if (opt & OPT_j) n_bytes_to_skip = xstrtooff_sfx(str_j, 0, bkm); + if (opt & OPT_l) decode_format_string("d4"); + if (opt & OPT_o) decode_format_string("o2"); + //if (opt & OPT_t)... + while (lst_t) { + decode_format_string(llist_pop(&lst_t)); + } + if (opt & OPT_v) verbose = 1; + if (opt & OPT_x) decode_format_string("x2"); + if (opt & OPT_s) decode_format_string("d2"); + if (opt & OPT_S) { + string_min = 3; + string_min = xstrtou_sfx(str_S, 0, bkm); + flag_dump_strings = 1; + } + //if (opt & OPT_w)... + //if (opt & OPT_traditional)... + + if (flag_dump_strings && n_specs > 0) + bb_error_msg_and_die("no type may be specified when dumping strings"); + + /* If the --traditional option is used, there may be from + * 0 to 3 remaining command line arguments; handle each case + * separately. + * od [file] [[+]offset[.][b] [[+]label[.][b]]] + * The offset and pseudo_start have the same syntax. + * + * FIXME: POSIX 1003.1-2001 with XSI requires support for the + * traditional syntax even if --traditional is not given. */ + +#if ENABLE_GETOPT_LONG + if (opt & OPT_traditional) { + off_t o1, o2; + + if (argc == 1) { + if (parse_old_offset(argv[0], &o1)) { + n_bytes_to_skip = o1; + --argc; + ++argv; + } + } else if (argc == 2) { + if (parse_old_offset(argv[0], &o1) + && parse_old_offset(argv[1], &o2) + ) { + n_bytes_to_skip = o1; + flag_pseudo_start = 1; + pseudo_start = o2; + argv += 2; + argc -= 2; + } else if (parse_old_offset(argv[1], &o2)) { + n_bytes_to_skip = o2; + --argc; + argv[1] = argv[0]; + ++argv; + } else { + bb_error_msg_and_die("invalid second operand " + "in compatibility mode '%s'", argv[1]); + } + } else if (argc == 3) { + if (parse_old_offset(argv[1], &o1) + && parse_old_offset(argv[2], &o2) + ) { + n_bytes_to_skip = o1; + flag_pseudo_start = 1; + pseudo_start = o2; + argv[2] = argv[0]; + argv += 2; + argc -= 2; + } else { + bb_error_msg_and_die("in compatibility mode " + "the last two arguments must be offsets"); + } + } else if (argc > 3) { + bb_error_msg_and_die("compatibility mode supports " + "at most three arguments"); + } + + if (flag_pseudo_start) { + if (format_address == format_address_none) { + address_base_char = 'o'; + address_pad_len_char = '7'; + format_address = format_address_paren; + } else + format_address = format_address_label; + } + } +#endif + + if (limit_bytes_to_format) { + end_offset = n_bytes_to_skip + max_bytes_to_format; + if (end_offset < n_bytes_to_skip) + bb_error_msg_and_die("skip-bytes + read-bytes is too large"); + } + + if (n_specs == 0) { + decode_format_string("o2"); + n_specs = 1; + } + + /* If no files were listed on the command line, + set the global pointer FILE_LIST so that it + references the null-terminated list of one name: "-". */ + file_list = bb_argv_dash; + if (argc > 0) { + /* Set the global pointer FILE_LIST so that it + references the first file-argument on the command-line. */ + file_list = (char const *const *) argv; + } + + /* open the first input file */ + open_next_file(); + /* skip over any unwanted header bytes */ + skip(n_bytes_to_skip); + if (!in_stream) + return EXIT_FAILURE; + + pseudo_offset = (flag_pseudo_start ? pseudo_start - n_bytes_to_skip : 0); + + /* Compute output block length. */ + l_c_m = get_lcm(); + + if (opt & OPT_w) { /* -w: width */ + if (!bytes_per_block || bytes_per_block % l_c_m != 0) { + bb_error_msg("warning: invalid width %u; using %d instead", + (unsigned)bytes_per_block, l_c_m); + bytes_per_block = l_c_m; + } + } else { + bytes_per_block = l_c_m; + if (l_c_m < DEFAULT_BYTES_PER_BLOCK) + bytes_per_block *= DEFAULT_BYTES_PER_BLOCK / l_c_m; + } + +#ifdef DEBUG + for (i = 0; i < n_specs; i++) { + printf("%d: fmt=\"%s\" width=%d\n", + i, spec[i].fmt_string, width_bytes[spec[i].size]); + } +#endif + + if (flag_dump_strings) + dump_strings(n_bytes_to_skip, end_offset); + else + dump(n_bytes_to_skip, end_offset); + + if (fclose(stdin) == EOF) + bb_perror_msg_and_die(bb_msg_standard_input); + + return ioerror; +} diff --git a/coreutils/printenv.c b/coreutils/printenv.c new file mode 100644 index 0000000..6971f72 --- /dev/null +++ b/coreutils/printenv.c @@ -0,0 +1,33 @@ +/* vi: set sw=4 ts=4: */ +/* + * printenv implementation for busybox + * + * Copyright (C) 2005 by Erik Andersen + * Copyright (C) 2005 by Mike Frysinger + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +#include "libbb.h" + +int printenv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int printenv_main(int argc UNUSED_PARAM, char **argv) +{ + /* no variables specified, show whole env */ + if (!argv[1]) { + int e = 0; + while (environ[e]) + puts(environ[e++]); + } else { + /* search for specified variables and print them out if found */ + char *arg, *env; + + while ((arg = *++argv) != NULL) { + env = getenv(arg); + if (env) + puts(env); + } + } + + fflush_stdout_and_exit(EXIT_SUCCESS); +} diff --git a/coreutils/printf.c b/coreutils/printf.c new file mode 100644 index 0000000..ca8e51c --- /dev/null +++ b/coreutils/printf.c @@ -0,0 +1,386 @@ +/* vi: set sw=4 ts=4: */ +/* printf - format and print data + + Copyright 1999 Dave Cinege + Portions copyright (C) 1990-1996 Free Software Foundation, Inc. + + Licensed under GPL v2 or later, see file LICENSE in this tarball for details. +*/ + +/* Usage: printf format [argument...] + + A front end to the printf function that lets it be used from the shell. + + Backslash escapes: + + \" = double quote + \\ = backslash + \a = alert (bell) + \b = backspace + \c = produce no further output + \f = form feed + \n = new line + \r = carriage return + \t = horizontal tab + \v = vertical tab + \0ooo = octal number (ooo is 0 to 3 digits) + \xhhh = hexadecimal number (hhh is 1 to 3 digits) + + Additional directive: + + %b = print an argument string, interpreting backslash escapes + + The 'format' argument is re-used as many times as necessary + to convert all of the given arguments. + + David MacKenzie +*/ + +// 19990508 Busy Boxed! Dave Cinege + +#include "libbb.h" + +/* A note on bad input: neither bash 3.2 nor coreutils 6.10 stop on it. + * They report it: + * bash: printf: XXX: invalid number + * printf: XXX: expected a numeric value + * bash: printf: 123XXX: invalid number + * printf: 123XXX: value not completely converted + * but then they use 0 (or partially converted numeric prefix) as a value + * and continue. They exit with 1 in this case. + * Both accept insane field width/precision (e.g. %9999999999.9999999999d). + * Both print error message and assume 0 if %*.*f width/precision is "bad" + * (but negative numbers are not "bad"). + * Both accept negative numbers for %u specifier. + * + * We try to be compatible. We are not compatible here: + * - we do not accept -NUM for %u + * - exit code is 0 even if "invalid number" was seen (FIXME) + * See "if (errno)" checks in the code below. + */ + +typedef void FAST_FUNC (*converter)(const char *arg, void *result); + +static int multiconvert(const char *arg, void *result, converter convert) +{ + if (*arg == '"' || *arg == '\'') { + arg = utoa((unsigned char)arg[1]); + } + errno = 0; + convert(arg, result); + if (errno) { + bb_error_msg("%s: invalid number", arg); + return 1; + } + return 0; +} + +static void FAST_FUNC conv_strtoul(const char *arg, void *result) +{ + *(unsigned long*)result = bb_strtoul(arg, NULL, 0); +} +static void FAST_FUNC conv_strtol(const char *arg, void *result) +{ + *(long*)result = bb_strtol(arg, NULL, 0); +} +static void FAST_FUNC conv_strtod(const char *arg, void *result) +{ + char *end; + /* Well, this one allows leading whitespace... so what? */ + /* What I like much less is that "-" accepted too! :( */ + *(double*)result = strtod(arg, &end); + if (end[0]) { + errno = ERANGE; + *(double*)result = 0; + } +} + +/* Callers should check errno to detect errors */ +static unsigned long my_xstrtoul(const char *arg) +{ + unsigned long result; + if (multiconvert(arg, &result, conv_strtoul)) + result = 0; + return result; +} +static long my_xstrtol(const char *arg) +{ + long result; + if (multiconvert(arg, &result, conv_strtol)) + result = 0; + return result; +} +static double my_xstrtod(const char *arg) +{ + double result; + multiconvert(arg, &result, conv_strtod); + return result; +} + +static void print_esc_string(char *str) +{ + while (*str) { + if (*str == '\\') { + str++; + bb_putchar(bb_process_escape_sequence((const char **)&str)); + } else { + bb_putchar(*str); + str++; + } + } +} + +static void print_direc(char *format, unsigned fmt_length, + int field_width, int precision, + const char *argument) +{ + long lv; + double dv; + char saved; + char *have_prec, *have_width; + + saved = format[fmt_length]; + format[fmt_length] = '\0'; + + have_prec = strstr(format, ".*"); + have_width = strchr(format, '*'); + if (have_width - 1 == have_prec) + have_width = NULL; + + switch (format[fmt_length - 1]) { + case 'c': + printf(format, *argument); + break; + case 'd': + case 'i': + lv = my_xstrtol(argument); + print_long: + /* if (errno) return; - see comment at the top */ + if (!have_width) { + if (!have_prec) + printf(format, lv); + else + printf(format, precision, lv); + } else { + if (!have_prec) + printf(format, field_width, lv); + else + printf(format, field_width, precision, lv); + } + break; + case 'o': + case 'u': + case 'x': + case 'X': + lv = my_xstrtoul(argument); + /* cheat: unsigned long and long have same width, so... */ + goto print_long; + case 's': + /* Are char* and long the same? (true for most arches) */ + if (sizeof(argument) == sizeof(lv)) { + lv = (long)(ptrdiff_t)argument; + goto print_long; + } else { /* Hope compiler will optimize it out */ + if (!have_width) { + if (!have_prec) + printf(format, argument); + else + printf(format, precision, argument); + } else { + if (!have_prec) + printf(format, field_width, argument); + else + printf(format, field_width, precision, argument); + } + break; + } + case 'f': + case 'e': + case 'E': + case 'g': + case 'G': + dv = my_xstrtod(argument); + /* if (errno) return; */ + if (!have_width) { + if (!have_prec) + printf(format, dv); + else + printf(format, precision, dv); + } else { + if (!have_prec) + printf(format, field_width, dv); + else + printf(format, field_width, precision, dv); + } + break; + } /* switch */ + + format[fmt_length] = saved; +} + +/* Handle params for "%*.*f". Negative numbers are ok (compat). */ +static int get_width_prec(const char *str) +{ + int v = bb_strtoi(str, NULL, 10); + if (errno) { + bb_error_msg("%s: invalid number", str); + v = 0; + } + return v; +} + +/* Print the text in FORMAT, using ARGV for arguments to any '%' directives. + Return advanced ARGV. */ +static char **print_formatted(char *f, char **argv) +{ + char *direc_start; /* Start of % directive. */ + unsigned direc_length; /* Length of % directive. */ + int field_width; /* Arg to first '*' */ + int precision; /* Arg to second '*' */ + char **saved_argv = argv; + + for (; *f; ++f) { + switch (*f) { + case '%': + direc_start = f++; + direc_length = 1; + field_width = precision = 0; + if (*f == '%') { + bb_putchar('%'); + break; + } + if (*f == 'b') { + if (*argv) { + print_esc_string(*argv); + ++argv; + } + break; + } + if (strchr("-+ #", *f)) { + ++f; + ++direc_length; + } + if (*f == '*') { + ++f; + ++direc_length; + if (*argv) + field_width = get_width_prec(*argv++); + } else { + while (isdigit(*f)) { + ++f; + ++direc_length; + } + } + if (*f == '.') { + ++f; + ++direc_length; + if (*f == '*') { + ++f; + ++direc_length; + if (*argv) + precision = get_width_prec(*argv++); + } else { + while (isdigit(*f)) { + ++f; + ++direc_length; + } + } + } + /* Remove size modifiers - "%Ld" would try to printf + * long long, we pass long, and it spews garbage */ + if ((*f | 0x20) == 'l' || *f == 'h' || *f == 'z') { + overlapping_strcpy(f, f + 1); + } +//FIXME: actually, the same happens with bare "%d": +//it printfs an int, but we pass long! +//What saves us is that on most arches stack slot +//is pointer-sized -> long-sized -> ints are promoted to longs +// for variadic functions -> printf("%d", int_v) is in reality +// indistinqushable from printf("%d", long_v) -> +// since printf("%d", int_v) works, printf("%d", long_v) has to work. +//But "clean" solution would be to add "l" to d,i,o,x,X. +//Probably makes sense to go all the way to "ll" then. +//Coreutils support long long-sized arguments. + + /* needed - try "printf %" without it */ + if (!strchr("diouxXfeEgGcs", *f)) { + bb_error_msg("%s: invalid format", direc_start); + /* causes main() to exit with error */ + return saved_argv - 1; + } + ++direc_length; + if (*argv) { + print_direc(direc_start, direc_length, field_width, + precision, *argv); + ++argv; + } else { + print_direc(direc_start, direc_length, field_width, + precision, ""); + } + /* if (errno) return saved_argv - 1; */ + break; + case '\\': + if (*++f == 'c') { + return saved_argv; /* causes main() to exit */ + } + bb_putchar(bb_process_escape_sequence((const char **)&f)); + f--; + break; + default: + bb_putchar(*f); + } + } + + return argv; +} + +int printf_main(int argc UNUSED_PARAM, char **argv) +{ + char *format; + char **argv2; + + /* We must check that stdout is not closed. + * The reason for this is highly non-obvious. + * printf_main is used from shell. + * Shell must correctly handle 'printf "%s" foo' + * if stdout is closed. With stdio, output gets shoveled into + * stdout buffer, and even fflush cannot clear it out. It seems that + * even if libc receives EBADF on write attempts, it feels determined + * to output data no matter what. So it will try later, + * and possibly will clobber future output. Not good. */ +// TODO: check fcntl() & O_ACCMODE == O_WRONLY or O_RDWR? + if (fcntl(1, F_GETFL) == -1) + return 1; /* match coreutils 6.10 (sans error msg to stderr) */ + //if (dup2(1, 1) != 1) - old way + // return 1; + + /* bash builtin errors out on "printf '-%s-\n' foo", + * coreutils-6.9 works. Both work with "printf -- '-%s-\n' foo". + * We will mimic coreutils. */ + if (argv[1] && argv[1][0] == '-' && argv[1][1] == '-' && !argv[1][2]) + argv++; + if (!argv[1]) { + if (ENABLE_ASH_BUILTIN_PRINTF + && applet_name[0] != 'p' + ) { + bb_error_msg("usage: printf FORMAT [ARGUMENT...]"); + return 2; /* bash compat */ + } + bb_show_usage(); + } + + format = argv[1]; + argv2 = argv + 2; + + do { + argv = argv2; + argv2 = print_formatted(format, argv); + } while (argv2 > argv && *argv2); + + /* coreutils compat (bash doesn't do this): + if (*argv) + fprintf(stderr, "excess args ignored"); + */ + + return (argv2 < argv); /* if true, print_formatted errored out */ +} diff --git a/coreutils/pwd.c b/coreutils/pwd.c new file mode 100644 index 0000000..57953d2 --- /dev/null +++ b/coreutils/pwd.c @@ -0,0 +1,27 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini pwd implementation for busybox + * + * Copyright (C) 1995, 1996 by Bruce Perens . + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +#include "libbb.h" + +/* This is a NOFORK applet. Be very careful! */ + +int pwd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int pwd_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) +{ + char *buf; + + buf = xrealloc_getcwd_or_warn(NULL); + if (buf != NULL) { + puts(buf); + free(buf); + return fflush(stdout); + } + + return EXIT_FAILURE; +} diff --git a/coreutils/readlink.c b/coreutils/readlink.c new file mode 100644 index 0000000..721fd85 --- /dev/null +++ b/coreutils/readlink.c @@ -0,0 +1,49 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini readlink implementation for busybox + * + * Copyright (C) 2000,2001 Matt Kraai + * + * Licensed under GPL v2 or later, see file LICENSE in this tarball for details. + */ + +#include "libbb.h" + +int readlink_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int readlink_main(int argc UNUSED_PARAM, char **argv) +{ + char *buf; + char *fname; + char pathbuf[PATH_MAX]; + + USE_FEATURE_READLINK_FOLLOW( + unsigned opt; + /* We need exactly one non-option argument. */ + opt_complementary = "=1"; + opt = getopt32(argv, "f"); + fname = argv[optind]; + ) + SKIP_FEATURE_READLINK_FOLLOW( + const unsigned opt = 0; + if (argc != 2) bb_show_usage(); + fname = argv[1]; + ) + + /* compat: coreutils readlink reports errors silently via exit code */ + logmode = LOGMODE_NONE; + + if (opt) { + buf = realpath(fname, pathbuf); + } else { + buf = xmalloc_readlink_or_warn(fname); + } + + if (!buf) + return EXIT_FAILURE; + puts(buf); + + if (ENABLE_FEATURE_CLEAN_UP && !opt) + free(buf); + + fflush_stdout_and_exit(EXIT_SUCCESS); +} diff --git a/coreutils/realpath.c b/coreutils/realpath.c new file mode 100644 index 0000000..28906ba --- /dev/null +++ b/coreutils/realpath.c @@ -0,0 +1,46 @@ +/* vi: set sw=4 ts=4: */ + +/* BB_AUDIT SUSv3 N/A -- Apparently a busybox extension. */ + +/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org) + * + * Now does proper error checking on output and returns a failure exit code + * if one or more paths cannot be resolved. + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +#include "libbb.h" + +int realpath_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int realpath_main(int argc UNUSED_PARAM, char **argv) +{ + int retval = EXIT_SUCCESS; + +#if PATH_MAX > (BUFSIZ+1) + RESERVE_CONFIG_BUFFER(resolved_path, PATH_MAX); +# define resolved_path_MUST_FREE 1 +#else +#define resolved_path bb_common_bufsiz1 +# define resolved_path_MUST_FREE 0 +#endif + + if (!*++argv) { + bb_show_usage(); + } + + do { + if (realpath(*argv, resolved_path) != NULL) { + puts(resolved_path); + } else { + retval = EXIT_FAILURE; + bb_simple_perror_msg(*argv); + } + } while (*++argv); + +#if ENABLE_FEATURE_CLEAN_UP && resolved_path_MUST_FREE + RELEASE_CONFIG_BUFFER(resolved_path); +#endif + + fflush_stdout_and_exit(retval); +} diff --git a/coreutils/rm.c b/coreutils/rm.c new file mode 100644 index 0000000..975f226 --- /dev/null +++ b/coreutils/rm.c @@ -0,0 +1,55 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini rm implementation for busybox + * + * Copyright (C) 2001 Matt Kraai + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +/* BB_AUDIT SUSv3 compliant */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/rm.html */ + +/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org) + * + * Size reduction. + */ + +#include "libbb.h" + +/* This is a NOFORK applet. Be very careful! */ + +int rm_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int rm_main(int argc UNUSED_PARAM, char **argv) +{ + int status = 0; + int flags = 0; + unsigned opt; + + opt_complementary = "f-i:i-f"; + opt = getopt32(argv, "fiRr"); + argv += optind; + if (opt & 1) + flags |= FILEUTILS_FORCE; + if (opt & 2) + flags |= FILEUTILS_INTERACTIVE; + if (opt & 12) + flags |= FILEUTILS_RECUR; + + if (*argv != NULL) { + do { + const char *base = bb_get_last_path_component_strip(*argv); + + if (DOT_OR_DOTDOT(base)) { + bb_error_msg("cannot remove '.' or '..'"); + } else if (remove_file(*argv, flags) >= 0) { + continue; + } + status = 1; + } while (*++argv); + } else if (!(flags & FILEUTILS_FORCE)) { + bb_show_usage(); + } + + return status; +} diff --git a/coreutils/rmdir.c b/coreutils/rmdir.c new file mode 100644 index 0000000..2450a43 --- /dev/null +++ b/coreutils/rmdir.c @@ -0,0 +1,70 @@ +/* vi: set sw=4 ts=4: */ +/* + * rmdir implementation for busybox + * + * Copyright (C) 2003 Manuel Novoa III + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +/* BB_AUDIT SUSv3 compliant */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/rmdir.html */ + +#include "libbb.h" + +/* This is a NOFORK applet. Be very careful! */ + + +#define PARENTS 0x01 +#define IGNORE_NON_EMPTY 0x02 + +int rmdir_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int rmdir_main(int argc UNUSED_PARAM, char **argv) +{ + int status = EXIT_SUCCESS; + int flags; + char *path; + +#if ENABLE_FEATURE_RMDIR_LONG_OPTIONS + static const char rmdir_longopts[] ALIGN1 = + "parents\0" No_argument "p" + /* Debian etch: many packages fail to be purged or installed + * because they desperately want this option: */ + "ignore-fail-on-non-empty\0" No_argument "\xff" + ; + applet_long_options = rmdir_longopts; +#endif + flags = getopt32(argv, "p"); + argv += optind; + + if (!*argv) { + bb_show_usage(); + } + + do { + path = *argv; + + while (1) { + if (rmdir(path) < 0) { +#if ENABLE_FEATURE_RMDIR_LONG_OPTIONS + if ((flags & IGNORE_NON_EMPTY) && errno == ENOTEMPTY) + break; +#endif + bb_perror_msg("'%s'", path); /* Match gnu rmdir msg. */ + status = EXIT_FAILURE; + } else if (flags & PARENTS) { + /* Note: path was not "" since rmdir succeeded. */ + path = dirname(path); + /* Path is now just the parent component. Dirname + * returns "." if there are no parents. + */ + if (NOT_LONE_CHAR(path, '.')) { + continue; + } + } + break; + } + } while (*++argv); + + return status; +} diff --git a/coreutils/seq.c b/coreutils/seq.c new file mode 100644 index 0000000..01d71f2 --- /dev/null +++ b/coreutils/seq.c @@ -0,0 +1,40 @@ +/* vi: set sw=4 ts=4: */ +/* + * seq implementation for busybox + * + * Copyright (C) 2004, Glenn McGrath + * + * Licensed under the GPL v2, see the file LICENSE in this tarball. + */ + +#include "libbb.h" + +/* This is a NOFORK applet. Be very careful! */ + + +int seq_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int seq_main(int argc, char **argv) +{ + double last, increment, i; + + i = increment = 1; + switch (argc) { + case 4: + increment = atof(argv[2]); + case 3: + i = atof(argv[1]); + case 2: + last = atof(argv[argc-1]); + break; + default: + bb_show_usage(); + } + + /* You should note that this is pos-5.0.91 semantics, -- FK. */ + while ((increment > 0 && i <= last) || (increment < 0 && i >= last)) { + printf("%g\n", i); + i += increment; + } + + return fflush(stdout); +} diff --git a/coreutils/sleep.c b/coreutils/sleep.c new file mode 100644 index 0000000..de18dd0 --- /dev/null +++ b/coreutils/sleep.c @@ -0,0 +1,104 @@ +/* vi: set sw=4 ts=4: */ +/* + * sleep implementation for busybox + * + * Copyright (C) 2003 Manuel Novoa III + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +/* BB_AUDIT SUSv3 compliant */ +/* BB_AUDIT GNU issues -- fancy version matches except args must be ints. */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/sleep.html */ + +/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org) + * + * Rewritten to do proper arg and error checking. + * Also, added a 'fancy' configuration to accept multiple args with + * time suffixes for seconds, minutes, hours, and days. + */ + +#include "libbb.h" + +/* This is a NOFORK applet. Be very careful! */ + + +#if ENABLE_FEATURE_FANCY_SLEEP || ENABLE_FEATURE_FLOAT_SLEEP +static const struct suffix_mult sfx[] = { + { "s", 1 }, + { "m", 60 }, + { "h", 60*60 }, + { "d", 24*60*60 }, + { } +}; +#endif + +int sleep_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int sleep_main(int argc UNUSED_PARAM, char **argv) +{ +#if ENABLE_FEATURE_FLOAT_SLEEP + double duration; + struct timespec ts; +#else + unsigned duration; +#endif + + ++argv; + if (!*argv) + bb_show_usage(); + +#if ENABLE_FEATURE_FLOAT_SLEEP + + duration = 0; + do { + char *arg = *argv; + if (strchr(arg, '.')) { + double d; + int len = strspn(arg, "0123456789."); + char sv = arg[len]; + arg[len] = '\0'; + d = bb_strtod(arg, NULL); + if (errno) + bb_show_usage(); + arg[len] = sv; + len--; + sv = arg[len]; + arg[len] = '1'; + duration += d * xatoul_sfx(&arg[len], sfx); + arg[len] = sv; + } else + duration += xatoul_sfx(arg, sfx); + } while (*++argv); + + ts.tv_sec = MAXINT(typeof(ts.tv_sec)); + ts.tv_nsec = 0; + if (duration >= 0 && duration < ts.tv_sec) { + ts.tv_sec = duration; + ts.tv_nsec = (duration - ts.tv_sec) * 1000000000; + } + do { + errno = 0; + nanosleep(&ts, &ts); + } while (errno == EINTR); + +#elif ENABLE_FEATURE_FANCY_SLEEP + + duration = 0; + do { + duration += xatou_range_sfx(*argv, 0, UINT_MAX - duration, sfx); + } while (*++argv); + sleep(duration); + +#else /* simple */ + + duration = xatou(*argv); + sleep(duration); + // Off. If it's really needed, provide example why + //if (sleep(duration)) { + // bb_perror_nomsg_and_die(); + //} + +#endif + + return EXIT_SUCCESS; +} diff --git a/coreutils/sort.c b/coreutils/sort.c new file mode 100644 index 0000000..fad6d12 --- /dev/null +++ b/coreutils/sort.c @@ -0,0 +1,406 @@ +/* vi: set sw=4 ts=4: */ +/* + * SuS3 compliant sort implementation for busybox + * + * Copyright (C) 2004 by Rob Landley + * + * MAINTAINER: Rob Landley + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + * + * See SuS3 sort standard at: + * http://www.opengroup.org/onlinepubs/007904975/utilities/sort.html + */ + +#include "libbb.h" + +/* This is a NOEXEC applet. Be very careful! */ + + +/* + sort [-m][-o output][-bdfinru][-t char][-k keydef]... [file...] + sort -c [-bdfinru][-t char][-k keydef][file] +*/ + +/* These are sort types */ +static const char OPT_STR[] ALIGN1 = "ngMucszbrdfimS:T:o:k:t:"; +enum { + FLAG_n = 1, /* Numeric sort */ + FLAG_g = 2, /* Sort using strtod() */ + FLAG_M = 4, /* Sort date */ +/* ucsz apply to root level only, not keys. b at root level implies bb */ + FLAG_u = 8, /* Unique */ + FLAG_c = 0x10, /* Check: no output, exit(!ordered) */ + FLAG_s = 0x20, /* Stable sort, no ascii fallback at end */ + FLAG_z = 0x40, /* Input and output is NUL terminated, not \n */ +/* These can be applied to search keys, the previous four can't */ + FLAG_b = 0x80, /* Ignore leading blanks */ + FLAG_r = 0x100, /* Reverse */ + FLAG_d = 0x200, /* Ignore !(isalnum()|isspace()) */ + FLAG_f = 0x400, /* Force uppercase */ + FLAG_i = 0x800, /* Ignore !isprint() */ + FLAG_m = 0x1000, /* ignored: merge already sorted files; do not sort */ + FLAG_S = 0x2000, /* ignored: -S, --buffer-size=SIZE */ + FLAG_T = 0x4000, /* ignored: -T, --temporary-directory=DIR */ + FLAG_o = 0x8000, + FLAG_k = 0x10000, + FLAG_t = 0x20000, + FLAG_bb = 0x80000000, /* Ignore trailing blanks */ +}; + +#if ENABLE_FEATURE_SORT_BIG +static char key_separator; + +static struct sort_key { + struct sort_key *next_key; /* linked list */ + unsigned range[4]; /* start word, start char, end word, end char */ + unsigned flags; +} *key_list; + +static char *get_key(char *str, struct sort_key *key, int flags) +{ + int start = 0, end = 0, len, j; + unsigned i; + + /* Special case whole string, so we don't have to make a copy */ + if (key->range[0] == 1 && !key->range[1] && !key->range[2] && !key->range[3] + && !(flags & (FLAG_b | FLAG_d | FLAG_f | FLAG_i | FLAG_bb)) + ) { + return str; + } + + /* Find start of key on first pass, end on second pass */ + len = strlen(str); + for (j = 0; j < 2; j++) { + if (!key->range[2*j]) + end = len; + /* Loop through fields */ + else { + end = 0; + for (i = 1; i < key->range[2*j] + j; i++) { + if (key_separator) { + /* Skip body of key and separator */ + while (str[end]) { + if (str[end++] == key_separator) + break; + } + } else { + /* Skip leading blanks */ + while (isspace(str[end])) + end++; + /* Skip body of key */ + while (str[end]) { + if (isspace(str[end])) + break; + end++; + } + } + } + } + if (!j) start = end; + } + /* Strip leading whitespace if necessary */ +//XXX: skip_whitespace() + if (flags & FLAG_b) + while (isspace(str[start])) start++; + /* Strip trailing whitespace if necessary */ + if (flags & FLAG_bb) + while (end > start && isspace(str[end-1])) end--; + /* Handle offsets on start and end */ + if (key->range[3]) { + end += key->range[3] - 1; + if (end > len) end = len; + } + if (key->range[1]) { + start += key->range[1] - 1; + if (start > len) start = len; + } + /* Make the copy */ + if (end < start) end = start; + str = xstrndup(str+start, end-start); + /* Handle -d */ + if (flags & FLAG_d) { + for (start = end = 0; str[end]; end++) + if (isspace(str[end]) || isalnum(str[end])) + str[start++] = str[end]; + str[start] = '\0'; + } + /* Handle -i */ + if (flags & FLAG_i) { + for (start = end = 0; str[end]; end++) + if (isprint(str[end])) + str[start++] = str[end]; + str[start] = '\0'; + } + /* Handle -f */ + if (flags & FLAG_f) + for (i = 0; str[i]; i++) + str[i] = toupper(str[i]); + + return str; +} + +static struct sort_key *add_key(void) +{ + struct sort_key **pkey = &key_list; + while (*pkey) + pkey = &((*pkey)->next_key); + return *pkey = xzalloc(sizeof(struct sort_key)); +} + +#define GET_LINE(fp) \ + ((option_mask32 & FLAG_z) \ + ? bb_get_chunk_from_file(fp, NULL) \ + : xmalloc_fgetline(fp)) +#else +#define GET_LINE(fp) xmalloc_fgetline(fp) +#endif + +/* Iterate through keys list and perform comparisons */ +static int compare_keys(const void *xarg, const void *yarg) +{ + int flags = option_mask32, retval = 0; + char *x, *y; + +#if ENABLE_FEATURE_SORT_BIG + struct sort_key *key; + + for (key = key_list; !retval && key; key = key->next_key) { + flags = key->flags ? key->flags : option_mask32; + /* Chop out and modify key chunks, handling -dfib */ + x = get_key(*(char **)xarg, key, flags); + y = get_key(*(char **)yarg, key, flags); +#else + /* This curly bracket serves no purpose but to match the nesting + level of the for () loop we're not using */ + { + x = *(char **)xarg; + y = *(char **)yarg; +#endif + /* Perform actual comparison */ + switch (flags & 7) { + default: + bb_error_msg_and_die("unknown sort type"); + break; + /* Ascii sort */ + case 0: +#if ENABLE_LOCALE_SUPPORT + retval = strcoll(x, y); +#else + retval = strcmp(x, y); +#endif + break; +#if ENABLE_FEATURE_SORT_BIG + case FLAG_g: { + char *xx, *yy; + double dx = strtod(x, &xx); + double dy = strtod(y, &yy); + /* not numbers < NaN < -infinity < numbers < +infinity) */ + if (x == xx) + retval = (y == yy ? 0 : -1); + else if (y == yy) + retval = 1; + /* Check for isnan */ + else if (dx != dx) + retval = (dy != dy) ? 0 : -1; + else if (dy != dy) + retval = 1; + /* Check for infinity. Could underflow, but it avoids libm. */ + else if (1.0 / dx == 0.0) { + if (dx < 0) + retval = (1.0 / dy == 0.0 && dy < 0) ? 0 : -1; + else + retval = (1.0 / dy == 0.0 && dy > 0) ? 0 : 1; + } else if (1.0 / dy == 0.0) + retval = (dy < 0) ? 1 : -1; + else + retval = (dx > dy) ? 1 : ((dx < dy) ? -1 : 0); + break; + } + case FLAG_M: { + struct tm thyme; + int dx; + char *xx, *yy; + + xx = strptime(x, "%b", &thyme); + dx = thyme.tm_mon; + yy = strptime(y, "%b", &thyme); + if (!xx) + retval = (!yy) ? 0 : -1; + else if (!yy) + retval = 1; + else + retval = (dx == thyme.tm_mon) ? 0 : dx - thyme.tm_mon; + break; + } + /* Full floating point version of -n */ + case FLAG_n: { + double dx = atof(x); + double dy = atof(y); + retval = (dx > dy) ? 1 : ((dx < dy) ? -1 : 0); + break; + } + } /* switch */ + /* Free key copies. */ + if (x != *(char **)xarg) free(x); + if (y != *(char **)yarg) free(y); + /* if (retval) break; - done by for () anyway */ +#else + /* Integer version of -n for tiny systems */ + case FLAG_n: + retval = atoi(x) - atoi(y); + break; + } /* switch */ +#endif + } /* for */ + + /* Perform fallback sort if necessary */ + if (!retval && !(option_mask32 & FLAG_s)) + retval = strcmp(*(char **)xarg, *(char **)yarg); + + if (flags & FLAG_r) return -retval; + return retval; +} + +#if ENABLE_FEATURE_SORT_BIG +static unsigned str2u(char **str) +{ + unsigned long lu; + if (!isdigit((*str)[0])) + bb_error_msg_and_die("bad field specification"); + lu = strtoul(*str, str, 10); + if ((sizeof(long) > sizeof(int) && lu > INT_MAX) || !lu) + bb_error_msg_and_die("bad field specification"); + return lu; +} +#endif + +int sort_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int sort_main(int argc UNUSED_PARAM, char **argv) +{ + FILE *fp, *outfile = stdout; + char *line, **lines = NULL; + char *str_ignored, *str_o, *str_t; + llist_t *lst_k = NULL; + int i, flag; + int linecount = 0; + + xfunc_error_retval = 2; + + /* Parse command line options */ + /* -o and -t can be given at most once */ + opt_complementary = "o--o:t--t:" /* -t, -o: maximum one of each */ + "k::"; /* -k takes list */ + getopt32(argv, OPT_STR, &str_ignored, &str_ignored, &str_o, &lst_k, &str_t); +#if ENABLE_FEATURE_SORT_BIG + if (option_mask32 & FLAG_o) outfile = xfopen_for_write(str_o); + if (option_mask32 & FLAG_t) { + if (!str_t[0] || str_t[1]) + bb_error_msg_and_die("bad -t parameter"); + key_separator = str_t[0]; + } + /* parse sort key */ + while (lst_k) { + enum { + FLAG_allowed_for_k = + FLAG_n | /* Numeric sort */ + FLAG_g | /* Sort using strtod() */ + FLAG_M | /* Sort date */ + FLAG_b | /* Ignore leading blanks */ + FLAG_r | /* Reverse */ + FLAG_d | /* Ignore !(isalnum()|isspace()) */ + FLAG_f | /* Force uppercase */ + FLAG_i | /* Ignore !isprint() */ + 0 + }; + struct sort_key *key = add_key(); + char *str_k = llist_pop(&lst_k); + const char *temp2; + + i = 0; /* i==0 before comma, 1 after (-k3,6) */ + while (*str_k) { + /* Start of range */ + /* Cannot use bb_strtou - suffix can be a letter */ + key->range[2*i] = str2u(&str_k); + if (*str_k == '.') { + str_k++; + key->range[2*i+1] = str2u(&str_k); + } + while (*str_k) { + if (*str_k == ',' && !i++) { + str_k++; + break; + } /* no else needed: fall through to syntax error + because comma isn't in OPT_STR */ + temp2 = strchr(OPT_STR, *str_k); + if (!temp2) + bb_error_msg_and_die("unknown key option"); + flag = 1 << (temp2 - OPT_STR); + if (flag & ~FLAG_allowed_for_k) + bb_error_msg_and_die("unknown sort type"); + /* b after ',' means strip _trailing_ space */ + if (i && flag == FLAG_b) flag = FLAG_bb; + key->flags |= flag; + str_k++; + } + } + } +#endif + /* global b strips leading and trailing spaces */ + if (option_mask32 & FLAG_b) option_mask32 |= FLAG_bb; + + /* Open input files and read data */ + argv += optind; + if (!*argv) + *--argv = (char*)"-"; + do { + /* coreutils 6.9 compat: abort on first open error, + * do not continue to next file: */ + fp = xfopen_stdin(*argv); + for (;;) { + line = GET_LINE(fp); + if (!line) break; + lines = xrealloc_vector(lines, 6, linecount); + lines[linecount++] = line; + } + fclose_if_not_stdin(fp); + } while (*++argv); + +#if ENABLE_FEATURE_SORT_BIG + /* if no key, perform alphabetic sort */ + if (!key_list) + add_key()->range[0] = 1; + /* handle -c */ + if (option_mask32 & FLAG_c) { + int j = (option_mask32 & FLAG_u) ? -1 : 0; + for (i = 1; i < linecount; i++) + if (compare_keys(&lines[i-1], &lines[i]) > j) { + fprintf(stderr, "Check line %d\n", i); + return EXIT_FAILURE; + } + return EXIT_SUCCESS; + } +#endif + /* Perform the actual sort */ + qsort(lines, linecount, sizeof(char *), compare_keys); + /* handle -u */ + if (option_mask32 & FLAG_u) { + flag = 0; + /* coreutils 6.3 drop lines for which only key is the same */ + /* -- disabling last-resort compare... */ + option_mask32 |= FLAG_s; + for (i = 1; i < linecount; i++) { + if (!compare_keys(&lines[flag], &lines[i])) + free(lines[i]); + else + lines[++flag] = lines[i]; + } + if (linecount) linecount = flag+1; + } + /* Print it */ + flag = (option_mask32 & FLAG_z) ? '\0' : '\n'; + for (i = 0; i < linecount; i++) + fprintf(outfile, "%s%c", lines[i], flag); + + fflush_stdout_and_exit(EXIT_SUCCESS); +} diff --git a/coreutils/split.c b/coreutils/split.c new file mode 100644 index 0000000..f1ec64b --- /dev/null +++ b/coreutils/split.c @@ -0,0 +1,139 @@ +/* vi: set sw=4 ts=4: */ +/* + * split - split a file into pieces + * Copyright (c) 2007 Bernhard Reutner-Fischer + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ +/* BB_AUDIT: SUSv3 compliant + * SUSv3 requirements: + * http://www.opengroup.org/onlinepubs/009695399/utilities/split.html + */ +#include "libbb.h" + +static const struct suffix_mult split_suffices[] = { +#if ENABLE_FEATURE_SPLIT_FANCY + { "b", 512 }, +#endif + { "k", 1024 }, + { "m", 1024*1024 }, +#if ENABLE_FEATURE_SPLIT_FANCY + { "g", 1024*1024*1024 }, +#endif + { } +}; + +/* Increment the suffix part of the filename. + * Returns NULL if we are out of filenames. + */ +static char *next_file(char *old, unsigned suffix_len) +{ + size_t end = strlen(old); + unsigned i = 1; + char *curr; + + do { + curr = old + end - i; + if (*curr < 'z') { + *curr += 1; + break; + } + i++; + if (i > suffix_len) { + return NULL; + } + *curr = 'a'; + } while (1); + + return old; +} + +#define read_buffer bb_common_bufsiz1 +enum { READ_BUFFER_SIZE = COMMON_BUFSIZE - 1 }; + +#define SPLIT_OPT_l (1<<0) +#define SPLIT_OPT_b (1<<1) +#define SPLIT_OPT_a (1<<2) + +int split_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int split_main(int argc UNUSED_PARAM, char **argv) +{ + unsigned suffix_len = 2; + char *pfx; + char *count_p; + const char *sfx; + off_t cnt = 1000; + off_t remaining = 0; + unsigned opt; + ssize_t bytes_read, to_write; + char *src; + + opt_complementary = "?2:a+"; /* max 2 args; -a N */ + opt = getopt32(argv, "l:b:a:", &count_p, &count_p, &suffix_len); + + if (opt & SPLIT_OPT_l) + cnt = XATOOFF(count_p); + if (opt & SPLIT_OPT_b) // FIXME: also needs XATOOFF + cnt = xatoull_sfx(count_p, split_suffices); + sfx = "x"; + + argv += optind; + if (argv[0]) { + if (argv[1]) + sfx = argv[1]; + xmove_fd(xopen(argv[0], O_RDONLY), 0); + } else { + argv[0] = (char *) bb_msg_standard_input; + } + + if (NAME_MAX < strlen(sfx) + suffix_len) + bb_error_msg_and_die("suffix too long"); + + { + char *char_p = xzalloc(suffix_len + 1); + memset(char_p, 'a', suffix_len); + pfx = xasprintf("%s%s", sfx, char_p); + if (ENABLE_FEATURE_CLEAN_UP) + free(char_p); + } + + while (1) { + bytes_read = safe_read(STDIN_FILENO, read_buffer, READ_BUFFER_SIZE); + if (!bytes_read) + break; + if (bytes_read < 0) + bb_simple_perror_msg_and_die(argv[0]); + src = read_buffer; + do { + if (!remaining) { + if (!pfx) + bb_error_msg_and_die("suffixes exhausted"); + xmove_fd(xopen(pfx, O_WRONLY | O_CREAT | O_TRUNC), 1); + pfx = next_file(pfx, suffix_len); + remaining = cnt; + } + + if (opt & SPLIT_OPT_b) { + /* split by bytes */ + to_write = (bytes_read < remaining) ? bytes_read : remaining; + remaining -= to_write; + } else { + /* split by lines */ + /* can be sped up by using _memrchr_ + * and writing many lines at once... */ + char *end = memchr(src, '\n', bytes_read); + if (end) { + --remaining; + to_write = end - src + 1; + } else { + to_write = bytes_read; + } + } + + xwrite(STDOUT_FILENO, src, to_write); + bytes_read -= to_write; + src += to_write; + } while (bytes_read); + } + return EXIT_SUCCESS; +} diff --git a/coreutils/stat.c b/coreutils/stat.c new file mode 100644 index 0000000..c34c06a --- /dev/null +++ b/coreutils/stat.c @@ -0,0 +1,654 @@ +/* vi: set sw=4 ts=4: */ +/* + * stat -- display file or file system status + * + * Copyright (C) 2001, 2002, 2003, 2004, 2005 Free Software Foundation. + * Copyright (C) 2005 by Erik Andersen + * Copyright (C) 2005 by Mike Frysinger + * Copyright (C) 2006 by Yoshinori Sato + * + * Written by Michael Meskes + * Taken from coreutils and turned into a busybox applet by Mike Frysinger + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +#include "libbb.h" + +/* vars to control behavior */ +#define OPT_FILESYS (1 << 0) +#define OPT_TERSE (1 << 1) +#define OPT_DEREFERENCE (1 << 2) +#define OPT_SELINUX (1 << 3) + +#if ENABLE_FEATURE_STAT_FORMAT +typedef bool (*statfunc_ptr)(const char *, const char *); +#else +typedef bool (*statfunc_ptr)(const char *); +#endif + +static const char *file_type(const struct stat *st) +{ + /* See POSIX 1003.1-2001 XCU Table 4-8 lines 17093-17107 + * for some of these formats. + * To keep diagnostics grammatical in English, the + * returned string must start with a consonant. + */ + if (S_ISREG(st->st_mode)) return st->st_size == 0 ? "regular empty file" : "regular file"; + if (S_ISDIR(st->st_mode)) return "directory"; + if (S_ISBLK(st->st_mode)) return "block special file"; + if (S_ISCHR(st->st_mode)) return "character special file"; + if (S_ISFIFO(st->st_mode)) return "fifo"; + if (S_ISLNK(st->st_mode)) return "symbolic link"; + if (S_ISSOCK(st->st_mode)) return "socket"; + if (S_TYPEISMQ(st)) return "message queue"; + if (S_TYPEISSEM(st)) return "semaphore"; + if (S_TYPEISSHM(st)) return "shared memory object"; +#ifdef S_TYPEISTMO + if (S_TYPEISTMO(st)) return "typed memory object"; +#endif + return "weird file"; +} + +static const char *human_time(time_t t) +{ + /* Old + static char *str; + str = ctime(&t); + str[strlen(str)-1] = '\0'; + return str; + */ + /* coreutils 6.3 compat: */ + + /*static char buf[sizeof("YYYY-MM-DD HH:MM:SS.000000000")] ALIGN1;*/ +#define buf bb_common_bufsiz1 + + strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S.000000000", localtime(&t)); + return buf; +#undef buf +} + +/* Return the type of the specified file system. + * Some systems have statfvs.f_basetype[FSTYPSZ]. (AIX, HP-UX, and Solaris) + * Others have statfs.f_fstypename[MFSNAMELEN]. (NetBSD 1.5.2) + * Still others have neither and have to get by with f_type (Linux). + */ +static const char *human_fstype(uint32_t f_type) +{ + static const struct types { + uint32_t type; + const char *const fs; + } humantypes[] = { + { 0xADFF, "affs" }, + { 0x1Cd1, "devpts" }, + { 0x137D, "ext" }, + { 0xEF51, "ext2" }, + { 0xEF53, "ext2/ext3" }, + { 0x3153464a, "jfs" }, + { 0x58465342, "xfs" }, + { 0xF995E849, "hpfs" }, + { 0x9660, "isofs" }, + { 0x4000, "isofs" }, + { 0x4004, "isofs" }, + { 0x137F, "minix" }, + { 0x138F, "minix (30 char.)" }, + { 0x2468, "minix v2" }, + { 0x2478, "minix v2 (30 char.)" }, + { 0x4d44, "msdos" }, + { 0x4006, "fat" }, + { 0x564c, "novell" }, + { 0x6969, "nfs" }, + { 0x9fa0, "proc" }, + { 0x517B, "smb" }, + { 0x012FF7B4, "xenix" }, + { 0x012FF7B5, "sysv4" }, + { 0x012FF7B6, "sysv2" }, + { 0x012FF7B7, "coh" }, + { 0x00011954, "ufs" }, + { 0x012FD16D, "xia" }, + { 0x5346544e, "ntfs" }, + { 0x1021994, "tmpfs" }, + { 0x52654973, "reiserfs" }, + { 0x28cd3d45, "cramfs" }, + { 0x7275, "romfs" }, + { 0x858458f6, "romfs" }, + { 0x73717368, "squashfs" }, + { 0x62656572, "sysfs" }, + { 0, "UNKNOWN" } + }; + + int i; + + for (i = 0; humantypes[i].type; ++i) + if (humantypes[i].type == f_type) + break; + return humantypes[i].fs; +} + +#if ENABLE_FEATURE_STAT_FORMAT +static void strcatc(char *str, char c) +{ + int len = strlen(str); + str[len++] = c; + str[len] = '\0'; +} + +static void printfs(char *pformat, const char *msg) +{ + strcatc(pformat, 's'); + printf(pformat, msg); +} + +/* print statfs info */ +static void print_statfs(char *pformat, const char m, + const char *const filename, const void *data + USE_SELINUX(, security_context_t scontext)) +{ + const struct statfs *statfsbuf = data; + if (m == 'n') { + printfs(pformat, filename); + } else if (m == 'i') { + strcat(pformat, "Lx"); + printf(pformat, statfsbuf->f_fsid); + } else if (m == 'l') { + strcat(pformat, "lu"); + printf(pformat, statfsbuf->f_namelen); + } else if (m == 't') { + strcat(pformat, "lx"); + printf(pformat, (unsigned long) (statfsbuf->f_type)); /* no equiv */ + } else if (m == 'T') { + printfs(pformat, human_fstype(statfsbuf->f_type)); + } else if (m == 'b') { + strcat(pformat, "jd"); + printf(pformat, (intmax_t) (statfsbuf->f_blocks)); + } else if (m == 'f') { + strcat(pformat, "jd"); + printf(pformat, (intmax_t) (statfsbuf->f_bfree)); + } else if (m == 'a') { + strcat(pformat, "jd"); + printf(pformat, (intmax_t) (statfsbuf->f_bavail)); + } else if (m == 's' || m == 'S') { + strcat(pformat, "lu"); + printf(pformat, (unsigned long) (statfsbuf->f_bsize)); + } else if (m == 'c') { + strcat(pformat, "jd"); + printf(pformat, (intmax_t) (statfsbuf->f_files)); + } else if (m == 'd') { + strcat(pformat, "jd"); + printf(pformat, (intmax_t) (statfsbuf->f_ffree)); +#if ENABLE_SELINUX + } else if (m == 'C' && (option_mask32 & OPT_SELINUX)) { + printfs(pformat, scontext); +#endif + } else { + strcatc(pformat, 'c'); + printf(pformat, m); + } +} + +/* print stat info */ +static void print_stat(char *pformat, const char m, + const char *const filename, const void *data + USE_SELINUX(, security_context_t scontext)) +{ +#define TYPE_SIGNED(t) (! ((t) 0 < (t) -1)) + struct stat *statbuf = (struct stat *) data; + struct passwd *pw_ent; + struct group *gw_ent; + + if (m == 'n') { + printfs(pformat, filename); + } else if (m == 'N') { + strcatc(pformat, 's'); + if (S_ISLNK(statbuf->st_mode)) { + char *linkname = xmalloc_readlink_or_warn(filename); + if (linkname == NULL) + return; + /*printf("\"%s\" -> \"%s\"", filename, linkname); */ + printf(pformat, filename); + printf(" -> "); + printf(pformat, linkname); + free(linkname); + } else { + printf(pformat, filename); + } + } else if (m == 'd') { + strcat(pformat, "ju"); + printf(pformat, (uintmax_t) statbuf->st_dev); + } else if (m == 'D') { + strcat(pformat, "jx"); + printf(pformat, (uintmax_t) statbuf->st_dev); + } else if (m == 'i') { + strcat(pformat, "ju"); + printf(pformat, (uintmax_t) statbuf->st_ino); + } else if (m == 'a') { + strcat(pformat, "lo"); + printf(pformat, (unsigned long) (statbuf->st_mode & (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO))); + } else if (m == 'A') { + printfs(pformat, bb_mode_string(statbuf->st_mode)); + } else if (m == 'f') { + strcat(pformat, "lx"); + printf(pformat, (unsigned long) statbuf->st_mode); + } else if (m == 'F') { + printfs(pformat, file_type(statbuf)); + } else if (m == 'h') { + strcat(pformat, "lu"); + printf(pformat, (unsigned long) statbuf->st_nlink); + } else if (m == 'u') { + strcat(pformat, "lu"); + printf(pformat, (unsigned long) statbuf->st_uid); + } else if (m == 'U') { + setpwent(); + pw_ent = getpwuid(statbuf->st_uid); + printfs(pformat, (pw_ent != NULL) ? pw_ent->pw_name : "UNKNOWN"); + } else if (m == 'g') { + strcat(pformat, "lu"); + printf(pformat, (unsigned long) statbuf->st_gid); + } else if (m == 'G') { + setgrent(); + gw_ent = getgrgid(statbuf->st_gid); + printfs(pformat, (gw_ent != NULL) ? gw_ent->gr_name : "UNKNOWN"); + } else if (m == 't') { + strcat(pformat, "lx"); + printf(pformat, (unsigned long) major(statbuf->st_rdev)); + } else if (m == 'T') { + strcat(pformat, "lx"); + printf(pformat, (unsigned long) minor(statbuf->st_rdev)); + } else if (m == 's') { + strcat(pformat, "ju"); + printf(pformat, (uintmax_t) (statbuf->st_size)); + } else if (m == 'B') { + strcat(pformat, "lu"); + printf(pformat, (unsigned long) 512); //ST_NBLOCKSIZE + } else if (m == 'b') { + strcat(pformat, "ju"); + printf(pformat, (uintmax_t) statbuf->st_blocks); + } else if (m == 'o') { + strcat(pformat, "lu"); + printf(pformat, (unsigned long) statbuf->st_blksize); + } else if (m == 'x') { + printfs(pformat, human_time(statbuf->st_atime)); + } else if (m == 'X') { + strcat(pformat, TYPE_SIGNED(time_t) ? "ld" : "lu"); + printf(pformat, (unsigned long) statbuf->st_atime); + } else if (m == 'y') { + printfs(pformat, human_time(statbuf->st_mtime)); + } else if (m == 'Y') { + strcat(pformat, TYPE_SIGNED(time_t) ? "ld" : "lu"); + printf(pformat, (unsigned long) statbuf->st_mtime); + } else if (m == 'z') { + printfs(pformat, human_time(statbuf->st_ctime)); + } else if (m == 'Z') { + strcat(pformat, TYPE_SIGNED(time_t) ? "ld" : "lu"); + printf(pformat, (unsigned long) statbuf->st_ctime); +#if ENABLE_SELINUX + } else if (m == 'C' && (option_mask32 & OPT_SELINUX)) { + printfs(pformat, scontext); +#endif + } else { + strcatc(pformat, 'c'); + printf(pformat, m); + } +} + +static void print_it(const char *masterformat, const char *filename, + void (*print_func) (char*, char, const char*, const void* USE_SELINUX(, security_context_t scontext)), + const void *data + USE_SELINUX(, security_context_t scontext) ) +{ + /* Create a working copy of the format string */ + char *format = xstrdup(masterformat); + /* Add 2 to accomodate our conversion of the stat '%s' format string + * to the printf '%llu' one. */ + char *dest = xmalloc(strlen(format) + 2 + 1); + char *b; + + b = format; + while (b) { + size_t len; + char *p = strchr(b, '%'); + if (!p) { + /* coreutils 6.3 always prints at the end */ + /*fputs(b, stdout);*/ + puts(b); + break; + } + *p++ = '\0'; + fputs(b, stdout); + + /* dest = "%" */ + len = strspn(p, "#-+.I 0123456789"); + dest[0] = '%'; + memcpy(dest + 1, p, len); + dest[1 + len] = '\0'; + p += len; + + b = p + 1; + switch (*p) { + case '\0': + b = NULL; + /* fall through */ + case '%': + bb_putchar('%'); + break; + default: + /* Completes "%" with specifier and printfs */ + print_func(dest, *p, filename, data USE_SELINUX(,scontext)); + break; + } + } + + free(format); + free(dest); +} +#endif + +/* Stat the file system and print what we find. */ +#if !ENABLE_FEATURE_STAT_FORMAT +#define do_statfs(filename, format) do_statfs(filename) +#endif +static bool do_statfs(const char *filename, const char *format) +{ +#if !ENABLE_FEATURE_STAT_FORMAT + const char *format; +#endif + struct statfs statfsbuf; +#if ENABLE_SELINUX + security_context_t scontext = NULL; + + if (option_mask32 & OPT_SELINUX) { + if ((option_mask32 & OPT_DEREFERENCE + ? lgetfilecon(filename, &scontext) + : getfilecon(filename, &scontext) + ) < 0 + ) { + bb_perror_msg(filename); + return 0; + } + } +#endif + if (statfs(filename, &statfsbuf) != 0) { + bb_perror_msg("cannot read file system information for '%s'", filename); + return 0; + } + +#if ENABLE_FEATURE_STAT_FORMAT + if (format == NULL) { +#if !ENABLE_SELINUX + format = (option_mask32 & OPT_TERSE + ? "%n %i %l %t %s %b %f %a %c %d\n" + : " File: \"%n\"\n" + " ID: %-8i Namelen: %-7l Type: %T\n" + "Block size: %-10s\n" + "Blocks: Total: %-10b Free: %-10f Available: %a\n" + "Inodes: Total: %-10c Free: %d"); +#else + format = (option_mask32 & OPT_TERSE + ? (option_mask32 & OPT_SELINUX ? "%n %i %l %t %s %b %f %a %c %d %C\n": + "%n %i %l %t %s %b %f %a %c %d\n") + : (option_mask32 & OPT_SELINUX ? + " File: \"%n\"\n" + " ID: %-8i Namelen: %-7l Type: %T\n" + "Block size: %-10s\n" + "Blocks: Total: %-10b Free: %-10f Available: %a\n" + "Inodes: Total: %-10c Free: %d" + " S_context: %C\n": + " File: \"%n\"\n" + " ID: %-8i Namelen: %-7l Type: %T\n" + "Block size: %-10s\n" + "Blocks: Total: %-10b Free: %-10f Available: %a\n" + "Inodes: Total: %-10c Free: %d\n") + ); +#endif /* SELINUX */ + } + print_it(format, filename, print_statfs, &statfsbuf USE_SELINUX(, scontext)); +#else /* FEATURE_STAT_FORMAT */ + format = (option_mask32 & OPT_TERSE + ? "%s %llx %lu " + : " File: \"%s\"\n" + " ID: %-8Lx Namelen: %-7lu "); + printf(format, + filename, + statfsbuf.f_fsid, + statfsbuf.f_namelen); + + if (option_mask32 & OPT_TERSE) + printf("%lx ", (unsigned long) (statfsbuf.f_type)); + else + printf("Type: %s\n", human_fstype(statfsbuf.f_type)); + +#if !ENABLE_SELINUX + format = (option_mask32 & OPT_TERSE + ? "%lu %ld %ld %ld %ld %ld\n" + : "Block size: %-10lu\n" + "Blocks: Total: %-10jd Free: %-10jd Available: %jd\n" + "Inodes: Total: %-10jd Free: %jd\n"); + printf(format, + (unsigned long) (statfsbuf.f_bsize), + (intmax_t) (statfsbuf.f_blocks), + (intmax_t) (statfsbuf.f_bfree), + (intmax_t) (statfsbuf.f_bavail), + (intmax_t) (statfsbuf.f_files), + (intmax_t) (statfsbuf.f_ffree)); +#else + format = (option_mask32 & OPT_TERSE + ? (option_mask32 & OPT_SELINUX ? "%lu %ld %ld %ld %ld %ld %C\n": + "%lu %ld %ld %ld %ld %ld\n") + : (option_mask32 & OPT_SELINUX ? + "Block size: %-10lu\n" + "Blocks: Total: %-10jd Free: %-10jd Available: %jd\n" + "Inodes: Total: %-10jd Free: %jd" + "S_context: %C\n": + "Block size: %-10lu\n" + "Blocks: Total: %-10jd Free: %-10jd Available: %jd\n" + "Inodes: Total: %-10jd Free: %jd\n")); + printf(format, + (unsigned long) (statfsbuf.f_bsize), + (intmax_t) (statfsbuf.f_blocks), + (intmax_t) (statfsbuf.f_bfree), + (intmax_t) (statfsbuf.f_bavail), + (intmax_t) (statfsbuf.f_files), + (intmax_t) (statfsbuf.f_ffree), + scontext); + + if (scontext) + freecon(scontext); +#endif +#endif /* FEATURE_STAT_FORMAT */ + return 1; +} + +/* stat the file and print what we find */ +#if !ENABLE_FEATURE_STAT_FORMAT +#define do_stat(filename, format) do_stat(filename) +#endif +static bool do_stat(const char *filename, const char *format) +{ + struct stat statbuf; +#if ENABLE_SELINUX + security_context_t scontext = NULL; + + if (option_mask32 & OPT_SELINUX) { + if ((option_mask32 & OPT_DEREFERENCE + ? lgetfilecon(filename, &scontext) + : getfilecon(filename, &scontext) + ) < 0 + ) { + bb_perror_msg(filename); + return 0; + } + } +#endif + if ((option_mask32 & OPT_DEREFERENCE ? stat : lstat) (filename, &statbuf) != 0) { + bb_perror_msg("cannot stat '%s'", filename); + return 0; + } + +#if ENABLE_FEATURE_STAT_FORMAT + if (format == NULL) { +#if !ENABLE_SELINUX + if (option_mask32 & OPT_TERSE) { + format = "%n %s %b %f %u %g %D %i %h %t %T %X %Y %Z %o"; + } else { + if (S_ISBLK(statbuf.st_mode) || S_ISCHR(statbuf.st_mode)) { + format = + " File: \"%N\"\n" + " Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n" + "Device: %Dh/%dd\tInode: %-10i Links: %-5h" + " Device type: %t,%T\n" + "Access: (%04a/%10.10A) Uid: (%5u/%8U) Gid: (%5g/%8G)\n" + "Access: %x\n" "Modify: %y\n" "Change: %z\n"; + } else { + format = + " File: \"%N\"\n" + " Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n" + "Device: %Dh/%dd\tInode: %-10i Links: %h\n" + "Access: (%04a/%10.10A) Uid: (%5u/%8U) Gid: (%5g/%8G)\n" + "Access: %x\n" "Modify: %y\n" "Change: %z\n"; + } + } +#else + if (option_mask32 & OPT_TERSE) { + format = (option_mask32 & OPT_SELINUX ? + "%n %s %b %f %u %g %D %i %h %t %T %X %Y %Z %o %C\n": + "%n %s %b %f %u %g %D %i %h %t %T %X %Y %Z %o\n"); + } else { + if (S_ISBLK(statbuf.st_mode) || S_ISCHR(statbuf.st_mode)) { + format = (option_mask32 & OPT_SELINUX ? + " File: \"%N\"\n" + " Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n" + "Device: %Dh/%dd\tInode: %-10i Links: %-5h" + " Device type: %t,%T\n" + "Access: (%04a/%10.10A) Uid: (%5u/%8U) Gid: (%5g/%8G)\n" + " S_Context: %C\n" + "Access: %x\n" "Modify: %y\n" "Change: %z\n": + " File: \"%N\"\n" + " Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n" + "Device: %Dh/%dd\tInode: %-10i Links: %-5h" + " Device type: %t,%T\n" + "Access: (%04a/%10.10A) Uid: (%5u/%8U) Gid: (%5g/%8G)\n" + "Access: %x\n" "Modify: %y\n" "Change: %z\n"); + } else { + format = (option_mask32 & OPT_SELINUX ? + " File: \"%N\"\n" + " Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n" + "Device: %Dh/%dd\tInode: %-10i Links: %h\n" + "Access: (%04a/%10.10A) Uid: (%5u/%8U) Gid: (%5g/%8G)\n" + "S_Context: %C\n" + "Access: %x\n" "Modify: %y\n" "Change: %z\n": + " File: \"%N\"\n" + " Size: %-10s\tBlocks: %-10b IO Block: %-6o %F\n" + "Device: %Dh/%dd\tInode: %-10i Links: %h\n" + "Access: (%04a/%10.10A) Uid: (%5u/%8U) Gid: (%5g/%8G)\n" + "Access: %x\n" "Modify: %y\n" "Change: %z\n"); + } + } +#endif + } + print_it(format, filename, print_stat, &statbuf USE_SELINUX(, scontext)); +#else /* FEATURE_STAT_FORMAT */ + if (option_mask32 & OPT_TERSE) { + printf("%s %ju %ju %lx %lu %lu %jx %ju %lu %lx %lx %lu %lu %lu %lu" + SKIP_SELINUX("\n"), + filename, + (uintmax_t) (statbuf.st_size), + (uintmax_t) statbuf.st_blocks, + (unsigned long) statbuf.st_mode, + (unsigned long) statbuf.st_uid, + (unsigned long) statbuf.st_gid, + (uintmax_t) statbuf.st_dev, + (uintmax_t) statbuf.st_ino, + (unsigned long) statbuf.st_nlink, + (unsigned long) major(statbuf.st_rdev), + (unsigned long) minor(statbuf.st_rdev), + (unsigned long) statbuf.st_atime, + (unsigned long) statbuf.st_mtime, + (unsigned long) statbuf.st_ctime, + (unsigned long) statbuf.st_blksize + ); +#if ENABLE_SELINUX + if (option_mask32 & OPT_SELINUX) + printf(" %lc\n", *scontext); + else + bb_putchar('\n'); +#endif + } else { + char *linkname = NULL; + + struct passwd *pw_ent; + struct group *gw_ent; + setgrent(); + gw_ent = getgrgid(statbuf.st_gid); + setpwent(); + pw_ent = getpwuid(statbuf.st_uid); + + if (S_ISLNK(statbuf.st_mode)) + linkname = xmalloc_readlink_or_warn(filename); + if (linkname) + printf(" File: \"%s\" -> \"%s\"\n", filename, linkname); + else + printf(" File: \"%s\"\n", filename); + + printf(" Size: %-10ju\tBlocks: %-10ju IO Block: %-6lu %s\n" + "Device: %jxh/%jud\tInode: %-10ju Links: %-5lu", + (uintmax_t) (statbuf.st_size), + (uintmax_t) statbuf.st_blocks, + (unsigned long) statbuf.st_blksize, + file_type(&statbuf), + (uintmax_t) statbuf.st_dev, + (uintmax_t) statbuf.st_dev, + (uintmax_t) statbuf.st_ino, + (unsigned long) statbuf.st_nlink); + if (S_ISBLK(statbuf.st_mode) || S_ISCHR(statbuf.st_mode)) + printf(" Device type: %lx,%lx\n", + (unsigned long) major(statbuf.st_rdev), + (unsigned long) minor(statbuf.st_rdev)); + else + bb_putchar('\n'); + printf("Access: (%04lo/%10.10s) Uid: (%5lu/%8s) Gid: (%5lu/%8s)\n", + (unsigned long) (statbuf.st_mode & (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO)), + bb_mode_string(statbuf.st_mode), + (unsigned long) statbuf.st_uid, + (pw_ent != NULL) ? pw_ent->pw_name : "UNKNOWN", + (unsigned long) statbuf.st_gid, + (gw_ent != NULL) ? gw_ent->gr_name : "UNKNOWN"); +#if ENABLE_SELINUX + printf(" S_Context: %lc\n", *scontext); +#endif + printf("Access: %s\n" "Modify: %s\n" "Change: %s\n", + human_time(statbuf.st_atime), + human_time(statbuf.st_mtime), + human_time(statbuf.st_ctime)); + } +#endif /* FEATURE_STAT_FORMAT */ + return 1; +} + +int stat_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int stat_main(int argc, char **argv) +{ + USE_FEATURE_STAT_FORMAT(char *format = NULL;) + int i; + int ok = 1; + statfunc_ptr statfunc = do_stat; + + getopt32(argv, "ftL" + USE_SELINUX("Z") + USE_FEATURE_STAT_FORMAT("c:", &format) + ); + + if (option_mask32 & OPT_FILESYS) /* -f */ + statfunc = do_statfs; + if (argc == optind) /* files */ + bb_show_usage(); + +#if ENABLE_SELINUX + if (option_mask32 & OPT_SELINUX) { + selinux_or_die(); + } +#endif /* ENABLE_SELINUX */ + for (i = optind; i < argc; ++i) + ok &= statfunc(argv[i] USE_FEATURE_STAT_FORMAT(, format)); + + return (ok ? EXIT_SUCCESS : EXIT_FAILURE); +} diff --git a/coreutils/stty.c b/coreutils/stty.c new file mode 100644 index 0000000..3605e3c --- /dev/null +++ b/coreutils/stty.c @@ -0,0 +1,1439 @@ +/* vi: set sw=4 ts=4: */ +/* stty -- change and print terminal line settings + Copyright (C) 1990-1999 Free Software Foundation, Inc. + + Licensed under the GPL v2 or later, see the file LICENSE in this tarball. +*/ +/* Usage: stty [-ag] [-F device] [setting...] + + Options: + -a Write all current settings to stdout in human-readable form. + -g Write all current settings to stdout in stty-readable form. + -F Open and use the specified device instead of stdin + + If no args are given, write to stdout the baud rate and settings that + have been changed from their defaults. Mode reading and changes + are done on the specified device, or stdin if none was specified. + + David MacKenzie + + Special for busybox ported by Vladimir Oleynik 2001 + + */ + +#include "libbb.h" + +#ifndef _POSIX_VDISABLE +# define _POSIX_VDISABLE ((unsigned char) 0) +#endif + +#define Control(c) ((c) & 0x1f) +/* Canonical values for control characters */ +#ifndef CINTR +# define CINTR Control('c') +#endif +#ifndef CQUIT +# define CQUIT 28 +#endif +#ifndef CERASE +# define CERASE 127 +#endif +#ifndef CKILL +# define CKILL Control('u') +#endif +#ifndef CEOF +# define CEOF Control('d') +#endif +#ifndef CEOL +# define CEOL _POSIX_VDISABLE +#endif +#ifndef CSTART +# define CSTART Control('q') +#endif +#ifndef CSTOP +# define CSTOP Control('s') +#endif +#ifndef CSUSP +# define CSUSP Control('z') +#endif +#if defined(VEOL2) && !defined(CEOL2) +# define CEOL2 _POSIX_VDISABLE +#endif +/* ISC renamed swtch to susp for termios, but we'll accept either name */ +#if defined(VSUSP) && !defined(VSWTCH) +# define VSWTCH VSUSP +# define CSWTCH CSUSP +#endif +#if defined(VSWTCH) && !defined(CSWTCH) +# define CSWTCH _POSIX_VDISABLE +#endif + +/* SunOS 5.3 loses (^Z doesn't work) if 'swtch' is the same as 'susp'. + So the default is to disable 'swtch.' */ +#if defined(__sparc__) && defined(__svr4__) +# undef CSWTCH +# define CSWTCH _POSIX_VDISABLE +#endif + +#if defined(VWERSE) && !defined(VWERASE) /* AIX-3.2.5 */ +# define VWERASE VWERSE +#endif +#if defined(VDSUSP) && !defined(CDSUSP) +# define CDSUSP Control('y') +#endif +#if !defined(VREPRINT) && defined(VRPRNT) /* Irix 4.0.5 */ +# define VREPRINT VRPRNT +#endif +#if defined(VREPRINT) && !defined(CRPRNT) +# define CRPRNT Control('r') +#endif +#if defined(VWERASE) && !defined(CWERASE) +# define CWERASE Control('w') +#endif +#if defined(VLNEXT) && !defined(CLNEXT) +# define CLNEXT Control('v') +#endif +#if defined(VDISCARD) && !defined(VFLUSHO) +# define VFLUSHO VDISCARD +#endif +#if defined(VFLUSH) && !defined(VFLUSHO) /* Ultrix 4.2 */ +# define VFLUSHO VFLUSH +#endif +#if defined(CTLECH) && !defined(ECHOCTL) /* Ultrix 4.3 */ +# define ECHOCTL CTLECH +#endif +#if defined(TCTLECH) && !defined(ECHOCTL) /* Ultrix 4.2 */ +# define ECHOCTL TCTLECH +#endif +#if defined(CRTKIL) && !defined(ECHOKE) /* Ultrix 4.2 and 4.3 */ +# define ECHOKE CRTKIL +#endif +#if defined(VFLUSHO) && !defined(CFLUSHO) +# define CFLUSHO Control('o') +#endif +#if defined(VSTATUS) && !defined(CSTATUS) +# define CSTATUS Control('t') +#endif + +/* Which speeds to set */ +enum speed_setting { + input_speed, output_speed, both_speeds +}; + +/* Which member(s) of 'struct termios' a mode uses */ +enum { + /* Do NOT change the order or values, as mode_type_flag() + * depends on them */ + control, input, output, local, combination +}; + +/* Flags for 'struct mode_info' */ +#define SANE_SET 1 /* Set in 'sane' mode */ +#define SANE_UNSET 2 /* Unset in 'sane' mode */ +#define REV 4 /* Can be turned off by prepending '-' */ +#define OMIT 8 /* Don't display value */ + + +/* Each mode. + * This structure should be kept as small as humanly possible. + */ +struct mode_info { + const uint8_t type; /* Which structure element to change */ + const uint8_t flags; /* Setting and display options */ + /* only these values are ever used, so... */ +#if (CSIZE | NLDLY | CRDLY | TABDLY | BSDLY | VTDLY | FFDLY) < 0x100 + const uint8_t mask; +#elif (CSIZE | NLDLY | CRDLY | TABDLY | BSDLY | VTDLY | FFDLY) < 0x10000 + const uint16_t mask; +#else + const tcflag_t mask; /* Other bits to turn off for this mode */ +#endif + /* was using short here, but ppc32 was unhappy */ + const tcflag_t bits; /* Bits to set for this mode */ +}; + +enum { + /* Must match mode_name[] and mode_info[] order! */ + IDX_evenp = 0, + IDX_parity, + IDX_oddp, + IDX_nl, + IDX_ek, + IDX_sane, + IDX_cooked, + IDX_raw, + IDX_pass8, + IDX_litout, + IDX_cbreak, + IDX_crt, + IDX_dec, +#ifdef IXANY + IDX_decctlq, +#endif +#if defined(TABDLY) || defined(OXTABS) + IDX_tabs, +#endif +#if defined(XCASE) && defined(IUCLC) && defined(OLCUC) + IDX_lcase, + IDX_LCASE, +#endif +}; + +#define MI_ENTRY(N,T,F,B,M) N "\0" + +/* Mode names given on command line */ +static const char mode_name[] = + MI_ENTRY("evenp", combination, REV | OMIT, 0, 0 ) + MI_ENTRY("parity", combination, REV | OMIT, 0, 0 ) + MI_ENTRY("oddp", combination, REV | OMIT, 0, 0 ) + MI_ENTRY("nl", combination, REV | OMIT, 0, 0 ) + MI_ENTRY("ek", combination, OMIT, 0, 0 ) + MI_ENTRY("sane", combination, OMIT, 0, 0 ) + MI_ENTRY("cooked", combination, REV | OMIT, 0, 0 ) + MI_ENTRY("raw", combination, REV | OMIT, 0, 0 ) + MI_ENTRY("pass8", combination, REV | OMIT, 0, 0 ) + MI_ENTRY("litout", combination, REV | OMIT, 0, 0 ) + MI_ENTRY("cbreak", combination, REV | OMIT, 0, 0 ) + MI_ENTRY("crt", combination, OMIT, 0, 0 ) + MI_ENTRY("dec", combination, OMIT, 0, 0 ) +#ifdef IXANY + MI_ENTRY("decctlq", combination, REV | OMIT, 0, 0 ) +#endif +#if defined(TABDLY) || defined(OXTABS) + MI_ENTRY("tabs", combination, REV | OMIT, 0, 0 ) +#endif +#if defined(XCASE) && defined(IUCLC) && defined(OLCUC) + MI_ENTRY("lcase", combination, REV | OMIT, 0, 0 ) + MI_ENTRY("LCASE", combination, REV | OMIT, 0, 0 ) +#endif + MI_ENTRY("parenb", control, REV, PARENB, 0 ) + MI_ENTRY("parodd", control, REV, PARODD, 0 ) + MI_ENTRY("cs5", control, 0, CS5, CSIZE) + MI_ENTRY("cs6", control, 0, CS6, CSIZE) + MI_ENTRY("cs7", control, 0, CS7, CSIZE) + MI_ENTRY("cs8", control, 0, CS8, CSIZE) + MI_ENTRY("hupcl", control, REV, HUPCL, 0 ) + MI_ENTRY("hup", control, REV | OMIT, HUPCL, 0 ) + MI_ENTRY("cstopb", control, REV, CSTOPB, 0 ) + MI_ENTRY("cread", control, SANE_SET | REV, CREAD, 0 ) + MI_ENTRY("clocal", control, REV, CLOCAL, 0 ) +#ifdef CRTSCTS + MI_ENTRY("crtscts", control, REV, CRTSCTS, 0 ) +#endif + MI_ENTRY("ignbrk", input, SANE_UNSET | REV, IGNBRK, 0 ) + MI_ENTRY("brkint", input, SANE_SET | REV, BRKINT, 0 ) + MI_ENTRY("ignpar", input, REV, IGNPAR, 0 ) + MI_ENTRY("parmrk", input, REV, PARMRK, 0 ) + MI_ENTRY("inpck", input, REV, INPCK, 0 ) + MI_ENTRY("istrip", input, REV, ISTRIP, 0 ) + MI_ENTRY("inlcr", input, SANE_UNSET | REV, INLCR, 0 ) + MI_ENTRY("igncr", input, SANE_UNSET | REV, IGNCR, 0 ) + MI_ENTRY("icrnl", input, SANE_SET | REV, ICRNL, 0 ) + MI_ENTRY("ixon", input, REV, IXON, 0 ) + MI_ENTRY("ixoff", input, SANE_UNSET | REV, IXOFF, 0 ) + MI_ENTRY("tandem", input, REV | OMIT, IXOFF, 0 ) +#ifdef IUCLC + MI_ENTRY("iuclc", input, SANE_UNSET | REV, IUCLC, 0 ) +#endif +#ifdef IXANY + MI_ENTRY("ixany", input, SANE_UNSET | REV, IXANY, 0 ) +#endif +#ifdef IMAXBEL + MI_ENTRY("imaxbel", input, SANE_SET | REV, IMAXBEL, 0 ) +#endif + MI_ENTRY("opost", output, SANE_SET | REV, OPOST, 0 ) +#ifdef OLCUC + MI_ENTRY("olcuc", output, SANE_UNSET | REV, OLCUC, 0 ) +#endif +#ifdef OCRNL + MI_ENTRY("ocrnl", output, SANE_UNSET | REV, OCRNL, 0 ) +#endif +#ifdef ONLCR + MI_ENTRY("onlcr", output, SANE_SET | REV, ONLCR, 0 ) +#endif +#ifdef ONOCR + MI_ENTRY("onocr", output, SANE_UNSET | REV, ONOCR, 0 ) +#endif +#ifdef ONLRET + MI_ENTRY("onlret", output, SANE_UNSET | REV, ONLRET, 0 ) +#endif +#ifdef OFILL + MI_ENTRY("ofill", output, SANE_UNSET | REV, OFILL, 0 ) +#endif +#ifdef OFDEL + MI_ENTRY("ofdel", output, SANE_UNSET | REV, OFDEL, 0 ) +#endif +#ifdef NLDLY + MI_ENTRY("nl1", output, SANE_UNSET, NL1, NLDLY) + MI_ENTRY("nl0", output, SANE_SET, NL0, NLDLY) +#endif +#ifdef CRDLY + MI_ENTRY("cr3", output, SANE_UNSET, CR3, CRDLY) + MI_ENTRY("cr2", output, SANE_UNSET, CR2, CRDLY) + MI_ENTRY("cr1", output, SANE_UNSET, CR1, CRDLY) + MI_ENTRY("cr0", output, SANE_SET, CR0, CRDLY) +#endif + +#ifdef TABDLY + MI_ENTRY("tab3", output, SANE_UNSET, TAB3, TABDLY) + MI_ENTRY("tab2", output, SANE_UNSET, TAB2, TABDLY) + MI_ENTRY("tab1", output, SANE_UNSET, TAB1, TABDLY) + MI_ENTRY("tab0", output, SANE_SET, TAB0, TABDLY) +#else +# ifdef OXTABS + MI_ENTRY("tab3", output, SANE_UNSET, OXTABS, 0 ) +# endif +#endif + +#ifdef BSDLY + MI_ENTRY("bs1", output, SANE_UNSET, BS1, BSDLY) + MI_ENTRY("bs0", output, SANE_SET, BS0, BSDLY) +#endif +#ifdef VTDLY + MI_ENTRY("vt1", output, SANE_UNSET, VT1, VTDLY) + MI_ENTRY("vt0", output, SANE_SET, VT0, VTDLY) +#endif +#ifdef FFDLY + MI_ENTRY("ff1", output, SANE_UNSET, FF1, FFDLY) + MI_ENTRY("ff0", output, SANE_SET, FF0, FFDLY) +#endif + MI_ENTRY("isig", local, SANE_SET | REV, ISIG, 0 ) + MI_ENTRY("icanon", local, SANE_SET | REV, ICANON, 0 ) +#ifdef IEXTEN + MI_ENTRY("iexten", local, SANE_SET | REV, IEXTEN, 0 ) +#endif + MI_ENTRY("echo", local, SANE_SET | REV, ECHO, 0 ) + MI_ENTRY("echoe", local, SANE_SET | REV, ECHOE, 0 ) + MI_ENTRY("crterase", local, REV | OMIT, ECHOE, 0 ) + MI_ENTRY("echok", local, SANE_SET | REV, ECHOK, 0 ) + MI_ENTRY("echonl", local, SANE_UNSET | REV, ECHONL, 0 ) + MI_ENTRY("noflsh", local, SANE_UNSET | REV, NOFLSH, 0 ) +#ifdef XCASE + MI_ENTRY("xcase", local, SANE_UNSET | REV, XCASE, 0 ) +#endif +#ifdef TOSTOP + MI_ENTRY("tostop", local, SANE_UNSET | REV, TOSTOP, 0 ) +#endif +#ifdef ECHOPRT + MI_ENTRY("echoprt", local, SANE_UNSET | REV, ECHOPRT, 0 ) + MI_ENTRY("prterase", local, REV | OMIT, ECHOPRT, 0 ) +#endif +#ifdef ECHOCTL + MI_ENTRY("echoctl", local, SANE_SET | REV, ECHOCTL, 0 ) + MI_ENTRY("ctlecho", local, REV | OMIT, ECHOCTL, 0 ) +#endif +#ifdef ECHOKE + MI_ENTRY("echoke", local, SANE_SET | REV, ECHOKE, 0 ) + MI_ENTRY("crtkill", local, REV | OMIT, ECHOKE, 0 ) +#endif + ; + +#undef MI_ENTRY +#define MI_ENTRY(N,T,F,B,M) { T, F, M, B }, + +static const struct mode_info mode_info[] = { + /* This should be verbatim cut-n-paste copy of the above MI_ENTRYs */ + MI_ENTRY("evenp", combination, REV | OMIT, 0, 0 ) + MI_ENTRY("parity", combination, REV | OMIT, 0, 0 ) + MI_ENTRY("oddp", combination, REV | OMIT, 0, 0 ) + MI_ENTRY("nl", combination, REV | OMIT, 0, 0 ) + MI_ENTRY("ek", combination, OMIT, 0, 0 ) + MI_ENTRY("sane", combination, OMIT, 0, 0 ) + MI_ENTRY("cooked", combination, REV | OMIT, 0, 0 ) + MI_ENTRY("raw", combination, REV | OMIT, 0, 0 ) + MI_ENTRY("pass8", combination, REV | OMIT, 0, 0 ) + MI_ENTRY("litout", combination, REV | OMIT, 0, 0 ) + MI_ENTRY("cbreak", combination, REV | OMIT, 0, 0 ) + MI_ENTRY("crt", combination, OMIT, 0, 0 ) + MI_ENTRY("dec", combination, OMIT, 0, 0 ) +#ifdef IXANY + MI_ENTRY("decctlq", combination, REV | OMIT, 0, 0 ) +#endif +#if defined(TABDLY) || defined(OXTABS) + MI_ENTRY("tabs", combination, REV | OMIT, 0, 0 ) +#endif +#if defined(XCASE) && defined(IUCLC) && defined(OLCUC) + MI_ENTRY("lcase", combination, REV | OMIT, 0, 0 ) + MI_ENTRY("LCASE", combination, REV | OMIT, 0, 0 ) +#endif + MI_ENTRY("parenb", control, REV, PARENB, 0 ) + MI_ENTRY("parodd", control, REV, PARODD, 0 ) + MI_ENTRY("cs5", control, 0, CS5, CSIZE) + MI_ENTRY("cs6", control, 0, CS6, CSIZE) + MI_ENTRY("cs7", control, 0, CS7, CSIZE) + MI_ENTRY("cs8", control, 0, CS8, CSIZE) + MI_ENTRY("hupcl", control, REV, HUPCL, 0 ) + MI_ENTRY("hup", control, REV | OMIT, HUPCL, 0 ) + MI_ENTRY("cstopb", control, REV, CSTOPB, 0 ) + MI_ENTRY("cread", control, SANE_SET | REV, CREAD, 0 ) + MI_ENTRY("clocal", control, REV, CLOCAL, 0 ) +#ifdef CRTSCTS + MI_ENTRY("crtscts", control, REV, CRTSCTS, 0 ) +#endif + MI_ENTRY("ignbrk", input, SANE_UNSET | REV, IGNBRK, 0 ) + MI_ENTRY("brkint", input, SANE_SET | REV, BRKINT, 0 ) + MI_ENTRY("ignpar", input, REV, IGNPAR, 0 ) + MI_ENTRY("parmrk", input, REV, PARMRK, 0 ) + MI_ENTRY("inpck", input, REV, INPCK, 0 ) + MI_ENTRY("istrip", input, REV, ISTRIP, 0 ) + MI_ENTRY("inlcr", input, SANE_UNSET | REV, INLCR, 0 ) + MI_ENTRY("igncr", input, SANE_UNSET | REV, IGNCR, 0 ) + MI_ENTRY("icrnl", input, SANE_SET | REV, ICRNL, 0 ) + MI_ENTRY("ixon", input, REV, IXON, 0 ) + MI_ENTRY("ixoff", input, SANE_UNSET | REV, IXOFF, 0 ) + MI_ENTRY("tandem", input, REV | OMIT, IXOFF, 0 ) +#ifdef IUCLC + MI_ENTRY("iuclc", input, SANE_UNSET | REV, IUCLC, 0 ) +#endif +#ifdef IXANY + MI_ENTRY("ixany", input, SANE_UNSET | REV, IXANY, 0 ) +#endif +#ifdef IMAXBEL + MI_ENTRY("imaxbel", input, SANE_SET | REV, IMAXBEL, 0 ) +#endif + MI_ENTRY("opost", output, SANE_SET | REV, OPOST, 0 ) +#ifdef OLCUC + MI_ENTRY("olcuc", output, SANE_UNSET | REV, OLCUC, 0 ) +#endif +#ifdef OCRNL + MI_ENTRY("ocrnl", output, SANE_UNSET | REV, OCRNL, 0 ) +#endif +#ifdef ONLCR + MI_ENTRY("onlcr", output, SANE_SET | REV, ONLCR, 0 ) +#endif +#ifdef ONOCR + MI_ENTRY("onocr", output, SANE_UNSET | REV, ONOCR, 0 ) +#endif +#ifdef ONLRET + MI_ENTRY("onlret", output, SANE_UNSET | REV, ONLRET, 0 ) +#endif +#ifdef OFILL + MI_ENTRY("ofill", output, SANE_UNSET | REV, OFILL, 0 ) +#endif +#ifdef OFDEL + MI_ENTRY("ofdel", output, SANE_UNSET | REV, OFDEL, 0 ) +#endif +#ifdef NLDLY + MI_ENTRY("nl1", output, SANE_UNSET, NL1, NLDLY) + MI_ENTRY("nl0", output, SANE_SET, NL0, NLDLY) +#endif +#ifdef CRDLY + MI_ENTRY("cr3", output, SANE_UNSET, CR3, CRDLY) + MI_ENTRY("cr2", output, SANE_UNSET, CR2, CRDLY) + MI_ENTRY("cr1", output, SANE_UNSET, CR1, CRDLY) + MI_ENTRY("cr0", output, SANE_SET, CR0, CRDLY) +#endif + +#ifdef TABDLY + MI_ENTRY("tab3", output, SANE_UNSET, TAB3, TABDLY) + MI_ENTRY("tab2", output, SANE_UNSET, TAB2, TABDLY) + MI_ENTRY("tab1", output, SANE_UNSET, TAB1, TABDLY) + MI_ENTRY("tab0", output, SANE_SET, TAB0, TABDLY) +#else +# ifdef OXTABS + MI_ENTRY("tab3", output, SANE_UNSET, OXTABS, 0 ) +# endif +#endif + +#ifdef BSDLY + MI_ENTRY("bs1", output, SANE_UNSET, BS1, BSDLY) + MI_ENTRY("bs0", output, SANE_SET, BS0, BSDLY) +#endif +#ifdef VTDLY + MI_ENTRY("vt1", output, SANE_UNSET, VT1, VTDLY) + MI_ENTRY("vt0", output, SANE_SET, VT0, VTDLY) +#endif +#ifdef FFDLY + MI_ENTRY("ff1", output, SANE_UNSET, FF1, FFDLY) + MI_ENTRY("ff0", output, SANE_SET, FF0, FFDLY) +#endif + MI_ENTRY("isig", local, SANE_SET | REV, ISIG, 0 ) + MI_ENTRY("icanon", local, SANE_SET | REV, ICANON, 0 ) +#ifdef IEXTEN + MI_ENTRY("iexten", local, SANE_SET | REV, IEXTEN, 0 ) +#endif + MI_ENTRY("echo", local, SANE_SET | REV, ECHO, 0 ) + MI_ENTRY("echoe", local, SANE_SET | REV, ECHOE, 0 ) + MI_ENTRY("crterase", local, REV | OMIT, ECHOE, 0 ) + MI_ENTRY("echok", local, SANE_SET | REV, ECHOK, 0 ) + MI_ENTRY("echonl", local, SANE_UNSET | REV, ECHONL, 0 ) + MI_ENTRY("noflsh", local, SANE_UNSET | REV, NOFLSH, 0 ) +#ifdef XCASE + MI_ENTRY("xcase", local, SANE_UNSET | REV, XCASE, 0 ) +#endif +#ifdef TOSTOP + MI_ENTRY("tostop", local, SANE_UNSET | REV, TOSTOP, 0 ) +#endif +#ifdef ECHOPRT + MI_ENTRY("echoprt", local, SANE_UNSET | REV, ECHOPRT, 0 ) + MI_ENTRY("prterase", local, REV | OMIT, ECHOPRT, 0 ) +#endif +#ifdef ECHOCTL + MI_ENTRY("echoctl", local, SANE_SET | REV, ECHOCTL, 0 ) + MI_ENTRY("ctlecho", local, REV | OMIT, ECHOCTL, 0 ) +#endif +#ifdef ECHOKE + MI_ENTRY("echoke", local, SANE_SET | REV, ECHOKE, 0 ) + MI_ENTRY("crtkill", local, REV | OMIT, ECHOKE, 0 ) +#endif +}; + +enum { + NUM_mode_info = ARRAY_SIZE(mode_info) +}; + + +/* Control characters */ +struct control_info { + const uint8_t saneval; /* Value to set for 'stty sane' */ + const uint8_t offset; /* Offset in c_cc */ +}; + +enum { + /* Must match control_name[] and control_info[] order! */ + CIDX_intr = 0, + CIDX_quit, + CIDX_erase, + CIDX_kill, + CIDX_eof, + CIDX_eol, +#ifdef VEOL2 + CIDX_eol2, +#endif +#ifdef VSWTCH + CIDX_swtch, +#endif + CIDX_start, + CIDX_stop, + CIDX_susp, +#ifdef VDSUSP + CIDX_dsusp, +#endif +#ifdef VREPRINT + CIDX_rprnt, +#endif +#ifdef VWERASE + CIDX_werase, +#endif +#ifdef VLNEXT + CIDX_lnext, +#endif +#ifdef VFLUSHO + CIDX_flush, +#endif +#ifdef VSTATUS + CIDX_status, +#endif + CIDX_min, + CIDX_time, +}; + +#define CI_ENTRY(n,s,o) n "\0" + +/* Name given on command line */ +static const char control_name[] = + CI_ENTRY("intr", CINTR, VINTR ) + CI_ENTRY("quit", CQUIT, VQUIT ) + CI_ENTRY("erase", CERASE, VERASE ) + CI_ENTRY("kill", CKILL, VKILL ) + CI_ENTRY("eof", CEOF, VEOF ) + CI_ENTRY("eol", CEOL, VEOL ) +#ifdef VEOL2 + CI_ENTRY("eol2", CEOL2, VEOL2 ) +#endif +#ifdef VSWTCH + CI_ENTRY("swtch", CSWTCH, VSWTCH ) +#endif + CI_ENTRY("start", CSTART, VSTART ) + CI_ENTRY("stop", CSTOP, VSTOP ) + CI_ENTRY("susp", CSUSP, VSUSP ) +#ifdef VDSUSP + CI_ENTRY("dsusp", CDSUSP, VDSUSP ) +#endif +#ifdef VREPRINT + CI_ENTRY("rprnt", CRPRNT, VREPRINT) +#endif +#ifdef VWERASE + CI_ENTRY("werase", CWERASE, VWERASE ) +#endif +#ifdef VLNEXT + CI_ENTRY("lnext", CLNEXT, VLNEXT ) +#endif +#ifdef VFLUSHO + CI_ENTRY("flush", CFLUSHO, VFLUSHO ) +#endif +#ifdef VSTATUS + CI_ENTRY("status", CSTATUS, VSTATUS ) +#endif + /* These must be last because of the display routines */ + CI_ENTRY("min", 1, VMIN ) + CI_ENTRY("time", 0, VTIME ) + ; + +#undef CI_ENTRY +#define CI_ENTRY(n,s,o) { s, o }, + +static const struct control_info control_info[] = { + /* This should be verbatim cut-n-paste copy of the above CI_ENTRYs */ + CI_ENTRY("intr", CINTR, VINTR ) + CI_ENTRY("quit", CQUIT, VQUIT ) + CI_ENTRY("erase", CERASE, VERASE ) + CI_ENTRY("kill", CKILL, VKILL ) + CI_ENTRY("eof", CEOF, VEOF ) + CI_ENTRY("eol", CEOL, VEOL ) +#ifdef VEOL2 + CI_ENTRY("eol2", CEOL2, VEOL2 ) +#endif +#ifdef VSWTCH + CI_ENTRY("swtch", CSWTCH, VSWTCH ) +#endif + CI_ENTRY("start", CSTART, VSTART ) + CI_ENTRY("stop", CSTOP, VSTOP ) + CI_ENTRY("susp", CSUSP, VSUSP ) +#ifdef VDSUSP + CI_ENTRY("dsusp", CDSUSP, VDSUSP ) +#endif +#ifdef VREPRINT + CI_ENTRY("rprnt", CRPRNT, VREPRINT) +#endif +#ifdef VWERASE + CI_ENTRY("werase", CWERASE, VWERASE ) +#endif +#ifdef VLNEXT + CI_ENTRY("lnext", CLNEXT, VLNEXT ) +#endif +#ifdef VFLUSHO + CI_ENTRY("flush", CFLUSHO, VFLUSHO ) +#endif +#ifdef VSTATUS + CI_ENTRY("status", CSTATUS, VSTATUS ) +#endif + /* These must be last because of the display routines */ + CI_ENTRY("min", 1, VMIN ) + CI_ENTRY("time", 0, VTIME ) +}; + +enum { + NUM_control_info = ARRAY_SIZE(control_info) +}; + + +struct globals { + const char *device_name; // = bb_msg_standard_input; + /* The width of the screen, for output wrapping */ + unsigned max_col; // = 80; + /* Current position, to know when to wrap */ + unsigned current_col; + char buf[10]; +}; +#define G (*(struct globals*)&bb_common_bufsiz1) +#define INIT_G() do { \ + G.device_name = bb_msg_standard_input; \ + G.max_col = 80; \ +} while (0) + + +/* Return a string that is the printable representation of character CH */ +/* Adapted from 'cat' by Torbjorn Granlund */ +static const char *visible(unsigned ch) +{ + char *bpout = G.buf; + + if (ch == _POSIX_VDISABLE) + return ""; + + if (ch >= 128) { + ch -= 128; + *bpout++ = 'M'; + *bpout++ = '-'; + } + + if (ch < 32) { + *bpout++ = '^'; + *bpout++ = ch + 64; + } else if (ch < 127) { + *bpout++ = ch; + } else { + *bpout++ = '^'; + *bpout++ = '?'; + } + + *bpout = '\0'; + return G.buf; +} + +static tcflag_t *mode_type_flag(unsigned type, const struct termios *mode) +{ + static const uint8_t tcflag_offsets[] ALIGN1 = { + offsetof(struct termios, c_cflag), /* control */ + offsetof(struct termios, c_iflag), /* input */ + offsetof(struct termios, c_oflag), /* output */ + offsetof(struct termios, c_lflag) /* local */ + }; + + if (type <= local) { + return (tcflag_t*) (((char*)mode) + tcflag_offsets[type]); + } + return NULL; +} + +static void set_speed_or_die(enum speed_setting type, const char *const arg, + struct termios * const mode) +{ + speed_t baud; + + baud = tty_value_to_baud(xatou(arg)); + + if (type != output_speed) { /* either input or both */ + cfsetispeed(mode, baud); + } + if (type != input_speed) { /* either output or both */ + cfsetospeed(mode, baud); + } +} + +static NORETURN void perror_on_device_and_die(const char *fmt) +{ + bb_perror_msg_and_die(fmt, G.device_name); +} + +static void perror_on_device(const char *fmt) +{ + bb_perror_msg(fmt, G.device_name); +} + +/* Print format string MESSAGE and optional args. + Wrap to next line first if it won't fit. + Print a space first unless MESSAGE will start a new line */ +static void wrapf(const char *message, ...) +{ + char buf[128]; + va_list args; + unsigned buflen; + + va_start(args, message); + buflen = vsnprintf(buf, sizeof(buf), message, args); + va_end(args); + /* We seem to be called only with suitable lengths, but check if + somebody failed to adhere to this assumption just to be sure. */ + if (!buflen || buflen >= sizeof(buf)) return; + + if (G.current_col > 0) { + G.current_col++; + if (buf[0] != '\n') { + if (G.current_col + buflen >= G.max_col) { + bb_putchar('\n'); + G.current_col = 0; + } else + bb_putchar(' '); + } + } + fputs(buf, stdout); + G.current_col += buflen; + if (buf[buflen-1] == '\n') + G.current_col = 0; +} + +static void set_window_size(const int rows, const int cols) +{ + struct winsize win = { 0, 0, 0, 0 }; + + if (ioctl(STDIN_FILENO, TIOCGWINSZ, &win)) { + if (errno != EINVAL) { + goto bail; + } + memset(&win, 0, sizeof(win)); + } + + if (rows >= 0) + win.ws_row = rows; + if (cols >= 0) + win.ws_col = cols; + + if (ioctl(STDIN_FILENO, TIOCSWINSZ, (char *) &win)) +bail: + perror_on_device("%s"); +} + +static void display_window_size(const int fancy) +{ + const char *fmt_str = "%s\0%s: no size information for this device"; + unsigned width, height; + + if (get_terminal_width_height(STDIN_FILENO, &width, &height)) { + if ((errno != EINVAL) || ((fmt_str += 2), !fancy)) { + perror_on_device(fmt_str); + } + } else { + wrapf(fancy ? "rows %d; columns %d;" : "%d %d\n", + height, width); + } +} + +static const struct suffix_mult stty_suffixes[] = { + { "b", 512 }, + { "k", 1024 }, + { "B", 1024 }, + { } +}; + +static const struct mode_info *find_mode(const char *name) +{ + int i = index_in_strings(mode_name, name); + return i >= 0 ? &mode_info[i] : NULL; +} + +static const struct control_info *find_control(const char *name) +{ + int i = index_in_strings(control_name, name); + return i >= 0 ? &control_info[i] : NULL; +} + +enum { + param_need_arg = 0x80, + param_line = 1 | 0x80, + param_rows = 2 | 0x80, + param_cols = 3 | 0x80, + param_columns = 4 | 0x80, + param_size = 5, + param_speed = 6, + param_ispeed = 7 | 0x80, + param_ospeed = 8 | 0x80, +}; + +static int find_param(const char *const name) +{ + static const char params[] ALIGN1 = + "line\0" /* 1 */ + "rows\0" /* 2 */ + "cols\0" /* 3 */ + "columns\0" /* 4 */ + "size\0" /* 5 */ + "speed\0" /* 6 */ + "ispeed\0" + "ospeed\0"; + int i = index_in_strings(params, name) + 1; + if (i == 0) + return 0; + if (i != 5 && i != 6) + i |= 0x80; + return i; +} + +static int recover_mode(const char *arg, struct termios *mode) +{ + int i, n; + unsigned chr; + unsigned long iflag, oflag, cflag, lflag; + + /* Scan into temporaries since it is too much trouble to figure out + the right format for 'tcflag_t' */ + if (sscanf(arg, "%lx:%lx:%lx:%lx%n", + &iflag, &oflag, &cflag, &lflag, &n) != 4) + return 0; + mode->c_iflag = iflag; + mode->c_oflag = oflag; + mode->c_cflag = cflag; + mode->c_lflag = lflag; + arg += n; + for (i = 0; i < NCCS; ++i) { + if (sscanf(arg, ":%x%n", &chr, &n) != 1) + return 0; + mode->c_cc[i] = chr; + arg += n; + } + + /* Fail if there are too many fields */ + if (*arg != '\0') + return 0; + + return 1; +} + +static void display_recoverable(const struct termios *mode, + int UNUSED_PARAM dummy) +{ + int i; + printf("%lx:%lx:%lx:%lx", + (unsigned long) mode->c_iflag, (unsigned long) mode->c_oflag, + (unsigned long) mode->c_cflag, (unsigned long) mode->c_lflag); + for (i = 0; i < NCCS; ++i) + printf(":%x", (unsigned int) mode->c_cc[i]); + bb_putchar('\n'); +} + +static void display_speed(const struct termios *mode, int fancy) +{ + //01234567 8 9 + const char *fmt_str = "%lu %lu\n\0ispeed %lu baud; ospeed %lu baud;"; + unsigned long ispeed, ospeed; + + ospeed = ispeed = cfgetispeed(mode); + if (ispeed == 0 || ispeed == (ospeed = cfgetospeed(mode))) { + ispeed = ospeed; /* in case ispeed was 0 */ + //0123 4 5 6 7 8 9 + fmt_str = "%lu\n\0\0\0\0\0speed %lu baud;"; + } + if (fancy) fmt_str += 9; + wrapf(fmt_str, tty_baud_to_value(ispeed), tty_baud_to_value(ospeed)); +} + +static void do_display(const struct termios *mode, const int all) +{ + int i; + tcflag_t *bitsp; + unsigned long mask; + int prev_type = control; + + display_speed(mode, 1); + if (all) + display_window_size(1); +#ifdef HAVE_C_LINE + wrapf("line = %d;\n", mode->c_line); +#else + wrapf("\n"); +#endif + + for (i = 0; i != CIDX_min; ++i) { + /* If swtch is the same as susp, don't print both */ +#if VSWTCH == VSUSP + if (i == CIDX_swtch) + continue; +#endif + /* If eof uses the same slot as min, only print whichever applies */ +#if VEOF == VMIN + if ((mode->c_lflag & ICANON) == 0 + && (i == CIDX_eof || i == CIDX_eol) + ) { + continue; + } +#endif + wrapf("%s = %s;", nth_string(control_name, i), + visible(mode->c_cc[control_info[i].offset])); + } +#if VEOF == VMIN + if ((mode->c_lflag & ICANON) == 0) +#endif + wrapf("min = %d; time = %d;", mode->c_cc[VMIN], mode->c_cc[VTIME]); + if (G.current_col) wrapf("\n"); + + for (i = 0; i < NUM_mode_info; ++i) { + if (mode_info[i].flags & OMIT) + continue; + if (mode_info[i].type != prev_type) { + /* wrapf("\n"); */ + if (G.current_col) wrapf("\n"); + prev_type = mode_info[i].type; + } + + bitsp = mode_type_flag(mode_info[i].type, mode); + mask = mode_info[i].mask ? mode_info[i].mask : mode_info[i].bits; + if ((*bitsp & mask) == mode_info[i].bits) { + if (all || (mode_info[i].flags & SANE_UNSET)) + wrapf("-%s"+1, nth_string(mode_name, i)); + } else { + if ((all && mode_info[i].flags & REV) + || (!all && (mode_info[i].flags & (SANE_SET | REV)) == (SANE_SET | REV)) + ) { + wrapf("-%s", nth_string(mode_name, i)); + } + } + } + if (G.current_col) wrapf("\n"); +} + +static void sane_mode(struct termios *mode) +{ + int i; + tcflag_t *bitsp; + + for (i = 0; i < NUM_control_info; ++i) { +#if VMIN == VEOF + if (i == CIDX_min) + break; +#endif + mode->c_cc[control_info[i].offset] = control_info[i].saneval; + } + + for (i = 0; i < NUM_mode_info; ++i) { + if (mode_info[i].flags & SANE_SET) { + bitsp = mode_type_flag(mode_info[i].type, mode); + *bitsp = (*bitsp & ~((unsigned long)mode_info[i].mask)) + | mode_info[i].bits; + } else if (mode_info[i].flags & SANE_UNSET) { + bitsp = mode_type_flag(mode_info[i].type, mode); + *bitsp = *bitsp & ~((unsigned long)mode_info[i].mask) + & ~mode_info[i].bits; + } + } +} + +/* Save set_mode from #ifdef forest plague */ +#ifndef ONLCR +#define ONLCR 0 +#endif +#ifndef OCRNL +#define OCRNL 0 +#endif +#ifndef ONLRET +#define ONLRET 0 +#endif +#ifndef XCASE +#define XCASE 0 +#endif +#ifndef IXANY +#define IXANY 0 +#endif +#ifndef TABDLY +#define TABDLY 0 +#endif +#ifndef OXTABS +#define OXTABS 0 +#endif +#ifndef IUCLC +#define IUCLC 0 +#endif +#ifndef OLCUC +#define OLCUC 0 +#endif +#ifndef ECHOCTL +#define ECHOCTL 0 +#endif +#ifndef ECHOKE +#define ECHOKE 0 +#endif + +static void set_mode(const struct mode_info *info, int reversed, + struct termios *mode) +{ + tcflag_t *bitsp; + + bitsp = mode_type_flag(info->type, mode); + + if (bitsp) { + if (reversed) + *bitsp = *bitsp & ~info->mask & ~info->bits; + else + *bitsp = (*bitsp & ~info->mask) | info->bits; + return; + } + + /* Combination mode */ + if (info == &mode_info[IDX_evenp] || info == &mode_info[IDX_parity]) { + if (reversed) + mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8; + else + mode->c_cflag = (mode->c_cflag & ~PARODD & ~CSIZE) | PARENB | CS7; + } else if (info == &mode_info[IDX_oddp]) { + if (reversed) + mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8; + else + mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARODD | PARENB; + } else if (info == &mode_info[IDX_nl]) { + if (reversed) { + mode->c_iflag = (mode->c_iflag | ICRNL) & ~INLCR & ~IGNCR; + mode->c_oflag = (mode->c_oflag | ONLCR) & ~OCRNL & ~ONLRET; + } else { + mode->c_iflag = mode->c_iflag & ~ICRNL; + if (ONLCR) mode->c_oflag = mode->c_oflag & ~ONLCR; + } + } else if (info == &mode_info[IDX_ek]) { + mode->c_cc[VERASE] = CERASE; + mode->c_cc[VKILL] = CKILL; + } else if (info == &mode_info[IDX_sane]) { + sane_mode(mode); + } else if (info == &mode_info[IDX_cbreak]) { + if (reversed) + mode->c_lflag |= ICANON; + else + mode->c_lflag &= ~ICANON; + } else if (info == &mode_info[IDX_pass8]) { + if (reversed) { + mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB; + mode->c_iflag |= ISTRIP; + } else { + mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8; + mode->c_iflag &= ~ISTRIP; + } + } else if (info == &mode_info[IDX_litout]) { + if (reversed) { + mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB; + mode->c_iflag |= ISTRIP; + mode->c_oflag |= OPOST; + } else { + mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8; + mode->c_iflag &= ~ISTRIP; + mode->c_oflag &= ~OPOST; + } + } else if (info == &mode_info[IDX_raw] || info == &mode_info[IDX_cooked]) { + if ((info == &mode_info[IDX_raw] && reversed) + || (info == &mode_info[IDX_cooked] && !reversed) + ) { + /* Cooked mode */ + mode->c_iflag |= BRKINT | IGNPAR | ISTRIP | ICRNL | IXON; + mode->c_oflag |= OPOST; + mode->c_lflag |= ISIG | ICANON; +#if VMIN == VEOF + mode->c_cc[VEOF] = CEOF; +#endif +#if VTIME == VEOL + mode->c_cc[VEOL] = CEOL; +#endif + } else { + /* Raw mode */ + mode->c_iflag = 0; + mode->c_oflag &= ~OPOST; + mode->c_lflag &= ~(ISIG | ICANON | XCASE); + mode->c_cc[VMIN] = 1; + mode->c_cc[VTIME] = 0; + } + } + else if (IXANY && info == &mode_info[IDX_decctlq]) { + if (reversed) + mode->c_iflag |= IXANY; + else + mode->c_iflag &= ~IXANY; + } + else if (TABDLY && info == &mode_info[IDX_tabs]) { + if (reversed) + mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB3; + else + mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB0; + } + else if (OXTABS && info == &mode_info[IDX_tabs]) { + if (reversed) + mode->c_oflag |= OXTABS; + else + mode->c_oflag &= ~OXTABS; + } else + if (XCASE && IUCLC && OLCUC + && (info == &mode_info[IDX_lcase] || info == &mode_info[IDX_LCASE]) + ) { + if (reversed) { + mode->c_lflag &= ~XCASE; + mode->c_iflag &= ~IUCLC; + mode->c_oflag &= ~OLCUC; + } else { + mode->c_lflag |= XCASE; + mode->c_iflag |= IUCLC; + mode->c_oflag |= OLCUC; + } + } else if (info == &mode_info[IDX_crt]) { + mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE; + } else if (info == &mode_info[IDX_dec]) { + mode->c_cc[VINTR] = 3; /* ^C */ + mode->c_cc[VERASE] = 127; /* DEL */ + mode->c_cc[VKILL] = 21; /* ^U */ + mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE; + if (IXANY) mode->c_iflag &= ~IXANY; + } +} + +static void set_control_char_or_die(const struct control_info *info, + const char *arg, struct termios *mode) +{ + unsigned char value; + + if (info == &control_info[CIDX_min] || info == &control_info[CIDX_time]) + value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes); + else if (arg[0] == '\0' || arg[1] == '\0') + value = arg[0]; + else if (!strcmp(arg, "^-") || !strcmp(arg, "undef")) + value = _POSIX_VDISABLE; + else if (arg[0] == '^') { /* Ignore any trailing junk (^Cjunk) */ + value = arg[1] & 0x1f; /* Non-letters get weird results */ + if (arg[1] == '?') + value = 127; + } else + value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes); + mode->c_cc[info->offset] = value; +} + +#define STTY_require_set_attr (1 << 0) +#define STTY_speed_was_set (1 << 1) +#define STTY_verbose_output (1 << 2) +#define STTY_recoverable_output (1 << 3) +#define STTY_noargs (1 << 4) + +int stty_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int stty_main(int argc, char **argv) +{ + struct termios mode; + void (*output_func)(const struct termios *, const int); + const char *file_name = NULL; + int display_all = 0; + int stty_state; + int k; + + INIT_G(); + + stty_state = STTY_noargs; + output_func = do_display; + + /* First pass: only parse/verify command line params */ + k = 0; + while (argv[++k]) { + const struct mode_info *mp; + const struct control_info *cp; + const char *arg = argv[k]; + const char *argnext = argv[k+1]; + int param; + + if (arg[0] == '-') { + int i; + mp = find_mode(arg+1); + if (mp) { + if (!(mp->flags & REV)) + goto invalid_argument; + stty_state &= ~STTY_noargs; + continue; + } + /* It is an option - parse it */ + i = 0; + while (arg[++i]) { + switch (arg[i]) { + case 'a': + stty_state |= STTY_verbose_output; + output_func = do_display; + display_all = 1; + break; + case 'g': + stty_state |= STTY_recoverable_output; + output_func = display_recoverable; + break; + case 'F': + if (file_name) + bb_error_msg_and_die("only one device may be specified"); + file_name = &arg[i+1]; /* "-Fdevice" ? */ + if (!file_name[0]) { /* nope, "-F device" */ + int p = k+1; /* argv[p] is argnext */ + file_name = argnext; + if (!file_name) + bb_error_msg_and_die(bb_msg_requires_arg, "-F"); + /* remove -F param from arg[vc] */ + --argc; + while (argv[p]) { argv[p] = argv[p+1]; ++p; } + } + goto end_option; + default: + goto invalid_argument; + } + } + end_option: + continue; + } + + mp = find_mode(arg); + if (mp) { + stty_state &= ~STTY_noargs; + continue; + } + + cp = find_control(arg); + if (cp) { + if (!argnext) + bb_error_msg_and_die(bb_msg_requires_arg, arg); + /* called for the side effect of xfunc death only */ + set_control_char_or_die(cp, argnext, &mode); + stty_state &= ~STTY_noargs; + ++k; + continue; + } + + param = find_param(arg); + if (param & param_need_arg) { + if (!argnext) + bb_error_msg_and_die(bb_msg_requires_arg, arg); + ++k; + } + + switch (param) { +#ifdef HAVE_C_LINE + case param_line: +# ifndef TIOCGWINSZ + xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes); + break; +# endif /* else fall-through */ +#endif +#ifdef TIOCGWINSZ + case param_rows: + case param_cols: + case param_columns: + xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes); + break; + case param_size: +#endif + case param_speed: + break; + case param_ispeed: + /* called for the side effect of xfunc death only */ + set_speed_or_die(input_speed, argnext, &mode); + break; + case param_ospeed: + /* called for the side effect of xfunc death only */ + set_speed_or_die(output_speed, argnext, &mode); + break; + default: + if (recover_mode(arg, &mode) == 1) break; + if (tty_value_to_baud(xatou(arg)) != (speed_t) -1) break; + invalid_argument: + bb_error_msg_and_die("invalid argument '%s'", arg); + } + stty_state &= ~STTY_noargs; + } + + /* Specifying both -a and -g is an error */ + if ((stty_state & (STTY_verbose_output | STTY_recoverable_output)) == + (STTY_verbose_output | STTY_recoverable_output)) + bb_error_msg_and_die("verbose and stty-readable output styles are mutually exclusive"); + /* Specifying -a or -g with non-options is an error */ + if (!(stty_state & STTY_noargs) && + (stty_state & (STTY_verbose_output | STTY_recoverable_output))) + bb_error_msg_and_die("modes may not be set when specifying an output style"); + + /* Now it is safe to start doing things */ + if (file_name) { + int fd, fdflags; + G.device_name = file_name; + fd = xopen(G.device_name, O_RDONLY | O_NONBLOCK); + if (fd != STDIN_FILENO) { + dup2(fd, STDIN_FILENO); + close(fd); + } + fdflags = fcntl(STDIN_FILENO, F_GETFL); + if (fdflags < 0 || + fcntl(STDIN_FILENO, F_SETFL, fdflags & ~O_NONBLOCK) < 0) + perror_on_device_and_die("%s: cannot reset non-blocking mode"); + } + + /* Initialize to all zeroes so there is no risk memcmp will report a + spurious difference in an uninitialized portion of the structure */ + memset(&mode, 0, sizeof(mode)); + if (tcgetattr(STDIN_FILENO, &mode)) + perror_on_device_and_die("%s"); + + if (stty_state & (STTY_verbose_output | STTY_recoverable_output | STTY_noargs)) { + get_terminal_width_height(STDOUT_FILENO, &G.max_col, NULL); + output_func(&mode, display_all); + return EXIT_SUCCESS; + } + + /* Second pass: perform actions */ + k = 0; + while (argv[++k]) { + const struct mode_info *mp; + const struct control_info *cp; + const char *arg = argv[k]; + const char *argnext = argv[k+1]; + int param; + + if (arg[0] == '-') { + mp = find_mode(arg+1); + if (mp) { + set_mode(mp, 1 /* reversed */, &mode); + stty_state |= STTY_require_set_attr; + } + /* It is an option - already parsed. Skip it */ + continue; + } + + mp = find_mode(arg); + if (mp) { + set_mode(mp, 0 /* non-reversed */, &mode); + stty_state |= STTY_require_set_attr; + continue; + } + + cp = find_control(arg); + if (cp) { + ++k; + set_control_char_or_die(cp, argnext, &mode); + stty_state |= STTY_require_set_attr; + continue; + } + + param = find_param(arg); + if (param & param_need_arg) { + ++k; + } + + switch (param) { +#ifdef HAVE_C_LINE + case param_line: + mode.c_line = xatoul_sfx(argnext, stty_suffixes); + stty_state |= STTY_require_set_attr; + break; +#endif +#ifdef TIOCGWINSZ + case param_cols: + set_window_size(-1, xatoul_sfx(argnext, stty_suffixes)); + break; + case param_size: + display_window_size(0); + break; + case param_rows: + set_window_size(xatoul_sfx(argnext, stty_suffixes), -1); + break; +#endif + case param_speed: + display_speed(&mode, 0); + break; + case param_ispeed: + set_speed_or_die(input_speed, argnext, &mode); + stty_state |= (STTY_require_set_attr | STTY_speed_was_set); + break; + case param_ospeed: + set_speed_or_die(output_speed, argnext, &mode); + stty_state |= (STTY_require_set_attr | STTY_speed_was_set); + break; + default: + if (recover_mode(arg, &mode) == 1) + stty_state |= STTY_require_set_attr; + else /* true: if (tty_value_to_baud(xatou(arg)) != (speed_t) -1) */{ + set_speed_or_die(both_speeds, arg, &mode); + stty_state |= (STTY_require_set_attr | STTY_speed_was_set); + } /* else - impossible (caught in the first pass): + bb_error_msg_and_die("invalid argument '%s'", arg); */ + } + } + + if (stty_state & STTY_require_set_attr) { + struct termios new_mode; + + if (tcsetattr(STDIN_FILENO, TCSADRAIN, &mode)) + perror_on_device_and_die("%s"); + + /* POSIX (according to Zlotnick's book) tcsetattr returns zero if + it performs *any* of the requested operations. This means it + can report 'success' when it has actually failed to perform + some proper subset of the requested operations. To detect + this partial failure, get the current terminal attributes and + compare them to the requested ones */ + + /* Initialize to all zeroes so there is no risk memcmp will report a + spurious difference in an uninitialized portion of the structure */ + memset(&new_mode, 0, sizeof(new_mode)); + if (tcgetattr(STDIN_FILENO, &new_mode)) + perror_on_device_and_die("%s"); + + if (memcmp(&mode, &new_mode, sizeof(mode)) != 0) { +#ifdef CIBAUD + /* SunOS 4.1.3 (at least) has the problem that after this sequence, + tcgetattr (&m1); tcsetattr (&m1); tcgetattr (&m2); + sometimes (m1 != m2). The only difference is in the four bits + of the c_cflag field corresponding to the baud rate. To save + Sun users a little confusion, don't report an error if this + happens. But suppress the error only if we haven't tried to + set the baud rate explicitly -- otherwise we'd never give an + error for a true failure to set the baud rate */ + + new_mode.c_cflag &= (~CIBAUD); + if ((stty_state & STTY_speed_was_set) + || memcmp(&mode, &new_mode, sizeof(mode)) != 0) +#endif + perror_on_device_and_die("%s: cannot perform all requested operations"); + } + } + + return EXIT_SUCCESS; +} diff --git a/coreutils/sum.c b/coreutils/sum.c new file mode 100644 index 0000000..60f3b30 --- /dev/null +++ b/coreutils/sum.c @@ -0,0 +1,99 @@ +/* vi: set sw=4 ts=4: */ +/* + * sum -- checksum and count the blocks in a file + * Like BSD sum or SysV sum -r, except like SysV sum if -s option is given. + * + * Copyright (C) 86, 89, 91, 1995-2002, 2004 Free Software Foundation, Inc. + * Copyright (C) 2005 by Erik Andersen + * Copyright (C) 2005 by Mike Frysinger + * + * Written by Kayvan Aghaiepour and David MacKenzie + * Taken from coreutils and turned into a busybox applet by Mike Frysinger + * + * Licensed under the GPL v2 or later, see the file LICENSE in this tarball. + */ + +#include "libbb.h" + +enum { SUM_BSD, PRINT_NAME, SUM_SYSV }; + +/* BSD: calculate and print the rotated checksum and the size in 1K blocks + The checksum varies depending on sizeof (int). */ +/* SYSV: calculate and print the checksum and the size in 512-byte blocks */ +/* Return 1 if successful. */ +static unsigned sum_file(const char *file, unsigned type) +{ +#define buf bb_common_bufsiz1 + unsigned long long total_bytes = 0; + int fd, r; + /* The sum of all the input bytes, modulo (UINT_MAX + 1). */ + unsigned s = 0; + + fd = open_or_warn_stdin(file); + if (fd == -1) + return 0; + + while (1) { + size_t bytes_read = safe_read(fd, buf, BUFSIZ); + + if ((ssize_t)bytes_read <= 0) { + r = (fd && close(fd) != 0); + if (!bytes_read && !r) + /* no error */ + break; + bb_perror_msg(file); + return 0; + } + + total_bytes += bytes_read; + if (type >= SUM_SYSV) { + do s += buf[--bytes_read]; while (bytes_read); + } else { + r = 0; + do { + s = (s >> 1) + ((s & 1) << 15); + s += buf[r++]; + s &= 0xffff; /* Keep it within bounds. */ + } while (--bytes_read); + } + } + + if (type < PRINT_NAME) + file = ""; + if (type >= SUM_SYSV) { + r = (s & 0xffff) + ((s & 0xffffffff) >> 16); + s = (r & 0xffff) + (r >> 16); + printf("%d %llu %s\n", s, (total_bytes + 511) / 512, file); + } else + printf("%05d %5llu %s\n", s, (total_bytes + 1023) / 1024, file); + return 1; +#undef buf +} + +int sum_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int sum_main(int argc UNUSED_PARAM, char **argv) +{ + unsigned n; + unsigned type = SUM_BSD; + + n = getopt32(argv, "sr"); + argv += optind; + if (n & 1) type = SUM_SYSV; + /* give the bsd priority over sysv func */ + if (n & 2) type = SUM_BSD; + + if (!argv[0]) { + /* Do not print the name */ + n = sum_file("-", type); + } else { + /* Need to print the name if either + - more than one file given + - doing sysv */ + type += (argv[1] || type == SUM_SYSV); + n = 1; + do { + n &= sum_file(*argv, type); + } while (*++argv); + } + return !n; +} diff --git a/coreutils/sync.c b/coreutils/sync.c new file mode 100644 index 0000000..f00a3d0 --- /dev/null +++ b/coreutils/sync.c @@ -0,0 +1,25 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini sync implementation for busybox + * + * Copyright (C) 1995, 1996 by Bruce Perens . + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +/* BB_AUDIT SUSv3 N/A -- Matches GNU behavior. */ + +#include "libbb.h" + +/* This is a NOFORK applet. Be very careful! */ + +int sync_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int sync_main(int argc, char **argv UNUSED_PARAM) +{ + /* coreutils-6.9 compat */ + bb_warn_ignoring_args(argc - 1); + + sync(); + + return EXIT_SUCCESS; +} diff --git a/coreutils/tac.c b/coreutils/tac.c new file mode 100644 index 0000000..d70e23a --- /dev/null +++ b/coreutils/tac.c @@ -0,0 +1,106 @@ +/* vi: set sw=4 ts=4: */ +/* + * tac implementation for busybox + * + * Copyright (C) 2003 Yang Xiaopeng + * Copyright (C) 2007 Natanael Copa + * Copyright (C) 2007 Tito Ragusa + * + * Licensed under GPLv2, see file License in this tarball for details. + * + */ + +/* tac - concatenate and print files in reverse */ + +/* Based on Yang Xiaopeng's (yxp at hanwang.com.cn) patch + * http://www.uclibc.org/lists/busybox/2003-July/008813.html + */ + +#include "libbb.h" + +/* This is a NOEXEC applet. Be very careful! */ + +struct lstring { + int size; + char buf[1]; +}; + +int tac_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int tac_main(int argc UNUSED_PARAM, char **argv) +{ + char **name; + FILE *f; + struct lstring *line = NULL; + llist_t *list = NULL; + int retval = EXIT_SUCCESS; + +#if ENABLE_DESKTOP +/* tac from coreutils 6.9 supports: + -b, --before + attach the separator before instead of after + -r, --regex + interpret the separator as a regular expression + -s, --separator=STRING + use STRING as the separator instead of newline +We support none, but at least we will complain or handle "--": +*/ + getopt32(argv, ""); + argv += optind; +#else + argv++; +#endif + if (!*argv) + *--argv = (char *)"-"; + /* We will read from last file to first */ + name = argv; + while (*name) + name++; + + do { + int ch, i; + + name--; + f = fopen_or_warn_stdin(*name); + if (f == NULL) { + /* error message is printed by fopen_or_warn_stdin */ + retval = EXIT_FAILURE; + continue; + } + + errno = i = 0; + do { + ch = fgetc(f); + if (ch != EOF) { + if (!(i & 0x7f)) + /* Grow on every 128th char */ + line = xrealloc(line, i + 0x7f + sizeof(int) + 1); + line->buf[i++] = ch; + } + if (ch == '\n' || (ch == EOF && i != 0)) { + line = xrealloc(line, i + sizeof(int)); + line->size = i; + llist_add_to(&list, line); + line = NULL; + i = 0; + } + } while (ch != EOF); + /* fgetc sets errno to ENOENT on EOF, we don't want + * to warn on this non-error! */ + if (errno && errno != ENOENT) { + bb_simple_perror_msg(*name); + retval = EXIT_FAILURE; + } + } while (name != argv); + + while (list) { + line = (struct lstring *)list->data; + xwrite(STDOUT_FILENO, line->buf, line->size); + if (ENABLE_FEATURE_CLEAN_UP) { + free(llist_pop(&list)); + } else { + list = list->link; + } + } + + return retval; +} diff --git a/coreutils/tail.c b/coreutils/tail.c new file mode 100644 index 0000000..2505fc3 --- /dev/null +++ b/coreutils/tail.c @@ -0,0 +1,284 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini tail implementation for busybox + * + * Copyright (C) 2001 by Matt Kraai + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +/* BB_AUDIT SUSv3 compliant (need fancy for -c) */ +/* BB_AUDIT GNU compatible -c, -q, and -v options in 'fancy' configuration. */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/tail.html */ + +/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org) + * + * Pretty much rewritten to fix numerous bugs and reduce realloc() calls. + * Bugs fixed (although I may have forgotten one or two... it was pretty bad) + * 1) mixing printf/write without fflush()ing stdout + * 2) no check that any open files are present + * 3) optstring had -q taking an arg + * 4) no error checking on write in some cases, and a warning even then + * 5) q and s interaction bug + * 6) no check for lseek error + * 7) lseek attempted when count==0 even if arg was +0 (from top) + */ + +#include "libbb.h" + +static const struct suffix_mult tail_suffixes[] = { + { "b", 512 }, + { "k", 1024 }, + { "m", 1024*1024 }, + { } +}; + +struct globals { + bool status; +}; +#define G (*(struct globals*)&bb_common_bufsiz1) + +static void tail_xprint_header(const char *fmt, const char *filename) +{ + if (fdprintf(STDOUT_FILENO, fmt, filename) < 0) + bb_perror_nomsg_and_die(); +} + +static ssize_t tail_read(int fd, char *buf, size_t count) +{ + ssize_t r; + off_t current; + struct stat sbuf; + + /* (A good comment is missing here) */ + current = lseek(fd, 0, SEEK_CUR); + /* /proc files report zero st_size, don't lseek them. */ + if (fstat(fd, &sbuf) == 0 && sbuf.st_size) + if (sbuf.st_size < current) + lseek(fd, 0, SEEK_SET); + + r = full_read(fd, buf, count); + if (r < 0) { + bb_perror_msg(bb_msg_read_error); + G.status = EXIT_FAILURE; + } + + return r; +} + +static const char header_fmt[] ALIGN1 = "\n==> %s <==\n"; + +static unsigned eat_num(const char *p) +{ + if (*p == '-') + p++; + else if (*p == '+') { + p++; + G.status = 1; /* mark that we saw "+" */ + } + return xatou_sfx(p, tail_suffixes); +} + +int tail_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int tail_main(int argc, char **argv) +{ + unsigned count = 10; + unsigned sleep_period = 1; + bool from_top; + int header_threshhold = 1; + const char *str_c, *str_n; + + char *tailbuf; + size_t tailbufsize; + int taillen = 0; + int newlines_seen = 0; + int nfiles, nread, nwrite, i, opt; + unsigned seen; + + int *fds; + char *s, *buf; + const char *fmt; + +#if ENABLE_INCLUDE_SUSv2 || ENABLE_FEATURE_FANCY_TAIL + /* Allow legacy syntax of an initial numeric option without -n. */ + if (argv[1] && (argv[1][0] == '+' || argv[1][0] == '-') + && isdigit(argv[1][1]) + ) { + count = eat_num(&argv[1][1]); + argv++; + argc--; + } +#endif + + USE_FEATURE_FANCY_TAIL(opt_complementary = "s+";) /* -s N */ + opt = getopt32(argv, "fc:n:" USE_FEATURE_FANCY_TAIL("qs:v"), + &str_c, &str_n USE_FEATURE_FANCY_TAIL(,&sleep_period)); +#define FOLLOW (opt & 0x1) +#define COUNT_BYTES (opt & 0x2) + //if (opt & 0x1) // -f + if (opt & 0x2) count = eat_num(str_c); // -c + if (opt & 0x4) count = eat_num(str_n); // -n +#if ENABLE_FEATURE_FANCY_TAIL + if (opt & 0x8) header_threshhold = INT_MAX; // -q + if (opt & 0x20) header_threshhold = 0; // -v +#endif + argc -= optind; + argv += optind; + from_top = G.status; /* 1 if there was "-c +N" or "-n +N" */ + G.status = EXIT_SUCCESS; + + /* open all the files */ + fds = xmalloc(sizeof(int) * (argc + 1)); + if (!argv[0]) { + struct stat statbuf; + + if (!fstat(STDIN_FILENO, &statbuf) && S_ISFIFO(statbuf.st_mode)) { + opt &= ~1; /* clear FOLLOW */ + } + *argv = (char *) bb_msg_standard_input; + } + nfiles = i = 0; + do { + int fd = open_or_warn_stdin(argv[i]); + if (fd < 0) { + G.status = EXIT_FAILURE; + continue; + } + fds[nfiles] = fd; + argv[nfiles++] = argv[i]; + } while (++i < argc); + + if (!nfiles) + bb_error_msg_and_die("no files"); + + /* prepare the buffer */ + tailbufsize = BUFSIZ; + if (!from_top && COUNT_BYTES) { + if (tailbufsize < count + BUFSIZ) { + tailbufsize = count + BUFSIZ; + } + } + tailbuf = xmalloc(tailbufsize); + + /* tail the files */ + fmt = header_fmt + 1; /* Skip header leading newline on first output. */ + i = 0; + do { + if (nfiles > header_threshhold) { + tail_xprint_header(fmt, argv[i]); + fmt = header_fmt; + } + + /* Optimizing count-bytes case if the file is seekable. + * Beware of backing up too far. + * Also we exclude files with size 0 (because of /proc/xxx) */ + if (COUNT_BYTES && !from_top) { + off_t current = lseek(fds[i], 0, SEEK_END); + if (current > 0) { + if (count == 0) + continue; /* showing zero lines is easy :) */ + current -= count; + if (current < 0) + current = 0; + xlseek(fds[i], current, SEEK_SET); + bb_copyfd_size(fds[i], STDOUT_FILENO, count); + continue; + } + } + + buf = tailbuf; + taillen = 0; + seen = 1; + newlines_seen = 0; + while ((nread = tail_read(fds[i], buf, tailbufsize-taillen)) > 0) { + if (from_top) { + nwrite = nread; + if (seen < count) { + if (COUNT_BYTES) { + nwrite -= (count - seen); + seen = count; + } else { + s = buf; + do { + --nwrite; + if (*s++ == '\n' && ++seen == count) { + break; + } + } while (nwrite); + } + } + xwrite(STDOUT_FILENO, buf + nread - nwrite, nwrite); + } else if (count) { + if (COUNT_BYTES) { + taillen += nread; + if (taillen > (int)count) { + memmove(tailbuf, tailbuf + taillen - count, count); + taillen = count; + } + } else { + int k = nread; + int newlines_in_buf = 0; + + do { /* count '\n' in last read */ + k--; + if (buf[k] == '\n') { + newlines_in_buf++; + } + } while (k); + + if (newlines_seen + newlines_in_buf < (int)count) { + newlines_seen += newlines_in_buf; + taillen += nread; + } else { + int extra = (buf[nread-1] != '\n'); + + k = newlines_seen + newlines_in_buf + extra - count; + s = tailbuf; + while (k) { + if (*s == '\n') { + k--; + } + s++; + } + taillen += nread - (s - tailbuf); + memmove(tailbuf, s, taillen); + newlines_seen = count - extra; + } + if (tailbufsize < (size_t)taillen + BUFSIZ) { + tailbufsize = taillen + BUFSIZ; + tailbuf = xrealloc(tailbuf, tailbufsize); + } + } + buf = tailbuf + taillen; + } + } /* while (tail_read() > 0) */ + if (!from_top) { + xwrite(STDOUT_FILENO, tailbuf, taillen); + } + } while (++i < nfiles); + + buf = xrealloc(tailbuf, BUFSIZ); + + fmt = NULL; + + if (FOLLOW) while (1) { + sleep(sleep_period); + i = 0; + do { + if (nfiles > header_threshhold) { + fmt = header_fmt; + } + while ((nread = tail_read(fds[i], buf, BUFSIZ)) > 0) { + if (fmt) { + tail_xprint_header(fmt, argv[i]); + fmt = NULL; + } + xwrite(STDOUT_FILENO, buf, nread); + } + } while (++i < nfiles); + } + if (ENABLE_FEATURE_CLEAN_UP) { + free(fds); + } + return G.status; +} diff --git a/coreutils/tee.c b/coreutils/tee.c new file mode 100644 index 0000000..dc947c9 --- /dev/null +++ b/coreutils/tee.c @@ -0,0 +1,107 @@ +/* vi: set sw=4 ts=4: */ +/* + * tee implementation for busybox + * + * Copyright (C) 2003 Manuel Novoa III + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +/* BB_AUDIT SUSv3 compliant */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/tee.html */ + +#include "libbb.h" +#include + +int tee_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int tee_main(int argc, char **argv) +{ + const char *mode = "w\0a"; + FILE **files; + FILE **fp; + char **names; + char **np; + char retval; +//TODO: make unconditional +#if ENABLE_FEATURE_TEE_USE_BLOCK_IO + ssize_t c; +# define buf bb_common_bufsiz1 +#else + int c; +#endif + retval = getopt32(argv, "ia"); /* 'a' must be 2nd */ + argc -= optind; + argv += optind; + + mode += (retval & 2); /* Since 'a' is the 2nd option... */ + + if (retval & 1) { + signal(SIGINT, SIG_IGN); /* TODO - switch to sigaction. (why?) */ + } + retval = EXIT_SUCCESS; + /* gnu tee ignores SIGPIPE in case one of the output files is a pipe + * that doesn't consume all its input. Good idea... */ + signal(SIGPIPE, SIG_IGN); + + /* Allocate an array of FILE *'s, with one extra for a sentinal. */ + fp = files = xzalloc(sizeof(FILE *) * (argc + 2)); + np = names = argv - 1; + + files[0] = stdout; + goto GOT_NEW_FILE; + do { + *fp = stdout; + if (NOT_LONE_DASH(*argv)) { + *fp = fopen_or_warn(*argv, mode); + if (*fp == NULL) { + retval = EXIT_FAILURE; + argv++; + continue; + } + } + *np = *argv++; + GOT_NEW_FILE: + setbuf(*fp, NULL); /* tee must not buffer output. */ + fp++; + np++; + } while (*argv); + /* names[0] will be filled later */ + +#if ENABLE_FEATURE_TEE_USE_BLOCK_IO + while ((c = safe_read(STDIN_FILENO, buf, sizeof(buf))) > 0) { + fp = files; + do + fwrite(buf, 1, c, *fp++); + while (*fp); + } + if (c < 0) { /* Make sure read errors are signaled. */ + retval = EXIT_FAILURE; + } +#else + setvbuf(stdout, NULL, _IONBF, 0); + while ((c = getchar()) != EOF) { + fp = files; + do + putc(c, *fp++); + while (*fp); + } +#endif + + /* Now we need to check for i/o errors on stdin and the various + * output files. Since we know that the first entry in the output + * file table is stdout, we can save one "if ferror" test by + * setting the first entry to stdin and checking stdout error + * status with fflush_stdout_and_exit()... although fflush()ing + * is unnecessary here. */ + np = names; + fp = files; + names[0] = (char *) bb_msg_standard_input; + files[0] = stdin; + do { /* Now check for input and output errors. */ + /* Checking ferror should be sufficient, but we may want to fclose. + * If we do, remember not to close stdin! */ + die_if_ferror(*fp++, *np++); + } while (*fp); + + fflush_stdout_and_exit(retval); +} diff --git a/coreutils/test.c b/coreutils/test.c new file mode 100644 index 0000000..ae40192 --- /dev/null +++ b/coreutils/test.c @@ -0,0 +1,770 @@ +/* vi: set sw=4 ts=4: */ +/* + * test implementation for busybox + * + * Copyright (c) by a whole pile of folks: + * + * test(1); version 7-like -- author Erik Baalbergen + * modified by Eric Gisin to be used as built-in. + * modified by Arnold Robbins to add SVR3 compatibility + * (-x -c -b -p -u -g -k) plus Korn's -L -nt -ot -ef and new -S (socket). + * modified by J.T. Conklin for NetBSD. + * modified by Herbert Xu to be used as built-in in ash. + * modified by Erik Andersen to be used + * in busybox. + * modified by Bernhard Reutner-Fischer to be useable (i.e. a bit less bloaty). + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + * + * Original copyright notice states: + * "This program is in the Public Domain." + */ + +#include "libbb.h" +#include + +/* This is a NOFORK applet. Be very careful! */ + +/* test_main() is called from shells, and we need to be extra careful here. + * This is true regardless of PREFER_APPLETS and STANDALONE_SHELL + * state. */ + + +/* test(1) accepts the following grammar: + oexpr ::= aexpr | aexpr "-o" oexpr ; + aexpr ::= nexpr | nexpr "-a" aexpr ; + nexpr ::= primary | "!" primary + primary ::= unary-operator operand + | operand binary-operator operand + | operand + | "(" oexpr ")" + ; + unary-operator ::= "-r"|"-w"|"-x"|"-f"|"-d"|"-c"|"-b"|"-p"| + "-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"|"-L"|"-S"; + + binary-operator ::= "="|"=="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"| + "-nt"|"-ot"|"-ef"; + operand ::= +*/ + +#define TEST_DEBUG 0 + +enum token { + EOI, + FILRD, + FILWR, + FILEX, + FILEXIST, + FILREG, + FILDIR, + FILCDEV, + FILBDEV, + FILFIFO, + FILSOCK, + FILSYM, + FILGZ, + FILTT, + FILSUID, + FILSGID, + FILSTCK, + FILNT, + FILOT, + FILEQ, + FILUID, + FILGID, + STREZ, + STRNZ, + STREQ, + STRNE, + STRLT, + STRGT, + INTEQ, + INTNE, + INTGE, + INTGT, + INTLE, + INTLT, + UNOT, + BAND, + BOR, + LPAREN, + RPAREN, + OPERAND +}; +#define is_int_op(a) (((unsigned char)((a) - INTEQ)) <= 5) +#define is_str_op(a) (((unsigned char)((a) - STREZ)) <= 5) +#define is_file_op(a) (((unsigned char)((a) - FILNT)) <= 2) +#define is_file_access(a) (((unsigned char)((a) - FILRD)) <= 2) +#define is_file_type(a) (((unsigned char)((a) - FILREG)) <= 5) +#define is_file_bit(a) (((unsigned char)((a) - FILSUID)) <= 2) + +#if TEST_DEBUG +int depth; +#define nest_msg(...) do { \ + depth++; \ + fprintf(stderr, "%*s", depth*2, ""); \ + fprintf(stderr, __VA_ARGS__); \ +} while (0) +#define unnest_msg(...) do { \ + fprintf(stderr, "%*s", depth*2, ""); \ + fprintf(stderr, __VA_ARGS__); \ + depth--; \ +} while (0) +#define dbg_msg(...) do { \ + fprintf(stderr, "%*s", depth*2, ""); \ + fprintf(stderr, __VA_ARGS__); \ +} while (0) +#define unnest_msg_and_return(expr, ...) do { \ + number_t __res = (expr); \ + fprintf(stderr, "%*s", depth*2, ""); \ + fprintf(stderr, __VA_ARGS__, res); \ + depth--; \ + return __res; \ +} while (0) +static const char *const TOKSTR[] = { + "EOI", + "FILRD", + "FILWR", + "FILEX", + "FILEXIST", + "FILREG", + "FILDIR", + "FILCDEV", + "FILBDEV", + "FILFIFO", + "FILSOCK", + "FILSYM", + "FILGZ", + "FILTT", + "FILSUID", + "FILSGID", + "FILSTCK", + "FILNT", + "FILOT", + "FILEQ", + "FILUID", + "FILGID", + "STREZ", + "STRNZ", + "STREQ", + "STRNE", + "STRLT", + "STRGT", + "INTEQ", + "INTNE", + "INTGE", + "INTGT", + "INTLE", + "INTLT", + "UNOT", + "BAND", + "BOR", + "LPAREN", + "RPAREN", + "OPERAND" +}; +#else +#define nest_msg(...) ((void)0) +#define unnest_msg(...) ((void)0) +#define dbg_msg(...) ((void)0) +#define unnest_msg_and_return(expr, ...) return expr +#endif + +enum token_types { + UNOP, + BINOP, + BUNOP, + BBINOP, + PAREN +}; + +struct operator_t { + char op_text[4]; + unsigned char op_num, op_type; +}; + +static const struct operator_t ops[] = { + { "-r", FILRD , UNOP }, + { "-w", FILWR , UNOP }, + { "-x", FILEX , UNOP }, + { "-e", FILEXIST, UNOP }, + { "-f", FILREG , UNOP }, + { "-d", FILDIR , UNOP }, + { "-c", FILCDEV , UNOP }, + { "-b", FILBDEV , UNOP }, + { "-p", FILFIFO , UNOP }, + { "-u", FILSUID , UNOP }, + { "-g", FILSGID , UNOP }, + { "-k", FILSTCK , UNOP }, + { "-s", FILGZ , UNOP }, + { "-t", FILTT , UNOP }, + { "-z", STREZ , UNOP }, + { "-n", STRNZ , UNOP }, + { "-h", FILSYM , UNOP }, /* for backwards compat */ + + { "-O" , FILUID , UNOP }, + { "-G" , FILGID , UNOP }, + { "-L" , FILSYM , UNOP }, + { "-S" , FILSOCK, UNOP }, + { "=" , STREQ , BINOP }, + { "==" , STREQ , BINOP }, + { "!=" , STRNE , BINOP }, + { "<" , STRLT , BINOP }, + { ">" , STRGT , BINOP }, + { "-eq", INTEQ , BINOP }, + { "-ne", INTNE , BINOP }, + { "-ge", INTGE , BINOP }, + { "-gt", INTGT , BINOP }, + { "-le", INTLE , BINOP }, + { "-lt", INTLT , BINOP }, + { "-nt", FILNT , BINOP }, + { "-ot", FILOT , BINOP }, + { "-ef", FILEQ , BINOP }, + { "!" , UNOT , BUNOP }, + { "-a" , BAND , BBINOP }, + { "-o" , BOR , BBINOP }, + { "(" , LPAREN , PAREN }, + { ")" , RPAREN , PAREN }, +}; + + +#if ENABLE_FEATURE_TEST_64 +typedef int64_t number_t; +#else +typedef int number_t; +#endif + + +/* We try to minimize both static and stack usage. */ +struct test_statics { + char **args; + /* set only by check_operator(), either to bogus struct + * or points to matching operator_t struct. Never NULL. */ + const struct operator_t *last_operator; + gid_t *group_array; + int ngroups; + jmp_buf leaving; +}; + +/* See test_ptr_hack.c */ +extern struct test_statics *const test_ptr_to_statics; + +#define S (*test_ptr_to_statics) +#define args (S.args ) +#define last_operator (S.last_operator) +#define group_array (S.group_array ) +#define ngroups (S.ngroups ) +#define leaving (S.leaving ) + +#define INIT_S() do { \ + (*(struct test_statics**)&test_ptr_to_statics) = xzalloc(sizeof(S)); \ + barrier(); \ +} while (0) +#define DEINIT_S() do { \ + free(test_ptr_to_statics); \ +} while (0) + +static number_t primary(enum token n); + +static void syntax(const char *op, const char *msg) NORETURN; +static void syntax(const char *op, const char *msg) +{ + if (op && *op) { + bb_error_msg("%s: %s", op, msg); + } else { + bb_error_msg("%s: %s"+4, msg); + } + longjmp(leaving, 2); +} + +/* atoi with error detection */ +//XXX: FIXME: duplicate of existing libbb function? +static number_t getn(const char *s) +{ + char *p; +#if ENABLE_FEATURE_TEST_64 + long long r; +#else + long r; +#endif + + errno = 0; +#if ENABLE_FEATURE_TEST_64 + r = strtoll(s, &p, 10); +#else + r = strtol(s, &p, 10); +#endif + + if (errno != 0) + syntax(s, "out of range"); + + if (*(skip_whitespace(p))) + syntax(s, "bad number"); + + return r; +} + +/* UNUSED +static int newerf(const char *f1, const char *f2) +{ + struct stat b1, b2; + + return (stat(f1, &b1) == 0 && + stat(f2, &b2) == 0 && b1.st_mtime > b2.st_mtime); +} + +static int olderf(const char *f1, const char *f2) +{ + struct stat b1, b2; + + return (stat(f1, &b1) == 0 && + stat(f2, &b2) == 0 && b1.st_mtime < b2.st_mtime); +} + +static int equalf(const char *f1, const char *f2) +{ + struct stat b1, b2; + + return (stat(f1, &b1) == 0 && + stat(f2, &b2) == 0 && + b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino); +} +*/ + + +static enum token check_operator(char *s) +{ + static const struct operator_t no_op = { + .op_num = -1, + .op_type = -1 + }; + const struct operator_t *op; + + last_operator = &no_op; + if (s == NULL) { + return EOI; + } + + op = ops; + do { + if (strcmp(s, op->op_text) == 0) { + last_operator = op; + return op->op_num; + } + op++; + } while (op < ops + ARRAY_SIZE(ops)); + + return OPERAND; +} + + +static int binop(void) +{ + const char *opnd1, *opnd2; + const struct operator_t *op; + number_t val1, val2; + + opnd1 = *args; + check_operator(*++args); + op = last_operator; + + opnd2 = *++args; + if (opnd2 == NULL) + syntax(op->op_text, "argument expected"); + + if (is_int_op(op->op_num)) { + val1 = getn(opnd1); + val2 = getn(opnd2); + if (op->op_num == INTEQ) + return val1 == val2; + if (op->op_num == INTNE) + return val1 != val2; + if (op->op_num == INTGE) + return val1 >= val2; + if (op->op_num == INTGT) + return val1 > val2; + if (op->op_num == INTLE) + return val1 <= val2; + if (op->op_num == INTLT) + return val1 < val2; + } + if (is_str_op(op->op_num)) { + val1 = strcmp(opnd1, opnd2); + if (op->op_num == STREQ) + return val1 == 0; + if (op->op_num == STRNE) + return val1 != 0; + if (op->op_num == STRLT) + return val1 < 0; + if (op->op_num == STRGT) + return val1 > 0; + } + /* We are sure that these three are by now the only binops we didn't check + * yet, so we do not check if the class is correct: + */ +/* if (is_file_op(op->op_num)) */ + { + struct stat b1, b2; + + if (stat(opnd1, &b1) || stat(opnd2, &b2)) + return 0; /* false, since at least one stat failed */ + if (op->op_num == FILNT) + return b1.st_mtime > b2.st_mtime; + if (op->op_num == FILOT) + return b1.st_mtime < b2.st_mtime; + if (op->op_num == FILEQ) + return b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino; + } + return 1; /* NOTREACHED */ +} + + +static void initialize_group_array(void) +{ + ngroups = getgroups(0, NULL); + if (ngroups > 0) { + /* FIXME: ash tries so hard to not die on OOM, + * and we spoil it with just one xrealloc here */ + /* We realloc, because test_main can be entered repeatedly by shell. + * Testcase (ash): 'while true; do test -x some_file; done' + * and watch top. (some_file must have owner != you) */ + group_array = xrealloc(group_array, ngroups * sizeof(gid_t)); + getgroups(ngroups, group_array); + } +} + + +/* Return non-zero if GID is one that we have in our groups list. */ +//XXX: FIXME: duplicate of existing libbb function? +// see toplevel TODO file: +// possible code duplication ingroup() and is_a_group_member() +static int is_a_group_member(gid_t gid) +{ + int i; + + /* Short-circuit if possible, maybe saving a call to getgroups(). */ + if (gid == getgid() || gid == getegid()) + return 1; + + if (ngroups == 0) + initialize_group_array(); + + /* Search through the list looking for GID. */ + for (i = 0; i < ngroups; i++) + if (gid == group_array[i]) + return 1; + + return 0; +} + + +/* Do the same thing access(2) does, but use the effective uid and gid, + and don't make the mistake of telling root that any file is + executable. */ +static int test_eaccess(char *path, int mode) +{ + struct stat st; + unsigned int euid = geteuid(); + + if (stat(path, &st) < 0) + return -1; + + if (euid == 0) { + /* Root can read or write any file. */ + if (mode != X_OK) + return 0; + + /* Root can execute any file that has any one of the execute + bits set. */ + if (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) + return 0; + } + + if (st.st_uid == euid) /* owner */ + mode <<= 6; + else if (is_a_group_member(st.st_gid)) + mode <<= 3; + + if (st.st_mode & mode) + return 0; + + return -1; +} + + +static int filstat(char *nm, enum token mode) +{ + struct stat s; + unsigned i = i; /* gcc 3.x thinks it can be used uninitialized */ + + if (mode == FILSYM) { +#ifdef S_IFLNK + if (lstat(nm, &s) == 0) { + i = S_IFLNK; + goto filetype; + } +#endif + return 0; + } + + if (stat(nm, &s) != 0) + return 0; + if (mode == FILEXIST) + return 1; + if (is_file_access(mode)) { + if (mode == FILRD) + i = R_OK; + if (mode == FILWR) + i = W_OK; + if (mode == FILEX) + i = X_OK; + return test_eaccess(nm, i) == 0; + } + if (is_file_type(mode)) { + if (mode == FILREG) + i = S_IFREG; + if (mode == FILDIR) + i = S_IFDIR; + if (mode == FILCDEV) + i = S_IFCHR; + if (mode == FILBDEV) + i = S_IFBLK; + if (mode == FILFIFO) { +#ifdef S_IFIFO + i = S_IFIFO; +#else + return 0; +#endif + } + if (mode == FILSOCK) { +#ifdef S_IFSOCK + i = S_IFSOCK; +#else + return 0; +#endif + } + filetype: + return ((s.st_mode & S_IFMT) == i); + } + if (is_file_bit(mode)) { + if (mode == FILSUID) + i = S_ISUID; + if (mode == FILSGID) + i = S_ISGID; + if (mode == FILSTCK) + i = S_ISVTX; + return ((s.st_mode & i) != 0); + } + if (mode == FILGZ) + return s.st_size > 0L; + if (mode == FILUID) + return s.st_uid == geteuid(); + if (mode == FILGID) + return s.st_gid == getegid(); + return 1; /* NOTREACHED */ +} + + +static number_t nexpr(enum token n) +{ + number_t res; + + nest_msg(">nexpr(%s)\n", TOKSTR[n]); + if (n == UNOT) { + res = !nexpr(check_operator(*++args)); + unnest_msg("aexpr(%s)\n", TOKSTR[n]); + res = nexpr(n); + dbg_msg("aexpr: nexpr:%lld, next args:%s\n", res, args[1]); + if (check_operator(*++args) == BAND) { + dbg_msg("aexpr: arg is AND, next args:%s\n", args[1]); + res = aexpr(check_operator(*++args)) && res; + unnest_msg("oexpr(%s)\n", TOKSTR[n]); + res = aexpr(n); + dbg_msg("oexpr: aexpr:%lld, next args:%s\n", res, args[1]); + if (check_operator(*++args) == BOR) { + dbg_msg("oexpr: next arg is OR, next args:%s\n", args[1]); + res = oexpr(check_operator(*++args)) || res; + unnest_msg("primary(%s)\n", TOKSTR[n]); + if (n == EOI) { + syntax(NULL, "argument expected"); + } + if (n == LPAREN) { + res = oexpr(check_operator(*++args)); + if (check_operator(*++args) != RPAREN) + syntax(NULL, "closing paren expected"); + unnest_msg("op_type == BINOP) + unnest_msg_and_return(binop(), "op_type == UNOP) { + /* unary expression */ + if (args[1] == NULL) +// syntax(args0_op->op_text, "argument expected"); + goto check_emptiness; + args++; + if (n == STREZ) + unnest_msg_and_return(args[0][0] == '\0', "op_type == BINOP) { + /* args[2] is known to be NULL, isn't it bound to fail? */ + unnest_msg_and_return(binop(), "op_type == BINOP) { + /* "test [!] arg1 arg2" */ + args = &argv[0]; + res = (binop() == 0); + goto ret; + } + } + + /* Some complex expression. Undo '!' removal */ + if (negate) { + negate = 0; + //argc++; + argv--; + } +#endif + args = &argv[0]; + res = !oexpr(check_operator(*args)); + + if (*args != NULL && *++args != NULL) { + /* TODO: example when this happens? */ + bb_error_msg("%s: unknown operand", *args); + res = 2; + } + ret: + DEINIT_S(); +// return negate ? !res : res; + return res; +} diff --git a/coreutils/test_ptr_hack.c b/coreutils/test_ptr_hack.c new file mode 100644 index 0000000..a05203d --- /dev/null +++ b/coreutils/test_ptr_hack.c @@ -0,0 +1,23 @@ +/* vi: set sw=4 ts=4: */ +/* + * Copyright (C) 2008 by Denys Vlasenko + * + * Licensed under GPLv2, see file LICENSE in this tarball for details. + */ + +struct test_statics; + +#ifndef GCC_COMBINE + +/* We cheat here. It is declared as const ptr in libbb.h, + * but here we make it live in R/W memory */ +struct test_statics *test_ptr_to_statics; + +#else + +/* gcc -combine will see through and complain */ +/* Using alternative method which is more likely to break + * on weird architectures, compilers, linkers and so on */ +struct test_statics *const test_ptr_to_statics __attribute__ ((section (".data"))); + +#endif diff --git a/coreutils/touch.c b/coreutils/touch.c new file mode 100644 index 0000000..92f2023 --- /dev/null +++ b/coreutils/touch.c @@ -0,0 +1,91 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini touch implementation for busybox + * + * Copyright (C) 1999-2004 by Erik Andersen + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +/* BB_AUDIT SUSv3 _NOT_ compliant -- options -a, -m, -r, -t not supported. */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/touch.html */ + +/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org) + * + * Previous version called open() and then utime(). While this will be + * be necessary to implement -r and -t, it currently only makes things bigger. + * Also, exiting on a failure was a bug. All args should be processed. + */ + +#include "libbb.h" + +/* This is a NOFORK applet. Be very careful! */ + +/* coreutils implements: + * -a change only the access time + * -c, --no-create + * do not create any files + * -d, --date=STRING + * parse STRING and use it instead of current time + * -f (ignored, BSD compat) + * -m change only the modification time + * -r, --reference=FILE + * use this file's times instead of current time + * -t STAMP + * use [[CC]YY]MMDDhhmm[.ss] instead of current time + * --time=WORD + * change the specified time: WORD is access, atime, or use + */ + +int touch_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int touch_main(int argc UNUSED_PARAM, char **argv) +{ +#if ENABLE_DESKTOP + struct utimbuf timebuf; + char *reference_file = NULL; +#else +#define reference_file NULL +#define timebuf (*(struct utimbuf*)NULL) +#endif + int fd; + int status = EXIT_SUCCESS; + int flags = getopt32(argv, "c" USE_DESKTOP("r:") + /*ignored:*/ "fma" + USE_DESKTOP(, &reference_file)); + + flags &= 1; /* only -c bit is left */ + argv += optind; + if (!*argv) { + bb_show_usage(); + } + + if (reference_file) { + struct stat stbuf; + xstat(reference_file, &stbuf); + timebuf.actime = stbuf.st_atime; + timebuf.modtime = stbuf.st_mtime; + } + + do { + if (utime(*argv, reference_file ? &timebuf : NULL)) { + if (errno == ENOENT) { /* no such file */ + if (flags) { /* creation is disabled, so ignore */ + continue; + } + /* Try to create the file. */ + fd = open(*argv, O_RDWR | O_CREAT, + S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH + ); + if ((fd >= 0) && !close(fd)) { + if (reference_file) + utime(*argv, &timebuf); + continue; + } + } + status = EXIT_FAILURE; + bb_simple_perror_msg(*argv); + } + } while (*++argv); + + return status; +} diff --git a/coreutils/tr.c b/coreutils/tr.c new file mode 100644 index 0000000..c736c71 --- /dev/null +++ b/coreutils/tr.c @@ -0,0 +1,250 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini tr implementation for busybox + * + ** Copyright (c) 1987,1997, Prentice Hall All rights reserved. + * + * The name of Prentice Hall may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * Copyright (c) Michiel Huisjes + * + * This version of tr is adapted from Minix tr and was modified + * by Erik Andersen to be used in busybox. + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ +/* http://www.opengroup.org/onlinepubs/009695399/utilities/tr.html + * TODO: xdigit, graph, print + */ +#include "libbb.h" + +#define ASCII 0377 + +static void map(char *pvector, + unsigned char *string1, unsigned int string1_len, + unsigned char *string2, unsigned int string2_len) +{ + char last = '0'; + unsigned int i, j; + + for (j = 0, i = 0; i < string1_len; i++) { + if (string2_len <= j) + pvector[string1[i]] = last; + else + pvector[string1[i]] = last = string2[j++]; + } +} + +/* supported constructs: + * Ranges, e.g., 0-9 ==> 0123456789 + * Ranges, e.g., [0-9] ==> 0123456789 + * Escapes, e.g., \a ==> Control-G + * Character classes, e.g. [:upper:] ==> A...Z + * Equiv classess, e.g. [=A=] ==> A (hmmmmmmm?) + */ +static unsigned int expand(const char *arg, char *buffer) +{ + char *buffer_start = buffer; + unsigned i; /* can't be unsigned char: must be able to hold 256 */ + unsigned char ac; + + while (*arg) { + if (*arg == '\\') { + arg++; + *buffer++ = bb_process_escape_sequence(&arg); + continue; + } + if (arg[1] == '-') { /* "0-9..." */ + ac = arg[2]; + if (ac == '\0') { /* "0-": copy verbatim */ + *buffer++ = *arg++; /* copy '0' */ + continue; /* next iter will copy '-' and stop */ + } + i = *arg; + while (i <= ac) /* ok: i is unsigned _int_ */ + *buffer++ = i++; + arg += 3; /* skip 0-9 */ + continue; + } + if (*arg == '[') { /* "[xyz..." */ + arg++; + i = *arg++; + /* "[xyz...", i=x, arg points to y */ + if (ENABLE_FEATURE_TR_CLASSES && i == ':') { +#define CLO ":]\0" + static const char classes[] ALIGN1 = + "alpha"CLO "alnum"CLO "digit"CLO + "lower"CLO "upper"CLO "space"CLO + "blank"CLO "punct"CLO "cntrl"CLO; +#define CLASS_invalid 0 /* we increment the retval */ +#define CLASS_alpha 1 +#define CLASS_alnum 2 +#define CLASS_digit 3 +#define CLASS_lower 4 +#define CLASS_upper 5 +#define CLASS_space 6 +#define CLASS_blank 7 +#define CLASS_punct 8 +#define CLASS_cntrl 9 +//#define CLASS_xdigit 10 +//#define CLASS_graph 11 +//#define CLASS_print 12 + smalluint j; + { /* not really pretty.. */ + char *tmp = xstrndup(arg, 7); // warning: xdigit would need 8, not 7 + j = index_in_strings(classes, tmp) + 1; + free(tmp); + } + if (j == CLASS_alnum || j == CLASS_digit) { + for (i = '0'; i <= '9'; i++) + *buffer++ = i; + } + if (j == CLASS_alpha || j == CLASS_alnum || j == CLASS_upper) { + for (i = 'A'; i <= 'Z'; i++) + *buffer++ = i; + } + if (j == CLASS_alpha || j == CLASS_alnum || j == CLASS_lower) { + for (i = 'a'; i <= 'z'; i++) + *buffer++ = i; + } + if (j == CLASS_space || j == CLASS_blank) { + *buffer++ = '\t'; + if (j == CLASS_space) { + *buffer++ = '\n'; + *buffer++ = '\v'; + *buffer++ = '\f'; + *buffer++ = '\r'; + } + *buffer++ = ' '; + } + if (j == CLASS_punct || j == CLASS_cntrl) { + for (i = '\0'; i <= ASCII; i++) + if ((j == CLASS_punct && isprint(i) && !isalnum(i) && !isspace(i)) + || (j == CLASS_cntrl && iscntrl(i))) + *buffer++ = i; + } + if (j == CLASS_invalid) { + *buffer++ = '['; + *buffer++ = ':'; + continue; + } + break; + } + /* "[xyz...", i=x, arg points to y */ + if (ENABLE_FEATURE_TR_EQUIV && i == '=') { /* [=CHAR=] */ + *buffer++ = *arg; /* copy CHAR */ + if (!*arg || arg[1] != '=' || arg[2] != ']') + bb_show_usage(); + arg += 3; /* skip CHAR=] */ + continue; + } + if (i == '\0' || *arg != '-') { /* not [x-...] - copy verbatim */ + *buffer++ = '['; + arg--; /* points to x */ + continue; /* copy all, including eventual ']' */ + } + /* [x-z] */ + arg++; /* skip - */ + if (arg[0] == '\0' || arg[1] != ']') + bb_show_usage(); + ac = *arg++; + while (i <= ac) + *buffer++ = i++; + arg++; /* skip ] */ + continue; + } + *buffer++ = *arg++; + } + return (buffer - buffer_start); +} + +static int complement(char *buffer, int buffer_len) +{ + int i, j, ix; + char conv[ASCII + 2]; + + ix = 0; + for (i = '\0'; i <= ASCII; i++) { + for (j = 0; j < buffer_len; j++) + if (buffer[j] == i) + break; + if (j == buffer_len) + conv[ix++] = i & ASCII; + } + memcpy(buffer, conv, ix); + return ix; +} + +int tr_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int tr_main(int argc UNUSED_PARAM, char **argv) +{ + int output_length = 0, input_length; + int i; + smalluint flags; + ssize_t read_chars = 0; + size_t in_index = 0, out_index = 0; + unsigned last = UCHAR_MAX + 1; /* not equal to any char */ + unsigned char coded, c; + unsigned char *output = xmalloc(BUFSIZ); + char *vector = xzalloc((ASCII+1) * 3); + char *invec = vector + (ASCII+1); + char *outvec = vector + (ASCII+1) * 2; + +#define TR_OPT_complement (1 << 0) +#define TR_OPT_delete (1 << 1) +#define TR_OPT_squeeze_reps (1 << 2) + + flags = getopt32(argv, "+cds"); /* '+': stop at first non-option */ + argv += optind; + + for (i = 0; i <= ASCII; i++) { + vector[i] = i; + /*invec[i] = outvec[i] = FALSE; - done by xzalloc */ + } + +#define tr_buf bb_common_bufsiz1 + if (*argv != NULL) { + input_length = expand(*argv++, tr_buf); + if (flags & TR_OPT_complement) + input_length = complement(tr_buf, input_length); + if (*argv) { + if (argv[0][0] == '\0') + bb_error_msg_and_die("STRING2 cannot be empty"); + output_length = expand(*argv, (char *)output); + map(vector, (unsigned char *)tr_buf, input_length, output, output_length); + } + for (i = 0; i < input_length; i++) + invec[(unsigned char)tr_buf[i]] = TRUE; + for (i = 0; i < output_length; i++) + outvec[output[i]] = TRUE; + } + + for (;;) { + /* If we're out of input, flush output and read more input. */ + if ((ssize_t)in_index == read_chars) { + if (out_index) { + xwrite(STDOUT_FILENO, (char *)output, out_index); + out_index = 0; + } + read_chars = safe_read(STDIN_FILENO, tr_buf, BUFSIZ); + if (read_chars <= 0) { + if (read_chars < 0) + bb_perror_msg_and_die(bb_msg_read_error); + exit(EXIT_SUCCESS); + } + in_index = 0; + } + c = tr_buf[in_index++]; + coded = vector[c]; + if ((flags & TR_OPT_delete) && invec[c]) + continue; + if ((flags & TR_OPT_squeeze_reps) && last == coded + && (invec[c] || outvec[coded])) + continue; + output[out_index++] = last = coded; + } + /* NOTREACHED */ + return EXIT_SUCCESS; +} diff --git a/coreutils/true.c b/coreutils/true.c new file mode 100644 index 0000000..8a7e6ae --- /dev/null +++ b/coreutils/true.c @@ -0,0 +1,21 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini true implementation for busybox + * + * Copyright (C) 1999-2004 by Erik Andersen + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +/* BB_AUDIT SUSv3 compliant */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/true.html */ + +#include "libbb.h" + +/* This is a NOFORK applet. Be very careful! */ + +int true_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int true_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) +{ + return EXIT_SUCCESS; +} diff --git a/coreutils/tty.c b/coreutils/tty.c new file mode 100644 index 0000000..e832894 --- /dev/null +++ b/coreutils/tty.c @@ -0,0 +1,44 @@ +/* vi: set sw=4 ts=4: */ +/* + * tty implementation for busybox + * + * Copyright (C) 2003 Manuel Novoa III + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +/* BB_AUDIT SUSv3 compliant */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/tty.html */ + +#include "libbb.h" + +int tty_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int tty_main(int argc, char **argv SKIP_INCLUDE_SUSv2(UNUSED_PARAM)) +{ + const char *s; + USE_INCLUDE_SUSv2(int silent;) /* Note: No longer relevant in SUSv3. */ + int retval; + + xfunc_error_retval = 2; /* SUSv3 requires > 1 for error. */ + + USE_INCLUDE_SUSv2(silent = getopt32(argv, "s");) + USE_INCLUDE_SUSv2(argc -= optind;) + SKIP_INCLUDE_SUSv2(argc -= 1;) + + /* gnu tty outputs a warning that it is ignoring all args. */ + bb_warn_ignoring_args(argc); + + retval = 0; + + s = ttyname(0); + if (s == NULL) { + /* According to SUSv3, ttyname can fail with EBADF or ENOTTY. + * We know the file descriptor is good, so failure means not a tty. */ + s = "not a tty"; + retval = 1; + } + USE_INCLUDE_SUSv2(if (!silent) puts(s);) + SKIP_INCLUDE_SUSv2(puts(s);) + + fflush_stdout_and_exit(retval); +} diff --git a/coreutils/uname.c b/coreutils/uname.c new file mode 100644 index 0000000..e28285c --- /dev/null +++ b/coreutils/uname.c @@ -0,0 +1,99 @@ +/* vi: set sw=4 ts=4: */ +/* uname -- print system information + * Copyright (C) 1989-1999 Free Software Foundation, Inc. + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +/* BB_AUDIT SUSv3 compliant */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/uname.html */ + +/* Option Example + + -s, --sysname SunOS + -n, --nodename rocky8 + -r, --release 4.0 + -v, --version + -m, --machine sun + -a, --all SunOS rocky8 4.0 sun + + The default behavior is equivalent to '-s'. + + David MacKenzie */ + +/* Busyboxed by Erik Andersen */ + +/* Further size reductions by Glenn McGrath and Manuel Novoa III. */ + +/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org) + * + * Now does proper error checking on i/o. Plus some further space savings. + */ + +#include +#include "libbb.h" + +typedef struct { + struct utsname name; + char processor[8]; /* for "unknown" */ +} uname_info_t; + +static const char options[] ALIGN1 = "snrvmpa"; +static const unsigned short utsname_offset[] = { + offsetof(uname_info_t, name.sysname), + offsetof(uname_info_t, name.nodename), + offsetof(uname_info_t, name.release), + offsetof(uname_info_t, name.version), + offsetof(uname_info_t, name.machine), + offsetof(uname_info_t, processor) +}; + +int uname_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int uname_main(int argc UNUSED_PARAM, char **argv) +{ + uname_info_t uname_info; +#if defined(__sparc__) && defined(__linux__) + char *fake_sparc = getenv("FAKE_SPARC"); +#endif + const unsigned short *delta; + char toprint; + + toprint = getopt32(argv, options); + + if (argv[optind]) { /* coreutils-6.9 compat */ + bb_show_usage(); + } + + if (toprint & (1 << 6)) { /* -a => all opts on */ + toprint = 0x3f; + } + + if (toprint == 0) { /* no opts => -s (sysname) */ + toprint = 1; + } + + uname(&uname_info.name); /* never fails */ + +#if defined(__sparc__) && defined(__linux__) + if (fake_sparc && (fake_sparc[0] | 0x20) == 'y') { + strcpy(uname_info.name.machine, "sparc"); + } +#endif + + strcpy(uname_info.processor, "unknown"); + + delta = utsname_offset; + do { + if (toprint & 1) { + /* printf would not be safe here */ + fputs((char *)(&uname_info) + *delta, stdout); + if (toprint > 1) { + bb_putchar(' '); + } + } + ++delta; + } while (toprint >>= 1); + bb_putchar('\n'); + + fflush_stdout_and_exit(EXIT_SUCCESS); /* coreutils-6.9 compat */ +} diff --git a/coreutils/uniq.c b/coreutils/uniq.c new file mode 100644 index 0000000..0918621 --- /dev/null +++ b/coreutils/uniq.c @@ -0,0 +1,102 @@ +/* vi: set sw=4 ts=4: */ +/* + * uniq implementation for busybox + * + * Copyright (C) 2005 Manuel Novoa III + * + * Licensed under the GPL v2 or later, see the file LICENSE in this tarball. + */ + +/* BB_AUDIT SUSv3 compliant */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/uniq.html */ + +#include "libbb.h" + +static FILE *xgetoptfile_uniq_s(char **argv, int read0write2) +{ + const char *n; + + n = *argv; + if (n != NULL) { + if ((*n != '-') || n[1]) { + return xfopen(n, "r\0w" + read0write2); + } + } + return (read0write2) ? stdout : stdin; +} + +int uniq_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int uniq_main(int argc UNUSED_PARAM, char **argv) +{ + FILE *in, *out; + const char *s0, *e0, *s1, *e1, *input_filename; + unsigned long dups; + unsigned skip_fields, skip_chars, max_chars; + unsigned opt; + unsigned i; + + enum { + OPT_c = 0x1, + OPT_d = 0x2, + OPT_u = 0x4, + OPT_f = 0x8, + OPT_s = 0x10, + OPT_w = 0x20, + }; + + skip_fields = skip_chars = 0; + max_chars = -1; + + opt_complementary = "f+:s+:w+"; + opt = getopt32(argv, "cduf:s:w:", &skip_fields, &skip_chars, &max_chars); + argv += optind; + + input_filename = *argv; + + in = xgetoptfile_uniq_s(argv, 0); + if (*argv) { + ++argv; + } + out = xgetoptfile_uniq_s(argv, 2); + if (*argv && argv[1]) { + bb_show_usage(); + } + + s1 = e1 = NULL; /* prime the pump */ + + do { + s0 = s1; + e0 = e1; + dups = 0; + + /* gnu uniq ignores newlines */ + while ((s1 = xmalloc_fgetline(in)) != NULL) { + e1 = s1; + for (i = skip_fields; i; i--) { + e1 = skip_whitespace(e1); + e1 = skip_non_whitespace(e1); + } + for (i = skip_chars; *e1 && i; i--) { + ++e1; + } + + if (!s0 || strncmp(e0, e1, max_chars)) { + break; + } + + ++dups; /* note: testing for overflow seems excessive. */ + } + + if (s0) { + if (!(opt & (OPT_d << !!dups))) { /* (if dups, opt & OPT_e) */ + fprintf(out, "\0%ld " + (opt & 1), dups + 1); /* 1 == OPT_c */ + fprintf(out, "%s\n", s0); + } + free((void *)s0); + } + } while (s1); + + die_if_ferror(in, input_filename); + + fflush_stdout_and_exit(EXIT_SUCCESS); +} diff --git a/coreutils/usleep.c b/coreutils/usleep.c new file mode 100644 index 0000000..e7acd5f --- /dev/null +++ b/coreutils/usleep.c @@ -0,0 +1,28 @@ +/* vi: set sw=4 ts=4: */ +/* + * usleep implementation for busybox + * + * Copyright (C) 2003 Manuel Novoa III + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +/* BB_AUDIT SUSv3 N/A -- Apparently a busybox extension. */ + +#include "libbb.h" + +/* This is a NOFORK applet. Be very careful! */ + +int usleep_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int usleep_main(int argc UNUSED_PARAM, char **argv) +{ + if (!argv[1]) { + bb_show_usage(); + } + + if (usleep(xatou(argv[1]))) { + bb_perror_nomsg_and_die(); + } + + return EXIT_SUCCESS; +} diff --git a/coreutils/uudecode.c b/coreutils/uudecode.c new file mode 100644 index 0000000..0298a4b --- /dev/null +++ b/coreutils/uudecode.c @@ -0,0 +1,224 @@ +/* vi: set sw=4 ts=4: */ +/* + * Copyright 2003, Glenn McGrath + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + * + * Based on specification from + * http://www.opengroup.org/onlinepubs/007904975/utilities/uuencode.html + * + * Bugs: the spec doesn't mention anything about "`\n`\n" prior to the + * "end" line + */ + + +#include "libbb.h" + +static void read_stduu(FILE *src_stream, FILE *dst_stream) +{ + char *line; + + while ((line = xmalloc_fgetline(src_stream)) != NULL) { + int encoded_len, str_len; + char *line_ptr, *dst; + + if (strcmp(line, "end") == 0) { + return; /* the only non-error exit */ + } + + line_ptr = line; + while (*line_ptr) { + *line_ptr = (*line_ptr - 0x20) & 0x3f; + line_ptr++; + } + str_len = line_ptr - line; + + encoded_len = line[0] * 4 / 3; + /* Check that line is not too short. (we tolerate + * overly _long_ line to accomodate possible extra '`'). + * Empty line case is also caught here. */ + if (str_len <= encoded_len) { + break; /* go to bb_error_msg_and_die("short file"); */ + } + if (encoded_len <= 0) { + /* Ignore the "`\n" line, why is it even in the encode file ? */ + free(line); + continue; + } + if (encoded_len > 60) { + bb_error_msg_and_die("line too long"); + } + + dst = line; + line_ptr = line + 1; + do { + /* Merge four 6 bit chars to three 8 bit chars */ + *dst++ = line_ptr[0] << 2 | line_ptr[1] >> 4; + encoded_len--; + if (encoded_len == 0) { + break; + } + + *dst++ = line_ptr[1] << 4 | line_ptr[2] >> 2; + encoded_len--; + if (encoded_len == 0) { + break; + } + + *dst++ = line_ptr[2] << 6 | line_ptr[3]; + line_ptr += 4; + encoded_len -= 2; + } while (encoded_len > 0); + fwrite(line, 1, dst - line, dst_stream); + free(line); + } + bb_error_msg_and_die("short file"); +} + +static void read_base64(FILE *src_stream, FILE *dst_stream) +{ + int term_count = 1; + + while (1) { + char translated[4]; + int count = 0; + + while (count < 4) { + char *table_ptr; + int ch; + + /* Get next _valid_ character. + * global vector bb_uuenc_tbl_base64[] contains this string: + * "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=\n" + */ + do { + ch = fgetc(src_stream); + if (ch == EOF) { + bb_error_msg_and_die("short file"); + } + table_ptr = strchr(bb_uuenc_tbl_base64, ch); + } while (table_ptr == NULL); + + /* Convert encoded character to decimal */ + ch = table_ptr - bb_uuenc_tbl_base64; + + if (*table_ptr == '=') { + if (term_count == 0) { + translated[count] = '\0'; + break; + } + term_count++; + } else if (*table_ptr == '\n') { + /* Check for terminating line */ + if (term_count == 5) { + return; + } + term_count = 1; + continue; + } else { + translated[count] = ch; + count++; + term_count = 0; + } + } + + /* Merge 6 bit chars to 8 bit */ + if (count > 1) { + fputc(translated[0] << 2 | translated[1] >> 4, dst_stream); + } + if (count > 2) { + fputc(translated[1] << 4 | translated[2] >> 2, dst_stream); + } + if (count > 3) { + fputc(translated[2] << 6 | translated[3], dst_stream); + } + } +} + +int uudecode_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int uudecode_main(int argc UNUSED_PARAM, char **argv) +{ + FILE *src_stream; + char *outname = NULL; + char *line; + + opt_complementary = "?1"; /* 1 argument max */ + getopt32(argv, "o:", &outname); + argv += optind; + + if (!*argv) + *--argv = (char*)"-"; + src_stream = xfopen_stdin(*argv); + + /* Search for the start of the encoding */ + while ((line = xmalloc_fgetline(src_stream)) != NULL) { + void (*decode_fn_ptr)(FILE *src, FILE *dst); + char *line_ptr; + FILE *dst_stream; + int mode; + + if (strncmp(line, "begin-base64 ", 13) == 0) { + line_ptr = line + 13; + decode_fn_ptr = read_base64; + } else if (strncmp(line, "begin ", 6) == 0) { + line_ptr = line + 6; + decode_fn_ptr = read_stduu; + } else { + free(line); + continue; + } + + /* begin line found. decode and exit */ + mode = bb_strtou(line_ptr, NULL, 8); + if (outname == NULL) { + outname = strchr(line_ptr, ' '); + if ((outname == NULL) || (*outname == '\0')) { + break; + } + outname++; + } + dst_stream = stdout; + if (NOT_LONE_DASH(outname)) { + dst_stream = xfopen_for_write(outname); + fchmod(fileno(dst_stream), mode & (S_IRWXU | S_IRWXG | S_IRWXO)); + } + free(line); + decode_fn_ptr(src_stream, dst_stream); + /* fclose_if_not_stdin(src_stream); - redundant */ + return EXIT_SUCCESS; + } + bb_error_msg_and_die("no 'begin' line"); +} + +/* Test script. +Put this into an empty dir with busybox binary, an run. + +#!/bin/sh +test -x busybox || { echo "No ./busybox?"; exit; } +ln -sf busybox uudecode +ln -sf busybox uuencode +>A_null +echo -n A >A +echo -n AB >AB +echo -n ABC >ABC +echo -n ABCD >ABCD +echo -n ABCDE >ABCDE +echo -n ABCDEF >ABCDEF +cat busybox >A_bbox +for f in A*; do + echo uuencode $f + ./uuencode $f <$f >u_$f + ./uuencode -m $f <$f >m_$f +done +mkdir unpk_u unpk_m 2>/dev/null +for f in u_*; do + ./uudecode <$f -o unpk_u/${f:2} + diff -a ${f:2} unpk_u/${f:2} >/dev/null 2>&1 + echo uudecode $f: $? +done +for f in m_*; do + ./uudecode <$f -o unpk_m/${f:2} + diff -a ${f:2} unpk_m/${f:2} >/dev/null 2>&1 + echo uudecode $f: $? +done +*/ diff --git a/coreutils/uuencode.c b/coreutils/uuencode.c new file mode 100644 index 0000000..e19f996 --- /dev/null +++ b/coreutils/uuencode.c @@ -0,0 +1,61 @@ +/* vi: set sw=4 ts=4: */ +/* + * Copyright (C) 2000 by Glenn McGrath + * + * based on the function base64_encode from http.c in wget v1.6 + * Copyright (C) 1995, 1996, 1997, 1998, 2000 Free Software Foundation, Inc. + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +#include "libbb.h" + +enum { + SRC_BUF_SIZE = 45, /* This *MUST* be a multiple of 3 */ + DST_BUF_SIZE = 4 * ((SRC_BUF_SIZE + 2) / 3), +}; + +int uuencode_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int uuencode_main(int argc, char **argv) +{ + struct stat stat_buf; + int src_fd = STDIN_FILENO; + const char *tbl; + mode_t mode; + char src_buf[SRC_BUF_SIZE]; + char dst_buf[DST_BUF_SIZE + 1]; + + tbl = bb_uuenc_tbl_std; + mode = 0666 & ~umask(0666); + opt_complementary = "-1:?2"; /* must have 1 or 2 args */ + if (getopt32(argv, "m")) { + tbl = bb_uuenc_tbl_base64; + } + argv += optind; + if (argc == optind + 2) { + src_fd = xopen(*argv, O_RDONLY); + fstat(src_fd, &stat_buf); + mode = stat_buf.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO); + argv++; + } + + printf("begin%s %o %s", tbl == bb_uuenc_tbl_std ? "" : "-base64", mode, *argv); + while (1) { + size_t size = full_read(src_fd, src_buf, SRC_BUF_SIZE); + if (!size) + break; + if ((ssize_t)size < 0) + bb_perror_msg_and_die(bb_msg_read_error); + /* Encode the buffer we just read in */ + bb_uuencode(dst_buf, src_buf, size, tbl); + bb_putchar('\n'); + if (tbl == bb_uuenc_tbl_std) { + bb_putchar(tbl[size]); + } + fflush(stdout); + xwrite(STDOUT_FILENO, dst_buf, 4 * ((size + 2) / 3)); + } + printf(tbl == bb_uuenc_tbl_std ? "\n`\nend\n" : "\n====\n"); + + fflush_stdout_and_exit(EXIT_SUCCESS); +} diff --git a/coreutils/wc.c b/coreutils/wc.c new file mode 100644 index 0000000..d0e5482 --- /dev/null +++ b/coreutils/wc.c @@ -0,0 +1,205 @@ +/* vi: set sw=4 ts=4: */ +/* + * wc implementation for busybox + * + * Copyright (C) 2003 Manuel Novoa III + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +/* BB_AUDIT SUSv3 _NOT_ compliant -- option -m is not currently supported. */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/wc.html */ + +/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org) + * + * Rewritten to fix a number of problems and do some size optimizations. + * Problems in the previous busybox implementation (besides bloat) included: + * 1) broken 'wc -c' optimization (read note below) + * 2) broken handling of '-' args + * 3) no checking of ferror on EOF returns + * 4) isprint() wasn't considered when word counting. + * + * TODO: + * + * When locale support is enabled, count multibyte chars in the '-m' case. + * + * NOTES: + * + * The previous busybox wc attempted an optimization using stat for the + * case of counting chars only. I omitted that because it was broken. + * It didn't take into account the possibility of input coming from a + * pipe, or input from a file with file pointer not at the beginning. + * + * To implement such a speed optimization correctly, not only do you + * need the size, but also the file position. Note also that the + * file position may be past the end of file. Consider the example + * (adapted from example in gnu wc.c) + * + * echo hello > /tmp/testfile && + * (dd ibs=1k skip=1 count=0 &> /dev/null; wc -c) < /tmp/testfile + * + * for which 'wc -c' should output '0'. + */ + +#include "libbb.h" + +#if ENABLE_LOCALE_SUPPORT +#define isspace_given_isprint(c) isspace(c) +#else +#undef isspace +#undef isprint +#define isspace(c) ((((c) == ' ') || (((unsigned int)((c) - 9)) <= (13 - 9)))) +#define isprint(c) (((unsigned int)((c) - 0x20)) <= (0x7e - 0x20)) +#define isspace_given_isprint(c) ((c) == ' ') +#endif + +#if ENABLE_FEATURE_WC_LARGE +#define COUNT_T unsigned long long +#define COUNT_FMT "llu" +#else +#define COUNT_T unsigned +#define COUNT_FMT "u" +#endif + +enum { + WC_LINES = 0, + WC_WORDS = 1, + WC_CHARS = 2, + WC_LENGTH = 3 +}; + +int wc_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int wc_main(int argc UNUSED_PARAM, char **argv) +{ + FILE *fp; + const char *s, *arg; + const char *start_fmt = " %9"COUNT_FMT + 1; + const char *fname_fmt = " %s\n"; + COUNT_T *pcounts; + COUNT_T counts[4]; + COUNT_T totals[4]; + unsigned linepos; + unsigned u; + int num_files = 0; + int c; + smallint status = EXIT_SUCCESS; + smallint in_word; + unsigned print_type; + + print_type = getopt32(argv, "lwcL"); + + if (print_type == 0) { + print_type = (1 << WC_LINES) | (1 << WC_WORDS) | (1 << WC_CHARS); + } + + argv += optind; + if (!argv[0]) { + *--argv = (char *) bb_msg_standard_input; + fname_fmt = "\n"; + if (!((print_type-1) & print_type)) /* exactly one option? */ + start_fmt = "%"COUNT_FMT; + } + + memset(totals, 0, sizeof(totals)); + + pcounts = counts; + + while ((arg = *argv++) != 0) { + ++num_files; + fp = fopen_or_warn_stdin(arg); + if (!fp) { + status = EXIT_FAILURE; + continue; + } + + memset(counts, 0, sizeof(counts)); + linepos = 0; + in_word = 0; + + do { + /* Our -w doesn't match GNU wc exactly... oh well */ + + ++counts[WC_CHARS]; + c = getc(fp); + if (isprint(c)) { + ++linepos; + if (!isspace_given_isprint(c)) { + in_word = 1; + continue; + } + } else if (((unsigned int)(c - 9)) <= 4) { + /* \t 9 + * \n 10 + * \v 11 + * \f 12 + * \r 13 + */ + if (c == '\t') { + linepos = (linepos | 7) + 1; + } else { /* '\n', '\r', '\f', or '\v' */ + DO_EOF: + if (linepos > counts[WC_LENGTH]) { + counts[WC_LENGTH] = linepos; + } + if (c == '\n') { + ++counts[WC_LINES]; + } + if (c != '\v') { + linepos = 0; + } + } + } else if (c == EOF) { + if (ferror(fp)) { + bb_simple_perror_msg(arg); + status = EXIT_FAILURE; + } + --counts[WC_CHARS]; + goto DO_EOF; /* Treat an EOF as '\r'. */ + } else { + continue; + } + + counts[WC_WORDS] += in_word; + in_word = 0; + if (c == EOF) { + break; + } + } while (1); + + if (totals[WC_LENGTH] < counts[WC_LENGTH]) { + totals[WC_LENGTH] = counts[WC_LENGTH]; + } + totals[WC_LENGTH] -= counts[WC_LENGTH]; + + fclose_if_not_stdin(fp); + + OUTPUT: + /* coreutils wc tries hard to print pretty columns + * (saves results for all files, find max col len etc...) + * we won't try that hard, it will bloat us too much */ + s = start_fmt; + u = 0; + do { + if (print_type & (1 << u)) { + printf(s, pcounts[u]); + s = " %9"COUNT_FMT; /* Ok... restore the leading space. */ + } + totals[u] += pcounts[u]; + } while (++u < 4); + printf(fname_fmt, arg); + } + + /* If more than one file was processed, we want the totals. To save some + * space, we set the pcounts ptr to the totals array. This has the side + * effect of trashing the totals array after outputting it, but that's + * irrelavent since we no longer need it. */ + if (num_files > 1) { + num_files = 0; /* Make sure we don't get here again. */ + arg = "total"; + pcounts = totals; + --argv; + goto OUTPUT; + } + + fflush_stdout_and_exit(status); +} diff --git a/coreutils/who.c b/coreutils/who.c new file mode 100644 index 0000000..baf526b --- /dev/null +++ b/coreutils/who.c @@ -0,0 +1,80 @@ +/* vi: set sw=4 ts=4: */ +/*---------------------------------------------------------------------- + * Mini who is used to display user name, login time, + * idle time and host name. + * + * Author: Da Chen + * + * This is a free document; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation: + * http://www.gnu.org/copyleft/gpl.html + * + * Copyright (c) 2002 AYR Networks, Inc. + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + * + *---------------------------------------------------------------------- + */ +/* BB_AUDIT SUSv3 _NOT_ compliant -- missing options -b, -d, -H, -l, -m, -p, -q, -r, -s, -t, -T, -u; Missing argument 'file'. */ + +#include "libbb.h" +#include +#include + +static void idle_string(char *str6, time_t t) +{ + t = time(NULL) - t; + + /*if (t < 60) { + str6[0] = '.'; + str6[1] = '\0'; + return; + }*/ + if (t >= 0 && t < (24 * 60 * 60)) { + sprintf(str6, "%02d:%02d", + (int) (t / (60 * 60)), + (int) ((t % (60 * 60)) / 60)); + return; + } + strcpy(str6, "old"); +} + +int who_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int who_main(int argc UNUSED_PARAM, char **argv) +{ + char str6[6]; + struct utmp *ut; + struct stat st; + char *name; + unsigned opt; + + opt_complementary = "=0"; + opt = getopt32(argv, "a"); + + setutent(); + printf("USER TTY IDLE TIME HOST\n"); + while ((ut = getutent()) != NULL) { + if (ut->ut_user[0] && (opt || ut->ut_type == USER_PROCESS)) { + time_t tmp; + /* ut->ut_line is device name of tty - "/dev/" */ + name = concat_path_file("/dev", ut->ut_line); + str6[0] = '?'; + str6[1] = '\0'; + if (stat(name, &st) == 0) + idle_string(str6, st.st_atime); + /* manpages say ut_tv.tv_sec *is* time_t, + * but some systems have it wrong */ + tmp = ut->ut_tv.tv_sec; + /* 15 chars for time: Nov 10 19:33:20 */ + printf("%-10s %-8s %-9s %-15.15s %s\n", + ut->ut_user, ut->ut_line, str6, + ctime(&tmp) + 4, ut->ut_host); + if (ENABLE_FEATURE_CLEAN_UP) + free(name); + } + } + if (ENABLE_FEATURE_CLEAN_UP) + endutent(); + return EXIT_SUCCESS; +} diff --git a/coreutils/whoami.c b/coreutils/whoami.c new file mode 100644 index 0000000..6756d4b --- /dev/null +++ b/coreutils/whoami.c @@ -0,0 +1,26 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini whoami implementation for busybox + * + * Copyright (C) 2000 Edward Betts . + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +/* BB_AUDIT SUSv3 N/A -- Matches GNU behavior. */ + +#include "libbb.h" + +/* This is a NOFORK applet. Be very careful! */ + +int whoami_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int whoami_main(int argc, char **argv UNUSED_PARAM) +{ + if (argc > 1) + bb_show_usage(); + + /* Will complain and die if username not found */ + puts(bb_getpwuid(NULL, -1, geteuid())); + + return fflush(stdout); +} diff --git a/coreutils/yes.c b/coreutils/yes.c new file mode 100644 index 0000000..9d3f675 --- /dev/null +++ b/coreutils/yes.c @@ -0,0 +1,42 @@ +/* vi: set sw=4 ts=4: */ +/* + * yes implementation for busybox + * + * Copyright (C) 2003 Manuel Novoa III + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +/* BB_AUDIT SUSv3 N/A -- Matches GNU behavior. */ + +/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org) + * + * Size reductions and removed redundant applet name prefix from error messages. + */ + +#include "libbb.h" + +/* This is a NOFORK applet. Be very careful! */ + +int yes_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int yes_main(int argc, char **argv) +{ + char **pp; + + argv[0] = (char*)"y"; + if (argc != 1) { + ++argv; + } + + do { + pp = argv; + while (1) { + fputs(*pp, stdout); + if (!*++pp) + break; + putchar(' '); + } + } while (putchar('\n') != EOF); + + bb_perror_nomsg_and_die(); +} -- cgit v1.2.3