hello all, On Monday 06 March 2006 20:37, i wrote: > ... > this patch --not committed-- allows a JAR's Manifest file to be > generated with platform-independent line-endings...
the JAR specs explicitly state that Name headers must not exceed 70 bytes. the updated patch ensure this is the case. cheers; rsn
Index: Manifest.java =================================================================== RCS file: /cvsroot/classpath/classpath/java/util/jar/Manifest.java,v retrieving revision 1.10 diff -u -r1.10 Manifest.java --- Manifest.java 13 Sep 2005 22:19:15 -0000 1.10 +++ Manifest.java 6 Mar 2006 16:52:47 -0000 @@ -37,14 +37,12 @@ package java.util.jar; +import java.io.BufferedOutputStream; import java.io.BufferedReader; -import java.io.BufferedWriter; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.io.PrintWriter; import java.util.Hashtable; import java.util.Iterator; import java.util.Map; @@ -60,6 +58,9 @@ { // Fields + /** Platform-independent line-ending. */ + private static final byte[] CRLF = new byte[] { 0x0D, 0x0A }; + /** The main attributes of the manifest (jar file). */ private final Attributes mainAttr; @@ -156,12 +157,14 @@ } /** - * XXX + * Read and merge a <code>Mainfest</code> from the designated input stream. + * + * @param in the input stream to read from. + * @throws IOException if an I/O related exception occurs during the process. */ public void read(InputStream in) throws IOException { - BufferedReader br = - new BufferedReader(new InputStreamReader(in, "8859_1")); + BufferedReader br = new BufferedReader(new InputStreamReader(in, "UTF-8")); read_main_section(getMainAttributes(), br); read_individual_sections(getEntries(), br); } @@ -310,32 +313,35 @@ } /** - * XXX + * Writes the contents of this <code>Manifest</code> to the designated + * output stream. Line-endings are platform-independent and consist of the + * 2-codepoint sequence <code>0x0D</code> and <code>0x0A</code>. + * + * @param out the output stream to write this <code>Manifest</code> to. + * @throws IOException if an I/O related exception occurs during the process. */ public void write(OutputStream out) throws IOException { - PrintWriter pw = - new PrintWriter(new - BufferedWriter(new OutputStreamWriter(out, "8859_1"))); - write_main_section(getMainAttributes(), pw); - pw.println(); - write_individual_sections(getEntries(), pw); - if (pw.checkError()) - { - throw new JarException("Error while writing manifest"); - } + BufferedOutputStream bos = out instanceof BufferedOutputStream + ? (BufferedOutputStream) out + : new BufferedOutputStream(out, 4096); + write_main_section(getMainAttributes(), bos); + bos.write(CRLF); + write_individual_sections(getEntries(), bos); + bos.flush(); } // Private Static functions for writing the Manifest file to a PrintWriter - private static void write_main_section(Attributes attr, - PrintWriter pw) throws JarException + private static void write_main_section(Attributes attr, OutputStream out) + throws IOException { - write_version_info(attr, pw); - write_main_attributes(attr, pw); + write_version_info(attr, out); + write_main_attributes(attr, out); } - private static void write_version_info(Attributes attr, PrintWriter pw) + private static void write_version_info(Attributes attr, OutputStream out) + throws IOException { // First check if there is already a version attribute set String version = attr.getValue(Attributes.Name.MANIFEST_VERSION); @@ -343,40 +349,80 @@ { version = "1.0"; } - write_header(Attributes.Name.MANIFEST_VERSION.toString(), version, pw); + write_header(Attributes.Name.MANIFEST_VERSION.toString(), version, out); } - private static void write_header(String name, String value, PrintWriter pw) + /** + * The basic method for writing <code>Mainfest</code> attributes. This + * implementation respects the rule stated in the Jar Specification concerning + * the maximum allowed line length; i.e. + * + * <pre> + * No line may be longer than 72 bytes (not characters), in its UTF8-encoded + * form. If a value would make the initial line longer than this, it should + * be continued on extra lines (each starting with a single SPACE). + * </pre> + * and + * <pre> + * Because header names cannot be continued, the maximum length of a header + * name is 70 bytes (there must be a colon and a SPACE after the name). + * </pre> + * + * @param name the name of the attribute. + * @param value the value of the attribute. + * @param out the output stream to write the attribute's name/value pair to. + * @throws IOException if an I/O related exception occurs during the process. + */ + private static void write_header(String name, String value, OutputStream out) + throws IOException { - pw.print(name + ": "); - - int last = 68 - name.length(); - if (last > value.length()) - { - pw.println(value); + String target = name + ": "; + byte[] b = target.getBytes("UTF-8"); + if (b.length > 72) + throw new IOException("Attribute's name already longer than 70 bytes"); + + if (b.length == 72) + { + out.write(b); + out.write(CRLF); + target = " " + value; } else + target = target + value; + + int n; + while (true) { - pw.println(value.substring(0, last)); - } - while (last < value.length()) - { - pw.print(" "); - int end = (last + 69); - if (end > value.length()) - { - pw.println(value.substring(last)); - } - else - { - pw.println(value.substring(last, end)); - } - last = end; + b = target.getBytes("UTF-8"); + if (b.length < 73) + { + out.write(b); + break; + } + + // find an appropriate character position to break on + n = 72; + while (true) + { + b = target.substring(0, n).getBytes("UTF-8"); + if (b.length < 73) + break; + + n--; + if (n < 1) + throw new IOException("Header is unbreakable and longer than 72 bytes"); + } + + out.write(b); + out.write(CRLF); + target = " " + target.substring(n); } + + out.write(CRLF); } - private static void write_main_attributes(Attributes attr, PrintWriter pw) - throws JarException + private static void write_main_attributes(Attributes attr, OutputStream out) + throws IOException { Iterator it = attr.entrySet().iterator(); while (it.hasNext()) @@ -384,14 +430,12 @@ Map.Entry entry = (Map.Entry) it.next(); // Don't print the manifest version again if (!Attributes.Name.MANIFEST_VERSION.equals(entry.getKey())) - { - write_attribute_entry(entry, pw); - } + write_attribute_entry(entry, out); } } - private static void write_attribute_entry(Map.Entry entry, PrintWriter pw) - throws JarException + private static void write_attribute_entry(Map.Entry entry, OutputStream out) + throws IOException { String name = entry.getKey().toString(); String value = entry.getValue().toString(); @@ -406,31 +450,31 @@ JarException("Header cannot start with the four letters 'From'" + name); } - write_header(name, value, pw); + write_header(name, value, out); } - private static void write_individual_sections(Map entries, PrintWriter pw) - throws JarException + private static void write_individual_sections(Map entries, OutputStream out) + throws IOException { Iterator it = entries.entrySet().iterator(); while (it.hasNext()) { - Map.Entry entry = (Map.Entry) it.next(); - write_header("Name", entry.getKey().toString(), pw); - write_entry_attributes((Attributes) entry.getValue(), pw); - pw.println(); + Map.Entry entry = (Map.Entry) it.next(); + write_header("Name", entry.getKey().toString(), out); + write_entry_attributes((Attributes) entry.getValue(), out); + out.write(CRLF); } } - private static void write_entry_attributes(Attributes attr, PrintWriter pw) - throws JarException + private static void write_entry_attributes(Attributes attr, OutputStream out) + throws IOException { Iterator it = attr.entrySet().iterator(); while (it.hasNext()) { Map.Entry entry = (Map.Entry) it.next(); - write_attribute_entry(entry, pw); + write_attribute_entry(entry, out); } }
pgpcozwfUxswe.pgp
Description: PGP signature