OSDN Git Service

Add new source files
[armadillo/armadillo1.git] / src / jp / sfjp / armadillo / archive / tar / DumpTarHeader.java
1 package jp.sfjp.armadillo.archive.tar;
2
3 import java.io.*;
4 import java.nio.*;
5 import java.nio.channels.*;
6 import java.util.*;
7 import java.util.zip.*;
8 import jp.sfjp.armadillo.archive.*;
9 import jp.sfjp.armadillo.time.*;
10
11 /**
12  * Dump TAR archive header.
13  */
14 public final class DumpTarHeader extends DumpArchiveHeader {
15
16     private static final int BLOCK_SIZE = 512;
17     private static final String MAGIC_USTAR = "ustar";
18     private static final TimeT TIME_T = new TimeT();
19
20     private static final String fmt1 = "  * %s = %s%n";
21     private static final String fmt2 = "  %1$-16s = [0x%2$08X] [0o%2$012o] ( %2$d )%n";
22     private static final String fmt3 = "  %-16s = \"%s\"%n";
23
24     private final ByteBuffer buffer;
25
26     public DumpTarHeader() {
27         this.buffer = ByteBuffer.allocate(BLOCK_SIZE);
28     }
29
30     @Override
31     public void dump(InputStream is, PrintWriter out) throws IOException {
32         int p = 0;
33         while (true) {
34             printOffset(out, p);
35             final int readLength = draw(is, buffer);
36             if (readLength == 0)
37                 break;
38             p += readLength;
39             if (readLength != BLOCK_SIZE)
40                 warn(out, "bad header: size=%d", readLength);
41             if (isEmptyBlock()) {
42                 out.println("  ( EMPTY BLOCK )");
43                 continue;
44             }
45             try {
46                 if (isUstar())
47                     p += readUstar(is, out);
48                 else
49                     p += readTar(is, out);
50             }
51             catch (Exception ex) {
52                 throw new IOException(ex);
53             }
54         }
55         printEnd(out, "TAR", p);
56     }
57
58     private boolean isUstar() {
59         final String s = new String(buffer.array(), 257, 5);
60         return s.equals(MAGIC_USTAR);
61     }
62
63     private static int draw(InputStream is, ByteBuffer buffer) throws IOException {
64         buffer.clear();
65         buffer.limit(BLOCK_SIZE);
66         Channels.newChannel(is).read(buffer);
67         int length = buffer.position();
68         buffer.rewind();
69         return length;
70     }
71
72     private int readTar(InputStream is, PrintWriter out) throws IOException {
73         int readSize = 0;
74         // Header Block (TAR Format)
75         final String name; // name of file (100bytes)
76         final int mode; // file mode (8bytes)
77         final int uid; // owner user ID (8bytes)
78         final int gid; // owner group ID (8bytes)
79         final long size; // length of file in bytes (12bytes)
80         final long mtime; // modify time of file (12bytes)
81         final int chksum; // checksum for header (8bytes)
82         final int link; // indicator for links (1byte)
83         name = clip(100);
84         mode = clipAsInt(8);
85         uid = clipAsInt(8);
86         gid = clipAsInt(8);
87         size = clipAsLong(12);
88         mtime = clipAsLong(12);
89         chksum = clipAsInt(8);
90         link = clipAsInt(1);
91         clip(100);
92         printHeaderName(out, "TAR (old) format");
93         out.printf(fmt1, "name", name);
94         p(out, "mode", mode, 8);
95         p(out, "uid", uid, 8);
96         p(out, "gid", gid, 8);
97         p(out, "size", size, 12);
98         p(out, "mtime", mtime, 12);
99         p(out, "chksum", chksum, 8);
100         p(out, "link", link, 1);
101         if (size > 0)
102             readSize += skipBlock(is, size);
103         return readSize;
104     }
105
106     private int readUstar(InputStream is, PrintWriter out) throws IOException {
107         int readSize = 0;
108         // Header Block (USTAR Format)
109         final String name; // name of file (100bytes)
110         final int mode; // file mode (8bytes)
111         final int uid; // owner user ID (8bytes)
112         final int gid; // owner group ID (8bytes)
113         final long size; // length of file in bytes (12bytes)
114         final long mtime; // modify time of file (12bytes)
115         final int chksum; // checksum for header (8bytes)
116         final char typeflag; // type of file (1byte)
117         final String linkname; // name of linked file (100bytes)
118         final String magic; // USTAR indicator (6bytes)
119         final int version; // USTAR version (2bytes)
120         final String uname; // owner user name (32bytes)
121         final String gname; // owner group name (32bytes)
122         final int devmajor; // device major number (8bytes)
123         final int devminor; // device minor number (8bytes)
124         final String prefix; // prefix for file name (155bytes)
125         name = clip(100);
126         mode = clipAsInt(8);
127         uid = clipAsInt(8);
128         gid = clipAsInt(8);
129         size = clipAsLong(12);
130         mtime = clipAsLong(12);
131         chksum = clipAsInt(8);
132         typeflag = clipAsChar();
133         linkname = clip(100);
134         magic = clip(6);
135         version = clipAsInt(2);
136         uname = clip(32);
137         gname = clip(32);
138         devmajor = clipAsInt(8);
139         devminor = clipAsInt(8);
140         prefix = clip(155);
141         printHeaderName(out, "USTAR format");
142         out.printf(fmt1, "name", name);
143         out.printf(fmt1, "mtime as date", toDate(mtime));
144         p(out, "mode", mode, 8);
145         p(out, "uid", uid, 8);
146         p(out, "gid", gid, 8);
147         p(out, "size", size, 12);
148         p(out, "mtime", mtime, 12);
149         p(out, "chksum", chksum, 8);
150         out.printf(fmt3, "typeflag", typeflag);
151         out.printf(fmt3, "linkname", linkname);
152         out.printf(fmt3, "magic", magic);
153         out.printf(fmt2, "version", version);
154         out.printf(fmt3, "uname", uname);
155         out.printf(fmt3, "gname", gname);
156         out.printf(fmt2, "devmajor", devmajor);
157         out.printf(fmt2, "devminor", devminor);
158         out.printf(fmt3, "prefix", prefix);
159         if (typeflag == 'L') {
160             // LongLink
161             readSize += draw(is, buffer);
162             final String s = clip(512);
163             out.printf(fmt1, "LongLink", s);
164         }
165         else if (size > 0)
166             readSize += skipBlock(is, size);
167         return readSize;
168     }
169
170     private long skipBlock(InputStream is, long size) throws IOException {
171         long skippedSize = 0;
172         long skipCount = size;
173         while (skipCount > 0) {
174             final long skipped = is.skip(BLOCK_SIZE);
175             skipCount -= skipped;
176             if (skipped != BLOCK_SIZE && skipCount > 0)
177                 throw new IllegalStateException("bad skip size: " + skipped);
178             skippedSize += skipped;
179         }
180         return skippedSize;
181     }
182
183     private String clip(int length) {
184         final int p = buffer.position();
185         int availableLength = 0;
186         for (int i = 0; i < length; i++) {
187             if (buffer.get() == 0x00)
188                 break;
189             ++availableLength;
190         }
191         buffer.rewind();
192         buffer.position(p);
193         StringBuilder sb = new StringBuilder();
194         for (int i = 0; i < availableLength; i++) {
195             final int x = buffer.get() & 0xFF;
196             if (x < 0x20 || x > 0x7F)
197                 sb.append(String.format("\\%o", x));
198             else
199                 sb.append((char)x);
200         }
201         buffer.position(p + length);
202         return sb.toString();
203     }
204
205     private char clipAsChar() {
206         final String s = clip(1);
207         assert s.length() == 1 && s.matches("^[A-Za-z0-9]$");
208         return s.charAt(0);
209     }
210
211     private int clipAsInt(int length) {
212         final String s = clipAsNumberString(length);
213         return s.isEmpty() ? 0 : Integer.parseInt(s, 8);
214     }
215
216     private long clipAsLong(int length) {
217         final String s = clipAsNumberString(length);
218         return s.isEmpty() ? 0 : Long.parseLong(s, 8);
219     }
220
221     private String clipAsNumberString(int length) {
222         byte[] bytes = new byte[length];
223         buffer.get(bytes);
224         int i = 0;
225         for (; i < length; i++) {
226             byte b = bytes[i];
227             if (b == 0x00)
228                 break;
229             assert b == 0x20 || b >= 0x30 && b <= 0x39;
230         }
231         return (new String(bytes, 0, i)).trim();
232     }
233
234     private boolean isEmptyBlock() {
235         byte[] bytes = buffer.array();
236         for (int i = 0; i < bytes.length; i++)
237             if (bytes[i] != 0x00)
238                 return false;
239         return true;
240     }
241
242     static long getSkipSize(long size) {
243         if (size == 0 || size == BLOCK_SIZE || size % BLOCK_SIZE == 0)
244             return 0;
245         else
246             return BLOCK_SIZE - ((size > BLOCK_SIZE) ? size % BLOCK_SIZE : size);
247
248     }
249
250     static InputStream getInputStream(File file) throws IOException {
251         ArchiveType type = ArchiveType.of(file.getName());
252         switch (type) {
253             case TAR:
254                 return new FileInputStream(file);
255             case TARGZ:
256                 return new GZIPInputStream(new FileInputStream(file));
257             default:
258                 throw new UnsupportedOperationException("not TAR ? : " + type);
259         }
260     }
261
262     static Date toDate(long timet) {
263         return new Date(TIME_T.toMilliseconds(timet));
264     }
265
266     static <T> void p(PrintWriter out, String name, T value, int width) {
267         final int wh;
268         final int wo;
269         switch (width) {
270             case 1:
271                 wh = 2;
272                 wo = 2;
273                 break;
274             case 8:
275                 wh = 6;
276                 wo = 8;
277                 break;
278             case 12:
279                 wh = 6;
280                 wo = 8;
281                 break;
282             default:
283                 wh = 8;
284                 wo = width;
285         }
286         final String fmt = "  %1$-16s = [0x%2$0" + wh + "X] [0o%2$0" + wo + "o] ( %2$d )%n";
287         out.printf(fmt, name, value);
288     }
289
290 }