diff options
Diffstat (limited to 'coreutils')
78 files changed, 14839 insertions, 0 deletions
diff --git a/coreutils/Config.in b/coreutils/Config.in new file mode 100644 index 0000000..000f3a8 --- /dev/null +++ b/coreutils/Config.in @@ -0,0 +1,782 @@ +# +# For a description of the syntax of this configuration file, +# see scripts/kbuild/config-language.txt. +# + +menu "Coreutils" + +config BASENAME + bool "basename" + default n + help + basename is used to strip the directory and suffix from filenames, + leaving just the filename itself. Enable this option if you wish + to enable the 'basename' utility. + +config CAL + bool "cal" + default n + help + cal is used to display a monthly calender. + +config CAT + bool "cat" + default n + help + cat is used to concatenate files and print them to the standard + output. Enable this option if you wish to enable the 'cat' utility. + +config CATV + bool "catv" + default n + help + Display nonprinting characters as escape sequences (like some + implementations' cat -v option). + +config CHGRP + bool "chgrp" + default n + help + chgrp is used to change the group ownership of files. + +config CHMOD + bool "chmod" + default n + help + chmod is used to change the access permission of files. + +config CHOWN + bool "chown" + default n + help + chown is used to change the user and/or group ownership + of files. + +config CHROOT + bool "chroot" + default n + help + chroot is used to change the root directory and run a command. + The default command is `/bin/sh'. + +config CKSUM + bool "cksum" + default n + help + cksum is used to calculate the CRC32 checksum of a file. + +config CMP + bool "cmp" + default n + help + cmp is used to compare two files and returns the result + to standard output. + +config COMM + bool "comm" + default n + help + comm is used to compare two files line by line and return + a three-column output. + +config CP + bool "cp" + default n + help + cp is used to copy files and directories. + +config CUT + bool "cut" + default n + help + cut is used to print selected parts of lines from + each file to stdout. + +config DATE + bool "date" + default n + help + date is used to set the system date or display the + current time in the given format. + +config FEATURE_DATE_ISOFMT + bool "Enable ISO date format output (-I)" + default y + depends on DATE + help + Enable option (-I) to output an ISO-8601 compliant + date/time string. + +config DD + bool "dd" + default n + help + dd copies a file (from standard input to standard output, + by default) using specific input and output blocksizes, + while optionally performing conversions on it. + +config FEATURE_DD_SIGNAL_HANDLING + bool "Enable DD signal handling for status reporting" + default y + depends on DD + help + sending a SIGUSR1 signal to a running `dd' process makes it + print to standard error the number of records read and written + so far, then to resume copying. + + $ dd if=/dev/zero of=/dev/null& pid=$! $ kill -USR1 $pid; sleep 1; kill $pid + 10899206+0 records in 10899206+0 records out + +config FEATURE_DD_IBS_OBS + bool "Enable ibs, obs and conv options" + default n + depends on DD + help + Enables support for writing a certain number of bytes in and out, + at a time, and performing conversions on the data stream. + +config DF + bool "df" + default n + help + df reports the amount of disk space used and available + on filesystems. + +config DIFF + bool "diff" + default n + help + diff compares two files or directories and outputs the + differences between them in a form that can be given to + the patch command. + +config FEATURE_DIFF_BINARY + bool "Enable checks for binary files" + default y + depends on DIFF + help + This option enables support for checking for binary files + before a comparison is carried out. + +config FEATURE_DIFF_DIR + bool "Enable directory support" + default y + depends on DIFF + help + This option enables support for directory and subdirectory + comparison. + +config FEATURE_DIFF_MINIMAL + bool "Enable -d option to find smaller sets of changes" + default n + depends on DIFF + help + Enabling this option allows the use of -d to make diff + try hard to find the smallest possible set of changes. + +config DIRNAME + bool "dirname" + default n + help + dirname is used to strip a non-directory suffix from + a file name. + +config DOS2UNIX + bool "dos2unix/unix2dos" + default n + help + dos2unix is used to convert a text file from DOS format to + UNIX format, and vice versa. + +config UNIX2DOS + bool + default y + depends on DOS2UNIX + help + unix2dos is used to convert a text file from UNIX format to + DOS format, and vice versa. + +config DU + bool "du (default blocksize of 512 bytes)" + default n + help + du is used to report the amount of disk space used + for specified files. + +config FEATURE_DU_DEFAULT_BLOCKSIZE_1K + bool "Use a default blocksize of 1024 bytes (1K)" + default y + depends on DU + help + Use a blocksize of (1K) instead of the default 512b. + +config ECHO + bool "echo (basic SuSv3 version taking no options)" + default n + help + echo is used to print a specified string to stdout. + +# this entry also appears in shell/Config.in, next to the echo builtin +config FEATURE_FANCY_ECHO + bool "Enable echo options (-n and -e)" + default y + depends on ECHO + help + This adds options (-n and -e) to echo. + +config ENV + bool "env" + default n + help + env is used to set an environment variable and run + a command; without options it displays the current + environment. + +config FEATURE_ENV_LONG_OPTIONS + bool "Enable long options" + default n + depends on ENV && GETOPT_LONG + help + Support long options for the env applet. + +config EXPR + bool "expr" + default n + help + expr is used to calculate numbers and print the result + to standard output. + +config EXPR_MATH_SUPPORT_64 + bool "Extend Posix numbers support to 64 bit" + default n + depends on EXPR + help + Enable 64-bit math support in the expr applet. This will make + the applet slightly larger, but will allow computation with very + large numbers. + +config FALSE + bool "false" + default n + help + false returns an exit code of FALSE (1). + +config FOLD + bool "fold" + default n + help + Wrap text to fit a specific width. + +config HEAD + bool "head" + default n + help + head is used to print the first specified number of lines + from files. + +config FEATURE_FANCY_HEAD + bool "Enable head options (-c, -q, and -v)" + default n + depends on HEAD + help + This enables the head options (-c, -q, and -v). + +config HOSTID + bool "hostid" + default n + help + hostid prints the numeric identifier (in hexadecimal) for + the current host. + +config ID + bool "id" + default n + help + id displays the current user and group ID names. + +config INSTALL + bool "install" + default n + help + Copy files and set attributes. + +config FEATURE_INSTALL_LONG_OPTIONS + bool "Enable long options" + default n + depends on INSTALL && GETOPT_LONG + help + Support long options for the install applet. + +config LENGTH + bool "length" + default n + help + length is used to print out the length of a specified string. + +config LN + bool "ln" + default n + help + ln is used to create hard or soft links between files. + +config LOGNAME + bool "logname" + default n + help + logname is used to print the current user's login name. + +config LS + bool "ls" + default n + help + ls is used to list the contents of directories. + +config FEATURE_LS_FILETYPES + bool "Enable filetyping options (-p and -F)" + default y + depends on LS + help + Enable the ls options (-p and -F). + +config FEATURE_LS_FOLLOWLINKS + bool "Enable symlinks dereferencing (-L)" + default y + depends on LS + help + Enable the ls option (-L). + +config FEATURE_LS_RECURSIVE + bool "Enable recursion (-R)" + default y + depends on LS + help + Enable the ls option (-R). + +config FEATURE_LS_SORTFILES + bool "Sort the file names" + default y + depends on LS + help + Allow ls to sort file names alphabetically. + +config FEATURE_LS_TIMESTAMPS + bool "Show file timestamps" + default y + depends on LS + help + Allow ls to display timestamps for files. + +config FEATURE_LS_USERNAME + bool "Show username/groupnames" + default y + depends on LS + help + Allow ls to display username/groupname for files. + +config FEATURE_LS_COLOR + bool "Allow use of color to identify file types" + default y + depends on LS && GETOPT_LONG + help + This enables the --color option to ls. + +config FEATURE_LS_COLOR_IS_DEFAULT + bool "Produce colored ls output by default" + default n + depends on FEATURE_LS_COLOR + help + Saying yes here will turn coloring on by default, + even if no "--color" option is given to the ls command. + This is not recommended, since the colors are not + configurable, and the output may not be legible on + many output screens. + +config MD5SUM + bool "md5sum" + default n + help + md5sum is used to print or check MD5 checksums. + +config MKDIR + bool "mkdir" + default n + help + mkdir is used to create directories with the specified names. + +config FEATURE_MKDIR_LONG_OPTIONS + bool "Enable long options" + default n + depends on MKDIR && GETOPT_LONG + help + Support long options for the mkdir applet. + +config MKFIFO + bool "mkfifo" + default n + help + mkfifo is used to create FIFOs (named pipes). + The `mknod' program can also create FIFOs. + +config MKNOD + bool "mknod" + default n + help + mknod is used to create FIFOs or block/character special + files with the specified names. + +config MV + bool "mv" + default n + help + mv is used to move or rename files or directories. + +config FEATURE_MV_LONG_OPTIONS + bool "Enable long options" + default n + depends on MV && GETOPT_LONG + help + Support long options for the mv applet. + +config NICE + bool "nice" + default n + help + nice runs a program with modified scheduling priority. + +config NOHUP + bool "nohup" + default n + help + run a command immune to hangups, with output to a non-tty. + +config OD + bool "od" + default n + help + od is used to dump binary files in octal and other formats. + +config PRINTENV + bool "printenv" + default n + help + printenv is used to print all or part of environment. + +config PRINTF + bool "printf" + default n + help + printf is used to format and print specified strings. + It's similar to `echo' except it has more options. + +config PWD + bool "pwd" + default n + help + pwd is used to print the current directory. + +config REALPATH + bool "realpath" + default n + help + Return the canonicalized absolute pathname. + This isn't provided by GNU shellutils, but where else does it belong. + +config RM + bool "rm" + default n + help + rm is used to remove files or directories. + +config RMDIR + bool "rmdir" + default n + help + rmdir is used to remove empty directories. + +config SEQ + bool "seq" + default n + help + print a sequence of numbers + +config SHA1SUM + bool "sha1sum" + default n + help + Compute and check SHA1 message digest + +config SLEEP + bool "sleep (single integer arg with no suffix)" + default n + help + sleep is used to pause for a specified number of seconds, + +config FEATURE_FANCY_SLEEP + bool "Enable multiple integer args and optional time suffixes" + default n + depends on SLEEP + help + Allow sleep to pause for specified minutes, hours, and days. + +config SORT + bool "sort" + default n + help + sort is used to sort lines of text in specified files. + +config FEATURE_SORT_BIG + bool "full SuSv3 compliant sort (Support -ktcsbdfiozgM)" + default y + depends on SORT + help + Without this, sort only supports -r, -u, and an integer version + of -n. Selecting this adds sort keys, floating point support, and + more. This adds a little over 3k to a nonstatic build on x86. + + The SuSv3 sort standard is available at: + http://www.opengroup.org/onlinepubs/007904975/utilities/sort.html + +config STAT + bool "stat" + default n + help + display file or filesystem status. + +config FEATURE_STAT_FORMAT + bool "Enable custom formats (-c)" + default n + depends on STAT + help + Without this, stat will not support the '-c format' option where + users can pass a custom format string for output. This adds about + 7k to a nonstatic build on amd64. + +config STTY + bool "stty" + default n + help + stty is used to change and print terminal line settings. + +config SUM + bool "sum" + default n + help + checksum and count the blocks in a file + +config SYNC + bool "sync" + default n + help + sync is used to flush filesystem buffers. + +config TAIL + bool "tail" + default n + help + tail is used to print the last specified number of lines + from files. + +config FEATURE_FANCY_TAIL + bool "Enable extra tail options (-q, -s, and -v)" + default y + depends on TAIL + help + The options (-q, -s, and -v) are provided by GNU tail, but + are not specific in the SUSv3 standard. + +config TEE + bool "tee" + default n + help + tee is used to read from standard input and write + to standard output and files. + +config FEATURE_TEE_USE_BLOCK_IO + bool "Enable block i/o (larger/faster) instead of byte i/o." + default n + depends on TEE + help + Enable this option for a faster tee, at expense of size. + +config TEST + bool "test" + default n + help + test is used to check file types and compare values, + returning an appropriate exit code. The bash shell + has test built in, ash can build it in optionally. + +config FEATURE_TEST_64 + bool "Extend test to 64 bit" + default n + depends on TEST + help + Enable 64-bit support in test. + +config TOUCH + bool "touch" + default n + help + touch is used to create or change the access and/or + modification timestamp of specified files. + +config TR + bool "tr" + default n + help + tr is used to squeeze, and/or delete characters from standard + input, writing to standard output. + +config FEATURE_TR_CLASSES + bool "Enable character classes (such as [:upper:])" + default n + depends on TR + help + Enable character classes, enabling commands such as: + tr [:upper:] [:lower:] to convert input into lowercase. + +config FEATURE_TR_EQUIV + bool "Enable equivalence classes" + default n + depends on TR + help + Enable equivalence classes, which essentially add the enclosed + character to the current set. For instance, tr [=a=] xyz would + replace all instances of 'a' with 'xyz'. This option is mainly + useful for cases when no other way of expressing a character + is possible. + +config TRUE + bool "true" + default n + help + true returns an exit code of TRUE (0). + +config TTY + bool "tty" + default n + help + tty is used to print the name of the current terminal to + standard output. + +config UNAME + bool "uname" + default n + help + uname is used to print system information. + +config UNIQ + bool "uniq" + default n + help + uniq is used to remove duplicate lines from a sorted file. + +config USLEEP + bool "usleep" + default n + help + usleep is used to pause for a specified number of microseconds. + +config UUDECODE + bool "uudecode" + default n + help + uudecode is used to decode a uuencoded file. + +config UUENCODE + bool "uuencode" + default n + help + uuencode is used to uuencode a file. + +config WATCH + bool "watch" + default n + select DATE + help + watch is used to execute a program periodically, showing + output to the screen. + +config WC + bool "wc" + default n + help + wc is used to print the number of bytes, words, and lines, + in specified files. + +config FEATURE_WC_LARGE + bool "Support very large files in wc" + default n + depends on WC + help + Use "unsigned long long" in wc for count variables + +config WHO + bool "who" + default n + select FEATURE_UTMP + help + who is used to show who is logged on. + +config WHOAMI + bool "whoami" + default n + help + whoami is used to print the username of the current + user id (same as id -un). + +config YES + bool "yes" + default n + help + yes is used to repeatedly output a specific string, or + the default string `y'. + +comment "Common options for cp and mv" + depends on CP || MV + +config FEATURE_PRESERVE_HARDLINKS + bool "Preserve hard links" + default n + depends on CP || MV + help + Allow cp and mv to preserve hard links. + +comment "Common options for ls, more and telnet" + depends on LS || MORE || TELNET + +config FEATURE_AUTOWIDTH + bool "Calculate terminal & column widths" + default y + depends on LS || MORE || TELNET + help + This option allows utilities such as 'ls', 'more' and 'telnet' + to determine the width of the screen, which can allow them to + display additional text or avoid wrapping text onto the next line. + If you leave this disabled, your utilities will be especially + primitive and will be unable to determine the current screen width. + +comment "Common options for df, du, ls" + depends on DF || DU || LS + +config FEATURE_HUMAN_READABLE + bool "Support for human readable output (example 13k, 23M, 235G)" + default n + depends on DF || DU || LS + help + Allow df, du, and ls to have human readable output. + +comment "Common options for md5sum, sha1sum" + depends on MD5SUM || SHA1SUM + +config FEATURE_MD5_SHA1_SUM_CHECK + bool "Enable -c, -s and -w options" + default n + depends on MD5SUM || SHA1SUM + help + Enabling the -c options allows files to be checked + against pre-calculated hash values. + + -s and -w are useful options when verifying checksums. + +endmenu diff --git a/coreutils/Kbuild b/coreutils/Kbuild new file mode 100644 index 0000000..cfb508d --- /dev/null +++ b/coreutils/Kbuild @@ -0,0 +1,81 @@ +# Makefile for busybox +# +# Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org> +# +# Licensed under the GPL v2, see the file LICENSE in this tarball. + +libs-y += libcoreutils/ + +lib-y:= +lib-$(CONFIG_BASENAME) += basename.o +lib-$(CONFIG_CAL) += cal.o +lib-$(CONFIG_CAT) += cat.o +lib-$(CONFIG_CATV) += catv.o +lib-$(CONFIG_CHGRP) += chgrp.o chown.o +lib-$(CONFIG_CHMOD) += chmod.o +lib-$(CONFIG_CHOWN) += chown.o +lib-$(CONFIG_CHROOT) += chroot.o +lib-$(CONFIG_CKSUM) += cksum.o +lib-$(CONFIG_CMP) += cmp.o +lib-$(CONFIG_COMM) += comm.o +lib-$(CONFIG_CP) += cp.o +lib-$(CONFIG_CUT) += cut.o +lib-$(CONFIG_DATE) += date.o +lib-$(CONFIG_DD) += dd.o +lib-$(CONFIG_DF) += df.o +lib-$(CONFIG_DIFF) += diff.o +lib-$(CONFIG_DIRNAME) += dirname.o +lib-$(CONFIG_DOS2UNIX) += dos2unix.o +lib-$(CONFIG_DU) += du.o +lib-$(CONFIG_ECHO) += echo.o +lib-$(CONFIG_ENV) += env.o +lib-$(CONFIG_EXPR) += expr.o +lib-$(CONFIG_FALSE) += false.o +lib-$(CONFIG_FOLD) += fold.o +lib-$(CONFIG_HEAD) += head.o +lib-$(CONFIG_HOSTID) += hostid.o +lib-$(CONFIG_ID) += id.o +lib-$(CONFIG_INSTALL) += install.o +lib-$(CONFIG_LENGTH) += length.o +lib-$(CONFIG_LN) += ln.o +lib-$(CONFIG_LOGNAME) += logname.o +lib-$(CONFIG_LS) += ls.o +lib-$(CONFIG_MD5SUM) += md5_sha1_sum.o +lib-$(CONFIG_MKDIR) += mkdir.o +lib-$(CONFIG_MKFIFO) += mkfifo.o +lib-$(CONFIG_MKNOD) += mknod.o +lib-$(CONFIG_MV) += mv.o +lib-$(CONFIG_NICE) += nice.o +lib-$(CONFIG_NOHUP) += nohup.o +lib-$(CONFIG_OD) += od.o +lib-$(CONFIG_PRINTENV) += printenv.o +lib-$(CONFIG_PRINTF) += printf.o +lib-$(CONFIG_PWD) += pwd.o +lib-$(CONFIG_REALPATH) += realpath.o +lib-$(CONFIG_RM) += rm.o +lib-$(CONFIG_RMDIR) += rmdir.o +lib-$(CONFIG_SEQ) += seq.o +lib-$(CONFIG_SHA1SUM) += md5_sha1_sum.o +lib-$(CONFIG_SLEEP) += sleep.o +lib-$(CONFIG_SORT) += sort.o +lib-$(CONFIG_STAT) += stat.o +lib-$(CONFIG_STTY) += stty.o +lib-$(CONFIG_SUM) += sum.o +lib-$(CONFIG_SYNC) += sync.o +lib-$(CONFIG_TAIL) += tail.o +lib-$(CONFIG_TEE) += tee.o +lib-$(CONFIG_TEST) += test.o +lib-$(CONFIG_TOUCH) += touch.o +lib-$(CONFIG_TR) += tr.o +lib-$(CONFIG_TRUE) += true.o +lib-$(CONFIG_TTY) += tty.o +lib-$(CONFIG_UNAME) += uname.o +lib-$(CONFIG_UNIQ) += uniq.o +lib-$(CONFIG_USLEEP) += usleep.o +lib-$(CONFIG_UUDECODE) += uudecode.o +lib-$(CONFIG_UUENCODE) += uuencode.o +lib-$(CONFIG_WATCH) += watch.o +lib-$(CONFIG_WC) += wc.o +lib-$(CONFIG_WHO) += who.o +lib-$(CONFIG_WHOAMI) += whoami.o +lib-$(CONFIG_YES) += yes.o diff --git a/coreutils/basename.c b/coreutils/basename.c new file mode 100644 index 0000000..30f76dc --- /dev/null +++ b/coreutils/basename.c @@ -0,0 +1,50 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini basename implementation for busybox + * + * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org> + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + * + */ + +/* BB_AUDIT SUSv3 compliant */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/basename.html */ + + +/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org) + * + * Changes: + * 1) Now checks for too many args. Need at least one and at most two. + * 2) Don't check for options, as per SUSv3. + * 3) Save some space by using strcmp(). Calling strncmp() here was silly. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include "busybox.h" + +int basename_main(int argc, char **argv) +{ + size_t m, n; + char *s; + + if (((unsigned int)(argc-2)) >= 2) { + bb_show_usage(); + } + + s = bb_get_last_path_component(*++argv); + + if (*++argv) { + n = strlen(*argv); + m = strlen(s); + if ((m > n) && ((strcmp)(s+m-n, *argv) == 0)) { + s[m-n] = '\0'; + } + } + + puts(s); + + fflush_stdout_and_exit(EXIT_SUCCESS); +} diff --git a/coreutils/cal.c b/coreutils/cal.c new file mode 100644 index 0000000..681a88d --- /dev/null +++ b/coreutils/cal.c @@ -0,0 +1,356 @@ +/* vi: set sw=4 ts=4: */ +/* + * Calendar implementation for busybox + * + * See original copyright at the end of this file + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +/* BB_AUDIT SUSv3 compliant with -j and -y extensions (from util-linux). */ +/* BB_AUDIT BUG: The output of 'cal -j 1752' is incorrect. The upstream + * BB_AUDIT BUG: version in util-linux seems to be broken as well. */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/cal.html */ + +/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org) + * + * Major size reduction... over 50% (>1.5k) on i386. + */ + +#include "busybox.h" + +#define THURSDAY 4 /* for reformation */ +#define SATURDAY 6 /* 1 Jan 1 was a Saturday */ + +#define FIRST_MISSING_DAY 639787 /* 3 Sep 1752 */ +#define NUMBER_MISSING_DAYS 11 /* 11 day correction */ + +#define MAXDAYS 42 /* max slots in a month array */ +#define SPACE -1 /* used in day array */ + +static const char days_in_month[] = { + 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 +}; + +static const char sep1752[] = { + 1, 2, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30 +}; + +static int julian; + +/* leap year -- account for Gregorian reformation in 1752 */ +#define leap_year(yr) \ + ((yr) <= 1752 ? !((yr) % 4) : \ + (!((yr) % 4) && ((yr) % 100)) || !((yr) % 400)) + +static int is_leap_year(int year) +{ + return leap_year(year); +} +#undef leap_year +#define leap_year(yr) is_leap_year(yr) + +/* number of centuries since 1700, not inclusive */ +#define centuries_since_1700(yr) \ + ((yr) > 1700 ? (yr) / 100 - 17 : 0) + +/* number of centuries since 1700 whose modulo of 400 is 0 */ +#define quad_centuries_since_1700(yr) \ + ((yr) > 1600 ? ((yr) - 1600) / 400 : 0) + +/* number of leap years between year 1 and this year, not inclusive */ +#define leap_years_since_year_1(yr) \ + ((yr) / 4 - centuries_since_1700(yr) + quad_centuries_since_1700(yr)) + +static void center (char *, int, int); +static void day_array (int, int, int *); +static void trim_trailing_spaces_and_print (char *); + +static void blank_string(char *buf, size_t buflen); +static char *build_row(char *p, int *dp); + +#define DAY_LEN 3 /* 3 spaces per day */ +#define J_DAY_LEN (DAY_LEN + 1) +#define WEEK_LEN 20 /* 7 * 3 - one space at the end */ +#define J_WEEK_LEN (WEEK_LEN + 7) +#define HEAD_SEP 2 /* spaces between day headings */ + +int cal_main(int argc, char **argv) +{ + struct tm *local_time; + struct tm zero_tm; + time_t now; + int month, year, flags, i; + char *month_names[12]; + char day_headings[28]; /* 28 for julian, 21 for nonjulian */ + char buf[40]; + +// Done in busybox.c, ok to remove? +//#ifdef CONFIG_LOCALE_SUPPORT +// setlocale(LC_TIME, ""); +//#endif + + flags = getopt32(argc, argv, "jy"); + + julian = flags & 1; + + argv += optind; + + month = 0; + + if ((argc -= optind) > 2) { + bb_show_usage(); + } + + if (!argc) { + time(&now); + local_time = localtime(&now); + year = local_time->tm_year + 1900; + if (!(flags & 2)) { + month = local_time->tm_mon + 1; + } + } else { + if (argc == 2) { + month = xatoul_range(*argv++, 1, 12); + } + year = xatoul_range(*argv, 1, 9999); + } + + blank_string(day_headings, sizeof(day_headings) - 7 + 7*julian); + + i = 0; + do { + zero_tm.tm_mon = i; + strftime(buf, sizeof(buf), "%B", &zero_tm); + month_names[i] = xstrdup(buf); + + if (i < 7) { + zero_tm.tm_wday = i; + strftime(buf, sizeof(buf), "%a", &zero_tm); + strncpy(day_headings + i * (3+julian) + julian, buf, 2); + } + } while (++i < 12); + + if (month) { + int row, len, days[MAXDAYS]; + int *dp = days; + char lineout[30]; + + day_array(month, year, dp); + len = sprintf(lineout, "%s %d", month_names[month - 1], year); + printf("%*s%s\n%s\n", + ((7*julian + WEEK_LEN) - len) / 2, "", + lineout, day_headings); + for (row = 0; row < 6; row++) { + build_row(lineout, dp)[0] = '\0'; + dp += 7; + trim_trailing_spaces_and_print(lineout); + } + } else { + int row, which_cal, week_len, days[12][MAXDAYS]; + int *dp; + char lineout[80]; + + sprintf(lineout, "%d", year); + center(lineout, + (WEEK_LEN * 3 + HEAD_SEP * 2) + + julian * (J_WEEK_LEN * 2 + HEAD_SEP + - (WEEK_LEN * 3 + HEAD_SEP * 2)), + 0); + puts("\n"); /* two \n's */ + for (i = 0; i < 12; i++) { + day_array(i + 1, year, days[i]); + } + blank_string(lineout, sizeof(lineout)); + week_len = WEEK_LEN + julian * (J_WEEK_LEN - WEEK_LEN); + for (month = 0; month < 12; month += 3-julian) { + center(month_names[month], week_len, HEAD_SEP); + if (!julian) { + center(month_names[month + 1], week_len, HEAD_SEP); + } + center(month_names[month + 2 - julian], week_len, 0); + printf("\n%s%*s%s", day_headings, HEAD_SEP, "", day_headings); + if (!julian) { + printf("%*s%s", HEAD_SEP, "", day_headings); + } + putchar('\n'); + for (row = 0; row < (6*7); row += 7) { + for (which_cal = 0; which_cal < 3-julian; which_cal++) { + dp = days[month + which_cal] + row; + build_row(lineout + which_cal * (week_len + 2), dp); + } + /* blank_string took care of nul termination. */ + trim_trailing_spaces_and_print(lineout); + } + } + } + + fflush_stdout_and_exit(0); +} + +/* + * day_array -- + * Fill in an array of 42 integers with a calendar. Assume for a moment + * that you took the (maximum) 6 rows in a calendar and stretched them + * out end to end. You would have 42 numbers or spaces. This routine + * builds that array for any month from Jan. 1 through Dec. 9999. + */ +static void day_array(int month, int year, int *days) +{ + long temp; + int i; + int j_offset; + int day, dw, dm; + + memset(days, SPACE, MAXDAYS * sizeof(int)); + + if ((month == 9) && (year == 1752)) { + size_t oday = 0; + + j_offset = julian * 244; + do { + days[oday+2] = sep1752[oday] + j_offset; + } while (++oday < sizeof(sep1752)); + + return; + } + + /* day_in_year + * return the 1 based day number within the year + */ + day = 1; + if ((month > 2) && leap_year(year)) { + ++day; + } + + i = month; + while (i) { + day += days_in_month[--i]; + } + + /* day_in_week + * return the 0 based day number for any date from 1 Jan. 1 to + * 31 Dec. 9999. Assumes the Gregorian reformation eliminates + * 3 Sep. 1752 through 13 Sep. 1752. Returns Thursday for all + * missing days. + */ + dw = THURSDAY; + temp = (long)(year - 1) * 365 + leap_years_since_year_1(year - 1) + + day; + if (temp < FIRST_MISSING_DAY) { + dw = ((temp - 1 + SATURDAY) % 7); + } else if (temp >= (FIRST_MISSING_DAY + NUMBER_MISSING_DAYS)) { + dw = (((temp - 1 + SATURDAY) - NUMBER_MISSING_DAYS) % 7); + } + + if (!julian) { + day = 1; + } + + dm = days_in_month[month]; + if ((month == 2) && leap_year(year)) { + ++dm; + } + + while (dm) { + days[dw++] = day++; + --dm; + } +} + +static void trim_trailing_spaces_and_print(char *s) +{ + char *p = s; + + while (*p) { + ++p; + } + while (p > s) { + --p; + if (!(isspace)(*p)) { /* We want the function... not the inline. */ + p[1] = '\0'; + break; + } + } + + puts(s); +} + +static void center(char *str, int len, int separate) +{ + int n = strlen(str); + len -= n; + printf("%*s%*s", (len/2) + n, str, (len/2) + (len % 2) + separate, ""); +} + +static void blank_string(char *buf, size_t buflen) +{ + memset(buf, ' ', buflen); + buf[buflen-1] = '\0'; +} + +static char *build_row(char *p, int *dp) +{ + int col, val, day; + + memset(p, ' ', (julian + DAY_LEN) * 7); + + col = 0; + do { + if ((day = *dp++) != SPACE) { + if (julian) { + ++p; + if (day >= 100) { + *p = '0'; + p[-1] = (day / 100) + '0'; + day %= 100; + } + } + if ((val = day / 10) > 0) { + *p = val + '0'; + } + *++p = day % 10 + '0'; + p += 2; + } else { + p += DAY_LEN + julian; + } + } while (++col < 7); + + return p; +} + +/* + * Copyright (c) 1989, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kim Letkeman. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + + diff --git a/coreutils/cat.c b/coreutils/cat.c new file mode 100644 index 0000000..a959805 --- /dev/null +++ b/coreutils/cat.c @@ -0,0 +1,41 @@ +/* vi: set sw=4 ts=4: */ +/* + * cat implementation for busybox + * + * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org> + * + * Licensed under GPLv2, see file License in this tarball for details. + */ + +/* BB_AUDIT SUSv3 compliant */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/cat.html */ + +#include "busybox.h" +#include <unistd.h> + +int cat_main(int argc, char **argv) +{ + FILE *f; + int retval = EXIT_SUCCESS; + + getopt32(argc, argv, "u"); + + argv += optind; + if (!*argv) { + *--argv = "-"; + } + + do { + f = fopen_or_warn_stdin(*argv); + if (f) { + off_t r = bb_copyfd_eof(fileno(f), STDOUT_FILENO); + fclose_if_not_stdin(f); + if (r >= 0) { + continue; + } + } + retval = EXIT_FAILURE; + } while (*++argv); + + return retval; +} diff --git a/coreutils/catv.c b/coreutils/catv.c new file mode 100644 index 0000000..66f3069 --- /dev/null +++ b/coreutils/catv.c @@ -0,0 +1,69 @@ +/* vi: set sw=4 ts=4: */ +/* + * cat -v implementation for busybox + * + * Copyright (C) 2006 Rob Landley <rob@landley.net> + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +/* See "Cat -v considered harmful" at + * http://cm.bell-labs.com/cm/cs/doc/84/kp.ps.gz */ + +#include "busybox.h" + +int catv_main(int argc, char **argv) +{ + int retval = EXIT_SUCCESS, fd; + unsigned flags; + + flags = getopt32(argc, argv, "etv"); +#define CATV_OPT_e (1<<0) +#define CATV_OPT_t (1<<1) +#define CATV_OPT_v (1<<2) + flags ^= CATV_OPT_v; + + argv += optind; + do { + /* Read from stdin if there's nothing else to do. */ + fd = 0; + if (*argv && 0 > (fd = xopen(*argv, O_RDONLY))) + retval = EXIT_FAILURE; + else for (;;) { + int i, res; + + res = read(fd, bb_common_bufsiz1, sizeof(bb_common_bufsiz1)); + if (res < 0) + retval = EXIT_FAILURE; + if (res < 1) + break; + for (i = 0; i < res; i++) { + char c = bb_common_bufsiz1[i]; + + if (c > 126 && (flags & CATV_OPT_v)) { + if (c == 127) { + printf("^?"); + continue; + } else { + printf("M-"); + c -= 128; + } + } + if (c < 32) { + if (c == 10) { + if (flags & CATV_OPT_e) + putchar('$'); + } else if (flags & (c==9 ? CATV_OPT_t : CATV_OPT_v)) { + printf("^%c", c+'@'); + continue; + } + } + putchar(c); + } + } + if (ENABLE_FEATURE_CLEAN_UP && fd) + close(fd); + } while (*++argv); + + fflush_stdout_and_exit(retval); +} diff --git a/coreutils/chgrp.c b/coreutils/chgrp.c new file mode 100644 index 0000000..da5b129 --- /dev/null +++ b/coreutils/chgrp.c @@ -0,0 +1,27 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini chgrp implementation for busybox + * + * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org> + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +/* BB_AUDIT SUSv3 defects - unsupported options -H, -L, and -P. */ +/* BB_AUDIT GNU defects - unsupported long options. */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/chgrp.html */ + +#include "busybox.h" + +int chgrp_main(int argc, char **argv) +{ + /* "chgrp [opts] abc file(s)" == "chown [opts] :abc file(s)" */ + char **p = argv; + while (*++p) { + if (p[0][0] != '-') { + p[0] = xasprintf(":%s", p[0]); + break; + } + } + return chown_main(argc, argv); +} diff --git a/coreutils/chmod.c b/coreutils/chmod.c new file mode 100644 index 0000000..990e9b4 --- /dev/null +++ b/coreutils/chmod.c @@ -0,0 +1,157 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini chmod implementation for busybox + * + * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org> + * + * Reworked by (C) 2002 Vladimir Oleynik <dzo@simtreas.ru> + * to correctly parse '-rwxgoa' + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +/* BB_AUDIT SUSv3 compliant */ +/* BB_AUDIT GNU defects - unsupported long options. */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/chmod.html */ + +#include "busybox.h" + +#define OPT_RECURSE (option_mask32 & 1) +#define OPT_VERBOSE (USE_DESKTOP(option_mask32 & 2) SKIP_DESKTOP(0)) +#define OPT_CHANGED (USE_DESKTOP(option_mask32 & 4) SKIP_DESKTOP(0)) +#define OPT_QUIET (USE_DESKTOP(option_mask32 & 8) SKIP_DESKTOP(0)) +#define OPT_STR "R" USE_DESKTOP("vcf") + +/* coreutils: + * chmod never changes the permissions of symbolic links; the chmod + * system call cannot change their permissions. This is not a problem + * since the permissions of symbolic links are never used. + * However, for each symbolic link listed on the command line, chmod changes + * the permissions of the pointed-to file. In contrast, chmod ignores + * symbolic links encountered during recursive directory traversals. + */ + +static int fileAction(const char *fileName, struct stat *statbuf, void* junk, int depth) +{ + mode_t newmode; + + /* match coreutils behavior */ + if (depth == 0) { + /* statbuf holds lstat result, but we need stat (follow link) */ + if (stat(fileName, statbuf)) + goto err; + } else { /* depth > 0: skip links */ + if (S_ISLNK(statbuf->st_mode)) + return TRUE; + } + newmode = statbuf->st_mode; + + if (!bb_parse_mode((char *)junk, &newmode)) + bb_error_msg_and_die("invalid mode: %s", (char *)junk); + + if (chmod(fileName, newmode) == 0) { + if (OPT_VERBOSE + || (OPT_CHANGED && statbuf->st_mode != newmode) + ) { + printf("mode of '%s' changed to %04o (%s)\n", fileName, + newmode & 07777, bb_mode_string(newmode)+1); + } + return TRUE; + } + err: + if (!OPT_QUIET) + bb_perror_msg("%s", fileName); + return FALSE; +} + +int chmod_main(int argc, char **argv) +{ + int retval = EXIT_SUCCESS; + char *arg, **argp; + char *smode; + + /* Convert first encountered -r into ar, -w into aw etc + * so that getopt would not eat it */ + argp = argv; + while ((arg = *++argp)) { + /* Mode spec must be the first arg (sans -R etc) */ + /* (protect against mishandling e.g. "chmod 644 -r") */ + if (arg[0] != '-') { + arg = NULL; + break; + } + /* An option. Not a -- or valid option? */ + if (arg[1] && !strchr("-"OPT_STR, arg[1])) { + arg[0] = 'a'; + break; + } + } + + /* Parse options */ + opt_complementary = "-2"; + getopt32(argc, argv, ("-"OPT_STR) + 1); /* Reuse string */ + argv += optind; + + /* Restore option-like mode if needed */ + if (arg) arg[0] = '-'; + + /* Ok, ready to do the deed now */ + smode = *argv++; + do { + if (!recursive_action(*argv, + OPT_RECURSE, // recurse + FALSE, // follow links: coreutils doesn't + FALSE, // depth first + fileAction, // file action + fileAction, // dir action + smode, // user data + 0) // depth + ) { + retval = EXIT_FAILURE; + } + } while (*++argv); + + return retval; +} + +/* +Security: chmod is too important and too subtle. +This is a test script (busybox chmod versus coreutils). +Run it in empty dir. Probably requires bash. + +#!/bin/sh +function create() { + rm -rf $1; mkdir $1 + ( + cd $1 || exit 1 + mkdir dir + >up + >file + >dir/file + ln -s dir linkdir + ln -s file linkfile + ln -s ../up dir/up + ) +} +function tst() { + (cd test1; $t1 $1) + (cd test2; $t2 $1) + (cd test1; ls -lR) >out1 + (cd test2; ls -lR) >out2 + echo "chmod $1" >out.diff + if ! diff -u out1 out2 >>out.diff; then exit 1; fi + mv out.diff out1.diff +} +t1="/tmp/busybox chmod" +t2="/usr/bin/chmod" +create test1; create test2 +tst "a+w file" +tst "a-w dir" +tst "a+w linkfile" +tst "a-w linkdir" +tst "-R a+w file" +tst "-R a-w dir" +tst "-R a+w linkfile" +tst "-R a-w linkdir" +tst "a-r,a+x linkfile" +*/ diff --git a/coreutils/chown.c b/coreutils/chown.c new file mode 100644 index 0000000..fddce7c --- /dev/null +++ b/coreutils/chown.c @@ -0,0 +1,98 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini chown implementation for busybox + * + * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org> + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +/* BB_AUDIT SUSv3 defects - unsupported options -H, -L, and -P. */ +/* BB_AUDIT GNU defects - unsupported long options. */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/chown.html */ + +#include "busybox.h" + +static uid_t uid = -1; +static gid_t gid = -1; + +static int (*chown_func)(const char *, uid_t, gid_t) = chown; + +#define OPT_RECURSE (option_mask32 & 1) +#define OPT_NODEREF (option_mask32 & 2) +#define OPT_VERBOSE (USE_DESKTOP(option_mask32 & 4) SKIP_DESKTOP(0)) +#define OPT_CHANGED (USE_DESKTOP(option_mask32 & 8) SKIP_DESKTOP(0)) +#define OPT_QUIET (USE_DESKTOP(option_mask32 & 0x10) SKIP_DESKTOP(0)) +#define OPT_STR ("Rh" USE_DESKTOP("vcf")) + +/* TODO: + * -H if a command line argument is a symbolic link to a directory, traverse it + * -L traverse every symbolic link to a directory encountered + * -P do not traverse any symbolic links (default) + */ + +static int fileAction(const char *fileName, struct stat *statbuf, + void ATTRIBUTE_UNUSED *junk, int depth) +{ + // TODO: -H/-L/-P + // if (depth ... && S_ISLNK(statbuf->st_mode)) .... + + if (!chown_func(fileName, + (uid == (uid_t)-1) ? statbuf->st_uid : uid, + (gid == (gid_t)-1) ? statbuf->st_gid : gid)) { + if (OPT_VERBOSE + || (OPT_CHANGED && (statbuf->st_uid != uid || statbuf->st_gid != gid)) + ) { + printf("changed ownership of '%s' to %u:%u\n", fileName, uid, gid); + } + return TRUE; + } + if (!OPT_QUIET) + bb_perror_msg("%s", fileName); /* A filename can have % in it... */ + return FALSE; +} + +int chown_main(int argc, char **argv) +{ + int retval = EXIT_SUCCESS; + char *groupName; + + opt_complementary = "-2"; + getopt32(argc, argv, OPT_STR); + + if (OPT_NODEREF) chown_func = lchown; + + argv += optind; + + /* First, check if there is a group name here */ + groupName = strchr(*argv, '.'); + if (!groupName) { + groupName = strchr(*argv, ':'); + } + + /* Check for the username and groupname */ + if (groupName) { + *groupName++ = '\0'; + gid = get_ug_id(groupName, bb_xgetgrnam); + } + if (--groupName != *argv) + uid = get_ug_id(*argv, bb_xgetpwnam); + ++argv; + + /* Ok, ready to do the deed now */ + do { + if (!recursive_action(*argv, + OPT_RECURSE, // recurse + FALSE, // follow links: TODO: -H/-L/-P + FALSE, // depth first + fileAction, // file action + fileAction, // dir action + NULL, // user data + 0) // depth + ) { + retval = EXIT_FAILURE; + } + } while (*++argv); + + return retval; +} diff --git a/coreutils/chroot.c b/coreutils/chroot.c new file mode 100644 index 0000000..62cfdc2 --- /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 <andersen@codepoet.org> + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +/* BB_AUDIT SUSv3 N/A -- Matches GNU behavior. */ + +#include "busybox.h" + +int chroot_main(int argc, char **argv) +{ + if (argc < 2) { + bb_show_usage(); + } + + ++argv; + if (chroot(*argv)) { + bb_perror_msg_and_die("cannot change root directory to %s", *argv); + } + xchdir("/"); + + ++argv; + if (argc == 2) { + argv -= 2; + if (!(*argv = getenv("SHELL"))) { + *argv = (char *) DEFAULT_SHELL; + } + argv[1] = (char *) "-i"; + } + + execvp(*argv, argv); + bb_perror_msg_and_die("cannot execute %s", *argv); +} diff --git a/coreutils/cksum.c b/coreutils/cksum.c new file mode 100644 index 0000000..5221332 --- /dev/null +++ b/coreutils/cksum.c @@ -0,0 +1,53 @@ +/* vi: set sw=4 ts=4: */ +/* + * cksum - calculate the CRC32 checksum of a file + * + * Copyright (C) 2006 by Rob Sullivan, with ideas from code by Walter Harms + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. */ + +#include "busybox.h" + +int cksum_main(int argc, char **argv) +{ + + uint32_t *crc32_table = crc32_filltable(1); + + FILE *fp; + uint32_t crc; + long length, filesize; + int bytes_read; + char *cp; + + int inp_stdin = (argc == optind) ? 1 : 0; + + do { + fp = fopen_or_warn_stdin((inp_stdin) ? bb_msg_standard_input : *++argv); + + crc = 0; + length = 0; + + while ((bytes_read = fread(bb_common_bufsiz1, 1, BUFSIZ, fp)) > 0) { + cp = bb_common_bufsiz1; + length += bytes_read; + while (bytes_read--) + crc = (crc << 8) ^ crc32_table[((crc >> 24) ^ (*cp++)) & 0xffL]; + } + + filesize = length; + + for (; length; length >>= 8) + crc = (crc << 8) ^ crc32_table[((crc >> 24) ^ length) & 0xffL]; + crc ^= 0xffffffffL; + + if (inp_stdin) { + printf("%" PRIu32 " %li\n", crc, filesize); + break; + } + + printf("%" PRIu32 " %li %s\n", crc, filesize, *argv); + fclose(fp); + } while (*(argv + 1)); + + fflush_stdout_and_exit(EXIT_SUCCESS); +} diff --git a/coreutils/cmp.c b/coreutils/cmp.c new file mode 100644 index 0000000..71007ea --- /dev/null +++ b/coreutils/cmp.c @@ -0,0 +1,127 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini cmp implementation for busybox + * + * Copyright (C) 2000,2001 by Matt Kraai <kraai@alumni.carnegiemellon.edu> + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +/* BB_AUDIT SUSv3 (virtually) compliant -- uses nicer GNU format for -l. */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/cmp.html */ + +/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org) + * + * Original version majorly reworked for SUSv3 compliance, bug fixes, and + * size optimizations. Changes include: + * 1) Now correctly distinguishes between errors and actual file differences. + * 2) Proper handling of '-' args. + * 3) Actual error checking of i/o. + * 4) Accept SUSv3 -l option. Note that we use the slightly nicer gnu format + * in the '-l' case. + */ + +#include "busybox.h" + +static FILE *cmp_xfopen_input(const char * const filename) +{ + FILE *fp; + + fp = fopen_or_warn_stdin(filename); + if (fp) + return fp; + exit(xfunc_error_retval); /* We already output an error message. */ +} + +static const char fmt_eof[] = "cmp: EOF on %s\n"; +static const char fmt_differ[] = "%s %s differ: char %d, line %d\n"; +// This fmt_l_opt uses gnu-isms. SUSv3 would be "%.0s%.0s%d %o %o\n" +static const char fmt_l_opt[] = "%.0s%.0s%d %3o %3o\n"; + +static const char opt_chars[] = "sl"; +#define CMP_OPT_s (1<<0) +#define CMP_OPT_l (1<<1) + +int cmp_main(int argc, char **argv) +{ + FILE *fp1, *fp2, *outfile = stdout; + const char *filename1, *filename2 = "-"; + const char *fmt; + int c1, c2, char_pos = 0, line_pos = 1; + unsigned opt; + int retval = 0; + + xfunc_error_retval = 2; /* 1 is returned if files are different. */ + + opt = getopt32(argc, argv, opt_chars); + + if (((opt & (CMP_OPT_s|CMP_OPT_l)) == (CMP_OPT_s|CMP_OPT_l)) + || (((unsigned int)(--argc - optind)) > 1)) + bb_show_usage(); + + fp1 = cmp_xfopen_input(filename1 = *(argv += optind)); + + if (*++argv) { + filename2 = *argv; + } + fp2 = cmp_xfopen_input(filename2); + + if (fp1 == fp2) { /* Paranioa check... stdin == stdin? */ + /* Note that we don't bother reading stdin. Neither does gnu wc. + * But perhaps we should, so that other apps down the chain don't + * get the input. Consider 'echo hello | (cmp - - && cat -)'. + */ + return 0; + } + + if (opt & CMP_OPT_l) + fmt = fmt_l_opt; + else + fmt = fmt_differ; + + do { + c1 = getc(fp1); + c2 = getc(fp2); + ++char_pos; + if (c1 != c2) { /* Remember: a read error may have occurred. */ + retval = 1; /* But assume the files are different for now. */ + if (c2 == EOF) { + /* We know that fp1 isn't at EOF or in an error state. But to + * save space below, things are setup to expect an EOF in fp1 + * if an EOF occurred. So, swap things around. + */ + fp1 = fp2; + filename1 = filename2; + c1 = c2; + } + if (c1 == EOF) { + die_if_ferror(fp1, filename1); + fmt = fmt_eof; /* Well, no error, so it must really be EOF. */ + outfile = stderr; + /* There may have been output to stdout (option -l), so + * make sure we fflush before writing to stderr. */ + xfflush_stdout(); + } + if (!(opt & CMP_OPT_s)) { + if (opt & CMP_OPT_l) { + line_pos = c1; /* line_pos is unused in the -l case. */ + } + fprintf(outfile, fmt, filename1, filename2, char_pos, line_pos, c2); + if (opt) { /* This must be -l since not -s. */ + /* If we encountered an EOF, + * the while check will catch it. */ + continue; + } + } + break; + } + if (c1 == '\n') { + ++line_pos; + } + } while (c1 != EOF); + + die_if_ferror(fp1, filename1); + die_if_ferror(fp2, filename2); + + fflush_stdout_and_exit(retval); +} diff --git a/coreutils/comm.c b/coreutils/comm.c new file mode 100644 index 0000000..91f0177 --- /dev/null +++ b/coreutils/comm.c @@ -0,0 +1,126 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini comm implementation for busybox + * + * Copyright (C) 2005 by Robert Sullivan <cogito.ergo.cogito@gmail.com> + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +#include "busybox.h" + +#define COMM_OPT_1 0x01 +#define COMM_OPT_2 0x02 +#define COMM_OPT_3 0x04 + +/* These three variables control behaviour if non-zero */ + +static int only_file_1; +static int only_file_2; +static int both; + +/* writeline outputs the input given, appropriately aligned according to class */ +static void writeline(char *line, int class) +{ + if (class == 0) { + if (!only_file_1) + return; + } else if (class == 1) { + if (!only_file_2) + return; + if (only_file_1) + putchar('\t'); + } + else /*if (class == 2)*/ { + if (!both) + return; + if (only_file_1) + putchar('\t'); + if (only_file_2) + putchar('\t'); + } + fputs(line, stdout); +} + +/* This is the real core of the program - lines are compared here */ +static void cmp_files(char **infiles) +{ +#define LINE_LEN 100 +#define BB_EOF_0 0x1 +#define BB_EOF_1 0x2 + char thisline[2][LINE_LEN]; + FILE *streams[2]; + int i; + + for (i = 0; i < 2; ++i) { + streams[i] = ((infiles[i][0] == '=' && infiles[i][1]) ? stdin : xfopen(infiles[i], "r")); + fgets(thisline[i], LINE_LEN, streams[i]); + } + + while (*thisline[0] || *thisline[1]) { + int order = 0; + + i = 0; + if (feof(streams[0])) i |= BB_EOF_0; + if (feof(streams[1])) i |= BB_EOF_1; + + if (!*thisline[0]) + order = 1; + else if (!*thisline[1]) + order = -1; + else { + int tl0_len, tl1_len; + tl0_len = strlen(thisline[0]); + tl1_len = strlen(thisline[1]); + order = memcmp(thisline[0], thisline[1], tl0_len < tl1_len ? tl0_len : tl1_len); + if (!order) + order = tl0_len < tl1_len ? -1 : tl0_len != tl1_len; + } + + if (order == 0 && !i) + writeline(thisline[1], 2); + else if (order > 0 && !(i & BB_EOF_1)) + writeline(thisline[1], 1); + else if (order < 0 && !(i & BB_EOF_0)) + writeline(thisline[0], 0); + + if (i & BB_EOF_0 & BB_EOF_1) { + break; + + } else if (i) { + i = (i & BB_EOF_0 ? 1 : 0); + while (!feof(streams[i])) { + if ((order < 0 && i) || (order > 0 && !i)) + writeline(thisline[i], i); + fgets(thisline[i], LINE_LEN, streams[i]); + } + break; + + } else { + if (order >= 0) + fgets(thisline[1], LINE_LEN, streams[1]); + if (order <= 0) + fgets(thisline[0], LINE_LEN, streams[0]); + } + } + + fclose(streams[0]); + fclose(streams[1]); +} + +int comm_main(int argc, char **argv) +{ + unsigned long flags; + + flags = getopt32(argc, argv, "123"); + + if (optind + 2 != argc) + bb_show_usage(); + + only_file_1 = !(flags & COMM_OPT_1); + only_file_2 = !(flags & COMM_OPT_2); + both = !(flags & COMM_OPT_3); + + cmp_files(argv + optind); + exit(EXIT_SUCCESS); +} diff --git a/coreutils/cp.c b/coreutils/cp.c new file mode 100644 index 0000000..47ad85e --- /dev/null +++ b/coreutils/cp.c @@ -0,0 +1,92 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini cp implementation for busybox + * + * Copyright (C) 2000 by Matt Kraai <kraai@alumni.carnegiemellon.edu> + * + * Licensed under GPL v2 or later, see file LICENSE in this tarball for details. + */ + +/* http://www.opengroup.org/onlinepubs/007904975/utilities/cp.html */ + +/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org) + * + * Size reduction. + */ + +#include "busybox.h" +#include "libcoreutils/coreutils.h" + +int cp_main(int argc, char **argv) +{ + struct stat source_stat; + struct stat dest_stat; + const char *last; + const char *dest; + int s_flags; + int d_flags; + int flags; + int status = 0; + enum { + OPT_a = 1 << (sizeof(FILEUTILS_CP_OPTSTR)-1), + OPT_r = 1 << (sizeof(FILEUTILS_CP_OPTSTR)), + OPT_P = 1 << (sizeof(FILEUTILS_CP_OPTSTR)+1), + OPT_H = 1 << (sizeof(FILEUTILS_CP_OPTSTR)+2), + OPT_L = 1 << (sizeof(FILEUTILS_CP_OPTSTR)+3), + }; + + // Soft- and hardlinking don't mix + // -P and -d are the same (-P is POSIX, -d is GNU) + // -r and -R are the same + // -a = -pdR + opt_complementary = "?:l--s:s--l:Pd:rR:apdR"; + flags = getopt32(argc, argv, FILEUTILS_CP_OPTSTR "arPHL"); + /* Default behavior of cp is to dereference, so we don't have to do + * anything special when we are given -L. + * The behavior of -H is *almost* like -L, but not quite, so let's + * just ignore it too for fun. + if (flags & OPT_L) ... + if (flags & OPT_H) ... // deref command-line params only + */ + + flags ^= FILEUTILS_DEREFERENCE; /* The sense of this flag was reversed. */ + + if (optind + 2 > argc) { + bb_show_usage(); + } + + last = argv[argc - 1]; + argv += optind; + + /* If there are only two arguments and... */ + if (optind + 2 == argc) { + s_flags = cp_mv_stat2(*argv, &source_stat, + (flags & FILEUTILS_DEREFERENCE) ? stat : lstat); + if (s_flags < 0) + return EXIT_FAILURE; + d_flags = cp_mv_stat(last, &dest_stat); + if (d_flags < 0) + return EXIT_FAILURE; + + /* ...if neither is a directory or... */ + if ( !((s_flags | d_flags) & 2) || + /* ...recursing, the 1st is a directory, and the 2nd doesn't exist... */ + ((flags & FILEUTILS_RECUR) && (s_flags & 2) && !d_flags) + ) { + /* ...do a simple copy. */ + dest = xstrdup(last); + goto DO_COPY; /* Note: optind+2==argc implies argv[1]==last below. */ + } + } + + do { + dest = concat_path_file(last, bb_get_last_path_component(*argv)); + DO_COPY: + if (copy_file(*argv, dest, flags) < 0) { + status = 1; + } + free((void*)dest); + } while (*++argv != last); + + return status; +} diff --git a/coreutils/cut.c b/coreutils/cut.c new file mode 100644 index 0000000..a538e3d --- /dev/null +++ b/coreutils/cut.c @@ -0,0 +1,285 @@ +/* vi: set sw=4 ts=4: */ +/* + * cut.c - minimalist version of cut + * + * Copyright (C) 1999,2000,2001 by Lineo, inc. + * Written by Mark Whitley <markw@codepoet.org> + * debloated by Bernhard Fischer + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +#include "busybox.h" + +/* option vars */ +static const char optstring[] = "b:c:f:d:sn"; +#define CUT_OPT_BYTE_FLGS (1<<0) +#define CUT_OPT_CHAR_FLGS (1<<1) +#define CUT_OPT_FIELDS_FLGS (1<<2) +#define CUT_OPT_DELIM_FLGS (1<<3) +#define CUT_OPT_SUPPRESS_FLGS (1<<4) + +static char delim = '\t'; /* delimiter, default is tab */ + +struct cut_list { + int startpos; + int endpos; +}; + +enum { + BOL = 0, + EOL = INT_MAX, + NON_RANGE = -1 +}; + +/* growable array holding a series of lists */ +static struct cut_list *cut_lists; +static unsigned int nlists; /* number of elements in above list */ + + +static int cmpfunc(const void *a, const void *b) +{ + return (((struct cut_list *) a)->startpos - + ((struct cut_list *) b)->startpos); + +} + +static void cut_file(FILE * file) +{ + char *line = NULL; + unsigned int linenum = 0; /* keep these zero-based to be consistent */ + + /* go through every line in the file */ + while ((line = xmalloc_getline(file)) != NULL) { + + /* set up a list so we can keep track of what's been printed */ + char * printed = xzalloc(strlen(line) * sizeof(char)); + char * orig_line = line; + unsigned int cl_pos = 0; + int spos; + + /* cut based on chars/bytes XXX: only works when sizeof(char) == byte */ + if (option_mask32 & (CUT_OPT_CHAR_FLGS | CUT_OPT_BYTE_FLGS)) { + /* print the chars specified in each cut list */ + for (; cl_pos < nlists; cl_pos++) { + spos = cut_lists[cl_pos].startpos; + while (spos < strlen(line)) { + if (!printed[spos]) { + printed[spos] = 'X'; + putchar(line[spos]); + } + spos++; + if (spos > cut_lists[cl_pos].endpos + || cut_lists[cl_pos].endpos == NON_RANGE) + break; + } + } + } else if (delim == '\n') { /* cut by lines */ + spos = cut_lists[cl_pos].startpos; + + /* get out if we have no more lists to process or if the lines + * are lower than what we're interested in */ + if (linenum < spos || cl_pos >= nlists) + goto next_line; + + /* if the line we're looking for is lower than the one we were + * passed, it means we displayed it already, so move on */ + while (spos < linenum) { + spos++; + /* go to the next list if we're at the end of this one */ + if (spos > cut_lists[cl_pos].endpos + || cut_lists[cl_pos].endpos == NON_RANGE) { + cl_pos++; + /* get out if there's no more lists to process */ + if (cl_pos >= nlists) + goto next_line; + spos = cut_lists[cl_pos].startpos; + /* get out if the current line is lower than the one + * we just became interested in */ + if (linenum < spos) + goto next_line; + } + } + + /* If we made it here, it means we've found the line we're + * looking for, so print it */ + puts(line); + goto next_line; + } else { /* cut by fields */ + int ndelim = -1; /* zero-based / one-based problem */ + int nfields_printed = 0; + char *field = NULL; + const char delimiter[2] = { delim, 0 }; + + /* does this line contain any delimiters? */ + if (strchr(line, delim) == NULL) { + if (!(option_mask32 & CUT_OPT_SUPPRESS_FLGS)) + puts(line); + goto next_line; + } + + /* process each list on this line, for as long as we've got + * a line to process */ + for (; cl_pos < nlists && line; cl_pos++) { + spos = cut_lists[cl_pos].startpos; + do { + /* find the field we're looking for */ + while (line && ndelim < spos) { + field = strsep(&line, delimiter); + ndelim++; + } + + /* we found it, and it hasn't been printed yet */ + if (field && ndelim == spos && !printed[ndelim]) { + /* if this isn't our first time through, we need to + * print the delimiter after the last field that was + * printed */ + if (nfields_printed > 0) + putchar(delim); + fputs(field, stdout); + printed[ndelim] = 'X'; + nfields_printed++; /* shouldn't overflow.. */ + } + + spos++; + + /* keep going as long as we have a line to work with, + * this is a list, and we're not at the end of that + * list */ + } while (spos <= cut_lists[cl_pos].endpos && line + && cut_lists[cl_pos].endpos != NON_RANGE); + } + } + /* if we printed anything at all, we need to finish it with a + * newline cuz we were handed a chomped line */ + putchar('\n'); + next_line: + linenum++; + free(printed); + free(orig_line); + } +} + +static const char _op_on_field[] = " only when operating on fields"; + +int cut_main(int argc, char **argv) +{ + char *sopt, *ltok; + + opt_complementary = "b--bcf:c--bcf:f--bcf"; + getopt32(argc, argv, optstring, &sopt, &sopt, &sopt, <ok); + if (!(option_mask32 & (CUT_OPT_BYTE_FLGS | CUT_OPT_CHAR_FLGS | CUT_OPT_FIELDS_FLGS))) + bb_error_msg_and_die("expected a list of bytes, characters, or fields"); + if (option_mask32 & BB_GETOPT_ERROR) + bb_error_msg_and_die("only one type of list may be specified"); + + if (option_mask32 & CUT_OPT_DELIM_FLGS) { + if (strlen(ltok) > 1) { + bb_error_msg_and_die("the delimiter must be a single character"); + } + delim = ltok[0]; + } + + /* non-field (char or byte) cutting has some special handling */ + if (!(option_mask32 & CUT_OPT_FIELDS_FLGS)) { + if (option_mask32 & CUT_OPT_SUPPRESS_FLGS) { + bb_error_msg_and_die + ("suppressing non-delimited lines makes sense%s", + _op_on_field); + } + if (delim != '\t') { + bb_error_msg_and_die + ("a delimiter may be specified%s", _op_on_field); + } + } + + /* + * parse list and put values into startpos and endpos. + * valid list formats: N, N-, N-M, -M + * more than one list can be separated by commas + */ + { + char *ntok; + int s = 0, e = 0; + + /* take apart the lists, one by one (they are separated with commas */ + while ((ltok = strsep(&sopt, ",")) != NULL) { + + /* it's actually legal to pass an empty list */ + if (strlen(ltok) == 0) + continue; + + /* get the start pos */ + ntok = strsep(<ok, "-"); + if (ntok == NULL) { + bb_error_msg + ("internal error: ntok is null for start pos!?\n"); + } else if (strlen(ntok) == 0) { + s = BOL; + } else { + s = xatoi_u(ntok); + /* account for the fact that arrays are zero based, while + * the user expects the first char on the line to be char #1 */ + if (s != 0) + s--; + } + + /* get the end pos */ + ntok = strsep(<ok, "-"); + if (ntok == NULL) { + e = NON_RANGE; + } else if (strlen(ntok) == 0) { + e = EOL; + } else { + e = xatoi_u(ntok); + /* if the user specified and end position of 0, that means "til the + * end of the line */ + if (e == 0) + e = EOL; + e--; /* again, arrays are zero based, lines are 1 based */ + if (e == s) + e = NON_RANGE; + } + + /* if there's something left to tokenize, the user passed + * an invalid list */ + if (ltok) + bb_error_msg_and_die("invalid byte or field list"); + + /* add the new list */ + cut_lists = xrealloc(cut_lists, sizeof(struct cut_list) * (++nlists)); + cut_lists[nlists-1].startpos = s; + cut_lists[nlists-1].endpos = e; + } + + /* make sure we got some cut positions out of all that */ + if (nlists == 0) + bb_error_msg_and_die("missing list of positions"); + + /* now that the lists are parsed, we need to sort them to make life + * easier on us when it comes time to print the chars / fields / lines + */ + qsort(cut_lists, nlists, sizeof(struct cut_list), cmpfunc); + } + + /* argv[(optind)..(argc-1)] should be names of file to process. If no + * files were specified or '-' was specified, take input from stdin. + * Otherwise, we process all the files specified. */ + if (argv[optind] == NULL + || (argv[optind][0] == '-' && argv[optind][1] == '\0')) { + cut_file(stdin); + } else { + FILE *file; + + for (; optind < argc; optind++) { + file = fopen_or_warn(argv[optind], "r"); + if (file) { + cut_file(file); + fclose(file); + } + } + } + if (ENABLE_FEATURE_CLEAN_UP) + free(cut_lists); + return EXIT_SUCCESS; +} diff --git a/coreutils/date.c b/coreutils/date.c new file mode 100644 index 0000000..37ccfd5 --- /dev/null +++ b/coreutils/date.c @@ -0,0 +1,239 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini date implementation for busybox + * + * by Matthew Grant <grantma@anathoth.gen.nz> + * + * iso-format handling added by Robert Griebl <griebl@gmx.de> + * bugfixes and cleanup by Bernhard Fischer + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. +*/ + +#include "busybox.h" + +/* This 'date' command supports only 2 time setting formats, + all the GNU strftime stuff (its in libc, lets use it), + setting time using UTC and displaying it, as well as + an RFC 2822 compliant date output for shell scripting + mail commands */ + +/* Input parsing code is always bulky - used heavy duty libc stuff as + much as possible, missed out a lot of bounds checking */ + +/* Default input handling to save surprising some people */ + + +#define DATE_OPT_RFC2822 0x01 +#define DATE_OPT_SET 0x02 +#define DATE_OPT_UTC 0x04 +#define DATE_OPT_DATE 0x08 +#define DATE_OPT_REFERENCE 0x10 +#define DATE_OPT_TIMESPEC 0x20 +#define DATE_OPT_HINT 0x40 + +static void xputenv(char *s) +{ + if (putenv(s) != 0) + bb_error_msg_and_die(bb_msg_memory_exhausted); +} + +static void maybe_set_utc(int opt) +{ + if (opt & DATE_OPT_UTC) + xputenv("TZ=UTC0"); +} + +int date_main(int argc, char **argv) +{ + time_t tm; + struct tm tm_time; + unsigned opt; + int ifmt = -1; + char *date_str = NULL; + char *date_fmt = NULL; + char *filename = NULL; + char *isofmt_arg; + char *hintfmt_arg; + + opt_complementary = "?:d--s:s--d" + USE_FEATURE_DATE_ISOFMT(":R--I:I--R"); + opt = getopt32(argc, argv, "Rs:ud:r:" + USE_FEATURE_DATE_ISOFMT("I::D:"), + &date_str, &date_str, &filename + USE_FEATURE_DATE_ISOFMT(, &isofmt_arg, &hintfmt_arg)); + maybe_set_utc(opt); + + if (ENABLE_FEATURE_DATE_ISOFMT && (opt & DATE_OPT_TIMESPEC)) { + if (!isofmt_arg) { + ifmt = 0; /* default is date */ + } else { + const char * const isoformats[] = + {"date", "hours", "minutes", "seconds"}; + + for (ifmt = 0; ifmt < 4; ifmt++) + if (!strcmp(isofmt_arg, isoformats[ifmt])) { + break; + } + if (ifmt == 4) /* parse error */ + bb_show_usage(); + } + } + + /* XXX, date_fmt == NULL from this always */ + if ((date_fmt == NULL) && (optind < argc) && (argv[optind][0] == '+')) { + date_fmt = &argv[optind][1]; /* Skip over the '+' */ + } else if (date_str == NULL) { + opt |= DATE_OPT_SET; + date_str = argv[optind]; + } + + /* Now we have parsed all the information except the date format + which depends on whether the clock is being set or read */ + + if (filename) { + struct stat statbuf; + xstat(filename, &statbuf); + tm = statbuf.st_mtime; + } else + time(&tm); + memcpy(&tm_time, localtime(&tm), sizeof(tm_time)); + /* Zero out fields - take her back to midnight! */ + if (date_str != NULL) { + tm_time.tm_sec = 0; + tm_time.tm_min = 0; + tm_time.tm_hour = 0; + + /* Process any date input to UNIX time since 1 Jan 1970 */ + if (ENABLE_FEATURE_DATE_ISOFMT && (opt & DATE_OPT_HINT)) { + strptime(date_str, hintfmt_arg, &tm_time); + } else if (strchr(date_str, ':') != NULL) { + /* Parse input and assign appropriately to tm_time */ + + if (sscanf(date_str, "%d:%d:%d", &tm_time.tm_hour, &tm_time.tm_min, + &tm_time.tm_sec) == 3) { + /* no adjustments needed */ + } else if (sscanf(date_str, "%d:%d", &tm_time.tm_hour, + &tm_time.tm_min) == 2) { + /* no adjustments needed */ + } else if (sscanf(date_str, "%d.%d-%d:%d:%d", &tm_time.tm_mon, + &tm_time.tm_mday, &tm_time.tm_hour, + &tm_time.tm_min, &tm_time.tm_sec) == 5) { + /* Adjust dates from 1-12 to 0-11 */ + tm_time.tm_mon -= 1; + } else if (sscanf(date_str, "%d.%d-%d:%d", &tm_time.tm_mon, + &tm_time.tm_mday, + &tm_time.tm_hour, &tm_time.tm_min) == 4) { + /* Adjust dates from 1-12 to 0-11 */ + tm_time.tm_mon -= 1; + } else if (sscanf(date_str, "%d.%d.%d-%d:%d:%d", &tm_time.tm_year, + &tm_time.tm_mon, &tm_time.tm_mday, + &tm_time.tm_hour, &tm_time.tm_min, + &tm_time.tm_sec) == 6) { + tm_time.tm_year -= 1900; /* Adjust years */ + tm_time.tm_mon -= 1; /* Adjust dates from 1-12 to 0-11 */ + } else if (sscanf(date_str, "%d.%d.%d-%d:%d", &tm_time.tm_year, + &tm_time.tm_mon, &tm_time.tm_mday, + &tm_time.tm_hour, &tm_time.tm_min) == 5) { + tm_time.tm_year -= 1900; /* Adjust years */ + tm_time.tm_mon -= 1; /* Adjust dates from 1-12 to 0-11 */ + } else { + bb_error_msg_and_die(bb_msg_invalid_date, date_str); + } + } else { + int nr; + char *cp; + + nr = sscanf(date_str, "%2d%2d%2d%2d%d", &tm_time.tm_mon, + &tm_time.tm_mday, &tm_time.tm_hour, &tm_time.tm_min, + &tm_time.tm_year); + + if (nr < 4 || nr > 5) { + bb_error_msg_and_die(bb_msg_invalid_date, date_str); + } + + cp = strchr(date_str, '.'); + if (cp) { + nr = sscanf(cp + 1, "%2d", &tm_time.tm_sec); + if (nr != 1) { + bb_error_msg_and_die(bb_msg_invalid_date, date_str); + } + } + + /* correct for century - minor Y2K problem here? */ + if (tm_time.tm_year >= 1900) { + tm_time.tm_year -= 1900; + } + /* adjust date */ + tm_time.tm_mon -= 1; + } + + /* Correct any day of week and day of year etc. fields */ + tm_time.tm_isdst = -1; /* Be sure to recheck dst. */ + tm = mktime(&tm_time); + if (tm < 0) { + bb_error_msg_and_die(bb_msg_invalid_date, date_str); + } + maybe_set_utc(opt); + + /* if setting time, set it */ + if ((opt & DATE_OPT_SET) && stime(&tm) < 0) { + bb_perror_msg("cannot set date"); + } + } + + /* Display output */ + + /* Deal with format string */ + + if (date_fmt == NULL) { + int i; + date_fmt = xzalloc(32); + if (ENABLE_FEATURE_DATE_ISOFMT && ifmt >= 0) { + strcpy(date_fmt, "%Y-%m-%d"); + if (ifmt > 0) { + i = 8; + date_fmt[i++] = 'T'; + date_fmt[i++] = '%'; + date_fmt[i++] = 'H'; + if (ifmt > 1) { + date_fmt[i++] = ':'; + date_fmt[i++] = '%'; + date_fmt[i++] = 'M'; + } + if (ifmt > 2) { + date_fmt[i++] = ':'; + date_fmt[i++] = '%'; + date_fmt[i++] = 'S'; + } +format_utc: + date_fmt[i++] = '%'; + date_fmt[i] = (opt & DATE_OPT_UTC) ? 'Z' : 'z'; + } + } else if (opt & DATE_OPT_RFC2822) { + /* Undo busybox.c for date -R */ + setlocale(LC_TIME, "C"); + strcpy(date_fmt, "%a, %d %b %Y %H:%M:%S "); + i = 22; + goto format_utc; + } else /* default case */ + date_fmt = "%a %b %e %H:%M:%S %Z %Y"; + } + + if (*date_fmt == '\0') { + /* With no format string, just print a blank line */ + *bb_common_bufsiz1 = 0; + } else { + /* Handle special conversions */ + + if (strncmp(date_fmt, "%f", 2) == 0) { + date_fmt = "%Y.%m.%d-%H:%M:%S"; + } + + /* Generate output string */ + strftime(bb_common_bufsiz1, 200, date_fmt, &tm_time); + } + puts(bb_common_bufsiz1); + + return EXIT_SUCCESS; +} diff --git a/coreutils/dd.c b/coreutils/dd.c new file mode 100644 index 0000000..01f37ab --- /dev/null +++ b/coreutils/dd.c @@ -0,0 +1,248 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini dd implementation for busybox + * + * + * Copyright (C) 2000,2001 Matt Kraai + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +#include "busybox.h" +#include <signal.h> /* For FEATURE_DD_SIGNAL_HANDLING */ + +static const struct suffix_mult dd_suffixes[] = { + { "c", 1 }, + { "w", 2 }, + { "b", 512 }, + { "kD", 1000 }, + { "k", 1024 }, + { "K", 1024 }, // compat with coreutils dd + { "MD", 1000000 }, + { "M", 1048576 }, + { "GD", 1000000000 }, + { "G", 1073741824 }, + { NULL, 0 } +}; + +static off_t out_full, out_part, in_full, in_part; + +static void dd_output_status(int ATTRIBUTE_UNUSED cur_signal) +{ + fprintf(stderr, "%"OFF_FMT"d+%"OFF_FMT"d records in\n" + "%"OFF_FMT"d+%"OFF_FMT"d records out\n", + in_full, in_part, + out_full, out_part); +} + +static ssize_t full_write_or_warn(int fd, const void *buf, size_t len, + const char* filename) +{ + ssize_t n = full_write(fd, buf, len); + if (n < 0) + bb_perror_msg("writing '%s'", filename); + return n; +} + +#if ENABLE_LFS +#define XATOU_SFX xatoull_sfx +#else +#define XATOU_SFX xatoul_sfx +#endif + +int dd_main(int argc, char **argv) +{ + enum { + sync_flag = 1 << 0, + noerror = 1 << 1, + trunc_flag = 1 << 2, + twobufs_flag = 1 << 3, + }; + int flags = trunc_flag; + size_t oc = 0, ibs = 512, obs = 512; + ssize_t n, w; + off_t seek = 0, skip = 0, count = OFF_T_MAX; + int oflag, ifd, ofd; + const char *infile = NULL, *outfile = NULL; + char *ibuf, *obuf; + + if (ENABLE_FEATURE_DD_SIGNAL_HANDLING) { + struct sigaction sa; + + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = dd_output_status; + sa.sa_flags = SA_RESTART; + sigemptyset(&sa.sa_mask); + sigaction(SIGUSR1, &sa, 0); + } + + for (n = 1; n < argc; n++) { + char *arg = argv[n]; + /* Must fit into positive ssize_t */ + if (ENABLE_FEATURE_DD_IBS_OBS && !strncmp("ibs=", arg, 4)) + ibs = xatoul_range_sfx(arg+4, 0, ((size_t)-1L)/2, dd_suffixes); + else if (ENABLE_FEATURE_DD_IBS_OBS && !strncmp("obs=", arg, 4)) + obs = xatoul_range_sfx(arg+4, 0, ((size_t)-1L)/2, dd_suffixes); + else if (!strncmp("bs=", arg, 3)) + ibs = obs = xatoul_range_sfx(arg+3, 0, ((size_t)-1L)/2, dd_suffixes); + /* These can be large: */ + else if (!strncmp("count=", arg, 6)) + count = XATOU_SFX(arg+6, dd_suffixes); + else if (!strncmp("seek=", arg, 5)) + seek = XATOU_SFX(arg+5, dd_suffixes); + else if (!strncmp("skip=", arg, 5)) + skip = XATOU_SFX(arg+5, dd_suffixes); + + else if (!strncmp("if=", arg, 3)) + infile = arg+3; + else if (!strncmp("of=", arg, 3)) + outfile = arg+3; + else if (ENABLE_FEATURE_DD_IBS_OBS && !strncmp("conv=", arg, 5)) { + arg += 5; + while (1) { + if (!strncmp("notrunc", arg, 7)) { + flags &= ~trunc_flag; + arg += 7; + } else if (!strncmp("sync", arg, 4)) { + flags |= sync_flag; + arg += 4; + } else if (!strncmp("noerror", arg, 7)) { + flags |= noerror; + arg += 7; + } else { + bb_error_msg_and_die(bb_msg_invalid_arg, arg, "conv"); + } + if (arg[0] == '\0') break; + if (*arg++ != ',') bb_show_usage(); + } + } else + bb_show_usage(); + } + + ibuf = obuf = xmalloc(ibs); + if (ibs != obs) { + flags |= twobufs_flag; + obuf = xmalloc(obs); + } + + if (infile != NULL) + ifd = xopen(infile, O_RDONLY); + else { + ifd = STDIN_FILENO; + infile = bb_msg_standard_input; + } + + if (outfile != NULL) { + oflag = O_WRONLY | O_CREAT; + + if (!seek && (flags & trunc_flag)) + oflag |= O_TRUNC; + + ofd = xopen(outfile, oflag); + + if (seek && (flags & trunc_flag)) { + if (ftruncate(ofd, seek * obs) < 0) { + struct stat st; + + if (fstat(ofd, &st) < 0 || S_ISREG(st.st_mode) || + S_ISDIR(st.st_mode)) + goto die_outfile; + } + } + } else { + ofd = STDOUT_FILENO; + outfile = bb_msg_standard_output; + } + + if (skip) { + if (lseek(ifd, skip * ibs, SEEK_CUR) < 0) { + while (skip-- > 0) { + n = safe_read(ifd, ibuf, ibs); + if (n < 0) + bb_perror_msg_and_die("%s", infile); + if (n == 0) + break; + } + } + } + + if (seek) { + if (lseek(ofd, seek * obs, SEEK_CUR) < 0) + goto die_outfile; + } + + while (in_full + in_part != count) { + if (flags & noerror) { + /* Pre-zero the buffer when doing the noerror thing */ + memset(ibuf, '\0', ibs); + } + + n = safe_read(ifd, ibuf, ibs); + if (n == 0) + break; + if (n < 0) { + if (flags & noerror) { + n = ibs; + bb_perror_msg("%s", infile); + } else + bb_perror_msg_and_die("%s", infile); + } + if ((size_t)n == ibs) + in_full++; + else { + in_part++; + if (flags & sync_flag) { + memset(ibuf + n, '\0', ibs - n); + n = ibs; + } + } + if (flags & twobufs_flag) { + char *tmp = ibuf; + while (n) { + size_t d = obs - oc; + + if (d > n) + d = n; + memcpy(obuf + oc, tmp, d); + n -= d; + tmp += d; + oc += d; + if (oc == obs) { + w = full_write_or_warn(ofd, obuf, obs, outfile); + if (w < 0) goto out_status; + if (w == obs) + out_full++; + else if (w > 0) + out_part++; + oc = 0; + } + } + } else { + w = full_write_or_warn(ofd, ibuf, n, outfile); + if (w < 0) goto out_status; + if (w == obs) + out_full++; + else if (w > 0) + out_part++; + } + } + + if (ENABLE_FEATURE_DD_IBS_OBS && oc) { + w = full_write_or_warn(ofd, obuf, oc, outfile); + if (w < 0) goto out_status; + if (w > 0) + out_part++; + } + if (close(ifd) < 0) { + bb_perror_msg_and_die("%s", infile); + } + + if (close(ofd) < 0) { + die_outfile: + bb_perror_msg_and_die("%s", outfile); + } + out_status: + dd_output_status(0); + + return EXIT_SUCCESS; +} diff --git a/coreutils/df.c b/coreutils/df.c new file mode 100644 index 0000000..6ae3eed --- /dev/null +++ b/coreutils/df.c @@ -0,0 +1,154 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini df implementation for busybox + * + * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org> + * based on original code by (I think) Bruce Perens <bruce@pixar.com>. + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +/* BB_AUDIT SUSv3 _NOT_ compliant -- options -P and -t missing. Also blocksize. */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/df.html */ + +/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org) + * + * Size reduction. Removed floating point dependency. Added error checking + * on output. Output stats on 0-sized filesystems if specifically listed on + * the command line. Properly round *-blocks, Used, and Available quantities. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <mntent.h> +#include <sys/vfs.h> +#include "busybox.h" + +#ifndef CONFIG_FEATURE_HUMAN_READABLE +static long kscale(long b, long bs) +{ + return ( b * (long long) bs + 1024/2 ) / 1024; +} +#endif + +int df_main(int argc, char **argv) +{ + long blocks_used; + long blocks_percent_used; +#ifdef CONFIG_FEATURE_HUMAN_READABLE + unsigned long df_disp_hr = 1024; +#endif + int status = EXIT_SUCCESS; + unsigned opt; + FILE *mount_table; + struct mntent *mount_entry; + struct statfs s; + static const char hdr_1k[] = "1k-blocks"; /* default display is kilobytes */ + const char *disp_units_hdr = hdr_1k; + +#ifdef CONFIG_FEATURE_HUMAN_READABLE + opt_complementary = "h-km:k-hm:m-hk"; + opt = getopt32(argc, argv, "hmk"); + if (opt & 1) { + df_disp_hr = 0; + disp_units_hdr = " Size"; + } + if (opt & 2) { + df_disp_hr = 1024*1024; + disp_units_hdr = "1M-blocks"; + } +#else + opt = getopt32(argc, argv, "k"); +#endif + + printf("Filesystem%11s%-15sUsed Available Use%% Mounted on\n", + "", disp_units_hdr); + + mount_table = NULL; + argv += optind; + if (optind >= argc) { + mount_table = setmntent(bb_path_mtab_file, "r"); + if (!mount_table) { + bb_perror_msg_and_die(bb_path_mtab_file); + } + } + + do { + const char *device; + const char *mount_point; + + if (mount_table) { + mount_entry = getmntent(mount_table); + if (!mount_entry) { + endmntent(mount_table); + break; + } + } else { + mount_point = *argv++; + if (!mount_point) { + break; + } + mount_entry = find_mount_point(mount_point, bb_path_mtab_file); + if (!mount_entry) { + bb_error_msg("%s: can't find mount point", mount_point); + SET_ERROR: + status = EXIT_FAILURE; + continue; + } + } + + device = mount_entry->mnt_fsname; + mount_point = mount_entry->mnt_dir; + + if (statfs(mount_point, &s) != 0) { + bb_perror_msg("%s", mount_point); + goto SET_ERROR; + } + + if ((s.f_blocks > 0) || !mount_table){ + blocks_used = s.f_blocks - s.f_bfree; + blocks_percent_used = 0; + if (blocks_used + s.f_bavail) { + blocks_percent_used = (((long long) blocks_used) * 100 + + (blocks_used + s.f_bavail)/2 + ) / (blocks_used + s.f_bavail); + } + + if (strcmp(device, "rootfs") == 0) { + continue; + } else if (strcmp(device, "/dev/root") == 0) { + /* Adjusts device to be the real root device, + * or leaves device alone if it can't find it */ + device = find_block_device("/"); + if (!device) { + goto SET_ERROR; + } + } + +#ifdef CONFIG_FEATURE_HUMAN_READABLE + printf("%-20s %9s ", device, + make_human_readable_str(s.f_blocks, s.f_bsize, df_disp_hr)); + + printf("%9s ", + make_human_readable_str( (s.f_blocks - s.f_bfree), + s.f_bsize, df_disp_hr)); + + printf("%9s %3ld%% %s\n", + make_human_readable_str(s.f_bavail, s.f_bsize, df_disp_hr), + blocks_percent_used, mount_point); +#else + printf("%-20s %9ld %9ld %9ld %3ld%% %s\n", + device, + kscale(s.f_blocks, s.f_bsize), + kscale(s.f_blocks-s.f_bfree, s.f_bsize), + kscale(s.f_bavail, s.f_bsize), + blocks_percent_used, mount_point); +#endif + } + + } while (1); + + fflush_stdout_and_exit(status); +} diff --git a/coreutils/diff.c b/coreutils/diff.c new file mode 100644 index 0000000..2ed26ba --- /dev/null +++ b/coreutils/diff.c @@ -0,0 +1,1243 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini diff implementation for busybox, adapted from OpenBSD diff. + * + * Copyright (C) 2006 by Robert Sullivan <cogito.ergo.cogito@hotmail.com> + * Copyright (c) 2003 Todd C. Miller <Todd.Miller@courtesan.com> + * + * Sponsored in part by the Defense Advanced Research Projects + * Agency (DARPA) and Air Force Research Laboratory, Air Force + * Materiel Command, USAF, under agreement number F39502-99-1-0512. + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +#include "busybox.h" + +#define FSIZE_MAX 32768 + +/* + * Output flags + */ +#define D_HEADER 1 /* Print a header/footer between files */ +#define D_EMPTY1 2 /* Treat first file as empty (/dev/null) */ +#define D_EMPTY2 4 /* Treat second file as empty (/dev/null) */ + +/* + * Status values for print_status() and diffreg() return values + * Guide: + * D_SAME - files are the same + * D_DIFFER - files differ + * D_BINARY - binary files differ + * D_COMMON - subdirectory common to both dirs + * D_ONLY - file only exists in one dir + * D_MISMATCH1 - path1 a dir, path2 a file + * D_MISMATCH2 - path1 a file, path2 a dir + * D_ERROR - error occurred + * D_SKIPPED1 - skipped path1 as it is a special file + * D_SKIPPED2 - skipped path2 as it is a special file + */ + +#define D_SAME 0 +#define D_DIFFER (1<<0) +#define D_BINARY (1<<1) +#define D_COMMON (1<<2) +#define D_ONLY (1<<3) +#define D_MISMATCH1 (1<<4) +#define D_MISMATCH2 (1<<5) +#define D_ERROR (1<<6) +#define D_SKIPPED1 (1<<7) +#define D_SKIPPED2 (1<<8) + +/* Command line options */ +static unsigned long cmd_flags; + +#define FLAG_a (1<<0) +#define FLAG_b (1<<1) +#define FLAG_d (1<<2) +#define FLAG_i (1<<3) +#define FLAG_L (1<<4) +#define FLAG_N (1<<5) +#define FLAG_q (1<<6) +#define FLAG_r (1<<7) +#define FLAG_s (1<<8) +#define FLAG_S (1<<9) +#define FLAG_t (1<<10) +#define FLAG_T (1<<11) +#define FLAG_U (1<<12) +#define FLAG_w (1<<13) + +/* XXX: FIXME: the following variables should be static, but gcc currently + * creates a much bigger object if we do this. */ +int context, status; +char *start, *label[2]; +struct stat stb1, stb2; +char **dl; +static int dl_count = 0; + +struct cand { + int x; + int y; + int pred; +}; + +struct line { + int serial; + int value; +} *file[2]; + +/* + * The following struct is used to record change information + * doing a "context" or "unified" diff. (see routine "change" to + * understand the highly mnemonic field names) + */ +struct context_vec { + int a; /* start line in old file */ + int b; /* end line in old file */ + int c; /* start line in new file */ + int d; /* end line in new file */ +}; + +static int *J; /* will be overlaid on class */ +static int *class; /* will be overlaid on file[0] */ +static int *klist; /* will be overlaid on file[0] after class */ +static int *member; /* will be overlaid on file[1] */ +static int clen; +static int len[2]; +static int pref, suff; /* length of prefix and suffix */ +static int slen[2]; +static int anychange; +static long *ixnew; /* will be overlaid on file[1] */ +static long *ixold; /* will be overlaid on klist */ +static struct cand *clist; /* merely a free storage pot for candidates */ +static int clistlen; /* the length of clist */ +static struct line *sfile[2]; /* shortened by pruning common prefix/suffix */ +static struct context_vec *context_vec_start; +static struct context_vec *context_vec_end; +static struct context_vec *context_vec_ptr; + +static void print_only(const char *path, size_t dirlen, const char *entry) +{ + if (dirlen > 1) + dirlen--; + printf("Only in %.*s: %s\n", (int) dirlen, path, entry); +} + +static void print_status(int val, char *path1, char *path2, char *entry) +{ + const char *const _entry = entry ? entry : ""; + char *_path1 = entry ? concat_path_file(path1, _entry) : path1; + char *_path2 = entry ? concat_path_file(path2, _entry) : path2; + + switch (val) { + case D_ONLY: + print_only(path1, strlen(path1), entry); + break; + case D_COMMON: + printf("Common subdirectories: %s and %s\n", _path1, _path2); + break; + case D_BINARY: + printf("Binary files %s and %s differ\n", _path1, _path2); + break; + case D_DIFFER: + if (cmd_flags & FLAG_q) + printf("Files %s and %s differ\n", _path1, _path2); + break; + case D_SAME: + if (cmd_flags & FLAG_s) + printf("Files %s and %s are identical\n", _path1, _path2); + break; + case D_MISMATCH1: + printf("File %s is a directory while file %s is a regular file\n", + _path1, _path2); + break; + case D_MISMATCH2: + printf("File %s is a regular file while file %s is a directory\n", + _path1, _path2); + break; + case D_SKIPPED1: + printf("File %s is not a regular file or directory and was skipped\n", + _path1); + break; + case D_SKIPPED2: + printf("File %s is not a regular file or directory and was skipped\n", + _path2); + break; + } + if (entry) { + free(_path1); + free(_path2); + } +} + +/* + * Hash function taken from Robert Sedgewick, Algorithms in C, 3d ed., p 578. + */ +static int readhash(FILE * f) +{ + int i, t, space; + int sum; + + sum = 1; + space = 0; + if (!(cmd_flags & FLAG_b) && !(cmd_flags & FLAG_w)) { + if (FLAG_i) + for (i = 0; (t = getc(f)) != '\n'; i++) { + if (t == EOF) { + if (i == 0) + return 0; + break; + } + sum = sum * 127 + t; + } else + for (i = 0; (t = getc(f)) != '\n'; i++) { + if (t == EOF) { + if (i == 0) + return 0; + break; + } + sum = sum * 127 + t; + } + } else { + for (i = 0;;) { + switch (t = getc(f)) { + case '\t': + case '\r': + case '\v': + case '\f': + case ' ': + space++; + continue; + default: + if (space && !(cmd_flags & FLAG_w)) { + i++; + space = 0; + } + sum = sum * 127 + t; + i++; + continue; + case EOF: + if (i == 0) + return 0; + /* FALLTHROUGH */ + case '\n': + break; + } + break; + } + } + /* + * There is a remote possibility that we end up with a zero sum. + * Zero is used as an EOF marker, so return 1 instead. + */ + return (sum == 0 ? 1 : sum); +} + + + +/* + * Check to see if the given files differ. + * Returns 0 if they are the same, 1 if different, and -1 on error. + */ +static int files_differ(FILE * f1, FILE * f2, int flags) +{ + char buf1[BUFSIZ], buf2[BUFSIZ]; + size_t i, j; + + if ((flags & (D_EMPTY1 | D_EMPTY2)) || stb1.st_size != stb2.st_size || + (stb1.st_mode & S_IFMT) != (stb2.st_mode & S_IFMT)) + return 1; + while (1) { + i = fread(buf1, 1, sizeof(buf1), f1); + j = fread(buf2, 1, sizeof(buf2), f2); + if (i != j) + return 1; + if (i == 0 && j == 0) { + if (ferror(f1) || ferror(f2)) + return 1; + return 0; + } + if (memcmp(buf1, buf2, i) != 0) + return 1; + } +} + +static void prepare(int i, FILE * fd, off_t filesize) +{ + struct line *p; + int h; + size_t j, sz; + + rewind(fd); + + sz = (filesize <= FSIZE_MAX ? filesize : FSIZE_MAX) / 25; + if (sz < 100) + sz = 100; + + p = xmalloc((sz + 3) * sizeof(struct line)); + for (j = 0; (h = readhash(fd));) { + if (j == sz) { + sz = sz * 3 / 2; + p = xrealloc(p, (sz + 3) * sizeof(struct line)); + } + p[++j].value = h; + } + len[i] = j; + file[i] = p; +} + +static void prune(void) +{ + int i, j; + + for (pref = 0; pref < len[0] && pref < len[1] && + file[0][pref + 1].value == file[1][pref + 1].value; pref++); + for (suff = 0; suff < len[0] - pref && suff < len[1] - pref && + file[0][len[0] - suff].value == file[1][len[1] - suff].value; + suff++); + for (j = 0; j < 2; j++) { + sfile[j] = file[j] + pref; + slen[j] = len[j] - pref - suff; + for (i = 0; i <= slen[j]; i++) + sfile[j][i].serial = i; + } +} + +static void equiv(struct line *a, int n, struct line *b, int m, int *c) +{ + int i, j; + + i = j = 1; + while (i <= n && j <= m) { + if (a[i].value < b[j].value) + a[i++].value = 0; + else if (a[i].value == b[j].value) + a[i++].value = j; + else + j++; + } + while (i <= n) + a[i++].value = 0; + b[m + 1].value = 0; + j = 0; + while (++j <= m) { + c[j] = -b[j].serial; + while (b[j + 1].value == b[j].value) { + j++; + c[j] = b[j].serial; + } + } + c[j] = -1; +} + +static int isqrt(int n) +{ + int y, x = 1; + + if (n == 0) + return 0; + + do { + y = x; + x = n / x; + x += y; + x /= 2; + } while ((x - y) > 1 || (x - y) < -1); + + return x; +} + +static int newcand(int x, int y, int pred) +{ + struct cand *q; + + if (clen == clistlen) { + clistlen = clistlen * 11 / 10; + clist = xrealloc(clist, clistlen * sizeof(struct cand)); + } + q = clist + clen; + q->x = x; + q->y = y; + q->pred = pred; + return clen++; +} + + +static int search(int *c, int k, int y) +{ + int i, j, l, t; + + if (clist[c[k]].y < y) /* quick look for typical case */ + return k + 1; + i = 0; + j = k + 1; + while (1) { + l = i + j; + if ((l >>= 1) <= i) + break; + t = clist[c[l]].y; + if (t > y) + j = l; + else if (t < y) + i = l; + else + return l; + } + return l + 1; +} + + +static int stone(int *a, int n, int *b, int *c) +{ + int i, k, y, j, l; + int oldc, tc, oldl; + unsigned int numtries; + +#if ENABLE_FEATURE_DIFF_MINIMAL + const unsigned int bound = + (cmd_flags & FLAG_d) ? UINT_MAX : MAX(256, isqrt(n)); +#else + const unsigned int bound = MAX(256, isqrt(n)); +#endif + k = 0; + c[0] = newcand(0, 0, 0); + for (i = 1; i <= n; i++) { + j = a[i]; + if (j == 0) + continue; + y = -b[j]; + oldl = 0; + oldc = c[0]; + numtries = 0; + do { + if (y <= clist[oldc].y) + continue; + l = search(c, k, y); + if (l != oldl + 1) + oldc = c[l - 1]; + if (l <= k) { + if (clist[c[l]].y <= y) + continue; + tc = c[l]; + c[l] = newcand(i, y, oldc); + oldc = tc; + oldl = l; + numtries++; + } else { + c[l] = newcand(i, y, oldc); + k++; + break; + } + } while ((y = b[++j]) > 0 && numtries < bound); + } + return k; +} + +static void unravel(int p) +{ + struct cand *q; + int i; + + for (i = 0; i <= len[0]; i++) + J[i] = i <= pref ? i : i > len[0] - suff ? i + len[1] - len[0] : 0; + for (q = clist + p; q->y != 0; q = clist + q->pred) + J[q->x + pref] = q->y + pref; +} + + +static void unsort(struct line *f, int l, int *b) +{ + int *a, i; + + a = xmalloc((l + 1) * sizeof(int)); + for (i = 1; i <= l; i++) + a[f[i].serial] = f[i].value; + for (i = 1; i <= l; i++) + b[i] = a[i]; + free(a); +} + +static int skipline(FILE * f) +{ + int i, c; + + for (i = 1; (c = getc(f)) != '\n' && c != EOF; i++) + continue; + return i; +} + + +/* + * Check does double duty: + * 1. ferret out any fortuitous correspondences due + * to confounding by hashing (which result in "jackpot") + * 2. collect random access indexes to the two files + */ +static void check(FILE * f1, FILE * f2) +{ + int i, j, jackpot, c, d; + long ctold, ctnew; + + rewind(f1); + rewind(f2); + j = 1; + ixold[0] = ixnew[0] = 0; + jackpot = 0; + ctold = ctnew = 0; + for (i = 1; i <= len[0]; i++) { + if (J[i] == 0) { + ixold[i] = ctold += skipline(f1); + continue; + } + while (j < J[i]) { + ixnew[j] = ctnew += skipline(f2); + j++; + } + if ((cmd_flags & FLAG_b) || (cmd_flags & FLAG_w) + || (cmd_flags & FLAG_i)) { + while (1) { + c = getc(f1); + d = getc(f2); + /* + * GNU diff ignores a missing newline + * in one file if bflag || wflag. + */ + if (((cmd_flags & FLAG_b) || (cmd_flags & FLAG_w)) && + ((c == EOF && d == '\n') || (c == '\n' && d == EOF))) { + break; + } + ctold++; + ctnew++; + if ((cmd_flags & FLAG_b) && isspace(c) && isspace(d)) { + do { + if (c == '\n') + break; + ctold++; + } while (isspace(c = getc(f1))); + do { + if (d == '\n') + break; + ctnew++; + } while (isspace(d = getc(f2))); + } else if (cmd_flags & FLAG_w) { + while (isspace(c) && c != '\n') { + c = getc(f1); + ctold++; + } + while (isspace(d) && d != '\n') { + d = getc(f2); + ctnew++; + } + } + if (c != d) { + jackpot++; + J[i] = 0; + if (c != '\n' && c != EOF) + ctold += skipline(f1); + if (d != '\n' && c != EOF) + ctnew += skipline(f2); + break; + } + if (c == '\n' || c == EOF) + break; + } + } else { + while (1) { + ctold++; + ctnew++; + if ((c = getc(f1)) != (d = getc(f2))) { + J[i] = 0; + if (c != '\n' && c != EOF) + ctold += skipline(f1); + if (d != '\n' && c != EOF) + ctnew += skipline(f2); + break; + } + if (c == '\n' || c == EOF) + break; + } + } + ixold[i] = ctold; + ixnew[j] = ctnew; + j++; + } + for (; j <= len[1]; j++) + ixnew[j] = ctnew += skipline(f2); +} + +/* shellsort CACM #201 */ +static void sort(struct line *a, int n) +{ + struct line *ai, *aim, w; + int j, m = 0, k; + + if (n == 0) + return; + for (j = 1; j <= n; j *= 2) + m = 2 * j - 1; + for (m /= 2; m != 0; m /= 2) { + k = n - m; + for (j = 1; j <= k; j++) { + for (ai = &a[j]; ai > a; ai -= m) { + aim = &ai[m]; + if (aim < ai) + break; /* wraparound */ + if (aim->value > ai[0].value || + (aim->value == ai[0].value && aim->serial > ai[0].serial)) + break; + w.value = ai[0].value; + ai[0].value = aim->value; + aim->value = w.value; + w.serial = ai[0].serial; + ai[0].serial = aim->serial; + aim->serial = w.serial; + } + } + } +} + + +static void uni_range(int a, int b) +{ + if (a < b) + printf("%d,%d", a, b - a + 1); + else if (a == b) + printf("%d", b); + else + printf("%d,0", b); +} + +static int fetch(long *f, int a, int b, FILE * lb, int ch) +{ + int i, j, c, lastc, col, nc; + + if (a > b) + return 0; + for (i = a; i <= b; i++) { + fseek(lb, f[i - 1], SEEK_SET); + nc = f[i] - f[i - 1]; + if (ch != '\0') { + putchar(ch); + if (cmd_flags & FLAG_T) + putchar('\t'); + } + col = 0; + for (j = 0, lastc = '\0'; j < nc; j++, lastc = c) { + if ((c = getc(lb)) == EOF) { + puts("\n\\ No newline at end of file"); + return 0; + } + if (c == '\t' && (cmd_flags & FLAG_t)) { + do { + putchar(' '); + } while (++col & 7); + } else { + putchar(c); + col++; + } + } + } + return 0; +} + +static int asciifile(FILE * f) +{ +#if ENABLE_FEATURE_DIFF_BINARY + unsigned char buf[BUFSIZ]; + int i, cnt; +#endif + + if ((cmd_flags & FLAG_a) || f == NULL) + return 1; + +#if ENABLE_FEATURE_DIFF_BINARY + rewind(f); + cnt = fread(buf, 1, sizeof(buf), f); + for (i = 0; i < cnt; i++) { + if (!isprint(buf[i]) && !isspace(buf[i])) { + return 0; + } + } +#endif + return 1; +} + +/* dump accumulated "unified" diff changes */ +static void dump_unified_vec(FILE * f1, FILE * f2) +{ + struct context_vec *cvp = context_vec_start; + int lowa, upb, lowc, upd; + int a, b, c, d; + char ch; + + if (context_vec_start > context_vec_ptr) + return; + + b = d = 0; /* gcc */ + lowa = MAX(1, cvp->a - context); + upb = MIN(len[0], context_vec_ptr->b + context); + lowc = MAX(1, cvp->c - context); + upd = MIN(len[1], context_vec_ptr->d + context); + + fputs("@@ -", stdout); + uni_range(lowa, upb); + fputs(" +", stdout); + uni_range(lowc, upd); + fputs(" @@", stdout); + putchar('\n'); + + /* + * Output changes in "unified" diff format--the old and new lines + * are printed together. + */ + for (; cvp <= context_vec_ptr; cvp++) { + a = cvp->a; + b = cvp->b; + c = cvp->c; + d = cvp->d; + + /* + * c: both new and old changes + * d: only changes in the old file + * a: only changes in the new file + */ + if (a <= b && c <= d) + ch = 'c'; + else + ch = (a <= b) ? 'd' : 'a'; + if (ch == 'c' || ch == 'd') { + fetch(ixold, lowa, a - 1, f1, ' '); + fetch(ixold, a, b, f1, '-'); + } + if (ch == 'a') + fetch(ixnew, lowc, c - 1, f2, ' '); + if (ch == 'c' || ch == 'a') + fetch(ixnew, c, d, f2, '+'); + lowa = b + 1; + lowc = d + 1; + } + fetch(ixnew, d + 1, upd, f2, ' '); + + context_vec_ptr = context_vec_start - 1; +} + + +static void print_header(const char *file1, const char *file2) +{ + if (label[0] != NULL) + printf("%s %s\n", "---", label[0]); + else + printf("%s %s\t%s", "---", file1, ctime(&stb1.st_mtime)); + if (label[1] != NULL) + printf("%s %s\n", "+++", label[1]); + else + printf("%s %s\t%s", "+++", file2, ctime(&stb2.st_mtime)); +} + + + +/* + * Indicate that there is a difference between lines a and b of the from file + * to get to lines c to d of the to file. If a is greater then b then there + * are no lines in the from file involved and this means that there were + * lines appended (beginning at b). If c is greater than d then there are + * lines missing from the to file. + */ +static void change(char *file1, FILE * f1, char *file2, FILE * f2, int a, + int b, int c, int d) +{ + static size_t max_context = 64; + + if (a > b && c > d) + return; + if (cmd_flags & FLAG_q) + return; + + /* + * Allocate change records as needed. + */ + if (context_vec_ptr == context_vec_end - 1) { + ptrdiff_t offset = context_vec_ptr - context_vec_start; + + max_context <<= 1; + context_vec_start = xrealloc(context_vec_start, + max_context * + sizeof(struct context_vec)); + context_vec_end = context_vec_start + max_context; + context_vec_ptr = context_vec_start + offset; + } + if (anychange == 0) { + /* + * Print the context/unidiff header first time through. + */ + print_header(file1, file2); + anychange = 1; + } else if (a > context_vec_ptr->b + (2 * context) + 1 && + c > context_vec_ptr->d + (2 * context) + 1) { + /* + * If this change is more than 'context' lines from the + * previous change, dump the record and reset it. + */ + dump_unified_vec(f1, f2); + } + context_vec_ptr++; + context_vec_ptr->a = a; + context_vec_ptr->b = b; + context_vec_ptr->c = c; + context_vec_ptr->d = d; + return; + +} + + +static void output(char *file1, FILE * f1, char *file2, FILE * f2) +{ + + /* Note that j0 and j1 can't be used as they are defined in math.h. + * This also allows the rather amusing variable 'j00'... */ + int m, i0, i1, j00, j01; + + rewind(f1); + rewind(f2); + m = len[0]; + J[0] = 0; + J[m + 1] = len[1] + 1; + for (i0 = 1; i0 <= m; i0 = i1 + 1) { + while (i0 <= m && J[i0] == J[i0 - 1] + 1) + i0++; + j00 = J[i0 - 1] + 1; + i1 = i0 - 1; + while (i1 < m && J[i1 + 1] == 0) + i1++; + j01 = J[i1 + 1] - 1; + J[i1] = j01; + change(file1, f1, file2, f2, i0, i1, j00, j01); + } + if (m == 0) { + change(file1, f1, file2, f2, 1, 0, 1, len[1]); + } + if (anychange != 0) { + dump_unified_vec(f1, f2); + } +} + +/* + * The following code uses an algorithm due to Harold Stone, + * which finds a pair of longest identical subsequences in + * the two files. + * + * The major goal is to generate the match vector J. + * J[i] is the index of the line in file1 corresponding + * to line i file0. J[i] = 0 if there is no + * such line in file1. + * + * Lines are hashed so as to work in core. All potential + * matches are located by sorting the lines of each file + * on the hash (called ``value''). In particular, this + * collects the equivalence classes in file1 together. + * Subroutine equiv replaces the value of each line in + * file0 by the index of the first element of its + * matching equivalence in (the reordered) file1. + * To save space equiv squeezes file1 into a single + * array member in which the equivalence classes + * are simply concatenated, except that their first + * members are flagged by changing sign. + * + * Next the indices that point into member are unsorted into + * array class according to the original order of file0. + * + * The cleverness lies in routine stone. This marches + * through the lines of file0, developing a vector klist + * of "k-candidates". At step i a k-candidate is a matched + * pair of lines x,y (x in file0 y in file1) such that + * there is a common subsequence of length k + * between the first i lines of file0 and the first y + * lines of file1, but there is no such subsequence for + * any smaller y. x is the earliest possible mate to y + * that occurs in such a subsequence. + * + * Whenever any of the members of the equivalence class of + * lines in file1 matable to a line in file0 has serial number + * less than the y of some k-candidate, that k-candidate + * with the smallest such y is replaced. The new + * k-candidate is chained (via pred) to the current + * k-1 candidate so that the actual subsequence can + * be recovered. When a member has serial number greater + * that the y of all k-candidates, the klist is extended. + * At the end, the longest subsequence is pulled out + * and placed in the array J by unravel + * + * With J in hand, the matches there recorded are + * checked against reality to assure that no spurious + * matches have crept in due to hashing. If they have, + * they are broken, and "jackpot" is recorded--a harmless + * matter except that a true match for a spuriously + * mated line may now be unnecessarily reported as a change. + * + * Much of the complexity of the program comes simply + * from trying to minimize core utilization and + * maximize the range of doable problems by dynamically + * allocating what is needed and reusing what is not. + * The core requirements for problems larger than somewhat + * are (in words) 2*length(file0) + length(file1) + + * 3*(number of k-candidates installed), typically about + * 6n words for files of length n. + */ + +static int diffreg(char *ofile1, char *ofile2, int flags) +{ + char *file1 = ofile1; + char *file2 = ofile2; + FILE *f1 = NULL; + FILE *f2 = NULL; + int rval = D_SAME; + int i; + + anychange = 0; + context_vec_ptr = context_vec_start - 1; + + if (S_ISDIR(stb1.st_mode) != S_ISDIR(stb2.st_mode)) + return (S_ISDIR(stb1.st_mode) ? D_MISMATCH1 : D_MISMATCH2); + if (strcmp(file1, "-") == 0 && strcmp(file2, "-") == 0) + goto closem; + + if (flags & D_EMPTY1) + f1 = xfopen(bb_dev_null, "r"); + else { + if (strcmp(file1, "-") == 0) + f1 = stdin; + else + f1 = xfopen(file1, "r"); + } + + if (flags & D_EMPTY2) + f2 = xfopen(bb_dev_null, "r"); + else { + if (strcmp(file2, "-") == 0) + f2 = stdin; + else + f2 = xfopen(file2, "r"); + } + + if ((i = files_differ(f1, f2, flags)) == 0) + goto closem; + else if (i != 1) { /* 1 == ok */ + /* error */ + status |= 2; + goto closem; + } + + if (!asciifile(f1) || !asciifile(f2)) { + rval = D_BINARY; + status |= 1; + goto closem; + } + + prepare(0, f1, stb1.st_size); + prepare(1, f2, stb2.st_size); + prune(); + sort(sfile[0], slen[0]); + sort(sfile[1], slen[1]); + + member = (int *) file[1]; + equiv(sfile[0], slen[0], sfile[1], slen[1], member); + member = xrealloc(member, (slen[1] + 2) * sizeof(int)); + + class = (int *) file[0]; + unsort(sfile[0], slen[0], class); + class = xrealloc(class, (slen[0] + 2) * sizeof(int)); + + klist = xmalloc((slen[0] + 2) * sizeof(int)); + clen = 0; + clistlen = 100; + clist = xmalloc(clistlen * sizeof(struct cand)); + i = stone(class, slen[0], member, klist); + free(member); + free(class); + + J = xrealloc(J, (len[0] + 2) * sizeof(int)); + unravel(klist[i]); + free(clist); + free(klist); + + ixold = xrealloc(ixold, (len[0] + 2) * sizeof(long)); + ixnew = xrealloc(ixnew, (len[1] + 2) * sizeof(long)); + check(f1, f2); + output(file1, f1, file2, f2); + + closem: + if (anychange) { + status |= 1; + if (rval == D_SAME) + rval = D_DIFFER; + } + if (f1 != NULL) + fclose(f1); + if (f2 != NULL) + fclose(f2); + if (file1 != ofile1) + free(file1); + if (file2 != ofile2) + free(file2); + return rval; +} + +#if ENABLE_FEATURE_DIFF_DIR +static void do_diff(char *dir1, char *path1, char *dir2, char *path2) +{ + + int flags = D_HEADER; + int val; + + char *fullpath1 = xasprintf("%s/%s", dir1, path1); + char *fullpath2 = xasprintf("%s/%s", dir2, path2); + + if (stat(fullpath1, &stb1) != 0) { + flags |= D_EMPTY1; + memset(&stb1, 0, sizeof(stb1)); + fullpath1 = xasprintf("%s/%s", dir1, path2); + } + if (stat(fullpath2, &stb2) != 0) { + flags |= D_EMPTY2; + memset(&stb2, 0, sizeof(stb2)); + stb2.st_mode = stb1.st_mode; + fullpath2 = xasprintf("%s/%s", dir2, path1); + } + + if (stb1.st_mode == 0) + stb1.st_mode = stb2.st_mode; + + if (S_ISDIR(stb1.st_mode) && S_ISDIR(stb2.st_mode)) { + printf("Common subdirectories: %s and %s\n", fullpath1, fullpath2); + return; + } + + if (!S_ISREG(stb1.st_mode) && !S_ISDIR(stb1.st_mode)) + val = D_SKIPPED1; + else if (!S_ISREG(stb2.st_mode) && !S_ISDIR(stb2.st_mode)) + val = D_SKIPPED2; + else + val = diffreg(fullpath1, fullpath2, flags); + + print_status(val, fullpath1, fullpath2, NULL); +} +#endif + +#if ENABLE_FEATURE_DIFF_DIR +static int dir_strcmp(const void *p1, const void *p2) +{ + return strcmp(*(char *const *) p1, *(char *const *) p2); +} + +/* This function adds a filename to dl, the directory listing. */ + +static int add_to_dirlist(const char *filename, + struct stat ATTRIBUTE_UNUSED * sb, void *userdata, + int depth ATTRIBUTE_UNUSED) +{ + dl_count++; + dl = xrealloc(dl, dl_count * sizeof(char *)); + dl[dl_count - 1] = xstrdup(filename); + if (cmd_flags & FLAG_r) { + int *pp = (int *) userdata; + int path_len = *pp + 1; + + dl[dl_count - 1] = &(dl[dl_count - 1])[path_len]; + } + return TRUE; +} + +/* This returns a sorted directory listing. */ +static char **get_dir(char *path) +{ + + int i; + char **retval; + + /* If -r has been set, then the recursive_action function will be + * used. Unfortunately, this outputs the root directory along with + * the recursed paths, so use void *userdata to specify the string + * length of the root directory. It can then be removed in + * add_to_dirlist. */ + + int path_len = strlen(path); + void *userdata = &path_len; + + /* Reset dl_count - there's no need to free dl as xrealloc does + * the job nicely. */ + dl_count = 0; + + /* Now fill dl with a listing. */ + if (cmd_flags & FLAG_r) + recursive_action(path, TRUE, TRUE, FALSE, add_to_dirlist, NULL, + userdata, 0); + else { + DIR *dp; + struct dirent *ep; + + dp = warn_opendir(path); + while ((ep = readdir(dp))) { + if ((!strcmp(ep->d_name, "..")) || (!strcmp(ep->d_name, "."))) + continue; + add_to_dirlist(ep->d_name, NULL, NULL, 0); + } + closedir(dp); + } + + /* Sort dl alphabetically. */ + qsort(dl, dl_count, sizeof(char *), dir_strcmp); + + /* Copy dl so that we can return it. */ + retval = xmalloc(dl_count * sizeof(char *)); + for (i = 0; i < dl_count; i++) + retval[i] = xstrdup(dl[i]); + + return retval; +} + +static void diffdir(char *p1, char *p2) +{ + + char **dirlist1, **dirlist2; + char *dp1, *dp2; + int dirlist1_count, dirlist2_count; + int pos; + + /* Check for trailing slashes. */ + + dp1 = last_char_is(p1, '/'); + if (dp1 != NULL) + *dp1 = '\0'; + dp2 = last_char_is(p2, '/'); + if (dp2 != NULL) + *dp2 = '\0'; + + /* Get directory listings for p1 and p2. */ + + dirlist1 = get_dir(p1); + dirlist1_count = dl_count; + dirlist1[dirlist1_count] = NULL; + dirlist2 = get_dir(p2); + dirlist2_count = dl_count; + dirlist2[dirlist2_count] = NULL; + + /* If -S was set, find the starting point. */ + if (start) { + while (*dirlist1 != NULL && strcmp(*dirlist1, start) < 0) + dirlist1++; + while (*dirlist2 != NULL && strcmp(*dirlist2, start) < 0) + dirlist2++; + if ((*dirlist1 == NULL) || (*dirlist2 == NULL)) + bb_error_msg(bb_msg_invalid_arg, "NULL", "-S"); + } + + /* Now that both dirlist1 and dirlist2 contain sorted directory + * listings, we can start to go through dirlist1. If both listings + * contain the same file, then do a normal diff. Otherwise, behaviour + * is determined by whether the -N flag is set. */ + while (*dirlist1 != NULL || *dirlist2 != NULL) { + dp1 = *dirlist1; + dp2 = *dirlist2; + pos = dp1 == NULL ? 1 : dp2 == NULL ? -1 : strcmp(dp1, dp2); + if (pos == 0) { + do_diff(p1, dp1, p2, dp2); + dirlist1++; + dirlist2++; + } else if (pos < 0) { + if (cmd_flags & FLAG_N) + do_diff(p1, dp1, p2, NULL); + else + print_only(p1, strlen(p1) + 1, dp1); + dirlist1++; + } else { + if (cmd_flags & FLAG_N) + do_diff(p1, NULL, p2, dp2); + else + print_only(p2, strlen(p2) + 1, dp2); + dirlist2++; + } + } +} +#endif + + + +int diff_main(int argc, char **argv) +{ + int gotstdin = 0; + + char *U_opt; + llist_t *L_arg = NULL; + + opt_complementary = "L::"; + cmd_flags = getopt32(argc, argv, "abdiL:NqrsS:tTU:wu", + &L_arg, &start, &U_opt); + + if (cmd_flags & FLAG_L) { + while (L_arg) { + if (label[0] == NULL) + label[0] = L_arg->data; + else if (label[1] == NULL) + label[1] = L_arg->data; + else + bb_show_usage(); + + L_arg = L_arg->link; + } + + /* If both label[0] and label[1] were set, they need to be swapped. */ + if (label[0] && label[1]) { + char *tmp; + + tmp = label[1]; + label[1] = label[0]; + label[0] = tmp; + } + } + + context = 3; /* This is the default number of lines of context. */ + if (cmd_flags & FLAG_U) { + context = xatoul_range(U_opt, 1, INT_MAX); + } + argc -= optind; + argv += optind; + + /* + * Do sanity checks, fill in stb1 and stb2 and call the appropriate + * driver routine. Both drivers use the contents of stb1 and stb2. + */ + if (argc < 2) { + bb_error_msg("missing filename"); + bb_show_usage(); + } + if (strcmp(argv[0], "-") == 0) { + fstat(STDIN_FILENO, &stb1); + gotstdin = 1; + } else + xstat(argv[0], &stb1); + if (strcmp(argv[1], "-") == 0) { + fstat(STDIN_FILENO, &stb2); + gotstdin = 1; + } else + xstat(argv[1], &stb2); + if (gotstdin && (S_ISDIR(stb1.st_mode) || S_ISDIR(stb2.st_mode))) + bb_error_msg_and_die("can't compare - to a directory"); + if (S_ISDIR(stb1.st_mode) && S_ISDIR(stb2.st_mode)) { +#if ENABLE_FEATURE_DIFF_DIR + diffdir(argv[0], argv[1]); +#else + bb_error_msg_and_die("directory comparison not supported"); +#endif + } else { + if (S_ISDIR(stb1.st_mode)) { + argv[0] = concat_path_file(argv[0], argv[1]); + xstat(argv[0], &stb1); + } + if (S_ISDIR(stb2.st_mode)) { + argv[1] = concat_path_file(argv[1], argv[0]); + xstat(argv[1], &stb2); + } + print_status(diffreg(argv[0], argv[1], 0), argv[0], argv[1], NULL); + } + exit(status); +} diff --git a/coreutils/dirname.c b/coreutils/dirname.c new file mode 100644 index 0000000..e986a97 --- /dev/null +++ b/coreutils/dirname.c @@ -0,0 +1,26 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini dirname implementation for busybox + * + * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org> + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +/* BB_AUDIT SUSv3 compliant */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/dirname.html */ + +#include <stdio.h> +#include <stdlib.h> +#include "busybox.h" + +int dirname_main(int argc, char **argv) +{ + if (argc != 2) { + bb_show_usage(); + } + + puts(dirname(argv[1])); + + fflush_stdout_and_exit(EXIT_SUCCESS); +} diff --git a/coreutils/dos2unix.c b/coreutils/dos2unix.c new file mode 100644 index 0000000..1ed8771 --- /dev/null +++ b/coreutils/dos2unix.c @@ -0,0 +1,115 @@ +/* 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 <hanecak@megaloman.sk>. + * 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 "busybox.h" + +enum ConvType { + CT_UNIX2DOS = 1, + CT_DOS2UNIX +} ConvType; + +/* if fn is NULL then input is stdin and output is stdout */ +static int convert(char *fn) +{ + FILE *in, *out; + int i; + + if (fn != NULL) { + in = xfopen(fn, "rw"); + /* + The file is then created with mode read/write and + permissions 0666 for glibc 2.0.6 and earlier or + 0600 for glibc 2.0.7 and later. + */ + snprintf(bb_common_bufsiz1, sizeof(bb_common_bufsiz1), "%sXXXXXX", fn); + /* + sizeof bb_common_bufsiz1 is 4096, so it should be big enough to + hold the full path. However if the output is truncated the + subsequent call to mkstemp would fail. + */ + if ((i = mkstemp(&bb_common_bufsiz1[0])) == -1 + || chmod(bb_common_bufsiz1, 0600) == -1) { + bb_perror_nomsg_and_die(); + } + out = fdopen(i, "w+"); + if (!out) { + close(i); + remove(bb_common_bufsiz1); + } + } else { + in = stdin; + out = stdout; + } + + while ((i = fgetc(in)) != EOF) { + if (i == '\r') + continue; + if (i == '\n') { + if (ConvType == CT_UNIX2DOS) + fputc('\r', out); + fputc('\n', out); + continue; + } + fputc(i, out); + } + + if (fn != NULL) { + if (fclose(in) < 0 || fclose(out) < 0) { + bb_perror_nomsg(); + remove(bb_common_bufsiz1); + return -2; + } + /* Assume they are both on the same filesystem (which + * should be true since we put them into the same directory + * so we _should_ be ok, but you never know... */ + if (rename(bb_common_bufsiz1, fn) < 0) { + bb_perror_msg("cannot rename '%s' as '%s'", bb_common_bufsiz1, fn); + return -1; + } + } + + return 0; +} + +int dos2unix_main(int argc, char *argv[]) +{ + int o; + + /* See if we are supposed to be doing dos2unix or unix2dos */ + if (applet_name[0] == 'd') { + ConvType = CT_DOS2UNIX; /*2 */ + } else { + ConvType = CT_UNIX2DOS; /*1 */ + } + /* -u and -d are mutally exclusive */ + opt_complementary = "?:u--d:d--u"; + /* process parameters */ + /* -u convert to unix */ + /* -d convert to dos */ + o = getopt32(argc, argv, "du"); + + /* Do the conversion requested by an argument else do the default + * conversion depending on our name. */ + if (o) + ConvType = o; + + if (optind < argc) { + while (optind < argc) + if ((o = convert(argv[optind++])) < 0) + break; + } else + o = convert(NULL); + + return o; +} diff --git a/coreutils/du.c b/coreutils/du.c new file mode 100644 index 0000000..a547b1e --- /dev/null +++ b/coreutils/du.c @@ -0,0 +1,250 @@ +/* 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 <beppu@codepoet.org> + * Copyright (C) 2002 Edward Betts <edward@debian.org> + * + * 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 "busybox.h" + +#ifdef CONFIG_FEATURE_HUMAN_READABLE +# ifdef CONFIG_FEATURE_DU_DEFAULT_BLOCKSIZE_1K +static unsigned long disp_hr = 1024; +# else +static unsigned long disp_hr = 512; +# endif +#elif defined CONFIG_FEATURE_DU_DEFAULT_BLOCKSIZE_1K +static unsigned int disp_k = 1; +#else +static unsigned int disp_k; /* bss inits to 0 */ +#endif + +static int max_print_depth = INT_MAX; +static nlink_t count_hardlinks = 1; + +static int status +#if EXIT_SUCCESS == 0 + = EXIT_SUCCESS +#endif + ; +static int print_files; +static int slink_depth; +static int du_depth; +static int one_file_system; +static dev_t dir_dev; + + +static void print(long size, const char * const filename) +{ + /* TODO - May not want to defer error checking here. */ +#ifdef CONFIG_FEATURE_HUMAN_READABLE + printf("%s\t%s\n", make_human_readable_str(size, 512, disp_hr), + filename); +#else + if (disp_k) { + size++; + size >>= 1; + } + printf("%ld\t%s\n", size, filename); +#endif +} + +/* tiny recursive du */ +static long du(const char * const filename) +{ + struct stat statbuf; + long sum; + + if ((lstat(filename, &statbuf)) != 0) { + bb_perror_msg("%s", filename); + status = EXIT_FAILURE; + return 0; + } + + if (one_file_system) { + if (du_depth == 0) { + dir_dev = statbuf.st_dev; + } else if (dir_dev != statbuf.st_dev) { + return 0; + } + } + + sum = statbuf.st_blocks; + + if (S_ISLNK(statbuf.st_mode)) { + if (slink_depth > du_depth) { /* -H or -L */ + if ((stat(filename, &statbuf)) != 0) { + bb_perror_msg("%s", filename); + status = EXIT_FAILURE; + return 0; + } + sum = statbuf.st_blocks; + if (slink_depth == 1) { + slink_depth = INT_MAX; /* Convert -H to -L. */ + } + } + } + + if (statbuf.st_nlink > count_hardlinks) { + /* Add files/directories with links only once */ + if (is_in_ino_dev_hashtable(&statbuf, NULL)) { + 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) { + 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; + ++du_depth; + sum += du(newfile); + --du_depth; + free(newfile); + } + closedir(dir); + } else if (du_depth > print_files) { + return sum; + } + if (du_depth <= max_print_depth) { + print(sum, filename); + } + return sum; +} + +int du_main(int argc, char **argv) +{ + long total; + int slink_depth_save; + int print_final_total; + char *smax_print_depth; + unsigned opt; + +#ifdef CONFIG_FEATURE_DU_DEFUALT_BLOCKSIZE_1K + if (getenv("POSIXLY_CORRECT")) { /* TODO - a new libbb function? */ +#ifdef CONFIG_FEATURE_HUMAN_READABLE + disp_hr = 512; +#else + disp_k = 0; +#endif + } +#endif + + /* 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. + */ +#ifdef CONFIG_FEATURE_HUMAN_READABLE + opt_complementary = "h-km:k-hm:m-hk:H-L:L-H:s-d:d-s"; + opt = getopt32(argc, argv, "aHkLsx" "d:" "lc" "hm", &smax_print_depth); + if((opt & (1 << 9))) { + /* -h opt */ + disp_hr = 0; + } + if((opt & (1 << 10))) { + /* -m opt */ + disp_hr = 1024*1024; + } + if((opt & (1 << 2))) { + /* -k opt */ + disp_hr = 1024; + } +#else + opt_complementary = "H-L:L-H:s-d:d-s"; + opt = getopt32(argc, argv, "aHkLsx" "d:" "lc", &smax_print_depth); +#if !defined CONFIG_FEATURE_DU_DEFAULT_BLOCKSIZE_1K + if((opt & (1 << 2))) { + /* -k opt */ + disp_k = 1; + } +#endif +#endif + if((opt & (1 << 0))) { + /* -a opt */ + print_files = INT_MAX; + } + if((opt & (1 << 1))) { + /* -H opt */ + slink_depth = 1; + } + if((opt & (1 << 3))) { + /* -L opt */ + slink_depth = INT_MAX; + } + if((opt & (1 << 4))) { + /* -s opt */ + max_print_depth = 0; + } + one_file_system = opt & (1 << 5); /* -x opt */ + if((opt & (1 << 6))) { + /* -d opt */ + max_print_depth = xatoi_u(smax_print_depth); + } + if((opt & (1 << 7))) { + /* -l opt */ + count_hardlinks = INT_MAX; + } + print_final_total = opt & (1 << 8); /* -c opt */ + + /* go through remaining args (if any) */ + argv += optind; + if (optind >= argc) { + *--argv = "."; + if (slink_depth == 1) { + slink_depth = 0; + } + } + + slink_depth_save = slink_depth; + total = 0; + do { + total += du(*argv); + slink_depth = slink_depth_save; + } while (*++argv); +#ifdef CONFIG_FEATURE_CLEAN_UP + reset_ino_dev_hashtable(); +#endif + + if (print_final_total) { + print(total, "total"); + } + + fflush_stdout_and_exit(status); +} diff --git a/coreutils/echo.c b/coreutils/echo.c new file mode 100644 index 0000000..99063ae --- /dev/null +++ b/coreutils/echo.c @@ -0,0 +1,158 @@ +/* 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 version did not allow 4-digit octals. + */ + + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include "busybox.h" + +int bb_echo(int ATTRIBUTE_UNUSED argc, char **argv) +{ +#ifndef CONFIG_FEATURE_FANCY_ECHO +#define eflag '\\' + ++argv; +#else + const char *p; + int nflag = 1; + int eflag = 0; + + while (*++argv && (**argv == '-')) { + /* 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. + */ + + if (!*(p = *argv + 1)) { /* A single '-', so echo it. */ + goto just_echo; + } + + do { + if (strrchr("neE", *p) == 0) { + goto just_echo; + } + } while (*++p); + + /* All of the options in this arg are valid, so handle them. */ + p = *argv + 1; + do { + if (*p == 'n') { + nflag = 0; + } else if (*p == 'e') { + eflag = '\\'; + } else { + eflag = 0; + } + } while (*++p); + } + +just_echo: +#endif + while (*argv) { + int c; + + while ((c = *(*argv)++)) { + if (c == eflag) { /* Check for escape seq. */ + if (**argv == 'c') { + /* '\c' means cancel newline and + * ignore all subsequent chars. */ + return 0; + } +#ifndef CONFIG_FEATURE_FANCY_ECHO + /* SUSv3 specifies that octal escapes must begin with '0'. */ + if (((unsigned int)(**argv - '1')) >= 7) +#endif + { + /* Since SUSv3 mandates a first digit of 0, 4-digit octals + * of the form \0### are accepted. */ + if ((**argv == '0') && (((unsigned int)(argv[0][1] - '0')) < 8)) { + (*argv)++; + } + /* bb_process_escape_sequence can handle nul correctly */ + c = bb_process_escape_sequence((const char **) argv); + } + } + putchar(c); + } + + if (*++argv) { + putchar(' '); + } + } + +#ifdef CONFIG_FEATURE_FANCY_ECHO + if (nflag) { + putchar('\n'); + } +#else + putchar('\n'); +#endif + return 0; +} + +int echo_main(int argc, char** argv) +{ + (void)bb_echo(argc, argv); + fflush_stdout_and_exit(EXIT_SUCCESS); +} + +/*- + * 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. <BSD Advertising Clause omitted per the July 22, 1999 licensing change + * ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change> + * + * 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 + */ diff --git a/coreutils/env.c b/coreutils/env.c new file mode 100644 index 0000000..2ce99b0 --- /dev/null +++ b/coreutils/env.c @@ -0,0 +1,129 @@ +/* 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 <andersen@codepoet.org> + */ + +/* 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 <dzo@simtreas.ru> (C) 2003 + * - correct "-" option usage + * - multiple "-u unsetenv" support + * - GNU long option support + * - use xfunc_error_retval + */ + +#include "busybox.h" +#include <errno.h> +#include <getopt.h> /* struct option */ + +#if ENABLE_FEATURE_ENV_LONG_OPTIONS +static const struct option env_long_options[] = { + { "ignore-environment", 0, NULL, 'i' }, + { "unset", 1, NULL, 'u' }, + { 0, 0, 0, 0 } +}; +#endif + +int env_main(int argc, char** argv) +{ + static char *cleanenv[1] = { NULL }; + + char **ep; + unsigned opt; + llist_t *unset_env = NULL; + extern char **environ; + + opt_complementary = "u::"; +#if ENABLE_FEATURE_ENV_LONG_OPTIONS + applet_long_options = env_long_options; +#endif + + opt = getopt32(argc, argv, "+iu:", &unset_env); + + argv += optind; + if (*argv && (argv[0][0] == '-') && !argv[0][1]) { + opt |= 1; + ++argv; + } + + if (opt & 1) + environ = cleanenv; + else if (opt & 2) { + while (unset_env) { + unsetenv(unset_env->data); + unset_env = unset_env->link; + } + } + + while (*argv && (strchr(*argv, '=') != NULL)) { + if (putenv(*argv) < 0) { + bb_perror_msg_and_die("putenv"); + } + ++argv; + } + + if (*argv) { + execvp(*argv, argv); + /* SUSv3-mandated exit codes. */ + xfunc_error_retval = (errno == ENOENT) ? 127 : 126; + bb_perror_msg_and_die("%s", *argv); + } + + for (ep = environ; *ep; ep++) { + puts(*ep); + } + + fflush_stdout_and_exit(0); +} + +/* + * 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. <BSD Advertising Clause omitted per the July 22, 1999 licensing change + * ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change> + * + * 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/expr.c b/coreutils/expr.c new file mode 100644 index 0000000..1914734 --- /dev/null +++ b/coreutils/expr.c @@ -0,0 +1,505 @@ +/* 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 <edward@debian.org>. + * Copyright (C) 2003-2005 Vladimir Oleynik <dzo@simtreas.ru> + * - 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 "busybox.h" +#include "xregex.h" + +/* The kinds of value we can have. */ +enum valtype { + integer, + string +}; +typedef enum valtype TYPE; + +#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... */ + +/* A value is.... */ +struct valinfo { + TYPE 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. */ +static char **args; + +static VALUE *docolon(VALUE * sv, VALUE * pv); +static VALUE *eval(void); +static VALUE *int_value(arith_t i); +static VALUE *str_value(char *s); +static int nextarg(char *str); +static int null(VALUE * v); +static int toarith(VALUE * v); +static void freev(VALUE * v); +static void tostring(VALUE * v); + +int expr_main(int argc, char **argv) +{ + VALUE *v; + + if (argc == 1) { + bb_error_msg_and_die("too few arguments"); + } + + args = argv + 1; + + v = eval(); + if (*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)); +} + +/* Return a VALUE for I. */ + +static VALUE *int_value(arith_t i) +{ + VALUE *v; + + v = xmalloc(sizeof(VALUE)); + v->type = integer; + v->u.i = i; + return v; +} + +/* Return a VALUE for S. */ + +static VALUE *str_value(char *s) +{ + VALUE *v; + + v = xmalloc(sizeof(VALUE)); + 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; + else /* string: */ + return v->u.s[0] == '\0' || strcmp(v->u.s, "0") == 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 int 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 nonzero if the next token matches STR exactly. + STR must not be NULL. */ + +static int nextarg(char *str) +{ + if (*args == NULL) + return 0; + return strcmp(*args, str) == 0; +} + +/* The comparison operator handling functions. */ + +static int cmp_common(VALUE * l, VALUE * r, int op) +{ + int cmpval; + + if (l->type == string || r->type == string) { + tostring(l); + tostring(r); + cmpval = strcmp(l->u.s, r->u.s); + } else + cmpval = l->u.i - r->u.i; + if (op == '<') + return cmpval < 0; + else if (op == ('L' + 'E')) + return cmpval <= 0; + else if (op == '=') + return cmpval == 0; + else if (op == '!') + return cmpval != 0; + else if (op == '>') + return cmpval > 0; + else /* >= */ + return cmpval >= 0; +} + +/* 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 == '/' || op == '%') && ri == 0) + bb_error_msg_and_die("division by zero"); + if (op == '+') + return li + ri; + else if (op == '-') + return li - ri; + else if (op == '*') + return li * ri; + else if (op == '/') + return li / ri; + else + 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] == '^') { + fprintf(stderr, "\ +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)); + if (regcomp(&re_buffer, pv->u.s, 0) != 0) + bb_error_msg_and_die("invalid regular expression"); + + /* 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); + } + return v; +} + +/* Handle bare operands and ( expr ) syntax. */ + +static VALUE *eval7(void) +{ + VALUE *v; + + if (!*args) + bb_error_msg_and_die("syntax error"); + + if (nextarg("(")) { + args++; + v = eval(); + if (!nextarg(")")) + bb_error_msg_and_die("syntax error"); + args++; + return v; + } + + if (nextarg(")")) + bb_error_msg_and_die("syntax error"); + + return str_value(*args++); +} + +/* Handle match, substr, index, length, and quote keywords. */ + +static VALUE *eval6(void) +{ + VALUE *l, *r, *v, *i1, *i2; + + if (nextarg("quote")) { + args++; + if (!*args) + bb_error_msg_and_die("syntax error"); + return str_value(*args++); + } else if (nextarg("length")) { + args++; + r = eval6(); + tostring(r); + v = int_value(strlen(r->u.s)); + freev(r); + return v; + } else if (nextarg("match")) { + args++; + l = eval6(); + r = eval6(); + v = docolon(l, r); + freev(l); + freev(r); + return v; + } else if (nextarg("index")) { + args++; + l = eval6(); + 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); + return v; + } else if (nextarg("substr")) { + args++; + l = eval6(); + 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; + } else + return eval7(); +} + +/* Handle : operator (pattern matching). + Calls docolon to do the real work. */ + +static VALUE *eval5(void) +{ + VALUE *l, *r, *v; + + l = eval6(); + while (nextarg(":")) { + 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) { + if (nextarg("*")) + op = '*'; + else if (nextarg("/")) + op = '/'; + else if (nextarg("%")) + op = '%'; + else + return l; + 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) { + if (nextarg("+")) + op = '+'; + else if (nextarg("-")) + op = '-'; + else + return l; + 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) { + if (nextarg("<")) + op = '<'; + else if (nextarg("<=")) + op = 'L' + 'E'; + else if (nextarg("=") || nextarg("==")) + op = '='; + else if (nextarg("!=")) + op = '!'; + else if (nextarg(">=")) + op = 'G' + 'E'; + else if (nextarg(">")) + op = '>'; + else + return l; + 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("&")) { + 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("|")) { + args++; + r = eval1(); + if (null(l)) { + freev(l); + l = r; + } else + freev(r); + } + return l; +} diff --git a/coreutils/false.c b/coreutils/false.c new file mode 100644 index 0000000..084bc0c --- /dev/null +++ b/coreutils/false.c @@ -0,0 +1,19 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini false implementation for busybox + * + * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org> + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +/* BB_AUDIT SUSv3 compliant */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/false.html */ + +#include <stdlib.h> +#include "busybox.h" + +int false_main(int ATTRIBUTE_UNUSED argc, char ATTRIBUTE_UNUSED **argv) +{ + return EXIT_FAILURE; +} diff --git a/coreutils/fold.c b/coreutils/fold.c new file mode 100644 index 0000000..9ca693d --- /dev/null +++ b/coreutils/fold.c @@ -0,0 +1,154 @@ +/* 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 <bug1@iinet.net.au> + + Licensed under the GPL v2 or later, see the file LICENSE in this tarball. +*/ + +#include "busybox.h" + +static unsigned long flags; +#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 (!(flags & 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) +{ + char *w_opt; + int width = 80; + int i; + int errs = 0; + + if(!ENABLE_DEBUG_YANK_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); + } + } + } + } + + flags = getopt32(argc, argv, "bsw:", &w_opt); + if (flags & FLAG_WIDTH) + width = xatoul_range(w_opt, 1, 10000); + + argv += optind; + if (!*argv) { + *--argv = "-"; + } + + 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. */ + static char *line_out = NULL; + static int allocated_out = 0; + + 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 (flags & 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); + 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 (ferror(istream) || fclose_if_not_stdin(istream)) { + bb_perror_msg("%s", *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..f2c9483 --- /dev/null +++ b/coreutils/head.c @@ -0,0 +1,139 @@ +/* vi: set sw=4 ts=4: */ +/* + * head implementation for busybox + * + * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org> + * + * 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 "busybox.h" + +static const char head_opts[] = + "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 }, + { NULL, 0 } +}; +#endif + +static const char header_fmt_str[] = "\n==> %s <==\n"; + +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_DEBUG_YANK_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_DEBUG_YANK_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(); + } + } + + argv += optind; + if (!*argv) { + *--argv = "-"; + } + + fmt = header_fmt_str + 1; +#if ENABLE_FEATURE_FANCY_HEAD + if (argc - optind <= header_threshhold) { + header_threshhold = 0; + } +#else + if (argc <= optind + 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_perror_msg("%s", *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..65447aa --- /dev/null +++ b/coreutils/hostid.c @@ -0,0 +1,25 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini hostid implementation for busybox + * + * Copyright (C) 2000 Edward Betts <edward@debian.org>. + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +/* BB_AUDIT SUSv3 N/A -- Matches GNU behavior. */ + +#include <stdlib.h> +#include <unistd.h> +#include "busybox.h" + +int hostid_main(int argc, char ATTRIBUTE_UNUSED **argv) +{ + if (argc > 1) { + bb_show_usage(); + } + + printf("%lx\n", gethostid()); + + fflush_stdout_and_exit(EXIT_SUCCESS); +} diff --git a/coreutils/id.c b/coreutils/id.c new file mode 100644 index 0000000..66874a7 --- /dev/null +++ b/coreutils/id.c @@ -0,0 +1,111 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini id implementation for busybox + * + * Copyright (C) 2000 by Randolph Chung <tausq@debian.org> + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +/* BB_AUDIT SUSv3 _NOT_ compliant -- option -G is not currently supported. */ +/* Hacked by Tito Ragusa (C) 2004 to handle usernames of whatever length and to + * be more similar to GNU id. + */ + +#include "busybox.h" +#include <stdio.h> +#include <unistd.h> +#include <sys/types.h> + +#ifdef CONFIG_SELINUX +#include <selinux/selinux.h> /* for is_selinux_enabled() */ +#endif + +#define PRINT_REAL 1 +#define NAME_NOT_NUMBER 2 +#define JUST_USER 4 +#define JUST_GROUP 8 + +static short printf_full(unsigned int id, const char *arg, const char prefix) +{ + const char *fmt = "%cid=%u"; + short status = EXIT_FAILURE; + + if (arg) { + fmt = "%cid=%u(%s)"; + status = EXIT_SUCCESS; + } + printf(fmt, prefix, id, arg); + return status; +} + +int id_main(int argc, char **argv) +{ + struct passwd *p; + uid_t uid; + gid_t gid; + unsigned long flags; + short status; + + /* Don't allow -n -r -nr -ug -rug -nug -rnug */ + /* Don't allow more than one username */ + opt_complementary = "?1:?:u--g:g--u:r?ug:n?ug"; + flags = getopt32(argc, argv, "rnug"); + + /* This values could be overwritten later */ + uid = geteuid(); + gid = getegid(); + if (flags & PRINT_REAL) { + uid = getuid(); + gid = getgid(); + } + + if (argv[optind]) { + p = getpwnam(argv[optind]); + /* bb_xgetpwnam is needed because it exits on failure */ + uid = bb_xgetpwnam(argv[optind]); + gid = p->pw_gid; + /* in this case PRINT_REAL is the same */ + } + + if (flags & (JUST_GROUP | JUST_USER)) { + /* JUST_GROUP and JUST_USER are mutually exclusive */ + if (flags & NAME_NOT_NUMBER) { + /* bb_getpwuid and bb_getgrgid exit on failure so puts cannot segfault */ + puts((flags & JUST_USER) ? bb_getpwuid(NULL, uid, -1 ) : bb_getgrgid(NULL, gid, -1 )); + } else { + printf("%u\n", (flags & JUST_USER) ? uid : gid); + } + /* exit */ + fflush_stdout_and_exit(EXIT_SUCCESS); + } + + /* Print full info like GNU id */ + /* bb_getpwuid doesn't exit on failure here */ + status = printf_full(uid, bb_getpwuid(NULL, uid, 0), 'u'); + putchar(' '); + /* bb_getgrgid doesn't exit on failure here */ + status |= printf_full(gid, bb_getgrgid(NULL, gid, 0), 'g'); + +#ifdef CONFIG_SELINUX + if (is_selinux_enabled()) { + security_context_t mysid; + char context[80]; + int len = sizeof(context); + + getcon(&mysid); + context[0] = '\0'; + if (mysid) { + len = strlen(mysid)+1; + safe_strncpy(context, mysid, len); + freecon(mysid); + } else { + safe_strncpy(context, "unknown", 8); + } + printf(" context=%s", context); + } +#endif + + putchar('\n'); + fflush_stdout_and_exit(status); +} diff --git a/coreutils/install.c b/coreutils/install.c new file mode 100644 index 0000000..3e00390 --- /dev/null +++ b/coreutils/install.c @@ -0,0 +1,131 @@ +/* vi: set sw=4 ts=4: */ +/* + * Copyright (C) 2003 by Glenn McGrath <bug1@iinet.net.au> + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + * + * TODO: -d option, need a way of recursively making directories and changing + * owner/group, will probably modify bb_make_directory(...) + */ + +#include "busybox.h" +#include "libcoreutils/coreutils.h" +#include <libgen.h> +#include <getopt.h> /* struct option */ + +#define INSTALL_OPT_CMD 1 +#define INSTALL_OPT_DIRECTORY 2 +#define INSTALL_OPT_PRESERVE_TIME 4 +#define INSTALL_OPT_STRIP 8 +#define INSTALL_OPT_GROUP 16 +#define INSTALL_OPT_MODE 32 +#define INSTALL_OPT_OWNER 64 + +#if ENABLE_FEATURE_INSTALL_LONG_OPTIONS +static const struct option install_long_options[] = { + { "directory", 0, NULL, 'd' }, + { "preserve-timestamps", 0, NULL, 'p' }, + { "strip", 0, NULL, 's' }, + { "group", 0, NULL, 'g' }, + { "mode", 0, NULL, 'm' }, + { "owner", 0, NULL, 'o' }, + { 0, 0, 0, 0 } +}; +#endif + +int install_main(int argc, char **argv) +{ + mode_t mode; + uid_t uid; + gid_t gid; + char *gid_str = "-1"; + char *uid_str = "-1"; + char *mode_str = "0755"; + int copy_flags = FILEUTILS_DEREFERENCE | FILEUTILS_FORCE; + int ret = EXIT_SUCCESS, flags, i, isdir; + +#if ENABLE_FEATURE_INSTALL_LONG_OPTIONS + applet_long_options = install_long_options; +#endif + opt_complementary = "?:s--d:d--s"; + /* -c exists for backwards compatibility, its needed */ + flags = getopt32(argc, argv, "cdpsg:m:o:", &gid_str, &mode_str, &uid_str); /* 'a' must be 2nd */ + + /* preserve access and modification time, this is GNU behaviour, BSD only preserves modification time */ + if (flags & INSTALL_OPT_PRESERVE_TIME) { + copy_flags |= FILEUTILS_PRESERVE_STATUS; + } + bb_parse_mode(mode_str, &mode); + gid = get_ug_id(gid_str, bb_xgetgrnam); + uid = get_ug_id(uid_str, bb_xgetpwnam); + umask(0); + + /* Create directories + * don't use bb_make_directory() as it can't change uid or gid + * perhaps bb_make_directory() should be improved. + */ + if (flags & INSTALL_OPT_DIRECTORY) { + for (argv += optind; *argv; argv++) { + char *old_argv_ptr = *argv + 1; + char *argv_ptr; + do { + argv_ptr = strchr(old_argv_ptr, '/'); + old_argv_ptr = argv_ptr; + if (argv_ptr) { + *argv_ptr = '\0'; + old_argv_ptr++; + } + if (mkdir(*argv, mode) == -1) { + if (errno != EEXIST) { + bb_perror_msg("cannot create %s", *argv); + ret = EXIT_FAILURE; + break; + } + } + else if (lchown(*argv, uid, gid) == -1) { + bb_perror_msg("cannot change ownership of %s", *argv); + ret = EXIT_FAILURE; + break; + } + if (argv_ptr) { + *argv_ptr = '/'; + } + } while (old_argv_ptr); + } + return ret; + } + + { + struct stat statbuf; + isdir = lstat(argv[argc - 1], &statbuf)<0 + ? 0 : S_ISDIR(statbuf.st_mode); + } + for (i = optind; i < argc - 1; i++) { + char *dest; + + dest = argv[argc - 1]; + if (isdir) dest = concat_path_file(argv[argc - 1], basename(argv[i])); + ret |= copy_file(argv[i], dest, copy_flags); + + /* Set the file mode */ + if (chmod(dest, mode) == -1) { + bb_perror_msg("cannot change permissions of %s", dest); + ret = EXIT_FAILURE; + } + + /* Set the user and group id */ + if (lchown(dest, uid, gid) == -1) { + bb_perror_msg("cannot change ownership of %s", dest); + ret = EXIT_FAILURE; + } + if (flags & INSTALL_OPT_STRIP) { + if (execlp("strip", "strip", dest, NULL) == -1) { + bb_error_msg("strip failed"); + ret = EXIT_FAILURE; + } + } + 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..6413be0 --- /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 <stdlib.h> +#include <string.h> +#include <stdio.h> +#include "busybox.h" + +int length_main(int argc, char **argv) +{ + if ((argc != 2) || (**(++argv) == '-')) { + bb_show_usage(); + } + + printf("%lu\n", (unsigned long)strlen(*argv)); + + fflush_stdout_and_exit(EXIT_SUCCESS); +} 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 <andersen@codepoet.org> +# +# 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..102c7b5 --- /dev/null +++ b/coreutils/libcoreutils/coreutils.h @@ -0,0 +1,16 @@ +/* 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 + +typedef int (*stat_func)(const char *fn, struct stat *ps); + +extern int cp_mv_stat2(const char *fn, struct stat *fn_stat, stat_func sf); +extern int cp_mv_stat(const char *fn, struct stat *fn_stat); + +extern mode_t getopt_mk_fifo_nod(int argc, char **argv); + +#endif diff --git a/coreutils/libcoreutils/cp_mv_stat.c b/coreutils/libcoreutils/cp_mv_stat.c new file mode 100644 index 0000000..2e4f25e --- /dev/null +++ b/coreutils/libcoreutils/cp_mv_stat.c @@ -0,0 +1,43 @@ +/* vi: set sw=4 ts=4: */ +/* + * coreutils utility routine + * + * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org> + * + * 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 cp_mv_stat2(const char *fn, struct stat *fn_stat, stat_func sf) +{ + if (sf(fn, fn_stat) < 0) { + if (errno != ENOENT) { + bb_perror_msg("cannot stat '%s'", fn); + return -1; + } + return 0; + } else if (S_ISDIR(fn_stat->st_mode)) { + return 3; + } + return 1; +} + +int 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..3a3d341 --- /dev/null +++ b/coreutils/libcoreutils/getopt_mk_fifo_nod.c @@ -0,0 +1,40 @@ +/* vi: set sw=4 ts=4: */ +/* + * coreutils utility routine + * + * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org> + * + * 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 <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include "libbb.h" +#include "coreutils.h" + +mode_t getopt_mk_fifo_nod(int argc, char **argv) +{ + mode_t mode = 0666; + char *smode = NULL; + + getopt32(argc, argv, "m:", &smode); + if(smode) { + if (bb_parse_mode(smode, &mode)) + umask(0); + } + return mode; +} diff --git a/coreutils/ln.c b/coreutils/ln.c new file mode 100644 index 0000000..231a3bf --- /dev/null +++ b/coreutils/ln.c @@ -0,0 +1,104 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini ln implementation for busybox + * + * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org> + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +/* BB_AUDIT SUSv3 compliant */ +/* BB_AUDIT GNU options missing: -d, -F, -i, and -v. */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/ln.html */ + +#include "busybox.h" + +#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) +{ + int status = EXIT_SUCCESS; + int flag; + char *last; + char *src_name; + char *src; + char *suffix = "~"; + struct stat statbuf; + int (*link_func)(const char *, const char *); + + flag = getopt32(argc, 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(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(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_perror_msg("%s", *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_perror_msg("%s", 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_perror_msg("%s", 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..695a736 --- /dev/null +++ b/coreutils/logname.c @@ -0,0 +1,42 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini logname implementation for busybox + * + * Copyright (C) 2000 Edward Betts <edward@debian.org>. + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +/* BB_AUDIT SUSv3 compliant */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/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 <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include "busybox.h" + +int logname_main(int argc, char ATTRIBUTE_UNUSED **argv) +{ + const char *p; + + if (argc > 1) { + bb_show_usage(); + } + + if ((p = getlogin()) != NULL) { + puts(p); + fflush_stdout_and_exit(EXIT_SUCCESS); + } + + bb_perror_msg_and_die("getlogin"); +} diff --git a/coreutils/ls.c b/coreutils/ls.c new file mode 100644 index 0000000..960c161 --- /dev/null +++ b/coreutils/ls.c @@ -0,0 +1,940 @@ +/* vi: set sw=4 ts=4: */ +/* + * tiny-ls.c version 0.1.0: A minimalist 'ls' + * Copyright (C) 1996 Brian Candler <B.Candler@pobox.com> + * + * 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 <blocks>" 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 "busybox.h" +#include <getopt.h> + +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)]) + +/* colored LS support by JaWi, janwillem.janssen@lxtreme.nl */ +#if ENABLE_FEATURE_LS_COLOR +static int show_color; +/* long option entry used only for --color, which has no short option + * equivalent */ +static const struct option ls_color_opt[] = { + { "color", optional_argument, NULL, 1 }, + { NULL, 0, NULL, 0 } +}; +#else +enum { show_color = 0 }; +#endif + +/* + * a directory entry and its stat info are stored here + */ +struct dnode { /* the basic node */ + char *name; /* the dir entry name */ + 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 */ +}; +typedef struct dnode dnode_t; + +static struct dnode **list_dir(const char *); +static struct dnode **dnalloc(int); +static int list_single(struct dnode *); + +static unsigned all_fmt; + +#if ENABLE_FEATURE_AUTOWIDTH +static unsigned tabstops = COLUMN_GAP; +static unsigned terminal_width = TERMINAL_WIDTH; +#else +enum { + tabstops = COLUMN_GAP, + terminal_width = TERMINAL_WIDTH, +}; +#endif + +static int status = EXIT_SUCCESS; + +static struct dnode *my_stat(char *fullname, char *name) +{ + struct stat dstat; + struct dnode *cur; + USE_SELINUX(security_context_t sid = NULL;) + + if (all_fmt & FOLLOW_LINKS) { +#if ENABLE_SELINUX + if (is_selinux_enabled()) { + getfilecon(fullname, &sid); + } +#endif + if (stat(fullname, &dstat)) { + bb_perror_msg("%s", fullname); + status = EXIT_FAILURE; + return 0; + } + } else { +#if ENABLE_SELINUX + if (is_selinux_enabled()) { + lgetfilecon(fullname,&sid); + } +#endif + if (lstat(fullname, &dstat)) { + bb_perror_msg("%s", fullname); + status = 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++) { + 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(cur->fullname); /* free the filename */ + free(cur); /* free the dnode */ + } + free(dnp); /* free the array holding the dnode pointers */ +} +#else +#define dfree(...) do {} while(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)) { + 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) do {} while(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 = strlen(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) + puts(""); + 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) { + status = 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] == 0 || ( + entry->d_name[1] == '.' + && entry->d_name[2] == 0)) + && !(all_fmt & DISP_DOT)) + continue; + if (!(all_fmt & DISP_HIDDEN)) + continue; + } + fullname = concat_path_file(path, entry->d_name); + cur = my_stat(fullname, strrchr(fullname, '/') + 1); + 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(struct dnode *dn) +{ + int i, column = 0; + +#if ENABLE_FEATURE_LS_USERNAME + char scratch[16]; +#endif +#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 + bb_getpwuid(scratch, dn->dstat.st_uid, sizeof(scratch)); + printf("%-8.8s ", scratch); + bb_getgrgid(scratch, dn->dstat.st_gid, sizeof(scratch)); + printf("%-8.8s", scratch); + 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) { + age = time(NULL) - 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 + column += printf("%s", dn->name); + if (show_color) { + printf("\033[0m"); + } + break; + case LIST_SYMLINK: + if (S_ISLNK(dn->dstat.st_mode)) { + char *lpath = xreadlink(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[] = "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:"); + +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 + (1U<<31) +}; + + +int ls_main(int argc, char **argv) +{ + struct dnode **dnd; + struct dnode **dnf; + struct dnode **dnp; + struct dnode *dn; + struct dnode *cur; + unsigned opt; + int nfiles = 0; + int dnfiles; + int dndirs; + int oi; + int ac; + int i; + char **av; + USE_FEATURE_AUTOWIDTH(char *tabstops_str = NULL;) + USE_FEATURE_AUTOWIDTH(char *terminal_width_str = NULL;) + USE_FEATURE_LS_COLOR(char *color_opt;) + + all_fmt = LIST_SHORT | + (ENABLE_FEATURE_LS_SORTFILES * (SORT_NAME | SORT_FORWARD)); + +#if ENABLE_FEATURE_AUTOWIDTH + /* Obtain the terminal width */ + get_terminal_width_height(STDOUT_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 = getopt32(argc, argv, ls_options, &tabstops_str, &terminal_width_str + USE_FEATURE_LS_COLOR(, &color_opt)); + if (tabstops_str) + tabstops = xatou(tabstops_str); + if (terminal_width_str) + terminal_width = xatou(terminal_width_str); +#else + opt = getopt32(argc, 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; + 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"))) + show_color = 1; + } + if (opt & (1 << i)) { /* next flag after short options */ + if (!color_opt || !strcmp("always", color_opt)) + show_color = 1; + else if (color_opt && !strcmp("never", color_opt)) + show_color = 0; + else if (color_opt && !strcmp("auto", color_opt) && 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); + + /* + * when there are no cmd line args we have to supply a default "." arg. + * we will create a second argv array, "av" that will hold either + * our created "." arg, or the real cmd line args. The av array + * just holds the pointers- we don't move the date the pointers + * point to. + */ + ac = argc - optind; /* how many cmd line args are left */ + if (ac < 1) { + static const char *const dotdir[] = { "." }; + + av = (char **) dotdir; + ac = 1; + } else { + av = argv + optind; + } + + /* now, everything is in the av array */ + if (ac > 1) + all_fmt |= DISP_DIRNAME; /* 2 or more items? label directories */ + + /* stuff the command line file names into a dnode array */ + dn = NULL; + for (oi = 0; oi < ac; oi++) { + cur = my_stat(av[oi], av[oi]); + if (!cur) + continue; + cur->allocated = 0; + cur->next = dn; + dn = cur; + nfiles++; + } + + /* 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 status; +} diff --git a/coreutils/md5_sha1_sum.c b/coreutils/md5_sha1_sum.c new file mode 100644 index 0000000..e8d3a15 --- /dev/null +++ b/coreutils/md5_sha1_sum.c @@ -0,0 +1,186 @@ +/* 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 "busybox.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) +{ + int len = 0; + char *hex_value = xmalloc((hash_length * 2) + 2); + while (hash_length--) { + len += sprintf(hex_value + len, "%02x", *hash_value++); + } + return 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 (*update)(const void*, size_t, void*); + void (*final)(void*, void*); + + src_fd = STDIN_FILENO; + if (filename[0] != '-' || filename[1]) { /* not "-" */ + src_fd = open(filename, O_RDONLY); + if (src_fd < 0) { + bb_perror_msg("%s", filename); + return NULL; + } + } + + /* figure specific hash algorithims */ + if (ENABLE_MD5SUM && hash_algo==HASH_MD5) { + md5_begin(&context.md5); + update = (void (*)(const void*, size_t, void*))md5_hash; + final = (void (*)(void*, void*))md5_end; + hash_len = 16; + } else if (ENABLE_SHA1SUM && hash_algo==HASH_SHA1) { + sha1_begin(&context.sha1); + update = (void (*)(const void*, size_t, void*))sha1_hash; + final = (void (*)(void*, 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) +{ + int return_value = EXIT_SUCCESS; + uint8_t *hash_value; + unsigned flags; + hash_algo_t hash_algo = ENABLE_MD5SUM + ? (ENABLE_SHA1SUM ? (**argv=='m' ? HASH_MD5 : HASH_SHA1) : HASH_MD5) + : HASH_SHA1; + + if (ENABLE_FEATURE_MD5_SHA1_SUM_CHECK) + flags = getopt32(argc, argv, "scw"); + else optind = 1; + + 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 (argc == optind) { + argv[argc++] = "-"; + } + + if (ENABLE_FEATURE_MD5_SHA1_SUM_CHECK && (flags & FLAG_CHECK)) { + FILE *pre_computed_stream; + int count_total = 0; + int count_failed = 0; + char *file_ptr = argv[optind]; + char *line; + + if (optind + 1 != argc) { + bb_error_msg_and_die + ("only one argument may be specified when using -c"); + } + + pre_computed_stream = stdin; + if (file_ptr[0] != '-' || file_ptr[1]) { /* not "-" */ + pre_computed_stream = xfopen(file_ptr, "r"); + } + + while ((line = xmalloc_getline(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 { + while (optind < argc) { + char *file_ptr = argv[optind++]; + + hash_value = hash_file(file_ptr, hash_algo); + if (hash_value == NULL) { + return_value = EXIT_FAILURE; + } else { + printf("%s %s\n", hash_value, file_ptr); + free(hash_value); + } + } + } + return return_value; +} diff --git a/coreutils/mkdir.c b/coreutils/mkdir.c new file mode 100644 index 0000000..2cc9c7a --- /dev/null +++ b/coreutils/mkdir.c @@ -0,0 +1,66 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini mkdir implementation for busybox + * + * Copyright (C) 2001 Matt Kraai <kraai@alumni.carnegiemellon.edu> + * + * 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. + */ + +#include <stdlib.h> +#include <unistd.h> +#include <getopt.h> /* struct option */ +#include "busybox.h" + +#if ENABLE_FEATURE_MKDIR_LONG_OPTIONS +static const struct option mkdir_long_options[] = { + { "mode", 1, NULL, 'm' }, + { "parents", 0, NULL, 'p' }, + { 0, 0, 0, 0 } +}; +#endif + +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_FEATURE_MKDIR_LONG_OPTIONS + applet_long_options = mkdir_long_options; +#endif + opt = getopt32(argc, argv, "m:p", &smode); + 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 (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..24d27e7 --- /dev/null +++ b/coreutils/mkfifo.c @@ -0,0 +1,38 @@ +/* vi: set sw=4 ts=4: */ +/* + * mkfifo implementation for busybox + * + * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org> + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +/* BB_AUDIT SUSv3 compliant */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/mkfifo.html */ + +#include <stdlib.h> +#include <unistd.h> +#include <sys/types.h> +#include "busybox.h" +#include "libcoreutils/coreutils.h" + +int mkfifo_main(int argc, char **argv) +{ + mode_t mode; + int retval = EXIT_SUCCESS; + + mode = getopt_mk_fifo_nod(argc, argv); + + if (!*(argv += optind)) { + bb_show_usage(); + } + + do { + if (mkfifo(*argv, mode) < 0) { + bb_perror_msg("%s", *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..8ca511c --- /dev/null +++ b/coreutils/mknod.c @@ -0,0 +1,50 @@ +/* vi: set sw=4 ts=4: */ +/* + * mknod implementation for busybox + * + * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org> + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +/* BB_AUDIT SUSv3 N/A -- Matches GNU behavior. */ + +#include <sys/sysmacros.h> // For makedev + +#include "busybox.h" +#include "libcoreutils/coreutils.h" + +static const char modes_chars[] = { '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) +{ + mode_t mode; + dev_t dev; + const char *name; + + mode = getopt_mk_fifo_nod(argc, argv); + argv += optind; + argc -= optind; + + if ((argc >= 2) && ((name = strchr(modes_chars, argv[1][0])) != NULL)) { + mode |= modes_cubp[(int)(name[4])]; + + dev = 0; + if ((*name != 'p') && ((argc -= 2) == 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_perror_msg_and_die("%s", name); + } + } + bb_show_usage(); +} diff --git a/coreutils/mv.c b/coreutils/mv.c new file mode 100644 index 0000000..353124b --- /dev/null +++ b/coreutils/mv.c @@ -0,0 +1,131 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini mv implementation for busybox + * + * Copyright (C) 2000 by Matt Kraai <kraai@alumni.carnegiemellon.edu> + * + * 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 <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <dirent.h> +#include <errno.h> +#include <stdlib.h> +#include <getopt.h> /* struct option */ +#include "busybox.h" +#include "libcoreutils/coreutils.h" + +#if ENABLE_FEATURE_MV_LONG_OPTIONS +static const struct option mv_long_options[] = { + { "interactive", 0, NULL, 'i' }, + { "force", 0, NULL, 'f' }, + { 0, 0, 0, 0 } +}; +#endif + +#define OPT_FILEUTILS_FORCE 1 +#define OPT_FILEUTILS_INTERACTIVE 2 + +static const char fmt[] = "cannot overwrite %sdirectory with %sdirectory"; + +int mv_main(int argc, char **argv) +{ + struct stat dest_stat; + const char *last; + const char *dest; + unsigned long flags; + int dest_exists; + int status = 0; + +#if ENABLE_FEATURE_MV_LONG_OPTIONS + applet_long_options = mv_long_options; +#endif + opt_complementary = "f-i:i-f"; + flags = getopt32(argc, argv, "fi"); + if (optind + 2 > argc) { + bb_show_usage(); + } + + last = argv[argc - 1]; + argv += optind; + + if (optind + 2 == argc) { + dest_exists = cp_mv_stat(last, &dest_stat); + if (dest_exists < 0) { + return 1; + } + + if (!(dest_exists & 2)) { + dest = last; + goto DO_MOVE; + } + } + + do { + dest = concat_path_file(last, bb_get_last_path_component(*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_stat(*argv, &source_stat)) < 1) { + bb_perror_msg("cannot rename '%s'", *argv); + } else { + 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; + } + } + if ((copy_file(*argv, dest, + FILEUTILS_RECUR | FILEUTILS_PRESERVE_STATUS) >= 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..2938618 --- /dev/null +++ b/coreutils/nice.c @@ -0,0 +1,54 @@ +/* vi: set sw=4 ts=4: */ +/* + * nice implementation for busybox + * + * Copyright (C) 2005 Manuel Novoa III <mjn3@codepoet.org> + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +#include <sys/resource.h> +#include "busybox.h" + +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); + } + } + + execvp(*argv, argv); /* Now exec the desired program. */ + + /* The exec failed... */ + xfunc_error_retval = (errno == ENOENT) ? 127 : 126; /* SUSv3 */ + bb_perror_msg_and_die("%s", *argv); +} diff --git a/coreutils/nohup.c b/coreutils/nohup.c new file mode 100644 index 0000000..21adfc1 --- /dev/null +++ b/coreutils/nohup.c @@ -0,0 +1,53 @@ +/* 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 <rob@landley.net> + * Copyright 2006 Bernhard Fischer + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +#include "busybox.h" + +int nohup_main(int argc, char **argv) +{ + int temp, nullfd; + char *nohupout, *home = NULL; + + xfunc_error_retval = 127; + + if (argc<2) bb_show_usage(); + + nullfd = xopen(bb_dev_null, O_WRONLY|O_APPEND); + /* If stdin is a tty, detach from it. */ + if (isatty(STDIN_FILENO)) dup2(nullfd, 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 dup2(nullfd, STDOUT_FILENO); + + /* If we have a tty on strderr, announce filename and redirect to stdout. + * Else redirect to /dev/null. + */ + temp = isatty(STDERR_FILENO); + if (temp) bb_error_msg("appending to %s", nohupout); + dup2(temp ? STDOUT_FILENO : nullfd, STDERR_FILENO); + close(nullfd); + signal (SIGHUP, SIG_IGN); + + execvp(argv[1],argv+1); + if (00 && ENABLE_FEATURE_CLEAN_UP && home) free(nohupout); + bb_perror_msg_and_die("%s", argv[1]); +} diff --git a/coreutils/od.c b/coreutils/od.c new file mode 100644 index 0000000..8de8662 --- /dev/null +++ b/coreutils/od.c @@ -0,0 +1,224 @@ +/* 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. + */ + + +#if ENABLE_DESKTOP +/* This one provides -t (busybox's own build script needs it) */ +#include "od_bloaty.c" +#else + +#include <getopt.h> +#include "busybox.h" +#include "dump.h" + +#define isdecdigit(c) isdigit(c) +#define ishexdigit(c) (isxdigit)(c) + +static void +odoffset(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; + /* + * bb_dump_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; + } + + /* bb_dump_skip over the number */ + if (base == 16) + for (num = p; ishexdigit(*p); ++p); + else + for (num = p; isdecdigit(*p); ++p); + + /* check for no number */ + if (num == p) + return; + + /* if terminates with a '.', base is decimal */ + if (*p == '.') { + if (base) + return; + base = 10; + } + + bb_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) + bb_dump_skip = 0; + else { + if (*p) { + if (*p == 'b') { + bb_dump_skip *= 512; + ++p; + } else if (*p == 'B') { + bb_dump_skip *= 1024; + ++p; + } + } + if (*p) + bb_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: + bb_dump_fshead->nextfu->fmt[TYPE_OFFSET] + = bb_dump_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[] = "aBbcDdeFfHhIiLlOoXxv"; + +static const char od_o2si[] = { + 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) +{ + int ch; + int first = 1; + char *p; + bb_dump_vflag = FIRST; + bb_dump_length = -1; + + while ((ch = getopt(argc, argv, od_opts)) > 0) { + if (ch == 'v') { + bb_dump_vflag = ALL; + } else if (((p = strchr(od_opts, ch)) != NULL) && (*p != '\0')) { + if (first) { + first = 0; + bb_dump_add("\"%07.7_Ao\n\""); + bb_dump_add("\"%07.7_ao \""); + } else { + bb_dump_add("\" \""); + } + bb_dump_add(add_strings[(int)od_o2si[(p-od_opts)]]); + } else { /* P, p, s, w, or other unhandled */ + bb_show_usage(); + } + } + if (!bb_dump_fshead) { + bb_dump_add("\"%07.7_Ao\n\""); + bb_dump_add("\"%07.7_ao \" 8/2 \"%06o \" \"\\n\""); + } + + argc -= optind; + argv += optind; + + odoffset(argc, &argv); + + return bb_dump_dump(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..9f1a582 --- /dev/null +++ b/coreutils/od_bloaty.c @@ -0,0 +1,1466 @@ +/* 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 Denis 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 "busybox.h" +#include <getopt.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[] = +{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[] = +{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[] = +{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[] = +{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[] = { + -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 dummy { + int assert_width_bytes_matches_size_spec_decl + [sizeof width_bytes / sizeof width_bytes[0] == N_SIZE_SPECS ? 1 : -1]; +}; + +static size_t string_min; +static int flag_dump_strings; + +/* Non-zero if an old-style 'pseudo-address' was specified. */ +static int flag_pseudo_start; + +/* The difference between the old-style pseudo starting address and + the number of bytes to skip. */ +static off_t pseudo_offset; + +/* 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 number of input bytes to skip before formatting and writing. */ +static off_t n_bytes_to_skip; // = 0; + +/* When zero, MAX_BYTES_TO_FORMAT and END_OFFSET are ignored, and all + input is formatted. */ +static int limit_bytes_to_format; // = 0; + +/* The maximum number of bytes that will be formatted. */ +static off_t max_bytes_to_format; + +/* The offset of the first byte after the last byte to be formatted. */ +static off_t end_offset; + +/* When nonzero 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 int abbreviate_duplicate_blocks = 1; + +/* An array of specs describing how to format each input block. */ +static size_t n_specs; +static struct tspec *spec; + +/* 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 size_t bytes_per_block; + +/* Human-readable representation of *file_list (for error messages). + It differs from *file_list only when *file_list is "-". */ +static char const *input_filename; + +/* A NULL-terminated list of the file-arguments from the command line. */ +static char const *const *file_list; + +/* Initializer for file_list if no file-arguments + were specified on the command line. */ +static char const *const default_file_list[] = { "-", NULL }; + +/* The input stream associated with the current file. */ +static FILE *in_stream; + +static int ioerror; + +#define MAX_INTEGRAL_TYPE_SIZE sizeof(ulonglong_t) +static unsigned char integral_type_size[MAX_INTEGRAL_TYPE_SIZE + 1] = { + [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 unsigned char fp_type_size[MAX_FP_TYPE_SIZE + 1] = { + /* 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 thru 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 ATTRIBUTE_UNUSED) +{ + /* Names for some non-printing characters. */ + static const char charname[33][3] = { + "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 ATTRIBUTE_UNUSED) +{ + // 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) { + input_filename = *file_list; + if (!input_filename) + return; + file_list++; + in_stream = fopen_or_warn_stdin(input_filename); + if (in_stream) { + if (in_stream == stdin) + input_filename = bb_msg_standard_input; + 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", input_filename); + 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, make *NEXT point at the + character following the just-decoded format (if *NEXT is non-NULL), + and return zero. For example, if S were "d4afL" + *NEXT would be set 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 void +decode_one_format(const char *s_orig, const char *s, const char **next, + 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; + + assert(tspec != NULL); + + switch (*s) { + case 'd': + case 'o': + case 'u': + case 'x': { + static const char CSIL[] = "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[] = { + sizeof(char), + sizeof(short), + sizeof(int), + sizeof(long), + }; + size = CSIL_sizeof[p - CSIL]; + } + +#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[] = "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[] = "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++; + + if (next != NULL) + *next = s; +} + +/* Decode the modern od format string S. Append the decoded + representation to the global array SPEC, reallocating SPEC if + necessary. Return zero if S is valid, nonzero otherwise. */ + +static void +decode_format_string(const char *s) +{ + const char *s_orig = s; + + while (*s != '\0') { + struct tspec tspec; + const char *next; + + decode_one_format(s_orig, s, &next, &tspec); + + assert(s != next); + s = next; + n_specs++; + spec = xrealloc(spec, n_specs * sizeof(*spec)); + memcpy(&spec[n_specs-1], &tspec, sizeof *spec); + } +} + +/* 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 nonnegative size, + position the file pointer by reading. */ + char buf[BUFSIZ]; + size_t n_bytes_read, n_bytes_to_read = BUFSIZ; + + 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 ATTRIBUTE_UNUSED, char c ATTRIBUTE_UNUSED) +{ +} + +static char address_fmt[] = "%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, ')'); + /* BUG in coreutils 5.2.1! must be "if (c) putchar(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 (abbreviate_duplicate_blocks + && !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 }, + { NULL, 0 } + }; + 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. Accumulate return values from calls to read_block and + check_and_close, and if any was nonzero, return nonzero. + Otherwise, return zero. */ + +static void +dump(void) +{ + char *block[2]; + off_t current_offset; + int idx; + size_t n_bytes_read; + + block[0] = xmalloc(2*bytes_per_block); + block[1] = block[0] + bytes_per_block; + + current_offset = n_bytes_to_skip; + + 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. The return + value is nonzero if any errors occured, zero otherwise. */ + +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. Return nonzero if an error + occurs. Otherwise, return zero. */ + +static void +dump_strings(void) +{ + size_t bufsize = MAX(100, string_min); + char *buf = xmalloc(bufsize); + off_t address = n_bytes_to_skip; + + 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 null-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: putc(c, stdout); + } + } + 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) +{ + static const struct suffix_mult bkm[] = { + { "b", 512 }, + { "k", 1024 }, + { "m", 1024*1024 }, + { NULL, 0 } + }; + 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 = 0; // only for gcc + 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 struct option long_options[] = { + { "skip-bytes", required_argument, NULL, 'j' }, + { "address-radix", required_argument, NULL, 'A' }, + { "read-bytes", required_argument, NULL, 'N' }, + { "format", required_argument, NULL, 't' }, + { "output-duplicates", no_argument, NULL, 'v' }, + { "strings", optional_argument, NULL, 'S' }, + { "width", optional_argument, NULL, 'w' }, + { "traditional", no_argument, NULL, 0xff }, + { NULL, 0, NULL, 0 } + }; +#endif + char *str_A, *str_N, *str_j, *str_S; + char *str_w = NULL; + llist_t *lst_t = NULL; + + spec = NULL; + format_address = format_address_std; + address_base_char = 'o'; + address_pad_len_char = '7'; + flag_dump_strings = 0; + + /* Parse command line */ + opt_complementary = "t::"; // list +#if ENABLE_GETOPT_LONG + applet_long_options = long_options; +#endif + opt = getopt32(argc, 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, &str_w); + argc -= optind; + argv += optind; + if (opt & OPT_A) { + static const char doxn[] = "doxn"; + static const char doxn_address_base_char[] = { + 'u', 'o', 'x', /* '?' fourth one is not important */ + }; + static const uint8_t doxn_address_pad_len_char[] = { + '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)... + lst_t = rev_llist(lst_t); + while (lst_t) { + decode_format_string(lst_t->data); + lst_t = lst_t->link; + } + if (opt & OPT_v) abbreviate_duplicate_blocks = 0; + 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 = default_file_list; + 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 1; + + 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 */ + bytes_per_block = 32; + if (str_w) + bytes_per_block = xatou(str_w); + if (!bytes_per_block || bytes_per_block % l_c_m != 0) { + bb_error_msg("warning: invalid width %u; using %d instead", + 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(); + else + dump(); + + if (fclose(stdin) == EOF) + bb_perror_msg_and_die(bb_msg_standard_input); + + return (ioerror != 0); /* err != 0 - return 1 (failure) */ +} diff --git a/coreutils/printenv.c b/coreutils/printenv.c new file mode 100644 index 0000000..ec50f71 --- /dev/null +++ b/coreutils/printenv.c @@ -0,0 +1,41 @@ +/* vi: set sw=4 ts=4: */ +/* + * printenv implementation for busybox + * + * Copyright (C) 2005 by Erik Andersen <andersen@codepoet.org> + * Copyright (C) 2005 by Mike Frysinger <vapier@gentoo.org> + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include "busybox.h" + +int printenv_main(int argc, char **argv) +{ + extern char **environ; + int e = 0; + + /* no variables specified, show whole env */ + if (argc == 1) + while (environ[e]) + puts(environ[e++]); + + /* search for specified variables and print them out if found */ + else { + int i; + size_t l; + char *arg, *env; + + for (i=1; (arg = argv[i]); ++i) + for (; (env = environ[e]); ++e) { + l = strlen(arg); + if (!strncmp(env, arg, l) && env[l] == '=') + puts(env + l + 1); + } + } + + fflush_stdout_and_exit(0); +} diff --git a/coreutils/printf.c b/coreutils/printf.c new file mode 100644 index 0000000..0e81835 --- /dev/null +++ b/coreutils/printf.c @@ -0,0 +1,321 @@ +/* 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 <djm@gnu.ai.mit.edu> */ + + +// 19990508 Busy Boxed! Dave Cinege + +#include "busybox.h" + +static int print_formatted(char *format, int argc, char **argv); +static void print_direc(char *start, size_t length, + int field_width, int precision, char *argument); + +typedef void (*converter)(char *arg, void *result); + +static void multiconvert(char *arg, void *result, converter convert) +{ + char s[16]; + if (*arg == '"' || *arg == '\'') { + sprintf(s, "%d", (unsigned char)arg[1]); + arg = s; + } + convert(arg, result); + if (errno) /* Huh, looks strange... bug? */ + fputs(arg, stderr); +} + +static void conv_strtoul(char *arg, void *result) +{ + *(unsigned long*)result = bb_strtoul(arg, NULL, 10); +} +static void conv_strtol(char *arg, void *result) +{ + *(long*)result = bb_strtol(arg, NULL, 10); +} +static void conv_strtod(char *arg, void *result) +{ + char *end; + /* Well, this one allows leading whitespace... so what */ + /* What I like much less is that "-" is accepted too! :( */ + *(double*)result = strtod(arg, &end); + if (end[0]) errno = ERANGE; +} + +static unsigned long my_xstrtoul(char *arg) +{ + unsigned long result; + multiconvert(arg, &result, conv_strtoul); + return result; +} + +static long my_xstrtol(char *arg) +{ + long result; + multiconvert(arg, &result, conv_strtol); + return result; +} + +static double my_xstrtod(char *arg) +{ + double result; + multiconvert(arg, &result, conv_strtod); + return result; +} + +static void print_esc_string(char *str) +{ + for (; *str; str++) { + if (*str == '\\') { + str++; + putchar(bb_process_escape_sequence((const char **)&str)); + } else { + putchar(*str); + } + + } +} + +int printf_main(int argc, char **argv) +{ + char *format; + int args_used; + + if (argc <= 1 || argv[1][0] == '-') { + bb_show_usage(); + } + + format = argv[1]; + argc -= 2; + argv += 2; + + do { + args_used = print_formatted(format, argc, argv); + argc -= args_used; + argv += args_used; + } + while (args_used > 0 && argc > 0); + +/* if (argc > 0) + fprintf(stderr, "excess args ignored"); +*/ + + return EXIT_SUCCESS; +} + +/* Print the text in FORMAT, using ARGV (with ARGC elements) for + arguments to any '%' directives. + Return the number of elements of ARGV used. */ + +static int print_formatted(char *format, int argc, char **argv) +{ + int save_argc = argc; /* Preserve original value. */ + char *f; /* Pointer into 'format'. */ + char *direc_start; /* Start of % directive. */ + size_t direc_length; /* Length of % directive. */ + int field_width; /* Arg to first '*', or -1 if none. */ + int precision; /* Arg to second '*', or -1 if none. */ + + for (f = format; *f; ++f) { + switch (*f) { + case '%': + direc_start = f++; + direc_length = 1; + field_width = precision = -1; + if (*f == '%') { + putchar('%'); + break; + } + if (*f == 'b') { + if (argc > 0) { + print_esc_string(*argv); + ++argv; + --argc; + } + break; + } + if (strchr("-+ #", *f)) { + ++f; + ++direc_length; + } + if (*f == '*') { + ++f; + ++direc_length; + if (argc > 0) { + field_width = my_xstrtoul(*argv); + ++argv; + --argc; + } else + field_width = 0; + } else + while (isdigit(*f)) { + ++f; + ++direc_length; + } + if (*f == '.') { + ++f; + ++direc_length; + if (*f == '*') { + ++f; + ++direc_length; + if (argc > 0) { + precision = my_xstrtoul(*argv); + ++argv; + --argc; + } else + precision = 0; + } else + while (isdigit(*f)) { + ++f; + ++direc_length; + } + } + if (*f == 'l' || *f == 'L' || *f == 'h') { + ++f; + ++direc_length; + } + /* + if (!strchr ("diouxXfeEgGcs", *f)) + fprintf(stderr, "%%%c: invalid directive", *f); + */ + ++direc_length; + if (argc > 0) { + print_direc(direc_start, direc_length, field_width, + precision, *argv); + ++argv; + --argc; + } else + print_direc(direc_start, direc_length, field_width, + precision, ""); + break; + + case '\\': + if (*++f == 'c') + exit(0); + putchar(bb_process_escape_sequence((const char **)&f)); + f--; + break; + + default: + putchar(*f); + } + } + + return save_argc - argc; +} + +static void +print_direc(char *start, size_t length, int field_width, int precision, + char *argument) +{ + char *p; /* Null-terminated copy of % directive. */ + + p = xmalloc((unsigned) (length + 1)); + strncpy(p, start, length); + p[length] = 0; + + switch (p[length - 1]) { + case 'd': + case 'i': + if (field_width < 0) { + if (precision < 0) + printf(p, my_xstrtol(argument)); + else + printf(p, precision, my_xstrtol(argument)); + } else { + if (precision < 0) + printf(p, field_width, my_xstrtol(argument)); + else + printf(p, field_width, precision, my_xstrtol(argument)); + } + break; + + case 'o': + case 'u': + case 'x': + case 'X': + if (field_width < 0) { + if (precision < 0) + printf(p, my_xstrtoul(argument)); + else + printf(p, precision, my_xstrtoul(argument)); + } else { + if (precision < 0) + printf(p, field_width, my_xstrtoul(argument)); + else + printf(p, field_width, precision, my_xstrtoul(argument)); + } + break; + + case 'f': + case 'e': + case 'E': + case 'g': + case 'G': + if (field_width < 0) { + if (precision < 0) + printf(p, my_xstrtod(argument)); + else + printf(p, precision, my_xstrtod(argument)); + } else { + if (precision < 0) + printf(p, field_width, my_xstrtod(argument)); + else + printf(p, field_width, precision, my_xstrtod(argument)); + } + break; + + case 'c': + printf(p, *argument); + break; + + case 's': + if (field_width < 0) { + if (precision < 0) + printf(p, argument); + else + printf(p, precision, argument); + } else { + if (precision < 0) + printf(p, field_width, argument); + else + printf(p, field_width, precision, argument); + } + break; + } + + free(p); +} diff --git a/coreutils/pwd.c b/coreutils/pwd.c new file mode 100644 index 0000000..bd36d62 --- /dev/null +++ b/coreutils/pwd.c @@ -0,0 +1,24 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini pwd implementation for busybox + * + * Copyright (C) 1995, 1996 by Bruce Perens <bruce@pixar.com>. + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +#include <stdio.h> +#include <stdlib.h> +#include "busybox.h" + +int pwd_main(int argc, char **argv) +{ + char *buf; + + if ((buf = xgetcwd(NULL)) != NULL) { + puts(buf); + fflush_stdout_and_exit(EXIT_SUCCESS); + } + + return EXIT_FAILURE; +} diff --git a/coreutils/realpath.c b/coreutils/realpath.c new file mode 100644 index 0000000..5f3242f --- /dev/null +++ b/coreutils/realpath.c @@ -0,0 +1,48 @@ +/* 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 <limits.h> +#include <stdlib.h> +#include "busybox.h" + +int realpath_main(int argc, 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 (--argc == 0) { + bb_show_usage(); + } + + do { + argv++; + if (realpath(*argv, resolved_path) != NULL) { + puts(resolved_path); + } else { + retval = EXIT_FAILURE; + bb_perror_msg("%s", *argv); + } + } while (--argc); + +#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..5df7d5f --- /dev/null +++ b/coreutils/rm.c @@ -0,0 +1,52 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini rm implementation for busybox + * + * Copyright (C) 2001 Matt Kraai <kraai@alumni.carnegiemellon.edu> + * + * 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 <unistd.h> +#include "busybox.h" + +int rm_main(int argc, char **argv) +{ + int status = 0; + int flags = 0; + unsigned opt; + + opt_complementary = "f-i:i-f"; + opt = getopt32(argc, argv, "fiRr"); + if(opt & 1) + flags |= FILEUTILS_FORCE; + if(opt & 2) + flags |= FILEUTILS_INTERACTIVE; + if(opt & 12) + flags |= FILEUTILS_RECUR; + + if (*(argv += optind) != NULL) { + do { + const char *base = bb_get_last_path_component(*argv); + + if ((base[0] == '.') && (!base[1] || ((base[1] == '.') && !base[2]))) { + 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..c61bae7 --- /dev/null +++ b/coreutils/rmdir.c @@ -0,0 +1,60 @@ +/* vi: set sw=4 ts=4: */ +/* + * rmdir implementation for busybox + * + * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org> + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +/* BB_AUDIT SUSv3 compliant */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/rmdir.html */ + +#include <stdlib.h> +#include <unistd.h> +#include <libgen.h> +#include "busybox.h" + +int rmdir_main(int argc, char **argv) +{ + int status = EXIT_SUCCESS; + int flags; + int do_dot; + char *path; + + flags = getopt32(argc, argv, "p"); + + argv += optind; + + if (!*argv) { + bb_show_usage(); + } + + do { + path = *argv; + + /* Record if the first char was a '.' so we can use dirname later. */ + do_dot = (*path == '.'); + + do { + if (rmdir(path) < 0) { + bb_perror_msg("'%s'", path); /* Match gnu rmdir msg. */ + status = EXIT_FAILURE; + } else if (flags) { + /* Note: path was not empty or null since rmdir succeeded. */ + path = dirname(path); + /* Path is now just the parent component. Note that dirname + * returns "." if there are no parents. We must distinguish + * this from the case of the original path starting with '.'. + */ + if (do_dot || (*path != '.') || path[1]) { + continue; + } + } + break; + } while (1); + + } while (*++argv); + + return status; +} diff --git a/coreutils/seq.c b/coreutils/seq.c new file mode 100644 index 0000000..f2b4706 --- /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 <stdio.h> +#include <stdlib.h> +#include "busybox.h" + +int seq_main(int argc, char **argv) +{ + double last, first, increment, i; + + first = increment = 1; + switch (argc) { + case 4: + increment = atof(argv[2]); + case 3: + first = 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. */ + for (i = first; + (increment > 0 && i <= last) || (increment < 0 && i >=last); + i += increment) + { + printf("%g\n", i); + } + + return EXIT_SUCCESS; +} diff --git a/coreutils/sleep.c b/coreutils/sleep.c new file mode 100644 index 0000000..e32e215 --- /dev/null +++ b/coreutils/sleep.c @@ -0,0 +1,67 @@ +/* vi: set sw=4 ts=4: */ +/* + * sleep implementation for busybox + * + * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org> + * + * 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 <stdlib.h> +#include <limits.h> +#include <unistd.h> +#include "busybox.h" + +#ifdef CONFIG_FEATURE_FANCY_SLEEP +static const struct suffix_mult sfx[] = { + { "s", 1 }, + { "m", 60 }, + { "h", 60*60 }, + { "d", 24*60*60 }, + { NULL, 0 } +}; +#endif + +int sleep_main(int argc, char **argv) +{ + unsigned int duration; + +#ifdef CONFIG_FEATURE_FANCY_SLEEP + + if (argc < 2) { + bb_show_usage(); + } + + ++argv; + duration = 0; + do { + duration += xatoul_range_sfx(*argv, 0, UINT_MAX-duration, sfx); + } while (*++argv); + +#else /* CONFIG_FEATURE_FANCY_SLEEP */ + + if (argc != 2) { + bb_show_usage(); + } + + duration = xatou(argv[1]); + +#endif /* CONFIG_FEATURE_FANCY_SLEEP */ + + if (sleep(duration)) { + bb_perror_nomsg_and_die(); + } + + return EXIT_SUCCESS; +} diff --git a/coreutils/sort.c b/coreutils/sort.c new file mode 100644 index 0000000..c23bf9c --- /dev/null +++ b/coreutils/sort.c @@ -0,0 +1,357 @@ +/* vi: set sw=4 ts=4: */ +/* + * SuS3 compliant sort implementation for busybox + * + * Copyright (C) 2004 by Rob Landley <rob@landley.net> + * + * MAINTAINER: Rob Landley <rob@landley.net> + * + * 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 "busybox.h" + +static int global_flags; + +/* + sort [-m][-o output][-bdfinru][-t char][-k keydef]... [file...] + sort -c [-bdfinru][-t char][-k keydef][file] +*/ + +/* These are sort types */ +#define FLAG_n 1 /* Numeric sort */ +#define FLAG_g 2 /* Sort using strtod() */ +#define FLAG_M 4 /* Sort date */ +/* ucsz apply to root level only, not keys. b at root level implies bb */ +#define FLAG_u 8 /* Unique */ +#define FLAG_c 16 /* Check: no output, exit(!ordered) */ +#define FLAG_s 32 /* Stable sort, no ascii fallback at end */ +#define FLAG_z 64 /* Input is null terminated, not \n */ +/* These can be applied to search keys, the previous four can't */ +#define FLAG_b 128 /* Ignore leading blanks */ +#define FLAG_r 256 /* Reverse */ +#define FLAG_d 512 /* Ignore !(isalnum()|isspace()) */ +#define FLAG_f 1024 /* Force uppercase */ +#define FLAG_i 2048 /* Ignore !isprint() */ +#define FLAG_bb 32768 /* Ignore trailing blanks */ + + +#if ENABLE_FEATURE_SORT_BIG +static char key_separator; + +static struct sort_key { + struct sort_key *next_key; /* linked list */ + unsigned short range[4]; /* start word, start char, end word, end char */ + int flags; +} *key_list; + +static char *get_key(char *str, struct sort_key *key, int flags) +{ + int start = 0, end = 0, len, i, j; + + /* 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++) { + /* Skip leading blanks or first separator */ + if (str[end]) { + if (!key_separator) + while (isspace(str[end])) end++; + } + /* Skip body of key */ + for (; str[end]; end++) { + if (key_separator) { + if (str[end] == key_separator) + break; + } else { + if (isspace(str[end])) + break; + } + } + } + } + if (!j) start = end; + } + /* Key with explicit separator starts after separator */ + if (key_separator && str[start] == key_separator) + start++; + /* 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) \ + ((global_flags & FLAG_z) \ + ? bb_get_chunk_from_file(fp, NULL) \ + : xmalloc_getline(fp)) +#else +#define GET_LINE(fp) xmalloc_getline(fp) +#endif + +/* Iterate through keys list and perform comparisons */ +static int compare_keys(const void *xarg, const void *yarg) +{ + int flags = global_flags, 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 : global_flags; + /* 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: + retval = strcmp(x, y); + 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 + } + /* Perform fallback sort if necessary */ + if (!retval && !(global_flags & FLAG_s)) + retval = strcmp(*(char **)xarg, *(char **)yarg); + + if (flags & FLAG_r) return -retval; + return retval; +} + +int sort_main(int argc, char **argv) +{ + FILE *fp, *outfile = NULL; + int linecount = 0, i, flag; + char *line, **lines = NULL, *optlist = "ngMucszbrdfimS:T:o:k:t:"; + int c; + + xfunc_error_retval = 2; + /* Parse command line options */ + while ((c = getopt(argc, argv, optlist)) > 0) { + line = strchr(optlist, c); + if (!line) bb_show_usage(); + switch (*line) { +#if ENABLE_FEATURE_SORT_BIG + case 'o': + if (outfile) bb_error_msg_and_die("too many -o"); + outfile = xfopen(optarg, "w"); + break; + case 't': + if (key_separator || optarg[1]) + bb_error_msg_and_die("too many -t"); + key_separator = *optarg; + break; + /* parse sort key */ + case 'k': { + struct sort_key *key = add_key(); + char *temp, *temp2; + + temp = optarg; + for (i = 0; *temp;) { + /* Start of range */ + key->range[2*i] = (unsigned short)strtol(temp, &temp, 10); + if (*temp == '.') + key->range[(2*i)+1] = (unsigned short)strtol(temp+1, &temp, 10); + for (; *temp; temp++) { + if (*temp == ',' && !i++) { + temp++; + break; + } /* no else needed: fall through to syntax error + because comma isn't in optlist */ + temp2 = strchr(optlist, *temp); + flag = (1 << (temp2 - optlist)); + if (!temp2 || (flag > FLAG_M && flag < FLAG_b)) + bb_error_msg_and_die("unknown key option"); + /* b after ',' means strip _trailing_ space */ + if (i && flag == FLAG_b) flag = FLAG_bb; + key->flags |= flag; + } + } + break; + } +#endif + default: + global_flags |= (1 << (line - optlist)); + /* global b strips leading and trailing spaces */ + if (global_flags & FLAG_b) global_flags |= FLAG_bb; + break; + } + } + /* Open input files and read data */ + for (i = argv[optind] ? optind : optind-1; argv[i]; i++) { + fp = stdin; + if (i >= optind && (argv[i][0] != '-' || argv[i][1])) + fp = xfopen(argv[i], "r"); + for (;;) { + line = GET_LINE(fp); + if (!line) break; + if (!(linecount & 63)) + lines = xrealloc(lines, sizeof(char *) * (linecount + 64)); + lines[linecount++] = line; + } + fclose(fp); + } +#if ENABLE_FEATURE_SORT_BIG + /* if no key, perform alphabetic sort */ + if (!key_list) + add_key()->range[0] = 1; + /* handle -c */ + if (global_flags & FLAG_c) { + int j = (global_flags & 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 1; + } + return 0; + } +#endif + /* Perform the actual sort */ + qsort(lines, linecount, sizeof(char *), compare_keys); + /* handle -u */ + if (global_flags & FLAG_u) { + for (flag = 0, 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 */ + if (!outfile) outfile = stdout; + for (i = 0; i < linecount; i++) + fprintf(outfile, "%s\n", lines[i]); + + fflush_stdout_and_exit(EXIT_SUCCESS); +} diff --git a/coreutils/stat.c b/coreutils/stat.c new file mode 100644 index 0000000..31dd662 --- /dev/null +++ b/coreutils/stat.c @@ -0,0 +1,538 @@ +/* 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 <andersen@codepoet.org> + * Copyright (C) 2005 by Mike Frysinger <vapier@gentoo.org> + * + * 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 "busybox.h" + +/* vars to control behavior */ +#define OPT_TERSE 2 +#define OPT_DEREFERENCE 4 +static long flags; + +static char const *file_type(struct stat const *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 char const *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")]; + strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S.000000000", localtime(&t)); + return 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 char const *human_fstype(long f_type) +{ + int i; + static const struct types { + long type; + const char *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" } + }; + for (i=0; humantypes[i].type; ++i) + if (humantypes[i].type == f_type) + break; + return humantypes[i].fs; +} + +#ifdef CONFIG_FEATURE_STAT_FORMAT +/* print statfs info */ +static void print_statfs(char *pformat, size_t buf_len, char m, + char const *filename, void const *data) +{ + struct statfs const *statfsbuf = data; + + switch (m) { + case 'n': + strncat(pformat, "s", buf_len); + printf(pformat, filename); + break; + case 'i': + strncat(pformat, "Lx", buf_len); + printf(pformat, statfsbuf->f_fsid); + break; + case 'l': + strncat(pformat, "lu", buf_len); + printf(pformat, statfsbuf->f_namelen); + break; + case 't': + strncat(pformat, "lx", buf_len); + printf(pformat, (unsigned long int) (statfsbuf->f_type)); /* no equiv. */ + break; + case 'T': + strncat(pformat, "s", buf_len); + printf(pformat, human_fstype(statfsbuf->f_type)); + break; + case 'b': + strncat(pformat, "jd", buf_len); + printf(pformat, (intmax_t) (statfsbuf->f_blocks)); + break; + case 'f': + strncat(pformat, "jd", buf_len); + printf(pformat, (intmax_t) (statfsbuf->f_bfree)); + break; + case 'a': + strncat(pformat, "jd", buf_len); + printf(pformat, (intmax_t) (statfsbuf->f_bavail)); + break; + case 'S': + case 's': + strncat(pformat, "lu", buf_len); + printf(pformat, (unsigned long int) (statfsbuf->f_bsize)); + break; + case 'c': + strncat(pformat, "jd", buf_len); + printf(pformat, (intmax_t) (statfsbuf->f_files)); + break; + case 'd': + strncat(pformat, "jd", buf_len); + printf(pformat, (intmax_t) (statfsbuf->f_ffree)); + break; + default: + strncat(pformat, "c", buf_len); + printf(pformat, m); + break; + } +} + +/* print stat info */ +static void print_stat(char *pformat, size_t buf_len, char m, + char const *filename, void const *data) +{ +#define TYPE_SIGNED(t) (! ((t) 0 < (t) -1)) + struct stat *statbuf = (struct stat *) data; + struct passwd *pw_ent; + struct group *gw_ent; + + switch (m) { + case 'n': + strncat(pformat, "s", buf_len); + printf(pformat, filename); + break; + case 'N': + strncat(pformat, "s", buf_len); + if (S_ISLNK(statbuf->st_mode)) { + char *linkname = xreadlink(filename); + if (linkname == NULL) { + bb_perror_msg("cannot read symbolic link '%s'", filename); + return; + } + /*printf("\"%s\" -> \"%s\"", filename, linkname); */ + printf(pformat, filename); + printf(" -> "); + printf(pformat, linkname); + } else { + printf(pformat, filename); + } + break; + case 'd': + strncat(pformat, "ju", buf_len); + printf(pformat, (uintmax_t) statbuf->st_dev); + break; + case 'D': + strncat(pformat, "jx", buf_len); + printf(pformat, (uintmax_t) statbuf->st_dev); + break; + case 'i': + strncat(pformat, "ju", buf_len); + printf(pformat, (uintmax_t) statbuf->st_ino); + break; + case 'a': + strncat(pformat, "lo", buf_len); + printf(pformat, (unsigned long int) (statbuf->st_mode & (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO))); + break; + case 'A': + strncat(pformat, "s", buf_len); + printf(pformat, bb_mode_string(statbuf->st_mode)); + break; + case 'f': + strncat(pformat, "lx", buf_len); + printf(pformat, (unsigned long int) statbuf->st_mode); + break; + case 'F': + strncat(pformat, "s", buf_len); + printf(pformat, file_type(statbuf)); + break; + case 'h': + strncat(pformat, "lu", buf_len); + printf(pformat, (unsigned long int) statbuf->st_nlink); + break; + case 'u': + strncat(pformat, "lu", buf_len); + printf(pformat, (unsigned long int) statbuf->st_uid); + break; + case 'U': + strncat(pformat, "s", buf_len); + setpwent(); + pw_ent = getpwuid(statbuf->st_uid); + printf(pformat, (pw_ent != 0L) ? pw_ent->pw_name : "UNKNOWN"); + break; + case 'g': + strncat(pformat, "lu", buf_len); + printf(pformat, (unsigned long int) statbuf->st_gid); + break; + case 'G': + strncat(pformat, "s", buf_len); + setgrent(); + gw_ent = getgrgid(statbuf->st_gid); + printf(pformat, (gw_ent != 0L) ? gw_ent->gr_name : "UNKNOWN"); + break; + case 't': + strncat(pformat, "lx", buf_len); + printf(pformat, (unsigned long int) major(statbuf->st_rdev)); + break; + case 'T': + strncat(pformat, "lx", buf_len); + printf(pformat, (unsigned long int) minor(statbuf->st_rdev)); + break; + case 's': + strncat(pformat, "ju", buf_len); + printf(pformat, (uintmax_t) (statbuf->st_size)); + break; + case 'B': + strncat(pformat, "lu", buf_len); + printf(pformat, (unsigned long int) 512); //ST_NBLOCKSIZE + break; + case 'b': + strncat(pformat, "ju", buf_len); + printf(pformat, (uintmax_t) statbuf->st_blocks); + break; + case 'o': + strncat(pformat, "lu", buf_len); + printf(pformat, (unsigned long int) statbuf->st_blksize); + break; + case 'x': + strncat(pformat, "s", buf_len); + printf(pformat, human_time(statbuf->st_atime)); + break; + case 'X': + strncat(pformat, TYPE_SIGNED(time_t) ? "ld" : "lu", buf_len); + printf(pformat, (unsigned long int) statbuf->st_atime); + break; + case 'y': + strncat(pformat, "s", buf_len); + printf(pformat, human_time(statbuf->st_mtime)); + break; + case 'Y': + strncat(pformat, TYPE_SIGNED(time_t) ? "ld" : "lu", buf_len); + printf(pformat, (unsigned long int) statbuf->st_mtime); + break; + case 'z': + strncat(pformat, "s", buf_len); + printf(pformat, human_time(statbuf->st_ctime)); + break; + case 'Z': + strncat(pformat, TYPE_SIGNED(time_t) ? "ld" : "lu", buf_len); + printf(pformat, (unsigned long int) statbuf->st_ctime); + break; + default: + strncat(pformat, "c", buf_len); + printf(pformat, m); + break; + } +} + +static void print_it(char const *masterformat, char const *filename, + void (*print_func) (char *, size_t, char, char const *, void const *), + void const *data) +{ + char *b; + + /* 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. */ + size_t n_alloc = strlen(format) + 2 + 1; + char *dest = xmalloc(n_alloc); + + b = format; + while (b) { + size_t len; + char *p = strchr(b, '%'); + if (!p) { + /* coreutils 6.3 always print <cr> at the end */ + /*fputs(b, stdout);*/ + puts(b); + break; + } + *p++ = '\0'; + fputs(b, stdout); + + 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 '%': + putchar('%'); + break; + default: + print_func(dest, n_alloc, *p, filename, data); + break; + } + } + + free(format); + free(dest); +} +#endif + +/* Stat the file system and print what we find. */ +static int do_statfs(char const *filename, char const *format) +{ + struct statfs statfsbuf; + + if (statfs(filename, &statfsbuf) != 0) { + bb_perror_msg("cannot read file system information for '%s'", filename); + return 0; + } + +#ifdef CONFIG_FEATURE_STAT_FORMAT + if (format == NULL) + format = (flags & 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"); + print_it(format, filename, print_statfs, &statfsbuf); +#else + + format = (flags & OPT_TERSE + ? "%s %llx %lu " + : " File: \"%s\"\n" + " ID: %-8Lx Namelen: %-7lu "); + printf(format, + filename, + statfsbuf.f_fsid, + statfsbuf.f_namelen); + + if (flags & OPT_TERSE) + printf("%lx ", (unsigned long int) (statfsbuf.f_type)); + else + printf("Type: %s\n", human_fstype(statfsbuf.f_type)); + + format = (flags & 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 int) (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)); +#endif + + return 1; +} + +/* stat the file and print what we find */ +static int do_stat(char const *filename, char const *format) +{ + struct stat statbuf; + + if ((flags & OPT_DEREFERENCE ? stat : lstat) (filename, &statbuf) != 0) { + bb_perror_msg("cannot stat '%s'", filename); + return 0; + } + +#ifdef CONFIG_FEATURE_STAT_FORMAT + if (format == NULL) { + if (flags & 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"; + } + } + } + print_it(format, filename, print_stat, &statbuf); +#else + if (flags & OPT_TERSE) { + printf("%s %ju %ju %lx %lu %lu %jx %ju %lu %lx %lx %lu %lu %lu %lu\n", + filename, + (uintmax_t) (statbuf.st_size), + (uintmax_t) statbuf.st_blocks, + (unsigned long int) statbuf.st_mode, + (unsigned long int) statbuf.st_uid, + (unsigned long int) statbuf.st_gid, + (uintmax_t) statbuf.st_dev, + (uintmax_t) statbuf.st_ino, + (unsigned long int) statbuf.st_nlink, + (unsigned long int) major(statbuf.st_rdev), + (unsigned long int) minor(statbuf.st_rdev), + (unsigned long int) statbuf.st_atime, + (unsigned long int) statbuf.st_mtime, + (unsigned long int) statbuf.st_ctime, + (unsigned long int) statbuf.st_blksize + ); + } 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 = xreadlink(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 int) statbuf.st_blksize, + file_type(&statbuf), + (uintmax_t) statbuf.st_dev, + (uintmax_t) statbuf.st_dev, + (uintmax_t) statbuf.st_ino, + (unsigned long int) statbuf.st_nlink); + if (S_ISBLK(statbuf.st_mode) || S_ISCHR(statbuf.st_mode)) + printf(" Device type: %lx,%lx\n", + (unsigned long int) major(statbuf.st_rdev), + (unsigned long int) minor(statbuf.st_rdev)); + else + putchar('\n'); + printf("Access: (%04lo/%10.10s) Uid: (%5lu/%8s) Gid: (%5lu/%8s)\n" + "Access: %s\n" "Modify: %s\n" "Change: %s\n", + (unsigned long int) (statbuf.st_mode & (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO)), + bb_mode_string(statbuf.st_mode), + (unsigned long int) statbuf.st_uid, + (pw_ent != 0L) ? pw_ent->pw_name : "UNKNOWN", + (unsigned long int) statbuf.st_gid, + (gw_ent != 0L) ? gw_ent->gr_name : "UNKNOWN", + human_time(statbuf.st_atime), + human_time(statbuf.st_mtime), + human_time(statbuf.st_ctime)); + } +#endif + return 1; +} + +int stat_main(int argc, char **argv) +{ + int i; + char *format = NULL; + int ok = 1; + int (*statfunc)(char const *, char const *) = do_stat; + + flags = getopt32(argc, argv, "ftL" + USE_FEATURE_STAT_FORMAT("c:", &format) + ); + + if (flags & 1) /* -f */ + statfunc = do_statfs; + if (argc == optind) /* files */ + bb_show_usage(); + + for (i = optind; i < argc; ++i) + ok &= statfunc(argv[i], format); + + return (ok ? EXIT_SUCCESS : EXIT_FAILURE); +} diff --git a/coreutils/stty.c b/coreutils/stty.c new file mode 100644 index 0000000..22784a2 --- /dev/null +++ b/coreutils/stty.c @@ -0,0 +1,1302 @@ +/* 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 <djm@gnu.ai.mit.edu> + + Special for busybox ported by Vladimir Oleynik <dzo@simtreas.ru> 2001 + + */ + +#include "busybox.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 +}; + +static const char evenp [] = "evenp"; +static const char raw [] = "raw"; +static const char stty_min [] = "min"; +static const char stty_time [] = "time"; +static const char stty_swtch[] = "swtch"; +static const char stty_eol [] = "eol"; +static const char stty_eof [] = "eof"; +static const char parity [] = "parity"; +static const char stty_oddp [] = "oddp"; +static const char stty_nl [] = "nl"; +static const char stty_ek [] = "ek"; +static const char stty_sane [] = "sane"; +static const char cbreak [] = "cbreak"; +static const char stty_pass8[] = "pass8"; +static const char litout [] = "litout"; +static const char cooked [] = "cooked"; +static const char decctlq [] = "decctlq"; +static const char stty_tabs [] = "tabs"; +static const char stty_lcase[] = "lcase"; +static const char stty_LCASE[] = "LCASE"; +static const char stty_crt [] = "crt"; +static const char stty_dec [] = "dec"; + +/* 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 */ +struct mode_info { + const char *name; /* Name given on command line */ + char type; /* Which structure element to change */ + char flags; /* Setting and display options */ + unsigned short mask; /* Other bits to turn off for this mode */ + unsigned long bits; /* Bits to set for this mode */ +}; + +#define MI_ENTRY(N,T,F,B,M) { N, T, F, M, B } + +static const struct mode_info mode_info[] = { + 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 + MI_ENTRY(evenp, combination, REV | OMIT, 0, 0 ), + MI_ENTRY(parity, combination, REV | OMIT, 0, 0 ), + MI_ENTRY(stty_oddp, combination, REV | OMIT, 0, 0 ), + MI_ENTRY(stty_nl, combination, REV | OMIT, 0, 0 ), + MI_ENTRY(stty_ek, combination, OMIT, 0, 0 ), + MI_ENTRY(stty_sane, combination, OMIT, 0, 0 ), + MI_ENTRY(cooked, combination, REV | OMIT, 0, 0 ), + MI_ENTRY(raw, combination, REV | OMIT, 0, 0 ), + MI_ENTRY(stty_pass8, combination, REV | OMIT, 0, 0 ), + MI_ENTRY(litout, combination, REV | OMIT, 0, 0 ), + MI_ENTRY(cbreak, combination, REV | OMIT, 0, 0 ), +#ifdef IXANY + MI_ENTRY(decctlq, combination, REV | OMIT, 0, 0 ), +#endif +#if defined(TABDLY) || defined(OXTABS) + MI_ENTRY(stty_tabs, combination, REV | OMIT, 0, 0 ), +#endif +#if defined(XCASE) && defined(IUCLC) && defined(OLCUC) + MI_ENTRY(stty_lcase, combination, REV | OMIT, 0, 0 ), + MI_ENTRY(stty_LCASE, combination, REV | OMIT, 0, 0 ), +#endif + MI_ENTRY(stty_crt, combination, OMIT, 0, 0 ), + MI_ENTRY(stty_dec, combination, OMIT, 0, 0 ), +}; + +enum { + NUM_mode_info = (sizeof(mode_info) / sizeof(mode_info[0])) +}; + +/* Control character settings */ +struct control_info { + const char *name; /* Name given on command line */ + unsigned char saneval; /* Value to set for 'stty sane' */ + unsigned char offset; /* Offset in c_cc */ +}; + +/* Control characters */ + +static const struct control_info control_info[] = { + {"intr", CINTR, VINTR}, + {"quit", CQUIT, VQUIT}, + {"erase", CERASE, VERASE}, + {"kill", CKILL, VKILL}, + {stty_eof, CEOF, VEOF}, + {stty_eol, CEOL, VEOL}, +#ifdef VEOL2 + {"eol2", CEOL2, VEOL2}, +#endif +#ifdef VSWTCH + {stty_swtch, CSWTCH, VSWTCH}, +#endif + {"start", CSTART, VSTART}, + {"stop", CSTOP, VSTOP}, + {"susp", CSUSP, VSUSP}, +#ifdef VDSUSP + {"dsusp", CDSUSP, VDSUSP}, +#endif +#ifdef VREPRINT + {"rprnt", CRPRNT, VREPRINT}, +#endif +#ifdef VWERASE + {"werase", CWERASE, VWERASE}, +#endif +#ifdef VLNEXT + {"lnext", CLNEXT, VLNEXT}, +#endif +#ifdef VFLUSHO + {"flush", CFLUSHO, VFLUSHO}, +#endif +#ifdef VSTATUS + {"status", CSTATUS, VSTATUS}, +#endif + /* These must be last because of the display routines */ + {stty_min, 1, VMIN}, + {stty_time, 0, VTIME}, +}; + +enum { + NUM_control_info = (sizeof(control_info) / sizeof(control_info[0])) +}; + +/* The width of the screen, for output wrapping */ +static unsigned max_col = 80; /* default */ +/* Current position, to know when to wrap */ +static unsigned current_col; +static const char *device_name = bb_msg_standard_input; + +/* Return a string that is the printable representation of character CH */ +/* Adapted from 'cat' by Torbjorn Granlund */ +static const char *visible(unsigned int ch) +{ + static char buf[10]; + char *bpout = buf; + + if (ch == _POSIX_VDISABLE) + return "<undef>"; + + 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 buf; +} + +static tcflag_t *mode_type_flag(unsigned type, const struct termios *mode) +{ + static const unsigned char tcflag_offsets[] = { + 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 speed_t string_to_baud_or_die(const char *arg) +{ + return tty_value_to_baud(xatou(arg)); +} + +static void set_speed_or_die(enum speed_setting type, const char *arg, + struct termios *mode) +{ + speed_t baud; + + baud = string_to_baud_or_die(arg); + + if (type != output_speed) { /* either input or both */ + cfsetispeed(mode, baud); + } + if (type != input_speed) { /* either output or both */ + cfsetospeed(mode, baud); + } +} + +static ATTRIBUTE_NORETURN void perror_on_device_and_die(const char *fmt) +{ + bb_perror_msg_and_die(fmt, device_name); +} + +static void perror_on_device(const char *fmt) +{ + bb_perror_msg(fmt, device_name); +} + +/* No, inline won't be as efficient (gcc 3.4.3) */ +#define streq(a,b) (!strcmp((a),(b))) + +/* 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; + int buflen; + + va_start(args, message); + vsnprintf(buf, sizeof(buf), message, args); + va_end(args); + buflen = strlen(buf); + if (!buflen) return; + + if (current_col > 0) { + current_col++; + if (buf[0] != '\n') { + if (current_col + buflen >= max_col) { + putchar('\n'); + current_col = 0; + } else + putchar(' '); + } + } + fputs(buf, stdout); + current_col += buflen; + if (buf[buflen-1] == '\n') + current_col = 0; +} + +#ifdef TIOCGWINSZ + +static int get_win_size(int fd, struct winsize *win) +{ + return ioctl(fd, TIOCGWINSZ, (char *) win); +} + +static void set_window_size(int rows, int cols) +{ + struct winsize win; + + if (get_win_size(STDIN_FILENO, &win)) { + if (errno != EINVAL) { + perror_on_device("%s"); + return; + } + memset(&win, 0, sizeof(win)); + } + + if (rows >= 0) + win.ws_row = rows; + if (cols >= 0) + win.ws_col = cols; + +# ifdef TIOCSSIZE + /* Alexander Dupuy <dupuy@cs.columbia.edu> wrote: + The following code deals with a bug in the SunOS 4.x (and 3.x?) kernel. + This comment from sys/ttold.h describes Sun's twisted logic - a better + test would have been (ts_lines > 64k || ts_cols > 64k || ts_cols == 0). + At any rate, the problem is gone in Solaris 2.x */ + + if (win.ws_row == 0 || win.ws_col == 0) { + struct ttysize ttysz; + + ttysz.ts_lines = win.ws_row; + ttysz.ts_cols = win.ws_col; + + win.ws_row = win.ws_col = 1; + + if ((ioctl(STDIN_FILENO, TIOCSWINSZ, (char *) &win) != 0) + || (ioctl(STDIN_FILENO, TIOCSSIZE, (char *) &ttysz) != 0)) { + perror_on_device("%s"); + } + return; + } +# endif + + if (ioctl(STDIN_FILENO, TIOCSWINSZ, (char *) &win)) + perror_on_device("%s"); +} + +static void display_window_size(int fancy) +{ + const char *fmt_str = "%s\0%s: no size information for this device"; + struct winsize win; + + if (get_win_size(STDIN_FILENO, &win)) { + if ((errno != EINVAL) || ((fmt_str += 2), !fancy)) { + perror_on_device(fmt_str); + } + } else { + wrapf(fancy ? "rows %d; columns %d;" : "%d %d\n", + win.ws_row, win.ws_col); + } +} + +#else /* !TIOCGWINSZ */ + +static inline void display_window_size(int fancy) {} + +#endif /* !TIOCGWINSZ */ + +static int screen_columns_or_die(void) +{ + const char *s; + +#ifdef TIOCGWINSZ + struct winsize win; + + /* With Solaris 2.[123], this ioctl fails and errno is set to + EINVAL for telnet (but not rlogin) sessions. + On ISC 3.0, it fails for the console and the serial port + (but it works for ptys). + It can also fail on any system when stdout isn't a tty. + In case of any failure, just use the default */ + if (get_win_size(STDOUT_FILENO, &win) == 0 && win.ws_col > 0) + return win.ws_col; +#endif + + s = getenv("COLUMNS"); + if (s) + return xatoi_u(s); + return 80; +} + +static const struct suffix_mult stty_suffixes[] = { + {"b", 512 }, + {"k", 1024}, + {"B", 1024}, + {NULL, 0 } +}; + +static const struct mode_info *find_mode(const char *name) +{ + int i; + for (i = 0; i < NUM_mode_info; ++i) + if (streq(name, mode_info[i].name)) + return &mode_info[i]; + return 0; +} + +static const struct control_info *find_control(const char *name) +{ + int i; + for (i = 0; i < NUM_control_info; ++i) + if (streq(name, control_info[i].name)) + return &control_info[i]; + return 0; +} + +enum { + param_need_arg = 0x80, + param_line = 1 | 0x80, + param_rows = 2 | 0x80, + param_cols = 3 | 0x80, + param_size = 4, + param_speed = 5, + param_ispeed = 6 | 0x80, + param_ospeed = 7 | 0x80, +}; + +static int find_param(const char *name) +{ +#ifdef HAVE_C_LINE + if (streq(name, "line")) return param_line; +#endif +#ifdef TIOCGWINSZ + if (streq(name, "rows")) return param_rows; + if (streq(name, "cols")) return param_cols; + if (streq(name, "columns")) return param_cols; + if (streq(name, "size")) return param_size; +#endif + if (streq(name, "speed")) return param_speed; + if (streq(name, "ispeed")) return param_ispeed; + if (streq(name, "ospeed")) return param_ospeed; + return 0; +} + + +static int recover_mode(const char *arg, struct termios *mode); +static void set_mode(const struct mode_info *info, + int reversed, struct termios *mode); +static void display_all(const struct termios *mode); +static void display_changed(const struct termios *mode); +static void display_recoverable(const struct termios *mode); +static void display_speed(const struct termios *mode, int fancy); +static void sane_mode(struct termios *mode); +static void set_control_char_or_die(const struct control_info *info, + const char *arg, struct termios *mode); + +int stty_main(int argc, char **argv) +{ + struct termios mode; + void (*output_func)(const struct termios *); + const char *file_name = NULL; + int require_set_attr; + int speed_was_set; + int verbose_output; + int recoverable_output; + int noargs; + int k; + + output_func = display_changed; + noargs = 1; + speed_was_set = 0; + require_set_attr = 0; + verbose_output = 0; + recoverable_output = 0; + + /* 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)) + bb_error_msg_and_die("invalid argument '%s'", arg); + noargs = 0; + continue; + } + /* It is an option - parse it */ + i = 0; + while (arg[++i]) { + switch (arg[i]) { + case 'a': + verbose_output = 1; + output_func = display_all; + break; + case 'g': + recoverable_output = 1; + 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: + bb_error_msg_and_die("invalid argument '%s'", arg); + } + } +end_option: + continue; + } + + mp = find_mode(arg); + if (mp) { + noargs = 0; + 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); + noargs = 0; + ++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: + 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 (string_to_baud_or_die(arg) != (speed_t) -1) break; + bb_error_msg_and_die("invalid argument '%s'", arg); + } + noargs = 0; + } + + /* Specifying both -a and -g is an error */ + if (verbose_output && 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 (!noargs && (verbose_output || 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; + device_name = file_name; + fd = xopen(device_name, O_RDONLY | O_NONBLOCK); + if (fd != STDIN_FILENO) { + dup2(fd, STDIN_FILENO); + close(fd); + } + fdflags = fcntl(STDIN_FILENO, F_GETFL); + if (fdflags == -1 || 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 (verbose_output || recoverable_output || noargs) { + max_col = screen_columns_or_die(); + output_func(&mode); + 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); + } + /* It is an option - already parsed. Skip it */ + continue; + } + + mp = find_mode(arg); + if (mp) { + set_mode(mp, 0 /* non-reversed */, &mode); + continue; + } + + cp = find_control(arg); + if (cp) { + ++k; + set_control_char_or_die(cp, argnext, &mode); + 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); + require_set_attr = 1; + 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); + speed_was_set = 1; + require_set_attr = 1; + break; + case param_ospeed: + set_speed_or_die(output_speed, argnext, &mode); + speed_was_set = 1; + require_set_attr = 1; + break; + default: + if (recover_mode(arg, &mode) == 1) + require_set_attr = 1; + else /* true: if (string_to_baud_or_die(arg) != (speed_t) -1) */ { + set_speed_or_die(both_speeds, arg, &mode); + speed_was_set = 1; + require_set_attr = 1; + } /* else - impossible (caught in the first pass): + bb_error_msg_and_die("invalid argument '%s'", arg); */ + } + } + + if (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 (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; +} + +/* 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 & ~((unsigned long)info->mask) & ~info->bits; + else + *bitsp = (*bitsp & ~((unsigned long)info->mask)) | info->bits; + return; + } + + /* Combination mode */ + if (info->name == evenp || info->name == 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->name == stty_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->name == stty_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->name == stty_ek) { + mode->c_cc[VERASE] = CERASE; + mode->c_cc[VKILL] = CKILL; + } else if (info->name == stty_sane) { + sane_mode(mode); + } + else if (info->name == cbreak) { + if (reversed) + mode->c_lflag |= ICANON; + else + mode->c_lflag &= ~ICANON; + } else if (info->name == stty_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->name == 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->name == raw || info->name == cooked) { + if ((info->name[0] == 'r' && reversed) + || (info->name[0] == 'c' && !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->name == decctlq) { + if (reversed) + mode->c_iflag |= IXANY; + else + mode->c_iflag &= ~IXANY; + } + else if (TABDLY && info->name == stty_tabs) { + if (reversed) + mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB3; + else + mode->c_oflag = (mode->c_oflag & ~TABDLY) | TAB0; + } + else if (OXTABS && info->name == stty_tabs) { + if (reversed) + mode->c_oflag |= OXTABS; + else + mode->c_oflag &= ~OXTABS; + } + else if (XCASE && IUCLC && OLCUC + && (info->name == stty_lcase || info->name == stty_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->name == stty_crt) { + mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE; + } + else if (info->name == stty_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->name == stty_min || info->name == stty_time) + value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes); + else if (arg[0] == '\0' || arg[1] == '\0') + value = arg[0]; + else if (streq(arg, "^-") || streq(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; +} + +static void display_changed(const struct termios *mode) +{ + int i; + tcflag_t *bitsp; + unsigned long mask; + int prev_type = control; + + display_speed(mode, 1); +#ifdef HAVE_C_LINE + wrapf("line = %d;\n", mode->c_line); +#else + wrapf("\n"); +#endif + + for (i = 0; control_info[i].name != stty_min; ++i) { + if (mode->c_cc[control_info[i].offset] == control_info[i].saneval) + continue; + /* If swtch is the same as susp, don't print both */ +#if VSWTCH == VSUSP + if (control_info[i].name == stty_swtch) + continue; +#endif + /* If eof uses the same slot as min, only print whichever applies */ +#if VEOF == VMIN + if ((mode->c_lflag & ICANON) == 0 + && (control_info[i].name == stty_eof + || control_info[i].name == stty_eol)) continue; +#endif + wrapf("%s = %s;", control_info[i].name, + visible(mode->c_cc[control_info[i].offset])); + } + if ((mode->c_lflag & ICANON) == 0) { + wrapf("min = %d; time = %d;", (int) mode->c_cc[VMIN], + (int) mode->c_cc[VTIME]); + } + if (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) { + if (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 (mode_info[i].flags & SANE_UNSET) { + wrapf("%s", mode_info[i].name); + } + } else if ((mode_info[i].flags & (SANE_SET | REV)) == (SANE_SET | REV)) { + wrapf("-%s", mode_info[i].name); + } + } + if (current_col) wrapf("\n"); +} + +static void display_all(const struct termios *mode) +{ + int i; + tcflag_t *bitsp; + unsigned long mask; + int prev_type = control; + + display_speed(mode, 1); + display_window_size(1); +#ifdef HAVE_C_LINE + wrapf("line = %d;\n", mode->c_line); +#else + wrapf("\n"); +#endif + + for (i = 0; control_info[i].name != stty_min; ++i) { + /* If swtch is the same as susp, don't print both */ +#if VSWTCH == VSUSP + if (control_info[i].name == stty_swtch) + continue; +#endif + /* If eof uses the same slot as min, only print whichever applies */ +#if VEOF == VMIN + if ((mode->c_lflag & ICANON) == 0 + && (control_info[i].name == stty_eof + || control_info[i].name == stty_eol)) continue; +#endif + wrapf("%s = %s;", control_info[i].name, + 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 (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"); + 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) + wrapf("%s", mode_info[i].name); + else if (mode_info[i].flags & REV) + wrapf("-%s", mode_info[i].name); + } + if (current_col) wrapf("\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 display_recoverable(const struct termios *mode) +{ + 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]); + putchar('\n'); +} + +static int recover_mode(const char *arg, struct termios *mode) +{ + int i, n; + unsigned int 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 sane_mode(struct termios *mode) +{ + int i; + tcflag_t *bitsp; + + for (i = 0; i < NUM_control_info; ++i) { +#if VMIN == VEOF + if (control_info[i].name == stty_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; + } + } +} diff --git a/coreutils/sum.c b/coreutils/sum.c new file mode 100644 index 0000000..93f4e22 --- /dev/null +++ b/coreutils/sum.c @@ -0,0 +1,156 @@ +/* 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 <andersen@codepoet.org> + * Copyright (C) 2005 by Mike Frysinger <vapier@gentoo.org> + * + * 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 "busybox.h" + +/* 1 if any of the files read were the standard input */ +static int have_read_stdin; + +/* make a little more readable and avoid using strcmp for just 2 bytes */ +#define IS_STDIN(s) (s[0] == '-' && s[1] == '\0') + +/* Calculate and print the rotated checksum and the size in 1K blocks + of file FILE, or of the standard input if FILE is "-". + If PRINT_NAME is >1, print FILE next to the checksum and size. + The checksum varies depending on sizeof (int). + Return 1 if successful. */ +static int bsd_sum_file(const char *file, int print_name) +{ + FILE *fp; + int checksum = 0; /* The checksum mod 2^16. */ + uintmax_t total_bytes = 0; /* The number of bytes. */ + int ch; /* Each character read. */ + int ret = 0; + + if (IS_STDIN(file)) { + fp = stdin; + have_read_stdin++; + } else { + fp = fopen_or_warn(file, "r"); + if (fp == NULL) + goto out; + } + + while ((ch = getc(fp)) != EOF) { + ++total_bytes; + checksum = (checksum >> 1) + ((checksum & 1) << 15); + checksum += ch; + checksum &= 0xffff; /* Keep it within bounds. */ + } + + if (ferror(fp)) { + bb_perror_msg(file); + fclose_if_not_stdin(fp); + goto out; + } + + if (fclose_if_not_stdin(fp) == EOF) { + bb_perror_msg(file); + goto out; + } + ret++; + printf("%05d %5ju ", checksum, (total_bytes+1023)/1024); + if (print_name > 1) + puts(file); + else + puts(""); +out: + return ret; +} + +/* Calculate and print the checksum and the size in 512-byte blocks + of file FILE, or of the standard input if FILE is "-". + If PRINT_NAME is >0, print FILE next to the checksum and size. + Return 1 if successful. */ +#define MY_BUF_SIZE 8192 +static int sysv_sum_file(const char *file, int print_name) +{ + RESERVE_CONFIG_BUFFER(buf, MY_BUF_SIZE); + int fd; + uintmax_t total_bytes = 0; + + /* The sum of all the input bytes, modulo (UINT_MAX + 1). */ + unsigned int s = 0; + + if (IS_STDIN(file)) { + fd = 0; + have_read_stdin = 1; + } else { + fd = open(file, O_RDONLY); + if (fd == -1) + goto release_and_ret; + } + + while (1) { + size_t bytes_read = safe_read(fd, buf, MY_BUF_SIZE); + + if (bytes_read == 0) + break; + + if (bytes_read == -1) { +release_and_ret: + bb_perror_msg(file); + RELEASE_CONFIG_BUFFER(buf); + if (!IS_STDIN(file)) + close(fd); + return 0; + } + + total_bytes += bytes_read; + while (bytes_read--) + s += buf[bytes_read]; + } + + if (!IS_STDIN(file) && close(fd) == -1) + goto release_and_ret; + else + RELEASE_CONFIG_BUFFER(buf); + + { + int r = (s & 0xffff) + ((s & 0xffffffff) >> 16); + s = (r & 0xffff) + (r >> 16); + + printf("%d %ju ", s, (total_bytes+511)/512); + } + puts(print_name ? file : ""); + + return 1; +} + +int sum_main(int argc, char **argv) +{ + int flags; + int ok; + int (*sum_func)(const char *, int) = bsd_sum_file; + + /* give the bsd func priority over sysv func */ + flags = getopt32(argc, argv, "sr"); + if (flags & 1) + sum_func = sysv_sum_file; + if (flags & 2) + sum_func = bsd_sum_file; + + have_read_stdin = 0; + if ((argc - optind) == 0) + ok = sum_func("-", 0); + else + for (ok = 1; optind < argc; optind++) + ok &= sum_func(argv[optind], 1); + + if (have_read_stdin && fclose(stdin) == EOF) + bb_perror_msg_and_die("-"); + + exit(ok ? EXIT_SUCCESS : EXIT_FAILURE); +} diff --git a/coreutils/sync.c b/coreutils/sync.c new file mode 100644 index 0000000..59f0b50 --- /dev/null +++ b/coreutils/sync.c @@ -0,0 +1,23 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini sync implementation for busybox + * + * Copyright (C) 1995, 1996 by Bruce Perens <bruce@pixar.com>. + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +/* BB_AUDIT SUSv3 N/A -- Matches GNU behavior. */ + +#include <stdlib.h> +#include <unistd.h> +#include "busybox.h" + +int sync_main(int argc, char **argv) +{ + bb_warn_ignoring_args(argc - 1); + + sync(); + + return EXIT_SUCCESS; +} diff --git a/coreutils/tail.c b/coreutils/tail.c new file mode 100644 index 0000000..ed5ea14 --- /dev/null +++ b/coreutils/tail.c @@ -0,0 +1,326 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini tail implementation for busybox + * + * Copyright (C) 2001 by Matt Kraai <kraai@alumni.carnegiemellon.edu> + * + * 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 <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/stat.h> +#include "busybox.h" + +static const struct suffix_mult tail_suffixes[] = { + { "b", 512 }, + { "k", 1024 }, + { "m", 1048576 }, + { NULL, 0 } +}; + +static int status; + +static void tail_xbb_full_write(const char *buf, size_t len) +{ + /* If we get a write error, there is really no sense in continuing. */ + if (full_write(STDOUT_FILENO, buf, len) < 0) + bb_perror_nomsg_and_die(); +} + +static void tail_xprint_header(const char *fmt, const char *filename) +{ +#if defined __GLIBC__ + if (dprintf(STDOUT_FILENO, fmt, filename) < 0) { + bb_perror_nomsg_and_die(); + } +#else + int hdr_len = strlen(fmt) + strlen(filename); + char *hdr = xzalloc(hdr_len); + sprintf(hdr, filename, filename); + tail_xbb_full_write(hdr, hdr_len); +#endif +} + +static ssize_t tail_read(int fd, char *buf, size_t count) +{ + ssize_t r; + off_t current,end; + struct stat sbuf; + + end = current = lseek(fd, 0, SEEK_CUR); + if (!fstat(fd, &sbuf)) + end = sbuf.st_size; + lseek(fd, end < current ? 0 : current, SEEK_SET); + if ((r = safe_read(fd, buf, count)) < 0) { + bb_perror_msg(bb_msg_read_error); + status = EXIT_FAILURE; + } + + return r; +} + +static const char tail_opts[] = + "fn:c:" +#if ENABLE_FEATURE_FANCY_TAIL + "qs:v" +#endif + ; + +static const char header_fmt[] = "\n==> %s <==\n"; + +int tail_main(int argc, char **argv) +{ + long count = 10; + unsigned sleep_period = 1; + int from_top = 0; + int follow = 0; + int header_threshhold = 1; + int count_bytes = 0; + + char *tailbuf; + size_t tailbufsize; + int taillen = 0; + int newline = 0; + + int *fds, nfiles, nread, nwrite, seen, i, opt; + char *s, *buf; + const char *fmt; + +#if !ENABLE_DEBUG_YANK_SUSv2 || ENABLE_FEATURE_FANCY_TAIL + /* Allow legacy syntax of an initial numeric option without -n. */ + if (argc >= 2 && (argv[1][0] == '+' || argv[1][0] == '-') + && isdigit(argv[1][1]) + ) { + optind = 2; + optarg = argv[1]; + goto GET_COUNT; + } +#endif + + while ((opt = getopt(argc, argv, tail_opts)) > 0) { + switch (opt) { + case 'f': + follow = 1; + break; + case 'c': + count_bytes = 1; + /* FALLS THROUGH */ + case 'n': +#if !ENABLE_DEBUG_YANK_SUSv2 || ENABLE_FEATURE_FANCY_TAIL + GET_COUNT: +#endif + count = xatol_sfx(optarg, tail_suffixes); + /* Note: Leading whitespace is an error trapped above. */ + if (*optarg == '+') { + from_top = 1; + } else { + from_top = 0; + } + if (count < 0) { + count = -count; + } + break; +#if ENABLE_FEATURE_FANCY_TAIL + case 'q': + header_threshhold = INT_MAX; + break; + case 's': + sleep_period = xatou(optarg); + break; + case 'v': + header_threshhold = 0; + break; +#endif + default: + bb_show_usage(); + } + } + + /* open all the files */ + fds = (int *)xmalloc(sizeof(int) * (argc - optind + 1)); + + argv += optind; + nfiles = i = 0; + + if ((argc -= optind) == 0) { + struct stat statbuf; + + if (!fstat(STDIN_FILENO, &statbuf) && S_ISFIFO(statbuf.st_mode)) { + follow = 0; + } + /* --argv; */ + *argv = (char *) bb_msg_standard_input; + goto DO_STDIN; + } + + do { + if ((argv[i][0] == '-') && !argv[i][1]) { + DO_STDIN: + fds[nfiles] = STDIN_FILENO; + } else if ((fds[nfiles] = open(argv[i], O_RDONLY)) < 0) { + bb_perror_msg("%s", argv[i]); + status = EXIT_FAILURE; + continue; + } + argv[nfiles] = argv[i]; + ++nfiles; + } while (++i < argc); + + if (!nfiles) { + bb_error_msg_and_die("no files"); + } + + tailbufsize = BUFSIZ; + + /* tail the files */ + if (from_top < count_bytes) { /* Each is 0 or 1, so true iff 0 < 1. */ + /* Hence, !from_top && count_bytes */ + if (tailbufsize < count) { + tailbufsize = count + BUFSIZ; + } + } + + buf = tailbuf = xmalloc(tailbufsize); + + fmt = header_fmt + 1; /* Skip header leading newline on first output. */ + i = 0; + do { + /* Be careful. It would be possible to optimize the count-bytes + * case if the file is seekable. If you do though, remember that + * starting file position may not be the beginning of the file. + * Beware of backing up too far. See example in wc.c. + */ + if ((!(count|from_top)) && (lseek(fds[i], 0, SEEK_END) >= 0)) { + continue; + } + + if (nfiles > header_threshhold) { + tail_xprint_header(fmt, argv[i]); + fmt = header_fmt; + } + + buf = tailbuf; + taillen = 0; + seen = 1; + newline = 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); + } + } + tail_xbb_full_write(buf + nread - nwrite, nwrite); + } else if (count) { + if (count_bytes) { + taillen += nread; + if (taillen > count) { + memmove(tailbuf, tailbuf + taillen - count, count); + taillen = count; + } + } else { + int k = nread; + int nbuf = 0; + + while (k) { + --k; + if (buf[k] == '\n') { + ++nbuf; + } + } + + if (newline + nbuf < count) { + newline += nbuf; + taillen += nread; + + } else { + int extra = 0; + if (buf[nread-1] != '\n') { + extra = 1; + } + + k = newline + nbuf + extra - count; + s = tailbuf; + while (k) { + if (*s == '\n') { + --k; + } + ++s; + } + + taillen += nread - (s - tailbuf); + memmove(tailbuf, s, taillen); + newline = count - extra; + } + if (tailbufsize < taillen + BUFSIZ) { + tailbufsize = taillen + BUFSIZ; + tailbuf = xrealloc(tailbuf, tailbufsize); + } + } + buf = tailbuf + taillen; + } + } + + if (!from_top) { + tail_xbb_full_write(tailbuf, taillen); + } + + taillen = 0; + } while (++i < nfiles); + + buf = xrealloc(tailbuf, BUFSIZ); + + fmt = NULL; + + while (follow) { + sleep(sleep_period); + i = 0; + do { + if (nfiles > header_threshhold) { + fmt = header_fmt; + } + while ((nread = tail_read(fds[i], buf, sizeof(buf))) > 0) { + if (fmt) { + tail_xprint_header(fmt, argv[i]); + fmt = NULL; + } + tail_xbb_full_write(buf, nread); + } + } while (++i < nfiles); + } + + return status; +} diff --git a/coreutils/tee.c b/coreutils/tee.c new file mode 100644 index 0000000..640a231 --- /dev/null +++ b/coreutils/tee.c @@ -0,0 +1,100 @@ +/* vi: set sw=4 ts=4: */ +/* + * tee implementation for busybox + * + * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org> + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +/* BB_AUDIT SUSv3 compliant */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/tee.html */ + +#include "busybox.h" +#include <signal.h> + +int tee_main(int argc, char **argv) +{ + const char *mode = "w\0a"; + FILE **files; + FILE **fp; + char **names; + char **np; + int flags; + int retval = EXIT_SUCCESS; +#if ENABLE_FEATURE_TEE_USE_BLOCK_IO + ssize_t c; +# define buf bb_common_bufsiz1 +#else + int c; +#endif + flags = getopt32(argc, argv, "ia"); /* 'a' must be 2nd */ + argc -= optind; + argv += optind; + + mode += (flags & 2); /* Since 'a' is the 2nd option... */ + + if (flags & 1) { + signal(SIGINT, SIG_IGN); /* TODO - switch to sigaction. */ + } + /* 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); /* TODO - switch to sigaction. */ + + /* 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 = fopen_or_warn(*argv, mode); + if (*fp == NULL) { + retval = EXIT_FAILURE; + continue; + } + *np = *argv++; + GOT_NEW_FILE: + setbuf(*fp++, NULL); /* tee must not buffer output. */ + np++; + } while (*argv); + /* names[0] will be filled later */ + +#if ENABLE_FEATURE_TEE_USE_BLOCK_IO + while ((c = safe_read(STDIN_FILENO, buf, BUFSIZ)) > 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..ebb4f90 --- /dev/null +++ b/coreutils/test.c @@ -0,0 +1,583 @@ +/* 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 <andersen@codepoet.org> to be used + * in busybox. + * + * 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 "busybox.h" +#include <unistd.h> +#include <ctype.h> +#include <errno.h> +#include <string.h> +#include <setjmp.h> + +/* 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 ::= <any legal UNIX file name> +*/ + +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 +}; + +enum token_types { + UNOP, + BINOP, + BUNOP, + BBINOP, + PAREN +}; + +static const struct t_op { + const char *op_text; + short op_num, op_type; +} 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}, { + 0, 0, 0} +}; + +#ifdef CONFIG_FEATURE_TEST_64 +typedef int64_t arith_t; +#else +typedef int arith_t; +#endif + +static char **t_wp; +static struct t_op const *t_wp_op; +static gid_t *group_array; +static int ngroups; + +static enum token t_lex(char *s); +static arith_t oexpr(enum token n); +static arith_t aexpr(enum token n); +static arith_t nexpr(enum token n); +static int binop(void); +static arith_t primary(enum token n); +static int filstat(char *nm, enum token mode); +static arith_t getn(const char *s); +static int newerf(const char *f1, const char *f2); +static int olderf(const char *f1, const char *f2); +static int equalf(const char *f1, const char *f2); +static int test_eaccess(char *path, int mode); +static int is_a_group_member(gid_t gid); +static void initialize_group_array(void); + +static jmp_buf leaving; + +int bb_test(int argc, char **argv) +{ + int res; + + if (strcmp(argv[0], "[") == 0) { + if (strcmp(argv[--argc], "]")) { + bb_error_msg("missing ]"); + return 2; + } + argv[argc] = NULL; + } else if (strcmp(argv[0], "[[") == 0) { + if (strcmp(argv[--argc], "]]")) { + bb_error_msg("missing ]]"); + return 2; + } + argv[argc] = NULL; + } + + res = setjmp(leaving); + if (res) + return res; + + /* resetting ngroups is probably unnecessary. it will + * force a new call to getgroups(), which prevents using + * group data fetched during a previous call. but the + * only way the group data could be stale is if there's + * been an intervening call to setgroups(), and this + * isn't likely in the case of a shell. paranoia + * prevails... + */ + ngroups = 0; + + /* Implement special cases from POSIX.2, section 4.62.4 */ + switch (argc) { + case 1: + return 1; + case 2: + return *argv[1] == '\0'; + case 3: + if (argv[1][0] == '!' && argv[1][1] == '\0') { + return *argv[2] != '\0'; + } + break; + case 4: + if (argv[1][0] != '!' || argv[1][1] != '\0') { + if (t_lex(argv[2]), t_wp_op && t_wp_op->op_type == BINOP) { + t_wp = &argv[1]; + return binop() == 0; + } + } + break; + case 5: + if (argv[1][0] == '!' && argv[1][1] == '\0') { + if (t_lex(argv[3]), t_wp_op && t_wp_op->op_type == BINOP) { + t_wp = &argv[2]; + return binop() != 0; + } + } + break; + } + + t_wp = &argv[1]; + res = !oexpr(t_lex(*t_wp)); + + if (*t_wp != NULL && *++t_wp != NULL) { + bb_error_msg("%s: unknown operand", *t_wp); + return 2; + } + return res; +} + +static void syntax(const char *op, const char *msg) +{ + if (op && *op) { + bb_error_msg("%s: %s", op, msg); + } else { + bb_error_msg("%s", msg); + } + longjmp(leaving, 2); +} + +static arith_t oexpr(enum token n) +{ + arith_t res; + + res = aexpr(n); + if (t_lex(*++t_wp) == BOR) { + return oexpr(t_lex(*++t_wp)) || res; + } + t_wp--; + return res; +} + +static arith_t aexpr(enum token n) +{ + arith_t res; + + res = nexpr(n); + if (t_lex(*++t_wp) == BAND) + return aexpr(t_lex(*++t_wp)) && res; + t_wp--; + return res; +} + +static arith_t nexpr(enum token n) +{ + if (n == UNOT) + return !nexpr(t_lex(*++t_wp)); + return primary(n); +} + +static arith_t primary(enum token n) +{ + arith_t res; + + if (n == EOI) { + syntax(NULL, "argument expected"); + } + if (n == LPAREN) { + res = oexpr(t_lex(*++t_wp)); + if (t_lex(*++t_wp) != RPAREN) + syntax(NULL, "closing paren expected"); + return res; + } + if (t_wp_op && t_wp_op->op_type == UNOP) { + /* unary expression */ + if (*++t_wp == NULL) + syntax(t_wp_op->op_text, "argument expected"); + switch (n) { + case STREZ: + return strlen(*t_wp) == 0; + case STRNZ: + return strlen(*t_wp) != 0; + case FILTT: + return isatty(getn(*t_wp)); + default: + return filstat(*t_wp, n); + } + } + + if (t_lex(t_wp[1]), t_wp_op && t_wp_op->op_type == BINOP) { + return binop(); + } + + return strlen(*t_wp) > 0; +} + +static int binop(void) +{ + const char *opnd1, *opnd2; + struct t_op const *op; + + opnd1 = *t_wp; + (void) t_lex(*++t_wp); + op = t_wp_op; + + if ((opnd2 = *++t_wp) == (char *) 0) + syntax(op->op_text, "argument expected"); + + switch (op->op_num) { + case STREQ: + return strcmp(opnd1, opnd2) == 0; + case STRNE: + return strcmp(opnd1, opnd2) != 0; + case STRLT: + return strcmp(opnd1, opnd2) < 0; + case STRGT: + return strcmp(opnd1, opnd2) > 0; + case INTEQ: + return getn(opnd1) == getn(opnd2); + case INTNE: + return getn(opnd1) != getn(opnd2); + case INTGE: + return getn(opnd1) >= getn(opnd2); + case INTGT: + return getn(opnd1) > getn(opnd2); + case INTLE: + return getn(opnd1) <= getn(opnd2); + case INTLT: + return getn(opnd1) < getn(opnd2); + case FILNT: + return newerf(opnd1, opnd2); + case FILOT: + return olderf(opnd1, opnd2); + case FILEQ: + return equalf(opnd1, opnd2); + } + /* NOTREACHED */ + return 1; +} + +static int filstat(char *nm, enum token mode) +{ + struct stat s; + unsigned int i; + + 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; + + switch (mode) { + case FILRD: + return test_eaccess(nm, R_OK) == 0; + case FILWR: + return test_eaccess(nm, W_OK) == 0; + case FILEX: + return test_eaccess(nm, X_OK) == 0; + case FILEXIST: + return 1; + case FILREG: + i = S_IFREG; + goto filetype; + case FILDIR: + i = S_IFDIR; + goto filetype; + case FILCDEV: + i = S_IFCHR; + goto filetype; + case FILBDEV: + i = S_IFBLK; + goto filetype; + case FILFIFO: +#ifdef S_IFIFO + i = S_IFIFO; + goto filetype; +#else + return 0; +#endif + case FILSOCK: +#ifdef S_IFSOCK + i = S_IFSOCK; + goto filetype; +#else + return 0; +#endif + case FILSUID: + i = S_ISUID; + goto filebit; + case FILSGID: + i = S_ISGID; + goto filebit; + case FILSTCK: + i = S_ISVTX; + goto filebit; + case FILGZ: + return s.st_size > 0L; + case FILUID: + return s.st_uid == geteuid(); + case FILGID: + return s.st_gid == getegid(); + default: + return 1; + } + + filetype: + return ((s.st_mode & S_IFMT) == i); + + filebit: + return ((s.st_mode & i) != 0); +} + +static enum token t_lex(char *s) +{ + struct t_op const *op = ops; + + if (s == 0) { + t_wp_op = (struct t_op *) 0; + return EOI; + } + while (op->op_text) { + if (strcmp(s, op->op_text) == 0) { + t_wp_op = op; + return op->op_num; + } + op++; + } + t_wp_op = (struct t_op *) 0; + return OPERAND; +} + +/* atoi with error detection */ +static arith_t getn(const char *s) +{ + char *p; +#ifdef CONFIG_FEATURE_TEST_64 + long long r; +#else + long r; +#endif + + errno = 0; +#ifdef CONFIG_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; +} + +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); +} + +/* 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 void initialize_group_array(void) +{ + ngroups = getgroups(0, NULL); + if (ngroups > 0) { + group_array = xmalloc(ngroups * sizeof(gid_t)); + getgroups(ngroups, group_array); + } +} + +/* Return non-zero if GID is one that we have in our groups list. */ +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; +} + + +/* applet entry point */ + +int test_main(int argc, char **argv) +{ + exit(bb_test(argc, argv)); +} + diff --git a/coreutils/touch.c b/coreutils/touch.c new file mode 100644 index 0000000..e1af7d0 --- /dev/null +++ b/coreutils/touch.c @@ -0,0 +1,63 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini touch implementation for busybox + * + * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org> + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +/* BB_AUDIT SUSv3 _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 <stdio.h> +#include <sys/types.h> +#include <fcntl.h> +#include <utime.h> +#include <errno.h> +#include <unistd.h> +#include <stdlib.h> +#include "busybox.h" + +int touch_main(int argc, char **argv) +{ + int fd; + int flags; + int status = EXIT_SUCCESS; + + flags = getopt32(argc, argv, "c"); + + argv += optind; + + if (!*argv) { + bb_show_usage(); + } + + do { + if (utime(*argv, NULL)) { + if (errno == ENOENT) { /* no such file*/ + if (flags & 1) { /* 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)) { + continue; + } + } + status = EXIT_FAILURE; + bb_perror_msg("%s", *argv); + } + } while (*++argv); + + return status; +} diff --git a/coreutils/tr.c b/coreutils/tr.c new file mode 100644 index 0000000..237ade0 --- /dev/null +++ b/coreutils/tr.c @@ -0,0 +1,253 @@ +/* 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 <andersen@codepoet.org> to be used in busybox. + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +#include "busybox.h" + +// Even with -funsigned-char, gcc still complains about char as an array index. + +#define GCC4_IS_STUPID int + +#define ASCII 0377 + +/* some "globals" shared across this file */ +static char com_fl, del_fl, sq_fl; +/* these last are pointers to static buffers declared in tr_main */ +static char *poutput, *pvector, *pinvec, *poutvec; + +static void convert(void) +{ + int read_chars = 0, in_index = 0, out_index = 0, c, coded, last = -1; + + for (;;) { + // If we're out of input, flush output and read more input. + + if (in_index == read_chars) { + if (out_index) { + if (write(1, (char *) poutput, out_index) != out_index) + bb_error_msg_and_die(bb_msg_write_error); + out_index = 0; + } + + if ((read_chars = read(0, bb_common_bufsiz1, BUFSIZ)) <= 0) { + if (write(1, (char *) poutput, out_index) != out_index) + bb_error_msg(bb_msg_write_error); + exit(0); + } + in_index = 0; + } + c = bb_common_bufsiz1[in_index++]; + coded = pvector[c]; + if (del_fl && pinvec[c]) + continue; + if (sq_fl && last == coded && (pinvec[c] || poutvec[coded])) + continue; + poutput[out_index++] = last = coded; + } + + /* NOTREACHED */ +} + +static void map(char *string1, unsigned int string1_len, + 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[(GCC4_IS_STUPID)string1[i]] = last; + else + pvector[(GCC4_IS_STUPID)string1[i]] = last = string2[j++]; + } +} + +/* supported constructs: + * Ranges, e.g., [0-9] ==> 0123456789 + * Escapes, e.g., \a ==> Control-G + * Character classes, e.g. [:upper:] ==> A ... Z + */ +static unsigned int expand(const char *arg, char *buffer) +{ + char *buffer_start = buffer; + int i, ac; + + while (*arg) { + if (*arg == '\\') { + arg++; + *buffer++ = bb_process_escape_sequence(&arg); + } else if (*(arg+1) == '-') { + ac = *(arg+2); + if(ac == 0) { + *buffer++ = *arg++; + continue; + } + i = *arg; + while (i <= ac) + *buffer++ = i++; + arg += 3; /* Skip the assumed a-z */ + } else if (*arg == '[') { + arg++; + i = *arg++; + if (ENABLE_FEATURE_TR_CLASSES && i == ':') { + if (strncmp(arg, "alpha", 5) == 0) { + for (i = 'A'; i <= 'Z'; i++) + *buffer++ = i; + for (i = 'a'; i <= 'z'; i++) + *buffer++ = i; + } + else if (strncmp(arg, "alnum", 5) == 0) { + for (i = '0'; i <= '9'; i++) + *buffer++ = i; + for (i = 'A'; i <= 'Z'; i++) + *buffer++ = i; + for (i = 'a'; i <= 'z'; i++) + *buffer++ = i; + } + else if (strncmp(arg, "digit", 5) == 0) + for (i = '0'; i <= '9'; i++) + *buffer++ = i; + else if (strncmp(arg, "lower", 5) == 0) + for (i = 'a'; i <= 'z'; i++) + *buffer++ = i; + else if (strncmp(arg, "upper", 5) == 0) + for (i = 'A'; i <= 'Z'; i++) + *buffer++ = i; + else if (strncmp(arg, "space", 5) == 0) { + const char s[] = "\t\n\v\f\r "; + strcat((char*)buffer, s); + buffer += sizeof(s) - 1; + } + else if (strncmp(arg, "blank", 5) == 0) { + *buffer++ = '\t'; + *buffer++ = ' '; + } + /* gcc gives a warning if braces aren't used here */ + else if (strncmp(arg, "punct", 5) == 0) { + for (i = 0; i <= ASCII; i++) + if (isprint(i) && (!isalnum(i)) && (!isspace(i))) + *buffer++ = i; + } + else if (strncmp(arg, "cntrl", 5) == 0) { + for (i = 0; i <= ASCII; i++) + if (iscntrl(i)) + *buffer++ = i; + } + else { + *buffer++ = '['; + *buffer++ = ':'; + continue; + } + break; + } + if (ENABLE_FEATURE_TR_EQUIV && i == '=') { + *buffer++ = *arg; + /* skip the closing =] */ + arg += 3; + continue; + } + if (*arg++ != '-') { + *buffer++ = '['; + arg -= 2; + continue; + } + ac = *arg++; + while (i <= ac) + *buffer++ = i++; + arg++; /* Skip the assumed ']' */ + } else + *buffer++ = *arg++; + } + + return (buffer - buffer_start); +} + +static int complement(char *buffer, int buffer_len) +{ + short 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) +{ + unsigned char *ptr; + int output_length=0, input_length; + int idx = 1; + int i; + RESERVE_CONFIG_BUFFER(output, BUFSIZ); + RESERVE_CONFIG_BUFFER(vector, ASCII+1); + RESERVE_CONFIG_BUFFER(invec, ASCII+1); + RESERVE_CONFIG_BUFFER(outvec, ASCII+1); + + /* ... but make them available globally */ + poutput = output; + pvector = vector; + pinvec = invec; + poutvec = outvec; + + if (argc > 1 && argv[idx][0] == '-') { + for (ptr = (unsigned char *) &argv[idx][1]; *ptr; ptr++) { + switch (*ptr) { + case 'c': + com_fl = TRUE; + break; + case 'd': + del_fl = TRUE; + break; + case 's': + sq_fl = TRUE; + break; + default: + bb_show_usage(); + } + } + idx++; + } + for (i = 0; i <= ASCII; i++) { + vector[i] = i; + invec[i] = outvec[i] = FALSE; + } + + if (argv[idx] != NULL) { + input_length = expand(argv[idx++], bb_common_bufsiz1); + if (com_fl) + input_length = complement(bb_common_bufsiz1, input_length); + if (argv[idx] != NULL) { + if (*argv[idx] == '\0') + bb_error_msg_and_die("STRING2 cannot be empty"); + output_length = expand(argv[idx], output); + map(bb_common_bufsiz1, input_length, output, output_length); + } + for (i = 0; i < input_length; i++) + invec[(GCC4_IS_STUPID)bb_common_bufsiz1[i]] = TRUE; + for (i = 0; i < output_length; i++) + outvec[(GCC4_IS_STUPID)output[i]] = TRUE; + } + convert(); + return 0; +} diff --git a/coreutils/true.c b/coreutils/true.c new file mode 100644 index 0000000..388e897 --- /dev/null +++ b/coreutils/true.c @@ -0,0 +1,19 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini true implementation for busybox + * + * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org> + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +/* BB_AUDIT SUSv3 compliant */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/true.html */ + +#include <stdlib.h> +#include "busybox.h" + +int true_main(int argc, char **argv) +{ + return EXIT_SUCCESS; +} diff --git a/coreutils/tty.c b/coreutils/tty.c new file mode 100644 index 0000000..b2ab862 --- /dev/null +++ b/coreutils/tty.c @@ -0,0 +1,45 @@ +/* vi: set sw=4 ts=4: */ +/* + * tty implementation for busybox + * + * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org> + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +/* BB_AUDIT SUSv3 compliant */ +/* http://www.opengroup.org/onlinepubs/007904975/utilities/tty.html */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include "busybox.h" + +int tty_main(int argc, char **argv) +{ + const char *s; + int silent; /* Note: No longer relevant in SUSv3. */ + int retval; + + xfunc_error_retval = 2; /* SUSv3 requires > 1 for error. */ + + silent = getopt32(argc, argv, "s"); + + /* gnu tty outputs a warning that it is ignoring all args. */ + bb_warn_ignoring_args(argc - optind); + + retval = 0; + + if ((s = ttyname(0)) == NULL) { + /* According to SUSv3, ttyname can on fail with EBADF or ENOTTY. + * We know the file descriptor is good, so failure means not a tty. */ + s = "not a tty"; + retval = 1; + } + + if (!silent) { + puts(s); + } + + fflush_stdout_and_exit(retval); +} diff --git a/coreutils/uname.c b/coreutils/uname.c new file mode 100644 index 0000000..5a3eafe --- /dev/null +++ b/coreutils/uname.c @@ -0,0 +1,107 @@ +/* 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 <djm@gnu.ai.mit.edu> */ + +/* 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 <stdio.h> +#include <stdlib.h> +#include <stddef.h> +#include <string.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/utsname.h> +#include "busybox.h" + +typedef struct { + struct utsname name; + char processor[8]; /* for "unknown" */ +} uname_info_t; + +static const char options[] = "snrvmpa"; +static const unsigned short int 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) +{ + uname_info_t uname_info; +#if defined(__sparc__) && defined(__linux__) + char *fake_sparc = getenv("FAKE_SPARC"); +#endif + const unsigned short int *delta; + char toprint; + + toprint = getopt32(argc, argv, options); + + if (argc != optind) { + bb_show_usage(); + } + + if (toprint & (1 << 6)) { + toprint = 0x3f; + } + + if (toprint == 0) { + toprint = 1; /* sysname */ + } + + if (uname(&uname_info.name) == -1) { + bb_error_msg_and_die("cannot get system name"); + } + +#if defined(__sparc__) && defined(__linux__) + if ((fake_sparc != NULL) + && ((fake_sparc[0] == 'y') + || (fake_sparc[0] == 'Y'))) { + strcpy(uname_info.name.machine, "sparc"); + } +#endif + + strcpy(uname_info.processor, "unknown"); + + delta = utsname_offset; + do { + if (toprint & 1) { + printf(((char *)(&uname_info)) + *delta); + if (toprint > 1) { + putchar(' '); + } + } + ++delta; + } while (toprint >>= 1); + putchar('\n'); + + fflush_stdout_and_exit(EXIT_SUCCESS); +} diff --git a/coreutils/uniq.c b/coreutils/uniq.c new file mode 100644 index 0000000..100f2be --- /dev/null +++ b/coreutils/uniq.c @@ -0,0 +1,103 @@ +/* vi: set sw=4 ts=4: */ +/* + * uniq implementation for busybox + * + * Copyright (C) 2005 Manuel Novoa III <mjn3@codepoet.org> + * + * 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 "busybox.h" + +static const char uniq_opts[] = "f:s:" "cdu\0\1\2\4"; + +static FILE *xgetoptfile_uniq_s(char **argv, int read0write2) +{ + const char *n; + + if ((n = *argv) != NULL) { + if ((*n != '-') || n[1]) { + return xfopen(n, "r\0w" + read0write2); + } + } + return (read0write2) ? stdout : stdin; +} + +int uniq_main(int argc, char **argv) +{ + FILE *in, *out; + unsigned long dups, skip_fields, skip_chars, i, uniq_flags; + const char *s0, *e0, *s1, *e1, *input_filename; + int opt; + + uniq_flags = skip_fields = skip_chars = 0; + + while ((opt = getopt(argc, argv, uniq_opts)) > 0) { + if ((opt == 'f') || (opt == 's')) { + unsigned long t = xatoul(optarg); + if (opt == 'f') { + skip_fields = t; + } else { + skip_chars = t; + } + } else if ((s0 = strchr(uniq_opts, opt)) != NULL) { + uniq_flags |= s0[4]; + } else { + bb_show_usage(); + } + } + + input_filename = *(argv += optind); + + 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_getline(in)) != NULL) { + e1 = s1; + for (i = skip_fields; i; i--) { + e1 = skip_whitespace(e1); + while (*e1 && !isspace(*e1)) { + ++e1; + } + } + for (i = skip_chars; *e1 && i; i--) { + ++e1; + } + + if (!s0 || strcmp(e0, e1)) { + break; + } + + ++dups; /* Note: Testing for overflow seems excessive. */ + } + + if (s0) { + if (!(uniq_flags & (2 << !!dups))) { + fprintf(out, "\0%d " + (uniq_flags & 1), dups + 1); + 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..de473a7 --- /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 <mjn3@codepoet.org> + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +/* BB_AUDIT SUSv3 N/A -- Apparently a busybox extension. */ + +#include <stdlib.h> +#include <limits.h> +#include <unistd.h> +#include "busybox.h" + +int usleep_main(int argc, char **argv) +{ + if (argc != 2) { + 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..8b7de74 --- /dev/null +++ b/coreutils/uudecode.c @@ -0,0 +1,181 @@ +/* vi: set sw=4 ts=4: */ +/* + * Copyright 2003, Glenn McGrath <bug1@iinet.net.au> + * + * 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 "busybox.h" + +static int read_stduu(FILE *src_stream, FILE *dst_stream) +{ + char *line; + + while ((line = xmalloc_getline(src_stream)) != NULL) { + int length; + char *line_ptr = line; + + if (strcmp(line, "end") == 0) { + return EXIT_SUCCESS; + } + length = ((*line_ptr - 0x20) & 0x3f)* 4 / 3; + + if (length <= 0) { + /* Ignore the "`\n" line, why is it even in the encode file ? */ + continue; + } + if (length > 60) { + bb_error_msg_and_die("line too long"); + } + + line_ptr++; + /* Tolerate an overly long line to accomodate a possible exta '`' */ + if (strlen(line_ptr) < (size_t)length) { + bb_error_msg_and_die("short file"); + } + + while (length > 0) { + /* Merge four 6 bit chars to three 8 bit chars */ + fputc(((line_ptr[0] - 0x20) & 077) << 2 | ((line_ptr[1] - 0x20) & 077) >> 4, dst_stream); + line_ptr++; + length--; + if (length == 0) { + break; + } + + fputc(((line_ptr[0] - 0x20) & 077) << 4 | ((line_ptr[1] - 0x20) & 077) >> 2, dst_stream); + line_ptr++; + length--; + if (length == 0) { + break; + } + + fputc(((line_ptr[0] - 0x20) & 077) << 6 | ((line_ptr[1] - 0x20) & 077), dst_stream); + line_ptr += 2; + length -= 2; + } + free(line); + } + bb_error_msg_and_die("short file"); +} + +static int read_base64(FILE *src_stream, FILE *dst_stream) +{ + static const char base64_table[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=\n"; + char term_count = 0; + + while (1) { + char translated[4]; + int count = 0; + + while (count < 4) { + char *table_ptr; + int ch; + + /* Get next _valid_ character */ + do { + ch = fgetc(src_stream); + if (ch == EOF) { + bb_error_msg_and_die("short file"); + } + } while ((table_ptr = strchr(base64_table, ch)) == NULL); + + /* Convert encoded charcter to decimal */ + ch = table_ptr - base64_table; + + 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 EXIT_SUCCESS; + } + term_count = 1; + continue; + } else { + translated[count] = ch; + count++; + term_count = 0; + } + } + + /* Merge 6 bit chars to 8 bit */ + 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) +{ + FILE *src_stream; + char *outname = NULL; + char *line; + + getopt32(argc, argv, "o:", &outname); + + if (optind == argc) { + src_stream = stdin; + } else if (optind + 1 == argc) { + src_stream = xfopen(argv[optind], "r"); + } else { + bb_show_usage(); + } + + /* Search for the start of the encoding */ + while ((line = xmalloc_getline(src_stream)) != NULL) { + int (*decode_fn_ptr)(FILE * src, FILE * dst); + char *line_ptr; + FILE *dst_stream; + int mode; + int ret; + + 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; + } + + mode = strtoul(line_ptr, NULL, 8); + if (outname == NULL) { + outname = strchr(line_ptr, ' '); + if ((outname == NULL) || (*outname == '\0')) { + break; + } + outname++; + } + if (strcmp(outname, "-") == 0) { + dst_stream = stdout; + } else { + dst_stream = xfopen(outname, "w"); + chmod(outname, mode & (S_IRWXU | S_IRWXG | S_IRWXO)); + } + free(line); + ret = decode_fn_ptr(src_stream, dst_stream); + fclose_if_not_stdin(src_stream); + return ret; + } + bb_error_msg_and_die("no 'begin' line"); +} diff --git a/coreutils/uuencode.c b/coreutils/uuencode.c new file mode 100644 index 0000000..e8f8d54 --- /dev/null +++ b/coreutils/uuencode.c @@ -0,0 +1,75 @@ +/* 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 "busybox.h" + + +#define SRC_BUF_SIZE 45 // This *MUST* be a multiple of 3 +#define DST_BUF_SIZE 4 * ((SRC_BUF_SIZE + 2) / 3) +int uuencode_main(int argc, char **argv) +{ + const size_t src_buf_size = SRC_BUF_SIZE; + const size_t dst_buf_size = DST_BUF_SIZE; + size_t write_size = dst_buf_size; + struct stat stat_buf; + FILE *src_stream = stdin; + const char *tbl; + size_t size; + mode_t mode; + RESERVE_CONFIG_BUFFER(src_buf, SRC_BUF_SIZE + 1); + RESERVE_CONFIG_BUFFER(dst_buf, DST_BUF_SIZE + 1); + + tbl = bb_uuenc_tbl_std; + if (getopt32(argc, argv, "m") & 1) { + tbl = bb_uuenc_tbl_base64; + } + + switch (argc - optind) { + case 2: + src_stream = xfopen(argv[optind], "r"); + xstat(argv[optind], &stat_buf); + mode = stat_buf.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO); + if (src_stream == stdout) { + puts("NULL"); + } + break; + case 1: + mode = 0666 & ~umask(0666); + break; + default: + bb_show_usage(); + } + + printf("begin%s %o %s", tbl == bb_uuenc_tbl_std ? "" : "-base64", mode, argv[argc - 1]); + + while ((size = fread(src_buf, 1, src_buf_size, src_stream)) > 0) { + if (size != src_buf_size) { + /* write_size is always 60 until the last line */ + write_size = (4 * ((size + 2) / 3)); + /* pad with 0s so we can just encode extra bits */ + memset(&src_buf[size], 0, src_buf_size - size); + } + /* Encode the buffer we just read in */ + bb_uuencode((unsigned char*)src_buf, dst_buf, size, tbl); + + putchar('\n'); + if (tbl == bb_uuenc_tbl_std) { + putchar(tbl[size]); + } + if (fwrite(dst_buf, 1, write_size, stdout) != write_size) { + bb_perror_msg_and_die(bb_msg_write_error); + } + } + printf(tbl == bb_uuenc_tbl_std ? "\n`\nend\n" : "\n====\n"); + + die_if_ferror(src_stream, "source"); /* TODO - Fix this! */ + + fflush_stdout_and_exit(EXIT_SUCCESS); +} diff --git a/coreutils/watch.c b/coreutils/watch.c new file mode 100644 index 0000000..81856c8 --- /dev/null +++ b/coreutils/watch.c @@ -0,0 +1,79 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini watch implementation for busybox + * + * Copyright (C) 2001 by Michael Habermann <mhabermann@gmx.de> + * Copyrigjt (C) Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org) + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +/* BB_AUDIT SUSv3 N/A */ +/* BB_AUDIT GNU defects -- only option -n is supported. */ + +#include "busybox.h" + +// procps 2.0.18: +// watch [-d] [-n seconds] +// [--differences[=cumulative]] [--interval=seconds] command +// +// procps-3.2.3: +// watch [-dt] [-n seconds] +// [--differences[=cumulative]] [--interval=seconds] [--no-title] command +// +// (procps 3.x and procps 2.x are forks, not newer/older versions of the same) + +int watch_main(int argc, char **argv) +{ + unsigned opt; + unsigned period = 2; + unsigned cmdlen = 1; // 1 for terminal NUL + char *header = NULL; + char *cmd; + char *tmp; + char **p; + + opt_complementary = "-1"; // at least one param please + opt = getopt32(argc, argv, "+dtn:", &tmp); + //if (opt & 0x1) // -d (ignore) + //if (opt & 0x2) // -t + if (opt & 0x4) period = xatou(tmp); + argv += optind; + + p = argv; + while (*p) + cmdlen += strlen(*p++) + 1; + tmp = cmd = xmalloc(cmdlen); + while (*argv) { + tmp += sprintf(tmp, " %s", *argv); + argv++; + } + cmd++; // skip initial space + + while (1) { + printf("\033[H\033[J"); + if (!(opt & 0x2)) { // no -t + int width, len; + char *thyme; + time_t t; + + get_terminal_width_height(STDOUT_FILENO, &width, 0); + header = xrealloc(header, width--); + // '%-*s' pads header with spaces to the full width + snprintf(header, width, "Every %ds: %-*s", period, width, cmd); + time(&t); + thyme = ctime(&t); + len = strlen(thyme); + if (len < width) + strcpy(header + width - len, thyme); + puts(header); + } + fflush(stdout); + // TODO: 'real' watch pipes cmd's output to itself + // and does not allow it to overflow the screen + // (taking into account linewrap!) + system(cmd); + sleep(period); + } + return 0; // gcc thinks we can reach this :) +} diff --git a/coreutils/wc.c b/coreutils/wc.c new file mode 100644 index 0000000..973929e --- /dev/null +++ b/coreutils/wc.c @@ -0,0 +1,204 @@ +/* vi: set sw=4 ts=4: */ +/* + * wc implementation for busybox + * + * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org> + * + * 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 "busybox.h" + +#ifdef CONFIG_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) +{ + FILE *fp; + const char *s, *arg; + const char *start_fmt = "%9"COUNT_FMT; + 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; + char status = EXIT_SUCCESS; + char in_word; + unsigned print_type; + + print_type = getopt32(argc, 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_perror_msg("%s", 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..4cd4265 --- /dev/null +++ b/coreutils/who.c @@ -0,0 +1,67 @@ +/* vi: set sw=4 ts=4: */ +/*---------------------------------------------------------------------- + * Mini who is used to display user name, login time, + * idle time and host name. + * + * Author: Da Chen <dchen@ayrnetworks.com> + * + * 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. + * + *---------------------------------------------------------------------- + */ + +#include "busybox.h" +#include <utmp.h> +#include <time.h> + +static const char * idle_string (time_t t) +{ + static char str[6]; + + time_t s = time(NULL) - t; + + if (s < 60) + return "."; + if (s < (24 * 60 * 60)) { + sprintf(str, "%02d:%02d", + (int) (s / (60 * 60)), + (int) ((s % (60 * 60)) / 60)); + return str; + } + return "old"; +} + +int who_main(int argc, char **argv) +{ + struct utmp *ut; + struct stat st; + char *name; + + if (argc > 1) { + bb_show_usage(); + } + + setutent(); + printf("USER TTY IDLE TIME HOST\n"); + while ((ut = getutent()) != NULL) { + if (ut->ut_user[0] && ut->ut_type == USER_PROCESS) { + time_t thyme = ut->ut_tv.tv_sec; + + /* ut->ut_line is device name of tty - "/dev/" */ + name = concat_path_file("/dev", ut->ut_line); + printf("%-10s %-8s %-8s %-12.12s %s\n", ut->ut_user, ut->ut_line, + (stat(name, &st)) ? "?" : idle_string(st.st_atime), + ctime(&thyme) + 4, ut->ut_host); + if (ENABLE_FEATURE_CLEAN_UP) free(name); + } + } + if (ENABLE_FEATURE_CLEAN_UP) endutent(); + return 0; +} diff --git a/coreutils/whoami.c b/coreutils/whoami.c new file mode 100644 index 0000000..df714f2 --- /dev/null +++ b/coreutils/whoami.c @@ -0,0 +1,25 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini whoami implementation for busybox + * + * Copyright (C) 2000 Edward Betts <edward@debian.org>. + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +/* BB_AUDIT SUSv3 N/A -- Matches GNU behavior. */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include "busybox.h" + +int whoami_main(int argc, char **argv) +{ + if (argc > 1) + bb_show_usage(); + + puts(bb_getpwuid(NULL, geteuid(), -1)); + /* exits on error */ + fflush_stdout_and_exit(EXIT_SUCCESS); +} diff --git a/coreutils/yes.c b/coreutils/yes.c new file mode 100644 index 0000000..894506a --- /dev/null +++ b/coreutils/yes.c @@ -0,0 +1,41 @@ +/* vi: set sw=4 ts=4: */ +/* + * yes implementation for busybox + * + * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org> + * + * 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 "busybox.h" + +int yes_main(int argc, char **argv) +{ + static const char fmt_str[] = " %s"; + const char *fmt; + char **first_arg; + + *argv = "y"; + if (argc != 1) { + ++argv; + } + + first_arg = argv; + do { + fmt = fmt_str + 1; + do { + printf(fmt, *argv); + fmt = fmt_str; + } while (*++argv); + argv = first_arg; + } while (putchar('\n') != EOF); + + bb_perror_nomsg_and_die(); +} |