[Toybox] [PATCH] tail fixes

Felix Janda felix.janda at posteo.de
Sat Aug 31 03:53:09 PDT 2013


This should now hopefully fix the earlier segfaults.

In the case that lseek doesn't work and we count from the end
the algorithms for tail -n and tail -c are now more separate.
(tail -c is now more efficient since it adds lenghts instead of
counting bytes.)


POSIX tail BTW does only accept one input file. tail is not specified
by LSB. I guess it is convenient when combined with "-f" to watch
multiple files.

For tail -f, how about putting the fds into an array and then using
select() to watch the fds?

GNU and busybox print the header again when the file last read from
changes. They behave differently when a file occurs on the command
line more than once.

# HG changeset patch
# User Felix Janda <felix.janda at posteo.de>
# Date 1377945041 -7200
# Node ID 3cd61d16aabec82505da96a142663cedbba8d12a
# Parent  9a059e9b3a17a372fc8b225f512af57c72f4eeaa
tail: Some fixes

- Rewrite most of the not lseek() logic
- Change meaning of len in line_list
- Use single instead of double linked list

diff -r 9a059e9b3a17 -r 3cd61d16aabe toys/posix/tail.c
--- a/toys/posix/tail.c	Fri Aug 30 20:09:10 2013 +0200
+++ b/toys/posix/tail.c	Sat Aug 31 12:30:41 2013 +0200
@@ -38,19 +38,20 @@
 )
 
 struct line_list {
-  struct line_list *next, *prev;
+  struct line_list *next;
   char *data;
-  int len;
+  size_t len;
 };
 
-static struct line_list *get_chunk(int fd, int len)
+static struct line_list *get_chunk(int fd, size_t len)
 {
   struct line_list *line = xmalloc(sizeof(struct line_list)+len);
 
-  line->data = ((char *)line) + sizeof(struct line_list);
+  line->data = (char*)line + sizeof(struct line_list);
   line->len = readall(fd, line->data, len);
+  line->next = 0;
 
-  if (line->len < 1) {
+  if (line->len + 1 < 2) {
     free(line);
     return 0;
   }
@@ -61,7 +62,9 @@
 static void dump_chunk(void *ptr)
 {
   struct line_list *list = ptr;
-  xwrite(1, list->data, list->len);
+  size_t len = list->len - (list->data - (char*)list - sizeof(struct line_list));
+
+  xwrite(1, list->data, len);
   free(list);
 }
 
@@ -71,11 +74,11 @@
 static int try_lseek(int fd, long bytes, long lines)
 {
   struct line_list *list = 0, *temp;
-  int flag = 0, chunk = sizeof(toybuf);
-  ssize_t pos = lseek(fd, 0, SEEK_END);
+  int flag = 0;
+  size_t chunk = sizeof(toybuf), pos = lseek(fd, 0, SEEK_END);
 
   // If lseek() doesn't work on this stream, return now.
-  if (pos<0) return 0;
+  if (pos == -1) return 0;
 
   // Seek to the right spot, output data from there.
   if (bytes) {
@@ -88,40 +91,36 @@
 
   bytes = pos;
   while (lines && pos) {
-    int offset;
+    size_t offset;
 
     // Read in next chunk from end of file
-    if (chunk>pos) chunk = pos;
+    if (chunk > pos) chunk = pos;
     pos -= chunk;
     if (pos != lseek(fd, pos, SEEK_SET)) {
       perror_msg("seek failed");
       break;
     }
     if (!(temp = get_chunk(fd, chunk))) break;
-    if (list) list->next = temp;
+    if (list) temp->next = list;
     list = temp;
 
     // Count newlines in this chunk.
     offset = list->len;
     while (offset--) {
       // If the last line ends with a newline, that one doesn't count.
-      if (!flag) {
-        flag++;
-
-        continue;
-      }
+      if (!flag) flag++;
 
       // Start outputting data right after newline
-      if (list->data[offset] == '\n' && !++lines) {
+      else if (list->data[offset] == '\n' && !++lines) {
         offset++;
         list->data += offset;
-        list->len -= offset;
 
-        break;
+        goto done;
       }
     }
   }
 
+done:
   // Output stored data
   llist_traverse(list, dump_chunk);
 
@@ -143,58 +142,54 @@
   // Are we measuring from the end of the file?
 
   if (bytes<0 || lines<0) {
-    struct line_list *list = 0, *new;
+    struct line_list *head = 0, *tail, *new;
+    // circular buffer of lines
+    struct {
+      char *start;
+      struct line_list *inchunk;
+    } *l = xzalloc(2*-lines*sizeof(void*));
+    int i = 0, flag = 0;
+    size_t count, len = bytes;
 
     // The slow codepath is always needed, and can handle all input,
     // so make lseek support optional.
-    if (CFG_TAIL_SEEK && try_lseek(fd, bytes, lines));
+    if (CFG_TAIL_SEEK && try_lseek(fd, bytes, lines)) return;
 
     // Read data until we run out, keep a trailing buffer
-    else for (;;) {
-      int len, count;
+    for (;;) {
       char *try;
 
       if (!(new = get_chunk(fd, sizeof(toybuf)))) break;
       // append in order
-      dlist_add_nomalloc((void *)&list, (struct double_list *)new);
+      if (head) tail->next = new;
+      else head = new;
+      tail = new;
 
-      // Measure new chunk, discarding extra data from buffer
-      len = new->len;
       try = new->data;
-      for (count=0; count<len; count++) {
-        if ((toys.optflags & FLAG_c) && bytes) {
-          bytes++;
-          continue;
+      if (lines) for (count = 0; count < new->len; count++, try++) {
+        if (flag) { // last char was a newline
+            while (l[i].inchunk && (l[i].inchunk!=head)) free(llist_pop(&head));
+            l[i].inchunk = tail;
+            l[i].start = try;
+            i = (i + 1) % -lines;
+            flag = 0;
         }
-
-        if (lines) {
-          if(try[count] != '\n' && count != len-1) continue;
-          if (lines<0) {
-            if (!++lines) ++lines;
-            continue;
-          }
+        if (*try == '\n') flag = 1;
+      } else { // bytes
+        if (len + new->len < len) flag = 1; // overflow -> have now read enough
+        for (len += new->len; flag && (len - head->len < len);) {
+          len -= head->len;
+          free(llist_pop(&head));
         }
-
-        // Time to discard data; given that bytes and lines were
-        // nonzero coming in, we can't discard too much if we're
-        // measuring right.
-        do {
-          char c = *(list->data++);
-          if (!(--list->len)) {
-            struct line_list *next = list->next;
-            list->prev->next = next;
-            list->next->prev = list->prev;
-            free(list);
-            list = next;
-          }
-          if (c == '\n') break;
-        } while (lines);
       }
     }
+    if (lines) head->data = l[i].start;
+    else head->data += len;
 
     // Output/free the buffer.
-    llist_traverse(list, dump_chunk);
+    llist_traverse(head, dump_chunk);
 
+    free(l);
   // Measuring from the beginning of the file.
   } else for (;;) {
     int len, offset = 0;

 1377946389.0


More information about the Toybox mailing list