Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

8173970: jar tool should have a way to extract to a directory #2752

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -30,6 +30,8 @@
import java.lang.module.ModuleDescriptor.Version;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashSet;
import java.util.Set;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import jdk.internal.module.ModulePath;
@@ -215,6 +217,14 @@ void process(Main jartool, String opt, String arg) {
if (jartool.info == null)
jartool.info = GNUStyleOptions::printVersion;
}
},
new Option(true, true, OptionType.EXTRACT, "--dir") {
void process(Main jartool, String opt, String arg) throws BadArgs {
if (jartool.xdestDir != null) {
throw new BadArgs("error.extract.multiple.dest.dir").showUsage(true);
}
jartool.xdestDir = arg;
}
}
};

@@ -224,7 +234,8 @@ void process(Main jartool, String opt, String arg) {
CREATE("create"),
CREATE_UPDATE("create.update"),
CREATE_UPDATE_INDEX("create.update.index"),
OTHER("other");
OTHER("other"),
EXTRACT("extract");

/** Resource lookup section prefix. */
final String name;
@@ -1,5 +1,5 @@
/*
* Copyright (c) 1996, 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1996, 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -154,6 +154,9 @@ public int hashCode() {

boolean suppressDeprecateMsg = false;

// destination directory for extraction
String xdestDir = null;

/* To support additional GNU Style informational options */
Consumer<PrintWriter> info;

@@ -359,6 +362,15 @@ public synchronized boolean run(String args[]) {
}
}
} else if (xflag) {
if (xdestDir != null) {
final Path destPath = Paths.get(xdestDir);
try {
Files.createDirectories(destPath);
} catch (IOException ioe) {
throw new IOException(formatMsg("error.create.dir",
destPath.toString()), ioe);
}
}
replaceFSC(filesMap);
// For the extract action, when extracting all the entries,
// access using the ZipInputStream class is most efficient,
@@ -599,6 +611,11 @@ boolean parseArgs(String args[]) {
}
/* change the directory */
String dir = args[++i];
if (xflag && xdestDir != null) {
// extract option doesn't allow more than one destination directory
usageError(getMsg("error.extract.multiple.dest.dir"));
return false;
}
dir = (dir.endsWith(File.separator) ?
dir : (dir + File.separator));
dir = dir.replace(File.separatorChar, '/');
@@ -610,8 +627,12 @@ boolean parseArgs(String args[]) {
if (hasUNC) { // Restore Windows UNC path.
dir = "/" + dir;
}
pathsMap.get(version).add(dir);
nameBuf[k++] = dir + args[++i];
if (xflag) {
xdestDir = dir;
} else {
pathsMap.get(version).add(dir);
nameBuf[k++] = dir + args[++i];
}
} else if (args[i].startsWith("--release")) {
int v = BASE_VERSION;
try {
@@ -670,6 +691,10 @@ boolean parseArgs(String args[]) {
return false;
}
}
if (xflag && pflag && xdestDir != null) {
usageError(getMsg("error.extract.pflag.not.allowed"));
return false;
}
return true;
}

@@ -1306,7 +1331,7 @@ void updateLastModifiedTime(Set<ZipEntry> zes) throws IOException {
if (lastModified != -1) {
String name = safeName(ze.getName().replace(File.separatorChar, '/'));
if (name.length() != 0) {
File f = new File(name.replace('/', File.separatorChar));
File f = new File(xdestDir, name.replace('/', File.separatorChar));
f.setLastModified(lastModified);
}
}
@@ -1320,6 +1345,10 @@ void updateLastModifiedTime(Set<ZipEntry> zes) throws IOException {
* (indicating this was a zip file without "leading garbage")
*/
boolean extract(InputStream in, String files[]) throws IOException {
if (vflag) {
output(formatMsg("out.extract.dir", Path.of(xdestDir == null ? "." : xdestDir).normalize()
.toAbsolutePath().toString()));
}
ZipInputStream zis = new ZipInputStream(in);
ZipEntry e;
// Set of all directory entries specified in archive. Disallows
@@ -1354,6 +1383,10 @@ boolean extract(InputStream in, String files[]) throws IOException {
* Extracts specified entries from JAR file, via ZipFile.
*/
void extract(String fname, String files[]) throws IOException {
if (vflag) {
output(formatMsg("out.extract.dir", Path.of(xdestDir == null ? "." : xdestDir).normalize()
.toAbsolutePath().toString()));
}
ZipFile zf = new ZipFile(fname);
Set<ZipEntry> dirs = newDirSet();
Enumeration<? extends ZipEntry> zes = zf.entries();
@@ -1382,16 +1415,24 @@ void extract(String fname, String files[]) throws IOException {
*/
ZipEntry extractFile(InputStream is, ZipEntry e) throws IOException {
ZipEntry rc = null;
// The spec requres all slashes MUST be forward '/', it is possible
// The spec requires all slashes MUST be forward '/', it is possible
// an offending zip/jar entry may uses the backwards slash in its
// name. It might cause problem on Windows platform as it skips
// our "safe" check for leading slahs and dot-dot. So replace them
// our "safe" check for leading slash and dot-dot. So replace them
// with '/'.
String name = safeName(e.getName().replace(File.separatorChar, '/'));
if (name.length() == 0) {
return rc; // leading '/' or 'dot-dot' only path
}
File f = new File(name.replace('/', File.separatorChar));
// the xdestDir points to the user specified location where the jar needs to
// be extracted. By default xdestDir is null and represents current working
// directory.
// jar extraction using -P option is only allowed when the destination
// directory isn't specified (and hence defaults to current working directory).
// In such cases using this java.io.File constructor which accepts a null parent path
// allows us to extract entries that may have leading slashes and hence may need
// to be extracted outside of the current directory.
File f = new File(xdestDir, name.replace('/', File.separatorChar));

This comment has been minimized.

@LanceAndersen

LanceAndersen Mar 31, 2021
Contributor

Could you please add a comment regarding xdestDir and also correct the typo on line 1418 'requres' -> 'requires'

This comment has been minimized.

@jaikiran

jaikiran Apr 1, 2021
Author Member

Fixed the typo and also added code comment about the xdestDir usage and semantics. If the new comment needs clarification/changes, do let me know.

if (e.isDirectory()) {
if (f.exists()) {
if (!f.isDirectory()) {
@@ -1,5 +1,5 @@
#
# Copyright (c) 1999, 2018, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 1999, 2021, Oracle and/or its affiliates. All rights reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
@@ -58,6 +58,10 @@ error.incorrect.length=\
incorrect length while processing: {0}
error.create.tempfile=\
Could not create a temporary file
error.extract.multiple.dest.dir=\
You may not specify the '-C' or '--dir' option more than once with the '-x' option
error.extract.pflag.not.allowed=\
You may not specify '-Px' with the '-C' or '--dir' options
error.hash.dep=\
Hashing module {0} dependences, unable to find module {1} on module path
error.module.options.without.info=\
@@ -159,6 +163,8 @@ out.inflated=\
\ inflated: {0}
out.size=\
(in = {0}) (out= {1})
out.extract.dir=\
extracting to directory: {0}

usage.compat=\
\Compatibility Interface:\
@@ -180,6 +186,7 @@ Options:\n\
\ \ -i generate index information for the specified jar files\n\
\ \ -C change to the specified directory and include the following file\n\
If any file is a directory then it is processed recursively.\n\
When used in extract mode, extracts the jar to the specified directory\n\
The manifest file name, the archive file name and the entry point name are\n\
specified in the same order as the 'm', 'f' and 'e' flags.\n\n\
Example 1: to archive two class files into an archive called classes.jar: \n\
@@ -239,7 +246,8 @@ main.help.opt.any=\
\ Operation modifiers valid in any mode:\n\
\n\
\ -C DIR Change to the specified directory and include the\n\
\ following file
\ following file. When used in extract mode, extracts\n\
\ the jar to the specified directory

This comment has been minimized.

@LanceAndersen

LanceAndersen Mar 31, 2021
Contributor

Should this be updated on line 187 as well in the compatibility mode section?

This comment has been minimized.

@jaikiran

jaikiran Apr 1, 2021
Author Member

Updated in latest version of the PR.

main.help.opt.any.file=\
\ -f, --file=FILE The archive file name. When omitted, either stdin or\n\
\ stdout is used based on the operation\n\
@@ -302,3 +310,7 @@ main.help.postopt=\
\n\
\ Mandatory or optional arguments to long options are also mandatory or optional\n\
\ for any corresponding short options.
main.help.opt.extract=\
\ Operation modifiers valid only in extract mode:\n
main.help.opt.extract.dir=\
\ --dir Directory into which the jar will be extracted
ProTip! Use n and p to navigate between commits in a pull request.