Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
a95b881
Reject bad command-line options with a non-zero exit status
mernst Oct 7, 2026
23f6b2e
All tools accept -h and --help
mernst Oct 7, 2026
6380a6a
Remove stale comment in DtraceDiff
mernst Oct 7, 2026
788e418
Merge branch 'bad-option-core' into help-option-all-tools
mernst Oct 7, 2026
056584e
AnnotateNullable: print usage to stderr on error
mernst Oct 7, 2026
23bf08d
Address code review: reject "- " with no short options, report instru…
mernst Oct 7, 2026
d20d9f8
Merge remote-tracking branch 'origin/bad-option-core' into bad-option…
mernst Oct 7, 2026
0242d85
Address code review: recognize -h in instrumenter, fix runcluster.pl …
mernst Oct 7, 2026
9fc85fa
Address code review: SplitterFactoryTest.mainHelper throws instead of…
mernst Oct 7, 2026
ada0caf
Provide DaikonGetopt helpers that the per-tool PRs use; leave instrum…
mernst Oct 7, 2026
62bf241
Merge remote-tracking branch 'origin/bad-option-core' into help-optio…
mernst Oct 7, 2026
e560e3e
Merge branch 'master' of https://github.com/codespecs/daikon into bad…
mernst Oct 7, 2026
2118b35
Merge remote-tracking branch 'origin/bad-option-core' into help-optio…
mernst Oct 7, 2026
e8abacc
Move the runtimechecker changes to the runtimechecker-options branch
mernst Oct 10, 2026
335272a
Merge branch 'bad-option-core' of github.com:mernst/daikon into help-…
mernst Oct 10, 2026
2b46b30
Address code review: remove unused DaikonGetopt helpers, reject stray…
mernst Oct 11, 2026
50e3e2a
Simplify DaikonGetopt: one constructor, which always handles -h and -…
mernst Oct 11, 2026
7df7f23
Move test-driver fixes and doc spelling fixes to separate pull requests
mernst Oct 11, 2026
4f54694
Handle DaikonTerminationException in SampleTester and SplitterFactory…
mernst Oct 11, 2026
117da2e
Brevity
mernst Oct 11, 2026
3bee663
Merge branch 'master' into bad-option-core
mernst Oct 11, 2026
68cc049
Merge branch 'bad-option-core' into help-option-all-tools
mernst Oct 12, 2026
3e5adec
Merge branch 'master' of github.com:codespecs/daikon into help-option…
mernst Oct 12, 2026
b1c773d
Add the DaikonGetopt helper that this pull request uses: nonOptionArgs
mernst Oct 12, 2026
5b001be
Report trace-reading errors in ReadTrace and SplitDtrace as user errors
mernst Oct 12, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions doc/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ Further documentation can be found in:
## Version 5.9.1 (???, 2026)

* Daikon and its tools now reject a malformed command-line option.
* All tools accept `-h` and `--help`, which print a usage message and exit
with status 0.
* `daikon.diff.Diff` no longer accepts the `-u` and `-z` options, which had
no effect.

Expand Down
3 changes: 3 additions & 0 deletions doc/daikon.texinfo
Original file line number Diff line number Diff line change
Expand Up @@ -7034,6 +7034,9 @@ java -cp $@{DAIKONDIR@}/daikon.jar daikon.AnnotateNullable @i{[@var{flags}]} @va
The flags are:
@table @option

@item -h --help
Print usage message.

@item -n --nonnull-annotations
Adds @samp{NonNull} annotations as well as @samp{Nullable} annotations. Unlike @samp{Nullable}
annotations, @samp{NonNull} annotations are not guaranteed to be correct.
Expand Down
16 changes: 14 additions & 2 deletions java/daikon/AnnotateNullable.java
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,24 @@ private AnnotateNullable() {
@Option("-n Insert NonNull as well as Nullable annotations")
public static boolean nonnull_annotations = false;

/** If true, print a usage message and exit. */
@Option("-h Display usage information")
public static boolean help = false;

public static void main(String[] args) throws IOException {

Options options =
new Options("plume.AnnotateNullable [options] <inv_file>", AnnotateNullable.class);
new Options("daikon.AnnotateNullable [options] <inv_file>", AnnotateNullable.class);
String[] inv_files = options.parse(true, args);
assert inv_files.length == 1;
if (help) {
options.printUsage();
System.exit(0);
}
if (inv_files.length != 1) {
System.err.println("must specify exactly one invariant file");
options.printUsage(System.err);
System.exit(1);
}

// Read the serialized invariant file
File inv_file = new File(inv_files[0]);
Expand Down
2 changes: 1 addition & 1 deletion java/daikon/Chicory.java
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ public static void main(String[] args) {
public static void check_args(Options options, String[] target_args) {
if (help) {
options.printUsage();
System.exit(1);
System.exit(0);
}
if (nesting_depth < 0) {
System.out.printf("nesting depth (%d) must not be negative%n", nesting_depth);
Expand Down
38 changes: 38 additions & 0 deletions java/daikon/DaikonGetopt.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import gnu.getopt.Getopt;
import gnu.getopt.LongOpt;
import java.util.Arrays;
import org.plumelib.util.ArraysPlume;

/**
Expand Down Expand Up @@ -42,6 +43,43 @@ public DaikonGetopt(String[] argv, String optstring, LongOpt[] longopts, String
opterr = false;
}

/**
* Processes the command-line arguments of a program that takes no options other than {@code -h}
* and {@code --help}.
*
* @param argv the command-line arguments
* @param usage the usage message to print for {@code -h} and {@code --help}
* @return the arguments that are not options
* @throws Daikon.NormalTermination after printing the usage message, if an argument requests it
* @throws Daikon.UserError if an argument is any other option
*/
public static String[] nonOptionArgs(String[] argv, String usage) {
return argsAfterOptions(argv, "", usage);
}

/**
* Processes command-line arguments, permitting no options other than {@code -h} and {@code
* --help}.
*
* @param argv the command-line arguments
* @param optstring "" to process all the arguments, or "+" to stop at the first non-option
* @param usage the usage message to print for {@code -h} and {@code --help}
* @return the arguments that Getopt did not process as options
* @throws Daikon.NormalTermination after printing the usage message, if an argument requests it
* @throws Daikon.UserError if an argument is any other option
*/
private static String[] argsAfterOptions(String[] argv, String optstring, String usage) {
// Getopt permutes its argument array, so give it a copy to avoid modifying the caller's array.
String[] argvCopy = argv.clone();
DaikonGetopt g = new DaikonGetopt(argvCopy, optstring, new LongOpt[0], usage);
// Every option other than -h and --help is bad, so getopt() returns only -1 or throws.
int c = g.getopt();
if (c != -1) {
throw new Daikon.BugInDaikon("getopt() returned " + c);
}
return Arrays.copyOfRange(argvCopy, g.getOptind(), argvCopy.length);
}

/**
* Like {@link Getopt#getopt()}, but never returns {@code '?'}, {@code ':'}, or {@code 'h'}.
* Getopt returns {@code ':'} for a missing argument if the optstring starts with {@code ':'}.
Expand Down
2 changes: 1 addition & 1 deletion java/daikon/DynComp.java
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ public static void main(String[] args) {
public static void check_args(Options options, String[] targetArgs) {
if (help) {
options.printUsage();
System.exit(1);
System.exit(0);
}
if (nesting_depth < 0) {
System.out.printf("nesting depth (%d) must not be negative%n", nesting_depth);
Expand Down
34 changes: 29 additions & 5 deletions java/daikon/SplitDtrace.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
Expand All @@ -29,6 +30,9 @@
*/
public final class SplitDtrace {

/** The usage message for this program. */
private static final String usage = "Usage: java daikon.SplitDtrace file.dtrace[.gz]";

/** Do not instantiate. */
private SplitDtrace() {
throw new UnsupportedOperationException("Do not instantiate");
Expand All @@ -40,14 +44,29 @@ private SplitDtrace() {
* @param args one argument, the name of the .dtrace or .dtrace.gz file
*/
public static void main(String[] args) throws IOException {
if (args.length != 1) {
throw new RuntimeException(
"You must supply one argument which is the filename of the dtrace file");
try {
mainHelper(args);
} catch (Daikon.DaikonTerminationException e) {
Daikon.handleDaikonTerminationException(e);
}
}

/**
* This does the work of {@link #main(String[])}, but it never calls System.exit, so it is
* appropriate to be called programmatically.
*
* @param args command-line arguments, like those of {@link #main}
* @throws IOException if there is a problem reading or writing a file
*/
public static void mainHelper(String[] args) throws IOException {
String[] files = DaikonGetopt.nonOptionArgs(args, usage);
if (files.length != 1) {
throw new Daikon.UserError(usage);
}
String filename = args[0].trim();
String filename = files[0].trim();
boolean isGz = filename.endsWith(".dtrace.gz");
if (!filename.endsWith(".dtrace") && !isGz) {
throw new RuntimeException(
throw new Daikon.UserError(
"Filename must end with .dtrace or .dtrace.gz: filename=" + filename);
}
int declNum = 1;
Expand All @@ -56,6 +75,9 @@ public static void main(String[] args) throws IOException {
ArrayList<String> rec = new ArrayList<>();
while (true) {
readRec(reader, rec);
if (rec.isEmpty()) {
throw new Daikon.UserError("No DECLARE record in " + filename);
}
if (isDeclare(rec)) {
break;
}
Expand All @@ -71,6 +93,8 @@ public static void main(String[] args) throws IOException {
recNum++;
}
}
} catch (FileNotFoundException e) {
throw new Daikon.UserError(e, "File not found");
}

System.out.println(
Expand Down
4 changes: 4 additions & 0 deletions java/daikon/dcomp/BuildJDK.java
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,10 @@ public static void main(String[] args) throws IOException {
DynComp.class,
DCInstrument.class);
String[] cl_args = options.parse(true, args);
if (DynComp.help) {
options.printUsage();
System.exit(0);
}
if (cl_args.length < 1) {
System.err.println("must specify destination dir");
options.printUsage();
Expand Down
4 changes: 4 additions & 0 deletions java/daikon/dcomp/BuildJDK24.java
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,10 @@ public static void main(String[] args) throws IOException {
DynComp.class,
DCInstrument24.class);
String[] cl_args = options.parse(true, args);
if (DynComp.help) {
options.printUsage();
System.exit(0);
}
if (cl_args.length < 1) {
System.err.println("must specify destination dir");
options.printUsage();
Expand Down
2 changes: 2 additions & 0 deletions java/daikon/test/BadOptionTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import daikon.Daikon;
import daikon.DaikonGetopt;
import daikon.PrintInvariants;
import daikon.SplitDtrace;
import daikon.UnionInvariants;
import daikon.tools.DtraceDiff;
import gnu.getopt.LongOpt;
Expand Down Expand Up @@ -153,6 +154,7 @@ public void testPrintInvariantsUnrecognizedOption() {
public void testHelpOption() {
assertPrintsUsage(() -> DtraceDiff.mainHelper(new String[] {"--help"}));
assertPrintsUsage(() -> UnionInvariants.mainHelper(new String[] {"--help"}));
assertPrintsUsage(() -> SplitDtrace.mainHelper(new String[] {"-h", "x"}));
}

/**
Expand Down
10 changes: 6 additions & 4 deletions java/daikon/tools/DtraceNonceFixer.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

package daikon.tools;

import daikon.DaikonGetopt;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.PrintWriter;
Expand Down Expand Up @@ -53,14 +54,15 @@ public static void main(String[] args) {
* @param args command-line arguments, like those of {@link #main}
*/
public static void mainHelper(final String[] args) {
if (args.length != 1) {
String[] files = DaikonGetopt.nonOptionArgs(args, usage);
if (files.length != 1) {
throw new daikon.Daikon.UserError(usage);
}

String outputFilename =
args[0].endsWith(".gz") ? (args[0] + "_fixed.gz") : (args[0] + "_fixed");
files[0].endsWith(".gz") ? (files[0] + "_fixed.gz") : (files[0] + "_fixed");

try (BufferedReader br1 = FilesPlume.newBufferedFileReader(args[0]);
try (BufferedReader br1 = FilesPlume.newBufferedFileReader(files[0]);
PrintWriter out1 = new PrintWriter(FilesPlume.newBufferedFileWriter(outputFilename))) {

// maxNonce - the biggest nonce ever found in the file
Expand Down Expand Up @@ -93,7 +95,7 @@ public static void mainHelper(final String[] args) {

// now go back and add the OBJECT and CLASS invocations
String allFixedFilename =
outputFilename.endsWith(".gz") ? (args[0] + "_all_fixed.gz") : (args[0] + "_all_fixed");
outputFilename.endsWith(".gz") ? (files[0] + "_all_fixed.gz") : (files[0] + "_all_fixed");

try (BufferedReader br2 = FilesPlume.newBufferedFileReader(outputFilename);
PrintWriter out2 = new PrintWriter(FilesPlume.newBufferedFileWriter(allFixedFilename))) {
Expand Down
29 changes: 27 additions & 2 deletions java/daikon/tools/ReadTrace.java
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
package daikon.tools;

import daikon.Daikon;
import daikon.DaikonGetopt;
import daikon.FileIO;
import daikon.PptMap;
import daikon.PptTopLevel;
import daikon.ValueTuple;
import daikon.VarInfo;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
Expand Down Expand Up @@ -32,6 +35,9 @@
*/
public class ReadTrace {

/** The usage message for this program. */
private static final String usage = "Usage: java daikon.tools.ReadTrace file1 file2 ...";

/** Do not instantiate. */
private ReadTrace() {
throw new UnsupportedOperationException("Do not instantiate");
Expand All @@ -43,12 +49,31 @@ private ReadTrace() {
* @param args data trace file names, read from the command line
*/
public static void main(String[] args) {
try {
mainHelper(args);
} catch (Daikon.DaikonTerminationException e) {
Daikon.handleDaikonTerminationException(e);
}
}

/**
* This does the work of {@link #main(String[])}, but it never calls System.exit, so it is
* appropriate to be called programmatically.
*
* @param args command-line arguments, like those of {@link #main}
*/
public static void mainHelper(String[] args) {
String[] traceFiles = DaikonGetopt.nonOptionArgs(args, usage);
if (traceFiles.length == 0) {
throw new Daikon.UserError("No trace files specified" + Daikon.lineSep + usage);
}
CollectDataProcessor processor = new CollectDataProcessor();
PptMap ppts = new PptMap();
try {
// `read_data_trace_files()` requires the list of files to be modifiable.
FileIO.read_data_trace_files(new ArrayList<>(Arrays.asList(args)), ppts, processor, false);
} catch (Exception e) {
FileIO.read_data_trace_files(
new ArrayList<>(Arrays.asList(traceFiles)), ppts, processor, false);
} catch (IOException e) {
throw new Error(e);
}

Expand Down
Loading