diff --git a/Ghidra/Features/Base/ghidra_scripts/AskValuesExampleScript.java b/Ghidra/Features/Base/ghidra_scripts/AskValuesExampleScript.java
index ab90f04894..f6d24e929a 100644
--- a/Ghidra/Features/Base/ghidra_scripts/AskValuesExampleScript.java
+++ b/Ghidra/Features/Base/ghidra_scripts/AskValuesExampleScript.java
@@ -75,7 +75,7 @@ public class AskValuesExampleScript extends GhidraScript {
// show up in the tool and when you release the consumer, it will be closed.
// NOTE: if you call getProgram() more than once, the consumer will be added multiple times
// and you must release it multiple times
- Program program = values.getProgram("Other Program", this, state.getTool());
+ Program program = values.getProgram("Other Program", this, state.getTool(), true);
println("Name = " + name);
println("Count = " + age);
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/script/GhidraScript.java b/Ghidra/Features/Base/src/main/java/ghidra/app/script/GhidraScript.java
index 8ca84a937c..605edbd772 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/script/GhidraScript.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/script/GhidraScript.java
@@ -2732,7 +2732,9 @@ public abstract class GhidraScript extends FlatProgramAPI {
/**
* Returns a Program, using the title parameter for guidance. The actual behavior of the
- * method depends on your environment, which can be GUI or headless.
+ * method depends on your environment, which can be GUI or headless. If in headless mode,
+ * the program will not be upgraded (see {@link #askProgram(String, boolean)} if you want
+ * more control). In GUI mode, the user will be prompted to upgrade.
*
* Regardless of environment -- if script arguments have been set, this method will use the
* next argument in the array and advance the array index so the next call to an ask method
@@ -2757,20 +2759,75 @@ public abstract class GhidraScript extends FlatProgramAPI {
*
* @param title the title of the pop-up dialog (in GUI mode) or the variable name (in
* headless mode)
- * @return the user-selected Program with this script as the consumer or null if a program was
- * not selected. NOTE: It is very important that the program instance returned by this method
- * ALWAYS be properly released when no longer needed. The script which invoked this method must be
+ * @return the user-selected Program with this script as the consumer if a program was
+ * selected. Null is returned if a program is not selected. NOTE: It is very important that
+ * the program instance returned by this method ALWAYS be properly released when no longer
+ * needed. The script which invoked this method must be
* specified as the consumer upon release (i.e., {@code program.release(this) } - failure to
* properly release the program may result in improper project disposal. If the program was
* opened by the tool, the tool will be a second consumer responsible for its own release.
- * @throws VersionException if the Program is out-of-date from the version of GHIDRA
+ * @throws VersionException if the Program is out-of-date from the version of Ghidra and an
+ * upgrade was not been performed. In non-headless mode, the user will have already been
+ * notified via a popup dialog.
* @throws IOException if there is an error accessing the Program's DomainObject
- * @throws CancelledException if the operation is cancelled
+ * @throws CancelledException if the program open operation is cancelled
* @throws IllegalArgumentException if in headless mode, there was a missing or invalid program
* specified in the .properties file
*/
public Program askProgram(String title)
throws VersionException, IOException, CancelledException {
+ return askProgram(title, false);
+ }
+
+ /**
+ * Returns a Program, using the title parameter for guidance with the option to upgrade
+ * if needed. The actual behavior of the method depends on your environment, which can be
+ * GUI or headless. You can control whether or not the program is allowed to upgrade via
+ * the {@code upgradeIfNeeded} parameter.
+ *
+ * Regardless of environment -- if script arguments have been set, this method will use the
+ * next argument in the array and advance the array index so the next call to an ask method
+ * will get the next argument. If there are no script arguments and a .properties file
+ * sharing the same base name as the Ghidra Script exists (i.e., Script1.properties for
+ * Script1.java), then this method will then look there for the String value to return.
+ * The method will look in the .properties file by searching for a property name that is the
+ * title String parameter. If that property name exists and its value represents a valid
+ * program, then the .properties value will be used in the following way:
+ *
* @param consumer the consumer to be used to open the program * @param tool optional tool that if non-null, the program will also be opened in the tool + * @param upgradeIfNeeded if true, program will be upgraded if needed and possible. If false, + * the program will only be upgraded after first prompting the user. In headless mode, it will + * attempt to upgrade only if the parameter is true. * @param monitor task monitor for cancelling the open program. - * @return a program for the current program value value. If the current program file value - * is null, then null will be returned. - * @throws VersionException if the Program being opened is an older version than the + * @return a program for the currently selected program file. If no file chosen, returns null + * @throws VersionException if the Program is out-of-date from the version of GHIDRA and an + * upgrade was not been performed. In non-headless mode, the user will have already been + * notified via a popup dialog. * current Ghidra Program version. * @throws IOException if there is an error accessing the Program's DomainObject * @throws CancelledException if the operation is cancelled */ - public Program openProgram(Object consumer, Tool tool, TaskMonitor monitor) - throws VersionException, IOException, CancelledException { + public Program openProgram(Object consumer, Tool tool, boolean upgradeIfNeeded, + TaskMonitor monitor) throws VersionException, IOException, CancelledException { DomainFile domainFile = getValue(); if (domainFile == null) { return null; } - Program program = (Program) domainFile.getDomainObject(consumer, true, false, monitor); + Program program = doOpenProgram(domainFile, consumer, upgradeIfNeeded, monitor); if (tool != null && program != null) { tool.getService(ProgramManager.class).openProgram(program); @@ -113,4 +119,21 @@ public class ProgramFileValue extends ProjectFileValue { return program; } + private Program doOpenProgram(DomainFile domainFile, Object consumer, boolean upgradeIfNeeded, + TaskMonitor monitor) throws VersionException, IOException, CancelledException { + + try { + return (Program) domainFile.getDomainObject(consumer, upgradeIfNeeded, false, monitor); + } + catch (VersionException e) { + if (SystemUtilities.isInHeadlessMode()) { + throw e; + } + if (VersionExceptionHandler.isUpgradeOK(null, domainFile, "Open ", e)) { + return (Program) domainFile.getDomainObject(consumer, true, false, monitor); + } + throw e; + } + } + } diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/features/base/values/ProgramFileValueTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/features/base/values/ProgramFileValueTest.java index 3ec8026472..258d81f248 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/features/base/values/ProgramFileValueTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/features/base/values/ProgramFileValueTest.java @@ -36,7 +36,7 @@ public class ProgramFileValueTest extends AbstractValueIntegrationTest { values.setProgram(NAME, programA); assertTrue(values.hasValue(NAME)); - assertEquals(programA, values.getProgram(NAME, this, null)); + assertEquals(programA, values.getProgram(NAME, this, null, true)); } @Test @@ -46,12 +46,12 @@ public class ProgramFileValueTest extends AbstractValueIntegrationTest { assertTrue(values.isDefined(NAME)); assertTrue(values.hasValue(NAME)); - assertEquals(programA, values.getProgram(NAME, this, null)); + assertEquals(programA, values.getProgram(NAME, this, null, true)); values.setProgram(NAME, programB); assertTrue(values.hasValue(NAME)); - assertEquals(programB, values.getProgram(NAME, this, null)); + assertEquals(programB, values.getProgram(NAME, this, null, true)); values.setProgram(NAME, null); assertFalse(values.hasValue(NAME)); @@ -96,7 +96,7 @@ public class ProgramFileValueTest extends AbstractValueIntegrationTest { pressOk(); assertFalse(values.hasValue(NAME)); - assertNull(values.getProgram(NAME, this, null)); + assertNull(values.getProgram(NAME, this, null, true)); } @Test @@ -109,7 +109,7 @@ public class ProgramFileValueTest extends AbstractValueIntegrationTest { pressOk(); assertTrue(values.hasValue(NAME)); - assertEquals(programA, values.getProgram(NAME, this, null)); + assertEquals(programA, values.getProgram(NAME, this, null, true)); } @Test @@ -121,7 +121,7 @@ public class ProgramFileValueTest extends AbstractValueIntegrationTest { pressOk(); assertTrue(values.hasValue(NAME)); - assertEquals(programA, values.getProgram(NAME, this, null)); + assertEquals(programA, values.getProgram(NAME, this, null, true)); } @Test @@ -134,7 +134,7 @@ public class ProgramFileValueTest extends AbstractValueIntegrationTest { pressOk(); assertTrue(values.hasValue(NAME)); - assertEquals(programB, values.getProgram(NAME, this, null)); + assertEquals(programB, values.getProgram(NAME, this, null, true)); } @Test @@ -149,7 +149,7 @@ public class ProgramFileValueTest extends AbstractValueIntegrationTest { setProjectFileOnProjectTree(values.getAbstractValue(NAME), programA.getDomainFile()); pressOk(); - Program p = values.getProgram(NAME, this, tool); + Program p = values.getProgram(NAME, this, tool, true); allOpenPrograms = programManagerService.getAllOpenPrograms(); assertEquals(1, allOpenPrograms.length); @@ -164,9 +164,9 @@ public class ProgramFileValueTest extends AbstractValueIntegrationTest { setProjectFileOnProjectTree(values.getAbstractValue(NAME), programA.getDomainFile()); pressOk(); - Program p1 = values.getProgram(NAME, this, null); + Program p1 = values.getProgram(NAME, this, null, true); assertEquals(2, programA.getConsumerList().size()); - Program p2 = values.getProgram(NAME, this, null); + Program p2 = values.getProgram(NAME, this, null, true); assertEquals(p1, p2); assertEquals(3, programA.getConsumerList().size()); p1.release(this); diff --git a/Ghidra/Features/VersionTracking/ghidra_scripts/AutoVersionTrackingScript.java b/Ghidra/Features/VersionTracking/ghidra_scripts/AutoVersionTrackingScript.java index 8934e5fb00..72241ced58 100644 --- a/Ghidra/Features/VersionTracking/ghidra_scripts/AutoVersionTrackingScript.java +++ b/Ghidra/Features/VersionTracking/ghidra_scripts/AutoVersionTrackingScript.java @@ -115,17 +115,19 @@ public class AutoVersionTrackingScript extends GhidraScript { }); startupValues = askValues("Enter Auto Version Tracking Information", - "Changing these options will not change the corresponding tool options", - startupValues); + "Changing these options will not change the corresponding tool options", startupValues); DomainFolder folder = startupValues.getProjectFolder("Version Tracking Session Folder"); String name = startupValues.getString("Version Tracking Session Name"); boolean isCurrentProgramSourceProg = startupValues.getBoolean("Check if current program is the Source Program"); - Program otherProgram = - startupValues.getProgram("Please select the other program", this, state.getTool()); + // setting auto upgrade to isHeadless, will cause headless uses to auto upgrade, but in + // Gui mode, will prompt before upgrading. + boolean autoUpgradeIfNeeded = isRunningHeadless(); + Program otherProgram = startupValues.getProgram("Please select the other program", this, + state.getTool(), autoUpgradeIfNeeded); if (isCurrentProgramSourceProg) { sourceProgram = currentProgram; @@ -140,15 +142,12 @@ public class AutoVersionTrackingScript extends GhidraScript { return; } - // Need to end the script transaction or it interferes with vt things that need locks end(true); - VTSession session = VTSessionDB.createVTSession(name, sourceProgram, destinationProgram, this); - if (folder.getFile(name) == null) { folder.createFile(name, session, monitor); } @@ -157,7 +156,7 @@ public class AutoVersionTrackingScript extends GhidraScript { GhidraValuesMap optionsMap = createDefaultOptions(); // if running script in GUI get options from user and update the vtOptions with them - if(!isRunningHeadless()) { + if (!isRunningHeadless()) { optionsMap = getOptionsFromUser(); } @@ -177,12 +176,10 @@ public class AutoVersionTrackingScript extends GhidraScript { ToolOptions vtOptions = setToolOptionsFromOptionsMap(optionsMap); - AutoVersionTrackingTask autoVtTask = - new AutoVersionTrackingTask(session, vtOptions); + AutoVersionTrackingTask autoVtTask = new AutoVersionTrackingTask(session, vtOptions); TaskLauncher.launch(autoVtTask); - // if not running headless user can decide whether to save or not // if running headless - must save here or nothing that was done in this script will be // accessible later. @@ -191,7 +188,6 @@ public class AutoVersionTrackingScript extends GhidraScript { session.save(); } - println(autoVtTask.getStatusMsg()); otherProgram.release(this); } @@ -257,8 +253,7 @@ public class AutoVersionTrackingScript extends GhidraScript { GhidraValuesMap optionsValues = createDefaultOptions(); optionsValues = askValues("Enter Auto Version Tracking Options", - "These options will not be saved to your current tool options.", - optionsValues); + "These options will not be saved to your current tool options.", optionsValues); return optionsValues; }