< prev index next >

src/java.base/share/classes/java/util/jar/Manifest.java

Print this page
rev 51096 : 8205525: Improve exception messages during manifest parsing of jar archives


  36 
  37 /**
  38  * The Manifest class is used to maintain Manifest entry names and their
  39  * associated Attributes. There are main Manifest Attributes as well as
  40  * per-entry Attributes. For information on the Manifest format, please
  41  * see the
  42  * <a href="{@docRoot}/../specs/jar/jar.html">
  43  * Manifest format specification</a>.
  44  *
  45  * @author  David Connelly
  46  * @see     Attributes
  47  * @since   1.2
  48  */
  49 public class Manifest implements Cloneable {
  50     // manifest main attributes
  51     private Attributes attr = new Attributes();
  52 
  53     // manifest entries
  54     private Map<String, Attributes> entries = new HashMap<>();
  55 



  56     /**
  57      * Constructs a new, empty Manifest.
  58      */
  59     public Manifest() {
  60     }
  61 
  62     /**
  63      * Constructs a new Manifest from the specified input stream.
  64      *
  65      * @param is the input stream containing manifest data
  66      * @throws IOException if an I/O error has occurred
  67      */
  68     public Manifest(InputStream is) throws IOException {
  69         read(is);
  70     }
  71 
  72     /**












  73      * Constructs a new Manifest that is a copy of the specified Manifest.
  74      *
  75      * @param man the Manifest to copy
  76      */
  77     public Manifest(Manifest man) {
  78         attr.putAll(man.getMainAttributes());
  79         entries.putAll(man.getEntries());
  80     }
  81 
  82     /**
  83      * Returns the main Attributes for the Manifest.
  84      * @return the main Attributes for the Manifest
  85      */
  86     public Attributes getMainAttributes() {
  87         return attr;
  88     }
  89 
  90     /**
  91      * Returns a Map of the entries contained in this Manifest. Each entry
  92      * is represented by a String name (key) and associated Attributes (value).


 176             index += 74; // + line width + line break ("\r\n")
 177             length += 3; // + line break ("\r\n") and space
 178         }
 179         return;
 180     }
 181 
 182     /**
 183      * Reads the Manifest from the specified InputStream. The entry
 184      * names and attributes read will be merged in with the current
 185      * manifest entries.
 186      *
 187      * @param is the input stream
 188      * @exception IOException if an I/O error has occurred
 189      */
 190     public void read(InputStream is) throws IOException {
 191         // Buffered input stream for reading manifest data
 192         FastInputStream fis = new FastInputStream(is);
 193         // Line buffer
 194         byte[] lbuf = new byte[512];
 195         // Read the main attributes for the manifest
 196         attr.read(fis, lbuf);
 197         // Total number of entries, attributes read
 198         int ecount = 0, acount = 0;
 199         // Average size of entry attributes
 200         int asize = 2;
 201         // Now parse the manifest entries
 202         int len;
 203         String name = null;
 204         boolean skipEmptyLines = true;
 205         byte[] lastline = null;
 206 
 207         while ((len = fis.readLine(lbuf)) != -1) {
 208             byte c = lbuf[--len];


 209             if (c != '\n' && c != '\r') {
 210                 throw new IOException("manifest line too long");

 211             }
 212             if (len > 0 && lbuf[len-1] == '\r') {
 213                 --len;
 214             }
 215             if (len == 0 && skipEmptyLines) {
 216                 continue;
 217             }
 218             skipEmptyLines = false;
 219 
 220             if (name == null) {
 221                 name = parseName(lbuf, len);
 222                 if (name == null) {
 223                     throw new IOException("invalid manifest format");

 224                 }
 225                 if (fis.peek() == ' ') {
 226                     // name is wrapped
 227                     lastline = new byte[len - 6];
 228                     System.arraycopy(lbuf, 6, lastline, 0, len - 6);
 229                     continue;
 230                 }
 231             } else {
 232                 // continuation line
 233                 byte[] buf = new byte[lastline.length + len - 1];
 234                 System.arraycopy(lastline, 0, buf, 0, lastline.length);
 235                 System.arraycopy(lbuf, 1, buf, lastline.length, len - 1);
 236                 if (fis.peek() == ' ') {
 237                     // name is wrapped
 238                     lastline = buf;
 239                     continue;
 240                 }
 241                 name = new String(buf, 0, buf.length, "UTF8");
 242                 lastline = null;
 243             }
 244             Attributes attr = getAttributes(name);
 245             if (attr == null) {
 246                 attr = new Attributes(asize);
 247                 entries.put(name, attr);
 248             }
 249             attr.read(fis, lbuf);
 250             ecount++;
 251             acount += attr.size();
 252             //XXX: Fix for when the average is 0. When it is 0,
 253             // you get an Attributes object with an initial
 254             // capacity of 0, which tickles a bug in HashMap.
 255             asize = Math.max(2, acount / ecount);
 256 
 257             name = null;
 258             skipEmptyLines = true;
 259         }
 260     }
 261 
 262     private String parseName(byte[] lbuf, int len) {
 263         if (toLower(lbuf[0]) == 'n' && toLower(lbuf[1]) == 'a' &&
 264             toLower(lbuf[2]) == 'm' && toLower(lbuf[3]) == 'e' &&
 265             lbuf[4] == ':' && lbuf[5] == ' ') {
 266             try {
 267                 return new String(lbuf, 6, len - 6, "UTF8");
 268             }
 269             catch (Exception e) {




  36 
  37 /**
  38  * The Manifest class is used to maintain Manifest entry names and their
  39  * associated Attributes. There are main Manifest Attributes as well as
  40  * per-entry Attributes. For information on the Manifest format, please
  41  * see the
  42  * <a href="{@docRoot}/../specs/jar/jar.html">
  43  * Manifest format specification</a>.
  44  *
  45  * @author  David Connelly
  46  * @see     Attributes
  47  * @since   1.2
  48  */
  49 public class Manifest implements Cloneable {
  50     // manifest main attributes
  51     private Attributes attr = new Attributes();
  52 
  53     // manifest entries
  54     private Map<String, Attributes> entries = new HashMap<>();
  55 
  56     // name of the corresponding jar archive if available.
  57     private String jarFilename = null;
  58 
  59     /**
  60      * Constructs a new, empty Manifest.
  61      */
  62     public Manifest() {
  63     }
  64 
  65     /**
  66      * Constructs a new Manifest from the specified input stream.
  67      *
  68      * @param is the input stream containing manifest data
  69      * @throws IOException if an I/O error has occurred
  70      */
  71     public Manifest(InputStream is) throws IOException {
  72         read(is);
  73     }
  74 
  75     /**
  76      * Constructs a new Manifest from the specified input stream.
  77      *
  78      * @param is the input stream containing manifest data
  79      * @param jarFilename the name of the corresponding jar archive if available
  80      * @throws IOException if an I/O error has occured
  81      */
  82     Manifest(InputStream is, String jarFilename) throws IOException {
  83         this.jarFilename = jarFilename;
  84         read(is);
  85     }
  86 
  87     /**
  88      * Constructs a new Manifest that is a copy of the specified Manifest.
  89      *
  90      * @param man the Manifest to copy
  91      */
  92     public Manifest(Manifest man) {
  93         attr.putAll(man.getMainAttributes());
  94         entries.putAll(man.getEntries());
  95     }
  96 
  97     /**
  98      * Returns the main Attributes for the Manifest.
  99      * @return the main Attributes for the Manifest
 100      */
 101     public Attributes getMainAttributes() {
 102         return attr;
 103     }
 104 
 105     /**
 106      * Returns a Map of the entries contained in this Manifest. Each entry
 107      * is represented by a String name (key) and associated Attributes (value).


 191             index += 74; // + line width + line break ("\r\n")
 192             length += 3; // + line break ("\r\n") and space
 193         }
 194         return;
 195     }
 196 
 197     /**
 198      * Reads the Manifest from the specified InputStream. The entry
 199      * names and attributes read will be merged in with the current
 200      * manifest entries.
 201      *
 202      * @param is the input stream
 203      * @exception IOException if an I/O error has occurred
 204      */
 205     public void read(InputStream is) throws IOException {
 206         // Buffered input stream for reading manifest data
 207         FastInputStream fis = new FastInputStream(is);
 208         // Line buffer
 209         byte[] lbuf = new byte[512];
 210         // Read the main attributes for the manifest
 211         int lineNumber = attr.read(fis, lbuf, jarFilename, 0);
 212         // Total number of entries, attributes read
 213         int ecount = 0, acount = 0;
 214         // Average size of entry attributes
 215         int asize = 2;
 216         // Now parse the manifest entries
 217         int len;
 218         String name = null;
 219         boolean skipEmptyLines = true;
 220         byte[] lastline = null;
 221 
 222         while ((len = fis.readLine(lbuf)) != -1) {
 223             byte c = lbuf[--len];
 224             lineNumber++;
 225 
 226             if (c != '\n' && c != '\r') {
 227                 throw new IOException("manifest line too long ("
 228                            + Attributes.getErrorPosition(jarFilename, lineNumber) + ")");
 229             }
 230             if (len > 0 && lbuf[len-1] == '\r') {
 231                 --len;
 232             }
 233             if (len == 0 && skipEmptyLines) {
 234                 continue;
 235             }
 236             skipEmptyLines = false;
 237 
 238             if (name == null) {
 239                 name = parseName(lbuf, len);
 240                 if (name == null) {
 241                     throw new IOException("invalid manifest format"
 242                               + Attributes.getErrorPosition(jarFilename, lineNumber) + ")");
 243                 }
 244                 if (fis.peek() == ' ') {
 245                     // name is wrapped
 246                     lastline = new byte[len - 6];
 247                     System.arraycopy(lbuf, 6, lastline, 0, len - 6);
 248                     continue;
 249                 }
 250             } else {
 251                 // continuation line
 252                 byte[] buf = new byte[lastline.length + len - 1];
 253                 System.arraycopy(lastline, 0, buf, 0, lastline.length);
 254                 System.arraycopy(lbuf, 1, buf, lastline.length, len - 1);
 255                 if (fis.peek() == ' ') {
 256                     // name is wrapped
 257                     lastline = buf;
 258                     continue;
 259                 }
 260                 name = new String(buf, 0, buf.length, "UTF8");
 261                 lastline = null;
 262             }
 263             Attributes attr = getAttributes(name);
 264             if (attr == null) {
 265                 attr = new Attributes(asize);
 266                 entries.put(name, attr);
 267             }
 268             lineNumber = attr.read(fis, lbuf, jarFilename, lineNumber);
 269             ecount++;
 270             acount += attr.size();
 271             //XXX: Fix for when the average is 0. When it is 0,
 272             // you get an Attributes object with an initial
 273             // capacity of 0, which tickles a bug in HashMap.
 274             asize = Math.max(2, acount / ecount);
 275 
 276             name = null;
 277             skipEmptyLines = true;
 278         }
 279     }
 280 
 281     private String parseName(byte[] lbuf, int len) {
 282         if (toLower(lbuf[0]) == 'n' && toLower(lbuf[1]) == 'a' &&
 283             toLower(lbuf[2]) == 'm' && toLower(lbuf[3]) == 'e' &&
 284             lbuf[4] == ':' && lbuf[5] == ' ') {
 285             try {
 286                 return new String(lbuf, 6, len - 6, "UTF8");
 287             }
 288             catch (Exception e) {


< prev index next >