Subversion Repositories planix.SVN

Rev

Blame | Last modification | View Log | RSS feed

/*
 * tarsplit [-d] [-p pfx] [-s size] - split a tar archive into independent
 *      tar archives under some size
 */

#include <u.h>
#include <libc.h>
#include <ctype.h>
#include "tar.h"

enum {
        Stdin,
        Endsize = 2 * Tblock,           /* size of zero blocks at archive end */
};

/* private data */
static char *filenm;
static char *prefix = "ts.";
static vlong size = 512*1024*1024;      /* fits on a CD with room to spare */

static int
opennext(int out, char *prefix)
{
        static int filenum = 0;

        if (out >= 0) {
                fprint(2, "%s: opennext called with file open\n", argv0);
                exits("open botch");
        }
        free(filenm);
        filenm = smprint("%s%.5d", prefix, filenum++);
        fprint(2, "%s: %s ...", filenm, thisnm);
        out = create(filenm, OWRITE, 0666);
        if (out < 0)
                sysfatal("%s: %r", filenm);
        newarch();
        return out;
}

static int
split(int in, int out, char * /* inname */)
{
        vlong len, membsz;
        uvlong outoff = 0;
        static Hblock hdr;
        Hblock *hp = &hdr;

        while (getdir(hp, in, &len)) {
                membsz = Tblock + ROUNDUP((uvlong)len, Tblock);
                if (outoff + membsz + Endsize > size) { /* won't fit? */
                        out = closeout(out, filenm, 1);
                        if (membsz + Endsize > size)
                                sysfatal("archive member %s (%,lld) + overhead "
                                        "exceeds size limit %,lld", hp->name,
                                        len, size);
                }
                if (out < 0)
                        out = opennext(out, prefix);
                /* write directory block */
                writetar(out, (char *)hp, Tblock);
                outoff = passtar(hp, in, out, len);
        }
        return out;
}

void
usage(void)
{
        fprint(2, "usage: %s [-p pfx] [-s size] [file]...\n", argv0);
        exits("usage");
}

void
main(int argc, char **argv)
{
        int out = -1;

        ARGBEGIN {
        case 'p':
                prefix = EARGF(usage());
                break;
        case 's':
                size = atoll(EARGF(usage()));
                if (size < Tblock + Endsize)
                        sysfatal("implausible max size of %lld bytes", size);
                break;
        default:
                usage();
        } ARGEND

        if (argc <= 0)
                out = split(Stdin, out, "/fd/0");
        else
                for (; argc-- > 0; argv++) {
                        int in = open(argv[0], OREAD);

                        if (in < 0)
                                sysfatal("%s: %r", argv[0]);
                        out = split(in, out, argv[0]);
                        close(in);
                }
        closeout(out, filenm, 1);
        exits(0);
}