aboutsummaryrefslogtreecommitdiff
path: root/toys/posix
diff options
context:
space:
mode:
Diffstat (limited to 'toys/posix')
-rw-r--r--toys/posix/basename.c45
-rw-r--r--toys/posix/cal.c134
-rw-r--r--toys/posix/cat.c42
-rw-r--r--toys/posix/chgrp.c124
-rw-r--r--toys/posix/chmod.c72
-rw-r--r--toys/posix/cksum.c87
-rw-r--r--toys/posix/cmp.c92
-rw-r--r--toys/posix/comm.c85
-rw-r--r--toys/posix/cp.c224
-rw-r--r--toys/posix/date.c95
-rw-r--r--toys/posix/df.c154
-rw-r--r--toys/posix/dirname.c25
-rw-r--r--toys/posix/echo.c97
-rw-r--r--toys/posix/env.c52
-rw-r--r--toys/posix/false.c23
-rw-r--r--toys/posix/head.c61
-rw-r--r--toys/posix/id.c119
-rw-r--r--toys/posix/kill.c74
-rw-r--r--toys/posix/link.c27
-rw-r--r--toys/posix/ln.c70
-rw-r--r--toys/posix/logname.c29
-rw-r--r--toys/posix/ls.c493
-rw-r--r--toys/posix/mkdir.c76
-rw-r--r--toys/posix/mkfifo.c47
-rw-r--r--toys/posix/nice.c41
-rw-r--r--toys/posix/nohup.c42
-rw-r--r--toys/posix/od.c274
-rw-r--r--toys/posix/patch.c412
-rw-r--r--toys/posix/pwd.c30
-rw-r--r--toys/posix/rmdir.c43
-rw-r--r--toys/posix/sed.c63
-rw-r--r--toys/posix/sleep.c51
-rw-r--r--toys/posix/sort.c421
-rw-r--r--toys/posix/tail.c232
-rw-r--r--toys/posix/tee.c75
-rw-r--r--toys/posix/true.c23
-rw-r--r--toys/posix/tty.c32
-rw-r--r--toys/posix/uname.c74
-rw-r--r--toys/posix/uniq.c131
-rw-r--r--toys/posix/unlink.c26
-rw-r--r--toys/posix/wc.c60
-rw-r--r--toys/posix/who.c46
-rw-r--r--toys/posix/xargs.c188
43 files changed, 4611 insertions, 0 deletions
diff --git a/toys/posix/basename.c b/toys/posix/basename.c
new file mode 100644
index 00000000..9f228b41
--- /dev/null
+++ b/toys/posix/basename.c
@@ -0,0 +1,45 @@
+/* vi: set sw=4 ts=4:
+ *
+ * basename.c - Return non-directory portion of a pathname
+ *
+ * Copyright 2012 Tryn Mirell <tryn@mirell.org>
+ *
+ * See http://opengroup.org/onlinepubs/9699919799/utilities/basename.html
+
+
+USE_BASENAME(NEWTOY(basename, "<1>2", TOYFLAG_USR|TOYFLAG_BIN))
+
+config BASENAME
+ bool "basename"
+ default y
+ help
+ usage: basename string [suffix]
+
+ Return non-directory portion of a pathname removing suffix
+*/
+
+#include "toys.h"
+
+void basename_main(void)
+{
+ char *arg = toys.optargs[0], *suffix = toys.optargs[1], *base;
+
+ while ((base = strrchr(arg, '/'))) {
+ if (base == arg) break;
+ if (!base[1]) *base = 0;
+ else {
+ base++;
+ break;
+ }
+ }
+
+ if (!base) base = arg;
+
+ // chop off the suffix if provided
+ if (suffix) {
+ arg = base + strlen(base) - strlen(suffix);
+ if (arg > base && !strcmp(arg, suffix)) *arg = 0;
+ }
+
+ puts(base);
+}
diff --git a/toys/posix/cal.c b/toys/posix/cal.c
new file mode 100644
index 00000000..1c018e2f
--- /dev/null
+++ b/toys/posix/cal.c
@@ -0,0 +1,134 @@
+/* vi: set sw=4 ts=4:
+ *
+ * cal.c - show calendar.
+ *
+ * Copyright 2011 Rob Landley <rob@landley.net>
+ *
+ * See http://opengroup.org/onlinepubs/9699919799/utilities/cal.html
+
+USE_CAL(NEWTOY(cal, ">2", TOYFLAG_USR|TOYFLAG_BIN))
+
+config CAL
+ bool "cal"
+ default y
+ help
+ usage: cal [[month] year]
+ Print a calendar.
+
+ With one argument, prints all months of the specified year.
+ With two arguments, prints calendar for month and year.
+*/
+
+#include "toys.h"
+
+// Write calendar into buffer: each line is 20 chars wide, end indicated
+// by empty string.
+
+static char *calstrings(char *buf, struct tm *tm)
+{
+ char temp[21];
+ int wday, mday, start, len, line;
+
+ // header
+ len = strftime(temp, 21, "%B %Y", tm);
+ len += (20-len)/2;
+ buf += sprintf(buf, "%*s%*s ", len, temp, 20-len, "");
+ buf++;
+ buf += sprintf(buf, "Su Mo Tu We Th Fr Sa ");
+ buf++;
+
+ // What day of the week does this month start on?
+ if (tm->tm_mday>1)
+ start = (36+tm->tm_wday-tm->tm_mday)%7;
+ else start = tm->tm_wday;
+
+ // What day does this month end on? Alas, libc doesn't tell us...
+ len = 31;
+ if (tm->tm_mon == 1) {
+ int year = tm->tm_year;
+ len = 28;
+ if (!(year & 3) && !((year&100) && !(year&400))) len++;
+ } else if ((tm->tm_mon+(tm->tm_mon>6 ? 1 : 0)) & 1) len = 30;
+
+ for (mday=line=0;line<6;line++) {
+ for (wday=0; wday<7; wday++) {
+ char *pat = " ";
+ if (!mday ? wday==start : mday<len) {
+ pat = "%2d ";
+ mday++;
+ }
+ buf += sprintf(buf, pat, mday);
+ }
+ buf++;
+ }
+
+ return buf;
+}
+
+void xcheckrange(long val, long low, long high)
+{
+ char *err = "%ld %s than %ld";
+
+ if (val < low) error_exit(err, val, "less", low);
+ if (val > high) error_exit(err, val, "greater", high);
+}
+
+// Worst case scenario toybuf usage: sizeof(struct tm) plus 21 bytes/line
+// plus 8 lines/month plus 12 months, comes to a bit over 2k of our 4k buffer.
+
+void cal_main(void)
+{
+ struct tm *tm;
+ char *buf = toybuf;
+
+ if (toys.optc) {
+ // Conveniently starts zeroed
+ tm = (struct tm *)toybuf;
+ buf += sizeof(struct tm);
+
+ // Last argument is year, one before that (if any) is month.
+ xcheckrange(tm->tm_year = atol(toys.optargs[--toys.optc]),1,9999);
+ tm->tm_year -= 1900;
+ tm->tm_mday = 1;
+ tm->tm_hour = 12; // noon to avoid timezone weirdness
+ if (toys.optc) {
+ xcheckrange(tm->tm_mon = atol(toys.optargs[--toys.optc]),1,12);
+ tm->tm_mon--;
+
+ // Print 12 months of the year
+
+ } else {
+ char *bufs[12];
+ int i, j, k;
+
+ for (i=0; i<12; i++) {
+ tm->tm_mon=i;
+ mktime(tm);
+ buf = calstrings(bufs[i]=buf, tm);
+ }
+
+ // 4 rows, 6 lines each, 3 columns
+ for (i=0; i<4; i++) {
+ for (j=0; j<8; j++) {
+ for(k=0; k<3; k++) {
+ char **b = bufs+(k+i*3);
+ *b += printf("%s ", *b);
+ }
+ puts("");
+ }
+ }
+ return;
+ }
+
+ // What day of the week does that start on?
+ mktime(tm);
+
+ } else {
+ time_t now;
+ time(&now);
+ tm = localtime(&now);
+ }
+
+ calstrings(buf, tm);
+ while (*buf) buf += printf("%s\n", buf);
+}
diff --git a/toys/posix/cat.c b/toys/posix/cat.c
new file mode 100644
index 00000000..81bfcafd
--- /dev/null
+++ b/toys/posix/cat.c
@@ -0,0 +1,42 @@
+/* vi: set sw=4 ts=4:
+ *
+ * cat.c - copy inputs to stdout.
+ *
+ * Copyright 2006 Rob Landley <rob@landley.net>
+ *
+ * See http://www.opengroup.org/onlinepubs/009695399/utilities/cat.html
+
+USE_CAT(NEWTOY(cat, "u", TOYFLAG_BIN))
+
+config CAT
+ bool "cat"
+ default y
+ help
+ usage: cat [-u] [file...]
+ Copy (concatenate) files to stdout. If no files listed, copy from stdin.
+ Filename "-" is a synonym for stdin.
+
+ -u Copy one byte at a time (slow).
+*/
+
+#include "toys.h"
+
+static void do_cat(int fd, char *name)
+{
+ int len, size=toys.optflags ? 1 : sizeof(toybuf);
+
+ for (;;) {
+ len = read(fd, toybuf, size);
+ if (len<0) {
+ perror_msg("%s",name);
+ toys.exitval = EXIT_FAILURE;
+ }
+ if (len<1) break;
+ xwrite(1, toybuf, len);
+ }
+}
+
+void cat_main(void)
+{
+ loopfiles(toys.optargs, do_cat);
+}
diff --git a/toys/posix/chgrp.c b/toys/posix/chgrp.c
new file mode 100644
index 00000000..ad4e351a
--- /dev/null
+++ b/toys/posix/chgrp.c
@@ -0,0 +1,124 @@
+/* vi: set sw=4 ts=4:
+ *
+ * chown.c - Change ownership
+ *
+ * Copyright 2012 Georgi Chorbadzhiyski <georgi@unixsol.org>
+ *
+ * See http://pubs.opengroup.org/onlinepubs/009695399/utilities/chown.html
+ * See http://pubs.opengroup.org/onlinepubs/009695399/utilities/chgrp.html
+ *
+ * TODO: group only one of [HLP]
+
+USE_CHGRP(NEWTOY(chgrp, "<2hPLHRfv", TOYFLAG_BIN))
+USE_CHGRP(OLDTOY(chown, chgrp, "<2hPLHRfv", TOYFLAG_BIN))
+
+config CHGRP
+ bool "chgrp/chown"
+ default y
+ help
+ usage: chown [-RHLP] [-fvh] [owner][:group] file...
+ usage: chgrp [-RHLP] [-fvh] group file...
+
+ Change ownership of one or more files.
+
+ -f suppress most error messages.
+ -h change symlinks instead of what they point to
+ -R recurse into subdirectories (implies -h).
+ -H with -R change target of symlink, follow command line symlinks
+ -L with -R change target of symlink, follow all symlinks
+ -P with -R change symlink, do not follow symlinks (default)
+ -v verbose output.
+*/
+
+#include "toys.h"
+
+#define FLAG_v 1
+#define FLAG_f 2
+#define FLAG_R 4
+#define FLAG_H 8
+#define FLAG_L 16
+#define FLAG_P 32
+#define FLAG_h 64
+
+DEFINE_GLOBALS(
+ uid_t owner;
+ gid_t group;
+ char *owner_name, *group_name;
+ int symfollow;
+)
+
+#define TT this.chgrp
+
+static int do_chgrp(struct dirtree *node)
+{
+ int fd, ret, flags = toys.optflags;
+
+ // Depth first search
+ if (!dirtree_notdotdot(node)) return 0;
+ if ((flags & FLAG_R) && node->data != -1 && S_ISDIR(node->st.st_mode))
+ return DIRTREE_COMEAGAIN|((flags&FLAG_L) ? DIRTREE_SYMFOLLOW : 0);
+
+ fd = dirtree_parentfd(node);
+ ret = fchownat(fd, node->name, TT.owner, TT.group,
+ (flags&(FLAG_L|FLAG_H)) || !(flags&(FLAG_h|FLAG_R))
+ ? 0 : AT_SYMLINK_NOFOLLOW);
+
+ if (ret || (flags & FLAG_v)) {
+ char *path = dirtree_path(node, 0);
+ if (flags & FLAG_v)
+ xprintf("%s %s%s%s %s\n", toys.which->name,
+ TT.owner_name ? TT.owner_name : "",
+ toys.which->name[2]=='o' && TT.group_name ? ":" : "",
+ TT.group_name ? TT.group_name : "", path);
+ if (ret == -1 && !(toys.optflags & FLAG_f))
+ perror_msg("changing owner:group of '%s' to '%s:%s'", path,
+ TT.owner_name, TT.group_name);
+ free(path);
+ }
+ toys.exitval |= ret;
+
+ return 0;
+}
+
+void chgrp_main(void)
+{
+ int ischown = toys.which->name[2] == 'o';
+ char **s, *own;
+
+ // Distinguish chown from chgrp
+ if (ischown) {
+ char *grp;
+ struct passwd *p;
+
+ own = xstrdup(*toys.optargs);
+ if ((grp = strchr(own, ':')) || (grp = strchr(own, '.'))) {
+ *(grp++) = 0;
+ TT.group_name = grp;
+ }
+ if (*own) {
+ TT.owner_name = own;
+ p = getpwnam(own);
+ // TODO: trailing garbage?
+ if (!p && isdigit(*own)) p=getpwuid(atoi(own));
+ if (!p) error_exit("no user '%s'", own);
+ TT.owner = p->pw_uid;
+ }
+ } else TT.group_name = *toys.optargs;
+
+ if (TT.group_name) {
+ struct group *g;
+ g = getgrnam(TT.group_name);
+ if (!g) g=getgrgid(atoi(TT.group_name));
+ if (!g) error_exit("no group '%s'", TT.group_name);
+ TT.group = g->gr_gid;
+ }
+
+ for (s=toys.optargs+1; *s; s++) {
+ struct dirtree *new = dirtree_add_node(AT_FDCWD, *s,
+ toys.optflags&(FLAG_H|FLAG_L));
+ if (new) handle_callback(new, do_chgrp);
+ else toys.exitval = 1;
+ }
+
+ if (CFG_TOYBOX_FREE) free(own);
+}
diff --git a/toys/posix/chmod.c b/toys/posix/chmod.c
new file mode 100644
index 00000000..e41598c1
--- /dev/null
+++ b/toys/posix/chmod.c
@@ -0,0 +1,72 @@
+/* vi: set sw=4 ts=4:
+ *
+ * chmod.c - Change file mode bits
+ *
+ * Copyright 2012 Rob Landley <rob@landley.net>
+ *
+ * See http://pubs.opengroup.org/onlinepubs/009695399/utilities/chmod.html
+ *
+
+USE_CHMOD(NEWTOY(chmod, "<2?vR", TOYFLAG_BIN))
+
+config CHMOD
+ bool "chmod"
+ default y
+ help
+ usage: chmod [-R] MODE FILE...
+
+ Change mode of listed file[s] (recursively with -R).
+
+ MODE can be (comma-separated) stanzas: [ugoa][+-=][rwxstXugo]
+
+ Stanzas are applied in order: For each category (u = user,
+ g = group, o = other, a = all three, if none specified default is a),
+ set (+), clear (-), or copy (=), r = read, w = write, x = execute.
+ s = u+s = suid, g+s = sgid, o+s = sticky. (+t is an alias for o+s).
+ suid/sgid: execute as the user/group who owns the file.
+ sticky: can't delete files you don't own out of this directory
+ X = x for directories or if any category already has x set.
+
+ Or MODE can be an octal value up to 7777 ug uuugggooo top +
+ bit 1 = o+x, bit 1<<8 = u+w, 1<<11 = g+1 sstrwxrwxrwx bottom
+
+ Examples:
+ chmod u+w file - allow owner of "file" to write to it.
+ chmod 744 file - user can read/write/execute, everyone else read only
+*/
+
+#include "toys.h"
+
+DEFINE_GLOBALS(
+ char *mode;
+)
+
+#define TT this.chmod
+
+#define FLAG_R 1
+#define FLAG_v 2
+
+int do_chmod(struct dirtree *try)
+{
+ mode_t mode;
+
+ if (!dirtree_notdotdot(try)) return 0;
+
+ mode = string_to_mode(TT.mode, try->st.st_mode);
+ if (toys.optflags & FLAG_v) {
+ char *s = dirtree_path(try, 0);
+ printf("chmod '%s' to %04o\n", s, mode);
+ free(s);
+ }
+ wfchmodat(dirtree_parentfd(try), try->name, mode);
+
+ return (toys.optflags & FLAG_R) ? DIRTREE_RECURSE : 0;
+}
+
+void chmod_main(void)
+{
+ TT.mode = *toys.optargs;
+ char **file;
+
+ for (file = toys.optargs+1; *file; file++) dirtree_read(*file, do_chmod);
+}
diff --git a/toys/posix/cksum.c b/toys/posix/cksum.c
new file mode 100644
index 00000000..213bc74a
--- /dev/null
+++ b/toys/posix/cksum.c
@@ -0,0 +1,87 @@
+/* vi: set sw=4 ts=4:
+ *
+ * cksum.c - produce crc32 checksum value for each input
+ *
+ * Copyright 2008 Rob Landley <rob@landley.net>
+ *
+ * See http://www.opengroup.org/onlinepubs/009695399/utilities/cksum.html
+
+USE_CKSUM(NEWTOY(cksum, "IPLN", TOYFLAG_BIN))
+
+config CKSUM
+ bool "cksum"
+ default y
+ help
+ usage: cksum [-IPLN] [file...]
+
+ For each file, output crc32 checksum value, length and name of file.
+ If no files listed, copy from stdin. Filename "-" is a synonym for stdin.
+
+ -L Little endian (defaults to big endian)
+ -P Pre-inversion
+ -I Skip post-inversion
+ -N Do not include length in CRC calculation
+*/
+
+#include "toys.h"
+
+DEFINE_GLOBALS(
+ unsigned crc_table[256];
+)
+
+#define TT this.cksum
+
+static unsigned cksum_be(unsigned crc, unsigned char c)
+{
+ return (crc<<8)^TT.crc_table[(crc>>24)^c];
+}
+
+static unsigned cksum_le(unsigned crc, unsigned char c)
+{
+ return TT.crc_table[(crc^c)&0xff] ^ (crc>>8);
+}
+
+static void do_cksum(int fd, char *name)
+{
+ unsigned crc = (toys.optflags&4) ? 0xffffffff : 0;
+ uint64_t llen = 0, llen2;
+ unsigned (*cksum)(unsigned crc, unsigned char c);
+
+
+ cksum = (toys.optflags&2) ? cksum_le : cksum_be;
+ // CRC the data
+
+ for (;;) {
+ int len, i;
+
+ len = read(fd, toybuf, sizeof(toybuf));
+ if (len<0) {
+ perror_msg("%s",name);
+ toys.exitval = EXIT_FAILURE;
+ }
+ if (len<1) break;
+
+ llen += len;
+ for (i=0; i<len; i++) crc=cksum(crc, toybuf[i]);
+ }
+
+ // CRC the length
+
+ llen2 = llen;
+ if (!(toys.optflags&1)) {
+ while (llen) {
+ crc = cksum(crc, llen);
+ llen >>= 8;
+ }
+ }
+
+ printf("%u %"PRIu64, (toys.optflags&8) ? crc : ~crc, llen2);
+ if (strcmp("-", name)) printf(" %s", name);
+ xputc('\n');
+}
+
+void cksum_main(void)
+{
+ crc_init(TT.crc_table, toys.optflags&2);
+ loopfiles(toys.optargs, do_cksum);
+}
diff --git a/toys/posix/cmp.c b/toys/posix/cmp.c
new file mode 100644
index 00000000..0afca381
--- /dev/null
+++ b/toys/posix/cmp.c
@@ -0,0 +1,92 @@
+/* vi: set sw=4 ts=4:
+ *
+ * cmp.c - Compare two files.
+ *
+ * Copyright 2012 Timothy Elliott <tle@holymonkey.com>
+ *
+ * See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/cmp.html
+
+USE_CMP(NEWTOY(cmp, "<2>2ls", TOYFLAG_USR|TOYFLAG_BIN))
+
+config CMP
+ bool "cmp"
+ default y
+ help
+ usage: cmp [-l] [-s] FILE1 FILE2
+
+ Compare the contents of two files.
+
+ -l show all differing bytes
+ -s silent
+*/
+
+#include "toys.h"
+
+#define FLAG_s 1
+#define FLAG_l 2
+
+DEFINE_GLOBALS(
+ int fd;
+ char *name;
+)
+
+#define TT this.cmp
+
+// This handles opening the file and
+
+void do_cmp(int fd, char *name)
+{
+ int i, len1, len2, min_len, size = sizeof(toybuf)/2;
+ long byte_no = 1, line_no = 1;
+ char *buf2 = toybuf+size;
+
+ // First time through, cache the data and return.
+ if (!TT.fd) {
+ TT.name = name;
+ // On return the old filehandle is closed, and this assures that even
+ // if we were called with stdin closed, the new filehandle != 0.
+ TT.fd = dup(fd);
+ return;
+ }
+
+ for (;;) {
+ len1 = readall(TT.fd, toybuf, size);
+ len2 = readall(fd, buf2, size);
+
+ min_len = len1 < len2 ? len1 : len2;
+ for (i=0; i<min_len; i++) {
+ if (toybuf[i] != buf2[i]) {
+ toys.exitval = 1;
+ if (toys.optflags & FLAG_l)
+ printf("%ld %o %o\n", byte_no, toybuf[i], buf2[i]);
+ else {
+ if (!(toys.optflags & FLAG_s)) {
+ printf("%s %s differ: char %ld, line %ld\n",
+ TT.name, name, byte_no, line_no);
+ toys.exitval++;
+ }
+ goto out;
+ }
+ }
+ byte_no++;
+ if (toybuf[i] == '\n') line_no++;
+ }
+ if (len1 != len2) {
+ if (!(toys.optflags & FLAG_s)) {
+ fprintf(stderr, "cmp: EOF on %s\n",
+ len1 < len2 ? TT.name : name);
+ }
+ toys.exitval = 1;
+ break;
+ }
+ if (len1 < 1) break;
+ }
+out:
+ if (CFG_TOYBOX_FREE) close(TT.fd);
+}
+
+void cmp_main(void)
+{
+ loopfiles_rw(toys.optargs, O_RDONLY, 0, toys.optflags&FLAG_s, do_cmp);
+}
+
diff --git a/toys/posix/comm.c b/toys/posix/comm.c
new file mode 100644
index 00000000..1c2267ac
--- /dev/null
+++ b/toys/posix/comm.c
@@ -0,0 +1,85 @@
+/* vi: set sw=4 ts=4:
+ *
+ * comm.c - select or reject lines common to two files
+ *
+ * Copyright 2012 Ilya Kuzmich <ikv@safe-mail.net>
+ *
+ * See http://pubs.opengroup.org/onlinepubs/009695399/utilities/comm.html
+
+// <# and ># take single digit, so 321 define flags
+USE_COMM(NEWTOY(comm, "<2>2321", TOYFLAG_USR|TOYFLAG_BIN))
+
+config COMM
+ bool "comm"
+ default y
+ help
+ usage: comm [-123] FILE1 FILE2
+
+ Reads FILE1 and FILE2, which should be ordered, and produces three text
+ columns as output: lines only in FILE1; lines only in FILE2; and lines
+ in both files. Filename "-" is a synonym for stdin.
+
+ -1 suppress the output column of lines unique to FILE1
+ -2 suppress the output column of lines unique to FILE2
+ -3 suppress the output column of lines duplicated in FILE1 and FILE2
+*/
+
+#include "toys.h"
+
+#define FLAG_1 1
+#define FLAG_2 2
+#define FLAG_3 4
+
+static void writeline(const char *line, int col)
+{
+ if (col == 0 && toys.optflags & FLAG_1) return;
+ else if (col == 1) {
+ if (toys.optflags & FLAG_2) return;
+ if (!(toys.optflags & FLAG_1)) putchar('\t');
+ } else if (col == 2) {
+ if (toys.optflags & FLAG_3) return;
+ if (!(toys.optflags & FLAG_1)) putchar('\t');
+ if (!(toys.optflags & FLAG_2)) putchar('\t');
+ }
+ puts(line);
+}
+
+void comm_main(void)
+{
+ int file[2];
+ char *line[2];
+ int i;
+
+ if (toys.optflags == 7) return;
+
+ for (i = 0; i < 2; i++) {
+ file[i] = strcmp("-", toys.optargs[i]) ? xopen(toys.optargs[i], O_RDONLY) : 0;
+ line[i] = get_line(file[i]);
+ }
+
+ while (line[0] && line[1]) {
+ int order = strcmp(line[0], line[1]);
+
+ if (order == 0) {
+ writeline(line[0], 2);
+ for (i = 0; i < 2; i++) {
+ free(line[i]);
+ line[i] = get_line(file[i]);
+ }
+ } else {
+ i = order < 0 ? 0 : 1;
+ writeline(line[i], i);
+ free(line[i]);
+ line[i] = get_line(file[i]);
+ }
+ }
+
+ /* print rest of the longer file */
+ for (i = line[0] ? 0 : 1; line[i];) {
+ writeline(line[i], i);
+ free(line[i]);
+ line[i] = get_line(file[i]);
+ }
+
+ if (CFG_TOYBOX_FREE) for (i = 0; i < 2; i--) xclose(file[i]);
+}
diff --git a/toys/posix/cp.c b/toys/posix/cp.c
new file mode 100644
index 00000000..bc922b3d
--- /dev/null
+++ b/toys/posix/cp.c
@@ -0,0 +1,224 @@
+/* vi: set sw=4 ts=4:
+ *
+ * cp.c - Copy files.
+ *
+ * Copyright 2008 Rob Landley <rob@landley.net>
+ *
+ * See http://www.opengroup.org/onlinepubs/009695399/utilities/cp.html
+ *
+ * "R+ra+d+p+r"
+USE_CP(NEWTOY(cp, "<2vslrRdpaHLPif", TOYFLAG_BIN))
+
+config CP
+ bool "cp (broken by dirtree changes)"
+ default n
+ help
+ usage: cp -fiprdal SOURCE... DEST
+
+ Copy files from SOURCE to DEST. If more than one SOURCE, DEST must
+ be a directory.
+
+ -f force copy by deleting destination file
+ -i interactive, prompt before overwriting existing DEST
+ -p preserve timestamps, ownership, and permissions
+ -r recurse into subdirectories (DEST must be a directory)
+ -d don't dereference symlinks
+ -a same as -dpr
+ -l hard link instead of copying
+ -v verbose
+*/
+
+#include "toys.h"
+
+#define FLAG_f 1
+#define FLAG_i 2
+#define FLAG_P 4 // todo
+#define FLAG_L 8 // todo
+#define FLAG_H 16 // todo
+#define FLAG_a 32
+#define FLAG_p 64
+#define FLAG_d 128 // todo
+#define FLAG_R 256
+#define FLAG_r 512
+#define FLAG_l 1024 // todo
+#define FLAG_s 2048 // todo
+#define FLAG_v 4098
+
+DEFINE_GLOBALS(
+ char *destname;
+ int destisdir;
+ int destisnew;
+ int keep_symlinks;
+)
+
+#define TT this.cp
+
+// Copy an individual file or directory to target.
+
+void cp_file(char *src, char *dst, struct stat *srcst)
+{
+ int fdout = -1;
+
+ // -i flag is specified and dst file exists.
+ if ((toys.optflags&FLAG_i) && !access(dst, R_OK)
+ && !yesno("cp: overwrite", 1))
+ return;
+
+ if (toys.optflags & FLAG_v)
+ printf("'%s' -> '%s'\n", src, dst);
+
+ // Copy directory or file to destination.
+
+ if (S_ISDIR(srcst->st_mode)) {
+ struct stat st2;
+
+ // Always make directory writeable to us, so we can create files in it.
+ //
+ // Yes, there's a race window between mkdir() and open() so it's
+ // possible that -p can be made to chown a directory other than the one
+ // we created. The closest we can do to closing this is make sure
+ // that what we open _is_ a directory rather than something else.
+
+ if ((mkdir(dst, srcst->st_mode | 0200) && errno != EEXIST)
+ || 0>(fdout=open(dst, 0)) || fstat(fdout, &st2)
+ || !S_ISDIR(st2.st_mode))
+ {
+ perror_exit("mkdir '%s'", dst);
+ }
+ } else if (TT.keep_symlinks && S_ISLNK(srcst->st_mode)) {
+ char *link = xreadlink(src);
+
+ // Note: -p currently has no effect on symlinks. How do you get a
+ // filehandle to them? O_NOFOLLOW causes the open to fail.
+ if (!link || symlink(link, dst)) perror_msg("link '%s'", dst);
+ free(link);
+ return;
+ } else if (toys.optflags & FLAG_l) {
+ if (link(src, dst)) perror_msg("link '%s'");
+ return;
+ } else {
+ int fdin, i;
+
+ fdin = xopen(src, O_RDONLY);
+ for (i=2 ; i; i--) {
+ fdout = open(dst, O_RDWR|O_CREAT|O_TRUNC, srcst->st_mode);
+ if (fdout>=0 || !(toys.optflags & FLAG_f)) break;
+ unlink(dst);
+ }
+ if (fdout<0) perror_exit("%s", dst);
+ xsendfile(fdin, fdout);
+ close(fdin);
+ }
+
+ // Inability to set these isn't fatal, some require root access.
+ // Can't do fchmod() etc here because -p works on mkdir, too.
+
+ if (toys.optflags & (FLAG_p|FLAG_a)) {
+ int mask = umask(0);
+ struct utimbuf ut;
+
+ (void) fchown(fdout,srcst->st_uid, srcst->st_gid);
+ ut.actime = srcst->st_atime;
+ ut.modtime = srcst->st_mtime;
+ utime(dst, &ut);
+ umask(mask);
+ }
+ xclose(fdout);
+}
+
+// Callback from dirtree_read() for each file/directory under a source dir.
+
+int cp_node(struct dirtree *node)
+{
+ char *path = dirtree_path(node, 0); // TODO: use openat() instead
+ char *s = path+strlen(path);
+ struct dirtree *n;
+
+ // Find appropriate chunk of path for destination.
+
+ n = node;
+ if (!TT.destisdir) n = n->parent;
+ for (;;n = n->parent) {
+ while (s!=path) {
+ if (*(--s)=='/') break;
+ }
+ if (!n) break;
+ }
+ if (s != path) s++;
+
+ s = xmsprintf("%s/%s", TT.destname, s);
+ cp_file(path, s, &(node->st));
+ free(s);
+ free(path); // redo this whole darn function.
+
+ return 0;
+}
+
+void cp_main(void)
+{
+ struct stat st;
+ int i;
+
+ // Grab target argument. (Guaranteed to be there due to "<2" above.)
+
+ TT.destname = toys.optargs[--toys.optc];
+
+ // If destination doesn't exist, are we ok with that?
+
+ if (stat(TT.destname, &st)) {
+ if (toys.optc>1) goto error_notdir;
+ TT.destisnew++;
+
+ // If destination exists...
+
+ } else {
+ if (S_ISDIR(st.st_mode)) TT.destisdir++;
+ else if (toys.optc > 1) goto error_notdir;
+ }
+
+ // Handle sources
+
+ for (i=0; i<toys.optc; i++) {
+ char *src = toys.optargs[i];
+ char *dst;
+
+ // Skip src==dest (TODO check inodes to catch "cp blah ./blah").
+
+ if (!strcmp(src, TT.destname)) continue;
+
+ // Skip nonexistent sources.
+
+ TT.keep_symlinks = toys.optflags & (FLAG_d|FLAG_a);
+ if (TT.keep_symlinks ? lstat(src, &st) : stat(src, &st))
+ {
+ perror_msg("'%s'", src);
+ toys.exitval = 1;
+ continue;
+ }
+
+ // Copy directory or file.
+
+ if (TT.destisdir) {
+ dst = strrchr(src, '/');
+ if (dst) dst++;
+ else dst=src;
+ dst = xmsprintf("%s/%s", TT.destname, dst);
+ } else dst = TT.destname;
+ if (S_ISDIR(st.st_mode)) {
+ if (toys.optflags & (FLAG_r|FLAG_R|FLAG_a)) {
+ cp_file(src, dst, &st);
+
+ TT.keep_symlinks++;
+ strncpy(toybuf, src, sizeof(toybuf)-1);
+ toybuf[sizeof(toybuf)-1]=0;
+ dirtree_read(toybuf, cp_node);
+ } else error_msg("Skipped dir '%s'", src);
+ } else cp_file(src, dst, &st);
+ if (TT.destisdir) free(dst);
+ }
+
+ return;
+
+error_notdir:
+ error_exit("'%s' isn't a directory", TT.destname);
+}
diff --git a/toys/posix/date.c b/toys/posix/date.c
new file mode 100644
index 00000000..925565a2
--- /dev/null
+++ b/toys/posix/date.c
@@ -0,0 +1,95 @@
+/* vi: set sw=4 ts=4:
+ *
+ * date.c - set/get the date
+ *
+ * Copyright 2012 Andre Renaud <andre@bluewatersys.com>
+ *
+ * See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/date.html
+
+USE_DATE(NEWTOY(date, "r:u", TOYFLAG_BIN))
+
+config DATE
+ bool "date"
+ default y
+ help
+ usage: date [-u] [-r file] [+format] | mmddhhmm[[cc]yy]
+
+ Set/get the current date/time
+*/
+
+#include "toys.h"
+
+DEFINE_GLOBALS(
+ char *file;
+)
+
+#define TT this.date
+
+#define FLAG_u 1
+#define FLAG_r 2
+
+void date_main(void)
+{
+ const char *format_string = "%a %b %e %H:%M:%S %Z %Y";
+ time_t now = time(NULL);
+ struct tm tm;
+
+ if (TT.file) {
+ struct stat st;
+
+ xstat(TT.file, &st);
+ now = st.st_mtim.tv_sec;
+ }
+ ((toys.optflags & FLAG_u) ? gmtime_r : localtime_r)(&now, &tm);
+
+ // Display the date?
+ if (!toys.optargs[0] || toys.optargs[0][0] == '+') {
+ if (toys.optargs[0]) format_string = toys.optargs[0]+1;
+ if (!strftime(toybuf, sizeof(toybuf), format_string, &tm))
+ perror_msg("bad format `%s'", format_string);
+
+ puts(toybuf);
+
+ // Set the date
+ } else {
+ struct timeval tv;
+ char *s = *toys.optargs;
+ int len = strlen(s);
+
+ if (len < 8 || len > 12 || (len & 1)) error_msg("bad date `%s'", s);
+
+ // Date format: mmddhhmm[[cc]yy]
+ memset(&tm, 0, sizeof(tm));
+ len = sscanf(s, "%2u%2u%2u%2u", &tm.tm_mon, &tm.tm_mday, &tm.tm_hour,
+ &tm.tm_min);
+ tm.tm_mon--;
+
+ // If year specified, overwrite one we fetched earlier
+ if (len > 8) {
+ sscanf(s, "%u", &tm.tm_year);
+ if (len == 12) tm.tm_year -= 1900;
+ /* 69-99 = 1969-1999, 0 - 68 = 2000-2068 */
+ else if (tm.tm_year < 69) tm.tm_year += 100;
+ }
+
+ if (toys.optflags & FLAG_u) {
+ // Get the UTC version of a struct tm
+ char *tz = CFG_TOYBOX_FREE ? getenv("TZ") : 0;
+ setenv("TZ", "UTC", 1);
+ tzset();
+ tv.tv_sec = mktime(&tm);
+ if (CFG_TOYBOX_FREE) {
+ if (tz) setenv("TZ", tz, 1);
+ else unsetenv("TZ");
+ tzset();
+ }
+ } else tv.tv_sec = mktime(&tm);
+
+ if (tv.tv_sec == (time_t)-1) error_msg("bad `%s'", toys.optargs[0]);
+ tv.tv_usec = 0;
+ if (!strftime(toybuf, sizeof(toybuf), format_string, &tm))
+ perror_msg("bad format `%s'", format_string);
+ puts(toybuf);
+ if (settimeofday(&tv, NULL) < 0) perror_msg("cannot set date");
+ }
+}
diff --git a/toys/posix/df.c b/toys/posix/df.c
new file mode 100644
index 00000000..72c15195
--- /dev/null
+++ b/toys/posix/df.c
@@ -0,0 +1,154 @@
+/* vi: set sw=4 ts=4:
+ *
+ * df.c - report free disk space.
+ *
+ * Copyright 2006 Rob Landley <rob@landley.net>
+ *
+ * See http://www.opengroup.org/onlinepubs/009695399/utilities/df.html
+
+USE_DF(NEWTOY(df, "Pkt*a", TOYFLAG_USR|TOYFLAG_SBIN))
+
+config DF
+ bool "df (disk free)"
+ default y
+ help
+ usage: df [-t type] [FILESYSTEM ...]
+
+ The "disk free" command, df shows total/used/available disk space for
+ each filesystem listed on the command line, or all currently mounted
+ filesystems.
+
+ -t type
+ Display only filesystems of this type.
+
+config DF_PEDANTIC
+ bool "options -P and -k"
+ default y
+ depends on DF
+ help
+ usage: df [-Pk]
+
+ -P The SUSv3 "Pedantic" option
+
+ Provides a slightly less useful output format dictated by
+ the Single Unix Specification version 3, and sets the
+ units to 512 bytes instead of the default 1024 bytes.
+
+ -k Sets units back to 1024 bytes (the default without -P)
+*/
+
+#include "toys.h"
+
+DEFINE_GLOBALS(
+ struct arg_list *fstype;
+
+ long units;
+)
+
+#define TT this.df
+
+static void show_mt(struct mtab_list *mt)
+{
+ int len;
+ long size, used, avail, percent;
+ uint64_t block;
+
+ // Return if it wasn't found (should never happen, but with /etc/mtab...)
+ if (!mt) return;
+
+ // If we have -t, skip other filesystem types
+ if (TT.fstype) {
+ struct arg_list *al;
+
+ for (al = TT.fstype; al; al = al->next) {
+ if (!strcmp(mt->type, al->arg)) break;
+ }
+ if (!al) return;
+ }
+
+ // If we don't have -a, skip synthetic filesystems
+ if (!(toys.optflags & 1) && !mt->statvfs.f_blocks) return;
+
+ // Figure out how much total/used/free space this filesystem has,
+ // forcing 64-bit math because filesystems are big now.
+ block = mt->statvfs.f_bsize ? mt->statvfs.f_bsize : 1;
+ size = (long)((block * mt->statvfs.f_blocks) / TT.units);
+ used = (long)((block * (mt->statvfs.f_blocks-mt->statvfs.f_bfree))
+ / TT.units);
+ avail = (long)((block
+ * (getuid() ? mt->statvfs.f_bavail : mt->statvfs.f_bfree))
+ / TT.units);
+ percent = size ? 100-(long)((100*(uint64_t)avail)/size) : 0;
+
+ // Figure out appropriate spacing
+ len = 25 - strlen(mt->device);
+ if (len < 1) len = 1;
+ if (CFG_DF_PEDANTIC && (toys.optflags & 8)) {
+ printf("%s %ld %ld %ld %ld%% %s\n", mt->device, size, used, avail,
+ percent, mt->dir);
+ } else {
+ printf("%s% *ld % 10ld % 9ld % 3ld%% %s\n",mt->device, len,
+ size, used, avail, percent, mt->dir);
+ }
+}
+
+void df_main(void)
+{
+ struct mtab_list *mt, *mt2, *mtlist;
+
+ // Handle -P and -k
+ TT.units = 1024;
+ if (CFG_DF_PEDANTIC && (toys.optflags & 8)) {
+ // Units are 512 bytes if you select "pedantic" without "kilobytes".
+ if ((toys.optflags&3) == 1) TT.units = 512;
+ printf("Filesystem %ld-blocks Used Available Capacity Mounted on\n",
+ TT.units);
+ } else puts("Filesystem\t1K-blocks\tUsed Available Use% Mounted on");
+
+ mtlist = getmountlist(1);
+
+ // If we have a list of filesystems on the command line, loop through them.
+ if (*toys.optargs) {
+ char **next;
+
+ for(next = toys.optargs; *next; next++) {
+ struct stat st;
+
+ // Stat it (complain if we can't).
+ if(stat(*next, &st)) {
+ perror_msg("`%s'", *next);
+ toys.exitval = 1;
+ continue;
+ }
+
+ // Find and display this filesystem. Use _last_ hit in case of
+ // -- bind mounts.
+ mt2 = NULL;
+ for (mt = mtlist; mt; mt = mt->next)
+ if (st.st_dev == mt->stat.st_dev) mt2 = mt;
+ show_mt(mt2);
+ }
+ } else {
+ // Get and loop through mount list.
+
+ for (mt = mtlist; mt; mt = mt->next) {
+ struct mtab_list *mt2, *mt3;
+
+ if (!mt->stat.st_dev) continue;
+
+ // Filter out overmounts.
+ mt3 = mt;
+ for (mt2 = mt->next; mt2; mt2 = mt2->next) {
+ if (mt->stat.st_dev == mt2->stat.st_dev) {
+ // For --bind mounts, take last match
+ if (!strcmp(mt->device, mt2->device)) mt3 = mt2;
+ // Filter out overmounts
+ mt2->stat.st_dev = 0;
+ }
+ }
+ show_mt(mt3);
+ }
+ }
+
+ if (CFG_TOYBOX_FREE) llist_traverse(mtlist, free);
+}
diff --git a/toys/posix/dirname.c b/toys/posix/dirname.c
new file mode 100644
index 00000000..5dc60181
--- /dev/null
+++ b/toys/posix/dirname.c
@@ -0,0 +1,25 @@
+/* vi: set sw=4 ts=4:
+ *
+ * dirname.c - show directory portion of path
+ *
+ * Copyright 2011 Rob Landley <rob@landley.net>
+ *
+ * See http://opengroup.org/onlinepubs/9699919799/utilities/dirname.html
+
+USE_DIRNAME(NEWTOY(dirname, "<1", TOYFLAG_USR|TOYFLAG_BIN))
+
+config DIRNAME
+ bool "dirname"
+ default y
+ help
+ usage: dirname PATH
+
+ Show directory portion of path.
+*/
+
+#include "toys.h"
+
+void dirname_main(void)
+{
+ puts(dirname(*toys.optargs));
+}
diff --git a/toys/posix/echo.c b/toys/posix/echo.c
new file mode 100644
index 00000000..6fb9e43d
--- /dev/null
+++ b/toys/posix/echo.c
@@ -0,0 +1,97 @@
+/* vi: set sw=4 ts=4:
+ *
+ * echo.c - echo supporting -n and -e.
+ *
+ * Copyright 2007 Rob Landley <rob@landley.net>
+ *
+ * See http://www.opengroup.org/onlinepubs/009695399/utilities/echo.html
+
+USE_ECHO(NEWTOY(echo, "^?en", TOYFLAG_BIN))
+
+config ECHO
+ bool "echo"
+ default y
+ help
+ usage: echo [-ne] [args...]
+
+ Write each argument to stdout, with one space between each, followed
+ by a newline.
+
+ -n No trailing newline.
+ -e Process the following escape sequences:
+ \\ backslash
+ \0NNN octal values (1 to 3 digits)
+ \a alert (beep/flash)
+ \b backspace
+ \c stop output here (avoids trailing newline)
+ \f form feed
+ \n newline
+ \r carriage return
+ \t horizontal tab
+ \v vertical tab
+ \xHH hexadecimal values (1 to 2 digits)
+*/
+
+#define THIS echo
+#include "toys.h"
+
+#define FLAG_e (1<<1)
+#define FLAG_n (1<<0)
+
+void echo_main(void)
+{
+ int i = 0, out;
+ char *arg, *from = "\\abfnrtv", *to = "\\\a\b\f\n\r\t\v", *c;
+
+ for (;;) {
+ arg = toys.optargs[i];
+ if (!arg) break;
+ if (i++) xputc(' ');
+
+ // Should we output arg verbatim?
+
+ if (!(toys.optflags&FLAG_e)) {
+ xprintf("%s", arg);
+ continue;
+ }
+
+ // Handle -e
+
+ for (c=arg;;) {
+ if (!(out = *(c++))) break;
+
+ // handle \escapes
+ if (out == '\\' && *c) {
+ int n = 0, slash = *(c++);
+ char *found = strchr(from, slash);
+ if (found) out = to[found-from];
+ else if (slash == 'c') goto done;
+ else if (slash == '0') {
+ out = 0;
+ while (*c>='0' && *c<='7' && n++<3)
+ out = (out*8)+*(c++)-'0';
+ } else if (slash == 'x') {
+ out = 0;
+ while (n++<2) {
+ if (*c>='0' && *c<='9')
+ out = (out*16)+*(c++)-'0';
+ else {
+ int temp = tolower(*c);
+ if (temp>='a' && temp<='f') {
+ out = (out*16)+temp-'a'+10;
+ c++;
+ } else break;
+ }
+ }
+ // Slash in front of unknown character, print literal.
+ } else c--;
+ }
+ xputc(out);
+ }
+ }
+
+ // Output "\n" if no -n
+ if (!(toys.optflags&FLAG_n)) xputc('\n');
+done:
+ xflush();
+}
diff --git a/toys/posix/env.c b/toys/posix/env.c
new file mode 100644
index 00000000..7cda85f9
--- /dev/null
+++ b/toys/posix/env.c
@@ -0,0 +1,52 @@
+/* vi: set sw=4 ts=4:
+ *
+ * env.c - Set the environment for command invocation.
+ *
+ * Copyright 2012 Tryn Mirell <tryn@mirell.org>
+ * env.c
+
+USE_ENV(NEWTOY(env, "^i", TOYFLAG_USR|TOYFLAG_BIN))
+
+config ENV
+ bool "env"
+ default y
+ help
+ usage: env [-i] [NAME=VALUE...] [command [option...]]
+
+ Set the environment for command invocation.
+
+ -i Clear existing environment.
+*/
+
+#include "toys.h"
+
+extern char **environ;
+
+void env_main(void)
+{
+ char **ev;
+ char **command = NULL;
+ char *del = "=";
+
+ if (toys.optflags & 1) clearenv();
+
+ for (ev = toys.optargs; *ev != NULL; ev++) {
+ char *env, *val = NULL;
+
+ env = strtok(*ev, del);
+
+ if (env) val = strtok(NULL, del);
+
+ if (val) setenv(env, val, 1);
+ else {
+ command = ev;
+ break;
+ }
+ }
+
+ if (!command) {
+ char **ep;
+ for (ep = environ; *ep; ep++) xputs(*ep);
+ return;
+ } else xexec(command);
+}
diff --git a/toys/posix/false.c b/toys/posix/false.c
new file mode 100644
index 00000000..7b570bd2
--- /dev/null
+++ b/toys/posix/false.c
@@ -0,0 +1,23 @@
+/* vi: set sw=4 ts=4:
+ *
+ * false.c - Return nonzero.
+ *
+ * Copyright 2007 Rob Landley <rob@landley.net>
+ *
+ * See http://www.opengroup.org/onlinepubs/009695399/utilities/false.html
+
+USE_FALSE(NEWTOY(false, NULL, TOYFLAG_BIN))
+
+config FALSE
+ bool "false"
+ default y
+ help
+ Return nonzero.
+*/
+
+#include "toys.h"
+
+void false_main(void)
+{
+ toys.exitval = 1;
+}
diff --git a/toys/posix/head.c b/toys/posix/head.c
new file mode 100644
index 00000000..1d1e54a3
--- /dev/null
+++ b/toys/posix/head.c
@@ -0,0 +1,61 @@
+/* vi: set sw=4 ts=4:
+ *
+ * head.c - copy first lines from input to stdout.
+ *
+ * Copyright 2006 Timothy Elliott <tle@holymonkey.com>
+ *
+ * See http://www.opengroup.org/onlinepubs/009695399/utilities/head.html
+
+USE_HEAD(NEWTOY(head, "n#<0=10", TOYFLAG_BIN))
+
+config HEAD
+ bool "head"
+ default y
+ help
+ usage: head [-n number] [file...]
+
+ Copy first lines from files to stdout. If no files listed, copy from
+ stdin. Filename "-" is a synonym for stdin.
+
+ -n Number of lines to copy.
+*/
+
+#include "toys.h"
+
+DEFINE_GLOBALS(
+ long lines;
+ int file_no;
+)
+
+#define TT this.head
+
+static void do_head(int fd, char *name)
+{
+ int i, len, lines=TT.lines, size=sizeof(toybuf);
+
+ if (toys.optc > 1) {
+ // Print an extra newline for all but the first file
+ if (TT.file_no++) xprintf("\n");
+ xprintf("==> %s <==\n", name);
+ xflush();
+ }
+
+ while (lines) {
+ len = read(fd, toybuf, size);
+ if (len<0) {
+ perror_msg("%s",name);
+ toys.exitval = EXIT_FAILURE;
+ }
+ if (len<1) break;
+
+ for(i=0; i<len;)
+ if (toybuf[i++] == '\n' && !--lines) break;
+
+ xwrite(1, toybuf, i);
+ }
+}
+
+void head_main(void)
+{
+ loopfiles(toys.optargs, do_head);
+}
diff --git a/toys/posix/id.c b/toys/posix/id.c
new file mode 100644
index 00000000..973cda5a
--- /dev/null
+++ b/toys/posix/id.c
@@ -0,0 +1,119 @@
+/* vi: set sw=4 ts=4:
+ *
+ * id.c - print real and effective user and group IDs
+ *
+ * Copyright 2012 Sony Network Entertainment, Inc.
+ *
+ * by Tim Bird <tim.bird@am.sony.com>
+ *
+ * See http://www.opengroup.org/onlinepubs/009695399/utilities/id.html
+
+USE_ID(NEWTOY(id, "nGgru", TOYFLAG_BIN))
+
+config ID
+ bool "id"
+ default y
+ help
+ usage: id [-nGgru]
+
+ Print user and group ID.
+
+ -n print names instead of numeric IDs (to be used with -Ggu)
+ -G Show only the group IDs
+ -g Show only the effective group ID
+ -r Show real ID instead of effective ID
+ -u Show only the effective user ID
+*/
+
+#include "toys.h"
+
+#define FLAG_n (1<<4)
+#define FLAG_G (1<<3)
+#define FLAG_g (1<<2)
+#define FLAG_r (1<<1)
+#define FLAG_u 1
+
+static void s_or_u(char *s, unsigned u, int done)
+{
+ if (toys.optflags & FLAG_n) printf("%s", s);
+ else printf("%u", u);
+ if (done) {
+ xputc('\n');
+ exit(0);
+ }
+}
+
+static void showid(char *header, unsigned u, char *s)
+{
+ printf("%s%u(%s)", header, u, s);
+}
+
+struct passwd *xgetpwuid(uid_t uid)
+{
+ struct passwd *pwd = getpwuid(uid);
+ if (!pwd) error_exit(NULL);
+ return pwd;
+}
+
+struct group *xgetgrgid(gid_t gid)
+{
+ struct group *group = getgrgid(gid);
+ if (!group) error_exit(NULL);
+ return group;
+}
+
+void id_main(void)
+{
+ int flags = toys.optflags, i, ngroups;
+ struct passwd *pw;
+ struct group *grp;
+ uid_t uid = getuid(), euid = geteuid();
+ gid_t gid = getgid(), egid = getegid(), *groups;
+
+ /* check if a username is given */
+ if (*toys.optargs) {
+ if (!(pw = getpwnam(*toys.optargs)))
+ error_exit("no such user '%s'", *toys.optargs);
+ uid = euid = pw->pw_uid;
+ gid = egid = pw->pw_gid;
+ }
+
+ i = toys.optflags & FLAG_r;
+ pw = xgetpwuid(i ? uid : euid);
+ if (flags & FLAG_u) s_or_u(pw->pw_name, pw->pw_uid, 1);
+
+ grp = xgetgrgid(i ? gid : egid);
+ if (flags & FLAG_g) s_or_u(grp->gr_name, grp->gr_gid, 1);
+
+ if (!(flags & FLAG_G)) {
+ showid("uid=", pw->pw_uid, pw->pw_name);
+ showid(" gid=", grp->gr_gid, grp->gr_name);
+
+ if (!i) {
+ if (uid != euid) {
+ pw = xgetpwuid(euid);
+ showid(" euid=", pw->pw_uid, pw->pw_name);
+ }
+ if (gid != egid) {
+ grp = xgetgrgid(egid);
+ showid(" egid=", grp->gr_gid, grp->gr_name);
+ }
+ }
+
+ showid(" groups=", grp->gr_gid, grp->gr_name);
+ }
+
+
+ groups = (gid_t *)toybuf;
+ if (0 >= (ngroups = getgroups(sizeof(toybuf)/sizeof(gid_t), groups)))
+ perror_exit(0);
+
+ for (i = 0; i < ngroups; i++) {
+ xputc(' ');
+ if (!(grp = getgrgid(groups[i]))) perror_msg(0);
+ else if (flags & FLAG_G)
+ s_or_u(grp->gr_name, grp->gr_gid, 0);
+ else if (grp->gr_gid != egid) showid("", grp->gr_gid, grp->gr_name);
+ }
+ xputc('\n');
+}
diff --git a/toys/posix/kill.c b/toys/posix/kill.c
new file mode 100644
index 00000000..e64a146b
--- /dev/null
+++ b/toys/posix/kill.c
@@ -0,0 +1,74 @@
+/* vi: set sw=4 ts=4:
+ *
+ * kill.c - a program to send signals to processes
+ *
+ * Copyright 2012 Daniel Walter <d.walter@0x90.at>
+ *
+ * See http://opengroup.org/onlinepubs/9699919799/utilities/kill.html
+
+USE_KILL(NEWTOY(kill, "?s: l", TOYFLAG_BIN))
+
+config KILL
+ bool "kill"
+ default y
+ help
+ usage: kill [-l [SIGNAL] | -s SIGNAL | -SIGNAL] pid...
+
+ Send a signal to a process
+
+*/
+
+#include "toys.h"
+
+DEFINE_GLOBALS(
+ char *signame;
+)
+
+#define TT this.kill
+
+void kill_main(void)
+{
+ int signum;
+ char *tmp, **args = toys.optargs;
+ pid_t pid;
+
+ // list signal(s)
+ if (toys.optflags & 1) {
+ if (*args) {
+ int signum = sig_to_num(*args);
+ char *s = NULL;
+
+ if (signum>=0) s = num_to_sig(signum&127);
+ puts(s ? s : "UNKNOWN");
+ } else sig_to_num(NULL);
+ return;
+ }
+
+ // signal must come before pids, so "kill -9 -1" isn't confusing.
+
+ if (!TT.signame && *args && **args=='-') TT.signame=*(args++)+1;
+ if (TT.signame) {
+ char *arg;
+ int i = strtol(TT.signame, &arg, 10);
+ if (!*arg) arg = num_to_sig(i);
+ else arg = TT.signame;
+
+ if (!arg || -1 == (signum = sig_to_num(arg)))
+ error_exit("Unknown signal '%s'", arg);
+ } else signum = SIGTERM;
+
+ if (!*args) {
+ toys.exithelp++;
+ error_exit("missing argument");
+ }
+
+ while (*args) {
+ char *arg = *(args++);
+
+ pid = strtol(arg, &tmp, 10);
+ if (*tmp || kill(pid, signum) < 0) {
+ error_msg("unknown pid '%s'", arg);
+ toys.exitval = EXIT_FAILURE;
+ }
+ }
+}
diff --git a/toys/posix/link.c b/toys/posix/link.c
new file mode 100644
index 00000000..69a9a60f
--- /dev/null
+++ b/toys/posix/link.c
@@ -0,0 +1,27 @@
+/* vi: set sw=4 ts=4:
+ *
+ * link.c - hardlink a file
+ *
+ * Copyright 2011 Rob Landley <rob@landley.net>
+ *
+ * See http://opengroup.org/onlinepubs/9699919799/utilities/link.html
+
+USE_LINK(NEWTOY(link, "<2>2", TOYFLAG_USR|TOYFLAG_BIN))
+
+config LINK
+ bool "link"
+ default y
+ help
+ usage: link FILE NEWLINK
+
+ Create hardlink to a file.
+*/
+
+#include "toys.h"
+
+void link_main(void)
+{
+ if (link(toys.optargs[0], toys.optargs[1]))
+ perror_exit("couldn't link '%s' to '%s'", toys.optargs[1],
+ toys.optargs[0]);
+}
diff --git a/toys/posix/ln.c b/toys/posix/ln.c
new file mode 100644
index 00000000..6f55971a
--- /dev/null
+++ b/toys/posix/ln.c
@@ -0,0 +1,70 @@
+/* vi: set sw=4 ts=4:
+ *
+ * ln.c - Create filesystem links
+ *
+ * Copyright 2012 Andre Renaud <andre@bluewatersys.com>
+ *
+ * See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/ln.html
+
+USE_LN(NEWTOY(ln, "<1nfs", TOYFLAG_BIN))
+
+config LN
+ bool "ln"
+ default y
+ help
+ usage: ln [-sf] [FROM...] TO
+
+ Create a link between FROM and TO.
+ With only one argument, create link in current directory.
+
+ -s Create a symbolic link
+ -f Force the creation of the link, even if TO already exists
+ -n Symlink at destination treated as file
+*/
+
+#include "toys.h"
+
+#define FLAG_s 1
+#define FLAG_f 2
+#define FLAG_n 4
+
+void ln_main(void)
+{
+ char *dest = toys.optargs[--toys.optc], *new;
+ struct stat buf;
+ int i;
+
+ // With one argument, create link in current directory.
+ if (!toys.optc) {
+ toys.optc++;
+ dest=".";
+ }
+
+ // Is destination a directory?
+ if (((toys.optflags&FLAG_n) ? lstat : stat)(dest, &buf)
+ || !S_ISDIR(buf.st_mode))
+ {
+ if (toys.optc>1) error_exit("'%s' not a directory");
+ buf.st_mode = 0;
+ }
+
+ for (i=0; i<toys.optc; i++) {
+ int rc;
+ char *try = toys.optargs[i];
+
+ if (S_ISDIR(buf.st_mode)) {
+ new = strrchr(try, '/');
+ if (!new) new = try;
+ new = xmsprintf("%s/%s", dest, new);
+ } else new = dest;
+ /* Silently unlink the existing target. If it doesn't exist,
+ * then we just move on */
+ if (toys.optflags & FLAG_f) unlink(new);
+
+ rc = (toys.optflags & FLAG_s) ? symlink(try, new) : link(try, new);
+ if (rc)
+ perror_exit("cannot create %s link from '%s' to '%s'",
+ (toys.optflags & FLAG_s) ? "symbolic" : "hard", try, new);
+ if (new != dest) free(new);
+ }
+}
diff --git a/toys/posix/logname.c b/toys/posix/logname.c
new file mode 100644
index 00000000..b93e3366
--- /dev/null
+++ b/toys/posix/logname.c
@@ -0,0 +1,29 @@
+/* vi: set sw=4 ts=4:
+ *
+ * logname.c - Print user's login name.
+ *
+ * Copyright 2012 Elie De Brauwer <eliedebrauwer@gmail.com>
+ *
+ * See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/logname.html
+
+USE_LOGNAME(NEWTOY(logname, ">0", TOYFLAG_BIN))
+
+config LOGNAME
+ bool "logname"
+ default y
+ help
+ usage: logname
+
+ Prints the calling user's name or an error when this cannot be
+ determined.
+*/
+
+#include "toys.h"
+
+void logname_main(void)
+{
+ if (getlogin_r(toybuf, sizeof(toybuf))){
+ error_exit("no login name");
+ }
+ xputs(toybuf);
+}
diff --git a/toys/posix/ls.c b/toys/posix/ls.c
new file mode 100644
index 00000000..1881df71
--- /dev/null
+++ b/toys/posix/ls.c
@@ -0,0 +1,493 @@
+/* vi: set sw=4 ts=4:
+ *
+ * ls.c - list files
+ *
+ * Copyright 2012 Andre Renaud <andre@bluewatersys.com>
+ * Copyright 2012 Rob Landley <rob@landley.net>
+ *
+ * See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/ls.html
+
+// "[-Cl]"
+USE_LS(NEWTOY(ls, "goACFHLRSacdfiklmnpqrstux1", TOYFLAG_BIN))
+
+config LS
+ bool "ls"
+ default y
+ help
+ usage: ls [-ACFHLRSacdfiklmnpqrstux1] [directory...]
+ list files
+
+ what to show:
+ -a all files including .hidden
+ -c use ctime for timestamps
+ -d directory, not contents
+ -i inode number
+ -k block sizes in kilobytes
+ -p put a '/' after directory names
+ -q unprintable chars as '?'
+ -s size (in blocks)
+ -u use access time for timestamps
+ -A list all files except . and ..
+ -H follow command line symlinks
+ -L follow symlinks
+ -R recursively list files in subdirectories
+ -F append file type indicator (/=dir, *=exe, @=symlink, |=FIFO)
+
+ output formats:
+ -1 list one file per line
+ -C columns (sorted vertically)
+ -g like -l but no owner
+ -l long (show full details for each file)
+ -m comma separated
+ -n like -l but numeric uid/gid
+ -o like -l but no group
+ -x columns (sorted horizontally)
+
+ sorting (default is alphabetical):
+ -f unsorted
+ -r reverse
+ -t timestamp
+ -S size
+*/
+
+#include "toys.h"
+
+#define FLAG_1 (1<<0)
+#define FLAG_x (1<<1)
+#define FLAG_u (1<<2)
+#define FLAG_t (1<<3)
+#define FLAG_s (1<<4)
+#define FLAG_r (1<<5)
+#define FLAG_q (1<<6)
+#define FLAG_p (1<<7)
+#define FLAG_n (1<<8)
+#define FLAG_m (1<<9)
+#define FLAG_l (1<<10)
+#define FLAG_k (1<<11)
+#define FLAG_i (1<<12)
+#define FLAG_f (1<<13)
+#define FLAG_d (1<<14)
+#define FLAG_c (1<<15)
+#define FLAG_a (1<<16)
+#define FLAG_S (1<<17)
+#define FLAG_R (1<<18)
+#define FLAG_L (1<<19)
+#define FLAG_H (1<<20)
+#define FLAG_F (1<<21)
+#define FLAG_C (1<<22)
+#define FLAG_A (1<<23)
+#define FLAG_o (1<<24)
+#define FLAG_g (1<<25)
+
+// test sst output (suid/sticky in ls flaglist)
+
+// ls -lR starts .: then ./subdir:
+
+DEFINE_GLOBALS(
+ struct dirtree *files;
+
+ unsigned screen_width;
+ int nl_title;
+
+ // group and user can make overlapping use of the utoa() buf, so move it
+ char uid_buf[12];
+)
+
+#define TT this.ls
+
+void dlist_to_dirtree(struct dirtree *parent)
+{
+ // Turn double_list into dirtree
+ struct dirtree *dt = parent->child;
+ if (dt) {
+ dt->parent->next = NULL;
+ while (dt) {
+ dt->parent = parent;
+ dt = dt->next;
+ }
+ }
+}
+
+static char endtype(struct stat *st)
+{
+ mode_t mode = st->st_mode;
+ if ((toys.optflags&(FLAG_F|FLAG_p)) && S_ISDIR(mode)) return '/';
+ if (toys.optflags & FLAG_F) {
+ if (S_ISLNK(mode)) return '@';
+ if (S_ISREG(mode) && (mode&0111)) return '*';
+ if (S_ISFIFO(mode)) return '|';
+ if (S_ISSOCK(mode)) return '=';
+ }
+ return 0;
+}
+
+static char *getusername(uid_t uid)
+{
+ struct passwd *pw = getpwuid(uid);
+ utoa_to_buf(uid, TT.uid_buf, 12);
+ return pw ? pw->pw_name : TT.uid_buf;
+}
+
+static char *getgroupname(gid_t gid)
+{
+ struct group *gr = getgrgid(gid);
+ return gr ? gr->gr_name : utoa(gid);
+}
+
+// Figure out size of printable entry fields for display indent/wrap
+
+static void entrylen(struct dirtree *dt, unsigned *len)
+{
+ struct stat *st = &(dt->st);
+ unsigned flags = toys.optflags;
+
+ *len = strlen(dt->name);
+ if (endtype(st)) ++*len;
+ if (flags & FLAG_m) ++*len;
+
+ if (flags & FLAG_i) *len += (len[1] = numlen(st->st_ino));
+ if (flags & (FLAG_l|FLAG_o|FLAG_n|FLAG_g)) {
+ unsigned fn = flags & FLAG_n;
+ len[2] = numlen(st->st_nlink);
+ len[3] = strlen(fn ? utoa(st->st_uid) : getusername(st->st_uid));
+ len[4] = strlen(fn ? utoa(st->st_gid) : getgroupname(st->st_gid));
+ len[5] = numlen(st->st_size);
+ }
+ if (flags & FLAG_s) *len += (len[6] = numlen(st->st_blocks));
+}
+
+static int compare(void *a, void *b)
+{
+ struct dirtree *dta = *(struct dirtree **)a;
+ struct dirtree *dtb = *(struct dirtree **)b;
+ int ret = 0, reverse = (toys.optflags & FLAG_r) ? -1 : 1;
+
+ if (toys.optflags & FLAG_S) {
+ if (dta->st.st_size > dtb->st.st_size) ret = -1;
+ else if (dta->st.st_size < dtb->st.st_size) ret = 1;
+ }
+ if (toys.optflags & FLAG_t) {
+ if (dta->st.st_mtime > dtb->st.st_mtime) ret = -1;
+ else if (dta->st.st_mtime < dtb->st.st_mtime) ret = 1;
+ }
+ if (!ret) ret = strcmp(dta->name, dtb->name);
+ return ret * reverse;
+}
+
+// callback from dirtree_recurse() determining how to handle this entry.
+
+static int filter(struct dirtree *new)
+{
+ int flags = toys.optflags;
+
+ // Special case to handle enormous dirs without running out of memory.
+ if (flags == (FLAG_1|FLAG_f)) {
+ xprintf("%s\n", new->name);
+ return 0;
+ }
+
+ if (!(flags&FLAG_f)) {
+ if (flags & FLAG_a) return 0;
+ if (!(flags & FLAG_A) && new->name[0]=='.') return 0;
+ }
+
+ if (flags & FLAG_u) new->st.st_mtime = new->st.st_atime;
+ if (flags & FLAG_c) new->st.st_mtime = new->st.st_ctime;
+ if (flags & FLAG_k) new->st.st_blocks = (new->st.st_blocks + 1) / 2;
+ return dirtree_notdotdot(new);
+}
+
+// For column view, calculate horizontal position (for padding) and return
+// index of next entry to display.
+
+static unsigned long next_column(unsigned long ul, unsigned long dtlen,
+ unsigned columns, unsigned *xpos)
+{
+ unsigned long transition;
+ unsigned height, widecols;
+
+ // Horizontal sort is easy
+ if (!(toys.optflags & FLAG_C)) {
+ *xpos = ul % columns;
+ return ul;
+ }
+
+ // vertical sort
+
+ // For -x, calculate height of display, rounded up
+ height = (dtlen+columns-1)/columns;
+
+ // Sanity check: does wrapping render this column count impossible
+ // due to the right edge wrapping eating a whole row?
+ if (height*columns - dtlen >= height) {
+ *xpos = columns;
+ return 0;
+ }
+
+ // Uneven rounding goes along right edge
+ widecols = dtlen % height;
+ if (!widecols) widecols = height;
+ transition = widecols * columns;
+ if (ul < transition) {
+ *xpos = ul % columns;
+ return (*xpos*height) + (ul/columns);
+ }
+
+ ul -= transition;
+ *xpos = ul % (columns-1);
+
+ return (*xpos*height) + widecols + (ul/(columns-1));
+}
+
+// Display a list of dirtree entries, according to current format
+// Output types -1, -l, -C, or stream
+
+static void listfiles(int dirfd, struct dirtree *indir)
+{
+ struct dirtree *dt, **sort = 0;
+ unsigned long dtlen = 0, ul = 0;
+ unsigned width, flags = toys.optflags, totals[7], len[7],
+ *colsizes = (unsigned *)(toybuf+260), columns = (sizeof(toybuf)-260)/4;
+
+ memset(totals, 0, sizeof(totals));
+
+ // Silently descend into single directory listed by itself on command line.
+ // In this case only show dirname/total header when given -R.
+ if (!indir->parent) {
+ if (!(dt = indir->child)) return;
+ if (S_ISDIR(dt->st.st_mode) && !dt->next && !(flags & FLAG_d)) {
+ dt->extra = 1;
+ listfiles(open(dt->name, 0), dt);
+ return;
+ }
+ } else {
+ // Read directory contents. We dup() the fd because this will close it.
+ indir->data = dup(dirfd);
+ dirtree_recurse(indir, filter, (flags&FLAG_L));
+ }
+
+ // Copy linked list to array and sort it. Directories go in array because
+ // we visit them in sorted order.
+
+ for (;;) {
+ for (dt = indir->child; dt; dt = dt->next) {
+ if (sort) sort[dtlen] = dt;
+ dtlen++;
+ }
+ if (sort) break;
+ sort = xmalloc(dtlen * sizeof(void *));
+ dtlen = 0;
+ continue;
+ }
+
+ // Label directory if not top of tree, or if -R
+ if (indir->parent && (!indir->extra || (flags & FLAG_R)))
+ {
+ char *path = dirtree_path(indir, 0);
+
+ if (TT.nl_title++) xputc('\n');
+ xprintf("%s:\n", path);
+ free(path);
+ }
+
+ if (!(flags & FLAG_f)) qsort(sort, dtlen, sizeof(void *), (void *)compare);
+
+ // Find largest entry in each field for display alignment
+ if (flags & (FLAG_C|FLAG_x)) {
+
+ // columns can't be more than toybuf can hold, or more than files,
+ // or > 1/2 screen width (one char filename, one space).
+ if (columns > TT.screen_width/2) columns = TT.screen_width/2;
+ if (columns > dtlen) columns = dtlen;
+
+ // Try to fit as many columns as we can, dropping down by one each time
+ for (;columns > 1; columns--) {
+ unsigned c, totlen = columns;
+
+ memset(colsizes, 0, columns*sizeof(unsigned));
+ for (ul=0; ul<dtlen; ul++) {
+ entrylen(sort[next_column(ul, dtlen, columns, &c)], len);
+ if (c == columns) break;
+ // Does this put us over budget?
+ if (*len > colsizes[c]) {
+ totlen += *len-colsizes[c];
+ colsizes[c] = *len;
+ if (totlen > TT.screen_width) break;
+ }
+ }
+ // If it fit, stop here
+ if (ul == dtlen) break;
+ }
+ } else if (flags & (FLAG_l|FLAG_o|FLAG_n|FLAG_g|FLAG_s)) {
+ unsigned long blocks = 0;
+
+ for (ul = 0; ul<dtlen; ul++)
+ {
+ entrylen(sort[ul], len);
+ for (width=0; width<6; width++)
+ if (len[width] > totals[width]) totals[width] = len[width];
+ blocks += sort[ul]->st.st_blocks;
+ }
+
+ if (indir->parent) xprintf("total %lu\n", blocks);
+ }
+
+ // Loop through again to produce output.
+ memset(toybuf, ' ', 256);
+ width = 0;
+ for (ul = 0; ul<dtlen; ul++) {
+ unsigned curcol;
+ unsigned long next = next_column(ul, dtlen, columns, &curcol);
+ struct stat *st = &(sort[next]->st);
+ mode_t mode = st->st_mode;
+ char et = endtype(st);
+
+ // Skip directories at the top of the tree when -d isn't set
+ if (S_ISDIR(mode) && !indir->parent && !(flags & FLAG_d)) continue;
+ TT.nl_title=1;
+
+ // Handle padding and wrapping for display purposes
+ entrylen(sort[next], len);
+ if (ul) {
+ if (flags & FLAG_m) xputc(',');
+ if (flags & (FLAG_C|FLAG_x)) {
+ if (!curcol) xputc('\n');
+ } else if ((flags & FLAG_1) || width+1+*len > TT.screen_width) {
+ xputc('\n');
+ width = 0;
+ } else {
+ xputc(' ');
+ width++;
+ }
+ }
+ width += *len;
+
+ if (flags & FLAG_i)
+ xprintf("% *lu ", len[1], (unsigned long)st->st_ino);
+ if (flags & FLAG_s)
+ xprintf("% *lu ", len[6], (unsigned long)st->st_blocks);
+
+ if (flags & (FLAG_l|FLAG_o|FLAG_n|FLAG_g)) {
+ struct tm *tm;
+ char perm[11], thyme[64], c, d, *usr, *upad, *grp, *grpad;
+ int i, bit;
+
+ perm[10]=0;
+ for (i=0; i<9; i++) {
+ bit = mode & (1<<i);
+ c = i%3;
+ if (!c && (mode & (1<<((d=i/3)+9)))) {
+ c = "tss"[d];
+ if (!bit) c &= ~0x20;
+ } else c = bit ? "xwr"[c] : '-';
+ perm[9-i] = c;
+ }
+
+ if (S_ISDIR(mode)) c = 'd';
+ else if (S_ISBLK(mode)) c = 'b';
+ else if (S_ISCHR(mode)) c = 'c';
+ else if (S_ISLNK(mode)) c = 'l';
+ else if (S_ISFIFO(mode)) c = 'p';
+ else if (S_ISSOCK(mode)) c = 's';
+ else c = '-';
+ *perm = c;
+
+ tm = localtime(&(st->st_mtime));
+ strftime(thyme, sizeof(thyme), "%F %H:%M", tm);
+
+ if (flags&FLAG_o) grp = grpad = toybuf+256;
+ else {
+ grp = (flags&FLAG_n) ? utoa(st->st_gid)
+ : getgroupname(st->st_gid);
+ grpad = toybuf+256-(totals[4]-len[4]);
+ }
+
+ if (flags&FLAG_g) usr = upad = toybuf+256;
+ else {
+ upad = toybuf+255-(totals[3]-len[3]);
+ if (flags&FLAG_n) {
+ usr = TT.uid_buf;
+ utoa_to_buf(st->st_uid, TT.uid_buf, 12);
+ } else usr = getusername(st->st_uid);
+ }
+
+ // Coerce the st types into something we know we can print.
+ xprintf("%s% *ld %s%s%s%s% *"PRId64" %s ", perm, totals[2]+1,
+ (long)st->st_nlink, usr, upad, grp, grpad, totals[5]+1,
+ (int64_t)st->st_size, thyme);
+ }
+
+ if (flags & FLAG_q) {
+ char *p;
+ for (p=sort[next]->name; *p; p++) xputc(isprint(*p) ? *p : '?');
+ } else xprintf("%s", sort[next]->name);
+ if ((flags & (FLAG_l|FLAG_o|FLAG_n|FLAG_g)) && S_ISLNK(mode))
+ xprintf(" -> %s", sort[next]->symlink);
+
+ if (et) xputc(et);
+
+ // Pad columns
+ if (flags & (FLAG_C|FLAG_x)) {
+ curcol = colsizes[curcol] - *len;
+ if (curcol >= 0) xprintf("%s", toybuf+255-curcol);
+ }
+ }
+
+ if (width) xputc('\n');
+
+ // Free directory entries, recursing first if necessary.
+
+ for (ul = 0; ul<dtlen; free(sort[ul++])) {
+ if ((flags & FLAG_d) || !S_ISDIR(sort[ul]->st.st_mode)
+ || !dirtree_notdotdot(sort[ul])) continue;
+
+ // Recurse into dirs if at top of the tree or given -R
+ if (!indir->parent || (flags & FLAG_R))
+ listfiles(openat(dirfd, sort[ul]->name, 0), sort[ul]);
+ }
+ free(sort);
+ if (dirfd != AT_FDCWD) close(indir->data);
+}
+
+void ls_main(void)
+{
+ char **s, *noargs[] = {".", 0};
+ struct dirtree *dt;
+
+ // Do we have an implied -1
+ if (!isatty(1) || (toys.optflags&(FLAG_l|FLAG_o|FLAG_n|FLAG_g)))
+ toys.optflags |= FLAG_1;
+ else {
+ TT.screen_width = 80;
+ terminal_size(&TT.screen_width, NULL);
+ }
+ // The optflags parsing infrastructure should really do this for us,
+ // but currently it has "switch off when this is set", so "-dR" and "-Rd"
+ // behave differently
+ if (toys.optflags & FLAG_d) toys.optflags &= ~FLAG_R;
+
+ // Iterate through command line arguments, collecting directories and files.
+ // Non-absolute paths are relative to current directory.
+ TT.files = dirtree_add_node(0, 0, 0);
+ for (s = *toys.optargs ? toys.optargs : noargs; *s; s++) {
+ dt = dirtree_add_node(AT_FDCWD, *s,
+ (toys.optflags & (FLAG_L|FLAG_H|FLAG_l))^FLAG_l);
+
+ if (!dt) {
+ toys.exitval = 1;
+ continue;
+ }
+
+ // Typecast means double_list->prev temporarirly goes in dirtree->parent
+ dlist_add_nomalloc((struct double_list **)&TT.files->child,
+ (struct double_list *)dt);
+ }
+
+ // Turn double_list into dirtree
+ dlist_to_dirtree(TT.files);
+
+ // Display the files we collected
+ listfiles(AT_FDCWD, TT.files);
+
+ if (CFG_TOYBOX_FREE) free(TT.files);
+}
diff --git a/toys/posix/mkdir.c b/toys/posix/mkdir.c
new file mode 100644
index 00000000..90329019
--- /dev/null
+++ b/toys/posix/mkdir.c
@@ -0,0 +1,76 @@
+/* vi: set sw=4 ts=4:
+ *
+ * mkdir.c - Make directories
+ *
+ * Copyright 2012 Georgi Chorbadzhiyski <georgi@unixsol.org>
+ *
+ * See http://pubs.opengroup.org/onlinepubs/009695399/utilities/mkdir.html
+ *
+ * TODO: Add -m
+
+USE_MKDIR(NEWTOY(mkdir, "<1p", TOYFLAG_BIN))
+
+config MKDIR
+ bool "mkdir"
+ default y
+ help
+ usage: mkdir [-p] [dirname...]
+ Create one or more directories.
+
+ -p make parent directories as needed.
+*/
+
+#include "toys.h"
+
+DEFINE_GLOBALS(
+ long mode;
+)
+
+#define TT this.mkdir
+
+static int do_mkdir(char *dir)
+{
+ struct stat buf;
+ char *s;
+
+ // mkdir -p one/two/three is not an error if the path already exists,
+ // but is if "three" is a file. The others we dereference and catch
+ // not-a-directory along the way, but the last one we must explicitly
+ // test for. Might as well do it up front.
+
+ if (!stat(dir, &buf) && !S_ISDIR(buf.st_mode)) {
+ errno = EEXIST;
+ return 1;
+ }
+
+ for (s=dir; ; s++) {
+ char save=0;
+
+ // Skip leading / of absolute paths.
+ if (s!=dir && *s == '/' && toys.optflags) {
+ save = *s;
+ *s = 0;
+ } else if (*s) continue;
+
+ if (mkdir(dir, TT.mode)<0 && (!toys.optflags || errno != EEXIST))
+ return 1;
+
+ if (!(*s = save)) break;
+ }
+
+ return 0;
+}
+
+void mkdir_main(void)
+{
+ char **s;
+
+ TT.mode = 0777;
+
+ for (s=toys.optargs; *s; s++) {
+ if (do_mkdir(*s)) {
+ perror_msg("cannot create directory '%s'", *s);
+ toys.exitval = 1;
+ }
+ }
+}
diff --git a/toys/posix/mkfifo.c b/toys/posix/mkfifo.c
new file mode 100644
index 00000000..ab466fc3
--- /dev/null
+++ b/toys/posix/mkfifo.c
@@ -0,0 +1,47 @@
+/* vi: set sw=4 ts=4:
+ *
+ * mkfifo.c - Create FIFOs (named pipes)
+ *
+ * Copyright 2012 Georgi Chorbadzhiyski <georgi@unixsol.org>
+ *
+ * See http://pubs.opengroup.org/onlinepubs/009695399/utilities/mkfifo.html
+ *
+ * TODO: Add -m
+
+USE_MKFIFO(NEWTOY(mkfifo, "<1m:", TOYFLAG_BIN))
+
+config MKFIFO
+ bool "mkfifo"
+ default y
+ help
+ usage: mkfifo [fifo_name...]
+
+ Create FIFOs (named pipes).
+*/
+
+#include "toys.h"
+
+DEFINE_GLOBALS(
+ char *m_string;
+ mode_t mode;
+)
+
+#define TT this.mkfifo
+#define FLAG_m (1)
+
+void mkfifo_main(void)
+{
+ char **s;
+
+ TT.mode = 0666;
+ if (toys.optflags & FLAG_m) {
+ TT.mode = string_to_mode(TT.m_string, 0);
+ }
+
+ for (s = toys.optargs; *s; s++) {
+ if (mknod(*s, S_IFIFO | TT.mode, 0) < 0) {
+ perror_msg("cannot create fifo '%s'", *s);
+ toys.exitval = 1;
+ }
+ }
+}
diff --git a/toys/posix/nice.c b/toys/posix/nice.c
new file mode 100644
index 00000000..84975dfc
--- /dev/null
+++ b/toys/posix/nice.c
@@ -0,0 +1,41 @@
+/* vi: set sw=4 ts=4:
+ *
+ * nice.c - Run a program at a different niceness level.
+ *
+ * Copyright 2010 Rob Landley <rob@landley.net>
+ *
+ * See http://www.opengroup.org/onlinepubs/9699919799/utilities/nice.html
+
+USE_NICE(NEWTOY(nice, "^<1n#", TOYFLAG_USR|TOYFLAG_BIN))
+
+config NICE
+ bool "nice"
+ default y
+ help
+ usage: nice [-n PRIORITY] command [args...]
+
+ Run a command line at an increased or decreased scheduling priority.
+
+ Higher numbers make a program yield more CPU time, from -20 (highest
+ priority) to 19 (lowest). By default processes inherit their parent's
+ niceness (usually 0). By default this command adds 10 to the parent's
+ priority. Only root can set a negative niceness level.
+*/
+
+#include "toys.h"
+
+DEFINE_GLOBALS(
+ long priority;
+)
+
+#define TT this.nice
+
+void nice_main(void)
+{
+ if (!toys.optflags) TT.priority = 10;
+
+ errno = 0;
+ if (nice(TT.priority)==-1 && errno) perror_exit("Can't set priority");
+
+ xexec(toys.optargs);
+}
diff --git a/toys/posix/nohup.c b/toys/posix/nohup.c
new file mode 100644
index 00000000..e11fb094
--- /dev/null
+++ b/toys/posix/nohup.c
@@ -0,0 +1,42 @@
+/* vi: set sw=4 ts=4:
+ *
+ * nohup.c - run commandline with SIGHUP blocked.
+ *
+ * Copyright 2011 Rob Landley <rob@landley.net>
+ *
+ * See http://opengroup.org/onlinepubs/9699919799/utilities/nohup.html
+
+USE_NOHUP(NEWTOY(nohup, "<1", TOYFLAG_USR|TOYFLAG_BIN))
+
+config NOHUP
+ bool "nohup"
+ default y
+ help
+ usage: nohup COMMAND [ARGS...]
+
+ Run a command that survives the end of its terminal.
+ If stdin is a tty, redirect from /dev/null
+ If stdout is a tty, redirect to file "nohup.out"
+*/
+
+#include "toys.h"
+
+void nohup_main(void)
+{
+ signal(SIGHUP, SIG_IGN);
+ if (isatty(1)) {
+ close(1);
+ if (-1 == open("nohup.out", O_CREAT|O_APPEND|O_WRONLY,
+ S_IRUSR|S_IWUSR ))
+ {
+ char *temp = getenv("HOME");
+ temp = xmsprintf("%s/%s", temp ? temp : "", "nohup.out");
+ xcreate(temp, O_CREAT|O_APPEND|O_WRONLY, S_IRUSR|S_IWUSR);
+ }
+ }
+ if (isatty(0)) {
+ close(0);
+ open("/dev/null", O_RDONLY);
+ }
+ xexec(toys.optargs);
+}
diff --git a/toys/posix/od.c b/toys/posix/od.c
new file mode 100644
index 00000000..dfde4440
--- /dev/null
+++ b/toys/posix/od.c
@@ -0,0 +1,274 @@
+/* vi: set sw=4 ts=4:
+ *
+ * od.c - Provide octal/hex dumps of data
+ *
+ * Copyright 2012 Andre Renaud <andre@bluewatersys.com>
+ * Copyright 2012 Rob Landley <rob@landley.net>
+ *
+ * See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/od.html
+
+USE_OD(NEWTOY(od, "j#vN#xsodcbA:t*", TOYFLAG_USR|TOYFLAG_BIN))
+
+config OD
+ bool "od"
+ default y
+ help
+ usage: od [-bdosxv] [-j #] [-N #] [-A doxn] [-t arg]
+
+ -A Address base (decimal, octal, hexdecimal, none)
+ -t output type(s) a (ascii) c (char) d (decimal) foux
+*/
+
+#include "toys.h"
+
+#define FLAG_t (1 << 0)
+#define FLAG_A (1 << 1)
+#define FLAG_b (1 << 2)
+#define FLAG_c (1 << 3)
+#define FLAG_d (1 << 4)
+#define FLAG_o (1 << 5)
+#define FLAG_s (1 << 6)
+#define FLAG_x (1 << 7)
+#define FLAG_N (1 << 8)
+#define FLAG_v (1 << 9)
+
+DEFINE_GLOBALS(
+ struct arg_list *output_base;
+ char *address_base;
+ long max_count;
+ long jump_bytes;
+
+ unsigned types, leftover, star, address_idx;
+ char *buf;
+ uint64_t bufs[4]; // force 64-bit alignment
+ off_t pos;
+)
+
+#define TT this.od
+
+static char *ascii = "nulsohstxetxeotenqackbel bs ht nl vt ff cr so si"
+ "dledc1dc2dc3dc4naksynetbcan emsubesc fs gs rs us sp";
+
+struct odtype {
+ int type;
+ int size;
+};
+
+static void od_outline(void)
+{
+ unsigned flags = toys.optflags;
+ char *abases[] = {"", "%07d", "%07o", "%06x"};
+ struct odtype *types = (struct odtype *)toybuf, *t;
+ int i, len;
+
+ if (TT.leftover<16) memset(TT.buf+TT.leftover, 0, 16-TT.leftover);
+
+ // Handle duplciate lines as *
+ if (!(flags&FLAG_v) && TT.jump_bytes != TT.pos && TT.leftover
+ && !memcmp(TT.bufs, TT.bufs + 2, 16))
+ {
+ if (!TT.star) {
+ xputs("*");
+ TT.star++;
+ }
+
+ // Print line position
+ } else {
+ TT.star = 0;
+
+ xprintf(abases[TT.address_idx], TT.pos);
+ if (!TT.leftover) {
+ if (TT.address_idx) xputc('\n');
+ return;
+ }
+ }
+
+ TT.pos += len = TT.leftover;
+ TT.leftover = 0;
+ if (TT.star) return;
+
+ // For each output type, print one line
+
+ for (i=0; i<TT.types; i++) {
+ int j = 0, pad = i ? 8 : 0;
+ char buf[128];
+
+ t = types+i;
+ while (j<len) {
+ unsigned k;
+ int throw = 0;
+
+ // Handle ascii
+ if (t->type < 2) {
+ char c = TT.buf[j++];
+ pad += 4;
+
+ if (!t->type) {
+ c &= 127;
+ if (c<=32) sprintf(buf, "%.3s", ascii+(3*c));
+ else if (c==127) strcpy(buf, "del");
+ else sprintf(buf, "%c", c);
+ } else {
+ char *bfnrtav = "\b\f\n\r\t\a\v", *s = strchr(bfnrtav, c);
+ if (s) sprintf(buf, "\\%c", "bfnrtav0"[s-bfnrtav]);
+ else if (c < 32 || c >= 127) sprintf(buf, "%03o", c);
+ else {
+ // TODO: this should be UTF8 aware.
+ sprintf(buf, "%c", c);
+ }
+ }
+ } else if (CFG_TOYBOX_FLOAT && t->type == 6) {
+ long double ld;
+ union {float f; double d; long double ld;} fdl;
+
+ memcpy(&fdl, TT.buf+j, t->size);
+ j += t->size;
+ if (sizeof(float) == t->size) {
+ ld = fdl.f;
+ pad += (throw = 8)+7;
+ } else if (sizeof(double) == t->size) {
+ ld = fdl.d;
+ pad += (throw = 17)+8;
+ } else if (sizeof(long double) == t->size) {
+ ld = fdl.ld;
+ pad += (throw = 21)+9;
+ } else error_exit("bad -tf '%d'", t->size);
+
+ sprintf(buf, "%.*Le", throw, ld);
+ // Integer types
+ } else {
+ unsigned long long ll = 0, or;
+ char *c[] = {"%*lld", "%*llu", "%0*llo", "%0*llx"},
+ *class = c[t->type-2];
+
+ // Work out width of field
+ if (t->size == 8) {
+ or = -1LL;
+ if (t->type == 2) or >>= 1;
+ } else or = (1LL<<(8*t->size))-1;
+ throw = sprintf(buf, class, 0, or);
+
+ // Accumulate integer based on size argument
+ for (k=0; k < t->size; k++) {
+ or = TT.buf[j++];
+ ll |= or << (8*(IS_BIG_ENDIAN ? t->size-k-1 : k));
+ }
+
+ // Handle negative values
+ if (t->type == 2) {
+ or = sizeof(or) - t->size;
+ throw++;
+ if (or && (ll & (1l<<((8*t->size)-1))))
+ ll |= ((or<<(8*or))-1) << (8*t->size);
+ }
+
+ sprintf(buf, class, throw, ll);
+ pad += throw+1;
+ }
+ xprintf("%*s", pad, buf);
+ pad = 0;
+ }
+ xputc('\n');
+ }
+
+ // buffer toggle for "same as last time" check.
+ TT.buf = (char *)((TT.buf == (char *)TT.bufs) ? TT.bufs+2 : TT.bufs);
+}
+
+static void do_od(int fd, char *name)
+{
+ // Skip input, possibly more than one entire file.
+ if (TT.jump_bytes < TT.pos) {
+ off_t off = lskip(fd, TT.jump_bytes);
+ if (off > 0) TT.pos += off;
+ if (TT.jump_bytes < TT.pos) return;
+ }
+
+ for(;;) {
+ char *buf = TT.buf + TT.leftover;
+ int len = 16 - TT.leftover;
+
+ if (toys.optflags & FLAG_N) {
+ if (!TT.max_count) break;
+ if (TT.max_count < len) len = TT.max_count;
+ }
+
+ len = readall(fd, buf, len);
+ if (len < 0) {
+ perror_msg("%s", name);
+ break;
+ }
+ if (TT.max_count) TT.max_count -= len;
+ TT.leftover += len;
+ if (TT.leftover < 16) break;
+
+ od_outline();
+ }
+}
+
+static void append_base(char *base)
+{
+ char *s = base;
+ struct odtype *types = (struct odtype *)toybuf;
+ int type;
+
+ for (;;) {
+ int size = 1;
+
+ if (!*s) return;
+ if (TT.types >= sizeof(toybuf)/sizeof(struct odtype)) break;
+ if (-1 == (type = stridx("acduox"USE_TOYBOX_FLOAT("f"), *(s++)))) break;
+
+ if (isdigit(*s)) {
+ size = strtol(s, &s, 10);
+ if (type < 2 && size != 1) break;
+ if (CFG_TOYBOX_FLOAT && type == 6 && size == sizeof(long double));
+ else if (size < 0 || size > 8) break;
+ } else if (CFG_TOYBOX_FLOAT && type == 6) {
+ int sizes[] = {sizeof(float), sizeof(double), sizeof(long double)};
+ if (-1 == (size = stridx("FDL", *s))) size = sizeof(double);
+ else {
+ s++;
+ size = sizes[size];
+ }
+ } else if (type > 1) {
+ if (-1 == (size = stridx("CSIL", *s))) size = 4;
+ else {
+ s++;
+ size = 1 << size;
+ }
+ }
+
+ types[TT.types].type = type;
+ types[TT.types].size = size;
+ TT.types++;
+ }
+
+ error_exit("bad -t %s", base);
+}
+
+void od_main(void)
+{
+ struct arg_list *arg;
+
+ TT.buf = (char *)TT.bufs;
+
+ if (!TT.address_base) TT.address_idx = 2;
+ else if (0>(TT.address_idx = stridx("ndox", *TT.address_base)))
+ error_exit("bad -A '%c'", *TT.address_base);
+
+ // Collect -t entries
+
+ for (arg = TT.output_base; arg; arg = arg->next) append_base(arg->arg);
+ if (toys.optflags & FLAG_b) append_base("o1");
+ if (toys.optflags & FLAG_d) append_base("u2");
+ if (toys.optflags & FLAG_o) append_base("o2");
+ if (toys.optflags & FLAG_s) append_base("d2");
+ if (toys.optflags & FLAG_x) append_base("x2");
+ if (!TT.output_base) append_base("o2");
+
+ loopfiles(toys.optargs, do_od);
+
+ if (TT.leftover) od_outline();
+ od_outline();
+}
diff --git a/toys/posix/patch.c b/toys/posix/patch.c
new file mode 100644
index 00000000..647bb18f
--- /dev/null
+++ b/toys/posix/patch.c
@@ -0,0 +1,412 @@
+/* vi: set sw=4 ts=4:
+ *
+ * patch.c - Apply a "universal" diff.
+ *
+ * Copyright 2007 Rob Landley <rob@landley.net>
+ *
+ * see http://www.opengroup.org/onlinepubs/009695399/utilities/patch.html
+ * (But only does -u, because who still cares about "ed"?)
+ *
+ * TODO:
+ * -b backup
+ * -l treat all whitespace as a single space
+ * -N ignore already applied
+ * -d chdir first
+ * -D define wrap #ifdef and #ifndef around changes
+ * -o outfile output here instead of in place
+ * -r rejectfile write rejected hunks to this file
+ *
+ * -E remove empty files --remove-empty-files
+ * -f force (no questions asked)
+ * -F fuzz (number, default 2)
+ * [file] which file to patch
+
+USE_PATCH(NEWTOY(patch, USE_TOYBOX_DEBUG("x")"up#i:R", TOYFLAG_USR|TOYFLAG_BIN))
+
+config PATCH
+ bool "patch"
+ default y
+ help
+ usage: patch [-i file] [-p depth] [-Ru]
+
+ Apply a unified diff to one or more files.
+
+ -i Input file (defaults=stdin)
+ -p number of '/' to strip from start of file paths (default=all)
+ -R Reverse patch.
+ -u Ignored (only handles "unified" diffs)
+
+ This version of patch only handles unified diffs, and only modifies
+ a file when all all hunks to that file apply. Patch prints failed
+ hunks to stderr, and exits with nonzero status if any hunks fail.
+
+ A file compared against /dev/null (or with a date <= the epoch) is
+ created/deleted as appropriate.
+*/
+
+#include "toys.h"
+
+DEFINE_GLOBALS(
+ char *infile;
+ long prefix;
+
+ struct double_list *current_hunk;
+ long oldline, oldlen, newline, newlen;
+ long linenum;
+ int context, state, filein, fileout, filepatch, hunknum;
+ char *tempname;
+)
+
+#define TT this.patch
+
+#define FLAG_REVERSE 1
+#define FLAG_PATHLEN 4
+
+// Dispose of a line of input, either by writing it out or discarding it.
+
+// state < 2: just free
+// state = 2: write whole line to stderr
+// state = 3: write whole line to fileout
+// state > 3: write line+1 to fileout when *line != state
+
+#define PATCH_DEBUG (CFG_TOYBOX_DEBUG && (toys.optflags & 16))
+
+static void do_line(void *data)
+{
+ struct double_list *dlist = (struct double_list *)data;
+
+ if (TT.state>1 && *dlist->data != TT.state) {
+ char *s = dlist->data+(TT.state>3 ? 1 : 0);
+ int i = TT.state == 2 ? 2 : TT.fileout;
+
+ xwrite(i, s, strlen(s));
+ xwrite(i, "\n", 1);
+ }
+
+ if (PATCH_DEBUG) fprintf(stderr, "DO %d: %s\n", TT.state, dlist->data);
+
+ free(dlist->data);
+ free(data);
+}
+
+static void finish_oldfile(void)
+{
+ if (TT.tempname) replace_tempfile(TT.filein, TT.fileout, &TT.tempname);
+ TT.fileout = TT.filein = -1;
+}
+
+static void fail_hunk(void)
+{
+ if (!TT.current_hunk) return;
+ TT.current_hunk->prev->next = 0;
+
+ fprintf(stderr, "Hunk %d FAILED %ld/%ld.\n",
+ TT.hunknum, TT.oldline, TT.newline);
+ toys.exitval = 1;
+
+ // If we got to this point, we've seeked to the end. Discard changes to
+ // this file and advance to next file.
+
+ TT.state = 2;
+ llist_traverse(TT.current_hunk, do_line);
+ TT.current_hunk = NULL;
+ delete_tempfile(TT.filein, TT.fileout, &TT.tempname);
+ TT.state = 0;
+}
+
+// Given a hunk of a unified diff, make the appropriate change to the file.
+// This does not use the location information, but instead treats a hunk
+// as a sort of regex. Copies data from input to output until it finds
+// the change to be made, then outputs the changed data and returns.
+// (Finding EOF first is an error.) This is a single pass operation, so
+// multiple hunks must occur in order in the file.
+
+static int apply_one_hunk(void)
+{
+ struct double_list *plist, *buf = NULL, *check;
+ int matcheof = 0, reverse = toys.optflags & FLAG_REVERSE, backwarn = 0;
+
+ // Break doubly linked list so we can use singly linked traversal function.
+ TT.current_hunk->prev->next = NULL;
+
+ // Match EOF if there aren't as many ending context lines as beginning
+ for (plist = TT.current_hunk; plist; plist = plist->next) {
+ if (plist->data[0]==' ') matcheof++;
+ else matcheof = 0;
+ if (PATCH_DEBUG) fprintf(stderr, "HUNK:%s\n", plist->data);
+ }
+ matcheof = matcheof < TT.context;
+
+ if (PATCH_DEBUG) fprintf(stderr,"MATCHEOF=%c\n", matcheof ? 'Y' : 'N');
+
+ // Loop through input data searching for this hunk. Match all context
+ // lines and all lines to be removed until we've found the end of a
+ // complete hunk.
+ plist = TT.current_hunk;
+ buf = NULL;
+ if (TT.context) for (;;) {
+ char *data = get_line(TT.filein);
+
+ TT.linenum++;
+
+ // Figure out which line of hunk to compare with next. (Skip lines
+ // of the hunk we'd be adding.)
+ while (plist && *plist->data == "+-"[reverse]) {
+ if (data && !strcmp(data, plist->data+1)) {
+ if (!backwarn) backwarn = TT.linenum;
+ }
+ plist = plist->next;
+ }
+
+ // Is this EOF?
+ if (!data) {
+ if (PATCH_DEBUG) fprintf(stderr, "INEOF\n");
+
+ // Does this hunk need to match EOF?
+ if (!plist && matcheof) break;
+
+ if (backwarn)
+ fprintf(stderr, "Possibly reversed hunk %d at %ld\n",
+ TT.hunknum, TT.linenum);
+
+ // File ended before we found a place for this hunk.
+ fail_hunk();
+ goto done;
+ } else if (PATCH_DEBUG) fprintf(stderr, "IN: %s\n", data);
+ check = dlist_add(&buf, data);
+
+ // Compare this line with next expected line of hunk.
+ // todo: teach the strcmp() to ignore whitespace.
+
+ // A match can fail because the next line doesn't match, or because
+ // we hit the end of a hunk that needed EOF, and this isn't EOF.
+
+ // If match failed, flush first line of buffered data and
+ // recheck buffered data for a new match until we find one or run
+ // out of buffer.
+
+ for (;;) {
+ if (!plist || strcmp(check->data, plist->data+1)) {
+ // Match failed. Write out first line of buffered data and
+ // recheck remaining buffered data for a new match.
+
+ if (PATCH_DEBUG)
+ fprintf(stderr, "NOT: %s\n", plist->data);
+
+ TT.state = 3;
+ check = llist_pop(&buf);
+ check->prev->next = buf;
+ buf->prev = check->prev;
+ do_line(check);
+ plist = TT.current_hunk;
+
+ // If we've reached the end of the buffer without confirming a
+ // match, read more lines.
+ if (check==buf) {
+ buf = 0;
+ break;
+ }
+ check = buf;
+ } else {
+ if (PATCH_DEBUG)
+ fprintf(stderr, "MAYBE: %s\n", plist->data);
+ // This line matches. Advance plist, detect successful match.
+ plist = plist->next;
+ if (!plist && !matcheof) goto out;
+ check = check->next;
+ if (check == buf) break;
+ }
+ }
+ }
+out:
+ // We have a match. Emit changed data.
+ TT.state = "-+"[reverse];
+ llist_traverse(TT.current_hunk, do_line);
+ TT.current_hunk = NULL;
+ TT.state = 1;
+done:
+ if (buf) {
+ buf->prev->next = NULL;
+ llist_traverse(buf, do_line);
+ }
+
+ return TT.state;
+}
+
+// Read a patch file and find hunks, opening/creating/deleting files.
+// Call apply_one_hunk() on each hunk.
+
+// state 0: Not in a hunk, look for +++.
+// state 1: Found +++ file indicator, look for @@
+// state 2: In hunk: counting initial context lines
+// state 3: In hunk: getting body
+
+void patch_main(void)
+{
+ int reverse = toys.optflags&FLAG_REVERSE, state = 0, patchlinenum = 0,
+ strip = 0;
+ char *oldname = NULL, *newname = NULL;
+
+ if (TT.infile) TT.filepatch = xopen(TT.infile, O_RDONLY);
+ TT.filein = TT.fileout = -1;
+
+ // Loop through the lines in the patch
+ for (;;) {
+ char *patchline;
+
+ patchline = get_line(TT.filepatch);
+ if (!patchline) break;
+
+ // Other versions of patch accept damaged patches,
+ // so we need to also.
+ if (strip || !patchlinenum++) {
+ int len = strlen(patchline);
+ if (patchline[len-1] == '\r') {
+ if (!strip) fprintf(stderr, "Removing DOS newlines\n");
+ strip = 1;
+ patchline[len-1]=0;
+ }
+ }
+ if (!*patchline) {
+ free(patchline);
+ patchline = xstrdup(" ");
+ }
+
+ // Are we assembling a hunk?
+ if (state >= 2) {
+ if (*patchline==' ' || *patchline=='+' || *patchline=='-') {
+ dlist_add(&TT.current_hunk, patchline);
+
+ if (*patchline != '+') TT.oldlen--;
+ if (*patchline != '-') TT.newlen--;
+
+ // Context line?
+ if (*patchline==' ' && state==2) TT.context++;
+ else state=3;
+
+ // If we've consumed all expected hunk lines, apply the hunk.
+
+ if (!TT.oldlen && !TT.newlen) state = apply_one_hunk();
+ continue;
+ }
+ fail_hunk();
+ state = 0;
+ continue;
+ }
+
+ // Open a new file?
+ if (!strncmp("--- ", patchline, 4) || !strncmp("+++ ", patchline, 4)) {
+ char *s, **name = &oldname;
+ int i;
+
+ if (*patchline == '+') {
+ name = &newname;
+ state = 1;
+ }
+
+ free(*name);
+ finish_oldfile();
+
+ // Trim date from end of filename (if any). We don't care.
+ for (s = patchline+4; *s && *s!='\t'; s++)
+ if (*s=='\\' && s[1]) s++;
+ i = atoi(s);
+ if (i>1900 && i<=1970)
+ *name = xstrdup("/dev/null");
+ else {
+ *s = 0;
+ *name = xstrdup(patchline+4);
+ }
+
+ // We defer actually opening the file because svn produces broken
+ // patches that don't signal they want to create a new file the
+ // way the patch man page says, so you have to read the first hunk
+ // and _guess_.
+
+ // Start a new hunk? Usually @@ -oldline,oldlen +newline,newlen @@
+ // but a missing ,value means the value is 1.
+ } else if (state == 1 && !strncmp("@@ -", patchline, 4)) {
+ int i;
+ char *s = patchline+4;
+
+ // Read oldline[,oldlen] +newline[,newlen]
+
+ TT.oldlen = TT.newlen = 1;
+ TT.oldline = strtol(s, &s, 10);
+ if (*s == ',') TT.oldlen=strtol(s+1, &s, 10);
+ TT.newline = strtol(s+2, &s, 10);
+ if (*s == ',') TT.newlen = strtol(s+1, &s, 10);
+
+ TT.context = 0;
+ state = 2;
+
+ // If this is the first hunk, open the file.
+ if (TT.filein == -1) {
+ int oldsum, newsum, del = 0;
+ char *name;
+
+ oldsum = TT.oldline + TT.oldlen;
+ newsum = TT.newline + TT.newlen;
+
+ name = reverse ? oldname : newname;
+
+ // We're deleting oldname if new file is /dev/null (before -p)
+ // or if new hunk is empty (zero context) after patching
+ if (!strcmp(name, "/dev/null") || !(reverse ? oldsum : newsum))
+ {
+ name = reverse ? newname : oldname;
+ del++;
+ }
+
+ // handle -p path truncation.
+ for (i = 0, s = name; *s;) {
+ if ((toys.optflags & FLAG_PATHLEN) && TT.prefix == i) break;
+ if (*s++ != '/') continue;
+ while (*s == '/') s++;
+ name = s;
+ i++;
+ }
+
+ if (del) {
+ printf("removing %s\n", name);
+ xunlink(name);
+ state = 0;
+ // If we've got a file to open, do so.
+ } else if (!(toys.optflags & FLAG_PATHLEN) || i <= TT.prefix) {
+ // If the old file was null, we're creating a new one.
+ if (!strcmp(oldname, "/dev/null") || !oldsum) {
+ printf("creating %s\n", name);
+ s = strrchr(name, '/');
+ if (s) {
+ *s = 0;
+ xmkpath(name, -1);
+ *s = '/';
+ }
+ TT.filein = xcreate(name, O_CREAT|O_EXCL|O_RDWR, 0666);
+ } else {
+ printf("patching %s\n", name);
+ TT.filein = xopen(name, O_RDWR);
+ }
+ TT.fileout = copy_tempfile(TT.filein, name, &TT.tempname);
+ TT.linenum = 0;
+ TT.hunknum = 0;
+ }
+ }
+
+ TT.hunknum++;
+
+ continue;
+ }
+
+ // If we didn't continue above, discard this line.
+ free(patchline);
+ }
+
+ finish_oldfile();
+
+ if (CFG_TOYBOX_FREE) {
+ close(TT.filepatch);
+ free(oldname);
+ free(newname);
+ }
+}
diff --git a/toys/posix/pwd.c b/toys/posix/pwd.c
new file mode 100644
index 00000000..d84c504a
--- /dev/null
+++ b/toys/posix/pwd.c
@@ -0,0 +1,30 @@
+/* vi: set sw=4 ts=4:
+ *
+ * pwd.c - Print working directory.
+ *
+ * Copyright 2006 Rob Landley <rob@landley.net>
+ *
+ * See http://www.opengroup.org/onlinepubs/009695399/utilities/echo.html
+ *
+ * TODO: add -L -P
+
+USE_PWD(NEWTOY(pwd, NULL, TOYFLAG_BIN))
+
+config PWD
+ bool "pwd"
+ default y
+ help
+ usage: pwd
+
+ The print working directory command prints the current directory.
+*/
+
+#include "toys.h"
+
+void pwd_main(void)
+{
+ char *pwd = xgetcwd();
+
+ xprintf("%s\n", pwd);
+ if (CFG_TOYBOX_FREE) free(pwd);
+}
diff --git a/toys/posix/rmdir.c b/toys/posix/rmdir.c
new file mode 100644
index 00000000..be93cb69
--- /dev/null
+++ b/toys/posix/rmdir.c
@@ -0,0 +1,43 @@
+/* vi: set sw=4 ts=4:
+ *
+ * rmdir.c - remove directory/path
+ *
+ * Copyright 2008 Rob Landley <rob@landley.net>
+ *
+ * See http://opengroup.org/onlinepubs/9699919799/utilities/rmdir.html
+
+USE_RMDIR(NEWTOY(rmdir, "<1p", TOYFLAG_BIN))
+
+config RMDIR
+ bool "rmdir"
+ default y
+ help
+ usage: rmdir [-p] [dirname...]
+ Remove one or more directories.
+
+ -p Remove path.
+*/
+
+#include "toys.h"
+
+static void do_rmdir(char *name)
+{
+ for (;;) {
+ char *temp;
+
+ if (rmdir(name)) {
+ perror_msg("%s",name);
+ return;
+ }
+ if (!toys.optflags) return;
+ if (!(temp=strrchr(name,'/'))) return;
+ *temp=0;
+ }
+}
+
+void rmdir_main(void)
+{
+ char **s;
+
+ for (s=toys.optargs; *s; s++) do_rmdir(*s);
+}
diff --git a/toys/posix/sed.c b/toys/posix/sed.c
new file mode 100644
index 00000000..c4eb1a44
--- /dev/null
+++ b/toys/posix/sed.c
@@ -0,0 +1,63 @@
+/* vi: set sw=4 ts=4:
+ *
+ * sed.c - Stream editor.
+ *
+ * Copyright 2008 Rob Landley <rob@landley.net>
+ *
+ * See http://www.opengroup.org/onlinepubs/009695399/utilities/sed.c
+
+USE_SED(NEWTOY(sed, "irne*", TOYFLAG_BIN))
+
+config SED
+ bool "sed"
+ default n
+ help
+ usage: sed [-irn] {command | [-e command]...} [FILE...]
+
+ Stream EDitor, transforms text by appling commands to each line
+ of input.
+*/
+
+#include "toys.h"
+#include "lib/xregcomp.h"
+
+DEFINE_GLOBALS(
+ struct arg_list *commands;
+)
+
+#define TT this.sed
+
+struct sed_command {
+ // Doubly linked list of commands.
+ struct sed_command *next, *prev;
+
+ // Regexes for s/match/data/ and /match_begin/,/match_end/command
+ regex_t *match, *match_begin, *match_end;
+
+ // For numeric ranges ala 10,20command
+ int first_line, last_line;
+
+ // Which match to replace, 0 for all.
+ int which;
+
+ // s and w commands can write to a file. Slight optimization: we use 0
+ // instead of -1 to mean no file here, because even when there's no stdin
+ // our input file would take fd 0.
+ int outfd;
+
+ // Data string for (saicytb)
+ char *data;
+
+ // Which command letter is this?
+ char command;
+};
+
+void sed_main(void)
+{
+ struct arg_list *test;
+
+ for (test = TT.commands; test; test = test->next)
+ dprintf(2,"command=%s\n",test->arg);
+
+ printf("Hello world\n");
+}
diff --git a/toys/posix/sleep.c b/toys/posix/sleep.c
new file mode 100644
index 00000000..ef6d827b
--- /dev/null
+++ b/toys/posix/sleep.c
@@ -0,0 +1,51 @@
+/* vi: set sw=4 ts=4:
+ *
+ * sleep.c - Wait for a number of seconds.
+ *
+ * Copyright 2007 Rob Landley <rob@landley.net>
+ * Copyright 2012 Georgi Chorbadzhiyski <georgi@unixsol.org>
+ *
+ * See http://www.opengroup.org/onlinepubs/009695399/utilities/sleep.html
+
+USE_SLEEP(NEWTOY(sleep, "<1", TOYFLAG_BIN))
+
+config SLEEP
+ bool "sleep"
+ default y
+ help
+ usage: sleep SECONDS
+
+ Wait before exiting.
+
+config SLEEP_FLOAT
+ bool
+ default y
+ depends on SLEEP && TOYBOX_FLOAT
+ help
+ The delay can be a decimal fraction. An optional suffix can be "m"
+ (minutes), "h" (hours), "d" (days), or "s" (seconds, the default).
+*/
+
+#include "toys.h"
+
+void sleep_main(void)
+{
+
+ if (!CFG_TOYBOX_FLOAT) toys.exitval = sleep(atol(*toys.optargs));
+ else {
+ char *arg;
+ double d = strtod(*toys.optargs, &arg);
+ struct timespec tv;
+
+ // Parse suffix
+ if (*arg) {
+ int ismhd[]={1,60,3600,86400};
+ char *smhd = "smhd", *c = strchr(smhd, *arg);
+ if (!c) error_exit("Unknown suffix '%c'", *arg);
+ d *= ismhd[c-smhd];
+ }
+
+ tv.tv_nsec=1000000000*(d-(tv.tv_sec = (unsigned long)d));
+ toys.exitval = !!nanosleep(&tv, NULL);
+ }
+}
diff --git a/toys/posix/sort.c b/toys/posix/sort.c
new file mode 100644
index 00000000..07a57163
--- /dev/null
+++ b/toys/posix/sort.c
@@ -0,0 +1,421 @@
+/* vi: set sw=4 ts=4:
+ *
+ * sort.c - put input lines into order
+ *
+ * Copyright 2004, 2008 Rob Landley <rob@landley.net>
+ *
+ * See http://www.opengroup.org/onlinepubs/007904975/utilities/sort.html
+
+USE_SORT(NEWTOY(sort, USE_SORT_FLOAT("g")USE_SORT_BIG("S:T:m" "o:k*t:xbMcszdfi") "run", TOYFLAG_USR|TOYFLAG_BIN))
+
+config SORT
+ bool "sort"
+ default y
+ help
+ usage: sort [-run] [FILE...]
+
+ Sort all lines of text from input files (or stdin) to stdout.
+
+ -r reverse
+ -u unique lines only
+ -n numeric order (instead of alphabetical)
+
+config SORT_BIG
+ bool "SuSv3 options (Support -ktcsbdfiozM)"
+ default y
+ depends on SORT
+ help
+ usage: sort [-bcdfiMsz] [-k#[,#[x]] [-t X]] [-o FILE]
+
+ -b ignore leading blanks (or trailing blanks in second part of key)
+ -c check whether input is sorted
+ -d dictionary order (use alphanumeric and whitespace chars only)
+ -f force uppercase (case insensitive sort)
+ -i ignore nonprinting characters
+ -M month sort (jan, feb, etc).
+ -x Hexadecimal numerical sort
+ -s skip fallback sort (only sort with keys)
+ -z zero (null) terminated input
+ -k sort by "key" (see below)
+ -t use a key separator other than whitespace
+ -o output to FILE instead of stdout
+
+ Sorting by key looks at a subset of the words on each line. -k2
+ uses the second word to the end of the line, -k2,2 looks at only
+ the second word, -k2,4 looks from the start of the second to the end
+ of the fourth word. Specifying multiple keys uses the later keys as
+ tie breakers, in order. A type specifier appended to a sort key
+ (such as -2,2n) applies only to sorting that key.
+
+config SORT_FLOAT
+ bool "Floating point (-g)"
+ default y
+ depends on SORT_BIG
+ help
+ usage: sort [-g]
+
+ This version of sort requires floating point.
+
+ -g general numeric sort (double precision with nan and inf)
+
+*/
+
+#include "toys.h"
+
+DEFINE_GLOBALS(
+ char *key_separator;
+ struct arg_list *raw_keys;
+ char *outfile;
+ char *ignore1, ignore2; // GNU compatability NOPs for -S and -T.
+
+ void *key_list;
+ int linecount;
+ char **lines;
+)
+
+#define TT this.sort
+
+// The sort types are n, g, and M.
+// u, c, s, and z apply to top level only, not to keys.
+// b at top level implies bb.
+// The remaining options can be applied to search keys.
+
+#define FLAG_n (1<<0) // Sort type: numeric
+#define FLAG_u (1<<1) // Unique
+#define FLAG_r (1<<2) // Reverse output order
+
+#define FLAG_i (1<<3) // Ignore !isprint()
+#define FLAG_f (1<<4) // Force uppercase
+#define FLAG_d (1<<5) // Ignore !(isalnum()|isspace())
+#define FLAG_z (1<<6) // Input is null terminated, not \n
+#define FLAG_s (1<<7) // Stable sort, no ascii fallback at end
+#define FLAG_c (1<<8) // Check only. No output, exit(!ordered)
+#define FLAG_M (1<<9) // Sort type: date
+#define FLAG_b (1<<10) // Ignore leading blanks
+#define FLAG_x (1<<11) // Hex sort
+#define FLAG_g (1<<18) // Sort type: strtod()
+
+// Left off dealing with FLAG_b/FLAG_bb logic...
+
+#define FLAG_bb (1<<31) // Ignore trailing blanks
+
+struct sort_key
+{
+ struct sort_key *next_key; // linked list
+ unsigned range[4]; // start word, start char, end word, end char
+ int flags;
+};
+
+// Copy of the part of this string corresponding to a key/flags.
+
+static char *get_key_data(char *str, struct sort_key *key, int flags)
+{
+ int start=0, end, 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
+ if (str[end] && !TT.key_separator)
+ while (isspace(str[end])) end++;
+
+ // Skip body of key
+ for (; str[end]; end++) {
+ if (TT.key_separator) {
+ if (str[end]==*TT.key_separator) break;
+ } else if (isspace(str[end])) break;
+ }
+ }
+ }
+ if (!j) start=end;
+ }
+
+ // Key with explicit separator starts after the separator
+ if (TT.key_separator && str[start]==*TT.key_separator) start++;
+
+ // Strip leading and trailing whitespace if necessary
+ if (flags&FLAG_b) while (isspace(str[start])) start++;
+ 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;
+}
+
+// append a sort_key to key_list.
+
+static struct sort_key *add_key(void)
+{
+ void **stupid_compiler = &TT.key_list;
+ struct sort_key **pkey = (struct sort_key **)stupid_compiler;
+
+ while (*pkey) pkey = &((*pkey)->next_key);
+ return *pkey = xzalloc(sizeof(struct sort_key));
+}
+
+// Perform actual comparison
+static int compare_values(int flags, char *x, char *y)
+{
+ int ff = flags & (FLAG_n|FLAG_g|FLAG_M|FLAG_x);
+
+ // Ascii sort
+ if (!ff) return strcmp(x, y);
+
+ if (CFG_SORT_FLOAT && ff == FLAG_g) {
+ char *xx,*yy;
+ double dx = strtod(x,&xx), dy = strtod(y,&yy);
+ int xinf, yinf;
+
+ // not numbers < NaN < -infinity < numbers < +infinity
+
+ if (x==xx) return y==yy ? 0 : -1;
+ if (y==yy) return 1;
+
+ // Check for isnan
+ if (dx!=dx) return (dy!=dy) ? 0 : -1;
+ if (dy!=dy) return 1;
+
+ // Check for infinity. (Could underflow, but avoids needing libm.)
+ xinf = (1.0/dx == 0.0);
+ yinf = (1.0/dy == 0.0);
+ if (xinf) {
+ if(dx<0) return (yinf && dy<0) ? 0 : -1;
+ return (yinf && dy>0) ? 0 : 1;
+ }
+ if (yinf) return dy<0 ? 1 : -1;
+
+ return dx>dy ? 1 : (dx<dy ? -1 : 0);
+ } else if (CFG_SORT_BIG && ff == 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) return !yy ? 0 : -1;
+ else if (!yy) return 1;
+ else return dx==thyme.tm_mon ? 0 : dx-thyme.tm_mon;
+
+ } else if (CFG_SORT_BIG && ff == FLAG_x) {
+ return strtol(x, NULL, 16)-strtol(y, NULL, 16);
+ // This has to be ff == FLAG_n
+ } else {
+ // Full floating point version of -n
+ if (CFG_SORT_FLOAT) {
+ double dx = atof(x), dy = atof(y);
+
+ return dx>dy ? 1 : (dx<dy ? -1 : 0);
+ // Integer version of -n for tiny systems
+ } else return atoi(x)-atoi(y);
+ }
+}
+
+
+// Callback from qsort(): Iterate through key_list and perform comparisons.
+static int compare_keys(const void *xarg, const void *yarg)
+{
+ int flags = toys.optflags, retval = 0;
+ char *x, *y, *xx = *(char **)xarg, *yy = *(char **)yarg;
+ struct sort_key *key;
+
+ if (CFG_SORT_BIG) {
+ for (key=(struct sort_key *)TT.key_list; !retval && key;
+ key = key->next_key)
+ {
+ flags = key->flags ? key->flags : toys.optflags;
+
+ // Chop out and modify key chunks, handling -dfib
+
+ x = get_key_data(xx, key, flags);
+ y = get_key_data(yy, key, flags);
+
+ retval = compare_values(flags, x, y);
+
+ // Free the copies get_key_data() made.
+
+ if (x != xx) free(x);
+ if (y != yy) free(y);
+
+ if (retval) break;
+ }
+ } else retval = compare_values(flags, xx, yy);
+
+ // Perform fallback sort if necessary
+ if (!retval && !(CFG_SORT_BIG && (toys.optflags&FLAG_s))) {
+ retval = strcmp(xx, yy);
+ flags = toys.optflags;
+ }
+
+ return retval * ((flags&FLAG_r) ? -1 : 1);
+}
+
+// Callback from loopfiles to handle input files.
+static void sort_read(int fd, char *name)
+{
+ // Read each line from file, appending to a big array.
+
+ for (;;) {
+ char * line = (CFG_SORT_BIG && (toys.optflags&FLAG_z))
+ ? get_rawline(fd, NULL, 0) : get_line(fd);
+
+ if (!line) break;
+
+ // handle -c here so we don't allocate more memory than necessary.
+ if (CFG_SORT_BIG && (toys.optflags&FLAG_c)) {
+ int j = (toys.optflags&FLAG_u) ? -1 : 0;
+
+ if (TT.lines && compare_keys((void *)&TT.lines, &line)>j)
+ error_exit("%s: Check line %d\n", name, TT.linecount);
+ free(TT.lines);
+ TT.lines = (char **)line;
+ } else {
+ if (!(TT.linecount&63))
+ TT.lines = xrealloc(TT.lines, sizeof(char *)*(TT.linecount+64));
+ TT.lines[TT.linecount] = line;
+ }
+ TT.linecount++;
+ }
+}
+
+void sort_main(void)
+{
+ int idx, fd = 1;
+
+ // Open output file if necessary.
+ if (CFG_SORT_BIG && TT.outfile)
+ fd = xcreate(TT.outfile, O_CREAT|O_TRUNC|O_WRONLY, 0666);
+
+ // Parse -k sort keys.
+ if (CFG_SORT_BIG && TT.raw_keys) {
+ struct arg_list *arg;
+
+ for (arg = TT.raw_keys; arg; arg = arg->next) {
+ struct sort_key *key = add_key();
+ char *temp;
+ int flag;
+
+ idx = 0;
+ temp = arg->arg;
+ while (*temp) {
+ // Start of range
+ key->range[2*idx] = (unsigned)strtol(temp, &temp, 10);
+ if (*temp=='.')
+ key->range[(2*idx)+1] = (unsigned)strtol(temp+1, &temp, 10);
+
+ // Handle flags appended to a key type.
+ for (;*temp;temp++) {
+ char *temp2, *optlist;
+
+ // Note that a second comma becomes an "Unknown key" error.
+
+ if (*temp==',' && !idx++) {
+ temp++;
+ break;
+ }
+
+ // Which flag is this?
+
+ optlist = toys.which->options;
+ temp2 = strchr(optlist, *temp);
+ flag = (1<<(optlist-temp2+strlen(optlist)-1));
+
+ // Was it a flag that can apply to a key?
+
+ if (!temp2 || flag>FLAG_b
+ || (flag&(FLAG_u|FLAG_c|FLAG_s|FLAG_z)))
+ {
+ error_exit("Unknown key option.");
+ }
+ // b after , means strip _trailing_ space, not leading.
+ if (idx && flag==FLAG_b) flag = FLAG_bb;
+ key->flags |= flag;
+ }
+ }
+ }
+ }
+
+ // global b flag strips both leading and trailing spaces
+ if (toys.optflags&FLAG_b) toys.optflags |= FLAG_bb;
+
+ // If no keys, perform alphabetic sort over the whole line.
+ if (CFG_SORT_BIG && !TT.key_list) add_key()->range[0] = 1;
+
+ // Open input files and read data, populating TT.lines[TT.linecount]
+ loopfiles(toys.optargs, sort_read);
+
+ // The compare (-c) logic was handled in sort_read(),
+ // so if we got here, we're done.
+ if (CFG_SORT_BIG && (toys.optflags&FLAG_c)) goto exit_now;
+
+ // Perform the actual sort
+ qsort(TT.lines, TT.linecount, sizeof(char *), compare_keys);
+
+ // handle unique (-u)
+ if (toys.optflags&FLAG_u) {
+ int jdx;
+
+ for (jdx=0, idx=1; idx<TT.linecount; idx++) {
+ if (!compare_keys(&TT.lines[jdx], &TT.lines[idx]))
+ free(TT.lines[idx]);
+ else TT.lines[++jdx] = TT.lines[idx];
+ }
+ if (TT.linecount) TT.linecount = jdx+1;
+ }
+
+ // Output result
+ for (idx = 0; idx<TT.linecount; idx++) {
+ char *s = TT.lines[idx];
+ xwrite(fd, s, strlen(s));
+ if (CFG_TOYBOX_FREE) free(s);
+ xwrite(fd, "\n", 1);
+ }
+
+exit_now:
+ if (CFG_TOYBOX_FREE) {
+ if (fd != 1) close(fd);
+ free(TT.lines);
+ }
+}
diff --git a/toys/posix/tail.c b/toys/posix/tail.c
new file mode 100644
index 00000000..8783d6ba
--- /dev/null
+++ b/toys/posix/tail.c
@@ -0,0 +1,232 @@
+/* vi: set sw=4 ts=4:
+ *
+ * tail.c - copy last lines from input to stdout.
+ *
+ * Copyright 2012 Timothy Elliott <tle@holymonkey.com>
+ *
+ * See http://www.opengroup.org/onlinepubs/009695399/utilities/tail.html
+
+USE_TAIL(NEWTOY(tail, "fc-n-", TOYFLAG_BIN))
+
+config TAIL
+ bool "tail"
+ default y
+ help
+ usage: tail [-n|c number] [-f] [file...]
+
+ Copy last lines from files to stdout. If no files listed, copy from
+ stdin. Filename "-" is a synonym for stdin.
+
+ -n output the last X lines (default 10), +X counts from start.
+ -c output the last X bytes, +X counts from start
+ -f follow file, waiting for more data to be appended
+
+config TAIL_SEEK
+ bool "tail seek support"
+ default y
+ depends on TAIL
+ help
+ This version uses lseek, which is faster on large files.
+*/
+
+#include "toys.h"
+
+DEFINE_GLOBALS(
+ long lines;
+ long bytes;
+
+ int file_no;
+)
+
+#define TT this.tail
+
+#define FLAG_n 1
+#define FLAG_c 2
+#define FLAG_f 4
+
+struct line_list {
+ struct line_list *next, *prev;
+ char *data;
+ int len;
+};
+
+static struct line_list *get_chunk(int fd, int len)
+{
+ struct line_list *line = xmalloc(sizeof(struct line_list)+len);
+
+ line->data = ((char *)line) + sizeof(struct line_list);
+ line->len = readall(fd, line->data, len);
+
+ if (line->len < 1) {
+ free(line);
+ return 0;
+ }
+
+ return line;
+}
+
+static void dump_chunk(void *ptr)
+{
+ struct line_list *list = ptr;
+ xwrite(1, list->data, list->len);
+ free(list);
+}
+
+// Reading through very large files is slow. Using lseek can speed things
+// up a lot, but isn't applicable to all input (cat | tail).
+// Note: bytes and lines are negative here.
+static int try_lseek(int fd, long bytes, long lines)
+{
+ struct line_list *list = 0, *temp;
+ int flag = 0, chunk = sizeof(toybuf);
+ ssize_t pos = lseek(fd, 0, SEEK_END);
+
+ // If lseek() doesn't work on this stream, return now.
+ if (pos<0) return 0;
+
+ // Seek to the right spot, output data from there.
+ if (bytes) {
+ if (lseek(fd, bytes, SEEK_END)<0) lseek(fd, 0, SEEK_SET);
+ xsendfile(fd, 1);
+ return 1;
+ }
+
+ // Read from end to find enough lines, then output them.
+
+ bytes = pos;
+ while (lines && pos) {
+ int offset;
+
+ // Read in next chunk from end of file
+ if (chunk>pos) chunk = pos;
+ pos -= chunk;
+ if (pos != lseek(fd, pos, SEEK_SET)) {
+ perror_msg("seek failed");
+ break;
+ }
+ if (!(temp = get_chunk(fd, chunk))) break;
+ if (list) list->next = temp;
+ list = temp;
+
+ // Count newlines in this chunk.
+ offset = list->len;
+ while (offset--) {
+ // If the last line ends with a newline, that one doesn't count.
+ if (!flag) {
+ flag++;
+
+ continue;
+ }
+
+ // Start outputting data right after newline
+ if (list->data[offset] == '\n' && !++lines) {
+ offset++;
+ list->data += offset;
+ list->len -= offset;
+
+ break;
+ }
+ }
+ }
+
+ // Output stored data
+ llist_traverse(list, dump_chunk);
+
+ // In case of -f
+ lseek(fd, bytes, SEEK_SET);
+ return 1;
+}
+
+// Called for each file listed on command line, and/or stdin
+static void do_tail(int fd, char *name)
+{
+ long bytes = TT.bytes, lines = TT.lines;
+
+ if (toys.optc > 1) {
+ if (TT.file_no++) xputc('\n');
+ xprintf("==> %s <==\n", name);
+ }
+
+ // Are we measuring from the end of the file?
+
+ if (bytes<0 || lines<0) {
+ struct line_list *list = 0, *new;
+
+ // The slow codepath is always needed, and can handle all input,
+ // so make lseek support optional.
+ if (CFG_TAIL_SEEK && try_lseek(fd, bytes, lines));
+
+ // Read data until we run out, keep a trailing buffer
+ else for (;;) {
+ int len, count;
+ char *try;
+
+ if (!(new = get_chunk(fd, sizeof(toybuf)))) break;
+ // append in order
+ dlist_add_nomalloc((struct double_list **)&list,
+ (struct double_list *)new);
+
+ // Measure new chunk, discarding extra data from buffer
+ len = new->len;
+ try = new->data;
+ for (count=0; count<len; count++) {
+ if ((toys.optflags & FLAG_c) && bytes) {
+ bytes++;
+ continue;
+ }
+
+ if (lines) {
+ if(try[count] != '\n' && count != len-1) continue;
+ if (lines<0) {
+ if (!++lines) ++lines;
+ continue;
+ }
+ }
+
+ // Time to discard data; given that bytes and lines were
+ // nonzero coming in, we can't discard too much if we're
+ // measuring right.
+ do {
+ char c = *(list->data++);
+ if (!(--list->len)) {
+ struct line_list *next = list->next;
+ list->prev->next = next;
+ list->next->prev = list->prev;
+ free(list);
+ list = next;
+ }
+ if (c == '\n') break;
+ } while (lines);
+ }
+ }
+
+ // Output/free the buffer.
+ llist_traverse(list, dump_chunk);
+
+ // Measuring from the beginning of the file.
+ } else for (;;) {
+ int len, offset = 0;
+
+ // Error while reading does not exit. Error writing does.
+ len = read(fd, toybuf, sizeof(toybuf));
+ if (len<1) break;
+ while (bytes > 1 || lines > 1) {
+ bytes--;
+ if (toybuf[offset++] == '\n') lines--;
+ if (offset >= len) break;
+ }
+ if (offset<len) xwrite(1, toybuf+offset, len-offset);
+ }
+
+ // -f support: cache name/descriptor
+}
+
+void tail_main(void)
+{
+ // if nothing specified, default -n to -10
+ if (!(toys.optflags&(FLAG_n|FLAG_c))) TT.lines = -10;
+
+ loopfiles(toys.optargs, do_tail);
+
+ // do -f stuff
+}
diff --git a/toys/posix/tee.c b/toys/posix/tee.c
new file mode 100644
index 00000000..c11fb5c4
--- /dev/null
+++ b/toys/posix/tee.c
@@ -0,0 +1,75 @@
+/* vi: set sw=4 ts=4:
+ *
+ * tee.c - cat to multiple outputs.
+ *
+ * Copyright 2008 Rob Landley <rob@landley.net>
+ *
+ * See http://www.opengroup.org/onlinepubs/009695399/utilities/tee.html
+
+USE_TEE(NEWTOY(tee, "ia", TOYFLAG_BIN))
+
+config TEE
+ bool "tee"
+ default y
+ help
+ usage: tee [-ai] [file...]
+
+ Copy stdin to each listed file, and also to stdout.
+ Filename "-" is a synonym for stdout.
+
+ -a append to files.
+ -i ignore SIGINT.
+*/
+
+#include "toys.h"
+
+DEFINE_GLOBALS(
+ void *outputs;
+)
+
+#define TT this.tee
+
+struct fd_list {
+ struct fd_list *next;
+ int fd;
+};
+
+// Open each output file, saving filehandles to a linked list.
+
+static void do_tee_open(int fd, char *name)
+{
+ struct fd_list *temp;
+
+ temp = xmalloc(sizeof(struct fd_list));
+ temp->next = TT.outputs;
+ temp->fd = fd;
+ TT.outputs = temp;
+}
+
+void tee_main(void)
+{
+ if (toys.optflags&2) signal(SIGINT, SIG_IGN);
+
+ // Open output files
+ loopfiles_rw(toys.optargs,
+ O_RDWR|O_CREAT|((toys.optflags&1)?O_APPEND:O_TRUNC), 0666, 0,
+ do_tee_open);
+
+ for (;;) {
+ struct fd_list *fdl;
+ int len;
+
+ // Read data from stdin
+ len = xread(0, toybuf, sizeof(toybuf));
+ if (len<1) break;
+
+ // Write data to each output file, plus stdout.
+ fdl = TT.outputs;
+ for (;;) {
+ if(len != writeall(fdl ? fdl->fd : 1, toybuf, len)) toys.exitval=1;
+ if (!fdl) break;
+ fdl = fdl->next;
+ }
+ }
+
+}
diff --git a/toys/posix/true.c b/toys/posix/true.c
new file mode 100644
index 00000000..582b2cae
--- /dev/null
+++ b/toys/posix/true.c
@@ -0,0 +1,23 @@
+/* vi: set sw=4 ts=4:
+ *
+ * true.c - Return zero.
+ *
+ * Copyright 2007 Rob Landley <rob@landley.net>
+ *
+ * See http://www.opengroup.org/onlinepubs/009695399/utilities/true.html
+
+USE_TRUE(NEWTOY(true, NULL, TOYFLAG_BIN))
+
+config TRUE
+ bool "true"
+ default y
+ help
+ Return zero.
+*/
+
+#include "toys.h"
+
+void true_main(void)
+{
+ return;
+}
diff --git a/toys/posix/tty.c b/toys/posix/tty.c
new file mode 100644
index 00000000..661e919a
--- /dev/null
+++ b/toys/posix/tty.c
@@ -0,0 +1,32 @@
+/* vi: set sw=4 ts=4:
+ *
+ * tty.c - Show stdin's terminal name
+ *
+ * Copyright 2011 Rob Landley <rob@landley.net>
+ *
+ * See http://opengroup.org/onlinepubs/9699919799/utilities/tty.html
+
+USE_TTY(NEWTOY(tty, "s", TOYFLAG_USR|TOYFLAG_BIN))
+
+config TTY
+ bool "tty"
+ default y
+ help
+ Show filename of terminal connected to stdin.
+
+ Prints "not a tty" and exits with nonzero status if no terminal
+ is connected to stdin.
+
+ -s silent mode
+*/
+
+#include "toys.h"
+
+void tty_main(void)
+{
+ char *tty = ttyname(0);
+
+ if (!toys.optflags) puts(tty ? tty : "not a tty");
+
+ toys.exitval = !tty;
+}
diff --git a/toys/posix/uname.c b/toys/posix/uname.c
new file mode 100644
index 00000000..185d633e
--- /dev/null
+++ b/toys/posix/uname.c
@@ -0,0 +1,74 @@
+/* vi: set sw=4 ts=4:
+ *
+ * uname.c - return system name
+ *
+ * Copyright 2008 Rob Landley <rob@landley.net>
+ *
+ * See http://www.opengroup.org/onlinepubs/009695399/utilities/uname.html
+
+USE_UNAME(NEWTOY(uname, "amvrns", TOYFLAG_BIN))
+
+config UNAME
+ bool "uname"
+ default y
+ help
+ usage: uname [-asnrvmpio]
+
+ Print system information.
+
+ -s System name
+ -n Network (domain) name
+ -r Release number
+ -v Version (build date)
+ -m Machine (hardware) name
+ -a All of the above
+*/
+
+#include "toys.h"
+
+// If a 32 bit x86 build environment working in a chroot under an x86-64
+// kernel returns x86_64 for -m it confuses ./configure. Special case it.
+
+#if defined(__i686__)
+#define GROSS "i686"
+#elif defined(__i586__)
+#define GROSS "i586"
+#elif defined(__i486__)
+#define GROSS "i486"
+#elif defined(__i386__)
+#define GROSS "i386"
+#endif
+
+#define FLAG_a (1<<5)
+
+void uname_main(void)
+{
+ int i, flags = toys.optflags, needspace=0;
+
+ uname((void *)toybuf);
+
+ if (!flags) flags=1;
+ for (i=0; i<5; i++) {
+ char *c = toybuf+(65*i);
+
+ if (flags & ((1<<i)|FLAG_a)) {
+ int len = strlen(c);
+
+ // This problem originates in autoconf, so of course the solution
+ // is horribly ugly.
+#ifdef GROSS
+ if (i==4 && !strcmp(c,"x86_64")) printf(GROSS);
+ else
+#endif
+
+ if (needspace++) {
+ // We can't decrement on the first entry, because
+ // needspace would be 0
+ *(--c)=' ';
+ len++;
+ }
+ xwrite(1, c, len);
+ }
+ }
+ putchar('\n');
+}
diff --git a/toys/posix/uniq.c b/toys/posix/uniq.c
new file mode 100644
index 00000000..c16c08f1
--- /dev/null
+++ b/toys/posix/uniq.c
@@ -0,0 +1,131 @@
+/* vi: set sw=4 ts=4:
+ *
+ * uniq.c - report or filter out repeated lines in a file
+ *
+ * Copyright 2012 Georgi Chorbadzhiyski <georgi@unixsol.org>
+ *
+ * See http://www.opengroup.org/onlinepubs/009695399/utilities/uniq.html
+
+USE_UNIQ(NEWTOY(uniq, "f#s#w#zicdu", TOYFLAG_BIN))
+
+config UNIQ
+ bool "uniq"
+ default y
+ help
+ usage: uniq [-cduiz] [-w maxchars] [-f fields] [-s char] [input_file [output_file]]
+
+ Report or filter out repeated lines in a file
+
+ -c show counts before each line
+ -d show only lines that are repeated
+ -u show only lines that are unique
+ -i ignore case when comparing lines
+ -z lines end with \0 not \n
+ -w compare maximum X chars per line
+ -f ignore first X fields
+ -s ignore first X chars
+*/
+
+#include "toys.h"
+
+DEFINE_GLOBALS(
+ long maxchars;
+ long nchars;
+ long nfields;
+ long repeats;
+)
+
+#define TT this.uniq
+
+#define FLAG_z 16
+#define FLAG_i 8
+#define FLAG_c 4
+#define FLAG_d 2
+#define FLAG_u 1
+
+static char *skip(char *str)
+{
+ long nchars = TT.nchars, nfields;
+
+ // Skip fields first
+ for (nfields = TT.nfields; nfields; str++) {
+ while (*str && isspace(*str)) str++;
+ while (*str && !isspace(*str)) str++;
+ nfields--;
+ }
+ // Skip chars
+ while (*str && nchars--) str++;
+
+ return str;
+}
+
+static void print_line(FILE *f, char *line)
+{
+ if (toys.optflags & (TT.repeats ? FLAG_u : FLAG_d)) return;
+ if (toys.optflags & FLAG_c) fprintf(f, "%7lu ", TT.repeats + 1);
+ fputs(line, f);
+ if (toys.optflags & FLAG_z) fputc(0, f);
+}
+
+void uniq_main(void)
+{
+ FILE *infile = stdin, *outfile = stdout;
+ char *thisline = NULL, *prevline = NULL, *tmpline, eol = '\n';
+ size_t thissize, prevsize = 0, tmpsize;
+
+ if (toys.optc >= 1) infile = xfopen(toys.optargs[0], "r");
+ if (toys.optc >= 2) outfile = xfopen(toys.optargs[1], "w");
+
+ if (toys.optflags & FLAG_z) eol = 0;
+
+ // If first line can't be read
+ if (getdelim(&prevline, &prevsize, eol, infile) < 0)
+ return;
+
+ while (getdelim(&thisline, &thissize, eol, infile) > 0) {
+ int diff;
+ char *t1, *t2;
+
+ // If requested get the chosen fields + character offsets.
+ if (TT.nfields || TT.nchars) {
+ t1 = skip(thisline);
+ t2 = skip(prevline);
+ } else {
+ t1 = thisline;
+ t2 = prevline;
+ }
+
+ if (TT.maxchars == 0) {
+ diff = !(toys.optflags & FLAG_i)
+ ? strcmp(t1, t2)
+ : strcasecmp(t1, t2);
+ } else {
+ diff = !(toys.optflags & FLAG_i)
+ ? strncmp(t1, t2, TT.maxchars)
+ : strncasecmp(t1, t2, TT.maxchars);
+ }
+
+ if (diff == 0) { // same
+ TT.repeats++;
+ } else {
+ print_line(outfile, prevline);
+
+ TT.repeats = 0;
+
+ tmpline = prevline;
+ prevline = thisline;
+ thisline = tmpline;
+
+ tmpsize = prevsize;
+ prevsize = thissize;
+ thissize = tmpsize;
+ }
+ }
+
+ print_line(outfile, prevline);
+
+ if (CFG_TOYBOX_FREE) {
+ free(prevline);
+ free(thisline);
+ }
+}
diff --git a/toys/posix/unlink.c b/toys/posix/unlink.c
new file mode 100644
index 00000000..19660c5d
--- /dev/null
+++ b/toys/posix/unlink.c
@@ -0,0 +1,26 @@
+/* vi: set sw=4 ts=4:
+ *
+ * unlink.c - delete one file
+ *
+ * Copyright 2011 Rob Landley <rob@landley.net>
+ *
+ * See http://opengroup.org/onlinepubs/9699919799/utilities/unlink.html
+
+USE_UNLINK(NEWTOY(unlink, "<1>1", TOYFLAG_USR|TOYFLAG_BIN))
+
+config UNLINK
+ bool "unlink"
+ default y
+ help
+ usage: unlink FILE
+
+ Deletes one file.
+*/
+
+#include "toys.h"
+
+void unlink_main(void)
+{
+ if (unlink(*toys.optargs))
+ perror_exit("Couldn't unlink `%s'", *toys.optargs);
+}
diff --git a/toys/posix/wc.c b/toys/posix/wc.c
new file mode 100644
index 00000000..17801188
--- /dev/null
+++ b/toys/posix/wc.c
@@ -0,0 +1,60 @@
+/* vi: set sw=4 ts=4:
+ *
+ * wc.c - Word count
+ *
+ * Copyright 2011 Rob Landley <rob@landley.net>
+ *
+ * See http://opengroup.org/onlinepubs/9699919799/utilities/wc.html
+
+USE_WC(NEWTOY(wc, "cwl", TOYFLAG_USR|TOYFLAG_BIN))
+
+config WC
+ bool "wc"
+ default y
+ help
+ usage: wc -lwc [FILE...]
+
+ Count lines, words, and characters in input.
+
+ -l show lines
+ -w show words
+ -c show characters
+
+ By default outputs lines, words, characters, and filename for each
+ argument (or from stdin if none).
+*/
+
+#include "toys.h"
+
+static void do_wc(int fd, char *name)
+{
+ int i, len;
+ unsigned long word=0, lengths[]={0,0,0};
+
+ for (;;) {
+ len = read(fd, toybuf, sizeof(toybuf));
+ if (len<0) {
+ perror_msg("%s",name);
+ toys.exitval = EXIT_FAILURE;
+ }
+ if (len<1) break;
+ for (i=0; i<len; i++) {
+ if (toybuf[i]==10) lengths[0]++;
+ if (isspace(toybuf[i])) word=0;
+ else {
+ if (!word) lengths[1]++;
+ word=1;
+ }
+ lengths[2]++;
+ }
+ }
+ for (i=0; i<3; i++)
+ if (!toys.optflags || (toys.optflags&(1<<i)))
+ printf("%ld ", lengths[i]);
+ printf("%s\n", (!toys.optflags && strcmp(name,"-")) ? name : "");
+}
+
+void wc_main(void)
+{
+ loopfiles(toys.optargs, do_wc);
+}
diff --git a/toys/posix/who.c b/toys/posix/who.c
new file mode 100644
index 00000000..d407c6b2
--- /dev/null
+++ b/toys/posix/who.c
@@ -0,0 +1,46 @@
+/* vi: set sw=4 ts=4:
+ *
+ * who.c - display who is on the system
+ *
+ * Copyright 2012 ProFUSION Embedded Systems
+ *
+ * by Luis Felipe Strano Moraes <lfelipe@profusion.mobi>
+ *
+ * See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/who.html
+
+USE_WHO(NEWTOY(who, NULL, TOYFLAG_BIN))
+
+config WHO
+ bool "who"
+ default n
+ help
+ usage: who
+
+ Print logged user information on system
+
+*/
+
+#include "toys.h"
+
+void who_main(void)
+{
+ struct utmpx *entry;
+
+ setutxent();
+
+ while ((entry = getutxent())) {
+ if (entry->ut_type == USER_PROCESS) {
+ time_t time;
+ int time_size;
+ char * times;
+
+ time = entry->ut_tv.tv_sec;
+ times = ctime(&time);
+ time_size = strlen(times) - 2;
+ printf("%s\t%s\t%*.*s\t(%s)\n", entry->ut_user, entry->ut_line, time_size, time_size, ctime(&time), entry->ut_host);
+
+ }
+ }
+
+ endutxent();
+}
diff --git a/toys/posix/xargs.c b/toys/posix/xargs.c
new file mode 100644
index 00000000..0d513253
--- /dev/null
+++ b/toys/posix/xargs.c
@@ -0,0 +1,188 @@
+/* vi: set sw=4 ts=4:
+ *
+ * xargs.c - Run command with arguments taken from stdin.
+ *
+ * Copyright 2011 Rob Landley <rob@landley.net>
+ *
+ * See http://opengroup.org/onlinepubs/9699919799/utilities/xargs.html
+
+USE_XARGS(NEWTOY(xargs, "^I:E:L#ptxrn#<1s#0", TOYFLAG_USR|TOYFLAG_BIN))
+
+config XARGS
+ bool "xargs"
+ default y
+ help
+ usage: xargs [-ptxr0] [-s NUM] [-n NUM] [-L NUM] [-E STR] COMMAND...
+
+ Run command line one or more times, appending arguments from stdin.
+
+ If command exits with 255, don't launch another even if arguments remain.
+
+ -s Size in bytes per command line
+ -n Max number of arguments per command
+ -0 Each argument is NULL terminated, no whitespace or quote processing
+ #-p Prompt for y/n from tty before running each command
+ #-t Trace, print command line to stderr
+ #-x Exit if can't fit everything in one command
+ #-r Don't run command with empty input
+ #-L Max number of lines of input per command
+ -E stop at line matching string
+*/
+
+#include "toys.h"
+
+DEFINE_GLOBALS(
+ long max_bytes;
+ long max_entries;
+ long L;
+ char *eofstr;
+ char *I;
+
+ long entries, bytes;
+ char delim;
+)
+
+#define TT this.xargs
+
+// If out==NULL count TT.bytes and TT.entries, stopping at max.
+// Otherwise, fill out out[]
+
+// Returning NULL means need more data.
+// Returning char * means hit data limits, start of data left over
+// Returning 1 means hit data limits, but consumed all data
+// Returning 2 means hit -E eofstr
+
+static char *handle_entries(char *data, char **entry)
+{
+ if (TT.delim) {
+ char *s = data;
+
+ // Chop up whitespace delimited string into args
+ while (*s) {
+ char *save;
+
+ while (isspace(*s)) {
+ if (entry) *s = 0;
+ s++;
+ }
+
+ if (TT.max_entries && TT.entries >= TT.max_entries)
+ return *s ? s : (char *)1;
+
+ if (!*s) break;
+ save = s;
+
+ for (;;) {
+ if (++TT.bytes >= TT.max_bytes && TT.max_bytes) return save;
+ if (!*s || isspace(*s)) break;
+ s++;
+ }
+ if (TT.eofstr) {
+ int len = s-save;
+ if (len == strlen(TT.eofstr) && !strncmp(save, TT.eofstr, len))
+ return (char *)2;
+ }
+ if (entry) entry[TT.entries] = save;
+ ++TT.entries;
+ }
+
+ // -0 support
+ } else {
+ TT.bytes += strlen(data)+1;
+ if (TT.max_bytes && TT.bytes >= TT.max_bytes) return data;
+ if (TT.max_entries && TT.entries >= TT.max_entries)
+ return (char *)1;
+ if (entry) entry[TT.entries] = data;
+ TT.entries++;
+ }
+
+ return NULL;
+}
+
+void xargs_main(void)
+{
+ struct double_list *dlist = NULL;
+ int entries, bytes, done = 0, status;
+ char *data = NULL;
+
+ if (!(toys.optflags&1)) TT.delim = '\n';
+
+ // If no optargs, call echo.
+ if (!toys.optc) {
+ free(toys.optargs);
+ *(toys.optargs = xzalloc(2*sizeof(char *)))="echo";
+ toys.optc = 1;
+ }
+
+ for (entries = 0, bytes = -1; entries < toys.optc; entries++, bytes++)
+ bytes += strlen(toys.optargs[entries]);
+
+ // Loop through exec chunks.
+ while (data || !done) {
+ char **out;
+
+ TT.entries = 0;
+ TT.bytes = bytes;
+
+ // Loop reading input
+ for (;;) {
+
+ // Read line
+ if (!data) {
+ ssize_t l = 0;
+ l = getdelim(&data, (size_t *)&l, TT.delim, stdin);
+
+ if (l<0) {
+ data = 0;
+ done++;
+ break;
+ }
+ }
+ dlist_add(&dlist, data);
+
+ // Count data used
+ data = handle_entries(data, NULL);
+ if (!data) continue;
+ if (data == (char *)2) done++;
+ if ((long)data <= 2) data = 0;
+ else data = xstrdup(data);
+
+ break;
+ }
+
+ // Accumulate cally thing
+
+ if (data && !TT.entries) error_exit("argument too long");
+ out = xzalloc((entries+TT.entries+1)*sizeof(char *));
+
+ if (dlist) {
+ struct double_list *dtemp;
+
+ // Fill out command line to exec
+ memcpy(out, toys.optargs, entries*sizeof(char *));
+ TT.entries = 0;
+ TT.bytes = bytes;
+ dlist->prev->next = 0;
+ for (dtemp = dlist; dtemp; dtemp = dtemp->next)
+ handle_entries(dtemp->data, out+entries);
+ }
+ pid_t pid=fork();
+ if (!pid) {
+ xclose(0);
+ open("/dev/null", O_RDONLY);
+ xexec(out);
+ }
+ waitpid(pid, &status, 0);
+ status = WEXITSTATUS(status);
+
+ // Abritrary number of execs, can't just leak memory each time...
+ while (dlist) {
+ struct double_list *dtemp = dlist->next;
+
+ free(dlist->data);
+ free(dlist);
+ dlist = dtemp;
+ }
+ free(out);
+ }
+}