Some busybox updates.  See the changelog for details if you care.
 -Erik
diff --git a/Changelog b/Changelog
index 673d1aa..4dbb46e 100644
--- a/Changelog
+++ b/Changelog
@@ -15,12 +15,23 @@
 	* Optional support contributed by Ben Collins <bcollins@debian.org> 
 	    for the kernel init chroot patch by Werner Almesberger, which 
 	    allows init to chroot to a new device, and umount the old one.
-	* added (and documented) "-n" option for head - 
-	    contributed Friedrich Vedder <fwv@myrtle.lahn.de>
-	* Cleanup for a number of usage messages -- also 
-	    contributed Friedrich Vedder <fwv@myrtle.lahn.de>
 	* Fixed bug that wouldn't let one chown a symlink -- it would
 	  always dereference before.  -beppu
+	* Fixed a bug where init could have reference already freed memory.
+	    Found and fixed by Taketoshi Sano <kgh12351@nifty.ne.jp>
+	* Several contributions from Friedrich Vedder <fwv@myrtle.lahn.de>
+	* added (and documented) "-n" option for head - 
+	* Cleanup for a number of usage messages -- also 
+	    contributed Friedrich Vedder <fwv@myrtle.lahn.de>
+	* Cosmetic fix to busybox.c (Don't print a comma at the
+	    end of line if there are no more application names).
+	* Fixed a stupid bug in "head" option handling ("head -n" would segfault).
+	* Moved commonly used functions "xmalloc()" and "exit()"
+	    to utility.c (with proper #ifdef's).
+	* Created a tiny tail implementation, removing -c, -q, -v, and making
+	    tail -f work only with a single file.  This reduced tail 
+	    from 6k to 2.4k.  The bigger/more featured tail can still be
+	    had by disabling BB_FEATURE_SIMPLE_TAIL in dusybox.defs.h
 
 
 	-Erik Andersen
diff --git a/applets/busybox.c b/applets/busybox.c
index 67485de..d59b285 100644
--- a/applets/busybox.c
+++ b/applets/busybox.c
@@ -83,7 +83,7 @@
 #ifdef BB_FREE			//usr/bin
     {"free", free_main},
 #endif
-#ifdef BB_DEALLOCVT			//usr/bin
+#ifdef BB_DEALLOCVT		//usr/bin
     {"deallocvt", deallocvt_main},
 #endif
 #ifdef BB_FSCK_MINIX		//sbin
@@ -328,7 +328,7 @@
 
 	while (a->name != 0) {
 	    col+=fprintf(stderr, "%s%s", ((col==0)? "\t":", "), (a++)->name);
-	    if (col>60) {
+	    if (col>60 && a->name != 0) {
 		fprintf(stderr, ",\n");
 		col=0;
 	    }
diff --git a/archival/gunzip.c b/archival/gunzip.c
index 84f5d02..fddcc76 100644
--- a/archival/gunzip.c
+++ b/archival/gunzip.c
@@ -321,6 +321,9 @@
 #define WARN(msg) {fprintf msg ; \
 		   if (exit_code == OK) exit_code = WARNING;}
 
+#define do_exit(c) exit(c)
+
+
 	/* in unzip.c */
 extern int unzip      OF((int in, int out));
 
@@ -359,7 +362,6 @@
 extern void warn          OF((char *a, char *b));
 extern void read_error    OF((void));
 extern void write_error   OF((void));
-extern voidp xmalloc      OF((unsigned int size));
 
 	/* in inflate.c */
 extern int inflate OF((void));
@@ -679,7 +681,6 @@
 /* local functions */
 
 local int  get_method   OF((int in));
-local void do_exit(int exitcode) __attribute__ ((noreturn));
 
 #define strequ(s1, s2) (strcmp((s1),(s2)) == 0)
 
@@ -927,30 +928,6 @@
     }
 }
 
-
-/* ========================================================================
- * Free all dynamically allocated variables and exit with the given code.
- */
-local void do_exit(exitcode)
-    int exitcode;
-{
-    static int in_exit = 0;
-
-    if (in_exit) exit(exitcode);
-    in_exit = 1;
-    FREE(inbuf);
-    FREE(outbuf);
-    FREE(d_buf);
-    FREE(window);
-#ifndef MAXSEG_64K
-    FREE(tab_prefix);
-#else
-    FREE(tab_prefix0);
-    FREE(tab_prefix1);
-#endif
-    exit(exitcode);
-}
-
 /* ========================================================================
  * Signal and error handler.
  */
@@ -1284,13 +1261,6 @@
 /* ========================================================================
  * Error handlers.
  */
-void error(m)
-    char *m;
-{
-    fprintf(stderr, "\n%s\n", m);
-    abort_gzip();
-}
-
 void warn(a, b)
     char *a, *b;            /* message strings juxtaposed in output */
 {
@@ -1317,18 +1287,6 @@
 
 
 /* ========================================================================
- * Semi-safe malloc -- never returns NULL.
- */
-voidp xmalloc (size)
-    unsigned size;
-{
-    voidp cp = (voidp)malloc (size);
-
-    if (cp == NULL) error("out of memory");
-    return cp;
-}
-
-/* ========================================================================
  * Table of CRC-32's of all single-byte values (made by makecrc.c)
  */
 static const ulg crc_32_tab[] = {
diff --git a/archival/gzip.c b/archival/gzip.c
index 76df3ad..3438ee4 100644
--- a/archival/gzip.c
+++ b/archival/gzip.c
@@ -277,7 +277,8 @@
 #define WARN(msg) {if (!quiet) fprintf msg ; \
 		   if (exit_code == OK) exit_code = WARNING;}
 
-local void do_exit(int exitcode) __attribute__ ((noreturn));
+#define do_exit(c) exit(c)
+
 
 	/* in zip.c: */
 extern int zip        OF((int in, int out));
@@ -328,7 +329,6 @@
 extern void read_error    OF((void));
 extern void write_error   OF((void));
 extern void display_ratio OF((long num, long den, FILE *file));
-extern voidp xmalloc      OF((unsigned int size));
 
 	/* in inflate.c */
 extern int inflate OF((void));
@@ -1912,29 +1912,6 @@
     do_exit(exit_code);
 }
 
-/* ========================================================================
- * Free all dynamically allocated variables and exit with the given code.
- */
-local void do_exit(int exitcode)
-{
-    static int in_exit = 0;
-
-    if (in_exit) exit(exitcode);
-    in_exit = 1;
-    if (env != NULL)  free(env),  env  = NULL;
-    if (args != NULL) free((char*)args), args = NULL;
-    FREE(inbuf);
-    FREE(outbuf);
-    FREE(d_buf);
-    FREE(window);
-#ifndef MAXSEG_64K
-    FREE(tab_prefix);
-#else
-    FREE(tab_prefix0);
-    FREE(tab_prefix1);
-#endif
-    exit(exitcode);
-}
 /* trees.c -- output deflated data using Huffman coding
  * Copyright (C) 1992-1993 Jean-loup Gailly
  * This is free software; you can redistribute it and/or modify it under the
diff --git a/busybox.c b/busybox.c
index 67485de..d59b285 100644
--- a/busybox.c
+++ b/busybox.c
@@ -83,7 +83,7 @@
 #ifdef BB_FREE			//usr/bin
     {"free", free_main},
 #endif
-#ifdef BB_DEALLOCVT			//usr/bin
+#ifdef BB_DEALLOCVT		//usr/bin
     {"deallocvt", deallocvt_main},
 #endif
 #ifdef BB_FSCK_MINIX		//sbin
@@ -328,7 +328,7 @@
 
 	while (a->name != 0) {
 	    col+=fprintf(stderr, "%s%s", ((col==0)? "\t":", "), (a++)->name);
-	    if (col>60) {
+	    if (col>60 && a->name != 0) {
 		fprintf(stderr, ",\n");
 		col=0;
 	    }
diff --git a/busybox.def.h b/busybox.def.h
index c56f151..8adccdc 100644
--- a/busybox.def.h
+++ b/busybox.def.h
@@ -133,6 +133,11 @@
 //Enable init being called as /linuxrc
 #define BB_FEATURE_LINUXRC
 //
+//
+//Simple tail implementation (2k vs 6k for the full one).  Still
+//provides 'tail -f' support -- but for only one file at a time.
+#define BB_FEATURE_SIMPLE_TAIL
+//
 // Enable support for loop devices in mount
 #define BB_FEATURE_MOUNT_LOOP
 //
diff --git a/coreutils/head.c b/coreutils/head.c
index 4e58bdc..bc7f354 100644
--- a/coreutils/head.c
+++ b/coreutils/head.c
@@ -61,7 +61,7 @@
 	    switch (opt) {
 		case 'n':
 		    tmplen = 0;
-		    if (i++ < argc)
+		    if (++i < argc)
 			tmplen = atoi(argv[i]);
 		    if (tmplen < 1)
 			usage(head_usage);
@@ -105,4 +105,4 @@
     exit(0);
 }
 
-/* $Id: head.c,v 1.5 2000/01/23 18:19:02 erik Exp $ */
+/* $Id: head.c,v 1.6 2000/01/25 18:13:53 erik Exp $ */
diff --git a/coreutils/printf.c b/coreutils/printf.c
index 02d0811..5be3a67 100644
--- a/coreutils/printf.c
+++ b/coreutils/printf.c
@@ -121,8 +121,6 @@
 #define hextobin(c) ((c)>='a'&&(c)<='f' ? (c)-'a'+10 : (c)>='A'&&(c)<='F' ? (c)-'A'+10 : (c)-'0')
 #define octtobin(c) ((c) - '0')
 
-char *xmalloc ();
-
 static double xstrtod __P ((char *s));
 static int print_esc __P ((char *escstart));
 static int print_formatted __P ((char *format, int argc, char **argv));
diff --git a/coreutils/tail.c b/coreutils/tail.c
index 697177d..5198892 100644
--- a/coreutils/tail.c
+++ b/coreutils/tail.c
@@ -1,3 +1,402 @@
+#include "internal.h"
+/* This file contains _two_ implementations of tail.  One is
+ * a bit more full featured, but costs 6k.  The other (i.e. the
+ * SIMPLE_TAIL one) is less capable, but is good enough for about
+ * 99% of the things folks want to use tail for, and only costs 2k.
+ */
+
+
+#ifdef BB_FEATURE_SIMPLE_TAIL
+
+/* tail -- output the last part of file(s)
+   Copyright (C) 89, 90, 91, 95, 1996 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+   Original version by Paul Rubin <phr@ocf.berkeley.edu>.
+   Extensions by David MacKenzie <djm@gnu.ai.mit.edu>.
+   tail -f for multiple files by Ian Lance Taylor <ian@airs.com>.  
+
+   Rewrote the option parser, removed locales support,
+    and generally busyboxed, Erik Andersen <andersen@lineo.com>
+
+   Removed superfluous options and associated code ("-c", "-n", "-q").
+   Removed "tail -f" suport for multiple files.
+   Both changes by Friedrich Vedder <fwv@myrtle.lahn.de>.
+
+ */
+
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <ctype.h>
+
+
+#define XWRITE(fd, buffer, n_bytes)					\
+  do {									\
+      if (n_bytes > 0 && fwrite ((buffer), 1, (n_bytes), stdout) == 0)	\
+	  error("write error");					\
+  } while (0)
+
+/* Number of items to tail.  */
+#define DEFAULT_N_LINES 10
+
+/* Size of atomic reads.  */
+#ifndef BUFSIZ
+#define BUFSIZ (512 * 8)
+#endif
+
+/* If nonzero, read from the end of one file until killed.  */
+static int forever;
+
+/* If nonzero, print filename headers.  */
+static int print_headers;
+
+const char tail_usage[] =
+    "tail [OPTION] [FILE]...\n\n"
+    "Print last 10 lines of each FILE to standard output.\n"
+    "With more than one FILE, precede each with a header giving the\n"
+    "file name. With no FILE, or when FILE is -, read standard input.\n\n"
+    "Options:\n"
+    "\t-n NUM\t\tPrint last NUM lines instead of first 10\n"
+    "\t-f\t\tOutput data as the file grows.  This version\n"
+    "\t\t\tof 'tail -f' supports only one file at a time.\n";
+
+
+static void write_header(const char *filename)
+{
+    static int first_file = 1;
+
+    printf("%s==> %s <==\n", (first_file ? "" : "\n"), filename);
+    first_file = 0;
+}
+
+/* Print the last N_LINES lines from the end of file FD.
+   Go backward through the file, reading `BUFSIZ' bytes at a time (except
+   probably the first), until we hit the start of the file or have
+   read NUMBER newlines.
+   POS starts out as the length of the file (the offset of the last
+   byte of the file + 1).
+   Return 0 if successful, 1 if an error occurred.  */
+
+static int
+file_lines(const char *filename, int fd, long int n_lines, off_t pos)
+{
+    char buffer[BUFSIZ];
+    int bytes_read;
+    int i;			/* Index into `buffer' for scanning.  */
+
+    if (n_lines == 0)
+	return 0;
+
+    /* Set `bytes_read' to the size of the last, probably partial, buffer;
+       0 < `bytes_read' <= `BUFSIZ'.  */
+    bytes_read = pos % BUFSIZ;
+    if (bytes_read == 0)
+	bytes_read = BUFSIZ;
+    /* Make `pos' a multiple of `BUFSIZ' (0 if the file is short), so that all
+       reads will be on block boundaries, which might increase efficiency.  */
+    pos -= bytes_read;
+    lseek(fd, pos, SEEK_SET);
+    bytes_read = fullRead(fd, buffer, bytes_read);
+    if (bytes_read == -1)
+	error("read error");
+
+    /* Count the incomplete line on files that don't end with a newline.  */
+    if (bytes_read && buffer[bytes_read - 1] != '\n')
+	--n_lines;
+
+    do {
+	/* Scan backward, counting the newlines in this bufferfull.  */
+	for (i = bytes_read - 1; i >= 0; i--) {
+	    /* Have we counted the requested number of newlines yet?  */
+	    if (buffer[i] == '\n' && n_lines-- == 0) {
+		/* If this newline wasn't the last character in the buffer,
+		   print the text after it.  */
+		if (i != bytes_read - 1)
+		    XWRITE(STDOUT_FILENO, &buffer[i + 1],
+			   bytes_read - (i + 1));
+		return 0;
+	    }
+	}
+	/* Not enough newlines in that bufferfull.  */
+	if (pos == 0) {
+	    /* Not enough lines in the file; print the entire file.  */
+	    lseek(fd, (off_t) 0, SEEK_SET);
+	    return 0;
+	}
+	pos -= BUFSIZ;
+	lseek(fd, pos, SEEK_SET);
+    }
+    while ((bytes_read = fullRead(fd, buffer, BUFSIZ)) > 0);
+    if (bytes_read == -1)
+	error("read error");
+
+    return 0;
+}
+
+/* Print the last N_LINES lines from the end of the standard input,
+   open for reading as pipe FD.
+   Buffer the text as a linked list of LBUFFERs, adding them as needed.
+   Return 0 if successful, 1 if an error occured.  */
+
+static int pipe_lines(const char *filename, int fd, long int n_lines)
+{
+    struct linebuffer {
+	int nbytes, nlines;
+	char buffer[BUFSIZ];
+	struct linebuffer *next;
+    };
+    typedef struct linebuffer LBUFFER;
+    LBUFFER *first, *last, *tmp;
+    int i;			/* Index into buffers.  */
+    int total_lines = 0;	/* Total number of newlines in all buffers.  */
+    int errors = 0;
+
+    first = last = (LBUFFER *) xmalloc(sizeof(LBUFFER));
+    first->nbytes = first->nlines = 0;
+    first->next = NULL;
+    tmp = (LBUFFER *) xmalloc(sizeof(LBUFFER));
+
+    /* Input is always read into a fresh buffer.  */
+    while ((tmp->nbytes = fullRead(fd, tmp->buffer, BUFSIZ)) > 0) {
+	tmp->nlines = 0;
+	tmp->next = NULL;
+
+	/* Count the number of newlines just read.  */
+	for (i = 0; i < tmp->nbytes; i++)
+	    if (tmp->buffer[i] == '\n')
+		++tmp->nlines;
+	total_lines += tmp->nlines;
+
+	/* If there is enough room in the last buffer read, just append the new
+	   one to it.  This is because when reading from a pipe, `nbytes' can
+	   often be very small.  */
+	if (tmp->nbytes + last->nbytes < BUFSIZ) {
+	    memcpy(&last->buffer[last->nbytes], tmp->buffer, tmp->nbytes);
+	    last->nbytes += tmp->nbytes;
+	    last->nlines += tmp->nlines;
+	} else {
+	    /* If there's not enough room, link the new buffer onto the end of
+	       the list, then either free up the oldest buffer for the next
+	       read if that would leave enough lines, or else malloc a new one.
+	       Some compaction mechanism is possible but probably not
+	       worthwhile.  */
+	    last = last->next = tmp;
+	    if (total_lines - first->nlines > n_lines) {
+		tmp = first;
+		total_lines -= first->nlines;
+		first = first->next;
+	    } else
+		tmp = (LBUFFER *) xmalloc(sizeof(LBUFFER));
+	}
+    }
+    if (tmp->nbytes == -1)
+	error("read error");
+
+    free((char *) tmp);
+
+    /* This prevents a core dump when the pipe contains no newlines.  */
+    if (n_lines == 0)
+	goto free_lbuffers;
+
+    /* Count the incomplete line on files that don't end with a newline.  */
+    if (last->buffer[last->nbytes - 1] != '\n') {
+	++last->nlines;
+	++total_lines;
+    }
+
+    /* Run through the list, printing lines.  First, skip over unneeded
+       buffers.  */
+    for (tmp = first; total_lines - tmp->nlines > n_lines; tmp = tmp->next)
+	total_lines -= tmp->nlines;
+
+    /* Find the correct beginning, then print the rest of the file.  */
+    if (total_lines > n_lines) {
+	char *cp;
+
+	/* Skip `total_lines' - `n_lines' newlines.  We made sure that
+	   `total_lines' - `n_lines' <= `tmp->nlines'.  */
+	cp = tmp->buffer;
+	for (i = total_lines - n_lines; i; --i)
+	    while (*cp++ != '\n')
+		/* Do nothing.  */ ;
+	i = cp - tmp->buffer;
+    } else
+	i = 0;
+    XWRITE(STDOUT_FILENO, &tmp->buffer[i], tmp->nbytes - i);
+
+    for (tmp = tmp->next; tmp; tmp = tmp->next)
+	XWRITE(STDOUT_FILENO, tmp->buffer, tmp->nbytes);
+
+  free_lbuffers:
+    while (first) {
+	tmp = first->next;
+	free((char *) first);
+	first = tmp;
+    }
+    return errors;
+}
+
+/* Display file FILENAME from the current position in FD to the end.
+   If `forever' is nonzero, keep reading from the end of the file
+   until killed.  Return the number of bytes read from the file.  */
+
+static long dump_remainder(const char *filename, int fd)
+{
+    char buffer[BUFSIZ];
+    int bytes_read;
+    long total;
+
+    total = 0;
+  output:
+    while ((bytes_read = fullRead(fd, buffer, BUFSIZ)) > 0) {
+	XWRITE(STDOUT_FILENO, buffer, bytes_read);
+	total += bytes_read;
+    }
+    if (bytes_read == -1)
+	error("read error");
+    if (forever) {
+	fflush(stdout);
+	sleep(1);
+	goto output;
+    }
+
+    return total;
+}
+
+/* Output the last N_LINES lines of file FILENAME open for reading in FD.
+   Return 0 if successful, 1 if an error occurred.  */
+
+static int tail_lines(const char *filename, int fd, long int n_lines)
+{
+    struct stat stats;
+    off_t length;
+
+    if (print_headers)
+	write_header(filename);
+
+    if (fstat(fd, &stats))
+	error("fstat error");
+
+    /* Use file_lines only if FD refers to a regular file with
+       its file pointer positioned at beginning of file.  */
+    /* FIXME: adding the lseek conjunct is a kludge.
+       Once there's a reasonable test suite, fix the true culprit:
+       file_lines.  file_lines shouldn't presume that the input
+       file pointer is initially positioned to beginning of file.  */
+    if (S_ISREG(stats.st_mode)
+	&& lseek(fd, (off_t) 0, SEEK_CUR) == (off_t) 0) {
+	length = lseek(fd, (off_t) 0, SEEK_END);
+	if (length != 0 && file_lines(filename, fd, n_lines, length))
+	    return 1;
+	dump_remainder(filename, fd);
+    } else
+	return pipe_lines(filename, fd, n_lines);
+
+    return 0;
+}
+
+/* Display the last N_UNITS lines of file FILENAME.
+   "-" for FILENAME means the standard input.
+   Return 0 if successful, 1 if an error occurred.  */
+
+static int tail_file(const char *filename, off_t n_units)
+{
+    int fd, errors;
+
+    if (!strcmp(filename, "-")) {
+	filename = "standard input";
+	errors = tail_lines(filename, 0, (long) n_units);
+    } else {
+	/* Not standard input.  */
+	fd = open(filename, O_RDONLY);
+	if (fd == -1)
+	    error("open error");
+
+	errors = tail_lines(filename, fd, (long) n_units);
+	close(fd);
+    }
+
+    return errors;
+}
+
+extern int tail_main(int argc, char **argv)
+{
+    int exit_status = 0;
+    int n_units = DEFAULT_N_LINES;
+    int n_tmp, i;
+    char opt;
+
+    forever = print_headers = 0;
+
+    /* parse argv[] */
+    for (i = 1; i < argc; i++) {
+	if (argv[i][0] == '-') {
+	    opt = argv[i][1];
+	    switch (opt) {
+	    case 'f':
+		forever = 1;
+		break;
+	    case 'n':
+		n_tmp = 0;
+		if (++i < argc)
+		    n_tmp = atoi(argv[i]);
+		if (n_tmp < 1)
+		    usage(tail_usage);
+		n_units = n_tmp;
+		break;
+	    case '-':
+	    case 'h':
+		usage(tail_usage);
+	    default:
+		fprintf(stderr, "tail: invalid option -- %c\n", opt);
+		usage(tail_usage);
+	    }
+	} else {
+	    break;
+	}
+    }
+
+    if (i + 1 < argc) {
+	if (forever) {
+	    fprintf(stderr,
+		    "tail: option -f is invalid with multiple files\n");
+	    usage(tail_usage);
+	}
+	print_headers = 1;
+    }
+
+    if (i >= argc) {
+	exit_status |= tail_file("-", n_units);
+    } else {
+	for (; i < argc; i++)
+	    exit_status |= tail_file(argv[i], n_units);
+    }
+
+    exit(exit_status == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
+}
+
+
+#else
+// Here follows the code for the full featured tail code
+
+
 /* tail -- output the last part of file(s)
    Copyright (C) 89, 90, 91, 95, 1996 Free Software Foundation, Inc.
 
@@ -42,7 +441,7 @@
 #define NDEBUG 1
 
 
-static void error(int i, int errnum, char* fmt, ...)
+static void detailed_error(int i, int errnum, char* fmt, ...)
 {
     va_list arguments;
 
@@ -60,7 +459,7 @@
       assert ((fd) == 1);						\
       assert ((n_bytes) >= 0);						\
       if (n_bytes > 0 && fwrite ((buffer), 1, (n_bytes), stdout) == 0)	\
-	error (EXIT_FAILURE, errno, "write error");			\
+	detailed_error (EXIT_FAILURE, errno, "write error");			\
     }									\
   while (0)
 
@@ -100,8 +499,6 @@
   multiple_files, always, never
 };
 
-char *xmalloc ();
-
 /* The name this program was run with.  */
 char *program_name;
 
@@ -168,7 +565,7 @@
   bytes_read = fullRead (fd, buffer, bytes_read);
   if (bytes_read == -1)
     {
-      error (0, errno, "%s", filename);
+      detailed_error (0, errno, "%s", filename);
       return 1;
     }
 
@@ -204,7 +601,7 @@
   while ((bytes_read = fullRead (fd, buffer, BUFSIZ)) > 0);
   if (bytes_read == -1)
     {
-      error (0, errno, "%s", filename);
+      detailed_error (0, errno, "%s", filename);
       return 1;
     }
   return 0;
@@ -276,7 +673,7 @@
     }
   if (tmp->nbytes == -1)
     {
-      error (0, errno, "%s", filename);
+      detailed_error (0, errno, "%s", filename);
       errors = 1;
       free ((char *) tmp);
       goto free_lbuffers;
@@ -390,7 +787,7 @@
     }
   if (tmp->nbytes == -1)
     {
-      error (0, errno, "%s", filename);
+      detailed_error (0, errno, "%s", filename);
       errors = 1;
       free ((char *) tmp);
       goto free_cbuffers;
@@ -438,7 +835,7 @@
     n_bytes -= bytes_read;
   if (bytes_read == -1)
     {
-      error (0, errno, "%s", filename);
+      detailed_error (0, errno, "%s", filename);
       return 1;
     }
   else if (n_bytes < 0)
@@ -466,7 +863,7 @@
     }
   if (bytes_read == -1)
     {
-      error (0, errno, "%s", filename);
+      detailed_error (0, errno, "%s", filename);
       return 1;
     }
   else if (bytes_to_skip < bytes_read)
@@ -496,7 +893,7 @@
       total += bytes_read;
     }
   if (bytes_read == -1)
-    error (EXIT_FAILURE, errno, "%s", filename);
+    detailed_error (EXIT_FAILURE, errno, "%s", filename);
   if (forever)
     {
       fflush (stdout);
@@ -540,7 +937,7 @@
 	    continue;
 	  if (fstat (file_descs[i], &stats) < 0)
 	    {
-	      error (0, errno, "%s", names[i]);
+	      detailed_error (0, errno, "%s", names[i]);
 	      file_descs[i] = -1;
 	      continue;
 	    }
@@ -590,7 +987,7 @@
      error, either.  */
   if (fstat (fd, &stats))
     {
-      error (0, errno, "%s", filename);
+      detailed_error (0, errno, "%s", filename);
       return 1;
     }
 
@@ -619,7 +1016,7 @@
 	    }
 	  else
 	    {
-	      error (0, errno, "%s", filename);
+	      detailed_error (0, errno, "%s", filename);
 	      return 1;
 	    }
 
@@ -656,7 +1053,7 @@
 
   if (fstat (fd, &stats))
     {
-      error (0, errno, "%s", filename);
+      detailed_error (0, errno, "%s", filename);
       return 1;
     }
 
@@ -723,12 +1120,12 @@
 	{
 	  if (fstat (0, &stats) < 0)
 	    {
-	      error (0, errno, "standard input");
+	      detailed_error (0, errno, "standard input");
 	      errors = 1;
 	    }
 	  else if (!S_ISREG (stats.st_mode))
 	    {
-	      error (0, 0,
+	      detailed_error (0, 0,
 		     "standard input: cannot follow end of non-regular file");
 	      errors = 1;
 	    }
@@ -749,7 +1146,7 @@
 	{
 	  if (forever_multiple)
 	    file_descs[filenum] = -1;
-	  error (0, errno, "%s", filename);
+	  detailed_error (0, errno, "%s", filename);
 	  errors = 1;
 	}
       else
@@ -761,12 +1158,12 @@
 	    {
 	      if (fstat (fd, &stats) < 0)
 		{
-		  error (0, errno, "%s", filename);
+		  detailed_error (0, errno, "%s", filename);
 		  errors = 1;
 		}
 	      else if (!S_ISREG (stats.st_mode))
 		{
-		  error (0, 0, "%s: cannot follow end of non-regular file",
+		  detailed_error (0, 0, "%s: cannot follow end of non-regular file",
 			 filename);
 		  errors = 1;
 		}
@@ -785,7 +1182,7 @@
 	    {
 	      if (close (fd))
 		{
-		  error (0, errno, "%s", filename);
+		  detailed_error (0, errno, "%s", filename);
 		  errors = 1;
 		}
 	    }
@@ -903,8 +1300,11 @@
     }
 
   if (have_read_stdin && close (0) < 0)
-    error (EXIT_FAILURE, errno, "-");
+    detailed_error (EXIT_FAILURE, errno, "-");
   if (fclose (stdout) == EOF)
-    error (EXIT_FAILURE, errno, "write error");
+    detailed_error (EXIT_FAILURE, errno, "write error");
   exit (exit_status == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
 }
+
+
+#endif
diff --git a/gunzip.c b/gunzip.c
index 84f5d02..fddcc76 100644
--- a/gunzip.c
+++ b/gunzip.c
@@ -321,6 +321,9 @@
 #define WARN(msg) {fprintf msg ; \
 		   if (exit_code == OK) exit_code = WARNING;}
 
+#define do_exit(c) exit(c)
+
+
 	/* in unzip.c */
 extern int unzip      OF((int in, int out));
 
@@ -359,7 +362,6 @@
 extern void warn          OF((char *a, char *b));
 extern void read_error    OF((void));
 extern void write_error   OF((void));
-extern voidp xmalloc      OF((unsigned int size));
 
 	/* in inflate.c */
 extern int inflate OF((void));
@@ -679,7 +681,6 @@
 /* local functions */
 
 local int  get_method   OF((int in));
-local void do_exit(int exitcode) __attribute__ ((noreturn));
 
 #define strequ(s1, s2) (strcmp((s1),(s2)) == 0)
 
@@ -927,30 +928,6 @@
     }
 }
 
-
-/* ========================================================================
- * Free all dynamically allocated variables and exit with the given code.
- */
-local void do_exit(exitcode)
-    int exitcode;
-{
-    static int in_exit = 0;
-
-    if (in_exit) exit(exitcode);
-    in_exit = 1;
-    FREE(inbuf);
-    FREE(outbuf);
-    FREE(d_buf);
-    FREE(window);
-#ifndef MAXSEG_64K
-    FREE(tab_prefix);
-#else
-    FREE(tab_prefix0);
-    FREE(tab_prefix1);
-#endif
-    exit(exitcode);
-}
-
 /* ========================================================================
  * Signal and error handler.
  */
@@ -1284,13 +1261,6 @@
 /* ========================================================================
  * Error handlers.
  */
-void error(m)
-    char *m;
-{
-    fprintf(stderr, "\n%s\n", m);
-    abort_gzip();
-}
-
 void warn(a, b)
     char *a, *b;            /* message strings juxtaposed in output */
 {
@@ -1317,18 +1287,6 @@
 
 
 /* ========================================================================
- * Semi-safe malloc -- never returns NULL.
- */
-voidp xmalloc (size)
-    unsigned size;
-{
-    voidp cp = (voidp)malloc (size);
-
-    if (cp == NULL) error("out of memory");
-    return cp;
-}
-
-/* ========================================================================
  * Table of CRC-32's of all single-byte values (made by makecrc.c)
  */
 static const ulg crc_32_tab[] = {
diff --git a/gzip.c b/gzip.c
index 76df3ad..3438ee4 100644
--- a/gzip.c
+++ b/gzip.c
@@ -277,7 +277,8 @@
 #define WARN(msg) {if (!quiet) fprintf msg ; \
 		   if (exit_code == OK) exit_code = WARNING;}
 
-local void do_exit(int exitcode) __attribute__ ((noreturn));
+#define do_exit(c) exit(c)
+
 
 	/* in zip.c: */
 extern int zip        OF((int in, int out));
@@ -328,7 +329,6 @@
 extern void read_error    OF((void));
 extern void write_error   OF((void));
 extern void display_ratio OF((long num, long den, FILE *file));
-extern voidp xmalloc      OF((unsigned int size));
 
 	/* in inflate.c */
 extern int inflate OF((void));
@@ -1912,29 +1912,6 @@
     do_exit(exit_code);
 }
 
-/* ========================================================================
- * Free all dynamically allocated variables and exit with the given code.
- */
-local void do_exit(int exitcode)
-{
-    static int in_exit = 0;
-
-    if (in_exit) exit(exitcode);
-    in_exit = 1;
-    if (env != NULL)  free(env),  env  = NULL;
-    if (args != NULL) free((char*)args), args = NULL;
-    FREE(inbuf);
-    FREE(outbuf);
-    FREE(d_buf);
-    FREE(window);
-#ifndef MAXSEG_64K
-    FREE(tab_prefix);
-#else
-    FREE(tab_prefix0);
-    FREE(tab_prefix1);
-#endif
-    exit(exitcode);
-}
 /* trees.c -- output deflated data using Huffman coding
  * Copyright (C) 1992-1993 Jean-loup Gailly
  * This is free software; you can redistribute it and/or modify it under the
diff --git a/head.c b/head.c
index 4e58bdc..bc7f354 100644
--- a/head.c
+++ b/head.c
@@ -61,7 +61,7 @@
 	    switch (opt) {
 		case 'n':
 		    tmplen = 0;
-		    if (i++ < argc)
+		    if (++i < argc)
 			tmplen = atoi(argv[i]);
 		    if (tmplen < 1)
 			usage(head_usage);
@@ -105,4 +105,4 @@
     exit(0);
 }
 
-/* $Id: head.c,v 1.5 2000/01/23 18:19:02 erik Exp $ */
+/* $Id: head.c,v 1.6 2000/01/25 18:13:53 erik Exp $ */
diff --git a/init.c b/init.c
index b0a8582..5b80cc5 100644
--- a/init.c
+++ b/init.c
@@ -488,9 +488,14 @@
 static void halt_signal(int sig)
 {
     shutdown_system();
-    message(CONSOLE,
-	    "The system is halted. Press CTRL-ALT-DEL or turn off power\r\n");
+    message(CONSOLE, "The system is halted. Press %s or turn off power\r\n",
+    	(secondConsole == NULL) /* serial console */
+	    ? "Reset" : "CTRL-ALT-DEL");
     sync();
+
+    /* allow time for last message to reach serial console */
+    sleep(2);
+
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
     if (sig == SIGUSR2)
 	reboot(RB_POWER_OFF);
@@ -505,6 +510,10 @@
     shutdown_system();
     message(CONSOLE, "Please stand by while rebooting the system.\r\n");
     sync();
+
+    /* allow time for last message to reach serial console */
+    sleep(2);
+
     reboot(RB_AUTOBOOT);
     exit(0);
 }
@@ -580,7 +589,9 @@
     /* execute init in the (hopefully) new root */
     execve("/sbin/init",argv_init,envp_init);
 
-    message(CONSOLE, "ERROR: Could not exec new init. Hit ctrl+alt+delete to reboot.\r\n");
+    message(CONSOLE, "ERROR: Could not exec new init. Press %s to reboot.\r\n",
+    	(secondConsole == NULL) /* serial console */
+	    ? "Reset" : "CTRL-ALT-DEL");
     return;
 } 
 #endif /* BB_FEATURE_INIT_CHROOT */
@@ -592,11 +603,14 @@
 {
     initAction* newAction;
 
+    if (*cons == '\0')
+    	cons = console;
+ 
     /* If BusyBox detects that a serial console is in use, 
-     * then entries containing non-empty id fields will _not_ be run.
+     * then entries not refering to the console or null devices will _not_ be run.
      * The exception to this rule is the null device.
      */
-    if (secondConsole == NULL && (*cons != '\0' || strncmp(cons, "null", 4)))
+    if (secondConsole == NULL && strcmp(cons, console) && strcmp(cons, "/dev/null"))
 	return;
 
     newAction = calloc ((size_t)(1), sizeof(initAction));
@@ -608,10 +622,7 @@
     initActionList = newAction;
     strncpy( newAction->process, process, 255);
     newAction->action = action;
-    if (*cons != '\0') {
-	strncpy(newAction->console, cons, 255);
-    } else
-	strncpy(newAction->console, console, 255);
+    strncpy(newAction->console, cons, 255);
     newAction->pid = 0;
 //    message(LOG|CONSOLE, "process='%s' action='%d' console='%s'\n",
 //	    newAction->process, newAction->action, newAction->console);
@@ -620,9 +631,13 @@
 void delete_initAction (initAction *action)
 {
     initAction *a, *b=NULL;
-    for( a=initActionList ; a; b=a, a=a->nextPtr) {
-	if (a == action && b != NULL) {
-	    b->nextPtr=a->nextPtr;
+    for( a=initActionList ; a ; b=a, a=a->nextPtr) {
+	if (a == action) {
+	    if (b==NULL) {
+		initActionList=a->nextPtr;
+	    } else {
+		b->nextPtr=a->nextPtr;
+	    }
 	    free( a);
 	    break;
 	}
@@ -805,8 +820,8 @@
 	/* Ask first then start a shell on tty2 */
 	if (secondConsole != NULL) 
 	    new_initAction( ASKFIRST, SHELL, secondConsole);
-	/* Ask first then start a shell on tty1 */
-	new_initAction( ASKFIRST, SHELL, console);
+	/* Start a shell on tty1 */
+	new_initAction( RESPAWN, SHELL, console);
     } else {
 	/* Not in single user mode -- see what inittab says */
 
diff --git a/init/init.c b/init/init.c
index b0a8582..5b80cc5 100644
--- a/init/init.c
+++ b/init/init.c
@@ -488,9 +488,14 @@
 static void halt_signal(int sig)
 {
     shutdown_system();
-    message(CONSOLE,
-	    "The system is halted. Press CTRL-ALT-DEL or turn off power\r\n");
+    message(CONSOLE, "The system is halted. Press %s or turn off power\r\n",
+    	(secondConsole == NULL) /* serial console */
+	    ? "Reset" : "CTRL-ALT-DEL");
     sync();
+
+    /* allow time for last message to reach serial console */
+    sleep(2);
+
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
     if (sig == SIGUSR2)
 	reboot(RB_POWER_OFF);
@@ -505,6 +510,10 @@
     shutdown_system();
     message(CONSOLE, "Please stand by while rebooting the system.\r\n");
     sync();
+
+    /* allow time for last message to reach serial console */
+    sleep(2);
+
     reboot(RB_AUTOBOOT);
     exit(0);
 }
@@ -580,7 +589,9 @@
     /* execute init in the (hopefully) new root */
     execve("/sbin/init",argv_init,envp_init);
 
-    message(CONSOLE, "ERROR: Could not exec new init. Hit ctrl+alt+delete to reboot.\r\n");
+    message(CONSOLE, "ERROR: Could not exec new init. Press %s to reboot.\r\n",
+    	(secondConsole == NULL) /* serial console */
+	    ? "Reset" : "CTRL-ALT-DEL");
     return;
 } 
 #endif /* BB_FEATURE_INIT_CHROOT */
@@ -592,11 +603,14 @@
 {
     initAction* newAction;
 
+    if (*cons == '\0')
+    	cons = console;
+ 
     /* If BusyBox detects that a serial console is in use, 
-     * then entries containing non-empty id fields will _not_ be run.
+     * then entries not refering to the console or null devices will _not_ be run.
      * The exception to this rule is the null device.
      */
-    if (secondConsole == NULL && (*cons != '\0' || strncmp(cons, "null", 4)))
+    if (secondConsole == NULL && strcmp(cons, console) && strcmp(cons, "/dev/null"))
 	return;
 
     newAction = calloc ((size_t)(1), sizeof(initAction));
@@ -608,10 +622,7 @@
     initActionList = newAction;
     strncpy( newAction->process, process, 255);
     newAction->action = action;
-    if (*cons != '\0') {
-	strncpy(newAction->console, cons, 255);
-    } else
-	strncpy(newAction->console, console, 255);
+    strncpy(newAction->console, cons, 255);
     newAction->pid = 0;
 //    message(LOG|CONSOLE, "process='%s' action='%d' console='%s'\n",
 //	    newAction->process, newAction->action, newAction->console);
@@ -620,9 +631,13 @@
 void delete_initAction (initAction *action)
 {
     initAction *a, *b=NULL;
-    for( a=initActionList ; a; b=a, a=a->nextPtr) {
-	if (a == action && b != NULL) {
-	    b->nextPtr=a->nextPtr;
+    for( a=initActionList ; a ; b=a, a=a->nextPtr) {
+	if (a == action) {
+	    if (b==NULL) {
+		initActionList=a->nextPtr;
+	    } else {
+		b->nextPtr=a->nextPtr;
+	    }
 	    free( a);
 	    break;
 	}
@@ -805,8 +820,8 @@
 	/* Ask first then start a shell on tty2 */
 	if (secondConsole != NULL) 
 	    new_initAction( ASKFIRST, SHELL, secondConsole);
-	/* Ask first then start a shell on tty1 */
-	new_initAction( ASKFIRST, SHELL, console);
+	/* Start a shell on tty1 */
+	new_initAction( RESPAWN, SHELL, console);
     } else {
 	/* Not in single user mode -- see what inittab says */
 
diff --git a/internal.h b/internal.h
index b77feab..500a63e 100644
--- a/internal.h
+++ b/internal.h
@@ -175,6 +175,11 @@
 extern long getNum (const char *cp);
 extern pid_t findInitPid();
 
+#if defined BB_GUNZIP || defined BB_GZIP || defined BB_PRINTF || defined BB_TAIL
+extern void *xmalloc (size_t size);
+extern void error(char *msg);
+#endif
+
 #if (__GLIBC__ < 2) && (defined BB_SYSLOGD || defined BB_INIT)
 extern int vdprintf(int d, const char *format, va_list ap);
 #endif
diff --git a/printf.c b/printf.c
index 02d0811..5be3a67 100644
--- a/printf.c
+++ b/printf.c
@@ -121,8 +121,6 @@
 #define hextobin(c) ((c)>='a'&&(c)<='f' ? (c)-'a'+10 : (c)>='A'&&(c)<='F' ? (c)-'A'+10 : (c)-'0')
 #define octtobin(c) ((c) - '0')
 
-char *xmalloc ();
-
 static double xstrtod __P ((char *s));
 static int print_esc __P ((char *escstart));
 static int print_formatted __P ((char *format, int argc, char **argv));
diff --git a/tail.c b/tail.c
index 697177d..5198892 100644
--- a/tail.c
+++ b/tail.c
@@ -1,3 +1,402 @@
+#include "internal.h"
+/* This file contains _two_ implementations of tail.  One is
+ * a bit more full featured, but costs 6k.  The other (i.e. the
+ * SIMPLE_TAIL one) is less capable, but is good enough for about
+ * 99% of the things folks want to use tail for, and only costs 2k.
+ */
+
+
+#ifdef BB_FEATURE_SIMPLE_TAIL
+
+/* tail -- output the last part of file(s)
+   Copyright (C) 89, 90, 91, 95, 1996 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+   Original version by Paul Rubin <phr@ocf.berkeley.edu>.
+   Extensions by David MacKenzie <djm@gnu.ai.mit.edu>.
+   tail -f for multiple files by Ian Lance Taylor <ian@airs.com>.  
+
+   Rewrote the option parser, removed locales support,
+    and generally busyboxed, Erik Andersen <andersen@lineo.com>
+
+   Removed superfluous options and associated code ("-c", "-n", "-q").
+   Removed "tail -f" suport for multiple files.
+   Both changes by Friedrich Vedder <fwv@myrtle.lahn.de>.
+
+ */
+
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <ctype.h>
+
+
+#define XWRITE(fd, buffer, n_bytes)					\
+  do {									\
+      if (n_bytes > 0 && fwrite ((buffer), 1, (n_bytes), stdout) == 0)	\
+	  error("write error");					\
+  } while (0)
+
+/* Number of items to tail.  */
+#define DEFAULT_N_LINES 10
+
+/* Size of atomic reads.  */
+#ifndef BUFSIZ
+#define BUFSIZ (512 * 8)
+#endif
+
+/* If nonzero, read from the end of one file until killed.  */
+static int forever;
+
+/* If nonzero, print filename headers.  */
+static int print_headers;
+
+const char tail_usage[] =
+    "tail [OPTION] [FILE]...\n\n"
+    "Print last 10 lines of each FILE to standard output.\n"
+    "With more than one FILE, precede each with a header giving the\n"
+    "file name. With no FILE, or when FILE is -, read standard input.\n\n"
+    "Options:\n"
+    "\t-n NUM\t\tPrint last NUM lines instead of first 10\n"
+    "\t-f\t\tOutput data as the file grows.  This version\n"
+    "\t\t\tof 'tail -f' supports only one file at a time.\n";
+
+
+static void write_header(const char *filename)
+{
+    static int first_file = 1;
+
+    printf("%s==> %s <==\n", (first_file ? "" : "\n"), filename);
+    first_file = 0;
+}
+
+/* Print the last N_LINES lines from the end of file FD.
+   Go backward through the file, reading `BUFSIZ' bytes at a time (except
+   probably the first), until we hit the start of the file or have
+   read NUMBER newlines.
+   POS starts out as the length of the file (the offset of the last
+   byte of the file + 1).
+   Return 0 if successful, 1 if an error occurred.  */
+
+static int
+file_lines(const char *filename, int fd, long int n_lines, off_t pos)
+{
+    char buffer[BUFSIZ];
+    int bytes_read;
+    int i;			/* Index into `buffer' for scanning.  */
+
+    if (n_lines == 0)
+	return 0;
+
+    /* Set `bytes_read' to the size of the last, probably partial, buffer;
+       0 < `bytes_read' <= `BUFSIZ'.  */
+    bytes_read = pos % BUFSIZ;
+    if (bytes_read == 0)
+	bytes_read = BUFSIZ;
+    /* Make `pos' a multiple of `BUFSIZ' (0 if the file is short), so that all
+       reads will be on block boundaries, which might increase efficiency.  */
+    pos -= bytes_read;
+    lseek(fd, pos, SEEK_SET);
+    bytes_read = fullRead(fd, buffer, bytes_read);
+    if (bytes_read == -1)
+	error("read error");
+
+    /* Count the incomplete line on files that don't end with a newline.  */
+    if (bytes_read && buffer[bytes_read - 1] != '\n')
+	--n_lines;
+
+    do {
+	/* Scan backward, counting the newlines in this bufferfull.  */
+	for (i = bytes_read - 1; i >= 0; i--) {
+	    /* Have we counted the requested number of newlines yet?  */
+	    if (buffer[i] == '\n' && n_lines-- == 0) {
+		/* If this newline wasn't the last character in the buffer,
+		   print the text after it.  */
+		if (i != bytes_read - 1)
+		    XWRITE(STDOUT_FILENO, &buffer[i + 1],
+			   bytes_read - (i + 1));
+		return 0;
+	    }
+	}
+	/* Not enough newlines in that bufferfull.  */
+	if (pos == 0) {
+	    /* Not enough lines in the file; print the entire file.  */
+	    lseek(fd, (off_t) 0, SEEK_SET);
+	    return 0;
+	}
+	pos -= BUFSIZ;
+	lseek(fd, pos, SEEK_SET);
+    }
+    while ((bytes_read = fullRead(fd, buffer, BUFSIZ)) > 0);
+    if (bytes_read == -1)
+	error("read error");
+
+    return 0;
+}
+
+/* Print the last N_LINES lines from the end of the standard input,
+   open for reading as pipe FD.
+   Buffer the text as a linked list of LBUFFERs, adding them as needed.
+   Return 0 if successful, 1 if an error occured.  */
+
+static int pipe_lines(const char *filename, int fd, long int n_lines)
+{
+    struct linebuffer {
+	int nbytes, nlines;
+	char buffer[BUFSIZ];
+	struct linebuffer *next;
+    };
+    typedef struct linebuffer LBUFFER;
+    LBUFFER *first, *last, *tmp;
+    int i;			/* Index into buffers.  */
+    int total_lines = 0;	/* Total number of newlines in all buffers.  */
+    int errors = 0;
+
+    first = last = (LBUFFER *) xmalloc(sizeof(LBUFFER));
+    first->nbytes = first->nlines = 0;
+    first->next = NULL;
+    tmp = (LBUFFER *) xmalloc(sizeof(LBUFFER));
+
+    /* Input is always read into a fresh buffer.  */
+    while ((tmp->nbytes = fullRead(fd, tmp->buffer, BUFSIZ)) > 0) {
+	tmp->nlines = 0;
+	tmp->next = NULL;
+
+	/* Count the number of newlines just read.  */
+	for (i = 0; i < tmp->nbytes; i++)
+	    if (tmp->buffer[i] == '\n')
+		++tmp->nlines;
+	total_lines += tmp->nlines;
+
+	/* If there is enough room in the last buffer read, just append the new
+	   one to it.  This is because when reading from a pipe, `nbytes' can
+	   often be very small.  */
+	if (tmp->nbytes + last->nbytes < BUFSIZ) {
+	    memcpy(&last->buffer[last->nbytes], tmp->buffer, tmp->nbytes);
+	    last->nbytes += tmp->nbytes;
+	    last->nlines += tmp->nlines;
+	} else {
+	    /* If there's not enough room, link the new buffer onto the end of
+	       the list, then either free up the oldest buffer for the next
+	       read if that would leave enough lines, or else malloc a new one.
+	       Some compaction mechanism is possible but probably not
+	       worthwhile.  */
+	    last = last->next = tmp;
+	    if (total_lines - first->nlines > n_lines) {
+		tmp = first;
+		total_lines -= first->nlines;
+		first = first->next;
+	    } else
+		tmp = (LBUFFER *) xmalloc(sizeof(LBUFFER));
+	}
+    }
+    if (tmp->nbytes == -1)
+	error("read error");
+
+    free((char *) tmp);
+
+    /* This prevents a core dump when the pipe contains no newlines.  */
+    if (n_lines == 0)
+	goto free_lbuffers;
+
+    /* Count the incomplete line on files that don't end with a newline.  */
+    if (last->buffer[last->nbytes - 1] != '\n') {
+	++last->nlines;
+	++total_lines;
+    }
+
+    /* Run through the list, printing lines.  First, skip over unneeded
+       buffers.  */
+    for (tmp = first; total_lines - tmp->nlines > n_lines; tmp = tmp->next)
+	total_lines -= tmp->nlines;
+
+    /* Find the correct beginning, then print the rest of the file.  */
+    if (total_lines > n_lines) {
+	char *cp;
+
+	/* Skip `total_lines' - `n_lines' newlines.  We made sure that
+	   `total_lines' - `n_lines' <= `tmp->nlines'.  */
+	cp = tmp->buffer;
+	for (i = total_lines - n_lines; i; --i)
+	    while (*cp++ != '\n')
+		/* Do nothing.  */ ;
+	i = cp - tmp->buffer;
+    } else
+	i = 0;
+    XWRITE(STDOUT_FILENO, &tmp->buffer[i], tmp->nbytes - i);
+
+    for (tmp = tmp->next; tmp; tmp = tmp->next)
+	XWRITE(STDOUT_FILENO, tmp->buffer, tmp->nbytes);
+
+  free_lbuffers:
+    while (first) {
+	tmp = first->next;
+	free((char *) first);
+	first = tmp;
+    }
+    return errors;
+}
+
+/* Display file FILENAME from the current position in FD to the end.
+   If `forever' is nonzero, keep reading from the end of the file
+   until killed.  Return the number of bytes read from the file.  */
+
+static long dump_remainder(const char *filename, int fd)
+{
+    char buffer[BUFSIZ];
+    int bytes_read;
+    long total;
+
+    total = 0;
+  output:
+    while ((bytes_read = fullRead(fd, buffer, BUFSIZ)) > 0) {
+	XWRITE(STDOUT_FILENO, buffer, bytes_read);
+	total += bytes_read;
+    }
+    if (bytes_read == -1)
+	error("read error");
+    if (forever) {
+	fflush(stdout);
+	sleep(1);
+	goto output;
+    }
+
+    return total;
+}
+
+/* Output the last N_LINES lines of file FILENAME open for reading in FD.
+   Return 0 if successful, 1 if an error occurred.  */
+
+static int tail_lines(const char *filename, int fd, long int n_lines)
+{
+    struct stat stats;
+    off_t length;
+
+    if (print_headers)
+	write_header(filename);
+
+    if (fstat(fd, &stats))
+	error("fstat error");
+
+    /* Use file_lines only if FD refers to a regular file with
+       its file pointer positioned at beginning of file.  */
+    /* FIXME: adding the lseek conjunct is a kludge.
+       Once there's a reasonable test suite, fix the true culprit:
+       file_lines.  file_lines shouldn't presume that the input
+       file pointer is initially positioned to beginning of file.  */
+    if (S_ISREG(stats.st_mode)
+	&& lseek(fd, (off_t) 0, SEEK_CUR) == (off_t) 0) {
+	length = lseek(fd, (off_t) 0, SEEK_END);
+	if (length != 0 && file_lines(filename, fd, n_lines, length))
+	    return 1;
+	dump_remainder(filename, fd);
+    } else
+	return pipe_lines(filename, fd, n_lines);
+
+    return 0;
+}
+
+/* Display the last N_UNITS lines of file FILENAME.
+   "-" for FILENAME means the standard input.
+   Return 0 if successful, 1 if an error occurred.  */
+
+static int tail_file(const char *filename, off_t n_units)
+{
+    int fd, errors;
+
+    if (!strcmp(filename, "-")) {
+	filename = "standard input";
+	errors = tail_lines(filename, 0, (long) n_units);
+    } else {
+	/* Not standard input.  */
+	fd = open(filename, O_RDONLY);
+	if (fd == -1)
+	    error("open error");
+
+	errors = tail_lines(filename, fd, (long) n_units);
+	close(fd);
+    }
+
+    return errors;
+}
+
+extern int tail_main(int argc, char **argv)
+{
+    int exit_status = 0;
+    int n_units = DEFAULT_N_LINES;
+    int n_tmp, i;
+    char opt;
+
+    forever = print_headers = 0;
+
+    /* parse argv[] */
+    for (i = 1; i < argc; i++) {
+	if (argv[i][0] == '-') {
+	    opt = argv[i][1];
+	    switch (opt) {
+	    case 'f':
+		forever = 1;
+		break;
+	    case 'n':
+		n_tmp = 0;
+		if (++i < argc)
+		    n_tmp = atoi(argv[i]);
+		if (n_tmp < 1)
+		    usage(tail_usage);
+		n_units = n_tmp;
+		break;
+	    case '-':
+	    case 'h':
+		usage(tail_usage);
+	    default:
+		fprintf(stderr, "tail: invalid option -- %c\n", opt);
+		usage(tail_usage);
+	    }
+	} else {
+	    break;
+	}
+    }
+
+    if (i + 1 < argc) {
+	if (forever) {
+	    fprintf(stderr,
+		    "tail: option -f is invalid with multiple files\n");
+	    usage(tail_usage);
+	}
+	print_headers = 1;
+    }
+
+    if (i >= argc) {
+	exit_status |= tail_file("-", n_units);
+    } else {
+	for (; i < argc; i++)
+	    exit_status |= tail_file(argv[i], n_units);
+    }
+
+    exit(exit_status == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
+}
+
+
+#else
+// Here follows the code for the full featured tail code
+
+
 /* tail -- output the last part of file(s)
    Copyright (C) 89, 90, 91, 95, 1996 Free Software Foundation, Inc.
 
@@ -42,7 +441,7 @@
 #define NDEBUG 1
 
 
-static void error(int i, int errnum, char* fmt, ...)
+static void detailed_error(int i, int errnum, char* fmt, ...)
 {
     va_list arguments;
 
@@ -60,7 +459,7 @@
       assert ((fd) == 1);						\
       assert ((n_bytes) >= 0);						\
       if (n_bytes > 0 && fwrite ((buffer), 1, (n_bytes), stdout) == 0)	\
-	error (EXIT_FAILURE, errno, "write error");			\
+	detailed_error (EXIT_FAILURE, errno, "write error");			\
     }									\
   while (0)
 
@@ -100,8 +499,6 @@
   multiple_files, always, never
 };
 
-char *xmalloc ();
-
 /* The name this program was run with.  */
 char *program_name;
 
@@ -168,7 +565,7 @@
   bytes_read = fullRead (fd, buffer, bytes_read);
   if (bytes_read == -1)
     {
-      error (0, errno, "%s", filename);
+      detailed_error (0, errno, "%s", filename);
       return 1;
     }
 
@@ -204,7 +601,7 @@
   while ((bytes_read = fullRead (fd, buffer, BUFSIZ)) > 0);
   if (bytes_read == -1)
     {
-      error (0, errno, "%s", filename);
+      detailed_error (0, errno, "%s", filename);
       return 1;
     }
   return 0;
@@ -276,7 +673,7 @@
     }
   if (tmp->nbytes == -1)
     {
-      error (0, errno, "%s", filename);
+      detailed_error (0, errno, "%s", filename);
       errors = 1;
       free ((char *) tmp);
       goto free_lbuffers;
@@ -390,7 +787,7 @@
     }
   if (tmp->nbytes == -1)
     {
-      error (0, errno, "%s", filename);
+      detailed_error (0, errno, "%s", filename);
       errors = 1;
       free ((char *) tmp);
       goto free_cbuffers;
@@ -438,7 +835,7 @@
     n_bytes -= bytes_read;
   if (bytes_read == -1)
     {
-      error (0, errno, "%s", filename);
+      detailed_error (0, errno, "%s", filename);
       return 1;
     }
   else if (n_bytes < 0)
@@ -466,7 +863,7 @@
     }
   if (bytes_read == -1)
     {
-      error (0, errno, "%s", filename);
+      detailed_error (0, errno, "%s", filename);
       return 1;
     }
   else if (bytes_to_skip < bytes_read)
@@ -496,7 +893,7 @@
       total += bytes_read;
     }
   if (bytes_read == -1)
-    error (EXIT_FAILURE, errno, "%s", filename);
+    detailed_error (EXIT_FAILURE, errno, "%s", filename);
   if (forever)
     {
       fflush (stdout);
@@ -540,7 +937,7 @@
 	    continue;
 	  if (fstat (file_descs[i], &stats) < 0)
 	    {
-	      error (0, errno, "%s", names[i]);
+	      detailed_error (0, errno, "%s", names[i]);
 	      file_descs[i] = -1;
 	      continue;
 	    }
@@ -590,7 +987,7 @@
      error, either.  */
   if (fstat (fd, &stats))
     {
-      error (0, errno, "%s", filename);
+      detailed_error (0, errno, "%s", filename);
       return 1;
     }
 
@@ -619,7 +1016,7 @@
 	    }
 	  else
 	    {
-	      error (0, errno, "%s", filename);
+	      detailed_error (0, errno, "%s", filename);
 	      return 1;
 	    }
 
@@ -656,7 +1053,7 @@
 
   if (fstat (fd, &stats))
     {
-      error (0, errno, "%s", filename);
+      detailed_error (0, errno, "%s", filename);
       return 1;
     }
 
@@ -723,12 +1120,12 @@
 	{
 	  if (fstat (0, &stats) < 0)
 	    {
-	      error (0, errno, "standard input");
+	      detailed_error (0, errno, "standard input");
 	      errors = 1;
 	    }
 	  else if (!S_ISREG (stats.st_mode))
 	    {
-	      error (0, 0,
+	      detailed_error (0, 0,
 		     "standard input: cannot follow end of non-regular file");
 	      errors = 1;
 	    }
@@ -749,7 +1146,7 @@
 	{
 	  if (forever_multiple)
 	    file_descs[filenum] = -1;
-	  error (0, errno, "%s", filename);
+	  detailed_error (0, errno, "%s", filename);
 	  errors = 1;
 	}
       else
@@ -761,12 +1158,12 @@
 	    {
 	      if (fstat (fd, &stats) < 0)
 		{
-		  error (0, errno, "%s", filename);
+		  detailed_error (0, errno, "%s", filename);
 		  errors = 1;
 		}
 	      else if (!S_ISREG (stats.st_mode))
 		{
-		  error (0, 0, "%s: cannot follow end of non-regular file",
+		  detailed_error (0, 0, "%s: cannot follow end of non-regular file",
 			 filename);
 		  errors = 1;
 		}
@@ -785,7 +1182,7 @@
 	    {
 	      if (close (fd))
 		{
-		  error (0, errno, "%s", filename);
+		  detailed_error (0, errno, "%s", filename);
 		  errors = 1;
 		}
 	    }
@@ -903,8 +1300,11 @@
     }
 
   if (have_read_stdin && close (0) < 0)
-    error (EXIT_FAILURE, errno, "-");
+    detailed_error (EXIT_FAILURE, errno, "-");
   if (fclose (stdout) == EOF)
-    error (EXIT_FAILURE, errno, "write error");
+    detailed_error (EXIT_FAILURE, errno, "write error");
   exit (exit_status == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
 }
+
+
+#endif
diff --git a/utility.c b/utility.c
index 4b67ce9..8139f38 100644
--- a/utility.c
+++ b/utility.c
@@ -175,7 +175,7 @@
 	}
     } else if (S_ISFIFO(srcStatBuf.st_mode)) {
 	//fprintf(stderr, "copying fifo %s to %s\n", srcName, destName);
-	if (mkfifo(destName, 644)) {
+	if (mkfifo(destName, 0644)) {
 	    perror(destName);
 	    return (FALSE);
 	}
@@ -406,7 +406,6 @@
     else
 	status = lstat(fileName, &statbuf);
 
-    status = lstat(fileName, &statbuf);
     if (status < 0) {
 	perror(fileName);
 	return (FALSE);
@@ -1118,6 +1117,24 @@
 }
 #endif
 
+#if defined BB_GUNZIP || defined BB_GZIP || defined BB_PRINTF || defined BB_TAIL
+extern void *xmalloc (size_t size)
+{
+    void *cp = malloc (size);
+
+    if (cp == NULL) {
+	error("out of memory");
+    }
+    return cp;
+}
+
+extern void error(char *msg)
+{
+    fprintf(stderr, "\n%s\n", msg);
+    exit(1);
+}
+#endif
+
 #if (__GLIBC__ < 2) && (defined BB_SYSLOGD || defined BB_INIT)
 extern int vdprintf(int d, const char *format, va_list ap)
 {