OpenCores
URL https://opencores.org/ocsvn/openrisc/openrisc/trunk

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [gnu-dev/] [or1k-gcc/] [libjava/] [classpath/] [gnu/] [java/] [util/] [jar/] [JarUtils.java] - Blame information for rev 769

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 769 jeremybenn
/* JarUtils.java -- Utility methods for reading/writing Manifest[-like] files
2
   Copyright (C) 2006, 2007 Free Software Foundation, Inc.
3
 
4
This file is part of GNU Classpath.
5
 
6
GNU Classpath is free software; you can redistribute it and/or modify
7
it under the terms of the GNU General Public License as published by
8
the Free Software Foundation; either version 2, or (at your option)
9
any later version.
10
 
11
GNU Classpath is distributed in the hope that it will be useful, but
12
WITHOUT ANY WARRANTY; without even the implied warranty of
13
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
General Public License for more details.
15
 
16
You should have received a copy of the GNU General Public License
17
along with GNU Classpath; see the file COPYING.  If not, write to the
18
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19
02110-1301 USA.
20
 
21
Linking this library statically or dynamically with other modules is
22
making a combined work based on this library.  Thus, the terms and
23
conditions of the GNU General Public License cover the whole
24
combination.
25
 
26
As a special exception, the copyright holders of this library give you
27
permission to link this library with independent modules to produce an
28
executable, regardless of the license terms of these independent
29
modules, and to copy and distribute the resulting executable under
30
terms of your choice, provided that you also meet, for each linked
31
independent module, the terms and conditions of the license of that
32
module.  An independent module is a module which is not derived from
33
or based on this library.  If you modify this library, you may extend
34
this exception to your version of the library, but you are not
35
obligated to do so.  If you do not wish to do so, delete this
36
exception statement from your version. */
37
 
38
 
39
package gnu.java.util.jar;
40
 
41
import gnu.classpath.SystemProperties;
42
 
43
import java.io.BufferedOutputStream;
44
import java.io.BufferedReader;
45
import java.io.IOException;
46
import java.io.InputStream;
47
import java.io.InputStreamReader;
48
import java.io.OutputStream;
49
import java.util.Iterator;
50
import java.util.Map;
51
import java.util.jar.Attributes;
52
import java.util.jar.JarException;
53
import java.util.jar.Attributes.Name;
54
import java.util.logging.Logger;
55
 
56
/**
57
 * Utility methods for reading and writing JAR <i>Manifest</i> and
58
 * <i>Manifest-like</i> files.
59
 * <p>
60
 * JAR-related files that resemble <i>Manifest</i> files are Signature files
61
 * (with an <code>.SF</code> extension) found in signed JARs.
62
 */
63
public abstract class JarUtils
64
{
65
  // We used to log here, but this causes problems during bootstrap,
66
  // and it didn't seem worthwhile to preserve this.  Still, this
67
  // might be useful for debugging.
68
  // private static final Logger log = Logger.getLogger(JarUtils.class.getName());
69
  public static final String META_INF = "META-INF/";
70
  public static final String DSA_SUFFIX = ".DSA";
71
  public static final String SF_SUFFIX = ".SF";
72
  public static final String NAME = "Name";
73
 
74
  /**
75
   * The original string representation of the manifest version attribute name.
76
   */
77
  public static final String MANIFEST_VERSION = "Manifest-Version";
78
 
79
  /**
80
   * The original string representation of the signature version attribute
81
   * name.
82
   */
83
  public static final String SIGNATURE_VERSION = "Signature-Version";
84
 
85
  /** Platform-independent line-ending. */
86
  public static final byte[] CRLF = new byte[] { 0x0D, 0x0A };
87
  private static final String DEFAULT_MF_VERSION = "1.0";
88
  private static final String DEFAULT_SF_VERSION = "1.0";
89
  private static final Name CREATED_BY = new Name("Created-By");
90
  private static final String CREATOR = SystemProperties.getProperty("java.version")
91
                                        + " ("
92
                                        + SystemProperties.getProperty("java.vendor")
93
                                        + ")";
94
 
95
  // default 0-arguments constructor
96
 
97
  // Methods for reading Manifest files from InputStream ----------------------
98
 
99
  public static void
100
  readMFManifest(Attributes attr, Map entries, InputStream in)
101
      throws IOException
102
  {
103
    BufferedReader br = new BufferedReader(new InputStreamReader(in, "UTF-8"));
104
    readMainSection(attr, br);
105
    readIndividualSections(entries, br);
106
  }
107
 
108
  public static void
109
  readSFManifest(Attributes attr, Map entries, InputStream in)
110
      throws IOException
111
  {
112
    BufferedReader br = new BufferedReader(new InputStreamReader(in, "UTF-8"));
113
    String version_header = Name.SIGNATURE_VERSION.toString();
114
    try
115
      {
116
        String version = expectHeader(version_header, br);
117
        attr.putValue(SIGNATURE_VERSION, version);
118
        // This may cause problems during VM bootstrap.
119
        // if (! DEFAULT_SF_VERSION.equals(version))
120
        //  log.warning("Unexpected version number: " + version
121
        //              + ". Continue (but may fail later)");
122
      }
123
    catch (IOException ioe)
124
      {
125
        throw new JarException("Signature file MUST start with a "
126
                               + version_header + ": " + ioe.getMessage());
127
      }
128
    read_attributes(attr, br);
129
 
130
    // read individual sections
131
    String s = br.readLine();
132
    while (s != null && s.length() > 0)
133
      {
134
        Attributes eAttr = readSectionName(s, br, entries);
135
        read_attributes(eAttr, br);
136
        s = br.readLine();
137
      }
138
  }
139
 
140
  private static void readMainSection(Attributes attr, BufferedReader br)
141
      throws IOException
142
  {
143
    // According to the spec we should actually call read_version_info() here.
144
    read_attributes(attr, br);
145
    // Explicitly set Manifest-Version attribute if not set in Main
146
    // attributes of Manifest.
147
    // XXX (rsn): why 0.0 and not 1.0?
148
    if (attr.getValue(Name.MANIFEST_VERSION) == null)
149
      attr.putValue(MANIFEST_VERSION, "0.0");
150
  }
151
 
152
  private static void readIndividualSections(Map entries, BufferedReader br)
153
      throws IOException
154
  {
155
    String s = br.readLine();
156
    while (s != null && (! s.equals("")))
157
      {
158
        Attributes attr = readSectionName(s, br, entries);
159
        read_attributes(attr, br);
160
        s = br.readLine();
161
      }
162
  }
163
 
164
  /**
165
   * Pedantic method that requires the next attribute in the Manifest to be the
166
   * "Manifest-Version". This follows the Manifest spec closely but reject some
167
   * jar Manifest files out in the wild.
168
   */
169
  private static void readVersionInfo(Attributes attr, BufferedReader br)
170
      throws IOException
171
  {
172
    String version_header = Name.MANIFEST_VERSION.toString();
173
    try
174
      {
175
        String value = expectHeader(version_header, br);
176
        attr.putValue(MANIFEST_VERSION, value);
177
      }
178
    catch (IOException ioe)
179
      {
180
        throw new JarException("Manifest should start with a " + version_header
181
                               + ": " + ioe.getMessage());
182
      }
183
  }
184
 
185
  private static String expectHeader(String header, BufferedReader br)
186
      throws IOException
187
  {
188
    String s = br.readLine();
189
    if (s == null)
190
      throw new JarException("unexpected end of file");
191
 
192
    return expectHeader(header, br, s);
193
  }
194
 
195
  private static void read_attributes(Attributes attr, BufferedReader br)
196
      throws IOException
197
  {
198
    String s = br.readLine();
199
    while (s != null && (! s.equals("")))
200
      {
201
        readAttribute(attr, s, br);
202
        s = br.readLine();
203
      }
204
  }
205
 
206
  private static void
207
  readAttribute(Attributes attr, String s, BufferedReader br) throws IOException
208
  {
209
    try
210
      {
211
        int colon = s.indexOf(": ");
212
        String name = s.substring(0, colon);
213
        String value_start = s.substring(colon + 2);
214
        String value = readHeaderValue(value_start, br);
215
        attr.putValue(name, value);
216
      }
217
    catch (IndexOutOfBoundsException iobe)
218
      {
219
        throw new JarException("Manifest contains a bad header: " + s);
220
      }
221
  }
222
 
223
  private static String readHeaderValue(String s, BufferedReader br)
224
      throws IOException
225
  {
226
    boolean try_next = true;
227
    while (try_next)
228
      {
229
        // Lets see if there is something on the next line
230
        br.mark(1);
231
        if (br.read() == ' ')
232
          s += br.readLine();
233
        else
234
          {
235
            br.reset();
236
            try_next = false;
237
          }
238
      }
239
    return s;
240
  }
241
 
242
  private static Attributes
243
  readSectionName(String s, BufferedReader br, Map entries) throws JarException
244
  {
245
    try
246
      {
247
        String name = expectHeader(NAME, br, s);
248
        Attributes attr = new Attributes();
249
        entries.put(name, attr);
250
        return attr;
251
      }
252
    catch (IOException ioe)
253
      {
254
        throw new JarException("Section should start with a Name header: "
255
                               + ioe.getMessage());
256
      }
257
  }
258
 
259
  private static String expectHeader(String header, BufferedReader br, String s)
260
      throws IOException
261
  {
262
    try
263
      {
264
        String name = s.substring(0, header.length() + 1);
265
        if (name.equalsIgnoreCase(header + ":"))
266
          {
267
            String value_start = s.substring(header.length() + 2);
268
            return readHeaderValue(value_start, br);
269
          }
270
      }
271
    catch (IndexOutOfBoundsException ignored)
272
      {
273
      }
274
    // If we arrive here, something went wrong
275
    throw new JarException("unexpected '" + s + "'");
276
  }
277
 
278
  // Methods for writing Manifest files to an OutputStream --------------------
279
 
280
  public static void
281
  writeMFManifest(Attributes attr, Map entries, OutputStream stream)
282
      throws IOException
283
  {
284
    BufferedOutputStream out = stream instanceof BufferedOutputStream
285
                               ? (BufferedOutputStream) stream
286
                               : new BufferedOutputStream(stream, 4096);
287
    writeVersionInfo(attr, out);
288
    Iterator i;
289
    Map.Entry e;
290
    for (i = attr.entrySet().iterator(); i.hasNext();)
291
      {
292
        e = (Map.Entry) i.next();
293
        // Don't print the manifest version again
294
        if (! Name.MANIFEST_VERSION.equals(e.getKey()))
295
          writeAttributeEntry(e, out);
296
      }
297
    out.write(CRLF);
298
 
299
    Iterator j;
300
    for (i = entries.entrySet().iterator(); i.hasNext();)
301
      {
302
        e = (Map.Entry) i.next();
303
        writeHeader(NAME, e.getKey().toString(), out);
304
        Attributes eAttr = (Attributes) e.getValue();
305
        for (j = eAttr.entrySet().iterator(); j.hasNext();)
306
          {
307
            Map.Entry e2 = (Map.Entry) j.next();
308
            writeAttributeEntry(e2, out);
309
          }
310
        out.write(CRLF);
311
      }
312
 
313
    out.flush();
314
  }
315
 
316
  public static void
317
  writeSFManifest(Attributes attr, Map entries, OutputStream stream)
318
      throws IOException
319
  {
320
    BufferedOutputStream out = stream instanceof BufferedOutputStream
321
                               ? (BufferedOutputStream) stream
322
                               : new BufferedOutputStream(stream, 4096);
323
    writeHeader(Name.SIGNATURE_VERSION.toString(), DEFAULT_SF_VERSION, out);
324
    writeHeader(CREATED_BY.toString(), CREATOR, out);
325
    Iterator i;
326
    Map.Entry e;
327
    for (i = attr.entrySet().iterator(); i.hasNext();)
328
      {
329
        e = (Map.Entry) i.next();
330
        Name name = (Name) e.getKey();
331
        if (Name.SIGNATURE_VERSION.equals(name) || CREATED_BY.equals(name))
332
          continue;
333
 
334
        writeHeader(name.toString(), (String) e.getValue(), out);
335
      }
336
    out.write(CRLF);
337
 
338
    Iterator j;
339
    for (i = entries.entrySet().iterator(); i.hasNext();)
340
      {
341
        e = (Map.Entry) i.next();
342
        writeHeader(NAME, e.getKey().toString(), out);
343
        Attributes eAttr = (Attributes) e.getValue();
344
        for (j = eAttr.entrySet().iterator(); j.hasNext();)
345
          {
346
            Map.Entry e2 = (Map.Entry) j.next();
347
            writeHeader(e2.getKey().toString(), (String) e2.getValue(), out);
348
          }
349
        out.write(CRLF);
350
      }
351
 
352
    out.flush();
353
  }
354
 
355
  private static void writeVersionInfo(Attributes attr, OutputStream out)
356
      throws IOException
357
  {
358
    // First check if there is already a version attribute set
359
    String version = attr.getValue(Name.MANIFEST_VERSION);
360
    if (version == null)
361
      version = DEFAULT_MF_VERSION;
362
 
363
    writeHeader(Name.MANIFEST_VERSION.toString(), version, out);
364
  }
365
 
366
  private static void writeAttributeEntry(Map.Entry entry, OutputStream out)
367
      throws IOException
368
  {
369
    String name = entry.getKey().toString();
370
    String value = entry.getValue().toString();
371
    if (name.equalsIgnoreCase(NAME))
372
      throw new JarException("Attributes cannot be called 'Name'");
373
 
374
    if (name.startsWith("From"))
375
      throw new JarException("Header cannot start with the four letters 'From'"
376
                             + name);
377
 
378
    writeHeader(name, value, out);
379
  }
380
 
381
  /**
382
   * The basic method for writing <code>Mainfest</code> attributes. This
383
   * implementation respects the rule stated in the Jar Specification concerning
384
   * the maximum allowed line length; i.e.
385
   *
386
   * <pre>
387
   * No line may be longer than 72 bytes (not characters), in its UTF8-encoded
388
   * form. If a value would make the initial line longer than this, it should
389
   * be continued on extra lines (each starting with a single SPACE).
390
   * </pre>
391
   *
392
   * and
393
   *
394
   * <pre>
395
   * Because header names cannot be continued, the maximum length of a header
396
   * name is 70 bytes (there must be a colon and a SPACE after the name).
397
   * </pre>
398
   *
399
   * @param name the name of the attribute.
400
   * @param value the value of the attribute.
401
   * @param out the output stream to write the attribute's name/value pair to.
402
   * @throws IOException if an I/O related exception occurs during the process.
403
   */
404
  private static void writeHeader(String name, String value, OutputStream out)
405
      throws IOException
406
  {
407
    String target = name + ": ";
408
    byte[] b = target.getBytes("UTF-8");
409
    if (b.length > 72)
410
      throw new IOException("Attribute's name already longer than 70 bytes");
411
 
412
    if (b.length == 72)
413
      {
414
        out.write(b);
415
        out.write(CRLF);
416
        target = " " + value;
417
      }
418
    else
419
      target = target + value;
420
 
421
    int n;
422
    while (true)
423
      {
424
        b = target.getBytes("UTF-8");
425
        if (b.length < 73)
426
          {
427
            out.write(b);
428
            break;
429
          }
430
 
431
        // find an appropriate character position to break on
432
        n = 72;
433
        while (true)
434
          {
435
            b = target.substring(0, n).getBytes("UTF-8");
436
            if (b.length < 73)
437
              break;
438
 
439
            n--;
440
            if (n < 1)
441
              throw new IOException("Header is unbreakable and longer than 72 bytes");
442
          }
443
 
444
        out.write(b);
445
        out.write(CRLF);
446
        target = " " + target.substring(n);
447
      }
448
 
449
    out.write(CRLF);
450
  }
451
}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.