blob: 35cb63c392f909cdc130d9a4c717343c87af943f [file] [log] [blame]
package org.eclipse.cdt.utils.coff;
/*
* (c) Copyright QNX Software Systems Ltd. 2002.
* All Rights Reserved.
*/
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.Vector;
/**
* The <code>AR</code> class is used for parsing standard ELF archive (ar) files.
*
* Each object within the archive is represented by an ARHeader class. Each of
* of these objects can then be turned into an PE object for performing PE
* class operations.
* @see ARHeader
*/
public class PEArchive {
protected String filename;
protected RandomAccessFile rfile;
protected long strtbl_pos = -1;
private ARHeader[] headers;
public void dispose() {
try {
if (rfile != null) {
rfile.close();
rfile = null;
}
} catch (IOException e) {
}
}
/**
* Do not leak fds.
*/
protected void finalize() throws Throwable {
try {
dispose();
} finally {
super.finalize();
}
}
/**
* The <code>ARHeader</code> class is used to store the per-object file
* archive headers. It can also create an PE object for inspecting
* the object file data.
*/
public class ARHeader {
private String object_name;
private String modification_time;
private String uid;
private String gid;
private String mode;
private long size;
private long elf_offset;
/**
* Remove the padding from the archive header strings.
*/
private String removeBlanks(String str) {
while (str.charAt(str.length() - 1) == ' ')
str = str.substring(0, str.length() - 1);
return str;
}
/**
* Look up the name stored in the archive's string table based
* on the offset given.
*
* Maintains <code>rfile</code> file location.
*
* @param offset
* Offset into the string table for first character of the name.
* @throws IOException
* <code>offset</code> not in string table bounds.
*/
private String nameFromStringTable(long offset) throws IOException {
StringBuffer name = new StringBuffer(0);
long pos = rfile.getFilePointer();
try {
if (strtbl_pos != -1) {
byte temp;
rfile.seek(strtbl_pos + offset);
while ((temp = rfile.readByte()) != '\n')
name.append((char) temp);
}
} finally {
rfile.seek(pos);
}
return name.toString();
}
/**
* Creates a new archive header object.
*
* Assumes that rfile is already at the correct location in the file.
*
* @throws IOException
* There was an error processing the header data from the file.
*/
public ARHeader() throws IOException {
byte[] object_name = new byte[16];
byte[] modification_time = new byte[12];
byte[] uid = new byte[6];
byte[] gid = new byte[6];
byte[] mode = new byte[8];
byte[] size = new byte[10];
byte[] trailer = new byte[2];
//
// Read in the archive header data. Fixed sizes.
//
rfile.read(object_name);
rfile.read(modification_time);
rfile.read(uid);
rfile.read(gid);
rfile.read(mode);
rfile.read(size);
rfile.read(trailer);
//
// Save this location so we can create the PE object later.
//
elf_offset = rfile.getFilePointer();
//
// Convert the raw bytes into strings and numbers.
//
this.object_name = removeBlanks(new String(object_name));
this.modification_time = new String(modification_time);
this.uid = new String(uid);
this.gid = new String(gid);
this.mode = new String(mode);
this.size = Long.parseLong(removeBlanks(new String(size)));
//
// If the name is of the format "/<number>", get name from the
// string table.
//
if (strtbl_pos != -1
&& this.object_name.length() > 1
&& this.object_name.charAt(0) == '/') {
try {
long offset = Long.parseLong(this.object_name.substring(1));
this.object_name = nameFromStringTable(offset);
} catch (java.lang.Exception e) {
}
}
//
// Strip the trailing / from the object name.
//
int len = this.object_name.length();
if (len > 2 && this.object_name.charAt(len - 1) == '/') {
this.object_name = this.object_name.substring(0, len - 1);
}
}
/** Get the name of the object file */
public String getObjectName() {
return object_name;
}
/** Get the size of the object file . */
public long getSize() {
return size;
}
/**
* Create an new PE object for the object file.
*
* @throws IOException
* Not a valid PE object file.
* @return A new PE object.
* @see PE#PE( String, long )
*/
public PE getPE() throws IOException {
return new PE(filename, elf_offset);
}
public PE getPE(boolean filter_on) throws IOException {
return new PE(filename, elf_offset, filter_on);
}
public byte[] getObjectData() throws IOException {
byte[] temp = new byte[(int) size];
rfile.seek(elf_offset);
rfile.read(temp);
return temp;
}
}
public static boolean isARHeader(byte[] ident) {
if (ident.length < 7
|| ident[0] != '!'
|| ident[1] != '<'
|| ident[2] != 'a'
|| ident[3] != 'r'
|| ident[4] != 'c'
|| ident[5] != 'h'
|| ident[6] != '>')
return false;
return true;
}
/**
* Creates a new <code>AR</code> object from the contents of
* the given file.
*
* @param filename The file to process.
* @throws IOException The file is not a valid archive.
*/
public PEArchive(String filename) throws IOException {
this.filename = filename;
rfile = new RandomAccessFile(filename, "r");
String hdr = rfile.readLine();
if (hdr == null || hdr.compareTo("!<arch>") != 0) {
rfile.close();
throw new IOException("Not a valid archive file.");
}
}
/** Load the headers from the file (if required). */
private void loadHeaders() throws IOException {
if (headers != null)
return;
Vector v = new Vector();
try {
//
// Check for EOF condition
//
while (rfile.getFilePointer() < rfile.length()) {
ARHeader header = new ARHeader();
String name = header.getObjectName();
long pos = rfile.getFilePointer();
//
// If the name starts with a / it is specical.
//
if (name.charAt(0) != '/')
v.add(header);
//
// If the name is "//" then this is the string table section.
//
if (name.compareTo("//") == 0)
strtbl_pos = pos;
//
// Compute the location of the next header in the archive.
//
pos += header.getSize();
if ((pos % 2) != 0)
pos++;
rfile.seek(pos);
}
} catch (IOException e) {
}
headers = (ARHeader[]) v.toArray(new ARHeader[0]);
}
/**
* Get an array of all the object file headers for this archive.
*
* @throws IOException
* Unable to process the archive file.
* @return An array of headers, one for each object within the archive.
* @see ARHeader
*/
public ARHeader[] getHeaders() throws IOException {
loadHeaders();
return headers;
}
private boolean stringInStrings(String str, String[] set) {
for (int i = 0; i < set.length; i++)
if (str.compareTo(set[i]) == 0)
return true;
return false;
}
public String[] extractFiles(String outdir, String[] names)
throws IOException {
Vector names_used = new Vector();
String object_name;
int count;
loadHeaders();
count = 0;
for (int i = 0; i < headers.length; i++) {
object_name = headers[i].getObjectName();
if (names != null && !stringInStrings(object_name, names))
continue;
object_name = "" + count + "_" + object_name;
count++;
byte[] data = headers[i].getObjectData();
File output = new File(outdir, object_name);
names_used.add(object_name);
RandomAccessFile rfile = new RandomAccessFile(output, "rw");
rfile.write(data);
rfile.close();
}
return (String[]) names_used.toArray(new String[0]);
}
public String[] extractFiles(String outdir) throws IOException {
return extractFiles(outdir, null);
}
}