blob: d18b23fe90b61bf28bb36cf08a84cb665d3aa5da [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.commons.compress.archivers.ar;
import java.io.IOException;
import java.io.OutputStream;
import org.apache.commons.compress.archivers.ArchiveEntry;
import org.apache.commons.compress.archivers.ArchiveOutputStream;
/**
* Implements the "ar" archive format as an output stream.
*
* @NotThreadSafe
*/
public class ArArchiveOutputStream extends ArchiveOutputStream {
private final OutputStream out;
private long archiveOffset = 0;
private long entryOffset = 0;
private ArArchiveEntry prevEntry;
private boolean haveUnclosedEntry = true;
public ArArchiveOutputStream( final OutputStream pOut ) {
this.out = pOut;
}
private long writeArchiveHeader() throws IOException {
byte [] header = ArArchiveEntry.HEADER.getBytes();
out.write(header);
return header.length;
}
public void closeArchiveEntry() throws IOException {
if (prevEntry != null && haveUnclosedEntry && (entryOffset % 2) != 0) {
out.write('\n'); // Pad byte
archiveOffset++;
}
haveUnclosedEntry = false;
}
public void putArchiveEntry( final ArchiveEntry pEntry ) throws IOException {
ArArchiveEntry pArEntry = (ArArchiveEntry)pEntry;
if (prevEntry == null) {
archiveOffset += writeArchiveHeader();
} else {
if (prevEntry.getLength() != entryOffset) {
throw new IOException("length does not match entry (" + prevEntry.getLength() + " != " + entryOffset);
}
closeArchiveEntry();
}
prevEntry = pArEntry;
archiveOffset += writeEntryHeader(pArEntry);
entryOffset = 0;
haveUnclosedEntry = true;
}
private long fill( final long pOffset, final long pNewOffset, final char pFill ) throws IOException {
final long diff = pNewOffset - pOffset;
if (diff > 0) {
for (int i = 0; i < diff; i++) {
write(pFill);
}
}
return pNewOffset;
}
private long write( final String data ) throws IOException {
final byte[] bytes = data.getBytes("ascii");
write(bytes);
return bytes.length;
}
private long writeEntryHeader( final ArArchiveEntry pEntry ) throws IOException {
long offset = 0;
final String n = pEntry.getName();
if (n.length() > 16) {
throw new IOException("filename too long");
}
offset += write(n);
offset = fill(offset, 16, ' ');
final String m = "" + (pEntry.getLastModified() / 1000);
if (m.length() > 12) {
throw new IOException("modified too long");
}
offset += write(m);
offset = fill(offset, 28, ' ');
final String u = "" + pEntry.getUserId();
if (u.length() > 6) {
throw new IOException("userid too long");
}
offset += write(u);
offset = fill(offset, 34, ' ');
final String g = "" + pEntry.getGroupId();
if (g.length() > 6) {
throw new IOException("groupid too long");
}
offset += write(g);
offset = fill(offset, 40, ' ');
final String fm = "" + Integer.toString(pEntry.getMode(), 8);
if (fm.length() > 8) {
throw new IOException("filemode too long");
}
offset += write(fm);
offset = fill(offset, 48, ' ');
final String s = "" + pEntry.getLength();
if (s.length() > 10) {
throw new IOException("size too long");
}
offset += write(s);
offset = fill(offset, 58, ' ');
offset += write(ArArchiveEntry.TRAILER);
return offset;
}
public void write(int b) throws IOException {
out.write(b);
entryOffset++;
}
public void write(byte[] b, int off, int len) throws IOException {
out.write(b, off, len);
entryOffset += len;
}
public void write(byte[] b) throws IOException {
out.write(b);
entryOffset += b.length;
}
public void close() throws IOException {
closeArchiveEntry();
out.close();
prevEntry = null;
}
}