xref: /plan9-contrib/sys/src/cmd/tarsplit/tarsplit.c (revision ad6ca847b1a6a504acb0003cd6c5c6d92687369b)
1 /*
2  * tarsplit [-d] [-p pfx] [-s size] - split a tar archive into independent
3  *	tar archives under some size
4  */
5 
6 #include <u.h>
7 #include <libc.h>
8 #include <ctype.h>
9 #include "tar.h"
10 
11 enum {
12 	Stdin,
13 	Endsize	= 2 * Tblock,		/* size of zero blocks at archive end */
14 };
15 
16 /* private data */
17 static char *filenm;
18 static char *prefix = "ts.";
19 static vlong size = 512*1024*1024;	/* fits on a CD with room to spare */
20 
21 static int
opennext(int out,char * prefix)22 opennext(int out, char *prefix)
23 {
24 	static int filenum = 0;
25 
26 	if (out >= 0) {
27 		fprint(2, "%s: opennext called with file open\n", argv0);
28 		exits("open botch");
29 	}
30 	free(filenm);
31 	filenm = smprint("%s%.5d", prefix, filenum++);
32 	fprint(2, "%s: %s ...", filenm, thisnm);
33 	out = create(filenm, OWRITE, 0666);
34 	if (out < 0)
35 		sysfatal("%s: %r", filenm);
36 	newarch();
37 	return out;
38 }
39 
40 static int
split(int in,int out,char *)41 split(int in, int out, char * /* inname */)
42 {
43 	vlong len, membsz;
44 	uvlong outoff = 0;
45 	static Hblock hdr;
46 	Hblock *hp = &hdr;
47 
48 	while (getdir(hp, in, &len)) {
49 		membsz = Tblock + ROUNDUP((uvlong)len, Tblock);
50 		if (outoff + membsz + Endsize > size) {	/* won't fit? */
51 			out = closeout(out, filenm, 1);
52 			if (membsz + Endsize > size)
53 				sysfatal("archive member %s (%,lld) + overhead "
54 					"exceeds size limit %,lld", hp->name,
55 					len, size);
56 		}
57 		if (out < 0)
58 			out = opennext(out, prefix);
59 		/* write directory block */
60 		writetar(out, (char *)hp, Tblock);
61 		outoff = passtar(hp, in, out, len);
62 	}
63 	return out;
64 }
65 
66 void
usage(void)67 usage(void)
68 {
69 	fprint(2, "usage: %s [-p pfx] [-s size] [file]...\n", argv0);
70 	exits("usage");
71 }
72 
73 void
main(int argc,char ** argv)74 main(int argc, char **argv)
75 {
76 	int out = -1;
77 
78 	ARGBEGIN {
79 	case 'p':
80 		prefix = EARGF(usage());
81 		break;
82 	case 's':
83 		size = atoll(EARGF(usage()));
84 		if (size < Tblock + Endsize)
85 			sysfatal("implausible max size of %lld bytes", size);
86 		break;
87 	default:
88 		usage();
89 	} ARGEND
90 
91 	if (argc <= 0)
92 		out = split(Stdin, out, "/fd/0");
93 	else
94 		for (; argc-- > 0; argv++) {
95 			int in = open(argv[0], OREAD);
96 
97 			if (in < 0)
98 				sysfatal("%s: %r", argv[0]);
99 			out = split(in, out, argv[0]);
100 			close(in);
101 		}
102 	closeout(out, filenm, 1);
103 	exits(0);
104 }
105