/*******************************************************************************
* Copyright (c) 2004, 2008 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM - Initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.utils.som;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.Vector;
import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.utils.coff.ReadMemoryAccess;
/**
* The <code>AR</code> class is used for parsing standard SOM archive (ar) files.
*
* @author vhirsl
*/
public class AR {
public static final String NL = System.getProperty("line.separator", "\n"); //$NON-NLS-1$ //$NON-NLS-2$
protected String filename;
protected RandomAccessFile file;
private byte[] ar_magic = new byte[8];
private LSTHeader lstHeader;
private ARHeader[] memberHeaders;
/**
* Archive and archive member header. Does not include 8-byte magic character.
*
* @author vhirsl
*/
public class ARHeader {
public static final int HEADER_SIZE = 60;
// fields
private byte[] ar_name = new byte[16]; // file member name - '/' terminated
private byte[] ar_date = new byte[12]; // file member date - decimal
private byte[] ar_uid = new byte[6]; // file member user id - decimal
private byte[] ar_gid = new byte[6]; // file member group id - decimal
private byte[] ar_mode = new byte[8]; // file member mode - octal
private byte[] ar_size = new byte[10]; // file member size - decimal
private byte[] ar_fmag = new byte[2]; // ARFMAG - string to end header
// derived information
String name;
public int somOffset;
public int somSize;
public ARHeader(long offset) throws IOException {
try {
getRandomAccessFile();
file.seek(offset);
file.read(ar_name);
for (int i = 0; i < 16; ++ i) {
if (ar_name[i] == '/') {
name = new String(ar_name, 0, i);
}
}
file.read(ar_date);
file.read(ar_uid);
file.read(ar_gid);
file.read(ar_mode);
file.read(ar_size);
file.read(ar_fmag);
} catch (IOException e) {
dispose();
CCorePlugin.log(e);
}
}
/** Get the name of the object file */
public String getObjectName() {
return name;
}
/** Get the size of the object file . */
public long getSize() {
return somSize;
}
public byte[] getObjectData() throws IOException {
byte[] temp = new byte[somSize];
file = getRandomAccessFile();
file.seek(somOffset);
file.read(temp);
dispose();
return temp;
}
public long getObjectDataOffset() {
return somOffset;
}
}
/**
* Library Symbol Table header
*
* @author vhirsl
*/
public class LSTHeader {
public static final int LST_HEADER_OFFSET = 68;
public static final int LST_HEADER_SIZE = 19 * 4;
// record fields
public short system_id;
public short a_magic;
public int version_id;
public int file_time_sec;
public int file_time_nano;
public int hash_loc;
public int hash_size;
public int module_count;
public int module_limit;
public int dir_loc;
public int export_loc;
public int export_count;
public int import_loc;
public int aux_loc;
public int aux_size;
public int string_loc;
public int string_size;
public int free_list;
public int file_end;
public int checksum;
public LSTHeader() throws IOException {
try {
getRandomAccessFile();
file.seek(LST_HEADER_OFFSET);
byte[] lstRecord = new byte[LST_HEADER_SIZE];
file.readFully(lstRecord);
ReadMemoryAccess memory = new ReadMemoryAccess(lstRecord, false);
system_id = memory.getShort();
a_magic = memory.getShort();
version_id = memory.getInt();
file_time_sec = memory.getInt();
file_time_nano = memory.getInt();
hash_loc = memory.getInt();
hash_size = memory.getInt();
module_count = memory.getInt();
module_limit = memory.getInt();
dir_loc = memory.getInt();
export_loc = memory.getInt();
export_count = memory.getInt();
import_loc = memory.getInt();
aux_loc = memory.getInt();
aux_size = memory.getInt();
string_loc = memory.getInt();
string_size = memory.getInt();
free_list = memory.getInt();
file_end = memory.getInt();
checksum = memory.getInt();
} catch (IOException e) {
dispose();
CCorePlugin.log(e);
}
}
}
/**
* 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 AR(String filename) throws IOException {
this.filename = filename;
file = new RandomAccessFile(filename, "r"); //$NON-NLS-1$
file.read(ar_magic);
if (!isARHeader(ar_magic)) {
file.close();
throw new IOException(CCorePlugin.getResourceString("Util.exception.invalidArchive")); //$NON-NLS-1$
}
// load a LST header
lstHeader = new LSTHeader();
}
public void dispose() {
try {
if (file != null) {
file.close();
file = null;
}
} catch (IOException e) {
}
}
@Override
protected void finalize() throws Throwable {
try {
dispose();
} finally {
super.finalize();
}
}
public static boolean isARHeader(byte[] ident) {
if (ident == null || ident.length < 8
|| ident[0] != '!'
|| ident[1] != '<'
|| ident[2] != 'a'
|| ident[3] != 'r'
|| ident[4] != 'c'
|| ident[5] != 'h'
|| ident[6] != '>'
|| ident[7] != '\n')
return false;
return true;
}
protected RandomAccessFile getRandomAccessFile () throws IOException {
if (file == null) {
file = new RandomAccessFile(filename, "r"); //$NON-NLS-1$
}
return file;
}
/**
* 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 memberHeaders;
}
/** Load the headers from the file (if required). */
private void loadHeaders() throws IOException {
if (memberHeaders != null)
return;
Vector<ARHeader> v = new Vector<ARHeader>();
try {
//
// Check for EOF condition
//
// get the SOM directory
long somDirOffset = lstHeader.dir_loc + LSTHeader.LST_HEADER_OFFSET;
// each SOM Directory entry has 2 32bit words: SOM offset from LST and size
int somDirSize = lstHeader.module_limit * 8;
getRandomAccessFile();
file.seek(somDirOffset);
byte[] somDirectory = new byte[somDirSize];
file.readFully(somDirectory);
ReadMemoryAccess memory = new ReadMemoryAccess(somDirectory, false);
for (int i = 0; i < lstHeader.module_limit; ++i) {
int somOffset = memory.getInt();
int somSize = memory.getInt();
ARHeader aHeader = new ARHeader(somOffset-ARHeader.HEADER_SIZE);
aHeader.somOffset = somOffset;
aHeader.somSize = somSize;
v.add(aHeader);
}
} catch (IOException e) {
}
memberHeaders = v.toArray(new ARHeader[v.size()]);
}
public String[] extractFiles(String outdir) throws IOException {
return extractFiles(outdir, null);
}
private String[] extractFiles(String outdir, String[] names) throws IOException {
Vector<String> names_used = new Vector<String>();
String object_name;
int count;
loadHeaders();
count = 0;
for (ARHeader memberHeader : memberHeaders) {
object_name = memberHeader.getObjectName();
if (names != null && !stringInStrings(object_name, names))
continue;
object_name = "" + count + "_" + object_name; //$NON-NLS-1$ //$NON-NLS-2$
count++;
byte[] data = memberHeader.getObjectData();
File output = new File(outdir, object_name);
names_used.add(object_name);
RandomAccessFile rfile = new RandomAccessFile(output, "rw"); //$NON-NLS-1$
rfile.write(data);
rfile.close();
}
return names_used.toArray(new String[0]);
}
private boolean stringInStrings(String str, String[] set) {
for (String element : set)
if (str.compareTo(element) == 0)
return true;
return false;
}
@Override
public String toString() {
StringBuffer buffer = new StringBuffer();
if (lstHeader != null) {
buffer.append("LST HEADER VALUES").append(NL); //$NON-NLS-1$
buffer.append("system_id = ").append(lstHeader.system_id).append(NL); //$NON-NLS-1$
buffer.append("a_magic = ").append(lstHeader.a_magic).append(NL); //$NON-NLS-1$
buffer.append("version_id = ").append(lstHeader.version_id).append(NL); //$NON-NLS-1$
buffer.append("module_count = ").append(lstHeader.module_count).append(NL); //$NON-NLS-1$
buffer.append("module_limit = ").append(lstHeader.module_limit).append(NL); //$NON-NLS-1$
buffer.append("dir_loc = ").append(lstHeader.dir_loc).append(NL); //$NON-NLS-1$
for (int i = 0; i < memberHeaders.length; ++i) {
buffer.append("MEMBER HEADER VALUES").append(NL); //$NON-NLS-1$
buffer.append("name = ").append(memberHeaders[i].getObjectName()).append(NL); //$NON-NLS-1$
buffer.append("somOffset = ").append(memberHeaders[i].somOffset).append(NL); //$NON-NLS-1$
buffer.append("somSize = ").append(memberHeaders[i].getSize()).append(NL); //$NON-NLS-1$
}
}
return buffer.toString();
}
public static void main(String[] args) {
try {
AR ar = new AR(args[0]);
ar.getHeaders();
ar.extractFiles(args[1]);
System.out.println(ar);
} catch (IOException e) {
e.printStackTrace();
}
}
}