style fixes in ghidra.app. {plugin.core.script script}

This commit is contained in:
Jason P. Leasure 2020-06-08 13:00:26 -04:00
parent 6bc33bdf65
commit 2015f13542
22 changed files with 442 additions and 372 deletions

View File

@ -24,6 +24,7 @@ import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import javax.swing.Icon;
import javax.swing.KeyStroke;
import docking.ActionContext;
@ -49,18 +50,15 @@ class GhidraScriptActionManager {
KeyEvent.VK_R, DockingUtils.CONTROL_KEY_MODIFIER_MASK | InputEvent.SHIFT_DOWN_MASK);
private static final String SCRIPT_ACTIONS_KEY = "Scripts_Actions_Key";
private static final String RESOURCE_FILE_ACTION_RUN_GROUP = "1";
private GhidraScriptComponentProvider provider;
private GhidraScriptMgrPlugin plugin;
private GhidraScriptInfoManager infoManager;
private DockingAction refreshAction;
private DockingAction bundleStatusAction;
private DockingAction showBundleStatusAction;
private DockingAction newAction;
private DockingAction runAction;
private DockingAction runLastAction;
private DockingAction globalRunLastAction;
private DockingAction editAction;
private DockingAction eclipseAction;
private DockingAction deleteAction;
private DockingAction renameAction;
private DockingAction keyBindingAction;
private DockingAction helpAction;
@ -174,15 +172,12 @@ class GhidraScriptActionManager {
globalRunLastAction.firePropertyChanged(DockingActionIf.DESCRIPTION_PROPERTY, "", newDesc);
}
private void createActions() {
//
// 'run' actions
//
String runGroup = "1";
runAction = new DockingAction("Run", plugin.getName()) {
private DockingAction createScriptAction(String name, String menuEntry,
String actionDescription, Icon icon, String toolBarGroup, Runnable runnable) {
DockingAction action = new DockingAction(name, plugin.getName()) {
@Override
public void actionPerformed(ActionContext context) {
provider.runScript();
runnable.run();
}
@Override
@ -191,188 +186,79 @@ class GhidraScriptActionManager {
return contextObject instanceof ResourceFile;
}
};
runAction.setPopupMenuData(new MenuData(new String[] { "Run" },
ResourceManager.loadImage("images/play.png"), null));
runAction.setToolBarData(
new ToolBarData(ResourceManager.loadImage("images/play.png"), runGroup));
action.setPopupMenuData(new MenuData(new String[] { menuEntry }, icon));
action.setToolBarData(new ToolBarData(icon, toolBarGroup));
runAction.setDescription("Run Script");
runAction.setEnabled(false);
plugin.getTool().addLocalAction(provider, runAction);
action.setDescription(actionDescription);
action.setEnabled(false);
runLastAction = new RerunLastScriptAction(runGroup);
plugin.getTool().addLocalAction(provider, action);
return action;
}
private DockingAction createScriptTableAction(String name, String actionDescription, Icon icon,
Runnable runnable) {
DockingAction action = new DockingAction(name, plugin.getName()) {
@Override
public void actionPerformed(ActionContext context) {
runnable.run();
}
@Override
public boolean isAddToPopup(ActionContext context) {
Object contextObject = context.getContextObject();
return (contextObject instanceof GTable) || (contextObject instanceof ResourceFile);
}
};
action.setPopupMenuData(new MenuData(new String[] { name }, icon));
action.setToolBarData(new ToolBarData(icon, null));
action.setDescription(actionDescription);
action.setEnabled(true);
plugin.getTool().addLocalAction(provider, action);
return action;
}
private void createActions() {
createScriptAction("Run", "Run Script", "Run Script",
ResourceManager.loadImage("images/play.png"), RESOURCE_FILE_ACTION_RUN_GROUP,
provider::runScript);
runLastAction = new RerunLastScriptAction(RESOURCE_FILE_ACTION_RUN_GROUP);
plugin.getTool().addLocalAction(provider, runLastAction);
globalRunLastAction = new RerunLastScriptAction("Xtra");
plugin.getTool().addAction(globalRunLastAction);
//
// End 'run' actions
//
createScriptAction("Edit", "Edit with basic editor", "Edit Script with basic editor",
ResourceManager.loadImage("images/accessories-text-editor.png"), null,
provider::editScriptBuiltin);
editAction = new DockingAction("Edit", plugin.getName()) {
@Override
public void actionPerformed(ActionContext context) {
provider.editScriptBuiltin();
}
createScriptAction("EditEclipse", "Edit with Eclipse", "Edit Script with Eclipse",
ResourceManager.loadImage("images/eclipse.png"), null, provider::editScriptEclipse);
@Override
public boolean isEnabledForContext(ActionContext context) {
Object contextObject = context.getContextObject();
return contextObject instanceof ResourceFile;
}
};
editAction.setPopupMenuData(new MenuData(new String[] { "Edit with basic editor" },
ResourceManager.loadImage("images/accessories-text-editor.png"), null));
editAction.setToolBarData(
new ToolBarData(ResourceManager.loadImage("images/accessories-text-editor.png"), null));
editAction.setDescription("Edit Script with basic editor");
editAction.setEnabled(false);
plugin.getTool().addLocalAction(provider, editAction);
keyBindingAction =
createScriptAction("Key Binding", "Assign Key Binding", "Assign Key Binding",
ResourceManager.loadImage("images/key.png"), null, provider::assignKeyBinding);
eclipseAction = new DockingAction("EditEclipse", plugin.getName()) {
@Override
public void actionPerformed(ActionContext context) {
provider.editScriptEclipse();
}
createScriptAction("Delete", "Delete", "Delete Script",
ResourceManager.loadImage("images/edit-delete.png"), null, provider::deleteScript);
@Override
public boolean isEnabledForContext(ActionContext context) {
Object contextObject = context.getContextObject();
return contextObject instanceof ResourceFile;
}
};
eclipseAction.setPopupMenuData(new MenuData(new String[] { "Edit with Eclipse" },
ResourceManager.loadImage("images/eclipse.png"), null));
eclipseAction.setToolBarData(
new ToolBarData(ResourceManager.loadImage("images/eclipse.png"), null));
eclipseAction.setDescription("Edit Script with Eclipse");
eclipseAction.setEnabled(false);
plugin.getTool().addLocalAction(provider, eclipseAction);
renameAction = createScriptAction("Rename", "Rename", "Rename Script",
ResourceManager.loadImage("images/textfield_rename.png"), null, provider::renameScript);
keyBindingAction = new DockingAction("Key Binding", plugin.getName()) {
@Override
public void actionPerformed(ActionContext context) {
provider.assignKeyBinding();
}
newAction = createScriptTableAction("New", "Create New Script",
ResourceManager.loadImage("images/script_add.png"), provider::newScript);
@Override
public boolean isEnabledForContext(ActionContext context) {
Object contextObject = context.getContextObject();
return contextObject instanceof ResourceFile;
}
};
keyBindingAction.setPopupMenuData(new MenuData(new String[] { "Assign Key Binding" },
ResourceManager.loadImage("images/key.png"), null));
keyBindingAction.setToolBarData(
new ToolBarData(ResourceManager.loadImage("images/key.png"), null));
createScriptTableAction("Refresh", "Refresh Script List",
Icons.REFRESH_ICON, provider::refresh);
keyBindingAction.setDescription("Assign Key Binding");
keyBindingAction.setEnabled(false);
plugin.getTool().addLocalAction(provider, keyBindingAction);
deleteAction = new DockingAction("Delete", plugin.getName()) {
@Override
public void actionPerformed(ActionContext context) {
provider.deleteScript();
}
@Override
public boolean isEnabledForContext(ActionContext context) {
Object contextObject = context.getContextObject();
return contextObject instanceof ResourceFile;
}
};
deleteAction.setPopupMenuData(new MenuData(new String[] { "Delete" },
ResourceManager.loadImage("images/edit-delete.png"), null));
deleteAction.setToolBarData(
new ToolBarData(ResourceManager.loadImage("images/edit-delete.png"), null));
deleteAction.setDescription("Delete Script");
deleteAction.setEnabled(false);
plugin.getTool().addLocalAction(provider, deleteAction);
renameAction = new DockingAction("Rename", plugin.getName()) {
@Override
public void actionPerformed(ActionContext context) {
provider.renameScript();
}
@Override
public boolean isEnabledForContext(ActionContext context) {
Object contextObject = context.getContextObject();
return contextObject instanceof ResourceFile;
}
};
renameAction.setPopupMenuData(new MenuData(new String[] { "Rename" },
ResourceManager.loadImage("images/textfield_rename.png"), null));
renameAction.setToolBarData(
new ToolBarData(ResourceManager.loadImage("images/textfield_rename.png"), null));
renameAction.setDescription("Rename Script");
renameAction.setEnabled(false);
plugin.getTool().addLocalAction(provider, renameAction);
newAction = new DockingAction("New", plugin.getName()) {
@Override
public void actionPerformed(ActionContext context) {
provider.newScript();
}
@Override
public boolean isAddToPopup(ActionContext context) {
Object contextObject = context.getContextObject();
return (contextObject instanceof GTable) || (contextObject instanceof ResourceFile);
}
};
newAction.setPopupMenuData(new MenuData(new String[] { "New" },
ResourceManager.loadImage("images/script_add.png"), null));
newAction.setToolBarData(
new ToolBarData(ResourceManager.loadImage("images/script_add.png"), null));
newAction.setDescription("Create New Script");
newAction.setEnabled(true);
plugin.getTool().addLocalAction(provider, newAction);
refreshAction = new DockingAction("Refresh", plugin.getName()) {
@Override
public void actionPerformed(ActionContext context) {
provider.refresh();
}
@Override
public boolean isAddToPopup(ActionContext context) {
Object contextObject = context.getContextObject();
return (contextObject instanceof GTable) || (contextObject instanceof ResourceFile);
}
};
refreshAction.setPopupMenuData(
new MenuData(new String[] { "Refresh" }, Icons.REFRESH_ICON, null));
refreshAction.setToolBarData(new ToolBarData(Icons.REFRESH_ICON, null));
refreshAction.setDescription("Refresh Script List");
refreshAction.setEnabled(true);
plugin.getTool().addLocalAction(provider, refreshAction);
bundleStatusAction = new DockingAction("Script Directories", plugin.getName()) {
@Override
public void actionPerformed(ActionContext context) {
provider.showBundleStatusComponent();
}
@Override
public boolean isAddToPopup(ActionContext context) {
Object contextObject = context.getContextObject();
return (contextObject instanceof GTable) || (contextObject instanceof ResourceFile);
}
};
bundleStatusAction.setPopupMenuData(new MenuData(new String[] { "Bundle Status" },
ResourceManager.loadImage("images/text_list_bullets.png"), null));
bundleStatusAction.setToolBarData(
new ToolBarData(ResourceManager.loadImage("images/text_list_bullets.png"), null));
bundleStatusAction.setDescription("Bundle Status");
bundleStatusAction.setEnabled(true);
plugin.getTool().addLocalAction(provider, bundleStatusAction);
showBundleStatusAction = createScriptTableAction("Script Directories",
"Manage Script Directories", ResourceManager.loadImage("images/text_list_bullets.png"),
provider::showBundleStatusComponent);
helpAction = new DockingAction("Ghidra API Help", plugin.getName()) {
@Override
@ -441,7 +327,7 @@ class GhidraScriptActionManager {
}
HelpLocation getPathHelpLocation() {
return new HelpLocation(plugin.getName(), bundleStatusAction.getName());
return new HelpLocation(plugin.getName(), showBundleStatusAction.getName());
}
HelpLocation getKeyBindingHelpLocation() {

View File

@ -20,6 +20,7 @@ import java.awt.Rectangle;
import java.awt.event.*;
import java.io.*;
import java.util.*;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import javax.swing.*;
@ -57,13 +58,12 @@ import util.CollectionUtils;
import utilities.util.FileUtilities;
public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
static final String WINDOW_GROUP = "Script Group";
private static final double TOP_PREFERRED_RESIZE_WEIGHT = .80;
private static final String DESCRIPTION_DIVIDER_LOCATION = "DESCRIPTION_DIVIDER_LOCATION";
private static final String FILTER_TEXT = "FILTER_TEXT";
static final String WINDOW_GROUP = "Script Group";
private Map<ResourceFile, GhidraScriptEditorComponentProvider> editorMap = new HashMap<>();
private final GhidraScriptMgrPlugin plugin;
private JPanel component;
@ -126,7 +126,7 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
updateTitle();
}
private void build() {
private void buildCategoryTree() {
scriptRoot = new RootNode();
scriptCategoryTree = new GTree(scriptRoot);
@ -159,6 +159,10 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
scriptCategoryTree.getSelectionModel()
.setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
}
private void build() {
buildCategoryTree();
tableModel = new GhidraScriptTableModel(this, infoManager);
@ -231,10 +235,11 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
component.add(dataDescriptionSplit, BorderLayout.CENTER);
}
//==================================================================================================
// Inner Classes
//==================================================================================================
/**
* Restore state for bundles, user actions, and filter.
*
* @param saveState the state object
*/
public void readConfigState(SaveState saveState) {
bundleHost.restoreManagedBundleState(saveState, getTool());
@ -259,6 +264,12 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
tableFilterPanel.setFilterText(filterText);
}
/**
* Save state for bundles, user actions, and filter.
*
* @param saveState the state object
*/
public void writeConfigState(SaveState saveState) {
bundleHost.saveManagedBundleState(saveState);
@ -283,6 +294,9 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
bundleStatusComponentProvider.dispose();
}
/**
* @return the bundle host used for scripting, ultimately from {@link GhidraScriptUtil#getBundleHost()}
*/
public BundleHost getBundleHost() {
return bundleHost;
}
@ -299,10 +313,6 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
return editorMap;
}
void showBundleStatusComponent() {
bundleStatusComponentProvider.setVisible(true);
}
void assignKeyBinding() {
ResourceFile script = getSelectedScript();
ScriptAction action = actionManager.createAction(script);
@ -360,82 +370,72 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
renameScriptByCopying(script, provider, renameFile);
}
/**
* Copy a script, renaming references to the class name.
*
* @param sourceScript source script
* @param destinationScript destination script
* @throws IOException if we fail to create temp, write contents, copy, or delete temp
*/
private void copyScript(ResourceFile sourceScript, ResourceFile destinationScript)
throws IOException {
String oldClassName = GhidraScriptUtil.getBaseName(sourceScript);
String newClassName = GhidraScriptUtil.getBaseName(destinationScript);
ResourceFile parentFile = sourceScript.getParentFile();
ResourceFile temp = new ResourceFile(parentFile, "ghidraScript.tmp");
try (PrintWriter writer = new PrintWriter(temp.getOutputStream())) {
try (BufferedReader reader =
new BufferedReader(new InputStreamReader(sourceScript.getInputStream()))) {
while (true) {
String line = reader.readLine();
if (line == null) {
break;
}
writer.println(line.replaceAll(oldClassName, newClassName));
}
}
}
FileUtilities.copyFile(temp, destinationScript, TaskMonitor.DUMMY);
temp.delete();
}
private void renameScriptByCopying(ResourceFile script, GhidraScriptProvider provider,
ResourceFile renameFile) {
String oldClassName = GhidraScriptUtil.getBaseName(script);
String newClassName = GhidraScriptUtil.getBaseName(renameFile);
ResourceFile temp = null;
PrintWriter writer = null;
BufferedReader reader = null;
try {
ResourceFile parentFile = script.getParentFile();
temp = new ResourceFile(parentFile, "ghidraScript.tmp");
writer = new PrintWriter(temp.getOutputStream());
reader = new BufferedReader(new InputStreamReader(script.getInputStream()));
while (true) {
String line = reader.readLine();
if (line == null) {
break;
}
writer.println(line.replaceAll(oldClassName, newClassName));
}
reader.close();
writer.close();
FileUtilities.copyFile(temp, renameFile, TaskMonitor.DUMMY);
if (!renameFile.exists()) {
Msg.showWarn(getClass(), getComponent(), "Unable to rename script",
"The rename operation failed.\nPlease check file permissions.");
return;
}
if (!provider.deleteScript(script)) {
Msg.showWarn(getClass(), getComponent(), "Unable to rename script",
"Unable to remove original file.\nPlease check file permissions.");
renameFile.delete();
return;
}
infoManager.removeMetadata(script);
if (actionManager.hasScriptAction(script)) {
KeyStroke ks = actionManager.getKeyBinding(script);
actionManager.removeAction(script);
ScriptAction action = actionManager.createAction(renameFile);
action.setKeyBindingData(new KeyBindingData(ks));
}
assert !infoManager
.containsMetadata(renameFile) : "renamed script already has metadata";
infoManager.getScriptInfo(renameFile);
tableModel.switchScript(script, renameFile);
setSelectedScript(renameFile);
copyScript(script, renameFile);
}
catch (IOException e) {
Msg.showError(getClass(), getComponent(), "Unable to rename script", e.getMessage());
return;
}
finally {
if (reader != null) {
try {
reader.close();
}
catch (IOException e) {
// we tried
}
}
if (writer != null) {
writer.close();
}
if (temp != null) {
temp.delete();
}
if (!renameFile.exists()) {
Msg.showWarn(getClass(), getComponent(), "Unable to rename script",
"The rename operation failed.\nPlease check file permissions.");
return;
}
if (!provider.deleteScript(script)) {
Msg.showWarn(getClass(), getComponent(), "Unable to rename script",
"Unable to remove original file.\nPlease check file permissions.");
renameFile.delete();
return;
}
infoManager.removeMetadata(script);
if (actionManager.hasScriptAction(script)) {
KeyStroke ks = actionManager.getKeyBinding(script);
actionManager.removeAction(script);
ScriptAction action = actionManager.createAction(renameFile);
action.setKeyBindingData(new KeyBindingData(ks));
}
assert !infoManager.containsMetadata(renameFile) : "renamed script already has metadata";
infoManager.getScriptInfo(renameFile);
tableModel.switchScript(script, renameFile);
setSelectedScript(renameFile);
}
JTable getTable() {
@ -450,19 +450,27 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
return tableModel.getScriptAt(tableFilterPanel.getModelRow(viewRowIndex));
}
/**
* @return enabled bundle paths from the scripting bundle host
*/
public List<ResourceFile> getScriptDirectories() {
return bundleHost.getGhidraBundles()
.stream()
.filter(bundle -> bundle.isEnabled() && bundle instanceof GhidraSourceBundle)
.filter(GhidraSourceBundle.class::isInstance)
.filter(GhidraBundle::isEnabled)
.map(GhidraBundle::getPath)
.collect(Collectors.toList());
}
/**
* @return non-system bundle paths from the scripting bundle host
*/
public List<ResourceFile> getWritableScriptDirectories() {
return bundleHost.getGhidraBundles()
.stream()
.filter(GhidraSourceBundle.class::isInstance)
.filter(bundle -> !bundle.isSystemBundle())
.filter(Predicate.not(GhidraBundle::isSystemBundle))
.filter(GhidraBundle::isEnabled)
.map(GhidraBundle::getPath)
.collect(Collectors.toList());
}
@ -496,7 +504,7 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
}
else {
Msg.showInfo(getClass(), getComponent(), getName(),
"Unable to delete script '" + script.getName() + "'" + "\n" +
"Unable to delete script '" + script.getName() + "'\n" +
"Please verify the file permissions.");
}
}
@ -521,7 +529,7 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
}
}
public void enableScriptDirectory(ResourceFile scriptDir) {
void enableScriptDirectory(ResourceFile scriptDir) {
bundleHost.enablePath(scriptDir);
Msg.showInfo(this, getComponent(), "Script Path Added/Enabled",
"The directory has been automatically enabled for use:\n" +
@ -699,6 +707,10 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
return categoryPath;
}
void showBundleStatusComponent() {
bundleStatusComponentProvider.setVisible(true);
}
class RefreshingBundleHostListener implements BundleHostListener {
@Override

View File

@ -41,9 +41,8 @@ import resources.Icons;
import resources.ResourceManager;
public class GhidraScriptEditorComponentProvider extends ComponentProvider {
private static final int MAX_UNDO_REDO_SIZE = 50;
static final String EDITOR_COMPONENT_NAME="EDITOR";
static final String CHANGE_DESTINATION_TITLE = "Where Would You Like to Store Your Changes?";
static final String FILE_ON_DISK_CHANGED_TITLE = "File Changed on Disk";
static final String FILE_ON_DISK_MISSING_TITLE = "File on Disk is Missing";
@ -53,7 +52,9 @@ public class GhidraScriptEditorComponentProvider extends ComponentProvider {
static final String KEEP_CHANGES_TEXT = "Keep Changes";
static final String DISCARD_CHANGES_TEXT = "Discard Changes";
static Font defaultFont = new Font("monospaced", Font.PLAIN, 12);
private static final int MAX_UNDO_REDO_SIZE = 50;
private static Font defaultFont = new Font("monospaced", Font.PLAIN, 12);
static void restoreState(SaveState saveState) {
String name = saveState.getString("DEFAULT_FONT_NAME", "Monospaced");
@ -68,6 +69,7 @@ public class GhidraScriptEditorComponentProvider extends ComponentProvider {
saveState.putInt("DEFAULT_FONT_SIZE", defaultFont.getSize());
}
private GhidraScriptMgrPlugin plugin;
private GhidraScriptComponentProvider provider;
private String title;
@ -675,7 +677,7 @@ public class GhidraScriptEditorComponentProvider extends ComponentProvider {
super(text);
setFont(defaultFont);
setName("EDITOR");
setName(EDITOR_COMPONENT_NAME);
setWrapStyleWord(false);
Document document = getDocument();
document.addUndoableEditListener(e -> {

View File

@ -26,8 +26,7 @@ import ghidra.app.plugin.ProgramPlugin;
import ghidra.app.plugin.core.eclipse.EclipseConnection;
import ghidra.app.plugin.core.eclipse.EclipseIntegrationOptionsPlugin;
import ghidra.app.plugin.core.osgi.BundleHost;
import ghidra.app.script.GhidraScriptUtil;
import ghidra.app.script.GhidraState;
import ghidra.app.script.*;
import ghidra.app.services.*;
import ghidra.framework.options.SaveState;
import ghidra.framework.options.ToolOptions;
@ -49,12 +48,17 @@ import ghidra.util.task.TaskListener;
)
//@formatter:on
public class GhidraScriptMgrPlugin extends ProgramPlugin implements GhidraScriptService {
private static int loaded = 0;
private final GhidraScriptComponentProvider provider;
private static int loaded = 0;
private final BundleHost bundleHost;
/**
* {@link GhidraScriptMgrPlugin} is the entry point for all {@link GhidraScript} capabilities.
*
* @param tool the tool this plugin is added to
*/
public GhidraScriptMgrPlugin(PluginTool tool) {
super(tool, true, true, true);
if (loaded == 0) {
@ -111,6 +115,11 @@ public class GhidraScriptMgrPlugin extends ProgramPlugin implements GhidraScript
provider.runScript(scriptName, listener);
}
/**
* Attempts to run a script in a {@link RunScriptTask}.
*
* @param scriptFile the script's source file
*/
public void runScript(ResourceFile scriptFile) {
provider.runScript(scriptFile);
}

View File

@ -35,13 +35,12 @@ import ghidra.util.table.column.GColumnRenderer;
import resources.Icons;
class GhidraScriptTableModel extends GDynamicColumnTableModel<ResourceFile, Object> {
static final String SCRIPT_ACTION_COLUMN_NAME = "In Tool";
static final String SCRIPT_STATUS_COLUMN_NAME = "Status";
private static final String EMPTY_STRING = "";
private static final ImageIcon ERROR_IMG = Icons.ERROR_ICON;
static final String SCRIPT_ACTION_COLUMN_NAME = "In Tool";
static final String SCRIPT_STATUS_COLUMN_NAME = "Status";
private GhidraScriptComponentProvider provider;
private List<ResourceFile> scriptList = new ArrayList<>();
private final GhidraScriptInfoManager infoManager;
@ -276,7 +275,7 @@ class GhidraScriptTableModel extends GDynamicColumnTableModel<ResourceFile, Obje
@Override
public String getColumnName() {
return "Status";
return SCRIPT_STATUS_COLUMN_NAME;
}
@Override

View File

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -17,5 +16,5 @@
package ghidra.app.plugin.core.script;
public interface Ingredient {
public IngredientDescription [] getIngredientDescriptions();
IngredientDescription [] getIngredientDescriptions();
}

View File

@ -30,13 +30,13 @@ import ghidra.app.script.GhidraScriptUtil;
import ghidra.util.HelpLocation;
public class PickProviderDialog extends DialogComponentProvider {
private static String lastSelectedProviderDescription;
private List<GhidraScriptProvider> providers;
private ListPanel listPanel;
private JComponent parent;
private boolean wasCancelled;
private static String lastSelectedProviderDescription;
PickProviderDialog(JComponent parent, HelpLocation help) {
super("New Script: Type");
this.parent = parent;
@ -55,6 +55,12 @@ public class PickProviderDialog extends DialogComponentProvider {
setHelpLocation(help);
}
/**
* Constructor used in testing only!
*
* @param testItems values to populate model with
* @param defaultItem the default selection
*/
public PickProviderDialog(List<String> testItems, String defaultItem) {
super("New Script: Type");
@ -76,6 +82,8 @@ public class PickProviderDialog extends DialogComponentProvider {
/**
* For testing...
*
* @param provider the provider selection
*/
void setSelectedProvider(GhidraScriptProvider provider) {
listPanel.setSelectedValue(provider);
@ -95,6 +103,9 @@ public class PickProviderDialog extends DialogComponentProvider {
return (GhidraScriptProvider) listPanel.getSelectedValue();
}
/**
* close any open dialog
*/
public void dispose() {
close();
}

View File

@ -37,6 +37,8 @@ import ghidra.util.HelpLocation;
public class SaveDialog extends DialogComponentProvider implements ListSelectionListener {
protected GhidraScriptComponentProvider componentProvider;
protected ResourceFile scriptFile;
private GhidraScriptProvider provider;
private List<ResourceFile> paths;
@ -44,15 +46,22 @@ public class SaveDialog extends DialogComponentProvider implements ListSelection
private JTextField nameField;
private boolean cancelled;
protected ResourceFile scriptFile;
public SaveDialog(Component parent, String title,
GhidraScriptComponentProvider componentProvider, ResourceFile scriptFile,
HelpLocation help) {
SaveDialog(Component parent, String title, GhidraScriptComponentProvider componentProvider,
ResourceFile scriptFile, HelpLocation help) {
this(parent, title, componentProvider, componentProvider.getWritableScriptDirectories(),
scriptFile, help);
}
/**
* Only called directly from testing!
*
* @param parent parent component
* @param title dialog title
* @param componentProvider the provider
* @param scriptDirs list of directories to give as options when saving
* @param scriptFile the default save location
* @param help contextual help, e.g. for rename or save
*/
public SaveDialog(Component parent, String title,
GhidraScriptComponentProvider componentProvider, List<ResourceFile> scriptDirs,
ResourceFile scriptFile, HelpLocation help) {

View File

@ -27,7 +27,7 @@ public class ScriptCategoryNode extends GTreeNode {
private final String name;
public ScriptCategoryNode(String name) {
ScriptCategoryNode(String name) {
this.name = name;
}

View File

@ -129,6 +129,9 @@ import ghidra.util.task.TaskMonitor;
* @see ghidra.program.model.listing.Program
*/
public abstract class GhidraScript extends FlatProgramAPI {
// Stores last-selected value for askXxx() methods, used to pre-populate askXxx()
// GUI dialogs if they are run more than once
private static Map<String, Map<Class<?>, Object>> askMap = new HashMap<>();
protected ResourceFile sourceFile;
protected GhidraState state;
@ -174,16 +177,19 @@ public abstract class GhidraScript extends FlatProgramAPI {
SUSPENDED
}
// Stores last-selected value for askXxx() methods, used to pre-populate askXxx()
// GUI dialogs if they are run more than once
private static Map<String, Map<Class<?>, Object>> askMap = new HashMap<>();
/**
* The run method is where the script specific code is placed.
* @throws Exception if any exception occurs.
*/
protected abstract void run() throws Exception;
/**
* Set the context for this script.
*
* @param state state object
* @param monitor the monitor to use during run
* @param writer the target of script "print" statements
*/
public final void set(GhidraState state, TaskMonitor monitor, PrintWriter writer) {
this.state = state;
this.monitor = monitor;
@ -191,6 +197,14 @@ public abstract class GhidraScript extends FlatProgramAPI {
loadVariablesFromState();
}
/**
* Execute/run script and {@link #doCleanup} afterwards.
*
* @param runState state object
* @param runMonitor the monitor to use during run
* @param runWriter the target of script "print" statements
* @throws Exception if the script excepts
*/
public final void execute(GhidraState runState, TaskMonitor runMonitor, PrintWriter runWriter)
throws Exception {
boolean success = false;
@ -203,8 +217,8 @@ public abstract class GhidraScript extends FlatProgramAPI {
}
}
private final void doExecute(GhidraState runState, TaskMonitor runMonitor,
PrintWriter runWriter) throws Exception {
private void doExecute(GhidraState runState, TaskMonitor runMonitor, PrintWriter runWriter)
throws Exception {
this.state = runState;
this.monitor = runMonitor;
this.writer = runWriter;
@ -478,6 +492,11 @@ public abstract class GhidraScript extends FlatProgramAPI {
return state;
}
/**
* Set the script {@link #currentAddress}, {@link #currentLocation}, and update state object.
*
* @param address the new address
*/
public final void setCurrentLocation(Address address) {
state.setCurrentAddress(address);
this.currentAddress = address;
@ -545,8 +564,8 @@ public abstract class GhidraScript extends FlatProgramAPI {
if (isRunningHeadless()) {
// only change client authenticator in headless mode
try {
HeadlessClientAuthenticator.installHeadlessClientAuthenticator(
ClientUtil.getUserName(), null, false);
HeadlessClientAuthenticator
.installHeadlessClientAuthenticator(ClientUtil.getUserName(), null, false);
}
catch (IOException e) {
throw new RuntimeException("Unexpected Exception", e);
@ -1408,7 +1427,7 @@ public abstract class GhidraScript extends FlatProgramAPI {
// Tests if text actually equals "true" or "false
String tempBool = analysisOptionValue.toLowerCase();
if (tempBool.equals("true") || tempBool.equals("false")) {
if ("true".equals(tempBool) || "false".equals(tempBool)) {
options.setBoolean(analysisOption, Boolean.valueOf(tempBool));
}
@ -1785,13 +1804,13 @@ public abstract class GhidraScript extends FlatProgramAPI {
}
else {
try {
SwingUtilities.invokeAndWait(
() -> Msg.showInfo(getClass(), null, name, message));
SwingUtilities
.invokeAndWait(() -> Msg.showInfo(getClass(), null, name, message));
}
catch (InterruptedException e1) {
catch (InterruptedException e) {
// shouldn't happen
}
catch (InvocationTargetException e1) {
catch (InvocationTargetException e) {
// shouldn't happen
}
}
@ -1970,7 +1989,7 @@ public abstract class GhidraScript extends FlatProgramAPI {
}
private interface CancellableFunction<T, R> {
public R apply(T t) throws CancelledException;
R apply(T t) throws CancelledException;
}
/**
@ -2778,10 +2797,10 @@ public abstract class GhidraScript extends FlatProgramAPI {
* @throws IllegalArgumentException if the parsed value is not a valid double.
*/
public double parseDouble(String val) {
if (val.equalsIgnoreCase("pi")) {
if ("pi".equalsIgnoreCase(val)) {
return Math.PI;
}
if (val.equalsIgnoreCase("e")) {
if ("e".equalsIgnoreCase(val)) {
return Math.E;
}
try {
@ -3254,7 +3273,7 @@ public abstract class GhidraScript extends FlatProgramAPI {
* @throws IllegalArgumentException if the parsed value is not a valid boolean.
*/
public Boolean parseBoolean(String val) {
if (val.equalsIgnoreCase("true") || val.equalsIgnoreCase("false")) {
if ("true".equalsIgnoreCase(val) || "false".equalsIgnoreCase(val)) {
return Boolean.parseBoolean(val);
}
throw new IllegalArgumentException("Invalid boolean: " + val);

View File

@ -28,6 +28,9 @@ public class GhidraScriptInfoManager {
private Map<ResourceFile, ScriptInfo> scriptFileToInfoMap = new HashMap<>();
private Map<String, List<ResourceFile>> scriptNameToFilesMap = new HashMap<>();
/**
* clear stored metadata
*/
public void dispose() {
clearMetadata();
}
@ -110,6 +113,12 @@ public class GhidraScriptInfoManager {
return scriptFileToInfoMap.containsKey(scriptFile);
}
/**
* Get {@link ScriptInfo} for {@code script} under the assumption that it's already managed.
*
* @param script the script
* @return info or null if the assumption was wrong. If null is returned, an error dialog is shown
*/
public ScriptInfo getExistingScriptInfo(ResourceFile script) {
ScriptInfo info = scriptFileToInfoMap.get(script);
if (info == null) {

View File

@ -33,7 +33,7 @@ public class GhidraScriptProperties {
private HashMap<String, String> propertiesMap;
private String baseName;
public GhidraScriptProperties() {
GhidraScriptProperties() {
propertiesMap = new HashMap<>();
}
@ -73,6 +73,9 @@ public class GhidraScriptProperties {
}
}
/**
* @return the properties file name
*/
public String getFilename() {
return baseName + ".properties";
}
@ -138,6 +141,10 @@ public class GhidraScriptProperties {
return propertiesMap.put(key.trim(), value);
}
/**
* @param keyString the property name
* @return the value of the key in the properties file, or an empty string if no property exists
*/
public String getValue(String keyString) {
if (propertiesMap.size() == 0) {
@ -151,10 +158,19 @@ public class GhidraScriptProperties {
return "";
}
/**
* @return true if there are no properties
*/
public boolean isEmpty() {
return (propertiesMap.size() == 0);
}
/**
* Remove the named property
*
* @param keyString the property name
* @return the previous value or null
*/
protected String remove(String keyString) {
return propertiesMap.remove(keyString);
}
@ -163,14 +179,25 @@ public class GhidraScriptProperties {
propertiesMap.clear();
}
/**
* @param keyString a property name
* @return true if the key exists in the property file
*/
public boolean containsKey(String keyString) {
return propertiesMap.containsKey(keyString);
}
/**
* @param valueString a value string
* @return true if any property has the given value
*/
public boolean containsValue(String valueString) {
return propertiesMap.containsValue(valueString);
}
/**
* @return the property names for all properties
*/
public Set<String> keySet() {
return propertiesMap.keySet();
}

View File

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -20,7 +19,7 @@ import generic.jar.ResourceFile;
class GhidraScriptUnsupportedClassVersionError extends RuntimeException {
private ResourceFile classFile;
private final ResourceFile classFile;
GhidraScriptUnsupportedClassVersionError(UnsupportedClassVersionError cause,
ResourceFile classFile) {

View File

@ -15,7 +15,8 @@
*/
package ghidra.app.script;
import java.io.*;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.*;
import java.util.stream.Collectors;
@ -31,31 +32,38 @@ import ghidra.util.classfinder.ClassSearcher;
* A utility class for managing script directories and ScriptInfo objects.
*/
public class GhidraScriptUtil {
private static final String SCRIPTS_SUBDIR_NAME = "ghidra_scripts";
private static final String DEV_SCRIPTS_SUBDIR_NAME = "developer_scripts";
private static List<GhidraScriptProvider> providers = null;
/**
* User's home scripts directory
*/
public static String USER_SCRIPTS_DIR = buildUserScriptsDirectory();
static BundleHost _bundleHost;
private static BundleHost bundleHost;
private static final String SCRIPTS_SUBDIR_NAME = "ghidra_scripts";
private static final String DEV_SCRIPTS_SUBDIR_NAME = "developer_scripts";
private static List<GhidraScriptProvider> providers;
/**
* @return the bundle host used for scripting
*/
public static BundleHost getBundleHost() {
return _bundleHost;
return bundleHost;
}
private static void setBundleHost(BundleHost bundleHost) {
if (_bundleHost != null) {
/**
* set the bundle host and start the framework
*
* @param aBundleHost the bundle host
*/
private static void setBundleHost(BundleHost aBundleHost) {
if (bundleHost != null) {
throw new RuntimeException("GhidraScriptUtil initialized multiple times!");
}
try {
_bundleHost = bundleHost;
_bundleHost.startFramework();
bundleHost = aBundleHost;
bundleHost.startFramework();
}
catch (OSGiException | IOException e) {
e.printStackTrace();
@ -66,26 +74,29 @@ public class GhidraScriptUtil {
/**
* initialize state of GhidraScriptUtil with user, system paths, and optional extra system paths.
*
* @param bundleHost the host to use
* @param aBundleHost the host to use
* @param extraSystemPaths additional system paths for this run, can be null
*
*/
public static void initialize(BundleHost bundleHost, List<String> extraSystemPaths) {
setBundleHost(bundleHost);
public static void initialize(BundleHost aBundleHost, List<String> extraSystemPaths) {
setBundleHost(aBundleHost);
if (extraSystemPaths != null) {
for (String path : extraSystemPaths) {
bundleHost.add(new ResourceFile(path), true, true);
}
}
bundleHost.add(GhidraScriptUtil.getUserScriptDirectory(), true, false);
bundleHost.add(GhidraScriptUtil.getSystemScriptPaths(), true, true);
bundleHost.add(getUserScriptDirectory(), true, false);
bundleHost.add(getSystemScriptPaths(), true, true);
}
/**
* dispose of the bundle host and providers list
*/
public static void dispose() {
if (_bundleHost != null) {
_bundleHost.dispose();
_bundleHost = null;
if (bundleHost != null) {
bundleHost.dispose();
bundleHost = null;
}
providers = null;
}
@ -95,8 +106,10 @@ public class GhidraScriptUtil {
* @return a list of the current script directories
*/
public static List<ResourceFile> getScriptSourceDirectories() {
return _bundleHost.getBundlePaths().stream().filter(ResourceFile::isDirectory).collect(
Collectors.toList());
return bundleHost.getBundlePaths()
.stream()
.filter(ResourceFile::isDirectory)
.collect(Collectors.toList());
}
public static ResourceFile getSourceDirectoryContaining(ResourceFile sourceFile) {
@ -199,8 +212,10 @@ public class GhidraScriptUtil {
@Deprecated
public static List<ResourceFile> getExplodedCompiledSourceBundlePaths() {
try {
return Files.list(BundleHost.getOsgiDir()).filter(Files::isDirectory).map(
x -> new ResourceFile(x.toFile())).collect(Collectors.toList());
return Files.list(BundleHost.getOsgiDir())
.filter(Files::isDirectory)
.map(x -> new ResourceFile(x.toFile()))
.collect(Collectors.toList());
}
catch (IOException e) {
Msg.showError(GhidraScriptUtil.class, null, "error",
@ -231,7 +246,7 @@ public class GhidraScriptUtil {
* @return a list of all Ghidra script providers
*/
// Note: this method is synchronized so that two threads do not try to create the list when null
public synchronized static List<GhidraScriptProvider> getProviders() {
public static synchronized List<GhidraScriptProvider> getProviders() {
if (providers == null) {
List<GhidraScriptProvider> newProviders =
new ArrayList<>(ClassSearcher.getInstances(GhidraScriptProvider.class));
@ -338,8 +353,7 @@ public class GhidraScriptUtil {
return path + ".java";
}
static ResourceFile findScriptFileInPaths(
Collection<ResourceFile> scriptDirectories,
static ResourceFile findScriptFileInPaths(Collection<ResourceFile> scriptDirectories,
String filename) {
String validatedName = fixupName(filename);

View File

@ -41,7 +41,7 @@ public class GhidraState {
private ProgramSelection currentSelection;
private ProgramSelection currentHighlight;
private HashMap<String, Object> envmap = new HashMap<>();
private GatherParamPanel gatherParamPanel = null;
private GatherParamPanel gatherParamPanel;
private Project project;
private final boolean isGlobalState;
@ -105,6 +105,7 @@ public class GhidraState {
/**
* Sets the current program.
* @param program the new program object
*/
public void setCurrentProgram(Program program) {
if (program == currentProgram) {
@ -117,11 +118,19 @@ public class GhidraState {
gatherParamPanel.currentProgramChanged();
}
/**
* @return the address of the current location
*/
public Address getCurrentAddress() {
return currentLocation != null ? currentLocation.getAddress() : null;
}
/**
* If it differs, set the current location to the given address and fire a {@link ProgramLocationPluginEvent}.
*
* @param address the address
*/
public void setCurrentAddress(Address address) {
if (SystemUtilities.isEqual(address, getCurrentAddress())) {
return;
@ -129,10 +138,18 @@ public class GhidraState {
setCurrentLocation(new ProgramLocation(currentProgram, address));
}
/**
* @return the current location
*/
public ProgramLocation getCurrentLocation() {
return currentLocation;
}
/**
* If it differs, set the current location and fire a {@link ProgramLocationPluginEvent}.
*
* @param location
*/
public void setCurrentLocation(ProgramLocation location) {
if (SystemUtilities.isEqual(currentLocation, location)) {
return;
@ -144,10 +161,18 @@ public class GhidraState {
}
}
/**
* @return the currently highlighted selection
*/
public ProgramSelection getCurrentHighlight() {
return currentHighlight;
}
/**
* Set the currently highlighted selection and fire a {@link ProgramHighlightPluginEvent}.
*
* @param highlight the selection
*/
public void setCurrentHighlight(ProgramSelection highlight) {
if (SystemUtilities.isEqual(currentHighlight, highlight)) {
return;
@ -162,10 +187,18 @@ public class GhidraState {
}
}
/**
* @return the current selection
*/
public ProgramSelection getCurrentSelection() {
return currentSelection;
}
/**
* Set the current selection and fire a {@link kProgramSelectionPluginEvent}.
*
* @param selection the selection
*/
public void setCurrentSelection(ProgramSelection selection) {
if (SystemUtilities.isEqual(currentSelection, selection)) {
return;
@ -181,27 +214,27 @@ public class GhidraState {
}
public void addEnvironmentVar(String name, byte value) {
envmap.put(name, new Byte(value));
envmap.put(name, Byte.valueOf(value));
}
public void addEnvironmentVar(String name, short value) {
envmap.put(name, new Short(value));
envmap.put(name, Short.valueOf(value));
}
public void addEnvironmentVar(String name, int value) {
envmap.put(name, new Integer(value));
envmap.put(name, Integer.valueOf(value));
}
public void addEnvironmentVar(String name, long value) {
envmap.put(name, new Long(value));
envmap.put(name, Long.valueOf(value));
}
public void addEnvironmentVar(String name, float value) {
envmap.put(name, new Float(value));
envmap.put(name, Float.valueOf(value));
}
public void addEnvironmentVar(String name, double value) {
envmap.put(name, new Double(value));
envmap.put(name, Double.valueOf(value));
}
public void addEnvironmentVar(String name, Object value) {

View File

@ -24,18 +24,27 @@ import ghidra.app.plugin.core.osgi.*;
import ghidra.util.Msg;
public class JavaScriptProvider extends GhidraScriptProvider {
final private BundleHost _bundleHost;
private final BundleHost bundleHost;
/**
* Create a new {@link JavaScriptProvider} associated with the current bundle host used by scripting.
*/
public JavaScriptProvider() {
_bundleHost = GhidraScriptUtil.getBundleHost();
bundleHost = GhidraScriptUtil.getBundleHost();
}
/**
* Get the {@link GhidraSourceBundle} containing the given source file, assuming it already exists.
*
* @param sourceFile the source file
* @return the bundle
*/
public GhidraSourceBundle getBundleForSource(ResourceFile sourceFile) {
ResourceFile sourceDir = GhidraScriptUtil.getSourceDirectoryContaining(sourceFile);
if (sourceDir == null) {
return null;
}
return (GhidraSourceBundle) _bundleHost.getExistingGhidraBundle(sourceDir);
return (GhidraSourceBundle) bundleHost.getExistingGhidraBundle(sourceDir);
}
@Override
@ -53,7 +62,7 @@ public class JavaScriptProvider extends GhidraScriptProvider {
try {
Bundle osgiBundle = getBundleForSource(sourceFile).getOSGiBundle();
if (osgiBundle != null) {
_bundleHost.deactivateSynchronously(osgiBundle);
bundleHost.deactivateSynchronously(osgiBundle);
}
}
catch (GhidraBundleException | InterruptedException e) {
@ -92,13 +101,22 @@ public class JavaScriptProvider extends GhidraScriptProvider {
}
}
/**
* Activate and build the {@link GhidraSourceBundle} containing {@code sourceFile}
* then load the script's class from its class loader.
*
* @param sourceFile the source file
* @param writer the target for build messages
* @return the loaded {@link Class} object
* @throws Exception if build, activation, or class loading fail
*/
public Class<?> loadClass(ResourceFile sourceFile, PrintWriter writer) throws Exception {
GhidraSourceBundle gb = getBundleForSource(sourceFile);
gb.build(writer);
Bundle b = _bundleHost.install(gb);
_bundleHost.activateSynchronously(b);
Bundle b = bundleHost.install(gb);
bundleHost.activateSynchronously(b);
String classname = gb.classNameForScript(sourceFile);
Class<?> clazz = b.loadClass(classname); // throws ClassNotFoundException

View File

@ -34,11 +34,18 @@ public class ResourceFileJavaFileManager implements JavaFileManager {
private StandardJavaFileManager fileManager;
private List<ResourceFile> sourceDirs;
private Set<ResourceFile> avoid;
private Set<ResourceFile> filesToAvoid;
public ResourceFileJavaFileManager(List<ResourceFile> sourceDirs, Set<ResourceFile> avoid) {
/**
* Create a {@link JavaFileManager} for use by the {@link JavaCompiler}.
*
* @param sourceDirs the directories containing source
* @param filesToAvoid known "bad" files to hide from the compiler
*/
public ResourceFileJavaFileManager(List<ResourceFile> sourceDirs,
Set<ResourceFile> filesToAvoid) {
this.sourceDirs = sourceDirs;
this.avoid = avoid;
this.filesToAvoid = filesToAvoid;
JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler();
if (javaCompiler == null) {
throw new AssertException("Can't find java compiler");
@ -79,7 +86,7 @@ public class ResourceFileJavaFileManager implements JavaFileManager {
private void gatherFiles(ResourceFile root, ResourceFile file, List<JavaFileObject> accumulator,
Set<Kind> kinds, boolean recurse) {
List<ResourceFile> listFiles = new ArrayList<>(Arrays.asList(file.listFiles()));
listFiles.removeAll(avoid);
listFiles.removeAll(filesToAvoid);
for (ResourceFile resourceFile : listFiles) {
if (resourceFile.isDirectory()) {
if (recurse) {
@ -154,7 +161,7 @@ public class ResourceFileJavaFileManager implements JavaFileManager {
@Override
public JavaFileObject getJavaFileForInput(Location location, String className, Kind kind)
throws IOException {
if (!location.equals(StandardLocation.SOURCE_PATH) || className.equals("module-info")) {
if (!location.equals(StandardLocation.SOURCE_PATH) || "module-info".equals(className)) {
// Our Ghidra scripts will not use Java 9's module definition file (module-info.java).
return fileManager.getJavaFileForInput(location, className, kind);
}

View File

@ -20,6 +20,7 @@ import java.net.URI;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.NestingKind;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import generic.jar.ResourceFile;
@ -35,6 +36,13 @@ public class ResourceFileJavaFileObject implements JavaFileObject {
private String pathName;
private Kind kind;
/**
* Represents a {@link ResourceFile} for a {@link JavaCompiler} via a {@link ResourceFileJavaFileManager}
*
* @param sourceRoot the root source directory
* @param file the file
* @param kind the kind
*/
public ResourceFileJavaFileObject(ResourceFile sourceRoot, ResourceFile file, Kind kind) {
this.file = file;
this.kind = kind;
@ -43,6 +51,9 @@ public class ResourceFileJavaFileObject implements JavaFileObject {
pathName = file.getAbsolutePath().substring(sourceRootPath.length() + 1);
}
/**
* @return the {@link ResourceFile} this object represents
*/
public ResourceFile getFile() {
return file;
}

View File

@ -36,12 +36,8 @@ import resources.ResourceManager;
* This class parses the meta-data about a script.
*/
public class ScriptInfo {
private static final Pattern DOCUMENTATION_START = Pattern.compile("/\\*");
private static final Pattern DOCUMENTATION_END = Pattern.compile("\\*/");
/**
* The delimiter used to categories and menu paths.
* The delimiter used in categories and menu paths.
*/
public static final String DELIMITTER = ".";
@ -51,6 +47,9 @@ public class ScriptInfo {
static final String AT_MENUPATH = "@menupath";
static final String AT_TOOLBAR = "@toolbar";
private static final Pattern DOCUMENTATION_START = Pattern.compile("/\\*");
private static final Pattern DOCUMENTATION_END = Pattern.compile("\\*/");
// omit from METADATA to avoid pre-populating in new scripts
private static final String AT_IMPORTPACKAGE = "@importpackage";
@ -231,7 +230,7 @@ public class ScriptInfo {
description = buffer.toString();
modified = sourceFile.lastModified();
}
catch (Exception e) {
catch (IOException e) {
Msg.debug(this, "Unexpected exception reading script: " + sourceFile, e);
}
finally {
@ -403,6 +402,9 @@ public class ScriptInfo {
return keyBinding;
}
/**
* @return an error resulting from parsing keybinding metadata
*/
public String getKeyBindingErrorMessage() {
return keybindingErrorMessage;
}
@ -460,7 +462,6 @@ public class ScriptInfo {
String space = HTML_SPACE;
String htmlAuthor = bold("Author:") + space + escapeHTML(toString(author));
String htmlCategory = bold("Category:") + space + escapeHTML(toString(category));
String htmlKeyBinding = bold("Key Binding:") + space + getKeybindingToolTip();
String htmlMenuPath = bold("Menu Path:") + space + escapeHTML(toString(menupath));
@ -505,10 +506,16 @@ public class ScriptInfo {
return StringUtils.defaultString(joined);
}
/**
* @return true if the script either has compiler errors, or is a duplicate
*/
public boolean hasErrors() {
return isCompileErrors() || isDuplicate();
}
/**
* @return a generic error message
*/
public String getErrorMessage() {
if (isCompileErrors()) {
return "Script contains compiler errors";

View File

@ -16,6 +16,5 @@
package ghidra.app.script;
public interface StringTransformer<T> {
public T apply(String s);
T apply(String s);
}

View File

@ -174,11 +174,11 @@ public abstract class AbstractGhidraScriptMgrPluginTest
env.dispose();
}
static protected void wipe(ResourceFile path) throws IOException {
protected static void wipe(ResourceFile path) throws IOException {
wipe(Paths.get(path.getAbsolutePath()));
}
static protected void wipe(Path path) throws IOException {
protected static void wipe(Path path) throws IOException {
if (Files.exists(path)) {
for (Path p : (Iterable<Path>) Files.walk(path)
.sorted(Comparator.reverseOrder())::iterator) {
@ -392,7 +392,7 @@ public abstract class AbstractGhidraScriptMgrPluginTest
assertNotNull(editor);
editorTextArea = (JTextArea) findComponentByName(editor.getComponent(), "EDITOR");
editorTextArea = (JTextArea) findComponentByName(editor.getComponent(), GhidraScriptEditorComponentProvider.EDITOR_COMPONENT_NAME);
assertNotNull(editorTextArea);
buffer = new StringBuffer(editorTextArea.getText());
@ -411,7 +411,7 @@ public abstract class AbstractGhidraScriptMgrPluginTest
// initialize our editor variable to the newly opened editor
editor = waitForComponentProvider(GhidraScriptEditorComponentProvider.class);
editorTextArea = (JTextArea) findComponentByName(editor.getComponent(), "EDITOR");
editorTextArea = (JTextArea) findComponentByName(editor.getComponent(), GhidraScriptEditorComponentProvider.EDITOR_COMPONENT_NAME);
waitForSwing();
@ -850,7 +850,7 @@ public abstract class AbstractGhidraScriptMgrPluginTest
Map<ResourceFile, GhidraScriptEditorComponentProvider> map = provider.getEditorMap();
GhidraScriptEditorComponentProvider fileEditor = map.get(file);
final JTextArea textArea =
(JTextArea) findComponentByName(fileEditor.getComponent(), "EDITOR");
(JTextArea) findComponentByName(fileEditor.getComponent(), GhidraScriptEditorComponentProvider.EDITOR_COMPONENT_NAME);
assertNotNull(textArea);
final String[] box = new String[1];
@ -1164,7 +1164,7 @@ public abstract class AbstractGhidraScriptMgrPluginTest
return info.getSourceFile();
}
static protected String CANCELLABLE_SCRIPT_NAME = TestChangeProgramScript.class.getName();
protected static String CANCELLABLE_SCRIPT_NAME = TestChangeProgramScript.class.getName();
protected void cancel() throws Exception {
Window window = waitForWindowByTitleContaining(CANCELLABLE_SCRIPT_NAME);
@ -1494,7 +1494,7 @@ public abstract class AbstractGhidraScriptMgrPluginTest
protected JTextComponent grabScriptEditorTextArea() {
GhidraScriptEditorComponentProvider scriptEditor = grabScriptEditor();
JTextArea textArea = (JTextArea) findComponentByName(scriptEditor.getComponent(), "EDITOR");
JTextArea textArea = (JTextArea) findComponentByName(scriptEditor.getComponent(), GhidraScriptEditorComponentProvider.EDITOR_COMPONENT_NAME);
assertNotNull(textArea);
return textArea;
}
@ -1596,9 +1596,9 @@ public abstract class AbstractGhidraScriptMgrPluginTest
}
}
}
catch (CancelledException ce) {
catch (CancelledException e) {
doneLatch.countDown();
throw ce;
throw e;
}
doneLatch.countDown();

View File

@ -119,7 +119,7 @@ public class GhidraScriptMgrPluginScreenShots extends GhidraScreenShotGenerator
BundleStatusComponentProvider bundleStatusComponentProvider =
showProvider(BundleStatusComponentProvider.class);
bundleStatusComponentProvider.getModel().setPathsForTesting(paths);
bundleStatusComponentProvider.setPathsForTesting(paths);
waitForComponentProvider(BundleStatusComponentProvider.class);
captureComponent(bundleStatusComponentProvider.getComponent());