125
126
final byte[] realized = new byte[expected.length];
126
127
final int read = read(realized);
127
128
if (read != expected.length) {
128
throw new IOException("failed to read entry header. Occured at byte: " + getCount());
129
throw new IOException("failed to read entry trailer. Occured at byte: " + getBytesRead());
130
131
for (int i = 0; i < expected.length; i++) {
131
132
if (expected[i] != realized[i]) {
132
throw new IOException("invalid entry header. not read the content? Occured at byte: " + getCount());
133
throw new IOException("invalid entry trailer. not read the content? Occured at byte: " + getBytesRead());
137
138
entryOffset = offset;
139
// SVR4/GNU adds a trailing "/" to names
140
// GNU ar stores multiple extended filenames in the data section of a file with the name "//", this record is referred to by future headers. A header references an extended filename by storing a "/" followed by a decimal offset to the start of the filename in the extended filename data section. The format of this "//" file itself is simply a list of the long filenames, each separated by one or more LF characters. Note that the decimal offsets are number of characters, not line or string number within the "//" file.
142
// GNU ar uses a '/' to mark the end of the filename; this allows for the use of spaces without the use of an extended filename.
140
144
// entry name is stored as ASCII string
141
145
String temp = ArchiveUtils.toAsciiString(name).trim();
142
if (temp.endsWith("/")) {
147
if (temp.equals("//")){ // GNU extended filenames entry
148
int bufflen = asInt(length); // Assume length will fit in an int
149
namebuffer = new byte[bufflen];
150
int read = read(namebuffer, 0, bufflen);
151
if (read != bufflen){
152
throw new IOException("Failed to read complete // record: expected="+bufflen+" read="+read);
154
currentEntry = new ArArchiveEntry(temp, bufflen);
155
return getNextArEntry();
156
} else if (temp.endsWith("/")) { // GNU terminator
143
157
temp = temp.substring(0, temp.length() - 1);
158
} else if (temp.matches("^/\\d+")) {// GNU long filename ref.
159
int offset = Integer.parseInt(temp.substring(1));// get the offset
160
temp = getExtendedName(offset); // convert to the long name
145
currentEntry = new ArArchiveEntry(temp, Long.parseLong(new String(length).trim()));
162
currentEntry = new ArArchiveEntry(temp, asLong(length), asInt(userid, true),
163
asInt(groupid, true), asInt(filemode, 8),
164
asLong(lastmodified));
146
165
return currentEntry;
169
* Get an extended name from the GNU extended name buffer.
171
* @param offset pointer to entry within the buffer
172
* @return the extended file name; without trailing "/" if present.
173
* @throws IOException if name not found or buffer not set up
175
private String getExtendedName(int offset) throws IOException{
176
if (namebuffer == null) {
177
throw new IOException("Cannot process GNU long filename as no // record was found");
179
for(int i=offset; i < namebuffer.length; i++){
180
if (namebuffer[i]=='\012'){
181
if (namebuffer[i-1]=='/') {
182
i--; // drop trailing /
184
return ArchiveUtils.toAsciiString(namebuffer, offset, i-offset);
187
throw new IOException("Failed to read entry: "+offset);
189
private long asLong(byte[] input) {
190
return Long.parseLong(new String(input).trim());
193
private int asInt(byte[] input) {
194
return asInt(input, 10, false);
197
private int asInt(byte[] input, boolean treatBlankAsZero) {
198
return asInt(input, 10, treatBlankAsZero);
201
private int asInt(byte[] input, int base) {
202
return asInt(input, base, false);
205
private int asInt(byte[] input, int base, boolean treatBlankAsZero) {
206
String string = new String(input).trim();
207
if (string.length() == 0 && treatBlankAsZero) {
210
return Integer.parseInt(string, base);