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
