summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile2
-rw-r--r--changelog9
-rw-r--r--inotail.c144
-rw-r--r--inotail.h12
4 files changed, 153 insertions, 14 deletions
diff --git a/Makefile b/Makefile
index a93a097..bcb8cd4 100644
--- a/Makefile
+++ b/Makefile
@@ -4,7 +4,7 @@
#
# Licensed under the terms of the GNU General Public License; version 2 or later.
-VERSION = 0.3
+VERSION = 0.4
# Paths
prefix = /usr/local
diff --git a/changelog b/changelog
index 8af070a..05ced1b 100644
--- a/changelog
+++ b/changelog
@@ -1,3 +1,12 @@
+inotail 0.4
+
+ * Implemented tailing of stdin
+ * Fixed handling of EINTR signal (^Z/fg) (patch by Anthony Martinez)
+ * Improved error handling (patch by Folkert van Heusden)
+ * Added uninstall target to Makefile (patch by Folkert van Heusden)
+
+ -- Tobias Klauser <tklauser@distanz.ch> 2007-06-04 14:44
+
inotail 0.3
* Follow files even if they were moved
diff --git a/inotail.c b/inotail.c
index 967a0fa..28eebfd 100644
--- a/inotail.c
+++ b/inotail.c
@@ -39,7 +39,6 @@
#include "inotail.h"
#define PROGRAM_NAME "inotail"
-#define BUFFER_SIZE 4096
/* inotify event buffer length for one file */
#define INOTIFY_BUFLEN (4 * sizeof(struct inotify_event))
@@ -63,6 +62,18 @@ static const struct option long_opts[] = {
{ NULL, 0, NULL, 0 }
};
+static void *emalloc(size_t size)
+{
+ void *ret = malloc(size);
+
+ if (unlikely(!ret)) {
+ fprintf(stderr, "Error: Failed to allocate %d bytes of memory (%s)\n", size, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ return ret;
+}
+
static void usage(const int status)
{
fprintf(stdout, "Usage: %s [OPTION]... [FILE]...\n\n"
@@ -214,15 +225,119 @@ static off_t bytes_to_offset(struct file_struct *f, unsigned long n_bytes)
return offset;
}
-static ssize_t tail_pipe(struct file_struct *f)
+/* For now more or less a copy of pipe_lines() from coreutils tail */
+static ssize_t tail_pipe_lines(struct file_struct *f, unsigned long n_lines)
{
+ struct line_buf *first, *last, *tmp;
ssize_t rc;
- char buf[BUFFER_SIZE];
+ unsigned long total_lines = 0;
- if (verbose)
- write_header(f->name);
+ if (n_lines == 0)
+ return 0;
+
+ first = last = emalloc(sizeof(struct line_buf));
+ first->n_bytes = first->n_lines = 0;
+ first->next = NULL;
+ tmp = emalloc(sizeof(struct line_buf));
+
+ while (1) {
+ const char *p;
+
+ if ((rc = read(f->fd, tmp->buf, BUFFER_SIZE)) <= 0) {
+ if (rc < 0 && (errno == EINTR || errno == EAGAIN))
+ continue;
+ else
+ break;
+ }
+ tmp->n_bytes = rc;
+ tmp->n_lines = 0;
+ tmp->next = NULL;
+ p = tmp->buf;
+
+ /* Count the lines in the current buffer */
+ while ((p = memchr(p, '\n', tmp->buf + rc - p))) {
+ ++p;
+ ++tmp->n_lines;
+ }
+ total_lines += tmp->n_lines;
+
+ /* Try to append to the previous buffer if there's enough free
+ * space
+ */
+ if (tmp->n_bytes + last->n_bytes < BUFFER_SIZE) {
+ memcpy(&last->buf[last->n_bytes], tmp->buf, tmp->n_bytes);
+ last->n_bytes += tmp->n_bytes;
+ last->n_lines += tmp->n_lines;
+ } else {
+ last = last->next = tmp;
+ if (total_lines - first->n_lines > n_lines) {
+ tmp = first;
+ total_lines -= first->n_lines;
+ first = first->next;
+ } else
+ tmp = emalloc(sizeof(struct line_buf));
+ }
+ }
+
+ free(tmp);
+
+ if (rc < 0) {
+ fprintf(stderr, "Error: Could not read from %s\n", pretty_name(f->name));
+ goto err_out;
+ }
+
+ if (last->n_bytes == 0)
+ goto err_out;
+
+ /* Count incomplete lines */
+ if (last->buf[last->n_bytes - 1] != '\n') {
+ ++last->n_lines;
+ ++total_lines;
+ }
+
+ /* Skip unneeded buffers */
+ for (tmp = first; total_lines - tmp->n_lines > n_lines; tmp = tmp->next)
+ total_lines -= tmp->n_lines;
+
+ {
+ char const *p = tmp->buf;
+ if (total_lines > n_lines) {
+ size_t j;
+ for (j = total_lines - n_lines; j; --j) {
+ p = memchr(p, '\n', tmp->buf + tmp->n_bytes - p);
+ ++p;
+ }
+ }
+
+ write(STDOUT_FILENO, p, tmp->buf + tmp->n_bytes - p);
+ }
+
+ for (tmp = tmp->next; tmp; tmp = tmp->next)
+ if (write(STDOUT_FILENO, tmp->buf, tmp->n_bytes) <= 0) {
+ /* e.g. when writing to a pipe which gets closed */
+ fprintf(stderr, "Error: Could not write to stdout (%s)\n", strerror(errno));
+ rc = -1;
+ break;
+ }
+
+ rc = 0;
+
+err_out:
+ while (first) {
+ tmp = first->next;
+ free(first);
+ first = tmp;
+ }
+
+ return rc;
+}
+
+/* TODO: Implement me :) */
+static ssize_t tail_pipe_bytes(struct file_struct *f, unsigned long n_bytes)
+{
+ ssize_t rc;
+ char buf[BUFFER_SIZE];
- /* FIXME: We will just tail everything here for now */
while ((rc = read(f->fd, buf, BUFFER_SIZE)) > 0) {
if (write(STDOUT_FILENO, buf, (size_t) rc) <= 0) {
/* e.g. when writing to a pipe which gets closed */
@@ -265,8 +380,15 @@ static int tail_file(struct file_struct *f, unsigned long n_units, char mode, ch
}
/* Cannot seek on these */
- if (IS_PIPELIKE(finfo.st_mode) || f->fd == STDIN_FILENO)
- return tail_pipe(f);
+ if (IS_PIPELIKE(finfo.st_mode) || f->fd == STDIN_FILENO) {
+ if (verbose)
+ write_header(f->name);
+
+ if (mode == M_LINES)
+ return tail_pipe_lines(f, n_units);
+ else
+ return tail_pipe_bytes(f, n_units);
+ }
f->st_size = finfo.st_size;
@@ -281,14 +403,14 @@ static int tail_file(struct file_struct *f, unsigned long n_units, char mode, ch
return -1;
}
- if (verbose)
- write_header(f->name);
-
if (lseek(f->fd, offset, SEEK_SET) == (off_t) -1) {
fprintf(stderr, "Error: Could not seek in file '%s' (%s)\n", f->name, strerror(errno));
return -1;
}
+ if (verbose)
+ write_header(f->name);
+
while ((bytes_read = read(f->fd, buf, BUFFER_SIZE)) > 0)
write(STDOUT_FILENO, buf, (size_t) bytes_read);
diff --git a/inotail.h b/inotail.h
index 836c1de..8e0f30a 100644
--- a/inotail.h
+++ b/inotail.h
@@ -7,8 +7,8 @@
#ifndef _INOTAIL_H
#define _INOTAIL_H
-/* Number of items to tail. */
-#define DEFAULT_N_LINES 10
+#define BUFFER_SIZE 4096
+#define DEFAULT_N_LINES 10 /* Number of items to tail. */
/* tail modes */
enum { M_LINES, M_BYTES };
@@ -22,6 +22,14 @@ struct file_struct {
int i_watch; /* Inotify watch associated with file_struct */
};
+/* struct for linked list of buffers/lines in tail_pipe_lines */
+struct line_buf {
+ char buf[BUFFER_SIZE];
+ size_t n_lines;
+ size_t n_bytes;
+ struct line_buf *next;
+};
+
#define IS_PIPELIKE(mode) \
(S_ISFIFO(mode) || S_ISSOCK(mode))