package net.contrapunctus.rngzip; import gnu.getopt.Getopt; import gnu.getopt.LongOpt; import java.io.InputStream; import java.io.IOException; import java.io.OutputStream; import java.io.PrintStream; import net.contrapunctus.rngzip.io.RNGZSettings; public class Options { private static final String shortopts = "cDdE:fhikp::qS:s:T:tVvZ:"; private static LongOpt[] longopts = new LongOpt[] { new LongOpt("stdout", LongOpt.NO_ARGUMENT, null, 'c'), new LongOpt("debug", LongOpt.NO_ARGUMENT, null, 'D'), new LongOpt("decompress", LongOpt.NO_ARGUMENT, null, 'd'), new LongOpt("tree-encoder", LongOpt.REQUIRED_ARGUMENT, null, 'E'), new LongOpt("force", LongOpt.NO_ARGUMENT, null, 'f'), new LongOpt("help", LongOpt.NO_ARGUMENT, null, 'h'), new LongOpt("identify", LongOpt.NO_ARGUMENT, null, 'i'), new LongOpt("ignore-checksum", LongOpt.NO_ARGUMENT, null, 2 ), new LongOpt("keep", LongOpt.NO_ARGUMENT, null, 'k'), new LongOpt("pretty-print", LongOpt.OPTIONAL_ARGUMENT, null, 'p'), new LongOpt("quiet", LongOpt.NO_ARGUMENT, null, 'q'), new LongOpt("suffix", LongOpt.REQUIRED_ARGUMENT, null, 'S'), new LongOpt("schema", LongOpt.REQUIRED_ARGUMENT, null, 's'), new LongOpt("tree-compressor", LongOpt.REQUIRED_ARGUMENT, null, 'T'), new LongOpt("timings", LongOpt.NO_ARGUMENT, null, 't'), new LongOpt("version", LongOpt.NO_ARGUMENT, null, 'V'), new LongOpt("exact-version", LongOpt.NO_ARGUMENT, null, 3 ), new LongOpt("verbose", LongOpt.NO_ARGUMENT, null, 'v'), new LongOpt("data-compressor", LongOpt.REQUIRED_ARGUMENT, null, 'Z') }; private Getopt opt; private int curopt, errcount; private static PrintStream err = System.err; private final String myname; // identifier for this program RNGZSettings settings = new RNGZSettings(); boolean settings_p; // are any RNGZSettings changed from the defaults? boolean stdout_p; // write to standard out; don't touch files boolean debug_p; // trace compressor; replaces normal output boolean decompress_p; // decompress instead of compress boolean compress_p; boolean force_p; // force overwrite of output files boolean identify_p; // print information about .rnz files boolean ignore_sum_p; // decompress even if schema changed boolean keep_p; // do not remove input files boolean pretty_p; // line-break and indent XML output int pretty_tab = 2; // how far to indent? int verbosity = 1; // 0=errors only, 1=warnings, 2=stats&info String suffix = ".rnz"; // use this suffix on compressed files String schema; // use this schema (required to compress) boolean timings_p; // output timing information public Options (String myname) { this.myname = myname; } public int process (String[] args) { opt = new Getopt(myname, args, shortopts, longopts); curopt = opt.getopt(); while( curopt != -1 ) { handleCurrentOpt(); curopt = opt.getopt(); } int i = opt.getOptind(); check(i, args.length); if( errcount > 0 ) { System.exit(1); } opt = null; return i; } protected void check(int i, int n) { if( !decompress_p && !identify_p ) { // We ARE compressing compress_p = true; if( schema == null ) { err.printf("%s: error: schema must be specified when compressing%n", myname); errcount++; } } if( stdout_p && n > i+1 ) { err.printf("%s: error: specify at most one file "+ "at a time when using --stdout (-c)%n", myname); errcount++; } if( verbosity < 1 ) return; if( decompress_p || identify_p ) { // We are not compressing if( settings_p ) { err.printf("%s: warning: not compressing, so "+ "-E,-T,-Z will be ignored%n", myname); } } if( !decompress_p ) { // We are not decompressing final String msg = "%s: warning: not decompressing, so "; if( ignore_sum_p ) { err.printf(msg+"--ignore-checksum is irrelevant%n", myname); } if( pretty_p ) { err.printf(msg+"--pretty-print (-p) is irrelevant%n", myname); } } if( identify_p && !decompress_p ) { // We are identifying ONLY final String msg = "%s: warning: in identification mode (-i), "; if( schema != null ) { err.printf(msg+"--schema (-s) is irrelevant%n", myname); } if( force_p ) { err.printf(msg+"--force (-f) is irrelevant%n", myname); } if( timings_p ) { err.printf(msg+"--timings (-t) is irrelevant%n", myname); } } } protected void handleCurrentOpt() { switch( curopt ) { case '?': errcount++; break; case 'c': stdout_p = true; break; case 'D': debug_p = true; break; case 'd': decompress_p = true; break; case 'E': handleTreeEncoder(); break; case 'f': force_p = true; break; case 'h': showHelp(System.out); break; case 'i': identify_p = true; break; case 2 : ignore_sum_p = true; break; case 'k': keep_p = true; break; case 'p': handlePretty(); break; case 'q': verbosity--; break; case 'S': suffix = opt.getOptarg(); break; case 's': schema = opt.getOptarg(); break; case 'T': handleTreeCompressor(); break; case 't': timings_p = true; break; case 'V': showVersion(System.out); break; case 3 : showContext(System.out); break; case 'v': verbosity++; break; case 'Z': handleDataCompressor(); break; default: assert false : curopt; } } void handleTreeEncoder() { try { settings.setBitCoder(opt.getOptarg()); } catch(IllegalArgumentException x) { enumError(RNGZSettings.BitCoding.values()); } settings_p = true; } protected <T> void enumError(T[] vs) { reportInvalid(); err.print(" requires one of: "); for(T v : vs) { err.print(v.toString().toLowerCase()); err.print(' '); } err.println(); } protected void reportInvalid() { errcount++; err.printf("%s: invalid parameter: --%s", myname, longOptOf(curopt)); if( Character.isLetterOrDigit(curopt) ) { err.printf(" (-%c)", curopt); } } protected void invalid(String why) { reportInvalid(); err.printf(": %s%n", why); } protected String longOptOf (int o) { for( LongOpt lo : longopts ) { if( lo.getVal() == o ) { return lo.getName(); } } return null; } protected void handleTreeCompressor() { try { settings.setTreeCompressor(opt.getOptarg()); } catch(IllegalArgumentException x) { enumError(RNGZSettings.DataCompression.values()); } settings_p = true; } protected void handleDataCompressor() { try { settings.setDataCompressor(opt.getOptarg()); } catch(IllegalArgumentException x) { enumError(RNGZSettings.DataCompression.values()); } settings_p = true; } protected void handlePretty() { pretty_p = true; if(opt.getOptarg() != null) { try { pretty_tab = Integer.parseInt(opt.getOptarg()); } catch(NumberFormatException x) { invalid("requires an integer"); } } } protected void showHelp(PrintStream out) { out.printf("Usage: %s [options] [file ...]%n", myname); copyResource("help.txt", out); out.print("Coders: "); enumOptions(out, settings.DEFAULT_CODER, RNGZSettings.BitCoding.values()); out.print("Compressors: "); enumOptions(out, settings.DEFAULT_COMPRESSOR, RNGZSettings.DataCompression.values()); System.exit(0); } protected <T> void enumOptions(PrintStream out, T def, T[] vs) { for(T v : vs) { out.printf("%s%s ", v == def? "*" : "", v.toString().toLowerCase()); } out.println(); } protected static void copyResource (String name, OutputStream sink) { InputStream src = Options.class.getResourceAsStream(name); if( src == null ) { err.printf("error: %s is missing!%n", name); } else try { copyStream(src, sink); } catch(IOException x) { err.printf("error printing %s: %s%n", name, x); } } private static void copyStream (InputStream source, OutputStream sink) throws IOException { int k, SIZE = 128; byte[] buf = new byte [SIZE]; while ((k = source.read (buf, 0, SIZE)) != -1) { sink.write (buf, 0, k); } source.close(); } protected void showVersion(PrintStream out) { copyResource("version.txt", out); System.exit(0); } protected void showContext(PrintStream out) { copyResource("context.txt", out); System.exit(0); } }