Below is the file 'pipeprogress.c' from this revision. You can also download the file.


#include <sys/types.h>
#include <sys/stat.h>
#include <sys/uio.h>
#include <unistd.h>
#include <fcntl.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

#define  BUFFER_SIZE    (32*(1<<10))
#define  FALSE          0
#define  TRUE           (!(FALSE))

struct progress {
        long pos, size;
        int ticks, max_ticks;
};

void fail(const char *mesg)
{
        fprintf(stderr, "failure: %s (%s)\n", mesg, strerror(errno));
        exit(1);
}

void
display_progress(struct progress *p, int full_display)
{
        int i;

        /* ANSI codes nicked from http://en.wikipedia.org/wiki/ANSI_X3.64 */
        if (full_display) {
                fputs("\x1B[2K", stderr);
                fputs("\x1B[1G | ", stderr);
                for (i=0;i<p->ticks;i++) {
                        fputc('-', stderr);
                }
                for (;i<p->max_ticks;i++) {
                        fputc(' ', stderr);
                }
                fputs(" |", stderr);
        } else {
                fprintf(stderr, "\x1B[%dG-", p->ticks+4);
        }
        fprintf(stderr, "\x1B[%dG%2.f%%", p->max_ticks+4+4, (100.0 * p->pos) / (p->size));
}

void
new_progress(struct progress *p, long size, int max_ticks)
{
        p->pos = 0;
        p->size = size;
        p->ticks = 0;
        p->max_ticks = max_ticks;
        display_progress(p, TRUE);
}

void
update_progress(struct progress *p, long bytes)
{
        int new_ticks;
        p->pos += bytes;
        new_ticks = (p->pos) / (p->size / p->max_ticks);
        if (new_ticks != p->ticks) {
                display_progress(p, FALSE);
                p->ticks = new_ticks;
        }
}

void
finish_progress(struct progress *p)
{
        p->ticks = p->max_ticks;
        display_progress(p, TRUE);
        fputs("\n", stderr);
}

void
copy_to_fd(const char *buf, ssize_t nbytes, int to_fd)
{
        ssize_t written = 0;

        while (written < nbytes) {
                ssize_t wbytes = write(to_fd, buf+written, nbytes-written);
                if (wbytes < 0) {
                        fail("failure writing to stdout");
                }
                written += wbytes;
        }
}

void
pipe_progress(const char *path)
{
        struct progress p;
        char buf[BUFFER_SIZE];
        struct stat st;
        int fd;

        fd = open(path, 0);
        if (fd < 0) {
                fail("unable to open output file");
        }
        if (fstat(fd, &st) < 0) {
                fail("unable to stat file descriptor");
        }
        new_progress(&p, st.st_size, 60);
        for (;;) {
                int nbytes;
                nbytes = read(fd, &buf, BUFFER_SIZE);
                if (nbytes < 0) {
                        fail("error reading from file");
                } else if (nbytes == 0) {
                        /* done! */
                        break;
                }
                update_progress(&p, nbytes);
                copy_to_fd(buf, nbytes, 1);
        }
        finish_progress(&p);
}

int
main(int argc, char *argv[])
{
        int i;

        for (i=1;i<argc;i++) {
                pipe_progress(argv[i]);
        }

        return 0;
}