Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/sun/tools/jar/Manifest.java
38918 views
/*1* Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.tools.jar;2627import java.io.*;28import java.util.*;29import java.security.*;3031import sun.net.www.MessageHeader;32import java.util.Base64;3334/**35* This is OBSOLETE. DO NOT USE THIS. Use java.util.jar.Manifest36* instead. It has to stay here because some apps (namely HJ and HJV)37* call directly into it.38*39* @author David Brown40* @author Benjamin Renaud41*/4243public class Manifest {4445/* list of headers that all pertain to a particular46* file in the archive47*/48private Vector<MessageHeader> entries = new Vector<>();49private byte[] tmpbuf = new byte[512];50/* a hashtable of entries, for fast lookup */51private Hashtable<String, MessageHeader> tableEntries = new Hashtable<>();5253static final String[] hashes = {"SHA"};54static final byte[] EOL = {(byte)'\r', (byte)'\n'};5556static final boolean debug = false;57static final String VERSION = "1.0";58static final void debug(String s) {59if (debug)60System.out.println("man> " + s);61}6263public Manifest() {}6465public Manifest(byte[] bytes) throws IOException {66this(new ByteArrayInputStream(bytes), false);67}6869public Manifest(InputStream is) throws IOException {70this(is, true);71}7273/**74* Parse a manifest from a stream, optionally computing hashes75* for the files.76*/77public Manifest(InputStream is, boolean compute) throws IOException {78if (!is.markSupported()) {79is = new BufferedInputStream(is);80}81/* do not rely on available() here! */82while (true) {83is.mark(1);84if (is.read() == -1) { // EOF85break;86}87is.reset();88MessageHeader m = new MessageHeader(is);89if (compute) {90doHashes(m);91}92addEntry(m);93}94}9596/* recursively generate manifests from directory tree */97public Manifest(String[] files) throws IOException {98MessageHeader globals = new MessageHeader();99globals.add("Manifest-Version", VERSION);100String jdkVersion = System.getProperty("java.version");101globals.add("Created-By", "Manifest JDK "+jdkVersion);102addEntry(globals);103addFiles(null, files);104}105106public void addEntry(MessageHeader entry) {107entries.addElement(entry);108String name = entry.findValue("Name");109debug("addEntry for name: "+name);110if (name != null) {111tableEntries.put(name, entry);112}113}114115public MessageHeader getEntry(String name) {116return tableEntries.get(name);117}118119public MessageHeader entryAt(int i) {120return entries.elementAt(i);121}122123public Enumeration<MessageHeader> entries() {124return entries.elements();125}126127public void addFiles(File dir, String[] files) throws IOException {128if (files == null)129return;130for (int i = 0; i < files.length; i++) {131File file;132if (dir == null) {133file = new File(files[i]);134} else {135file = new File(dir, files[i]);136}137if (file.isDirectory()) {138addFiles(file, file.list());139} else {140addFile(file);141}142}143}144145/**146* File names are represented internally using "/";147* they are converted to the local format for anything else148*/149150private final String stdToLocal(String name) {151return name.replace('/', java.io.File.separatorChar);152}153154private final String localToStd(String name) {155name = name.replace(java.io.File.separatorChar, '/');156if (name.startsWith("./"))157name = name.substring(2);158else if (name.startsWith("/"))159name = name.substring(1);160return name;161}162163public void addFile(File f) throws IOException {164String stdName = localToStd(f.getPath());165if (tableEntries.get(stdName) == null) {166MessageHeader mh = new MessageHeader();167mh.add("Name", stdName);168addEntry(mh);169}170}171172public void doHashes(MessageHeader mh) throws IOException {173// If unnamed or is a directory return immediately174String name = mh.findValue("Name");175if (name == null || name.endsWith("/")) {176return;177}178179180/* compute hashes, write over any other "Hash-Algorithms" (?) */181for (int j = 0; j < hashes.length; ++j) {182InputStream is = new FileInputStream(stdToLocal(name));183try {184MessageDigest dig = MessageDigest.getInstance(hashes[j]);185186int len;187while ((len = is.read(tmpbuf, 0, tmpbuf.length)) != -1) {188dig.update(tmpbuf, 0, len);189}190mh.set(hashes[j] + "-Digest", Base64.getMimeEncoder().encodeToString(dig.digest()));191} catch (NoSuchAlgorithmException e) {192throw new JarException("Digest algorithm " + hashes[j] +193" not available.");194} finally {195is.close();196}197}198}199200/* Add a manifest file at current position in a stream201*/202public void stream(OutputStream os) throws IOException {203204PrintStream ps;205if (os instanceof PrintStream) {206ps = (PrintStream) os;207} else {208ps = new PrintStream(os);209}210211/* the first header in the file should be the global one.212* It should say "Manifest-Version: x.x"; if not add it213*/214MessageHeader globals = entries.elementAt(0);215216if (globals.findValue("Manifest-Version") == null) {217/* Assume this is a user-defined manifest. If it has a Name: <..>218* field, then it is not global, in which case we just add our own219* global Manifest-version: <version>220* If the first MessageHeader has no Name: <..>, we assume it221* is a global header and so prepend Manifest to it.222*/223String jdkVersion = System.getProperty("java.version");224225if (globals.findValue("Name") == null) {226globals.prepend("Manifest-Version", VERSION);227globals.add("Created-By", "Manifest JDK "+jdkVersion);228} else {229ps.print("Manifest-Version: "+VERSION+"\r\n"+230"Created-By: "+jdkVersion+"\r\n\r\n");231}232ps.flush();233}234235globals.print(ps);236237for (int i = 1; i < entries.size(); ++i) {238MessageHeader mh = entries.elementAt(i);239mh.print(ps);240}241}242243public static boolean isManifestName(String name) {244245// remove leading /246if (name.charAt(0) == '/') {247name = name.substring(1, name.length());248}249// case insensitive250name = name.toUpperCase();251252if (name.equals("META-INF/MANIFEST.MF")) {253return true;254}255return false;256}257}258259260