]> WPIA git - gigi.git/blob - lib/jtar/org/kamranzafar/jtar/TarInputStream.java
Adding jtar
[gigi.git] / lib / jtar / org / kamranzafar / jtar / TarInputStream.java
1 /**
2  * Copyright 2012 Kamran Zafar 
3  * 
4  * Licensed under the Apache License, Version 2.0 (the "License"); 
5  * you may not use this file except in compliance with the License. 
6  * You may obtain a copy of the License at 
7  * 
8  *      http://www.apache.org/licenses/LICENSE-2.0 
9  * 
10  * Unless required by applicable law or agreed to in writing, software 
11  * distributed under the License is distributed on an "AS IS" BASIS, 
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
13  * See the License for the specific language governing permissions and 
14  * limitations under the License. 
15  * 
16  */
17
18 package org.kamranzafar.jtar;
19
20 import java.io.FilterInputStream;
21 import java.io.IOException;
22 import java.io.InputStream;
23
24 /**
25  * @author Kamran Zafar
26  * 
27  */
28 public class TarInputStream extends FilterInputStream {
29
30         private static final int SKIP_BUFFER_SIZE = 2048;
31         private TarEntry currentEntry;
32         private long currentFileSize;
33         private long bytesRead;
34         private boolean defaultSkip = false;
35
36         public TarInputStream(InputStream in) {
37                 super(in);
38                 currentFileSize = 0;
39                 bytesRead = 0;
40         }
41
42         @Override
43         public boolean markSupported() {
44                 return false;
45         }
46
47         /**
48          * Not supported
49          * 
50          */
51         @Override
52         public synchronized void mark(int readlimit) {
53         }
54
55         /**
56          * Not supported
57          * 
58          */
59         @Override
60         public synchronized void reset() throws IOException {
61                 throw new IOException("mark/reset not supported");
62         }
63
64         /**
65          * Read a byte
66          * 
67          * @see java.io.FilterInputStream#read()
68          */
69         @Override
70         public int read() throws IOException {
71                 byte[] buf = new byte[1];
72
73                 int res = this.read(buf, 0, 1);
74
75                 if (res != -1) {
76                         return 0xFF & buf[0];
77                 }
78
79                 return res;
80         }
81
82         /**
83          * Checks if the bytes being read exceed the entry size and adjusts the byte
84          * array length. Updates the byte counters
85          * 
86          * 
87          * @see java.io.FilterInputStream#read(byte[], int, int)
88          */
89         @Override
90         public int read(byte[] b, int off, int len) throws IOException {
91                 if (currentEntry != null) {
92                         if (currentFileSize == currentEntry.getSize()) {
93                                 return -1;
94                         } else if ((currentEntry.getSize() - currentFileSize) < len) {
95                                 len = (int) (currentEntry.getSize() - currentFileSize);
96                         }
97                 }
98
99                 int br = super.read(b, off, len);
100
101                 if (br != -1) {
102                         if (currentEntry != null) {
103                                 currentFileSize += br;
104                         }
105
106                         bytesRead += br;
107                 }
108
109                 return br;
110         }
111
112         /**
113          * Returns the next entry in the tar file
114          * 
115          * @return TarEntry
116          * @throws IOException
117          */
118         public TarEntry getNextEntry() throws IOException {
119                 closeCurrentEntry();
120
121                 byte[] header = new byte[TarConstants.HEADER_BLOCK];
122                 byte[] theader = new byte[TarConstants.HEADER_BLOCK];
123                 int tr = 0;
124
125                 // Read full header
126                 while (tr < TarConstants.HEADER_BLOCK) {
127                         int res = read(theader, 0, TarConstants.HEADER_BLOCK - tr);
128
129                         if (res < 0) {
130                                 break;
131                         }
132
133                         System.arraycopy(theader, 0, header, tr, res);
134                         tr += res;
135                 }
136
137                 // Check if record is null
138                 boolean eof = true;
139                 for (byte b : header) {
140                         if (b != 0) {
141                                 eof = false;
142                                 break;
143                         }
144                 }
145
146                 if (!eof) {
147                         currentEntry = new TarEntry(header);
148                 }
149
150                 return currentEntry;
151         }
152
153         /**
154          * Returns the current offset (in bytes) from the beginning of the stream. 
155          * This can be used to find out at which point in a tar file an entry's content begins, for instance. 
156          */
157         public long getCurrentOffset() {
158                 return bytesRead;
159         }
160         
161         /**
162          * Closes the current tar entry
163          * 
164          * @throws IOException
165          */
166         protected void closeCurrentEntry() throws IOException {
167                 if (currentEntry != null) {
168                         if (currentEntry.getSize() > currentFileSize) {
169                                 // Not fully read, skip rest of the bytes
170                                 long bs = 0;
171                                 while (bs < currentEntry.getSize() - currentFileSize) {
172                                         long res = skip(currentEntry.getSize() - currentFileSize - bs);
173
174                                         if (res == 0 && currentEntry.getSize() - currentFileSize > 0) {
175                                                 // I suspect file corruption
176                                                 throw new IOException("Possible tar file corruption");
177                                         }
178
179                                         bs += res;
180                                 }
181                         }
182
183                         currentEntry = null;
184                         currentFileSize = 0L;
185                         skipPad();
186                 }
187         }
188
189         /**
190          * Skips the pad at the end of each tar entry file content
191          * 
192          * @throws IOException
193          */
194         protected void skipPad() throws IOException {
195                 if (bytesRead > 0) {
196                         int extra = (int) (bytesRead % TarConstants.DATA_BLOCK);
197
198                         if (extra > 0) {
199                                 long bs = 0;
200                                 while (bs < TarConstants.DATA_BLOCK - extra) {
201                                         long res = skip(TarConstants.DATA_BLOCK - extra - bs);
202                                         bs += res;
203                                 }
204                         }
205                 }
206         }
207
208         /**
209          * Skips 'n' bytes on the InputStream<br>
210          * Overrides default implementation of skip
211          * 
212          */
213         @Override
214         public long skip(long n) throws IOException {
215                 if (defaultSkip) {
216                         // use skip method of parent stream
217                         // may not work if skip not implemented by parent
218                         long bs = super.skip(n);
219                         bytesRead += bs;
220
221                         return bs;
222                 }
223
224                 if (n <= 0) {
225                         return 0;
226                 }
227
228                 long left = n;
229                 byte[] sBuff = new byte[SKIP_BUFFER_SIZE];
230
231                 while (left > 0) {
232                         int res = read(sBuff, 0, (int) (left < SKIP_BUFFER_SIZE ? left : SKIP_BUFFER_SIZE));
233                         if (res < 0) {
234                                 break;
235                         }
236                         left -= res;
237                 }
238
239                 return n - left;
240         }
241
242         public boolean isDefaultSkip() {
243                 return defaultSkip;
244         }
245
246         public void setDefaultSkip(boolean defaultSkip) {
247                 this.defaultSkip = defaultSkip;
248         }
249 }