Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 6 additions & 0 deletions doc/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@ Further documentation can be found in:
`make -C $DAIKONDIR/java javadoc`
It is also available at <http://plse.cs.washington.edu/daikon/download/api/> .

## Version 5.9.1 (???, 2026)

* Daikon and its tools now reject a malformed command-line option.
* `daikon.diff.Diff` no longer accepts the `-u` and `-z` options, which had
no effect.

## Version 5.9.0 (September 1, 2026)

* All Daikon tools now work with Java 8-26.
Expand Down
3 changes: 0 additions & 3 deletions doc/daikon.texinfo
Original file line number Diff line number Diff line change
Expand Up @@ -6749,9 +6749,6 @@ invariants must be justified. Does not print ``uninteresting'' invariants
Display the tree of all invariants. Includes invariants that are the
same in @var{file1} and @var{file2}, and unjustified invariants.

@item -u
Include ``uninteresting'' invariants in the tree of differing invariants.

@item -y
@itemx --ignore_unjustified
Include (statistically) unjustified invariants.
Expand Down
2 changes: 1 addition & 1 deletion java/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ JAVADOC_COMMAND := ${JAVADOC} -cp ${DAIKON_CLASSPATH}
JAVAC_COMMAND := ${JAVAC} -cp ${DAIKON_CLASSPATH} -Xlint -J-Xmx7g ${JAVAC_ARGS}
JAVA_COMMAND := ${JAVA} -cp ${DAIKON_CLASSPATH}

DOCLETPATH := ${DAIKONDIR}/java:$(wildcard ${DAIKONDIR}/java/lib/bcel-util-*.jar):$(wildcard ${DAIKONDIR}/java/lib/plume-util-*.jar):$(wildcard ${DAIKONDIR}/java/lib/reflection-util-*.jar):$(wildcard ${DAIKONDIR}/java/lib/hashmap-util-*.jar):${DAIKONDIR}/java/lib/daikon-plumelib.jar
DOCLETPATH := ${DAIKONDIR}/java:$(wildcard ${DAIKONDIR}/java/lib/java-getopt-*.jar):$(wildcard ${DAIKONDIR}/java/lib/bcel-util-*.jar):$(wildcard ${DAIKONDIR}/java/lib/plume-util-*.jar):$(wildcard ${DAIKONDIR}/java/lib/reflection-util-*.jar):$(wildcard ${DAIKONDIR}/java/lib/hashmap-util-*.jar):${DAIKONDIR}/java/lib/daikon-plumelib.jar

DOCLET_FLAGS :=
LINKOFFLINE := https://docs.oracle.com/javase/8/docs/api/
Expand Down
18 changes: 3 additions & 15 deletions java/daikon/Daikon.java
Original file line number Diff line number Diff line change
Expand Up @@ -1022,7 +1022,6 @@ static FileOptions read_options(String[] args, String usage) {
LongOpt[] longopts =
new LongOpt[] {
// Control output
new LongOpt(help_SWITCH, LongOpt.NO_ARGUMENT, null, 0),
new LongOpt(no_text_output_SWITCH, LongOpt.NO_ARGUMENT, null, 0),
new LongOpt(format_SWITCH, LongOpt.REQUIRED_ARGUMENT, null, 0),
new LongOpt(show_progress_SWITCH, LongOpt.NO_ARGUMENT, null, 0),
Expand Down Expand Up @@ -1055,7 +1054,7 @@ static FileOptions read_options(String[] args, String usage) {
new LongOpt(disc_reason_SWITCH, LongOpt.REQUIRED_ARGUMENT, null, 0),
new LongOpt(mem_stat_SWITCH, LongOpt.NO_ARGUMENT, null, 0),
};
Getopt g = new Getopt("daikon.Daikon", args, "ho:", longopts);
DaikonGetopt g = new DaikonGetopt(args, "o:", longopts, usage);
int c;

while ((c = g.getopt()) != -1) {
Expand All @@ -1065,10 +1064,7 @@ static FileOptions read_options(String[] args, String usage) {
String option_name = longopts[g.getLongind()].getName();

// Control output
if (help_SWITCH.equals(option_name)) {
System.out.println(usage);
throw new Daikon.NormalTermination();
} else if (no_text_output_SWITCH.equals(option_name)) {
if (no_text_output_SWITCH.equals(option_name)) {
no_text_output = true;
} else if (format_SWITCH.equals(option_name)) {
String format_name = getOptarg(g);
Expand Down Expand Up @@ -1367,12 +1363,9 @@ else if (ppt_regexp_SWITCH.equals(option_name)) {
} else if (mem_stat_SWITCH.equals(option_name)) {
use_mem_monitor = true;
} else {
throw new Daikon.UserError("Unknown option " + option_name + " on command line");
throw new Daikon.BugInDaikon("Unhandled long option " + option_name);
}
break;
case 'h':
System.out.println(usage);
throw new Daikon.NormalTermination();
case 'o':
String inv_filename = getOptarg(g);

Expand All @@ -1391,11 +1384,6 @@ else if (ppt_regexp_SWITCH.equals(option_name)) {
}
break;
//
case '?':
// break; // getopt() already printed an error
System.out.println(usage);
throw new Daikon.NormalTermination();
//
default:
throw new Daikon.BugInDaikon("getopt() returned " + c);
}
Expand Down
125 changes: 125 additions & 0 deletions java/daikon/DaikonGetopt.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
package daikon;

import static daikon.tools.nullness.NullnessUtil.castNonNull;

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

/**
* A command-line option processor that throws a {@link Daikon.UserError} that describes a bad
* command-line option, instead of printing a message and returning {@code '?'} or {@code ':'}. It
* also handles {@code -h} and {@code --help}: it prints the usage message and throws {@link
* Daikon.NormalTermination}.
*/
public class DaikonGetopt extends Getopt {

/**
* The name passed to {@link Getopt}, which uses it only in messages that DaikonGetopt suppresses.
*/
private static final String PROGNAME = "DaikonGetopt";

/** The usage message printed for {@code -h} and {@code --help}. */
private final String usage;

/**
* Creates a command-line option processor that recognizes short and long options, plus {@code -h}
* and {@code --help}, which print the usage message and throw {@link Daikon.NormalTermination}.
*
* @param argv the command-line arguments
* @param optstring the short options, in the format of {@link Getopt}; must not contain {@code h}
* @param longopts the long options; must not contain {@code help}
* @param usage the usage message to print for {@code -h} and {@code --help}
*/
public DaikonGetopt(String[] argv, String optstring, LongOpt[] longopts, String usage) {
super(
PROGNAME,
argv,
optstring + "h",
ArraysPlume.append(
longopts, new LongOpt(Daikon.help_SWITCH, LongOpt.NO_ARGUMENT, null, 'h')));
this.usage = usage;
opterr = false;
}

/**
* 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 ':'}.
*
* @return the next option, as described in {@link Getopt#getopt()}
* @throws Daikon.UserError if the next option is unrecognized, ambiguous, or malformed
* @throws Daikon.NormalTermination after printing the usage message, if the next option is {@code
* -h} or {@code --help}
*/
@Override
public int getopt() {
int c = super.getopt();
if (c == '?' || c == ':') {
throw new Daikon.UserError(badOptionMessage() + "; run with -h for usage");
}
if (c == 'h') {
System.out.println(usage);
throw new Daikon.NormalTermination();
}
return c;
}

/**
* Returns a description of the bad command-line option that {@link Getopt#getopt()} just
* rejected.
*
* @return a description of the bad command-line option
*/
private String badOptionMessage() {
// For every bad long option, Getopt clears nextchar and advances optind past the option. A
// bad short option has no such guarantee, because it may be followed by other short options in
// the same argument, as in "-xh".
if ("".equals(nextchar) && argv[optind - 1].startsWith("--")) {
return longOptionMessage(argv[optind - 1]);
}
char c = (char) optopt;
if (c == ':' || optstring.indexOf(c) == -1) {
return "Unrecognized command-line option -" + c;
} else {
return "Command-line option -" + c + " requires an argument";
}
}

/**
* Returns a description of a bad long command-line option. Uses {@code longind}, which Getopt
* sets to the index of the long option that matched the argument, or to -1 if none matched.
*
* @param arg the bad command-line argument, which starts with "--"
* @return a description of the bad command-line option
*/
private String longOptionMessage(String arg) {
int equalsPos = arg.indexOf('=');
String name = (equalsPos == -1) ? arg.substring(2) : arg.substring(2, equalsPos);
// Getopt treats an empty name, as in "--=foo", as a prefix of every long option.
if (name.isEmpty()) {
return "Unrecognized command-line option " + arg;
}
if (longind == -1) {
return "Unrecognized command-line option --" + name;
}
// Every constructor passes long options to Getopt.
LongOpt[] longopts = castNonNull(long_options);
LongOpt match = longopts[longind];
// For an inexact match, Getopt sets longind to the first long option that has the given
// prefix. The match is ambiguous if a later long option also has the prefix.
if (!match.getName().equals(name)) {
for (int i = longind + 1; i < longopts.length; i++) {
if (longopts[i].getName().startsWith(name)) {
return "Ambiguous command-line option --" + name;
}
}
}
if (equalsPos != -1 && match.getHasArg() == LongOpt.NO_ARGUMENT) {
return "Command-line option --" + match.getName() + " does not take an argument";
} else if (equalsPos == -1 && match.getHasArg() == LongOpt.REQUIRED_ARGUMENT) {
return "Command-line option --" + match.getName() + " requires an argument";
} else {
throw new Daikon.BugInDaikon("Getopt rejected " + arg + " for no known reason");
}
}
}
21 changes: 4 additions & 17 deletions java/daikon/MergeInvariants.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@

import daikon.split.PptSplitter;
import daikon.suppress.NIS;
import gnu.getopt.Getopt;
import gnu.getopt.LongOpt;
import java.io.File;
import java.io.FileNotFoundException;
Expand Down Expand Up @@ -97,25 +96,21 @@ public static void mainHelper(String[] args)

LongOpt[] longopts =
new LongOpt[] {
new LongOpt(Daikon.help_SWITCH, LongOpt.NO_ARGUMENT, null, 0),
new LongOpt(Daikon.config_option_SWITCH, LongOpt.REQUIRED_ARGUMENT, null, 0),
new LongOpt(Daikon.debugAll_SWITCH, LongOpt.NO_ARGUMENT, null, 0),
new LongOpt(Daikon.debug_SWITCH, LongOpt.REQUIRED_ARGUMENT, null, 0),
new LongOpt(Daikon.track_SWITCH, LongOpt.REQUIRED_ARGUMENT, null, 0),
};

Getopt g = new Getopt("daikon.MergeInvariants", args, "ho:", longopts);
DaikonGetopt g = new DaikonGetopt(args, "o:", longopts, usage);
int c;
while ((c = g.getopt()) != -1) {
switch (c) {

// long option
case 0:
String option_name = longopts[g.getLongind()].getName();
if (Daikon.help_SWITCH.equals(option_name)) {
System.out.println(usage);
throw new Daikon.NormalTermination();
} else if (Daikon.config_option_SWITCH.equals(option_name)) {
if (Daikon.config_option_SWITCH.equals(option_name)) {
String item = Daikon.getOptarg(g);
daikon.config.Configuration.getInstance().apply(item);
break;
Expand All @@ -133,14 +128,10 @@ public static void mainHelper(String[] args)
"Error parsing track argument '" + Daikon.getOptarg(g) + "' - " + error);
}
} else {
throw new Daikon.UserError("Unknown long option received: " + option_name);
throw new Daikon.BugInDaikon("Unhandled long option " + option_name);
}
break;

case 'h':
System.out.println(usage);
throw new Daikon.NormalTermination();

case 'o':
String output_inv_filename = Daikon.getOptarg(g);

Expand All @@ -160,12 +151,8 @@ public static void mainHelper(String[] args)
}
break;

case '?':
break; // getopt() already printed an error

default:
System.out.println("getopt() returned " + c);
break;
throw new Daikon.BugInDaikon("getopt() returned " + c);
}
}

Expand Down
19 changes: 4 additions & 15 deletions java/daikon/PrintInvariants.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
import daikon.split.PptSplitter;
import daikon.suppress.NIS;
import daikon.suppress.NISuppressionSet;
import gnu.getopt.Getopt;
import gnu.getopt.LongOpt;
import java.io.BufferedWriter;
import java.io.File;
Expand Down Expand Up @@ -329,7 +328,6 @@ public static void mainHelper(String[] args)

LongOpt[] longopts =
new LongOpt[] {
new LongOpt(Daikon.help_SWITCH, LongOpt.NO_ARGUMENT, null, 0),
new LongOpt(Daikon.format_SWITCH, LongOpt.REQUIRED_ARGUMENT, null, 0),
new LongOpt(Daikon.suppress_redundant_SWITCH, LongOpt.NO_ARGUMENT, null, 0),
new LongOpt(Daikon.output_num_samples_SWITCH, LongOpt.NO_ARGUMENT, null, 0),
Expand All @@ -344,17 +342,14 @@ public static void mainHelper(String[] args)
new LongOpt(
PrintInvariants.print_csharp_metadata_SWITCH, LongOpt.OPTIONAL_ARGUMENT, null, 0),
};
Getopt g = new Getopt("daikon.PrintInvariants", args, "h", longopts);
DaikonGetopt g = new DaikonGetopt(args, "", longopts, usage);
int c;
while ((c = g.getopt()) != -1) {
switch (c) {
case 0:
// got a long option
String option_name = longopts[g.getLongind()].getName();
if (Daikon.help_SWITCH.equals(option_name)) {
System.out.println(usage);
throw new Daikon.NormalTermination();
} else if (Daikon.ppt_regexp_SWITCH.equals(option_name)) {
if (Daikon.ppt_regexp_SWITCH.equals(option_name)) {
if (ppt_regexp != null) {
throw new Error(
"multiple --"
Expand Down Expand Up @@ -430,17 +425,11 @@ public static void mainHelper(String[] args)
} else if (Daikon.wrap_xml_SWITCH.equals(option_name)) {
wrap_xml = true;
} else {
throw new RuntimeException("Unknown long option received: " + option_name);
throw new Daikon.BugInDaikon("Unhandled long option " + option_name);
}
break;
case 'h':
System.out.println(usage);
throw new Daikon.NormalTermination();
case '?':
break; // getopt() already printed an error
default:
System.out.println("getopt() returned " + c);
break;
throw new Daikon.BugInDaikon("getopt() returned " + c);
}
}

Expand Down
18 changes: 4 additions & 14 deletions java/daikon/UnionInvariants.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package daikon;

import gnu.getopt.Getopt;
import gnu.getopt.LongOpt;
import java.io.File;
import org.plumelib.util.FilesPlume;
Expand Down Expand Up @@ -52,25 +51,19 @@ public static void mainHelper(String[] args) throws Exception {
new LongOpt[] {
new LongOpt(Daikon.suppress_redundant_SWITCH, LongOpt.NO_ARGUMENT, null, 0),
};
Getopt g = new Getopt("daikon.UnionInvariants", args, "ho:", longopts);
DaikonGetopt g = new DaikonGetopt(args, "o:", longopts, usage);
int c;
while ((c = g.getopt()) != -1) {
switch (c) {
case 0:
// got a long option
String option_name = longopts[g.getLongind()].getName();
if (Daikon.help_SWITCH.equals(option_name)) {
System.out.println(usage);
throw new Daikon.NormalTermination();
} else if (Daikon.suppress_redundant_SWITCH.equals(option_name)) {
if (Daikon.suppress_redundant_SWITCH.equals(option_name)) {
Daikon.suppress_redundant_invariants_with_simplify = true;
} else {
throw new Daikon.UserError("Unknown option received: " + option_name);
throw new Daikon.BugInDaikon("Unhandled long option " + option_name);
}
break;
case 'h':
System.out.println(usage);
throw new Daikon.NormalTermination();
case 'o':
String inv_filename = Daikon.getOptarg(g);

Expand All @@ -90,11 +83,8 @@ public static void mainHelper(String[] args) throws Exception {
}
break;
//
case '?':
break; // getopt() already printed an error
default:
System.out.println("getopt() returned " + c);
break;
throw new Daikon.BugInDaikon("getopt() returned " + c);
}
}

Expand Down
Loading
Loading