tail.c
author viric@llimona
Sat, 09 Feb 2008 11:56:03 +0100
changeset 196 bca29e2a1a86
parent 189 192ea76be533
child 203 664044b1de73
permissions -rw-r--r--
Fixing a bug related to a message on -t on last job, when it was skipped.

/*
    Task Spooler - a task queue system for the unix user
    Copyright (C) 2007  LluĂ­s Batlle i Rossell

    Please find the license in the provided COPYING file.
*/
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>

#include <sys/time.h> /* Dep de main.h */

#include "main.h"

enum { BSIZE=1024 };

static int min(int a, int b)
{
    if (a < b)
        return a;
    return b;
}

static int max(int a, int b)
{
    if (a > b)
        return a;
    return b;
}

static void tail_error(const char *str)
{
    fprintf(stderr, "%s", str);
    fprintf(stderr, ". errno: %i (%s)\n",
                    errno, strerror(errno));
    exit(-1);
}

static void seek_at_last_lines(int fd, int lines)
{
    char buf[BSIZE];
    int lines_found = 0;
    int last_lseek = BSIZE;
    int last_read;
    int move_offset;
    int i;

    last_lseek = lseek(fd, 0, SEEK_END);

    do
    {
        int next_read;
        next_read = min(last_lseek, BSIZE);

        /* we should end looping if last_lseek == 0
         * This means we already read all the file. */
        if (next_read <= 0)
            break;

        /* last_lseek will be -1 at the beginning of the file,
         * if we wanted to go farer than it. */
        last_lseek = lseek(fd, -BSIZE, SEEK_CUR);

        if (last_lseek == -1)
            last_lseek = lseek(fd, 0, SEEK_SET);

        last_read = read(fd, buf, next_read);
        if (last_read == -1)
        {
            if (errno == EINTR)
                continue;
            tail_error("Error reading");
        }


        for(i = last_read-1; i >= 0; --i)
        {
            if (buf[i] == '\n')
            {
                ++lines_found;
                if (lines_found > lines)
                    break;
            }
        }
        
        /* Go back the read bytes */
        last_lseek = lseek(fd, -last_read, SEEK_CUR);
    } while(lines_found < lines);

    /* Calculate the position */
    move_offset = i - last_read + 1;
    lseek(fd, move_offset, SEEK_CUR);
}

static void set_non_blocking(int fd)
{
    long arg;

    arg = O_RDONLY | O_NONBLOCK;
    fcntl(fd, F_SETFL, arg);
}

int tail_file(const char *fname)
{
    int fd;
    int res;
    int waiting_end = 1;
    int end_res = 0;
    int endfile_reached = 0;

    fd_set readset;

    fd = open(fname, O_RDONLY);

    if (fd == -1)
        tail_error("Error: Cannot open the outut file");

    seek_at_last_lines(fd, 10);

    /* we don't want the next read calls to block. */
    set_non_blocking(fd);

    do
    {
        char buf[BSIZE];
        int maxfd;

        FD_ZERO(&readset);
        maxfd = -1;
        if (!endfile_reached)
        {
            FD_SET(fd, &readset);
            maxfd = fd;
        }
        if (waiting_end)
        {
            FD_SET(server_socket, &readset);
            maxfd = max(fd, server_socket);
        }

        /* If we don't have fd's to wait for, let's sleep */
        if (maxfd == -1)
        {
            usleep(1 /* sec */* 1000000);
        } else
        {
            /* Otherwise, do a normal select */
            struct timeval tv = {1 /*sec*/, 0 };
            res = select(maxfd + 1, &readset, 0, 0, &tv);
        }

        if (FD_ISSET(server_socket, &readset))
        {
            end_res = c_wait_job();
            waiting_end = 0;
        }

        /* We always read when select awakes */
        res = read(fd, buf, BSIZE);
        if (res == -1)
        {
            if (errno == EINTR || errno == EAGAIN)
            {
                res = 1; /* Hack for the while condition */
                continue;
            }
            tail_error("Error reading");
        }

        if (res == 0)
            endfile_reached = 1;
        else
            endfile_reached = 0;

        write(1, buf, res);
    } while(!endfile_reached || waiting_end);

    close(fd);

    return end_res;
}