14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package sun.tools.jar;
27
28 import java.io.*;
29 import java.lang.module.Configuration;
30 import java.lang.module.FindException;
31 import java.lang.module.InvalidModuleDescriptorException;
32 import java.lang.module.ModuleDescriptor;
33 import java.lang.module.ModuleDescriptor.Exports;
34 import java.lang.module.ModuleDescriptor.Provides;
35 import java.lang.module.ModuleDescriptor.Opens;
36 import java.lang.module.ModuleDescriptor.Requires;
37 import java.lang.module.ModuleDescriptor.Version;
38 import java.lang.module.ModuleFinder;
39 import java.lang.module.ModuleReader;
40 import java.lang.module.ModuleReference;
41 import java.lang.module.ResolutionException;
42 import java.lang.module.ResolvedModule;
43 import java.net.URI;
44 import java.nio.ByteBuffer;
45 import java.nio.file.Path;
46 import java.nio.file.Files;
47 import java.nio.file.Paths;
48 import java.nio.file.StandardCopyOption;
49 import java.util.*;
50 import java.util.function.Consumer;
51 import java.util.function.Supplier;
52 import java.util.regex.Pattern;
53 import java.util.stream.Collectors;
54 import java.util.stream.Stream;
55 import java.util.zip.*;
56 import java.util.jar.*;
57 import java.util.jar.Pack200.*;
58 import java.util.jar.Manifest;
59 import java.text.MessageFormat;
60
61 import jdk.internal.module.Checks;
62 import jdk.internal.module.ModuleHashes;
63 import jdk.internal.module.ModuleHashesBuilder;
64 import jdk.internal.module.ModuleInfo;
65 import jdk.internal.module.ModuleInfoExtender;
66 import jdk.internal.module.ModuleResolution;
67 import jdk.internal.module.ModuleTarget;
68 import jdk.internal.util.jar.JarIndex;
69
70 import static jdk.internal.util.jar.JarIndex.INDEX_NAME;
71 import static java.util.jar.JarFile.MANIFEST_NAME;
72 import static java.util.stream.Collectors.joining;
73 import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
74
75 /**
76 * This class implements a simple utility for creating files in the JAR
77 * (Java Archive) file format. The JAR format is based on the ZIP file
78 * format, with optional meta-information stored in a MANIFEST entry.
79 */
80 public class Main {
81 String program;
82 PrintWriter out, err;
83 String fname, mname, ename;
84 String zname = "";
85 String rootjar = null;
86
87 private static final int BASE_VERSION = 0;
88
89 private static class Entry {
90 final String name;
91 final File file;
92 final boolean isDir;
93
134
135 // The last parsed --release value, if any. Used in conjunction with
136 // "-d,--describe-module" to select the operative module descriptor.
137 int releaseValue = -1;
138
139 /*
140 * cflag: create
141 * uflag: update
142 * xflag: xtract
143 * tflag: table
144 * vflag: verbose
145 * flag0: no zip compression (store only)
146 * Mflag: DO NOT generate a manifest file (just ZIP)
147 * iflag: generate jar index
148 * nflag: Perform jar normalization at the end
149 * pflag: preserve/don't strip leading slash and .. component from file name
150 * dflag: print module descriptor
151 */
152 boolean cflag, uflag, xflag, tflag, vflag, flag0, Mflag, iflag, nflag, pflag, dflag;
153
154 /* To support additional GNU Style informational options */
155 Consumer<PrintWriter> info;
156
157 /* Modular jar related options */
158 Version moduleVersion;
159 Pattern modulesToHash;
160 ModuleResolution moduleResolution = ModuleResolution.empty();
161 ModuleFinder moduleFinder = ModuleFinder.of();
162
163 static final String MODULE_INFO = "module-info.class";
164 static final String MANIFEST_DIR = "META-INF/";
165 static final String VERSIONS_DIR = MANIFEST_DIR + "versions/";
166 static final String VERSION = "1.0";
167 static final int VERSIONS_DIR_LENGTH = VERSIONS_DIR.length();
168 private static ResourceBundle rsrc;
169
170 /**
171 * If true, maintain compatibility with JDK releases prior to 6.0 by
172 * timestamping extracted files with the time at which they are extracted.
173 * Default is to use the time given in the archive.
221 this.program = program;
222 }
223
224 /**
225 * Creates a new empty temporary file in the same directory as the
226 * specified file. A variant of File.createTempFile.
227 */
228 private static File createTempFileInSameDirectoryAs(File file)
229 throws IOException {
230 File dir = file.getParentFile();
231 if (dir == null)
232 dir = new File(".");
233 return File.createTempFile("jartmp", null, dir);
234 }
235
236 private boolean ok;
237
238 /**
239 * Starts main program with the specified arguments.
240 */
241 public synchronized boolean run(String args[]) {
242 ok = true;
243 if (!parseArgs(args)) {
244 return false;
245 }
246 File tmpFile = null;
247 try {
248 if (cflag || uflag) {
249 if (fname != null) {
250 // The name of the zip file as it would appear as its own
251 // zip file entry. We use this to make sure that we don't
252 // add the zip file to itself.
253 zname = fname.replace(File.separatorChar, '/');
254 if (zname.startsWith("./")) {
255 zname = zname.substring(2);
256 }
257 }
258 }
259 if (cflag) {
260 Manifest manifest = null;
298
299 } else if (moduleVersion != null || modulesToHash != null) {
300 error(getMsg("error.module.options.without.info"));
301 return false;
302 }
303 if (vflag && fname == null) {
304 // Disable verbose output so that it does not appear
305 // on stdout along with file data
306 // error("Warning: -v option ignored");
307 vflag = false;
308 }
309 final String tmpbase = (fname == null)
310 ? "tmpjar"
311 : fname.substring(fname.indexOf(File.separatorChar) + 1);
312
313 tmpFile = createTemporaryFile(tmpbase, ".jar");
314 try (OutputStream out = new FileOutputStream(tmpFile)) {
315 create(new BufferedOutputStream(out, 4096), manifest);
316 }
317 if (nflag) {
318 File packFile = createTemporaryFile(tmpbase, ".pack");
319 try {
320 Packer packer = Pack200.newPacker();
321 Map<String, String> p = packer.properties();
322 p.put(Packer.EFFORT, "1"); // Minimal effort to conserve CPU
323 try (JarFile jarFile = new JarFile(tmpFile.getCanonicalPath());
324 OutputStream pack = new FileOutputStream(packFile))
325 {
326 packer.pack(jarFile, pack);
327 }
328 if (tmpFile.exists()) {
329 tmpFile.delete();
330 }
331 tmpFile = createTemporaryFile(tmpbase, ".jar");
332 try (OutputStream out = new FileOutputStream(tmpFile);
333 JarOutputStream jos = new JarOutputStream(out))
334 {
335 Unpacker unpacker = Pack200.newUnpacker();
336 unpacker.unpack(packFile, jos);
337 }
338 } finally {
339 Files.deleteIfExists(packFile.toPath());
340 }
341 }
342 validateAndClose(tmpFile);
343 } else if (uflag) {
344 File inputFile = null;
345 if (fname != null) {
346 inputFile = new File(fname);
347 tmpFile = createTempFileInSameDirectoryAs(inputFile);
348 } else {
349 vflag = false;
350 tmpFile = createTemporaryFile("tmpjar", ".jar");
351 }
352 expand();
353 try (FileInputStream in = (fname != null) ? new FileInputStream(inputFile)
354 : new FileInputStream(FileDescriptor.in);
355 FileOutputStream out = new FileOutputStream(tmpFile);
|
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package sun.tools.jar;
27
28 import java.io.*;
29 import java.lang.module.Configuration;
30 import java.lang.module.FindException;
31 import java.lang.module.InvalidModuleDescriptorException;
32 import java.lang.module.ModuleDescriptor;
33 import java.lang.module.ModuleDescriptor.Exports;
34 import java.lang.module.ModuleDescriptor.Opens;
35 import java.lang.module.ModuleDescriptor.Provides;
36 import java.lang.module.ModuleDescriptor.Version;
37 import java.lang.module.ModuleFinder;
38 import java.lang.module.ModuleReader;
39 import java.lang.module.ModuleReference;
40 import java.lang.module.ResolvedModule;
41 import java.net.URI;
42 import java.nio.ByteBuffer;
43 import java.nio.file.Files;
44 import java.nio.file.Path;
45 import java.nio.file.Paths;
46 import java.nio.file.StandardCopyOption;
47 import java.text.MessageFormat;
48 import java.util.*;
49 import java.util.function.Consumer;
50 import java.util.jar.Attributes;
51 import java.util.jar.JarFile;
52 import java.util.jar.JarOutputStream;
53 import java.util.jar.Manifest;
54 import java.util.regex.Pattern;
55 import java.util.stream.Collectors;
56 import java.util.stream.Stream;
57 import java.util.zip.CRC32;
58 import java.util.zip.ZipEntry;
59 import java.util.zip.ZipFile;
60 import java.util.zip.ZipInputStream;
61 import java.util.zip.ZipOutputStream;
62 import jdk.internal.module.Checks;
63 import jdk.internal.module.ModuleHashes;
64 import jdk.internal.module.ModuleHashesBuilder;
65 import jdk.internal.module.ModuleInfo;
66 import jdk.internal.module.ModuleInfoExtender;
67 import jdk.internal.module.ModuleResolution;
68 import jdk.internal.module.ModuleTarget;
69 import jdk.internal.util.jar.JarIndex;
70
71 import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
72 import static java.util.jar.JarFile.MANIFEST_NAME;
73 import static java.util.stream.Collectors.joining;
74 import static jdk.internal.util.jar.JarIndex.INDEX_NAME;
75
76 /**
77 * This class implements a simple utility for creating files in the JAR
78 * (Java Archive) file format. The JAR format is based on the ZIP file
79 * format, with optional meta-information stored in a MANIFEST entry.
80 */
81 public class Main {
82 String program;
83 PrintWriter out, err;
84 String fname, mname, ename;
85 String zname = "";
86 String rootjar = null;
87
88 private static final int BASE_VERSION = 0;
89
90 private static class Entry {
91 final String name;
92 final File file;
93 final boolean isDir;
94
135
136 // The last parsed --release value, if any. Used in conjunction with
137 // "-d,--describe-module" to select the operative module descriptor.
138 int releaseValue = -1;
139
140 /*
141 * cflag: create
142 * uflag: update
143 * xflag: xtract
144 * tflag: table
145 * vflag: verbose
146 * flag0: no zip compression (store only)
147 * Mflag: DO NOT generate a manifest file (just ZIP)
148 * iflag: generate jar index
149 * nflag: Perform jar normalization at the end
150 * pflag: preserve/don't strip leading slash and .. component from file name
151 * dflag: print module descriptor
152 */
153 boolean cflag, uflag, xflag, tflag, vflag, flag0, Mflag, iflag, nflag, pflag, dflag;
154
155 boolean suppressDeprecateMsg = false;
156
157 /* To support additional GNU Style informational options */
158 Consumer<PrintWriter> info;
159
160 /* Modular jar related options */
161 Version moduleVersion;
162 Pattern modulesToHash;
163 ModuleResolution moduleResolution = ModuleResolution.empty();
164 ModuleFinder moduleFinder = ModuleFinder.of();
165
166 static final String MODULE_INFO = "module-info.class";
167 static final String MANIFEST_DIR = "META-INF/";
168 static final String VERSIONS_DIR = MANIFEST_DIR + "versions/";
169 static final String VERSION = "1.0";
170 static final int VERSIONS_DIR_LENGTH = VERSIONS_DIR.length();
171 private static ResourceBundle rsrc;
172
173 /**
174 * If true, maintain compatibility with JDK releases prior to 6.0 by
175 * timestamping extracted files with the time at which they are extracted.
176 * Default is to use the time given in the archive.
224 this.program = program;
225 }
226
227 /**
228 * Creates a new empty temporary file in the same directory as the
229 * specified file. A variant of File.createTempFile.
230 */
231 private static File createTempFileInSameDirectoryAs(File file)
232 throws IOException {
233 File dir = file.getParentFile();
234 if (dir == null)
235 dir = new File(".");
236 return File.createTempFile("jartmp", null, dir);
237 }
238
239 private boolean ok;
240
241 /**
242 * Starts main program with the specified arguments.
243 */
244 @SuppressWarnings({"removal"})
245 public synchronized boolean run(String args[]) {
246 ok = true;
247 if (!parseArgs(args)) {
248 return false;
249 }
250 File tmpFile = null;
251 try {
252 if (cflag || uflag) {
253 if (fname != null) {
254 // The name of the zip file as it would appear as its own
255 // zip file entry. We use this to make sure that we don't
256 // add the zip file to itself.
257 zname = fname.replace(File.separatorChar, '/');
258 if (zname.startsWith("./")) {
259 zname = zname.substring(2);
260 }
261 }
262 }
263 if (cflag) {
264 Manifest manifest = null;
302
303 } else if (moduleVersion != null || modulesToHash != null) {
304 error(getMsg("error.module.options.without.info"));
305 return false;
306 }
307 if (vflag && fname == null) {
308 // Disable verbose output so that it does not appear
309 // on stdout along with file data
310 // error("Warning: -v option ignored");
311 vflag = false;
312 }
313 final String tmpbase = (fname == null)
314 ? "tmpjar"
315 : fname.substring(fname.indexOf(File.separatorChar) + 1);
316
317 tmpFile = createTemporaryFile(tmpbase, ".jar");
318 try (OutputStream out = new FileOutputStream(tmpFile)) {
319 create(new BufferedOutputStream(out, 4096), manifest);
320 }
321 if (nflag) {
322 if (!suppressDeprecateMsg) {
323 warn(formatMsg("warn.flag.is.deprecated", "-n"));
324 }
325 File packFile = createTemporaryFile(tmpbase, ".pack");
326 try {
327 java.util.jar.Pack200.Packer packer = java.util.jar.Pack200.newPacker();
328 Map<String, String> p = packer.properties();
329 p.put(java.util.jar.Pack200.Packer.EFFORT, "1"); // Minimal effort to conserve CPU
330 try (JarFile jarFile = new JarFile(tmpFile.getCanonicalPath());
331 OutputStream pack = new FileOutputStream(packFile))
332 {
333 packer.pack(jarFile, pack);
334 }
335 if (tmpFile.exists()) {
336 tmpFile.delete();
337 }
338 tmpFile = createTemporaryFile(tmpbase, ".jar");
339 try (OutputStream out = new FileOutputStream(tmpFile);
340 JarOutputStream jos = new JarOutputStream(out))
341 {
342 java.util.jar.Pack200.Unpacker unpacker = java.util.jar.Pack200.newUnpacker();
343 unpacker.unpack(packFile, jos);
344 }
345 } finally {
346 Files.deleteIfExists(packFile.toPath());
347 }
348 }
349 validateAndClose(tmpFile);
350 } else if (uflag) {
351 File inputFile = null;
352 if (fname != null) {
353 inputFile = new File(fname);
354 tmpFile = createTempFileInSameDirectoryAs(inputFile);
355 } else {
356 vflag = false;
357 tmpFile = createTemporaryFile("tmpjar", ".jar");
358 }
359 expand();
360 try (FileInputStream in = (fname != null) ? new FileInputStream(inputFile)
361 : new FileInputStream(FileDescriptor.in);
362 FileOutputStream out = new FileOutputStream(tmpFile);
|