hello all,

this patch --not committed-- allows a JAR's Manifest file to be 
generated with platform-independent line-endings.  since there are no 
Mauve tests --the only test i used was the jarsigner tool i'm 
developing-- i would appreciate it if somebody can go over the patch 
and check if it's correct.

2006-03-06  Raif S. Naffah  <[EMAIL PROTECTED]>

        * java/util/jar/Manifest.java: Removed unused imports.
        (CRLF): New constant.
        (read): Added method documentation.
        Use UTF-8 encoding instead of 8859_1.
        (write): Added method documentation.
        Use BufferedOutputStream (w/ 4K buffer) instead of PrintWriter.
        (write_main_section): Replace PrintWriter arg with OutputStream.
        Replace JarException with IOException.
        (write_version_info): Likewise.
        (write_main_attributes): Likewise.
        (write_attribute_entry): Likewise.
        (write_individual_sections): Likewise.
        (write_entry_attributes): Likewise.
        (write_header): Replace PrintWriter arg with OutputStream.
        Re-implemented.


TIA + 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 09:27:36 -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,75 @@
       {
 	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>
+   *
+   * @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 72 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 +425,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 +445,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);
       }
   }

Attachment: pgpR9AyZ3CJL1.pgp
Description: PGP signature

Reply via email to