OSDN Git Service

original
[gb-231r1-is01/Gingerbread_2.3.3_r1_IS01.git] / libcore / luni / src / main / java / java / util / jar / InitManifest.java
1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements.  See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License.  You may obtain a copy of the License at
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17
18 package java.util.jar;
19
20 import java.io.IOException;
21 import java.nio.charset.Charsets;
22 import java.util.Arrays;
23 import java.util.Map;
24
25 /**
26  * Reads a JAR file manifest. The specification is here:
27  * http://java.sun.com/javase/6/docs/technotes/guides/jar/jar.html
28  */
29 class InitManifest {
30     private final byte[] buf;
31
32     private int pos;
33
34     private Attributes.Name name;
35
36     private String value;
37
38     private final UnsafeByteSequence valueBuffer = new UnsafeByteSequence(80);
39     private int consecutiveLineBreaks = 0;
40
41     InitManifest(byte[] buf, Attributes main, Attributes.Name ver) throws IOException {
42         this.buf = buf;
43
44         // check a version attribute
45         if (!readHeader() || (ver != null && !name.equals(ver))) {
46             throw new IOException("Missing version attribute: " + ver);
47         }
48
49         main.put(name, value);
50         while (readHeader()) {
51             main.put(name, value);
52         }
53     }
54
55     void initEntries(Map<String, Attributes> entries,
56             Map<String, Manifest.Chunk> chunks) throws IOException {
57
58         int mark = pos;
59         while (readHeader()) {
60             if (!Attributes.Name.NAME.equals(name)) {
61                 throw new IOException("Entry is not named");
62             }
63             String entryNameValue = value;
64
65             Attributes entry = entries.get(entryNameValue);
66             if (entry == null) {
67                 entry = new Attributes(12);
68             }
69
70             while (readHeader()) {
71                 entry.put(name, value);
72             }
73
74             if (chunks != null) {
75                 if (chunks.get(entryNameValue) != null) {
76                     // TODO A bug: there might be several verification chunks for
77                     // the same name. I believe they should be used to update
78                     // signature in order of appearance; there are two ways to fix
79                     // this: either use a list of chunks, or decide on used
80                     // signature algorithm in advance and reread the chunks while
81                     // updating the signature; for now a defensive error is thrown
82                     throw new IOException("A jar verifier does not support more than one entry with the same name");
83                 }
84                 chunks.put(entryNameValue, new Manifest.Chunk(mark, pos));
85                 mark = pos;
86             }
87
88             entries.put(entryNameValue, entry);
89         }
90     }
91
92     int getPos() {
93         return pos;
94     }
95
96     /**
97      * Read a single line from the manifest buffer.
98      */
99     private boolean readHeader() throws IOException {
100         if (consecutiveLineBreaks > 1) {
101             // break a section on an empty line
102             consecutiveLineBreaks = 0;
103             return false;
104         }
105         readName();
106         consecutiveLineBreaks = 0;
107         readValue();
108         // if the last line break is missed, the line
109         // is ignored by the reference implementation
110         return consecutiveLineBreaks > 0;
111     }
112
113     private void readName() throws IOException {
114         int i = 0;
115         int mark = pos;
116
117         while (pos < buf.length) {
118             byte b = buf[pos++];
119
120             if (b == ':') {
121                 byte[] nameBuffer = Arrays.copyOfRange(buf, mark, pos - 1);
122
123                 if (buf[pos++] != ' ') {
124                     String name = new String(nameBuffer, Charsets.UTF_8);
125                     throw new IOException(String.format("Invalid value for attribute '%s'", name));
126                 }
127
128                 name = new Attributes.Name(nameBuffer);
129                 return;
130             }
131
132             if (!((b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z') || b == '_'
133                     || b == '-' || (b >= '0' && b <= '9'))) {
134                 throw new IOException("Invalid byte " + b + " in attribute");
135             }
136         }
137         if (i > 0) {
138             throw new IOException("Invalid attribute name: " +
139                     Arrays.toString(Arrays.copyOfRange(buf, mark, buf.length)));
140         }
141     }
142
143     private void readValue() throws IOException {
144         boolean lastCr = false;
145         int mark = pos;
146         int last = pos;
147         valueBuffer.rewind();
148         while (pos < buf.length) {
149             byte next = buf[pos++];
150             switch (next) {
151             case 0:
152                 throw new IOException("NUL character in a manifest");
153             case '\n':
154                 if (lastCr) {
155                     lastCr = false;
156                 } else {
157                     consecutiveLineBreaks++;
158                 }
159                 continue;
160             case '\r':
161                 lastCr = true;
162                 consecutiveLineBreaks++;
163                 continue;
164             case ' ':
165                 if (consecutiveLineBreaks == 1) {
166                     valueBuffer.write(buf, mark, last - mark);
167                     mark = pos;
168                     consecutiveLineBreaks = 0;
169                     continue;
170                 }
171             }
172
173             if (consecutiveLineBreaks >= 1) {
174                 pos--;
175                 break;
176             }
177             last = pos;
178         }
179
180         valueBuffer.write(buf, mark, last - mark);
181         value = valueBuffer.toString(Charsets.UTF_8);
182     }
183 }