xref: /inferno-os/appl/cmd/puttar.b (revision 30f20c659654702bf169e39d065836a48524d8df)
1# read list of pathnames on stdin, write POSIX.1 tar on stdout
2# Copyright(c)1996 Lucent Technologies.  All Rights Reserved.
3# 22 Dec 1996 ehg@bell-labs.com
4
5implement puttar;
6include "sys.m";
7	sys: Sys;
8	print, sprint, fprint: import sys;
9	stdout, stderr: ref sys->FD;
10include "draw.m";
11include "bufio.m";
12	bufio: Bufio;
13	Iobuf: import bufio;
14
15puttar: module{
16	init:   fn(nil: ref Draw->Context, nil: list of string);
17};
18
19Warning(mess: string)
20{
21	fprint(stderr,"warning: puttar: %s: %r\n",mess);
22}
23
24Error(mess: string){
25	fprint(stderr,"puttar: %s: %r\n",mess);
26	exit;
27}
28
29TBLOCK: con 512;	# tar logical blocksize
30NBLOCK: con 20;		# blocking factor for efficient write
31tarbuf := array[NBLOCK*TBLOCK] of byte;	# for output
32nblock := 0;		# how many blocks of data are in tarbuf
33
34flushblocks(){
35	if(nblock<=0) return;
36	if(nblock<NBLOCK){
37		for(i:=(nblock+1)*TBLOCK;i<NBLOCK*TBLOCK;i++)
38			tarbuf[i] = byte 0;
39	}
40	i := sys->write(stdout,tarbuf,NBLOCK*TBLOCK);
41	if(i!=NBLOCK*TBLOCK)
42		Error("write error");
43	nblock = 0;
44}
45
46putblock(data:array of byte){
47	# all writes are done through here, so we can guarantee
48	#              10kbyte blocks if writing to tape device
49	if(len data!=TBLOCK)
50		Error("putblock wants TBLOCK chunks");
51	tarbuf[nblock*TBLOCK:] = data;
52	nblock++;
53	if(nblock>=NBLOCK)
54		flushblocks();
55}
56
57packname(hdr:array of byte, name:string){
58	utf := array of byte name;
59	n := len utf;
60	if(n<=100){
61		hdr[0:] = utf;
62		return;
63	}
64	for(i:=n-101; i<n && int utf[i] != '/'; i++){}
65	if(i==n) Error(sprint("%s > 100 bytes",name));
66	if(i>155) Error(sprint("%s too long\n",name));
67	hdr[0:] = utf[i+1:n];
68	hdr[345:] = utf[0:i];  # tar supplies implicit slash
69}
70
71octal(width:int, val:int):array of byte{
72	octal := array of byte "01234567";
73	a := array[width] of byte;
74	for(i:=width-1; i>=0; i--){
75		a[i] = octal[val&7];
76		val >>= 3;
77	}
78	return a;
79}
80
81chksum(hdr: array of byte):int{
82	sum := 0;
83	for(i:=0; i<len hdr; i++)
84		sum += int hdr[i];
85	return sum;
86}
87
88hdr, zeros, ibuf : array of byte;
89
90tar(file : string)
91{
92	ifile: ref sys->FD;
93
94	(rc,stat) := sys->stat(file);
95	if(rc<0){ Warning(sprint("cannot stat %s",file)); return; };
96	ifile = sys->open(file,sys->OREAD);
97	if(ifile==nil) Error(sprint("cannot open %s",file));
98	hdr[0:] = zeros;
99	packname(hdr,file);
100	hdr[100:] = octal(7,stat.mode&8r777);
101	hdr[108:] = octal(7,1);
102	hdr[116:] = octal(7,1);
103	hdr[124:] = octal(11,int stat.length);
104	hdr[136:] = octal(11,stat.mtime);
105	hdr[148:] = array of byte "        "; # for chksum
106	hdr[156] = byte '0';
107	if(stat.mode&Sys->DMDIR) hdr[156] = byte '5';
108	hdr[257:] = array of byte "ustar";
109	hdr[263:] = array of byte "00";
110	hdr[265:] = array of byte stat.uid; # assumes len uid<=32
111	hdr[297:] = array of byte stat.gid;
112	hdr[329:] = octal(8,stat.dev);
113	hdr[337:] = octal(8,int stat.qid.path);
114	hdr[148:] = octal(7,chksum(hdr));
115	hdr[155] = byte 0;
116	putblock(hdr);
117	for(bytes := int stat.length; bytes>0;){
118		n := len ibuf;  if(n>bytes) n = bytes;  # min
119		if(sys->read(ifile,ibuf,n)!=n)
120			Error(sprint("read error on %s",file));
121		nb := (n+TBLOCK-1)/TBLOCK;
122		fill := nb*TBLOCK;
123		for(i:=n; i<fill; i++) ibuf[i] = byte 0;
124		for(i=0; i<nb; i++)
125			putblock(ibuf[i*TBLOCK:(i+1)*TBLOCK]);
126		bytes -= n;
127	}
128	ifile = nil;
129}
130
131rtar(file : string)
132{
133	tar(file);
134	# recurse if directory
135	(ok, dir) := sys->stat(file);
136	if (ok < 0){
137		Warning(sprint("cannot stat %s", file));
138		return;
139	}
140	if (dir.mode & Sys->DMDIR) {
141		fd := sys->open(file, sys->OREAD);
142		if (fd == nil)
143			Error(sprint("cannot open %s", file));
144		for (;;) {
145			(n, d) := sys->dirread(fd);
146			if (n <= 0)
147				break;
148			for (i := 0; i < n; i++) {
149				if (file[len file - 1] == '/')
150					rtar(file + d[i].name);
151				else
152					rtar(file + "/" + d[i].name);
153			}
154		}
155	}
156}
157
158init(nil: ref Draw->Context, args: list of string){
159	sys = load Sys Sys->PATH;
160	bufio = load Bufio Bufio->PATH;
161	stdout = sys->fildes(1);
162	stderr = sys->fildes(2);
163
164	hdr = array[TBLOCK] of byte;
165	zeros = array[TBLOCK] of {* => byte 0};
166	ibuf = array[len tarbuf] of byte;
167
168	if (tl args == nil) {
169		stdin := bufio->fopen(sys->fildes(0),bufio->OREAD);
170		if(stdin==nil) Error("can't fopen stdin");
171		while((file := stdin.gets('\n'))!=nil){
172			if(file[len file-1]=='\n') file = file[0:len file-1];
173			tar(file);
174		}
175	}
176	else {
177		for (args = tl args; args != nil; args = tl args)
178			rtar(hd args);
179	}
180	putblock(zeros);
181	putblock(zeros);	# format requires two empty blocks at end
182	flushblocks();
183}
184