Vendor `jopt-simple 5.0.3` .

This is necessary for vendoring jarjar, which is in turn necessary to
get Java coverage collection to work again.

Yak shaving for #2820.

Change-Id: Idfd45e7322e0763fd316a4f67b1a048c93bf54ba
diff --git a/third_party/java/jopt-simple/BUILD b/third_party/java/jopt-simple/BUILD
new file mode 100644
index 0000000..698edb3
--- /dev/null
+++ b/third_party/java/jopt-simple/BUILD
@@ -0,0 +1,7 @@
+licenses(["notice"])  # MIT
+
+java_library(
+    name = "jopt-simple",
+    srcs = glob(["src/main/**/*.java"]),
+    visibility = ["//visibility:public"],
+)
diff --git a/third_party/java/jopt-simple/LICENSE.txt b/third_party/java/jopt-simple/LICENSE.txt
new file mode 100644
index 0000000..85f923a
--- /dev/null
+++ b/third_party/java/jopt-simple/LICENSE.txt
@@ -0,0 +1,24 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
diff --git a/third_party/java/jopt-simple/README.bazel.md b/third_party/java/jopt-simple/README.bazel.md
new file mode 100644
index 0000000..9985e82
--- /dev/null
+++ b/third_party/java/jopt-simple/README.bazel.md
@@ -0,0 +1,6 @@
+This is jopt-simple 5.0.3. Reproduction:
+
+1. `git clone https://github.com/jopt-simple/jopt-simple`
+2. `git checkout jopt-simple-5.0.3` (commithash `b52b94611dba126a8236a10277d15676e31ab5f6`)
+3. Dump the contents of the `git` tree into `third_party/java/jopt-simple`
+4. Keep the existing, trivial `BUILD` file
diff --git a/third_party/java/jopt-simple/README.md b/third_party/java/jopt-simple/README.md
new file mode 100644
index 0000000..ffe1ce2
--- /dev/null
+++ b/third_party/java/jopt-simple/README.md
@@ -0,0 +1,7 @@
+[![Build Status](https://travis-ci.org/pholser/jopt-simple.svg?branch=master)](https://travis-ci.org/pholser/jopt-simple)
+
+Please see http://pholser.github.com/jopt-simple for more information.
+
+### Discussing
+
+There is [a Google group for JOpt Simple](https://groups.google.com/d/forum/jopt-simple).
diff --git a/third_party/java/jopt-simple/jopt-simple-eclipse-formatting.xml b/third_party/java/jopt-simple/jopt-simple-eclipse-formatting.xml
new file mode 100644
index 0000000..e4b2eb8
--- /dev/null
+++ b/third_party/java/jopt-simple/jopt-simple-eclipse-formatting.xml
@@ -0,0 +1,283 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<profiles version="12">
+<profile kind="CodeFormatterProfile" name="jopt-simple" version="12">
+<setting id="org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.disabling_tag" value="@formatter:off"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_field" value="0"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.use_on_off_tags" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_ellipsis" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_multiple_fields" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_conditional_expression" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_binary_operator" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_array_initializer" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_after_package" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.continuation_indentation" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_binary_operator" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_package" value="0"/>
+<setting id="org.eclipse.jdt.core.compiler.source" value="1.5"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.format_line_comments" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.join_wrapped_lines" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.align_type_members_on_columns" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_member_type" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_unary_operator" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.indent_parameter_description" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.lineSplit" value="120"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration" value="0"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.indentation.size" value="4"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.enabling_tag" value="@formatter:on"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_assignment" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.tabulation.char" value="space"/>
+<setting id="org.eclipse.jdt.core.compiler.problem.assertIdentifier" value="error"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_statements_compare_to_body" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_method" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_method_declaration" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_switch" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch" value="true"/>
+<setting id="org.eclipse.jdt.core.compiler.problem.enumIdentifier" value="error"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_block" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_ellipsis" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_method_declaration" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.compact_else_if" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.indent_root_tags" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_enum_constant" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.tabulation.size" value="4"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_empty_lines" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_block_in_case" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression" value="16"/>
+<setting id="org.eclipse.jdt.core.compiler.compliance" value="1.5"/>
+<setting id="org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_unary_operator" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_binary_expression" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type" value="insert"/>
+<setting id="org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode" value="enabled"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_label" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.format_javadoc_comments" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.line_length" value="120"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_between_import_groups" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body" value="0"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.wrap_before_binary_operator" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_statements_compare_to_block" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.join_lines_in_comments" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_compact_if" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_imports" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.format_html" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.format_source_code" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer" value="insert"/>
+<setting id="org.eclipse.jdt.core.compiler.codegen.targetPlatform" value="1.5"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.format_header" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.format_block_comments" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_enum_constants" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_type_declaration" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_after_imports" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column" value="false"/>
+</profile>
+</profiles>
diff --git a/third_party/java/jopt-simple/jopt-simple.importorder b/third_party/java/jopt-simple/jopt-simple.importorder
new file mode 100644
index 0000000..e94bb81
--- /dev/null
+++ b/third_party/java/jopt-simple/jopt-simple.importorder
@@ -0,0 +1,8 @@
+#Organize Import Order
+#Tue Oct 18 14:21:56 CDT 2011
+5=\#
+4=\#javax
+3=\#java
+2=
+1=javax
+0=java
diff --git a/third_party/java/jopt-simple/paste-examples.xml b/third_party/java/jopt-simple/paste-examples.xml
new file mode 100644
index 0000000..e1b6254
--- /dev/null
+++ b/third_party/java/jopt-simple/paste-examples.xml
@@ -0,0 +1,205 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project>
+    <target name="-paste-examples">
+        <filterchain id="htmlFilter">
+            <filterreader
+                classname="joptsimple.examples.ant.filters.HTMLEntitifier"
+                classpath="${example.class.path}"/>
+        </filterchain>
+        <loadfile property="short.options.example"
+            srcfile="${example.src.dir}/joptsimple/examples/ShortOptionsTest.java">
+            <filterchain refid="htmlFilter"/>
+        </loadfile>
+        <loadfile property="short.options.with.arguments.example"
+            srcfile="${example.src.dir}/joptsimple/examples/ShortOptionsWithArgumentsTest.java">
+            <filterchain refid="htmlFilter"/>
+        </loadfile>
+        <loadfile property="short.options.with.argument.positioning.example"
+            srcfile="${example.src.dir}/joptsimple/examples/ShortOptionsWithArgumentPositioningTest.java">
+            <filterchain refid="htmlFilter"/>
+        </loadfile>
+        <loadfile property="short.options.with.multiple.arguments.for.single.option.example"
+            srcfile="${example.src.dir}/joptsimple/examples/ShortOptionsWithMultipleArgumentsForSingleOptionTest.java">
+            <filterchain refid="htmlFilter"/>
+        </loadfile>
+        <loadfile property="short.options.clustering.example"
+            srcfile="${example.src.dir}/joptsimple/examples/ShortOptionsClusteringTest.java">
+            <filterchain refid="htmlFilter"/>
+        </loadfile>
+        <loadfile property="short.options.clustering.with.argument.example"
+           srcfile="${example.src.dir}/joptsimple/examples/ShortOptionsClusteringWithArgumentTest.java">
+           <filterchain refid="htmlFilter"/>
+        </loadfile>
+        <loadfile property="long.options.example"
+            srcfile="${example.src.dir}/joptsimple/examples/LongOptionsTest.java">
+            <filterchain refid="htmlFilter"/>
+        </loadfile>
+        <loadfile property="long.options.with.arguments.example"
+            srcfile="${example.src.dir}/joptsimple/examples/LongOptionsWithArgumentsTest.java">
+            <filterchain refid="htmlFilter"/>
+        </loadfile>
+        <loadfile property="long.options.with.argument.positioning.example"
+            srcfile="${example.src.dir}/joptsimple/examples/LongOptionsWithArgumentPositioningTest.java">
+            <filterchain refid="htmlFilter"/>
+        </loadfile>
+        <loadfile property="alternative.long.options.example"
+            srcfile="${example.src.dir}/joptsimple/examples/AlternativeLongOptionsTest.java">
+            <filterchain refid="htmlFilter"/>
+        </loadfile>
+        <loadfile property="option.argument.value.type.example"
+            srcfile="${example.src.dir}/joptsimple/examples/OptionArgumentValueTypeTest.java">
+            <filterchain refid="htmlFilter"/>
+        </loadfile>
+        <loadfile property="option.argument.converter.example"
+            srcfile="${example.src.dir}/joptsimple/examples/OptionArgumentConverterTest.java">
+            <filterchain refid="htmlFilter"/>
+        </loadfile>
+        <loadfile property="typesafe.option.argument.retrieval.example"
+            srcfile="${example.src.dir}/joptsimple/examples/TypesafeOptionArgumentRetrievalTest.java">
+            <filterchain refid="htmlFilter"/>
+        </loadfile>
+        <loadfile property="typesafe.option.argument.retrieval.example"
+            srcfile="${example.src.dir}/joptsimple/examples/TypesafeOptionArgumentRetrievalTest.java">
+            <filterchain refid="htmlFilter"/>
+        </loadfile>
+        <loadfile property="export.options.example"
+            srcfile="${example.src.dir}/joptsimple/examples/ExportOptionsTest.java">
+            <filterchain refid="htmlFilter"/>
+        </loadfile>
+        <loadfile property="required.options.example"
+            srcfile="${example.src.dir}/joptsimple/examples/RequiredOptionsTest.java">
+            <filterchain refid="htmlFilter"/>
+        </loadfile>
+        <loadfile property="required.if.options.example"
+            srcfile="${example.src.dir}/joptsimple/examples/RequiredIfExample.java">
+            <filterchain refid="htmlFilter"/>
+        </loadfile>
+        <loadfile property="required.unless.options.example"
+            srcfile="${example.src.dir}/joptsimple/examples/RequiredUnlessExample.java">
+            <filterchain refid="htmlFilter"/>
+        </loadfile>
+        <loadfile property="default.values.for.option.arguments.example"
+            srcfile="${example.src.dir}/joptsimple/examples/DefaultValuesForOptionArgumentsTest.java">
+            <filterchain refid="htmlFilter"/>
+        </loadfile>
+        <loadfile property="option.synonym.example"
+            srcfile="${example.src.dir}/joptsimple/examples/OptionSynonymTest.java">
+            <filterchain refid="htmlFilter"/>
+        </loadfile>
+        <loadfile property="multiple.delimited.arguments.example"
+            srcfile="${example.src.dir}/joptsimple/examples/MultipleDelimitedArgumentsTest.java">
+            <filterchain refid="htmlFilter"/>
+        </loadfile>
+        <loadfile property="signalling.end.of.options.example"
+            srcfile="${example.src.dir}/joptsimple/examples/SignallingEndOfOptionsTest.java">
+            <filterchain refid="htmlFilter"/>
+        </loadfile>
+        <loadfile property="posixly.correct.example"
+            srcfile="${example.src.dir}/joptsimple/examples/PosixlyCorrectTest.java">
+            <filterchain refid="htmlFilter"/>
+        </loadfile>
+        <loadfile property="special.optional.argument.handling.example"
+            srcfile="${example.src.dir}/joptsimple/examples/SpecialOptionalArgumentHandlingTest.java">
+            <filterchain refid="htmlFilter"/>
+        </loadfile>
+        <loadfile property="help.screen.example"
+            srcfile="${example.src.dir}/joptsimple/examples/HelpScreenExample.java">
+            <filterchain refid="htmlFilter"/>
+        </loadfile>
+        <java classname="joptsimple.examples.HelpScreenExample"
+            fork="yes"
+            output="${java.io.tmpdir}/help-output.txt"
+            classpath="${example.class.path}">
+        </java>
+        <loadfile property="help.screen.example.help"
+            srcfile="${java.io.tmpdir}/help-output.txt">
+            <filterchain refid="htmlFilter"/>
+        </loadfile>
+        <loadfile property="help.formatter.example"
+            srcfile="${example.src.dir}/joptsimple/examples/HelpFormatterExample.java">
+            <filterchain refid="htmlFilter"/>
+        </loadfile>
+        <java classname="joptsimple.examples.HelpFormatterExample"
+            fork="yes"
+            output="${java.io.tmpdir}/help-formatter-output.txt"
+            classpath="${example.class.path}">
+        </java>
+        <loadfile property="help.formatter.example.output"
+            srcfile="${java.io.tmpdir}/help-formatter-output.txt">
+            <filterchain refid="htmlFilter"/>
+        </loadfile>
+        <java classname="joptsimple.examples.ExceptionExample"
+            fork="yes"
+            output="${java.io.tmpdir}/stack-trace-output.txt"
+            classpath="${example.class.path}">
+        </java>
+        <loadfile property="example.stack.trace"
+            srcfile="${java.io.tmpdir}/stack-trace-output.txt">
+            <filterchain refid="htmlFilter"/>
+        </loadfile>
+        <loadfile property="unrecognized.options.allowed.example"
+            srcfile="${example.src.dir}/joptsimple/examples/UnrecognizedOptionsAllowedTest.java">
+            <filterchain refid="htmlFilter"/>
+        </loadfile>
+        <replace dir="${website.staging.dir}" includes="examples.html">
+            <replacefilter token="@SHORT_OPTIONS_EXAMPLE@"
+                value="${short.options.example}"/>
+            <replacefilter token="@SHORT_OPTIONS_WITH_ARGUMENTS_EXAMPLE@"
+                value="${short.options.with.arguments.example}"/>
+            <replacefilter token="@SHORT_OPTIONS_WITH_ARGUMENT_POSITIONING_EXAMPLE@"
+                value="${short.options.with.argument.positioning.example}"/>
+            <replacefilter token="@SHORT_OPTIONS_WITH_MULTIPLE_ARGUMENTS_FOR_SINGLE_OPTION_EXAMPLE@"
+                value="${short.options.with.multiple.arguments.for.single.option.example}"/>
+            <replacefilter token="@SHORT_OPTIONS_CLUSTERING_EXAMPLE@"
+                value="${short.options.clustering.example}"/>
+            <replacefilter token="@SHORT_OPTIONS_CLUSTERING_WITH_ARGUMENT_EXAMPLE@"
+                value="${short.options.clustering.with.argument.example}" />
+            <replacefilter token="@LONG_OPTIONS_EXAMPLE@"
+                value="${long.options.example}"/>
+            <replacefilter token="@LONG_OPTIONS_WITH_ARGUMENTS_EXAMPLE@"
+                value="${long.options.with.arguments.example}"/>
+            <replacefilter token="@LONG_OPTIONS_WITH_ARGUMENT_POSITIONING_EXAMPLE@"
+                value="${long.options.with.argument.positioning.example}"/>
+            <replacefilter token="@ALTERNATIVE_LONG_OPTIONS_EXAMPLE@"
+                value="${alternative.long.options.example}"/>
+            <replacefilter token="@OPTION_ARGUMENT_VALUE_TYPE_EXAMPLE@"
+                value="${option.argument.value.type.example}"/>
+            <replacefilter token="@OPTION_ARGUMENT_CONVERTER_EXAMPLE@"
+                value="${option.argument.converter.example}"/>
+            <replacefilter token="@TYPESAFE_OPTION_ARGUMENT_RETRIEVAL_EXAMPLE@"
+                value="${typesafe.option.argument.retrieval.example}"/>
+            <replacefilter token="@EXPORTING_OPTIONS_AND_ARGUMENTS_EXAMPLE@"
+                value="${export.options.example}"/>
+            <replacefilter token="@DEFAULT_VALUES_FOR_OPTION_ARGUMENTS_EXAMPLE@"
+                value="${default.values.for.option.arguments.example}"/>
+            <replacefilter token="@REQUIRED_OPTIONS_EXAMPLE@"
+                value="${required.options.example}"/>
+            <replacefilter token="@REQUIRED_IF_OPTIONS_EXAMPLE@"
+                value="${required.if.options.example}"/>
+            <replacefilter token="@REQUIRED_UNLESS_OPTIONS_EXAMPLE@"
+                value="${required.unless.options.example}"/>
+            <replacefilter token="@OPTION_SYNONYM_EXAMPLE@"
+                value="${option.synonym.example}"/>
+            <replacefilter token="@MULTIPLE_DELIMITED_ARGUMENTS_EXAMPLE@"
+                value="${multiple.delimited.arguments.example}"/>
+            <replacefilter token="@SIGNALLING_END_OF_OPTIONS_EXAMPLE@"
+                value="${signalling.end.of.options.example}"/>
+            <replacefilter token="@POSIXLY_CORRECT_EXAMPLE@"
+                value="${posixly.correct.example}"/>
+            <replacefilter token="@SPECIAL_OPTIONAL_ARGUMENT_HANDLING_EXAMPLE@"
+                value="${special.optional.argument.handling.example}"/>
+            <replacefilter token="@HELP_SCREEN_EXAMPLE@"
+                value="${help.screen.example}"/>
+            <replacefilter token="@HELP_SCREEN_EXAMPLE_HELP@"
+                value="${help.screen.example.help}"/>
+            <replacefilter token="@HELP_FORMATTER_EXAMPLE@"
+                value="${help.formatter.example}"/>
+            <replacefilter token="@HELP_FORMATTER_EXAMPLE_OUTPUT@"
+                value="${help.formatter.example.output}"/>
+            <replacefilter token="@EXAMPLE_STACK_TRACE@"
+                value="${example.stack.trace}"/>
+            <replacefilter token="@UNRECOGNIZED_OPTIONS_ALLOWED_EXAMPLE@"
+                value="${unrecognized.options.allowed.example}"/>
+        </replace>
+    </target>
+</project>
diff --git a/third_party/java/jopt-simple/pom.xml b/third_party/java/jopt-simple/pom.xml
new file mode 100644
index 0000000..c2fc280
--- /dev/null
+++ b/third_party/java/jopt-simple/pom.xml
@@ -0,0 +1,342 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.sonatype.oss</groupId>
+        <artifactId>oss-parent</artifactId>
+        <version>7</version>
+    </parent>
+    <groupId>net.sf.jopt-simple</groupId>
+    <artifactId>jopt-simple</artifactId>
+    <version>5.0.3</version>
+    <packaging>jar</packaging>
+    <name>JOpt Simple</name>
+    <url>http://pholser.github.io/jopt-simple</url>
+    <description>A Java library for parsing command line options</description>
+    <licenses>
+        <license>
+            <name>The MIT License</name>
+            <url>http://www.opensource.org/licenses/mit-license.php</url>
+            <distribution>repo</distribution>
+        </license>
+    </licenses>
+    <scm>
+        <connection>scm:git:git://github.com/pholser/jopt-simple.git</connection>
+        <developerConnection>scm:git:ssh://git@github.com/pholser/jopt-simple.git</developerConnection>
+        <url>https://github.com/pholser/jopt-simple</url>
+    </scm>
+    <issueManagement>
+        <system>GitHub</system>
+        <url>https://github.com/pholser/jopt-simple/issues</url>
+    </issueManagement>
+    <developers>
+        <developer>
+            <id>pholser</id>
+            <name>Paul Holser</name>
+            <email>pholser@alumni.rice.edu</email>
+            <url>http://www.pholser.com</url>
+        </developer>
+    </developers>
+    <properties>
+        <project.build.outputEncoding>UTF-8</project.build.outputEncoding>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <project.project.outputEncoding>UTF-8</project.project.outputEncoding>
+    </properties>
+    <dependencies>
+        <dependency>
+            <groupId>joda-time</groupId>
+            <artifactId>joda-time</artifactId>
+            <version>2.3</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.ant</groupId>
+            <artifactId>ant</artifactId>
+            <version>1.8.4</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.hamcrest</groupId>
+            <artifactId>hamcrest-all</artifactId>
+            <version>1.3</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>4.12</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.infinitest</groupId>
+            <artifactId>continuous-testing-toolkit</artifactId>
+            <version>1.0</version>
+            <scope>test</scope>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.hamcrest</groupId>
+                    <artifactId>hamcrest-all</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+    </dependencies>
+    <build>
+        <extensions>
+            <extension>
+                <groupId>org.apache.maven.scm</groupId>
+                <artifactId>maven-scm-provider-gitexe</artifactId>
+                <version>1.9.2</version>
+            </extension>
+            <extension>
+                <groupId>org.apache.maven.scm</groupId>
+                <artifactId>maven-scm-manager-plexus</artifactId>
+                <version>1.9.2</version>
+            </extension>
+            <extension>
+                <groupId>org.kathrynhuxtable.maven.wagon</groupId>
+                <artifactId>wagon-gitsite</artifactId>
+                <version>0.3.1</version>
+            </extension>
+        </extensions>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <version>3.2</version>
+                <configuration>
+                    <source>1.7</source>
+                    <target>1.7</target>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>animal-sniffer-maven-plugin</artifactId>
+                <version>1.13</version>
+                <configuration>
+                    <signature>
+                        <groupId>org.codehaus.mojo.signature</groupId>
+                        <artifactId>java17</artifactId>
+                        <version>1.0</version>
+                    </signature>
+                </configuration>
+                <executions>
+                    <execution>
+                        <id>check-signature</id>
+                        <goals>
+                            <goal>check</goal>
+                        </goals>
+                    </execution>
+                    <!-- Needs updated version of animal sniffer plugin
+                    <execution>
+                        <id>check-test-signature</id>
+                        <goals>
+                            <goal>test-check</goal>
+                        </goals>
+                    </execution>
+                    -->
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <version>2.3.7</version>
+                <extensions>true</extensions>
+                <configuration>
+                    <instructions>
+                        <Bundle-SymbolicName>${project.groupId}.${project.artifactId}</Bundle-SymbolicName>
+                        <Bundle-Name>${project.artifactId}</Bundle-Name>
+                        <Bundle-Version>${project.version}</Bundle-Version>
+                        <Bundle-Vendor>${project.name}</Bundle-Vendor>
+                        <Export-Package>joptsimple,joptsimple.util</Export-Package>
+                        <Private-Package>joptsimple.internal</Private-Package>
+                    </instructions>
+                </configuration>
+                <executions>
+                    <execution>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>bundle</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>cobertura-maven-plugin</artifactId>
+                <version>2.6</version>
+                <configuration>
+                    <check>
+                        <haltOnFailure>true</haltOnFailure>
+                        <totalBranchRate>96</totalBranchRate>
+                        <totalLineRate>99</totalLineRate>
+                    </check>
+                </configuration>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>clean</goal>
+                            <goal>check</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-pmd-plugin</artifactId>
+                <version>3.4</version>
+                <configuration>
+                    <minimumTokens>40</minimumTokens>
+                    <targetJdk>1.7</targetJdk>
+                </configuration>
+                <executions>
+                    <execution>
+                        <phase>compile</phase>
+                        <goals>
+                            <goal>cpd-check</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-antrun-plugin</artifactId>
+                <version>1.8</version>
+                <executions>
+                    <execution>
+                        <id>paste-examples</id>
+                        <phase>post-site</phase>
+                        <configuration>
+                            <target>
+                                <property name="java.io.tmpdir" value="${java.io.tmpdir}" />
+                                <property name="example.src.dir" value="src/test/java" />
+                                <property name="example.class.path" refid="maven.test.classpath" />
+                                <property name="website.staging.dir" value="${project.build.directory}/site" />
+                                <ant antfile="paste-examples.xml" target="-paste-examples" />
+                            </target>
+                        </configuration>
+                        <goals>
+                            <goal>run</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <artifactId>maven-site-plugin</artifactId>
+                <version>3.3</version>
+                <configuration>
+                    <outputEncoding>UTF-8</outputEncoding>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.pitest</groupId>
+                <artifactId>pitest-maven</artifactId>
+                <version>1.1.9</version>
+                <configuration>
+                    <targetClasses>
+                        <param>joptsimple*</param>
+                    </targetClasses>
+                    <targetTests>
+                        <param>*Test</param>
+                    </targetTests>
+                    <excludedMethods>
+                        <param>equals</param>
+                        <param>hashCode</param>
+                    </excludedMethods>
+                    <excludedClasses>
+                        <param>joptsimple.examples*</param>
+                        <param>*EqualsHash*Test</param>
+                        <param>*TestCase</param>
+                        <param>*Fixture</param>
+                        <param>*Harness</param>
+                        <param>*Matchers</param>
+                        <param>*Ctor</param>
+                        <param>*Problematic</param>
+                        <param>*ValueOfHaver</param>
+                    </excludedClasses>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-source-plugin</artifactId>
+                <version>2.4</version>
+                <executions>
+                    <execution>
+                       <id>attach-sources</id>
+                       <phase>package</phase>
+                       <goals>
+                         <goal>jar</goal>
+                       </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+    <reporting>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-project-info-reports-plugin</artifactId>
+                <version>2.8</version>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-javadoc-plugin</artifactId>
+                <version>2.9.1</version>
+                <configuration>
+                    <source>1.7</source>
+                    <show>public</show>
+                    <excludePackageNames>joptsimple.examples:joptsimple.internal:joptsimple.internal.*</excludePackageNames>
+                    <links>
+                        <link>https://docs.oracle.com/javase/8/docs/api</link>
+                    </links>
+                    <bottom><![CDATA[<i>&copy; Copyright 2004-2015 Paul R. Holser, Jr.  All rights reserved. Licensed under The MIT License. pholser@alumni.rice.edu</i>]]></bottom>
+                </configuration>
+                <reportSets>
+                    <reportSet>
+                        <reports>
+                            <report>javadoc</report>
+                        </reports>
+                    </reportSet>
+                </reportSets>
+            </plugin>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>cobertura-maven-plugin</artifactId>
+                <version>2.6</version>
+                <configuration>
+                    <check>
+                        <haltOnFailure>true</haltOnFailure>
+                        <totalBranchRate>97</totalBranchRate>
+                        <totalLineRate>99</totalLineRate>
+                    </check>
+                    <formats>
+                        <format>xml</format>
+                        <format>html</format>
+                    </formats>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>findbugs-maven-plugin</artifactId>
+                <version>3.0.0</version>
+                <configuration>
+                    <threshold>Low</threshold>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-pmd-plugin</artifactId>
+                <version>3.4</version>
+                <configuration>
+                    <minimumTokens>40</minimumTokens>
+                    <targetJdk>1.7</targetJdk>
+                </configuration>
+            </plugin>
+        </plugins>
+    </reporting>
+    <distributionManagement>
+        <site>
+            <id>github-project-site</id>
+            <url>gitsite:git@github.com/pholser/jopt-simple.git</url>
+        </site>
+    </distributionManagement>
+</project>
diff --git a/third_party/java/jopt-simple/src/main/java/joptsimple/AbstractOptionSpec.java b/third_party/java/jopt-simple/src/main/java/joptsimple/AbstractOptionSpec.java
new file mode 100644
index 0000000..2c045bd
--- /dev/null
+++ b/third_party/java/jopt-simple/src/main/java/joptsimple/AbstractOptionSpec.java
@@ -0,0 +1,148 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static java.util.Collections.*;
+
+import joptsimple.internal.Reflection;
+import joptsimple.internal.ReflectionException;
+
+import static joptsimple.internal.Strings.*;
+
+/**
+ * @param <V> represents the type of the arguments this option accepts
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public abstract class AbstractOptionSpec<V> implements OptionSpec<V>, OptionDescriptor {
+    private final List<String> options = new ArrayList<>();
+    private final String description;
+    private boolean forHelp;
+
+    AbstractOptionSpec( String option ) {
+        this( singletonList( option ), EMPTY );
+    }
+
+    AbstractOptionSpec( List<String> options, String description ) {
+        arrangeOptions( options );
+
+        this.description = description;
+    }
+
+    public final List<String> options() {
+        return unmodifiableList( options );
+    }
+
+    public final List<V> values( OptionSet detectedOptions ) {
+        return detectedOptions.valuesOf( this );
+    }
+
+    public final V value( OptionSet detectedOptions ) {
+        return detectedOptions.valueOf( this );
+    }
+
+    public String description() {
+        return description;
+    }
+
+    public final AbstractOptionSpec<V> forHelp() {
+        forHelp = true;
+        return this;
+    }
+
+    public final boolean isForHelp() {
+        return forHelp;
+    }
+
+    public boolean representsNonOptions() {
+        return false;
+    }
+
+    protected abstract V convert( String argument );
+
+    protected V convertWith( ValueConverter<V> converter, String argument ) {
+        try {
+            return Reflection.convertWith( converter, argument );
+        } catch ( ReflectionException | ValueConversionException ex ) {
+            throw new OptionArgumentConversionException( this, argument, ex );
+        }
+    }
+
+    protected String argumentTypeIndicatorFrom( ValueConverter<V> converter ) {
+        if ( converter == null )
+            return null;
+
+        String pattern = converter.valuePattern();
+        return pattern == null ? converter.valueType().getName() : pattern;
+    }
+
+    abstract void handleOption( OptionParser parser, ArgumentList arguments, OptionSet detectedOptions,
+        String detectedArgument );
+
+    private void arrangeOptions( List<String> unarranged ) {
+        if ( unarranged.size() == 1 ) {
+            options.addAll( unarranged );
+            return;
+        }
+
+        List<String> shortOptions = new ArrayList<>();
+        List<String> longOptions = new ArrayList<>();
+
+        for ( String each : unarranged ) {
+            if ( each.length() == 1 )
+                shortOptions.add( each );
+            else
+                longOptions.add( each );
+        }
+
+        sort( shortOptions );
+        sort( longOptions );
+
+        options.addAll( shortOptions );
+        options.addAll( longOptions );
+    }
+
+    @Override
+    public boolean equals( Object that ) {
+        if ( !( that instanceof AbstractOptionSpec<?> ) )
+            return false;
+
+        AbstractOptionSpec<?> other = (AbstractOptionSpec<?>) that;
+        return options.equals( other.options );
+    }
+
+    @Override
+    public int hashCode() {
+        return options.hashCode();
+    }
+
+    @Override
+    public String toString() {
+        return options.toString();
+    }
+}
diff --git a/third_party/java/jopt-simple/src/main/java/joptsimple/AlternativeLongOptionSpec.java b/third_party/java/jopt-simple/src/main/java/joptsimple/AlternativeLongOptionSpec.java
new file mode 100644
index 0000000..a3108c1
--- /dev/null
+++ b/third_party/java/jopt-simple/src/main/java/joptsimple/AlternativeLongOptionSpec.java
@@ -0,0 +1,65 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import joptsimple.internal.Messages;
+
+import java.util.Locale;
+
+import static java.util.Collections.*;
+
+import static joptsimple.ParserRules.*;
+
+/**
+ * Represents the {@code "-W"} form of long option specification.
+ *
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+class AlternativeLongOptionSpec extends ArgumentAcceptingOptionSpec<String> {
+    AlternativeLongOptionSpec() {
+        super( singletonList( RESERVED_FOR_EXTENSIONS ),
+            true,
+            Messages.message(
+                Locale.getDefault(),
+                "joptsimple.HelpFormatterMessages",
+                AlternativeLongOptionSpec.class,
+                "description" ) );
+
+        describedAs( Messages.message(
+            Locale.getDefault(),
+            "joptsimple.HelpFormatterMessages",
+            AlternativeLongOptionSpec.class,
+            "arg.description" ) );
+    }
+
+    @Override
+    protected void detectOptionArgument( OptionParser parser, ArgumentList arguments, OptionSet detectedOptions ) {
+        if ( !arguments.hasMore() )
+            throw new OptionMissingRequiredArgumentException( this );
+
+        arguments.treatNextAsLongOption();
+    }
+}
diff --git a/third_party/java/jopt-simple/src/main/java/joptsimple/ArgumentAcceptingOptionSpec.java b/third_party/java/jopt-simple/src/main/java/joptsimple/ArgumentAcceptingOptionSpec.java
new file mode 100644
index 0000000..ca4c59f
--- /dev/null
+++ b/third_party/java/jopt-simple/src/main/java/joptsimple/ArgumentAcceptingOptionSpec.java
@@ -0,0 +1,329 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.StringTokenizer;
+
+import static java.util.Collections.*;
+import static java.util.Objects.*;
+
+import static joptsimple.internal.Reflection.*;
+import static joptsimple.internal.Strings.*;
+
+/**
+ * <p>Specification of an option that accepts an argument.</p>
+ *
+ * <p>Instances are returned from {@link OptionSpecBuilder} methods to allow the formation of parser directives as
+ * sentences in a "fluent interface" language. For example:</p>
+ *
+ * <pre>
+ *   <code>
+ *   OptionParser parser = new OptionParser();
+ *   parser.accepts( "c" ).withRequiredArg().<strong>ofType( Integer.class )</strong>;
+ *   </code>
+ * </pre>
+ *
+ * <p>If no methods are invoked on an instance of this class, then that instance's option will treat its argument as
+ * a {@link String}.</p>
+ *
+ * @param <V> represents the type of the arguments this option accepts
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public abstract class ArgumentAcceptingOptionSpec<V> extends AbstractOptionSpec<V> {
+    private static final char NIL_VALUE_SEPARATOR = '\u0000';
+
+    private final boolean argumentRequired;
+    private final List<V> defaultValues = new ArrayList<>();
+
+    private boolean optionRequired;
+    private ValueConverter<V> converter;
+    private String argumentDescription = "";
+    private String valueSeparator = String.valueOf( NIL_VALUE_SEPARATOR );
+
+    ArgumentAcceptingOptionSpec( String option, boolean argumentRequired ) {
+        super( option );
+
+        this.argumentRequired = argumentRequired;
+    }
+
+    ArgumentAcceptingOptionSpec( List<String> options, boolean argumentRequired, String description ) {
+        super( options, description );
+
+        this.argumentRequired = argumentRequired;
+    }
+
+    /**
+     * <p>Specifies a type to which arguments of this spec's option are to be converted.</p>
+     *
+     * <p>JOpt Simple accepts types that have either:</p>
+     *
+     * <ol>
+     *   <li>a public static method called {@code valueOf} which accepts a single argument of type {@link String}
+     *   and whose return type is the same as the class on which the method is declared.  The {@code java.lang}
+     *   primitive wrapper classes have such methods.</li>
+     *
+     *   <li>a public constructor which accepts a single argument of type {@link String}.</li>
+     * </ol>
+     *
+     * <p>This class converts arguments using those methods in that order; that is, {@code valueOf} would be invoked
+     * before a one-{@link String}-arg constructor would.</p>
+     *
+     * <p>Invoking this method will trump any previous calls to this method or to
+     * {@link #withValuesConvertedBy(ValueConverter)}.</p>
+     *
+     * @param <T> represents the runtime class of the desired option argument type
+     * @param argumentType desired type of arguments to this spec's option
+     * @return self, so that the caller can add clauses to the fluent interface sentence
+     * @throws NullPointerException if the type is {@code null}
+     * @throws IllegalArgumentException if the type does not have the standard conversion methods
+     */
+    public final <T> ArgumentAcceptingOptionSpec<T> ofType( Class<T> argumentType ) {
+        return withValuesConvertedBy( findConverter( argumentType ) );
+    }
+
+    /**
+     * <p>Specifies a converter to use to translate arguments of this spec's option into Java objects.  This is useful
+     * when converting to types that do not have the requisite factory method or constructor for
+     * {@link #ofType(Class)}.</p>
+     *
+     * <p>Invoking this method will trump any previous calls to this method or to {@link #ofType(Class)}.
+     *
+     * @param <T> represents the runtime class of the desired option argument type
+     * @param aConverter the converter to use
+     * @return self, so that the caller can add clauses to the fluent interface sentence
+     * @throws NullPointerException if the converter is {@code null}
+     */
+    @SuppressWarnings( "unchecked" )
+    public final <T> ArgumentAcceptingOptionSpec<T> withValuesConvertedBy( ValueConverter<T> aConverter ) {
+        if ( aConverter == null )
+            throw new NullPointerException( "illegal null converter" );
+
+        converter = (ValueConverter<V>) aConverter;
+        return (ArgumentAcceptingOptionSpec<T>) this;
+    }
+
+    /**
+     * <p>Specifies a description for the argument of the option that this spec represents.  This description is used
+     * when generating help information about the parser.</p>
+     *
+     * @param description describes the nature of the argument of this spec's option
+     * @return self, so that the caller can add clauses to the fluent interface sentence
+     */
+    public final ArgumentAcceptingOptionSpec<V> describedAs( String description ) {
+        argumentDescription = description;
+        return this;
+    }
+
+    /**
+     * <p>Specifies a value separator for the argument of the option that this spec represents.  This allows a single
+     * option argument to represent multiple values for the option.  For example:</p>
+     *
+     * <pre>
+     *   <code>
+     *   parser.accepts( "z" ).withRequiredArg()
+     *       .<strong>withValuesSeparatedBy( ',' )</strong>;
+     *   OptionSet options = parser.parse( new String[] { "-z", "foo,bar,baz", "-z",
+     *       "fizz", "-z", "buzz" } );
+     *   </code>
+     * </pre>
+     *
+     * <p>Then <code>options.valuesOf( "z" )</code> would yield the list {@code [foo, bar, baz, fizz, buzz]}.</p>
+     *
+     * <p>You cannot use Unicode U+0000 as the separator.</p>
+     *
+     * @param separator a character separator
+     * @return self, so that the caller can add clauses to the fluent interface sentence
+     * @throws IllegalArgumentException if the separator is Unicode U+0000
+     */
+    public final ArgumentAcceptingOptionSpec<V> withValuesSeparatedBy( char separator ) {
+        if ( separator == NIL_VALUE_SEPARATOR )
+            throw new IllegalArgumentException( "cannot use U+0000 as separator" );
+
+        valueSeparator = String.valueOf( separator );
+        return this;
+    }
+
+    /**
+     * <p>Specifies a value separator for the argument of the option that this spec represents.  This allows a single
+     * option argument to represent multiple values for the option.  For example:</p>
+     *
+     * <pre>
+     *   <code>
+     *   parser.accepts( "z" ).withRequiredArg()
+     *       .<strong>withValuesSeparatedBy( ":::" )</strong>;
+     *   OptionSet options = parser.parse( new String[] { "-z", "foo:::bar:::baz", "-z",
+     *       "fizz", "-z", "buzz" } );
+     *   </code>
+     * </pre>
+     *
+     * <p>Then <code>options.valuesOf( "z" )</code> would yield the list {@code [foo, bar, baz, fizz, buzz]}.</p>
+     *
+     * <p>You cannot use Unicode U+0000 in the separator.</p>
+     *
+     * @param separator a string separator
+     * @return self, so that the caller can add clauses to the fluent interface sentence
+     * @throws IllegalArgumentException if the separator contains Unicode U+0000
+     */
+    public final ArgumentAcceptingOptionSpec<V> withValuesSeparatedBy( String separator ) {
+        if ( separator.indexOf( NIL_VALUE_SEPARATOR ) != -1 )
+            throw new IllegalArgumentException( "cannot use U+0000 in separator" );
+
+        valueSeparator = separator;
+        return this;
+    }
+
+    /**
+     * Specifies a set of default values for the argument of the option that this spec represents.
+     *
+     * @param value the first in the set of default argument values for this spec's option
+     * @param values the (optional) remainder of the set of default argument values for this spec's option
+     * @return self, so that the caller can add clauses to the fluent interface sentence
+     * @throws NullPointerException if {@code value}, {@code values}, or any elements of {@code values} are
+     * {@code null}
+     */
+    @SafeVarargs
+    public final ArgumentAcceptingOptionSpec<V> defaultsTo( V value, V... values ) {
+        addDefaultValue( value );
+        defaultsTo( values );
+
+        return this;
+    }
+
+    /**
+     * Specifies a set of default values for the argument of the option that this spec represents.
+     *
+     * @param values the set of default argument values for this spec's option
+     * @return self, so that the caller can add clauses to the fluent interface sentence
+     * @throws NullPointerException if {@code values} or any elements of {@code values} are {@code null}
+     */
+    public ArgumentAcceptingOptionSpec<V> defaultsTo( V[] values ) {
+        for ( V each : values )
+            addDefaultValue( each );
+
+        return this;
+    }
+
+    /**
+     * Marks this option as required. An {@link OptionException} will be thrown when
+     * {@link OptionParser#parse(java.lang.String...)} is called, if an option is marked as required and not specified
+     * on the command line.
+     *
+     * @return self, so that the caller can add clauses to the fluent interface sentence
+     */
+    public ArgumentAcceptingOptionSpec<V> required() {
+        optionRequired = true;
+        return this;
+    }
+
+    public boolean isRequired() {
+        return optionRequired;
+    }
+
+    private void addDefaultValue( V value ) {
+        requireNonNull( value );
+        defaultValues.add( value );
+    }
+
+    @Override
+    final void handleOption( OptionParser parser, ArgumentList arguments, OptionSet detectedOptions,
+        String detectedArgument ) {
+
+        if ( isNullOrEmpty( detectedArgument ) )
+            detectOptionArgument( parser, arguments, detectedOptions );
+        else
+            addArguments( detectedOptions, detectedArgument );
+    }
+
+    protected void addArguments( OptionSet detectedOptions, String detectedArgument ) {
+        StringTokenizer lexer = new StringTokenizer( detectedArgument, valueSeparator );
+        if ( !lexer.hasMoreTokens() )
+            detectedOptions.addWithArgument( this, detectedArgument );
+        else {
+            while ( lexer.hasMoreTokens() )
+                detectedOptions.addWithArgument( this, lexer.nextToken() );
+        }
+    }
+
+    protected abstract void detectOptionArgument( OptionParser parser, ArgumentList arguments,
+        OptionSet detectedOptions );
+
+    @Override
+    protected final V convert( String argument ) {
+        return convertWith( converter, argument );
+    }
+
+    protected boolean canConvertArgument( String argument ) {
+        StringTokenizer lexer = new StringTokenizer( argument, valueSeparator );
+
+        try {
+            while ( lexer.hasMoreTokens() )
+                convert( lexer.nextToken() );
+            return true;
+        } catch ( OptionException ignored ) {
+            return false;
+        }
+    }
+
+    protected boolean isArgumentOfNumberType() {
+        return converter != null && Number.class.isAssignableFrom( converter.valueType() );
+    }
+
+    public boolean acceptsArguments() {
+        return true;
+    }
+
+    public boolean requiresArgument() {
+        return argumentRequired;
+    }
+
+    public String argumentDescription() {
+        return argumentDescription;
+    }
+
+    public String argumentTypeIndicator() {
+        return argumentTypeIndicatorFrom( converter );
+    }
+
+    public List<V> defaultValues() {
+        return unmodifiableList( defaultValues );
+    }
+
+    @Override
+    public boolean equals( Object that ) {
+        if ( !super.equals( that ) )
+            return false;
+
+        ArgumentAcceptingOptionSpec<?> other = (ArgumentAcceptingOptionSpec<?>) that;
+        return requiresArgument() == other.requiresArgument();
+    }
+
+    @Override
+    public int hashCode() {
+        return super.hashCode() ^ ( argumentRequired ? 0 : 1 );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/main/java/joptsimple/ArgumentList.java b/third_party/java/jopt-simple/src/main/java/joptsimple/ArgumentList.java
new file mode 100644
index 0000000..744f31a
--- /dev/null
+++ b/third_party/java/jopt-simple/src/main/java/joptsimple/ArgumentList.java
@@ -0,0 +1,59 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import static joptsimple.ParserRules.*;
+
+/**
+ * <p>Wrapper for an array of command line arguments.</p>
+ *
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+class ArgumentList {
+    private final String[] arguments;
+    private int currentIndex;
+
+    ArgumentList( String... arguments ) {
+        this.arguments = arguments.clone();
+    }
+
+    boolean hasMore() {
+        return currentIndex < arguments.length;
+    }
+
+    String next() {
+        return arguments[ currentIndex++ ];
+    }
+
+    String peek() {
+        return arguments[ currentIndex ];
+    }
+
+    void treatNextAsLongOption() {
+        if ( HYPHEN_CHAR != arguments[ currentIndex ].charAt( 0 ) )
+            arguments[ currentIndex ] = DOUBLE_HYPHEN + arguments[ currentIndex ];
+    }
+}
diff --git a/third_party/java/jopt-simple/src/main/java/joptsimple/BuiltinHelpFormatter.java b/third_party/java/jopt-simple/src/main/java/joptsimple/BuiltinHelpFormatter.java
new file mode 100644
index 0000000..51ec603
--- /dev/null
+++ b/third_party/java/jopt-simple/src/main/java/joptsimple/BuiltinHelpFormatter.java
@@ -0,0 +1,565 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import java.util.*;
+
+import joptsimple.internal.Messages;
+import joptsimple.internal.Rows;
+import joptsimple.internal.Strings;
+
+import static joptsimple.ParserRules.*;
+import static joptsimple.internal.Classes.*;
+import static joptsimple.internal.Strings.*;
+
+/**
+ * <p>A help formatter that allows configuration of overall row width and column separator width.</p>
+ *
+ * <p>The formatter produces output in two sections: one for the options, and one for non-option arguments.</p>
+ *
+ * <p>The options section has two columns: the left column for the options, and the right column for their
+ * descriptions. The formatter will allow as much space as possible for the descriptions, by minimizing the option
+ * column's width, no greater than slightly less than half the overall desired width.</p>
+ *
+ * <p>The non-option arguments section is one column, occupying as much width as it can.</p>
+ *
+ * <p>Subclasses are free to override bits of this implementation as they see fit. Inspect the code
+ * carefully to understand the flow of control that this implementation guarantees.</p>
+ *
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class BuiltinHelpFormatter implements HelpFormatter {
+    private final Rows nonOptionRows;
+    private final Rows optionRows;
+
+    /**
+     * Makes a formatter with a pre-configured overall row width and column separator width.
+     */
+    BuiltinHelpFormatter() {
+        this( 80, 2 );
+    }
+
+    /**
+     * Makes a formatter with a given overall row width and column separator width.
+     *
+     * @param desiredOverallWidth how many characters wide to make the overall help display
+     * @param desiredColumnSeparatorWidth how many characters wide to make the separation between option column and
+     * description column
+     */
+    public BuiltinHelpFormatter( int desiredOverallWidth, int desiredColumnSeparatorWidth ) {
+        nonOptionRows = new Rows( desiredOverallWidth * 2, 0 );
+        optionRows = new Rows( desiredOverallWidth, desiredColumnSeparatorWidth );
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * <p>This implementation:</p>
+     * <ul>
+     *     <li>Sorts the given descriptors by their first elements of {@link OptionDescriptor#options()}</li>
+     *     <li>Passes the resulting sorted set to {@link #addRows(java.util.Collection)}</li>
+     *     <li>Returns the result of {@link #formattedHelpOutput()}</li>
+     * </ul>
+     */
+    public String format( Map<String, ? extends OptionDescriptor> options ) {
+        optionRows.reset();
+        nonOptionRows.reset();
+
+        Comparator<OptionDescriptor> comparator =
+            new Comparator<OptionDescriptor>() {
+                public int compare( OptionDescriptor first, OptionDescriptor second ) {
+                    return first.options().iterator().next().compareTo( second.options().iterator().next() );
+                }
+            };
+
+        Set<OptionDescriptor> sorted = new TreeSet<>( comparator );
+        sorted.addAll( options.values() );
+
+        addRows( sorted );
+
+        return formattedHelpOutput();
+    }
+
+    /**
+     * Adds a row of option help output in the left column, with empty space in the right column.
+     *
+     * @param single text to put in the left column
+     */
+    protected void addOptionRow( String single ) {
+        addOptionRow( single, "" );
+    }
+
+    /**
+     * Adds a row of option help output in the left and right columns.
+     *
+     * @param left text to put in the left column
+     * @param right text to put in the right column
+     */
+    protected void addOptionRow( String left, String right ) {
+        optionRows.add( left, right );
+    }
+
+    /**
+     * Adds a single row of non-option argument help.
+     *
+     * @param single single row of non-option argument help text
+     */
+    protected void addNonOptionRow( String single ) {
+        nonOptionRows.add( single, "" );
+    }
+
+    /**
+     * Resizes the columns of all the rows to be no wider than the widest element in that column.
+     */
+    protected void fitRowsToWidth() {
+        nonOptionRows.fitToWidth();
+        optionRows.fitToWidth();
+    }
+
+    /**
+     * Produces non-option argument help.
+     *
+     * @return non-option argument help
+     */
+    protected String nonOptionOutput() {
+        return nonOptionRows.render();
+    }
+
+    /**
+     * Produces help for options and their descriptions.
+     *
+     * @return option help
+     */
+    protected String optionOutput() {
+        return optionRows.render();
+    }
+
+    /**
+     * <p>Produces help output for an entire set of options and non-option arguments.</p>
+     *
+     * <p>This implementation concatenates:</p>
+     * <ul>
+     *     <li>the result of {@link #nonOptionOutput()}</li>
+     *     <li>if there is non-option output, a line separator</li>
+     *     <li>the result of {@link #optionOutput()}</li>
+     * </ul>
+     *
+     * @return help output for entire set of options and non-option arguments
+     */
+    protected String formattedHelpOutput() {
+        StringBuilder formatted = new StringBuilder();
+        String nonOptionDisplay = nonOptionOutput();
+        if ( !Strings.isNullOrEmpty( nonOptionDisplay ) )
+            formatted.append( nonOptionDisplay ).append( LINE_SEPARATOR );
+        formatted.append( optionOutput() );
+
+        return formatted.toString();
+    }
+
+    /**
+     * <p>Adds rows of help output for the given options.</p>
+     *
+     * <p>This implementation:</p>
+     * <ul>
+     *     <li>Calls {@link #addNonOptionsDescription(java.util.Collection)} with the options as the argument</li>
+ *         <li>If there are no options, calls {@link #addOptionRow(String)} with an argument that indicates
+ *         that no options are specified.</li>
+ *         <li>Otherwise, calls {@link #addHeaders(java.util.Collection)} with the options as the argument,
+ *         followed by {@link #addOptions(java.util.Collection)} with the options as the argument.</li>
+     *     <li>Calls {@link #fitRowsToWidth()}.</li>
+     * </ul>
+     *
+     * @param options descriptors for the configured options of a parser
+     */
+    protected void addRows( Collection<? extends OptionDescriptor> options ) {
+        addNonOptionsDescription( options );
+
+        if ( options.isEmpty() )
+            addOptionRow( message( "no.options.specified" ) );
+        else {
+            addHeaders( options );
+            addOptions( options );
+        }
+
+        fitRowsToWidth();
+    }
+
+    /**
+     * <p>Adds non-option arguments descriptions to the help output.</p>
+     *
+     * <p>This implementation:</p>
+     * <ul>
+     *     <li>{@linkplain #findAndRemoveNonOptionsSpec(java.util.Collection) Finds and removes the non-option
+     *     arguments descriptor}</li>
+     *     <li>{@linkplain #shouldShowNonOptionArgumentDisplay(OptionDescriptor) Decides whether there is
+     *     anything to show for non-option arguments}</li>
+     *     <li>If there is, {@linkplain #addNonOptionRow(String) adds a header row} and
+     *     {@linkplain #addNonOptionRow(String) adds a}
+     *     {@linkplain #createNonOptionArgumentsDisplay(OptionDescriptor) non-option arguments description} </li>
+     * </ul>
+     *
+     * @param options descriptors for the configured options of a parser
+     */
+    protected void addNonOptionsDescription( Collection<? extends OptionDescriptor> options ) {
+        OptionDescriptor nonOptions = findAndRemoveNonOptionsSpec( options );
+        if ( shouldShowNonOptionArgumentDisplay( nonOptions ) ) {
+            addNonOptionRow( message( "non.option.arguments.header" ) );
+            addNonOptionRow( createNonOptionArgumentsDisplay( nonOptions ) );
+        }
+    }
+
+    /**
+     * <p>Decides whether or not to show a non-option arguments help.</p>
+     *
+     * <p>This implementation responds with {@code true} if the non-option descriptor has a non-{@code null},
+     * non-empty value for any of {@link OptionDescriptor#description()},
+     * {@link OptionDescriptor#argumentTypeIndicator()}, or {@link OptionDescriptor#argumentDescription()}.</p>
+     *
+     * @param nonOptionDescriptor non-option argument descriptor
+     * @return {@code true} if non-options argument help should be shown
+     */
+    protected boolean shouldShowNonOptionArgumentDisplay( OptionDescriptor nonOptionDescriptor ) {
+        return !Strings.isNullOrEmpty( nonOptionDescriptor.description() )
+            || !Strings.isNullOrEmpty( nonOptionDescriptor.argumentTypeIndicator() )
+            || !Strings.isNullOrEmpty( nonOptionDescriptor.argumentDescription() );
+    }
+
+    /**
+     * <p>Creates a non-options argument help string.</p>
+     *
+     * <p>This implementation creates an empty string buffer and calls
+     * {@link #maybeAppendOptionInfo(StringBuilder, OptionDescriptor)}
+     * and {@link #maybeAppendNonOptionsDescription(StringBuilder, OptionDescriptor)}, passing them the
+     * buffer and the non-option arguments descriptor.</p>
+     *
+     * @param nonOptionDescriptor non-option argument descriptor
+     * @return help string for non-options
+     */
+    protected String createNonOptionArgumentsDisplay( OptionDescriptor nonOptionDescriptor ) {
+        StringBuilder buffer = new StringBuilder();
+        maybeAppendOptionInfo( buffer, nonOptionDescriptor );
+        maybeAppendNonOptionsDescription( buffer, nonOptionDescriptor );
+
+        return buffer.toString();
+    }
+
+    /**
+     * <p>Appends help for the given non-option arguments descriptor to the given buffer.</p>
+     *
+     * <p>This implementation appends {@code " -- "} if the buffer has text in it and the non-option arguments
+     * descriptor has a {@link OptionDescriptor#description()}; followed by the
+     * {@link OptionDescriptor#description()}.</p>
+     *
+     * @param buffer string buffer
+     * @param nonOptions non-option arguments descriptor
+     */
+    protected void maybeAppendNonOptionsDescription( StringBuilder buffer, OptionDescriptor nonOptions ) {
+        buffer.append( buffer.length() > 0 && !Strings.isNullOrEmpty( nonOptions.description() ) ? " -- " : "" )
+            .append( nonOptions.description() );
+    }
+
+    /**
+     * Finds the non-option arguments descriptor in the given collection, removes it, and returns it.
+     *
+     * @param options descriptors for the configured options of a parser
+     * @return the non-option arguments descriptor
+     */
+    protected OptionDescriptor findAndRemoveNonOptionsSpec( Collection<? extends OptionDescriptor> options ) {
+        for ( Iterator<? extends OptionDescriptor> it = options.iterator(); it.hasNext(); ) {
+            OptionDescriptor next = it.next();
+            if ( next.representsNonOptions() ) {
+                it.remove();
+                return next;
+            }
+        }
+
+        throw new AssertionError( "no non-options argument spec" );
+    }
+
+    /**
+     * <p>Adds help row headers for option help columns.</p>
+     *
+     * <p>This implementation uses the headers {@code "Option"} and {@code "Description"}. If the options contain
+     * a "required" option, the {@code "Option"} header looks like {@code "Option (* = required)}. Both headers
+     * are "underlined" using {@code "-"}.</p>
+     *
+     * @param options descriptors for the configured options of a parser
+     */
+    protected void addHeaders( Collection<? extends OptionDescriptor> options ) {
+        if ( hasRequiredOption( options ) ) {
+            addOptionRow( message( "option.header.with.required.indicator" ), message( "description.header" ) );
+            addOptionRow( message( "option.divider.with.required.indicator" ), message( "description.divider" ) );
+        } else {
+            addOptionRow( message( "option.header" ), message( "description.header" ) );
+            addOptionRow( message( "option.divider" ), message( "description.divider" ) );
+        }
+    }
+
+    /**
+     * Tells whether the given option descriptors contain a "required" option.
+     *
+     * @param options descriptors for the configured options of a parser
+     * @return {@code true} if at least one of the options is "required"
+     */
+    protected final boolean hasRequiredOption( Collection<? extends OptionDescriptor> options ) {
+        for ( OptionDescriptor each : options ) {
+            if ( each.isRequired() )
+                return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * <p>Adds help rows for the given options.</p>
+     *
+     * <p>This implementation loops over the given options, and for each, calls {@link #addOptionRow(String, String)}
+     * using the results of {@link #createOptionDisplay(OptionDescriptor)} and
+     * {@link #createDescriptionDisplay(OptionDescriptor)}, respectively, as arguments.</p>
+     *
+     * @param options descriptors for the configured options of a parser
+     */
+    protected void addOptions( Collection<? extends OptionDescriptor> options ) {
+        for ( OptionDescriptor each : options ) {
+            if ( !each.representsNonOptions() )
+                addOptionRow( createOptionDisplay( each ), createDescriptionDisplay( each ) );
+        }
+    }
+
+    /**
+     * <p>Creates a string for how the given option descriptor is to be represented in help.</p>
+     *
+     * <p>This implementation gives a string consisting of the concatenation of:</p>
+     * <ul>
+     *     <li>{@code "* "} for "required" options, otherwise {@code ""}</li>
+     *     <li>For each of the {@link OptionDescriptor#options()} of the descriptor, separated by {@code ", "}:
+     *         <ul>
+     *             <li>{@link #optionLeader(String)} of the option</li>
+     *             <li>the option</li>
+     *         </ul>
+     *     </li>
+     *     <li>the result of {@link #maybeAppendOptionInfo(StringBuilder, OptionDescriptor)}</li>
+     * </ul>
+     *
+     * @param descriptor a descriptor for a configured option of a parser
+     * @return help string
+     */
+    protected String createOptionDisplay( OptionDescriptor descriptor ) {
+        StringBuilder buffer = new StringBuilder( descriptor.isRequired() ? "* " : "" );
+
+        for ( Iterator<String> i = descriptor.options().iterator(); i.hasNext(); ) {
+            String option = i.next();
+            buffer.append( optionLeader( option ) );
+            buffer.append( option );
+
+            if ( i.hasNext() )
+                buffer.append( ", " );
+        }
+
+        maybeAppendOptionInfo( buffer, descriptor );
+
+        return buffer.toString();
+    }
+
+    /**
+     * <p>Gives a string that represents the given option's "option leader" in help.</p>
+     *
+     * <p>This implementation answers with {@code "--"} for options of length greater than one; otherwise answers
+     * with {@code "-"}.</p>
+     *
+     * @param option a string option
+     * @return an "option leader" string
+     */
+    protected String optionLeader( String option ) {
+        return option.length() > 1 ? DOUBLE_HYPHEN : HYPHEN;
+    }
+
+    /**
+     * <p>Appends additional info about the given option to the given buffer.</p>
+     *
+     * <p>This implementation:</p>
+     * <ul>
+     *     <li>calls {@link #extractTypeIndicator(OptionDescriptor)} for the descriptor</li>
+     *     <li>calls {@link joptsimple.OptionDescriptor#argumentDescription()} for the descriptor</li>
+     *     <li>if either of the above is present, calls
+     *     {@link #appendOptionHelp(StringBuilder, String, String, boolean)}</li>
+     * </ul>
+     *
+     * @param buffer string buffer
+     * @param descriptor a descriptor for a configured option of a parser
+     */
+    protected void maybeAppendOptionInfo( StringBuilder buffer, OptionDescriptor descriptor ) {
+        String indicator = extractTypeIndicator( descriptor );
+        String description = descriptor.argumentDescription();
+        if ( descriptor.acceptsArguments()
+            || !isNullOrEmpty( description )
+            || descriptor.representsNonOptions() ) {
+
+            appendOptionHelp( buffer, indicator, description, descriptor.requiresArgument() );
+        }
+    }
+
+    /**
+     * <p>Gives an indicator of the type of arguments of the option described by the given descriptor,
+     * for use in help.</p>
+     *
+     * <p>This implementation asks for the {@link OptionDescriptor#argumentTypeIndicator()} of the given
+     * descriptor, and if it is present and not {@code "java.lang.String"}, parses it as a fully qualified
+     * class name and returns the base name of that class; otherwise returns {@code "String"}.</p>
+     *
+     * @param descriptor a descriptor for a configured option of a parser
+     * @return type indicator text
+     */
+    protected String extractTypeIndicator( OptionDescriptor descriptor ) {
+        String indicator = descriptor.argumentTypeIndicator();
+
+        if ( !isNullOrEmpty( indicator ) && !String.class.getName().equals( indicator ) )
+            return shortNameOf( indicator );
+
+        return "String";
+    }
+
+    /**
+     * <p>Appends info about an option's argument to the given buffer.</p>
+     *
+     * <p>This implementation calls {@link #appendTypeIndicator(StringBuilder, String, String, char, char)} with
+     * the surrounding characters {@code '<'} and {@code '>'} for options with {@code required} arguments, and
+     * with the surrounding characters {@code '['} and {@code ']'} for options with optional arguments.</p>
+     *
+     * @param buffer string buffer
+     * @param typeIndicator type indicator
+     * @param description type description
+     * @param required indicator of "required"-ness of the argument of the option
+     */
+    protected void appendOptionHelp( StringBuilder buffer, String typeIndicator, String description,
+                                     boolean required ) {
+        if ( required )
+            appendTypeIndicator( buffer, typeIndicator, description, '<', '>' );
+        else
+            appendTypeIndicator( buffer, typeIndicator, description, '[', ']' );
+    }
+
+    /**
+     * <p>Appends a type indicator for an option's argument to the given buffer.</p>
+     *
+     * <p>This implementation appends, in order:</p>
+     * <ul>
+     *     <li>{@code ' '}</li>
+     *     <li>{@code start}</li>
+     *     <li>the type indicator, if not {@code null}</li>
+     *     <li>if the description is present, then {@code ": "} plus the description if the type indicator is
+     *     present; otherwise the description only</li>
+     *     <li>{@code end}</li>
+     * </ul>
+     *
+     * @param buffer string buffer
+     * @param typeIndicator type indicator
+     * @param description type description
+     * @param start starting character
+     * @param end ending character
+     */
+    protected void appendTypeIndicator( StringBuilder buffer, String typeIndicator, String description,
+                                        char start, char end ) {
+        buffer.append( ' ' ).append( start );
+        if ( typeIndicator != null )
+            buffer.append( typeIndicator );
+
+        if ( !Strings.isNullOrEmpty( description ) ) {
+            if ( typeIndicator != null )
+                buffer.append( ": " );
+
+            buffer.append( description );
+        }
+
+        buffer.append( end );
+    }
+
+    /**
+     * <p>Gives a string representing a description of the option with the given descriptor.</p>
+     *
+     * <p>This implementation:</p>
+     * <ul>
+     *     <li>Asks for the descriptor's {@link OptionDescriptor#defaultValues()}</li>
+     *     <li>If they're not present, answers the descriptor's {@link OptionDescriptor#description()}.</li>
+     *     <li>If they are present, concatenates and returns:
+     *         <ul>
+     *             <li>the descriptor's {@link OptionDescriptor#description()}</li>
+     *             <li>{@code ' '}</li>
+     *             <li>{@code "default: "} plus the result of {@link #createDefaultValuesDisplay(java.util.List)},
+     *             surrounded by parentheses</li>
+     *         </ul>
+     *     </li>
+     * </ul>
+     *
+     * @param descriptor a descriptor for a configured option of a parser
+     * @return display text for the option's description
+     */
+    protected String createDescriptionDisplay( OptionDescriptor descriptor ) {
+        List<?> defaultValues = descriptor.defaultValues();
+        if ( defaultValues.isEmpty() )
+            return descriptor.description();
+
+        String defaultValuesDisplay = createDefaultValuesDisplay( defaultValues );
+        return ( descriptor.description()
+            + ' '
+            + surround( message( "default.value.header" ) + ' ' + defaultValuesDisplay, '(', ')' )
+        ).trim();
+    }
+
+    /**
+     * <p>Gives a display string for the default values of an option's argument.</p>
+     *
+     * <p>This implementation gives the {@link Object#toString()} of the first value if there is only one value,
+     * otherwise gives the {@link Object#toString()} of the whole list.</p>
+     *
+     * @param defaultValues some default values for a given option's argument
+     * @return a display string for those default values
+     */
+    protected String createDefaultValuesDisplay( List<?> defaultValues ) {
+        return defaultValues.size() == 1 ? defaultValues.get( 0 ).toString() : defaultValues.toString();
+    }
+
+    /**
+     * <p>Looks up and gives a resource bundle message.</p>
+     *
+     * <p>This implementation looks in the bundle {@code "joptsimple.HelpFormatterMessages"} in the default
+     * locale, using a key that is the concatenation of this class's fully qualified name, {@code '.'},
+     * and the given key suffix, formats the corresponding value using the given arguments, and returns
+     * the result.</p>
+     *
+     * @param keySuffix suffix to use when looking up the bundle message
+     * @param args arguments to fill in the message template with
+     * @return a formatted localized message
+     */
+    protected String message( String keySuffix, Object... args ) {
+        return Messages.message(
+            Locale.getDefault(),
+            "joptsimple.HelpFormatterMessages",
+            BuiltinHelpFormatter.class,
+            keySuffix,
+            args );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/main/java/joptsimple/HelpFormatter.java b/third_party/java/jopt-simple/src/main/java/joptsimple/HelpFormatter.java
new file mode 100644
index 0000000..d178d70
--- /dev/null
+++ b/third_party/java/jopt-simple/src/main/java/joptsimple/HelpFormatter.java
@@ -0,0 +1,45 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import java.util.Map;
+
+/**
+ * <p>Represents objects charged with taking a set of option descriptions and producing some help text from them.</p>
+ *
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public interface HelpFormatter {
+    /**
+     * Produces help text, given a set of option descriptors.
+     *
+     * @param options descriptors for the configured options of a parser
+     * @return text to be used as help
+     * @see OptionParser#printHelpOn(java.io.Writer)
+     * @see OptionParser#formatHelpWith(HelpFormatter)
+     */
+    String format( Map<String, ? extends OptionDescriptor> options );
+}
diff --git a/third_party/java/jopt-simple/src/main/java/joptsimple/IllegalOptionSpecificationException.java b/third_party/java/jopt-simple/src/main/java/joptsimple/IllegalOptionSpecificationException.java
new file mode 100644
index 0000000..2147915
--- /dev/null
+++ b/third_party/java/jopt-simple/src/main/java/joptsimple/IllegalOptionSpecificationException.java
@@ -0,0 +1,46 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import static java.util.Collections.*;
+
+/**
+ * Thrown when the option parser is asked to recognize an option with illegal characters in it.
+ *
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+class IllegalOptionSpecificationException extends OptionException {
+    private static final long serialVersionUID = -1L;
+
+    IllegalOptionSpecificationException( String option ) {
+        super( singletonList( option ) );
+    }
+
+    @Override
+    Object[] messageArguments() {
+        return new Object[] { singleOptionString() };
+    }
+}
diff --git a/third_party/java/jopt-simple/src/main/java/joptsimple/MissingRequiredOptionsException.java b/third_party/java/jopt-simple/src/main/java/joptsimple/MissingRequiredOptionsException.java
new file mode 100644
index 0000000..dcb829f
--- /dev/null
+++ b/third_party/java/jopt-simple/src/main/java/joptsimple/MissingRequiredOptionsException.java
@@ -0,0 +1,46 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import java.util.List;
+
+/**
+ * Thrown when options marked as required are not specified on the command line.
+ *
+ * @author <a href="https://github.com/TC1">Emils Solmanis</a>
+ */
+class MissingRequiredOptionsException extends OptionException {
+    private static final long serialVersionUID = -1L;
+
+    protected MissingRequiredOptionsException( List<? extends OptionSpec<?>> missingRequiredOptions ) {
+        super( missingRequiredOptions );
+    }
+
+    @Override
+    Object[] messageArguments() {
+        return new Object[] { multipleOptionString() };
+    }
+}
diff --git a/third_party/java/jopt-simple/src/main/java/joptsimple/MultipleArgumentsForOptionException.java b/third_party/java/jopt-simple/src/main/java/joptsimple/MultipleArgumentsForOptionException.java
new file mode 100644
index 0000000..12d1485
--- /dev/null
+++ b/third_party/java/jopt-simple/src/main/java/joptsimple/MultipleArgumentsForOptionException.java
@@ -0,0 +1,46 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import static java.util.Collections.*;
+
+/**
+ * Thrown when asking an {@link OptionSet} for a single argument of an option when many have been specified.
+ *
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+class MultipleArgumentsForOptionException extends OptionException {
+    private static final long serialVersionUID = -1L;
+
+    MultipleArgumentsForOptionException( OptionSpec<?> options ) {
+        super( singleton( options ) );
+    }
+
+    @Override
+    Object[] messageArguments() {
+        return new Object[] { singleOptionString() };
+    }
+}
diff --git a/third_party/java/jopt-simple/src/main/java/joptsimple/NoArgumentOptionSpec.java b/third_party/java/jopt-simple/src/main/java/joptsimple/NoArgumentOptionSpec.java
new file mode 100644
index 0000000..82ac521
--- /dev/null
+++ b/third_party/java/jopt-simple/src/main/java/joptsimple/NoArgumentOptionSpec.java
@@ -0,0 +1,81 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import java.util.List;
+
+import static java.util.Collections.*;
+
+/**
+ * A specification for an option that does not accept arguments.
+ *
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+class NoArgumentOptionSpec extends AbstractOptionSpec<Void> {
+    NoArgumentOptionSpec( String option ) {
+        this( singletonList( option ), "" );
+    }
+
+    NoArgumentOptionSpec( List<String> options, String description ) {
+        super( options, description );
+    }
+
+    @Override
+    void handleOption( OptionParser parser, ArgumentList arguments, OptionSet detectedOptions,
+        String detectedArgument ) {
+
+        detectedOptions.add( this );
+    }
+
+    public boolean acceptsArguments() {
+        return false;
+    }
+
+    public boolean requiresArgument() {
+        return false;
+    }
+
+    public boolean isRequired() {
+        return false;
+    }
+
+    public String argumentDescription() {
+        return "";
+    }
+
+    public String argumentTypeIndicator() {
+        return "";
+    }
+
+    @Override
+    protected Void convert( String argument ) {
+        return null;
+    }
+
+    public List<Void> defaultValues() {
+        return emptyList();
+    }
+}
diff --git a/third_party/java/jopt-simple/src/main/java/joptsimple/NonOptionArgumentSpec.java b/third_party/java/jopt-simple/src/main/java/joptsimple/NonOptionArgumentSpec.java
new file mode 100644
index 0000000..7343d42
--- /dev/null
+++ b/third_party/java/jopt-simple/src/main/java/joptsimple/NonOptionArgumentSpec.java
@@ -0,0 +1,170 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import java.util.List;
+
+import static java.util.Arrays.*;
+import static java.util.Collections.*;
+import static joptsimple.internal.Reflection.*;
+
+/**
+ * <p>Specification of a command line's non-option arguments.</p>
+ *
+ * <p>Instances are returned from {@link OptionParser} methods to allow the formation of parser directives as
+ * sentences in a "fluent interface" language. For example:</p>
+ *
+ * <pre>
+ *   <code>
+ *   OptionParser parser = new OptionParser();
+ *   parser.nonOptions( "files to be processed" ).<strong>ofType( File.class )</strong>;
+ *   </code>
+ * </pre>
+ *
+ * <p>If no methods are invoked on an instance of this class, then that instance's option will treat the non-option
+ * arguments as {@link String}s.</p>
+ *
+ * @param <V> represents the type of the non-option arguments
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class NonOptionArgumentSpec<V> extends AbstractOptionSpec<V> {
+    static final String NAME = "[arguments]";
+
+    private ValueConverter<V> converter;
+    private String argumentDescription = "";
+
+    NonOptionArgumentSpec() {
+        this( "" );
+    }
+
+    NonOptionArgumentSpec( String description ) {
+        super( asList( NAME ), description );
+    }
+
+    /**
+     * <p>Specifies a type to which the non-option arguments are to be converted.</p>
+     *
+     * <p>JOpt Simple accepts types that have either:</p>
+     *
+     * <ol>
+     *   <li>a public static method called {@code valueOf} which accepts a single argument of type {@link String}
+     *   and whose return type is the same as the class on which the method is declared.  The {@code java.lang}
+     *   primitive wrapper classes have such methods.</li>
+     *
+     *   <li>a public constructor which accepts a single argument of type {@link String}.</li>
+     * </ol>
+     *
+     * <p>This class converts arguments using those methods in that order; that is, {@code valueOf} would be invoked
+     * before a one-{@link String}-arg constructor would.</p>
+     *
+     * <p>Invoking this method will trump any previous calls to this method or to
+     * {@link #withValuesConvertedBy(ValueConverter)}.</p>
+     *
+     * @param <T> represents the runtime class of the desired option argument type
+     * @param argumentType desired type of arguments to this spec's option
+     * @return self, so that the caller can add clauses to the fluent interface sentence
+     * @throws NullPointerException if the type is {@code null}
+     * @throws IllegalArgumentException if the type does not have the standard conversion methods
+     */
+    @SuppressWarnings( "unchecked" )
+    public <T> NonOptionArgumentSpec<T> ofType( Class<T> argumentType ) {
+        converter = (ValueConverter<V>) findConverter( argumentType );
+        return (NonOptionArgumentSpec<T>) this;
+    }
+
+    /**
+     * <p>Specifies a converter to use to translate non-option arguments into Java objects.  This is useful
+     * when converting to types that do not have the requisite factory method or constructor for
+     * {@link #ofType(Class)}.</p>
+     *
+     * <p>Invoking this method will trump any previous calls to this method or to {@link #ofType(Class)}.
+     *
+     * @param <T> represents the runtime class of the desired non-option argument type
+     * @param aConverter the converter to use
+     * @return self, so that the caller can add clauses to the fluent interface sentence
+     * @throws NullPointerException if the converter is {@code null}
+     */
+    @SuppressWarnings( "unchecked" )
+    public final <T> NonOptionArgumentSpec<T> withValuesConvertedBy( ValueConverter<T> aConverter ) {
+        if ( aConverter == null )
+            throw new NullPointerException( "illegal null converter" );
+
+        converter = (ValueConverter<V>) aConverter;
+        return (NonOptionArgumentSpec<T>) this;
+    }
+
+    /**
+     * <p>Specifies a description for the non-option arguments that this spec represents.  This description is used
+     * when generating help information about the parser.</p>
+     *
+     * @param description describes the nature of the argument of this spec's option
+     * @return self, so that the caller can add clauses to the fluent interface sentence
+     */
+    public NonOptionArgumentSpec<V> describedAs( String description ) {
+        argumentDescription = description;
+        return this;
+    }
+
+    @Override
+    protected final V convert( String argument ) {
+        return convertWith( converter, argument );
+    }
+
+    @Override
+    void handleOption( OptionParser parser, ArgumentList arguments, OptionSet detectedOptions,
+        String detectedArgument ) {
+
+        detectedOptions.addWithArgument( this, detectedArgument );
+    }
+
+    public List<?> defaultValues() {
+        return emptyList();
+    }
+
+    public boolean isRequired() {
+        return false;
+    }
+
+    public boolean acceptsArguments() {
+        return false;
+    }
+
+    public boolean requiresArgument() {
+        return false;
+    }
+
+    public String argumentDescription() {
+        return argumentDescription;
+    }
+
+    public String argumentTypeIndicator() {
+        return argumentTypeIndicatorFrom( converter );
+    }
+
+    public boolean representsNonOptions() {
+        return true;
+    }
+}
diff --git a/third_party/java/jopt-simple/src/main/java/joptsimple/OptionArgumentConversionException.java b/third_party/java/jopt-simple/src/main/java/joptsimple/OptionArgumentConversionException.java
new file mode 100644
index 0000000..193557d
--- /dev/null
+++ b/third_party/java/jopt-simple/src/main/java/joptsimple/OptionArgumentConversionException.java
@@ -0,0 +1,50 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import static java.util.Collections.*;
+
+/**
+ * Thrown when a problem occurs converting an argument of an option from {@link String} to another type.
+ *
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+class OptionArgumentConversionException extends OptionException {
+    private static final long serialVersionUID = -1L;
+
+    private final String argument;
+
+    OptionArgumentConversionException( OptionSpec<?> options, String argument, Throwable cause ) {
+        super( singleton( options ), cause );
+
+        this.argument = argument;
+    }
+
+    @Override
+    Object[] messageArguments() {
+        return new Object[] { argument, singleOptionString() };
+    }
+}
diff --git a/third_party/java/jopt-simple/src/main/java/joptsimple/OptionDeclarer.java b/third_party/java/jopt-simple/src/main/java/joptsimple/OptionDeclarer.java
new file mode 100644
index 0000000..7d54653
--- /dev/null
+++ b/third_party/java/jopt-simple/src/main/java/joptsimple/OptionDeclarer.java
@@ -0,0 +1,116 @@
+package joptsimple;
+
+import java.util.List;
+
+/**
+ * Trains the option parser. This interface aids integration that disposes declaration of options but not actual
+ * command-line parsing.
+ *
+ * Typical use is for another class to implement {@code OptionDeclarer} as a facade, forwarding calls to an
+ * {@code OptionParser} instance.
+ *
+ * Note that although this is an interface, the returned values of calls are concrete jopt-simple classes.
+ *
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ * @see OptionParser
+ * @since 4.6
+ */
+public interface OptionDeclarer {
+    /**
+     * Tells the parser to recognize the given option.
+     *
+     * <p>This method returns an instance of {@link OptionSpecBuilder} to allow the formation of parser directives
+     * as sentences in a fluent interface language. For example:</p>
+     *
+     * <pre><code>
+     *   OptionDeclarer parser = new OptionParser();
+     *   parser.<strong>accepts( "c" )</strong>.withRequiredArg().ofType( Integer.class );
+     * </code></pre>
+     *
+     * <p>If no methods are invoked on the returned {@link OptionSpecBuilder}, then the parser treats the option as
+     * accepting no argument.</p>
+     *
+     * @param option the option to recognize
+     * @return an object that can be used to flesh out more detail about the option
+     * @throws OptionException if the option contains illegal characters
+     * @throws NullPointerException if the option is {@code null}
+     */
+    OptionSpecBuilder accepts( String option );
+
+    /**
+     * Tells the parser to recognize the given option.
+     *
+     * @see #accepts(String)
+     * @param option the option to recognize
+     * @param description a string that describes the purpose of the option. This is used when generating help
+     * information about the parser.
+     * @return an object that can be used to flesh out more detail about the option
+     * @throws OptionException if the option contains illegal characters
+     * @throws NullPointerException if the option is {@code null}
+     */
+    OptionSpecBuilder accepts( String option, String description );
+
+    /**
+     * Tells the parser to recognize the given options, and treat them as synonymous.
+     *
+     * @see #accepts(String)
+     * @param options the options to recognize and treat as synonymous
+     * @return an object that can be used to flesh out more detail about the options
+     * @throws OptionException if any of the options contain illegal characters
+     * @throws NullPointerException if the option list or any of its elements are {@code null}
+     */
+    OptionSpecBuilder acceptsAll( List<String> options );
+
+    /**
+     * Tells the parser to recognize the given options, and treat them as synonymous.
+     *
+     * @see #acceptsAll(List)
+     * @param options the options to recognize and treat as synonymous
+     * @param description a string that describes the purpose of the option.  This is used when generating help
+     * information about the parser.
+     * @return an object that can be used to flesh out more detail about the options
+     * @throws OptionException if any of the options contain illegal characters
+     * @throws NullPointerException if the option list or any of its elements are {@code null}
+     * @throws IllegalArgumentException if the option list is empty
+     */
+    OptionSpecBuilder acceptsAll( List<String> options, String description );
+
+    /**
+     * Gives an object that represents an access point for non-option arguments on a command line.
+     *
+     * @return an object that can be used to flesh out more detail about the non-option arguments
+     */
+    NonOptionArgumentSpec<String> nonOptions();
+
+    /**
+     * Gives an object that represents an access point for non-option arguments on a command line.
+     *
+     * @see #nonOptions()
+     * @param description a string that describes the purpose of the non-option arguments. This is used when generating
+     * help information about the parser.
+     * @return an object that can be used to flesh out more detail about the non-option arguments
+     */
+    NonOptionArgumentSpec<String> nonOptions( String description );
+
+    /**
+     * Tells the parser whether or not to behave "POSIX-ly correct"-ly.
+     *
+     * @param setting {@code true} if the parser should behave "POSIX-ly correct"-ly
+     */
+    void posixlyCorrect( boolean setting );
+
+    /**
+     * <p>Tells the parser to treat unrecognized options as non-option arguments.</p>
+     *
+     * <p>If not called, then the parser raises an {@link OptionException} when it encounters an unrecognized
+     * option.</p>
+     */
+    void allowsUnrecognizedOptions();
+
+    /**
+     * Tells the parser either to recognize or ignore {@code -W}-style long options.
+     *
+     * @param recognize {@code true} if the parser is to recognize the special style of long options
+     */
+    void recognizeAlternativeLongOptions( boolean recognize );
+}
diff --git a/third_party/java/jopt-simple/src/main/java/joptsimple/OptionDescriptor.java b/third_party/java/jopt-simple/src/main/java/joptsimple/OptionDescriptor.java
new file mode 100644
index 0000000..f77c775
--- /dev/null
+++ b/third_party/java/jopt-simple/src/main/java/joptsimple/OptionDescriptor.java
@@ -0,0 +1,100 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import java.util.List;
+
+/**
+ * Describes options that an option parser recognizes, in ways that might be useful to {@linkplain HelpFormatter
+ * help screens}.
+ *
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public interface OptionDescriptor {
+    /**
+     * A set of options that are mutually synonymous.
+     *
+     * @return synonymous options
+     */
+    List<String> options();
+
+    /**
+     * Description of this option's purpose.
+     *
+     * @return a description for the option
+     */
+    String description();
+
+    /**
+     * What values will the option take if none are specified on the command line?
+     *
+     * @return any default values for the option
+     */
+    List<?> defaultValues();
+
+    /**
+     * Is this option {@linkplain ArgumentAcceptingOptionSpec#required() required} on a command line?
+     *
+     * @return whether the option is required
+     */
+    boolean isRequired();
+
+    /**
+     * Does this option {@linkplain ArgumentAcceptingOptionSpec accept arguments}?
+     *
+     * @return whether the option accepts arguments
+     */
+    boolean acceptsArguments();
+
+    /**
+     * Does this option {@linkplain OptionSpecBuilder#withRequiredArg() require an argument}?
+     *
+     * @return whether the option requires an argument
+     */
+    boolean requiresArgument();
+
+    /**
+     * Gives a short {@linkplain ArgumentAcceptingOptionSpec#describedAs(String) description} of the option's argument.
+     *
+     * @return a description for the option's argument
+     */
+    String argumentDescription();
+
+    /**
+     * Gives an indication of the {@linkplain ArgumentAcceptingOptionSpec#ofType(Class) expected type} of the option's
+     * argument.
+     *
+     * @return a description for the option's argument type
+     */
+    String argumentTypeIndicator();
+
+    /**
+     * Tells whether this object represents the non-option arguments of a command line.
+     *
+     * @return {@code true} if this represents non-option arguments
+     */
+    boolean representsNonOptions();
+}
diff --git a/third_party/java/jopt-simple/src/main/java/joptsimple/OptionException.java b/third_party/java/jopt-simple/src/main/java/joptsimple/OptionException.java
new file mode 100644
index 0000000..88e7e43
--- /dev/null
+++ b/third_party/java/jopt-simple/src/main/java/joptsimple/OptionException.java
@@ -0,0 +1,125 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.Set;
+
+import joptsimple.internal.Strings;
+
+import static java.util.Collections.*;
+import static joptsimple.internal.Messages.*;
+
+/**
+ * Thrown when a problem occurs during option parsing.
+ *
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public abstract class OptionException extends RuntimeException {
+    private static final long serialVersionUID = -1L;
+
+    private final List<String> options = new ArrayList<>();
+
+    protected OptionException( List<String> options ) {
+        this.options.addAll( options );
+    }
+
+    protected OptionException( Collection<? extends OptionSpec<?>> options ) {
+        this.options.addAll( specsToStrings( options ) );
+    }
+
+    protected OptionException( Collection<? extends OptionSpec<?>> options, Throwable cause ) {
+        super( cause );
+        this.options.addAll( specsToStrings( options ) );
+    }
+
+    private List<String> specsToStrings( Collection<? extends OptionSpec<?>> options ) {
+        List<String> strings = new ArrayList<>();
+        for ( OptionSpec<?> each : options )
+            strings.add( specToString( each ) );
+        return strings;
+    }
+
+    private String specToString( OptionSpec<?> option ) {
+        return Strings.join( new ArrayList<>( option.options() ), "/" );
+    }
+
+    /**
+     * Gives the option being considered when the exception was created.
+     *
+     * @return the option being considered when the exception was created
+     */
+    public List<String> options() {
+        return unmodifiableList( options );
+    }
+
+    protected final String singleOptionString() {
+        return singleOptionString( options.get( 0 ) );
+    }
+
+    protected final String singleOptionString( String option ) {
+        return option;
+    }
+
+    protected final String multipleOptionString() {
+        StringBuilder buffer = new StringBuilder( "[" );
+
+        Set<String> asSet = new LinkedHashSet<String>( options );
+        for ( Iterator<String> iter = asSet.iterator(); iter.hasNext(); ) {
+            buffer.append( singleOptionString(iter.next()) );
+            if ( iter.hasNext() )
+                buffer.append( ", " );
+        }
+
+        buffer.append( ']' );
+
+        return buffer.toString();
+    }
+
+    static OptionException unrecognizedOption( String option ) {
+        return new UnrecognizedOptionException( option );
+    }
+
+    @Override
+    public final String getMessage() {
+        return localizedMessage( Locale.getDefault() );
+    }
+
+    final String localizedMessage( Locale locale ) {
+        return formattedMessage( locale );
+    }
+
+    private String formattedMessage( Locale locale ) {
+        return message( locale, "joptsimple.ExceptionMessages", getClass(), "message", messageArguments() );
+    }
+
+    abstract Object[] messageArguments();
+}
diff --git a/third_party/java/jopt-simple/src/main/java/joptsimple/OptionMissingRequiredArgumentException.java b/third_party/java/jopt-simple/src/main/java/joptsimple/OptionMissingRequiredArgumentException.java
new file mode 100644
index 0000000..9c69f12
--- /dev/null
+++ b/third_party/java/jopt-simple/src/main/java/joptsimple/OptionMissingRequiredArgumentException.java
@@ -0,0 +1,46 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import static java.util.Arrays.*;
+
+/**
+ * Thrown when the option parser discovers options that require an argument, but are missing an argument.
+ *
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+class OptionMissingRequiredArgumentException extends OptionException {
+    private static final long serialVersionUID = -1L;
+
+    OptionMissingRequiredArgumentException( OptionSpec<?> option ) {
+        super( asList( option ) );
+    }
+
+    @Override
+    Object[] messageArguments() {
+        return new Object[] { singleOptionString() };
+    }
+}
diff --git a/third_party/java/jopt-simple/src/main/java/joptsimple/OptionParser.java b/third_party/java/jopt-simple/src/main/java/joptsimple/OptionParser.java
new file mode 100644
index 0000000..d141f62
--- /dev/null
+++ b/third_party/java/jopt-simple/src/main/java/joptsimple/OptionParser.java
@@ -0,0 +1,647 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.util.*;
+
+import joptsimple.internal.AbbreviationMap;
+import joptsimple.internal.SimpleOptionNameMap;
+import joptsimple.internal.OptionNameMap;
+import joptsimple.util.KeyValuePair;
+
+import static java.util.Collections.*;
+import static joptsimple.OptionException.*;
+import static joptsimple.OptionParserState.*;
+import static joptsimple.ParserRules.*;
+
+/**
+ * <p>Parses command line arguments, using a syntax that attempts to take from the best of POSIX {@code getopt()}
+ * and GNU {@code getopt_long()}.</p>
+ *
+ * <p>This parser supports short options and long options.</p>
+ *
+ * <ul>
+ *   <li><dfn>Short options</dfn> begin with a single hyphen ("{@code -}") followed by a single letter or digit,
+ *   or question mark ("{@code ?}"), or dot ("{@code .}"), or underscore ("{@code _}").</li>
+ *
+ *   <li>Short options can accept single arguments. The argument can be made required or optional. The option's
+ *   argument can occur:
+ *     <ul>
+ *       <li>in the slot after the option, as in {@code -d /tmp}</li>
+ *       <li>right up against the option, as in {@code -d/tmp}</li>
+ *       <li>right up against the option separated by an equals sign ({@code "="}), as in {@code -d=/tmp}</li>
+ *     </ul>
+ *   To specify <em>n</em> arguments for an option, specify the option <em>n</em> times, once for each argument,
+ *   as in {@code -d /tmp -d /var -d /opt}; or, when using the
+ *   {@linkplain ArgumentAcceptingOptionSpec#withValuesSeparatedBy(char) "separated values"} clause of the "fluent
+ *   interface" (see below), give multiple values separated by a given character as a single argument to the
+ *   option.</li>
+ *
+ *   <li>Short options can be clustered, so that {@code -abc} is treated as {@code -a -b -c}. If a short option
+ *   in the cluster can accept an argument, the remaining characters are interpreted as the argument for that
+ *   option.</li>
+ *
+ *   <li>An argument consisting only of two hyphens ({@code "--"}) signals that the remaining arguments are to be
+ *   treated as non-options.</li>
+ *
+ *   <li>An argument consisting only of a single hyphen is considered a non-option argument (though it can be an
+ *   argument of an option). Many Unix programs treat single hyphens as stand-ins for the standard input or standard
+ *   output streams.</li>
+ *
+ *   <li><dfn>Long options</dfn> begin with two hyphens ({@code "--"}), followed by multiple letters, digits,
+ *   hyphens, question marks, or dots. A hyphen cannot be the first character of a long option specification when
+ *   configuring the parser.</li>
+ *
+ *   <li>You can abbreviate long options, so long as the abbreviation is unique. Suppress this behavior if
+ *   you wish using {@linkplain OptionParser#OptionParser(boolean) this constructor}.</li>
+ *
+ *   <li>Long options can accept single arguments.  The argument can be made required or optional.  The option's
+ *   argument can occur:
+ *     <ul>
+ *       <li>in the slot after the option, as in {@code --directory /tmp}</li>
+ *       <li>right up against the option separated by an equals sign ({@code "="}), as in
+ *       {@code --directory=/tmp}
+ *     </ul>
+ *   Specify multiple arguments for a long option in the same manner as for short options (see above).</li>
+ *
+ *   <li>You can use a single hyphen ({@code "-"}) instead of a double hyphen ({@code "--"}) for a long
+ *   option.</li>
+ *
+ *   <li>The option {@code -W} is reserved.  If you tell the parser to {@linkplain
+ *   #recognizeAlternativeLongOptions(boolean) recognize alternative long options}, then it will treat, for example,
+ *   {@code -W foo=bar} as the long option {@code foo} with argument {@code bar}, as though you had written
+ *   {@code --foo=bar}.</li>
+ *
+ *   <li>You can specify {@code -W} as a valid short option, or use it as an abbreviation for a long option, but
+ *   {@linkplain #recognizeAlternativeLongOptions(boolean) recognizing alternative long options} will always supersede
+ *   this behavior.</li>
+ *
+ *   <li>You can specify a given short or long option multiple times on a single command line. The parser collects
+ *   any arguments specified for those options as a list.</li>
+ *
+ *   <li>If the parser detects an option whose argument is optional, and the next argument "looks like" an option,
+ *   that argument is not treated as the argument to the option, but as a potentially valid option. If, on the other
+ *   hand, the optional argument is typed as a derivative of {@link Number}, then that argument is treated as the
+ *   negative number argument of the option, even if the parser recognizes the corresponding numeric option.
+ *   For example:
+ *   <pre><code>
+ *     OptionParser parser = new OptionParser();
+ *     parser.accepts( "a" ).withOptionalArg().ofType( Integer.class );
+ *     parser.accepts( "2" );
+ *     OptionSet options = parser.parse( "-a", "-2" );
+ *   </code></pre>
+ *   In this case, the option set contains {@code "a"} with argument {@code -2}, not both {@code "a"} and
+ *   {@code "2"}. Swapping the elements in the <em>args</em> array gives the latter.</li>
+ * </ul>
+ *
+ * <p>There are two ways to tell the parser what options to recognize:</p>
+ *
+ * <ol>
+ *   <li>A "fluent interface"-style API for specifying options, available since version 2. Sentences in this fluent
+ *   interface language begin with a call to {@link #accepts(String) accepts} or {@link #acceptsAll(List)
+ *   acceptsAll} methods; calls on the ensuing chain of objects describe whether the options can take an argument,
+ *   whether the argument is required or optional, to what type arguments of the options should be converted if any,
+ *   etc. Since version 3, these calls return an instance of {@link OptionSpec}, which can subsequently be used to
+ *   retrieve the arguments of the associated option in a type-safe manner.</li>
+ *
+ *   <li>Since version 1, a more concise way of specifying short options has been to use the special {@linkplain
+ *   #OptionParser(String) constructor}. Arguments of options specified in this manner will be of type {@link String}.
+ *   Here are the rules for the format of the specification strings this constructor accepts:
+ *
+ *     <ul>
+ *       <li>Any letter or digit is treated as an option character.</li>
+ *
+ *       <li>An option character can be immediately followed by an asterisk ({@code *)} to indicate that
+ *       the option is a "help" option.</li>
+ *
+ *       <li>If an option character (with possible trailing asterisk) is followed by a single colon ({@code ":"}),
+ *       then the option requires an argument.</li>
+ *
+ *       <li>If an option character (with possible trailing asterisk) is followed by two colons ({@code "::"}),
+ *       then the option accepts an optional argument.</li>
+ *
+ *       <li>Otherwise, the option character accepts no argument.</li>
+ *
+ *       <li>If the option specification string begins with a plus sign ({@code "+" }), the parser will behave
+ *       "POSIX-ly correct".</li>
+ *
+ *       <li>If the option specification string contains the sequence {@code "W;"} (capital W followed by a
+ *       semicolon), the parser will recognize the alternative form of long options.</li>
+ *     </ul>
+ *   </li>
+ * </ol>
+ *
+ * <p>Each of the options in a list of options given to {@link #acceptsAll(List) acceptsAll} is treated as a
+ * synonym of the others.  For example:</p>
+ *   <pre>
+ *     <code>
+ *     OptionParser parser = new OptionParser();
+ *     parser.acceptsAll( asList( "w", "interactive", "confirmation" ) );
+ *     OptionSet options = parser.parse( "-w" );
+ *     </code>
+ *   </pre>
+ * <p>In this case, <code>options.{@link OptionSet#has(String) has}</code> would answer {@code true} when given arguments
+ * {@code "w"}, {@code "interactive"}, and {@code "confirmation"}. The {@link OptionSet} would give the same
+ * responses to these arguments for its other methods as well.</p>
+ *
+ * <p>By default, as with GNU {@code getopt()}, the parser allows intermixing of options and non-options. If, however,
+ * the parser has been created to be "POSIX-ly correct", then the first argument that does not look lexically like an
+ * option, and is not a required argument of a preceding option, signals the end of options. You can still bind
+ * optional arguments to their options using the abutting (for short options) or {@code =} syntax.</p>
+ *
+ * <p>Unlike GNU {@code getopt()}, this parser does not honor the environment variable {@code POSIXLY_CORRECT}.
+ * "POSIX-ly correct" parsers are configured by either:</p>
+ *
+ * <ol>
+ *   <li>using the method {@link #posixlyCorrect(boolean)}, or</li>
+ *
+ *   <li>using the {@linkplain #OptionParser(String) constructor} with an argument whose first character is a plus sign
+ *   ({@code "+"})</li>
+ * </ol>
+ *
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ * @see <a href="http://www.gnu.org/software/libc/manual">The GNU C Library</a>
+ */
+public class OptionParser implements OptionDeclarer {
+    private final OptionNameMap<AbstractOptionSpec<?>> recognizedOptions;
+    private final ArrayList<AbstractOptionSpec<?>> trainingOrder;
+    private final Map<List<String>, Set<OptionSpec<?>>> requiredIf;
+    private final Map<List<String>, Set<OptionSpec<?>>> requiredUnless;
+    private final Map<List<String>, Set<OptionSpec<?>>> availableIf;
+    private final Map<List<String>, Set<OptionSpec<?>>> availableUnless;
+
+    private OptionParserState state;
+    private boolean posixlyCorrect;
+    private boolean allowsUnrecognizedOptions;
+    private HelpFormatter helpFormatter = new BuiltinHelpFormatter();
+
+    /**
+     * Creates an option parser that initially recognizes no options, and does not exhibit "POSIX-ly correct"
+     * behavior.
+     */
+    public OptionParser() {
+        this(true);
+    }
+
+    /**
+     * Creates an option parser that initially recognizes no options, and does not exhibit "POSIX-ly correct"
+     * behavior.
+     *
+     * @param allowAbbreviations whether unambiguous abbreviations of long options should be recognized
+     * by the parser
+     */
+    public OptionParser( boolean allowAbbreviations ) {
+        trainingOrder = new ArrayList<>();
+        requiredIf = new HashMap<>();
+        requiredUnless = new HashMap<>();
+        availableIf = new HashMap<>();
+        availableUnless = new HashMap<>();
+        state = moreOptions( false );
+
+        recognizedOptions = allowAbbreviations
+            ? new AbbreviationMap<AbstractOptionSpec<?>>()
+            : new SimpleOptionNameMap<AbstractOptionSpec<?>>();
+
+        recognize( new NonOptionArgumentSpec<String>() );
+    }
+
+    /**
+     * Creates an option parser and configures it to recognize the short options specified in the given string.
+     *
+     * Arguments of options specified this way will be of type {@link String}.
+     *
+     * @param optionSpecification an option specification
+     * @throws NullPointerException if {@code optionSpecification} is {@code null}
+     * @throws OptionException if the option specification contains illegal characters or otherwise cannot be
+     * recognized
+     */
+    public OptionParser( String optionSpecification ) {
+        this();
+
+        new OptionSpecTokenizer( optionSpecification ).configure( this );
+    }
+
+    public OptionSpecBuilder accepts( String option ) {
+        return acceptsAll( singletonList( option ) );
+    }
+
+    public OptionSpecBuilder accepts( String option, String description ) {
+        return acceptsAll( singletonList( option ), description );
+    }
+
+    public OptionSpecBuilder acceptsAll( List<String> options ) {
+        return acceptsAll( options, "" );
+    }
+
+    public OptionSpecBuilder acceptsAll( List<String> options, String description ) {
+        if ( options.isEmpty() )
+            throw new IllegalArgumentException( "need at least one option" );
+
+        ensureLegalOptions( options );
+
+        return new OptionSpecBuilder( this, options, description );
+    }
+
+    public NonOptionArgumentSpec<String> nonOptions() {
+        NonOptionArgumentSpec<String> spec = new NonOptionArgumentSpec<>();
+
+        recognize( spec );
+
+        return spec;
+    }
+
+    public NonOptionArgumentSpec<String> nonOptions( String description ) {
+        NonOptionArgumentSpec<String> spec = new NonOptionArgumentSpec<>( description );
+
+        recognize( spec );
+
+        return spec;
+    }
+
+    public void posixlyCorrect( boolean setting ) {
+        posixlyCorrect = setting;
+        state = moreOptions( setting );
+    }
+
+    boolean posixlyCorrect() {
+        return posixlyCorrect;
+    }
+
+    public void allowsUnrecognizedOptions() {
+        allowsUnrecognizedOptions = true;
+    }
+
+    boolean doesAllowsUnrecognizedOptions() {
+        return allowsUnrecognizedOptions;
+    }
+
+    public void recognizeAlternativeLongOptions( boolean recognize ) {
+        if ( recognize )
+            recognize( new AlternativeLongOptionSpec() );
+        else
+            recognizedOptions.remove( String.valueOf( RESERVED_FOR_EXTENSIONS ) );
+    }
+
+    void recognize( AbstractOptionSpec<?> spec ) {
+        recognizedOptions.putAll( spec.options(), spec );
+        trainingOrder.add( spec );
+    }
+
+    /**
+     * Writes information about the options this parser recognizes to the given output sink.
+     *
+     * The output sink is flushed, but not closed.
+     *
+     * @param sink the sink to write information to
+     * @throws IOException if there is a problem writing to the sink
+     * @throws NullPointerException if {@code sink} is {@code null}
+     * @see #printHelpOn(Writer)
+     */
+    public void printHelpOn( OutputStream sink ) throws IOException {
+        printHelpOn( new OutputStreamWriter( sink ) );
+    }
+
+    /**
+     * Writes information about the options this parser recognizes to the given output sink.
+     *
+     * The output sink is flushed, but not closed.
+     *
+     * @param sink the sink to write information to
+     * @throws IOException if there is a problem writing to the sink
+     * @throws NullPointerException if {@code sink} is {@code null}
+     * @see #printHelpOn(OutputStream)
+     */
+    public void printHelpOn( Writer sink ) throws IOException {
+        sink.write( helpFormatter.format( _recognizedOptions() ) );
+        sink.flush();
+    }
+
+    /**
+     * Tells the parser to use the given formatter when asked to {@linkplain #printHelpOn(java.io.Writer) print help}.
+     *
+     * @param formatter the formatter to use for printing help
+     * @throws NullPointerException if the formatter is {@code null}
+     */
+    public void formatHelpWith( HelpFormatter formatter ) {
+        if ( formatter == null )
+            throw new NullPointerException();
+
+        helpFormatter = formatter;
+    }
+
+    /**
+     * Retrieves all options-spec pairings which have been configured for the parser in the same order as declared
+     * during training. Option flags for specs are alphabetized by {@link OptionSpec#options()}; only the order of the
+     * specs is preserved.
+     *
+     * (Note: prior to 4.7 the order was alphabetical across all options regardless of spec.)
+     *
+     * @return a map containing all the configured options and their corresponding {@link OptionSpec}
+     * @since 4.6
+     */
+    public Map<String, OptionSpec<?>> recognizedOptions() {
+        return new LinkedHashMap<String, OptionSpec<?>>( _recognizedOptions() );
+    }
+
+    private Map<String, AbstractOptionSpec<?>> _recognizedOptions() {
+        Map<String, AbstractOptionSpec<?>> options = new LinkedHashMap<>();
+        for ( AbstractOptionSpec<?> spec : trainingOrder ) {
+            for ( String option : spec.options() )
+                options.put( option, spec );
+        }
+        return options;
+    }
+
+   /**
+     * Parses the given command line arguments according to the option specifications given to the parser.
+     *
+     * @param arguments arguments to parse
+     * @return an {@link OptionSet} describing the parsed options, their arguments, and any non-option arguments found
+     * @throws OptionException if problems are detected while parsing
+     * @throws NullPointerException if the argument list is {@code null}
+     */
+    public OptionSet parse( String... arguments ) {
+        ArgumentList argumentList = new ArgumentList( arguments );
+        OptionSet detected = new OptionSet( recognizedOptions.toJavaUtilMap() );
+        detected.add( recognizedOptions.get( NonOptionArgumentSpec.NAME ) );
+
+        while ( argumentList.hasMore() )
+            state.handleArgument( this, argumentList, detected );
+
+        reset();
+
+        ensureRequiredOptions( detected );
+        ensureAllowedOptions( detected );
+
+        return detected;
+    }
+
+    /**
+     * Mandates mutual exclusiveness for the options built by the specified builders.
+     *
+     * @param specs descriptors for options that should be mutually exclusive on a command line.
+     * @throws NullPointerException if {@code specs} is {@code null}
+     */
+    public void mutuallyExclusive( OptionSpecBuilder... specs ) {
+        for ( int i = 0; i < specs.length; i++ ) {
+            for ( int j = 0; j < specs.length; j++ ) {
+                if ( i != j )
+                    specs[i].availableUnless( specs[j] );
+            }
+        }
+    }
+
+    private void ensureRequiredOptions( OptionSet options ) {
+        List<AbstractOptionSpec<?>> missingRequiredOptions = missingRequiredOptions(options);
+        boolean helpOptionPresent = isHelpOptionPresent( options );
+
+        if ( !missingRequiredOptions.isEmpty() && !helpOptionPresent )
+            throw new MissingRequiredOptionsException( missingRequiredOptions );
+    }
+
+    private void ensureAllowedOptions( OptionSet options ) {
+        List<AbstractOptionSpec<?>> forbiddenOptions = unavailableOptions( options );
+        boolean helpOptionPresent = isHelpOptionPresent( options );
+
+        if ( !forbiddenOptions.isEmpty() && !helpOptionPresent )
+            throw new UnavailableOptionException( forbiddenOptions );
+    }
+
+    private List<AbstractOptionSpec<?>> missingRequiredOptions( OptionSet options ) {
+        List<AbstractOptionSpec<?>> missingRequiredOptions = new ArrayList<>();
+
+        for ( AbstractOptionSpec<?> each : recognizedOptions.toJavaUtilMap().values() ) {
+            if ( each.isRequired() && !options.has( each ) )
+                missingRequiredOptions.add(each);
+        }
+
+        for ( Map.Entry<List<String>, Set<OptionSpec<?>>> each : requiredIf.entrySet() ) {
+            AbstractOptionSpec<?> required = specFor( each.getKey().iterator().next() );
+
+            if ( optionsHasAnyOf( options, each.getValue() ) && !options.has( required ) )
+                missingRequiredOptions.add( required );
+        }
+
+        for ( Map.Entry<List<String>, Set<OptionSpec<?>>> each : requiredUnless.entrySet() ) {
+            AbstractOptionSpec<?> required = specFor(each.getKey().iterator().next());
+
+            if ( !optionsHasAnyOf( options, each.getValue() ) && !options.has( required ) )
+                missingRequiredOptions.add( required );
+        }
+
+        return missingRequiredOptions;
+    }
+
+    private List<AbstractOptionSpec<?>> unavailableOptions(OptionSet options) {
+        List<AbstractOptionSpec<?>> unavailableOptions = new ArrayList<>();
+
+        for ( Map.Entry<List<String>, Set<OptionSpec<?>>> eachEntry : availableIf.entrySet() ) {
+            AbstractOptionSpec<?> forbidden = specFor( eachEntry.getKey().iterator().next() );
+
+            if ( !optionsHasAnyOf( options, eachEntry.getValue() ) && options.has( forbidden ) ) {
+                unavailableOptions.add(forbidden);
+            }
+        }
+
+        for ( Map.Entry<List<String>, Set<OptionSpec<?>>> eachEntry : availableUnless.entrySet() ) {
+            AbstractOptionSpec<?> forbidden = specFor( eachEntry.getKey().iterator().next() );
+
+            if ( optionsHasAnyOf( options, eachEntry.getValue() ) && options.has( forbidden ) ) {
+                unavailableOptions.add(forbidden);
+            }
+        }
+
+        return unavailableOptions;
+    }
+
+    private boolean optionsHasAnyOf( OptionSet options, Collection<OptionSpec<?>> specs ) {
+        for ( OptionSpec<?> each : specs ) {
+            if ( options.has( each ) )
+                return true;
+        }
+
+        return false;
+    }
+
+    private boolean isHelpOptionPresent( OptionSet options ) {
+        boolean helpOptionPresent = false;
+
+        for ( AbstractOptionSpec<?> each : recognizedOptions.toJavaUtilMap().values() ) {
+            if ( each.isForHelp() && options.has( each ) ) {
+                helpOptionPresent = true;
+                break;
+            }
+        }
+
+        return helpOptionPresent;
+    }
+
+    void handleLongOptionToken( String candidate, ArgumentList arguments, OptionSet detected ) {
+        KeyValuePair optionAndArgument = parseLongOptionWithArgument( candidate );
+
+        if ( !isRecognized( optionAndArgument.key ) )
+            throw unrecognizedOption( optionAndArgument.key );
+
+        AbstractOptionSpec<?> optionSpec = specFor( optionAndArgument.key );
+        optionSpec.handleOption( this, arguments, detected, optionAndArgument.value );
+    }
+
+    void handleShortOptionToken( String candidate, ArgumentList arguments, OptionSet detected ) {
+        KeyValuePair optionAndArgument = parseShortOptionWithArgument( candidate );
+
+        if ( isRecognized( optionAndArgument.key ) ) {
+            specFor( optionAndArgument.key ).handleOption( this, arguments, detected, optionAndArgument.value );
+        }
+        else
+            handleShortOptionCluster( candidate, arguments, detected );
+    }
+
+    private void handleShortOptionCluster( String candidate, ArgumentList arguments, OptionSet detected ) {
+        char[] options = extractShortOptionsFrom( candidate );
+        validateOptionCharacters( options );
+
+        for ( int i = 0; i < options.length; i++ ) {
+            AbstractOptionSpec<?> optionSpec = specFor( options[ i ] );
+
+            if ( optionSpec.acceptsArguments() && options.length > i + 1 ) {
+                String detectedArgument = String.valueOf( options, i + 1, options.length - 1 - i );
+                optionSpec.handleOption( this, arguments, detected, detectedArgument );
+                break;
+            }
+
+            optionSpec.handleOption( this, arguments, detected, null );
+        }
+    }
+
+    void handleNonOptionArgument( String candidate, ArgumentList arguments, OptionSet detectedOptions ) {
+        specFor( NonOptionArgumentSpec.NAME ).handleOption( this, arguments, detectedOptions, candidate );
+    }
+
+    void noMoreOptions() {
+        state = OptionParserState.noMoreOptions();
+    }
+
+    boolean looksLikeAnOption( String argument ) {
+        return isShortOptionToken( argument ) || isLongOptionToken( argument );
+    }
+
+    boolean isRecognized( String option ) {
+        return recognizedOptions.contains( option );
+    }
+
+    void requiredIf( List<String> precedentSynonyms, String required ) {
+        requiredIf( precedentSynonyms, specFor( required ) );
+    }
+
+    void requiredIf( List<String> precedentSynonyms, OptionSpec<?> required ) {
+        putDependentOption( precedentSynonyms, required, requiredIf );
+    }
+
+    void requiredUnless( List<String> precedentSynonyms, String required ) {
+        requiredUnless( precedentSynonyms, specFor( required ) );
+    }
+
+    void requiredUnless( List<String> precedentSynonyms, OptionSpec<?> required ) {
+        putDependentOption( precedentSynonyms, required, requiredUnless );
+    }
+
+    void availableIf( List<String> precedentSynonyms, String available ) {
+        availableIf( precedentSynonyms, specFor( available ) );
+    }
+
+    void availableIf( List<String> precedentSynonyms, OptionSpec<?> available) {
+        putDependentOption( precedentSynonyms, available, availableIf );
+    }
+
+    void availableUnless( List<String> precedentSynonyms, String available ) {
+        availableUnless( precedentSynonyms, specFor( available ) );
+    }
+
+    void availableUnless( List<String> precedentSynonyms, OptionSpec<?> available ) {
+        putDependentOption( precedentSynonyms, available, availableUnless );
+    }
+
+    private void putDependentOption( List<String> precedentSynonyms, OptionSpec<?> required,
+        Map<List<String>, Set<OptionSpec<?>>> target ) {
+
+        for ( String each : precedentSynonyms ) {
+            AbstractOptionSpec<?> spec = specFor( each );
+            if ( spec == null )
+                throw new UnconfiguredOptionException( precedentSynonyms );
+        }
+
+        Set<OptionSpec<?>> associated = target.get( precedentSynonyms );
+        if ( associated == null ) {
+            associated = new HashSet<>();
+            target.put( precedentSynonyms, associated );
+        }
+
+        associated.add( required );
+    }
+
+    private AbstractOptionSpec<?> specFor( char option ) {
+        return specFor( String.valueOf( option ) );
+    }
+
+    private AbstractOptionSpec<?> specFor( String option ) {
+        return recognizedOptions.get( option );
+    }
+
+    private void reset() {
+        state = moreOptions( posixlyCorrect );
+    }
+
+    private static char[] extractShortOptionsFrom( String argument ) {
+        char[] options = new char[ argument.length() - 1 ];
+        argument.getChars( 1, argument.length(), options, 0 );
+
+        return options;
+    }
+
+    private void validateOptionCharacters( char[] options ) {
+        for ( char each : options ) {
+            String option = String.valueOf( each );
+
+            if ( !isRecognized( option ) )
+                throw unrecognizedOption( option );
+
+            if ( specFor( option ).acceptsArguments() )
+                return;
+        }
+    }
+
+    private static KeyValuePair parseLongOptionWithArgument( String argument ) {
+        return KeyValuePair.valueOf( argument.substring( 2 ) );
+    }
+
+    private static KeyValuePair parseShortOptionWithArgument( String argument ) {
+        return KeyValuePair.valueOf( argument.substring( 1 ) );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/main/java/joptsimple/OptionParserState.java b/third_party/java/jopt-simple/src/main/java/joptsimple/OptionParserState.java
new file mode 100644
index 0000000..fe1000c
--- /dev/null
+++ b/third_party/java/jopt-simple/src/main/java/joptsimple/OptionParserState.java
@@ -0,0 +1,76 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import static joptsimple.ParserRules.*;
+
+/**
+ * Abstraction of parser state; mostly serves to model how a parser behaves depending on whether end-of-options
+ * has been detected.
+ *
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+abstract class OptionParserState {
+    static OptionParserState noMoreOptions() {
+        return new OptionParserState() {
+            @Override
+            protected void handleArgument( OptionParser parser, ArgumentList arguments, OptionSet detectedOptions ) {
+                parser.handleNonOptionArgument( arguments.next(), arguments, detectedOptions );
+            }
+        };
+    }
+
+    static OptionParserState moreOptions( final boolean posixlyCorrect ) {
+        return new OptionParserState() {
+            @Override
+            protected void handleArgument( OptionParser parser, ArgumentList arguments, OptionSet detectedOptions ) {
+                String candidate = arguments.next();
+                try {
+                    if ( isOptionTerminator( candidate ) ) {
+                        parser.noMoreOptions();
+                        return;
+                    } else if ( isLongOptionToken( candidate ) ) {
+                        parser.handleLongOptionToken( candidate, arguments, detectedOptions );
+                        return;
+                    } else if ( isShortOptionToken( candidate ) ) {
+                        parser.handleShortOptionToken( candidate, arguments, detectedOptions );
+                        return;
+                    }
+                } catch ( UnrecognizedOptionException e ) {
+                    if ( !parser.doesAllowsUnrecognizedOptions() )
+                        throw e;
+                }
+
+                if ( posixlyCorrect )
+                    parser.noMoreOptions();
+
+                parser.handleNonOptionArgument( candidate, arguments, detectedOptions );
+            }
+        };
+    }
+
+    protected abstract void handleArgument( OptionParser parser, ArgumentList arguments, OptionSet detectedOptions );
+}
diff --git a/third_party/java/jopt-simple/src/main/java/joptsimple/OptionSet.java b/third_party/java/jopt-simple/src/main/java/joptsimple/OptionSet.java
new file mode 100644
index 0000000..e67363f
--- /dev/null
+++ b/third_party/java/jopt-simple/src/main/java/joptsimple/OptionSet.java
@@ -0,0 +1,325 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.IdentityHashMap;
+import java.util.List;
+import java.util.Map;
+
+import static java.util.Collections.*;
+import static java.util.Objects.*;
+
+/**
+ * Representation of a group of detected command line options, their arguments, and non-option arguments.
+ *
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class OptionSet {
+    private final List<OptionSpec<?>> detectedSpecs;
+    private final Map<String, AbstractOptionSpec<?>> detectedOptions;
+    private final Map<AbstractOptionSpec<?>, List<String>> optionsToArguments;
+    private final Map<String, AbstractOptionSpec<?>> recognizedSpecs;
+    private final Map<String, List<?>> defaultValues;
+
+    /*
+     * Package-private because clients don't create these.
+     */
+    OptionSet( Map<String, AbstractOptionSpec<?>> recognizedSpecs ) {
+        detectedSpecs = new ArrayList<>();
+        detectedOptions = new HashMap<>();
+        optionsToArguments = new IdentityHashMap<>();
+        defaultValues = defaultValues( recognizedSpecs );
+        this.recognizedSpecs = recognizedSpecs;
+    }
+
+    /**
+     * Tells whether any options were detected.
+     *
+     * @return {@code true} if any options were detected
+     */
+    public boolean hasOptions() {
+        return !( detectedOptions.size() == 1 && detectedOptions.values().iterator().next().representsNonOptions() );
+    }
+
+    /**
+     * Tells whether the given option was detected.
+     *
+     * @param option the option to search for
+     * @return {@code true} if the option was detected
+     * @see #has(OptionSpec)
+     */
+    public boolean has( String option ) {
+        return detectedOptions.containsKey( option );
+    }
+
+    /**
+     * Tells whether the given option was detected.
+     *
+     * <p>This method recognizes only instances of options returned from the fluent interface methods.</p>
+     *
+     * <p>Specifying a {@linkplain ArgumentAcceptingOptionSpec#defaultsTo(Object, Object[])} default argument value}
+     * for an option does not cause this method to return {@code true} if the option was not detected on the command
+     * line.</p>
+     *
+     * @param option the option to search for
+     * @return {@code true} if the option was detected
+     * @see #has(String)
+     */
+    public boolean has( OptionSpec<?> option ) {
+        return optionsToArguments.containsKey( option );
+    }
+
+    /**
+     * Tells whether there are any arguments associated with the given option.
+     *
+     * @param option the option to search for
+     * @return {@code true} if the option was detected and at least one argument was detected for the option
+     * @see #hasArgument(OptionSpec)
+     */
+    public boolean hasArgument( String option ) {
+        AbstractOptionSpec<?> spec = detectedOptions.get( option );
+        return spec != null && hasArgument( spec );
+    }
+
+    /**
+     * Tells whether there are any arguments associated with the given option.
+     *
+     * <p>This method recognizes only instances of options returned from the fluent interface methods.</p>
+     *
+     * <p>Specifying a {@linkplain ArgumentAcceptingOptionSpec#defaultsTo(Object, Object[]) default argument value}
+     * for an option does not cause this method to return {@code true} if the option was not detected on the command
+     * line, or if the option can take an optional argument but did not have one on the command line.</p>
+     *
+     * @param option the option to search for
+     * @return {@code true} if the option was detected and at least one argument was detected for the option
+     * @throws NullPointerException if {@code option} is {@code null}
+     * @see #hasArgument(String)
+     */
+    public boolean hasArgument( OptionSpec<?> option ) {
+        requireNonNull( option );
+
+        List<String> values = optionsToArguments.get( option );
+        return values != null && !values.isEmpty();
+    }
+
+    /**
+     * Gives the argument associated with the given option.  If the option was given an argument type, the argument
+     * will take on that type; otherwise, it will be a {@link String}.
+     *
+     * <p>Specifying a {@linkplain ArgumentAcceptingOptionSpec#defaultsTo(Object, Object[]) default argument value}
+     * for an option will cause this method to return that default value even if the option was not detected on the
+     * command line, or if the option can take an optional argument but did not have one on the command line.</p>
+     *
+     * @param option the option to search for
+     * @return the argument of the given option; {@code null} if no argument is present, or that option was not
+     * detected
+     * @throws NullPointerException if {@code option} is {@code null}
+     * @throws OptionException if more than one argument was detected for the option
+     */
+    public Object valueOf( String option ) {
+        requireNonNull( option );
+
+        AbstractOptionSpec<?> spec = detectedOptions.get( option );
+        if ( spec == null ) {
+            List<?> defaults = defaultValuesFor( option );
+            return defaults.isEmpty() ? null : defaults.get( 0 );
+        }
+
+        return valueOf( spec );
+    }
+
+    /**
+     * Gives the argument associated with the given option.
+     *
+     * <p>This method recognizes only instances of options returned from the fluent interface methods.</p>
+     *
+     * @param <V> represents the type of the arguments the given option accepts
+     * @param option the option to search for
+     * @return the argument of the given option; {@code null} if no argument is present, or that option was not
+     * detected
+     * @throws OptionException if more than one argument was detected for the option
+     * @throws NullPointerException if {@code option} is {@code null}
+     * @throws ClassCastException if the arguments of this option are not of the expected type
+     */
+    public <V> V valueOf( OptionSpec<V> option ) {
+        requireNonNull( option );
+
+        List<V> values = valuesOf( option );
+        switch ( values.size() ) {
+            case 0:
+                return null;
+            case 1:
+                return values.get( 0 );
+            default:
+                throw new MultipleArgumentsForOptionException( option );
+        }
+    }
+
+    /**
+     * <p>Gives any arguments associated with the given option.  If the option was given an argument type, the
+     * arguments will take on that type; otherwise, they will be {@link String}s.</p>
+     *
+     * @param option the option to search for
+     * @return the arguments associated with the option, as a list of objects of the type given to the arguments; an
+     * empty list if no such arguments are present, or if the option was not detected
+     * @throws NullPointerException if {@code option} is {@code null}
+     */
+    public List<?> valuesOf( String option ) {
+        requireNonNull( option );
+
+        AbstractOptionSpec<?> spec = detectedOptions.get( option );
+        return spec == null ? defaultValuesFor( option ) : valuesOf( spec );
+    }
+
+    /**
+     * <p>Gives any arguments associated with the given option.  If the option was given an argument type, the
+     * arguments will take on that type; otherwise, they will be {@link String}s.</p>
+     *
+     * <p>This method recognizes only instances of options returned from the fluent interface methods.</p>
+     *
+     * @param <V> represents the type of the arguments the given option accepts
+     * @param option the option to search for
+     * @return the arguments associated with the option; an empty list if no such arguments are present, or if the
+     * option was not detected
+     * @throws NullPointerException if {@code option} is {@code null}
+     * @throws OptionException if there is a problem converting the option's arguments to the desired type; for
+     * example, if the type does not implement a correct conversion constructor or method
+     */
+    public <V> List<V> valuesOf( OptionSpec<V> option ) {
+        requireNonNull( option );
+
+        List<String> values = optionsToArguments.get( option );
+        if ( values == null || values.isEmpty() )
+            return defaultValueFor( option );
+
+        AbstractOptionSpec<V> spec = (AbstractOptionSpec<V>) option;
+        List<V> convertedValues = new ArrayList<>();
+        for ( String each : values )
+            convertedValues.add( spec.convert( each ) );
+
+        return unmodifiableList( convertedValues );
+    }
+
+    /**
+     * Gives the set of options that were detected, in the form of {@linkplain OptionSpec}s, in the order in which the
+     * options were found on the command line.
+     *
+     * @return the set of detected command line options
+     */
+    public List<OptionSpec<?>> specs() {
+        List<OptionSpec<?>> specs = detectedSpecs;
+        specs.removeAll( singletonList( detectedOptions.get( NonOptionArgumentSpec.NAME ) ) );
+
+        return unmodifiableList( specs );
+    }
+
+    /**
+     * Gives all declared options as a map of string to {@linkplain OptionSpec}.
+     *
+     * @return the declared options as a map
+     */
+    public Map<OptionSpec<?>, List<?>> asMap() {
+        Map<OptionSpec<?>, List<?>> map = new HashMap<>();
+
+        for ( AbstractOptionSpec<?> spec : recognizedSpecs.values() ) {
+            if ( !spec.representsNonOptions() )
+                map.put( spec, valuesOf( spec ) );
+        }
+
+        return unmodifiableMap( map );
+    }
+
+    /**
+     * @return the detected non-option arguments
+     */
+    public List<?> nonOptionArguments() {
+        AbstractOptionSpec<?> spec = detectedOptions.get( NonOptionArgumentSpec.NAME );
+        return valuesOf( spec );
+    }
+
+    void add( AbstractOptionSpec<?> spec ) {
+        addWithArgument( spec, null );
+    }
+
+    void addWithArgument( AbstractOptionSpec<?> spec, String argument ) {
+        detectedSpecs.add( spec );
+
+        for ( String each : spec.options() )
+            detectedOptions.put( each, spec );
+
+        List<String> optionArguments = optionsToArguments.get( spec );
+
+        if ( optionArguments == null ) {
+            optionArguments = new ArrayList<>();
+            optionsToArguments.put( spec, optionArguments );
+        }
+
+        if ( argument != null )
+            optionArguments.add( argument );
+    }
+
+    @Override
+    public boolean equals( Object that ) {
+        if ( this == that )
+            return true;
+
+        if ( that == null || !getClass().equals( that.getClass() ) )
+            return false;
+
+        OptionSet other = (OptionSet) that;
+        Map<AbstractOptionSpec<?>, List<String>> thisOptionsToArguments = new HashMap<>( optionsToArguments );
+        Map<AbstractOptionSpec<?>, List<String>> otherOptionsToArguments = new HashMap<>( other.optionsToArguments );
+        return detectedOptions.equals( other.detectedOptions )
+            && thisOptionsToArguments.equals( otherOptionsToArguments );
+    }
+
+    @Override
+    public int hashCode() {
+        Map<AbstractOptionSpec<?>, List<String>> thisOptionsToArguments = new HashMap<>( optionsToArguments );
+        return detectedOptions.hashCode() ^ thisOptionsToArguments.hashCode();
+    }
+
+    @SuppressWarnings( "unchecked" )
+    private <V> List<V> defaultValuesFor( String option ) {
+        if ( defaultValues.containsKey( option ) )
+            return unmodifiableList( (List<V>) defaultValues.get( option ) );
+
+        return emptyList();
+    }
+
+    private <V> List<V> defaultValueFor( OptionSpec<V> option ) {
+        return defaultValuesFor( option.options().iterator().next() );
+    }
+
+    private static Map<String, List<?>> defaultValues( Map<String, AbstractOptionSpec<?>> recognizedSpecs ) {
+        Map<String, List<?>> defaults = new HashMap<>();
+        for ( Map.Entry<String, AbstractOptionSpec<?>> each : recognizedSpecs.entrySet() )
+            defaults.put( each.getKey(), each.getValue().defaultValues() );
+        return defaults;
+    }
+}
diff --git a/third_party/java/jopt-simple/src/main/java/joptsimple/OptionSpec.java b/third_party/java/jopt-simple/src/main/java/joptsimple/OptionSpec.java
new file mode 100644
index 0000000..70d2d43
--- /dev/null
+++ b/third_party/java/jopt-simple/src/main/java/joptsimple/OptionSpec.java
@@ -0,0 +1,97 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import java.util.List;
+
+/**
+ * Describes options that an option parser recognizes.
+ *
+ * <p>Instances of this interface are returned by the "fluent interface" methods to allow retrieval of option arguments
+ * in a type-safe manner.  Here's an example:</p>
+ * 
+ * <pre><code>
+ *     OptionParser parser = new OptionParser();
+ *     <strong>OptionSpec&lt;Integer&gt;</strong> count =
+ *         parser.accepts( "count" ).withRequiredArg().ofType( Integer.class );
+ *     OptionSet options = parser.parse( "--count", "2" );
+ *     assert options.has( count );
+ *     int countValue = options.valueOf( count );
+ *     assert countValue == count.value( options );
+ *     List&lt;Integer&gt; countValues = options.valuesOf( count );
+ *     assert countValues.equals( count.values( options ) );
+ * </code></pre>
+ *
+ * @param <V> represents the type of the arguments this option accepts
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public interface OptionSpec<V> {
+    /**
+     * Gives any arguments associated with the given option in the given set of detected options.
+     *
+     * <p>Specifying a {@linkplain ArgumentAcceptingOptionSpec#defaultsTo(Object, Object[]) default argument value}
+     * for this option will cause this method to return that default value even if this option was not detected on the
+     * command line, or if this option can take an optional argument but did not have one on the command line.</p>
+     *
+     * @param detectedOptions the detected options to search in
+     * @return the arguments associated with this option; an empty list if no such arguments are present, or if this
+     * option was not detected
+     * @throws OptionException if there is a problem converting this option's arguments to the desired type; for
+     * example, if the type does not implement a correct conversion constructor or method
+     * @throws NullPointerException if {@code detectedOptions} is {@code null}
+     * @see OptionSet#valuesOf(OptionSpec)
+     */
+    List<V> values( OptionSet detectedOptions );
+
+    /**
+     * Gives the argument associated with the given option in the given set of detected options.
+     *
+     * <p>Specifying a {@linkplain ArgumentAcceptingOptionSpec#defaultsTo(Object, Object[]) default argument value}
+     * for this option will cause this method to return that default value even if this option was not detected on the
+     * command line, or if this option can take an optional argument but did not have one on the command line.</p>
+     *
+     * @param detectedOptions the detected options to search in
+     * @return the argument of the this option; {@code null} if no argument is present, or that option was not detected
+     * @throws OptionException if more than one argument was detected for the option
+     * @throws NullPointerException if {@code detectedOptions} is {@code null}
+     * @throws ClassCastException if the arguments of this option are not of the expected type
+     * @see OptionSet#valueOf(OptionSpec)
+     */
+    V value( OptionSet detectedOptions );
+
+    /**
+     * @return the string representations of this option
+     */
+    List<String> options();
+
+    /**
+     * Tells whether this option is designated as a "help" option. The presence of a "help" option on a command line
+     * means that missing "required" options will not cause parsing to fail.
+     *
+     * @return whether this option is designated as a "help" option
+     */
+    boolean isForHelp();
+}
diff --git a/third_party/java/jopt-simple/src/main/java/joptsimple/OptionSpecBuilder.java b/third_party/java/jopt-simple/src/main/java/joptsimple/OptionSpecBuilder.java
new file mode 100644
index 0000000..fd5a15a
--- /dev/null
+++ b/third_party/java/jopt-simple/src/main/java/joptsimple/OptionSpecBuilder.java
@@ -0,0 +1,275 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Allows callers to specify whether a given option accepts arguments (required or optional).
+ *
+ * <p>Instances are returned from {@link OptionParser#accepts(String)} to allow the formation of parser directives as
+ * sentences in a "fluent interface" language.  For example:</p>
+ *
+ * <pre><code>
+ *   OptionParser parser = new OptionParser();
+ *   parser.accepts( "c" ).<strong>withRequiredArg()</strong>.ofType( Integer.class );
+ * </code></pre>
+ *
+ * <p>If no methods are invoked on an instance of this class, then that instance's option will accept no argument.</p>
+ *
+ * <p>Note that you should not use the fluent interface clauses in a way that would defeat the typing of option
+ * arguments:</p>
+ *
+ * <pre><code>
+ *   OptionParser parser = new OptionParser();
+ *   ArgumentAcceptingOptionSpec&lt;String&gt; optionC =
+ *       parser.accepts( "c" ).withRequiredArg();
+ *   <strong>optionC.ofType( Integer.class );  // DON'T THROW AWAY THE TYPE!</strong>
+ *
+ *   String value = parser.parse( "-c", "2" ).valueOf( optionC );  // ClassCastException
+ * </code></pre>
+ *
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class OptionSpecBuilder extends NoArgumentOptionSpec {
+    private final OptionParser parser;
+
+    OptionSpecBuilder( OptionParser parser, List<String> options, String description ) {
+        super( options, description );
+
+        this.parser = parser;
+        attachToParser();
+    }
+
+    private void attachToParser() {
+        parser.recognize( this );
+    }
+
+    /**
+     * Informs an option parser that this builder's option requires an argument.
+     *
+     * @return a specification for the option
+     */
+    public ArgumentAcceptingOptionSpec<String> withRequiredArg() {
+        ArgumentAcceptingOptionSpec<String> newSpec = new RequiredArgumentOptionSpec<>( options(), description() );
+        parser.recognize( newSpec );
+
+        return newSpec;
+    }
+
+    /**
+     * Informs an option parser that this builder's option accepts an optional argument.
+     *
+     * @return a specification for the option
+     */
+    public ArgumentAcceptingOptionSpec<String> withOptionalArg() {
+        ArgumentAcceptingOptionSpec<String> newSpec =
+            new OptionalArgumentOptionSpec<>( options(), description() );
+        parser.recognize( newSpec );
+
+        return newSpec;
+    }
+
+    /**
+     * <p>Informs an option parser that this builder's option is required if the given option is present on the command
+     * line.</p>
+     *
+     * <p>For a given option, you <em>should not</em> mix this with {@link #requiredUnless(String, String...)
+     * requiredUnless} to avoid conflicts.</p>
+     *
+     * @param dependent an option whose presence on a command line makes this builder's option required
+     * @param otherDependents other options whose presence on a command line makes this builder's option required
+     * @return self, so that the caller can add clauses to the fluent interface sentence
+     * @throws OptionException if any of the dependent options haven't been configured in the parser yet
+     */
+    public OptionSpecBuilder requiredIf( String dependent, String... otherDependents ) {
+        List<String> dependents = validatedDependents( dependent, otherDependents );
+        for ( String each : dependents )
+            parser.requiredIf( options(), each );
+
+        return this;
+    }
+
+    /**
+     * <p>Informs an option parser that this builder's option is required if the given option is present on the command
+     * line.</p>
+     *
+     * <p>For a given option, you <em>should not</em> mix this with {@link #requiredUnless(OptionSpec, OptionSpec[])
+     * requiredUnless} to avoid conflicts.</p>
+     *
+     * <p>This method recognizes only instances of options returned from the fluent interface methods.</p>
+     *
+     * @param dependent the option whose presence on a command line makes this builder's option required
+     * @param otherDependents other options whose presence on a command line makes this builder's option required
+     * @return self, so that the caller can add clauses to the fluent interface sentence
+     */
+    public OptionSpecBuilder requiredIf( OptionSpec<?> dependent, OptionSpec<?>... otherDependents ) {
+        parser.requiredIf( options(), dependent );
+        for ( OptionSpec<?> each : otherDependents )
+            parser.requiredIf( options(), each );
+
+        return this;
+    }
+
+    /**
+     * <p>Informs an option parser that this builder's option is required if the given option is absent on the command
+     * line.</p>
+     *
+     * <p>For a given option, you <em>should not</em> mix this with {@link #requiredIf(OptionSpec, OptionSpec[])
+     * requiredIf} to avoid conflicts.</p>
+     *
+     * @param dependent an option whose absence on a command line makes this builder's option required
+     * @param otherDependents other options whose absence on a command line makes this builder's option required
+     * @return self, so that the caller can add clauses to the fluent interface sentence
+     * @throws OptionException if any of the dependent options haven't been configured in the parser yet
+     */
+    public OptionSpecBuilder requiredUnless( String dependent, String... otherDependents ) {
+        List<String> dependents = validatedDependents( dependent, otherDependents );
+        for ( String each : dependents ) {
+            parser.requiredUnless( options(), each );
+        }
+        return this;
+    }
+
+    /**
+     * <p>Informs an option parser that this builder's option is required if the given option is absent on the command
+     * line.</p>
+     *
+     * <p>For a given option, you <em>should not</em> mix this with {@link #requiredIf(OptionSpec, OptionSpec[])
+     * requiredIf} to avoid conflicts.</p>
+     *
+     * <p>This method recognizes only instances of options returned from the fluent interface methods.</p>
+     *
+     * @param dependent the option whose absence on a command line makes this builder's option required
+     * @param otherDependents other options whose absence on a command line makes this builder's option required
+     * @return self, so that the caller can add clauses to the fluent interface sentence
+     */
+    public OptionSpecBuilder requiredUnless( OptionSpec<?> dependent, OptionSpec<?>... otherDependents ) {
+        parser.requiredUnless( options(), dependent );
+        for ( OptionSpec<?> each : otherDependents )
+            parser.requiredUnless( options(), each );
+
+        return this;
+    }
+
+    /**
+     * <p>Informs an option parser that this builder's option is allowed if the given option is present on the command
+     * line.</p>
+     *
+     * <p>For a given option, you <em>should not</em> mix this with {@link #availableUnless(String, String...)
+     * availableUnless} to avoid conflicts.</p>
+     *
+     * @param dependent an option whose presence on a command line makes this builder's option allowed
+     * @param otherDependents other options whose presence on a command line makes this builder's option allowed
+     * @return self, so that the caller can add clauses to the fluent interface sentence
+     * @throws OptionException if any of the dependent options haven't been configured in the parser yet
+     */
+    public OptionSpecBuilder availableIf( String dependent, String... otherDependents ) {
+        List<String> dependents = validatedDependents( dependent, otherDependents );
+        for ( String each : dependents )
+            parser.availableIf( options(), each );
+
+        return this;
+    }
+
+    /**
+     * <p>Informs an option parser that this builder's option is allowed if the given option is present on the command
+     * line.</p>
+     *
+     * <p>For a given option, you <em>should not</em> mix this with {@link #availableUnless(OptionSpec, OptionSpec[])
+     * requiredUnless} to avoid conflicts.</p>
+     *
+     * <p>This method recognizes only instances of options returned from the fluent interface methods.</p>
+     *
+     * @param dependent the option whose presence on a command line makes this builder's option allowed
+     * @param otherDependents other options whose presence on a command line makes this builder's option allowed
+     * @return self, so that the caller can add clauses to the fluent interface sentence
+     */
+    public OptionSpecBuilder availableIf( OptionSpec<?> dependent, OptionSpec<?>... otherDependents ) {
+        parser.availableIf( options(), dependent );
+
+        for ( OptionSpec<?> each : otherDependents )
+            parser.availableIf( options(), each );
+
+        return this;
+    }
+
+    /**
+     * <p>Informs an option parser that this builder's option is allowed if the given option is absent on the command
+     * line.</p>
+     *
+     * <p>For a given option, you <em>should not</em> mix this with {@link #availableIf(OptionSpec, OptionSpec[])
+     * requiredIf} to avoid conflicts.</p>
+     *
+     * @param dependent an option whose absence on a command line makes this builder's option allowed
+     * @param otherDependents other options whose absence on a command line makes this builder's option allowed
+     * @return self, so that the caller can add clauses to the fluent interface sentence
+     * @throws OptionException if any of the dependent options haven't been configured in the parser yet
+     */
+    public OptionSpecBuilder availableUnless( String dependent, String... otherDependents ) {
+        List<String> dependents = validatedDependents( dependent, otherDependents );
+        for ( String each : dependents )
+            parser.availableUnless( options(), each );
+
+        return this;
+    }
+
+    /**
+     * <p>Informs an option parser that this builder's option is allowed if the given option is absent on the command
+     * line.</p>
+     *
+     * <p>For a given option, you <em>should not</em> mix this with {@link #availableIf(OptionSpec, OptionSpec[])
+     * requiredIf} to avoid conflicts.</p>
+     *
+     * <p>This method recognizes only instances of options returned from the fluent interface methods.</p>
+     *
+     * @param dependent the option whose absence on a command line makes this builder's option allowed
+     * @param otherDependents other options whose absence on a command line makes this builder's option allowed
+     * @return self, so that the caller can add clauses to the fluent interface sentence
+     */
+    public OptionSpecBuilder availableUnless( OptionSpec<?> dependent, OptionSpec<?>... otherDependents ) {
+        parser.availableUnless( options(), dependent );
+        for ( OptionSpec<?> each : otherDependents )
+            parser.availableUnless(options(), each);
+
+        return this;
+    }
+
+    private List<String> validatedDependents( String dependent, String... otherDependents ) {
+        List<String> dependents = new ArrayList<>();
+        dependents.add( dependent );
+        Collections.addAll( dependents, otherDependents );
+
+        for ( String each : dependents ) {
+            if ( !parser.isRecognized( each ) )
+                throw new UnconfiguredOptionException( each );
+        }
+
+        return dependents;
+    }
+}
diff --git a/third_party/java/jopt-simple/src/main/java/joptsimple/OptionSpecTokenizer.java b/third_party/java/jopt-simple/src/main/java/joptsimple/OptionSpecTokenizer.java
new file mode 100644
index 0000000..f9e4606
--- /dev/null
+++ b/third_party/java/jopt-simple/src/main/java/joptsimple/OptionSpecTokenizer.java
@@ -0,0 +1,126 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import java.util.NoSuchElementException;
+
+import static joptsimple.ParserRules.*;
+
+/**
+ * Tokenizes a short option specification string.
+ *
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+class OptionSpecTokenizer {
+    private static final char POSIXLY_CORRECT_MARKER = '+';
+    private static final char HELP_MARKER = '*';
+
+    private String specification;
+    private int index;
+
+    OptionSpecTokenizer( String specification ) {
+        if ( specification == null )
+            throw new NullPointerException( "null option specification" );
+
+        this.specification = specification;
+    }
+
+    boolean hasMore() {
+        return index < specification.length();
+    }
+
+    AbstractOptionSpec<?> next() {
+        if ( !hasMore() )
+            throw new NoSuchElementException();
+
+
+        String optionCandidate = String.valueOf( specification.charAt( index ) );
+        index++;
+
+        AbstractOptionSpec<?> spec;
+        if ( RESERVED_FOR_EXTENSIONS.equals( optionCandidate ) ) {
+            spec = handleReservedForExtensionsToken();
+
+            if ( spec != null )
+                return spec;
+        }
+
+        ensureLegalOption( optionCandidate );
+
+        if ( hasMore() ) {
+            boolean forHelp = false;
+            if ( specification.charAt( index ) == HELP_MARKER ) {
+                forHelp = true;
+                ++index;
+            }
+            spec = hasMore() && specification.charAt( index ) == ':'
+                ? handleArgumentAcceptingOption( optionCandidate )
+                : new NoArgumentOptionSpec( optionCandidate );
+            if ( forHelp )
+                spec.forHelp();
+        } else
+            spec = new NoArgumentOptionSpec( optionCandidate );
+
+        return spec;
+    }
+
+    void configure( OptionParser parser ) {
+        adjustForPosixlyCorrect( parser );
+
+        while ( hasMore() )
+            parser.recognize( next() );
+    }
+
+    private void adjustForPosixlyCorrect( OptionParser parser ) {
+        if ( POSIXLY_CORRECT_MARKER == specification.charAt( 0 ) ) {
+            parser.posixlyCorrect( true );
+            specification = specification.substring( 1 );
+        }
+    }
+
+    private AbstractOptionSpec<?> handleReservedForExtensionsToken() {
+        if ( !hasMore() )
+            return new NoArgumentOptionSpec( RESERVED_FOR_EXTENSIONS );
+
+        if ( specification.charAt( index ) == ';' ) {
+            ++index;
+            return new AlternativeLongOptionSpec();
+        }
+
+        return null;
+    }
+
+    private AbstractOptionSpec<?> handleArgumentAcceptingOption( String candidate ) {
+        index++;
+
+        if ( hasMore() && specification.charAt( index ) == ':' ) {
+            index++;
+            return new OptionalArgumentOptionSpec<String>( candidate );
+        }
+
+        return new RequiredArgumentOptionSpec<String>( candidate );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/main/java/joptsimple/OptionalArgumentOptionSpec.java b/third_party/java/jopt-simple/src/main/java/joptsimple/OptionalArgumentOptionSpec.java
new file mode 100644
index 0000000..cd641ed
--- /dev/null
+++ b/third_party/java/jopt-simple/src/main/java/joptsimple/OptionalArgumentOptionSpec.java
@@ -0,0 +1,69 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import java.util.List;
+
+/**
+ * Specification of an option that accepts an optional argument.
+ *
+ * @param <V> represents the type of the arguments this option accepts
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+class OptionalArgumentOptionSpec<V> extends ArgumentAcceptingOptionSpec<V> {
+    OptionalArgumentOptionSpec( String option ) {
+        super( option, false );
+    }
+
+    OptionalArgumentOptionSpec( List<String> options, String description ) {
+        super( options, false, description );
+    }
+
+    @Override
+    protected void detectOptionArgument( OptionParser parser, ArgumentList arguments, OptionSet detectedOptions ) {
+        if ( arguments.hasMore() ) {
+            String nextArgument = arguments.peek();
+
+            if ( !parser.looksLikeAnOption( nextArgument ) && canConvertArgument( nextArgument ) )
+                handleOptionArgument( parser, detectedOptions, arguments );
+            else if ( isArgumentOfNumberType() && canConvertArgument( nextArgument ) )
+                addArguments( detectedOptions, arguments.next() );
+            else
+                detectedOptions.add( this );
+        }
+        else
+            detectedOptions.add( this );
+    }
+
+    private void handleOptionArgument( OptionParser parser, OptionSet detectedOptions, ArgumentList arguments ) {
+        if ( parser.posixlyCorrect() ) {
+            detectedOptions.add( this );
+            parser.noMoreOptions();
+        }
+        else
+            addArguments( detectedOptions, arguments.next() );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/main/java/joptsimple/ParserRules.java b/third_party/java/jopt-simple/src/main/java/joptsimple/ParserRules.java
new file mode 100644
index 0000000..34ef781
--- /dev/null
+++ b/third_party/java/jopt-simple/src/main/java/joptsimple/ParserRules.java
@@ -0,0 +1,84 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import java.util.List;
+
+import static java.lang.Character.*;
+
+/**
+ * Can tell whether or not options are well-formed.
+ *
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+final class ParserRules {
+    static final char HYPHEN_CHAR = '-';
+    static final String HYPHEN = String.valueOf( HYPHEN_CHAR );
+    static final String DOUBLE_HYPHEN = "--";
+    static final String OPTION_TERMINATOR = DOUBLE_HYPHEN;
+    static final String RESERVED_FOR_EXTENSIONS = "W";
+
+    private ParserRules() {
+        throw new UnsupportedOperationException();
+    }
+
+    static boolean isShortOptionToken( String argument ) {
+        return argument.startsWith( HYPHEN )
+            && !HYPHEN.equals( argument )
+            && !isLongOptionToken( argument );
+    }
+
+    static boolean isLongOptionToken( String argument ) {
+        return argument.startsWith( DOUBLE_HYPHEN ) && !isOptionTerminator( argument );
+    }
+
+    static boolean isOptionTerminator( String argument ) {
+        return OPTION_TERMINATOR.equals( argument );
+    }
+
+    static void ensureLegalOption( String option ) {
+        if ( option.startsWith( HYPHEN ) )
+            throw new IllegalOptionSpecificationException( String.valueOf( option ) );
+
+        for ( int i = 0; i < option.length(); ++i )
+            ensureLegalOptionCharacter( option.charAt( i ) );
+    }
+
+    static void ensureLegalOptions( List<String> options ) {
+        for ( String each : options )
+            ensureLegalOption( each );
+    }
+
+    private static void ensureLegalOptionCharacter( char option ) {
+        if ( !( isLetterOrDigit( option ) || isAllowedPunctuation( option ) ) )
+            throw new IllegalOptionSpecificationException( String.valueOf( option ) );
+    }
+
+    private static boolean isAllowedPunctuation( char option ) {
+        String allowedPunctuation = "?._" + HYPHEN_CHAR;
+        return allowedPunctuation.indexOf( option ) != -1;
+    }
+}
diff --git a/third_party/java/jopt-simple/src/main/java/joptsimple/RequiredArgumentOptionSpec.java b/third_party/java/jopt-simple/src/main/java/joptsimple/RequiredArgumentOptionSpec.java
new file mode 100644
index 0000000..4c7ab88
--- /dev/null
+++ b/third_party/java/jopt-simple/src/main/java/joptsimple/RequiredArgumentOptionSpec.java
@@ -0,0 +1,52 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import java.util.List;
+
+/**
+ * Specification of an option that accepts a required argument.
+ *
+ * @param <V> represents the type of the arguments this option accepts
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+class RequiredArgumentOptionSpec<V> extends ArgumentAcceptingOptionSpec<V> {
+    RequiredArgumentOptionSpec( String option ) {
+        super( option, true );
+    }
+
+    RequiredArgumentOptionSpec( List<String> options, String description ) {
+        super( options, true, description );
+    }
+
+    @Override
+    protected void detectOptionArgument( OptionParser parser, ArgumentList arguments, OptionSet detectedOptions ) {
+        if ( !arguments.hasMore() )
+            throw new OptionMissingRequiredArgumentException( this );
+
+        addArguments( detectedOptions, arguments.next() );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/main/java/joptsimple/UnavailableOptionException.java b/third_party/java/jopt-simple/src/main/java/joptsimple/UnavailableOptionException.java
new file mode 100644
index 0000000..b1fa0e3
--- /dev/null
+++ b/third_party/java/jopt-simple/src/main/java/joptsimple/UnavailableOptionException.java
@@ -0,0 +1,45 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import java.util.List;
+
+/**
+ * Thrown when options marked as allowed are specified on the command line, but the options they depend upon are
+ * present/not present.
+ */
+class UnavailableOptionException extends OptionException {
+    private static final long serialVersionUID = -1L;
+
+    UnavailableOptionException( List<? extends OptionSpec<?>> forbiddenOptions ) {
+        super( forbiddenOptions );
+    }
+
+    @Override
+    Object[] messageArguments() {
+        return new Object[] { multipleOptionString() };
+    }
+}
diff --git a/third_party/java/jopt-simple/src/main/java/joptsimple/UnconfiguredOptionException.java b/third_party/java/jopt-simple/src/main/java/joptsimple/UnconfiguredOptionException.java
new file mode 100644
index 0000000..f62d88b
--- /dev/null
+++ b/third_party/java/jopt-simple/src/main/java/joptsimple/UnconfiguredOptionException.java
@@ -0,0 +1,52 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import java.util.List;
+
+import static java.util.Collections.*;
+
+/**
+ * Thrown when an option parser refers to an option that is not in fact configured already on the parser.
+ *
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+class UnconfiguredOptionException extends OptionException {
+    private static final long serialVersionUID = -1L;
+
+    UnconfiguredOptionException( String option ) {
+        this( singletonList( option ) );
+    }
+
+    UnconfiguredOptionException( List<String> options ) {
+        super( options );
+    }
+
+    @Override
+    Object[] messageArguments() {
+        return new Object[] { multipleOptionString() };
+    }
+}
diff --git a/third_party/java/jopt-simple/src/main/java/joptsimple/UnrecognizedOptionException.java b/third_party/java/jopt-simple/src/main/java/joptsimple/UnrecognizedOptionException.java
new file mode 100644
index 0000000..73e7d74
--- /dev/null
+++ b/third_party/java/jopt-simple/src/main/java/joptsimple/UnrecognizedOptionException.java
@@ -0,0 +1,46 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import static java.util.Collections.*;
+
+/**
+ * Thrown when the option parser encounters an unrecognized option.
+ *
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+class UnrecognizedOptionException extends OptionException {
+    private static final long serialVersionUID = -1L;
+
+    UnrecognizedOptionException( String option ) {
+        super( singletonList( option ) );
+    }
+
+    @Override
+    Object[] messageArguments() {
+        return new Object[] { singleOptionString() };
+    }
+}
diff --git a/third_party/java/jopt-simple/src/main/java/joptsimple/ValueConversionException.java b/third_party/java/jopt-simple/src/main/java/joptsimple/ValueConversionException.java
new file mode 100644
index 0000000..50d575d
--- /dev/null
+++ b/third_party/java/jopt-simple/src/main/java/joptsimple/ValueConversionException.java
@@ -0,0 +1,54 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+/**
+ * Thrown by {@link ValueConverter}s when problems occur in converting string values to other Java types.
+ *
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class ValueConversionException extends RuntimeException {
+    private static final long serialVersionUID = -1L;
+
+    /**
+     * Creates a new exception with the specified detail message.
+     *
+     * @param message the detail message
+     */
+    public ValueConversionException( String message ) {
+        this( message, null );
+    }
+
+    /**
+     * Creates a new exception with the specified detail message and cause.
+     *
+     * @param message the detail message
+     * @param cause the original exception
+     */
+    public ValueConversionException( String message, Throwable cause ) {
+        super( message, cause );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/main/java/joptsimple/ValueConverter.java b/third_party/java/jopt-simple/src/main/java/joptsimple/ValueConverter.java
new file mode 100644
index 0000000..bc968da
--- /dev/null
+++ b/third_party/java/jopt-simple/src/main/java/joptsimple/ValueConverter.java
@@ -0,0 +1,58 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+/**
+ * Instances of this interface are used to convert arguments of options into specific Java types.
+ *
+ * @param <V> constraint on the type of values being converted to
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public interface ValueConverter<V> {
+    /**
+     * Converts the given string value into a Java type.
+     *
+     * @param value the string to convert
+     * @return the converted value
+     * @throws ValueConversionException if a problem occurs while converting the value
+     */
+    V convert( String value );
+
+    /**
+     * Gives the class of the type of values this converter converts to.
+     *
+     * @return the target class for conversion
+     */
+    Class<? extends V> valueType();
+
+    /**
+     * Gives a string that describes the pattern of the values this converter expects, if any.  For example, a date
+     * converter can respond with a {@link java.text.SimpleDateFormat date format string}.
+     *
+     * @return a value pattern, or {@code null} if there's nothing interesting here
+     */
+    String valuePattern();
+}
diff --git a/third_party/java/jopt-simple/src/main/java/joptsimple/internal/AbbreviationMap.java b/third_party/java/jopt-simple/src/main/java/joptsimple/internal/AbbreviationMap.java
new file mode 100644
index 0000000..0d60183
--- /dev/null
+++ b/third_party/java/jopt-simple/src/main/java/joptsimple/internal/AbbreviationMap.java
@@ -0,0 +1,242 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple.internal;
+
+import java.util.Map;
+import java.util.TreeMap;
+
+/**
+ * <p>A map whose keys are strings; when a key/value pair is added to the map, the longest unique abbreviations of that
+ * key are added as well, and associated with the value. Thus:</p>
+ *
+ * <pre>
+ *   <code>
+ *   abbreviations.put( "good", "bye" );
+ *   </code>
+ * </pre>
+ *
+ * <p>would make it such that you could retrieve the value {@code "bye"} from the map using the keys {@code "good"},
+ * {@code "goo"}, {@code "go"}, and {@code "g"}. A subsequent invocation of:</p>
+ * <pre>
+ *   <code>
+ *   abbreviations.put( "go", "fish" );
+ *   </code>
+ * </pre>
+ *
+ * <p>would make it such that you could retrieve the value {@code "bye"} using the keys {@code "good"} and
+ * {@code "goo"}, and the value {@code "fish"} using the key {@code "go"}.  The key {@code "g"} would yield
+ * {@code null}, since it would no longer be a unique abbreviation.</p>
+ *
+ * <p>The data structure is much like a "trie".</p>
+ *
+ * @param <V> a constraint on the types of the values in the map
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ * @see <a href="http://perldoc.perl.org/Text/Abbrev.html">Perl's Text::Abbrev module</a>
+ * @see <a href="https://en.wikipedia.org/wiki/Radix_tree">Radix tree</a>
+ */
+public class AbbreviationMap<V> implements OptionNameMap<V> {
+    private final Map<Character, AbbreviationMap<V>> children = new TreeMap<>();
+
+    private String key;
+    private V value;
+    private int keysBeyond;
+
+    /**
+     * <p>Tells whether the given key is in the map, or whether the given key is a unique
+     * abbreviation of a key that is in the map.</p>
+     *
+     * @param key key to look up
+     * @return {@code true} if {@code key} is present in the map
+     * @throws NullPointerException if {@code key} is {@code null}
+     */
+    @Override
+    public boolean contains(String key) {
+        return get(key) != null;
+    }
+
+    /**
+     * <p>Answers the value associated with the given key.  The key can be a unique
+     * abbreviation of a key that is in the map. </p>
+     *
+     * @param key key to look up
+     * @return the value associated with {@code aKey}; or {@code null} if there is no
+     * such value or {@code aKey} is not a unique abbreviation of a key in the map
+     * @throws NullPointerException if {@code aKey} is {@code null}
+     */
+    @Override
+    public V get( String key ) {
+        char[] chars = charsOf( key );
+
+        AbbreviationMap<V> child = this;
+        for ( char each : chars ) {
+            child = child.children.get( each );
+            if ( child == null )
+                return null;
+        }
+
+        return child.value;
+    }
+
+    /**
+     * <p>Associates a given value with a given key.  If there was a previous
+     * association, the old value is replaced with the new one.</p>
+     *
+     * @param key key to create in the map
+     * @param newValue value to associate with the key
+     * @throws NullPointerException if {@code aKey} or {@code newValue} is {@code null}
+     * @throws IllegalArgumentException if {@code aKey} is a zero-length string
+     */
+    @Override
+    public void put( String key, V newValue ) {
+        if ( newValue == null )
+            throw new NullPointerException();
+        if ( key.length() == 0 )
+            throw new IllegalArgumentException();
+
+        char[] chars = charsOf(key);
+        add( chars, newValue, 0, chars.length );
+    }
+
+    /**
+     * <p>Associates a given value with a given set of keys.  If there was a previous
+     * association, the old value is replaced with the new one.</p>
+     *
+     * @param keys keys to create in the map
+     * @param newValue value to associate with the key
+     * @throws NullPointerException if {@code keys} or {@code newValue} is {@code null}
+     * @throws IllegalArgumentException if any of {@code keys} is a zero-length string
+     */
+    @Override
+    public void putAll( Iterable<String> keys, V newValue ) {
+        for ( String each : keys )
+            put( each, newValue );
+    }
+
+    private boolean add( char[] chars, V newValue, int offset, int length ) {
+        if ( offset == length ) {
+            value = newValue;
+            boolean wasAlreadyAKey = key != null;
+            key = new String( chars );
+            return !wasAlreadyAKey;
+        }
+
+        char nextChar = chars[ offset ];
+        AbbreviationMap<V> child = children.get( nextChar );
+        if ( child == null ) {
+            child = new AbbreviationMap<>();
+            children.put( nextChar, child );
+        }
+
+        boolean newKeyAdded = child.add( chars, newValue, offset + 1, length );
+
+        if ( newKeyAdded )
+            ++keysBeyond;
+
+        if ( key == null )
+            value = keysBeyond > 1 ? null : newValue;
+
+        return newKeyAdded;
+    }
+
+    /**
+     * <p>If the map contains the given key, dissociates the key from its value.</p>
+     *
+     * @param key key to remove
+     * @throws NullPointerException if {@code aKey} is {@code null}
+     * @throws IllegalArgumentException if {@code aKey} is a zero-length string
+     */
+    @Override
+    public void remove( String key ) {
+        if ( key.length() == 0 )
+            throw new IllegalArgumentException();
+
+        char[] keyChars = charsOf(key);
+        remove( keyChars, 0, keyChars.length );
+    }
+
+    private boolean remove( char[] aKey, int offset, int length ) {
+        if ( offset == length )
+            return removeAtEndOfKey();
+
+        char nextChar = aKey[ offset ];
+        AbbreviationMap<V> child = children.get( nextChar );
+        if ( child == null || !child.remove( aKey, offset + 1, length ) )
+            return false;
+
+        --keysBeyond;
+        if ( child.keysBeyond == 0 )
+            children.remove( nextChar );
+        if ( keysBeyond == 1 && key == null )
+            setValueToThatOfOnlyChild();
+
+        return true;
+    }
+
+    private void setValueToThatOfOnlyChild() {
+        Map.Entry<Character, AbbreviationMap<V>> entry = children.entrySet().iterator().next();
+        AbbreviationMap<V> onlyChild = entry.getValue();
+        value = onlyChild.value;
+    }
+
+    private boolean removeAtEndOfKey() {
+        if ( key == null )
+            return false;
+
+        key = null;
+        if ( keysBeyond == 1 )
+            setValueToThatOfOnlyChild();
+        else
+            value = null;
+
+        return true;
+    }
+
+    /**
+     * Gives a Java map representation of this abbreviation map.
+     *
+     * @return a Java map corresponding to this abbreviation map
+     */
+    @Override
+    public Map<String, V> toJavaUtilMap() {
+        Map<String, V> mappings = new TreeMap<>();
+        addToMappings( mappings );
+        return mappings;
+    }
+
+    private void addToMappings( Map<String, V> mappings ) {
+        if ( key != null )
+            mappings.put( key, value );
+
+        for ( AbbreviationMap<V> each : children.values() )
+            each.addToMappings( mappings );
+    }
+
+    private static char[] charsOf( String aKey ) {
+        char[] chars = new char[ aKey.length() ];
+        aKey.getChars( 0, aKey.length(), chars, 0 );
+        return chars;
+    }
+}
diff --git a/third_party/java/jopt-simple/src/main/java/joptsimple/internal/Classes.java b/third_party/java/jopt-simple/src/main/java/joptsimple/internal/Classes.java
new file mode 100644
index 0000000..f7030f8
--- /dev/null
+++ b/third_party/java/jopt-simple/src/main/java/joptsimple/internal/Classes.java
@@ -0,0 +1,75 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple.internal;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public final class Classes {
+    private static final Map<Class<?>, Class<?>> WRAPPERS = new HashMap<>( 13 );
+
+    static {
+        WRAPPERS.put( boolean.class, Boolean.class );
+        WRAPPERS.put( byte.class, Byte.class );
+        WRAPPERS.put( char.class, Character.class );
+        WRAPPERS.put( double.class, Double.class );
+        WRAPPERS.put( float.class, Float.class );
+        WRAPPERS.put( int.class, Integer.class );
+        WRAPPERS.put( long.class, Long.class );
+        WRAPPERS.put( short.class, Short.class );
+        WRAPPERS.put( void.class, Void.class );
+    }
+
+    private Classes() {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Gives the "short version" of the given class name.  Somewhat naive to inner classes.
+     *
+     * @param className class name to chew on
+     * @return the short name of the class
+     */
+    public static String shortNameOf( String className ) {
+        return className.substring( className.lastIndexOf( '.' ) + 1 );
+    }
+
+    /**
+     * Gives the primitive wrapper class for the given class. If the given class is not
+     * {@linkplain Class#isPrimitive() primitive}, returns the class itself.
+     *
+     * @param <T> generic class type
+     * @param clazz the class to check
+     * @return primitive wrapper type if {@code clazz} is primitive, otherwise {@code clazz}
+     */
+    @SuppressWarnings( "unchecked" )
+    public static <T> Class<T> wrapperOf( Class<T> clazz ) {
+        return clazz.isPrimitive() ? (Class<T>) WRAPPERS.get( clazz ) : clazz;
+    }
+}
diff --git a/third_party/java/jopt-simple/src/main/java/joptsimple/internal/Columns.java b/third_party/java/jopt-simple/src/main/java/joptsimple/internal/Columns.java
new file mode 100644
index 0000000..a012b60
--- /dev/null
+++ b/third_party/java/jopt-simple/src/main/java/joptsimple/internal/Columns.java
@@ -0,0 +1,106 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple.internal;
+
+import java.text.BreakIterator;
+import java.util.ArrayList;
+import java.util.List;
+
+import static java.text.BreakIterator.*;
+
+import static joptsimple.internal.Strings.*;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+class Columns {
+    private static final int INDENT_WIDTH = 2;
+
+    private final int optionWidth;
+    private final int descriptionWidth;
+
+    Columns( int optionWidth, int descriptionWidth ) {
+        this.optionWidth = optionWidth;
+        this.descriptionWidth = descriptionWidth;
+    }
+
+    List<Row> fit( Row row ) {
+        List<String> options = piecesOf( row.option, optionWidth );
+        List<String> descriptions = piecesOf( row.description, descriptionWidth );
+
+        List<Row> rows = new ArrayList<>();
+        for ( int i = 0; i < Math.max( options.size(), descriptions.size() ); ++i )
+            rows.add( new Row( itemOrEmpty( options, i ), itemOrEmpty( descriptions, i ) ) );
+
+        return rows;
+    }
+
+    private static String itemOrEmpty( List<String> items, int index ) {
+        return index >= items.size() ? "" : items.get( index );
+    }
+
+    private List<String> piecesOf( String raw, int width ) {
+        List<String> pieces = new ArrayList<>();
+
+        for ( String each : raw.trim().split( LINE_SEPARATOR ) )
+            pieces.addAll( piecesOfEmbeddedLine( each, width ) );
+
+        return pieces;
+    }
+
+    private List<String> piecesOfEmbeddedLine( String line, int width ) {
+        List<String> pieces = new ArrayList<>();
+
+        BreakIterator words = BreakIterator.getLineInstance();
+        words.setText( line );
+
+        StringBuilder nextPiece = new StringBuilder();
+
+        int start = words.first();
+        for ( int end = words.next(); end != DONE; start = end, end = words.next() )
+            nextPiece = processNextWord( line, nextPiece, start, end, width, pieces );
+
+        if ( nextPiece.length() > 0 )
+            pieces.add( nextPiece.toString() );
+
+        return pieces;
+    }
+
+    private StringBuilder processNextWord( String source, StringBuilder nextPiece, int start, int end, int width,
+                                           List<String> pieces ) {
+        StringBuilder augmented = nextPiece;
+
+        String word = source.substring( start, end );
+        if ( augmented.length() + word.length() > width ) {
+            pieces.add( augmented.toString().replaceAll( "\\s+$", "" ) );
+            augmented = new StringBuilder( repeat( ' ', INDENT_WIDTH ) ).append( word );
+        }
+        else
+            augmented.append( word );
+
+        return augmented;
+    }
+}
diff --git a/third_party/java/jopt-simple/src/main/java/joptsimple/internal/ConstructorInvokingValueConverter.java b/third_party/java/jopt-simple/src/main/java/joptsimple/internal/ConstructorInvokingValueConverter.java
new file mode 100644
index 0000000..d3a641b
--- /dev/null
+++ b/third_party/java/jopt-simple/src/main/java/joptsimple/internal/ConstructorInvokingValueConverter.java
@@ -0,0 +1,56 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple.internal;
+
+import java.lang.reflect.Constructor;
+
+import joptsimple.ValueConverter;
+
+import static joptsimple.internal.Reflection.*;
+
+/**
+ * @param <V> constraint on the type of values being converted to
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+class ConstructorInvokingValueConverter<V> implements ValueConverter<V> {
+    private final Constructor<V> ctor;
+
+    ConstructorInvokingValueConverter( Constructor<V> ctor ) {
+        this.ctor = ctor;
+    }
+
+    public V convert( String value ) {
+        return instantiate( ctor, value );
+    }
+
+    public Class<V> valueType() {
+        return ctor.getDeclaringClass();
+    }
+
+    public String valuePattern() {
+        return null;
+    }
+}
diff --git a/third_party/java/jopt-simple/src/main/java/joptsimple/internal/Messages.java b/third_party/java/jopt-simple/src/main/java/joptsimple/internal/Messages.java
new file mode 100644
index 0000000..5bb71fc
--- /dev/null
+++ b/third_party/java/jopt-simple/src/main/java/joptsimple/internal/Messages.java
@@ -0,0 +1,47 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple.internal;
+
+import java.text.MessageFormat;
+import java.util.Locale;
+import java.util.ResourceBundle;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class Messages {
+    private Messages() {
+        throw new UnsupportedOperationException();
+    }
+
+    public static String message( Locale locale, String bundleName, Class<?> type, String key, Object... args ) {
+        ResourceBundle bundle = ResourceBundle.getBundle( bundleName, locale );
+        String template = bundle.getString( type.getName() + '.' + key );
+        MessageFormat format = new MessageFormat( template );
+        format.setLocale( locale );
+        return format.format( args );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/main/java/joptsimple/internal/MethodInvokingValueConverter.java b/third_party/java/jopt-simple/src/main/java/joptsimple/internal/MethodInvokingValueConverter.java
new file mode 100644
index 0000000..503d677
--- /dev/null
+++ b/third_party/java/jopt-simple/src/main/java/joptsimple/internal/MethodInvokingValueConverter.java
@@ -0,0 +1,58 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple.internal;
+
+import java.lang.reflect.Method;
+
+import joptsimple.ValueConverter;
+
+import static joptsimple.internal.Reflection.*;
+
+/**
+ * @param <V> constraint on the type of values being converted to
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+class MethodInvokingValueConverter<V> implements ValueConverter<V> {
+    private final Method method;
+    private final Class<V> clazz;
+
+    MethodInvokingValueConverter( Method method, Class<V> clazz ) {
+        this.method = method;
+        this.clazz = clazz;
+    }
+
+    public V convert( String value ) {
+        return clazz.cast( invoke( method, value ) );
+    }
+
+    public Class<V> valueType() {
+        return clazz;
+    }
+
+    public String valuePattern() {
+        return null;
+    }
+}
diff --git a/third_party/java/jopt-simple/src/main/java/joptsimple/internal/OptionNameMap.java b/third_party/java/jopt-simple/src/main/java/joptsimple/internal/OptionNameMap.java
new file mode 100644
index 0000000..1b9e491
--- /dev/null
+++ b/third_party/java/jopt-simple/src/main/java/joptsimple/internal/OptionNameMap.java
@@ -0,0 +1,47 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple.internal;
+
+import java.util.Map;
+
+/**
+ * Map-like interface for storing String-value pairs.
+ *
+ * @param <V> type of values stored in the map
+ */
+public interface OptionNameMap<V> {
+    boolean contains( String key );
+
+    V get( String key );
+
+    void put( String key, V newValue );
+
+    void putAll( Iterable<String> keys, V newValue );
+
+    void remove( String key );
+
+    Map<String, V> toJavaUtilMap();
+}
diff --git a/third_party/java/jopt-simple/src/main/java/joptsimple/internal/Reflection.java b/third_party/java/jopt-simple/src/main/java/joptsimple/internal/Reflection.java
new file mode 100644
index 0000000..5dad882
--- /dev/null
+++ b/third_party/java/jopt-simple/src/main/java/joptsimple/internal/Reflection.java
@@ -0,0 +1,142 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple.internal;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import static java.lang.reflect.Modifier.*;
+
+import joptsimple.ValueConverter;
+
+import static joptsimple.internal.Classes.*;
+
+/**
+ * Helper methods for reflection.
+ *
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public final class Reflection {
+    private Reflection() {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Finds an appropriate value converter for the given class.
+     *
+     * @param <V> a constraint on the class object to introspect
+     * @param clazz class to introspect on
+     * @return a converter method or constructor
+     */
+    public static <V> ValueConverter<V> findConverter( Class<V> clazz ) {
+        Class<V> maybeWrapper = wrapperOf( clazz );
+
+        ValueConverter<V> valueOf = valueOfConverter( maybeWrapper );
+        if ( valueOf != null )
+            return valueOf;
+
+        ValueConverter<V> constructor = constructorConverter( maybeWrapper );
+        if ( constructor != null )
+            return constructor;
+
+        throw new IllegalArgumentException( clazz + " is not a value type" );
+    }
+
+    private static <V> ValueConverter<V> valueOfConverter( Class<V> clazz ) {
+        try {
+            Method valueOf = clazz.getMethod( "valueOf", String.class );
+            if ( meetsConverterRequirements( valueOf, clazz ) )
+                return new MethodInvokingValueConverter<>( valueOf, clazz );
+
+            return null;
+        } catch ( NoSuchMethodException ignored ) {
+            return null;
+        }
+    }
+
+    private static <V> ValueConverter<V> constructorConverter( Class<V> clazz ) {
+        try {
+            return new ConstructorInvokingValueConverter<>( clazz.getConstructor( String.class ) );
+        } catch ( NoSuchMethodException ignored ) {
+            return null;
+        }
+    }
+
+    /**
+     * Invokes the given constructor with the given arguments.
+     *
+     * @param <T> constraint on the type of the objects yielded by the constructor
+     * @param constructor constructor to invoke
+     * @param args arguments to hand to the constructor
+     * @return the result of invoking the constructor
+     * @throws ReflectionException in lieu of the gaggle of reflection-related exceptions
+     */
+    public static <T> T instantiate( Constructor<T> constructor, Object... args ) {
+        try {
+            return constructor.newInstance( args );
+        } catch ( Exception ex ) {
+            throw reflectionException( ex );
+        }
+    }
+
+    /**
+     * Invokes the given static method with the given arguments.
+     *
+     * @param method method to invoke
+     * @param args arguments to hand to the method
+     * @return the result of invoking the method
+     * @throws ReflectionException in lieu of the gaggle of reflection-related exceptions
+     */
+    public static Object invoke( Method method, Object... args ) {
+        try {
+            return method.invoke( null, args );
+        } catch ( Exception ex ) {
+            throw reflectionException( ex );
+        }
+    }
+
+    @SuppressWarnings( "unchecked" )
+    public static <V> V convertWith( ValueConverter<V> converter, String raw ) {
+        return converter == null ? (V) raw : converter.convert( raw );
+    }
+
+    private static boolean meetsConverterRequirements( Method method, Class<?> expectedReturnType ) {
+        int modifiers = method.getModifiers();
+        return isPublic( modifiers ) && isStatic( modifiers ) && expectedReturnType.equals( method.getReturnType() );
+    }
+
+    private static RuntimeException reflectionException( Exception ex ) {
+        if ( ex instanceof IllegalArgumentException )
+            return new ReflectionException( ex );
+        if ( ex instanceof InvocationTargetException )
+            return new ReflectionException( ex.getCause() );
+        if ( ex instanceof RuntimeException )
+            return (RuntimeException) ex;
+
+        return new ReflectionException( ex );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/main/java/joptsimple/internal/ReflectionException.java b/third_party/java/jopt-simple/src/main/java/joptsimple/internal/ReflectionException.java
new file mode 100644
index 0000000..17cd4d6
--- /dev/null
+++ b/third_party/java/jopt-simple/src/main/java/joptsimple/internal/ReflectionException.java
@@ -0,0 +1,39 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple.internal;
+
+/**
+ * This unchecked exception wraps reflection-oriented exceptions.
+ *
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class ReflectionException extends RuntimeException {
+    private static final long serialVersionUID = -2L;
+
+    ReflectionException( Throwable cause ) {
+        super( cause );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/main/java/joptsimple/internal/Row.java b/third_party/java/jopt-simple/src/main/java/joptsimple/internal/Row.java
new file mode 100644
index 0000000..13d8e34
--- /dev/null
+++ b/third_party/java/jopt-simple/src/main/java/joptsimple/internal/Row.java
@@ -0,0 +1,55 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple.internal;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+class Row {
+    final String option;
+    final String description;
+
+    Row( String option, String description ) {
+        this.option = option;
+        this.description = description;
+    }
+
+    @Override
+    public boolean equals( Object that ) {
+        if ( that == this )
+            return true;
+        if ( that == null || !getClass().equals( that.getClass() ) )
+            return false;
+
+        Row other = (Row) that;
+        return option.equals( other.option ) && description.equals( other.description );
+    }
+
+    @Override
+    public int hashCode() {
+        return option.hashCode() ^ description.hashCode();
+    }
+}
diff --git a/third_party/java/jopt-simple/src/main/java/joptsimple/internal/Rows.java b/third_party/java/jopt-simple/src/main/java/joptsimple/internal/Rows.java
new file mode 100644
index 0000000..c2cae82
--- /dev/null
+++ b/third_party/java/jopt-simple/src/main/java/joptsimple/internal/Rows.java
@@ -0,0 +1,103 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple.internal;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static java.lang.Math.*;
+
+import static joptsimple.internal.Strings.*;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class Rows {
+    private final int overallWidth;
+    private final int columnSeparatorWidth;
+    private final List<Row> rows = new ArrayList<>();
+
+    private int widthOfWidestOption;
+    private int widthOfWidestDescription;
+
+    public Rows( int overallWidth, int columnSeparatorWidth ) {
+        this.overallWidth = overallWidth;
+        this.columnSeparatorWidth = columnSeparatorWidth;
+    }
+
+    public void add( String option, String description ) {
+        add( new Row( option, description ) );
+    }
+
+    private void add( Row row ) {
+        rows.add( row );
+        widthOfWidestOption = max( widthOfWidestOption, row.option.length() );
+        widthOfWidestDescription = max( widthOfWidestDescription, row.description.length() );
+    }
+
+    public void reset() {
+        rows.clear();
+        widthOfWidestOption = 0;
+        widthOfWidestDescription = 0;
+    }
+
+    public void fitToWidth() {
+        Columns columns = new Columns( optionWidth(), descriptionWidth() );
+
+        List<Row> fitted = new ArrayList<>();
+        for ( Row each : rows )
+            fitted.addAll( columns.fit( each ) );
+
+        reset();
+
+        for ( Row each : fitted )
+            add( each );
+    }
+
+    public String render() {
+        StringBuilder buffer = new StringBuilder();
+
+        for ( Row each : rows ) {
+            pad( buffer, each.option, optionWidth() ).append( repeat( ' ', columnSeparatorWidth ) );
+            pad( buffer, each.description, descriptionWidth() ).append( LINE_SEPARATOR );
+        }
+
+        return buffer.toString();
+    }
+
+    private int optionWidth() {
+        return min( ( overallWidth - columnSeparatorWidth ) / 2, widthOfWidestOption );
+    }
+
+    private int descriptionWidth() {
+        return min( overallWidth - optionWidth() - columnSeparatorWidth, widthOfWidestDescription );
+    }
+
+    private StringBuilder pad( StringBuilder buffer, String s, int length ) {
+        buffer.append( s ).append( repeat( ' ', length - s.length() ) );
+        return buffer;
+    }
+}
diff --git a/third_party/java/jopt-simple/src/main/java/joptsimple/internal/SimpleOptionNameMap.java b/third_party/java/jopt-simple/src/main/java/joptsimple/internal/SimpleOptionNameMap.java
new file mode 100644
index 0000000..69ad6b5
--- /dev/null
+++ b/third_party/java/jopt-simple/src/main/java/joptsimple/internal/SimpleOptionNameMap.java
@@ -0,0 +1,67 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple.internal;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * <p>An {@code OptionNameMap} which wraps and behaves like {@code HashMap}.</p>
+ */
+public class SimpleOptionNameMap<V> implements OptionNameMap<V> {
+    private final Map<String, V> map = new HashMap<>();
+
+    @Override
+    public boolean contains( String key ) {
+        return map.containsKey( key );
+    }
+
+    @Override
+    public V get( String key ) {
+        return map.get( key );
+    }
+
+    @Override
+    public void put( String key, V newValue ) {
+        map.put( key, newValue );
+    }
+
+    @Override
+    public void putAll( Iterable<String> keys, V newValue ) {
+        for ( String each : keys )
+            map.put( each, newValue );
+    }
+
+    @Override
+    public void remove( String key ) {
+        map.remove( key );
+    }
+
+    @Override
+    public Map<String, V> toJavaUtilMap() {
+        return new HashMap<>( map );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/main/java/joptsimple/internal/Strings.java b/third_party/java/jopt-simple/src/main/java/joptsimple/internal/Strings.java
new file mode 100644
index 0000000..50ea3e4
--- /dev/null
+++ b/third_party/java/jopt-simple/src/main/java/joptsimple/internal/Strings.java
@@ -0,0 +1,115 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple.internal;
+
+import java.util.Iterator;
+
+import static java.lang.System.*;
+import static java.util.Arrays.*;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public final class Strings {
+    public static final String EMPTY = "";
+    public static final String LINE_SEPARATOR = getProperty( "line.separator" );
+
+    private Strings() {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Gives a string consisting of the given character repeated the given number of times.
+     *
+     * @param ch the character to repeat
+     * @param count how many times to repeat the character
+     * @return the resultant string
+     */
+    public static String repeat( char ch, int count ) {
+        StringBuilder buffer = new StringBuilder();
+
+        for ( int i = 0; i < count; ++i )
+            buffer.append( ch );
+
+        return buffer.toString();
+    }
+
+    /**
+     * Tells whether the given string is either {@code} or consists solely of whitespace characters.
+     *
+     * @param target string to check
+     * @return {@code true} if the target string is null or empty
+     */
+    public static boolean isNullOrEmpty( String target ) {
+        return target == null || target.isEmpty();
+    }
+
+
+    /**
+     * Gives a string consisting of a given string prepended and appended with surrounding characters.
+     *
+     * @param target a string
+     * @param begin character to prepend
+     * @param end character to append
+     * @return the surrounded string
+     */
+    public static String surround( String target, char begin, char end ) {
+        return begin + target + end;
+    }
+
+    /**
+     * Gives a string consisting of the elements of a given array of strings, each separated by a given separator
+     * string.
+     *
+     * @param pieces the strings to join
+     * @param separator the separator
+     * @return the joined string
+     */
+    public static String join( String[] pieces, String separator ) {
+        return join( asList( pieces ), separator );
+    }
+
+    /**
+     * Gives a string consisting of the string representations of the elements of a given array of objects,
+     * each separated by a given separator string.
+     *
+     * @param pieces the elements whose string representations are to be joined
+     * @param separator the separator
+     * @return the joined string
+     */
+    public static String join( Iterable<String> pieces, String separator ) {
+        StringBuilder buffer = new StringBuilder();
+
+        for ( Iterator<String> iter = pieces.iterator(); iter.hasNext(); ) {
+            buffer.append( iter.next() );
+
+            if ( iter.hasNext() )
+                buffer.append( separator );
+        }
+
+        return buffer.toString();
+    }
+}
diff --git a/third_party/java/jopt-simple/src/main/java/joptsimple/util/DateConverter.java b/third_party/java/jopt-simple/src/main/java/joptsimple/util/DateConverter.java
new file mode 100644
index 0000000..c3182ae
--- /dev/null
+++ b/third_party/java/jopt-simple/src/main/java/joptsimple/util/DateConverter.java
@@ -0,0 +1,114 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple.util;
+
+import java.text.DateFormat;
+import java.text.ParsePosition;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+
+import joptsimple.ValueConversionException;
+import joptsimple.ValueConverter;
+import joptsimple.internal.Messages;
+
+/**
+ * Converts values to {@link Date}s using a {@link DateFormat} object.
+ *
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class DateConverter implements ValueConverter<Date> {
+    private final DateFormat formatter;
+
+    /**
+     * Creates a converter that uses the given date formatter/parser.
+     *
+     * @param formatter the formatter/parser to use
+     * @throws NullPointerException if {@code formatter} is {@code null}
+     */
+    public DateConverter( DateFormat formatter ) {
+        if ( formatter == null )
+            throw new NullPointerException( "illegal null formatter" );
+
+        this.formatter = formatter;
+    }
+
+    /**
+     * Creates a converter that uses a {@link SimpleDateFormat} with the given date/time pattern.  The date formatter
+     * created is not {@link SimpleDateFormat#setLenient(boolean) lenient}.
+     *
+     * @param pattern expected date/time pattern
+     * @return the new converter
+     * @throws NullPointerException if {@code pattern} is {@code null}
+     * @throws IllegalArgumentException if {@code pattern} is invalid
+     */
+    public static DateConverter datePattern( String pattern ) {
+        SimpleDateFormat formatter = new SimpleDateFormat( pattern );
+        formatter.setLenient( false );
+
+        return new DateConverter( formatter );
+    }
+
+    public Date convert( String value ) {
+        ParsePosition position = new ParsePosition( 0 );
+
+        Date date = formatter.parse( value, position );
+        if ( position.getIndex() != value.length() )
+            throw new ValueConversionException( message( value ) );
+
+        return date;
+    }
+
+    public Class<Date> valueType() {
+        return Date.class;
+    }
+
+    public String valuePattern() {
+        return formatter instanceof SimpleDateFormat
+            ? ( (SimpleDateFormat) formatter ).toPattern()
+            : "";
+    }
+
+    private String message( String value ) {
+        String key;
+        Object[] arguments;
+
+        if ( formatter instanceof SimpleDateFormat ) {
+            key = "with.pattern.message";
+            arguments = new Object[] { value, ( (SimpleDateFormat) formatter ).toPattern() };
+        } else {
+            key = "without.pattern.message";
+            arguments = new Object[] { value };
+        }
+
+        return Messages.message(
+            Locale.getDefault(),
+            "joptsimple.ExceptionMessages",
+            DateConverter.class,
+            key,
+            arguments );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/main/java/joptsimple/util/EnumConverter.java b/third_party/java/jopt-simple/src/main/java/joptsimple/util/EnumConverter.java
new file mode 100644
index 0000000..c75b866
--- /dev/null
+++ b/third_party/java/jopt-simple/src/main/java/joptsimple/util/EnumConverter.java
@@ -0,0 +1,104 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2014 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple.util;
+
+import java.text.MessageFormat;
+import java.util.EnumSet;
+import java.util.Iterator;
+import java.util.ResourceBundle;
+
+import joptsimple.ValueConversionException;
+import joptsimple.ValueConverter;
+
+/**
+ * Converts values to {@link java.lang.Enum}s.
+ *
+ * @author <a href="mailto:christian.ohr@gmail.com">Christian Ohr</a>
+ */
+public abstract class EnumConverter<E extends Enum<E>> implements ValueConverter<E> {
+    private final Class<E> clazz;
+
+    private String delimiters = "[,]";
+
+    /**
+     * This constructor must be called by subclasses, providing the enum class as the parameter.
+     *
+     * @param clazz enum class
+     */
+    protected EnumConverter( Class<E> clazz ) {
+        this.clazz = clazz;
+    }
+
+    @Override
+    public E convert( String value ) {
+        for ( E each : valueType().getEnumConstants() ) {
+            if ( each.name().equalsIgnoreCase( value ) ) {
+                return each;
+            }
+        }
+
+        throw new ValueConversionException( message( value ) );
+    }
+
+    @Override
+    public Class<E> valueType() {
+        return clazz;
+    }
+
+    /**
+     * Sets the delimiters for the message string. Must be a 3-letter string,
+     * where the first character is the prefix, the second character is the
+     * delimiter between the values, and the 3rd character is the suffix.
+     *
+     * @param delimiters delimiters for message string. Default is [,]
+     */
+    public void setDelimiters( String delimiters ) {
+        this.delimiters = delimiters;
+    }
+
+    @Override
+    public String valuePattern() {
+        EnumSet<E> values = EnumSet.allOf( valueType() );
+
+        StringBuilder builder = new StringBuilder();
+        builder.append( delimiters.charAt(0) );
+        for ( Iterator<E> i = values.iterator(); i.hasNext(); ) {
+            builder.append( i.next().toString() );
+            if ( i.hasNext() )
+                builder.append( delimiters.charAt( 1 ) );
+        }
+        builder.append( delimiters.charAt( 2 ) );
+
+        return builder.toString();
+    }
+
+    private String message( String value ) {
+        ResourceBundle bundle = ResourceBundle.getBundle( "joptsimple.ExceptionMessages" );
+        Object[] arguments = new Object[] { value, valuePattern() };
+        String template = bundle.getString( EnumConverter.class.getName() + ".message" );
+        return new MessageFormat( template ).format( arguments );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/main/java/joptsimple/util/InetAddressConverter.java b/third_party/java/jopt-simple/src/main/java/joptsimple/util/InetAddressConverter.java
new file mode 100644
index 0000000..2f116f8
--- /dev/null
+++ b/third_party/java/jopt-simple/src/main/java/joptsimple/util/InetAddressConverter.java
@@ -0,0 +1,67 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple.util;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.Locale;
+
+import joptsimple.ValueConversionException;
+import joptsimple.ValueConverter;
+import joptsimple.internal.Messages;
+
+/**
+ * Converts values to {@link java.net.InetAddress} using {@link InetAddress#getByName(String) getByName}.
+ *
+ * @author <a href="mailto:r@ymund.de">Raymund F\u00FCl\u00F6p</a>
+ */
+public class InetAddressConverter implements ValueConverter<InetAddress> {
+    public InetAddress convert( String value ) {
+        try {
+            return InetAddress.getByName( value );
+        }
+        catch ( UnknownHostException e ) {
+            throw new ValueConversionException( message( value ) );
+        }
+    }
+
+    public Class<InetAddress> valueType() {
+        return InetAddress.class;
+    }
+
+    public String valuePattern() {
+        return null;
+    }
+
+    private String message( String value ) {
+        return Messages.message(
+            Locale.getDefault(),
+            "joptsimple.ExceptionMessages",
+            InetAddressConverter.class,
+            "message",
+            value );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/main/java/joptsimple/util/KeyValuePair.java b/third_party/java/jopt-simple/src/main/java/joptsimple/util/KeyValuePair.java
new file mode 100644
index 0000000..bdc2424
--- /dev/null
+++ b/third_party/java/jopt-simple/src/main/java/joptsimple/util/KeyValuePair.java
@@ -0,0 +1,83 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple.util;
+
+import static joptsimple.internal.Strings.*;
+
+/**
+ * <p>A simple string key/string value pair.</p>
+ *
+ * <p>This is useful as an argument type for options whose values take on the form {@code key=value}, such as JVM
+ * command line system properties.</p>
+ *
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public final class KeyValuePair {
+    public final String key;
+    public final String value;
+
+    private KeyValuePair( String key, String value ) {
+        this.key = key;
+        this.value = value;
+    }
+
+    /**
+     * Parses a string assumed to be of the form {@code key=value} into its parts.
+     *
+     * @param asString key-value string
+     * @return a key-value pair
+     * @throws NullPointerException if {@code stringRepresentation} is {@code null}
+     */
+    public static KeyValuePair valueOf( String asString ) {
+        int equalsIndex = asString.indexOf( '=' );
+        if ( equalsIndex == -1 )
+            return new KeyValuePair( asString, EMPTY );
+
+        String aKey = asString.substring( 0, equalsIndex );
+        String aValue = equalsIndex == asString.length() - 1 ? EMPTY : asString.substring( equalsIndex + 1 );
+
+        return new KeyValuePair( aKey, aValue );
+    }
+
+    @Override
+    public boolean equals( Object that ) {
+        if ( !( that instanceof KeyValuePair ) )
+            return false;
+
+        KeyValuePair other = (KeyValuePair) that;
+        return key.equals( other.key ) && value.equals( other.value );
+    }
+
+    @Override
+    public int hashCode() {
+        return key.hashCode() ^ value.hashCode();
+    }
+
+    @Override
+    public String toString() {
+        return key + '=' + value;
+    }
+}
diff --git a/third_party/java/jopt-simple/src/main/java/joptsimple/util/PathConverter.java b/third_party/java/jopt-simple/src/main/java/joptsimple/util/PathConverter.java
new file mode 100644
index 0000000..295bc00
--- /dev/null
+++ b/third_party/java/jopt-simple/src/main/java/joptsimple/util/PathConverter.java
@@ -0,0 +1,51 @@
+package joptsimple.util;
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.text.MessageFormat;
+import java.util.ResourceBundle;
+
+import joptsimple.ValueConversionException;
+import joptsimple.ValueConverter;
+
+/**
+ * Converts command line options to {@link Path} objects and checks the status of the underlying file.
+ */
+public class PathConverter implements ValueConverter<Path> {
+    private final PathProperties[] pathProperties;
+
+    public PathConverter( PathProperties... pathProperties ) {
+        this.pathProperties = pathProperties;
+    }
+
+    @Override
+    public Path convert( String value ) {
+        Path path = Paths.get(value);
+
+        if ( pathProperties != null ) {
+            for ( PathProperties each : pathProperties ) {
+                if ( !each.accept( path ) )
+                    throw new ValueConversionException( message( each.getMessageKey(), path.toString() ) );
+            }
+        }
+
+        return path;
+    }
+
+    @Override
+    public Class<Path> valueType() {
+        return Path.class;
+    }
+
+    @Override
+    public String valuePattern() {
+        return null;
+    }
+
+    private String message( String errorKey, String value ) {
+        ResourceBundle bundle = ResourceBundle.getBundle( "joptsimple.ExceptionMessages" );
+        Object[] arguments = new Object[] { value, valuePattern() };
+        String template = bundle.getString( PathConverter.class.getName() + "." + errorKey + ".message" );
+        return new MessageFormat( template ).format( arguments );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/main/java/joptsimple/util/PathProperties.java b/third_party/java/jopt-simple/src/main/java/joptsimple/util/PathProperties.java
new file mode 100644
index 0000000..a7fb045
--- /dev/null
+++ b/third_party/java/jopt-simple/src/main/java/joptsimple/util/PathProperties.java
@@ -0,0 +1,60 @@
+package joptsimple.util;
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+/**
+ * Enum for checking common conditions of files and directories.
+ *
+ * @see joptsimple.util.PathConverter
+ */
+public enum PathProperties {
+    FILE_EXISTING( "file.existing" ) {
+        @Override
+        boolean accept( Path path ) {
+            return Files.isRegularFile( path );
+        }
+    },
+    DIRECTORY_EXISTING( "directory.existing" ) {
+        @Override
+        boolean accept( Path path ) {
+            return Files.isDirectory( path );
+        }
+    },
+    NOT_EXISTING( "file.not.existing" ) {
+        @Override
+        boolean accept( Path path ) {
+            return Files.notExists( path );
+        }
+    },
+    FILE_OVERWRITABLE( "file.overwritable" ) {
+        @Override
+        boolean accept( Path path ) {
+            return FILE_EXISTING.accept( path ) && WRITABLE.accept( path );
+        }
+    },
+    READABLE( "file.readable" ) {
+        @Override
+        boolean accept( Path path ) {
+            return Files.isReadable( path );
+        }
+    },
+    WRITABLE( "file.writable" ) {
+        @Override
+        boolean accept( Path path ) {
+            return Files.isWritable( path );
+        }
+    };
+
+    private final String messageKey;
+
+    private PathProperties( String messageKey ) {
+        this.messageKey = messageKey;
+    }
+
+    abstract boolean accept( Path path );
+
+    String getMessageKey() {
+        return messageKey;
+    }
+}
diff --git a/third_party/java/jopt-simple/src/main/java/joptsimple/util/RegexMatcher.java b/third_party/java/jopt-simple/src/main/java/joptsimple/util/RegexMatcher.java
new file mode 100644
index 0000000..aca788c
--- /dev/null
+++ b/third_party/java/jopt-simple/src/main/java/joptsimple/util/RegexMatcher.java
@@ -0,0 +1,95 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple.util;
+
+import java.util.Locale;
+import java.util.regex.Pattern;
+
+import static java.util.regex.Pattern.*;
+import static joptsimple.internal.Messages.message;
+
+import joptsimple.ValueConversionException;
+import joptsimple.ValueConverter;
+
+/**
+ * Ensures that values entirely match a regular expression.
+ *
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class RegexMatcher implements ValueConverter<String> {
+    private final Pattern pattern;
+
+    /**
+     * Creates a matcher that uses the given regular expression, modified by the given flags.
+     *
+     * @param pattern the regular expression pattern
+     * @param flags modifying regex flags
+     * @throws IllegalArgumentException if bit values other than those corresponding to the defined match flags are
+     * set in {@code flags}
+     * @throws java.util.regex.PatternSyntaxException if the expression's syntax is invalid
+     */
+    public RegexMatcher( String pattern, int flags ) {
+        this.pattern = compile( pattern, flags );
+    }
+
+    /**
+     * Gives a matcher that uses the given regular expression.
+     *
+     * @param pattern the regular expression pattern
+     * @return the new converter
+     * @throws java.util.regex.PatternSyntaxException if the expression's syntax is invalid
+     */
+    public static ValueConverter<String> regex( String pattern ) {
+        return new RegexMatcher( pattern, 0 );
+    }
+
+    public String convert( String value ) {
+        if ( !pattern.matcher( value ).matches() ) {
+            raiseValueConversionFailure( value );
+        }
+
+        return value;
+    }
+
+    public Class<String> valueType() {
+        return String.class;
+    }
+
+    public String valuePattern() {
+        return pattern.pattern();
+    }
+
+    private void raiseValueConversionFailure( String value ) {
+        String message = message(
+            Locale.getDefault(),
+            "joptsimple.ExceptionMessages",
+            RegexMatcher.class,
+            "message",
+            value,
+            pattern.pattern() );
+        throw new ValueConversionException( message );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/main/resources/joptsimple/ExceptionMessages.properties b/third_party/java/jopt-simple/src/main/resources/joptsimple/ExceptionMessages.properties
new file mode 100644
index 0000000..e5ce44d
--- /dev/null
+++ b/third_party/java/jopt-simple/src/main/resources/joptsimple/ExceptionMessages.properties
@@ -0,0 +1,19 @@
+joptsimple.IllegalOptionSpecificationException.message = {0} is not a legal option character
+joptsimple.MissingRequiredOptionsException.message = Missing required option(s) {0}
+joptsimple.MultipleArgumentsForOptionException.message = Found multiple arguments for option {0}, but you asked for only one
+joptsimple.OptionArgumentConversionException.message = Cannot parse argument ''{0}'' of option {1}
+joptsimple.OptionMissingRequiredArgumentException.message = Option {0} requires an argument
+joptsimple.UnavailableOptionException.message = Option(s) {0} are unavailable given other options on the command line
+joptsimple.UnconfiguredOptionException.message = Option(s) {0} not configured on this parser
+joptsimple.UnrecognizedOptionException.message = {0} is not a recognized option
+joptsimple.util.DateConverter.without.pattern.message = Value [{0}] does not match date/time pattern
+joptsimple.util.DateConverter.with.pattern.message = Value [{0}] does not match date/time pattern [{1}]
+joptsimple.util.RegexMatcher.message = Value [{0}] did not match regex [{1}]
+joptsimple.util.EnumConverter.message = Value [{0}] is not one of [{1}]
+joptsimple.util.PathConverter.file.existing.message = File [{0}] does not exist
+joptsimple.util.PathConverter.directory.existing.message = Directory [{0}] does not exist
+joptsimple.util.PathConverter.file.not.existing.message = File [{0}] does already exist
+joptsimple.util.PathConverter.file.overwritable.message = File [{0}] is not overwritable
+joptsimple.util.PathConverter.file.readable.message = File [{0}] is not readable
+joptsimple.util.PathConverter.file.writable.message = File [{0}] is not writable
+joptsimple.util.InetAddressConverter.message = Cannot convert value [{0}] into an InetAddress
\ No newline at end of file
diff --git a/third_party/java/jopt-simple/src/main/resources/joptsimple/HelpFormatterMessages.properties b/third_party/java/jopt-simple/src/main/resources/joptsimple/HelpFormatterMessages.properties
new file mode 100644
index 0000000..d0faea7
--- /dev/null
+++ b/third_party/java/jopt-simple/src/main/resources/joptsimple/HelpFormatterMessages.properties
@@ -0,0 +1,11 @@
+joptsimple.BuiltinHelpFormatter.no.options.specified = No options specified
+joptsimple.BuiltinHelpFormatter.non.option.arguments.header = Non-option arguments:
+joptsimple.BuiltinHelpFormatter.option.header.with.required.indicator = Option (* = required)
+joptsimple.BuiltinHelpFormatter.option.divider.with.required.indicator = ---------------------
+joptsimple.BuiltinHelpFormatter.option.header = Option
+joptsimple.BuiltinHelpFormatter.option.divider = ------
+joptsimple.BuiltinHelpFormatter.description.header = Description
+joptsimple.BuiltinHelpFormatter.description.divider = -----------
+joptsimple.BuiltinHelpFormatter.default.value.header = default:
+joptsimple.AlternativeLongOptionSpec.description = Alternative form of long options
+joptsimple.AlternativeLongOptionSpec.arg.description = opt=value
diff --git a/third_party/java/jopt-simple/src/site/apt/acknowledgments.apt b/third_party/java/jopt-simple/src/site/apt/acknowledgments.apt
new file mode 100644
index 0000000..ec35ed7
--- /dev/null
+++ b/third_party/java/jopt-simple/src/site/apt/acknowledgments.apt
@@ -0,0 +1,90 @@
+                                  ---------------
+                                  Acknowledgments
+                                  ---------------
+
+    Thanks to the following people who have offered constructive feedback, support, contribution,
+    and/or praise for JOpt Simple:
+
+    * Paul Armstrong
+
+    * Guillaume Aubert
+
+    * David Beckingsale
+
+    * Jochen Bedersdorfer
+
+    * Louis Bergelson
+
+    * Ryan Breidenbach
+
+    * Erik Broes
+
+    * Carlos Cadete
+
+    * Laurent Caillette
+
+    * Matthew Daniel
+
+    * Hans Dockter
+
+    * Kevin C. Dorff
+
+    * Adam Fisk
+
+    * Raymund F\u00FCl\u00F6p
+
+    * Dave Jarvis
+
+    * Paul King
+
+    * El Kodus
+
+    * Alexander Kriegisch
+
+    * Zachary Kurmas
+
+    * Henning Luebbers
+
+    * Derek Mahar
+
+    * Bruno Mascret
+
+    * Antoine Neveux
+
+    * Christian Ohr
+
+    * Michael Osipov
+
+    * Brian Oxley (binkley)
+
+    * Andrew Parker
+
+    * Julien Ponge
+
+    * Rob Reed
+
+    * Mark Reinhold
+
+    * Alex Renger
+
+    * Andrew Robinson
+
+    * Jonathan Shook
+
+    * Jason Smith
+
+    * Emils Solmanis
+
+    * Leo Uzcategui
+
+    * Alan van Dam
+
+    * Douglas Wegscheid
+
+    * Chris K Wensel
+
+    * Ashley Williams
+
+    * Daniel Yokomizo
+
+    []
diff --git a/third_party/java/jopt-simple/src/site/apt/changes.apt b/third_party/java/jopt-simple/src/site/apt/changes.apt
new file mode 100644
index 0000000..2bc0d7d
--- /dev/null
+++ b/third_party/java/jopt-simple/src/site/apt/changes.apt
@@ -0,0 +1,385 @@
+                                       ----------
+                                       Change Log
+                                       ----------
+
+Changes in version 5.0.3
+
+    * Resolved {{{https://github.com/pholser/jopt-simple/issues/96} gh-96}} so that
+      options that take an argument but don't change the argument type from
+      <<<String>>> still show a <<<String>>> type indicator in the default
+      option help.
+
+    * Resolved {{{https://github.com/pholser/jopt-simple/issues/104} gh-104}} so
+      that <<<EnumConverter>>> performs case-insensitive matching against arguments
+      of options. Thanks to Martin Paljak for this.
+
+Changes in version 5.0.2
+
+    * Made some package-private abstract classes become public classes
+      with package-private constructors. This makes it possible to call some methods
+      from Kotlin that were previously inaccessible. Thanks to Mirko Friedenhagen
+      for this.
+
+Changes in version 5.0.1
+
+    * Resolved {{{https://github.com/pholser/jopt-simple/issues/92} gh-92}} so that
+      in the event that <<<printHelpOn()>>> is called more than once, the output
+      is not duplicated.
+
+Changes in version 5.0
+
+    * Abandoning JDKs prior to 7. All source is Java 7-compatible, and bytecode is compiled to 1.7
+      version.
+
+    * Resolved {{{https://github.com/pholser/jopt-simple/issues/51} gh-51}} by adding
+      <<<OptionSpecBuilder.availableIf()>>> and <<<OptionSpecBuilder.availableUnless()>>>. Thanks
+      to Christian Ohr for this.
+
+    * Resolved {{{https://github.com/pholser/jopt-simple/issues/70} gh-70}} by adding
+      <<<EnumConverter>>> and <<<PathConverter>>>. Thanks to Christian Ohr for this.
+
+    * Resolved {{{https://github.com/pholser/jopt-simple/issues/88} gh-88}} by adding
+      an <<<OptionParser>>> constructor to allow suppression of option abbreviations.
+      Thanks to Louis Bergelson for this.
+
+Changes in version 4.10
+
+    * Resolved {{{https://github.com/pholser/jopt-simple/issues/85} gh-85}}
+      by correcting the description column width consumption of the built-in
+      help formatter.
+
+Changes in version 4.9
+
+    * Resolved {{{https://github.com/pholser/jopt-simple/issues/79} gh-79}}
+      by correcting the behavior of <<<OptionException.multipleOptionString>>>
+      when handling options with synonyms.
+
+    * Resolved {{{https://github.com/pholser/jopt-simple/issues/72} gh-72}}
+      by externalizing some of the literal strings in <<<BuiltinHelpFormatter>>>
+      into resource bundles.
+
+    * Resolved {{{https://github.com/pholser/jopt-simple/issues/76} gh-76}}
+      by modifying the behavior of <<<OptionalArgumentOptionSpec>>> so that if
+      the argument following the option does not "look like" an option but can
+      be converted to the option argument's type, treat the argument as the argument
+      of the option.
+
+    * Resolved {{{https://github.com/pholser/jopt-simple/issues/68} gh-68}}
+      by modifying <<<BuiltinHelpFormatter>>> to be more extensible and documenting
+      the behavior of the newly accessible methods.
+
+    * Resolved {{{https://github.com/pholser/jopt-simple/issues/73} gh-73}}
+      by correcting the behavior of <<<OptionSet.specs()>>> to remove all instances
+      of the non-option argument spec.
+
+Changes in version 4.8
+
+    * Resolved {{{https://github.com/pholser/jopt-simple/issues/65} gh-65}}
+      by modifying the build file to create a JAR with OSGi metadata.
+
+    * Resolved {{{https://github.com/pholser/jopt-simple/issues/63} gh-63}}
+      by externalizing exception messages into resource bundles.
+      Currently, we ship only with messages for locale "en_US".
+      Translations for other locales are more than welcome.
+
+    * Resolved {{{https://github.com/pholser/jopt-simple/issues/58} gh-58}} by
+      rewriting exception messaging to clarify synonymous options.
+      Synonyms are be separated by slashes (/); lists of distinct options will
+      be separated by commas (,) in square brackets ([]).
+
+Changes in version 4.7
+
+    * Resolved {{{https://github.com/pholser/jopt-simple/issues/57} gh-57}} by admitting
+      the underscore as a legal option character.
+
+    * Resolved {{{https://github.com/pholser/jopt-simple/issues/53} gh-53}} by correcting
+      <<<OptionSet.hasOptions()>>> to answer correctly for no switches.
+
+    * Resolved {{{https://github.com/pholser/jopt-simple/issues/56} gh-56}} by correcting
+      the default help formatting not to use a <<<Set>>> for rows of output,
+      but <<<List>>>.
+
+    * Resolved {{{https://github.com/pholser/jopt-simple/issues/59} gh-59}} by relaxing
+      the return type of <<<ValueConverter.valueType()>>>.
+
+Changes in version 4.6
+
+    * Resolved {{{https://github.com/pholser/jopt-simple/issues/31} gh-31}} by offering
+      <<<OptionSpecBuilder.requiredUnless()>>>. Thanks to Christian Ohr for this.
+
+    * Resolved {{{https://github.com/pholser/jopt-simple/issues/38} gh-38}} by offering
+      <<<OptionParser.recognizedOptions()>>>. Thanks to Antoine Neveux for this.
+
+    * Resolved {{{https://github.com/pholser/jopt-simple/issues/35} gh-35}} by offering
+      <<<OptionSet.asMap()>>>. Thanks to Brian Oxley for this.
+
+Changes in version 4.5
+
+    * Resolved {{{https://github.com/pholser/jopt-simple/issues/17} gh-17}} by offering
+    <<<OptionParser.nonOptions()>>> and <<<NonOptionArgumentSpec>>>.
+
+    * Resolved {{{https://github.com/pholser/jopt-simple/pull/27} gh-27}} by offering
+    <<<OptionParser.allowsUnrecognizedOptions()>>>. Thanks to Erik Broes for this.
+
+Changes in version 4.4
+
+    * Resolved {{{https://github.com/pholser/jopt-simple/issues/16} gh-16}} by offering
+      <<<BuiltinHelpFormatter>>> that allows configuration of overall row width and
+      column separator width. Thanks to Ryan Breidenbach for contributing code to this.
+
+    * Resolved {{{https://github.com/pholser/jopt-simple/issues/12} gh-12}} by offering
+      <<<OptionSpecBuilder.requiredIf()>>>.
+
+    * Resolved {{{https://github.com/pholser/jopt-simple/issues/22} gh-22}}. Thanks to
+      El Kodus for this.
+
+    * Resolved {{{https://github.com/pholser/jopt-simple/issues/19} gh-19}}. Thanks to
+      El Kodus for this.
+
+    * Resolved {{{https://github.com/pholser/jopt-simple/issues/14} gh-14}} by offering
+      <<<AbstractOptionSpec.forHelp()>>>.
+
+    * Resolved {{{https://github.com/pholser/jopt-simple/issues/13} gh-13}} by offering
+      <<<ArgumentAcceptingOptionSpec.withValuesSeparatedBy(String)>>>.
+
+    * Added <<<InetAddressValueConverter>>>. Thanks to Raymund F\u00FCl\u00F6p for this.
+
+Changes in version 4.3
+
+    * Resolved {{{https://github.com/pholser/jopt-simple/issues/9} gh-9}} by offering
+      <<<OptionSet.specs()>>>, which gives a list of the specs corresponding to the
+      options detected on a parse, in the order in which the options occurred on the
+      command line.
+
+Changes in version 4.2
+
+    * Resolved {{{https://github.com/pholser/jopt-simple/issues/8} gh-8}} by offering
+      <<<ArgumentAcceptingOptionSpec.defaultsTo(V[])>>> in addition to
+      <<<ArgumentAcceptingOptionSpec.defaultsTo(V, V...)>>>.
+
+    * Removing some internal unused classes and methods.
+
+Changes in version 4.1
+
+    * Resolved {{{https://github.com/pholser/jopt-simple/issues/7} gh-7}} by allowing short option
+      clusters to contain options which can accept arguments. When such an option is encountered,
+      the remaining characters in the cluster are treated as the argument to the option. Thanks to
+      Alan van Dam for this.
+
+    * Resolved {{{https://github.com/pholser/jopt-simple/issues/6} gh-6}} with general improvements
+      to the project's site. Thanks to Michael Osipov for this.
+
+Changes in version 4.0
+
+    * Resolved {{{https://github.com/pholser/jopt-simple/issues/4} gh-4}} by adding method
+      <<<OptionParser.formatHelpWith(HelpFormatter)>>> to allow programmers to influence what
+      help is printed with <<<OptionParser.printHelpOn()>>>. A <<<HelpFormatter>>> is handed a
+      map, keyed by option text, whose values are <<<OptionDescriptor>>>s that describe the
+      options the parser has been configured with.
+
+    * Added method <<<OptionSet.hasOptions()>>>. Thanks to Michael Osipov for this.
+
+    * Resolved {{{https://github.com/pholser/jopt-simple/pull/3} gh-3}} by using
+      <<<SimpleDateFormat.toPattern()>>> rather than <<<SimpleDateFormat.toLocalizedPattern()>>>
+      in <<<DateConverter>>>. Thanks to Michael Osipov for this.
+
+    * Resolved {{{https://github.com/pholser/jopt-simple/issues/5} gh-5}} by allowing primitive
+      <<<Class>>> objects whose wrapper types are considered "value types" for purposes of
+      <<<ArgumentAcceptingOptionSpec.ofType()>>> in place of the wrapper classes. Thanks to
+      Daniel Yokomizo for suggesting this.
+
+Changes in version 3.3
+
+    * Resolved {{{https://github.com/pholser/jopt-simple/pull/1} gh-1}} by adding method
+      <<<required()>>> to class <<<ArgumentAcceptingOptionSpec>>>, to allow callers to indicate
+      that a given option must be present on the command line.  Thanks to Emils Solmanis for
+      adding this.
+
+Changes in version 3.2
+
+    * Added method <<<defaultsTo()>>> to class <<<ArgumentAcceptingOptionSpec>>>, to allow
+    callers to specify default values for arguments of options.  These influence the return
+    values of <<<OptionSpec.value(s)()>>> and <<<OptionSet.value(s)Of()>>>.  Default values
+    also show up in the help screen entries for their options.  This serves to resolve
+    {{{https://sourceforge.net/tracker/?func=detail&aid=2484524&group_id=125731&atid=703508} feature
+    request 2484524}}.
+
+    * Fixed a problem whereby the empty string, a string consisting solely of whitespace, or a
+    string with embedded whitespace weren't being properly recognized as arguments of options
+    when they should have.
+
+    * Resolved {{{https://sourceforge.net/tracker/?func=detail&aid=2793762&group_id=125731&atid=703508} feature
+    request 2793762}}, having to do with improving the behavior of embedded newlines in option
+    descriptions in help screens.
+
+Changes in version 3.1
+
+    * Added method <<<withValuesConvertedBy()>>> to class <<<ArgumentAcceptingOptionSpec>>>,
+    to allow callers to specify converters or validators that transform arguments of
+    options into instances of specific Java types.  This is useful for types which do not
+    meet the requirements of <<<ofType()>>>.
+
+    * Added classes <<<DateConverter>>> and <<<RegexMatcher>>> as examples of useful
+    argument converters.
+
+Changes in version 3.0.1
+
+    * Dependency on {{{http://ant.apache.org} Ant}} more appropriately test-scoped in the
+    {{{http://maven.apache.org} Maven}} POM.  That way, if you use JOpt Simple with Maven,
+    you don't download Ant unnecessarily.
+
+Changes in version 3.0
+
+    * Version 3.0 supports Java 5 and greater only.  If you need to use JOpt Simple in a
+    pre-Java-5 environment, use the latest 2.x release.
+
+    * Where appropriate, existing API calls have been updated to use Java generics.
+
+    * Extracted and surfaced interface <<<OptionSpec>>>.  <<<OptionSpec>>>s returned by
+    the fluent interface methods can be used to retrieve arguments of the options they
+    represent in a type-safe manner.
+
+    * Removed all previously deprecated methods.
+
+    * Added methods to <<<OptionSet>>> to allow detection of options and retrieval of
+    option arguments using instances of <<<OptionSpec>>>.
+
+    * Converted tests to JUnit 4.
+
+    * Switched to MIT License.
+
+    * Relaxing some <<<List>>> parameter types and return types of methods to
+    <<<Collection>>>.
+
+    * <<<OptionArgumentConversionException>>> no longer drops the original cause on the
+    floor; hence <<<OptionException>>> can now be created with a cause.
+
+    * {{{http://maven.apache.org} Maven}}-ized the project web site.
+
+    * Overhauled the examples page.
+
+Changes in version 2.4.1
+
+    * Fixed {{{https://sourceforge.net/tracker/index.php?func=detail&aid=2018262&group_id=125731&atid=703505} bug 2018262}}.
+
+Changes in version 2.4
+
+    * Added the <<<acceptsAll()>>> method to class <<<OptionParser>>>.  Options passed to
+    a given invocation of this method are treated as synonymous, so that each gives the
+    same answer when given as the argument to the methods <<<has()>>>, <<<valuesOf()>>>,
+    etc. on <<<OptionSet>>>.
+
+    * Surfaced the class <<<KeyValuePair>>>.  This class can be very handy as a value
+    type for arguments whose values take on the form <<<key=value>>>, such as the <<<-D>>>
+    arguments to JVMs.
+
+    * Fixed {{{https://sourceforge.net/tracker/index.php?func=detail&aid=1932577&group_id=125731&atid=703505} bug 1932577}}.
+
+    * Added method <<<withValuesSeparatedBy()>>> to class <<<ArgumentAcceptingOptionSpec>>>,
+    to allow callers to specify multiple values for an option as a single argument with
+    values separated by a given character.
+
+    * Method <<<ofType()>>> on class <<<ArgumentAcceptingOptionSpec>>> now returns self
+    rather than <<<void>>>.
+
+    * Deprecated another "get for get's sake": <<<OptionParser.setPosixlyCorrect()>>>
+    It is replaced with a less Java-beany-looking method.
+
+    * Cleaned up the help screens produced by <<<OptionParser.printHelpOn()>>> so they are
+    rendered as two 40-character columns, with long space-broken values split across lines
+    as needed.
+
+    * Fixed {{{https://sourceforge.net/tracker/index.php?func=detail&aid=1956418&group_id=125731&atid=703505} bug 1956418}}.
+
+Changes in version 2.3.6
+
+    * No changes to code; just getting {{{http://maven.apache.org} Maven}} to do releases
+    with its plugins.
+
+Changes in version 2.3.3
+
+    * Deprecated method <<<OptionSet.wasDetected()>>> in favor of <<<OptionSet.has()>>> --
+    the new name seems to read better.
+
+    * <<<OptionException>>>s now override <<<getMessage()>>> sensibly, so that if a caller
+    wants to handle the exception by catching it and displaying a message from the
+    exception, she can do so and get satisfactory results.
+
+    * {{{http://maven.apache.org} Maven}}-ized project's build.
+
+    * Minor internal changes.
+
+Changes in version 2.3.2
+
+    * Minor internal changes.
+
+Changes in version 2.3.1
+
+    * Fixed a bug with POSIX-ly correct parsers.  It was previously thought that POSIX-ly
+    correct parsers should signal end of options when they detect an argument that does
+    not lexically look like an option and could not be an argument of a previous option,
+    required or optional.  Such parsers now signal end of options when they detect an
+    argument that does not lexically look like an option, and is not an argument of a
+    previous option with a required argument.  If you want such an argument to be treated
+    as the argument of a preceding option whose argument is optional, you can still get
+    this behavior by appending the argument to the option, either with abutting syntax
+    (<<<-d/tmp>>>) or key-value syntax (<<<-d=/tmp>>>).
+
+Changes in version 2.3
+
+    * No feature changes in this release; but this release can now be used with JDK 1.3.
+    Previous releases could be used only with JDK 1.4 or newer.
+
+Changes in version 2.2
+
+    * Removed all previously deprecated methods.
+
+    * Re-clarified the contract of the one-arg <<<String>>> constructor of
+    <<<OptionParser>>>: the constructor now raises <<<NullPointerException>>> if its
+    argument is <<<null>>>.  This is the convention for Java library methods which receive
+    illegal <<<null>>> parameters.
+
+Changes in version 2.1
+
+    * Introduced a facility for <<<OptionParser>>>s to print a help screen, which
+    describes the options they accept.
+
+    * Added the ability to provide descriptions of options and their arguments when
+    configuring an <<<OptionParser>>>. These descriptions are printed in the
+    aforementioned help screens.
+
+    * Clarified the contract of the one-arg <<<String>>> constructor of <<<OptionParser>>>:
+    the constructor raises <<<IllegalArgumentException>>> if its argument is <<<null>>>.
+
+    * Deprecated <<<OptionParser.noArg()>>>, <<<OptionParser.requiredArg()>>>, and
+    <<<OptionParser.optionalArg()>>>.
+
+    * Deprecated some "gets for gets' sake": <<<OptionException.getOption()>>>,
+    <<<OptionSet.getNonOptionArguments()>>>.  They are replaced with less
+    Java-beany-looking methods.
+
+Changes in version 2.0
+
+    * Introduced a "fluent interface" API for specifying options for an <<<OptionParser>>>
+    to recognize.  The old methods for option specification still work--you need not
+    convert to the new methods.
+
+    * Added the ability to specify that option arguments should be converted to specific
+    types.  You can do this either with the old option specification methods or with the
+    new fluent interface API.
+
+    * Deprecated <<<OptionParser.requiresArg()>>> in favor of
+    <<<OptionParser.requiredArg()>>> -- the new name seems to read better.
+
+Version 1.0
+
+    * First major release.
+
+    * Includes concrete classes <<<OptionParser>>> and <<<OptionSet>>>.
+
+    * Supports POSIX <<<getopt()>>> and GNU <<<getopt_long()>>> command line syntax.
+
+    * No type conversion on option arguments, they are all treated as <<<String>>>s.
+
+    * Methods <<<noArg()>>>, <<<requiredArg()>>>, and <<<optionalArg()>>> tell an
+    <<<OptionParser>>> what options to recognize.
diff --git a/third_party/java/jopt-simple/src/site/apt/developer.apt b/third_party/java/jopt-simple/src/site/apt/developer.apt
new file mode 100644
index 0000000..59d5d82
--- /dev/null
+++ b/third_party/java/jopt-simple/src/site/apt/developer.apt
@@ -0,0 +1,48 @@
+                                   --------------
+                                   Developer Docs
+                                   --------------
+
+Help for developing JOpt Simple
+
+  This page has notes and hints for contributing to JOpt Simple, mostly around getting started and style questions.
+
+* Code Style
+
+  Style is important.  Why?  Chiefly to aid merges.  Reformatting code makes merging more difficult.  When needing to reformat unchanged code, please do so on a separate commit with a clear commit message indicating a non-code change.
+
+  In general use the Eclipse formatter (there is a plugin for IntelliJ IDEA).  See <<<jopt-simple-eclipse-formatting.xml>>> and <<<jopt-simple.importorder>>> in the project root.
+
+  The ultimate arbiter of good style is Paul Holser.
+
+** Braces
+
+  * Yes:
+
++----+
+for (int i = 0; i < 10; ++i) {
+    if (i < 5)
+        println(i);
+}
++----+
+
+  * No:
+
++----+
+for (int i = 0; i < 10; ++i)
+    if (i < 5)
+        println(i);
++----+
+
+* {{Building}}
+
+  There are no integration tests.  Your typical command line is:
+
++----+
+$ mvn clean test
++----+
+
+  Before pushing commits, please check that the site fully builds:
+
++----+
+$ mvn clean post-site
++----+
diff --git a/third_party/java/jopt-simple/src/site/apt/download.apt.vm b/third_party/java/jopt-simple/src/site/apt/download.apt.vm
new file mode 100644
index 0000000..dde9015
--- /dev/null
+++ b/third_party/java/jopt-simple/src/site/apt/download.apt.vm
@@ -0,0 +1,26 @@
+                                       --------
+                                       Download
+                                       --------
+
+    Releases of JOpt Simple are synced to the central Maven repository.  Declare a
+    dependency element in your POM like so:
+
++---------------------------------------------
+...
+<dependencies>
+  ...
+  <dependency>
+    <groupId>net.sf.jopt-simple</groupId>
+    <artifactId>jopt-simple</artifactId>
+    <version>${project.version}</version>
+  </dependency>
+  ...
+</dependencies>
+...
++---------------------------------------------
+
+    If you're not using Maven, you can still download the newer releases from the Maven
+    repository manually, {{{http://repo1.maven.org/maven2/net/sf/jopt-simple/jopt-simple} here}}.
+
+    You can find older releases at
+    {{{http://sourceforge.net/project/showfiles.php?group_id=125731} SourceForge}}.
diff --git a/third_party/java/jopt-simple/src/site/apt/examples.apt b/third_party/java/jopt-simple/src/site/apt/examples.apt
new file mode 100644
index 0000000..3b43dee
--- /dev/null
+++ b/third_party/java/jopt-simple/src/site/apt/examples.apt
@@ -0,0 +1,462 @@
+                                   -----------------
+                                   Examples of Usage
+                                   -----------------
+
+Preamble
+
+    The simplicity in JOpt "Simple" arises from two guiding principles:
+
+    * Stick as often as possible to supporting conventional Unix option syntaxes.
+
+    * Keep the surface area of the published API as small and simple as possible.
+
+    []
+
+    To the first principle: You will not see support in JOpt Simple for option "groups",
+    alternative option prefixes (<<<+>>>, <<</>>>), enforced multiplicity of option
+    arguments, etc.  JOpt Simple believes you can create a useful and understandable CLI
+    without all that stuff.  If you feel as though you need any of those features, there
+    are lots of other choices out there.  The author of JOpt Simple believes you'll want
+    to leverage its easy configuration, parsing, and option interrogation APIs instead of
+    using more feature-laden, but perhaps more confusing libraries.
+
+    To the second principle: JOpt Simple will make every attempt to keep the API free
+    of clutter.  The API is well factored, making it intuitive to use, and the entire
+    library is well tested, making it more reliable and predictable.  If you cannot look
+    at the Javadoc and quickly get a sense of what you need to do to use JOpt Simple,
+    then JOpt Simple has failed.  So by all means, let the author know what needs
+    improved.
+
+    With that said, let's take a tour through JOpt Simple's features.
+
+Options
+
+    JOpt Simple supports short options and long options, using a syntax that attempts to
+    take from the best of POSIX <<<getopt()>>> and GNU <<<getopt_long()>>>.
+
+* Short Options
+
+    Short options begin with a single hyphen (<<<->>>) followed by a single letter or
+    digit, or question mark (<<<?>>>), or dot (<<<.>>>).
+
++----------------------------------------------------------------------------------------
+@SHORT_OPTIONS_EXAMPLE@
++----------------------------------------------------------------------------------------
+
+    When you construct an <<<OptionParser>>> with a string of short option characters,
+    you configure that parser to recognize the options with those characters.
+
+** Arguments of Options
+
+    Short options can accept single arguments.  The argument can be made required or
+    optional.  When you construct an <<<OptionParser>>> with a string of short option
+    characters, append a single colon (<<<:>>>) to an option character to configure
+    that option to require an argument.  Append two colons (<<<::>>>) to an option
+    character to configure that option to accept an optional argument. Append an
+    asterisk (<<<*>>>) to an option character, but before any "argument" indicators,
+    to configure that option as a "help" option.
+
+    The syntax of the option specification string given to the <<<OptionParser>>>
+    constructor should look familiar to you if you have used GNU's <<<getopt()>>>
+    before.
+
++----------------------------------------------------------------------------------------
+@SHORT_OPTIONS_WITH_ARGUMENTS_EXAMPLE@
++----------------------------------------------------------------------------------------
+
+*** Specifying Arguments for a Short Option on the Command Line
+
+    A short option's argument can occur:
+
+    * in the position on the command line after the option
+
+    * right up against the option
+
+    * right up against the option separated by an equals sign (<<<=>>>)
+
+    []
+
++----------------------------------------------------------------------------------------
+@SHORT_OPTIONS_WITH_ARGUMENT_POSITIONING_EXAMPLE@
++----------------------------------------------------------------------------------------
+
+*** Multiple Arguments for a Single Option
+
+    To specify <n> arguments for a single option, specify the option <n> times on the
+    command line, once for each argument.  JOpt Simple reports the arguments given to the
+    option in the order in which they were encountered on the command line.
+
++----------------------------------------------------------------------------------------
+@SHORT_OPTIONS_WITH_MULTIPLE_ARGUMENTS_FOR_SINGLE_OPTION_EXAMPLE@
++----------------------------------------------------------------------------------------
+
+** Clustering Short Options
+
+    Short options can be <clustered> in a single argument.
+
++----------------------------------------------------------------------------------------
+@SHORT_OPTIONS_CLUSTERING_EXAMPLE@
++----------------------------------------------------------------------------------------
+
+    If one of the short options can accept an argument, the remaining characters are interpreted
+    as the argument for that option.
+
++----------------------------------------------------------------------------------------
+@SHORT_OPTIONS_CLUSTERING_WITH_ARGUMENT_EXAMPLE@
++----------------------------------------------------------------------------------------
+
+* Long Options/Fluent Interface
+
+    Long options begin with two hyphens (<<<-->>>), followed by multiple letters,
+    digits, hyphens, question marks, or dots.  A hyphen cannot be the first character of
+    a long option specification when configuring the parser.
+
+    Whereas short options can be configured using a constructor argument to
+    <<<OptionParser>>>, both long and short options can be configured using a "fluent
+    interface" API, that enables some very descriptive and powerful features.
+
++----------------------------------------------------------------------------------------
+@LONG_OPTIONS_EXAMPLE@
++----------------------------------------------------------------------------------------
+
+** Arguments of Options
+
+    Like short options, long options can accept single arguments.  The argument can be
+    made required or optional.  Use the methods <<<withRequiredArg()>>> and
+    <<<withOptionalArg()>>> on the return value of <<<OptionParser.accepts()>>> to signal
+    that an option takes a required or optional argument.
+
++----------------------------------------------------------------------------------------
+@LONG_OPTIONS_WITH_ARGUMENTS_EXAMPLE@
++----------------------------------------------------------------------------------------
+
+** Abbreviating Long Options
+
+    Notice in the example above that the command line uses abbreviations of command line
+    options.  You can abbreviate options so long as the abbreviation is unambiguous.
+    Even though you can abbreviate the options on the command line, you cannot address
+    the <<<OptionSet>>> using those abbreviations. You can use a special constructor
+    for the <<<OptionParser>>> that turns off abbreviation matching.
+
+** Using Single Hyphen on Long Options
+
+    As demonstrated in the example above, you can use a single hyphen instead of a
+    double hyphen to specify a long option -- but be careful that doing so doesn't
+    introduce ambiguity.
+
+*** Specifying Arguments for a Long Option on the Command Line
+
+    A long option's argument can occur:
+
+    * in the position on the command line after the option
+
+    * right up against the option separated by an equals sign (<<<=>>>)
+
+    []
+
++----------------------------------------------------------------------------------------
+@LONG_OPTIONS_WITH_ARGUMENT_POSITIONING_EXAMPLE@
++----------------------------------------------------------------------------------------
+
+*** Multiple Arguments for a Single Option
+
+    Specify multiple arguments for a long option in the same manner as for short options
+    (see above).
+
+** Alternative Form of Long Options
+
+    The option <<<-W>>> is reserved. If you tell the parser to recognize alternative
+    long options, then it will treat, for example, <<<-W foo=bar>>> as the long option
+    <<<foo>>> with argument bar, as though you had written <<<--foo=bar>>>.
+
+    You can specify <<<-W>>> as a valid short option, or use it as an abbreviation for a
+    long option, but recognizing alternative long options will always supersede this
+    behavior.
+
+    To recognize alternative long options, either construct an <<<OptionParser>>> with a
+    string of short option characters containing the sequence <<<W;>>> (a capital W
+    followed by a semicolon), or call the method
+    <<<OptionParser.recognizeAlternativeLongOptions()>>>.
+
++----------------------------------------------------------------------------------------
+@ALTERNATIVE_LONG_OPTIONS_EXAMPLE@
++----------------------------------------------------------------------------------------
+
+* Other Features
+
+** Converting Option Arguments to Other Types
+
+    Without action other than the <<<with*Arg()>>> methods, arguments of options are
+    returned as <<<String>>>s.  For backwards compatibility,
+    <<<OptionSet.valueOf(String)>>> and <<<OptionSet.valuesOf(String)>>> return
+    <<<Object>>> and <<<List<?>>>>, respectively, so to get the values out as
+    <<<String>>>s, you will need to downcast the results of those methods.
+
+    You can tell JOpt Simple to convert the arguments of options to different Java types
+    via the <<<ofType()>>> method on the return value of <<<with*Arg()>>>.  The
+    <<<Class>>> argument of <<<ofType()>>> must represent a Java class that has either:
+
+    * a <<<public static>>> method called <<<valueOf()>>> which accepts a single
+    <<<String>>> argument and whose return type is the type itself, or
+
+    * a <<<public>>> constructor which takes a single <<<String>>> argument.
+
+    []
+
+    If the class has both, the <<<valueOf()>>> method is used.
+
+    Note that <<<enum>>>s have a <<<valueOf()>>> method.
+
++----------------------------------------------------------------------------------------
+@OPTION_ARGUMENT_VALUE_TYPE_EXAMPLE@
++----------------------------------------------------------------------------------------
+
+    Another way to convert arguments of options is to specify a converter object via
+    <<<withValuesConvertedBy()>>>.  This is useful when the desired type for the arguments
+    does not meet the requirements that <<<ofType()>>> sets forth.  Such objects may not
+    perform any "conversion" at all, but rather can validate that arguments conform to
+    certain restrictions before passing through as-is.
+
+    You can also do this for the non-option arguments of your command line, if you desire
+    to treat them all as a single type.
+
++----------------------------------------------------------------------------------------
+@OPTION_ARGUMENT_CONVERTER_EXAMPLE@
++----------------------------------------------------------------------------------------
+
+** Retrieving Arguments of Options in a Type-Safe Manner
+
+    In the previous examples, we have been discarding the return values of the methods
+    of JOpt Simple's fluent interface.  If instead you retain them in variables of type
+    <<<OptionSpec>>>, you can use them to retrieve arguments of options in a type-safe
+    manner.
+
+    You can also do this for the non-option arguments of your command line, if you desire
+    to treat them all as a single type.
+
++----------------------------------------------------------------------------------------
+@TYPESAFE_OPTION_ARGUMENT_RETRIEVAL_EXAMPLE@
++----------------------------------------------------------------------------------------
+
+** Exporting Options and Arguments to other Code
+
+    As an integration aid for other libraries, you can use <<<OptionSet.asMap()>>> to
+    obtain a mapping of <<<OptionSpec>>> to option values, for example to create a
+    properties map of options.
+
+    Here is sample code to create properties whose keys have a common prefix.  The key is
+    choosen as the first non-short option:
+
++----------------------------------------------------------------------------------------
+@EXPORTING_OPTIONS_AND_ARGUMENTS_EXAMPLE@
++----------------------------------------------------------------------------------------
+
+** Default Values for Option Arguments
+
+    Often it is convenient to specify default values for the arguments of certain
+    command line options.  To do this, call the <<<defaultsTo()>>> method.
+
++----------------------------------------------------------------------------------------
+@DEFAULT_VALUES_FOR_OPTION_ARGUMENTS_EXAMPLE@
++----------------------------------------------------------------------------------------
+
+    You can see that <<<defaultsTo()>>> should relieve you of the burden of having to
+    check <<<has()>>> and/or <<<hasArgument()>>> on an <<<OptionSet>>> for a given option,
+    and has no bearing on the return values of those methods.  Specifying a default value
+    for an option with a required argument does not mean that you can elide an argument for
+    the option on the command line.
+
+    The type of values <<<defaultsTo()>>> expects is dictated by the class given by a
+    previous call to <<<ofType()>>> or <<<withValuesConvertedBy()>>>; if no such call has
+    been made, the type is <<<String>>>.
+
+** "Required" Options
+
+    You can indicate that a given option must be present on the command line via the
+    <<<required()>>> method.  Only options that accept arguments can be made "required".
+
+    An option designated as a "help" option via <<<forHelp()>>>, when present on the
+    command line, causes missing "required" options not to reject the command line.
+
++----------------------------------------------------------------------------------------
+@REQUIRED_OPTIONS_EXAMPLE@
++----------------------------------------------------------------------------------------
+
+** "Required" Dependent Options
+
+    You can indicate that a given option must be present on the command line if some
+    other option is present on the command line via the <<<requiredIf()>>> method.
+    Any option can be made "required if".
+
+    An option designated as a "help" option via <<<forHelp()>>>, when present on the
+    command line, causes missing "required if" options not to reject the command line.
+
++----------------------------------------------------------------------------------------
+@REQUIRED_IF_OPTIONS_EXAMPLE@
++----------------------------------------------------------------------------------------
+
+    You can also indicate that a given option must be present on the command line if some
+    other option is NOT present on the command line via the <<<requiredUnless()>>> method.
+    Any option can be made "required unless", but, to avoid potential conflicts, it should
+    not be "required if" at the same time.
+
++----------------------------------------------------------------------------------------
+@REQUIRED_UNLESS_OPTIONS_EXAMPLE@
++----------------------------------------------------------------------------------------
+
+** "Available" Dependent Options
+
+    Similarly to <<<requiredIf()>>> and <<<requiredUnless()>>>, you can indicate that a
+    given option can be present on the command line only if/unless some other option is
+    present on the command line, via the <<<availableIf()>>> and <<<availableUnless()>>>
+    methods.
+
+
+** Synonyms of Options
+
+    Sometimes it is useful to allow many different options to share the same meaning in
+    the program that uses them.  To specify that options are to be treated as synonymous,
+    use the <<<acceptsAll()>>> method of <<<OptionParser>>>.
+
++----------------------------------------------------------------------------------------
+@OPTION_SYNONYM_EXAMPLE@
++----------------------------------------------------------------------------------------
+
+** Concise Specification of Multiple Arguments for an Option
+
+    Another way to specify multiple arguments for an option is to tell the parser to
+    treat a single argument containing multiple delimited values as multiple arguments
+    for the option using the <<<withValuesSeparatedBy()>>> method.
+
++----------------------------------------------------------------------------------------
+@MULTIPLE_DELIMITED_ARGUMENTS_EXAMPLE@
++----------------------------------------------------------------------------------------
+
+** Signalling End of Options
+
+    An argument consisting only of two hyphens (<<<-->>>) signals that the remaining
+    arguments are to be treated as non-options.
+
+    An argument consisting only of a single hyphen is considered a non-option argument
+    (though it can be an argument of an option).  Many Unix programs treat single hyphens
+    as stand-ins for the standard input or standard output stream.
+
+*** Non-Option Arguments
+
+    Any arguments which are not options or arguments of options can be retrieved via
+    method <<<nonOptionArguments()>>> on <<<OptionSet>>>.  If the double hyphen is an
+    argument, it is ignored and is not a non-option argument.
+
++----------------------------------------------------------------------------------------
+@SIGNALLING_END_OF_OPTIONS_EXAMPLE@
++----------------------------------------------------------------------------------------
+
+** "POSIX-ly Correct"-ness
+
+    By default, as with GNU <<<getopt()>>>, JOpt Simple allows intermixing of options and
+    non-options.  If, however, the parser has been created to be "POSIX-ly correct", then
+    the first argument that does not look lexically like an option, and is not a required
+    argument of a preceding option, signals the end of options.  You can still bind
+    optional arguments to their options using the abutting (for short options) or
+    <<<=>>> syntax.
+
+    Unlike GNU <<<getopt()>>>, JOptSimple does not honor the environment variable
+    <<<POSIXLY_CORRECT>>>. "POSIX-ly correct" parsers are configured by either:
+
+    * using the method <<<OptionParser.posixlyCorrect()>>>
+
+    * using the <<<OptionParser>>> constructor with an argument whose first character is
+    a plus sign (<<<+>>>)
+
+    []
+
++----------------------------------------------------------------------------------------
+@POSIXLY_CORRECT_EXAMPLE@
++----------------------------------------------------------------------------------------
+
+** Special Optional Argument Handling
+
+    If the parser detects an option whose argument is optional, and the next argument
+    "looks like" an option, that argument is not treated as the argument to the option,
+    but as a potentially valid option.  If, on the other hand, the optional argument is
+    typed as a derivative of <<<Number>>>, then that argument is treated as the negative
+    number argument of the option, even if the parser recognizes the corresponding numeric
+    option.
+
++----------------------------------------------------------------------------------------
+@SPECIAL_OPTIONAL_ARGUMENT_HANDLING_EXAMPLE@
++----------------------------------------------------------------------------------------
+
+Generating Command Line Help
+
+    When you call method <<<OptionParser.printHelpOn()>>>, JOpt Simple will write a
+    help screen (80-column width) describing all the options it is configured with,
+    along with types of option arguments, whether the option is required (in angle
+    brackets) or optional (in square brackets), etc.  To give an option a description,
+    use <<<OptionParser.accepts*()>>> with a description argument.  To give an option
+    argument a description, use <<<describedAs()>>> on the return value of
+    <<<with*Arg()>>>.
+
++----------------------------------------------------------------------------------------
+@HELP_SCREEN_EXAMPLE@
++----------------------------------------------------------------------------------------
+
+    Here is what the help screen looks like for the example above:
+
++----------------------------------------------------------------------------------------
+@HELP_SCREEN_EXAMPLE_HELP@
++----------------------------------------------------------------------------------------
+
+    If you want to create your own help screen, give method
+    <<<OptionParser.formatHelpWith()>>> a <<<HelpFormatter>>> that builds the help screen
+    as a String. When you call <<<OptionParser.printHelpOn()>>>, JOpt Simple will use
+    your <<<HelpFormatter>>> to produce the help and write it to the given stream.
+
+    For example, this program:
+
++----------------------------------------------------------------------------------------
+@HELP_FORMATTER_EXAMPLE@
++----------------------------------------------------------------------------------------
+
+    yields the following output:
+
++----------------------------------------------------------------------------------------
+@HELP_FORMATTER_EXAMPLE_OUTPUT@
++----------------------------------------------------------------------------------------
+
+Handling Exceptions
+
+    JOpt Simple's classes raise some derivative of <<<OptionException>>> if they encounter
+    problems during parsing.  These exceptions are unchecked, so you don't have to do
+    anything with such an exception if you don't want to.  The rationale behind this
+    decision is that you will most likely be invoking JOpt Simple's functionality from a
+    <<<main()>>> method or very near to it, where a failure such as unrecognized arguments
+    can just stop down the JVM and yield a stack trace without much user or programmer
+    inconvenience.  So, without any exception handling at all, a user would see something
+    like this:
+
++----------------------------------------------------------------------------------------
+@EXAMPLE_STACK_TRACE@
++----------------------------------------------------------------------------------------
+
+    If you want to handle the exception yourself, you can catch <<<OptionException>>> in
+    your code, and do whatever you please with the contents of the exception, perhaps
+    using the help generation facility.
+
+* Suppressing <<<UnrecognizedOptionException>>>
+
+    Sometimes you want to ignore unrecognized options on the command line.
+
+    For example, you might be interested in handling only a part of the arguments given.
+    Or you might want to pass on options to another program and not bother the user with
+    providing two hyphens (<<<-->>>) to indicate the end of known options. Or maybe
+    you want to provide future forwards/backwards compatibility when you foresee passing
+    in new options to old code (or old code invoking new code with "old" arguments).
+
+    You can achieve this by using the method <<<OptionParser.allowsUnrecognizedOptions()>>>.
+    When you call this method, then any unrecognized options handed to <<<parse()>>> are
+    treated as non-option arguments, rather than causing an exception to be raised.
+
++----------------------------------------------------------------------------------------
+@UNRECOGNIZED_OPTIONS_ALLOWED_EXAMPLE@
++----------------------------------------------------------------------------------------
diff --git a/third_party/java/jopt-simple/src/site/apt/index.apt b/third_party/java/jopt-simple/src/site/apt/index.apt
new file mode 100644
index 0000000..88c2af8
--- /dev/null
+++ b/third_party/java/jopt-simple/src/site/apt/index.apt
@@ -0,0 +1,71 @@
+                      -----------
+                      JOpt Simple
+                      -----------
+
+    JOpt Simple is a Java library for parsing command line options, such as those you
+    might pass to an invocation of <<<javac>>>.
+
+    In the interest of striving for simplicity, as closely as possible JOpt Simple
+    attempts to honor the command line option syntaxes of
+    {{{http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap12.html} POSIX <<<getopt()>>>}}
+    and
+    GNU {{{http://www.gnu.org/software/libc/manual/html_node/Getopt-Long-Options.html} <<<getopt_long()>>>}}.
+    It also aims to make option parser configuration and retrieval of options and their
+    arguments simple and expressive, without being overly clever.
+
+    Here are some libraries that perform the same duties as JOpt Simple:
+
+    * {{{http://jargs.sourceforge.net} JArgs}}
+
+    * {{{http://jakarta.apache.org/commons/cli/} Jakarta Commons CLI}}
+
+    * {{{http://te-code.sourceforge.net/} TE-Code}} has a command line parsing library.
+
+    * {{{http://www.cs.ubc.ca/spider/lloyd/java/argparser.html} argparser}}
+
+    * {{{http://www.urbanophile.com/arenn/hacking/download.html} Java port of GNU getopt}}
+
+    * {{{https://args4j.dev.java.net/} Args4J}}
+
+    * {{{http://www.martiansoftware.com/jsap/} JSAP}}
+
+    * {{{http://clajr.sourceforge.net} CLAJR}}
+
+    * {{{http://ostermiller.org/utils/CmdLn.html} CmdLn}}
+
+    * {{{http://jewelcli.sourceforge.net} JewelCli}}
+
+    * {{{http://jcommando.sourceforge.net} JCommando}}
+
+    * {{{http://code.google.com/p/parse-cmd/} parse-cmd}}
+
+    * {{{http://jcommander.org} JCommander}}
+
+    * {{{http://types.cs.washington.edu/plume-lib/api/plume/Options.html} plume-lib Options}}
+
+    []
+
+    I hope you'll agree that JOpt Simple tops them all in ease of use and cleanliness of
+    code (although I admire JewelCli quite a bit).
+
+    I'd love to hear your constructive {{{mailto:pholser@alumni.rice.edu} feedback}},
+    especially suggestions for improvements to the library or the site!  If your project
+    is using JOpt Simple, do let me know.
+
+Projects Using JOpt Simple
+
+    Here are some we know of or have known of:
+
+    * {{{http://openjdk.java.net/} OpenJDK}}.
+    From {{{http://weblogs.java.net/blog/mreinhold/} Mark Reinhold}}: "I thought you might
+    be interested to know that we're using your jopt-simple library in the open-source
+    Java Development Kit.  Thanks for writing such a nice little library!  It's far
+    cleaner than any of the other alternatives out there."
+
+    * {{{http://www.gradle.org/} Gradle}}
+
+    * {{{http://natbraille.free.fr} NATBraille}}
+
+    * {{{https://minecraft.net} Minecraft}}
+
+    []
diff --git a/third_party/java/jopt-simple/src/site/resources/css/prettify.css b/third_party/java/jopt-simple/src/site/resources/css/prettify.css
new file mode 100644
index 0000000..f8c7db2
--- /dev/null
+++ b/third_party/java/jopt-simple/src/site/resources/css/prettify.css
@@ -0,0 +1,49 @@
+/* Pretty printing styles. Used with prettify.js. */
+
+.str { color: #080; }
+.kwd { color: #008; }
+.com { color: #800; }
+.typ { color: #606; }
+.lit { color: #066; }
+.pun { color: #660; }
+.pln { color: #000; }
+.tag { color: #008; }
+.atn { color: #606; }
+.atv { color: #080; }
+.dec { color: #606; }
+
+pre.prettyprint {
+    padding: 2px;
+    border: 1px solid #888;
+}
+
+@media print {
+    .str { color: #060; }
+
+    .kwd {
+        color: #006;
+        font-weight: bold;
+    }
+
+    .com {
+        color: #600;
+        font-style: italic;
+    }
+
+    .typ {
+        color: #404;
+        font-weight: bold;
+    }
+
+    .lit { color: #044; }
+    .pun { color: #440; }
+    .pln { color: #000; }
+
+    .tag {
+        color: #006;
+        font-weight: bold;
+    }
+
+    .atn { color: #404; }
+    .atv { color: #060; }
+}
diff --git a/third_party/java/jopt-simple/src/site/resources/images/jopt-simple-logo.png b/third_party/java/jopt-simple/src/site/resources/images/jopt-simple-logo.png
new file mode 100644
index 0000000..4a3f707
--- /dev/null
+++ b/third_party/java/jopt-simple/src/site/resources/images/jopt-simple-logo.png
Binary files differ
diff --git a/third_party/java/jopt-simple/src/site/resources/scripts/prettify.js b/third_party/java/jopt-simple/src/site/resources/scripts/prettify.js
new file mode 100644
index 0000000..de1e07f
--- /dev/null
+++ b/third_party/java/jopt-simple/src/site/resources/scripts/prettify.js
@@ -0,0 +1,1602 @@
+// Copyright (C) 2006 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+
+/**
+ * @fileoverview
+ * some functions for browser-side pretty printing of code contained in html.
+ *
+ * The lexer should work on a number of languages including C and friends,
+ * Java, Python, Bash, SQL, HTML, XML, CSS, Javascript, and Makefiles.
+ * It works passably on Ruby, PHP and Awk and a decent subset of Perl, but,
+ * because of commenting conventions, doesn't work on Smalltalk, Lisp-like, or
+ * CAML-like languages.
+ *
+ * If there's a language not mentioned here, then I don't know it, and don't
+ * know whether it works.  If it has a C-like, Bash-like, or XML-like syntax
+ * then it should work passably.
+ *
+ * Usage:
+ * 1) include this source file in an html page via
+ * <script type="text/javascript" src="/path/to/prettify.js"></script>
+ * 2) define style rules.  See the example page for examples.
+ * 3) mark the <pre> and <code> tags in your source with class=prettyprint.
+ *    You can also use the (html deprecated) <xmp> tag, but the pretty printer
+ *    needs to do more substantial DOM manipulations to support that, so some
+ *    css styles may not be preserved.
+ * That's it.  I wanted to keep the API as simple as possible, so there's no
+ * need to specify which language the code is in.
+ *
+ * Change log:
+ * cbeust, 2006/08/22
+ *   Java annotations (start with "@") are now captured as literals ("lit")
+ */
+
+var PR_keywords = {};
+/** initialize the keyword list for our target languages. */
+(function () {
+    var CPP_KEYWORDS = "abstract bool break case catch char class const " +
+                       "const_cast continue default delete deprecated dllexport dllimport do " +
+                       "double dynamic_cast else enum explicit extern false float for friend " +
+                       "goto if inline int long mutable naked namespace new noinline noreturn " +
+                       "nothrow novtable operator private property protected public register " +
+                       "reinterpret_cast return selectany short signed sizeof static " +
+                       "static_cast struct switch template this thread throw true try typedef " +
+                       "typeid typename union unsigned using declaration, directive uuid " +
+                       "virtual void volatile while typeof";
+    var CSHARP_KEYWORDS = "as base by byte checked decimal delegate descending " +
+                          "event finally fixed foreach from group implicit in interface internal " +
+                          "into is lock null object out override orderby params readonly ref sbyte " +
+                          "sealed stackalloc string select uint ulong unchecked unsafe ushort var";
+    var JAVA_KEYWORDS = "package synchronized boolean implements import throws " +
+                        "instanceof transient extends final strictfp native super";
+    var JSCRIPT_KEYWORDS = "debugger export function with NaN Infinity";
+    var PERL_KEYWORDS = "require sub unless until use elsif BEGIN END";
+    var PYTHON_KEYWORDS = "and assert def del elif except exec global lambda " +
+                          "not or pass print raise yield False True None";
+    var RUBY_KEYWORDS = "then end begin rescue ensure module when undef next " +
+                        "redo retry alias defined";
+    var SH_KEYWORDS = "done fi";
+
+    var KEYWORDS = [CPP_KEYWORDS, CSHARP_KEYWORDS, JAVA_KEYWORDS,
+        JSCRIPT_KEYWORDS, PERL_KEYWORDS, PYTHON_KEYWORDS,
+        RUBY_KEYWORDS, SH_KEYWORDS];
+    for ( var k = 0; k < KEYWORDS.length; k++ ) {
+        var kw = KEYWORDS[k].split(' ');
+        for ( var i = 0; i < kw.length; i++ ) {
+            if ( kw[i] ) {
+                PR_keywords[kw[i]] = true;
+            }
+        }
+    }
+}).call(this);
+
+// token style names.  correspond to css classes
+/** token style for a string literal */
+var PR_STRING = 'str';
+/** token style for a keyword */
+var PR_KEYWORD = 'kwd';
+/** token style for a comment */
+var PR_COMMENT = 'com';
+/** token style for a type */
+var PR_TYPE = 'typ';
+/** token style for a literal value.  e.g. 1, null, true. */
+var PR_LITERAL = 'lit';
+/** token style for a punctuation string. */
+var PR_PUNCTUATION = 'pun';
+/** token style for a punctuation string. */
+var PR_PLAIN = 'pln';
+
+/** token style for an sgml tag. */
+var PR_TAG = 'tag';
+/** token style for a markup declaration such as a DOCTYPE. */
+var PR_DECLARATION = 'dec';
+/** token style for embedded source. */
+var PR_SOURCE = 'src';
+/** token style for an sgml attribute name. */
+var PR_ATTRIB_NAME = 'atn';
+/** token style for an sgml attribute value. */
+var PR_ATTRIB_VALUE = 'atv';
+
+/** the number of characters between tab columns */
+var PR_TAB_WIDTH = 8;
+
+/** the position of the end of a token during.  A division of a string into
+ * n tokens can be represented as a series n - 1 token ends, as long as
+ * runs of whitespace warrant their own token.
+ * @private
+ */
+function PR_TokenEnd( end, style ) {
+    if ( undefined === style ) {
+        throw new Error('BAD');
+    }
+    if ( 'number' != typeof(end) ) {
+        throw new Error('BAD');
+    }
+    this.end = end;
+    this.style = style;
+}
+PR_TokenEnd.prototype.toString = function () {
+    return '[PR_TokenEnd ' + this.end +
+           (this.style ? ':' + this.style : '') + ']';
+};
+
+/** a chunk of text with a style.  These are used to represent both the output
+ * from the lexing functions as well as intermediate results.
+ * @constructor
+ * @param token the token text
+ * @param style one of the token styles defined in designdoc-template, or null
+ *   for a styleless token, such as an embedded html tag.
+ * @private
+ */
+function PR_Token( token, style ) {
+    if ( undefined === style ) {
+        throw new Error('BAD');
+    }
+    this.token = token;
+    this.style = style;
+}
+
+PR_Token.prototype.toString = function () {
+    return '[PR_Token ' + this.token + (this.style ? ':' + this.style : '') + ']';
+};
+
+/** a helper class that decodes common html entities used to escape special
+ * characters in source code.
+ * @constructor
+ * @private
+ */
+function PR_DecodeHelper() {
+    this.next = 0;
+    this.ch = '\0';
+}
+
+var PR_NAMED_ENTITIES = {
+    'lt':   '<',
+    'gt':   '>',
+    'quot': '"',
+    'apos': "'",
+    'amp':  '&'   // reencoding requires that & always be decoded properly
+};
+
+PR_DecodeHelper.prototype.decode = function ( s, i ) {
+    var next = i + 1;
+    var ch = s.charAt(i);
+    if ( '&' === ch ) {
+        var semi = s.indexOf(';', next);
+        if ( semi >= 0 && semi < next + 4 ) {
+            var entityName = s.substring(next, semi);
+            var decoded = null;
+            if ( entityName.charAt(0) === '#' ) {  // check for numeric entity
+                var ch1 = entityName.charAt(1);
+                var charCode;
+                if ( ch1 === 'x' || ch1 === 'X' ) {  // like &#xA0;
+                    charCode = parseInt(entityName.substring(2), 16);
+                }
+                else {  // like &#160;
+                    charCode = parseInt(entityName.substring(1), 10);
+                }
+                if ( !isNaN(charCode) ) {
+                    decoded = String.fromCharCode(charCode);
+                }
+            }
+            if ( !decoded ) {
+                decoded = PR_NAMED_ENTITIES[entityName.toLowerCase()];
+            }
+            if ( decoded ) {
+                ch = decoded;
+                next = semi + 1;
+            }
+            else {  // skip over unrecognized entity
+                next = i + 1;
+                ch = '\0';
+            }
+        }
+    }
+    this.next = next;
+    this.ch = ch;
+    return this.ch;
+};
+
+// some string utilities
+function PR_isWordChar( ch ) {
+    return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z');
+}
+
+function PR_isIdentifierStart( ch ) {
+    return PR_isWordChar(ch) || ch == '_' || ch == '$' || ch == '@';
+}
+
+function PR_isIdentifierPart( ch ) {
+    return PR_isIdentifierStart(ch) || PR_isDigitChar(ch);
+}
+
+function PR_isSpaceChar( ch ) {
+    return "\t \r\n".indexOf(ch) >= 0;
+}
+
+function PR_isDigitChar( ch ) {
+    return ch >= '0' && ch <= '9';
+}
+
+function PR_trim( s ) {
+    var i = 0, j = s.length - 1;
+    while ( i <= j && PR_isSpaceChar(s.charAt(i)) ) {
+        ++i;
+    }
+    while ( j > i && PR_isSpaceChar(s.charAt(j)) ) {
+        --j;
+    }
+    return s.substring(i, j + 1);
+}
+
+function PR_startsWith( s, prefix ) {
+    return s.length >= prefix.length && prefix == s.substring(0, prefix.length);
+}
+
+function PR_endsWith( s, suffix ) {
+    return s.length >= suffix.length &&
+           suffix == s.substring(s.length - suffix.length, s.length);
+}
+
+/** true iff prefix matches the first prefix characters in chars[0:len].
+ * @private
+ */
+function PR_prefixMatch( chars, len, prefix ) {
+    if ( len < prefix.length ) {
+        return false;
+    }
+    for ( var i = 0, n = prefix.length; i < n; ++i ) {
+        if ( prefix.charAt(i) != chars[i] ) {
+            return false;
+        }
+    }
+    return true;
+}
+
+/** like textToHtml but escapes double quotes to be attribute safe. */
+function PR_attribToHtml( str ) {
+    return str.replace(/&/g, '&amp;')
+        .replace(/</g, '&lt;')
+        .replace(/>/g, '&gt;')
+        .replace(/\"/g, '&quot;')
+        .replace(/\xa0/, '&nbsp;');
+}
+
+/** escapest html special characters to html. */
+function PR_textToHtml( str ) {
+    return str.replace(/&/g, '&amp;')
+        .replace(/</g, '&lt;')
+        .replace(/>/g, '&gt;')
+        .replace(/\xa0/g, '&nbsp;');
+}
+
+/** is the given node's innerHTML normally unescaped? */
+function PR_isRawContent( node ) {
+    return 'XMP' == node.tagName;
+}
+
+var PR_innerHtmlWorks = null;
+function PR_getInnerHtml( node ) {
+    // inner html is hopelessly broken in Safari 2.0.4 when the content is
+    // an html description of well formed XML and the containing tag is a PRE
+    // tag, so we detect that case and emulate innerHTML.
+    if ( null == PR_innerHtmlWorks ) {
+        var testNode = document.createElement('PRE');
+        testNode.appendChild(
+            document.createTextNode('<!DOCTYPE foo PUBLIC "foo bar">\n<foo />'));
+        PR_innerHtmlWorks = !/</.test(testNode.innerHTML);
+    }
+
+    if ( PR_innerHtmlWorks ) {
+        var content = node.innerHTML;
+        // XMP tags contain unescaped entities so require special handling.
+        if ( PR_isRawContent(node) ) {
+            content = PR_textToHtml(content);
+        }
+        return content;
+    }
+
+    var out = [];
+    for ( var child = node.firstChild; child; child = child.nextSibling ) {
+        PR_normalizedHtml(child, out);
+    }
+    return out.join('');
+}
+
+/**
+ * walks the DOM returning a properly escaped version of innerHTML.
+ */
+function PR_normalizedHtml( node, out ) {
+    switch ( node.nodeType ) {
+        case 1:  // an element
+            var name = node.tagName.toLowerCase();
+            out.push('\074', name);
+            for ( var i = 0; i < node.attributes.length; ++i ) {
+                var attr = node.attributes[i];
+                if ( !attr.specified ) {
+                    continue;
+                }
+                out.push(' ');
+                PR_normalizedHtml(attr, out);
+            }
+            out.push('>');
+            for ( var child = node.firstChild; child; child = child.nextSibling ) {
+                PR_normalizedHtml(child, out);
+            }
+            if ( node.firstChild || !/^(?:br|link|img)$/.test(name) ) {
+                out.push('<\/', name, '>');
+            }
+            break;
+        case 2: // an attribute
+            out.push(node.name.toLowerCase(), '="', PR_attribToHtml(node.value), '"');
+            break;
+        case 3: case 4: // text
+        out.push(PR_textToHtml(node.nodeValue));
+        break;
+    }
+}
+
+/** expand tabs to spaces
+ * @param {Array} chunks PR_Tokens possibly containing tabs
+ * @param {Number} tabWidth number of spaces between tab columns
+ * @return {Array} chunks with tabs replaced with spaces
+ */
+function PR_expandTabs( chunks, tabWidth ) {
+    var SPACES = '                ';
+
+    var charInLine = 0;
+    var decodeHelper = new PR_DecodeHelper();
+
+    var chunksOut = []
+    for ( var chunkIndex = 0; chunkIndex < chunks.length; ++chunkIndex ) {
+        var chunk = chunks[chunkIndex];
+        if ( chunk.style == null ) {
+            chunksOut.push(chunk);
+            continue;
+        }
+
+        var s = chunk.token;
+        var pos = 0;
+        // index of last character output
+        var out = [];
+
+        // walk over each character looking for tabs and newlines.
+        // On tabs, expand them.  On newlines, reset charInLine.
+        // Otherwise increment charInLine
+        for ( var charIndex = 0, n = s.length; charIndex < n;
+              charIndex = decodeHelper.next ) {
+            decodeHelper.decode(s, charIndex);
+            var ch = decodeHelper.ch;
+
+            switch ( ch ) {
+                case '\t':
+                    out.push(s.substring(pos, charIndex));
+                // calculate how much space we need in front of this part
+                // nSpaces is the amount of padding -- the number of spaces needed to
+                // move us to the next column, where columns occur at factors of
+                // tabWidth.
+                    var nSpaces = tabWidth - (charInLine % tabWidth);
+                    charInLine += nSpaces;
+                    for ( ; nSpaces >= 0; nSpaces -= SPACES.length ) {
+                        out.push(SPACES.substring(0, nSpaces));
+                    }
+                    pos = decodeHelper.next;
+                    break;
+                case '\n': case '\r':
+                charInLine = 0;
+                break;
+                default:
+                    ++charInLine;
+            }
+        }
+        out.push(s.substring(pos));
+        chunksOut.push(new PR_Token(out.join(''), chunk.style));
+    }
+    return chunksOut
+}
+
+/** split markup into chunks of html tags (style null) and
+ * plain text (style {@link #PR_PLAIN}).
+ *
+ * @param {String} s html.
+ * @return {Array} of PR_Tokens of style PR_PLAIN, and null.
+ * @private
+ */
+function PR_chunkify( s ) {
+    // The below pattern matches one of the following
+    // (1) /[^<]+/ : A run of characters other than '<'
+    // (2) /<\/?[a-zA-Z][^>]*>/ : A probably tag that should not be highlighted
+    // (3) /</ : A '<' that does not begin a larger chunk.  Treated as 1
+    var chunkPattern = /(?:[^<]+|<\/?[a-zA-Z][^>]*>|<)/g;
+    // since the pattern has the 'g' modifier and defines no capturing groups,
+    // this will return a list of all chunks which we then classify and wrap as
+    // PR_Tokens
+    var matches = s.match(chunkPattern);
+    var chunks = [];
+    if ( matches ) {
+        var lastChunk = null;
+        for ( var i = 0, n = matches.length; i < n; ++i ) {
+            var chunkText = matches[i];
+            var style;
+            if ( chunkText.length < 2 || chunkText.charAt(0) !== '<' ) {
+                if ( lastChunk && lastChunk.style === PR_PLAIN ) {
+                    lastChunk.token += chunkText;
+                    continue;
+                }
+                style = PR_PLAIN;
+            }
+            else {  // a tag
+                style = null;
+            }
+            lastChunk = new PR_Token(chunkText, style);
+            chunks.push(lastChunk);
+        }
+    }
+    return chunks;
+}
+
+/** walk the tokenEnds list and the chunk list in parallel to generate a list
+ * of split tokens.
+ * @private
+ */
+function PR_splitChunks( chunks, tokenEnds ) {
+    var tokens = [];
+    // the output
+
+    var ci = 0;
+    // index into chunks
+    // position of beginning of amount written so far in absolute space.
+    var posAbs = 0;
+    // position of amount written so far in chunk space
+    var posChunk = 0;
+
+    // current chunk
+    var chunk = new PR_Token('', null);
+
+    for ( var ei = 0, ne = tokenEnds.length, lastEnd = 0; ei < ne; ++ei ) {
+        var tokenEnd = tokenEnds[ei];
+        var end = tokenEnd.end;
+        if ( end === lastEnd ) {
+            continue;
+        }  // skip empty regions
+
+        var tokLen = end - posAbs;
+        var remainingInChunk = chunk.token.length - posChunk;
+        while ( remainingInChunk <= tokLen ) {
+            if ( remainingInChunk > 0 ) {
+                tokens.push(
+                    new PR_Token(chunk.token.substring(posChunk, chunk.token.length),
+                    null == chunk.style ? null : tokenEnd.style));
+            }
+            posAbs += remainingInChunk;
+            posChunk = 0;
+            if ( ci < chunks.length ) {
+                chunk = chunks[ci++];
+            }
+
+            tokLen = end - posAbs;
+            remainingInChunk = chunk.token.length - posChunk;
+        }
+
+        if ( tokLen ) {
+            tokens.push(
+                new PR_Token(chunk.token.substring(posChunk, posChunk + tokLen),
+                tokenEnd.style));
+            posAbs += tokLen;
+            posChunk += tokLen;
+        }
+    }
+
+    return tokens;
+}
+
+/** splits markup tokens into declarations, tags, and source chunks.
+ * @private
+ */
+function PR_splitMarkup( chunks ) {
+    // A state machine to split out declarations, tags, etc.
+    // This state machine deals with absolute space in the text, indexed by k,
+    // and position in the current chunk, indexed by pos and tokenStart to
+    // generate a list of the ends of tokens.
+    // Absolute space is calculated by considering the chunks as appended into
+    // one big string, as they were before being split.
+
+    // Known failure cases
+    // Server side scripting sections such as <?...?> in attributes.
+    // i.e. <span class="<? foo ?>">
+    // Handling this would require a stack, and we don't use PHP.
+
+    // The output: a list of pairs of PR_TokenEnd instances
+    var tokenEnds = [];
+
+    var state = 0;
+    // FSM state variable
+    var k = 0;
+    // position in absolute space of the start of the current chunk
+    var tokenStart = -1;
+    // the start of the current token
+
+    // Try to find a closing tag for any open <style> or <script> tags
+    // We can't do this at a later stage because then the following case
+    // would fail:
+    // <script>document.writeln('<!--');</script>
+
+    // We use tokenChars[:tokenCharsI] to accumulate the tag name so that we
+    // can check whether to enter into a no scripting section when the tag ends.
+    var tokenChars = new Array(12);
+    var tokenCharsI = 0;
+    // if non null, the tag prefix that we need to see to break out.
+    var endScriptTag = null;
+    var decodeHelper = new PR_DecodeHelper();
+
+    for ( var ci = 0, nc = chunks.length; ci < nc; ++ci ) {
+        var chunk = chunks[ci];
+        if ( PR_PLAIN != chunk.style ) {
+            k += chunk.token.length;
+            continue;
+        }
+
+        var s = chunk.token;
+        var pos = 0;
+        // the position past the last character processed so far in s
+
+        for ( var i = 0, n = s.length; i < n; /* i = next at bottom */ ) {
+            decodeHelper.decode(s, i);
+            var ch = decodeHelper.ch;
+            var next = decodeHelper.next;
+
+            var tokenStyle = null;
+            switch ( state ) {
+                case 0:
+                    if ( '<' == ch ) {
+                        state = 1;
+                    }
+                    break;
+                case 1:
+                    tokenCharsI = 0;
+                    if ( '/' == ch ) {  // only consider close tags if we're in script/style
+                        state = 7;
+                    }
+                    else if ( null == endScriptTag ) {
+                        if ( '!' == ch ) {
+                            state = 2;
+                        }
+                        else if ( PR_isWordChar(ch) ) {
+                            state = 8;
+                        }
+                        else if ( '?' == ch ) {
+                            state = 9;
+                        }
+                        else if ( '%' == ch ) {
+                            state = 11;
+                        }
+                        else if ( '<' != ch ) {
+                            state = 0;
+                        }
+                    }
+                    else if ( '<' != ch ) {
+                        state = 0;
+                    }
+                    break;
+                case 2:
+                    if ( '-' == ch ) {
+                        state = 4;
+                    }
+                    else if ( PR_isWordChar(ch) ) {
+                        state = 3;
+                    }
+                    else if ( '<' == ch ) {
+                        state = 1;
+                    }
+                    else {
+                        state = 0;
+                    }
+                    break;
+                case 3:
+                    if ( '>' == ch ) {
+                        state = 0;
+                        tokenStyle = PR_DECLARATION;
+                    }
+                    break;
+                case 4:
+                    if ( '-' == ch ) {
+                        state = 5;
+                    }
+                    break;
+                case 5:
+                    if ( '-' == ch ) {
+                        state = 6;
+                    }
+                    break;
+                case 6:
+                    if ( '>' == ch ) {
+                        state = 0;
+                        tokenStyle = PR_COMMENT;
+                    }
+                    else if ( '-' == ch ) {
+                        state = 6;
+                    }
+                    else {
+                        state = 4;
+                    }
+                    break;
+                case 7:
+                    if ( PR_isWordChar(ch) ) {
+                        state = 8;
+                    }
+                    else if ( '<' == ch ) {
+                        state = 1;
+                    }
+                    else {
+                        state = 0;
+                    }
+                    break;
+                case 8:
+                    if ( '>' == ch ) {
+                        state = 0;
+                        tokenStyle = PR_TAG;
+                    }
+                    break;
+                case 9:
+                    if ( '?' == ch ) {
+                        state = 10;
+                    }
+                    break;
+                case 10:
+                    if ( '>' == ch ) {
+                        state = 0;
+                        tokenStyle = PR_SOURCE;
+                    }
+                    else if ( '?' != ch ) {
+                        state = 9;
+                    }
+                    break;
+                case 11:
+                    if ( '%' == ch ) {
+                        state = 12;
+                    }
+                    break;
+                case 12:
+                    if ( '>' == ch ) {
+                        state = 0;
+                        tokenStyle = PR_SOURCE;
+                    }
+                    else if ( '%' != ch ) {
+                        state = 11;
+                    }
+                    break;
+            }
+
+            if ( tokenCharsI < tokenChars.length ) {
+                tokenChars[tokenCharsI++] = ch.toLowerCase();
+            }
+            if ( 1 == state ) {
+                tokenStart = k + i;
+            }
+            i = next;
+            if ( tokenStyle != null ) {
+                if ( null != tokenStyle ) {
+                    if ( endScriptTag ) {
+                        if ( PR_prefixMatch(tokenChars, tokenCharsI, endScriptTag) ) {
+                            endScriptTag = null;
+                        }
+                    }
+                    else {
+                        if ( PR_prefixMatch(tokenChars, tokenCharsI, 'script') ) {
+                            endScriptTag = '/script';
+                        }
+                        else if ( PR_prefixMatch(tokenChars, tokenCharsI, 'style') ) {
+                            endScriptTag = '/style';
+                        }
+                        else if ( PR_prefixMatch(tokenChars, tokenCharsI, 'xmp') ) {
+                            endScriptTag = '/xmp';
+                        }
+                    }
+                    // disallow the tag if endScriptTag is set and this was not an open
+                    // tag.
+                    if ( endScriptTag && tokenCharsI && '/' == tokenChars[0] ) {
+                        tokenStyle = null;
+                    }
+                }
+                if ( null != tokenStyle ) {
+                    tokenEnds.push(new PR_TokenEnd(tokenStart, PR_PLAIN));
+                    tokenEnds.push(new PR_TokenEnd(k + next, tokenStyle));
+                }
+            }
+        }
+        k += chunk.token.length;
+    }
+    tokenEnds.push(new PR_TokenEnd(k, PR_PLAIN));
+
+    return tokenEnds;
+}
+
+/** splits the given string into comment, string, and "other" tokens.
+ * @return {Array} of PR_Tokens with style in
+ *   (PR_STRING, PR_COMMENT, PR_PLAIN, null)
+ *   The result array may contain spurious zero length tokens.  Ignore them.
+ *
+ * @private
+ */
+function PR_splitStringAndCommentTokens( chunks ) {
+    // a state machine to split out comments, strings, and other stuff
+    var tokenEnds = [];
+    // positions of ends of tokens in absolute space
+    var state = 0;
+    // FSM state variable
+    var delim = -1;
+    // string delimiter
+    var k = 0;
+    // absolute position of beginning of current chunk
+
+    for ( var ci = 0, nc = chunks.length; ci < nc; ++ci ) {
+        var chunk = chunks[ci];
+        var s = chunk.token;
+        if ( PR_PLAIN == chunk.style ) {
+            var decodeHelper = new PR_DecodeHelper();
+            var last = -1;
+            var next;
+            for ( var i = 0, n = s.length; i < n; last = i,i = next ) {
+                decodeHelper.decode(s, i);
+                var ch = decodeHelper.ch;
+                next = decodeHelper.next;
+                if ( 0 == state ) {
+                    if ( ch == '"' || ch == '\'' || ch == '`' ) {
+                        tokenEnds.push(new PR_TokenEnd(k + i, PR_PLAIN));
+                        state = 1;
+                        delim = ch;
+                    }
+                    else if ( ch == '/' ) {
+                        state = 3;
+                    }
+                    else if ( ch == '#' ) {
+                        tokenEnds.push(new PR_TokenEnd(k + i, PR_PLAIN));
+                        state = 4;
+                    }
+                }
+                else if ( 1 == state ) {
+                    if ( ch == delim ) {
+                        state = 0;
+                        tokenEnds.push(new PR_TokenEnd(k + next, PR_STRING));
+                    }
+                    else if ( ch == '\\' ) {
+                        state = 2;
+                    }
+                }
+                else if ( 2 == state ) {
+                    state = 1;
+                }
+                else if ( 3 == state ) {
+                    if ( ch == '/' ) {
+                        state = 4;
+                        tokenEnds.push(new PR_TokenEnd(k + last, PR_PLAIN));
+                    }
+                    else if ( ch == '*' ) {
+                        state = 5;
+                        tokenEnds.push(new PR_TokenEnd(k + last, PR_PLAIN));
+                    }
+                    else {
+                        state = 0;
+                        // next loop will reenter state 0 without same value of i, so
+                        // ch will be reconsidered as start of new token.
+                        next = i;
+                    }
+                }
+                else if ( 4 == state ) {
+                    if ( ch == '\r' || ch == '\n' ) {
+                        state = 0;
+                        tokenEnds.push(new PR_TokenEnd(k + i, PR_COMMENT));
+                    }
+                }
+                else if ( 5 == state ) {
+                    if ( ch == '*' ) {
+                        state = 6;
+                    }
+                }
+                else if ( 6 == state ) {
+                    if ( ch == '/' ) {
+                        state = 0;
+                        tokenEnds.push(new PR_TokenEnd(k + next, PR_COMMENT));
+                    }
+                    else if ( ch != '*' ) {
+                        state = 5;
+                    }
+                }
+            }
+        }
+        k += s.length;
+    }
+    var endTokenType;
+    switch ( state ) {
+        case 1: case 2:
+        endTokenType = PR_STRING;
+        break;
+        case 4: case 5: case 6:
+        endTokenType = PR_COMMENT;
+        break;
+        default:
+            endTokenType = PR_PLAIN;
+            break;
+    }
+    // handle unclosed token which can legally happen for line comments (state 4)
+    tokenEnds.push(new PR_TokenEnd(k, endTokenType));
+    // a token ends at the end
+
+    return PR_splitChunks(chunks, tokenEnds);
+}
+
+/** used by lexSource to split a non string, non comment token.
+ * @private
+ */
+function PR_splitNonStringNonCommentToken( s, outlist ) {
+    var pos = 0;
+    var state = 0;
+
+    var decodeHelper = new PR_DecodeHelper();
+    var next;
+    for ( var i = 0; i <= s.length; i = next ) {
+        if ( i == s.length ) {
+            // nstate will not be equal to state, so it will append the token
+            nstate = -2;
+            next = i + 1;
+        }
+        else {
+            decodeHelper.decode(s, i);
+            next = decodeHelper.next;
+            var ch = decodeHelper.ch;
+
+            // the next state.
+            // if set to -1 then it will cause a reentry to state 0 without consuming
+            // another character.
+            var nstate = state;
+
+            switch ( state ) {
+                case 0:  // whitespace state
+                    if ( PR_isIdentifierStart(ch) ) {
+                        nstate = 1;
+                    }
+                    else if ( PR_isDigitChar(ch) ) {
+                        nstate = 2;
+                    }
+                    else if ( !PR_isSpaceChar(ch) ) {
+                        nstate = 3;
+                    }
+                    if ( nstate && pos < i ) {
+                        var t = s.substring(pos, i);
+                        outlist.push(new PR_Token(t, PR_PLAIN));
+                        pos = i;
+                    }
+                    break;
+                case 1:  // identifier state
+                    if ( !PR_isIdentifierPart(ch) ) {
+                        nstate = -1;
+                    }
+                    break;
+                case 2:  // number literal state
+                // handle numeric literals like
+                // 0x7f 300UL 100_000
+
+                // this does not treat floating point values as a single literal
+                //   0.1 and 3e-6
+                // are each split into multiple tokens
+                    if ( !(PR_isDigitChar(ch) || PR_isWordChar(ch) || ch == '_') ) {
+                        nstate = -1;
+                    }
+                    break;
+                case 3:  // punctuation state
+                    if ( PR_isIdentifierStart(ch) || PR_isDigitChar(ch) ||
+                         PR_isSpaceChar(ch) ) {
+                        nstate = -1;
+                    }
+                    break;
+            }
+        }
+
+        if ( nstate != state ) {
+            if ( nstate < 0 ) {
+                if ( i > pos ) {
+                    var t = s.substring(pos, i);
+                    var wordDecodeHelper = new PR_DecodeHelper();
+                    wordDecodeHelper.decode(t, 0);
+                    var ch0 = wordDecodeHelper.ch;
+                    var isSingleCharacter = wordDecodeHelper.next == t.length;
+                    var style;
+                    if ( PR_isIdentifierStart(ch0) ) {
+                        if ( PR_keywords[t] ) {
+                            style = PR_KEYWORD;
+                        }
+                        else if ( ch0 === '@' ) {
+                            style = PR_LITERAL;
+                        }
+                        else {
+                            // Treat any word that starts with an uppercase character and
+                            // contains at least one lowercase character as a type, or
+                            // ends with _t.
+                            // This works perfectly for Java, pretty well for C++, and
+                            // passably for Python.  The _t catches C structs.
+                            var isType = false;
+                            if ( ch0 >= 'A' && ch0 <= 'Z' ) {
+                                for ( var j = wordDecodeHelper.next;
+                                      j < t.length; j = wordDecodeHelper.next ) {
+                                    wordDecodeHelper.decode(t, j);
+                                    var ch1 = wordDecodeHelper.ch;
+                                    if ( ch1 >= 'a' && ch1 <= 'z' ) {
+                                        isType = true;
+                                        break;
+                                    }
+                                }
+                                if ( !isType && !isSingleCharacter &&
+                                     t.substring(t.length - 2) == '_t' ) {
+                                    isType = true;
+                                }
+                            }
+                            style = isType ? PR_TYPE : PR_PLAIN;
+                        }
+                    }
+                    else if ( PR_isDigitChar(ch0) ) {
+                        style = PR_LITERAL;
+                    }
+                    else if ( !PR_isSpaceChar(ch0) ) {
+                        style = PR_PUNCTUATION;
+                    }
+                    else {
+                        style = PR_PLAIN;
+                    }
+                    pos = i;
+                    outlist.push(new PR_Token(t, style));
+                }
+
+                state = 0;
+                if ( nstate == -1 ) {
+                    // don't increment.  This allows us to use state 0 to redispatch based
+                    // on the current character.
+                    next = i;
+                    continue;
+                }
+            }
+            state = nstate;
+        }
+    }
+
+}
+
+/** split a group of chunks of markup.
+ * @private
+ */
+function PR_tokenizeMarkup( chunks ) {
+    if ( !(chunks && chunks.length) ) {
+        return chunks;
+    }
+
+    var tokenEnds = PR_splitMarkup(chunks);
+    return PR_splitChunks(chunks, tokenEnds);
+}
+
+/** split tags attributes and their values out from the tag name, and
+ * recursively lex source chunks.
+ * @private
+ */
+function PR_splitTagAttributes( tokens ) {
+    var tokensOut = [];
+    var state = 0;
+    var stateStyle = PR_TAG;
+    var delim = null;
+    // attribute delimiter for quoted value state.
+    var decodeHelper = new PR_DecodeHelper();
+    for ( var ci = 0; ci < tokens.length; ++ci ) {
+        var tok = tokens[ci];
+        if ( PR_TAG == tok.style ) {
+            var s = tok.token;
+            var start = 0;
+            for ( var i = 0; i < s.length; /* i = next at bottom */ ) {
+                decodeHelper.decode(s, i);
+                var ch = decodeHelper.ch;
+                var next = decodeHelper.next;
+
+                var emitEnd = null;
+                // null or position of end of chunk to emit.
+                var nextStyle = null;
+                // null or next value of stateStyle
+                if ( ch == '>' ) {
+                    if ( PR_TAG != stateStyle ) {
+                        emitEnd = i;
+                        nextStyle = PR_TAG;
+                    }
+                }
+                else {
+                    switch ( state ) {
+                        case 0:
+                            if ( '<' == ch ) {
+                                state = 1;
+                            }
+                            break;
+                        case 1:
+                            if ( PR_isSpaceChar(ch) ) {
+                                state = 2;
+                            }
+                            break;
+                        case 2:
+                            if ( !PR_isSpaceChar(ch) ) {
+                                nextStyle = PR_ATTRIB_NAME;
+                                emitEnd = i;
+                                state = 3;
+                            }
+                            break;
+                        case 3:
+                            if ( '=' == ch ) {
+                                emitEnd = i;
+                                nextStyle = PR_TAG;
+                                state = 5;
+                            }
+                            else if ( PR_isSpaceChar(ch) ) {
+                                emitEnd = i;
+                                nextStyle = PR_TAG;
+                                state = 4;
+                            }
+                            break;
+                        case 4:
+                            if ( '=' == ch ) {
+                                state = 5;
+                            }
+                            else if ( !PR_isSpaceChar(ch) ) {
+                                emitEnd = i;
+                                nextStyle = PR_ATTRIB_NAME;
+                                state = 3;
+                            }
+                            break;
+                        case 5:
+                            if ( '"' == ch || '\'' == ch ) {
+                                emitEnd = i;
+                                nextStyle = PR_ATTRIB_VALUE;
+                                state = 6;
+                                delim = ch;
+                            }
+                            else if ( !PR_isSpaceChar(ch) ) {
+                                emitEnd = i;
+                                nextStyle = PR_ATTRIB_VALUE;
+                                state = 7;
+                            }
+                            break;
+                        case 6:
+                            if ( ch == delim ) {
+                                emitEnd = next;
+                                nextStyle = PR_TAG;
+                                state = 2;
+                            }
+                            break;
+                        case 7:
+                            if ( PR_isSpaceChar(ch) ) {
+                                emitEnd = i;
+                                nextStyle = PR_TAG;
+                                state = 2;
+                            }
+                            break;
+                    }
+                }
+                if ( emitEnd ) {
+                    if ( emitEnd > start ) {
+                        tokensOut.push(
+                            new PR_Token(s.substring(start, emitEnd), stateStyle));
+                        start = emitEnd;
+                    }
+                    stateStyle = nextStyle;
+                }
+                i = next;
+            }
+            if ( s.length > start ) {
+                tokensOut.push(new PR_Token(s.substring(start, s.length), stateStyle));
+            }
+        }
+        else {
+            if ( tok.style ) {
+                state = 0;
+                stateStyle = PR_TAG;
+            }
+            tokensOut.push(tok);
+        }
+    }
+    return tokensOut;
+}
+
+/** identify regions of markup that are really source code, and recursivley
+ * lex them.
+ * @private
+ */
+function PR_splitSourceNodes( tokens ) {
+    var tokensOut = [];
+    // when we see a <script> tag, store '/' here so that we know to end the
+    // source processing
+    var endScriptTag = null;
+    var decodeHelper = new PR_DecodeHelper();
+
+    var sourceChunks = null;
+
+    for ( var ci = 0, nc = tokens.length; /* break below */; ++ci ) {
+        var tok;
+
+        if ( ci < nc ) {
+            tok = tokens[ci];
+            if ( null == tok.style ) {
+                tokens.push(tok);
+                continue;
+            }
+        }
+        else if ( !endScriptTag ) {
+            break;
+        }
+        else {
+            // else pretend there's an end tag so we can gracefully handle
+            // unclosed source blocks
+            tok = new PR_Token('', null);
+        }
+
+        var s = tok.token;
+
+        if ( null == endScriptTag ) {
+            if ( PR_SOURCE == tok.style ) {
+                // split off any starting and trailing <?, <%
+                if ( '<' == decodeHelper.decode(s, 0) ) {
+                    decodeHelper.decode(s, decodeHelper.next);
+                    if ( '%' == decodeHelper.ch || '?' == decodeHelper.ch ) {
+                        endScriptTag = decodeHelper.ch;
+                        tokensOut.push(new PR_Token(s.substring(0, decodeHelper.next),
+                            PR_TAG));
+                        s = s.substring(decodeHelper.next, s.length);
+                    }
+                }
+            }
+            else if ( PR_TAG == tok.style ) {
+                if ( '<' == decodeHelper.decode(s, 0) &&
+                     '/' != s.charAt(decodeHelper.next) ) {
+                    var tagContent = s.substring(decodeHelper.next).toLowerCase();
+                    // FIXME(msamuel): this does not mirror exactly the code in
+                    // in PR_splitMarkup that defers splitting tags inside script and
+                    // style blocks.
+                    if ( PR_startsWith(tagContent, 'script') ||
+                         PR_startsWith(tagContent, 'style') ||
+                         PR_startsWith(tagContent, 'xmp') ) {
+                        endScriptTag = '/';
+                    }
+                }
+            }
+        }
+
+        if ( null != endScriptTag ) {
+            var endTok = null;
+            if ( PR_SOURCE == tok.style ) {
+                if ( endScriptTag == '%' || endScriptTag == '?' ) {
+                    var pos = s.lastIndexOf(endScriptTag);
+                    if ( pos >= 0 && '>' == decodeHelper.decode(s, pos + 1) &&
+                         s.length == decodeHelper.next ) {
+                        endTok = new PR_Token(s.substring(pos, s.length), PR_TAG);
+                        s = s.substring(0, pos);
+                    }
+                }
+                if ( null == sourceChunks ) {
+                    sourceChunks = [];
+                }
+                sourceChunks.push(new PR_Token(s, PR_PLAIN));
+            }
+            else if ( PR_PLAIN == tok.style ) {
+                if ( null == sourceChunks ) {
+                    sourceChunks = [];
+                }
+                sourceChunks.push(tok);
+            }
+            else if ( PR_TAG == tok.style ) {
+                // if it starts with </ then it must be the end tag.
+                if ( '<' == decodeHelper.decode(tok.token, 0) &&
+                     tok.token.length > decodeHelper.next &&
+                     '/' == decodeHelper.decode(tok.token, decodeHelper.next) ) {
+                    endTok = tok;
+                }
+                else {
+                    tokensOut.push(tok);
+                }
+            }
+            else if ( ci >= nc ) {
+                // force the token to close
+                endTok = tok;
+            }
+            else {
+                if ( sourceChunks ) {
+                    sourceChunks.push(tok);
+                }
+                else {
+                    // push remaining tag and attribute tokens from the opening tag
+                    tokensOut.push(tok);
+                }
+            }
+            if ( endTok ) {
+                if ( sourceChunks ) {
+                    var sourceTokens = PR_lexSource(sourceChunks);
+                    tokensOut.push(new PR_Token('<span class=embsrc>', null));
+                    for ( var si = 0, ns = sourceTokens.length; si < ns; ++si ) {
+                        tokensOut.push(sourceTokens[si]);
+                    }
+                    tokensOut.push(new PR_Token('</span>', null));
+                    sourceChunks = null;
+                }
+                if ( endTok.token ) {
+                    tokensOut.push(endTok);
+                }
+                endScriptTag = null;
+            }
+        }
+        else {
+            tokensOut.push(tok);
+        }
+    }
+    return tokensOut;
+}
+
+/** splits the quotes from an attribute value.
+ * ['"foo"'] -> ['"', 'foo', '"']
+ * @private
+ */
+function PR_splitAttributeQuotes( tokens ) {
+    var firstPlain = null, lastPlain = null;
+    for ( var i = 0; i < tokens.length; ++i ) {
+        if ( PR_PLAIN == tokens[i].style ) {
+            firstPlain = i;
+            break;
+        }
+    }
+    for ( var i = tokens.length; --i >= 0; ) {
+        if ( PR_PLAIN == tokens[i].style ) {
+            lastPlain = i;
+            break;
+        }
+    }
+    if ( null == firstPlain ) {
+        return tokens;
+    }
+
+    var decodeHelper = new PR_DecodeHelper();
+    var fs = tokens[firstPlain].token;
+    var fc = decodeHelper.decode(fs, 0);
+    if ( '"' != fc && '\'' != fc ) {
+        return tokens;
+    }
+    var fpos = decodeHelper.next;
+
+    var ls = tokens[lastPlain].token;
+    var lpos = ls.lastIndexOf('&');
+    if ( lpos < 0 ) {
+        lpos = ls.length - 1;
+    }
+    var lc = decodeHelper.decode(ls, lpos);
+    if ( lc != fc || decodeHelper.next != ls.length ) {
+        lc = null;
+        lpos = ls.length;
+    }
+
+    var tokensOut = [];
+    for ( var i = 0; i < firstPlain; ++i ) {
+        tokensOut.push(tokens[i]);
+    }
+    tokensOut.push(new PR_Token(fs.substring(0, fpos), PR_ATTRIB_VALUE));
+    if ( lastPlain == firstPlain ) {
+        tokensOut.push(new PR_Token(fs.substring(fpos, lpos), PR_PLAIN));
+    }
+    else {
+        tokensOut.push(new PR_Token(fs.substring(fpos, fs.length), PR_PLAIN));
+        for ( var i = firstPlain + 1; i < lastPlain; ++i ) {
+            tokensOut.push(tokens[i]);
+        }
+        if ( lc ) {
+            tokens.push(new PR_Token(ls.substring(0, lpos), PR_PLAIN));
+        }
+        else {
+            tokens.push(tokens[lastPlain]);
+        }
+    }
+    if ( lc ) {
+        tokensOut.push(new PR_Token(ls.substring(lpos, ls.length), PR_PLAIN));
+    }
+    for ( var i = lastPlain + 1; i < tokens.length; ++i ) {
+        tokensOut.push(tokens[i]);
+    }
+    return tokensOut;
+}
+
+/** identify attribute values that really contain source code and recursively
+ * lex them.
+ * @private
+ */
+function PR_splitSourceAttributes( tokens ) {
+    var tokensOut = [];
+
+    var sourceChunks = null;
+    var inSource = false;
+    var name = '';
+
+    for ( var ci = 0, nc = tokens.length; ci < nc; ++ci ) {
+        var tok = tokens[ci];
+        var outList = tokensOut;
+        if ( PR_TAG == tok.style ) {
+            if ( inSource ) {
+                inSource = false;
+                name = '';
+                if ( sourceChunks ) {
+                    tokensOut.push(new PR_Token('<span class=embsrc>', null));
+                    var sourceTokens =
+                        PR_lexSource(PR_splitAttributeQuotes(sourceChunks));
+                    for ( var si = 0, ns = sourceTokens.length; si < ns; ++si ) {
+                        tokensOut.push(sourceTokens[si]);
+                    }
+                    tokensOut.push(new PR_Token('</span>', null));
+                    sourceChunks = null;
+                }
+            }
+            else if ( name && tok.token.indexOf('=') >= 0 ) {
+                var nameLower = name.toLowerCase();
+                if ( PR_startsWith(nameLower, 'on') || 'style' == nameLower ) {
+                    inSource = true;
+                }
+            }
+            else {
+                name = '';
+            }
+        }
+        else if ( PR_ATTRIB_NAME == tok.style ) {
+            name += tok.token;
+        }
+        else if ( PR_ATTRIB_VALUE == tok.style ) {
+            if ( inSource ) {
+                if ( null == sourceChunks ) {
+                    sourceChunks = [];
+                }
+                outList = sourceChunks;
+                tok = new PR_Token(tok.token, PR_PLAIN);
+            }
+        }
+        else {
+            if ( sourceChunks ) {
+                outList = sourceChunks;
+            }
+        }
+        outList.push(tok);
+    }
+    return tokensOut;
+}
+
+/** returns a list of PR_Token objects given chunks of source code.
+ *
+ * This code treats ", ', and ` as string delimiters, and \ as a string escape.
+ * It does not recognize perl's qq() style strings.  It has no special handling
+ * for double delimiter escapes as in basic, or tje tripled delimiters used in
+ * python, but should work on those regardless although in those cases a single
+ * string literal may be broken up into multiple adjacent string literals.
+ *
+ * It recognizes C, C++, and shell style comments.
+ *
+ * @param chunks PR_Tokens with style in (null, PR_PLAIN)
+ */
+function PR_lexSource( chunks ) {
+    // split into strings, comments, and other.
+    // We do this because strings and comments are easily recognizable and can
+    // contain stuff that looks like other tokens, so we want to mark those early
+    // so we don't recurse into them.
+    var tokens = PR_splitStringAndCommentTokens(chunks);
+
+    // split non comment|string tokens on whitespace and word boundaries
+    var tokensOut = [];
+    for ( var i = 0; i < tokens.length; ++i ) {
+        var tok = tokens[i];
+        if ( PR_PLAIN === tok.style ) {
+            PR_splitNonStringNonCommentToken(tok.token, tokensOut);
+            continue;
+        }
+        tokensOut.push(tok);
+    }
+
+    return tokensOut;
+}
+
+/** returns a list of PR_Token objects given a string of markup.
+ *
+ * This code assumes that < tokens are html escaped, but " are not.
+ * It will do a resonable job with <, but will not recognize an &quot;
+ * as starting a string.
+ *
+ * This code recognizes a number of constructs.
+ * <!-- ... --> comment
+ * <!\w ... >   declaration
+ * <\w ... >    tag
+ * </\w ... >   tag
+ * <?...?>      embedded source
+ * &[#\w]...;   entity
+ *
+ * It does not recognizes %foo; entities.
+ *
+ * It will recurse into any <style>, <script>, and on* attributes using
+ * PR_lexSource.
+ */
+function PR_lexMarkup( chunks ) {
+    // This function works as follows:
+    // 1) Start by splitting the markup into text and tag chunks
+    //    Input:  String s
+    //    Output: List<PR_Token> where style in (PR_PLAIN, null)
+    // 2) Then split the text chunks further into comments, declarations,
+    //    tags, etc.
+    //    After each split, consider whether the token is the start of an
+    //    embedded source section, i.e. is an open <script> tag.  If it is,
+    //    find the corresponding close token, and don't bother to lex in between.
+    //    Input:  List<String>
+    //    Output: List<PR_Token> with style in (PR_TAG, PR_PLAIN, PR_SOURCE, null)
+    // 3) Finally go over each tag token and split out attribute names and values.
+    //    Input:  List<PR_Token>
+    //    Output: List<PR_Token> where style in
+    //            (PR_TAG, PR_PLAIN, PR_SOURCE, NAME, VALUE, null)
+    var tokensOut = PR_tokenizeMarkup(chunks);
+    tokensOut = PR_splitTagAttributes(tokensOut);
+    tokensOut = PR_splitSourceNodes(tokensOut);
+    tokensOut = PR_splitSourceAttributes(tokensOut);
+    return tokensOut;
+}
+
+/**
+ * classify the string as either source or markup and lex appropriately.
+ * @param {String} html
+ */
+function PR_lexOne( html ) {
+    var chunks = PR_expandTabs(PR_chunkify(html), PR_TAB_WIDTH);
+
+    // treat it as markup if the first non whitespace character is a < and the
+    // last non-whitespace character is a >
+    var isMarkup = false;
+    for ( var i = 0; i < chunks.length; ++i ) {
+        if ( PR_PLAIN == chunks[i].style ) {
+            if ( PR_startsWith(PR_trim(chunks[i].token), '&lt;') ) {
+                for ( var j = chunks.length; --j >= 0; ) {
+                    if ( PR_PLAIN == chunks[j].style ) {
+                        isMarkup = PR_endsWith(PR_trim(chunks[j].token), '&gt;');
+                        break;
+                    }
+                }
+            }
+            break;
+        }
+    }
+
+    return isMarkup ? PR_lexMarkup(chunks) : PR_lexSource(chunks);
+}
+
+/** pretty print a chunk of code.
+ *
+ * @param s code as html
+ * @return code as html, but prettier
+ */
+function prettyPrintOne( s ) {
+    try {
+        var tokens = PR_lexOne(s);
+        var out = [];
+        var lastStyle = null;
+        for ( var i = 0; i < tokens.length; i++ ) {
+            var t = tokens[i];
+            if ( t.style != lastStyle ) {
+                if ( lastStyle != null ) {
+                    out.push('</span>');
+                }
+                if ( t.style != null ) {
+                    out.push('<span class=', t.style, '>');
+                }
+                lastStyle = t.style;
+            }
+            var html = t.token;
+            if ( null != t.style ) {
+                // This interacts badly with some wikis which introduces paragraph tags
+                // into pre blocks for some strange reason.
+                // It's necessary for IE though which seems to lose the preformattedness
+                // of <pre> tags when their innerHTML is assigned.
+                // http://stud3.tuwien.ac.at/~e0226430/innerHtmlQuirk.html
+                html = html
+                    .replace(/(\r\n?|\n| ) /g, '$1&nbsp;')
+                    .replace(/\r\n?|\n/g, '<br>');
+            }
+            out.push(html);
+        }
+        if ( lastStyle != null ) {
+            out.push('</span>');
+        }
+        return out.join('');
+    }
+    catch ( e ) {
+        if ( 'console' in window ) {
+            console.log(e);
+            console.trace();
+        }
+        return s;
+    }
+}
+
+/** find all the < pre > and < code > tags in the DOM with class=prettyprint and
+ * prettify them.
+ */
+function prettyPrint() {
+    // fetch a list of nodes to rewrite
+    var codeSegments = [
+        document.getElementsByTagName('pre'),
+        document.getElementsByTagName('code'),
+        document.getElementsByTagName('xmp') ];
+    var elements = [];
+    for ( var i = 0; i < codeSegments.length; ++i ) {
+        for ( var j = 0; j < codeSegments[i].length; ++j ) {
+            elements.push(codeSegments[i][j]);
+        }
+    }
+    codeSegments = null;
+
+    // the loop is broken into a series of continuations to make sure that we
+    // don't make the browser unresponsive when rewriting a large page.
+    var k = 0;
+
+    function doWork() {
+        var endTime = new Date().getTime() + 250;
+        for ( ; k < elements.length && new Date().getTime() < endTime; k++ ) {
+            var cs = elements[k];
+            if ( cs.className && cs.className.indexOf('prettyprint') >= 0 ) {
+
+                // make sure this is not nested in an already prettified element
+                var nested = false;
+                for ( var p = cs.parentNode; p != null; p = p.parentNode ) {
+                    if ( (p.tagName == 'pre' || p.tagName == 'code' ||
+                          p.tagName == 'xmp') &&
+                         p.className && p.className.indexOf('prettyprint') >= 0 ) {
+                        nested = true;
+                        break;
+                    }
+                }
+                if ( !nested ) {
+                    // fetch the content as a snippet of properly escaped HTML.
+                    // Firefox adds newlines at the end.
+                    var content = PR_getInnerHtml(cs);
+                    content = content.replace(/(?:\r\n?|\n)$/, '');
+
+                    // do the pretty printing
+                    var newContent = prettyPrintOne(content);
+
+                    // push the prettified html back into the tag.
+                    if ( !PR_isRawContent(cs) ) {
+                        // just replace the old html with the new
+                        cs.innerHTML = newContent;
+                    }
+                    else {
+                        // we need to change the tag to a <pre> since <xmp>s do not allow
+                        // embedded tags such as the span tags used to attach styles to
+                        // sections of source code.
+                        var pre = document.createElement('PRE');
+                        for ( var i = 0; i < cs.attributes.length; ++i ) {
+                            var a = cs.attributes[i];
+                            if ( a.specified ) {
+                                pre.setAttribute(a.name, a.value);
+                            }
+                        }
+                        pre.innerHTML = newContent;
+                        // remove the old
+                        cs.parentNode.replaceChild(pre, cs);
+                    }
+                }
+            }
+        }
+        if ( k < elements.length ) {
+            // finish up in a continuation
+            setTimeout(doWork, 250);
+        }
+    }
+
+    doWork();
+}
diff --git a/third_party/java/jopt-simple/src/site/site.xml b/third_party/java/jopt-simple/src/site/site.xml
new file mode 100644
index 0000000..8dafe09
--- /dev/null
+++ b/third_party/java/jopt-simple/src/site/site.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project name="JOpt Simple - a Java command line parsing library">
+   <bannerLeft>
+        <name>JOpt Simple</name>
+        <src>images/jopt-simple-logo.png</src>
+        <href>http://pholser.github.com/jopt-simple/</href>
+    </bannerLeft>
+    <body>
+        <menu name="JOpt Simple">
+            <item name="Overview" href="index.html"/>
+            <item name="Download" href="download.html"/>
+            <item name="Examples" href="examples.html"/>
+            <item name="Change Log" href="changes.html"/>
+            <item name="Javadoc" href="apidocs/index.html"/>
+            <item name="Developer Docs" href="developer.html"/>
+            <item name="Acknowledgements" href="acknowledgments.html"/>
+        </menu>
+        <menu ref="reports"/>
+    </body>
+</project>
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/AbstractOptionParserFixture.java b/third_party/java/jopt-simple/src/test/java/joptsimple/AbstractOptionParserFixture.java
new file mode 100644
index 0000000..3385c51
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/AbstractOptionParserFixture.java
@@ -0,0 +1,59 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.rules.ExpectedException;
+
+import static org.junit.Assert.*;
+import static org.junit.rules.ExpectedException.*;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public abstract class AbstractOptionParserFixture {
+    @Rule public final ExpectedException thrown = none();
+
+    protected OptionParser parser;
+
+    @Before
+    public final void createNewParser() {
+        parser = new OptionParser();
+    }
+
+    static void assertOptionDetected( OptionSet detectedOptions, String option ) {
+        assertTrue( "option '" + option + "' not detected?", detectedOptions.has( option ) );
+    }
+
+    static void assertOptionNotDetected( OptionSet detectedOptions, String option ) {
+        assertOptionNotDetected( "option '" + option + "' detected?", detectedOptions, option );
+    }
+
+    static void assertOptionNotDetected( String message, OptionSet detectedOptions, String option ) {
+        assertFalse( message, detectedOptions.has( option ) );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/AbstractOptionSpecFixture.java b/third_party/java/jopt-simple/src/test/java/joptsimple/AbstractOptionSpecFixture.java
new file mode 100644
index 0000000..91901c9
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/AbstractOptionSpecFixture.java
@@ -0,0 +1,58 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import org.infinitest.toolkit.EqualsHashCodeTestSupport;
+import org.junit.Test;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public abstract class AbstractOptionSpecFixture extends EqualsHashCodeTestSupport {
+    @Override
+    protected AbstractOptionSpec<?> equal() {
+        return createEqualOptionSpecInstance();
+    }
+
+    @Override
+    protected AbstractOptionSpec<?> notEqual() {
+        return createNotEqualOptionSpecInstance();
+    }
+
+    protected abstract AbstractOptionSpec<?> createEqualOptionSpecInstance();
+
+    protected abstract AbstractOptionSpec<?> createNotEqualOptionSpecInstance();
+
+    @Test( expected = NullPointerException.class )
+    public final void testValuesWithNullOptionSet() {
+        createEqualOptionSpecInstance().values( null );
+    }
+
+    @Test( expected = NullPointerException.class )
+    public final void testValueWithNullOptionSet() {
+        createNotEqualOptionSpecInstance().value( null );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/AbstractOptionSpecOptionsImmutabilityTestCase.java b/third_party/java/jopt-simple/src/test/java/joptsimple/AbstractOptionSpecOptionsImmutabilityTestCase.java
new file mode 100644
index 0000000..0660640
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/AbstractOptionSpecOptionsImmutabilityTestCase.java
@@ -0,0 +1,53 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import java.util.List;
+
+import org.infinitest.toolkit.UnmodifiableListTestSupport;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public abstract class AbstractOptionSpecOptionsImmutabilityTestCase extends UnmodifiableListTestSupport<String> {
+    @Override
+    protected List<String> newList() {
+        AbstractOptionSpec<?> spec = newOptionSpec( containedItem() );
+        return spec.options();
+    }
+
+    @Override
+    protected final String newItem() {
+        return "not";
+    }
+
+    @Override
+    protected String containedItem() {
+        return "in";
+    }
+
+    protected abstract AbstractOptionSpec<?> newOptionSpec( String option );
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/AbstractOptionSpecValuesImmutabilityTestCase.java b/third_party/java/jopt-simple/src/test/java/joptsimple/AbstractOptionSpecValuesImmutabilityTestCase.java
new file mode 100644
index 0000000..9e41ef8
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/AbstractOptionSpecValuesImmutabilityTestCase.java
@@ -0,0 +1,52 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import java.util.Collections;
+import java.util.List;
+
+import org.infinitest.toolkit.UnmodifiableListTestSupport;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public abstract class AbstractOptionSpecValuesImmutabilityTestCase<T> extends UnmodifiableListTestSupport<T> {
+    @Override
+    protected List<T> newList() {
+        AbstractOptionSpec<T> spec = newOptionSpec();
+        OptionSet options = new OptionSet( Collections.<String, AbstractOptionSpec<?>> emptyMap() );
+        options.addWithArgument( spec, firstArg() );
+        options.addWithArgument( spec, secondArg() );
+
+        return spec.values( options );
+    }
+
+    protected abstract AbstractOptionSpec<T> newOptionSpec();
+
+    protected abstract String firstArg();
+
+    protected abstract String secondArg();
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/AlternativeLongOptionSpecOptionsImmutabilityTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/AlternativeLongOptionSpecOptionsImmutabilityTest.java
new file mode 100644
index 0000000..529557d
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/AlternativeLongOptionSpecOptionsImmutabilityTest.java
@@ -0,0 +1,43 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import static joptsimple.ParserRules.*;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class AlternativeLongOptionSpecOptionsImmutabilityTest extends AbstractOptionSpecOptionsImmutabilityTestCase {
+    @Override
+    protected AbstractOptionSpec<?> newOptionSpec( String option ) {
+        return new AlternativeLongOptionSpec();
+    }
+
+    @Override
+    protected String containedItem() {
+        return RESERVED_FOR_EXTENSIONS;
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/AlternativeLongOptionSpecValuesImmutabilityTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/AlternativeLongOptionSpecValuesImmutabilityTest.java
new file mode 100644
index 0000000..933c171
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/AlternativeLongOptionSpecValuesImmutabilityTest.java
@@ -0,0 +1,58 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class AlternativeLongOptionSpecValuesImmutabilityTest
+    extends AbstractOptionSpecValuesImmutabilityTestCase<String> {
+
+    @Override
+    protected AbstractOptionSpec<String> newOptionSpec() {
+        return new AlternativeLongOptionSpec();
+    }
+
+    @Override
+    protected String firstArg() {
+        return "x=y";
+    }
+
+    @Override
+    protected String secondArg() {
+        return "z=w";
+    }
+
+    @Override
+    protected String newItem() {
+        return "a=b";
+    }
+
+    @Override
+    protected String containedItem() {
+        return "x=y";
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/ArgumentAcceptingOptionSpecDefaultValuesImmutabilityTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/ArgumentAcceptingOptionSpecDefaultValuesImmutabilityTest.java
new file mode 100644
index 0000000..d20c48c
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/ArgumentAcceptingOptionSpecDefaultValuesImmutabilityTest.java
@@ -0,0 +1,50 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import java.util.List;
+
+import org.infinitest.toolkit.UnmodifiableListTestSupport;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class ArgumentAcceptingOptionSpecDefaultValuesImmutabilityTest extends UnmodifiableListTestSupport<String> {
+    @Override
+    protected List<String> newList() {
+        return new RequiredArgumentOptionSpec<String>( "option" ).defaultsTo( "a", "b" ).defaultValues();
+    }
+
+    @Override
+    protected String containedItem() {
+        return "a";
+    }
+
+    @Override
+    protected String newItem() {
+        return "c";
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/ArgumentAcceptingOptionSpecTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/ArgumentAcceptingOptionSpecTest.java
new file mode 100644
index 0000000..32ef589
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/ArgumentAcceptingOptionSpecTest.java
@@ -0,0 +1,136 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import org.infinitest.toolkit.Block;
+import org.junit.Test;
+
+import static org.infinitest.toolkit.Assertions.*;
+import static org.junit.Assert.*;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class ArgumentAcceptingOptionSpecTest {
+    @Test( expected = NullPointerException.class )
+    public void requiredArgOfNullType() {
+        new RequiredArgumentOptionSpec<Void>( "a" ).ofType( null );
+    }
+
+    @Test( expected = NullPointerException.class )
+    public void optionalArgOfNullType() {
+        new OptionalArgumentOptionSpec<Void>( "verbose" ).ofType( null );
+    }
+
+    @Test( expected = IllegalArgumentException.class )
+    public void requiredArgOfNonValueType() {
+        new RequiredArgumentOptionSpec<Void>( "threshold" ).ofType( Object.class );
+    }
+
+    @Test( expected = IllegalArgumentException.class )
+    public void optionalArgOfNonValueType() {
+        new OptionalArgumentOptionSpec<Void>( "max" ).ofType( Object.class );
+    }
+
+    @Test
+    public void requiredArgOfValueTypeBasedOnValueOf() {
+        assertNoException( new Block() {
+            public void execute() {
+                new RequiredArgumentOptionSpec<Void>( "threshold" ).ofType( ValueOfHaver.class );
+            }
+        } );
+    }
+
+    @Test
+    public void optionalArgOfValueTypeBasedOnValueOf() {
+        assertNoException( new Block() {
+            public void execute() {
+                new OptionalArgumentOptionSpec<Void>( "abc" ).ofType( ValueOfHaver.class );
+            }
+        } );
+    }
+
+    @Test
+    public void requiredArgOfValueTypeBasedOnCtor() {
+        assertNoException( new Block() {
+            public void execute() {
+                new RequiredArgumentOptionSpec<Void>( "threshold" ).ofType( Ctor.class );
+            }
+        } );
+    }
+
+    @Test
+    public void optionalArgOfValueTypeBasedOnCtor() {
+        final OptionalArgumentOptionSpec<Ctor> spec = new OptionalArgumentOptionSpec<>( "abc" );
+
+        assertNoException( new Block() {
+            public void execute() {
+                spec.ofType( Ctor.class );
+                assertEquals( "foo", spec.convert( "foo" ).getS() );
+            }
+        } );
+    }
+
+    @Test( expected = IllegalArgumentException.class )
+    public void rejectsUnicodeZeroAsCharValueSeparatorForRequiredArgument() {
+        new RequiredArgumentOptionSpec<Void>( "a" ).withValuesSeparatedBy( '\u0000' );
+    }
+
+    @Test( expected = IllegalArgumentException.class )
+    public void rejectsUnicodeZeroAsCharValueSeparatorForOptionalArgument() {
+        new OptionalArgumentOptionSpec<Void>( "b" ).withValuesSeparatedBy( '\u0000' );
+    }
+
+    @Test( expected = IllegalArgumentException.class )
+    public void rejectsUnicodeZeroInStringValueSeparatorForRequiredArgument() {
+        new RequiredArgumentOptionSpec<Void>( "c" ).withValuesSeparatedBy( "::\u0000::" );
+    }
+
+    @Test( expected = IllegalArgumentException.class )
+    public void rejectsUnicodeZeroInStringValueSeparatorForOptionalArgument() {
+        new OptionalArgumentOptionSpec<Void>( "d" ).withValuesSeparatedBy( "::::\u0000" );
+    }
+
+    @Test( expected = NullPointerException.class )
+    public void rejectsNullConverter() {
+        new RequiredArgumentOptionSpec<Void>( "c" ).withValuesConvertedBy( null );
+    }
+
+    @Test( expected = NullPointerException.class )
+    public void rejectsNullDefaultValue() {
+        new RequiredArgumentOptionSpec<Integer>( "d" ).defaultsTo( null );
+    }
+
+    @Test( expected = NullPointerException.class )
+    public void rejectsNullDefaultValueRemainder() {
+        new RequiredArgumentOptionSpec<Integer>( "d" ).defaultsTo( 2, (Integer[]) null );
+    }
+
+    @Test( expected = NullPointerException.class )
+    public void rejectsNullInDefaultValueRemainder() {
+        new RequiredArgumentOptionSpec<Integer>( "d" ).defaultsTo( 2, 3, null );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/ArgumentListHasNoMoreArgsTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/ArgumentListHasNoMoreArgsTest.java
new file mode 100644
index 0000000..1ae5075
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/ArgumentListHasNoMoreArgsTest.java
@@ -0,0 +1,56 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class ArgumentListHasNoMoreArgsTest {
+    private ArgumentList noMore;
+
+    @Before
+    public void setUp() {
+        noMore = new ArgumentList();
+    }
+
+    @Test( expected = IndexOutOfBoundsException.class )
+    public void peek() {
+        noMore.peek();
+    }
+
+    @Test( expected = IndexOutOfBoundsException.class )
+    public void next() {
+        noMore.next();
+    }
+
+    @Test( expected = IndexOutOfBoundsException.class )
+    public void treatNextAsLongOption() {
+        noMore.treatNextAsLongOption();
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/ArgumentListTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/ArgumentListTest.java
new file mode 100644
index 0000000..425a41a
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/ArgumentListTest.java
@@ -0,0 +1,75 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class ArgumentListTest {
+    private ArgumentList args;
+
+    @Before
+    public void setUp() {
+        args = new ArgumentList( "-a", "b", "c" );
+    }
+
+    @Test
+    public void nextAndPeek() {
+        assertNextArgumentIs( "-a" );
+        assertNextArgumentIs( "b" );
+        assertNextArgumentIs( "c" );
+        assertNoMoreArguments();
+    }
+
+    @Test
+    public void treatNextAsLongOption() {
+        args.treatNextAsLongOption();
+        assertNextArgumentIs( "-a" );
+
+        args.treatNextAsLongOption();
+        assertNextArgumentIs( "--b" );
+
+        args.treatNextAsLongOption();
+        assertNextArgumentIs( "--c" );
+
+        assertNoMoreArguments();
+    }
+
+    private void assertNextArgumentIs( String expectedArg ) {
+        assertTrue( "no more arguments?", args.hasMore() );
+        assertEquals( "peek?", expectedArg, args.peek() );
+        assertEquals( "next?", expectedArg, args.next() );
+    }
+
+    private void assertNoMoreArguments() {
+        assertFalse( "more arguments?", args.hasMore() );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/AvailableIfAnyTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/AvailableIfAnyTest.java
new file mode 100644
index 0000000..c3e4e59
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/AvailableIfAnyTest.java
@@ -0,0 +1,82 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2014 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import static java.util.Collections.emptyList;
+
+import static org.junit.Assert.assertEquals;
+
+public class AvailableIfAnyTest extends AbstractOptionParserFixture {
+    @Before
+    public void configureParser() {
+        OptionSpec<Void> a = parser.accepts( "a" );
+        parser.accepts( "b" );
+        OptionSpec<Void> c = parser.accepts( "c" );
+        parser.accepts( "d" );
+        parser.accepts( "n" ).availableIf( a, c );
+    }
+
+    @Test
+    public void rejectsCommandLineExistingForbiddenOption() {
+        thrown.expect( UnavailableOptionException.class );
+
+        parser.parse( "-b", "-n" );
+    }
+
+    @Test
+    public void rejectsCommandLineExistingOtherForbiddenOption() {
+        thrown.expect( UnavailableOptionException.class );
+
+        parser.parse( "-d", "-n" );
+    }
+
+    @Test
+    public void rejectsCommandLineOnlyForbiddenOption() {
+        thrown.expect( UnavailableOptionException.class );
+
+        parser.parse( "-n" );
+    }
+
+    @Test
+    public void rejectsCommandLineWithNotAllConditionallyRequiredOptionsPresent() {
+        thrown.expect( UnavailableOptionException.class );
+
+        parser.parse( "-b", "-d", "-n" );
+    }
+
+    @Test
+    public void acceptsCommandLineWithAllowedOptionsPresent() {
+        OptionSet options = parser.parse( "-a", "-c", "-n" );
+        
+        assertOptionDetected( options, "a" );
+        assertOptionDetected( options, "c" );
+        assertOptionDetected( options, "n" );
+        assertEquals( emptyList(), options.nonOptionArguments() );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/AvailableIfUnlessTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/AvailableIfUnlessTest.java
new file mode 100644
index 0000000..6f394b8
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/AvailableIfUnlessTest.java
@@ -0,0 +1,143 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2014 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import static java.util.Arrays.asList;
+import static java.util.Collections.emptyList;
+import static java.util.Collections.singletonList;
+
+import static org.junit.Assert.assertEquals;
+
+public class AvailableIfUnlessTest extends AbstractOptionParserFixture {
+    @Before
+    public void configureParser() {
+        parser.mutuallyExclusive(
+                parser.acceptsAll( asList( "ftp", "file-transfer" ) ),
+                parser.accepts( "file" ),
+                parser.accepts( "http" )
+        );
+
+        parser.acceptsAll( asList( "username", "userid" ) ).availableIf( "file-transfer" ).withRequiredArg();
+        parser.acceptsAll( asList( "password", "pwd" ) ).availableIf( "ftp" ).withRequiredArg();
+        parser.acceptsAll( asList( "directory", "dir" ) ).availableIf( "file" ).withRequiredArg();
+        parser.accepts( "?" ).forHelp();
+    }
+
+    @Test
+    public void rejectsEmptyMutualExclusiveness() {
+        thrown.expect( NullPointerException.class );
+
+        parser.mutuallyExclusive( (OptionSpecBuilder[]) null );
+    }
+
+    @Test
+    public void rejectsConflictingCommandLineOptions1() {
+        thrown.expect( UnavailableOptionException.class );
+
+        parser.parse( "--ftp", "--file" );
+    }
+
+    @Test
+    public void rejectsConflictingCommandLineOptions2() {
+        thrown.expect( UnavailableOptionException.class );
+
+        parser.parse( "--ftp", "--http" );
+    }
+
+    @Test
+    public void rejectsIncompatibleOptions1() {
+        thrown.expect( UnavailableOptionException.class );
+
+        parser.parse( "--file", "--username", "joe" );
+    }
+
+    @Test
+    public void rejectsIncompatibleOptions2() {
+        thrown.expect( UnavailableOptionException.class );
+
+        parser.parse( "--ftp", "--directory", "/tmp" );
+    }
+
+
+    @Test
+    public void acceptsCommandLineWithConditionallyAllowedOptionsPresent1() {
+        OptionSet options = parser.parse( "--ftp", "--userid", "joe", "--password=secret" );
+
+        assertOptionDetected( options, "ftp" );
+        assertOptionDetected( options, "username" );
+        assertOptionDetected( options, "password" );
+        assertEquals( singletonList( "joe" ), options.valuesOf( "username" ) );
+        assertEquals( singletonList( "secret" ), options.valuesOf( "password" ) );
+        assertEquals( emptyList(), options.nonOptionArguments() );
+    }
+
+    @Test
+    public void acceptsCommandLineWithConditionallyAllowedOptionsPresent2() {
+        OptionSet options = parser.parse( "--file", "--dir", "/tmp");
+
+        assertOptionDetected( options, "file" );
+        assertOptionDetected( options, "dir" );
+        assertEquals( singletonList( "/tmp" ), options.valuesOf( "dir" ) );
+        assertEquals( emptyList(), options.nonOptionArguments() );
+    }
+
+    @Test
+    public void rejectsCommandLineOnlyContainingForbiddenOptionSynonym() {
+        thrown.expect( UnavailableOptionException.class );
+
+        parser.parse( "--pwd", "secret" );
+    }
+
+    @Test
+    public void rejectsOptionNotAlreadyConfigured() {
+        thrown.expect( UnconfiguredOptionException.class );
+
+        parser.accepts( "foo" ).availableIf( "bar" );
+        parser.accepts( "foo" ).availableUnless( "bar" );
+    }
+
+    @Test
+    public void presenceOfHelpOptionNegatesAllowedIfness() {
+        OptionSet options = parser.parse( "--file", "--userid", "joe", "-?" );
+
+        assertOptionDetected( options, "file" );
+        assertOptionDetected( options, "?" );
+        assertEquals( emptyList(), options.nonOptionArguments() );
+    }
+
+    @Test
+    public void presenceOfHelpOptionNegatesAllowedUnlessness() {
+        OptionSet options = parser.parse( "--file", "--ftp",  "-?" );
+
+        assertOptionDetected( options, "file" );
+        assertOptionDetected( options, "file-transfer" );
+        assertOptionDetected( options, "?" );
+        assertEquals( emptyList(), options.nonOptionArguments() );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/AvailableUnlessAnyTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/AvailableUnlessAnyTest.java
new file mode 100644
index 0000000..c551916
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/AvailableUnlessAnyTest.java
@@ -0,0 +1,82 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2014 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import static java.util.Collections.emptyList;
+
+import static org.junit.Assert.assertEquals;
+
+public class AvailableUnlessAnyTest extends AbstractOptionParserFixture {
+    @Before
+    public void configureParser() {
+        parser.accepts( "a" );
+        parser.accepts( "b" );
+        OptionSpec<Void> c = parser.accepts( "c" );
+        parser.accepts( "d" );
+        parser.accepts( "n" ).availableUnless( "a" ).availableUnless( c );
+    }
+
+    @Test
+    public void rejectsCommandLineExistingForbiddenOption() {
+        thrown.expect( UnavailableOptionException.class );
+
+        parser.parse( "-a", "-n" );
+    }
+
+    @Test
+    public void rejectsCommandLineExistingOtherForbiddenOption() {
+        thrown.expect( UnavailableOptionException.class );
+
+        parser.parse( "-c", "-n" );
+    }
+
+    @Test
+    public void rejectsCommandLineOnlyForbiddenOption() {
+        OptionSet options = parser.parse( "-n" );
+
+        assertOptionDetected( options, "n" );
+    }
+
+    @Test
+    public void rejectsCommandLineWithNotAllConditionallyRequiredOptionsPresent() {
+        thrown.expect( UnavailableOptionException.class );
+
+        parser.parse( "-a", "-c", "-n" );
+    }
+
+    @Test
+    public void acceptsCommandLineWithAllowedOptionsPresent() {
+        OptionSet options = parser.parse( "-d", "-b", "-n" );
+        
+        assertOptionDetected( options, "b" );
+        assertOptionDetected( options, "d" );
+        assertOptionDetected( options, "n" );
+        assertEquals( emptyList(), options.nonOptionArguments() );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/ClusterVersusLongOptionTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/ClusterVersusLongOptionTest.java
new file mode 100644
index 0000000..be71a1e
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/ClusterVersusLongOptionTest.java
@@ -0,0 +1,84 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class ClusterVersusLongOptionTest extends AbstractOptionParserFixture {
+    @Before
+    public final void initializeParser() {
+        parser.accepts( "fox" );
+        parser.accepts( "f" );
+        parser.accepts( "o" );
+        parser.accepts( "x" );
+    }
+
+    @Test
+    public void resolvesToLongOptionEvenWithMatchingShortOptions() {
+        OptionSet options = parser.parse( "--fox" );
+        assertTrue( options.has( "fox" ) );
+        assertFalse( options.has( "f" ) );
+        assertFalse( options.has( "o" ) );
+        assertFalse( options.has( "x" ) );
+    }
+
+    @Test
+    public void resolvesToLongOptionWithSingleDashEvenWithMatchingShortOptions() {
+        OptionSet options = parser.parse( "-fox" );
+        assertTrue( options.has( "fox" ) );
+        assertFalse( options.has( "f" ) );
+        assertFalse( options.has( "o" ) );
+        assertFalse( options.has( "x" ) );
+    }
+
+    @Test
+    public void resolvesAbbreviationToLongOption() {
+        OptionSet options = parser.parse( "-fo" );
+        assertTrue( options.has( "fox" ) );
+        assertFalse( options.has( "f" ) );
+        assertFalse( options.has( "o" ) );
+    }
+
+    @Test
+    public void resolvesShortOptionToShortOption() {
+        OptionSet options = parser.parse( "-f" );
+        assertFalse( options.has( "fox" ) );
+        assertTrue( options.has( "f" ) );
+    }
+
+    @Test
+    public void resolvesShortOptionToShortOptionEvenWithDoubleHyphen() {
+        OptionSet options = parser.parse( "--f" );
+        assertFalse( options.has( "fox" ) );
+        assertTrue( options.has( "f" ) );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/ClusterVersusLongOptionWithOptionalArgumentTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/ClusterVersusLongOptionWithOptionalArgumentTest.java
new file mode 100644
index 0000000..4e91764
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/ClusterVersusLongOptionWithOptionalArgumentTest.java
@@ -0,0 +1,122 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import static java.util.Collections.*;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class ClusterVersusLongOptionWithOptionalArgumentTest extends AbstractOptionParserFixture {
+    @Before
+    public final void initializeParser() {
+        parser.accepts( "fox" );
+        parser.accepts( "f" );
+        parser.accepts( "o" );
+        parser.accepts( "x" ).withOptionalArg();
+    }
+
+    @Test
+    public void resolvesToLongOptionEvenWithMatchingShortOptions() {
+        OptionSet options = parser.parse( "--fox" );
+        assertTrue( options.has( "fox" ) );
+        assertFalse( options.has( "f" ) );
+        assertFalse( options.has( "o" ) );
+        assertFalse( options.has( "x" ) );
+    }
+
+    @Test
+    public void resolvesToLongOptionWithSingleDashEvenWithMatchingShortOptions() {
+        OptionSet options = parser.parse( "-fox" );
+        assertTrue( options.has( "fox" ) );
+        assertFalse( options.has( "f" ) );
+        assertFalse( options.has( "o" ) );
+        assertFalse( options.has( "x" ) );
+    }
+
+    @Test
+    public void resolvesAbbreviationToLongOption() {
+        OptionSet options = parser.parse( "-fo" );
+        assertTrue( options.has( "fox" ) );
+        assertFalse( options.has( "f" ) );
+        assertFalse( options.has( "o" ) );
+    }
+
+    @Test
+    public void resolvesShortOptionToShortOption() {
+        OptionSet options = parser.parse( "-f" );
+        assertFalse( options.has( "fox" ) );
+        assertTrue( options.has( "f" ) );
+    }
+
+    @Test
+    public void resolvesShortOptionToShortOptionEvenWithDoubleHyphen() {
+        OptionSet options = parser.parse( "--f" );
+        assertFalse( options.has( "fox" ) );
+        assertTrue( options.has( "f" ) );
+    }
+
+    @Test
+    public void resolvesToShortOptionsWithArgumentFollowingX() {
+        OptionSet options = parser.parse( "-foxbar" );
+        assertFalse( options.has( "fox" ) );
+        assertTrue( options.has( "f" ) );
+        assertTrue( options.has( "o" ) );
+        assertTrue( options.has( "x" ) );
+        assertEquals( singletonList( "bar" ), options.valuesOf( "x" ) );
+    }
+
+    @Test
+    public void shortOptionsInDifferentOrder() {
+        OptionSet options = parser.parse( "-fxo" );
+        assertFalse( options.has( "fox" ) );
+        assertTrue( options.has( "f" ) );
+        assertFalse( options.has( "o" ) );
+        assertTrue( options.has( "x" ) );
+        assertEquals( singletonList( "o" ), options.valuesOf( "x" ) );
+    }
+
+    @Test( expected = UnrecognizedOptionException.class )
+    public void longOptionWithMessedUpOrder() {
+        parser.parse( "--fxo" );
+    }
+
+    @Test
+    public void withArgumentComingAfterCluster() {
+        OptionSet options = parser.parse( "-fox", "bar" );
+
+        assertTrue( options.has( "fox" ) );
+        assertFalse( options.has( "f" ) );
+        assertFalse( options.has( "o" ) );
+        assertFalse( options.has( "x" ) );
+        assertEquals( singletonList( "bar" ), options.nonOptionArguments() );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/ClusterVersusLongOptionWithRequiredArgumentTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/ClusterVersusLongOptionWithRequiredArgumentTest.java
new file mode 100644
index 0000000..b20fe67
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/ClusterVersusLongOptionWithRequiredArgumentTest.java
@@ -0,0 +1,122 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import static java.util.Collections.*;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class ClusterVersusLongOptionWithRequiredArgumentTest extends AbstractOptionParserFixture {
+    @Before
+    public final void initializeParser() {
+        parser.accepts( "fox" );
+        parser.accepts( "f" );
+        parser.accepts( "o" );
+        parser.accepts( "x" ).withRequiredArg();
+    }
+
+    @Test
+    public void resolvesToLongOptionEvenWithMatchingShortOptions() {
+        OptionSet options = parser.parse( "--fox" );
+        assertTrue( options.has( "fox" ) );
+        assertFalse( options.has( "f" ) );
+        assertFalse( options.has( "o" ) );
+        assertFalse( options.has( "x" ) );
+    }
+
+    @Test
+    public void resolvesToLongOptionWithSingleDashEvenWithMatchingShortOptions() {
+        OptionSet options = parser.parse( "-fox" );
+        assertTrue( options.has( "fox" ) );
+        assertFalse( options.has( "f" ) );
+        assertFalse( options.has( "o" ) );
+        assertFalse( options.has( "x" ) );
+    }
+
+    @Test
+    public void resolvesAbbreviationToLongOption() {
+        OptionSet options = parser.parse( "-fo" );
+        assertTrue( options.has( "fox" ) );
+        assertFalse( options.has( "f" ) );
+        assertFalse( options.has( "o" ) );
+    }
+
+    @Test
+    public void resolvesShortOptionToShortOption() {
+        OptionSet options = parser.parse( "-f" );
+        assertFalse( options.has( "fox" ) );
+        assertTrue( options.has( "f" ) );
+    }
+
+    @Test
+    public void resolvesShortOptionToShortOptionEvenWithDoubleHyphen() {
+        OptionSet options = parser.parse( "--f" );
+        assertFalse( options.has( "fox" ) );
+        assertTrue( options.has( "f" ) );
+    }
+
+    @Test
+    public void resolvesToShortOptionsWithArgumentFollowingX() {
+        OptionSet options = parser.parse( "-foxbar" );
+        assertFalse( options.has( "fox" ) );
+        assertTrue( options.has( "f" ) );
+        assertTrue( options.has( "o" ) );
+        assertTrue( options.has( "x" ) );
+        assertEquals( singletonList( "bar" ), options.valuesOf( "x" ) );
+    }
+
+    @Test
+    public void shortOptionsInDifferentOrder() {
+        OptionSet options = parser.parse( "-fxo" );
+        assertFalse( options.has( "fox" ) );
+        assertTrue( options.has( "f" ) );
+        assertFalse( options.has( "o" ) );
+        assertTrue( options.has( "x" ) );
+        assertEquals( singletonList( "o" ), options.valuesOf( "x" ) );
+    }
+
+    @Test( expected = UnrecognizedOptionException.class )
+    public void longOptionWithMessedUpOrder() {
+        parser.parse( "--fxo" );
+    }
+
+    @Test
+    public void withArgumentComingAfterCluster() {
+        OptionSet options = parser.parse( "-fox", "bar" );
+
+        assertTrue( options.has( "fox" ) );
+        assertFalse( options.has( "f" ) );
+        assertFalse( options.has( "o" ) );
+        assertFalse( options.has( "x" ) );
+        assertEquals( singletonList( "bar" ), options.nonOptionArguments() );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/ClusterWhereManyOptionsAcceptArgumentsTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/ClusterWhereManyOptionsAcceptArgumentsTest.java
new file mode 100644
index 0000000..ae82f27
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/ClusterWhereManyOptionsAcceptArgumentsTest.java
@@ -0,0 +1,183 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import static java.util.Collections.*;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class ClusterWhereManyOptionsAcceptArgumentsTest extends AbstractOptionParserFixture {
+    @Before
+    public final void initializeParser() {
+        parser.accepts( "f" );
+        parser.accepts( "o" ).withOptionalArg();
+        parser.accepts( "x" ).withRequiredArg();
+    }
+
+    @Test
+    public void foxPermutation() {
+        OptionSet options = parser.parse( "-fox" );
+
+        assertTrue( options.has( "f" ) );
+        assertTrue( options.has( "o" ) );
+        assertFalse( options.has( "x" ) );
+
+        assertEquals( singletonList( "x" ), options.valuesOf( "o" ) );
+    }
+
+    @Test
+    public void fxoPermutation() {
+        OptionSet options = parser.parse( "-fxo" );
+
+        assertTrue( options.has( "f" ) );
+        assertFalse( options.has( "o" ) );
+        assertTrue( options.has( "x" ) );
+
+        assertEquals( singletonList( "o" ), options.valuesOf( "x" ) );
+    }
+
+    @Test
+    public void ofxPermutation() {
+        OptionSet options = parser.parse( "-ofx" );
+
+        assertFalse( options.has( "f" ) );
+        assertTrue( options.has( "o" ) );
+        assertFalse( options.has( "x" ) );
+
+        assertEquals( singletonList( "fx" ), options.valuesOf( "o" ) );
+    }
+
+    @Test
+    public void oxfPermutation() {
+        OptionSet options = parser.parse( "-oxf" );
+
+        assertFalse( options.has( "f" ) );
+        assertTrue( options.has( "o" ) );
+        assertFalse( options.has( "x" ) );
+
+        assertEquals( singletonList( "xf" ), options.valuesOf( "o" ) );
+    }
+
+    @Test
+    public void xofPermutation() {
+        OptionSet options = parser.parse( "-xof" );
+
+        assertFalse( options.has( "f" ) );
+        assertFalse( options.has( "o" ) );
+        assertTrue( options.has( "x" ) );
+
+        assertEquals( singletonList( "of" ), options.valuesOf( "x" ) );
+    }
+
+    @Test
+    public void xfoPermutation() {
+        OptionSet options = parser.parse( "-xfo" );
+
+        assertFalse( options.has( "f" ) );
+        assertFalse( options.has( "o" ) );
+        assertTrue( options.has( "x" ) );
+
+        assertEquals( singletonList( "fo" ), options.valuesOf( "x" ) );
+    }
+
+    @Test
+    public void foxPermutationWithFollowingArg() {
+        OptionSet options = parser.parse( "-fox", "bar" );
+
+        assertTrue( options.has( "f" ) );
+        assertTrue( options.has( "o" ) );
+        assertFalse( options.has( "x" ) );
+
+        assertEquals( singletonList( "x" ), options.valuesOf( "o" ) );
+        assertEquals( singletonList( "bar" ), options.nonOptionArguments() );
+    }
+
+    @Test
+    public void fxoPermutationWithFollowingArg() {
+        OptionSet options = parser.parse( "-fxo", "bar" );
+
+        assertTrue( options.has( "f" ) );
+        assertFalse( options.has( "o" ) );
+        assertTrue( options.has( "x" ) );
+
+        assertEquals( singletonList( "o" ), options.valuesOf( "x" ) );
+        assertEquals( singletonList( "bar" ), options.nonOptionArguments() );
+    }
+
+    @Test
+    public void ofxPermutationWithFollowingArg() {
+        OptionSet options = parser.parse( "-ofx", "bar" );
+
+        assertFalse( options.has( "f" ) );
+        assertTrue( options.has( "o" ) );
+        assertFalse( options.has( "x" ) );
+
+        assertEquals( singletonList( "fx" ), options.valuesOf( "o" ) );
+        assertEquals( singletonList( "bar" ), options.nonOptionArguments() );
+    }
+
+    @Test
+    public void oxfPermutationWithFollowingArg() {
+        OptionSet options = parser.parse( "-oxf", "bar" );
+
+        assertFalse( options.has( "f" ) );
+        assertTrue( options.has( "o" ) );
+        assertFalse( options.has( "x" ) );
+
+        assertEquals( singletonList( "xf" ), options.valuesOf( "o" ) );
+        assertEquals( singletonList( "bar" ), options.nonOptionArguments() );
+    }
+
+    @Test
+    public void xofPermutationWithFollowingArg() {
+        OptionSet options = parser.parse( "-xof", "bar" );
+
+        assertFalse( options.has( "f" ) );
+        assertFalse( options.has( "o" ) );
+        assertTrue( options.has( "x" ) );
+
+        assertEquals( singletonList( "of" ), options.valuesOf( "x" ) );
+        assertEquals( singletonList( "bar" ), options.nonOptionArguments() );
+    }
+
+    @Test
+    public void xfoPermutationWithFollowingArg() {
+        OptionSet options = parser.parse( "-xfo", "bar" );
+
+        assertFalse( options.has( "f" ) );
+        assertFalse( options.has( "o" ) );
+        assertTrue( options.has( "x" ) );
+
+        assertEquals( singletonList( "fo" ), options.valuesOf( "x" ) );
+        assertEquals( singletonList( "bar" ), options.nonOptionArguments() );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/ConfigurableOptionParserHelpTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/ConfigurableOptionParserHelpTest.java
new file mode 100644
index 0000000..066ca28
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/ConfigurableOptionParserHelpTest.java
@@ -0,0 +1,484 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import java.io.StringWriter;
+import java.math.BigDecimal;
+import java.util.Date;
+import java.util.List;
+
+import static java.math.BigDecimal.*;
+import static java.util.Arrays.*;
+import static java.util.Collections.*;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import static joptsimple.internal.Strings.*;
+import static joptsimple.util.DateConverter.*;
+import static org.junit.Assert.*;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class ConfigurableOptionParserHelpTest extends AbstractOptionParserFixture {
+    private StringWriter sink;
+
+    @Before
+    public final void createSink() {
+        parser.formatHelpWith( new BuiltinHelpFormatter( 120, 3 ) );
+        sink = new StringWriter();
+    }
+
+    @Test
+    public void unconfiguredParser() throws Exception {
+        parser.printHelpOn( sink );
+
+        assertHelpLines( "No options specified   ", EMPTY );
+    }
+
+    @Test
+    public void oneOptionNoArgNoDescription() throws Exception {
+        parser.accepts( "apple" );
+
+        parser.printHelpOn( sink );
+
+        assertHelpLines(
+            "Option    Description",
+            "------    -----------",
+            "--apple              ",
+            EMPTY );
+    }
+
+    @Test
+    public void oneOptionNoArgWithDescription() throws Exception {
+        parser.accepts( "a", "some description" );
+
+        parser.printHelpOn( sink );
+
+        assertHelpLines(
+            "Option   Description     ",
+            "------   -----------     ",
+            "-a       some description",
+            EMPTY );
+    }
+
+    @Test
+    public void twoOptionsNoArgWithDescription() throws Exception {
+        parser.accepts( "a", "some description" );
+        parser.accepts( "verbose", "even more description" );
+
+        parser.printHelpOn( sink );
+
+        assertHelpLines(
+            "Option      Description          ",
+            "------      -----------          ",
+            "-a          some description     ",
+            "--verbose   even more description",
+            EMPTY );
+    }
+
+    @Test
+    public void oneOptionRequiredArgNoDescription() throws Exception {
+        parser.accepts( "a" ).withRequiredArg();
+
+        parser.printHelpOn( sink );
+
+        assertHelpLines(
+            "Option        Description",
+            "------        -----------",
+            "-a <String>              ",
+            EMPTY );
+    }
+
+    @Test
+    public void oneOptionRequiredArgNoDescriptionWithType() throws Exception {
+        parser.accepts( "a" ).withRequiredArg().ofType( Integer.class );
+
+        parser.printHelpOn( sink );
+
+        assertHelpLines(
+            "Option         Description",
+            "------         -----------",
+            "-a <Integer>              ",
+            EMPTY );
+    }
+
+    @Test
+    public void oneOptionRequiredArgWithDescription() throws Exception {
+        parser.accepts( "a", "some value you need" ).withRequiredArg().describedAs( "numerical" );
+
+        parser.printHelpOn( sink );
+
+        assertHelpLines(
+            "Option                   Description        ",
+            "------                   -----------        ",
+            "-a <String: numerical>   some value you need",
+            EMPTY );
+    }
+
+    @Test
+    public void oneOptionRequiredArgWithDescriptionAndType() throws Exception {
+        parser.accepts( "a", "some value you need" ).withRequiredArg().describedAs( "numerical" )
+            .ofType( Integer.class );
+
+        parser.printHelpOn( sink );
+
+        assertHelpLines(
+            "Option                    Description        ",
+            "------                    -----------        ",
+            "-a <Integer: numerical>   some value you need",
+            EMPTY );
+    }
+
+    @Test
+    public void oneOptionOptionalArgNoDescription() throws Exception {
+        parser.accepts( "threshold" ).withOptionalArg();
+
+        parser.printHelpOn( sink );
+
+        assertHelpLines(
+            "Option                 Description",
+            "------                 -----------",
+            "--threshold [String]              ",
+            EMPTY );
+    }
+
+    @Test
+    public void oneOptionOptionalArgNoDescriptionWithType() throws Exception {
+        parser.accepts( "a" ).withOptionalArg().ofType( Float.class );
+
+        parser.printHelpOn( sink );
+
+        assertHelpLines(
+            "Option       Description",
+            "------       -----------",
+            "-a [Float]              ",
+            EMPTY );
+    }
+
+    @Test
+    public void oneOptionOptionalArgWithDescription() throws Exception {
+        parser.accepts( "threshold", "some value you need" ).withOptionalArg().describedAs( "positive integer" );
+
+        parser.printHelpOn( sink );
+
+        assertHelpLines(
+            "Option                                   Description        ",
+            "------                                   -----------        ",
+            "--threshold [String: positive integer]   some value you need",
+            EMPTY );
+    }
+
+    @Test
+    public void oneOptionOptionalArgWithDescriptionAndType() throws Exception {
+        parser.accepts( "threshold", "some value you need" ).withOptionalArg().describedAs( "positive decimal" )
+            .ofType( Double.class );
+
+        parser.printHelpOn( sink );
+
+        assertHelpLines(
+            "Option                                   Description        ",
+            "------                                   -----------        ",
+            "--threshold [Double: positive decimal]   some value you need",
+            EMPTY );
+    }
+
+    @Test
+    public void alternativeLongOptions() throws Exception {
+        parser.recognizeAlternativeLongOptions( true );
+
+        parser.printHelpOn( sink );
+
+        assertHelpLines(
+            "Option                   Description                     ",
+            "------                   -----------                     ",
+            "-W <String: opt=value>   Alternative form of long options",
+            EMPTY );
+    }
+
+    @Test
+    public void optionSynonymsWithoutArguments() throws Exception {
+        parser.acceptsAll( asList( "v", "chatty" ), "be verbose" );
+
+        parser.printHelpOn( sink );
+
+        assertHelpLines(
+            "Option         Description",
+            "------         -----------",
+            "-v, --chatty   be verbose ",
+            EMPTY );
+    }
+
+    @Test
+    public void optionSynonymsWithRequiredArgument() throws Exception {
+        parser.acceptsAll( asList( "L", "index" ), "set level" ).withRequiredArg().ofType( Integer.class );
+
+        parser.printHelpOn( sink );
+
+        assertHelpLines(
+            "Option                  Description",
+            "------                  -----------",
+            "-L, --index <Integer>   set level  ",
+            EMPTY );
+    }
+
+    @Test
+    public void optionSynonymsWithOptionalArgument() throws Exception {
+        parser.acceptsAll( asList( "d", "since" ), "date filter" ).withOptionalArg().describedAs( "yyyyMMdd" )
+            .ofType( Date.class );
+
+        parser.printHelpOn( sink );
+
+        assertHelpLines(
+            "Option                         Description",
+            "------                         -----------",
+            "-d, --since [Date: yyyyMMdd]   date filter",
+            EMPTY );
+    }
+
+    @Test
+    public void optionSynonymsSortedByShortOptionThenLexicographical() throws Exception {
+        parser.acceptsAll( asList( "v", "prolix", "chatty" ) );
+
+        parser.printHelpOn( sink );
+
+        assertHelpLines(
+            "Option                   Description",
+            "------                   -----------",
+            "-v, --chatty, --prolix              ",
+            EMPTY );
+    }
+
+    @Test
+    public void bothColumnsExceedingAllocatedWidths() throws Exception {
+        parser.acceptsAll( asList( "t", "threshold", "cutoff" ),
+                "a threshold value beyond which a certain level of the application should cease to write logs" )
+                .withRequiredArg()
+                .describedAs( "a positive decimal number that will represent the threshold that has been outlined" )
+                .ofType( Double.class );
+
+        parser.printHelpOn( sink );
+
+        assertHelpLines(
+            "Option                                                     Description                                          ",
+            "------                                                     -----------                                          ",
+            "-t, --cutoff, --threshold <Double: a positive decimal      a threshold value beyond which a certain level of the",
+            "  number that will represent the threshold that has been     application should cease to write logs             ",
+            "  outlined>                                                                                                     ",
+            EMPTY );
+    }
+
+    // Bug 2018262
+    @Test
+    public void gradleHelp() throws Exception {
+        parser.acceptsAll( asList( "n", "non-recursive" ), "Do not execute primary tasks of child projects." );
+        parser.acceptsAll( singletonList( "S" ),
+            "Don't trigger a System.exit(0) for normal termination. Used for Gradle's internal testing." );
+        parser.acceptsAll( asList( "I", "no-imports" ), "Disable usage of default imports for build script files." );
+        parser.acceptsAll( asList( "u", "no-search-upward" ),
+            "Don't search in parent folders for a settings.gradle file." );
+        parser.acceptsAll( asList( "x", "cache-off" ), "No caching of compiled build scripts." );
+        parser.acceptsAll( asList( "r", "rebuild-cache" ), "Rebuild the cache of compiled build scripts." );
+        parser.acceptsAll( asList( "v", "version" ), "Print version info." );
+        parser.acceptsAll( asList( "d", "debug" ), "Log in debug mode (includes normal stacktrace)." );
+        parser.acceptsAll( asList( "q", "quiet" ), "Log errors only." );
+        parser.acceptsAll( asList( "j", "ivy-debug" ), "Set Ivy log level to debug (very verbose)." );
+        parser.acceptsAll( asList( "i", "ivy-quiet" ), "Set Ivy log level to quiet." );
+        parser.acceptsAll( asList( "s", "stacktrace" ),
+            "Print out the stacktrace also for user exceptions (e.g. compile error)." );
+        parser.acceptsAll( asList( "f", "full-stacktrace" ),
+            "Print out the full (very verbose) stacktrace for any exceptions." );
+        parser.acceptsAll( asList( "t", "tasks" ), "Show list of all available tasks and their dependencies." );
+        parser.acceptsAll( asList( "p", "project-dir" ),
+            "Specifies the start dir for Gradle. Defaults to current dir." ).withRequiredArg().ofType( String.class );
+        parser.acceptsAll( asList( "g", "gradle-user-home" ), "Specifies the gradle user home dir." )
+            .withRequiredArg().ofType( String.class );
+        parser.acceptsAll( asList( "l", "plugin-properties-file" ), "Specifies the plugin.properties file." )
+            .withRequiredArg().ofType( String.class );
+        parser.acceptsAll( asList( "b", "buildfile" ),
+            "Specifies the build file name (also for subprojects). Defaults to build.gradle." )
+            .withRequiredArg().ofType( String.class );
+        parser.acceptsAll( asList( "D", "systemprop" ), "Set system property of the JVM (e.g. -Dmyprop=myvalue)." )
+            .withRequiredArg().ofType( String.class );
+        parser.acceptsAll( asList( "P", "projectprop" ),
+            "Set project property for the build script (e.g. -Pmyprop=myvalue)." )
+            .withRequiredArg().ofType( String.class );
+        parser.acceptsAll( asList( "e", "embedded" ), "Specify an embedded build script." )
+            .withRequiredArg().ofType( String.class );
+        parser.acceptsAll( asList( "B", "bootstrap-debug" ),
+            "Specify a text to be logged at the beginning (e.g. used by Gradle's bootstrap class)." )
+            .withRequiredArg().ofType( String.class );
+        parser.acceptsAll( asList( "h", "?" ), "Shows this help message." ).forHelp();
+
+        parser.printHelpOn( sink );
+
+        assertHelpLines(
+            "Option                                  Description                                                                    ",
+            "------                                  -----------                                                                    ",
+            "-?, -h                                  Shows this help message.                                                       ",
+            "-B, --bootstrap-debug <String>          Specify a text to be logged at the beginning (e.g. used by Gradle's bootstrap  ",
+            "                                          class).                                                                      ",
+            "-D, --systemprop <String>               Set system property of the JVM (e.g. -Dmyprop=myvalue).                        ",
+            "-I, --no-imports                        Disable usage of default imports for build script files.                       ",
+            "-P, --projectprop <String>              Set project property for the build script (e.g. -Pmyprop=myvalue).             ",
+            "-S                                      Don't trigger a System.exit(0) for normal termination. Used for Gradle's       ",
+            "                                          internal testing.                                                            ",
+            "-b, --buildfile <String>                Specifies the build file name (also for subprojects). Defaults to build.gradle.",
+            "-d, --debug                             Log in debug mode (includes normal stacktrace).                                ",
+            "-e, --embedded <String>                 Specify an embedded build script.                                              ",
+            "-f, --full-stacktrace                   Print out the full (very verbose) stacktrace for any exceptions.               ",
+            "-g, --gradle-user-home <String>         Specifies the gradle user home dir.                                            ",
+            "-i, --ivy-quiet                         Set Ivy log level to quiet.                                                    ",
+            "-j, --ivy-debug                         Set Ivy log level to debug (very verbose).                                     ",
+            "-l, --plugin-properties-file <String>   Specifies the plugin.properties file.                                          ",
+            "-n, --non-recursive                     Do not execute primary tasks of child projects.                                ",
+            "-p, --project-dir <String>              Specifies the start dir for Gradle. Defaults to current dir.                   ",
+            "-q, --quiet                             Log errors only.                                                               ",
+            "-r, --rebuild-cache                     Rebuild the cache of compiled build scripts.                                   ",
+            "-s, --stacktrace                        Print out the stacktrace also for user exceptions (e.g. compile error).        ",
+            "-t, --tasks                             Show list of all available tasks and their dependencies.                       ",
+            "-u, --no-search-upward                  Don't search in parent folders for a settings.gradle file.                     ",
+            "-v, --version                           Print version info.                                                            ",
+            "-x, --cache-off                         No caching of compiled build scripts.                                          ",
+            EMPTY );
+    }
+
+    @Test
+    public void dateConverterShowsDatePattern() throws Exception {
+        parser.accepts( "date", "a date" ).withRequiredArg().withValuesConvertedBy( datePattern( "MM/dd/yy" ) );
+
+        parser.printHelpOn( sink );
+
+        assertHelpLines(
+            "Option              Description",
+            "------              -----------",
+            "--date <MM/dd/yy>   a date     ",
+            EMPTY );
+    }
+
+    @Test
+    public void dateConverterShowsDatePatternInCombinationWithDescription() throws Exception {
+        parser.accepts( "date", "a date" ).withOptionalArg().describedAs( "your basic date pattern" )
+            .withValuesConvertedBy( datePattern( "MM/dd/yy" ) );
+
+        parser.printHelpOn( sink );
+
+        assertHelpLines(
+            "Option                                       Description",
+            "------                                       -----------",
+            "--date [MM/dd/yy: your basic date pattern]   a date     ",
+            EMPTY );
+    }
+
+    @Test
+    public void leavesEmbeddedNewlinesInDescriptionsAlone() throws Exception {
+        List<String> descriptionPieces =
+            asList( "Specify the output type.", "'raw' = raw data.", "'java' = java class" );
+        parser.accepts( "type", join( descriptionPieces, LINE_SEPARATOR ) );
+
+        parser.printHelpOn( sink );
+
+        assertHelpLines(
+            "Option   Description             ",
+            "------   -----------             ",
+            "--type   Specify the output type.",
+            "         'raw' = raw data.       ",
+            "         'java' = java class     ",
+            EMPTY );
+    }
+
+    @Test
+    public void includesDefaultValueForRequiredOptionArgument() throws Exception {
+        parser.accepts( "a" ).withRequiredArg().defaultsTo( "boo" );
+
+        parser.printHelpOn( sink );
+
+        assertHelpLines(
+            "Option        Description   ",
+            "------        -----------   ",
+            "-a <String>   (default: boo)",
+            EMPTY );
+    }
+
+    @Test
+    public void includesDefaultValueForOptionalOptionArgument() throws Exception {
+        parser.accepts( "b" ).withOptionalArg().ofType( Integer.class ).defaultsTo( 5 );
+
+        parser.printHelpOn( sink );
+
+        assertHelpLines(
+            "Option         Description ",
+            "------         ----------- ",
+            "-b [Integer]   (default: 5)",
+            EMPTY );
+    }
+
+    @Test
+    public void includesDefaultValueForArgumentWithDescription() throws Exception {
+        parser.accepts( "c", "a quantity" ).withOptionalArg().ofType( BigDecimal.class )
+            .describedAs( "quantity" ).defaultsTo( TEN );
+
+        parser.printHelpOn( sink );
+
+        assertHelpLines(
+            "Option                      Description             ",
+            "------                      -----------             ",
+            "-c [BigDecimal: quantity]   a quantity (default: 10)",
+            EMPTY );
+    }
+
+    @Test
+    public void includesListOfDefaultsForArgumentWithDescription() throws Exception {
+        parser.accepts( "d", "dizzle" ).withOptionalArg().ofType( Integer.class )
+            .describedAs( "double dizzle" ).defaultsTo( 2, 3, 5, 7 );
+
+        parser.printHelpOn( sink );
+
+        assertHelpLines(
+            "Option                        Description                   ",
+            "------                        -----------                   ",
+            "-d [Integer: double dizzle]   dizzle (default: [2, 3, 5, 7])",
+            EMPTY );
+    }
+
+    @Test
+    public void marksRequiredOptionsSpecially() throws Exception {
+        parser.accepts( "e" ).withRequiredArg().required();
+
+        parser.printHelpOn( sink );
+
+        assertHelpLines(
+            "Option (* = required)   Description",
+            "---------------------   -----------",
+            "* -e <String>                      ",
+            EMPTY );
+    }
+
+    private void assertHelpLines( String... expectedLines ) {
+        assertEquals( join( expectedLines, LINE_SEPARATOR ), sink.toString() );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/CreateWithOptionSpecificationStringTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/CreateWithOptionSpecificationStringTest.java
new file mode 100644
index 0000000..545676a
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/CreateWithOptionSpecificationStringTest.java
@@ -0,0 +1,51 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class CreateWithOptionSpecificationStringTest {
+    @Test
+    public void createsParserWithOptionSpecificationString() {
+        OptionParser first = new OptionParser() {
+            {
+                accepts( "i" ).withOptionalArg();
+                accepts( "j" ).withRequiredArg();
+                accepts( "k" );
+            }
+        };
+
+        OptionParser second = new OptionParser( "i::j:k" );
+
+        String[] args = { "-k", "-ifoo", "-jbar" };
+        assertEquals( first.parse( args ), second.parse( args ) );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/Ctor.java b/third_party/java/jopt-simple/src/test/java/joptsimple/Ctor.java
new file mode 100644
index 0000000..ad26a4d
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/Ctor.java
@@ -0,0 +1,45 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class Ctor {
+    private String s;
+
+    public Ctor( String s ) {
+        this.s = s;
+    }
+
+    public String getS() {
+        return s;
+    }
+
+    static Ctor valueOf( String s ) {
+        return new Ctor( s );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/DefaultSettingsOptionParserHelpTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/DefaultSettingsOptionParserHelpTest.java
new file mode 100644
index 0000000..e9fd215
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/DefaultSettingsOptionParserHelpTest.java
@@ -0,0 +1,753 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.StringWriter;
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+import joptsimple.util.InetAddressConverter;
+import org.junit.Before;
+import org.junit.Test;
+
+import static java.math.BigDecimal.*;
+import static java.util.Arrays.*;
+import static java.util.Collections.*;
+import static joptsimple.internal.Strings.*;
+import static joptsimple.util.DateConverter.*;
+import static org.junit.Assert.*;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class DefaultSettingsOptionParserHelpTest extends AbstractOptionParserFixture {
+    private StringWriter sink;
+
+    @Before
+    public final void createSink() {
+        sink = new StringWriter();
+    }
+
+    @Test
+    public void unconfiguredParser() throws Exception {
+        parser.printHelpOn( sink );
+
+        assertHelpLines( "No options specified  ", EMPTY );
+    }
+
+    @Test
+    public void repeatedCalls() throws Exception {
+        parser.accepts( "apple" );
+
+        parser.printHelpOn( sink );
+        parser.printHelpOn( sink );
+
+        assertHelpLines(
+            "Option   Description",
+            "------   -----------",
+            "--apple             ",
+            "Option   Description",
+            "------   -----------",
+            "--apple             ",
+            EMPTY );
+    }
+
+    @Test
+    public void oneOptionNoArgNoDescription() throws Exception {
+        parser.accepts( "apple" );
+
+        parser.printHelpOn( sink );
+
+        assertHelpLines(
+                "Option   Description",
+                "------   -----------",
+                "--apple             ",
+                EMPTY );
+    }
+
+    @Test
+    public void oneOptionNoArgWithDescription() throws Exception {
+        parser.accepts( "a", "some description" );
+
+        parser.printHelpOn( sink );
+
+        assertHelpLines(
+            "Option  Description     ",
+            "------  -----------     ",
+            "-a      some description",
+            EMPTY );
+    }
+
+    @Test
+    public void twoOptionsNoArgWithDescription() throws Exception {
+        parser.accepts( "a", "some description" );
+        parser.accepts( "verbose", "even more description" );
+
+        parser.printHelpOn( sink );
+
+        assertHelpLines(
+            "Option     Description          ",
+            "------     -----------          ",
+            "-a         some description     ",
+            "--verbose  even more description",
+            EMPTY );
+    }
+
+    @Test
+    public void oneOptionRequiredArgNoDescription() throws Exception {
+        parser.accepts( "a" ).withRequiredArg();
+
+        parser.printHelpOn( sink );
+
+        assertHelpLines(
+            "Option       Description",
+            "------       -----------",
+            "-a <String>             ",
+            EMPTY );
+    }
+
+    @Test
+    public void oneOptionRequiredArgNoDescriptionWithType() throws Exception {
+        parser.accepts( "a" ).withRequiredArg().ofType( Integer.class );
+
+        parser.printHelpOn( sink );
+
+        assertHelpLines(
+            "Option        Description",
+            "------        -----------",
+            "-a <Integer>             ",
+            EMPTY );
+    }
+
+    @Test
+    public void oneOptionRequiredArgWithDescription() throws Exception {
+        parser.accepts( "a", "some value you need" ).withRequiredArg().describedAs( "numerical" );
+
+        parser.printHelpOn( sink );
+
+        assertHelpLines(
+            "Option                  Description        ",
+            "------                  -----------        ",
+            "-a <String: numerical>  some value you need",
+            EMPTY );
+    }
+
+    @Test
+    public void oneOptionRequiredArgWithDescriptionAndType() throws Exception {
+        parser.accepts( "a", "some value you need" )
+                .withRequiredArg().describedAs( "numerical" ).ofType( Integer.class );
+
+        parser.printHelpOn( sink );
+
+        assertHelpLines(
+            "Option                   Description        ",
+            "------                   -----------        ",
+            "-a <Integer: numerical>  some value you need",
+            EMPTY );
+    }
+
+    @Test
+    public void oneOptionOptionalArgNoDescription() throws Exception {
+        parser.accepts( "threshold" ).withOptionalArg();
+
+        parser.printHelpOn( sink );
+
+        assertHelpLines(
+            "Option                Description",
+            "------                -----------",
+            "--threshold [String]             ",
+            EMPTY );
+    }
+
+    @Test
+    public void oneOptionOptionalArgNoDescriptionWithType() throws Exception {
+        parser.accepts( "a" ).withOptionalArg().ofType( Float.class );
+
+        parser.printHelpOn( sink );
+
+        assertHelpLines(
+            "Option      Description",
+            "------      -----------",
+            "-a [Float]             ",
+            EMPTY );
+    }
+
+    @Test
+    public void oneOptionOptionalArgWithDescription() throws Exception {
+        parser.accepts( "threshold", "some value you need" ).withOptionalArg().describedAs( "positive integer" );
+
+        parser.printHelpOn( sink );
+
+        assertHelpLines(
+            "Option                                  Description        ",
+            "------                                  -----------        ",
+            "--threshold [String: positive integer]  some value you need",
+            EMPTY );
+    }
+
+    @Test
+    public void oneOptionOptionalArgWithDescriptionAndType() throws Exception {
+        parser.accepts( "threshold", "some value you need" )
+            .withOptionalArg().describedAs( "positive decimal" ).ofType( Double.class );
+
+        parser.printHelpOn( sink );
+
+        assertHelpLines(
+            "Option                                  Description        ",
+            "------                                  -----------        ",
+            "--threshold [Double: positive decimal]  some value you need",
+            EMPTY );
+    }
+
+    @Test
+    public void alternativeLongOptions() throws Exception {
+        parser.recognizeAlternativeLongOptions( true );
+
+        parser.printHelpOn( sink );
+
+        assertHelpLines(
+            "Option                  Description                     ",
+            "------                  -----------                     ",
+            "-W <String: opt=value>  Alternative form of long options",
+            EMPTY );
+    }
+
+    @Test
+    public void optionSynonymsWithoutArguments() throws Exception {
+        parser.acceptsAll( asList( "v", "chatty" ), "be verbose" );
+
+        parser.printHelpOn( sink );
+
+        assertHelpLines(
+            "Option        Description",
+            "------        -----------",
+            "-v, --chatty  be verbose ",
+            EMPTY );
+    }
+
+    @Test
+    public void optionSynonymsWithRequiredArgument() throws Exception {
+        parser.acceptsAll( asList( "L", "index" ), "set level" ).withRequiredArg().ofType( Integer.class );
+
+        parser.printHelpOn( sink );
+
+        assertHelpLines(
+            "Option                 Description",
+            "------                 -----------",
+            "-L, --index <Integer>  set level  ",
+            EMPTY );
+    }
+
+    @Test
+    public void optionSynonymsWithOptionalArgument() throws Exception {
+        parser.acceptsAll( asList( "d", "since" ), "date filter" )
+            .withOptionalArg().describedAs( "yyyyMMdd" ).ofType( Date.class );
+
+        parser.printHelpOn( sink );
+
+        assertHelpLines(
+            "Option                        Description",
+            "------                        -----------",
+            "-d, --since [Date: yyyyMMdd]  date filter",
+            EMPTY );
+    }
+
+    @Test
+    public void optionSynonymsSortedByShortOptionThenLexicographical() throws Exception {
+        parser.acceptsAll( asList( "v", "prolix", "chatty" ) );
+
+        parser.printHelpOn( sink );
+
+        assertHelpLines(
+            "Option                  Description",
+            "------                  -----------",
+            "-v, --chatty, --prolix             ",
+            EMPTY );
+    }
+
+    @Test
+    public void writingToOutputStream() throws Exception {
+        ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
+
+        parser.printHelpOn( bytesOut );
+
+        assertEquals( "No options specified  " + LINE_SEPARATOR, bytesOut.toString() );
+    }
+
+    // Bug 1956418
+    @Test
+    public void outputStreamFlushedButNotClosedWhenPrintingHelp() throws Exception {
+        FakeOutputStream fake = new FakeOutputStream();
+
+        parser.printHelpOn( fake );
+
+        assertTrue( fake.flushed );
+        assertFalse( fake.closed );
+    }
+
+    @Test
+    public void bothColumnsExceedingAllocatedWidths() throws Exception {
+        parser.acceptsAll( asList( "t", "threshold", "cutoff" ),
+            "a threshold value beyond which a certain level of the application should cease to write logs" )
+            .withRequiredArg()
+            .describedAs( "a positive decimal number that will represent the threshold that has been outlined" )
+            .ofType( Double.class );
+
+        parser.printHelpOn( sink );
+
+        assertHelpLines(
+            "Option                                Description                       ",
+            "------                                -----------                       ",
+            "-t, --cutoff, --threshold <Double: a  a threshold value beyond which a  ",
+            "  positive decimal number that will     certain level of the application",
+            "  represent the threshold that has      should cease to write logs      ",
+            "  been outlined>                                                        ",
+            EMPTY );
+    }
+
+    // Bug 2018262
+    @Test
+    public void gradleHelp() throws Exception {
+        parser.acceptsAll( asList( "n", "non-recursive" ),
+            "Do not execute primary tasks of child projects." );
+        parser.acceptsAll( singletonList( "S" ),
+            "Don't trigger a System.exit(0) for normal termination. Used for Gradle's internal testing." );
+        parser.acceptsAll( asList( "I", "no-imports" ),
+            "Disable usage of default imports for build script files." );
+        parser.acceptsAll( asList( "u", "no-search-upward" ),
+            "Don't search in parent folders for a settings.gradle file." );
+        parser.acceptsAll( asList( "x", "cache-off" ),
+            "No caching of compiled build scripts." );
+        parser.acceptsAll( asList( "r", "rebuild-cache" ),
+            "Rebuild the cache of compiled build scripts." );
+        parser.acceptsAll( asList( "v", "version" ), "Print version info." );
+        parser.acceptsAll( asList( "d", "debug" ),
+            "Log in debug mode (includes normal stacktrace)." );
+        parser.acceptsAll( asList( "q", "quiet" ), "Log errors only." );
+        parser.acceptsAll( asList( "j", "ivy-debug" ),
+            "Set Ivy log level to debug (very verbose)." );
+        parser.acceptsAll( asList( "i", "ivy-quiet" ), "Set Ivy log level to quiet." );
+        parser.acceptsAll( asList( "s", "stacktrace" ),
+            "Print out the stacktrace also for user exceptions (e.g. compile error)." );
+        parser.acceptsAll( asList( "f", "full-stacktrace" ),
+            "Print out the full (very verbose) stacktrace for any exceptions." );
+        parser.acceptsAll( asList( "t", "tasks" ),
+            "Show list of all available tasks and their dependencies." );
+        parser.acceptsAll( asList( "p", "project-dir" ),
+            "Specifies the start dir for Gradle. Defaults to current dir." )
+            .withRequiredArg().ofType( String.class );
+        parser.acceptsAll( asList( "g", "gradle-user-home" ),
+            "Specifies the gradle user home dir." )
+            .withRequiredArg().ofType( String.class );
+        parser.acceptsAll( asList( "l", "plugin-properties-file" ),
+            "Specifies the plugin.properties file." )
+            .withRequiredArg().ofType( String.class );
+        parser.acceptsAll( asList( "b", "buildfile" ),
+            "Specifies the build file name (also for subprojects). Defaults to build.gradle." )
+            .withRequiredArg().ofType( String.class );
+        parser.acceptsAll( asList( "D", "systemprop" ),
+            "Set system property of the JVM (e.g. -Dmyprop=myvalue)." )
+            .withRequiredArg().ofType( String.class );
+        parser.acceptsAll( asList( "P", "projectprop" ),
+            "Set project property for the build script (e.g. -Pmyprop=myvalue)." )
+            .withRequiredArg().ofType( String.class );
+        parser.acceptsAll( asList( "e", "embedded" ),
+            "Specify an embedded build script." )
+            .withRequiredArg().ofType( String.class );
+        parser.acceptsAll( asList( "B", "bootstrap-debug" ),
+            "Specify a text to be logged at the beginning (e.g. used by Gradle's bootstrap class)." )
+            .withRequiredArg().ofType( String.class );
+        parser.acceptsAll( asList( "h", "?" ), "Shows this help message" ).forHelp();
+
+        parser.printHelpOn( sink );
+
+        assertHelpLines(
+            "Option                                 Description                              ",
+            "------                                 -----------                              ",
+            "-?, -h                                 Shows this help message                  ",
+            "-B, --bootstrap-debug <String>         Specify a text to be logged at the       ",
+            "                                         beginning (e.g. used by Gradle's       ",
+            "                                         bootstrap class).                      ",
+            "-D, --systemprop <String>              Set system property of the JVM (e.g. -   ",
+            "                                         Dmyprop=myvalue).                      ",
+            "-I, --no-imports                       Disable usage of default imports for     ",
+            "                                         build script files.                    ",
+            "-P, --projectprop <String>             Set project property for the build       ",
+            "                                         script (e.g. -Pmyprop=myvalue).        ",
+            "-S                                     Don't trigger a System.exit(0) for       ",
+            "                                         normal termination. Used for Gradle's  ",
+            "                                         internal testing.                      ",
+            "-b, --buildfile <String>               Specifies the build file name (also for  ",
+            "                                         subprojects). Defaults to build.gradle.",
+            "-d, --debug                            Log in debug mode (includes normal       ",
+            "                                         stacktrace).                           ",
+            "-e, --embedded <String>                Specify an embedded build script.        ",
+            "-f, --full-stacktrace                  Print out the full (very verbose)        ",
+            "                                         stacktrace for any exceptions.         ",
+            "-g, --gradle-user-home <String>        Specifies the gradle user home dir.      ",
+            "-i, --ivy-quiet                        Set Ivy log level to quiet.              ",
+            "-j, --ivy-debug                        Set Ivy log level to debug (very         ",
+            "                                         verbose).                              ",
+            "-l, --plugin-properties-file <String>  Specifies the plugin.properties file.    ",
+            "-n, --non-recursive                    Do not execute primary tasks of child    ",
+            "                                         projects.                              ",
+            "-p, --project-dir <String>             Specifies the start dir for Gradle.      ",
+            "                                         Defaults to current dir.               ",
+            "-q, --quiet                            Log errors only.                         ",
+            "-r, --rebuild-cache                    Rebuild the cache of compiled build      ",
+            "                                         scripts.                               ",
+            "-s, --stacktrace                       Print out the stacktrace also for user   ",
+            "                                         exceptions (e.g. compile error).       ",
+            "-t, --tasks                            Show list of all available tasks and     ",
+            "                                         their dependencies.                    ",
+            "-u, --no-search-upward                 Don't search in parent folders for a     ",
+            "                                         settings.gradle file.                  ",
+            "-v, --version                          Print version info.                      ",
+            "-x, --cache-off                        No caching of compiled build scripts.    ",
+            EMPTY );
+    }
+
+    @Test
+    public void dateConverterShowsDatePattern() throws Exception {
+        parser.accepts( "date", "a date" )
+            .withRequiredArg()
+            .withValuesConvertedBy( datePattern( "MM/dd/yy" ) );
+
+        parser.printHelpOn( sink );
+
+        assertHelpLines(
+            "Option             Description",
+            "------             -----------",
+            "--date <MM/dd/yy>  a date     ",
+            EMPTY );
+    }
+
+    @Test
+    public void dateConverterShowsDatePatternInCombinationWithDescription() throws Exception {
+        parser.accepts( "date", "a date" ).withOptionalArg()
+            .describedAs( "your basic date pattern" )
+            .withValuesConvertedBy( datePattern( "MM/dd/yy" ) );
+
+        parser.printHelpOn( sink );
+
+        assertHelpLines(
+            "Option                             Description",
+            "------                             -----------",
+            "--date [MM/dd/yy: your basic date  a date     ",
+            "  pattern]                                    ",
+            EMPTY );
+    }
+
+    @Test
+    public void inetAddressConverterShowsType() throws Exception {
+        parser.accepts( "addr", "an internet address" )
+            .withRequiredArg()
+            .withValuesConvertedBy( new InetAddressConverter() );
+
+        parser.printHelpOn( sink );
+
+        assertHelpLines(
+            "Option                Description        ",
+            "------                -----------        ",
+            "--addr <InetAddress>  an internet address",
+            EMPTY );
+    }
+
+    @Test
+    public void leavesEmbeddedNewlinesInDescriptionsAlone() throws Exception {
+        List<String> descriptionPieces =
+            asList( "Specify the output type.", "'raw' = raw data.", "'java' = java class" );
+        parser.accepts( "type", join( descriptionPieces, LINE_SEPARATOR ) );
+
+        parser.printHelpOn( sink );
+
+        assertHelpLines(
+            "Option  Description             ",
+            "------  -----------             ",
+            "--type  Specify the output type.",
+            "        'raw' = raw data.       ",
+            "        'java' = java class     ",
+            EMPTY );
+    }
+
+    @Test
+    public void includesDefaultValueForRequiredOptionArgument() throws Exception {
+        parser.accepts( "a" ).withRequiredArg().defaultsTo( "boo" );
+
+        parser.printHelpOn( sink );
+
+        assertHelpLines(
+            "Option       Description   ",
+            "------       -----------   ",
+            "-a <String>  (default: boo)",
+            EMPTY );
+    }
+
+    @Test
+    public void includesDefaultValueForOptionalOptionArgument() throws Exception {
+        parser.accepts( "b" ).withOptionalArg().ofType( Integer.class ).defaultsTo( 5 );
+
+        parser.printHelpOn( sink );
+
+        assertHelpLines(
+            "Option        Description ",
+            "------        ----------- ",
+            "-b [Integer]  (default: 5)",
+            EMPTY );
+    }
+
+    @Test
+    public void includesDefaultValueForArgumentWithDescription() throws Exception {
+        parser.accepts( "c", "a quantity" ).withOptionalArg().ofType( BigDecimal.class )
+            .describedAs( "quantity" ).defaultsTo( TEN );
+
+        parser.printHelpOn( sink );
+
+        assertHelpLines(
+            "Option                     Description             ",
+            "------                     -----------             ",
+            "-c [BigDecimal: quantity]  a quantity (default: 10)",
+            EMPTY );
+    }
+
+    @Test
+    public void includesListOfDefaultsForArgumentWithDescription() throws Exception {
+        parser.accepts( "d", "dizzle" ).withOptionalArg().ofType( Integer.class )
+            .describedAs( "double dizzle" ).defaultsTo( 2, 3, 5, 7 );
+
+        parser.printHelpOn( sink );
+
+        assertHelpLines(
+            "Option                       Description                   ",
+            "------                       -----------                   ",
+            "-d [Integer: double dizzle]  dizzle (default: [2, 3, 5, 7])",
+            EMPTY );
+    }
+
+    @Test
+    public void marksRequiredOptionsSpecially() throws Exception {
+        parser.accepts( "e" ).withRequiredArg().required();
+
+        parser.printHelpOn( sink );
+
+        assertHelpLines(
+            "Option (* = required)  Description",
+            "---------------------  -----------",
+            "* -e <String>                     ",
+            EMPTY );
+    }
+
+    @Test
+    public void showsNonOptionArgumentDescription() throws Exception {
+        parser.nonOptions( "stuff" );
+
+        parser.printHelpOn( sink );
+
+        assertHelpLines(
+            "Non-option arguments:",
+            "[String] -- stuff    ",
+            EMPTY,
+            "No options specified  ",
+            EMPTY );
+    }
+
+    @Test
+    public void showsNonOptionArgumentType() throws Exception {
+        parser.nonOptions().ofType( File.class );
+
+        parser.printHelpOn( sink );
+
+        assertHelpLines(
+            "Non-option arguments:",
+            "[File]               ",
+            EMPTY,
+            "No options specified  ",
+            EMPTY );
+    }
+
+    @Test
+    public void showsNonOptionArgumentTypeDescribedAs() throws Exception {
+        parser.nonOptions().describedAs( "files" );
+
+        parser.printHelpOn( sink );
+
+        assertHelpLines(
+            "Non-option arguments:",
+            "[String: files]      ",
+            EMPTY,
+            "No options specified  ",
+            EMPTY );
+    }
+
+    @Test
+    public void showsNonOptionArgumentTypeAndArgumentDescription() throws Exception {
+        parser.nonOptions().ofType( File.class ).describedAs( "files" );
+
+        parser.printHelpOn( sink );
+
+        assertHelpLines(
+            "Non-option arguments:",
+            "[File: files]        ",
+            EMPTY,
+            "No options specified  ",
+            EMPTY );
+    }
+
+    @Test
+    public void showsNonOptionArgumentTypeAndDescription() throws Exception {
+        parser.nonOptions( "some files to operate on" ).ofType( File.class );
+
+        parser.printHelpOn( sink );
+
+        assertHelpLines(
+            "Non-option arguments:             ",
+            "[File] -- some files to operate on",
+            EMPTY,
+            "No options specified  ",
+            EMPTY );
+    }
+
+    @Test
+    public void showsNonOptionArgumentTypeAndDescriptionAndArgumentDescription() throws Exception {
+        parser.nonOptions( "some files to operate on" ).ofType( File.class ).describedAs( "files" );
+
+        parser.printHelpOn( sink );
+
+        assertHelpLines(
+            "Non-option arguments:                    ",
+            "[File: files] -- some files to operate on",
+            EMPTY,
+            "No options specified  ",
+            EMPTY );
+    }
+
+    @Test
+    public void canUseCustomHelpFormatter() {
+        parser.accepts( "f" );
+
+        parser.formatHelpWith( new HelpFormatter() {
+            public String format( Map<String, ? extends OptionDescriptor> options ) {
+                assertEquals( 1, options.size() );
+                OptionDescriptor only = options.get( "f" );
+                assertEquals( asList( "f" ), new ArrayList<>( only.options() ) );
+                assertFalse( only.acceptsArguments() );
+                assertEquals( "", only.argumentDescription() );
+                assertEquals( "", only.argumentTypeIndicator() );
+                assertEquals( emptyList(), only.defaultValues() );
+                assertEquals( "", only.description() );
+                assertFalse( only.isRequired() );
+                assertFalse( only.requiresArgument() );
+                return null;
+            }
+        } );
+    }
+
+    @Test( expected = NullPointerException.class )
+    public void rejectsNullHelpFormatter() {
+        parser.formatHelpWith( null );
+    }
+
+    @Test
+    public void fixForIssue56() throws Exception {
+        parser.accepts( "password", "Server Password" ).withRequiredArg().ofType( String.class );
+        parser.accepts( "F", "Forward port mapping (ie: localhost:5900:localhost:5900)" ).withRequiredArg();
+        parser.accepts( "R", "Reverse port mapping (ie: localhost:5900:localhost:5900)" ).withRequiredArg();
+
+        parser.printHelpOn( sink );
+
+        assertHelpLines(
+            "Option               Description                                             ",
+            "------               -----------                                             ",
+            "-F <String>          Forward port mapping (ie: localhost:5900:localhost:5900)",
+            "-R <String>          Reverse port mapping (ie: localhost:5900:localhost:5900)",
+            "--password <String>  Server Password                                         ",
+            EMPTY );
+    }
+
+    @Test
+    public void fixForIssue85() throws Exception {
+        parser.acceptsAll( asList( "?", "help" ), "Display this help text" ).forHelp();
+        parser.acceptsAll( asList( "c", "check-avail" ),
+            "Check Galileo homepage for available books, compare with known ones" );
+        parser.acceptsAll( asList( "d", "download-dir" ),
+            "Download directory for openbooks; must exist" )
+            .withRequiredArg().ofType( File.class ).defaultsTo( new File( "." ) );
+        parser.acceptsAll( asList( "l", "log-level" ),
+            "Log level (0=normal, 1=verbose, 2=debug, 3=trace" )
+            .withRequiredArg().ofType( int.class ).defaultsTo( 0 );
+        parser.acceptsAll( asList( "m", "check-md5" ),
+            "Download all known books without storing them, verifying their MD5 checksum (slow! >1 Gb download)" );
+        parser.acceptsAll( asList( "t", "threading" ),
+            "Threading mode (0=single, 1=multi); single is slower, but better for diagnostics)" )
+            .withRequiredArg().ofType( int.class ).defaultsTo( 1 );
+        parser.acceptsAll( asList( "w", "write-config" ),
+            "Write editable book list to config.xml, enabling you to update MD5 checksums or add new books" );
+
+        parser.printHelpOn( sink );
+
+        assertHelpLines(
+            "Option                     Description                                          ",
+            "------                     -----------                                          ",
+            "-?, --help                 Display this help text                               ",
+            "-c, --check-avail          Check Galileo homepage for available books, compare  ",
+            "                             with known ones                                    ",
+            "-d, --download-dir <File>  Download directory for openbooks; must exist         ",
+            "                             (default: .)                                       ",
+            "-l, --log-level <Integer>  Log level (0=normal, 1=verbose, 2=debug, 3=trace     ",
+            "                             (default: 0)                                       ",
+            "-m, --check-md5            Download all known books without storing them,       ",
+            "                             verifying their MD5 checksum (slow! >1 Gb download)",
+            "-t, --threading <Integer>  Threading mode (0=single, 1=multi); single is        ",
+            "                             slower, but better for diagnostics) (default: 1)   ",
+            "-w, --write-config         Write editable book list to config.xml, enabling you ",
+            "                             to update MD5 checksums or add new books           ",
+            EMPTY );
+    }
+
+    private void assertHelpLines( String... expectedLines ) {
+        assertEquals( join( expectedLines, LINE_SEPARATOR ), sink.toString() );
+    }
+
+    static class FakeOutputStream extends ByteArrayOutputStream {
+        boolean closed;
+        boolean flushed;
+
+        @Override
+        public void close() {
+            this.closed = true;
+        }
+
+        @Override
+        public void flush() {
+            this.flushed = true;
+        }
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/EmptyOptionSetTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/EmptyOptionSetTest.java
new file mode 100644
index 0000000..640f0ff
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/EmptyOptionSetTest.java
@@ -0,0 +1,87 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import java.util.Collections;
+import static java.util.Collections.*;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class EmptyOptionSetTest {
+    private OptionSet empty;
+
+    @Before
+    public void setUp() {
+        empty = new OptionSet( Collections.<String, AbstractOptionSpec<?>> emptyMap() );
+        empty.add( new NonOptionArgumentSpec<>() );
+    }
+
+    @Test
+    public void valueOf() {
+        assertNull( empty.valueOf( "a" ) );
+    }
+
+    @Test
+    public void valuesOf() {
+        assertEquals( emptyList(), empty.valuesOf( "a" ) );
+    }
+
+    @Test
+    public void hasArgument() {
+        assertFalse( empty.hasArgument( "a" ) );
+    }
+
+    @Test
+    public void hasOptions() {
+        assertFalse( empty.hasOptions() );
+    }
+
+    @Test( expected = NullPointerException.class )
+    public void valueOfWithNullString() {
+        empty.valueOf( (String) null );
+    }
+
+    @Test( expected = NullPointerException.class )
+    public void valueOfWithNullOptionSpec() {
+        empty.valueOf( (OptionSpec<?>) null );
+    }
+
+    @Test( expected = NullPointerException.class )
+    public void valuesOfWithNullString() {
+        empty.valuesOf( (String) null );
+    }
+
+    @Test( expected = NullPointerException.class )
+    public void valuesOfWithNullOptionSpec() {
+        empty.valuesOf( (OptionSpec<?>) null );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/ExceptionMatchers.java b/third_party/java/jopt-simple/src/test/java/joptsimple/ExceptionMatchers.java
new file mode 100644
index 0000000..5299fef
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/ExceptionMatchers.java
@@ -0,0 +1,83 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import java.lang.reflect.InvocationTargetException;
+
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+import org.hamcrest.TypeSafeMatcher;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class ExceptionMatchers {
+    private ExceptionMatchers() {
+        throw new UnsupportedOperationException();
+    }
+
+    public static Matcher<OptionException> withOption( final String option ) {
+        return new TypeSafeMatcher<OptionException>() {
+            @Override
+            public boolean matchesSafely( OptionException target ) {
+                return target.options().contains( option );
+            }
+
+            public void describeTo( Description description ) {
+                description.appendText( "an OptionException indicating the option ");
+                description.appendValue( option );
+            }
+        };
+    }
+
+    public static Matcher<Throwable> withCauseOfType( final Class<? extends Throwable> type ) {
+        return new TypeSafeMatcher<Throwable>() {
+            @Override
+            public boolean matchesSafely( Throwable target ) {
+                return type.isInstance( target.getCause() );
+            }
+
+            public void describeTo( Description description ) {
+                description.appendText( "an exception with cause of type " );
+                description.appendValue( type );
+            }
+        };
+    }
+
+    public static Matcher<InvocationTargetException> withTargetOfType( final Class<? extends Throwable> type ) {
+        return new TypeSafeMatcher<InvocationTargetException>() {
+            @Override
+            public boolean matchesSafely( InvocationTargetException target ) {
+                return type.isInstance( target.getTargetException() );
+            }
+
+            public void describeTo( Description description ) {
+                description.appendText( "an InvocationTargetException with target of type " );
+                description.appendValue( type );
+            }
+        };
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/HandlingDefaultValuesForOptionArgumentsTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/HandlingDefaultValuesForOptionArgumentsTest.java
new file mode 100644
index 0000000..5ed3141
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/HandlingDefaultValuesForOptionArgumentsTest.java
@@ -0,0 +1,183 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+import static java.math.BigDecimal.*;
+import static java.util.Arrays.*;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class HandlingDefaultValuesForOptionArgumentsTest extends AbstractOptionParserFixture {
+    @Test
+    public void requiredArgOptionWithDefaultGivesArgIfArgSpecifiedOnCommandLine() {
+        OptionSpec<Integer> optionA =
+            parser.accepts( "a" ).withRequiredArg().ofType( Integer.class ).defaultsTo( 2 );
+
+        OptionSet options = parser.parse( "-a", "1" );
+
+        assertTrue( options.has( "a" ) );
+        assertTrue( options.has( optionA ) );
+        assertTrue( options.hasArgument( "a" ) );
+        assertTrue( options.hasArgument( optionA ) );
+        Integer expectedArgument = 1;
+        assertEquals( expectedArgument, options.valueOf( "a" ) );
+        assertEquals( expectedArgument, options.valueOf( optionA ) );
+        assertEquals( expectedArgument, optionA.value( options ) );
+        assertEquals( asList( expectedArgument ), options.valuesOf( "a" ) );
+        assertEquals( asList( expectedArgument ), options.valuesOf( optionA ) );
+        assertEquals( asList( expectedArgument ), optionA.values( options ) );
+    }
+
+    @Test( expected = OptionMissingRequiredArgumentException.class )
+    public void requiredArgOptionWithDefaultStillsFailToParseIfArgNotSpecifiedOnCommandLine() {
+        parser.accepts( "a" ).withRequiredArg().defaultsTo( "boo" );
+
+        parser.parse( "-a" );
+    }
+
+    @Test
+    public void optionalArgOptionWithDefaultGivesDefaultIfArgNotSpecifiedOnCommandLine() {
+        OptionSpec<Long> optionA =
+            parser.accepts( "a" ).withOptionalArg().ofType( Long.class ).defaultsTo( -1L );
+
+        OptionSet options = parser.parse( "-a" );
+
+        assertTrue( options.has( "a" ) );
+        assertTrue( options.has( optionA ) );
+        assertFalse( options.hasArgument( "a" ) );
+        assertFalse( options.hasArgument( optionA ) );
+        Long expectedArgument = -1L;
+        assertEquals( expectedArgument, options.valueOf( "a" ) );
+        assertEquals( expectedArgument, options.valueOf( optionA ) );
+        assertEquals( expectedArgument, optionA.value( options ) );
+        assertEquals( asList( expectedArgument ), options.valuesOf( "a" ) );
+        assertEquals( asList( expectedArgument ), options.valuesOf( optionA ) );
+        assertEquals( asList( expectedArgument ), optionA.values( options ) );
+    }
+
+    @Test
+    public void optionalArgOptionWithDefaultGivesArgIfSpecifiedOnCommandLine() {
+        OptionSpec<Long> optionA =
+            parser.accepts( "a" ).withOptionalArg().ofType( Long.class ).defaultsTo( -1L );
+
+        OptionSet options = parser.parse( "-a", "2" );
+
+        assertTrue( options.has( "a" ) );
+        assertTrue( options.has( optionA ) );
+        assertTrue( options.hasArgument( "a" ) );
+        assertTrue( options.hasArgument( optionA ) );
+        Long expectedArgument = 2L;
+        assertEquals( expectedArgument, options.valueOf( "a" ) );
+        assertEquals( expectedArgument, options.valueOf( optionA ) );
+        assertEquals( expectedArgument, optionA.value( options ) );
+        assertEquals( asList( expectedArgument ), options.valuesOf( "a" ) );
+        assertEquals( asList( expectedArgument ), options.valuesOf( optionA ) );
+        assertEquals( asList( expectedArgument ), optionA.values( options ) );
+    }
+
+    @Test
+    public void requiredArgOptionWithDefaultGivesDefaultIfOptionNotOnCommandLine() {
+        OptionSpec<BigDecimal> optionA =
+            parser.accepts( "a" ).withRequiredArg().ofType( BigDecimal.class ).defaultsTo( TEN );
+
+        OptionSet options = parser.parse();
+
+        assertFalse( options.has( "a" ) );
+        assertFalse( options.has( optionA ) );
+        assertFalse( options.hasArgument( "a" ) );
+        assertFalse( options.hasArgument( optionA ) );
+        assertEquals( TEN, options.valueOf( "a" ) );
+        assertEquals( TEN, options.valueOf( optionA ) );
+        assertEquals( TEN, optionA.value( options ) );
+        assertEquals( asList( TEN ), options.valuesOf( "a" ) );
+        assertEquals( asList( TEN ), options.valuesOf( optionA ) );
+        assertEquals( asList( TEN ), optionA.values( options ) );
+    }
+
+    @Test
+    public void optionalArgOptionWithDefaultGivesDefaultIfOptionNotOnCommandLine() {
+        OptionSpec<BigDecimal> optionA =
+            parser.accepts( "a" ).withOptionalArg().ofType( BigDecimal.class ).defaultsTo( TEN );
+
+        OptionSet options = parser.parse();
+
+        assertFalse( options.has( "a" ) );
+        assertFalse( options.has( optionA ) );
+        assertFalse( options.hasArgument( "a" ) );
+        assertFalse( options.hasArgument( optionA ) );
+        assertEquals( TEN, options.valueOf( "a" ) );
+        assertEquals( TEN, options.valueOf( optionA ) );
+        assertEquals( TEN, optionA.value( options ) );
+        assertEquals( asList( TEN ), options.valuesOf( "a" ) );
+        assertEquals( asList( TEN ), options.valuesOf( optionA ) );
+        assertEquals( asList( TEN ), optionA.values( options ) );
+    }
+
+    @Test
+    public void allowsListOfDefaults() {
+        OptionSpec<Integer> optionC =
+            parser.accepts( "c" ).withOptionalArg().ofType( Integer.class ).defaultsTo( 1, 2, 3 );
+
+        OptionSet options = parser.parse();
+
+        List<Integer> expected = asList( 1, 2, 3 );
+        assertEquals( expected, optionC.values( options ) );
+        assertEquals( expected, options.valuesOf( optionC ) );
+    }
+
+    @Test
+    public void specifiedOptionArgumentsTrumpsListOfDefaults() {
+        OptionSpec<Integer> optionC =
+            parser.accepts( "c" ).withRequiredArg().ofType( Integer.class )
+                .defaultsTo( 1, 2, 3 ).withValuesSeparatedBy( ',' );
+
+        OptionSet options = parser.parse( "-c", "4", "-c", "5", "-c", "6,7,8" );
+
+        List<Integer> expected = asList( 4, 5, 6, 7, 8 );
+        assertEquals( expected, optionC.values( options ) );
+        assertEquals( expected, options.valuesOf( optionC ) );
+    }
+
+    @Test
+    public void withCompileTimeArraySpecifyingDefaults() {
+        OptionSpec<Integer> optionD =
+            parser.accepts( "d" ).withRequiredArg().ofType( Integer.class ).defaultsTo( new Integer[] { 1, 2, 3 } );
+
+        OptionSet options = parser.parse();
+
+        List<Integer> expected = asList( 1, 2, 3 );
+        assertEquals( expected, optionD.values( options ) );
+        assertEquals( expected, options.valuesOf( optionD ) );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/InterleavedArgumentsTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/InterleavedArgumentsTest.java
new file mode 100644
index 0000000..e91fbd3
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/InterleavedArgumentsTest.java
@@ -0,0 +1,140 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import static java.util.Arrays.*;
+import static java.util.Collections.*;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class InterleavedArgumentsTest {
+    @Test
+    public void onlyAppearingToHaveOptionArguments() {
+        OptionParser parser = new OptionParser( "c" );
+
+        OptionSet options = parser.parse( "-c", "a", "-c", "b", "-c", "c", "-c", "d" );
+
+        assertTrue( options.has( "c" ) );
+        assertEquals( emptyList(), options.valuesOf( "c" ) );
+        assertEquals( asList( "a", "b", "c", "d" ), options.nonOptionArguments() );
+    }
+
+    @Test
+    public void onlyAppearingToHaveOptionArgumentsButPosixlyCorrect() {
+        OptionParser parser = new OptionParser( "+c" );
+
+        OptionSet options = parser.parse( "-c", "a", "-c", "b", "-c", "c", "-c", "d" );
+
+        assertTrue( options.has( "c" ) );
+        assertEquals( emptyList(), options.valuesOf( "c" ) );
+        assertEquals(
+            asList( "a", "-c", "b", "-c", "c", "-c", "d" ),
+            options.nonOptionArguments() );
+    }
+
+    @Test
+    public void requiredArgument() {
+        OptionParser parser = new OptionParser( "c:" );
+
+        OptionSet options = parser.parse( "-c", "a", "-c", "b", "-c", "c", "-c", "d" );
+
+        assertTrue( options.has( "c" ) );
+        assertEquals( asList( "a", "b", "c", "d" ), options.valuesOf( "c" ) );
+        assertEquals( emptyList(), options.nonOptionArguments() );
+    }
+
+    @Test
+    public void requiredArgumentAndPosixlyCorrect() {
+        OptionParser parser = new OptionParser( "+c:" );
+
+        OptionSet options = parser.parse( "-c", "a", "-c", "b", "-c", "c", "-c", "d" );
+
+        assertTrue( options.has( "c" ) );
+        assertEquals( asList( "a", "b", "c", "d" ), options.valuesOf( "c" ) );
+        assertEquals( emptyList(), options.nonOptionArguments() );
+    }
+
+    @Test
+    public void optionalArgument() {
+        OptionParser parser = new OptionParser( "c::" );
+
+        OptionSet options = parser.parse( "-c", "a", "-c", "b", "-c", "c", "-c", "d" );
+
+        assertTrue( options.has( "c" ) );
+        assertEquals( asList( "a", "b", "c", "d" ), options.valuesOf( "c" ) );
+        assertEquals( emptyList(), options.nonOptionArguments() );
+    }
+
+    @Test
+    public void optionalArgumentAndPosixlyCorrect() {
+        OptionParser parser = new OptionParser( "+c::" );
+
+        OptionSet options = parser.parse( "-c", "a", "-c", "b", "-c", "c", "-c", "d" );
+
+        assertTrue( options.has( "c" ) );
+        assertEquals( emptyList(), options.valuesOf( "c" ) );
+        assertEquals( asList( "a", "-c", "b", "-c", "c", "-c", "d" ), options.nonOptionArguments() );
+    }
+
+    @Test
+    public void leadingNonOptionCausesPosixlyCorrectToIgnoreRemainder() {
+        OptionParser parser = new OptionParser( "+c:" );
+        String[] args = { "boo", "-c", "a", "-c", "b", "-c", "c", "-c", "d" };
+
+        OptionSet options = parser.parse( args );
+
+        assertFalse( options.has( "c" ) );
+        assertEquals( emptyList(), options.valuesOf( "c" ) );
+        assertEquals( asList( args ), options.nonOptionArguments() );
+    }
+
+    @Test
+    public void optionalAbuttedArgumentVersusPosixlyCorrect() {
+        OptionParser parser = new OptionParser( "+c::" );
+
+        OptionSet options = parser.parse( "-ca", "-cb", "-c", "c", "-c", "d" );
+
+        assertTrue( options.has( "c" ) );
+        assertEquals( asList( "a", "b" ), options.valuesOf( "c" ) );
+        assertEquals( asList( "c", "-c", "d" ), options.nonOptionArguments() );
+    }
+
+    @Test
+    public void optionalKeyValuePairArgumentVersusPosixlyCorrect() {
+        OptionParser parser = new OptionParser( "+c::" );
+
+        OptionSet options = parser.parse( "-c=a", "-c=b", "-c", "c", "-c", "d" );
+
+        assertTrue( options.has( "c" ) );
+        assertEquals( asList( "a", "b" ), options.valuesOf( "c" ) );
+        assertEquals( asList( "c", "-c", "d" ), options.nonOptionArguments() );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/Issue76Test.java b/third_party/java/jopt-simple/src/test/java/joptsimple/Issue76Test.java
new file mode 100644
index 0000000..31221de
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/Issue76Test.java
@@ -0,0 +1,71 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import static java.util.Arrays.asList;
+import static org.junit.Assert.*;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class Issue76Test {
+    private OptionParser parser;
+    private OptionSpec<Integer> nonOptions;
+
+    @Before
+    public void setUp() {
+        parser = new OptionParser();
+        parser.accepts( "flag" ).withOptionalArg().withValuesConvertedBy( new ValueConverter<Boolean>() {
+            public Boolean convert( String value ) {
+                if ( "true".equals( value ) )
+                    return true;
+                if ( "false".equals( value ) )
+                    return false;
+                throw new ValueConversionException( "need 'true' or 'false'" );
+            }
+
+            public Class<? extends Boolean> valueType() {
+                return boolean.class;
+            }
+
+            public String valuePattern() {
+                return null;
+            }
+        });
+        nonOptions = parser.nonOptions().ofType( Integer.class );
+    }
+
+    @Test
+    public void suppliedExample() {
+        OptionSet parsed = parser.parse( "--flag", "1", "2" );
+
+        assertTrue( parsed.has( "flag" ) );
+        assertEquals( asList( 1, 2 ), nonOptions.values( parsed ) );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/JVMSystemPropertiesArgumentParsingTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/JVMSystemPropertiesArgumentParsingTest.java
new file mode 100644
index 0000000..49366cf
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/JVMSystemPropertiesArgumentParsingTest.java
@@ -0,0 +1,53 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import static java.util.Arrays.*;
+
+import joptsimple.util.KeyValuePair;
+import org.junit.Before;
+import org.junit.Test;
+
+import static joptsimple.util.KeyValuePair.*;
+import static org.junit.Assert.*;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class JVMSystemPropertiesArgumentParsingTest extends AbstractOptionParserFixture {
+    @Before
+    public final void initializeParser() {
+        parser.accepts( "D" ).withRequiredArg().ofType( KeyValuePair.class );
+    }
+
+    @Test
+    public void parsingJVMSystemPropertiesArguments() {
+        OptionSet options = parser.parse( "-Dfile.encoding=UTF-8", "-Dpath.separator=:" );
+        assertEquals(
+            asList( valueOf( "file.encoding=UTF-8" ), valueOf( "path.separator=:" ) ),
+            options.valuesOf( "D" ) );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/LongOptionNoArgumentTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/LongOptionNoArgumentTest.java
new file mode 100644
index 0000000..c41f9bb
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/LongOptionNoArgumentTest.java
@@ -0,0 +1,82 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import static java.util.Collections.*;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import static joptsimple.ExceptionMatchers.*;
+import static org.junit.Assert.*;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class LongOptionNoArgumentTest extends AbstractOptionParserFixture {
+    @Before
+    public final void initializeParser() {
+        parser.accepts( "verbose" );
+        parser.accepts( "verb" );
+    }
+
+    @Test
+    public void singleLongOption() {
+        OptionSet options = parser.parse( "--verbose" );
+
+        assertTrue( options.has( "verbose" ) );
+        assertEquals( emptyList(), options.valuesOf( "verbose" ) );
+        assertEquals( emptyList(), options.nonOptionArguments() );
+    }
+
+    @Test
+    public void singleLongOptionAbbreviated() {
+        OptionSet options = parser.parse( "--verbo" );
+
+        assertTrue( options.has( "verbose" ) );
+        assertFalse( options.has( "verb" ) );
+        assertEquals( emptyList(), options.valuesOf( "verbose" ) );
+        assertEquals( emptyList(), options.nonOptionArguments() );
+    }
+
+    @Test
+    public void abbreviationIsALegalOption() {
+        OptionSet options = parser.parse( "--verb" );
+
+        assertFalse( options.has( "verbose" ) );
+        assertTrue( options.has( "verb" ) );
+        assertEquals( emptyList(), options.valuesOf( "verb" ) );
+        assertEquals( emptyList(), options.nonOptionArguments() );
+    }
+
+    @Test
+    public void ambiguousAbbreviation() {
+        thrown.expect( UnrecognizedOptionException.class );
+        thrown.expect( withOption( "ver" ) );
+
+        parser.parse( "--ver" );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/LongOptionOptionalArgumentTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/LongOptionOptionalArgumentTest.java
new file mode 100644
index 0000000..93e4b4d
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/LongOptionOptionalArgumentTest.java
@@ -0,0 +1,72 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import static java.util.Collections.*;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class LongOptionOptionalArgumentTest extends AbstractOptionParserFixture {
+    @Before
+    public final void initializeParser() {
+        parser.accepts( "output" ).withOptionalArg();
+        parser.accepts( "a" );
+    }
+
+    @Test
+    public void argumentMissingTrailedByAnotherOption() {
+        OptionSet options = parser.parse( "--output", "-a" );
+
+        assertTrue( options.has( "output" ) );
+        assertTrue( options.has( "a" ) );
+        assertEquals( emptyList(), options.valuesOf( "output" ) );
+        assertEquals( emptyList(), options.nonOptionArguments() );
+    }
+
+    @Test
+    public void argumentSeparate() {
+        OptionSet options = parser.parse( "--output", "opt" );
+
+        assertTrue( options.has( "output" ) );
+        assertEquals( singletonList( "opt" ), options.valuesOf( "output" ) );
+        assertEquals( emptyList(), options.nonOptionArguments() );
+    }
+
+    @Test
+    public void argumentTogether() {
+        OptionSet options = parser.parse( "--output=opt" );
+
+        assertTrue( options.has( "output" ) );
+        assertEquals( singletonList( "opt" ), options.valuesOf( "output" ) );
+        assertEquals( emptyList(), options.nonOptionArguments() );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/LongOptionRequiredArgumentTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/LongOptionRequiredArgumentTest.java
new file mode 100644
index 0000000..fe77f06
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/LongOptionRequiredArgumentTest.java
@@ -0,0 +1,103 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import static java.util.Collections.*;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import static joptsimple.ExceptionMatchers.*;
+import static org.junit.Assert.*;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class LongOptionRequiredArgumentTest extends AbstractOptionParserFixture {
+    @Before
+    public final void initializeParser() {
+        parser.accepts( "quiet" ).withRequiredArg();
+        parser.accepts( "a" ).withOptionalArg();
+        parser.accepts( "y" ).withRequiredArg();
+    }
+
+    @Test
+    public void argumentSeparate() {
+        OptionSet options = parser.parse( "--quiet", "23" );
+
+        assertOptionDetected( options, "quiet" );
+        assertEquals( singletonList( "23" ), options.valuesOf( "quiet" ) );
+        assertEquals( emptyList(), options.nonOptionArguments() );
+    }
+
+    @Test
+    public void argumentFollowedByLegalOption() {
+        OptionSet options = parser.parse( "--quiet", "-a" );
+
+        assertOptionDetected( options, "quiet" );
+        assertOptionNotDetected( options, "a" );
+        assertEquals( singletonList( "-a" ), options.valuesOf( "quiet" ) );
+        assertEquals( emptyList(), options.nonOptionArguments() );
+    }
+
+    @Test
+    public void argumentTogether() {
+        OptionSet options = parser.parse( "--quiet=23" );
+
+        assertOptionDetected( options, "quiet" );
+        assertEquals( singletonList( "23" ), options.valuesOf( "quiet" ) );
+        assertEquals( emptyList(), options.nonOptionArguments() );
+    }
+
+    @Test
+    public void argumentMissing() {
+        thrown.expect( OptionMissingRequiredArgumentException.class );
+        thrown.expect( withOption( "quiet" ) );
+
+        parser.parse( "--quiet" );
+    }
+
+    @Test
+    public void shortOptionSpecifiedAsLongOptionWithArgument() {
+        OptionSet options = parser.parse( "--y=bar" );
+
+        assertOptionDetected( options, "y" );
+        assertEquals( singletonList( "bar" ), options.valuesOf( "y" ) );
+        assertEquals( emptyList(), options.nonOptionArguments() );
+    }
+
+    @Test
+    public void whenEndOfOptionsMarkerIsInPlaceOfRequiredArgument() {
+        OptionSet options = parser.parse( "--quiet", "--", "-y", "foo", "-a" );
+
+        assertOptionDetected( options, "quiet" );
+        assertOptionDetected( options, "y" );
+        assertOptionDetected( options, "a" );
+        assertEquals( singletonList( "--" ), options.valuesOf( "quiet" ) );
+        assertEquals( singletonList( "foo" ), options.valuesOf( "y" ) );
+        assertEquals( emptyList(), options.nonOptionArguments() );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/NoArgumentOptionSpecOptionsImmutabilityTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/NoArgumentOptionSpecOptionsImmutabilityTest.java
new file mode 100644
index 0000000..ec9e6a7
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/NoArgumentOptionSpecOptionsImmutabilityTest.java
@@ -0,0 +1,36 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class NoArgumentOptionSpecOptionsImmutabilityTest extends AbstractOptionSpecOptionsImmutabilityTestCase {
+    @Override
+    protected AbstractOptionSpec<?> newOptionSpec( String option ) {
+        return new NoArgumentOptionSpec( option );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/NoArgumentOptionSpecTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/NoArgumentOptionSpecTest.java
new file mode 100644
index 0000000..e128308
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/NoArgumentOptionSpecTest.java
@@ -0,0 +1,53 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import static java.util.Arrays.*;
+
+import org.junit.Test;
+
+import static org.infinitest.toolkit.CollectionMatchers.*;
+import static org.junit.Assert.*;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class NoArgumentOptionSpecTest extends AbstractOptionSpecFixture {
+    @Override
+    protected NoArgumentOptionSpec createEqualOptionSpecInstance() {
+        return new NoArgumentOptionSpec( "a" );
+    }
+
+    @Override
+    protected NoArgumentOptionSpec createNotEqualOptionSpecInstance() {
+        return new NoArgumentOptionSpec( "b" );
+    }
+
+    @Test
+    public void optionsContents() {
+        assertThat( createNotEqualOptionSpecInstance().options(), hasSameContentsAs( asList( "b" ) ) );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/NoArgumentOptionSpecValuesImmutabilityTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/NoArgumentOptionSpecValuesImmutabilityTest.java
new file mode 100644
index 0000000..c2212b3
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/NoArgumentOptionSpecValuesImmutabilityTest.java
@@ -0,0 +1,56 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class NoArgumentOptionSpecValuesImmutabilityTest extends AbstractOptionSpecValuesImmutabilityTestCase<Void> {
+    @Override
+    protected AbstractOptionSpec<Void> newOptionSpec() {
+        return new NoArgumentOptionSpec( "verbose" );
+    }
+
+    @Override
+    protected String firstArg() {
+        return "1";
+    }
+
+    @Override
+    protected String secondArg() {
+        return "2";
+    }
+
+    @Override
+    protected Void newItem() {
+        return null;
+    }
+
+    @Override
+    protected Void containedItem() {
+        return null;
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/NonOptionArgumentSpecTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/NonOptionArgumentSpecTest.java
new file mode 100644
index 0000000..feef727
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/NonOptionArgumentSpecTest.java
@@ -0,0 +1,108 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import org.junit.Test;
+
+import java.io.File;
+import java.text.SimpleDateFormat;
+import java.util.*;
+
+import static java.util.Arrays.*;
+import static java.util.Collections.*;
+import static joptsimple.util.DateConverter.*;
+import static org.junit.Assert.*;
+
+public class NonOptionArgumentSpecTest extends AbstractOptionParserFixture {
+    @Test
+    public void allowsTypingOfNonOptionArguments() {
+        OptionSpec<File> nonOptions = parser.nonOptions().ofType( File.class );
+
+        OptionSet options = parser.parse( "/opt", "/var" );
+
+        assertEquals( asList( new File( "/opt" ), new File( "/var" ) ), nonOptions.values( options ) );
+    }
+
+    @Test
+    public void allowsDescriptionOfNonOptionArguments() {
+        OptionSpec<String> nonOptions = parser.nonOptions( "directories" );
+
+        OptionSet options = parser.parse( "/opt", "/var" );
+
+        assertEquals( asList( "/opt", "/var" ), nonOptions.values( options ) );
+    }
+
+    @Test
+    public void allowsTypeAndDescriptionOfNonOptionArguments() {
+        OptionSpec<File> nonOptions = parser.nonOptions( "directories" ).ofType( File.class );
+
+        OptionSet options = parser.parse( "/opt", "/var" );
+
+        assertEquals( asList( new File( "/opt" ), new File( "/var" ) ), nonOptions.values( options ) );
+    }
+
+    @Test
+    public void allowsArgumentDescriptionForNonOptionArguments() {
+        OptionSpec<String> nonOptions = parser.nonOptions().describedAs( "dirs" );
+
+        OptionSet options = parser.parse( "/opt", "/var" );
+
+        assertEquals( asList( "/opt", "/var" ), nonOptions.values( options ) );
+    }
+
+    @Test
+    public void doesNotAcceptArguments() {
+        OptionDescriptor nonOptions = parser.nonOptions().describedAs( "dirs" );
+
+        assertFalse( nonOptions.acceptsArguments() );
+    }
+
+    @Test
+    public void convertingUsingConverter() throws Exception {
+        OptionSpec<Date> date = parser.nonOptions().withValuesConvertedBy( datePattern( "MM/dd/yyyy" ) );
+
+        OptionSet options = parser.parse( "01/24/2013" );
+
+        assertEquals(
+            singletonList( new SimpleDateFormat( "MM/dd/yyyy" ).parse( "01/24/2013" ) ),
+            date.values( options ) );
+    }
+
+    @Test( expected = NullPointerException.class )
+    public void convertingUsingNullConverter() {
+        parser.nonOptions().withValuesConvertedBy( null );
+    }
+
+    @Test
+    public void noSpecsCorrespondingToNonOptions() {
+        OptionParser parser = new OptionParser();
+        parser.nonOptions();
+
+        OptionSet options = parser.parse( "one", "two" );
+
+        assertEquals( emptyList(), options.specs() );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/OptionExceptionLocalizedMessageTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/OptionExceptionLocalizedMessageTest.java
new file mode 100644
index 0000000..e4cd337
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/OptionExceptionLocalizedMessageTest.java
@@ -0,0 +1,84 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Locale;
+
+import static java.util.Arrays.*;
+import static org.junit.Assert.*;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+@RunWith( Parameterized.class )
+public class OptionExceptionLocalizedMessageTest {
+    private final OptionException subject;
+    private final String expectedMessage;
+
+    public OptionExceptionLocalizedMessageTest( OptionException subject, String expectedMessage ) {
+        this.subject = subject;
+        this.expectedMessage = expectedMessage;
+    }
+
+    @Parameterized.Parameters
+    public static Collection<?> exceptionsAndMessages() {
+        return asList(
+            new Object[] { new IllegalOptionSpecificationException( "," ), "illegal option specification exception" },
+            new Object[] { new MultipleArgumentsForOptionException(
+                new RequiredArgumentOptionSpec<>( asList( "b", "c" ), "d" ) ),
+                "multiple arguments for option exception" },
+            new Object[] { new OptionArgumentConversionException(
+                new RequiredArgumentOptionSpec<>( asList( "c", "number" ), "x" ), "d", null ),
+                "option argument conversion exception" },
+            new Object[] { new OptionMissingRequiredArgumentException(
+                new RequiredArgumentOptionSpec<>( asList( "e", "honest" ), "" ) ),
+                "option missing required argument exception" },
+            new Object[] { new UnrecognizedOptionException( "f" ), "unrecognized option exception" },
+            new Object[] { new MissingRequiredOptionsException(
+                Arrays.<AbstractOptionSpec<?>> asList(
+                    new NoArgumentOptionSpec( "g" ), new NoArgumentOptionSpec( "h" ) ) ),
+                "missing required option exception" },
+            new Object[] { new MissingRequiredOptionsException(
+                    Arrays.<AbstractOptionSpec<?>> asList(
+                        new RequiredArgumentOptionSpec<>( asList( "p", "place" ), "spot" ),
+                        new RequiredArgumentOptionSpec<>( asList( "d", "data-dir" ), "dir" ) ) ),
+                    "missing required option exception" },
+            new Object[] { new UnconfiguredOptionException( asList( "i", "j" ) ),
+                "unconfigured option exception" }
+        );
+    }
+
+    @Test
+    public void givesCorrectExceptionMessage() {
+        assertEquals( expectedMessage, subject.localizedMessage( new Locale( "xx", "YY" ) ) );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/OptionExceptionMessageTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/OptionExceptionMessageTest.java
new file mode 100644
index 0000000..3afdd83
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/OptionExceptionMessageTest.java
@@ -0,0 +1,91 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import java.util.Arrays;
+import java.util.Collection;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import static com.google.common.collect.Lists.newArrayList;
+import static java.util.Arrays.*;
+import static org.junit.Assert.*;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+@RunWith( Parameterized.class )
+public class OptionExceptionMessageTest {
+    private final OptionException subject;
+    private final String expectedMessage;
+
+    public OptionExceptionMessageTest( OptionException subject, String expectedMessage ) {
+        this.subject = subject;
+        this.expectedMessage = expectedMessage;
+    }
+
+    @Parameterized.Parameters
+    public static Collection<?> exceptionsAndMessages() {
+        return asList(
+            new Object[] { new IllegalOptionSpecificationException( "," ), ", is not a legal option character" },
+            new Object[] { new MultipleArgumentsForOptionException(
+                new RequiredArgumentOptionSpec<>( asList( "b", "c" ), "d" ) ),
+                "Found multiple arguments for option b/c, but you asked for only one" },
+            new Object[] { new OptionArgumentConversionException(
+                new RequiredArgumentOptionSpec<>( asList( "c", "number" ), "x" ), "d", null ),
+                "Cannot parse argument 'd' of option c/number" },
+            new Object[] { new OptionMissingRequiredArgumentException(
+                new RequiredArgumentOptionSpec<>( asList( "e", "honest" ), "" ) ),
+                "Option e/honest requires an argument" },
+            new Object[] { new UnrecognizedOptionException( "f" ), "f is not a recognized option" },
+            new Object[] { new MissingRequiredOptionsException(
+                Arrays.<AbstractOptionSpec<?>> asList(
+                    new NoArgumentOptionSpec( "g" ), new NoArgumentOptionSpec( "h" ) ) ),
+                "Missing required option(s) [g, h]" },
+            new Object[] { new MissingRequiredOptionsException(
+                    Arrays.<AbstractOptionSpec<?>> asList(
+                        new RequiredArgumentOptionSpec<>( asList( "p", "place" ), "spot" ),
+                        new RequiredArgumentOptionSpec<>( asList( "d", "data-dir" ), "dir" ) ) ),
+                    "Missing required option(s) [p/place, d/data-dir]" },
+            new Object[] { new UnconfiguredOptionException( asList( "i", "j" ) ),
+                "Option(s) [i, j] not configured on this parser" },
+            new Object[] {
+                    new UnavailableOptionException(
+                            newArrayList( new NoArgumentOptionSpec( "a" ), new NoArgumentOptionSpec( "b" ) ) ),
+                    "Option(s) [a, b] are unavailable given other options on the command line"
+            }
+        );
+    }
+
+    @Test
+    public void givesCorrectExceptionMessage() {
+        assertEquals( expectedMessage, subject.getLocalizedMessage() );
+        assertEquals( expectedMessage, subject.getMessage() );
+        assertEquals( subject.getClass().getName() + ": " + expectedMessage, subject.toString() );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/OptionExceptionOptionsImmutabilityTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/OptionExceptionOptionsImmutabilityTest.java
new file mode 100644
index 0000000..465d960
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/OptionExceptionOptionsImmutabilityTest.java
@@ -0,0 +1,59 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import java.util.List;
+
+import org.infinitest.toolkit.UnmodifiableListTestSupport;
+
+import static java.util.Arrays.*;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class OptionExceptionOptionsImmutabilityTest extends UnmodifiableListTestSupport<String> {
+    @Override
+    protected List<String> newList() {
+        return new OptionException( asList( "a", "b" ) ) {
+            private static final long serialVersionUID = -1L;
+
+            @Override
+            Object[] messageArguments() {
+                return new Object[0];
+            }
+        }.options();
+    }
+
+    @Override
+    protected String newItem() {
+        return "c";
+    }
+
+    @Override
+    protected String containedItem() {
+        return "a";
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/OptionParserAlternateHelpFormatterTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/OptionParserAlternateHelpFormatterTest.java
new file mode 100644
index 0000000..b799df6
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/OptionParserAlternateHelpFormatterTest.java
@@ -0,0 +1,61 @@
+package joptsimple;
+
+import java.io.StringWriter;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import static java.util.Arrays.*;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.infinitest.toolkit.CollectionMatchers.*;
+import static org.junit.Assert.*;
+
+public class OptionParserAlternateHelpFormatterTest extends AbstractOptionParserFixture {
+    private StringWriter sink;
+    private Map<String, ? extends OptionDescriptor> captured;
+
+    @Before
+    public void primeParser() throws Exception {
+        captured = new HashMap<>();
+
+        parser.accepts( "b", "boo" );
+
+        parser.formatHelpWith( new HelpFormatter() {
+            public String format( Map<String, ? extends OptionDescriptor> options ) {
+                captured = options;
+                return "some help you are";
+            }
+        } );
+
+        sink = new StringWriter();
+
+        parser.printHelpOn( sink );
+    }
+
+    @Test
+    public void asksAlternateFormatterForHelpString() {
+        assertEquals( "some help you are", sink.toString() );
+    }
+
+    @Test
+    public void getsFedOptionDescriptorsForRecognizedOptions() {
+        assertEquals( 2, captured.size() );
+        Iterator<? extends Map.Entry<String,? extends OptionDescriptor>> iterator = captured.entrySet().iterator();
+        Map.Entry<String, ? extends OptionDescriptor> first = iterator.next();
+        assertEquals( "[arguments]", first.getKey() );
+        Map.Entry<String, ? extends OptionDescriptor> second = iterator.next();
+        assertEquals("b", second.getKey());
+        OptionDescriptor descriptor = second.getValue();
+        assertThat( descriptor.options(), hasSameContentsAs( asList( "b" ) ) );
+        assertEquals( "boo", descriptor.description() );
+        assertFalse( descriptor.acceptsArguments() );
+        assertFalse( descriptor.requiresArgument() );
+        assertEquals( "", descriptor.argumentDescription() );
+        assertEquals( "", descriptor.argumentTypeIndicator() );
+        assertEquals( Collections.emptyList(), descriptor.defaultValues() );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/OptionParserArgumentExceptionTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/OptionParserArgumentExceptionTest.java
new file mode 100644
index 0000000..4661795
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/OptionParserArgumentExceptionTest.java
@@ -0,0 +1,82 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import java.util.Collections;
+
+import static java.util.Collections.*;
+
+import org.junit.Test;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class OptionParserArgumentExceptionTest extends AbstractOptionParserFixture {
+    @Test( expected = NullPointerException.class )
+    public void createWithNullOptionSpec() {
+        new OptionParser( null );
+    }
+
+    @Test( expected = NullPointerException.class )
+    public void parseNull() {
+        parser.parse( (String[]) null );
+    }
+
+    @Test( expected = NullPointerException.class )
+    public void nullOptionToAccepts() {
+        parser.accepts( null );
+    }
+
+    @Test( expected = NullPointerException.class )
+    public void nullOptionToAcceptsWithDescription() {
+        parser.accepts( null, "a weird option" );
+    }
+
+    @Test( expected = NullPointerException.class )
+    public void nullOptionListToAcceptsAll() {
+        parser.acceptsAll( null );
+    }
+
+    @Test( expected = IllegalArgumentException.class )
+    public void emptyOptionListToAcceptsAll() {
+        parser.acceptsAll( Collections.<String> emptyList() );
+    }
+
+    @Test( expected = NullPointerException.class )
+    public void optionListContainingNullToAcceptsAll() {
+        parser.acceptsAll( singletonList( (String) null ) );
+    }
+
+    @Test( expected = NullPointerException.class )
+    public void nullOptionListToAcceptsAllWithDescription() {
+        parser.acceptsAll( null, "some weird options" );
+    }
+
+    @Test( expected = NullPointerException.class )
+    public void optionListContainingNullToAcceptsAllWithDescription() {
+        parser.acceptsAll( singletonList( (String) null ), "some weird options" );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/OptionParserNewDeclarationTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/OptionParserNewDeclarationTest.java
new file mode 100644
index 0000000..0381af2
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/OptionParserNewDeclarationTest.java
@@ -0,0 +1,290 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import java.math.BigInteger;
+import java.sql.Time;
+import java.sql.Timestamp;
+
+import static java.lang.Boolean.*;
+import static java.util.Arrays.*;
+import static java.util.Collections.*;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class OptionParserNewDeclarationTest extends AbstractOptionParserFixture {
+    @Test( expected = IllegalOptionSpecificationException.class )
+    public void acceptsIllegalCharacters() {
+        parser.accepts( "!" );
+    }
+
+    @Test
+    public void booleanArgumentType() {
+        parser.accepts( "a" ).withRequiredArg().ofType( Boolean.class );
+        parser.accepts( "b" ).withOptionalArg().ofType( Boolean.class );
+
+        OptionSet options = parser.parse( "-a", "true", "-b", "false" );
+
+        assertOptionDetected( options, "a" );
+        assertOptionDetected( options, "b" );
+        assertEquals( TRUE, options.valueOf( "a" ) );
+        assertEquals( singletonList( TRUE ), options.valuesOf( "a" ) );
+        assertEquals( FALSE, options.valueOf( "b" ) );
+        assertEquals( singletonList( FALSE ), options.valuesOf( "b" ) );
+        assertEquals( emptyList(), options.nonOptionArguments() );
+    }
+
+    @Test
+    public void byteArgumentType() {
+        parser.accepts( "a" ).withRequiredArg().ofType( Byte.class );
+        parser.accepts( "b" ).withOptionalArg().ofType( Byte.class );
+
+        OptionSet options = parser.parse( "-a", "-1", "-b", "-2" );
+
+        assertOptionDetected( options, "a" );
+        assertOptionDetected( options, "b" );
+        assertEquals( Byte.valueOf( "-1" ), options.valueOf( "a" ) );
+        assertEquals( singletonList( Byte.valueOf( "-1" ) ), options.valuesOf( "a" ) );
+        assertEquals( Byte.valueOf( "-2" ), options.valueOf( "b" ) );
+        assertEquals( singletonList( Byte.valueOf( "-2" ) ), options.valuesOf( "b" ) );
+        assertEquals( emptyList(), options.nonOptionArguments() );
+    }
+
+    @Test
+    public void doubleArgumentType() {
+        parser.accepts( "a" ).withRequiredArg().ofType( Double.class );
+        parser.accepts( "b" ).withOptionalArg().ofType( Double.class );
+
+        OptionSet options = parser.parse( "-a", "3.1415926D", "-b", "6.02E23" );
+
+        assertOptionDetected( options, "a" );
+        assertOptionDetected( options, "b" );
+        assertEquals( Double.valueOf( "3.1415926D" ), options.valueOf( "a" ) );
+        assertEquals( asList( Double.valueOf( "3.1415926D" ) ), options.valuesOf( "a" ) );
+        assertEquals( Double.valueOf( "6.02E23" ), options.valueOf( "b" ) );
+        assertEquals( asList( Double.valueOf( "6.02E23" ) ), options.valuesOf( "b" ) );
+        assertEquals( emptyList(), options.nonOptionArguments() );
+    }
+
+    @Test
+    public void floatArgumentType() {
+        parser.accepts( "a" ).withRequiredArg().ofType( Float.class );
+        parser.accepts( "b" ).withOptionalArg().ofType( Float.class );
+
+        OptionSet options = parser.parse( "-a", "3.1415926F", "-b", "6.02E23F" );
+
+        assertOptionDetected( options, "a" );
+        assertOptionDetected( options, "b" );
+        assertEquals( Float.valueOf( "3.1415926F" ), options.valueOf( "a" ) );
+        assertEquals( asList( Float.valueOf( "3.1415926F" ) ), options.valuesOf( "a" ) );
+        assertEquals( Float.valueOf( "6.02E23F" ), options.valueOf( "b" ) );
+        assertEquals( asList( Float.valueOf( "6.02E23F" ) ), options.valuesOf( "b" ) );
+        assertEquals( emptyList(), options.nonOptionArguments() );
+    }
+
+    @Test
+    public void integerArgumentType() {
+        parser.accepts( "a" ).withRequiredArg().ofType( Integer.class );
+        parser.accepts( "b" ).withOptionalArg().ofType( Integer.class );
+
+        OptionSet options = parser.parse( "-a", "12", "-b", "34" );
+
+        assertOptionDetected( options, "a" );
+        assertOptionDetected( options, "b" );
+        assertEquals( Integer.valueOf( "12" ), options.valueOf( "a" ) );
+        assertEquals( asList( Integer.valueOf( "12" ) ), options.valuesOf( "a" ) );
+        assertEquals( Integer.valueOf( "34" ), options.valueOf( "b" ) );
+        assertEquals( asList( Integer.valueOf( "34" ) ), options.valuesOf( "b" ) );
+        assertEquals( emptyList(), options.nonOptionArguments() );
+    }
+
+    @Test
+    public void longArgumentType() {
+        parser.accepts( "a" ).withRequiredArg().ofType( Long.class );
+        parser.accepts( "b" ).withOptionalArg().ofType( Long.class );
+
+        OptionSet options = parser.parse( "-a", "123454678901234", "-b", "98765432109876" );
+
+        assertOptionDetected( options, "a" );
+        assertOptionDetected( options, "b" );
+        assertEquals( Long.valueOf( "123454678901234" ), options.valueOf( "a" ) );
+        assertEquals( singletonList( Long.valueOf( "123454678901234" ) ), options.valuesOf( "a" ) );
+        assertEquals( Long.valueOf( "98765432109876" ), options.valueOf( "b" ) );
+        assertEquals( singletonList( Long.valueOf( "98765432109876" ) ), options.valuesOf( "b" ) );
+        assertEquals( emptyList(), options.nonOptionArguments() );
+    }
+
+    @Test
+    public void shortArgumentType() {
+        parser.accepts( "a" ).withRequiredArg().ofType( Short.class );
+        parser.accepts( "b" ).withOptionalArg().ofType( Short.class );
+
+        OptionSet options = parser.parse( "-a", "5675", "-b", "345" );
+
+        assertOptionDetected( options, "a" );
+        assertOptionDetected( options, "b" );
+        assertEquals( Short.valueOf( "5675" ), options.valueOf( "a" ) );
+        assertEquals( asList( Short.valueOf( "5675" ) ), options.valuesOf( "a" ) );
+        assertEquals( Short.valueOf( "345" ), options.valueOf( "b" ) );
+        assertEquals( asList( Short.valueOf( "345" ) ), options.valuesOf( "b" ) );
+        assertEquals( emptyList(), options.nonOptionArguments() );
+    }
+
+    @Test
+    public void sqlDateArgumentType() {
+        parser.accepts( "a" ).withRequiredArg().ofType( java.sql.Date.class );
+        parser.accepts( "b" ).withOptionalArg().ofType( java.sql.Date.class );
+
+        OptionSet options = parser.parse( "-a", "2001-09-11", "-b", "1941-12-07" );
+
+        assertOptionDetected( options, "a" );
+        assertOptionDetected( options, "b" );
+        assertEquals( java.sql.Date.valueOf( "2001-09-11" ), options.valueOf( "a" ) );
+        assertEquals( singletonList( java.sql.Date.valueOf( "2001-09-11" ) ), options.valuesOf( "a" ) );
+        assertEquals( java.sql.Date.valueOf( "1941-12-07" ), options.valueOf( "b" ) );
+        assertEquals( singletonList( java.sql.Date.valueOf( "1941-12-07" ) ), options.valuesOf( "b" ) );
+        assertEquals( emptyList(), options.nonOptionArguments() );
+    }
+
+    @Test
+    public void sqlTimeArgumentType() {
+        parser.accepts( "a" ).withRequiredArg().ofType( Time.class );
+        parser.accepts( "b" ).withOptionalArg().ofType( Time.class );
+
+        OptionSet options = parser.parse( "-a", "08:57:39", "-b", "23:59:59" );
+
+        assertOptionDetected( options, "a" );
+        assertOptionDetected( options, "b" );
+        assertEquals( Time.valueOf( "08:57:39" ), options.valueOf( "a" ) );
+        assertEquals( asList( Time.valueOf( "08:57:39" ) ), options.valuesOf( "a" ) );
+        assertEquals( Time.valueOf( "23:59:59" ), options.valueOf( "b" ) );
+        assertEquals( asList( Time.valueOf( "23:59:59" ) ), options.valuesOf( "b" ) );
+        assertEquals( emptyList(), options.nonOptionArguments() );
+    }
+
+    @Test
+    public void sqlTimestampArgumentType() {
+        parser.accepts( "a" ).withRequiredArg().ofType( Timestamp.class );
+        parser.accepts( "b" ).withOptionalArg().ofType( Timestamp.class );
+
+        OptionSet options = parser.parse( "-a", "1970-01-01 00:00:00", "-b", "1979-12-31 23:59:59.0123456" );
+
+        assertOptionDetected( options, "a" );
+        assertOptionDetected( options, "b" );
+        assertEquals( Timestamp.valueOf( "1970-01-01 00:00:00" ), options.valueOf( "a" ) );
+        assertEquals( singletonList( Timestamp.valueOf( "1970-01-01 00:00:00" ) ), options.valuesOf( "a" ) );
+        assertEquals( Timestamp.valueOf( "1979-12-31 23:59:59.0123456" ), options.valueOf( "b" ) );
+        assertEquals( singletonList( Timestamp.valueOf( "1979-12-31 23:59:59.0123456" ) ), options.valuesOf( "b" ) );
+        assertEquals( emptyList(), options.nonOptionArguments() );
+    }
+
+    @Test( expected = OptionException.class )
+    public void illegalOptionArgumentMethodConversion() {
+        parser.accepts( "a" ).withRequiredArg().ofType( Integer.class );
+
+        OptionSet options = parser.parse( "-a", "foo" );
+
+        options.valueOf( "a" );
+    }
+
+    @Test( expected = OptionException.class )
+    public void illegalOptionArgumentConstructorConversion() {
+        parser.accepts( "a" ).withRequiredArg().ofType( BigInteger.class );
+
+        OptionSet options = parser.parse( "-a", "foo" );
+
+        options.valueOf( "a" );
+    }
+
+    @Test
+    public void optionsWithOptionalNegativeNumberArguments() {
+        parser.accepts( "a" ).withOptionalArg().ofType( Integer.class );
+        parser.accepts( "b" ).withOptionalArg().ofType( Integer.class );
+        parser.accepts( "1" );
+        parser.accepts( "2" );
+
+        OptionSet options = parser.parse( "-a", "-1", "-b", "-2" );
+
+        assertOptionDetected( options, "a" );
+        assertOptionDetected( options, "b" );
+        assertEquals( Integer.valueOf( "-1" ), options.valueOf( "a" ) );
+        assertEquals( asList( Integer.valueOf( "-1" ) ), options.valuesOf( "a" ) );
+        assertEquals( Integer.valueOf( "-2" ), options.valueOf( "b" ) );
+        assertEquals( asList( Integer.valueOf( "-2" ) ), options.valuesOf( "b" ) );
+        assertEquals( emptyList(), options.nonOptionArguments() );
+    }
+
+    @Test
+    public void optionsWithOptionalNegativeNumberArgumentsAndNumberOptions() {
+        parser.accepts( "a" ).withOptionalArg().ofType( Integer.class );
+        parser.accepts( "b" ).withOptionalArg().ofType( Integer.class );
+        parser.accepts( "1" );
+        parser.accepts( "2" );
+
+        OptionSet options = parser.parse( "-1", "-2", "-a", "-b" );
+
+        assertOptionDetected( options, "1" );
+        assertOptionDetected( options, "a" );
+        assertOptionDetected( options, "2" );
+        assertOptionDetected( options, "b" );
+        assertNull( options.valueOf( "1" ) );
+        assertNull( options.valueOf( "a" ) );
+        assertNull( options.valueOf( "2" ) );
+        assertNull( options.valueOf( "b" ) );
+        assertEquals( emptyList(), options.valuesOf( "1" ) );
+        assertEquals( emptyList(), options.valuesOf( "a" ) );
+        assertEquals( emptyList(), options.valuesOf( "2" ) );
+        assertEquals( emptyList(), options.valuesOf( "b" ) );
+        assertEquals( emptyList(), options.nonOptionArguments() );
+    }
+
+    @Test
+    public void optionsWithNegativeNumberArgumentsAndNonNumberOptions() {
+        parser.accepts( "a" ).withOptionalArg().ofType( Integer.class );
+        parser.accepts( "b" ).withOptionalArg().ofType( Integer.class );
+        parser.accepts( "1" );
+        parser.accepts( "2" );
+
+        OptionSet options = parser.parse( "-1", "-a", "-b", "-2" );
+
+        assertOptionDetected( options, "1" );
+        assertOptionDetected( options, "a" );
+        assertOptionDetected( options, "b" );
+        assertOptionNotDetected( options, "2" );
+        assertNull( options.valueOf( "1" ) );
+        assertNull( options.valueOf( "a" ) );
+        assertEquals( Integer.valueOf( "-2" ), options.valueOf( "b" ) );
+        assertEquals( emptyList(), options.valuesOf( "1" ) );
+        assertEquals( emptyList(), options.valuesOf( "a" ) );
+        assertEquals( asList( Integer.valueOf( "-2" ) ), options.valuesOf( "b" ) );
+        assertEquals( emptyList(), options.nonOptionArguments() );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/OptionParserOptionExceptionTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/OptionParserOptionExceptionTest.java
new file mode 100644
index 0000000..3e219d52
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/OptionParserOptionExceptionTest.java
@@ -0,0 +1,78 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import org.junit.Test;
+
+import static joptsimple.ExceptionMatchers.*;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class OptionParserOptionExceptionTest extends AbstractOptionParserFixture {
+    @Test
+    public void unrecognizedOption() {
+        thrown.expect( UnrecognizedOptionException.class );
+        thrown.expect( withOption( "a" ) );
+
+        parser.parse( "-a" );
+    }
+
+    @Test
+    public void illegalOptionCharacter() {
+        thrown.expect( IllegalOptionSpecificationException.class );
+        thrown.expect( withOption( "%" ) );
+
+        parser.accepts( "%" );
+    }
+
+    @Test
+    public void asteriskIsIllegalOptionCharacter() {
+        thrown.expect( IllegalOptionSpecificationException.class );
+        thrown.expect( withOption( "*" ) );
+
+        parser.accepts( "*" );
+    }
+
+    @Test
+    public void tooManyHyphens() {
+        parser.accepts( "b" );
+        thrown.expect( UnrecognizedOptionException.class );
+        thrown.expect( withOption( "-b" ) );
+
+        parser.parse( "---b" );
+    }
+
+    @Test
+    public void valueOfWhenMultiples() {
+        parser.accepts( "e" ).withRequiredArg();
+        OptionSet options = parser.parse( "-e", "foo", "-e", "bar" );
+        thrown.expect( MultipleArgumentsForOptionException.class );
+        thrown.expect( withOption( "e" ) );
+
+        options.valueOf( "e" );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/OptionParserRecognizedOptionsTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/OptionParserRecognizedOptionsTest.java
new file mode 100644
index 0000000..3bddfb9
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/OptionParserRecognizedOptionsTest.java
@@ -0,0 +1,67 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package joptsimple;
+
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.Map;
+
+import static java.util.Arrays.asList;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+public class OptionParserRecognizedOptionsTest extends AbstractOptionParserFixture {
+    @Test
+    public void basicOptionsRecognized() {
+        parser.accepts( "first" ).withRequiredArg().required();
+        parser.accepts( "second" ).withOptionalArg();
+        parser.accepts( "third" ).forHelp();
+
+        Map<String, OptionSpec<?>> recognizedOptions = parser.recognizedOptions();
+
+        assertEquals( 4, recognizedOptions.size() );
+        assertTrue( recognizedOptions.keySet().contains( "first" ) );
+        assertTrue( recognizedOptions.keySet().contains( "second" ) );
+        assertTrue( recognizedOptions.keySet().contains( "third" ) );
+        assertTrue( recognizedOptions.keySet().contains( "[arguments]" ) );
+        assertTrue( recognizedOptions.get( "third" ).isForHelp() );
+        assertFalse( recognizedOptions.get( "second" ).isForHelp() );
+        assertNotNull( recognizedOptions.get( "first" ).options() );
+    }
+
+    @Test
+    public void parserPreservesTrainingOrder() {
+        final OptionSpecBuilder z = parser.acceptsAll( asList( "zebra", "aardvark" ) );
+        final OptionSpecBuilder y = parser.accepts( "yak" );
+        final OptionSpecBuilder x = parser.acceptsAll( asList( "baboon", "xantus" ) );
+
+        assertEquals( asList( "[arguments]", "aardvark", "zebra", "yak", "baboon", "xantus" ), new ArrayList<String>(
+            parser.recognizedOptions().keySet() ) );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/OptionParserTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/OptionParserTest.java
new file mode 100644
index 0000000..5538f2d
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/OptionParserTest.java
@@ -0,0 +1,318 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import static java.lang.Boolean.*;
+import static java.util.Arrays.*;
+import static java.util.Collections.*;
+
+import org.hamcrest.Description;
+import org.hamcrest.TypeSafeMatcher;
+import org.junit.Test;
+
+import static joptsimple.ExceptionMatchers.*;
+import static org.junit.Assert.*;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class OptionParserTest extends AbstractOptionParserFixture {
+    @Test
+    public void optionsAndNonOptionsInterspersed() {
+        parser.accepts( "i" ).withOptionalArg();
+        parser.accepts( "j" ).withOptionalArg();
+        parser.accepts( "k" );
+
+        OptionSet options =
+            parser.parse( "-ibar", "-i", "junk", "xyz", "-jixnay", "foo", "-k", "blah", "--", "yermom" );
+
+        assertOptionDetected( options, "i" );
+        assertOptionDetected( options, "j" );
+        assertOptionDetected( options, "k" );
+        assertEquals( asList( "bar", "junk" ), options.valuesOf( "i" ) );
+        assertEquals( singletonList( "ixnay" ), options.valuesOf( "j" ) );
+        assertEquals( emptyList(), options.valuesOf( "k" ) );
+        assertEquals( asList( "xyz", "foo", "blah", "yermom" ), options.nonOptionArguments() );
+    }
+
+    @Test
+    public void shortOptionSpecifiedAsLongOptionWithoutArgument() {
+        parser.accepts( "x" );
+
+        OptionSet options = parser.parse( "--x" );
+        assertOptionDetected( options, "x" );
+        assertEquals( emptyList(), options.valuesOf( "x" ) );
+        assertEquals( emptyList(), options.nonOptionArguments() );
+    }
+
+    @Test
+    public void longOptionLeadsWithSingleDash() {
+        parser.accepts( "quiet" );
+        parser.accepts( "queen" );
+
+        OptionSet options = parser.parse( "-quiet" );
+        assertOptionDetected( options, "quiet" );
+        assertEquals( emptyList(), options.valuesOf( "quiet" ) );
+        assertEquals( emptyList(), options.nonOptionArguments() );
+    }
+    
+    @Test
+    public void parsesLongOptionAsAbbreviatedShortOption() {
+        parser.accepts( "queen" );
+
+        parser.parse( "-que" );
+    }
+    
+    @Test
+    public void parsesLongOptionAsAbbreviatedLongOption() {
+        parser.accepts( "queen" );
+
+        parser.parse( "--que" );
+    }
+
+    @Test
+    public void longOptionLeadsWithSingleDashAmbiguous() {
+        parser.accepts( "quiet" );
+        parser.accepts( "queen" );
+        thrown.expect( UnrecognizedOptionException.class );
+        thrown.expect( withOption( "q" ) );
+
+        parser.parse( "-q" );
+    }
+
+    @Test
+    public void longOptionLeadsWithSingleDashAmbiguousButShortsAreLegal() {
+        parser.accepts( "quiet" );
+        parser.accepts( "queen" );
+        parser.accepts( "q" );
+        parser.accepts( "u" );
+
+        OptionSet options = parser.parse( "-qu" );
+        assertOptionDetected( options, "q" );
+        assertOptionDetected( options, "u" );
+        assertOptionNotDetected( options, "quiet" );
+        assertOptionNotDetected( options, "queen" );
+        assertEquals( emptyList(), options.valuesOf( "q" ) );
+        assertEquals( emptyList(), options.valuesOf( "u" ) );
+        assertEquals( emptyList(), options.nonOptionArguments() );
+    }
+
+    @Test
+    public void longOptionLeadsWithSingleDashAmbiguousButAShortIsIllegal() {
+        parser.accepts( "quiet" );
+        parser.accepts( "queen" );
+        parser.accepts( "q" );
+        thrown.expect( UnrecognizedOptionException.class );
+        thrown.expect( withOption( "u" ) );
+
+        parser.parse( "-qu" );
+    }
+
+    @Test
+    public void longOptionLeadsWithSingleDashAmbiguousButAShortAcceptsAnArgument() {
+        parser.accepts( "quiet" );
+        parser.accepts( "queen" );
+        parser.accepts( "q" ).withOptionalArg();
+
+        OptionSet options = parser.parse( "-qu" );
+        assertOptionDetected( options, "q" );
+        assertOptionNotDetected( options, "quiet" );
+        assertOptionNotDetected( options, "queen" );
+        assertEquals( singletonList( "u" ), options.valuesOf( "q" ) );
+        assertEquals( emptyList(), options.nonOptionArguments() );
+    }
+
+    @Test
+    public void resetHappensAfterParsing() {
+        parser.accepts( "i" ).withOptionalArg();
+        parser.accepts( "j" ).withOptionalArg();
+        parser.accepts( "k" );
+
+        String[] args = { "-ibar", "-i", "junk", "xyz", "-jixnay", "foo", "-k", "blah", "--", "yermom" };
+
+        OptionSet options = parser.parse( args );
+        assertEquals( options, parser.parse( args ) );
+    }
+
+    @Test
+    public void typedArguments() {
+        parser.accepts( "a" ).withRequiredArg().ofType( Boolean.class );
+        parser.accepts( "b" ).withOptionalArg().ofType( Integer.class );
+
+        OptionSet options = parser.parse( "-a", "false", "-b", "3", "extra" );
+
+        assertOptionDetected( options, "a" );
+        assertOptionDetected( options, "b" );
+        assertEquals( FALSE, options.valueOf( "a" ) );
+        assertEquals( singletonList( FALSE ), options.valuesOf( "a" ) );
+        assertEquals( Integer.valueOf( "3" ), options.valueOf( "b" ) );
+        assertEquals( singletonList( Integer.valueOf( "3" ) ), options.valuesOf( "b" ) );
+        assertEquals( singletonList( "extra" ), options.nonOptionArguments() );
+    }
+
+    @Test
+    public void allowsMixingOfOptionsAndNonOptions() {
+        parser.accepts( "i" ).withRequiredArg();
+        parser.accepts( "j" ).withOptionalArg();
+        parser.accepts( "k" );
+
+        OptionSet options = parser.parse( "a", "b", "c", "-i", "boo", "d", "e", "-k", "f", "-j" );
+
+        assertOptionDetected( options, "i" );
+        assertEquals( singletonList( "boo" ), options.valuesOf( "i" ) );
+        assertOptionDetected( options, "j" );
+        assertEquals( emptyList(), options.valuesOf( "j" ) );
+        assertOptionDetected( options, "k" );
+        assertEquals( emptyList(), options.valuesOf( "k" ) );
+        assertEquals( asList( "a", "b", "c", "d", "e", "f" ), options.nonOptionArguments() );
+    }
+
+    @Test
+    public void disallowsMixingOfOptionsAndNonOptionsUnderPosixlyCorrect() {
+        parser.accepts( "i" ).withRequiredArg();
+        parser.accepts( "j" ).withOptionalArg();
+        parser.accepts( "k" );
+        parser.posixlyCorrect( true );
+
+        OptionSet options = parser.parse( "a", "b", "c", "-i", "boo", "d", "e", "-k", "f", "-j" );
+
+        assertOptionNotDetected( options, "i" );
+        assertEquals( emptyList(), options.valuesOf( "i" ) );
+        assertOptionNotDetected( options, "j" );
+        assertEquals( emptyList(), options.valuesOf( "j" ) );
+        assertOptionNotDetected( options, "k" );
+        assertEquals( emptyList(), options.valuesOf( "j" ) );
+        assertEquals( asList( "a", "b", "c", "-i", "boo", "d", "e", "-k", "f", "-j" ), options.nonOptionArguments() );
+    }
+
+    @Test
+    public void doubleHyphenSignalsEndsOfOptions() {
+        OptionSet options = new OptionParser( "ab:c::de:f::" ) {
+            {
+                accepts( "verbose" );
+            }
+        }.parse( "-a", "-b=foo", "-c=bar", "--", "-d", "-verbose", "-e", "baz", "-f", "biz" );
+
+        assertOptionDetected( options, "a" );
+        assertEquals( emptyList(), options.valuesOf( "a" ) );
+        assertOptionDetected( options, "b" );
+        assertEquals( singletonList( "foo" ), options.valuesOf( "b" ) );
+        assertOptionDetected( options, "c" );
+        assertEquals( singletonList( "bar" ), options.valuesOf( "c" ) );
+        assertOptionNotDetected( options, "d" );
+        assertOptionNotDetected( options, "verbose" );
+        assertOptionNotDetected( options, "e" );
+        assertOptionNotDetected( options, "f" );
+        assertEquals( asList( "-d", "-verbose", "-e", "baz", "-f", "biz" ), options.nonOptionArguments() );
+    }
+
+    @Test
+    public void allowsEmptyStringAsArgumentOfOption() {
+        OptionSpec<String> optionI = parser.accepts( "i" ).withOptionalArg();
+
+        OptionSet options = parser.parse( "-i", "" );
+
+        assertOptionDetected( options, "i" );
+        assertEquals( "", optionI.value( options ) );
+    }
+
+    @Test
+    public void allowsWhitespaceyStringAsArgumentOfOption() {
+        String whitespace = "     \t\t\n\n\f\f     \r\r   ";
+        OptionSpec<String> optionJ = parser.accepts( "j" ).withRequiredArg();
+
+        OptionSet options = parser.parse( "-j", whitespace );
+
+        assertOptionDetected( options, "j" );
+        assertEquals( whitespace, optionJ.value( options ) );
+    }
+
+    @Test
+    public void allowsEmbeddedWhitespaceInArgumentOfOption() {
+        String withEmbeddedWhitespace = "   look at me, I'm flaunting the rules!   ";
+        OptionSpec<String> optionJ = parser.accepts( "j" ).withRequiredArg();
+
+        OptionSet options = parser.parse( "-j", withEmbeddedWhitespace );
+
+        assertOptionDetected( options, "j" );
+        assertEquals( withEmbeddedWhitespace, optionJ.value( options ) );
+    }
+    
+    @Test
+    public void requiredOptionWithArgMissing() {
+        parser.accepts( "t" ).withOptionalArg().required();
+        thrown.expect( MissingRequiredOptionsException.class );
+        thrown.expect( withOption( "t" ) );
+
+        parser.parse();
+    }
+
+    @Test
+    public void requiredOptionButHelpOptionPresent() {
+        parser.accepts( "t" ).withOptionalArg().required();
+        parser.accepts( "h" ).forHelp();
+
+        parser.parse( "-h" );
+    }
+
+    @Test
+    public void configurationPerformedLaterOverrideThosePerformedEarlierForTheSameOption() {
+        parser.accepts( "t" ).withRequiredArg();
+        parser.accepts( "t" ).withOptionalArg();
+
+        parser.parse( "-t" );
+    }
+
+    @Test
+    public void requiredOptionWithSynonymsMissing() {
+        parser.acceptsAll( asList( "h", "help", "?" ) );
+        parser.acceptsAll( asList( "f", "ff", "csv-file-name" ) ).withRequiredArg().required();
+
+        thrown.expect( MissingRequiredOptionsException.class );
+        thrown.expectMessage( new TypeSafeMatcher<String>() {
+            @Override
+            protected boolean matchesSafely( String item ) {
+                return "Missing required option(s) [f/csv-file-name/ff]".equals( item );
+            }
+
+            public void describeTo(Description description) {
+                // purposely doing nothing here
+            }
+        } );
+
+        parser.parse();
+    }
+
+    @Test
+    public void abbreviationsCanBeDisallowed() {
+        OptionParser parser = new OptionParser(false);
+        parser.accepts( "abbreviatable" );
+
+        thrown.expect( UnrecognizedOptionException.class );
+
+        parser.parse( "--abb" );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/OptionParserUnrecognizedOptionsAllowedTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/OptionParserUnrecognizedOptionsAllowedTest.java
new file mode 100644
index 0000000..8df74ca
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/OptionParserUnrecognizedOptionsAllowedTest.java
@@ -0,0 +1,54 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import static joptsimple.ExceptionMatchers.withOption;
+import static java.util.Arrays.asList;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+public class OptionParserUnrecognizedOptionsAllowedTest extends AbstractOptionParserFixture {
+    @Test
+    public void unrecognizedOptionDisallowed() {
+        assertFalse( parser.doesAllowsUnrecognizedOptions() );
+
+        thrown.expect( UnrecognizedOptionException.class );
+        thrown.expect( withOption( "a" ) );
+
+        parser.parse( "-a" );
+    }
+
+    @Test
+    public void unrecognizedOptionAllowed() {
+        parser.allowsUnrecognizedOptions();
+
+        OptionSet options = parser.parse( "-a" );
+
+        assertEquals( asList( "-a" ), options.nonOptionArguments() );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/OptionSetAsMapTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/OptionSetAsMapTest.java
new file mode 100644
index 0000000..0a8c9a6
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/OptionSetAsMapTest.java
@@ -0,0 +1,49 @@
+package joptsimple;
+
+import org.junit.Test;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static java.util.Arrays.asList;
+import static java.util.Collections.*;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+
+/**
+ * @author <a href="mailto:binkley@alumni.rice.edu">B. K. Oxley (binkley)</a>
+ */
+public class OptionSetAsMapTest extends AbstractOptionParserFixture {
+    @Test
+    public void gives() {
+        final OptionSpec<Void> a = parser.accepts( "a" );
+        final OptionSpec<String> b = parser.accepts( "b" ).withRequiredArg();
+        final OptionSpec<String> c = parser.accepts( "c" ).withOptionalArg();
+        final OptionSpec<String> d = parser.accepts( "d" ).withRequiredArg().defaultsTo( "1" );
+        final OptionSpec<String> e = parser.accepts( "e" ).withOptionalArg().defaultsTo( "2" );
+        final OptionSpec<String> f = parser.accepts( "f" ).withRequiredArg().defaultsTo( "3" );
+        final OptionSpec<String> g = parser.accepts( "g" ).withOptionalArg().defaultsTo( "4" );
+        final OptionSpec<Void> h = parser.accepts( "h" );
+
+        OptionSet options = parser.parse( "-a", "-e", "-c", "5", "-d", "6", "-b", "4", "-d", "7", "-e", "8" );
+
+        Map<OptionSpec<?>, List<?>> expected = new HashMap<OptionSpec<?>, List<?>>() {
+            private static final long serialVersionUID = Long.MIN_VALUE;
+
+            {
+                put( a, emptyList() );
+                put( b, asList( "4" ) );
+                put( c, asList( "5" ) );
+                put( d, asList( "6", "7" ) );
+                put( e, asList( "8" ) );
+                put( f, asList( "3" ) );
+                put( g, asList( "4" ) );
+                put( h, emptyList() );
+            }
+        };
+
+        assertThat( options.asMap(), is( equalTo( expected ) ) );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/OptionSetDetectedOptionsTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/OptionSetDetectedOptionsTest.java
new file mode 100644
index 0000000..3a77664
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/OptionSetDetectedOptionsTest.java
@@ -0,0 +1,50 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import java.util.Arrays;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class OptionSetDetectedOptionsTest extends AbstractOptionParserFixture {
+    @Test
+    public void givesSpecsInOrderOfCommandLinePresentation() {
+        OptionSpec<Void> a = parser.accepts( "a" );
+        OptionSpec<String> b = parser.accepts( "b" ).withRequiredArg();
+        OptionSpec<String> c = parser.accepts( "c" ).withOptionalArg();
+        OptionSpec<String> d = parser.accepts( "d" ).withRequiredArg().defaultsTo( "1" );
+        OptionSpec<String> e = parser.accepts( "e" ).withOptionalArg().defaultsTo( "2" );
+
+        OptionSet options = parser.parse( "-a", "-e", "-c", "5", "-d", "6", "-b", "4", "-d", "7", "-e", "8" );
+
+        assertEquals( Arrays.asList( a, e, c, d, b, d, e ), options.specs() );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/OptionSetEqualsHashCodeTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/OptionSetEqualsHashCodeTest.java
new file mode 100644
index 0000000..28757ff
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/OptionSetEqualsHashCodeTest.java
@@ -0,0 +1,59 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import java.util.Collections;
+
+import org.infinitest.toolkit.StrictEqualsHashCodeTestSupport;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class OptionSetEqualsHashCodeTest extends StrictEqualsHashCodeTestSupport {
+    @Override
+    protected OptionSet equal() {
+        OptionSet options = new OptionSet( Collections.<String, AbstractOptionSpec<?>> emptyMap() );
+        options.addWithArgument( new RequiredArgumentOptionSpec<String>( "anOption" ), "anArg" );
+        return options;
+    }
+
+    @Override
+    protected OptionSet notEqual() {
+        OptionSet options = new OptionSet( Collections.<String, AbstractOptionSpec<?>> emptyMap() );
+        options.addWithArgument( new RequiredArgumentOptionSpec<String>( "anOption" ), "aDifferentArg" );
+        return options;
+    }
+
+    @Override
+    protected Object equalButDifferentClass() {
+        OptionSet options =
+                new OptionSet( Collections.<String, AbstractOptionSpec<?>> emptyMap() ) {
+                // anonymous subclass
+            };
+        options.addWithArgument( new RequiredArgumentOptionSpec<String>( "anOption" ), "anArg" );
+        return options;
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/OptionSetNonOptionArgumentsImmutabilityTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/OptionSetNonOptionArgumentsImmutabilityTest.java
new file mode 100644
index 0000000..7a2f352
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/OptionSetNonOptionArgumentsImmutabilityTest.java
@@ -0,0 +1,56 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import java.util.Collections;
+import java.util.List;
+
+import org.infinitest.toolkit.UnmodifiableListTestSupport;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class OptionSetNonOptionArgumentsImmutabilityTest extends UnmodifiableListTestSupport<String> {
+    @SuppressWarnings( "unchecked" )
+    @Override
+    protected List<String> newList() {
+        OptionSet options = new OptionSet( Collections.<String, AbstractOptionSpec<?>> emptyMap() );
+        NonOptionArgumentSpec<String> nonOptions = new NonOptionArgumentSpec<>( null );
+        options.addWithArgument( nonOptions, "1" );
+        options.addWithArgument( nonOptions, "2" );
+        return (List<String>) options.nonOptionArguments();
+    }
+
+    @Override
+    protected String newItem() {
+        return "3";
+    }
+
+    @Override
+    protected String containedItem() {
+        return "2";
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/OptionSetSpecsImmutabilityTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/OptionSetSpecsImmutabilityTest.java
new file mode 100644
index 0000000..d728828
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/OptionSetSpecsImmutabilityTest.java
@@ -0,0 +1,53 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import java.util.Collections;
+import java.util.List;
+
+import org.infinitest.toolkit.UnmodifiableListTestSupport;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class OptionSetSpecsImmutabilityTest extends UnmodifiableListTestSupport<OptionSpec<?>> {
+    @Override
+    protected List<OptionSpec<?>> newList() {
+        OptionSet options = new OptionSet( Collections.<String, AbstractOptionSpec<?>> emptyMap() );
+        options.add( new NoArgumentOptionSpec( "a" ) );
+        return options.specs();
+    }
+
+    @Override
+    protected OptionSpec<?> newItem() {
+        return new NoArgumentOptionSpec( "b" );
+    }
+
+    @Override
+    protected OptionSpec<?> containedItem() {
+        return new NoArgumentOptionSpec( "a" );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/OptionSetValuesOfOptionStringImmutabilityTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/OptionSetValuesOfOptionStringImmutabilityTest.java
new file mode 100644
index 0000000..6f5189a
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/OptionSetValuesOfOptionStringImmutabilityTest.java
@@ -0,0 +1,57 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import java.util.Collections;
+import java.util.List;
+
+import org.infinitest.toolkit.UnmodifiableListTestSupport;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class OptionSetValuesOfOptionStringImmutabilityTest extends UnmodifiableListTestSupport<Object> {
+    @SuppressWarnings( "unchecked" )
+    @Override
+    protected List<Object> newList() {
+        RequiredArgumentOptionSpec<String> optionB = new RequiredArgumentOptionSpec<>( "b" );
+        OptionSet options = new OptionSet( Collections.<String, AbstractOptionSpec<?>> emptyMap() );
+        options.addWithArgument( optionB, "foo" ); 
+        options.addWithArgument( optionB, "bar" );
+
+        return (List<Object>) options.valuesOf( "b" );
+    }
+
+    @Override
+    protected String newItem() {
+        return "baz";
+    }
+
+    @Override
+    protected String containedItem() {
+        return "bar";
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/OptionSetValuesOfSpecImmutabilityTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/OptionSetValuesOfSpecImmutabilityTest.java
new file mode 100644
index 0000000..146652a
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/OptionSetValuesOfSpecImmutabilityTest.java
@@ -0,0 +1,55 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import java.util.Collections;
+import java.util.List;
+
+import org.infinitest.toolkit.UnmodifiableListTestSupport;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class OptionSetValuesOfSpecImmutabilityTest extends UnmodifiableListTestSupport<String> {
+    @Override
+    protected List<String> newList() {
+        RequiredArgumentOptionSpec<String> optionA = new RequiredArgumentOptionSpec<>( "a" );
+        OptionSet options = new OptionSet( Collections.<String, AbstractOptionSpec<?>> emptyMap() );
+        options.addWithArgument( optionA, "anArgument" );
+        options.addWithArgument( optionA, "anotherArgument" );
+        return options.valuesOf( optionA );
+    }
+
+    @Override
+    protected String newItem() {
+        return "yetAnotherArgument";
+    }
+
+    @Override
+    protected String containedItem() {
+        return "anArgument";
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/OptionSpecBuilderOptionsImmutabilityTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/OptionSpecBuilderOptionsImmutabilityTest.java
new file mode 100644
index 0000000..231e690
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/OptionSpecBuilderOptionsImmutabilityTest.java
@@ -0,0 +1,38 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import static java.util.Arrays.*;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class OptionSpecBuilderOptionsImmutabilityTest extends AbstractOptionSpecOptionsImmutabilityTestCase {
+    @Override
+    protected AbstractOptionSpec<?> newOptionSpec( String option ) {
+        return new OptionSpecBuilder( new OptionParser(), asList( option ), "" );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/OptionSpecBuilderValuesImmutabilityTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/OptionSpecBuilderValuesImmutabilityTest.java
new file mode 100644
index 0000000..3adc17d
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/OptionSpecBuilderValuesImmutabilityTest.java
@@ -0,0 +1,58 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import static java.util.Arrays.*;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class OptionSpecBuilderValuesImmutabilityTest extends AbstractOptionSpecValuesImmutabilityTestCase<Void> {
+    @Override
+    protected AbstractOptionSpec<Void> newOptionSpec() {
+        return new OptionSpecBuilder( new OptionParser(), asList( "a" ), "" );
+    }
+
+    @Override
+    protected String firstArg() {
+        return "Q";
+    }
+
+    @Override
+    protected String secondArg() {
+        return "W";
+    }
+
+    @Override
+    protected Void newItem() {
+        return null;
+    }
+
+    @Override
+    protected Void containedItem() {
+        return null;
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/OptionSpecTokenizerTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/OptionSpecTokenizerTest.java
new file mode 100644
index 0000000..6e40307
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/OptionSpecTokenizerTest.java
@@ -0,0 +1,205 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import java.util.NoSuchElementException;
+
+import static java.util.Collections.*;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import static org.infinitest.toolkit.CollectionMatchers.*;
+import static org.junit.Assert.*;
+import static org.junit.rules.ExpectedException.*;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class OptionSpecTokenizerTest {
+    @Rule public final ExpectedException thrown = none();
+
+    @Test
+    public void tokenizeEmpty() {
+        assertNoMoreTokens( new OptionSpecTokenizer( "" ) );
+    }
+
+    @Test
+    public void tokenizeOptionsWithoutArguments() {
+        OptionSpecTokenizer lexer = new OptionSpecTokenizer( "ab" );
+        assertNextTokenTakesNoArgument( lexer, 'a', false );
+        assertNextTokenTakesNoArgument( lexer, 'b', false );
+        assertNoMoreTokens( lexer );
+    }
+
+    @Test
+    public void tokenizeOptionsWithRequiredArguments() {
+        OptionSpecTokenizer lexer = new OptionSpecTokenizer( "c:d:" );
+        assertNextTokenRequiresAnArgument( lexer, 'c', false );
+        assertNextTokenRequiresAnArgument( lexer, 'd', false );
+        assertNoMoreTokens( lexer );
+    }
+
+    @Test
+    public void tokenizeOptionsWithOptionalArguments() {
+        OptionSpecTokenizer lexer = new OptionSpecTokenizer( "e::f::" );
+        assertNextTokenTakesAnOptionalArgument( lexer, 'e', false );
+        assertNextTokenTakesAnOptionalArgument( lexer, 'f', false );
+        assertNoMoreTokens( lexer );
+    }
+
+    @Test
+    public void tokenizeOptionsWithMixtureOfSpecTypes() {
+        OptionSpecTokenizer lexer = new OptionSpecTokenizer( "gh:i::j" );
+        assertNextTokenTakesNoArgument( lexer, 'g', false );
+        assertNextTokenRequiresAnArgument( lexer, 'h', false );
+        assertNextTokenTakesAnOptionalArgument( lexer, 'i', false );
+        assertNextTokenTakesNoArgument( lexer, 'j', false );
+        assertNoMoreTokens( lexer );
+    }
+
+    @Test
+    public void wByItself() {
+        OptionSpecTokenizer lexer = new OptionSpecTokenizer( "W" );
+        assertNextTokenTakesNoArgument( lexer, 'W', false );
+        assertNoMoreTokens( lexer );
+    }
+
+    @Test
+    public void wRequiredArg() {
+        OptionSpecTokenizer lexer = new OptionSpecTokenizer( "W:" );
+        assertNextTokenRequiresAnArgument( lexer, 'W', false );
+        assertNoMoreTokens( lexer );
+    }
+
+    @Test
+    public void wOptionalArg() {
+        OptionSpecTokenizer lexer = new OptionSpecTokenizer( "W::" );
+        assertNextTokenTakesAnOptionalArgument( lexer, 'W', false );
+        assertNoMoreTokens( lexer );
+    }
+
+    @Test
+    public void alternativeLongOptionsMarker() {
+        OptionSpecTokenizer lexer = new OptionSpecTokenizer( "W;" );
+        assertNextTokenRequiresAnArgument( lexer, 'W', false );
+        assertNoMoreTokens( lexer );
+    }
+
+    @Test
+    public void tokenizeOptionsWithoutArgumentsAndHelpMarker() {
+        OptionSpecTokenizer lexer = new OptionSpecTokenizer( "ab*" );
+        assertNextTokenTakesNoArgument( lexer, 'a', false );
+        assertNextTokenTakesNoArgument( lexer, 'b', true );
+        assertNoMoreTokens( lexer );
+    }
+
+    @Test
+    public void tokenizeOptionsWithRequiredArgumentsAndHelpMarker() {
+        OptionSpecTokenizer lexer = new OptionSpecTokenizer( "c*:d:" );
+        assertNextTokenRequiresAnArgument( lexer, 'c', true );
+        assertNextTokenRequiresAnArgument( lexer, 'd', false );
+        assertNoMoreTokens( lexer );
+    }
+
+    @Test
+    public void tokenizeOptionsWithOptionalArgumentsAndHelpMarker() {
+        OptionSpecTokenizer lexer = new OptionSpecTokenizer( "e*::f*::" );
+        assertNextTokenTakesAnOptionalArgument( lexer, 'e', true );
+        assertNextTokenTakesAnOptionalArgument( lexer, 'f', true );
+        assertNoMoreTokens( lexer );
+    }
+
+    @Test
+    public void tokenizeOptionsWithMixtureOfSpecTypesAndHelpMarker() {
+        OptionSpecTokenizer lexer = new OptionSpecTokenizer( "g*h:i*::j" );
+        assertNextTokenTakesNoArgument( lexer, 'g', true );
+        assertNextTokenRequiresAnArgument( lexer, 'h', false );
+        assertNextTokenTakesAnOptionalArgument( lexer, 'i', true );
+        assertNextTokenTakesNoArgument( lexer, 'j', false );
+        assertNoMoreTokens( lexer );
+    }
+
+    @Test
+    public void wByItselfWithHelpMarker() {
+        OptionSpecTokenizer lexer = new OptionSpecTokenizer( "W*" );
+        assertNextTokenTakesNoArgument( lexer, 'W', true );
+        assertNoMoreTokens( lexer );
+    }
+
+    @Test
+    public void wRequiredArgWithHelpMarker() {
+        OptionSpecTokenizer lexer = new OptionSpecTokenizer( "W*:" );
+        assertNextTokenRequiresAnArgument( lexer, 'W', true );
+        assertNoMoreTokens( lexer );
+    }
+
+    @Test
+    public void wOptionalArgWithHelpMarker() {
+        OptionSpecTokenizer lexer = new OptionSpecTokenizer( "W*::" );
+        assertNextTokenTakesAnOptionalArgument( lexer, 'W', true );
+        assertNoMoreTokens( lexer );
+    }
+
+    @Test
+    public void alternativeLongOptionsMarkerWithHelpMarker() {
+        OptionSpecTokenizer lexer = new OptionSpecTokenizer( "W*;" );
+        assertNextTokenTakesNoArgument( lexer, 'W', true );
+    }
+
+    private void assertNoMoreTokens( OptionSpecTokenizer lexer ) {
+        assertFalse( lexer.hasMore() );
+
+        thrown.expect( NoSuchElementException.class );
+        lexer.next();
+    }
+
+    private static void assertNextTokenTakesNoArgument( OptionSpecTokenizer lexer, char option, boolean forHelp ) {
+        assertNextToken( lexer, option, false, false, forHelp );
+    }
+
+    private static void assertNextTokenRequiresAnArgument( OptionSpecTokenizer lexer, char option, boolean forHelp ) {
+        assertNextToken( lexer, option, true, true, forHelp );
+    }
+
+    private static void assertNextTokenTakesAnOptionalArgument( OptionSpecTokenizer lexer, char option,
+        boolean forHelp ) {
+
+        assertNextToken( lexer, option, true, false, forHelp );
+    }
+
+    private static void assertNextToken( OptionSpecTokenizer lexer, char option, boolean acceptsArguments,
+        boolean requiresArgument, boolean forHelp ) {
+
+        assertTrue( "no more tokens?", lexer.hasMore() );
+        AbstractOptionSpec<?> spec = lexer.next();
+        assertThat( "option?", spec.options(), hasSameContentsAs( singleton( String.valueOf( option ) ) ) );
+        assertEquals( "accepts args?", acceptsArguments, spec.acceptsArguments() );
+        assertEquals( "requires arg?", requiresArgument, spec.requiresArgument() );
+        assertEquals( "for help?", forHelp, spec.isForHelp() );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/OptionSpecsReportTheirOptionsSortedSpeciallyTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/OptionSpecsReportTheirOptionsSortedSpeciallyTest.java
new file mode 100644
index 0000000..ff531dd
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/OptionSpecsReportTheirOptionsSortedSpeciallyTest.java
@@ -0,0 +1,49 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static java.util.Arrays.*;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class OptionSpecsReportTheirOptionsSortedSpeciallyTest {
+    @Test
+    public void shortOptionsComeFirstFollowedByLongOptionsLexicographically() {
+        AbstractOptionSpec<Void> spec = new NoArgumentOptionSpec( asList( "after-date", "n", "N", "past" ), "" );
+
+        List<String> actualOptions = new ArrayList<>( spec.options() );
+
+        assertEquals( asList( "N", "n", "after-date", "past" ), actualOptions );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/OptionSynonymCommonPrefixesTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/OptionSynonymCommonPrefixesTest.java
new file mode 100644
index 0000000..986761d
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/OptionSynonymCommonPrefixesTest.java
@@ -0,0 +1,88 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import static java.util.Arrays.*;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class OptionSynonymCommonPrefixesTest extends AbstractOptionParserFixture {
+    @Before
+    public final void initializeParser() {
+        parser.acceptsAll( asList( "v", "verbose" ) );
+    }
+
+    @Test
+    public void parsingFirstPrefix() {
+        assertDetections( "-v" );
+    }
+
+    @Test
+    public void parsingSecondPrefix() {
+        assertDetections( "-ve" );
+    }
+
+    @Test
+    public void parsingThirdPrefix() {
+        assertDetections( "-ver" );
+    }
+
+    @Test
+    public void parsingFourthPrefix() {
+        assertDetections( "-verb" );
+    }
+
+    @Test
+    public void parsingFifthPrefix() {
+        assertDetections( "-verbo" );
+    }
+
+    @Test
+    public void parsingSixthPrefix() {
+        assertDetections( "-verbos" );
+    }
+
+    @Test
+    public void parsingSeventhPrefix() {
+        assertDetections( "-verbose" );
+    }
+
+    private void assertDetections( String option ) {
+        OptionSet options = parser.parse( option );
+
+        assertOptionDetected( options, "v" );
+        assertOptionNotDetected( options, "ve" );
+        assertOptionNotDetected( options, "ver" );
+        assertOptionNotDetected( options, "verb" );
+        assertOptionNotDetected( options, "verbo" );
+        assertOptionNotDetected( options, "verbos" );
+        assertOptionDetected( options, "verbose" );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/OptionSynonymNoArgumentTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/OptionSynonymNoArgumentTest.java
new file mode 100644
index 0000000..2b66176
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/OptionSynonymNoArgumentTest.java
@@ -0,0 +1,71 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import static java.util.Arrays.*;
+import static java.util.Collections.*;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class OptionSynonymNoArgumentTest extends AbstractOptionParserFixture {
+    @Before
+    public final void initializeParser() {
+        parser.acceptsAll( asList( "N", "after-date", "newer" ) );
+    }
+
+    @Test
+    public void hasAllSynonymsWhenFirstSynonymParsed() {
+        assertDetections( "-N" );
+    }
+
+    @Test
+    public void hasAllSynonymsWhenSecondSynonymParsed() {
+        assertDetections( "--after-d" );
+    }
+
+    @Test
+    public void hasAllSynonymsWhenThirdSynonymParsed() {
+        assertDetections( "--n" );
+    }
+
+    private void assertDetections( String option ) {
+        OptionSet options = parser.parse( option );
+
+        assertOptionDetected( options, "N" );
+        assertOptionDetected( options, "after-date" );
+        assertOptionDetected( options, "newer" );
+        assertEquals( emptyList(), options.valuesOf( "N" ) );
+        assertEquals( emptyList(), options.valuesOf( "after-date" ) );
+        assertEquals( emptyList(), options.valuesOf( "newer" ) );
+        assertEquals( emptyList(), options.nonOptionArguments() );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/OptionSynonymOptionalArgumentTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/OptionSynonymOptionalArgumentTest.java
new file mode 100644
index 0000000..5ff03e5
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/OptionSynonymOptionalArgumentTest.java
@@ -0,0 +1,80 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import java.io.File;
+import java.util.List;
+
+import static java.util.Arrays.*;
+import static java.util.Collections.*;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class OptionSynonymOptionalArgumentTest extends AbstractOptionParserFixture {
+    private String optionArgument;
+
+    @Before
+    public final void initializeParser() {
+        parser.acceptsAll( asList( "d", "output-dir", "folder" ), "file" ).withRequiredArg().ofType( File.class );
+        optionArgument = "tmp";
+    }
+
+    @Test
+    public void hasAllSynonymsWhenFirstSynonymParsed() {
+        assertDetections( new String[] { "-d", optionArgument }, singletonList( new File( optionArgument ) ) );
+    }
+
+    @Test
+    public void hasAllSynonymsWhenSecondSynonymParsed() {
+        assertDetections( new String[] { "--ou", optionArgument }, singletonList( new File( optionArgument ) ) );
+    }
+
+    @Test
+    public void hasAllSynonymsWhenThirdSynonymParsed() {
+        assertDetections( new String[] { "--fold", optionArgument }, singletonList( new File( optionArgument ) ) );
+    }
+
+    @Test
+    public void reportsSameListOfArgumentsForEverySynonymOption() {
+        assertDetections(
+            new String[] { "-d", "opt", "--output-dir", "tmp", "--folder", "usr" },
+            asList( new File( "opt" ), new File( "tmp" ), new File( "usr" ) ) );
+    }
+
+    private void assertDetections( String[] args, List<?> optionArguments ) {
+        OptionSet options = parser.parse( args );
+        assertEquals( optionArguments, options.valuesOf( "d" ) );
+        assertEquals( optionArguments, options.valuesOf( "output-dir" ) );
+        assertEquals( optionArguments, options.valuesOf( "folder" ) );
+        assertEquals( emptyList(), options.nonOptionArguments() );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/OptionSynonymOverwritingTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/OptionSynonymOverwritingTest.java
new file mode 100644
index 0000000..3294d34
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/OptionSynonymOverwritingTest.java
@@ -0,0 +1,51 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import static java.util.Arrays.*;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class OptionSynonymOverwritingTest extends AbstractOptionParserFixture {
+    @Before
+    public final void initializeParser() {
+        parser.acceptsAll( asList( "h", "help" ) );
+        parser.acceptsAll( asList( "h", "oops" ) );
+    }
+
+    @Test
+    public void treatsHAsThoughItWereLastDefinedSynonymOfH() {
+        OptionSet options = parser.parse( "-h" );
+
+        assertOptionDetected( options, "h" );
+        assertOptionDetected( options, "oops" );
+        assertOptionNotDetected( options, "help" );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/OptionSynonymRequiredArgumentTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/OptionSynonymRequiredArgumentTest.java
new file mode 100644
index 0000000..10d7889
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/OptionSynonymRequiredArgumentTest.java
@@ -0,0 +1,82 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import java.util.List;
+
+import static java.util.Arrays.*;
+import static java.util.Collections.*;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class OptionSynonymRequiredArgumentTest extends AbstractOptionParserFixture {
+    private String optionArgument;
+
+    @Before
+    public final void initializeParser() {
+        parser.acceptsAll( asList( "N", "after-date", "newer" ), "date" ).withRequiredArg().ofType( Integer.class );
+        optionArgument = "2000";
+    }
+
+    @Test
+    public void hasAllSynonymsWhenFirstSynonymParsed() {
+        assertDetections( new String[] { "-N", optionArgument }, singletonList( Integer.valueOf( optionArgument ) ) );
+    }
+
+    @Test
+    public void hasAllSynonymsWhenSecondSynonymParsed() {
+        assertDetections(
+            new String[] { "--after-date", optionArgument },
+            singletonList( Integer.valueOf( optionArgument ) ) );
+    }
+
+    @Test
+    public void hasAllSynonymsWhenThirdSynonymParsed() {
+        assertDetections(
+            new String[] { "--newer", optionArgument },
+            singletonList( Integer.valueOf( optionArgument ) ) );
+    }
+
+    @Test
+    public void reportsSameListOfArgumentsForEverySynonymOption() {
+        assertDetections( new String[] { "-N", "1", "-aft", "2", "--ne", "3" }, asList( 1, 2, 3 ) );
+    }
+
+    private void assertDetections( String[] args, List<?> optionArguments ) {
+        OptionSet options = parser.parse( args );
+
+        assertEquals( optionArguments, options.valuesOf( "N" ) );
+        assertEquals( optionArguments, options.valuesOf( "after-date" ) );
+        assertEquals( optionArguments, options.valuesOf( "newer" ) );
+        assertEquals( emptyList(), options.nonOptionArguments() );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/OptionalArgumentOptionSpecOptionsImmutabilityTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/OptionalArgumentOptionSpecOptionsImmutabilityTest.java
new file mode 100644
index 0000000..86765a5
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/OptionalArgumentOptionSpecOptionsImmutabilityTest.java
@@ -0,0 +1,36 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class OptionalArgumentOptionSpecOptionsImmutabilityTest extends AbstractOptionSpecOptionsImmutabilityTestCase {
+    @Override
+    protected AbstractOptionSpec<?> newOptionSpec( String option ) {
+        return new OptionalArgumentOptionSpec<Integer>( option );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/OptionalArgumentOptionSpecTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/OptionalArgumentOptionSpecTest.java
new file mode 100644
index 0000000..6f5c275
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/OptionalArgumentOptionSpecTest.java
@@ -0,0 +1,53 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import static java.util.Arrays.*;
+
+import org.junit.Test;
+
+import static org.infinitest.toolkit.CollectionMatchers.*;
+import static org.junit.Assert.*;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class OptionalArgumentOptionSpecTest extends AbstractOptionSpecFixture {
+    @Override
+    protected OptionalArgumentOptionSpec<?> createEqualOptionSpecInstance() {
+        return new OptionalArgumentOptionSpec<Void>( "d" );
+    }
+
+    @Override
+    protected OptionalArgumentOptionSpec<?> createNotEqualOptionSpecInstance() {
+        return new OptionalArgumentOptionSpec<Void>( "c" );
+    }
+
+    @Test
+    public void optionsContents() {
+        assertThat( createNotEqualOptionSpecInstance().options(), hasSameContentsAs( asList( "c" ) ) );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/OptionalArgumentOptionSpecValuesImmutabilityTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/OptionalArgumentOptionSpecValuesImmutabilityTest.java
new file mode 100644
index 0000000..841b946
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/OptionalArgumentOptionSpecValuesImmutabilityTest.java
@@ -0,0 +1,58 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class OptionalArgumentOptionSpecValuesImmutabilityTest
+    extends AbstractOptionSpecValuesImmutabilityTestCase<Integer> {
+
+    @Override
+    protected AbstractOptionSpec<Integer> newOptionSpec() {
+        return new OptionalArgumentOptionSpec<Integer>( "life" ).ofType( Integer.class );
+    }
+
+    @Override
+    protected String firstArg() {
+        return "1";
+    }
+
+    @Override
+    protected String secondArg() {
+        return "2";
+    }
+
+    @Override
+    protected Integer newItem() {
+        return 3;
+    }
+
+    @Override
+    protected Integer containedItem() {
+        return 2;
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/ParserRulesUtilityClassTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/ParserRulesUtilityClassTest.java
new file mode 100644
index 0000000..82317be
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/ParserRulesUtilityClassTest.java
@@ -0,0 +1,35 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class ParserRulesUtilityClassTest extends UtilityClassesUninstantiabilityHarness {
+    public ParserRulesUtilityClassTest() {
+        super( ParserRules.class );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/ParsingSeparatedConvertedOptionValuesTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/ParsingSeparatedConvertedOptionValuesTest.java
new file mode 100644
index 0000000..4e18003
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/ParsingSeparatedConvertedOptionValuesTest.java
@@ -0,0 +1,82 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import java.io.File;
+
+import static java.util.Arrays.*;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class ParsingSeparatedConvertedOptionValuesTest extends AbstractOptionParserFixture {
+    @Test
+    public void parsesSeparatedValuesAsSeparateArgument() {
+        assertCorrectParse( "classpath", ':', "--classpath", "/usr:/opt:/var" );
+    }
+
+    @Test
+    public void parsesSeparatedValuesWhenArgumentAbuttedWithEquals() {
+        assertCorrectParse( "classpath", ':', "--classpath=/usr:/opt:/var" );
+    }
+
+    @Test
+    public void parsesEqualsSeparatedValuesWhenArgumentAbuttedWithEquals() {
+        assertCorrectParse( "classpath", '=', "--classpath=/usr=/opt=/var" );
+    }
+
+    @Test
+    public void parsesSeparatedValuesAbutting() {
+        assertCorrectParse( "c", ':', "-c/usr:/opt:/var" );
+    }
+
+    private void assertCorrectParse( String option, char separator, String... args ) {
+        parser.accepts( option ).withRequiredArg().withValuesSeparatedBy( separator )
+            .withValuesConvertedBy( new ValueConverter<File>() {
+                public File convert( String value ) {
+                    return new File( value );
+                }
+
+                public Class<File> valueType() {
+                    return File.class;
+                }
+
+                public String valuePattern() {
+                    return null;
+                }
+            } );
+
+        OptionSet options = parser.parse( args );
+
+        assertEquals(
+            asList( new File( "/usr" ), new File( "/opt" ), new File( "/var" ) ),
+            options.valuesOf( option ) );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/ParsingSeparatedStringOptionValuesTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/ParsingSeparatedStringOptionValuesTest.java
new file mode 100644
index 0000000..6cfe17f
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/ParsingSeparatedStringOptionValuesTest.java
@@ -0,0 +1,75 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import static java.util.Arrays.*;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class ParsingSeparatedStringOptionValuesTest extends AbstractOptionParserFixture {
+    @Test
+    public void parsesSeparatedValuesAsSeparateArgument() {
+        assertCorrectParse( "classpath", ":::", "--classpath", "/usr:::/opt:::/var" );
+    }
+
+    @Test
+    public void parsesSeparatedValuesWhenArgumentAbuttedWithEquals() {
+        assertCorrectParse( "classpath", ":", "--classpath=/usr:/opt:/var" );
+    }
+
+    @Test
+    public void parsesEqualsSeparatedValuesWhenArgumentAbuttedWithEquals() {
+        assertCorrectParse( "classpath", ":=", "--classpath=/usr:=/opt:=/var" );
+    }
+
+    @Test
+    public void parsesSeparatedValuesAbutting() {
+        assertCorrectParse( "c", ";", "-c/usr;/opt;/var" );
+    }
+
+    @Test
+    public void parsesHyphenSeparatedValuesAbutting() {
+        assertCorrectParse( "c", "--", "-c/usr--/opt--/var" );
+    }
+
+    @Test
+    public void handlesConsecutiveDelimiters() {
+        assertCorrectParse( "c", ",", "-c,/usr,,,/opt,,,/var,,,,," );
+    }
+
+    private void assertCorrectParse( String option, String separator, String... args ) {
+        parser.accepts( option ).withRequiredArg().withValuesSeparatedBy( separator );
+
+        OptionSet options = parser.parse( args );
+
+        assertEquals( asList( "/usr", "/opt", "/var" ), options.valuesOf( option ) );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/ParsingSeparatedTypedOptionValuesTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/ParsingSeparatedTypedOptionValuesTest.java
new file mode 100644
index 0000000..0cfc1e9
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/ParsingSeparatedTypedOptionValuesTest.java
@@ -0,0 +1,69 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import java.io.File;
+
+import static java.util.Arrays.*;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class ParsingSeparatedTypedOptionValuesTest extends AbstractOptionParserFixture {
+    @Test
+    public void parsesSeparatedValuesAsSeparateArgument() {
+        assertCorrectParse( "classpath", ':', "--classpath", "/usr:/opt:/var" );
+    }
+
+    @Test
+    public void parsesSeparatedValuesWhenArgumentAbuttedWithEquals() {
+        assertCorrectParse( "classpath", ':', "--classpath=/usr:/opt:/var" );
+    }
+
+    @Test
+    public void parsesEqualsSeparatedValuesWhenArgumentAbuttedWithEquals() {
+        assertCorrectParse( "classpath", '=', "--classpath=/usr=/opt=/var" );
+    }
+
+    @Test
+    public void parsesSeparatedValuesAbutting() {
+        assertCorrectParse( "c", ':', "-c/usr:/opt:/var" );
+    }
+
+    private void assertCorrectParse( String option, char separator, String... args ) {
+        parser.accepts( option ).withRequiredArg().ofType( File.class ).withValuesSeparatedBy( separator );
+
+        OptionSet options = parser.parse( args );
+
+        assertEquals(
+            asList( new File( "/usr" ), new File( "/opt" ), new File( "/var" ) ),
+            options.valuesOf( option ) );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/PlainOldOptionParserTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/PlainOldOptionParserTest.java
new file mode 100644
index 0000000..3fe8f50
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/PlainOldOptionParserTest.java
@@ -0,0 +1,46 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import static java.util.Arrays.*;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class PlainOldOptionParserTest extends AbstractOptionParserFixture {
+    @Test
+    public void nonOptionsOnly() {
+        String[] arguments = { "foo", "bar" };
+
+        OptionSet options = parser.parse( arguments );
+
+        assertEquals( asList( arguments ), options.nonOptionArguments() );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/PopulatedOptionSetTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/PopulatedOptionSetTest.java
new file mode 100644
index 0000000..5ac5036
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/PopulatedOptionSetTest.java
@@ -0,0 +1,72 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package joptsimple;
+
+import java.util.Collections;
+
+import static java.util.Collections.*;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class PopulatedOptionSetTest {
+    private OptionSet populated;
+
+    @Before
+    public void setUp() {
+        populated = new OptionSet( Collections.<String, AbstractOptionSpec<?>> emptyMap() );
+        populated.add( new NoArgumentOptionSpec( "a" ) );
+        populated.addWithArgument( new RequiredArgumentOptionSpec<String>( "b" ), "arg-of-b" );
+    }
+
+    @Test
+    public void hasArgument() {
+        assertFalse( populated.hasArgument( "a" ) );
+        assertTrue( populated.hasArgument( "b" ) );
+    }
+
+    @Test
+    public void valueOf() {
+        assertNull( populated.valueOf( "a" ) );
+        assertEquals( "arg-of-b", populated.valueOf( "b" ) );
+    }
+
+    @Test
+    public void valuesOf() {
+        assertEquals( emptyList(), populated.valuesOf( "a" ) );
+        assertEquals( singletonList( "arg-of-b" ), populated.valuesOf( "b" ) );
+    }
+
+    @Test
+    public void hasOptions() {
+        assertTrue( populated.hasOptions() );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/PosixlyCorrectOptionParserTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/PosixlyCorrectOptionParserTest.java
new file mode 100644
index 0000000..027c0a2
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/PosixlyCorrectOptionParserTest.java
@@ -0,0 +1,74 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import java.util.Collection;
+
+import static java.util.Arrays.*;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import static org.junit.Assert.*;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+@RunWith( Parameterized.class )
+public class PosixlyCorrectOptionParserTest {
+    private final OptionParser parser;
+
+    public PosixlyCorrectOptionParserTest( OptionParser parser ) {
+        this.parser = parser;
+    }
+
+    @Parameterized.Parameters
+    public static Collection<?> parsers() {
+        return asList( new Object[] {
+            new OptionParser() { {
+                posixlyCorrect( true );
+                accepts( "i" ).withRequiredArg();
+                accepts( "j" ).withOptionalArg();
+                accepts( "k" );
+            } } },
+            new Object[] { new OptionParser( "+i:j::k" ) } );
+    }
+
+    @Test
+    public void parseWithPosixlyCorrect() {
+        OptionSet options =
+            parser.parse( "-ibar", "-i", "junk", "xyz", "-jixnay", "foo", "-k", "blah", "--", "yermom" );
+
+        assertTrue( "i?", options.has( "i" ) );
+        assertFalse( "j?", options.has( "j" ) );
+        assertFalse( "k?", options.has( "k" ) );
+        assertEquals( "args of i?", asList( "bar", "junk" ), options.valuesOf( "i" ) );
+        assertEquals( "non-option args?",
+            asList( "xyz", "-jixnay", "foo", "-k", "blah", "--", "yermom" ),
+            options.nonOptionArguments() );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/PunctuationInLongOptionTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/PunctuationInLongOptionTest.java
new file mode 100644
index 0000000..94e4e23
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/PunctuationInLongOptionTest.java
@@ -0,0 +1,73 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class PunctuationInLongOptionTest extends AbstractOptionParserFixture {
+    @Test
+    public void allowsHyphensInTheMiddleOfOptionNames() {
+        assertOptionAllowed( "hyphenated-option" );
+    }
+
+    @Test
+    public void allowsHyphensPrettyMuchAnywhereButTheFront() {
+        assertOptionAllowed( "a-cra-zy-but-le--gal---o-p-t-i-o--n-----" );
+    }
+
+    @Test( expected = IllegalOptionSpecificationException.class )
+    public void disallowsHyphensAtFrontOfOptionName() {
+        parser.accepts( "-weird-option" );
+    }
+
+    @Test
+    public void allowsDotInOptionNames() {
+        assertOptionAllowed( "..what.is.the.d..e...a....l..." );
+    }
+
+    @Test
+    public void allowsQuestionMarkInOptionNames() {
+        assertOptionAllowed( "??who?k??n???e????w?" );
+    }
+
+    @Test
+    public void allowsUnderscoreInOptionNames() {
+        assertOptionAllowed( "super_long_option_name" );
+    }
+
+    private void assertOptionAllowed( String option ) {
+        parser.accepts( option );
+
+        OptionSet options = parser.parse( "--" + option );
+
+        assertTrue( options.has( option ) );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/PunctuationInShortOptionTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/PunctuationInShortOptionTest.java
new file mode 100644
index 0000000..20565b9
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/PunctuationInShortOptionTest.java
@@ -0,0 +1,61 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class PunctuationInShortOptionTest {
+    @Test( expected = IllegalOptionSpecificationException.class )
+    public void disallowsHyphenAsShortOption() {
+        new OptionParser( "-" );
+    }
+
+    @Test
+    public void allowsQuestionMarkAsShortOption() {
+        assertOptionAllowed( "?" );
+    }
+
+    @Test
+    public void allowsDotAsShortOption() {
+        assertOptionAllowed( "." );
+    }
+
+    @Test
+    public void allowsUnderscoreAsShortOption() {
+        assertOptionAllowed( "_" );
+    }
+
+    private void assertOptionAllowed( String option ) {
+        OptionSet options = new OptionParser( option ).parse( '-' + option );
+
+        assertTrue( options.has( option ) );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/RequiredArgumentOptionSpecEqualsHashCodeTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/RequiredArgumentOptionSpecEqualsHashCodeTest.java
new file mode 100644
index 0000000..9235d5c
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/RequiredArgumentOptionSpecEqualsHashCodeTest.java
@@ -0,0 +1,41 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class RequiredArgumentOptionSpecEqualsHashCodeTest extends AbstractOptionSpecFixture {
+    @Override
+    protected RequiredArgumentOptionSpec<?> createEqualOptionSpecInstance() {
+        return new RequiredArgumentOptionSpec<Void>( "a" );
+    }
+
+    @Override
+    protected RequiredArgumentOptionSpec<?> createNotEqualOptionSpecInstance() {
+        return new RequiredArgumentOptionSpec<Void>( "b" );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/RequiredArgumentOptionSpecOptionsImmutabilityTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/RequiredArgumentOptionSpecOptionsImmutabilityTest.java
new file mode 100644
index 0000000..e834d1b
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/RequiredArgumentOptionSpecOptionsImmutabilityTest.java
@@ -0,0 +1,36 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class RequiredArgumentOptionSpecOptionsImmutabilityTest extends AbstractOptionSpecOptionsImmutabilityTestCase {
+    @Override
+    protected AbstractOptionSpec<?> newOptionSpec( String option ) {
+        return new RequiredArgumentOptionSpec<String>( option );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/RequiredArgumentOptionSpecValuesImmutabilityTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/RequiredArgumentOptionSpecValuesImmutabilityTest.java
new file mode 100644
index 0000000..f75a068
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/RequiredArgumentOptionSpecValuesImmutabilityTest.java
@@ -0,0 +1,58 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class RequiredArgumentOptionSpecValuesImmutabilityTest
+    extends AbstractOptionSpecValuesImmutabilityTestCase<String> {
+
+    @Override
+    protected AbstractOptionSpec<String> newOptionSpec() {
+        return new RequiredArgumentOptionSpec<>( "file" );
+    }
+
+    @Override
+    protected String firstArg() {
+        return "arg1";
+    }
+
+    @Override
+    protected String secondArg() {
+        return "arg2";
+    }
+
+    @Override
+    protected String newItem() {
+        return "arg3";
+    }
+
+    @Override
+    protected String containedItem() {
+        return "arg1";
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/RequiredIfAnyTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/RequiredIfAnyTest.java
new file mode 100644
index 0000000..75c39d8
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/RequiredIfAnyTest.java
@@ -0,0 +1,86 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import static java.util.Collections.*;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class RequiredIfAnyTest extends AbstractOptionParserFixture {
+    @Before
+    public void configureParser() {
+        parser.accepts( "a" );
+        parser.accepts( "b" );
+        OptionSpec<Void> c = parser.accepts( "c" );
+        OptionSpec<Void> d = parser.accepts( "d" );
+        parser.accepts( "e" );
+        parser.accepts( "n" ).requiredIf( "a", "b" ).requiredIf( c, d );
+    }
+
+    @Test
+    public void rejectsCommandLineMissingConditionallyRequiredOption() {
+        thrown.expect( MissingRequiredOptionsException.class );
+
+        parser.parse( "-a" );
+    }
+
+    @Test
+    public void rejectsCommandLineMissingOtherConditionallyRequiredOption() {
+        thrown.expect( MissingRequiredOptionsException.class );
+
+        parser.parse( "-b" );
+    }
+
+    @Test
+    public void rejectsCommandLineWithNotAllConditionallyRequiredOptionsPresent() {
+        thrown.expect( MissingRequiredOptionsException.class );
+
+        parser.parse( "-a", "-b", "-c", "-d" );
+    }
+
+    @Test
+    public void acceptsCommandLineWithConditionallyRequiredOptionsPresent() {
+        OptionSet options = parser.parse( "-b", "-n" );
+        
+        assertOptionDetected( options, "b" );
+        assertOptionDetected( options, "n" );
+        assertEquals( emptyList(), options.nonOptionArguments() );
+    }
+
+    @Test
+    public void acceptsOptionWithPrerequisiteAsNormalIfPrerequisiteNotInPlay() {
+        OptionSet options = parser.parse( "-n" );
+
+        assertOptionDetected( options, "n" );
+        assertEquals( emptyList(), options.nonOptionArguments() );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/RequiredIfTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/RequiredIfTest.java
new file mode 100644
index 0000000..d5cbb17
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/RequiredIfTest.java
@@ -0,0 +1,112 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import static java.util.Arrays.*;
+import static java.util.Collections.*;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class RequiredIfTest extends AbstractOptionParserFixture {
+    @Before
+    public void configureParser() {
+        OptionSpec<Void> ftp = parser.acceptsAll( asList( "ftp", "file-transfer" ) );
+        parser.acceptsAll( asList( "username", "userid" ) ).requiredIf( "file-transfer" ).withRequiredArg();
+        parser.acceptsAll( asList( "password", "pwd" ) ).requiredIf( ftp ).withRequiredArg();
+        parser.accepts( "?" ).forHelp();
+    }
+
+    @Test
+    public void rejectsCommandLineMissingConditionallyRequiredOption() {
+        thrown.expect( MissingRequiredOptionsException.class );
+
+        parser.parse( "--ftp" );
+    }
+
+    @Test
+    public void rejectsCommandLineMissingConditionallyRequiredOptionSynonym() {
+        thrown.expect( MissingRequiredOptionsException.class );
+
+        parser.parse( "--file-transfer" );
+    }
+
+    @Test
+    public void rejectsCommandLineWithNotAllConditionallyRequiredOptionsPresent() {
+        thrown.expect( MissingRequiredOptionsException.class );
+
+        parser.parse( "--ftp", "--username", "joe" );
+    }
+
+    @Test
+    public void acceptsCommandLineWithConditionallyRequiredOptionsPresent() {
+        OptionSet options = parser.parse( "--ftp", "--userid", "joe", "--password=secret" );
+
+        assertOptionDetected( options, "ftp" );
+        assertOptionDetected( options, "username" );
+        assertOptionDetected( options, "password" );
+        assertEquals( singletonList( "joe" ), options.valuesOf( "username" ) );
+        assertEquals( singletonList( "secret" ), options.valuesOf( "password" ) );
+        assertEquals( emptyList(), options.nonOptionArguments() );
+    }
+
+    @Test
+    public void acceptsOptionWithPrerequisiteAsNormalIfPrerequisiteNotInPlay() {
+        OptionSet options = parser.parse( "--pwd", "secret" );
+
+        assertOptionDetected( options, "pwd" );
+        assertEquals( singletonList( "secret" ), options.valuesOf( "pwd" ) );
+        assertEquals( emptyList(), options.nonOptionArguments() );
+    }
+
+    @Test
+    public void rejectsOptionNotAlreadyConfigured() {
+        thrown.expect( UnconfiguredOptionException.class );
+
+        parser.accepts( "foo" ).requiredIf( "bar" );
+    }
+
+    @Test
+    public void rejectsOptionSpecNotAlreadyConfigured() {
+        thrown.expect( UnconfiguredOptionException.class );
+
+        parser.accepts( "foo" ).requiredIf( "bar" );
+    }
+
+    @Test
+    public void presenceOfHelpOptionNegatesRequiredIfness() {
+        OptionSet options = parser.parse( "--ftp", "-?" );
+
+        assertOptionDetected( options, "ftp" );
+        assertOptionDetected( options, "?" );
+        assertEquals( emptyList(), options.nonOptionArguments() );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/RequiredUnlessAnyTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/RequiredUnlessAnyTest.java
new file mode 100644
index 0000000..d61dd78
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/RequiredUnlessAnyTest.java
@@ -0,0 +1,78 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import static java.util.Collections.emptyList;
+import static org.junit.Assert.assertEquals;
+
+public class RequiredUnlessAnyTest extends AbstractOptionParserFixture {
+    @Before
+    public void configureParser() {
+        parser.accepts( "a" );
+        parser.accepts( "b" );
+        OptionSpec<Void> c = parser.accepts( "c" );
+        OptionSpec<Void> d = parser.accepts( "d" );
+        parser.accepts( "e" );
+        parser.accepts( "n" ).requiredUnless( "a", "b" ).requiredUnless( c, d );
+    }
+
+    @Test
+    public void commandLineMissingConditionallyRequiredOption() {
+        OptionSet options = parser.parse( "-a" );
+        assertOptionDetected( options, "a" );
+        assertOptionNotDetected( options, "n" );
+    }
+
+    @Test
+    public void commandLineWithNotAllConditionallyRequiredOptionsPresent() {
+        OptionSet options = parser.parse( "-a", "-b", "-c", "-d" );
+        assertOptionDetected( options, "a" );
+        assertOptionDetected( options, "b" );
+        assertOptionDetected( options, "c" );
+        assertOptionDetected( options, "d" );
+        assertOptionNotDetected( options, "n" );
+    }
+
+    @Test
+    public void acceptsCommandLineWithConditionallyRequiredOptionsPresent() {
+        OptionSet options = parser.parse( "-n" );
+
+        assertOptionDetected( options, "n" );
+        assertEquals( emptyList(), options.nonOptionArguments() );
+    }
+
+    @Test
+    public void acceptsOptionWithPrerequisiteAsNormalIfPrerequisiteNotInPlay() {
+        OptionSet options = parser.parse( "-a", "-n" );
+
+        assertOptionDetected( options, "a" );
+        assertOptionDetected( options, "n" );
+        assertEquals( emptyList(), options.nonOptionArguments() );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/RequiredUnlessTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/RequiredUnlessTest.java
new file mode 100644
index 0000000..ed5b47b
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/RequiredUnlessTest.java
@@ -0,0 +1,110 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import static java.util.Arrays.asList;
+import static java.util.Collections.emptyList;
+import static java.util.Collections.singletonList;
+import static org.junit.Assert.assertEquals;
+
+public class RequiredUnlessTest extends AbstractOptionParserFixture {
+    @Before
+    public void configureParser() {
+        OptionSpec<Void> anonymous = parser.accepts( "anonymous" );
+        parser.acceptsAll( asList( "username", "userid" ) ).requiredUnless( "anonymous" ).withRequiredArg();
+        parser.acceptsAll( asList( "password", "pwd" ) ).requiredUnless( anonymous ).withRequiredArg();
+        parser.accepts( "?" ).forHelp();
+    }
+
+    @Test
+    public void rejectsCommandLineMissingConditionallyRequiredOption() {
+        thrown.expect( MissingRequiredOptionsException.class );
+
+        parser.parse( "" );
+    }
+
+    @Test
+    public void rejectsCommandLineWithNotAllConditionallyRequiredOptionsPresent() {
+        thrown.expect( MissingRequiredOptionsException.class );
+
+        parser.parse( "--username", "joe" );
+    }
+
+    @Test
+    public void acceptsCommandLineWithConditionallyRequiredOptionsPresent() {
+        OptionSet options = parser.parse( "--userid", "joe", "--password=secret" );
+
+        assertOptionNotDetected( options, "anonymous" );
+        assertOptionDetected( options, "username" );
+        assertOptionDetected( options, "password" );
+        assertEquals( singletonList( "joe" ), options.valuesOf( "username" ) );
+        assertEquals( singletonList( "secret" ), options.valuesOf( "password" ) );
+        assertEquals( emptyList(), options.nonOptionArguments() );
+    }
+
+    @Test
+    public void acceptsOptionWithPrerequisite() {
+        OptionSet options = parser.parse( "--anonymous" );
+
+        assertOptionDetected( options, "anonymous" );
+        assertEquals( emptyList(), options.nonOptionArguments() );
+    }
+
+    @Test
+    public void acceptsOptionWithPrerequisiteAsNormal() {
+        OptionSet options = parser.parse( "--anonymous", "--pwd", "secret" );
+
+        assertOptionDetected( options, "anonymous" );
+        assertOptionDetected( options, "pwd" );
+        assertEquals( singletonList( "secret" ), options.valuesOf( "pwd" ) );
+        assertEquals( emptyList(), options.nonOptionArguments() );
+    }
+
+    @Test
+    public void rejectsOptionNotAlreadyConfigured() {
+        thrown.expect( UnconfiguredOptionException.class );
+
+        parser.accepts( "foo" ).requiredIf( "bar" );
+    }
+
+    @Test
+    public void rejectsOptionSpecNotAlreadyConfigured() {
+        thrown.expect( UnconfiguredOptionException.class );
+
+        parser.accepts( "foo" ).requiredIf( "bar" );
+    }
+
+    @Test
+    public void presenceOfHelpOptionNegatesRequiredUnlessness() {
+        OptionSet options = parser.parse( "-?" );
+
+        assertOptionDetected( options, "?" );
+        assertEquals( emptyList(), options.nonOptionArguments() );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/ShortOptionsNoArgumentTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/ShortOptionsNoArgumentTest.java
new file mode 100644
index 0000000..6c2af89
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/ShortOptionsNoArgumentTest.java
@@ -0,0 +1,108 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import static java.util.Arrays.*;
+import static java.util.Collections.*;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import static joptsimple.ParserRules.*;
+import static org.junit.Assert.*;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class ShortOptionsNoArgumentTest extends AbstractOptionParserFixture {
+    @Before
+    public final void initializeParser() {
+        parser.accepts( "a" );
+        parser.accepts( "b" );
+        parser.accepts( "c" );
+    }
+
+    @Test
+    public void singleOption() {
+        OptionSet options = parser.parse( "-a" );
+
+        assertOptionDetected( options, "a" );
+        assertNull( options.valueOf( "a" ) );
+        assertEquals( emptyList(), options.nonOptionArguments() );
+    }
+
+    @Test
+    public void twoSingleOptions() {
+        OptionSet options = parser.parse( "-a", "-b" );
+
+        assertOptionDetected( options, "a" );
+        assertOptionDetected( options, "b" );
+        assertNull( options.valueOf( "a" ) );
+        assertNull( options.valueOf( "b" ) );
+        assertEquals( emptyList(), options.nonOptionArguments() );
+    }
+
+    @Test
+    public void singleOptionWithOneNonOptionArgument() {
+        OptionSet options = parser.parse( "-c", "foo" );
+
+        assertOptionDetected( options, "c" );
+        assertNull( options.valueOf( "c" ) );
+        assertEquals( singletonList( "foo" ), options.nonOptionArguments() );
+    }
+
+    @Test
+    public void clusteredOptions() {
+        OptionSet options = parser.parse( "-bac" );
+
+        assertOptionDetected( options, "a" );
+        assertOptionDetected( options, "b" );
+        assertOptionDetected( options, "c" );
+        assertNull( options.valueOf( "a" ) );
+        assertNull( options.valueOf( "b" ) );
+        assertNull( options.valueOf( "c" ) );
+        assertEquals( emptyList(), options.nonOptionArguments() );
+    }
+
+    @Test
+    public void optionsTerminator() {
+        OptionSet options = parser.parse( "-a", OPTION_TERMINATOR, "-a", "-b" );
+
+        assertOptionDetected( options, "a" );
+        assertNull( options.valueOf( "a" ) );
+        assertOptionNotDetected( options, "b" );
+        assertEquals( asList( "-a", "-b" ), options.nonOptionArguments() );
+    }
+
+    @Test
+    public void appearingMultipleTimes() {
+        OptionSet options = parser.parse( "-b", "-b", "-b" );
+
+        assertOptionDetected( options, "b" );
+        assertNull( options.valueOf( "b" ) );
+        assertEquals( emptyList(), options.nonOptionArguments() );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/ShortOptionsOptionalArgumentTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/ShortOptionsOptionalArgumentTest.java
new file mode 100644
index 0000000..3616d88
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/ShortOptionsOptionalArgumentTest.java
@@ -0,0 +1,103 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import static java.util.Arrays.*;
+import static java.util.Collections.*;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import static joptsimple.ExceptionMatchers.*;
+import static org.junit.Assert.*;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class ShortOptionsOptionalArgumentTest extends AbstractOptionParserFixture {
+    @Before
+    public final void initializeParser() {
+        parser.accepts( "f" ).withOptionalArg();
+        parser.accepts( "g" ).withOptionalArg();
+        parser.accepts( "bar" ).withOptionalArg();
+    }
+
+    @Test
+    public void optionWithOptionalArgumentNotPresent() {
+        OptionSet options = parser.parse( "-f" );
+
+        assertOptionDetected( options, "f" );
+        assertEquals( emptyList(), options.valuesOf( "f" ) );
+        assertEquals( emptyList(), options.nonOptionArguments() );
+    }
+
+    @Test
+    public void optionWithOptionalArgumentPresent() {
+        OptionSet options = parser.parse( "-f", "bar" );
+
+        assertOptionDetected( options, "f" );
+        assertEquals( singletonList( "bar" ), options.valuesOf( "f" ) );
+        assertEquals( emptyList(), options.nonOptionArguments() );
+    }
+
+    @Test
+    public void optionWithOptionalArgumentThatLooksLikeAnInvalidOption() {
+        thrown.expect( UnrecognizedOptionException.class );
+        thrown.expect( withOption( "biz" ) );
+
+        parser.parse( "-f", "--biz" );
+    }
+
+    @Test
+    public void optionWithOptionalArgumentThatLooksLikeAValidOption() {
+        OptionSet options = parser.parse( "-f", "--bar" );
+
+        assertOptionDetected( options, "f" );
+        assertOptionDetected( options, "bar" );
+        assertEquals( emptyList(), options.valuesOf( "f" ) );
+        assertEquals( emptyList(), options.valuesOf( "bar" ) );
+        assertEquals( emptyList(), options.nonOptionArguments() );
+    }
+
+    @Test
+    public void optionWithOptionalArgumentFollowedByLegalOption() {
+        OptionSet options = parser.parse( "-f", "-g" );
+
+        assertOptionDetected( options, "f" );
+        assertOptionDetected( options, "g" );
+        assertEquals( emptyList(), options.valuesOf( "f" ) );
+        assertEquals( emptyList(), options.valuesOf( "g" ) );
+        assertEquals( emptyList(), options.nonOptionArguments() );
+    }
+
+    @Test
+    public void multipleOfSameOptionSomeWithArgsAndSomeWithout() {
+        OptionSet options = parser.parse( "-f", "-f", "foo", "-f", "-f", "bar", "-f" );
+
+        assertEquals( asList( "foo", "bar" ), options.valuesOf( "f" ) );
+        assertEquals( emptyList(), options.nonOptionArguments() );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/ShortOptionsRequiredArgumentTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/ShortOptionsRequiredArgumentTest.java
new file mode 100644
index 0000000..dea86ca
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/ShortOptionsRequiredArgumentTest.java
@@ -0,0 +1,130 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import static java.util.Arrays.*;
+import static java.util.Collections.*;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import static joptsimple.ExceptionMatchers.*;
+import static org.junit.Assert.*;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class ShortOptionsRequiredArgumentTest extends AbstractOptionParserFixture {
+    @Before
+    public final void initializeParser() {
+        parser.accepts( "d" ).withRequiredArg();
+        parser.accepts( "e" );
+        parser.accepts( "f" );
+        parser.accepts( "infile" ).withOptionalArg();
+    }
+
+    @Test
+    public void argumentNotPresent() {
+        thrown.expect( OptionMissingRequiredArgumentException.class );
+        thrown.expect( withOption( "d" ) );
+
+        parser.parse( "-d" );
+    }
+
+    @Test
+    public void withArgument() {
+        OptionSet options = parser.parse( "-d", "foo" );
+
+        assertOptionDetected( options, "d" );
+        assertEquals( "foo", options.valueOf( "d" ) );
+        assertEquals( emptyList(), options.nonOptionArguments() );
+    }
+
+    @Test
+    public void clusteredOptionsWithLastOneAcceptingAnArgumentButMissing() {
+        thrown.expect( OptionMissingRequiredArgumentException.class );
+        
+        parser.parse( "-fed" );
+    }
+    
+    @Test
+    public void clusteredOptionsWithLastOneAcceptingAnArgument() {
+        OptionSet options = parser.parse( "-fed", "foo" );
+        
+        assertOptionDetected( options, "d" );
+        assertOptionDetected( options, "f" );
+        assertOptionDetected( options, "e" );
+        assertEquals( "foo", options.valueOf( "d" ) );
+    }
+    
+    @Test
+    public void clusteredOptionsWithOneAcceptingAnArgument() {
+        OptionSet options = parser.parse( "-fde" );
+        
+        assertOptionDetected( options, "f" );
+        assertOptionDetected( options, "d" );
+        assertOptionNotDetected( options, "e" );
+        
+        assertEquals( "e", options.valueOf( "d" ) );
+    }
+
+    @Test
+    public void argumentNotPresentFollowedByAnotherOption() {
+        OptionSet options = parser.parse( "-d", "--infile" );
+
+        assertOptionDetected( options, "d" );
+        assertOptionNotDetected( options, "infile" );
+        assertEquals( "--infile", options.valueOf( "d" ) );
+        assertEquals( emptyList(), options.nonOptionArguments() );
+    }
+
+    @Test
+    public void appearingMultipleTimes() {
+        OptionSet options = parser.parse( "-d", "foo", "-d", "bar" );
+
+        assertOptionDetected( options, "d" );
+        assertEquals( asList( "foo", "bar" ), options.valuesOf( "d" ) );
+        assertEquals( emptyList(), options.nonOptionArguments() );
+    }
+
+    @Test
+    public void inSameToken() {
+        OptionSet options = parser.parse( "-dfoo" );
+
+        assertOptionDetected( options, "d" );
+        assertEquals( singletonList( "foo" ), options.valuesOf( "d" ) );
+        assertEquals( emptyList(), options.nonOptionArguments() );
+    }
+
+    @Test
+    public void whenEndOfOptionsMarkerIsInPlaceOfRequiredArgument() {
+        OptionSet options = parser.parse( "-d", "--", "foo", "bar" );
+
+        assertOptionDetected( options, "d" );
+        assertEquals( singletonList( "--" ), options.valuesOf( "d" ) );
+        assertEquals( asList( "foo", "bar" ), options.nonOptionArguments() );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/SingleHyphenTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/SingleHyphenTest.java
new file mode 100644
index 0000000..7c88a7d
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/SingleHyphenTest.java
@@ -0,0 +1,68 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import static java.util.Collections.*;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class SingleHyphenTest extends AbstractOptionParserFixture {
+    @Before
+    public final void initializeParser() {
+        parser.accepts( "o" ).withOptionalArg();
+    }
+
+    @Test
+    public void singleHyphen() {
+        OptionSet options = parser.parse( "-" );
+
+        assertEquals( singletonList( "-" ), options.nonOptionArguments() );
+    }
+
+    @Test
+    public void asArgumentOfOption() {
+        OptionSet options = parser.parse( "-o", "-" );
+
+        assertOptionDetected( options, "o" );
+        assertEquals( singletonList( "-" ), options.valuesOf( "o" ) );
+        assertEquals( emptyList(), options.nonOptionArguments() );
+    }
+
+    @Test
+    public void asArgumentOfOptionInSameToken() {
+        OptionSet options = parser.parse( "-o-" );
+
+        assertOptionDetected( options, "o" );
+        assertEquals( singletonList( "-" ), options.valuesOf( "o" ) );
+        assertEquals( emptyList(), options.nonOptionArguments() );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/ToStringTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/ToStringTest.java
new file mode 100644
index 0000000..0b01a78
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/ToStringTest.java
@@ -0,0 +1,73 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import java.util.Collection;
+
+import static com.google.common.collect.Lists.newArrayList;
+import static java.util.Arrays.*;
+
+import joptsimple.util.KeyValuePair;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import static org.hamcrest.CoreMatchers.*;
+import static org.junit.Assert.*;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+@RunWith( Parameterized.class )
+public class ToStringTest {
+    private final Object subject;
+    private final String[] substrings;
+
+    public ToStringTest( Object subject, String[] substrings ) {
+        this.subject = subject;
+        this.substrings = substrings.clone();
+    }
+
+    @Parameterized.Parameters
+    public static Collection<?> objectsAndStringRepresentations() {
+        return asList( new Object[][] {
+            { KeyValuePair.valueOf( "key=value" ), new String[] { "key", "=", "value" } },
+            { new UnrecognizedOptionException( "a" ), new String[] { "a" } },
+            { new NoArgumentOptionSpec( asList( "a", "b" ), "" ), new String[] { "[a, b]" } },
+            { new UnavailableOptionException(
+                    newArrayList( new NoArgumentOptionSpec( "a" ), new NoArgumentOptionSpec( "b" ) ) ),
+              new String[] { "[a, b]" } },
+        } );
+    }
+
+    @Test
+    public void givesUsefulStringRepresentations() {
+        String stringRepresentation = subject.toString();
+
+        for ( String each : substrings )
+            assertThat( stringRepresentation, containsString( each ) );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/TypesafeOptionArgumentRetrievalTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/TypesafeOptionArgumentRetrievalTest.java
new file mode 100644
index 0000000..cea1e72
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/TypesafeOptionArgumentRetrievalTest.java
@@ -0,0 +1,317 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import java.util.List;
+
+import org.junit.Test;
+
+import static java.lang.Short.*;
+import static java.util.Arrays.*;
+import static java.util.Collections.*;
+import static joptsimple.ExceptionMatchers.*;
+import static org.junit.Assert.*;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class TypesafeOptionArgumentRetrievalTest extends AbstractOptionParserFixture {
+    @Test
+    public void retrievalOfTypedRequiredArgumentsInATypesafeManner() {
+        OptionSpec<Integer> optionA = parser.accepts( "a" ).withRequiredArg().ofType( Integer.class );
+
+        OptionSet options = parser.parse( "-a", "1" );
+
+        assertTrue( options.has( optionA ) );
+        Integer valueFromOption = optionA.value( options );
+        assertEquals( Integer.valueOf( 1 ), valueFromOption );
+        Integer valueFromOptionSet = options.valueOf( optionA );
+        assertEquals( valueFromOption, valueFromOptionSet );
+
+        List<Integer> valuesFromOption = optionA.values( options );
+        assertEquals( asList( 1 ), valuesFromOption );
+        List<Integer> valuesFromOptionSet = options.valuesOf( optionA );
+        assertEquals( valuesFromOption, valuesFromOptionSet );
+    }
+
+    @Test
+    public void retrievalOfTypedOptionalArgumentsInATypesafeManner() {
+        OptionSpec<Double> optionB = parser.accepts( "b" ).withOptionalArg().ofType( Double.class );
+
+        OptionSet options = parser.parse( "-b", "3.14D" );
+
+        assertTrue( options.has( optionB ) );
+        assertEquals( Double.valueOf( 3.14D ), optionB.value( options ) );
+        assertEquals( asList( 3.14D ), optionB.values( options ) );
+    }
+
+    @Test
+    public void retrievalOfUntypedRequiredArgumentsInATypesafeManner() {
+        OptionSpec<String> optionC = parser.accepts( "c" ).withRequiredArg();
+
+        OptionSet options = parser.parse( "-c", "foo", "-c", "bar" );
+
+        assertTrue( options.has( optionC ) );
+        assertEquals( asList( "foo", "bar" ), optionC.values( options ) );
+    }
+
+    @Test
+    public void retrievalOfUntypedOptionalArgumentsInATypesafeManner() {
+        OptionSpec<String> optionD = parser.accepts( "d" ).withRequiredArg();
+
+        OptionSet options = parser.parse( "-d", "foo", "-d", "bar", "-d", "baz" );
+
+        assertTrue( options.has( optionD ) );
+        List<String> valuesFromOption = optionD.values( options );
+        assertEquals( asList( "foo", "bar", "baz" ), valuesFromOption );
+        List<String> valuesFromOptionSet = options.valuesOf( optionD );
+        assertEquals( valuesFromOption, valuesFromOptionSet );
+    }
+
+    @Test
+    public void retrievalWithVoidOption() {
+        OptionSpec<Void> optionE = parser.accepts( "e" );
+
+        OptionSet options = parser.parse( "-e" );
+
+        assertTrue( options.has( optionE ) );
+        assertEquals( emptyList(), options.valuesOf( optionE ) );
+    }
+
+    @Test
+    public void primitiveBooleanAllowedAsTypeSpecifier() {
+        OptionSpec<Boolean> optionA = parser.accepts( "a" ).withRequiredArg().ofType( boolean.class );
+
+        OptionSet options = parser.parse( "-a", "false" );
+
+        assertTrue( options.has( optionA ) );
+        assertEquals( asList( false ), options.valuesOf( optionA ) );
+    }
+
+    @Test
+    public void primitiveByteAllowedAsTypeSpecifier() {
+        OptionSpec<Byte> optionB = parser.accepts( "b" ).withOptionalArg().ofType( byte.class );
+
+        OptionSet options = parser.parse( "-b", "3" );
+
+        assertTrue( options.has( optionB ) );
+        assertEquals( asList( Byte.valueOf( "3" ) ), options.valuesOf( optionB ) );
+    }
+
+    @Test( expected = IllegalArgumentException.class )
+    public void primitiveCharAllowedAsTypeSpecifier() {
+        parser.accepts( "c" ).withRequiredArg().ofType( char.class );
+    }
+
+    @Test
+    public void primitiveDoubleAllowedAsTypeSpecifier() {
+        OptionSpec<Double> optionD = parser.accepts( "d" ).withOptionalArg().ofType( double.class );
+
+        OptionSet options = parser.parse( "-d", "3.1" );
+
+        assertTrue( options.has( optionD ) );
+        assertEquals( asList( 3.1D ), options.valuesOf( optionD ) );
+    }
+
+    @Test
+    public void primitiveFloatAllowedAsTypeSpecifier() {
+        OptionSpec<Float> optionE = parser.accepts( "e" ).withRequiredArg().ofType( float.class );
+
+        OptionSet options = parser.parse( "-e", "2.09" );
+
+        assertTrue( options.has( optionE ) );
+        assertEquals( asList( 2.09F ), options.valuesOf( optionE ) );
+    }
+
+    @Test
+    public void primitiveIntAllowedAsTypeSpecifier() {
+        OptionSpec<Integer> optionF = parser.accepts( "F" ).withRequiredArg().ofType( int.class );
+
+        OptionSet options = parser.parse( "-F", "91" );
+
+        assertTrue( options.has( optionF ) );
+        assertEquals( asList( 91 ), options.valuesOf( optionF ) );
+    }
+
+    @Test
+    public void primitiveLongAllowedAsTypeSpecifier() {
+        OptionSpec<Long> optionG = parser.accepts( "g" ).withOptionalArg().ofType( long.class );
+
+        OptionSet options = parser.parse("-g", "12");
+
+        assertTrue( options.has( optionG ) );
+        assertEquals( asList( 12L ), options.valuesOf( optionG ) );
+    }
+
+    @Test
+    public void primitiveShortAllowedAsTypeSpecifier() {
+        OptionSpec<Short> optionH = parser.accepts( "H" ).withRequiredArg().ofType( short.class );
+
+        OptionSet options = parser.parse( "-H", "8" );
+
+        assertTrue( options.has( optionH ) );
+        assertEquals( asList( Short.valueOf( "8" ) ), options.valuesOf( optionH ) );
+    }
+
+    @Test
+    public void cannotFoolHasWithAnOptionNotIssuedFromBuilder() {
+        parser.accepts( "e" );
+
+        OptionSet options = parser.parse( "-e" );
+
+        assertFalse( options.has( new FakeOptionSpec<Void>( "e" ) ) );
+    }
+
+    @Test
+    public void cannotFoolHasArgumentWithAnOptionNotIssuedFromBuilder() {
+        parser.accepts( "f" ).withRequiredArg();
+        OptionSpec<String> fakeOptionF = new FakeOptionSpec<>( "f" );
+
+        OptionSet options = parser.parse( "-f", "boo" );
+
+        assertFalse( options.hasArgument( fakeOptionF ) );
+    }
+
+    @Test
+    public void cannotFoolValueOfWithAnOptionNotIssuedFromBuilder() {
+        parser.accepts( "g" ).withRequiredArg();
+
+        OptionSet options = parser.parse( "-g", "foo" );
+
+        assertNull( options.valueOf( new FakeOptionSpec<String>( "g" ) ) );
+    }
+
+    @Test
+    public void cannotFoolValuesOfWithAnOptionNotIssuedFromBuilder() {
+        parser.accepts( "h" ).withRequiredArg();
+
+        OptionSet options = parser.parse( "-h", "foo", "-h", "bar" );
+
+        assertEquals( emptyList(), options.valuesOf( new FakeOptionSpec<String>( "h" ) ) );
+    }
+
+    @Test( expected = ClassCastException.class )
+    public void canSubvertTypeSafetyIfYouUseAnOptionSpecAsTheWrongType() {
+        ArgumentAcceptingOptionSpec<String> optionI = parser.accepts( "i" ).withRequiredArg();
+        optionI.ofType( Integer.class );
+
+        OptionSet options = parser.parse( "-i", "2" );
+
+        @SuppressWarnings( "unused" )
+        String value = optionI.value( options );
+    }
+
+    @Test( expected = ClassCastException.class )
+    public void canSubvertTypeSafetyIfYouGiveAnOptionSpecToOptionSetAsTheWrongType() {
+        ArgumentAcceptingOptionSpec<String> optionJ = parser.accepts( "j" ).withRequiredArg();
+        optionJ.ofType( Integer.class );
+
+        OptionSet options = parser.parse( "-j", "3" );
+
+        @SuppressWarnings( "unused" )
+        String value = options.valuesOf( optionJ ).get( 0 );
+    }
+
+    @Test
+    public void canUseBooleanType() {
+        OptionSpec<Boolean> optionK = parser.accepts( "k" ).withRequiredArg().ofType( Boolean.class );
+
+        OptionSet options = parser.parse( "-k", "true" );
+
+        assertTrue( optionK.value( options ) );
+    }
+
+    @Test
+    public void usesConverterIfProvided() {
+        OptionSpec<Short> optionL = parser.accepts( "L" ).withRequiredArg().withValuesConvertedBy(
+            new ValueConverter<Short>() {
+                public Short convert( String value ) {
+                    return parseShort( value );
+                }
+
+                public Class<Short> valueType() {
+                    return Short.class;
+                }
+
+                public String valuePattern() {
+                    return null;
+                }
+            } );
+
+        OptionSet options = parser.parse( "-L", "34" );
+
+        assertEquals( new Short( (short) 34 ), optionL.value( options ) );
+    }
+
+    @Test
+    public void wrapsValueConversionExceptionsRaisedByConverter() {
+        OptionSpec<Character> optionM = parser.accepts( "m" ).withRequiredArg().withValuesConvertedBy(
+            new ValueConverter<Character>() {
+                public Character convert( String value ) {
+                    throw new ValueConversionException( value );
+                }
+
+                public Class<Character> valueType() {
+                    return Character.class;
+                }
+
+                public String valuePattern() {
+                    return null;
+                }
+            } );
+
+        OptionSet options = parser.parse( "-m", "a" );
+
+        thrown.expect( OptionArgumentConversionException.class );
+        thrown.expect( withCauseOfType( ValueConversionException.class ) );
+
+        optionM.value( options );
+    }
+
+    private static class FakeOptionSpec<V> implements OptionSpec<V> {
+        private final String option;
+
+        FakeOptionSpec( String option ) {
+            this.option = option;
+        }
+
+        public List<String> options() {
+            return asList( option );
+        }
+
+        public V value( OptionSet detectedOptions ) {
+            return detectedOptions.valueOf( this );
+        }
+
+        public List<V> values( OptionSet detectedOptions ) {
+            return detectedOptions.valuesOf( this );
+        }
+
+        public boolean isForHelp() {
+            return false;
+        }
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/UtilityClassesUninstantiabilityHarness.java b/third_party/java/jopt-simple/src/test/java/joptsimple/UtilityClassesUninstantiabilityHarness.java
new file mode 100644
index 0000000..e70dba7
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/UtilityClassesUninstantiabilityHarness.java
@@ -0,0 +1,60 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import static joptsimple.ExceptionMatchers.*;
+import static org.junit.rules.ExpectedException.*;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public abstract class UtilityClassesUninstantiabilityHarness {
+    @Rule public final ExpectedException thrown = none();
+
+    private final Class<?> utility;
+
+    protected UtilityClassesUninstantiabilityHarness( Class<?> utility ) {
+        this.utility = utility;
+    }
+
+    @Test
+    public final void attemptToInstantiate() throws Exception {
+        Constructor<?> constructor = utility.getDeclaredConstructor();
+        constructor.setAccessible( true );
+
+        thrown.expect( InvocationTargetException.class );
+        thrown.expect( withTargetOfType( UnsupportedOperationException.class ) );
+
+        constructor.newInstance();
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/ValueConverterAdmitsSubclassesOfValueTypeTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/ValueConverterAdmitsSubclassesOfValueTypeTest.java
new file mode 100644
index 0000000..a7e726e
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/ValueConverterAdmitsSubclassesOfValueTypeTest.java
@@ -0,0 +1,63 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import static org.junit.Assert.*;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class ValueConverterAdmitsSubclassesOfValueTypeTest {
+    @Test
+    public void subclassOfValueType() {
+        ValueConverter<List<String>> converter = new ValueConverter<List<String>>() {
+            public List<String> convert( String value ) {
+                return Collections.singletonList( value );
+            }
+
+            public Class<? extends List<String>> valueType() {
+                return ListOfStrings.class;
+            }
+
+            public String valuePattern() {
+                return null;
+            }
+        };
+
+        assertEquals( Arrays.asList( "foo" ), converter.convert( "foo" ) );
+    }
+
+    static class ListOfStrings extends ArrayList<String> {
+        private static final long serialVersionUID = 1L;
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/ValueOfHaver.java b/third_party/java/jopt-simple/src/test/java/joptsimple/ValueOfHaver.java
new file mode 100644
index 0000000..a309260
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/ValueOfHaver.java
@@ -0,0 +1,39 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class ValueOfHaver {
+    private ValueOfHaver() {
+        // no-op
+    }
+
+    public static ValueOfHaver valueOf( String s ) {
+        return null;
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/WAsAbbreviationForLongOptionTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/WAsAbbreviationForLongOptionTest.java
new file mode 100644
index 0000000..119c14b
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/WAsAbbreviationForLongOptionTest.java
@@ -0,0 +1,64 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import static java.util.Collections.*;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import static joptsimple.ExceptionMatchers.*;
+import static org.junit.Assert.*;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class WAsAbbreviationForLongOptionTest extends AbstractOptionParserFixture {
+    @Before
+    public final void initializeParser() {
+        parser.accepts( "Wally" ).withRequiredArg();
+    }
+
+    @Test
+    public void abbreviation() {
+        OptionSet options = parser.parse( "--W", "silent" );
+
+        assertOptionDetected( options, "Wally" );
+        assertOptionNotDetected( options, "W" );
+        assertEquals( singletonList( "silent" ), options.valuesOf( "Wally" ) );
+        assertEquals( emptyList(), options.nonOptionArguments() );
+    }
+
+    @Test
+    public void recognizeLongOptionsTrumpsLongOptionAbbreviation() {
+        parser.recognizeAlternativeLongOptions( true );
+
+        thrown.expect( UnrecognizedOptionException.class );
+        thrown.expect( withOption( "silent" ) );
+
+        parser.parse( "--W", "silent" );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/WAsShortOptionTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/WAsShortOptionTest.java
new file mode 100644
index 0000000..8cfb523
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/WAsShortOptionTest.java
@@ -0,0 +1,63 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import static java.util.Collections.*;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import static joptsimple.ExceptionMatchers.*;
+import static org.junit.Assert.*;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class WAsShortOptionTest extends AbstractOptionParserFixture {
+    @Before
+    public final void initializeParser() {
+        parser.accepts( "W" );
+    }
+
+    @Test
+    public void wIsLegal() {
+        OptionSet options = parser.parse( "-W", "silent" );
+
+        assertOptionDetected( options, "W" );
+        assertEquals( emptyList(), options.valuesOf( "W" ) );
+        assertEquals( singletonList( "silent" ), options.nonOptionArguments() );
+    }
+
+    @Test
+    public void recognizeLongOptionsTrumpsShortOptionW() {
+        parser.recognizeAlternativeLongOptions( true );
+
+        thrown.expect( UnrecognizedOptionException.class );
+        thrown.expect( withOption( "silent" ) );
+
+        parser.parse( "-W", "silent" );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/WExtensionNoArgumentTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/WExtensionNoArgumentTest.java
new file mode 100644
index 0000000..cb8a5e9
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/WExtensionNoArgumentTest.java
@@ -0,0 +1,65 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import static java.util.Collections.*;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import static joptsimple.ExceptionMatchers.*;
+import static org.junit.Assert.*;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class WExtensionNoArgumentTest extends AbstractOptionParserFixture {
+    @Before
+    public final void initializeParser() {
+        parser.accepts( "silent" );
+        parser.recognizeAlternativeLongOptions( true );
+    }
+
+    @Test
+    public void turnOffWExtension() {
+        parser.recognizeAlternativeLongOptions( false );
+
+        thrown.expect( UnrecognizedOptionException.class );
+        thrown.expect( withOption( "W" ) );
+
+        parser.parse( "-W", "silent" );
+    }
+
+    @Test
+    public void wExtensionWithLongOptionWithoutAnArgument() {
+        OptionSet options = parser.parse( "-W", "silent" );
+
+        assertOptionNotDetected( options, "W" );
+        assertOptionDetected( options, "silent" );
+        assertEquals( emptyList(), options.valuesOf( "silent" ) );
+        assertEquals( emptyList(), options.nonOptionArguments() );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/WExtensionWithArgumentTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/WExtensionWithArgumentTest.java
new file mode 100644
index 0000000..76f247a
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/WExtensionWithArgumentTest.java
@@ -0,0 +1,116 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple;
+
+import static java.util.Collections.*;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import static joptsimple.ExceptionMatchers.*;
+import static org.junit.Assert.*;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class WExtensionWithArgumentTest extends AbstractOptionParserFixture {
+    @Before
+    public final void initializeParser() {
+        parser.recognizeAlternativeLongOptions( true );
+        parser.accepts( "silent" ).withOptionalArg();
+    }
+
+    @Test
+    public void separateArgument() {
+        OptionSet options = parser.parse( "-W", "silent", "4" );
+
+        assertOptionNotDetected( options, "W" );
+        assertOptionDetected( options, "silent" );
+        assertEquals( singletonList( "4" ), options.valuesOf( "silent" ) );
+        assertEquals( emptyList(), options.nonOptionArguments() );
+    }
+
+    @Test
+    public void togetherArgument() {
+        OptionSet options = parser.parse( "-W", "silent=6" );
+
+        assertOptionNotDetected( options, "W" );
+        assertOptionDetected( options, "silent" );
+        assertEquals( singletonList( "6" ), options.valuesOf( "silent" ) );
+        assertEquals( emptyList(), options.nonOptionArguments() );
+    }
+
+    @Test
+    public void abbreviationWithTogetherArgument() {
+        OptionSet options = parser.parse( "-W", "s=6" );
+
+        assertOptionNotDetected( options, "W" );
+        assertOptionDetected( options, "silent" );
+        assertOptionNotDetected( options, "s" );
+        assertEquals( singletonList( "6" ), options.valuesOf( "silent" ) );
+        assertEquals( emptyList(), options.nonOptionArguments() );
+    }
+
+    @Test
+    public void illegalLongOption() {
+        thrown.expect( UnrecognizedOptionException.class );
+        thrown.expect( withOption( "foo" ) );
+
+        parser.parse( "-W", "foo=bar" );
+    }
+
+    @Test
+    public void noMoreArguments() {
+        thrown.expect( OptionMissingRequiredArgumentException.class );
+        thrown.expect( withOption( "W" ) );
+
+        parser.parse( "-W" );
+    }
+
+    @Test
+    public void typedTogetherArg() {
+        OptionSpec<Integer> level = parser.accepts( "level" ).withRequiredArg().ofType( Integer.class );
+
+        OptionSet options = parser.parse( "-W", "level=4" );
+
+        assertOptionNotDetected( options, "W" );
+        assertOptionDetected( options, "level" );
+        assertEquals( singletonList( 4 ), options.valuesOf( level ) );
+        assertEquals( emptyList(), options.nonOptionArguments() );
+    }
+
+    @Test
+    public void typedSeparateArg() {
+        OptionSpec<Integer> floor = parser.accepts( "floor" ).withRequiredArg().ofType( Integer.class );
+
+        OptionSet options = parser.parse( "-W", "floor", "5" );
+
+        assertOptionNotDetected( options, "W" );
+        assertOptionDetected( options, "floor" );
+        assertEquals( singletonList( 5 ), options.valuesOf( floor ) );
+        assertEquals( emptyList(), options.nonOptionArguments() );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/examples/AlternativeLongOptionsTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/examples/AlternativeLongOptionsTest.java
new file mode 100644
index 0000000..3657a40
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/examples/AlternativeLongOptionsTest.java
@@ -0,0 +1,22 @@
+package joptsimple.examples;
+
+import joptsimple.OptionParser;
+import joptsimple.OptionSet;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+public class AlternativeLongOptionsTest {
+    @Test
+    public void handlesAlternativeLongOptions() {
+        OptionParser parser = new OptionParser( "W;" );
+        parser.recognizeAlternativeLongOptions( true );  // same effect as above
+        parser.accepts( "level" ).withRequiredArg();
+
+        OptionSet options = parser.parse( "-W", "level=5" );
+
+        assertTrue( options.has( "level" ) );
+        assertTrue( options.hasArgument( "level" ) );
+        assertEquals( "5", options.valueOf( "level" ) );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/examples/DefaultValuesForOptionArgumentsTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/examples/DefaultValuesForOptionArgumentsTest.java
new file mode 100644
index 0000000..cd11f07
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/examples/DefaultValuesForOptionArgumentsTest.java
@@ -0,0 +1,58 @@
+package joptsimple.examples;
+
+import java.io.File;
+
+import joptsimple.OptionException;
+import joptsimple.OptionParser;
+import joptsimple.OptionSet;
+import joptsimple.OptionSpec;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import static joptsimple.examples.Level.*;
+import static org.junit.Assert.*;
+import static org.junit.rules.ExpectedException.*;
+
+public class DefaultValuesForOptionArgumentsTest {
+    @Rule public final ExpectedException thrown = none();
+
+    @Test
+    public void allowsSpecificationOfDefaultValues() throws Exception {
+        File tempDir = new File( System.getProperty( "java.io.tmpdir" ) );
+        File tempFile = File.createTempFile( "aFile", ".txt" );
+        OptionParser parser = new OptionParser();
+        OptionSpec<File> infile =
+            parser.accepts( "infile" ).withRequiredArg().ofType( File.class ).defaultsTo( tempFile );
+        OptionSpec<File> outdir =
+            parser.accepts( "outdir" ).withRequiredArg().ofType( File.class ).defaultsTo( tempDir );
+        OptionSpec<Integer> bufferSize =
+            parser.accepts( "buffer-size" ).withOptionalArg().ofType( Integer.class ).defaultsTo( 4096 );
+        OptionSpec<Level> level =
+            parser.accepts( "level" ).withOptionalArg().ofType( Level.class ).defaultsTo( INFO );
+        OptionSpec<Integer> count =
+            parser.accepts( "count" ).withOptionalArg().ofType( Integer.class ).defaultsTo( 10 );
+
+        OptionSet options = parser.parse( "--level", "WARNING", "--count", "--infile", "/etc/passwd" );
+
+        assertEquals( new File( "/etc/passwd" ), infile.value( options ) );
+        assertTrue( options.has( infile ) );
+        assertTrue( options.hasArgument( infile ) );
+        assertEquals( tempDir, outdir.value( options ) );
+        assertFalse( options.has( outdir ) );
+        assertFalse( options.hasArgument( outdir ) );
+        assertEquals( Integer.valueOf( 4096 ), bufferSize.value( options ) );
+        assertFalse( options.has( bufferSize ) );
+        assertFalse( options.hasArgument( bufferSize ) );
+        assertEquals( WARNING, level.value( options ) );
+        assertTrue( options.has( level ) );
+        assertTrue( options.hasArgument( level ) );
+        assertEquals( Integer.valueOf( 10 ), count.value( options ) );
+        assertTrue( options.has( count ) );
+        assertFalse( options.hasArgument( count ) );
+
+        thrown.expect( OptionException.class );
+
+        parser.parse( "--outdir" );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/examples/ExceptionExample.java b/third_party/java/jopt-simple/src/test/java/joptsimple/examples/ExceptionExample.java
new file mode 100644
index 0000000..fb4a9e7
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/examples/ExceptionExample.java
@@ -0,0 +1,11 @@
+package joptsimple.examples;
+
+import joptsimple.OptionParser;
+
+public class ExceptionExample {
+    public static void main( String[] args ) {
+        OptionParser parser = new OptionParser();
+
+        parser.parse( "-x" );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/examples/ExportOptionsTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/examples/ExportOptionsTest.java
new file mode 100644
index 0000000..730e318
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/examples/ExportOptionsTest.java
@@ -0,0 +1,61 @@
+package joptsimple.examples;
+
+import com.google.common.base.Joiner;
+import joptsimple.OptionParser;
+import joptsimple.OptionSet;
+import joptsimple.OptionSpec;
+import org.junit.Test;
+
+import java.io.File;
+import java.util.List;
+import java.util.Map.Entry;
+import java.util.Properties;
+
+import static org.junit.Assert.assertEquals;
+
+public class ExportOptionsTest {
+    private static Properties asProperties( OptionSet options, String prefix ) {
+        Properties properties = new Properties();
+        for ( Entry<OptionSpec<?>, List<?>> entry : options.asMap().entrySet() ) {
+            OptionSpec<?> spec = entry.getKey();
+            properties.setProperty(
+                asPropertyKey( prefix, spec ),
+                asPropertyValue( entry.getValue(), options.has( spec ) ) );
+        }
+        return properties;
+    }
+
+    private static String asPropertyKey( String prefix, OptionSpec<?> spec ) {
+        List<String> flags = spec.options();
+        for ( String flag : flags )
+            if ( 1 < flag.length() )
+                return null == prefix ? flag : ( prefix + '.' + flag );
+        throw new IllegalArgumentException( "No usable non-short flag: " + flags );
+    }
+
+    private static String asPropertyValue( List<?> values, boolean present ) {
+        // Simple flags have no values; treat presence/absence as true/false
+        return values.isEmpty() ? String.valueOf( present ) : Joiner.on( "," ).join( values );
+    }
+
+    @Test
+    public void allowsExportOfOptions() {
+        Properties expected = new Properties();
+        expected.setProperty( "rice.count", "3" );
+        // Cannot check path as string directly - Windows flips the leading slash
+        expected.setProperty( "rice.output-dir", new File( "/tmp" ).toString() );
+        expected.setProperty( "rice.fun", "false" );
+        expected.setProperty( "rice.verbose", "true" );
+
+        OptionParser parser = new OptionParser();
+        OptionSpec<Integer> count = parser.accepts( "count" ).withRequiredArg().ofType( Integer.class );
+        OptionSpec<File> outputDir = parser.accepts( "output-dir" ).withOptionalArg().ofType( File.class );
+        OptionSpec<Void> verbose = parser.accepts( "verbose" );
+        OptionSpec<Void> fun = parser.accepts( "fun" );
+        OptionSpec<File> files = parser.nonOptions().ofType( File.class );
+
+        OptionSet options = parser.parse( "--count", "3", "--output-dir", "/tmp", "--verbose", "a.txt", "b.txt" );
+
+        assertEquals( expected, asProperties( options, "rice" ) );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/examples/HelpFormatterExample.java b/third_party/java/jopt-simple/src/test/java/joptsimple/examples/HelpFormatterExample.java
new file mode 100644
index 0000000..0262d2a
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/examples/HelpFormatterExample.java
@@ -0,0 +1,69 @@
+package joptsimple.examples;
+
+import java.io.File;
+import java.util.HashSet;
+import java.util.Map;
+
+import static java.io.File.*;
+import static java.util.Arrays.*;
+
+import joptsimple.HelpFormatter;
+import joptsimple.OptionDescriptor;
+import joptsimple.OptionParser;
+
+import static joptsimple.util.DateConverter.*;
+
+public class HelpFormatterExample {
+    static class MyFormatter implements HelpFormatter {
+        public String format( Map<String, ? extends OptionDescriptor> options ) {
+            StringBuilder buffer = new StringBuilder();
+            for ( OptionDescriptor each : new HashSet<>( options.values() ) ) {
+                buffer.append( lineFor( each ) );
+            }
+            return buffer.toString();
+        }
+
+        private String lineFor( OptionDescriptor descriptor ) {
+            if ( descriptor.representsNonOptions() ) {
+                return descriptor.argumentDescription() + '(' + descriptor.argumentTypeIndicator() + "): "
+                    + descriptor.description() + System.getProperty( "line.separator" );
+            }
+
+            StringBuilder line = new StringBuilder( descriptor.options().toString() );
+            line.append( ": description = " ).append( descriptor.description() );
+            line.append( ", required = " ).append( descriptor.isRequired() );
+            line.append( ", accepts arguments = " ).append( descriptor.acceptsArguments() );
+            line.append( ", requires argument = " ).append( descriptor.requiresArgument() );
+            line.append( ", argument description = " ).append( descriptor.argumentDescription() );
+            line.append( ", argument type indicator = " ).append( descriptor.argumentTypeIndicator() );
+            line.append( ", default values = " ).append( descriptor.defaultValues() );
+            line.append( System.getProperty( "line.separator" ) );
+            return line.toString();
+        }
+    }
+
+    public static void main( String[] args ) throws Exception {
+        OptionParser parser = new OptionParser() {
+            {
+                accepts( "c" ).withRequiredArg().ofType( Integer.class )
+                    .describedAs( "count" ).defaultsTo( 1 );
+                accepts( "q" ).withOptionalArg().ofType( Double.class )
+                    .describedAs( "quantity" );
+                accepts( "d", "some date" ).withRequiredArg().required()
+                    .withValuesConvertedBy( datePattern( "MM/dd/yy" ) );
+                acceptsAll( asList( "v", "talkative", "chatty" ), "be more verbose" );
+                accepts( "output-file" ).withOptionalArg().ofType( File.class )
+                     .describedAs( "file" );
+                acceptsAll( asList( "h", "?" ), "show help" ).forHelp();
+                acceptsAll( asList( "cp", "classpath" ) ).withRequiredArg()
+                    .describedAs( "path1" + pathSeparatorChar + "path2:..." )
+                    .ofType( File.class )
+                    .withValuesSeparatedBy( pathSeparatorChar );
+                nonOptions( "files to chew on" ).ofType( File.class ).describedAs( "input files" );
+            }
+        };
+
+        parser.formatHelpWith( new MyFormatter() );
+        parser.printHelpOn( System.out );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/examples/HelpScreenExample.java b/third_party/java/jopt-simple/src/test/java/joptsimple/examples/HelpScreenExample.java
new file mode 100644
index 0000000..44f024b
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/examples/HelpScreenExample.java
@@ -0,0 +1,35 @@
+package joptsimple.examples;
+
+import java.io.File;
+
+import static java.io.File.*;
+import static java.util.Arrays.*;
+
+import joptsimple.OptionParser;
+
+import static joptsimple.util.DateConverter.*;
+
+public class HelpScreenExample {
+    public static void main( String[] args ) throws Exception {
+        OptionParser parser = new OptionParser() {
+            {
+                accepts( "c" ).withRequiredArg().ofType( Integer.class )
+                    .describedAs( "count" ).defaultsTo( 1 );
+                accepts( "q" ).withOptionalArg().ofType( Double.class )
+                    .describedAs( "quantity" );
+                accepts( "d", "some date" ).withRequiredArg().required()
+                    .withValuesConvertedBy( datePattern( "MM/dd/yy" ) );
+                acceptsAll( asList( "v", "talkative", "chatty" ), "be more verbose" );
+                accepts( "output-file" ).withOptionalArg().ofType( File.class )
+                     .describedAs( "file" );
+                acceptsAll( asList( "h", "?" ), "show help" ).forHelp();
+                acceptsAll( asList( "cp", "classpath" ) ).withRequiredArg()
+                    .describedAs( "path1" + pathSeparatorChar + "path2:..." )
+                    .ofType( File.class )
+                    .withValuesSeparatedBy( pathSeparatorChar );
+            }
+        };
+
+        parser.printHelpOn( System.out );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/examples/Level.java b/third_party/java/jopt-simple/src/test/java/joptsimple/examples/Level.java
new file mode 100644
index 0000000..b3d95d4
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/examples/Level.java
@@ -0,0 +1,7 @@
+package joptsimple.examples;
+
+public enum Level {
+    WARNING,
+    INFO,
+    DEBUG
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/examples/LongOptionsTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/examples/LongOptionsTest.java
new file mode 100644
index 0000000..30fac0c
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/examples/LongOptionsTest.java
@@ -0,0 +1,21 @@
+package joptsimple.examples;
+
+import joptsimple.OptionParser;
+import joptsimple.OptionSet;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+public class LongOptionsTest {
+    @Test
+    public void acceptsLongOptions() {
+        OptionParser parser = new OptionParser();
+        parser.accepts( "flag" );
+        parser.accepts( "verbose" );
+
+        OptionSet options = parser.parse( "--flag" );
+
+        assertTrue( options.has( "flag" ) );
+        assertFalse( options.has( "verbose" ) );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/examples/LongOptionsWithArgumentPositioningTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/examples/LongOptionsWithArgumentPositioningTest.java
new file mode 100644
index 0000000..1cf018b
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/examples/LongOptionsWithArgumentPositioningTest.java
@@ -0,0 +1,26 @@
+package joptsimple.examples;
+
+import joptsimple.OptionParser;
+import joptsimple.OptionSet;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+public class LongOptionsWithArgumentPositioningTest {
+    @Test
+    public void allowsDifferentFormsOfPairingArgumentWithOption() {
+        OptionParser parser = new OptionParser();
+        parser.accepts( "count" ).withRequiredArg();
+        parser.accepts( "level" ).withOptionalArg();
+
+        OptionSet options = parser.parse( "--count", "4", "--level=3" );
+
+        assertTrue( options.has( "count" ) );
+        assertTrue( options.hasArgument( "count" ) );
+        assertEquals( "4", options.valueOf( "count" ) );
+
+        assertTrue( options.has( "level" ) );
+        assertTrue( options.hasArgument( "level" ) );
+        assertEquals( "3", options.valueOf( "level" ) );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/examples/LongOptionsWithArgumentsTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/examples/LongOptionsWithArgumentsTest.java
new file mode 100644
index 0000000..38ef67c
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/examples/LongOptionsWithArgumentsTest.java
@@ -0,0 +1,34 @@
+package joptsimple.examples;
+
+import static java.util.Arrays.*;
+import static java.util.Collections.*;
+
+import joptsimple.OptionParser;
+import joptsimple.OptionSet;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+public class LongOptionsWithArgumentsTest {
+    @Test
+    public void supportsLongOptionsWithArgumentsAndAbbreviations() {
+        OptionParser parser = new OptionParser();
+        parser.accepts( "flag" );
+        parser.accepts( "count" ).withRequiredArg();
+        parser.accepts( "level" ).withOptionalArg();
+
+        OptionSet options = parser.parse( "-flag", "--co", "3", "--lev" );
+
+        assertTrue( options.has( "flag" ) );
+
+        assertTrue( options.has( "count" ) );
+        assertTrue( options.hasArgument( "count" ) );
+        assertEquals( "3", options.valueOf( "count" ) );
+        assertEquals( asList( "3" ), options.valuesOf( "count" ) );
+
+        assertTrue( options.has( "level" ) );
+        assertFalse( options.hasArgument( "level" ) );
+        assertNull( options.valueOf( "level" ) );
+        assertEquals( emptyList(), options.valuesOf( "level" ) );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/examples/MultipleDelimitedArgumentsTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/examples/MultipleDelimitedArgumentsTest.java
new file mode 100644
index 0000000..47eefd0
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/examples/MultipleDelimitedArgumentsTest.java
@@ -0,0 +1,29 @@
+package joptsimple.examples;
+
+import java.io.File;
+
+import static java.io.File.*;
+import static java.util.Arrays.*;
+
+import joptsimple.OptionParser;
+import joptsimple.OptionSet;
+import joptsimple.OptionSpec;
+import org.junit.Test;
+
+import static joptsimple.examples.Strings.*;
+import static org.junit.Assert.*;
+
+public class MultipleDelimitedArgumentsTest {
+    @Test
+    public void supportsMultipleDelimitedArguments() {
+        OptionParser parser = new OptionParser();
+        OptionSpec<File> path = parser.accepts( "path" ).withRequiredArg().ofType( File.class )
+            .withValuesSeparatedBy( pathSeparatorChar );
+
+        OptionSet options = parser.parse( "--path", join( pathSeparatorChar, "/tmp", "/var", "/opt" ) );
+
+        assertTrue( options.has( path ) );
+        assertTrue( options.hasArgument( path ) );
+        assertEquals( asList( new File( "/tmp" ), new File( "/var" ), new File( "/opt" ) ), options.valuesOf( path ) );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/examples/OptionArgumentConverterTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/examples/OptionArgumentConverterTest.java
new file mode 100644
index 0000000..5841794
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/examples/OptionArgumentConverterTest.java
@@ -0,0 +1,49 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2014 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple.examples;
+
+import joptsimple.OptionParser;
+import joptsimple.OptionSet;
+import org.joda.time.LocalDate;
+import org.junit.Test;
+
+import static joptsimple.util.DateConverter.*;
+import static joptsimple.util.RegexMatcher.*;
+import static org.junit.Assert.*;
+
+public class OptionArgumentConverterTest {
+    @Test
+    public void usesConvertersOnOptionArgumentsWhenTold() {
+        OptionParser parser = new OptionParser();
+        parser.accepts( "birthdate" ).withRequiredArg().withValuesConvertedBy( datePattern( "MM/dd/yy" ) );
+        parser.accepts( "ssn" ).withRequiredArg().withValuesConvertedBy( regex( "\\d{3}-\\d{2}-\\d{4}" ));
+
+        OptionSet options = parser.parse( "--birthdate", "02/24/05", "--ssn", "123-45-6789" );
+
+        assertEquals( new LocalDate( 2005, 2, 24 ).toDate(), options.valueOf( "birthdate" ) );
+        assertEquals( "123-45-6789", options.valueOf( "ssn" ) );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/examples/OptionArgumentValueTypeTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/examples/OptionArgumentValueTypeTest.java
new file mode 100644
index 0000000..2f8e51c
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/examples/OptionArgumentValueTypeTest.java
@@ -0,0 +1,27 @@
+package joptsimple.examples;
+
+import joptsimple.OptionParser;
+import joptsimple.OptionSet;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+public class OptionArgumentValueTypeTest {
+    @Test
+    public void convertsArgumentsToJavaValueTypes() {
+        OptionParser parser = new OptionParser();
+        parser.accepts( "flag" );
+        parser.accepts( "count" ).withRequiredArg().ofType( Integer.class );
+        parser.accepts( "level" ).withOptionalArg().ofType( Level.class );
+
+        OptionSet options = parser.parse( "--count", "3", "--level", "DEBUG" );
+
+        assertTrue( options.has( "count" ) );
+        assertTrue( options.hasArgument( "count" ) );
+        assertEquals( 3, options.valueOf( "count" ) );
+
+        assertTrue( options.has( "level" ) );
+        assertTrue( options.hasArgument( "level" ) );
+        assertEquals( Level.DEBUG, options.valueOf( "level" ) );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/examples/OptionSynonymTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/examples/OptionSynonymTest.java
new file mode 100644
index 0000000..336c4d0
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/examples/OptionSynonymTest.java
@@ -0,0 +1,30 @@
+package joptsimple.examples;
+
+import java.util.List;
+
+import static java.util.Arrays.*;
+
+import joptsimple.OptionParser;
+import joptsimple.OptionSet;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+public class OptionSynonymTest {
+    @Test
+    public void supportsOptionSynonyms() {
+        OptionParser parser = new OptionParser();
+        List<String> synonyms = asList( "message", "blurb", "greeting" );
+        parser.acceptsAll( synonyms ).withRequiredArg();
+        String expectedMessage = "Hello";
+
+        OptionSet options = parser.parse( "--message", expectedMessage );
+
+        for ( String each : synonyms ) {
+            assertTrue( each, options.has( each ) );
+            assertTrue( each, options.hasArgument( each ) );
+            assertEquals( each, expectedMessage, options.valueOf( each ) );
+            assertEquals( each, asList( expectedMessage ), options.valuesOf( each ) );
+        }
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/examples/PosixlyCorrectTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/examples/PosixlyCorrectTest.java
new file mode 100644
index 0000000..7acb957
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/examples/PosixlyCorrectTest.java
@@ -0,0 +1,37 @@
+package joptsimple.examples;
+
+import static java.util.Arrays.*;
+import static java.util.Collections.*;
+
+import joptsimple.OptionParser;
+import joptsimple.OptionSet;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+public class PosixlyCorrectTest {
+    @Test
+    public void supportsPosixlyCorrectBehavior() {
+        OptionParser parser = new OptionParser( "i:j::k" );
+        String[] arguments = { "-ibar", "-i", "junk", "xyz", "-jixnay", "foo", "-k", "blah", "--", "bah" };
+
+        OptionSet options = parser.parse( arguments );
+
+        assertTrue( options.has( "i" ) );
+        assertTrue( options.has( "j" ) );
+        assertTrue( options.has( "k" ) );
+        assertEquals( asList( "bar", "junk" ), options.valuesOf( "i" ) );
+        assertEquals( asList( "ixnay" ), options.valuesOf( "j" ) );
+        assertEquals( asList( "xyz", "foo", "blah", "bah" ), options.nonOptionArguments() );
+
+        parser.posixlyCorrect( true );
+        options = parser.parse( arguments );
+
+        assertTrue( options.has( "i" ) );
+        assertFalse( options.has( "j" ) );
+        assertFalse( options.has( "k" ) );
+        assertEquals( asList( "bar", "junk" ), options.valuesOf( "i" ) );
+        assertEquals( emptyList(), options.valuesOf( "j" ) );
+        assertEquals( asList( "xyz", "-jixnay", "foo", "-k", "blah", "--", "bah" ), options.nonOptionArguments() );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/examples/RequiredIfExample.java b/third_party/java/jopt-simple/src/test/java/joptsimple/examples/RequiredIfExample.java
new file mode 100644
index 0000000..c062fff
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/examples/RequiredIfExample.java
@@ -0,0 +1,14 @@
+package joptsimple.examples;
+
+import joptsimple.OptionParser;
+
+public class RequiredIfExample {
+    public static void main( String[] args ) {
+        OptionParser parser = new OptionParser();
+        parser.accepts( "ftp" );
+        parser.accepts( "username" ).requiredIf( "ftp" ).withRequiredArg();
+        parser.accepts( "password" ).requiredIf( "ftp" ).withRequiredArg();
+
+        parser.parse( "--ftp" );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/examples/RequiredOptionsTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/examples/RequiredOptionsTest.java
new file mode 100644
index 0000000..5a40ea2
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/examples/RequiredOptionsTest.java
@@ -0,0 +1,49 @@
+package joptsimple.examples;
+
+import joptsimple.OptionException;
+import joptsimple.OptionParser;
+import joptsimple.OptionSet;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+public class RequiredOptionsTest {
+    @Test( expected = OptionException.class )
+    public void allowsSpecificationOfRequiredOptions() {
+        OptionParser parser = new OptionParser() {
+            {
+                accepts( "userid" ).withRequiredArg().required();
+                accepts( "password" ).withRequiredArg().required();
+            }
+        };
+
+        parser.parse( "--userid", "bob" );
+    }
+
+    @Test
+    public void aHelpOptionMeansRequiredOptionsNeedNotBePresent() {
+        OptionParser parser = new OptionParser() {
+            {
+                accepts( "userid" ).withRequiredArg().required();
+                accepts( "password" ).withRequiredArg().required();
+                accepts( "help" ).forHelp();
+            }
+        };
+
+        OptionSet options = parser.parse( "--help" );
+        assertTrue( options.has( "help" ) );
+    }
+    
+    @Test( expected = OptionException.class )
+    public void missingHelpOptionMeansRequiredOptionsMustBePresent() {
+        OptionParser parser = new OptionParser() {
+            {
+                accepts( "userid" ).withRequiredArg().required();
+                accepts( "password" ).withRequiredArg().required();
+                accepts( "help" ).forHelp();
+            }
+        };
+
+        parser.parse( "" );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/examples/RequiredUnlessExample.java b/third_party/java/jopt-simple/src/test/java/joptsimple/examples/RequiredUnlessExample.java
new file mode 100644
index 0000000..f55f6ca
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/examples/RequiredUnlessExample.java
@@ -0,0 +1,14 @@
+package joptsimple.examples;
+
+import joptsimple.OptionParser;
+
+public class RequiredUnlessExample {
+    public static void main( String[] args ) {
+        OptionParser parser = new OptionParser();
+        parser.accepts( "anonymous" );
+        parser.accepts( "username" ).requiredUnless( "anonymous" ).withRequiredArg();
+        parser.accepts( "password" ).requiredUnless( "anonymous" ).withRequiredArg();
+
+        parser.parse( "--anonymous" );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/examples/ShortOptionsClusteringTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/examples/ShortOptionsClusteringTest.java
new file mode 100644
index 0000000..9143e45
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/examples/ShortOptionsClusteringTest.java
@@ -0,0 +1,21 @@
+package joptsimple.examples;
+
+import joptsimple.OptionParser;
+import joptsimple.OptionSet;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+public class ShortOptionsClusteringTest {
+    @Test
+    public void allowsClusteringShortOptions() {
+        OptionParser parser = new OptionParser( "aBcd" );
+
+        OptionSet options = parser.parse( "-cdBa" );
+
+        assertTrue( options.has( "a" ) );
+        assertTrue( options.has( "B" ) );
+        assertTrue( options.has( "c" ) );
+        assertTrue( options.has( "d" ) );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/examples/ShortOptionsClusteringWithArgumentTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/examples/ShortOptionsClusteringWithArgumentTest.java
new file mode 100644
index 0000000..695f6ce
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/examples/ShortOptionsClusteringWithArgumentTest.java
@@ -0,0 +1,24 @@
+package joptsimple.examples;
+
+import joptsimple.OptionParser;
+import joptsimple.OptionSet;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+public class ShortOptionsClusteringWithArgumentTest {
+    @Test
+    public void allowsClusteringShortOptionsThatAcceptArguments() {
+        OptionParser parser = new OptionParser();
+        parser.accepts( "a" );
+        parser.accepts( "B" );
+        parser.accepts( "c" ).withRequiredArg();
+
+        OptionSet options = parser.parse( "-aBcfoo" );
+
+        assertTrue( options.has( "a" ) );
+        assertTrue( options.has( "B" ) );
+        assertTrue( options.has( "c" ) );
+        assertEquals( "foo", options.valueOf( "c" ) );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/examples/ShortOptionsTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/examples/ShortOptionsTest.java
new file mode 100644
index 0000000..4e3487c
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/examples/ShortOptionsTest.java
@@ -0,0 +1,21 @@
+package joptsimple.examples;
+
+import joptsimple.OptionParser;
+import joptsimple.OptionSet;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+public class ShortOptionsTest {
+    @Test
+    public void supportsShortOptions() {
+        OptionParser parser = new OptionParser( "aB?*." );
+
+        OptionSet options = parser.parse( "-a", "-B", "-?" );
+
+        assertTrue( options.has( "a" ) );
+        assertTrue( options.has( "B" ) );
+        assertTrue( options.has( "?" ) );
+        assertFalse( options.has( "." ) );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/examples/ShortOptionsWithArgumentPositioningTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/examples/ShortOptionsWithArgumentPositioningTest.java
new file mode 100644
index 0000000..720253e
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/examples/ShortOptionsWithArgumentPositioningTest.java
@@ -0,0 +1,28 @@
+package joptsimple.examples;
+
+import joptsimple.OptionParser;
+import joptsimple.OptionSet;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+public class ShortOptionsWithArgumentPositioningTest {
+    @Test
+    public void allowsDifferentFormsOfPairingArgumentWithOption() {
+        OptionParser parser = new OptionParser( "a:b:c::" );
+
+        OptionSet options = parser.parse( "-a", "foo", "-bbar", "-c=baz" );
+
+        assertTrue( options.has( "a" ) );
+        assertTrue( options.hasArgument( "a" ) );
+        assertEquals( "foo", options.valueOf( "a" ) );
+
+        assertTrue( options.has( "b" ) );
+        assertTrue( options.hasArgument( "b" ) );
+        assertEquals( "bar", options.valueOf( "b" ) );
+
+        assertTrue( options.has( "c" ) );
+        assertTrue( options.hasArgument( "c" ) );
+        assertEquals( "baz", options.valueOf( "c" ) );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/examples/ShortOptionsWithArgumentsTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/examples/ShortOptionsWithArgumentsTest.java
new file mode 100644
index 0000000..3ee7911
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/examples/ShortOptionsWithArgumentsTest.java
@@ -0,0 +1,31 @@
+package joptsimple.examples;
+
+import static java.util.Arrays.*;
+import static java.util.Collections.*;
+
+import joptsimple.OptionParser;
+import joptsimple.OptionSet;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+public class ShortOptionsWithArgumentsTest {
+    @Test
+    public void allowsOptionsToAcceptArguments() {
+        OptionParser parser = new OptionParser( "fc:q::" );
+
+        OptionSet options = parser.parse( "-f", "-c", "foo", "-q" );
+
+        assertTrue( options.has( "f" ) );
+
+        assertTrue( options.has( "c" ) );
+        assertTrue( options.hasArgument( "c" ) );
+        assertEquals( "foo", options.valueOf( "c" ) );
+        assertEquals( asList( "foo" ), options.valuesOf( "c" ) );
+
+        assertTrue( options.has( "q" ) );
+        assertFalse( options.hasArgument( "q" ) );
+        assertNull( options.valueOf( "q" ) );
+        assertEquals( emptyList(), options.valuesOf( "q" ) );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/examples/ShortOptionsWithMultipleArgumentsForSingleOptionTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/examples/ShortOptionsWithMultipleArgumentsForSingleOptionTest.java
new file mode 100644
index 0000000..8f113f8
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/examples/ShortOptionsWithMultipleArgumentsForSingleOptionTest.java
@@ -0,0 +1,31 @@
+package joptsimple.examples;
+
+import static java.util.Arrays.*;
+
+import joptsimple.OptionException;
+import joptsimple.OptionParser;
+import joptsimple.OptionSet;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import static org.junit.Assert.*;
+import static org.junit.rules.ExpectedException.*;
+
+public class ShortOptionsWithMultipleArgumentsForSingleOptionTest {
+    @Rule public final ExpectedException thrown = none();
+
+    @Test
+    public void allowsMultipleValuesForAnOption() {
+        OptionParser parser = new OptionParser( "a:" );
+
+        OptionSet options = parser.parse( "-a", "foo", "-abar", "-a=baz" );
+
+        assertTrue( options.has( "a" ) );
+        assertTrue( options.hasArgument( "a" ) );
+        assertEquals( asList( "foo", "bar", "baz" ), options.valuesOf( "a" ) );
+
+        thrown.expect( OptionException.class );
+        options.valueOf( "a" );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/examples/SignallingEndOfOptionsTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/examples/SignallingEndOfOptionsTest.java
new file mode 100644
index 0000000..9fd1fae
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/examples/SignallingEndOfOptionsTest.java
@@ -0,0 +1,31 @@
+package joptsimple.examples;
+
+import static java.util.Arrays.*;
+
+import joptsimple.OptionParser;
+import joptsimple.OptionSet;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+public class SignallingEndOfOptionsTest {
+    @Test
+    public void doubleHyphenSignalsEndOfOptions() {
+        OptionParser parser = new OptionParser( "ab:c::de:f::" );
+
+        OptionSet options = parser.parse( "-a", "-b=foo", "-c=bar", "--", "-d", "-e", "baz", "-f", "biz" );
+
+        assertTrue( options.has( "a" ) );
+        assertFalse( options.hasArgument( "a" ) );
+        assertTrue( options.has( "b" ) );
+        assertTrue( options.hasArgument( "b" ) );
+        assertEquals( asList( "foo" ), options.valuesOf( "b" ) );
+        assertTrue( options.has( "c" ) );
+        assertTrue( options.hasArgument( "c" ) );
+        assertEquals( asList( "bar" ), options.valuesOf( "c" ) );
+        assertFalse( options.has( "d" ) );
+        assertFalse( options.has( "e" ) );
+        assertFalse( options.has( "f" ) );
+        assertEquals( asList( "-d", "-e", "baz", "-f", "biz" ), options.nonOptionArguments() );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/examples/SpecialOptionalArgumentHandlingTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/examples/SpecialOptionalArgumentHandlingTest.java
new file mode 100644
index 0000000..4f05559
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/examples/SpecialOptionalArgumentHandlingTest.java
@@ -0,0 +1,31 @@
+package joptsimple.examples;
+
+import static java.util.Arrays.*;
+import static java.util.Collections.*;
+
+import joptsimple.OptionParser;
+import joptsimple.OptionSet;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+public class SpecialOptionalArgumentHandlingTest {
+    @Test
+    public void handlesNegativeNumberOptionalArguments() {
+        OptionParser parser = new OptionParser();
+        parser.accepts( "a" ).withOptionalArg().ofType( Integer.class );
+        parser.accepts( "2" );
+
+        OptionSet options = parser.parse( "-a", "-2" );
+
+        assertTrue( options.has( "a" ) );
+        assertFalse( options.has( "2" ) );
+        assertEquals( asList( -2 ), options.valuesOf( "a" ) );
+
+        options = parser.parse( "-2", "-a" );
+
+        assertTrue( options.has( "a" ) );
+        assertTrue( options.has( "2" ) );
+        assertEquals( emptyList(), options.valuesOf( "a" ) );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/examples/Strings.java b/third_party/java/jopt-simple/src/test/java/joptsimple/examples/Strings.java
new file mode 100644
index 0000000..4323506
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/examples/Strings.java
@@ -0,0 +1,19 @@
+package joptsimple.examples;
+
+import java.util.Iterator;
+
+import static java.util.Arrays.*;
+
+public class Strings {
+    public static String join( char delimiter, String... pieces ) {
+        StringBuilder builder = new StringBuilder();
+
+        for ( Iterator<String> iter = asList( pieces ).iterator(); iter.hasNext(); ) {
+            builder.append( iter.next() );
+            if ( iter.hasNext() )
+                builder.append( delimiter );
+        }
+
+        return builder.toString();
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/examples/TypesafeOptionArgumentRetrievalTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/examples/TypesafeOptionArgumentRetrievalTest.java
new file mode 100644
index 0000000..93cd0a5
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/examples/TypesafeOptionArgumentRetrievalTest.java
@@ -0,0 +1,45 @@
+package joptsimple.examples;
+
+import java.io.File;
+
+import static java.util.Arrays.*;
+
+import joptsimple.OptionParser;
+import joptsimple.OptionSet;
+import joptsimple.OptionSpec;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+public class TypesafeOptionArgumentRetrievalTest {
+    @Test
+    public void allowsTypesafeRetrievalOfOptionArguments() {
+        OptionParser parser = new OptionParser();
+        OptionSpec<Integer> count = parser.accepts( "count" ).withRequiredArg().ofType( Integer.class );
+        OptionSpec<File> outputDir = parser.accepts( "output-dir" ).withOptionalArg().ofType( File.class );
+        OptionSpec<Void> verbose = parser.accepts( "verbose" );
+        OptionSpec<File> files = parser.nonOptions().ofType( File.class );
+
+        OptionSet options = parser.parse( "--count", "3", "--output-dir", "/tmp", "--verbose", "a.txt", "b.txt" );
+
+        assertTrue( options.has( verbose ) );
+
+        assertTrue( options.has( count ) );
+        assertTrue( options.hasArgument( count ) );
+        Integer expectedCount = 3;
+        assertEquals( expectedCount, options.valueOf( count ) );
+        assertEquals( expectedCount, count.value( options ) );
+        assertEquals( asList( expectedCount ), options.valuesOf( count ) );
+        assertEquals( asList( expectedCount ), count.values( options ) );
+        assertEquals( asList( new File( "a.txt" ), new File( "b.txt" ) ), options.valuesOf( files ) );
+
+        assertTrue( options.has( outputDir ) );
+        assertTrue( options.hasArgument( outputDir ) );
+        File expectedFile = new File( "/tmp" );
+        assertEquals( expectedFile, options.valueOf( outputDir ) );
+        assertEquals( expectedFile, outputDir.value( options ) );
+        assertEquals( asList( expectedFile ), options.valuesOf( outputDir ) );
+        assertEquals( asList( expectedFile ), outputDir.values( options ) );
+        assertEquals( asList( new File( "a.txt" ), new File( "b.txt" ) ), files.values( options ) );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/examples/UnrecognizedOptionsAllowedTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/examples/UnrecognizedOptionsAllowedTest.java
new file mode 100644
index 0000000..6dc9972
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/examples/UnrecognizedOptionsAllowedTest.java
@@ -0,0 +1,23 @@
+package joptsimple.examples;
+
+import joptsimple.OptionParser;
+import joptsimple.OptionSet;
+import org.junit.Test;
+
+import static java.util.Arrays.*;
+import static org.junit.Assert.*;
+
+public class UnrecognizedOptionsAllowedTest {
+    @Test
+    public void acceptsLongOptions() {
+        OptionParser parser = new OptionParser();
+        parser.allowsUnrecognizedOptions();
+        parser.accepts( "f" );
+
+        OptionSet options = parser.parse( "-f", "-d" );
+
+        assertTrue( options.has( "f" ) );
+        assertFalse( options.has( "d" ) );
+        assertEquals( asList( "-d" ), options.nonOptionArguments() );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/examples/ant/filters/HTMLEntitifier.java b/third_party/java/jopt-simple/src/test/java/joptsimple/examples/ant/filters/HTMLEntitifier.java
new file mode 100644
index 0000000..0eabe4f
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/examples/ant/filters/HTMLEntitifier.java
@@ -0,0 +1,80 @@
+package joptsimple.examples.ant.filters;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.tools.ant.filters.BaseFilterReader;
+import org.apache.tools.ant.filters.ChainableReader;
+
+/**
+ * Ant filter class that transforms HTML special characters into their equivalent entities.
+ *
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class HTMLEntitifier extends BaseFilterReader implements ChainableReader {
+    private static final Map<Integer, String> ENTITIES = new HashMap<>();
+
+    static {
+        ENTITIES.put( (int) '<', "&lt;" );
+        ENTITIES.put( (int) '>', "&gt;" );
+        ENTITIES.put( (int) '"', "&quot;" );
+        ENTITIES.put( (int) '&', "&amp;" );
+    }
+
+    private String replacementData;
+    private int replacementIndex = -1;
+
+    /**
+     * Creates "dummy" instances.
+     */
+    public HTMLEntitifier() {
+        // empty on purpose
+    }
+
+    /**
+     * @param source where the data to filter comes from
+     */
+    public HTMLEntitifier( Reader source ) {
+        super( source );
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Reader chain( Reader source ) {
+        HTMLEntitifier newFilter = new HTMLEntitifier( source );
+        newFilter.setInitialized( true );
+
+        return newFilter;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int read() throws IOException {
+        if ( !getInitialized() )
+            setInitialized( true );
+
+        if ( replacementIndex > -1 ) {
+            int ch = replacementData.charAt( replacementIndex++ );
+
+            if ( replacementIndex >= replacementData.length() )
+                replacementIndex = -1;
+
+            return ch;
+        }
+
+        int nextChar = in.read();
+
+        if ( ENTITIES.containsKey( nextChar ) ) {
+            replacementData = ENTITIES.get( nextChar );
+            replacementIndex = 1;
+            return replacementData.charAt( 0 );
+        }
+
+        return nextChar;
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/internal/AbbreviationMapTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/internal/AbbreviationMapTest.java
new file mode 100644
index 0000000..abd4302
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/internal/AbbreviationMapTest.java
@@ -0,0 +1,361 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple.internal;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class AbbreviationMapTest {
+    private AbbreviationMap<String> abbreviations;
+
+    @Before
+    public void setUp() {
+        abbreviations = new AbbreviationMap<>();
+    }
+
+    @Test( expected = NullPointerException.class )
+    public void nullValuesAreIllegal() {
+        abbreviations.put( "cannotAddNullValue", null );
+    }
+
+    @Test( expected = NullPointerException.class )
+    public void nullKeysAreIllegalToAdd() {
+        abbreviations.put( null, "-1" );
+    }
+
+    @Test( expected = NullPointerException.class )
+    public void nullKeysAreIllegalToCheckForContains() {
+        abbreviations.contains( null );
+    }
+
+    @Test( expected = IllegalArgumentException.class )
+    public void zeroLengthKeysAreIllegalToAdd() {
+        abbreviations.put( "", "1" );
+    }
+
+    @Test( expected = NullPointerException.class )
+    public void nullKeysAreIllegalToRemove() {
+        abbreviations.remove( null );
+    }
+
+    @Test( expected = IllegalArgumentException.class )
+    public void zeroLengthKeysAreIllegalToRemove() {
+        abbreviations.remove( "" );
+    }
+
+    @Test
+    public void empty() {
+        assertFalse( abbreviations.contains( "boo" ) );
+        assertNull( abbreviations.get( "boo" ) );
+    }
+
+    @Test
+    public void addingOne() {
+        abbreviations.put( "box", "2" );
+
+        assertTrue( abbreviations.contains( "box" ) );
+        assertEquals( "2", abbreviations.get( "box" ) );
+        assertTrue( abbreviations.contains( "bo" ) );
+        assertEquals( "2", abbreviations.get( "bo" ) );
+        assertTrue( abbreviations.contains( "b" ) );
+        assertEquals( "2", abbreviations.get( "b" ) );
+    }
+
+    @Test
+    public void addingManyWithNoCommonPrefix() {
+        abbreviations.put( "box", "1" );
+        abbreviations.put( "cat", "2" );
+        abbreviations.put( "dog", "3" );
+
+        assertTrue( abbreviations.contains( "box" ) );
+        assertEquals( "1", abbreviations.get( "box" ) );
+        assertTrue( abbreviations.contains( "bo" ) );
+        assertEquals( "1", abbreviations.get( "bo" ) );
+        assertTrue( abbreviations.contains( "b" ) );
+        assertEquals( "1", abbreviations.get( "b" ) );
+        assertTrue( abbreviations.contains( "cat" ) );
+        assertEquals( "2", abbreviations.get( "cat" ) );
+        assertTrue( abbreviations.contains( "ca" ) );
+        assertEquals( "2", abbreviations.get( "ca" ) );
+        assertTrue( abbreviations.contains( "c" ) );
+        assertEquals( "2", abbreviations.get( "c" ) );
+        assertTrue( abbreviations.contains( "dog" ) );
+        assertEquals( "3", abbreviations.get( "dog" ) );
+        assertTrue( abbreviations.contains( "do" ) );
+        assertEquals( "3", abbreviations.get( "do" ) );
+        assertTrue( abbreviations.contains( "d" ) );
+        assertEquals( "3", abbreviations.get( "d" ) );
+    }
+
+    @Test
+    public void addingTwoWithCommonPrefix() {
+        abbreviations.put( "box", "3" );
+        abbreviations.put( "boy", "4" );
+
+        assertTrue( abbreviations.contains( "box" ) );
+        assertEquals( "3", abbreviations.get( "box" ) );
+        assertTrue( abbreviations.contains( "boy" ) );
+        assertEquals( "4", abbreviations.get( "boy" ) );
+        assertFalse( abbreviations.contains( "bo" ) );
+        assertNull( abbreviations.get( "bo" ) );
+        assertFalse( abbreviations.contains( "b" ) );
+        assertNull( abbreviations.get( "b" ) );
+    }
+
+    @Test
+    public void addingThreeWithSuccessivelySmallerPrefixes() {
+        abbreviations.put( "boy", "3" );
+        abbreviations.put( "bo", "2" );
+        abbreviations.put( "b", "1" );
+
+        assertTrue( abbreviations.contains( "boy" ) );
+        assertEquals( "3", abbreviations.get( "boy" ) );
+        assertTrue( abbreviations.contains( "bo" ) );
+        assertEquals( "2", abbreviations.get( "bo" ) );
+        assertTrue( abbreviations.contains( "b" ) );
+        assertEquals( "1", abbreviations.get( "b" ) );
+    }
+
+    @Test
+    public void addingThreeWithSuccessivelyLargerPrefixes() {
+        abbreviations.put( "b", "1" );
+        abbreviations.put( "bo", "2" );
+        abbreviations.put( "boy", "3" );
+
+        assertTrue( abbreviations.contains( "boy" ) );
+        assertEquals( "3", abbreviations.get( "boy" ) );
+        assertTrue( abbreviations.contains( "bo" ) );
+        assertEquals( "2", abbreviations.get( "bo" ) );
+        assertTrue( abbreviations.contains( "b" ) );
+        assertEquals( "1", abbreviations.get( "b" ) );
+    }
+
+    @Test
+    public void addingThreeWithMixOfPrefixSize() {
+        abbreviations.put( "boy", "3" );
+        abbreviations.put( "b", "1" );
+        abbreviations.put( "bo", "2" );
+
+        assertTrue( abbreviations.contains( "boy" ) );
+        assertEquals( "3", abbreviations.get( "boy" ) );
+        assertTrue( abbreviations.contains( "bo" ) );
+        assertEquals( "2", abbreviations.get( "bo" ) );
+        assertTrue( abbreviations.contains( "b" ) );
+        assertEquals( "1", abbreviations.get( "b" ) );
+    }
+
+    @Test
+    public void addingOneThenReplacing() {
+        abbreviations.put( "box", "2" );
+
+        assertTrue( abbreviations.contains( "box" ) );
+        assertEquals( "2", abbreviations.get( "box" ) );
+        assertTrue( abbreviations.contains( "bo" ) );
+        assertEquals( "2", abbreviations.get( "bo" ) );
+        assertTrue( abbreviations.contains( "b" ) );
+        assertEquals( "2", abbreviations.get( "b" ) );
+
+        abbreviations.put( "box", "3" );
+
+        assertTrue( abbreviations.contains( "box" ) );
+        assertEquals( "3", abbreviations.get( "box" ) );
+        assertTrue( abbreviations.contains( "bo" ) );
+        assertEquals( "3", abbreviations.get( "bo" ) );
+        assertTrue( abbreviations.contains( "b" ) );
+        assertEquals( "3", abbreviations.get( "b" ) );
+    }
+
+    @Test
+    public void removingNonExistentKeyHasNoEffect() {
+        abbreviations.put( "box", "2" );
+
+        abbreviations.remove( "cat" );
+
+        assertTrue( abbreviations.contains( "box" ) );
+        assertEquals( "2", abbreviations.get( "box" ) );
+        assertTrue( abbreviations.contains( "bo" ) );
+        assertEquals( "2", abbreviations.get( "bo" ) );
+        assertTrue( abbreviations.contains( "b" ) );
+        assertEquals( "2", abbreviations.get( "b" ) );
+    }
+
+    @Test
+    public void removingSingleKey() {
+        abbreviations.put( "box", "3" );
+
+        abbreviations.remove( "box" );
+
+        assertFalse( abbreviations.contains( "box" ) );
+        assertNull( abbreviations.get( "box" ) );
+        assertFalse( abbreviations.contains( "bo" ) );
+        assertNull( abbreviations.get( "bo" ) );
+        assertFalse( abbreviations.contains( "b" ) );
+        assertNull( abbreviations.get( "b" ) );
+    }
+
+    @Test
+    public void cannotRemoveByUniqueAbbreviation() {
+        abbreviations.put( "box", "4" );
+
+        abbreviations.remove( "bo" );
+        abbreviations.remove( "b" );
+
+        assertTrue( abbreviations.contains( "box" ) );
+        assertEquals( "4", abbreviations.get( "box" ) );
+        assertTrue( abbreviations.contains( "bo" ) );
+        assertEquals( "4", abbreviations.get( "bo" ) );
+        assertTrue( abbreviations.contains( "b" ) );
+        assertEquals( "4", abbreviations.get( "b" ) );
+    }
+
+    @Test
+    public void removeKeyWithCommonPrefix() {
+        abbreviations.put( "box", "-1" );
+        abbreviations.put( "boy", "-2" );
+
+        abbreviations.remove( "box" );
+
+        assertFalse( abbreviations.contains( "box" ) );
+        assertNull( abbreviations.get( "box" ) );
+        assertTrue( abbreviations.contains( "boy" ) );
+        assertEquals( "-2", abbreviations.get( "boy" ) );
+        assertTrue( abbreviations.contains( "bo" ) );
+        assertEquals( "-2", abbreviations.get( "bo" ) );
+        assertTrue( abbreviations.contains( "b" ) );
+        assertEquals( "-2", abbreviations.get( "b" ) );
+    }
+
+    @Test
+    public void addKeysWithCommonPrefixThenRemoveNonExistentKeyWithCommonPrefix() {
+        abbreviations.put( "box", "-1" );
+        abbreviations.put( "boy", "-2" );
+
+        abbreviations.remove( "bop" );
+
+        assertTrue( abbreviations.contains( "box" ) );
+        assertEquals( "-1", abbreviations.get( "box" ) );
+        assertTrue( abbreviations.contains( "boy" ) );
+        assertEquals( "-2", abbreviations.get( "boy" ) );
+        assertFalse( abbreviations.contains( "bo" ) );
+        assertNull( abbreviations.get( "bo" ) );
+        assertFalse( abbreviations.contains( "b" ) );
+        assertNull( abbreviations.get( "b" ) );
+    }
+
+    @Test
+    public void addKeysWithCommonPrefixesStairstepStyle() {
+        abbreviations.put( "a", "1" );
+        abbreviations.put( "abc", "2" );
+
+        assertTrue( abbreviations.contains( "a" ) );
+        assertEquals( "1", abbreviations.get( "a" ) );
+        assertTrue( abbreviations.contains( "ab" ) );
+        assertEquals( "2", abbreviations.get( "ab" ) );
+        assertTrue( abbreviations.contains( "abc" ) );
+        assertEquals( "2", abbreviations.get( "abc" ) );
+
+        abbreviations.put( "abcde", "3" );
+
+        assertTrue( abbreviations.contains( "a" ) );
+        assertEquals( "1", abbreviations.get( "a" ) );
+        assertFalse( abbreviations.contains( "ab" ) );
+        assertNull( abbreviations.get( "ab" ) );
+        assertTrue( abbreviations.contains( "abc" ) );
+        assertEquals( "2", abbreviations.get( "abc" ) );
+        assertTrue( abbreviations.contains( "abcd" ) );
+        assertEquals( "3", abbreviations.get( "abcd" ) );
+        assertTrue( abbreviations.contains( "abcde" ) );
+        assertEquals( "3", abbreviations.get( "abcde" ) );
+    }
+
+    @Test
+    public void addKeysWithCommonPrefixesStairstepStyleJumbled() {
+        abbreviations.put( "a", "1" );
+        abbreviations.put( "abcde", "3" );
+        abbreviations.put( "abc", "2" );
+
+        assertTrue( abbreviations.contains( "a" ) );
+        assertEquals( "1", abbreviations.get( "a" ) );
+        assertFalse( abbreviations.contains( "ab" ) );
+        assertNull( abbreviations.get( "ab" ) );
+        assertTrue( abbreviations.contains( "abc" ) );
+        assertEquals( "2", abbreviations.get( "abc" ) );
+        assertTrue( abbreviations.contains( "abcd" ) );
+        assertEquals( "3", abbreviations.get( "abcd" ) );
+        assertTrue( abbreviations.contains( "abcde" ) );
+        assertEquals( "3", abbreviations.get( "abcde" ) );
+    }
+
+    @Test
+    public void multipleKeysWithCommonPrefix() {
+        abbreviations.put( "good", "4" );
+        abbreviations.put( "goodyear", "8" );
+        abbreviations.put( "go", "2" );
+        abbreviations.put( "goodyea", "7" );
+        abbreviations.put( "goodye", "6" );
+
+        assertFalse( abbreviations.contains( "g" ) );
+        assertNull( abbreviations.get( "g" ) );
+        assertTrue( abbreviations.contains( "go" ) );
+        assertEquals( "2", abbreviations.get( "go" ) );
+        assertFalse( abbreviations.contains( "goo" ) );
+        assertNull( abbreviations.get( "goo" ) );
+        assertTrue( abbreviations.contains( "good" ) );
+        assertEquals( "4", abbreviations.get( "good" ) );
+        assertFalse( abbreviations.contains( "goody" ) );
+        assertNull( abbreviations.get( "goody" ) );
+        assertTrue( abbreviations.contains( "goodye" ) );
+        assertEquals( "6", abbreviations.get( "goodye" ) );
+        assertTrue( abbreviations.contains( "goodyea" ) );
+        assertEquals( "7", abbreviations.get( "goodyea" ) );
+        assertTrue( abbreviations.contains( "goodyea" ) );
+        assertEquals( "8", abbreviations.get( "goodyear" ) );
+
+        abbreviations.remove( "goodyea" );
+
+        assertFalse( abbreviations.contains( "g" ) );
+        assertNull( abbreviations.get( "g" ) );
+        assertTrue( abbreviations.contains( "go" ) );
+        assertEquals( "2", abbreviations.get( "go" ) );
+        assertFalse( abbreviations.contains( "goo" ) );
+        assertNull( abbreviations.get( "goo" ) );
+        assertTrue( abbreviations.contains( "good" ) );
+        assertEquals( "4", abbreviations.get( "good" ) );
+        assertFalse( abbreviations.contains( "goody" ) );
+        assertNull( abbreviations.get( "goody" ) );
+        assertTrue( abbreviations.contains( "goodye" ) );
+        assertEquals( "6", abbreviations.get( "goodye" ) );
+        assertTrue( abbreviations.contains( "goodyea" ) );
+        assertEquals( "8", abbreviations.get( "goodyea" ) );
+        assertTrue( abbreviations.contains( "goodyea" ) );
+        assertEquals( "8", abbreviations.get( "goodyear" ) );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/internal/AbbreviationMapToJavaUtilMapTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/internal/AbbreviationMapToJavaUtilMapTest.java
new file mode 100644
index 0000000..8bc0621
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/internal/AbbreviationMapToJavaUtilMapTest.java
@@ -0,0 +1,148 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple.internal;
+
+import static java.util.Collections.*;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class AbbreviationMapToJavaUtilMapTest {
+    private AbbreviationMap<String> abbreviations;
+
+    @Before
+    public void setUp() {
+        abbreviations = new AbbreviationMap<>();
+    }
+
+    @Test
+    public void empty() {
+        assertEquals( emptyMap(), abbreviations.toJavaUtilMap() );
+    }
+
+    @Test
+    public void addingOne() {
+        abbreviations.put( "box", "2" );
+
+        assertEquals( singletonMap( "box", "2" ), abbreviations.toJavaUtilMap() );
+    }
+
+    @Test
+    public void addingManyWithNoCommonPrefix() {
+        abbreviations.put( "box", "1" );
+        abbreviations.put( "cat", "2" );
+        abbreviations.put( "dog", "3" );
+
+        assertEquals( "{box=1, cat=2, dog=3}", abbreviations.toJavaUtilMap().toString() );
+    }
+
+    @Test
+    public void addingTwoWithCommonPrefix() {
+        abbreviations.put( "box", "3" );
+        abbreviations.put( "boy", "4" );
+
+        assertEquals( "{box=3, boy=4}", abbreviations.toJavaUtilMap().toString() );
+    }
+
+    @Test
+    public void addingThreeWithSuccessivelySmallerPrefixes() {
+        abbreviations.put( "boy", "3" );
+        abbreviations.put( "bo", "2" );
+        abbreviations.put( "b", "1" );
+
+        assertEquals( "{b=1, bo=2, boy=3}", abbreviations.toJavaUtilMap().toString() );
+    }
+
+    @Test
+    public void addingThreeWithSuccessivelyLargerPrefixes() {
+        abbreviations.put( "b", "1" );
+        abbreviations.put( "bo", "2" );
+        abbreviations.put( "boy", "3" );
+
+        assertEquals( "{b=1, bo=2, boy=3}", abbreviations.toJavaUtilMap().toString() );
+    }
+
+    @Test
+    public void addingThreeWithMixOfPrefixSize() {
+        abbreviations.put( "boy", "3" );
+        abbreviations.put( "b", "1" );
+        abbreviations.put( "bo", "2" );
+
+        assertEquals( "{b=1, bo=2, boy=3}", abbreviations.toJavaUtilMap().toString() );
+    }
+
+    @Test
+    public void addingOneThenReplacing() {
+        abbreviations.put( "box", "2" );
+        abbreviations.put( "box", "3" );
+
+        assertEquals( "{box=3}", abbreviations.toJavaUtilMap().toString() );
+    }
+
+    @Test
+    public void removeKeyWithCommonPrefix() {
+        abbreviations.put( "box", "-1" );
+        abbreviations.put( "boy", "-2" );
+        abbreviations.remove( "box" );
+
+        assertEquals( "{boy=-2}", abbreviations.toJavaUtilMap().toString() );
+    }
+
+    @Test
+    public void addKeysWithCommonPrefixesStairstepStyle() {
+        abbreviations.put( "a", "1" );
+        abbreviations.put( "abc", "2" );
+        abbreviations.put( "abcde", "3" );
+
+        assertEquals( "{a=1, abc=2, abcde=3}", abbreviations.toJavaUtilMap().toString() );
+    }
+
+    @Test
+    public void addKeysWithCommonPrefixesStairstepStyleJumbled() {
+        abbreviations.put( "a", "1" );
+        abbreviations.put( "abcde", "3" );
+        abbreviations.put( "abc", "2" );
+
+        assertEquals( "{a=1, abc=2, abcde=3}", abbreviations.toJavaUtilMap().toString() );
+    }
+
+    @Test
+    public void multipleKeysWithCommonPrefix() {
+        abbreviations.put( "good", "4" );
+        abbreviations.put( "goodyear", "8" );
+        abbreviations.put( "go", "2" );
+        abbreviations.put( "goodyea", "7" );
+        abbreviations.put( "goodye", "6" );
+        abbreviations.remove( "goodyea" );
+
+        assertEquals( "{go=2, good=4, goodye=6, goodyear=8}", abbreviations.toJavaUtilMap().toString() );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/internal/ClassesUtilityClassTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/internal/ClassesUtilityClassTest.java
new file mode 100644
index 0000000..89f3ea2
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/internal/ClassesUtilityClassTest.java
@@ -0,0 +1,37 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple.internal;
+
+import joptsimple.UtilityClassesUninstantiabilityHarness;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class ClassesUtilityClassTest extends UtilityClassesUninstantiabilityHarness {
+    public ClassesUtilityClassTest() {
+        super( Classes.class );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/internal/MessagesUtilityClassTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/internal/MessagesUtilityClassTest.java
new file mode 100644
index 0000000..a0c1051
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/internal/MessagesUtilityClassTest.java
@@ -0,0 +1,37 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple.internal;
+
+import joptsimple.UtilityClassesUninstantiabilityHarness;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class MessagesUtilityClassTest extends UtilityClassesUninstantiabilityHarness {
+    public MessagesUtilityClassTest() {
+        super( Messages.class );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/internal/Problematic.java b/third_party/java/jopt-simple/src/test/java/joptsimple/internal/Problematic.java
new file mode 100644
index 0000000..9ec93d6
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/internal/Problematic.java
@@ -0,0 +1,51 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple.internal;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+class Problematic {
+    private Problematic() {
+        // no-op
+    }
+
+    Problematic( String s ) {
+        throw new IllegalStateException(s);
+    }
+
+    private static void boo() {
+        // no-op
+    }
+
+    static void mute() {
+        new Problematic().stuff();
+    }
+
+    void stuff() {
+        boo();
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/internal/ReflectionTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/internal/ReflectionTest.java
new file mode 100644
index 0000000..06f9682
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/internal/ReflectionTest.java
@@ -0,0 +1,109 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple.internal;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import static joptsimple.internal.Reflection.*;
+import static org.junit.rules.ExpectedException.*;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class ReflectionTest {
+    @Rule public final ExpectedException thrown = none();
+
+    @Test
+    public void invokingConstructorQuietlyWrapsInstantiationException() throws Exception {
+        Constructor<AbstractProblematic> constructor = AbstractProblematic.class.getDeclaredConstructor();
+
+        thrown.expect( ReflectionException.class );
+        thrown.expectMessage( InstantiationException.class.getName() );
+
+        instantiate( constructor );
+    }
+
+    @Test
+    public void invokingConstructorQuietlyWrapsIllegalAccessException() throws Exception {
+        Constructor<Problematic> constructor = Problematic.class.getDeclaredConstructor();
+
+        thrown.expect( ReflectionException.class );
+        thrown.expectMessage( IllegalAccessException.class.getName() );
+
+        instantiate( constructor );
+    }
+
+    @Test
+    public void invokingConstructorQuietlyWrapsCauseOfInvocationTargetException() throws Exception {
+        Constructor<Problematic> constructor = Problematic.class.getDeclaredConstructor( String.class );
+
+        thrown.expect( ReflectionException.class );
+        thrown.expectMessage( IllegalStateException.class.getName() );
+
+        instantiate( constructor, "arg" );
+    }
+
+    @Test
+    public void invokingConstructorQuietlyWrapsIllegalArgumentException() throws Exception {
+        Constructor<Problematic> constructor = Problematic.class.getDeclaredConstructor(String.class);
+
+        thrown.expect( ReflectionException.class );
+        thrown.expectMessage( IllegalArgumentException.class.getName() );
+
+        instantiate( constructor );
+    }
+
+    @Test
+    public void invokingStaticMethodQuietlyWrapsIllegalAccessException() throws Exception {
+        Method method = Problematic.class.getDeclaredMethod( "boo" );
+
+        thrown.expect( ReflectionException.class );
+        thrown.expectMessage( IllegalAccessException.class.getName() );
+
+        invoke( method );
+    }
+
+    @Test
+    public void invokingStaticMethodQuietlyWrapsIllegalArgumentException() throws Exception {
+        Method method = Problematic.class.getDeclaredMethod( "mute" );
+
+        thrown.expect( ReflectionException.class );
+        thrown.expectMessage( IllegalArgumentException.class.getName() );
+
+        invoke( method, new Object() );
+    }
+
+    private abstract static class AbstractProblematic {
+        protected AbstractProblematic() {
+            // no-op
+        }
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/internal/ReflectionUtilityClassTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/internal/ReflectionUtilityClassTest.java
new file mode 100644
index 0000000..7634a49
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/internal/ReflectionUtilityClassTest.java
@@ -0,0 +1,37 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple.internal;
+
+import joptsimple.UtilityClassesUninstantiabilityHarness;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class ReflectionUtilityClassTest extends UtilityClassesUninstantiabilityHarness {
+    public ReflectionUtilityClassTest() {
+        super( Reflection.class );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/internal/RowEqualsHashCodeTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/internal/RowEqualsHashCodeTest.java
new file mode 100644
index 0000000..e1ad850
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/internal/RowEqualsHashCodeTest.java
@@ -0,0 +1,49 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple.internal;
+
+import org.infinitest.toolkit.StrictEqualsHashCodeTestSupport;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class RowEqualsHashCodeTest extends StrictEqualsHashCodeTestSupport {
+    @Override
+    protected Object equalButDifferentClass() {
+        return new Row( "o", "d" ) {
+        };
+    }
+
+    @Override
+    protected Object equal() throws Exception {
+        return new Row( "o", "d" );
+    }
+
+    @Override
+    protected Object notEqual() throws Exception {
+        return new Row( "o", "e" );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/internal/RowsTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/internal/RowsTest.java
new file mode 100644
index 0000000..c07522d
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/internal/RowsTest.java
@@ -0,0 +1,83 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple.internal;
+
+import org.junit.Test;
+
+import static joptsimple.internal.Strings.*;
+import static org.junit.Assert.*;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class RowsTest {
+    @Test
+    public void optionsAndDescriptionsWithinOverallWidth() {
+        Rows rows = new Rows( 40, 2 );
+        rows.add( "left one", "right one" );
+        rows.add( "another left one", "another right one" );
+
+        assertRows( rows,
+                "left one          right one        ",
+                "another left one  another right one" );
+    }
+
+    @Test
+    public void someOptionsExceedOverallWidth() {
+        Rows rows = new Rows( 40, 2 );
+        rows.add( "left one is pretty freaking long to be over here", "right one" );
+        rows.add( "another left one also has length that is quite excessive", "another right one" );
+
+        assertRows( rows,
+                "left one is pretty  right one        ",
+                "  freaking long to                   ",
+                "  be over here                       ",
+                "another left one    another right one",
+                "  also has length                    ",
+                "  that is quite                      ",
+                "  excessive                          " );
+    }
+
+    @Test
+    public void someDescriptionsExceedOverallWidth() {
+        Rows rows = new Rows( 40, 2 );
+        rows.add( "left one", "right one for the time we have chosen" );
+        rows.add( "another left one", "another right one could be used here instead" );
+
+        assertRows( rows,
+            "left one          right one for the    ",
+            "                    time we have chosen",
+            "another left one  another right one    ",
+            "                    could be used here ",
+            "                    instead            " );
+    }
+
+    private void assertRows( Rows rows, String... expected ) {
+        rows.fitToWidth();
+
+        assertEquals( join( expected, LINE_SEPARATOR ) + LINE_SEPARATOR, rows.render() );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/internal/SimpleOptionNameMapTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/internal/SimpleOptionNameMapTest.java
new file mode 100644
index 0000000..ae9ae0d
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/internal/SimpleOptionNameMapTest.java
@@ -0,0 +1,93 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple.internal;
+
+import java.util.List;
+import java.util.Map;
+
+import org.junit.Test;
+
+import static java.util.Arrays.*;
+
+import static org.junit.Assert.*;
+
+public class SimpleOptionNameMapTest {
+    private static final Integer VALUE = 1;
+    private static final String KEY = "someKey";
+    private static final String KEY2 = "someOtherKey";
+
+    @Test
+    public void putAndContains() {
+        SimpleOptionNameMap<Integer> map = new SimpleOptionNameMap<>();
+        assertFalse( map.contains( KEY ) );
+
+        map.put( KEY, 1 );
+
+        assertTrue( map.contains( KEY ) );
+    }
+
+    @Test
+    public void get() {
+        SimpleOptionNameMap<Integer> map = new SimpleOptionNameMap<>();
+        assertNull( map.get( KEY ) );
+
+        map.put( KEY, VALUE );
+
+        assertEquals( VALUE, map.get( KEY ) );
+    }
+
+    @Test
+    public void putAll() {
+        SimpleOptionNameMap<Integer> map = new SimpleOptionNameMap<>();
+        List<String> keys = asList( KEY, KEY2 );
+
+        map.putAll( keys, VALUE );
+
+        assertEquals( VALUE, map.get( KEY ) );
+        assertEquals( VALUE, map.get( KEY2 ) );
+    }
+
+    @Test
+    public void remove() {
+        SimpleOptionNameMap<Integer> map = new SimpleOptionNameMap<>();
+        map.put( KEY, 1 );
+
+        map.remove( KEY );
+
+        assertFalse( map.contains( KEY ) );
+    }
+
+    @Test
+    public void toJavaUtilMap() {
+        SimpleOptionNameMap<Integer> map = new SimpleOptionNameMap<>();
+        map.put( KEY, VALUE );
+
+        Map<String, Integer> javaUtilMap = map.toJavaUtilMap();
+
+        assertEquals( VALUE, javaUtilMap.get( KEY ) );
+        assertEquals( 1, javaUtilMap.size() );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/internal/StringsUtilityClassTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/internal/StringsUtilityClassTest.java
new file mode 100644
index 0000000..a56b2a5
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/internal/StringsUtilityClassTest.java
@@ -0,0 +1,37 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple.internal;
+
+import joptsimple.UtilityClassesUninstantiabilityHarness;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class StringsUtilityClassTest extends UtilityClassesUninstantiabilityHarness {
+    public StringsUtilityClassTest() {
+        super( Strings.class );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/util/DateConverterTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/util/DateConverterTest.java
new file mode 100644
index 0000000..93baa0ba
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/util/DateConverterTest.java
@@ -0,0 +1,152 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple.util;
+
+import java.text.DateFormat;
+import java.text.FieldPosition;
+import java.text.NumberFormat;
+import java.text.ParsePosition;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import static java.text.DateFormat.*;
+
+import joptsimple.ValueConversionException;
+import joptsimple.ValueConverter;
+import org.joda.time.LocalDate;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import static joptsimple.util.DateConverter.*;
+import static org.hamcrest.CoreMatchers.*;
+import static org.junit.Assert.*;
+import static org.junit.rules.ExpectedException.*;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class DateConverterTest {
+    @Rule public final ExpectedException thrown = none();
+
+    private DateFormat notASimpleDateFormat;
+    private SimpleDateFormat monthDayYear;
+
+    @Before
+    public void setUp() {
+        notASimpleDateFormat = new DateFormat() {
+            private static final long serialVersionUID = -1L;
+
+            {
+                setNumberFormat( NumberFormat.getInstance() );
+            }
+
+            @Override
+            public StringBuffer format( Date date, StringBuffer toAppendTo, FieldPosition fieldPosition ) {
+                return null;
+            }
+
+            @Override
+            public Date parse( String source, ParsePosition pos ) {
+                return null;
+            }
+        };
+
+        monthDayYear = new SimpleDateFormat( "MM/dd/yyyy" );
+    }
+
+    @Test( expected = NullPointerException.class )
+    public void rejectsNullDateFormatter() {
+        new DateConverter( null );
+    }
+
+    @Test
+    public void shouldConvertValuesToDatesUsingADateFormat() {
+        ValueConverter<Date> converter = new DateConverter( monthDayYear );
+
+        assertEquals( new LocalDate( 2009, 1, 24 ).toDate(), converter.convert( "01/24/2009" ) );
+    }
+
+    @Test
+    public void rejectsNonParsableValues() {
+        thrown.expect( ValueConversionException.class );
+
+        new DateConverter( getDateInstance() ).convert( "@(#*^" );
+    }
+
+    @Test
+    public void rejectsValuesThatDoNotEntirelyMatch() {
+        thrown.expect( ValueConversionException.class );
+
+        new DateConverter( monthDayYear ).convert( "12/25/09 00:00:00" );
+    }
+
+    @Test
+    public void shouldCreateSimpleDateFormatConverter() {
+        assertEquals( new LocalDate( 2009, 7, 4 ).toDate(), datePattern( "MM/dd/yyyy" ).convert( "07/04/2009" ) );
+    }
+
+    @Test
+    public void rejectsNullDatePattern() {
+        thrown.expect( NullPointerException.class );
+
+        datePattern( null );
+    }
+
+    @Test
+    public void shouldRaiseExceptionThatContainsDatePatternAndValue() {
+        thrown.expect( ValueConversionException.class );
+        thrown.expectMessage( "qwe" );
+        thrown.expectMessage( monthDayYear.toPattern() );
+
+        new DateConverter( monthDayYear ).convert( "qwe" );
+    }
+
+    @Test
+    public void shouldRaiseExceptionThatContainsValueOnlyIfNotASimpleDateFormat() {
+        thrown.expect( ValueConversionException.class );
+        thrown.expectMessage( "asdf" );
+        thrown.expectMessage( not( containsString( notASimpleDateFormat.toString() ) ) );
+
+        new DateConverter( notASimpleDateFormat ).convert( "asdf" );
+    }
+
+    @Test
+    public void shouldAnswerCorrectValueType() {
+        assertSame( Date.class, new DateConverter( monthDayYear ).valueType() );
+    }
+
+    @Test
+    public void shouldGiveNoValuePatternIfFormatterNotASimpleDateFormat() {
+        assertEquals( "", new DateConverter( notASimpleDateFormat ).valuePattern() );
+    }
+
+    @Test
+    public void shouldGiveValuePatternIfFormatterIsASimpleDateFormat() {
+        assertEquals( monthDayYear.toPattern(), datePattern( monthDayYear.toPattern() ).valuePattern() );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/util/EnumConverterTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/util/EnumConverterTest.java
new file mode 100644
index 0000000..617f146
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/util/EnumConverterTest.java
@@ -0,0 +1,95 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2014 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple.util;
+
+import joptsimple.ValueConversionException;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+import static org.junit.rules.ExpectedException.none;
+
+/**
+ * @author <a href="mailto:christian.ohr@gmail.com">Christian Ohr</a>
+ */
+public class EnumConverterTest {
+    @Rule
+    public final ExpectedException thrown = none();
+
+    private EnumConverter<TestEnum> converter;
+
+    @Before
+    public void setUp() {
+        converter = new TestEnumConverter();
+    }
+
+    @Test
+    public void convertsEnumValuesToEnum() {
+        assertEquals( TestEnum.A, converter.convert( "A" ) );
+    }
+
+    @Test
+    public void rejectsNonEnumeratedValues() {
+        thrown.expect( ValueConversionException.class );
+
+        converter.convert( "Z" );
+    }
+
+    @Test
+    public void answersCorrectValueType() {
+        assertSame( TestEnum.class, converter.valueType() );
+    }
+
+    @Test
+    public void givesDefaultValuePattern() {
+        assertEquals( "[A,B,C,D]", converter.valuePattern() );
+    }
+
+    @Test
+    public void givesCustomValuePattern() {
+        converter.setDelimiters( "(|)" );
+
+        assertEquals( "(A|B|C|D)", converter.valuePattern() );
+    }
+
+    @Test
+    public void ignoresCase() {
+        assertEquals( TestEnum.A, converter.convert( "a" ) );
+    }
+
+    private static class TestEnumConverter extends EnumConverter<TestEnum> {
+        TestEnumConverter() {
+            super( TestEnum.class );
+        }
+    }
+
+    private static enum TestEnum {
+        A, B, C, D
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/util/InetAddressConverterTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/util/InetAddressConverterTest.java
new file mode 100644
index 0000000..98f418f
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/util/InetAddressConverterTest.java
@@ -0,0 +1,60 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple.util;
+
+import java.net.InetAddress;
+import java.util.Random;
+
+import joptsimple.ValueConversionException;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+import static org.junit.Assume.*;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class InetAddressConverterTest {
+    private InetAddressConverter converter;
+
+    @Before
+    public void setUp() {
+        converter = new InetAddressConverter();
+    }
+
+    @Test
+    public void localhost() throws Exception {
+        assumeTrue( InetAddress.getByName( "127.0.0.1" ).isReachable( 5000 ) );
+
+        assertEquals( "127.0.0.1", converter.convert( "localhost" ).getHostAddress() );
+    }
+
+    @Test( expected = ValueConversionException.class )
+    public void unknownHost() {
+        converter.convert( "!(@&#()!*@&#" );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/util/KeyValuePairTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/util/KeyValuePairTest.java
new file mode 100644
index 0000000..e0c64e9
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/util/KeyValuePairTest.java
@@ -0,0 +1,88 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple.util;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class KeyValuePairTest {
+    @Test( expected = NullPointerException.class )
+    public void nullArg() {
+        KeyValuePair.valueOf( null );
+    }
+
+    @Test
+    public void empty() {
+        KeyValuePair pair = KeyValuePair.valueOf( "" );
+
+        assertEquals( "", pair.key );
+        assertEquals( "", pair.value );
+    }
+
+    @Test
+    public void noEqualsSign() {
+        KeyValuePair pair = KeyValuePair.valueOf( "aString" );
+
+        assertEquals( "aString", pair.key );
+        assertEquals( "", pair.value );
+    }
+
+    @Test
+    public void signAtEnd() {
+        KeyValuePair pair = KeyValuePair.valueOf( "aKey=" );
+
+        assertEquals( "aKey", pair.key );
+        assertEquals( "", pair.value );
+    }
+
+    @Test
+    public void signAtStart() {
+        KeyValuePair pair = KeyValuePair.valueOf( "=aValue" );
+
+        assertEquals( "", pair.key );
+        assertEquals( "aValue", pair.value );
+    }
+
+    @Test
+    public void typical() {
+        KeyValuePair pair = KeyValuePair.valueOf( "aKey=aValue" );
+
+        assertEquals( "aKey", pair.key );
+        assertEquals( "aValue", pair.value );
+    }
+
+    @Test
+    public void multipleEqualsSignsDoNotMatter() {
+        KeyValuePair pair = KeyValuePair.valueOf( "aKey=1=2=3=4" );
+
+        assertEquals( "aKey", pair.key );
+        assertEquals( "1=2=3=4", pair.value );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/util/KeyValuePairUnequalKeysTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/util/KeyValuePairUnequalKeysTest.java
new file mode 100644
index 0000000..fc130bf
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/util/KeyValuePairUnequalKeysTest.java
@@ -0,0 +1,43 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple.util;
+
+import org.infinitest.toolkit.EqualsHashCodeTestSupport;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class KeyValuePairUnequalKeysTest extends EqualsHashCodeTestSupport {
+    @Override
+    protected KeyValuePair equal() {
+        return KeyValuePair.valueOf( "x=z" );
+    }
+
+    @Override
+    protected KeyValuePair notEqual() {
+        return KeyValuePair.valueOf( "y=z" );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/util/KeyValuePairUnequalValuesTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/util/KeyValuePairUnequalValuesTest.java
new file mode 100644
index 0000000..4bb36b7
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/util/KeyValuePairUnequalValuesTest.java
@@ -0,0 +1,43 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple.util;
+
+import org.infinitest.toolkit.EqualsHashCodeTestSupport;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class KeyValuePairUnequalValuesTest extends EqualsHashCodeTestSupport {
+    @Override
+    protected KeyValuePair equal() {
+        return KeyValuePair.valueOf( "x=y" );
+    }
+
+    @Override
+    protected KeyValuePair notEqual() {
+        return KeyValuePair.valueOf( "x=z" );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/util/PathConverterTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/util/PathConverterTest.java
new file mode 100644
index 0000000..6b54393
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/util/PathConverterTest.java
@@ -0,0 +1,104 @@
+package joptsimple.util;
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+import joptsimple.ValueConversionException;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import static joptsimple.util.PathProperties.*;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+public class PathConverterTest {
+    @Rule
+    public ExpectedException exception = ExpectedException.none();
+
+    @Test
+    public void convertsValuesToPaths() throws Exception {
+        Path path = Files.createTempFile( "prefix", null );
+        path.toFile().deleteOnExit();
+
+        String pathName = path.toString();
+
+        assertEquals( path, new PathConverter( null ).convert( pathName ) );
+        assertEquals( path, new PathConverter().convert( pathName ) );
+    }
+
+    @Test
+    public void answersCorrectValueType() {
+        assertEquals( Path.class, new PathConverter().valueType() );
+    }
+
+    @Test
+    public void testReadableAndOverwritableFile() throws Exception {
+        Path path = Files.createTempFile( "prefix", null );
+        path.toFile().deleteOnExit();
+
+        String pathName = path.toString();
+
+        assertTrue( Files.isReadable( new PathConverter( READABLE ).convert( pathName ) ) );
+        assertTrue( Files.exists( new PathConverter( READABLE ).convert( pathName ) ) );
+        assertTrue( Files.isWritable( new PathConverter( READABLE ).convert( pathName ) ) );
+        assertTrue( Files.isWritable( new PathConverter( FILE_OVERWRITABLE).convert( pathName ) ) );
+    }
+
+    @Test
+    public void testNotExisting() throws Exception {
+        Path path = Files.createTempFile( "prefix", null );
+
+        Files.deleteIfExists( path );
+
+        assertFalse( Files.exists( new PathConverter( NOT_EXISTING ).convert( path.toString() ) ) );
+    }
+
+    @Test
+    public void testNotReadable() throws Exception {
+        Path path = Files.createTempFile( "prefix", null );
+        String pathName = path.toString();
+        Files.deleteIfExists( path );
+
+        exception.expect( ValueConversionException.class );
+        exception.expectMessage( "File [" + pathName );
+
+        new PathConverter( READABLE ).convert( pathName );
+    }
+
+    @Test
+    public void testDirectoryExisting() throws Exception {
+        Path path = Files.createTempDirectory( "prefix" );
+        path.toFile().deleteOnExit();
+
+        String pathName = path.toString();
+
+        assertTrue( Files.isDirectory( new PathConverter( DIRECTORY_EXISTING ).convert( pathName ) ) );
+    }
+
+    @Test
+    public void testDirectoryNotOverwritable() throws Exception {
+        Path path = Files.createTempDirectory( "prefix" );
+        path.toFile().deleteOnExit();
+
+        String pathName = path.toString();
+
+        exception.expect( ValueConversionException.class );
+        exception.expectMessage( "File [" + pathName );
+
+        new PathConverter( FILE_OVERWRITABLE).convert( pathName );
+    }
+
+    @Test
+    public void testNotExistingNotOverwritable() throws Exception {
+        Path path = Files.createTempDirectory( "prefix" );
+        String pathName = path.toString();
+        Files.deleteIfExists( path );
+
+        exception.expect( ValueConversionException.class );
+        exception.expectMessage( "File [" + pathName );
+
+        new PathConverter( FILE_OVERWRITABLE ).convert( pathName );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/util/PathPropertiesTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/util/PathPropertiesTest.java
new file mode 100644
index 0000000..1c6bf59
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/util/PathPropertiesTest.java
@@ -0,0 +1,54 @@
+package joptsimple.util;
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+import static joptsimple.util.PathProperties.*;
+
+public class PathPropertiesTest {
+    @Test
+    public void readableFile() throws Exception {
+        Path path = Files.createTempFile("prefix", null);
+
+        path.toFile().deleteOnExit();
+
+        assertTrue( READABLE.accept( path ) );
+        assertFalse( DIRECTORY_EXISTING.accept( path ) );
+        assertTrue( FILE_EXISTING.accept( path ) );
+        assertTrue( FILE_OVERWRITABLE.accept( path ) );
+        assertTrue( WRITABLE.accept( path ) );
+        assertFalse( NOT_EXISTING.accept( path ) );
+    }
+
+    @Test
+    public void nonExisting() throws Exception {
+        Path path = Files.createTempFile( "prefix", null );
+
+        Files.deleteIfExists( path );
+
+        assertFalse( READABLE.accept( path ) );
+        assertFalse( DIRECTORY_EXISTING.accept( path ) );
+        assertFalse( FILE_EXISTING.accept( path ) );
+        assertFalse( FILE_OVERWRITABLE.accept( path ) );
+        assertTrue( NOT_EXISTING.accept( path ) );
+        assertFalse( WRITABLE.accept( path ) );
+    }
+
+    @Test
+    public void directory() throws Exception {
+        Path path = Files.createTempDirectory( "prefix" );
+
+        path.toFile().deleteOnExit();
+
+        assertTrue( READABLE.accept( path ) );
+        assertTrue( DIRECTORY_EXISTING.accept( path ) );
+        assertFalse( FILE_EXISTING.accept( path ) );
+        assertFalse( FILE_OVERWRITABLE.accept( path ) );
+        assertFalse( NOT_EXISTING.accept( path ) );
+        assertTrue( WRITABLE.accept( path ) );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/java/joptsimple/util/RegexMatcherTest.java b/third_party/java/jopt-simple/src/test/java/joptsimple/util/RegexMatcherTest.java
new file mode 100644
index 0000000..56eae7e
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/java/joptsimple/util/RegexMatcherTest.java
@@ -0,0 +1,84 @@
+/*
+ The MIT License
+
+ Copyright (c) 2004-2015 Paul R. Holser, Jr.
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package joptsimple.util;
+
+import joptsimple.ValueConversionException;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import static joptsimple.util.RegexMatcher.*;
+import static org.junit.Assert.*;
+import static org.junit.rules.ExpectedException.*;
+
+/**
+ * @author <a href="mailto:pholser@alumni.rice.edu">Paul Holser</a>
+ */
+public class RegexMatcherTest {
+    @Rule public final ExpectedException thrown = none();
+
+    private RegexMatcher abc;
+
+    @Before
+    public void setUp() {
+        abc = new RegexMatcher( "abc", 0 );
+    }
+
+    @Test
+    public void shouldAttemptToMatchValueAgainstARegex() {
+        assertEquals( "abc", abc.convert( "abc" ) );
+    }
+
+    @Test( expected = ValueConversionException.class )
+    public void rejectsValueThatDoesNotMatchRegex() {
+        abc.convert( "abcd" );
+    }
+
+    @Test
+    public void raisesExceptionContainingValueAndPattern() {
+        thrown.expect( ValueConversionException.class );
+        thrown.expectMessage( "\\d+" );
+        thrown.expectMessage( "asdf" );
+
+        new RegexMatcher( "\\d+", 0 ).convert( "asdf" );
+    }
+
+    @Test
+    public void shouldOfferConvenienceMethodForCreatingMatcherWithNoFlags() {
+        assertEquals( "sourceforge.net", regex( "\\w+\\.\\w+" ).convert( "sourceforge.net" ) );
+    }
+
+    @Test
+    public void shouldAnswerCorrectValueType() {
+        assertEquals( String.class, abc.valueType() );
+    }
+
+    @Test
+    public void shouldGiveCorrectValuePattern() {
+        assertEquals( "abc", abc.valuePattern() );
+    }
+}
diff --git a/third_party/java/jopt-simple/src/test/resources/joptsimple/ExceptionMessages_xx_YY.properties b/third_party/java/jopt-simple/src/test/resources/joptsimple/ExceptionMessages_xx_YY.properties
new file mode 100644
index 0000000..ac78a62
--- /dev/null
+++ b/third_party/java/jopt-simple/src/test/resources/joptsimple/ExceptionMessages_xx_YY.properties
@@ -0,0 +1,10 @@
+joptsimple.IllegalOptionSpecificationException.message = illegal option specification exception
+joptsimple.MissingRequiredOptionsException.message = missing required option exception
+joptsimple.MultipleArgumentsForOptionException.message = multiple arguments for option exception
+joptsimple.OptionArgumentConversionException.message = option argument conversion exception
+joptsimple.OptionMissingRequiredArgumentException.message = option missing required argument exception
+joptsimple.UnconfiguredOptionException.message = unconfigured option exception
+joptsimple.UnrecognizedOptionException.message = unrecognized option exception
+joptsimple.util.DateConverter.without.pattern.message = date converter without pattern
+joptsimple.util.DateConverter.with.pattern.message = date converter with pattern
+joptsimple.util.RegexMatcher.message = regex matcher
\ No newline at end of file