aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorElliott Hughes <enh@google.com>2016-09-12 11:23:48 -0700
committerElliott Hughes <enh@google.com>2016-09-12 11:23:48 -0700
commitacd101ac633638e84dddcbaf0db2bf23fb2f1280 (patch)
tree144f03ac62ee54d339d01ecdd3e96afea90a0813
parent0f7f3a529143f7db542311303c675a6887283afc (diff)
parent9f42e832a186bb3f5320581a3a713d0a8a9c8384 (diff)
downloadandroid_external_toybox-acd101ac633638e84dddcbaf0db2bf23fb2f1280.tar.gz
android_external_toybox-acd101ac633638e84dddcbaf0db2bf23fb2f1280.tar.bz2
android_external_toybox-acd101ac633638e84dddcbaf0db2bf23fb2f1280.zip
Merge remote-tracking branch 'toybox/master' into HEAD
-rw-r--r--Makefile1
-rw-r--r--lib/help.c22
-rw-r--r--lib/lib.c43
-rw-r--r--lib/lib.h10
-rw-r--r--lib/xwrap.c22
-rw-r--r--scripts/runtest.sh2
-rwxr-xr-xtests/cmp.test6
-rwxr-xr-xtests/sed.test2
-rw-r--r--toys/other/fsync.c4
-rw-r--r--toys/other/truncate.c6
-rw-r--r--toys/posix/cmp.c10
-rw-r--r--toys/posix/file.c33
-rw-r--r--toys/posix/grep.c9
-rw-r--r--toys/posix/sed.c49
-rw-r--r--toys/posix/tail.c4
-rw-r--r--toys/posix/tee.c4
-rw-r--r--www/code.html61
17 files changed, 137 insertions, 151 deletions
diff --git a/Makefile b/Makefile
index 711d6247..6c5dd0d4 100644
--- a/Makefile
+++ b/Makefile
@@ -55,6 +55,7 @@ change:
clean::
rm -rf toybox generated change .singleconfig*
+# If singlemake was in generated/ "make clean; make test_ls" wouldn't work.
distclean: clean
rm -f toybox_old .config* .singlemake
diff --git a/lib/help.c b/lib/help.c
index 87a3df94..d0185678 100644
--- a/lib/help.c
+++ b/lib/help.c
@@ -2,9 +2,6 @@
#include "toys.h"
-#if !CFG_TOYBOX_HELP
-void show_help(FILE *out) {;}
-#else
#include "generated/help.h"
#undef NEWTOY
@@ -24,14 +21,15 @@ void show_help(FILE *out)
int i = toys.which-toy_list;
char *s;
- for (;;) {
- s = help_data;
- while (i--) s += strlen(s) + 1;
- // If it's an alias, restart search for real name
- if (*s != 255) break;
- i = toy_find(++s)-toy_list;
- }
+ if (CFG_TOYBOX_HELP) {
+ for (;;) {
+ s = help_data;
+ while (i--) s += strlen(s) + 1;
+ // If it's an alias, restart search for real name
+ if (*s != 255) break;
+ i = toy_find(++s)-toy_list;
+ }
- fprintf(out, "%s", s);
+ fprintf(out, "%s", s);
+ }
}
-#endif
diff --git a/lib/lib.c b/lib/lib.c
index 8f8b4a3b..fe85cbed 100644
--- a/lib/lib.c
+++ b/lib/lib.c
@@ -547,16 +547,20 @@ void poke(void *ptr, uint64_t val, int size)
}
// Iterate through an array of files, opening each one and calling a function
-// on that filehandle and name. The special filename "-" means stdin if
-// flags is O_RDONLY, stdout otherwise. An empty argument list calls
+// on that filehandle and name. The special filename "-" means stdin if
+// flags is O_RDONLY, stdout otherwise. An empty argument list calls
// function() on just stdin/stdout.
//
// Note: pass O_CLOEXEC to automatically close filehandles when function()
-// returns, otherwise filehandles must be closed by function()
-void loopfiles_rw(char **argv, int flags, int permissions, int failok,
+// returns, otherwise filehandles must be closed by function().
+// pass WARN_ONLY to produce warning messages about files it couldn't
+// open/create, and skip them. Otherwise function is called with fd -1.
+void loopfiles_rw(char **argv, int flags, int permissions,
void (*function)(int fd, char *name))
{
- int fd;
+ int fd, failok = !(flags&WARN_ONLY);
+
+ flags &= ~WARN_ONLY;
// If no arguments, read from stdin.
if (!*argv) function((flags & O_ACCMODE) != O_RDONLY ? 1 : 0, "-");
@@ -564,8 +568,8 @@ void loopfiles_rw(char **argv, int flags, int permissions, int failok,
// Filename "-" means read from stdin.
// Inability to open a file prints a warning, but doesn't exit.
- if (!strcmp(*argv, "-")) fd=0;
- else if (0>(fd = open(*argv, flags, permissions)) && !failok) {
+ if (!strcmp(*argv, "-")) fd = 0;
+ else if (0>(fd = notstdio(open(*argv, flags, permissions))) && !failok) {
perror_msg_raw(*argv);
continue;
}
@@ -574,10 +578,10 @@ void loopfiles_rw(char **argv, int flags, int permissions, int failok,
} while (*++argv);
}
-// Call loopfiles_rw with O_RDONLY|O_CLOEXEC and !failok (common case).
+// Call loopfiles_rw with O_RDONLY|O_CLOEXEC|WARN_ONLY (common case)
void loopfiles(char **argv, void (*function)(int fd, char *name))
{
- loopfiles_rw(argv, O_RDONLY|O_CLOEXEC, 0, 0, function);
+ loopfiles_rw(argv, O_RDONLY|O_CLOEXEC|WARN_ONLY, 0, function);
}
// Slow, but small.
@@ -1207,3 +1211,24 @@ char *getgroupname(gid_t gid)
return gr ? gr->gr_name : gnum;
}
+// Iterate over lines in file, calling function. Function can write 0 to
+// the line pointer if they want to keep it, or 1 to terminate processing,
+// otherwise line is freed. Passed file descriptor is closed at the end.
+void do_lines(int fd, void (*call)(char **pline, long len))
+{
+ FILE *fp = fd ? xfdopen(fd, "r") : stdin;
+
+ for (;;) {
+ char *line = 0;
+ ssize_t len;
+
+ len = getline(&line, (void *)&len, fp);
+ if (len > 0) {
+ call(&line, len);
+ if (line == (void *)1) break;
+ free(line);
+ } else break;
+ }
+
+ if (fd) fclose(fp);
+}
diff --git a/lib/lib.h b/lib/lib.h
index 43d6b1ff..69692b73 100644
--- a/lib/lib.h
+++ b/lib/lib.h
@@ -102,6 +102,11 @@ struct dirtree *dirtree_read(char *path, int (*callback)(struct dirtree *node));
void show_help(FILE *out);
+// Tell xopen and friends to print warnings but return -1 as necessary
+// The largest O_BLAH flag so far is arch/alpha's O_PATH at 0x800000 so
+// plenty of headroom.
+#define WARN_ONLY (1<<31)
+
// xwrap.c
void xstrncpy(char *dest, char *src, size_t size);
void xstrncat(char *dest, char *src, size_t size);
@@ -132,10 +137,12 @@ int xcreate(char *path, int flags, int mode);
int xopen(char *path, int flags);
int xcreate_stdio(char *path, int flags, int mode);
int xopen_stdio(char *path, int flags);
+int openro(char *path, int flags);
int xopenro(char *path);
void xpipe(int *pp);
void xclose(int fd);
int xdup(int fd);
+int notstdio(int fd);
FILE *xfdopen(int fd, char *mode);
FILE *xfopen(char *path, char *mode);
size_t xread(int fd, void *buf, size_t len);
@@ -198,7 +205,7 @@ char *chomp(char *s);
int unescape(char c);
int strstart(char **a, char *b);
off_t fdlength(int fd);
-void loopfiles_rw(char **argv, int flags, int permissions, int failok,
+void loopfiles_rw(char **argv, int flags, int permissions,
void (*function)(int fd, char *name));
void loopfiles(char **argv, void (*function)(int fd, char *name));
void xsendfile(int in, int out);
@@ -225,6 +232,7 @@ int regexec0(regex_t *preg, char *string, long len, int nmatch,
regmatch_t pmatch[], int eflags);
char *getusername(uid_t uid);
char *getgroupname(gid_t gid);
+void do_lines(int fd, void (*call)(char **pline, long len));
#define HR_SPACE 1 // Space between number and units
#define HR_B 2 // Use "B" for single byte units
diff --git a/lib/xwrap.c b/lib/xwrap.c
index 48e02965..66972f2b 100644
--- a/lib/xwrap.c
+++ b/lib/xwrap.c
@@ -318,11 +318,13 @@ void xunlink(char *path)
}
// Die unless we can open/create a file, returning file descriptor.
+// The meaning of O_CLOEXEC is reversed (it defaults on, pass it to disable)
+// and WARN_ONLY tells us not to exit.
int xcreate_stdio(char *path, int flags, int mode)
{
- int fd = open(path, flags^O_CLOEXEC, mode);
+ int fd = open(path, (flags^O_CLOEXEC)&~WARN_ONLY, mode);
- if (fd == -1) perror_exit_raw(path);
+ if (fd == -1) ((mode&WARN_ONLY) ? perror_msg_raw : perror_exit_raw)(path);
return fd;
}
@@ -355,6 +357,8 @@ int xdup(int fd)
// old one. (We should never be called with stdin/stdout/stderr closed, but...)
int notstdio(int fd)
{
+ if (fd<0) return fd;
+
while (fd<3) {
int fd2 = xdup(fd);
@@ -378,12 +382,18 @@ int xopen(char *path, int flags)
return notstdio(xopen_stdio(path, flags));
}
-// Open read only, treating "-" as a synonym for stdin.
-int xopenro(char *path)
+// Open read only, treating "-" as a synonym for stdin, defaulting to warn only
+int openro(char *path, int flags)
{
if (!strcmp(path, "-")) return 0;
- return xopen(path, O_RDONLY);
+ return xopen(path, flags^WARN_ONLY);
+}
+
+// Open read only, treating "-" as a synonym for stdin.
+int xopenro(char *path)
+{
+ return openro(path, O_RDONLY|WARN_ONLY);
}
FILE *xfdopen(int fd, char *mode)
@@ -674,6 +684,8 @@ char *xreadfile(char *name, char *buf, off_t len)
return buf;
}
+// The data argument to ioctl() is actually long, but it's usually used as
+// a pointer. If you need to feed in a number, do (void *)(long) typecast.
int xioctl(int fd, int request, void *data)
{
int rc;
diff --git a/scripts/runtest.sh b/scripts/runtest.sh
index 13b82f14..6e94d945 100644
--- a/scripts/runtest.sh
+++ b/scripts/runtest.sh
@@ -109,7 +109,7 @@ testing()
[ -n "$DEBUG" ] && set +x
- return $RETVAL
+ return 0
}
# Recursively grab an executable and all the libraries needed to run it.
diff --git a/tests/cmp.test b/tests/cmp.test
index c3e8f5be..3b2dd1b2 100755
--- a/tests/cmp.test
+++ b/tests/cmp.test
@@ -22,12 +22,14 @@ testing "EOF, return code" "cmp input input2 2>/dev/null || echo yes" "yes\n" "a
testing "diff, stdout" "cmp input input2" "input input2 differ: char 4, line 2\n" "ab\nx\nx" ""
testing "diff, return code" "cmp input input2 > /dev/null || echo yes" "yes\n" "ab\nx\nx" ""
-testing "-s EOF, return code" "cmp -s input input2 || echo yes" "yes\n" "ab\nc\nx" ""
-testing "-s diff, return code" "cmp -s input input2 || echo yes" "yes\n" "ab\nx\nx" ""
+testing "-s EOF, return code" "cmp -s input input2 2>&1 || echo yes" "yes\n" "ab\nc\nx" ""
+testing "-s diff, return code" "cmp -s input input2 2>&1 || echo yes" "yes\n" "ab\nx\nx" ""
testing "-l EOF, stderr" "cmp -l input input2 2>&1" "cmp: EOF on input2\n" "ab\nc\nx" ""
testing "-l diff and EOF, stdout and stderr" "cmp -l input input2 2>&1 | sort" "4 170 143\ncmp: EOF on input2\n" "ab\nx\nx" ""
+testing "-s not exist" "cmp -s input doesnotexist 2>&1 || echo yes" "yes\n" "" ""
+
rm input2
testing "stdin and file" "cmp input -" "input - differ: char 4, line 2\n" "ab\nc\n" "ab\nx\n"
diff --git a/tests/sed.test b/tests/sed.test
index d25148ed..f48b73e6 100755
--- a/tests/sed.test
+++ b/tests/sed.test
@@ -57,6 +57,8 @@ testing 'multiple regex address match' 'sed -n /on/,/off/p' \
'zap\nbone\nturtle\scoff\nfred\ntron\nlurid\noffer\nbecause\n'
testing 'regex address overlap' 'sed -n /on/,/off/p' "on\nzap\noffon\n" "" \
'on\nzap\noffon\nping\noff\n'
+testing 'getdelim with nested [:blah:]' 'sed -n "sa\a[a[:space:]bc]*aXXagp"' \
+ "ABXXCDXXEFXXGHXXIXX" "" "ABaaCDa EFaa aGHa a Ia "
# gGhHlnNpPqrstwxy:=
# s///#comment
diff --git a/toys/other/fsync.c b/toys/other/fsync.c
index e6f6c8d9..bd73f398 100644
--- a/toys/other/fsync.c
+++ b/toys/other/fsync.c
@@ -28,6 +28,6 @@ static void do_fsync(int fd, char *name)
void fsync_main(void)
{
- loopfiles_rw(toys.optargs, O_RDONLY|O_NOATIME|O_NOCTTY|O_CLOEXEC,
- 0, 0, do_fsync);
+ loopfiles_rw(toys.optargs, O_RDONLY|O_NOATIME|O_NOCTTY|O_CLOEXEC|WARN_ONLY,
+ 0, do_fsync);
}
diff --git a/toys/other/truncate.c b/toys/other/truncate.c
index bfe1f10c..6092d5a9 100644
--- a/toys/other/truncate.c
+++ b/toys/other/truncate.c
@@ -53,13 +53,13 @@ static void do_truncate(int fd, char *name)
void truncate_main(void)
{
- int cr = !(toys.optflags&1);
+ int cr = !(toys.optflags&FLAG_c);
if (-1 != (TT.type = stridx("+-<>/%", *TT.s))) TT.s++;
TT.size = atolx(TT.s);
// Create files with mask rwrwrw.
// Nonexistent files are only an error if we're supposed to create them.
- loopfiles_rw(toys.optargs, O_WRONLY|O_CLOEXEC|(cr ? O_CREAT : 0), 0666, cr,
- do_truncate);
+ loopfiles_rw(toys.optargs, O_WRONLY|O_CLOEXEC|(cr ? O_CREAT|WARN_ONLY : 0),
+ 0666, do_truncate);
}
diff --git a/toys/posix/cmp.c b/toys/posix/cmp.c
index 527fbfda..f4c34091 100644
--- a/toys/posix/cmp.c
+++ b/toys/posix/cmp.c
@@ -43,6 +43,8 @@ static void do_cmp(int fd, char *name)
return;
}
+ toys.exitval = 0;
+
for (;;) {
len1 = readall(TT.fd, toybuf, size);
len2 = readall(fd, buf2, size);
@@ -54,11 +56,9 @@ static void do_cmp(int fd, char *name)
if (toys.optflags & FLAG_l)
printf("%ld %o %o\n", byte_no, toybuf[i], buf2[i]);
else {
- if (!(toys.optflags & FLAG_s)) {
+ 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;
}
}
@@ -79,6 +79,8 @@ out:
void cmp_main(void)
{
- loopfiles_rw(toys.optargs, O_CLOEXEC, 0, toys.optflags&FLAG_s, do_cmp);
+ toys.exitval = 2;
+ loopfiles_rw(toys.optargs, O_CLOEXEC|(WARN_ONLY*!(toys.optflags&FLAG_s)), 0,
+ do_cmp);
}
diff --git a/toys/posix/file.c b/toys/posix/file.c
index 0cd9047c..27b4c0c2 100644
--- a/toys/posix/file.c
+++ b/toys/posix/file.c
@@ -221,24 +221,20 @@ static void do_regular_file(int fd, char *name, struct stat *sb)
else if (toybuf[5] == '1') cpioformat = "SVR4 with no CRC";
else if (toybuf[5] == '2') cpioformat = "SVR4 with CRC";
xprintf("ASCII cpio archive (%s)\n", cpioformat);
- }
- else if (len>33 && (magic=peek(&s,2), magic==0143561 || magic==070707)) {
+ } else if (len>33 && (magic=peek(&s,2), magic==0143561 || magic==070707)) {
if (magic == 0143561) printf("byte-swapped ");
xprintf("cpio archive\n");
- }
// tar archive (ustar/pax or gnu)
- else if (len>500 && !strncmp(s+257, "ustar", 5)) {
+ } else if (len>500 && !strncmp(s+257, "ustar", 5)) {
xprintf("POSIX tar archive%s\n", strncmp(s+262," ",2)?"":" (GNU)");
- }
// zip/jar/apk archive, ODF/OOXML document, or such
- else if (len>5 && strstart(&s, "PK\03\04")) {
+ } else if (len>5 && strstart(&s, "PK\03\04")) {
int ver = (int)(char)(toybuf[4]);
xprintf("Zip archive data");
if (ver)
xprintf(", requires at least v%d.%d to extract", ver/10, ver%10);
xputc('\n');
- }
- else {
+ } else {
char *what = 0;
int i, bytes;
@@ -279,23 +275,22 @@ void file_main(void)
// Can't use loopfiles here because it doesn't call function when can't open
for (arg = toys.optargs; *arg; arg++) {
- struct stat sb;
char *name = *arg, *what = "cannot open";
+ struct stat sb;
+ int fd = !strcmp(name, "-");
xprintf("%s: %*s", name, (int)(TT.max_name_len - strlen(name)), "");
- if (!lstat(name, &sb)) {
- if (S_ISFIFO(sb.st_mode)) what = "fifo";
- else if (S_ISREG(sb.st_mode)) {
- int fd = !strcmp(name, "-") ? 0 : open(name, O_RDONLY);
-
- if (fd!=-1) {
- if (!sb.st_size) what = "empty";
- else do_regular_file(fd, name, &sb);
+ if (fd || !lstat(name, &sb)) {
+ if (fd || S_ISREG(sb.st_mode)) {
+ if (!sb.st_size) what = "empty";
+ else if ((fd = openro(name, O_RDONLY)) != -1) {
+ do_regular_file(fd, name, &sb);
if (fd) close(fd);
- if (sb.st_size) continue;
+ continue;
}
- } else if (S_ISBLK(sb.st_mode)) what = "block special";
+ } else if (S_ISFIFO(sb.st_mode)) what = "fifo";
+ else if (S_ISBLK(sb.st_mode)) what = "block special";
else if (S_ISCHR(sb.st_mode)) what = "character special";
else if (S_ISDIR(sb.st_mode)) what = "directory";
else if (S_ISSOCK(sb.st_mode)) what = "socket";
diff --git a/toys/posix/grep.c b/toys/posix/grep.c
index 6423ea6b..2fe6ac27 100644
--- a/toys/posix/grep.c
+++ b/toys/posix/grep.c
@@ -81,17 +81,12 @@ static void outline(char *line, char dash, char *name, long lcount, long bcount,
static void do_grep(int fd, char *name)
{
struct double_list *dlb = 0;
- FILE *file = fdopen(fd, "r");
+ FILE *file = xfdopen(fd, "r");
long lcount = 0, mcount = 0, offset = 0, after = 0, before = 0;
char *bars = 0;
if (!fd) name = "(standard input)";
- if (!file) {
- perror_msg_raw(name);
- return;
- }
-
// Loop through lines of input
for (;;) {
char *line = 0, *start;
@@ -368,5 +363,5 @@ void grep_main(void)
if (!strcmp(*ss, "-")) do_grep(0, *ss);
else dirtree_read(*ss, do_grep_r);
}
- } else loopfiles_rw(ss, O_RDONLY, 0, 1, do_grep);
+ } else loopfiles_rw(ss, O_RDONLY|WARN_ONLY, 0, do_grep);
}
diff --git a/toys/posix/sed.c b/toys/posix/sed.c
index 71988248..31268ece 100644
--- a/toys/posix/sed.c
+++ b/toys/posix/sed.c
@@ -621,30 +621,6 @@ done:
free(line);
}
-// Genericish function, can probably get moved to lib.c
-
-// Iterate over lines in file, calling function. Function can write 0 to
-// the line pointer if they want to keep it, or 1 to terminate processing,
-// otherwise line is freed. Passed file descriptor is closed at the end.
-static void do_lines(int fd, void (*call)(char **pline, long len))
-{
- FILE *fp = fd ? xfdopen(fd, "r") : stdin;
-
- for (;;) {
- char *line = 0;
- ssize_t len;
-
- len = getline(&line, (void *)&len, fp);
- if (len > 0) {
- call(&line, len);
- if (line == (void *)1) break;
- free(line);
- } else break;
- }
-
- if (fd) fclose(fp);
-}
-
// Callback called on each input file
static void do_sed(int fd, char *name)
{
@@ -681,6 +657,7 @@ static char *unescape_delimited_string(char **pstr, char *delim)
{
char *to, *from, mode = 0, d;
+ // Grab leading delimiter (if necessary), allocate space for new string
from = *pstr;
if (!delim || !*delim) {
if (!(d = *(from++))) return 0;
@@ -694,13 +671,23 @@ static char *unescape_delimited_string(char **pstr, char *delim)
if (!*from) return 0;
// delimiter in regex character range doesn't count
- if (!mode && *from == '[') {
- mode = '[';
- if (from[1]=='-' || from[1]==']') *(to++) = *(from++);
- } else if (mode && *from == ']') mode = 0;
+ if (*from == '[') {
+ if (!mode) {
+ mode = ']';
+ if (from[1]=='-' || from[1]==']') *(to++) = *(from++);
+ } else if (mode == ']' && strchr(".=:", from[1])) {
+ *(to++) = *(from++);
+ mode = *from;
+ }
+ } else if (*from == mode) {
+ if (mode == ']') mode = 0;
+ else {
+ *(to++) = *(from++);
+ mode = ']';
+ }
// Length 1 range (X-X with same X) is "undefined" and makes regcomp err,
// but the perl build does it, so we need to filter it out.
- else if (mode && *from == '-' && from[-1] == from[1]) {
+ } else if (mode && *from == '-' && from[-1] == from[1]) {
from+=2;
continue;
} else if (*from == '\\') {
@@ -1034,8 +1021,8 @@ void sed_main(void)
TT.fdout = 1;
TT.remember = xstrdup("");
- // Inflict pattern upon input files
- loopfiles_rw(args, O_RDONLY, 0, 0, do_sed);
+ // Inflict pattern upon input files. Long version because !O_CLOEXEC
+ loopfiles_rw(args, O_RDONLY|WARN_ONLY, 0, do_sed);
if (!(toys.optflags & FLAG_i)) process_line(0, 0);
diff --git a/toys/posix/tail.c b/toys/posix/tail.c
index 787e116e..22b5ac6d 100644
--- a/toys/posix/tail.c
+++ b/toys/posix/tail.c
@@ -242,8 +242,8 @@ void tail_main(void)
if ((TT.ffd = inotify_init()) < 0) perror_exit("inotify_init");
TT.files = xmalloc(toys.optc*8);
}
- loopfiles_rw(args, O_RDONLY|(O_CLOEXEC*!(toys.optflags&FLAG_f)),
- 0, 0, do_tail);
+ loopfiles_rw(args, O_RDONLY|WARN_ONLY|(O_CLOEXEC*!(toys.optflags&FLAG_f)),
+ 0, do_tail);
if ((toys.optflags & FLAG_f) && TT.file_no) {
int len, last_fd = TT.files[(TT.file_no-1)*2], i, fd;
diff --git a/toys/posix/tee.c b/toys/posix/tee.c
index d5591b67..6167c8ad 100644
--- a/toys/posix/tee.c
+++ b/toys/posix/tee.c
@@ -49,8 +49,8 @@ void tee_main(void)
// Open output files
loopfiles_rw(toys.optargs,
- O_RDWR|O_CREAT|((toys.optflags & FLAG_a)?O_APPEND:O_TRUNC),
- 0666, 0, do_tee_open);
+ O_RDWR|O_CREAT|WARN_ONLY|((toys.optflags & FLAG_a)?O_APPEND:O_TRUNC),
+ 0666, do_tee_open);
for (;;) {
struct fd_list *fdl;
diff --git a/www/code.html b/www/code.html
index c0566b4c..77b0b211 100644
--- a/www/code.html
+++ b/www/code.html
@@ -743,12 +743,8 @@ away in libc.</p></blockquote>
<li><p><b>struct passwd *xgetpwuid(uid_t uid)<br />
struct group *xgetgrgid(gid_t gid)<br />
struct passwd *xgetpwnam(char *name)</b></p>
-
-<p></p>
</li>
-
-
<li><b>void xsetuser(struct passwd *pwd)</b></li>
<li><b>char *xreadlink(char *name)</b></li>
<li><b>char *xreadfile(char *name, char *buf, off_t len)</b></li>
@@ -760,53 +756,7 @@ struct passwd *xgetpwnam(char *name)</b></p>
</ul>
<a name="lib_lib"><h3>lib/lib.c</h3>
-<p>Eight gazillion common functions:</p>
-
-<ul>
-<li><b>void verror_msg(char *msg, int err, va_list va)</b></li>
-<li><b>void error_msg(char *msg, ...)</b></li>
-<li><b>void perror_msg(char *msg, ...)</b></li>
-<li><b>void error_exit(char *msg, ...)</b></li>
-<li><b>void perror_exit(char *msg, ...)</b></li>
-<li><b>ssize_t readall(int fd, void *buf, size_t len)</b></li>
-<li><b>ssize_t writeall(int fd, void *buf, size_t len)</b></li>
-<li><b>off_t lskip(int fd, off_t offset)</b></li>
-<li><b>int mkpathat(int atfd, char *dir, mode_t lastmode, int flags)</b></li>
-<li><b>struct string_list **splitpath(char *path, struct string_list **list)</b></li>
-<li><b>struct string_list *find_in_path(char *path, char *filename)</b></li>
-<li><b>long atolx(char *numstr)</b></li>
-<li><b>long atolx_range(char *numstr, long low, long high)</b></li>
-<li><b>int numlen(long l)</b></li>
-<li><b>int stridx(char *haystack, char needle)</b></li>
-<li><b>int strstart(char **a, char *b)</b></li>
-<li><b>off_t fdlength(int fd)</b></li>
-<li><b>char *readfile(char *name, char *ibuf, off_t len)</b></li>
-<li><b>void msleep(long miliseconds)</b></li>
-<li><b>int64_t peek_le(void *ptr, unsigned size)</b></li>
-<li><b>int64_t peek_be(void *ptr, unsigned size)</b></li>
-<li><b>int64_t peek(void *ptr, unsigned size)</b></li>
-<li><b>void poke(void *ptr, uint64_t val, int size)</b></li>
-<li><b>void loopfiles_rw(char **argv, int flags, int permissions, int failok,</b></li>
-<li><b>void loopfiles(char **argv, void (*function)(int fd, char *name))</b></li>
-<li><b>char *get_rawline(int fd, long *plen, char end)</b></li>
-<li><b>char *get_line(int fd)</b></li>
-<li><b>int wfchmodat(int fd, char *name, mode_t mode)</b></li>
-<li><b>static void tempfile_handler(int i)</b></li>
-<li><b>int copy_tempfile(int fdin, char *name, char **tempname)</b></li>
-<li><b>void delete_tempfile(int fdin, int fdout, char **tempname)</b></li>
-<li><b>void replace_tempfile(int fdin, int fdout, char **tempname)</b></li>
-<li><b>void crc_init(unsigned int *crc_table, int little_endian)</b></li>
-<li><b>int terminal_size(unsigned *xx, unsigned *yy)</b></li>
-<li><b>int yesno(char *prompt, int def)</b></li>
-<li><b>void generic_signal(int sig)</b></li>
-<li><b>void sigatexit(void *handler)</b></li>
-<li><b>int sig_to_num(char *pidstr)</b></li>
-<li><b>char *num_to_sig(int sig)</b></li>
-<li><b>mode_t string_to_mode(char *modestr, mode_t mode)</b></li>
-<li><b>void mode_to_string(mode_t mode, char *buf)</b></li>
-<li><b>void names_to_pid(char **names, int (*callback)(pid_t pid, char *name))</b></li>
-<li><b>int human_readable(char *buf, unsigned long long num)</b></li>
-</ul>
+<p>Eight gazillion common functions, see lib/lib.h for the moment:</p>
<h3>lib/portability.h</h3>
@@ -1087,6 +1037,15 @@ in the same order they're declared, and that padding won't be inserted between
consecutive variables of register size. Thus the first few entries can
be longs or pointers corresponding to the saved arguments.</p>
+<p>The main downside is that numeric arguments ("#" and "-" format)
+are limited to +- 2 billion on 32 bit platforms (the "truncate -s 8G"
+problem), because long is only 64 bits on 64 bit hosts, so the capabilities
+of some tools differ when built in 32 bit vs 64 bit mode. Fixing this
+kind of ugly and even embedded designs are slowly moving to 64 bits,
+so our current plan is to document the problem and wait it out. (If
+"x32 mode" and similar becomes popular enough, we may revisit this
+decision.)</p>
+
<p>See toys/example/*.c for longer examples of parsing options into the
GLOBALS block.</p>