merge tail and head into a single source file
authorPhil Sutter <phil@nwl.cc>
Mon, 5 Jan 2009 12:11:13 +0000 (13:11 +0100)
committerPhil Sutter <phil@nwl.cc>
Sun, 22 Feb 2009 02:00:35 +0000 (03:00 +0100)
This allows having the same init-function for both objects. Code size
could be further reduced, as the print-functions share some code, too.

src/Makefile.am
src/conky.c
src/head.c [deleted file]
src/head.h [deleted file]
src/tail.c [deleted file]
src/tail.h [deleted file]
src/tailhead.c [new file with mode: 0644]
src/tailhead.h [new file with mode: 0644]

index 973f5d1..568b621 100644 (file)
@@ -121,8 +121,6 @@ conky_SOURCES =             \
        conky.h                 \
        $(freebsd)              \
        fs.c                    \
-       head.c                  \
-       head.h                  \
        $(hddtemp)              \
        $(linux)                \
        logging.h               \
@@ -144,8 +142,8 @@ conky_SOURCES =             \
        $(x11)                  \
        $(xmms2)                \
        $(ibm)                  \
-       tail.c                  \
-       tail.h                  \
+       tailhead.c              \
+       tailhead.h              \
        temphelper.c            \
        temphelper.h            \
        text_object.h           \
@@ -167,8 +165,6 @@ EXTRA_DIST =                        \
        freebsd.c               \
        freebsd.h               \
        fs.h                    \
-       head.c                  \
-       head.h                  \
        hddtemp.c               \
        hddtemp.h               \
        linux.c                 \
@@ -190,8 +186,8 @@ EXTRA_DIST =                        \
        openbsd.c               \
        openbsd.h               \
        solaris.c               \
-       tail.c                  \
-       tail.h                  \
+       tailhead.c              \
+       tailhead.h              \
        top.h                   \
        diskio.h                \
        x11.c                   \
index 32376bb..413f056 100644 (file)
 #include "build.h"
 #include "diskio.h"
 #include "fs.h"
-#include "head.h"
 #include "logging.h"
 #include "mixer.h"
 #include "mail.h"
 #include "mboxscan.h"
 #include "temphelper.h"
-#include "tail.h"
+#include "tailhead.h"
 #include "top.h"
 
 /* check for OS and include appropriate headers */
diff --git a/src/head.c b/src/head.c
deleted file mode 100644 (file)
index 04c0823..0000000
+++ /dev/null
@@ -1,144 +0,0 @@
-#include "config.h"
-#include "conky.h"
-#include "logging.h"
-#include "tail.h" /* MAX_TAIL_LINES */
-
-int init_head_object(struct text_object *obj, const char *arg)
-{
-       char buf[64];
-       int n1, n2;
-       FILE *fp;
-       int numargs;
-
-       if (!arg) {
-               ERR("head needs arguments");
-               return 1;
-       }
-
-       numargs = sscanf(arg, "%63s %i %i", buf, &n1, &n2);
-
-       if (numargs < 2 || numargs > 3) {
-               ERR("incorrect number of arguments given to tail object");
-               return 1;
-       }
-
-       if (n1 < 1 || n1 > MAX_TAIL_LINES) {
-               ERR("invalid arg for tail, number of lines must be "
-                               "between 1 and %i", MAX_TAIL_LINES);
-               return 1;
-       }
-
-       if ((fp = fopen(buf, "r")) == NULL) {
-               ERR("head logfile does not exist, or you do not have "
-                               "correct permissions");
-               return 1;
-       }
-
-       obj->data.tail.logfile = malloc(text_buffer_size);
-       strcpy(obj->data.tail.logfile, buf);
-       obj->data.tail.wantedlines = n1;
-       obj->data.tail.interval = update_interval * 2;
-       fclose(fp);
-
-       /* XXX: the following implies update_interval >= 1 ?! */
-       if (numargs == 3 && (n2 < 1 || n2 < update_interval)) {
-               ERR("tail interval must be greater than "
-                   "0 and "PACKAGE_NAME"'s interval, ignoring");
-       } else if (numargs == 3) {
-                       obj->data.tail.interval = n2;
-       }
-       /* asumming all else worked */
-       obj->data.tail.buffer = malloc(text_buffer_size * 20);
-       return 0;
-}
-
-static long fwd_fcharfind(FILE *fp, char val, unsigned int step)
-{
-#define BUFSZ 0x1000
-       long ret = -1;
-       unsigned int count = 0;
-       static char buf[BUFSZ];
-       long orig_pos = ftell(fp);
-       long buf_pos = -1;
-       long buf_size = BUFSZ;
-       char *cur_found = NULL;
-
-       while (count < step) {
-               if (cur_found == NULL) {
-                       buf_size = fread(buf, 1, buf_size, fp);
-                       buf_pos = 0;
-               }
-               cur_found = memchr(buf + buf_pos, val, buf_size - buf_pos);
-               if (cur_found != NULL) {
-                       buf_pos = cur_found - buf + 1;
-                       count++;
-               } else {
-                       if (feof(fp)) {
-                               break;
-                       }
-               }
-       }
-       if (count == step) {
-               ret = ftell(fp) - buf_size + buf_pos - 1;
-       }
-       fseek(fp, orig_pos, SEEK_SET);
-       return ret;
-}
-
-int print_head_object(struct text_object *obj, char *p, size_t p_max_size)
-{
-       FILE *fp;
-       long nl = 0;
-       int iter;
-
-       if (current_update_time - obj->data.tail.last_update < obj->data.tail.interval) {
-               snprintf(p, p_max_size, "%s", obj->data.tail.buffer);
-               return 0;
-       }
-
-       obj->data.tail.last_update = current_update_time;
-
-       fp = fopen(obj->data.tail.logfile, "rt");
-       if (fp == NULL) {
-               /* Send one message, but do not consistently spam
-                * on missing logfiles. */
-               if (obj->data.tail.readlines != 0) {
-                       ERR("head logfile failed to open");
-                       strcpy(obj->data.tail.buffer, "Logfile Missing");
-               }
-               obj->data.tail.readlines = 0;
-               snprintf(p, p_max_size, "Logfile Missing");
-       } else {
-               obj->data.tail.readlines = 0;
-               for (iter = obj->data.tail.wantedlines; iter > 0;
-                               iter--) {
-                       nl = fwd_fcharfind(fp, '\n', iter);
-                       if (nl >= 0) {
-                               break;
-                       }
-               }
-               obj->data.tail.readlines = iter;
-               /* Make sure nl is at least 1 byte smaller than the
-                * buffer max size. */
-               if (nl > (long) ((text_buffer_size * 20) - 1)) {
-                       nl = text_buffer_size * 20 - 1;
-               }
-               nl = fread(obj->data.tail.buffer, 1, nl, fp);
-               fclose(fp);
-               if (nl > 0) {
-                       /* Clean up trailing newline, make sure the buffer
-                        * is null terminated. */
-                       if (obj->data.tail.buffer[nl - 1] == '\n') {
-                               obj->data.tail.buffer[nl - 1] = '\0';
-                       } else {
-                               obj->data.tail.buffer[nl] = '\0';
-                       }
-                       snprintf(p, p_max_size, "%s",
-                                       obj->data.tail.buffer);
-               } else {
-                       strcpy(obj->data.tail.buffer, "Logfile Empty");
-                       snprintf(p, p_max_size, "Logfile Empty");
-               }       /* nl > 0 */
-       }               /* if fp == null */
-       return 0;
-}
diff --git a/src/head.h b/src/head.h
deleted file mode 100644 (file)
index aadcd77..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-#ifndef _HEAD_H
-#define _HEAD_H
-
-#include "text_object.h"
-
-int init_head_object(struct text_object *, const char *);
-int print_head_object(struct text_object *, char *, size_t);
-
-#endif /* _HEAD_H */
diff --git a/src/tail.c b/src/tail.c
deleted file mode 100644 (file)
index 91df315..0000000
+++ /dev/null
@@ -1,282 +0,0 @@
-#include "config.h"
-#include "conky.h"
-#include "logging.h"
-#include "tail.h"
-#include "text_object.h"
-
-#include <errno.h>
-#include <fcntl.h>
-//#include <stdio.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-
-int init_tail_object(struct text_object *obj, const char *arg)
-{
-       char buf[64];
-       int n1, n2;
-       struct stat st;
-       FILE *fp = NULL;
-       int fd;
-       int numargs;
-
-       if (!arg) {
-               ERR("tail needs arguments");
-               return 1;
-       }
-
-       numargs = sscanf(arg, "%63s %i %i", buf, &n1, &n2);
-
-       if (numargs < 2 || numargs > 3) {
-               ERR("incorrect number of arguments given to tail object");
-               return 1;
-       }
-
-       if (n1 < 1 || n1 > MAX_TAIL_LINES) {
-               ERR("invalid arg for tail, number of lines must be "
-                               "between 1 and %i", MAX_TAIL_LINES);
-               return 1;
-       }
-
-       obj->data.tail.fd = -1;
-
-       if (stat(buf, &st) == 0) {
-               if (S_ISFIFO(st.st_mode)) {
-                       fd = open(buf, O_RDONLY | O_NONBLOCK);
-
-                       if (fd == -1) {
-                               ERR("tail logfile does not exist, or you do "
-                                               "not have correct permissions");
-                               return 1;
-                       }
-
-                       obj->data.tail.fd = fd;
-               } else {
-                       fp = fopen(buf, "r");
-               }
-       }
-
-       if (fp || obj->data.tail.fd != -1) {
-               obj->data.tail.logfile = malloc(text_buffer_size);
-               strcpy(obj->data.tail.logfile, buf);
-               obj->data.tail.wantedlines = n1;
-               obj->data.tail.interval = update_interval * 2;
-
-               if (obj->data.tail.fd == -1) {
-                       fclose(fp);
-               }
-       } else {
-               // fclose(fp);
-               ERR("tail logfile does not exist, or you do not have "
-                               "correct permissions");
-               return 1;
-       }
-       /* XXX: the following implies update_interval >= 1 ?! */
-       if (numargs == 3 && (n2 < 1 || n2 < update_interval)) {
-               ERR("tail interval must be greater than "
-                   "0 and "PACKAGE_NAME"'s interval, ignoring");
-       } else if (numargs == 3) {
-                       obj->data.tail.interval = n2;
-       }
-       /* asumming all else worked */
-       obj->data.tail.buffer = malloc(text_buffer_size * 20);
-       return 0;
-}
-
-/* Allows reading from a FIFO (i.e., /dev/xconsole).
- * The file descriptor is set to non-blocking which makes this possible.
- *
- * FIXME: Since lseek cannot seek a file descriptor long lines will break. */
-static void tail_pipe(struct text_object *obj, char *dst, size_t dst_size)
-{
-#define TAIL_PIPE_BUFSIZE      4096
-       int lines = 0;
-       int line_len = 0;
-       int last_line = 0;
-       int fd = obj->data.tail.fd;
-
-       while (1) {
-               char buf[TAIL_PIPE_BUFSIZE];
-               ssize_t len = read(fd, buf, sizeof(buf));
-               int i;
-
-               if (len == -1) {
-                       if (errno != EAGAIN) {
-                               strcpy(obj->data.tail.buffer, "Logfile Read Error");
-                               snprintf(dst, dst_size, "Logfile Read Error");
-                       }
-
-                       break;
-               } else if (len == 0) {
-                       strcpy(obj->data.tail.buffer, "Logfile Empty");
-                       snprintf(dst, dst_size, "Logfile Empty");
-                       break;
-               }
-
-               for (line_len = 0, i = 0; i < len; i++) {
-                       int pos = 0;
-                       char *p;
-
-                       if (buf[i] == '\n') {
-                               lines++;
-
-                               if (obj->data.tail.readlines > 0) {
-                                       int n;
-                                       int olines = 0;
-                                       int first_line = 0;
-
-                                       for (n = 0; obj->data.tail.buffer[n]; n++) {
-                                               if (obj->data.tail.buffer[n] == '\n') {
-                                                       if (!first_line) {
-                                                               first_line = n + 1;
-                                                       }
-
-                                                       if (++olines < obj->data.tail.wantedlines) {
-                                                               pos = n + 1;
-                                                               continue;
-                                                       }
-
-                                                       n++;
-                                                       p = obj->data.tail.buffer + first_line;
-                                                       pos = n - first_line;
-                                                       memmove(obj->data.tail.buffer,
-                                                                       obj->data.tail.buffer + first_line, strlen(p));
-                                                       obj->data.tail.buffer[pos] = 0;
-                                                       break;
-                                               }
-                                       }
-                               }
-
-                               p = buf + last_line;
-                               line_len++;
-                               memcpy(&(obj->data.tail.buffer[pos]), p, line_len);
-                               obj->data.tail.buffer[pos + line_len] = 0;
-                               last_line = i + 1;
-                               line_len = 0;
-                               obj->data.tail.readlines = lines;
-                               continue;
-                       }
-
-                       line_len++;
-               }
-       }
-
-       snprintf(dst, dst_size, "%s", obj->data.tail.buffer);
-}
-
-static long rev_fcharfind(FILE *fp, char val, unsigned int step)
-{
-#define BUFSZ 0x1000
-       long ret = -1;
-       unsigned int count = 0;
-       static char buf[BUFSZ];
-       long orig_pos = ftell(fp);
-       long buf_pos = -1;
-       long file_pos = orig_pos;
-       long buf_size = BUFSZ;
-       char *cur_found;
-
-       while (count < step) {
-               if (buf_pos <= 0) {
-                       if (file_pos > BUFSZ) {
-                               fseek(fp, file_pos - BUFSZ, SEEK_SET);
-                       } else {
-                               buf_size = file_pos;
-                               fseek(fp, 0, SEEK_SET);
-                       }
-                       file_pos = ftell(fp);
-                       buf_pos = fread(buf, 1, buf_size, fp);
-               }
-               cur_found = memrchr(buf, val, (size_t) buf_pos);
-               if (cur_found != NULL) {
-                       buf_pos = cur_found - buf;
-                       count++;
-               } else {
-                       buf_pos = -1;
-                       if (file_pos == 0) {
-                               break;
-                       }
-               }
-       }
-       fseek(fp, orig_pos, SEEK_SET);
-       if (count == step) {
-               ret = file_pos + buf_pos;
-       }
-       return ret;
-}
-
-int print_tail_object(struct text_object *obj, char *p, size_t p_max_size)
-{
-       FILE *fp;
-       long nl = 0, bsize;
-       int iter;
-
-       if (current_update_time - obj->data.tail.last_update < obj->data.tail.interval) {
-               snprintf(p, p_max_size, "%s", obj->data.tail.buffer);
-               return 0;
-       }
-
-       obj->data.tail.last_update = current_update_time;
-
-       if (obj->data.tail.fd != -1) {
-               tail_pipe(obj, p, p_max_size);
-               return 0;
-       }
-
-       fp = fopen(obj->data.tail.logfile, "rt");
-       if (fp == NULL) {
-               /* Send one message, but do not consistently spam
-                * on missing logfiles. */
-               if (obj->data.tail.readlines != 0) {
-                       ERR("tail logfile failed to open");
-                       strcpy(obj->data.tail.buffer, "Logfile Missing");
-               }
-               obj->data.tail.readlines = 0;
-               snprintf(p, p_max_size, "Logfile Missing");
-       } else {
-               obj->data.tail.readlines = 0;
-               /* -1 instead of 0 to avoid counting a trailing
-                * newline */
-               fseek(fp, -1, SEEK_END);
-               bsize = ftell(fp) + 1;
-               for (iter = obj->data.tail.wantedlines; iter > 0;
-                               iter--) {
-                       nl = rev_fcharfind(fp, '\n', iter);
-                       if (nl >= 0) {
-                               break;
-                       }
-               }
-               obj->data.tail.readlines = iter;
-               if (obj->data.tail.readlines
-                               < obj->data.tail.wantedlines) {
-                       fseek(fp, 0, SEEK_SET);
-               } else {
-                       fseek(fp, nl + 1, SEEK_SET);
-                       bsize -= ftell(fp);
-               }
-               /* Make sure bsize is at least 1 byte smaller than the
-                * buffer max size. */
-               if (bsize > (long) ((text_buffer_size * 20) - 1)) {
-                       fseek(fp, bsize - text_buffer_size * 20 - 1,
-                                       SEEK_CUR);
-                       bsize = text_buffer_size * 20 - 1;
-               }
-               bsize = fread(obj->data.tail.buffer, 1, bsize, fp);
-               fclose(fp);
-               if (bsize > 0) {
-                       /* Clean up trailing newline, make sure the
-                        * buffer is null terminated. */
-                       if (obj->data.tail.buffer[bsize - 1] == '\n') {
-                               obj->data.tail.buffer[bsize - 1] = '\0';
-                       } else {
-                               obj->data.tail.buffer[bsize] = '\0';
-                       }
-                       snprintf(p, p_max_size, "%s",
-                                       obj->data.tail.buffer);
-               } else {
-                       strcpy(obj->data.tail.buffer, "Logfile Empty");
-                       snprintf(p, p_max_size, "Logfile Empty");
-               }       /* bsize > 0 */
-       }               /* fp == NULL */
-       return 0;
-}
diff --git a/src/tail.h b/src/tail.h
deleted file mode 100644 (file)
index a269023..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-#ifndef _TAIL_H
-#define _TAIL_H
-
-#include "text_object.h"
-
-#define MAX_TAIL_LINES 100
-
-int init_tail_object(struct text_object *, const char *);
-int print_tail_object(struct text_object *, char *, size_t);
-
-#endif /* _TAIL_H */
diff --git a/src/tailhead.c b/src/tailhead.c
new file mode 100644 (file)
index 0000000..ab89d48
--- /dev/null
@@ -0,0 +1,407 @@
+/* Conky, a system monitor, based on torsmo
+ *
+ * Any original torsmo code is licensed under the BSD license
+ *
+ * All code written since the fork of torsmo is licensed under the GPL
+ *
+ * Please see COPYING for details
+ *
+ * Copyright (c) 2004, Hannu Saransaari and Lauri Hakkarainen
+ * Copyright (c) 2005-2008 Brenden Matthews, Philip Kovacs, et. al.
+ *     (see AUTHORS)
+ * All rights reserved.
+ *
+ * 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 3 of the License, 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+#include "config.h"
+#include "conky.h"
+#include "logging.h"
+#include "tailhead.h"
+#include "text_object.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+int init_tailhead_object(enum tailhead_type type,
+               struct text_object *obj, const char *arg)
+{
+       char buf[64];
+       int n1, n2;
+       struct stat st;
+       FILE *fp = NULL;
+       int fd;
+       int numargs;
+       const char *me;
+
+       /* FIXME: use #define for that */
+       me = (type == TAIL) ? "tail" : "head";
+
+       if (!arg) {
+               ERR("%s needs arguments", me);
+               return 1;
+       }
+
+       numargs = sscanf(arg, "%63s %i %i", buf, &n1, &n2);
+
+       if (numargs < 2 || numargs > 3) {
+               ERR("incorrect number of arguments given to %s object", me);
+               return 1;
+       }
+
+       if (n1 < 1 || n1 > MAX_TAIL_LINES) {
+               ERR("invalid arg for %s, number of lines must be "
+                               "between 1 and %i", me, MAX_TAIL_LINES);
+               return 1;
+       }
+
+       obj->data.tail.fd = -1;
+
+       if (type == HEAD) {
+               goto NO_FIFO;
+       }
+       if (stat(buf, &st) == 0) {
+               if (S_ISFIFO(st.st_mode)) {
+                       fd = open(buf, O_RDONLY | O_NONBLOCK);
+
+                       if (fd == -1) {
+                               ERR("%s logfile does not exist, or you do "
+                                   "not have correct permissions", me);
+                               return 1;
+                       }
+
+                       obj->data.tail.fd = fd;
+               } else {
+NO_FIFO:
+                       fp = fopen(buf, "r");
+               }
+       }
+
+       if (fp || obj->data.tail.fd != -1) {
+               obj->data.tail.logfile = malloc(text_buffer_size);
+               strcpy(obj->data.tail.logfile, buf);
+               obj->data.tail.wantedlines = n1;
+               obj->data.tail.interval = update_interval * 2;
+
+               if (obj->data.tail.fd == -1) {
+                       fclose(fp);
+               }
+       } else {
+               // fclose(fp);
+               ERR("%s logfile does not exist, or you do not have "
+                               "correct permissions", me);
+               return 1;
+       }
+       /* XXX: the following implies update_interval >= 1 ?! */
+       if (numargs == 3 && (n2 < 1 || n2 < update_interval)) {
+               ERR("%s interval must be greater than "
+                   "0 and "PACKAGE_NAME"'s interval, ignoring", me);
+       } else if (numargs == 3) {
+                       obj->data.tail.interval = n2;
+       }
+       /* asumming all else worked */
+       obj->data.tail.buffer = malloc(text_buffer_size * 20);
+       return 0;
+}
+
+/* Allows reading from a FIFO (i.e., /dev/xconsole).
+ * The file descriptor is set to non-blocking which makes this possible.
+ *
+ * FIXME: Since lseek cannot seek a file descriptor long lines will break. */
+static void tail_pipe(struct text_object *obj, char *dst, size_t dst_size)
+{
+#define TAIL_PIPE_BUFSIZE      4096
+       int lines = 0;
+       int line_len = 0;
+       int last_line = 0;
+       int fd = obj->data.tail.fd;
+
+       while (1) {
+               char buf[TAIL_PIPE_BUFSIZE];
+               ssize_t len = read(fd, buf, sizeof(buf));
+               int i;
+
+               if (len == -1) {
+                       if (errno != EAGAIN) {
+                               strcpy(obj->data.tail.buffer, "Logfile Read Error");
+                               snprintf(dst, dst_size, "Logfile Read Error");
+                       }
+
+                       break;
+               } else if (len == 0) {
+                       strcpy(obj->data.tail.buffer, "Logfile Empty");
+                       snprintf(dst, dst_size, "Logfile Empty");
+                       break;
+               }
+
+               for (line_len = 0, i = 0; i < len; i++) {
+                       int pos = 0;
+                       char *p;
+
+                       if (buf[i] == '\n') {
+                               lines++;
+
+                               if (obj->data.tail.readlines > 0) {
+                                       int n;
+                                       int olines = 0;
+                                       int first_line = 0;
+
+                                       for (n = 0; obj->data.tail.buffer[n]; n++) {
+                                               if (obj->data.tail.buffer[n] == '\n') {
+                                                       if (!first_line) {
+                                                               first_line = n + 1;
+                                                       }
+
+                                                       if (++olines < obj->data.tail.wantedlines) {
+                                                               pos = n + 1;
+                                                               continue;
+                                                       }
+
+                                                       n++;
+                                                       p = obj->data.tail.buffer + first_line;
+                                                       pos = n - first_line;
+                                                       memmove(obj->data.tail.buffer,
+                                                                       obj->data.tail.buffer + first_line, strlen(p));
+                                                       obj->data.tail.buffer[pos] = 0;
+                                                       break;
+                                               }
+                                       }
+                               }
+
+                               p = buf + last_line;
+                               line_len++;
+                               memcpy(&(obj->data.tail.buffer[pos]), p, line_len);
+                               obj->data.tail.buffer[pos + line_len] = 0;
+                               last_line = i + 1;
+                               line_len = 0;
+                               obj->data.tail.readlines = lines;
+                               continue;
+                       }
+
+                       line_len++;
+               }
+       }
+
+       snprintf(dst, dst_size, "%s", obj->data.tail.buffer);
+}
+
+static long rev_fcharfind(FILE *fp, char val, unsigned int step)
+{
+#define BUFSZ 0x1000
+       long ret = -1;
+       unsigned int count = 0;
+       static char buf[BUFSZ];
+       long orig_pos = ftell(fp);
+       long buf_pos = -1;
+       long file_pos = orig_pos;
+       long buf_size = BUFSZ;
+       char *cur_found;
+
+       while (count < step) {
+               if (buf_pos <= 0) {
+                       if (file_pos > BUFSZ) {
+                               fseek(fp, file_pos - BUFSZ, SEEK_SET);
+                       } else {
+                               buf_size = file_pos;
+                               fseek(fp, 0, SEEK_SET);
+                       }
+                       file_pos = ftell(fp);
+                       buf_pos = fread(buf, 1, buf_size, fp);
+               }
+               cur_found = memrchr(buf, val, (size_t) buf_pos);
+               if (cur_found != NULL) {
+                       buf_pos = cur_found - buf;
+                       count++;
+               } else {
+                       buf_pos = -1;
+                       if (file_pos == 0) {
+                               break;
+                       }
+               }
+       }
+       fseek(fp, orig_pos, SEEK_SET);
+       if (count == step) {
+               ret = file_pos + buf_pos;
+       }
+       return ret;
+}
+
+int print_tail_object(struct text_object *obj, char *p, size_t p_max_size)
+{
+       FILE *fp;
+       long nl = 0, bsize;
+       int iter;
+
+       if (current_update_time - obj->data.tail.last_update < obj->data.tail.interval) {
+               snprintf(p, p_max_size, "%s", obj->data.tail.buffer);
+               return 0;
+       }
+
+       obj->data.tail.last_update = current_update_time;
+
+       if (obj->data.tail.fd != -1) {
+               tail_pipe(obj, p, p_max_size);
+               return 0;
+       }
+
+       fp = fopen(obj->data.tail.logfile, "rt");
+       if (fp == NULL) {
+               /* Send one message, but do not consistently spam
+                * on missing logfiles. */
+               if (obj->data.tail.readlines != 0) {
+                       ERR("tail logfile failed to open");
+                       strcpy(obj->data.tail.buffer, "Logfile Missing");
+               }
+               obj->data.tail.readlines = 0;
+               snprintf(p, p_max_size, "Logfile Missing");
+       } else {
+               obj->data.tail.readlines = 0;
+               /* -1 instead of 0 to avoid counting a trailing
+                * newline */
+               fseek(fp, -1, SEEK_END);
+               bsize = ftell(fp) + 1;
+               for (iter = obj->data.tail.wantedlines; iter > 0;
+                               iter--) {
+                       nl = rev_fcharfind(fp, '\n', iter);
+                       if (nl >= 0) {
+                               break;
+                       }
+               }
+               obj->data.tail.readlines = iter;
+               if (obj->data.tail.readlines
+                               < obj->data.tail.wantedlines) {
+                       fseek(fp, 0, SEEK_SET);
+               } else {
+                       fseek(fp, nl + 1, SEEK_SET);
+                       bsize -= ftell(fp);
+               }
+               /* Make sure bsize is at least 1 byte smaller than the
+                * buffer max size. */
+               if (bsize > (long) ((text_buffer_size * 20) - 1)) {
+                       fseek(fp, bsize - text_buffer_size * 20 - 1,
+                                       SEEK_CUR);
+                       bsize = text_buffer_size * 20 - 1;
+               }
+               bsize = fread(obj->data.tail.buffer, 1, bsize, fp);
+               fclose(fp);
+               if (bsize > 0) {
+                       /* Clean up trailing newline, make sure the
+                        * buffer is null terminated. */
+                       if (obj->data.tail.buffer[bsize - 1] == '\n') {
+                               obj->data.tail.buffer[bsize - 1] = '\0';
+                       } else {
+                               obj->data.tail.buffer[bsize] = '\0';
+                       }
+                       snprintf(p, p_max_size, "%s",
+                                       obj->data.tail.buffer);
+               } else {
+                       strcpy(obj->data.tail.buffer, "Logfile Empty");
+                       snprintf(p, p_max_size, "Logfile Empty");
+               }       /* bsize > 0 */
+       }               /* fp == NULL */
+       return 0;
+}
+
+long fwd_fcharfind(FILE *fp, char val, unsigned int step)
+{
+#define BUFSZ 0x1000
+       long ret = -1;
+       unsigned int count = 0;
+       static char buf[BUFSZ];
+       long orig_pos = ftell(fp);
+       long buf_pos = -1;
+       long buf_size = BUFSZ;
+       char *cur_found = NULL;
+
+       while (count < step) {
+               if (cur_found == NULL) {
+                       buf_size = fread(buf, 1, buf_size, fp);
+                       buf_pos = 0;
+               }
+               cur_found = memchr(buf + buf_pos, val, buf_size - buf_pos);
+               if (cur_found != NULL) {
+                       buf_pos = cur_found - buf + 1;
+                       count++;
+               } else {
+                       if (feof(fp)) {
+                               break;
+                       }
+               }
+       }
+       if (count == step) {
+               ret = ftell(fp) - buf_size + buf_pos - 1;
+       }
+       fseek(fp, orig_pos, SEEK_SET);
+       return ret;
+}
+
+int print_head_object(struct text_object *obj, char *p, size_t p_max_size)
+{
+       FILE *fp;
+       long nl = 0;
+       int iter;
+
+       if (current_update_time - obj->data.tail.last_update < obj->data.tail.interval) {
+               snprintf(p, p_max_size, "%s", obj->data.tail.buffer);
+               return 0;
+       }
+
+       obj->data.tail.last_update = current_update_time;
+
+       fp = fopen(obj->data.tail.logfile, "rt");
+       if (fp == NULL) {
+               /* Send one message, but do not consistently spam
+                * on missing logfiles. */
+               if (obj->data.tail.readlines != 0) {
+                       ERR("head logfile failed to open");
+                       strcpy(obj->data.tail.buffer, "Logfile Missing");
+               }
+               obj->data.tail.readlines = 0;
+               snprintf(p, p_max_size, "Logfile Missing");
+       } else {
+               obj->data.tail.readlines = 0;
+               for (iter = obj->data.tail.wantedlines; iter > 0;
+                               iter--) {
+                       nl = fwd_fcharfind(fp, '\n', iter);
+                       if (nl >= 0) {
+                               break;
+                       }
+               }
+               obj->data.tail.readlines = iter;
+               /* Make sure nl is at least 1 byte smaller than the
+                * buffer max size. */
+               if (nl > (long) ((text_buffer_size * 20) - 1)) {
+                       nl = text_buffer_size * 20 - 1;
+               }
+               nl = fread(obj->data.tail.buffer, 1, nl, fp);
+               fclose(fp);
+               if (nl > 0) {
+                       /* Clean up trailing newline, make sure the buffer
+                        * is null terminated. */
+                       if (obj->data.tail.buffer[nl - 1] == '\n') {
+                               obj->data.tail.buffer[nl - 1] = '\0';
+                       } else {
+                               obj->data.tail.buffer[nl] = '\0';
+                       }
+                       snprintf(p, p_max_size, "%s",
+                                       obj->data.tail.buffer);
+               } else {
+                       strcpy(obj->data.tail.buffer, "Logfile Empty");
+                       snprintf(p, p_max_size, "Logfile Empty");
+               }       /* nl > 0 */
+       }               /* if fp == null */
+       return 0;
+}
diff --git a/src/tailhead.h b/src/tailhead.h
new file mode 100644 (file)
index 0000000..f3485f1
--- /dev/null
@@ -0,0 +1,47 @@
+/* Conky, a system monitor, based on torsmo
+ *
+ * Any original torsmo code is licensed under the BSD license
+ *
+ * All code written since the fork of torsmo is licensed under the GPL
+ *
+ * Please see COPYING for details
+ *
+ * Copyright (c) 2004, Hannu Saransaari and Lauri Hakkarainen
+ * Copyright (c) 2005-2008 Brenden Matthews, Philip Kovacs, et. al.
+ *     (see AUTHORS)
+ * All rights reserved.
+ *
+ * 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 3 of the License, 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+#ifndef _TAILHEAD_H
+#define _TAILHEAD_H
+
+#include "text_object.h"
+
+#define MAX_TAIL_LINES 100
+
+enum tailhead_type {
+       TAIL,
+       HEAD,
+};
+
+#define init_tail_object(o, a) init_tailhead_object(TAIL, o, a)
+#define init_head_object(o, a) init_tailhead_object(HEAD, o, a)
+
+int init_tailhead_object(enum tailhead_type,
+               struct text_object *, const char *);
+int print_head_object(struct text_object *, char *, size_t);
+int print_tail_object(struct text_object *, char *, size_t);
+
+#endif /* _TAILHEAD_H */