OSDN Git Service

Add new source files
[armadillo/armadillo1.git] / src / jp / sfjp / armadillo / archive / tar / TarHeader.java
1 package jp.sfjp.armadillo.archive.tar;
2
3 import java.io.*;
4 import java.util.*;
5
6 public final class TarHeader {
7
8     static final int BLOCK_SIZE = 512;
9
10     private static final String MAGIC_USTAR = "ustar";
11
12     private final byte[] buffer;
13     private int position;
14
15     public TarHeader() {
16         this.buffer = new byte[BLOCK_SIZE];
17         this.position = 0;
18     }
19
20     public TarEntry read(InputStream is) throws IOException {
21         reset();
22         final int readLength = draw(is, buffer);
23         if (readLength == 0)
24             return null;
25         if (readLength != BLOCK_SIZE)
26             throw new TarException("bad header: size=" + readLength);
27         if (buffer[0] == 0x00 && isEmptyBlock()) {
28             if (is.read(buffer) == BLOCK_SIZE && isEmptyBlock())
29                 return null;
30             throw new TarException("bad end-of-archive");
31         }
32         try {
33             if (isUstar())
34                 return readUstar(is);
35             else
36                 return readTar(is);
37         }
38         catch (RuntimeException ex) {
39             throw new TarException("bad header at " + position, ex);
40         }
41     }
42
43     private boolean isUstar() {
44         final String s = new String(buffer, 257, 5);
45         return s.equals(MAGIC_USTAR);
46     }
47
48     @SuppressWarnings("unused")
49     private TarEntry readTar(InputStream is) throws IOException {
50         // Header Block (TAR Format)
51         final byte[] name; // name of file (100bytes)
52         final int mode; // file mode (8bytes)
53         final int uid; // owner user ID (8bytes)
54         final int gid; // owner group ID (8bytes)
55         final long size; // length of file in bytes (12bytes)
56         final long mtime; // modify time of file (12bytes)
57         final int chksum; // checksum for header (8bytes)
58         final int link; // indicator for links (1byte)
59         final String linkname; // name of linked file (100bytes)
60         // ---
61         name = clipNameField(100);
62         mode = clipAsInt(8);
63         uid = clipAsInt(8);
64         gid = clipAsInt(8);
65         size = clipAsLong(12);
66         mtime = clipAsLong(12);
67         chksum = clipAsInt(8);
68         link = clipAsInt(1);
69         linkname = clipAsString(100);
70         TarEntry entry = new TarEntry();
71         entry.setName(name);
72         entry.setMode(mode);
73         entry.uid = uid;
74         entry.gid = gid;
75         entry.size = size;
76         entry.mtime = mtime;
77         entry.chksum = chksum;
78         entry.linkname = linkname;
79         return entry;
80     }
81
82     private TarEntry readUstar(InputStream is) throws IOException {
83         TarEntry entry = new TarEntry();
84         readUstar(is, entry);
85         if (entry.typeflag == 'L') {
86             // LongLink
87             final int readLength = draw(is, buffer);
88             assert readLength == BLOCK_SIZE;
89             assert entry.size <= Integer.MAX_VALUE;
90             position = 0;
91             byte[] bytes = clipNameField((int)entry.size);
92             position = 0;
93             final int readLength2 = draw(is, buffer);
94             assert readLength2 == BLOCK_SIZE;
95             readUstar(is, entry);
96             entry.setName(bytes);
97         }
98         return entry;
99     }
100
101     private void readUstar(InputStream is, TarEntry entry) throws IOException {
102         // Header Block (USTAR Format)
103         final byte[] name; // name of file (100bytes)
104         final int mode; // file mode (8bytes)
105         final int uid; // owner user ID (8bytes)
106         final int gid; // owner group ID (8bytes)
107         final long size; // length of file in bytes (12bytes)
108         final long mtime; // modify time of file (12bytes)
109         final int chksum; // checksum for header (8bytes)
110         final char typeflag; // type of file (1byte)
111         final String linkname; // name of linked file (100bytes)
112         final String magic; // USTAR indicator (6bytes)
113         final String version; // USTAR version (2bytes)
114         final String uname; // owner user name (32bytes)
115         final String gname; // owner group name (32bytes)
116         final String devmajor; // device major number (8bytes)
117         final String devminor; // device minor number (8bytes)
118         final String prefix; // prefix for file name (155bytes)
119         // ---
120         name = clipNameField(100);
121         mode = clipAsInt(8);
122         uid = clipAsInt(8);
123         gid = clipAsInt(8);
124         size = clipAsLong(12);
125         mtime = clipAsLong(12);
126         chksum = clipAsInt(8);
127         typeflag = clipChar();
128         linkname = clipAsString(100);
129         magic = clipAsString(6);
130         version = clipAsString(2);
131         uname = clipAsString(32);
132         gname = clipAsString(32);
133         devmajor = clipAsString(8);
134         devminor = clipAsString(8);
135         prefix = clipAsString(155);
136         entry.setName(name);
137         entry.setMode(mode);
138         entry.uid = uid;
139         entry.gid = gid;
140         entry.size = size;
141         entry.mtime = mtime;
142         entry.chksum = chksum;
143         entry.typeflag = typeflag;
144         entry.linkname = linkname;
145         entry.magic = magic;
146         entry.version = version;
147         entry.uname = uname;
148         entry.gname = gname;
149         entry.devmajor = devmajor;
150         entry.devminor = devminor;
151         entry.prefix = prefix;
152     }
153
154     private char clipChar() {
155         final String s = clipAsString(1);
156         if (s.isEmpty())
157             return ' ';
158         return s.charAt(0);
159     }
160
161     private static int draw(InputStream is, byte[] bytes) throws IOException {
162         int readLength = 0;
163         int offset = 0;
164         while (readLength < BLOCK_SIZE) {
165             int read = is.read(bytes, offset, BLOCK_SIZE - readLength);
166             if (read <= 0)
167                 break;
168             offset += read;
169             readLength += read;
170         }
171         return readLength;
172     }
173
174     public void write(OutputStream os, TarEntry entry) throws IOException {
175         try {
176             reset();
177             if (entry.magic.startsWith(MAGIC_USTAR))
178                 writeUstar(os, entry);
179             else
180                 writeTar(os, entry);
181         }
182         catch (RuntimeException ex) {
183             throw new TarException("bad header at " + position, ex);
184         }
185     }
186
187     private void writeTar(OutputStream os, TarEntry entry) throws IOException {
188         // Header Block (TAR Format)
189         final String name; // name of file (100bytes)
190         final int mode; // file mode (8bytes)
191         final int uid; // owner user ID (8bytes)
192         final int gid; // owner group ID (8bytes)
193         final long size; // length of file in bytes (12bytes)
194         final long mtime; // modify time of file (12bytes)
195         final int chksum; // checksum for header (8bytes)
196         final int link; // indicator for links (1byte)
197         final String linkname; // name of linked file (100bytes)
198         // ---
199         name = entry.name();
200         mode = entry.getMode();
201         uid = entry.uid;
202         gid = entry.gid;
203         size = entry.size;
204         mtime = entry.mtime;
205         chksum = entry.chksum;
206         link = 0;
207         linkname = entry.linkname;
208         patch(100, name);
209         patch(8, mode);
210         patch(8, uid);
211         patch(8, gid);
212         patch(12, size);
213         patch(12, mtime);
214         patch(8, chksum);
215         patch(1, link);
216         patch(100, linkname);
217         os.write(buffer);
218         throw new TarException("not impl yet (old Tar)");
219     }
220
221     @SuppressWarnings("unused")
222     private void writeUstar(OutputStream os, TarEntry entry) throws IOException {
223         // Header Block (USTAR Format)
224         final String name; // name of file (100bytes)
225         final int mode; // file mode (8bytes)
226         final int uid; // owner user ID (8bytes)
227         final int gid; // owner group ID (8bytes)
228         final long size; // length of file in bytes (12bytes)
229         final long mtime; // modify time of file (12bytes)
230         final int chksum; // checksum for header (8bytes)
231         final int typeflag; // type of file (1byte)
232         final String linkname; // name of linked file (100bytes)
233         final String magic; // USTAR indicator (6bytes)
234         final String version; // USTAR version (2bytes)
235         final String uname; // owner user name (32bytes)
236         final String gname; // owner group name (32bytes)
237         final String devmajor; // device major number (8bytes)
238         final String devminor; // device minor number (8bytes)
239         final String prefix; // prefix for file name (155bytes)
240         // ---
241         name = entry.name();
242         mode = entry.getMode();
243         uid = entry.uid;
244         gid = entry.gid;
245         size = entry.size;
246         mtime = entry.mtime;
247         chksum = entry.chksum;
248         typeflag = entry.typeflag;
249         linkname = entry.linkname;
250         magic = entry.magic;
251         version = entry.version;
252         uname = entry.uname;
253         gname = entry.gname;
254         devmajor = entry.devmajor;
255         devminor = entry.devminor;
256         prefix = entry.prefix;
257         patch(100, name);
258         patch(8, mode);
259         patch(8, uid);
260         patch(8, gid);
261         patch(12, size);
262         patch(12, mtime);
263         patch(8, "        "); // set after calculating checksum
264         patch(1, typeflag);
265         patch(100, linkname);
266         patch(6, MAGIC_USTAR + ' '); // ignore input
267         patch(2, version);
268         patch(32, uname);
269         patch(32, gname);
270         patch(8, devmajor);
271         patch(8, devminor);
272         patch(155, prefix);
273         // calculate checksum
274         int checksum = 0;
275         for (int i = 0; i < buffer.length; i++)
276             checksum += (buffer[i] & 0xFF);
277         position = 148;
278         patch(6, checksum);
279         entry.chksum = checksum;
280         os.write(buffer);
281     }
282
283     public void writeEndOfArchive(OutputStream os) throws IOException {
284         reset();
285         os.write(buffer);
286         os.write(buffer);
287         os.flush();
288     }
289
290     public void reset() {
291         position = 0;
292         Arrays.fill(buffer, (byte)0);
293     }
294
295     private byte[] clipNameField(int length) {
296         assert length <= BLOCK_SIZE - position;
297         final int p = position;
298         position += length;
299         int availableLength = 0;
300         for (int i = 0; i < length; i++) {
301             if (buffer[p + i] == 0x00)
302                 break;
303             ++availableLength;
304         }
305         byte[] nameb = new byte[availableLength];
306         System.arraycopy(buffer, 0, nameb, 0, availableLength);
307         return nameb;
308     }
309
310     private String clipAsString(int length) {
311         assert length <= BLOCK_SIZE - position;
312         final int p = position;
313         position += length;
314         int availableLength = 0;
315         for (int i = 0; i < length; i++) {
316             if (buffer[p + i] == 0x00)
317                 break;
318             ++availableLength;
319         }
320         StringBuilder s = new StringBuilder(length);
321         for (int i = 0; i < availableLength; i++) {
322             final byte b = buffer[p + i];
323             if (b < 0x20 || b > 0x7F)
324                 s.append(String.format("\\%o", (b & 0xFF)));
325             else
326                 s.append((char)b);
327         }
328         return s.toString();
329     }
330
331     private int clipAsInt(int length) {
332         final String s = clipAsNumberString(length);
333         return s.isEmpty() ? 0 : Integer.parseInt(s, 8);
334     }
335
336     private long clipAsLong(int length) {
337         final String s = clipAsNumberString(length);
338         return s.isEmpty() ? 0 : Long.parseLong(s, 8);
339     }
340
341     private String clipAsNumberString(int length) {
342         assert length <= BLOCK_SIZE - position;
343         final int p = position;
344         position += length;
345         int i = 0;
346         for (; i < length; i++) {
347             final byte b = buffer[p + i];
348             if (b == 0x00)
349                 break;
350             assert b == 0x20 || b >= 0x30 && b <= 0x39;
351             boolean x = b == 0x20 || b >= 0x30 && b <= 0x39;
352             if (!x)
353                 System.out.print("");
354         }
355         return (new String(buffer, p, i)).trim();
356     }
357
358     private int patch(int length, String value) {
359         final int p = position;
360         position += length;
361         byte[] data;
362         if (value == null || value.isEmpty())
363             data = new byte[0];
364         else
365             data = value.getBytes();
366         final int width = (data.length > length) ? length : data.length;
367         System.arraycopy(data, 0, buffer, p, width);
368         return data.length;
369     }
370
371     private int patch(int length, int value) {
372         final String s = padZero(Integer.toOctalString(value), length - 1);
373         return patch(length, s);
374     }
375
376     private int patch(int length, long value) {
377         final String s = padZero(Long.toOctalString(value), length - 1);
378         return patch(length, s);
379     }
380
381     private String padZero(String value, int length) {
382         final int valueLength = value.length();
383         if (valueLength >= length)
384             return value;
385         char[] buffer = new char[length];
386         int p = length - valueLength;
387         for (int i = 0; i < p; i++)
388             buffer[i] = '0';
389         for (int i = p; i < length; i++)
390             buffer[i] = value.charAt(i - p);
391         return String.valueOf(buffer);
392     }
393
394     private boolean isEmptyBlock() {
395         for (int i = 0; i < buffer.length; i++)
396             if (buffer[i] != 0x00)
397                 return false;
398         return true;
399     }
400
401     static long getSkipSize(long size) {
402         if (size == 0 || size == BLOCK_SIZE || size % BLOCK_SIZE == 0)
403             return 0;
404         else
405             return BLOCK_SIZE - ((size > BLOCK_SIZE) ? size % BLOCK_SIZE : size);
406
407     }
408
409 }