diff --git a/src/main/org/apache/tools/ant/taskdefs/Jar.java b/src/main/org/apache/tools/ant/taskdefs/Jar.java index 87e17083f..4a2fb740c 100644 --- a/src/main/org/apache/tools/ant/taskdefs/Jar.java +++ b/src/main/org/apache/tools/ant/taskdefs/Jar.java @@ -429,7 +429,7 @@ public class Jar extends Zip { JAR_MARKER); // time to write the manifest ByteArrayOutputStream baos = new ByteArrayOutputStream(); - OutputStreamWriter osw = new OutputStreamWriter(baos, "UTF-8"); + OutputStreamWriter osw = new OutputStreamWriter(baos, Manifest.JAR_ENCODING); PrintWriter writer = new PrintWriter(osw); manifest.write(writer); writer.flush(); diff --git a/src/main/org/apache/tools/ant/taskdefs/Manifest.java b/src/main/org/apache/tools/ant/taskdefs/Manifest.java index 3ff83b090..3fb4deb2e 100644 --- a/src/main/org/apache/tools/ant/taskdefs/Manifest.java +++ b/src/main/org/apache/tools/ant/taskdefs/Manifest.java @@ -83,11 +83,32 @@ public class Manifest { public static final String ERROR_FROM_FORBIDDEN = "Manifest attributes should not start " + "with \"" + ATTRIBUTE_FROM + "\" in \""; + /** Encoding to be used for JAR files. */ + public static final String JAR_ENCODING = "UTF-8"; + /** * An attribute for the manifest. * Those attributes that are not nested into a section will be added to the "Main" section. */ public static class Attribute { + + /** + * Maximum length of the name to have the value starting on the same + * line as the name. This to stay under 72 bytes total line length + * (including CRLF). + */ + private static final int MAX_NAME_VALUE_LENGTH = 68; + + /** + * Maximum length of the name according to the jar specification. + * In this case the first line will have 74 bytes total line length + * (including CRLF). This conflicts with the 72 bytes total line length + * max, but is the only possible conclusion from the manifest specification, if + * names with 70 bytes length are allowed, have to be on the first line, and + * have to be followed by ": ". + */ + private static final int MAX_NAME_LENGTH = 70; + /** The attribute's name */ private String name = null; @@ -302,12 +323,31 @@ public class Manifest { */ private void writeValue(PrintWriter writer, String value) throws IOException { - String line = name + ": " + value; - while (line.getBytes().length > MAX_LINE_LENGTH) { + String line = null; + int nameLength = name.getBytes(JAR_ENCODING).length; + if (nameLength > MAX_NAME_VALUE_LENGTH) + { + if (nameLength > MAX_NAME_LENGTH) + { + throw new IOException("Unable to write manifest line " + + name + ": " + value); + } + writer.print(name + ": " + EOL); + line = " " + value; + } + else + { + line = name + ": " + value; + } + while (line.getBytes(JAR_ENCODING).length > MAX_SECTION_LENGTH) { // try to find a MAX_LINE_LENGTH byte section int breakIndex = MAX_SECTION_LENGTH; + if (breakIndex >= line.length()) + { + breakIndex = line.length() - 1; + } String section = line.substring(0, breakIndex); - while (section.getBytes().length > MAX_SECTION_LENGTH + while (section.getBytes(JAR_ENCODING).length > MAX_SECTION_LENGTH && breakIndex > 0) { breakIndex--; section = line.substring(0, breakIndex); diff --git a/src/main/org/apache/tools/ant/taskdefs/ManifestTask.java b/src/main/org/apache/tools/ant/taskdefs/ManifestTask.java index c6cc02d7f..bc1aff508 100644 --- a/src/main/org/apache/tools/ant/taskdefs/ManifestTask.java +++ b/src/main/org/apache/tools/ant/taskdefs/ManifestTask.java @@ -199,7 +199,7 @@ public class ManifestTask extends Task { PrintWriter w = null; try { FileOutputStream fos = new FileOutputStream(manifestFile); - OutputStreamWriter osw = new OutputStreamWriter(fos, "UTF-8"); + OutputStreamWriter osw = new OutputStreamWriter(fos, Manifest.JAR_ENCODING); w = new PrintWriter(osw); toWrite.write(w); } catch (IOException e) { diff --git a/src/testcases/org/apache/tools/ant/taskdefs/ManifestTest.java b/src/testcases/org/apache/tools/ant/taskdefs/ManifestTest.java index 0e1f8dd6d..1a5877882 100644 --- a/src/testcases/org/apache/tools/ant/taskdefs/ManifestTest.java +++ b/src/testcases/org/apache/tools/ant/taskdefs/ManifestTest.java @@ -17,10 +17,13 @@ package org.apache.tools.ant.taskdefs; +import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.util.Enumeration; +import java.util.HashSet; +import java.util.Set; import org.apache.tools.ant.BuildFileTest; import org.apache.tools.ant.Project; @@ -40,6 +43,15 @@ public class ManifestTest extends BuildFileTest { "OfCourseTheAnswerIsThatIsWhatTheSpecRequiresAndIfAnythingHas" + "AProblemWithThatItIsNotABugInAnt"; + public static final String LONG_70_NAME + = "ThisNameIsJustSeventyCharactersWhichIsAllowedAccordingToTheSpecsFiller"; + public static final String LONG_68_NAME + = "ThisNameIsJustSixtyEightCharactersWhichIsAllowedAccordingToTheSpecsX"; + public static final String NOT_LONG_NAME + = "NameIsJustUnderSeventyCharactersWhichIsAllowedAccordingTheSpec"; + + public static final String VALUE = "NOT_LONG"; + public ManifestTest(String name) { super(name); } @@ -192,6 +204,10 @@ public class ManifestTest extends BuildFileTest { public void testLongLine() throws IOException, ManifestException { Project p = getProject(); p.setUserProperty("test.longline", LONG_LINE); + p.setUserProperty("test.long68name" , LONG_68_NAME); + p.setUserProperty("test.long70name" , LONG_70_NAME); + p.setUserProperty("test.notlongname" , NOT_LONG_NAME); + p.setUserProperty("test.value", VALUE); executeTarget("testLongLine"); Manifest manifest = getManifest(EXPANDED_MANIFEST); @@ -199,6 +215,32 @@ public class ManifestTest extends BuildFileTest { String classpath = mainSection.getAttributeValue("class-path"); assertEquals("Class-Path attribute was not set correctly - ", LONG_LINE, classpath); + + String value = mainSection.getAttributeValue(LONG_68_NAME); + assertEquals("LONG_68_NAME_VALUE_MISMATCH", VALUE, value); + value = mainSection.getAttributeValue(LONG_70_NAME); + assertEquals("LONG_70_NAME_VALUE_MISMATCH", VALUE, value); + value = mainSection.getAttributeValue(NOT_LONG_NAME); + assertEquals("NOT_LONG_NAME_VALUE_MISMATCH", VALUE, value); + + BufferedReader in = new BufferedReader(new FileReader(EXPANDED_MANIFEST)); + + Set set = new HashSet(); + String read = in.readLine(); + while (read != null) + { + set.add(read); + read = in.readLine(); + } + + assertTrue("Manifest file should have contained string ", set + .remove(" NOT_LONG")); + assertTrue("Manifest file should have contained string ", set + .remove(" NG")); + assertTrue("Manifest file should have contained string ", set + .remove(LONG_70_NAME + ": ")); + assertTrue("Manifest file should have contained string ", set + .remove(NOT_LONG_NAME + ": NOT_LO")); } /**