From 3fe39dce5d1a0b0946878c66bbd7f694c5aa38ea Mon Sep 17 00:00:00 2001 From: Erik Andersen Date: Tue, 25 Jan 2000 18:13:53 +0000 Subject: [PATCH] Some busybox updates. See the changelog for details if you care. -Erik --- Changelog | 19 +- applets/busybox.c | 4 +- archival/gunzip.c | 48 +---- archival/gzip.c | 27 +-- busybox.c | 4 +- busybox.def.h | 5 + coreutils/head.c | 4 +- coreutils/printf.c | 2 - coreutils/tail.c | 446 ++++++++++++++++++++++++++++++++++++++++++--- gunzip.c | 48 +---- gzip.c | 27 +-- head.c | 4 +- init.c | 43 +++-- init/init.c | 43 +++-- internal.h | 5 + printf.c | 2 - tail.c | 446 ++++++++++++++++++++++++++++++++++++++++++--- utility.c | 21 ++- 18 files changed, 966 insertions(+), 232 deletions(-) diff --git a/Changelog b/Changelog index 673d1aafd..4dbb46e4f 100644 --- a/Changelog +++ b/Changelog @@ -15,12 +15,23 @@ * Optional support contributed by Ben Collins 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 - * Cleanup for a number of usage messages -- also - contributed Friedrich Vedder * 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 + * Several contributions from Friedrich Vedder + * added (and documented) "-n" option for head - + * Cleanup for a number of usage messages -- also + contributed Friedrich Vedder + * 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 67485de8d..d59b2855e 100644 --- a/applets/busybox.c +++ b/applets/busybox.c @@ -83,7 +83,7 @@ static const struct Applet applets[] = { #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 @@ int busybox_main(int argc, char **argv) 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 84f5d02b7..fddcc7653 100644 --- a/archival/gunzip.c +++ b/archival/gunzip.c @@ -321,6 +321,9 @@ extern int save_orig_name; /* set if original name must be saved */ #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 error OF((char *m)); 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 @@ long header_bytes; /* number of bytes in gzip header */ /* 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 @@ local int get_method(in) } } - -/* ======================================================================== - * 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 @@ int strcspn(s, reject) /* ======================================================================== * 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 */ { @@ -1316,18 +1286,6 @@ void write_error() } -/* ======================================================================== - * 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) */ diff --git a/archival/gzip.c b/archival/gzip.c index 76df3ad9a..3438ee42f 100644 --- a/archival/gzip.c +++ b/archival/gzip.c @@ -277,7 +277,8 @@ extern int save_orig_name; /* set if original name must be saved */ #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 warn OF((char *a, char *b)); 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 @@ int gzip_main(int argc, char ** argv) 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 67485de8d..d59b2855e 100644 --- a/busybox.c +++ b/busybox.c @@ -83,7 +83,7 @@ static const struct Applet applets[] = { #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 @@ int busybox_main(int argc, char **argv) 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 c56f151db..8adccdc64 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 4e58bdcd7..bc7f35429 100644 --- a/coreutils/head.c +++ b/coreutils/head.c @@ -61,7 +61,7 @@ head_main(int argc, char **argv) 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 @@ head_main(int argc, char **argv) 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 02d08118b..5be3a67cb 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 697177dc7..5198892b6 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 . + Extensions by David MacKenzie . + tail -f for multiple files by Ian Lance Taylor . + + Rewrote the option parser, removed locales support, + and generally busyboxed, Erik Andersen + + Removed superfluous options and associated code ("-c", "-n", "-q"). + Removed "tail -f" suport for multiple files. + Both changes by Friedrich Vedder . + + */ + + +#include +#include +#include +#include +#include +#include + + +#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 @@ static void error(int i, int errnum, char* fmt, ...) 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 @@ enum header_mode multiple_files, always, never }; -char *xmalloc (); - /* The name this program was run with. */ char *program_name; @@ -168,7 +565,7 @@ file_lines (const char *filename, int fd, long int n_lines, off_t pos) 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 @@ file_lines (const char *filename, int fd, long int n_lines, off_t pos) 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 @@ pipe_lines (const char *filename, int fd, long int n_lines) } 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 @@ pipe_bytes (const char *filename, int fd, off_t n_bytes) } 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 @@ start_bytes (const char *filename, int fd, off_t n_bytes) 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 @@ start_lines (const char *filename, int fd, long int n_lines) } 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 @@ output: 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 @@ tail_forever (char **names, int nfiles) 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 @@ tail_bytes (const char *filename, int fd, off_t n_bytes) error, either. */ if (fstat (fd, &stats)) { - error (0, errno, "%s", filename); + detailed_error (0, errno, "%s", filename); return 1; } @@ -619,7 +1016,7 @@ tail_bytes (const char *filename, int fd, off_t n_bytes) } else { - error (0, errno, "%s", filename); + detailed_error (0, errno, "%s", filename); return 1; } @@ -656,7 +1053,7 @@ tail_lines (const char *filename, int fd, long int n_lines) if (fstat (fd, &stats)) { - error (0, errno, "%s", filename); + detailed_error (0, errno, "%s", filename); return 1; } @@ -723,12 +1120,12 @@ tail_file (const char *filename, off_t n_units, int filenum) { 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 @@ tail_file (const char *filename, off_t n_units, int filenum) { 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 @@ tail_file (const char *filename, off_t n_units, int filenum) { 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 @@ tail_file (const char *filename, off_t n_units, int filenum) { if (close (fd)) { - error (0, errno, "%s", filename); + detailed_error (0, errno, "%s", filename); errors = 1; } } @@ -903,8 +1300,11 @@ tail_main (int argc, char **argv) } 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 84f5d02b7..fddcc7653 100644 --- a/gunzip.c +++ b/gunzip.c @@ -321,6 +321,9 @@ extern int save_orig_name; /* set if original name must be saved */ #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 error OF((char *m)); 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 @@ long header_bytes; /* number of bytes in gzip header */ /* 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 @@ local int get_method(in) } } - -/* ======================================================================== - * 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 @@ int strcspn(s, reject) /* ======================================================================== * 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 */ { @@ -1316,18 +1286,6 @@ void write_error() } -/* ======================================================================== - * 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) */ diff --git a/gzip.c b/gzip.c index 76df3ad9a..3438ee42f 100644 --- a/gzip.c +++ b/gzip.c @@ -277,7 +277,8 @@ extern int save_orig_name; /* set if original name must be saved */ #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 warn OF((char *a, char *b)); 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 @@ int gzip_main(int argc, char ** argv) 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 4e58bdcd7..bc7f35429 100644 --- a/head.c +++ b/head.c @@ -61,7 +61,7 @@ head_main(int argc, char **argv) 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 @@ head_main(int argc, char **argv) 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 b0a85829d..5b80cc561 100644 --- a/init.c +++ b/init.c @@ -488,9 +488,14 @@ static void shutdown_system(void) 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 @@ static void reboot_signal(int sig) 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 @@ static void check_chroot(int sig) /* 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 @@ void new_initAction (initActionEnum action, { 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 @@ void new_initAction (initActionEnum action, 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 new_initAction (initActionEnum action, 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 @@ extern int init_main(int argc, char **argv) /* 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 b0a85829d..5b80cc561 100644 --- a/init/init.c +++ b/init/init.c @@ -488,9 +488,14 @@ static void shutdown_system(void) 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 @@ static void reboot_signal(int sig) 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 @@ static void check_chroot(int sig) /* 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 @@ void new_initAction (initActionEnum action, { 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 @@ void new_initAction (initActionEnum action, 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 new_initAction (initActionEnum action, 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 @@ extern int init_main(int argc, char **argv) /* 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 b77feabad..500a63e62 100644 --- a/internal.h +++ b/internal.h @@ -175,6 +175,11 @@ extern int check_wildcard_match(const char* text, const char* pattern); 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 02d08118b..5be3a67cb 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 697177dc7..5198892b6 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 . + Extensions by David MacKenzie . + tail -f for multiple files by Ian Lance Taylor . + + Rewrote the option parser, removed locales support, + and generally busyboxed, Erik Andersen + + Removed superfluous options and associated code ("-c", "-n", "-q"). + Removed "tail -f" suport for multiple files. + Both changes by Friedrich Vedder . + + */ + + +#include +#include +#include +#include +#include +#include + + +#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 @@ static void error(int i, int errnum, char* fmt, ...) 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 @@ enum header_mode multiple_files, always, never }; -char *xmalloc (); - /* The name this program was run with. */ char *program_name; @@ -168,7 +565,7 @@ file_lines (const char *filename, int fd, long int n_lines, off_t pos) 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 @@ file_lines (const char *filename, int fd, long int n_lines, off_t pos) 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 @@ pipe_lines (const char *filename, int fd, long int n_lines) } 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 @@ pipe_bytes (const char *filename, int fd, off_t n_bytes) } 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 @@ start_bytes (const char *filename, int fd, off_t n_bytes) 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 @@ start_lines (const char *filename, int fd, long int n_lines) } 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 @@ output: 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 @@ tail_forever (char **names, int nfiles) 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 @@ tail_bytes (const char *filename, int fd, off_t n_bytes) error, either. */ if (fstat (fd, &stats)) { - error (0, errno, "%s", filename); + detailed_error (0, errno, "%s", filename); return 1; } @@ -619,7 +1016,7 @@ tail_bytes (const char *filename, int fd, off_t n_bytes) } else { - error (0, errno, "%s", filename); + detailed_error (0, errno, "%s", filename); return 1; } @@ -656,7 +1053,7 @@ tail_lines (const char *filename, int fd, long int n_lines) if (fstat (fd, &stats)) { - error (0, errno, "%s", filename); + detailed_error (0, errno, "%s", filename); return 1; } @@ -723,12 +1120,12 @@ tail_file (const char *filename, off_t n_units, int filenum) { 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 @@ tail_file (const char *filename, off_t n_units, int filenum) { 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 @@ tail_file (const char *filename, off_t n_units, int filenum) { 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 @@ tail_file (const char *filename, off_t n_units, int filenum) { if (close (fd)) { - error (0, errno, "%s", filename); + detailed_error (0, errno, "%s", filename); errors = 1; } } @@ -903,8 +1300,11 @@ tail_main (int argc, char **argv) } 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 4b67ce9b7..8139f38d9 100644 --- a/utility.c +++ b/utility.c @@ -175,7 +175,7 @@ copyFile( const char *srcName, const char *destName, } } 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 @@ recursiveAction(const char *fileName, int recurse, int followLinks, int depthFir else status = lstat(fileName, &statbuf); - status = lstat(fileName, &statbuf); if (status < 0) { perror(fileName); return (FALSE); @@ -1118,6 +1117,24 @@ findInitPid() } #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) {