/*
 * reverse: A small program for reversing the order of lines in a text file.
 *
 * Copyright (c) 2007  Ted Percival <ted@midg3t.net>
 *
 * This script is free software; you can redistribute it and/or modify 
 * it under the terms of the GNU Lesser General Public License as 
 * published by the Free Software Foundation; either version 2.1 of the 
 * License, or (at your option) any later version.
 *
 * This script is provided 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 
 * Lesser General Public License for more details.
 *
 * You can receive a copy of the GNU Lesser General Public License from 
 * http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html or by writing 
 * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth 
 * Floor, Boston, MA  02110-1301  USA.
 */

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <assert.h>

static int reverse(const char *const data, size_t length, FILE* dest);

/**
 * Returns -1 on failure, 0 on success.
 */
int reverse(const char *const data, size_t length, FILE* dest) {
    const char *start, *lastchar;

    assert(data != NULL);
    assert(dest != NULL);

    if (length == 0)
        return 0;

    lastchar = &data[length];

    while (lastchar > data) {
        size_t ret;
        size_t len;

        start = --lastchar;

        while(start > data && *(start - 1) != '\n')
            --start;

        len = lastchar - start + 1;

        /* FIXME: Ignoring errors */
        ret = fwrite(start, sizeof(char), len, dest);

        if (ret < len) {
            perror("fwrite()");
            return -1;
        }

        /* For files with no newline at EOF */
        if (*lastchar != '\n')
            putc('\n', dest);

        lastchar = start;
    }

    return 0;
}

int main(int argc, char *argv[]) {
    const char *filename;
    int fd;
    char *file;
    struct stat fileinfo;
    size_t mmap_size;
    int ret;

    if (argc != 2) {
        fprintf(stderr, "Usage: %s <filename>\n", argv[0]);
        return 2;
    }

    filename = argv[1];

    fd = open(filename, O_RDONLY);
    if (fd == -1) {
        perror("open()");
        return 1;
    }

    if (fstat(fd, &fileinfo) == -1) {
        perror("fstat()");
        return 1;
    }

    if (fileinfo.st_size == 0) {
        close(fd);
        return 0;
    }

    mmap_size = fileinfo.st_size;
    mmap_size = ((int)(mmap_size / sysconf(_SC_PAGE_SIZE))+1) * sysconf(_SC_PAGE_SIZE);

    /* XXX: Mapping the entire file might cause errors. */
    file = mmap(NULL, mmap_size, PROT_READ, MAP_SHARED, fd, 0);

    if (file == MAP_FAILED) {
        perror("mmap()");
        return 1;
    }

    ret = !!reverse(file, fileinfo.st_size, stdout);

    munmap(file, fileinfo.st_size);

    close(fd);

    return ret;
}

/* vim:ts=4:sw=4:et:tw=80
 */

