/******************************************************************************* * Copyright (c) 2009 Zhao 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: * Zhao - initial API and implementation *******************************************************************************/ package org.eclipse.php.internal.core.phar; import java.io.*; import java.util.ArrayList; import java.util.List; import org.eclipse.core.runtime.CoreException; import org.eclipse.php.internal.core.tar.CBZip2OutputStreamForPhar; import org.eclipse.php.internal.core.tar.CRCable; import org.eclipse.php.internal.core.tar.GZIPOutputStreamForPhar; /** * the mean class to write a pure phar * * @author zhaozw * */ public class PharBufferedOutputStream implements IAchiveOutputStream { // private static final byte[] DEFAULT_STUB = // "<?php\r\n__HALT_COMPILER();\r\n".getBytes(); List<PharAchiveOutputEntry> entries = new ArrayList<PharAchiveOutputEntry>(); PharAchiveOutputEntry currentEntry; boolean currentStart; SignatureBufferedOutputStream os; OutputStream currentOutputStream; OutputStream manifestOutputStream; OutputStream fileDescriptionOutputStream; NumberedBufferedOutputStream fileContentStream; int currentIndex = 0; private File fileContent; private File manifest; private File fileDescription; PharPackage pharPackage; int fileNumber = 0; public PharBufferedOutputStream(SignatureBufferedOutputStream fileContentStream2, PharPackage pharPackage) throws IOException { this.os = fileContentStream2; manifest = File.createTempFile("temp1", ".tmp"); //$NON-NLS-1$ //$NON-NLS-2$ manifestOutputStream = new BufferedOutputStream(new FileOutputStream(manifest)); fileDescription = File.createTempFile("temp2", ".tmp"); //$NON-NLS-1$ //$NON-NLS-2$ fileDescriptionOutputStream = new BufferedOutputStream(new FileOutputStream(fileDescription)); fileContent = File.createTempFile("temp3", ".tmp"); //$NON-NLS-1$ //$NON-NLS-2$ fileContentStream = new NumberedBufferedOutputStream(new FileOutputStream(fileContent)); this.pharPackage = pharPackage; } public void writeStub(IStub stub) throws IOException, CoreException { stub.write(os); } public void close() throws IOException { os.close(); } private void mergeFiles() throws IOException { fileDescriptionOutputStream.close(); writeManifest(); manifestOutputStream.close(); fileContentStream.getInnerOutputStream().close(); writeTempFileToStream(manifest); writeTempFileToStream(fileDescription); writeTempFileToStream(fileContent); manifest.delete(); fileDescription.delete(); fileContent.delete(); } private void writeManifest() throws IOException { int aliasLength = 0; if (pharPackage.getAlias() != null) { aliasLength = pharPackage.getAlias().length(); } // fileNumber4 + version2 + bitmap4 + aliaslength4 + metadatalength4 = // 18 int manifestLength = (int) (18 + aliasLength + fileDescription.length()); writeInt(manifestOutputStream, manifestLength); writeInt(manifestOutputStream, fileNumber); // write Version manifestOutputStream.write(PharUtil.getStubVersionBytes(pharPackage.getStubVersion())); // manifestOutputStream.write(new byte[] { 17, 0 }); // writeString(manifestOutputStream,pharData.getStubVersion()); // write bitmap manifestOutputStream.write(PharUtil.getGlobalBitmap(pharPackage)); writeInt(manifestOutputStream, aliasLength); if (aliasLength != 0) { writeString(manifestOutputStream, pharPackage.getAlias()); } writeInt(manifestOutputStream, 0); } private void writeTempFileToStream(File file) throws IOException { InputStream contentStream = new BufferedInputStream(new FileInputStream(file)); try { int n; byte[] readBuffer = new byte[4096]; while ((n = contentStream.read(readBuffer)) > 0) { os.write(readBuffer, 0, n); } } finally { if (contentStream != null) { contentStream.close(); } } } public void putNextEntry(IAchiveOutputEntry output) throws IOException { assert output instanceof PharAchiveOutputEntry; fileNumber++; currentEntry = (PharAchiveOutputEntry) output; entries.add(currentEntry); currentStart = true; } private void setEntryInfo() throws IOException { // if(currentEntry != null){ // fileContentStream.flush(); currentEntry.setCompressedSize(fileContentStream.getCurrent() - currentIndex); currentEntry.setCrc(getCrc(currentOutputStream)); currentIndex = fileContentStream.getCurrent(); // } } private long getCrc(OutputStream currentOutputStream2) { return ((CRCable) currentOutputStream2).getCrc(); } public void write(byte[] b, int off, int len) throws IOException { // if currentStart is true,it should initialize the currentOutputStream // depends on the method of currentEntry if (currentStart) { currentStart = false; if (currentEntry.getMethod() == PharConstants.NONE_COMPRESSED) { currentOutputStream = fileContentStream; } else if (currentEntry.getMethod() == PharConstants.BZ2_COMPRESSED) { currentOutputStream = new CBZip2OutputStreamForPhar(fileContentStream); } else if (currentEntry.getMethod() == PharConstants.GZ_COMPRESSED) { currentOutputStream = new GZIPOutputStreamForPhar(fileContentStream); } currentOutputStream = new CRCableOutputStream(currentOutputStream); } currentOutputStream.write(b, off, len); } public void closeEntry() throws IOException { // the close method is called to force to flush the cache when the // phar's content is compressed currentOutputStream.close(); setEntryInfo(); writeCurrentEntry(); } // write the entry info to fileDescriptionOutputStream private void writeCurrentEntry() throws IOException { writeInt(fileDescriptionOutputStream, currentEntry.getName().length()); writeString(fileDescriptionOutputStream, currentEntry.getName()); writeInt(fileDescriptionOutputStream, (int) currentEntry.getSize()); writeInt(fileDescriptionOutputStream, (int) currentEntry.getTime()); writeInt(fileDescriptionOutputStream, (int) currentEntry.getCompressedSize()); writeInt(fileDescriptionOutputStream, (int) currentEntry.getCrc()); fileDescriptionOutputStream.write(PharUtil.getBitmapBytes(currentEntry)); // writeInt(fileDescriptionOutputStream, (int) // currentEntry.getMethod()); writeInt(fileDescriptionOutputStream, 0); } private void writeString(OutputStream os, String name) throws IOException { os.write(name.getBytes()); } /* * Writes integer in Intel byte order to a byte array, starting at a given * offset. */ private void writeInt(OutputStream os, int i) throws IOException { writeShort(os, i & 0xffff); writeShort(os, (i >> 16) & 0xffff); } /* * Writes short integer in Intel byte order to a byte array, starting at a * given offset */ private void writeShort(OutputStream os, int s) throws IOException { os.write((byte) (s & 0xff)); os.write((byte) ((s >> 8) & 0xff)); } public void writeSignature() throws IOException { mergeFiles(); os.flush(); byte[] signature = os.getSignature(); if (signature != null) { os.write(PharUtil.getWholeSignature(signature, pharPackage)); // os // .write(Digest.DIGEST_MAP.get(pharData.getSignature()) // .getBitMap()); // os.write("GBMB".getBytes()); } } }