|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
package com.sun.java.util.jar.pack; |
|
|
|
import java.io.BufferedInputStream; |
|
import java.io.BufferedOutputStream; |
|
import java.io.File; |
|
import java.io.FileInputStream; |
|
import java.io.FileOutputStream; |
|
import java.io.IOException; |
|
import java.io.InputStream; |
|
import java.io.OutputStream; |
|
import java.io.PrintStream; |
|
import java.text.MessageFormat; |
|
import java.nio.file.Files; |
|
import java.nio.file.Path; |
|
import java.util.ArrayList; |
|
import java.util.Arrays; |
|
import java.util.HashMap; |
|
import java.util.Iterator; |
|
import java.util.List; |
|
import java.util.ListIterator; |
|
import java.util.Map; |
|
import java.util.Properties; |
|
import java.util.ResourceBundle; |
|
import java.util.SortedMap; |
|
import java.util.TreeMap; |
|
import java.util.jar.JarFile; |
|
import java.util.jar.JarOutputStream; |
|
import java.util.jar.Pack200; |
|
import java.util.zip.GZIPInputStream; |
|
import java.util.zip.GZIPOutputStream; |
|
|
|
/** Command line interface for Pack200. |
|
*/ |
|
|
|
@SuppressWarnings({"removal"}) |
|
class Driver { |
|
private static final ResourceBundle RESOURCE = |
|
ResourceBundle.getBundle("com.sun.java.util.jar.pack.DriverResource"); |
|
private static boolean suppressDeprecateMsg = false; |
|
|
|
public static void main(String[] ava) throws IOException { |
|
List<String> av = new ArrayList<>(Arrays.asList(ava)); |
|
|
|
boolean doPack = true; |
|
boolean doUnpack = false; |
|
boolean doRepack = false; |
|
boolean doZip = true; |
|
suppressDeprecateMsg = av.remove("-XDsuppress-tool-removal-message"); |
|
String logFile = null; |
|
String verboseProp = Utils.DEBUG_VERBOSE; |
|
|
|
{ |
|
|
|
String arg0 = av.isEmpty() ? "" : av.get(0); |
|
switch (arg0) { |
|
case "--pack": |
|
av.remove(0); |
|
break; |
|
case "--unpack": |
|
av.remove(0); |
|
doPack = false; |
|
doUnpack = true; |
|
break; |
|
} |
|
} |
|
|
|
if (!suppressDeprecateMsg) { |
|
printDeprecateWarning(doPack, System.out); |
|
} |
|
|
|
|
|
Map<String,String> engProps = new HashMap<>(); |
|
engProps.put(verboseProp, System.getProperty(verboseProp)); |
|
|
|
String optionMap; |
|
String[] propTable; |
|
if (doPack) { |
|
optionMap = PACK200_OPTION_MAP; |
|
propTable = PACK200_PROPERTY_TO_OPTION; |
|
} else { |
|
optionMap = UNPACK200_OPTION_MAP; |
|
propTable = UNPACK200_PROPERTY_TO_OPTION; |
|
} |
|
|
|
|
|
Map<String,String> avProps = new HashMap<>(); |
|
try { |
|
for (;;) { |
|
String state = parseCommandOptions(av, optionMap, avProps); |
|
|
|
eachOpt: |
|
for (Iterator<String> opti = avProps.keySet().iterator(); |
|
opti.hasNext(); ) { |
|
String opt = opti.next(); |
|
String prop = null; |
|
for (int i = 0; i < propTable.length; i += 2) { |
|
if (opt.equals(propTable[1+i])) { |
|
prop = propTable[0+i]; |
|
break; |
|
} |
|
} |
|
if (prop != null) { |
|
String val = avProps.get(opt); |
|
opti.remove(); |
|
if (!prop.endsWith(".")) { |
|
|
|
if (!(opt.equals("--verbose") |
|
|| opt.endsWith("="))) { |
|
|
|
boolean flag = (val != null); |
|
if (opt.startsWith("--no-")) |
|
flag = !flag; |
|
val = flag? "true": "false"; |
|
} |
|
engProps.put(prop, val); |
|
} else if (prop.contains(".attribute.")) { |
|
for (String val1 : val.split("\0")) { |
|
String[] val2 = val1.split("=", 2); |
|
engProps.put(prop+val2[0], val2[1]); |
|
} |
|
} else { |
|
|
|
int idx = 1; |
|
for (String val1 : val.split("\0")) { |
|
String prop1; |
|
do { |
|
prop1 = prop+"cli."+(idx++); |
|
} while (engProps.containsKey(prop1)); |
|
engProps.put(prop1, val1); |
|
} |
|
} |
|
} |
|
} |
|
|
|
|
|
if ("--config-file=".equals(state)) { |
|
String propFile = av.remove(0); |
|
Properties fileProps = new Properties(); |
|
try (InputStream propIn = new FileInputStream(propFile)) { |
|
fileProps.load(propIn); |
|
} |
|
if (engProps.get(verboseProp) != null) |
|
fileProps.list(System.out); |
|
for (Map.Entry<Object,Object> me : fileProps.entrySet()) { |
|
engProps.put((String) me.getKey(), (String) me.getValue()); |
|
} |
|
} else if ("--version".equals(state)) { |
|
System.out.println(MessageFormat.format(RESOURCE.getString(DriverResource.VERSION), |
|
Driver.class.getName(), "1.31, 07/05/05")); |
|
return; |
|
} else if ("--help".equals(state)) { |
|
printUsage(doPack, true, System.out); |
|
System.exit(0); |
|
return; |
|
} else { |
|
break; |
|
} |
|
} |
|
} catch (IllegalArgumentException ee) { |
|
System.err.println(MessageFormat.format(RESOURCE.getString(DriverResource.BAD_ARGUMENT), ee)); |
|
printUsage(doPack, false, System.err); |
|
System.exit(2); |
|
return; |
|
} |
|
|
|
|
|
for (String opt : avProps.keySet()) { |
|
String val = avProps.get(opt); |
|
switch (opt) { |
|
case "--repack": |
|
doRepack = true; |
|
break; |
|
case "--no-gzip": |
|
doZip = (val == null); |
|
break; |
|
case "--log-file=": |
|
logFile = val; |
|
break; |
|
default: |
|
throw new InternalError(MessageFormat.format( |
|
RESOURCE.getString(DriverResource.BAD_OPTION), |
|
opt, avProps.get(opt))); |
|
} |
|
} |
|
|
|
if (logFile != null && !logFile.equals("")) { |
|
if (logFile.equals("-")) { |
|
System.setErr(System.out); |
|
} else { |
|
OutputStream log = new FileOutputStream(logFile); |
|
|
|
System.setErr(new PrintStream(log)); |
|
} |
|
} |
|
|
|
boolean verbose = (engProps.get(verboseProp) != null); |
|
|
|
String packfile = ""; |
|
if (!av.isEmpty()) |
|
packfile = av.remove(0); |
|
|
|
String jarfile = ""; |
|
if (!av.isEmpty()) |
|
jarfile = av.remove(0); |
|
|
|
String newfile = ""; |
|
String bakfile = ""; |
|
String tmpfile = ""; |
|
if (doRepack) { |
|
// The first argument is the target JAR file. |
|
// (Note: *.pac is nonstandard, but may be necessary |
|
|
|
if (packfile.toLowerCase().endsWith(".pack") || |
|
packfile.toLowerCase().endsWith(".pac") || |
|
packfile.toLowerCase().endsWith(".gz")) { |
|
System.err.println(MessageFormat.format( |
|
RESOURCE.getString(DriverResource.BAD_REPACK_OUTPUT), |
|
packfile)); |
|
printUsage(doPack, false, System.err); |
|
System.exit(2); |
|
} |
|
newfile = packfile; |
|
|
|
if (jarfile.equals("")) { |
|
// If only one file is given, it is the only JAR. |
|
|
|
jarfile = newfile; |
|
} |
|
tmpfile = createTempFile(newfile, ".pack").getPath(); |
|
packfile = tmpfile; |
|
doZip = false; |
|
} |
|
|
|
if (!av.isEmpty() |
|
// Accept jarfiles ending with .jar or .zip. |
|
|
|
|| !(jarfile.toLowerCase().endsWith(".jar") |
|
|| jarfile.toLowerCase().endsWith(".zip") |
|
|| (jarfile.equals("-") && !doPack))) { |
|
printUsage(doPack, false, System.err); |
|
System.exit(2); |
|
return; |
|
} |
|
|
|
if (doRepack) |
|
doPack = doUnpack = true; |
|
else if (doPack) |
|
doUnpack = false; |
|
|
|
Pack200.Packer jpack = Pack200.newPacker(); |
|
Pack200.Unpacker junpack = Pack200.newUnpacker(); |
|
|
|
jpack.properties().putAll(engProps); |
|
junpack.properties().putAll(engProps); |
|
if (doRepack && newfile.equals(jarfile)) { |
|
String zipc = getZipComment(jarfile); |
|
if (verbose && zipc.length() > 0) |
|
System.out.println(MessageFormat.format(RESOURCE.getString(DriverResource.DETECTED_ZIP_COMMENT), zipc)); |
|
if (zipc.indexOf(Utils.PACK_ZIP_ARCHIVE_MARKER_COMMENT) >= 0) { |
|
System.out.println(MessageFormat.format(RESOURCE.getString(DriverResource.SKIP_FOR_REPACKED), jarfile)); |
|
doPack = false; |
|
doUnpack = false; |
|
doRepack = false; |
|
} |
|
} |
|
|
|
try { |
|
|
|
if (doPack) { |
|
|
|
JarFile in = new JarFile(new File(jarfile)); |
|
OutputStream out; |
|
|
|
if (packfile.equals("-")) { |
|
out = System.out; |
|
|
|
System.setOut(System.err); |
|
} else if (doZip) { |
|
if (!packfile.endsWith(".gz")) { |
|
System.err.println(MessageFormat.format(RESOURCE.getString(DriverResource.WRITE_PACK_FILE), packfile)); |
|
printUsage(doPack, false, System.err); |
|
System.exit(2); |
|
} |
|
out = new FileOutputStream(packfile); |
|
out = new BufferedOutputStream(out); |
|
out = new GZIPOutputStream(out); |
|
} else { |
|
if (!packfile.toLowerCase().endsWith(".pack") && |
|
!packfile.toLowerCase().endsWith(".pac")) { |
|
System.err.println(MessageFormat.format(RESOURCE.getString(DriverResource.WRITE_PACKGZ_FILE),packfile)); |
|
printUsage(doPack, false, System.err); |
|
System.exit(2); |
|
} |
|
out = new FileOutputStream(packfile); |
|
out = new BufferedOutputStream(out); |
|
} |
|
jpack.pack(in, out); |
|
|
|
out.close(); |
|
} |
|
|
|
if (doRepack && newfile.equals(jarfile)) { |
|
// If the source and destination are the same, |
|
// we will move the input JAR aside while regenerating it. |
|
|
|
File bakf = createTempFile(jarfile, ".bak"); |
|
|
|
bakf.delete(); |
|
boolean okBackup = new File(jarfile).renameTo(bakf); |
|
if (!okBackup) { |
|
throw new Error(MessageFormat.format(RESOURCE.getString(DriverResource.SKIP_FOR_MOVE_FAILED),bakfile)); |
|
} else { |
|
|
|
bakfile = bakf.getPath(); |
|
} |
|
} |
|
|
|
if (doUnpack) { |
|
|
|
InputStream in; |
|
if (packfile.equals("-")) |
|
in = System.in; |
|
else |
|
in = new FileInputStream(new File(packfile)); |
|
BufferedInputStream inBuf = new BufferedInputStream(in); |
|
in = inBuf; |
|
if (Utils.isGZIPMagic(Utils.readMagic(inBuf))) { |
|
in = new GZIPInputStream(in); |
|
} |
|
String outfile = newfile.equals("")? jarfile: newfile; |
|
OutputStream fileOut; |
|
if (outfile.equals("-")) |
|
fileOut = System.out; |
|
else |
|
fileOut = new FileOutputStream(outfile); |
|
fileOut = new BufferedOutputStream(fileOut); |
|
try (JarOutputStream out = new JarOutputStream(fileOut)) { |
|
junpack.unpack(in, out); |
|
// p200 closes in but not out |
|
} |
|
// At this point, we have a good jarfile (or newfile, if -r) |
|
} |
|
|
|
if (!bakfile.equals("")) { |
|
|
|
new File(bakfile).delete(); |
|
bakfile = ""; |
|
} |
|
|
|
} finally { |
|
|
|
if (!bakfile.equals("")) { |
|
File jarFile = new File(jarfile); |
|
jarFile.delete(); |
|
new File(bakfile).renameTo(jarFile); |
|
} |
|
|
|
if (!tmpfile.equals("")) |
|
new File(tmpfile).delete(); |
|
} |
|
} |
|
|
|
private static |
|
File createTempFile(String basefile, String suffix) throws IOException { |
|
File base = new File(basefile); |
|
String prefix = base.getName(); |
|
if (prefix.length() < 3) prefix += "tmp"; |
|
|
|
File where = (base.getParentFile() == null && suffix.equals(".bak")) |
|
? new File(".").getAbsoluteFile() |
|
: base.getParentFile(); |
|
|
|
Path tmpfile = (where == null) |
|
? Files.createTempFile(prefix, suffix) |
|
: Files.createTempFile(where.toPath(), prefix, suffix); |
|
|
|
return tmpfile.toFile(); |
|
} |
|
|
|
private static |
|
void printDeprecateWarning(boolean doPack, PrintStream out) { |
|
String prog = doPack ? "pack200" : "unpack200"; |
|
out.println(MessageFormat.format(RESOURCE.getString(DriverResource.DEPRECATED), prog)); |
|
} |
|
|
|
private static |
|
void printUsage(boolean doPack, boolean full, PrintStream out) { |
|
String prog = doPack ? "pack200" : "unpack200"; |
|
String[] packUsage = (String[])RESOURCE.getObject(DriverResource.PACK_HELP); |
|
String[] unpackUsage = (String[])RESOURCE.getObject(DriverResource.UNPACK_HELP); |
|
String[] usage = doPack? packUsage: unpackUsage; |
|
for (int i = 0; i < usage.length; i++) { |
|
out.println(usage[i]); |
|
if (!full) { |
|
out.println(MessageFormat.format(RESOURCE.getString(DriverResource.MORE_INFO), prog)); |
|
break; |
|
} |
|
} |
|
// Print a warning at the end |
|
|
|
if (full && !suppressDeprecateMsg) { |
|
printDeprecateWarning(doPack, out); |
|
} |
|
} |
|
|
|
private static |
|
String getZipComment(String jarfile) throws IOException { |
|
byte[] tail = new byte[1000]; |
|
long filelen = new File(jarfile).length(); |
|
if (filelen <= 0) return ""; |
|
long skiplen = Math.max(0, filelen - tail.length); |
|
try (InputStream in = new FileInputStream(new File(jarfile))) { |
|
in.skip(skiplen); |
|
in.read(tail); |
|
for (int i = tail.length-4; i >= 0; i--) { |
|
if (tail[i+0] == 'P' && tail[i+1] == 'K' && |
|
tail[i+2] == 5 && tail[i+3] == 6) { |
|
|
|
i += 4+4+4+4+4+2; |
|
if (i < tail.length) |
|
return new String(tail, i, tail.length-i, "UTF8"); |
|
return ""; |
|
} |
|
} |
|
return ""; |
|
} |
|
} |
|
|
|
private static final String PACK200_OPTION_MAP = |
|
("" |
|
+"--repack $ \n -r +>- @--repack $ \n" |
|
+"--no-gzip $ \n -g +>- @--no-gzip $ \n" |
|
+"--strip-debug $ \n -G +>- @--strip-debug $ \n" |
|
+"--no-keep-file-order $ \n -O +>- @--no-keep-file-order $ \n" |
|
+"--segment-limit= *> = \n -S +> @--segment-limit= = \n" |
|
+"--effort= *> = \n -E +> @--effort= = \n" |
|
+"--deflate-hint= *> = \n -H +> @--deflate-hint= = \n" |
|
+"--modification-time= *> = \n -m +> @--modification-time= = \n" |
|
+"--pass-file= *> &\0 \n -P +> @--pass-file= &\0 \n" |
|
+"--unknown-attribute= *> = \n -U +> @--unknown-attribute= = \n" |
|
+"--class-attribute= *> &\0 \n -C +> @--class-attribute= &\0 \n" |
|
+"--field-attribute= *> &\0 \n -F +> @--field-attribute= &\0 \n" |
|
+"--method-attribute= *> &\0 \n -M +> @--method-attribute= &\0 \n" |
|
+"--code-attribute= *> &\0 \n -D +> @--code-attribute= &\0 \n" |
|
+"--config-file= *> . \n -f +> @--config-file= . \n" |
|
|
|
|
|
+"--no-strip-debug !--strip-debug \n" |
|
+"--gzip !--no-gzip \n" |
|
+"--keep-file-order !--no-keep-file-order \n" |
|
|
|
|
|
+"--verbose $ \n -v +>- @--verbose $ \n" |
|
+"--quiet !--verbose \n -q +>- !--verbose \n" |
|
+"--log-file= *> = \n -l +> @--log-file= = \n" |
|
|
|
+"--version . \n -V +> @--version . \n" |
|
+"--help . \n -? +> @--help . \n -h +> @--help . \n" |
|
|
|
|
|
+"-- . \n" |
|
+"- +? >- . \n" // report error if -XXX present; else use stdout |
|
); |
|
// Note: Collection options use "\0" as a delimiter between arguments. |
|
|
|
|
|
private static final String UNPACK200_OPTION_MAP = |
|
("" |
|
+"--deflate-hint= *> = \n -H +> @--deflate-hint= = \n" |
|
+"--verbose $ \n -v +>- @--verbose $ \n" |
|
+"--quiet !--verbose \n -q +>- !--verbose \n" |
|
+"--remove-pack-file $ \n -r +>- @--remove-pack-file $ \n" |
|
+"--log-file= *> = \n -l +> @--log-file= = \n" |
|
+"--config-file= *> . \n -f +> @--config-file= . \n" |
|
|
|
|
|
+"-- . \n" |
|
+"- +? >- . \n" |
|
+"--version . \n -V +> @--version . \n" |
|
+"--help . \n -? +> @--help . \n -h +> @--help . \n" |
|
); |
|
|
|
private static final String[] PACK200_PROPERTY_TO_OPTION = { |
|
Pack200.Packer.SEGMENT_LIMIT, "--segment-limit=", |
|
Pack200.Packer.KEEP_FILE_ORDER, "--no-keep-file-order", |
|
Pack200.Packer.EFFORT, "--effort=", |
|
Pack200.Packer.DEFLATE_HINT, "--deflate-hint=", |
|
Pack200.Packer.MODIFICATION_TIME, "--modification-time=", |
|
Pack200.Packer.PASS_FILE_PFX, "--pass-file=", |
|
Pack200.Packer.UNKNOWN_ATTRIBUTE, "--unknown-attribute=", |
|
Pack200.Packer.CLASS_ATTRIBUTE_PFX, "--class-attribute=", |
|
Pack200.Packer.FIELD_ATTRIBUTE_PFX, "--field-attribute=", |
|
Pack200.Packer.METHOD_ATTRIBUTE_PFX, "--method-attribute=", |
|
Pack200.Packer.CODE_ATTRIBUTE_PFX, "--code-attribute=", |
|
|
|
Utils.DEBUG_VERBOSE, "--verbose", |
|
Utils.COM_PREFIX+"strip.debug", "--strip-debug", |
|
}; |
|
|
|
private static final String[] UNPACK200_PROPERTY_TO_OPTION = { |
|
Pack200.Unpacker.DEFLATE_HINT, "--deflate-hint=", |
|
|
|
Utils.DEBUG_VERBOSE, "--verbose", |
|
Utils.UNPACK_REMOVE_PACKFILE, "--remove-pack-file", |
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private static |
|
String parseCommandOptions(List<String> args, |
|
String options, |
|
Map<String,String> properties) { |
|
//System.out.println(args+" // "+properties); |
|
|
|
String resultString = null; |
|
|
|
|
|
TreeMap<String,String[]> optmap = new TreeMap<>(); |
|
loadOptmap: |
|
for (String optline : options.split("\n")) { |
|
String[] words = optline.split("\\p{Space}+"); |
|
if (words.length == 0) continue loadOptmap; |
|
String opt = words[0]; |
|
words[0] = ""; |
|
if (opt.length() == 0 && words.length >= 1) { |
|
opt = words[1]; |
|
words[1] = ""; |
|
} |
|
if (opt.length() == 0) continue loadOptmap; |
|
String[] prevWords = optmap.put(opt, words); |
|
if (prevWords != null) |
|
throw new RuntimeException(MessageFormat.format(RESOURCE.getString(DriverResource.DUPLICATE_OPTION), optline.trim())); |
|
} |
|
|
|
|
|
ListIterator<String> argp = args.listIterator(); |
|
ListIterator<String> pbp = new ArrayList<String>().listIterator(); |
|
doArgs: |
|
for (;;) { |
|
// One trip through this loop per argument. |
|
|
|
String arg; |
|
if (pbp.hasPrevious()) { |
|
arg = pbp.previous(); |
|
pbp.remove(); |
|
} else if (argp.hasNext()) { |
|
arg = argp.next(); |
|
} else { |
|
|
|
break doArgs; |
|
} |
|
tryOpt: |
|
for (int optlen = arg.length(); ; optlen--) { |
|
|
|
String opt; |
|
|
|
findOpt: |
|
for (;;) { |
|
opt = arg.substring(0, optlen); |
|
if (optmap.containsKey(opt)) break findOpt; |
|
if (optlen == 0) break tryOpt; |
|
|
|
SortedMap<String,String[]> pfxmap = optmap.headMap(opt); |
|
|
|
int len = pfxmap.isEmpty() ? 0 : pfxmap.lastKey().length(); |
|
optlen = Math.min(len, optlen - 1); |
|
opt = arg.substring(0, optlen); |
|
// (Note: We could cut opt down to its common prefix with |
|
// pfxmap.lastKey, but that wouldn't save many cycles.) |
|
} |
|
opt = opt.intern(); |
|
assert(arg.startsWith(opt)); |
|
assert(opt.length() == optlen); |
|
String val = arg.substring(optlen); |
|
|
|
// Execute the option processing specs for this opt. |
|
|
|
boolean didAction = false; |
|
boolean isError = false; |
|
|
|
int pbpMark = pbp.nextIndex(); |
|
String[] specs = optmap.get(opt); |
|
eachSpec: |
|
for (String spec : specs) { |
|
if (spec.length() == 0) continue eachSpec; |
|
if (spec.startsWith("#")) break eachSpec; |
|
int sidx = 0; |
|
char specop = spec.charAt(sidx++); |
|
|
|
|
|
boolean ok; |
|
switch (specop) { |
|
case '+': |
|
|
|
ok = (val.length() != 0); |
|
specop = spec.charAt(sidx++); |
|
break; |
|
case '*': |
|
|
|
ok = true; |
|
specop = spec.charAt(sidx++); |
|
break; |
|
default: |
|
// No condition prefix means we require an exact |
|
|
|
ok = (val.length() == 0); |
|
break; |
|
} |
|
if (!ok) continue eachSpec; |
|
|
|
String specarg = spec.substring(sidx); |
|
switch (specop) { |
|
case '.': |
|
resultString = (specarg.length() != 0)? specarg.intern(): opt; |
|
break doArgs; |
|
case '?': |
|
resultString = (specarg.length() != 0)? specarg.intern(): arg; |
|
isError = true; |
|
break eachSpec; |
|
case '@': |
|
opt = specarg.intern(); |
|
break; |
|
case '>': |
|
pbp.add(specarg + val); |
|
val = ""; |
|
break; |
|
case '!': |
|
String negopt = (specarg.length() != 0)? specarg.intern(): opt; |
|
properties.remove(negopt); |
|
properties.put(negopt, null); |
|
didAction = true; |
|
break; |
|
case '$': |
|
String boolval; |
|
if (specarg.length() != 0) { |
|
|
|
boolval = specarg; |
|
} else { |
|
String old = properties.get(opt); |
|
if (old == null || old.length() == 0) { |
|
boolval = "1"; |
|
} else { |
|
|
|
boolval = ""+(1+Integer.parseInt(old)); |
|
} |
|
} |
|
properties.put(opt, boolval); |
|
didAction = true; |
|
break; |
|
case '=': |
|
case '&': |
|
|
|
boolean append = (specop == '&'); |
|
String strval; |
|
if (pbp.hasPrevious()) { |
|
strval = pbp.previous(); |
|
pbp.remove(); |
|
} else if (argp.hasNext()) { |
|
strval = argp.next(); |
|
} else { |
|
resultString = arg + " ?"; |
|
isError = true; |
|
break eachSpec; |
|
} |
|
if (append) { |
|
String old = properties.get(opt); |
|
if (old != null) { |
|
|
|
String delim = specarg; |
|
if (delim.length() == 0) delim = " "; |
|
strval = old + specarg + strval; |
|
} |
|
} |
|
properties.put(opt, strval); |
|
didAction = true; |
|
break; |
|
default: |
|
throw new RuntimeException(MessageFormat.format(RESOURCE.getString(DriverResource.BAD_SPEC),opt, spec)); |
|
} |
|
} |
|
|
|
|
|
if (didAction && !isError) { |
|
continue doArgs; |
|
} |
|
|
|
|
|
while (pbp.nextIndex() > pbpMark) { |
|
|
|
pbp.previous(); |
|
pbp.remove(); |
|
} |
|
|
|
if (isError) { |
|
throw new IllegalArgumentException(resultString); |
|
} |
|
|
|
if (optlen == 0) { |
|
|
|
break tryOpt; |
|
} |
|
} |
|
|
|
// If we come here, there was no matching option. |
|
|
|
pbp.add(arg); |
|
break doArgs; |
|
} |
|
|
|
args.subList(0, argp.nextIndex()).clear(); |
|
|
|
while (pbp.hasPrevious()) { |
|
args.add(0, pbp.previous()); |
|
} |
|
|
|
return resultString; |
|
} |
|
} |