Merge branch 'GP-3551_ghidra1_InternalProjectLinks'
@@ -15,6 +15,43 @@ applied Ghidra SRE capabilities to a variety of problems that involve analyzing
|
||||
generating deep insights for NSA analysts who seek a better understanding of potential
|
||||
vulnerabilities in networks and systems.
|
||||
|
||||
# What's coming in Ghidra 11.5
|
||||
This is a preview of what is coming in the future Ghidra 11.5 release.
|
||||
|
||||
**NOTE:** Ghidra Server: The Ghidra 11.5 server is compatible with Ghidra 9.2 and later Ghidra
|
||||
clients although the presence of any newer link-files within a repository may not be handled properly
|
||||
by client versions prior to 11.5 which lack support for the new storage format. Ghidra 11.5 clients
|
||||
which introduce new link-files into a project will not be able to add such files into version
|
||||
control if connected to older Ghidra Server versions.
|
||||
|
||||
## Project Link Files
|
||||
|
||||
Support for link-files within a Ghidra Project has been significantly expanded with this release and
|
||||
with it a new file storage type has been introduced which can create some incompatibilties if projects
|
||||
and repositories containing such files are used by older version of Ghidra or the Ghidra Server.
|
||||
|
||||
Previously only external folder and file links were supported through the use of a Ghidra URL.
|
||||
With 11.5 the ability to establish internal folder and file links has been introduced. The new
|
||||
storage format avoids the use of a database and relies only on a light-weight property file. Internal
|
||||
project links also allow for either absolute or relative links. Due to the fact that Ghidra allows
|
||||
a folder or file to have the same pathname, some abiguities can result. It is highly recommended that
|
||||
the use of conflicting folder and file pathnames be avoided.
|
||||
|
||||
The use of internally linked folders and files allows batch import processing to more accurately
|
||||
reflect the native file-system and its use of symbolic links which allow for the same content to
|
||||
be referenced by multiple paths. Allowing this within a Ghidra project can avoid the potential for
|
||||
importing content multiple times with the different paths and simply import once with additional
|
||||
link-files which reference it. How best to leverage links very much depends on the end-user's
|
||||
needs and project file management preferences. Special care must be taken when defining or
|
||||
traversing link-files to avoid external and circular references.
|
||||
|
||||
Additional Ghidra API methods have been provided or refined on the following classes to leverage
|
||||
link-files: `DomainFolder`, `DomainFile`, `LinkFile`, `LinkHandler`, `DomainFileFilter`,
|
||||
`DomainFileIterator`, etc.
|
||||
|
||||
...TO BE CONTINUED...
|
||||
|
||||
|
||||
# What's New in Ghidra 11.4
|
||||
This release includes new features, enhancements, performance improvements, quite a few bug fixes,
|
||||
and many pull-request contributions. Thanks to all those who have contributed their time, thoughts,
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@@ -116,10 +116,8 @@ public class DebuggerCopyActionsPlugin extends AbstractDebuggerPlugin {
|
||||
? view.getTrace().getFixedProgramView(view.getSnap())
|
||||
: view;
|
||||
|
||||
ExporterDialog dialog =
|
||||
new ExporterDialog(tool, fixed.getDomainFile(), fixed,
|
||||
getSelectionFromContext(context));
|
||||
tool.showDialog(dialog);
|
||||
ExporterDialog.showExporterDialog(tool, fixed.getDomainFile(), fixed,
|
||||
getSelectionFromContext(context));
|
||||
}
|
||||
|
||||
protected void activatedCopyIntoCurrentProgram(DebuggerProgramLocationActionContext context) {
|
||||
|
||||
@@ -26,6 +26,7 @@ import ghidra.app.plugin.core.debug.utils.ProgramURLUtils;
|
||||
import ghidra.framework.model.*;
|
||||
import ghidra.framework.options.Options;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.database.ProgramContentHandler;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.trace.model.Lifespan;
|
||||
@@ -73,7 +74,8 @@ public class ProgramModuleIndexer implements DomainFolderChangeListener {
|
||||
// TODO: Note language and prefer those from the same processor?
|
||||
// Will get difficult with new OBTR, since I'd need a platform
|
||||
// There's also the WoW64 issue....
|
||||
protected record IndexEntry(String name, String dfID, NameSource source) {}
|
||||
protected record IndexEntry(String name, String dfID, NameSource source) {
|
||||
}
|
||||
|
||||
protected class ModuleChangeListener
|
||||
implements DomainObjectListener, DomainObjectClosedListener {
|
||||
@@ -212,10 +214,13 @@ public class ProgramModuleIndexer implements DomainFolderChangeListener {
|
||||
if (disposed) {
|
||||
return;
|
||||
}
|
||||
if (!Program.class.isAssignableFrom(file.getDomainObjectClass())) {
|
||||
return;
|
||||
// Folder-links and program link-files are not handled. Using content type
|
||||
// to filter is the best way to control this. If program links should be considered
|
||||
// "Program.class.isAssignableFrom(domainFile.getDomainObjectClass())"
|
||||
// should be used.
|
||||
if (ProgramContentHandler.PROGRAM_CONTENT_TYPE.equals(file.getContentType())) {
|
||||
addToIndex(file, file.getMetadata());
|
||||
}
|
||||
addToIndex(file, file.getMetadata());
|
||||
}
|
||||
|
||||
protected void addToIndex(DomainFile file, Map<String, String> metadata) {
|
||||
@@ -383,9 +388,9 @@ public class ProgramModuleIndexer implements DomainFolderChangeListener {
|
||||
public DomainFile getBestMatch(TraceModule module, long snap, Program currentProgram,
|
||||
Collection<IndexEntry> entries) {
|
||||
Address base = module.getBase(snap);
|
||||
AddressSpace space = base == null
|
||||
? module.getTrace().getBaseAddressFactory().getDefaultAddressSpace()
|
||||
: base.getAddressSpace();
|
||||
AddressSpace space =
|
||||
base == null ? module.getTrace().getBaseAddressFactory().getDefaultAddressSpace()
|
||||
: base.getAddressSpace();
|
||||
return getBestMatch(space, module, snap, currentProgram, entries);
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
package ghidra.app.plugin.core.debug.service.tracemgr;
|
||||
|
||||
import static ghidra.framework.main.DataTreeDialogType.OPEN;
|
||||
import static ghidra.framework.main.DataTreeDialogType.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
@@ -223,9 +223,7 @@ public class DebuggerTraceManagerServicePlugin extends Plugin
|
||||
public void targetWithdrawn(Target target) {
|
||||
Swing.runLater(() -> updateCurrentTarget());
|
||||
boolean save = isSaveTracesByDefault();
|
||||
CompletableFuture<Void> flush = save
|
||||
? waitUnlockedDebounced(target)
|
||||
: AsyncUtils.nil();
|
||||
CompletableFuture<Void> flush = save ? waitUnlockedDebounced(target) : AsyncUtils.nil();
|
||||
flush.thenRunAsync(() -> {
|
||||
if (!isAutoCloseOnTerminate()) {
|
||||
return;
|
||||
@@ -416,20 +414,7 @@ public class DebuggerTraceManagerServicePlugin extends Plugin
|
||||
}
|
||||
|
||||
protected DataTreeDialog getTraceChooserDialog() {
|
||||
|
||||
DomainFileFilter filter = new DomainFileFilter() {
|
||||
|
||||
@Override
|
||||
public boolean accept(DomainFile df) {
|
||||
return Trace.class.isAssignableFrom(df.getDomainObjectClass());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean followLinkedFolders() {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
DomainFileFilter filter = new DefaultDomainFileFilter(Trace.class, false);
|
||||
return new DataTreeDialog(null, OpenTraceAction.NAME, OPEN, filter);
|
||||
}
|
||||
|
||||
@@ -454,11 +439,8 @@ public class DebuggerTraceManagerServicePlugin extends Plugin
|
||||
|
||||
@Override
|
||||
public void closeDeadTraces() {
|
||||
checkCloseTraces(targetService == null
|
||||
? getOpenTraces()
|
||||
: getOpenTraces().stream()
|
||||
.filter(t -> targetService.getTarget(t) == null)
|
||||
.toList(),
|
||||
checkCloseTraces(targetService == null ? getOpenTraces()
|
||||
: getOpenTraces().stream().filter(t -> targetService.getTarget(t) == null).toList(),
|
||||
false);
|
||||
}
|
||||
|
||||
@@ -790,8 +772,7 @@ public class DebuggerTraceManagerServicePlugin extends Plugin
|
||||
varView.setSnap(snap);
|
||||
varView.setPlatform(coordinates.getPlatform());
|
||||
fireLocationEvent(coordinates, cause);
|
||||
}, cause == ActivationCause.EMU_STATE_EDIT
|
||||
? SwingExecutorService.MAYBE_NOW // ProgramView may call .get on Swing thread
|
||||
}, cause == ActivationCause.EMU_STATE_EDIT ? SwingExecutorService.MAYBE_NOW // ProgramView may call .get on Swing thread
|
||||
: SwingExecutorService.LATER); // Respect event order
|
||||
}
|
||||
|
||||
@@ -845,7 +826,7 @@ public class DebuggerTraceManagerServicePlugin extends Plugin
|
||||
// TODO: Support upgrading
|
||||
e = new VersionException(e.getVersionIndicator(), false).combine(e);
|
||||
VersionExceptionHandler.showVersionError(null, file.getName(), file.getContentType(),
|
||||
"Open", e);
|
||||
"Open", false, e);
|
||||
return null;
|
||||
}
|
||||
catch (IOException e) {
|
||||
@@ -1069,10 +1050,7 @@ public class DebuggerTraceManagerServicePlugin extends Plugin
|
||||
|
||||
protected void checkCloseTraces(Collection<Trace> traces, boolean noConfirm) {
|
||||
List<Target> live =
|
||||
traces.stream()
|
||||
.map(t -> targetService.getTarget(t))
|
||||
.filter(t -> t != null)
|
||||
.toList();
|
||||
traces.stream().map(t -> targetService.getTarget(t)).filter(t -> t != null).toList();
|
||||
/**
|
||||
* A provider may be reading a trace, likely via the Swing thread, so schedule this on the
|
||||
* same thread to avoid a ClosedException.
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@@ -313,7 +313,7 @@ public class DBTraceContentHandler extends DBWithUserDataContentHandler<DBTrace>
|
||||
|
||||
@Override
|
||||
public String getContentTypeDisplayString() {
|
||||
return "Trace";
|
||||
return TRACE_CONTENT_TYPE;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@@ -15,32 +15,15 @@
|
||||
*/
|
||||
package ghidra.trace.database;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.swing.Icon;
|
||||
|
||||
import ghidra.framework.data.LinkHandler;
|
||||
import ghidra.framework.data.URLLinkObject;
|
||||
import ghidra.framework.model.DomainObject;
|
||||
import ghidra.framework.store.FileSystem;
|
||||
import ghidra.util.InvalidNameException;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
public class DBTraceLinkContentHandler extends LinkHandler<DBTrace> {
|
||||
|
||||
public static final String TRACE_LINK_CONTENT_TYPE = "TraceLink";
|
||||
public static DBTraceLinkContentHandler INSTANCE = new DBTraceLinkContentHandler();
|
||||
|
||||
@Override
|
||||
public long createFile(FileSystem fs, FileSystem userfs, String path, String name,
|
||||
DomainObject obj, TaskMonitor monitor)
|
||||
throws IOException, InvalidNameException, CancelledException {
|
||||
if (!(obj instanceof URLLinkObject)) {
|
||||
throw new IOException("Unsupported domain object: " + obj.getClass().getName());
|
||||
}
|
||||
return createFile((URLLinkObject) obj, TRACE_LINK_CONTENT_TYPE, fs, path, name,
|
||||
monitor);
|
||||
}
|
||||
public static final String TRACE_LINK_CONTENT_TYPE = "TraceLink";
|
||||
|
||||
@Override
|
||||
public String getContentType() {
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@@ -15,8 +15,6 @@
|
||||
*/
|
||||
package ghidra.machinelearning.functionfinding;
|
||||
|
||||
import static ghidra.framework.main.DataTreeDialogType.*;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
@@ -35,7 +33,7 @@ import docking.widgets.table.GTable;
|
||||
import docking.widgets.table.threaded.GThreadedTablePanel;
|
||||
import docking.widgets.textfield.IntegerTextField;
|
||||
import ghidra.app.services.ProgramManager;
|
||||
import ghidra.framework.main.DataTreeDialog;
|
||||
import ghidra.framework.main.ProgramFileChooser;
|
||||
import ghidra.framework.model.DomainFile;
|
||||
import ghidra.framework.preferences.Preferences;
|
||||
import ghidra.program.model.address.AddressSet;
|
||||
@@ -485,11 +483,7 @@ public class FunctionStartRFParamsDialog extends ReusableDialogComponentProvider
|
||||
}
|
||||
|
||||
private void searchOtherProgram(RandomForestRowObject modelRow) {
|
||||
DataTreeDialog dtd =
|
||||
new DataTreeDialog(null, "Select Program", OPEN, f -> {
|
||||
Class<?> c = f.getDomainObjectClass();
|
||||
return Program.class.isAssignableFrom(c);
|
||||
});
|
||||
ProgramFileChooser dtd = new ProgramFileChooser(null, "Select Program");
|
||||
dtd.show();
|
||||
DomainFile dFile = dtd.getDomainFile();
|
||||
if (dFile == null) {
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@@ -22,6 +22,7 @@ import java.net.URL;
|
||||
import ghidra.framework.model.DomainFile;
|
||||
import ghidra.framework.model.DomainFolder;
|
||||
import ghidra.framework.protocol.ghidra.*;
|
||||
import ghidra.framework.protocol.ghidra.GhidraURLQuery.LinkFileControl;
|
||||
import ghidra.program.database.ProgramContentHandler;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.util.Msg;
|
||||
@@ -56,7 +57,8 @@ public abstract class IterateRepository {
|
||||
throw new MalformedURLException("Unsupported repository URL: " + ghidraURL);
|
||||
}
|
||||
|
||||
GhidraURLQuery.queryUrl(ghidraURL, new GhidraURLResultHandlerAdapter(true) {
|
||||
// Query URL - may be either file or folder (no link following)
|
||||
GhidraURLQuery.queryUrl(ghidraURL, null, new GhidraURLResultHandlerAdapter(true) {
|
||||
|
||||
@Override
|
||||
public void processResult(DomainFolder domainFolder, URL url, TaskMonitor m)
|
||||
@@ -76,7 +78,9 @@ public abstract class IterateRepository {
|
||||
process(domainFile, monitor);
|
||||
}
|
||||
|
||||
}, monitor);
|
||||
// Link files are skipped to avoid duplicate processing
|
||||
// Processing should be done on actual folder - not a linked folder
|
||||
}, LinkFileControl.NO_FOLLOW, monitor);
|
||||
|
||||
}
|
||||
|
||||
@@ -115,12 +119,11 @@ public abstract class IterateRepository {
|
||||
private void process(DomainFile file, TaskMonitor monitor)
|
||||
throws IOException, CancelledException {
|
||||
|
||||
// Do not follow folder-links or consider program links. Using content type
|
||||
// to filter is best way to control this. If program links should be considered
|
||||
// "Program.class.isAssignableFrom(domainFile.getDomainObjectClass())"
|
||||
// Do not follow folder-links or consider program links to avoid possible duplication of
|
||||
// file processing. Using content type is the best way to restrict this. If program links
|
||||
// should be considered "Program.class.isAssignableFrom(domainFile.getDomainObjectClass())"
|
||||
// should be used.
|
||||
if (!ProgramContentHandler.PROGRAM_CONTENT_TYPE.equals(file.getContentType())) {
|
||||
// NOTE: linked-folders and linked-files are not currently supported
|
||||
return; // skip non-program file
|
||||
}
|
||||
|
||||
@@ -129,6 +132,7 @@ public abstract class IterateRepository {
|
||||
Msg.debug(IterateRepository.class, "Processing " + file.getPathname() + "...");
|
||||
monitor.setMessage("Processing: " + file.getName());
|
||||
monitor.incrementProgress(1);
|
||||
// NOTE: The following method invocation will follow all links if presented one
|
||||
program = (Program) file.getReadOnlyDomainObject(this, -1, monitor);
|
||||
process(program, monitor);
|
||||
}
|
||||
|
||||
@@ -398,6 +398,10 @@ src/main/help/help/topics/FrontEndPlugin/Project_Info.htm||GHIDRA||||END|
|
||||
src/main/help/help/topics/FrontEndPlugin/Re-opening_a_Project.htm||GHIDRA||||END|
|
||||
src/main/help/help/topics/FrontEndPlugin/Restore_Project.htm||GHIDRA||||END|
|
||||
src/main/help/help/topics/FrontEndPlugin/Saving_a_Ghidra_Project.htm||GHIDRA||||END|
|
||||
src/main/help/help/topics/FrontEndPlugin/images/AbsoluteBrokenFileLinkIcon.png||GHIDRA||||END|
|
||||
src/main/help/help/topics/FrontEndPlugin/images/AbsoluteBrokenFolderLinkIcon.png||GHIDRA||||END|
|
||||
src/main/help/help/topics/FrontEndPlugin/images/AbsoluteFileLinkIcon.png||GHIDRA||||END|
|
||||
src/main/help/help/topics/FrontEndPlugin/images/AbsoluteFolderLinkIcon.png||GHIDRA||||END|
|
||||
src/main/help/help/topics/FrontEndPlugin/images/ArchiveFileExists.png||GHIDRA||||END|
|
||||
src/main/help/help/topics/FrontEndPlugin/images/ArchiveProject.png||GHIDRA||||END|
|
||||
src/main/help/help/topics/FrontEndPlugin/images/ChangeAccessList.png||GHIDRA||||END|
|
||||
@@ -439,6 +443,7 @@ src/main/help/help/topics/FrontEndPlugin/images/VersionedFileIcon.png||GHIDRA|||
|
||||
src/main/help/help/topics/FrontEndPlugin/images/ViewOtherProjects.png||GHIDRA||||END|
|
||||
src/main/help/help/topics/FrontEndPlugin/images/ViewProjectAccessPanel.png||GHIDRA||||END|
|
||||
src/main/help/help/topics/FrontEndPlugin/images/hijack_file.png||GHIDRA||||END|
|
||||
src/main/help/help/topics/FrontEndPlugin/images/start-here_16.png||GHIDRA||||END|
|
||||
src/main/help/help/topics/FunctionComparison/FunctionComparison.htm||GHIDRA||||END|
|
||||
src/main/help/help/topics/FunctionComparison/images/AddFunctionsPanel.png||GHIDRA||||END|
|
||||
src/main/help/help/topics/FunctionComparison/images/AddToComparisonIcon.png||GHIDRA||||END|
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@@ -30,7 +30,7 @@ import ghidra.app.script.ImproperUseException;
|
||||
import ghidra.framework.data.GhidraFile;
|
||||
import ghidra.framework.data.GhidraFileData;
|
||||
import ghidra.framework.main.DataTreeDialog;
|
||||
import ghidra.framework.model.DomainFile;
|
||||
import ghidra.framework.model.*;
|
||||
import ghidra.framework.store.FolderItem;
|
||||
import ghidra.framework.store.local.LocalDatabaseItem;
|
||||
import ghidra.program.model.lang.LanguageDescription;
|
||||
@@ -115,8 +115,8 @@ public class FixLangId extends GhidraScript {
|
||||
if (langId != null) {
|
||||
Msg.warn(this, "Changing language ID from '" + record.getString(0) + "' to '" +
|
||||
langId + "' for program: " + df.getName());
|
||||
desc = DefaultLanguageService.getLanguageService().getLanguageDescription(
|
||||
new LanguageID(langId));
|
||||
desc = DefaultLanguageService.getLanguageService()
|
||||
.getLanguageDescription(new LanguageID(langId));
|
||||
long txId = dbh.startTransaction();
|
||||
try {
|
||||
record.setString(0, langId);
|
||||
@@ -139,7 +139,10 @@ public class FixLangId extends GhidraScript {
|
||||
|
||||
public DomainFile askProgramFile(String title) {
|
||||
final DomainFile[] domainFile = new DomainFile[] { null };
|
||||
final DataTreeDialog dtd = new DataTreeDialog(null, title, OPEN);
|
||||
// The file filter employed restricts selection to a program file within the active
|
||||
// project where we have the ability to update file data.
|
||||
final DataTreeDialog dtd =
|
||||
new DataTreeDialog(null, title, OPEN, new DefaultDomainFileFilter(Program.class, true));
|
||||
dtd.addOkActionListener(e -> {
|
||||
dtd.close();
|
||||
domainFile[0] = dtd.getDomainFile();
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
/* ###
|
||||
* 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@@ -20,48 +19,56 @@
|
||||
import ghidra.app.script.GhidraScript;
|
||||
import ghidra.framework.model.*;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.database.ProgramContentHandler;
|
||||
import ghidra.util.InvalidNameException;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class RenameProgramsInProjectScript extends GhidraScript {
|
||||
|
||||
@Override
|
||||
public void run() throws Exception {
|
||||
|
||||
if ( currentProgram != null ) {
|
||||
popup( "This script should be run from a tool with no open programs" );
|
||||
return;
|
||||
}
|
||||
|
||||
PluginTool tool = state.getTool();
|
||||
Project project = tool.getProject();
|
||||
ProjectData projectData = project.getProjectData();
|
||||
DomainFolder rootFolder = projectData.getRootFolder();
|
||||
recurseProjectFolder( rootFolder );
|
||||
}
|
||||
|
||||
private void recurseProjectFolder( DomainFolder domainFolder ) {
|
||||
DomainFile[] files = domainFolder.getFiles();
|
||||
for ( DomainFile domainFile : files ) {
|
||||
processDomainFile( domainFile );
|
||||
}
|
||||
DomainFolder[] folders = domainFolder.getFolders();
|
||||
for ( DomainFolder folder : folders ) {
|
||||
recurseProjectFolder( folder );
|
||||
}
|
||||
}
|
||||
|
||||
private void processDomainFile( DomainFile domainFile ) {
|
||||
String oldName = domainFile.getName();
|
||||
try {
|
||||
domainFile.setName( oldName + "_renamed" );
|
||||
}
|
||||
catch ( InvalidNameException e ) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
catch ( IOException e ) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void run() throws Exception {
|
||||
|
||||
if (currentProgram != null) {
|
||||
popup("This script should be run from a tool with no open programs.\n" +
|
||||
"Warning! If using file-links to programs within this project such linkages will break.");
|
||||
return;
|
||||
}
|
||||
|
||||
PluginTool tool = state.getTool();
|
||||
Project project = tool.getProject();
|
||||
ProjectData projectData = project.getProjectData();
|
||||
DomainFolder rootFolder = projectData.getRootFolder();
|
||||
recurseProjectFolder(rootFolder);
|
||||
}
|
||||
|
||||
private void recurseProjectFolder(DomainFolder domainFolder) throws CancelledException {
|
||||
DomainFile[] files = domainFolder.getFiles();
|
||||
for (DomainFile domainFile : files) {
|
||||
monitor.checkCancelled();
|
||||
processDomainFile(domainFile);
|
||||
}
|
||||
DomainFolder[] folders = domainFolder.getFolders();
|
||||
for (DomainFolder folder : folders) {
|
||||
monitor.checkCancelled();
|
||||
recurseProjectFolder(folder);
|
||||
}
|
||||
}
|
||||
|
||||
private void processDomainFile(DomainFile domainFile) {
|
||||
if (!ProgramContentHandler.PROGRAM_CONTENT_TYPE.equals(domainFile.getContentType())) {
|
||||
return;
|
||||
}
|
||||
String oldName = domainFile.getName();
|
||||
try {
|
||||
domainFile.setName(oldName + "_renamed");
|
||||
}
|
||||
catch (InvalidNameException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,6 +102,8 @@ public class RepositoryFileUpgradeScript extends GhidraScript {
|
||||
}
|
||||
|
||||
private int listCheckouts(DomainFolder folder) throws IOException, CancelledException {
|
||||
// Avoid following folder-links so we don't count the same file more than once.
|
||||
// Link-files will never be in a checked-out state.
|
||||
int count = 0;
|
||||
for (DomainFile df : folder.getFiles()) {
|
||||
monitor.checkCancelled();
|
||||
@@ -115,8 +117,8 @@ public class RepositoryFileUpgradeScript extends GhidraScript {
|
||||
}
|
||||
|
||||
private int listCheckouts(DomainFile df) throws IOException {
|
||||
if (!df.isVersioned()) {
|
||||
return 0;
|
||||
if (!df.isVersioned() || df.isLink()) {
|
||||
return 0; // ignore non-versioned files and link-files
|
||||
}
|
||||
int count = 0;
|
||||
for (ItemCheckoutStatus checkout : df.getCheckouts()) {
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@@ -27,7 +27,6 @@ public class VersionControl_ResetAll extends GhidraScript {
|
||||
public VersionControl_ResetAll() {
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void run() throws Exception {
|
||||
|
||||
@@ -54,7 +53,8 @@ public class VersionControl_ResetAll extends GhidraScript {
|
||||
if (monitor.isCancelled()) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Do not follow folder-links or consider program links. Checking the content type
|
||||
// is the best way to restrict this.
|
||||
if (!ProgramContentHandler.PROGRAM_CONTENT_TYPE.equals(file.getContentType()) ||
|
||||
!file.isVersioned() || file.getLatestVersion() < 2) {
|
||||
continue;// skip
|
||||
|
||||
@@ -60,6 +60,8 @@
|
||||
<LI><A href="#ProjectDataTable">Data Table</A></LI>
|
||||
|
||||
<LI><A href="#FileIcons">File Icons</A></LI>
|
||||
|
||||
<LI><A href="#GhidraURLFormats">Ghidra URL Formats</A></LI>
|
||||
|
||||
<LI><A href="#StatusWindow">Console</A></LI>
|
||||
|
||||
@@ -93,8 +95,10 @@
|
||||
<H2><A name="ActiveProjectPanel"></A>Active Project</H2>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<P>The Active Project view shows your programs and datatype archives in a tree view or a
|
||||
table view. The tree view is useful for organizing your files into folders and sub-folders.
|
||||
<P>The Active Project view shows the various files associated with the current
|
||||
project which has been open for update. Project files generally consist of programs and
|
||||
datatype archives but may also be related to other Ghidra content.
|
||||
The tree view is useful for organizing your files into folders and sub-folders.
|
||||
The table view is useful for sorting all your files on some particular attribute such as
|
||||
size, processor, or modification date. In either view, you open and perform various
|
||||
actions on program files or datatype archives.</P>
|
||||
@@ -105,13 +109,23 @@
|
||||
</CENTER>
|
||||
<BLOCKQUOTE>
|
||||
<P>The data tree shows all files in the project orgnanized into folders and sub-folders.
|
||||
<A href="#FileIcons">Icons for files</A>
|
||||
indicate whether they are under <A href=
|
||||
<A href="#FileIcons">Icons for files</A> indicate whether they are under <A href=
|
||||
"help/topics/VersionControl/project_repository.htm#Versioning">version control</A> and whether
|
||||
you have the file <A href=
|
||||
"help/topics/VersionControl/project_repository.htm#SampleCheckOutIcon">checked out</A>.
|
||||
Open this view by activating the "Tree View" tab.</P>
|
||||
In addition, unique icons are used to reflect content-type and if it corresponds to
|
||||
a link-file referring to another file or folder (see <A href="#Paste_Link">creating links</A>).
|
||||
Open this view by activating the project window "Tree View" tab.</P>
|
||||
|
||||
<P><IMG src="help/shared/tip.png" border="0">Although Ghidra allows a folder and file within
|
||||
the same parent folder to have the same name, it is recommended this be avoided if possible.
|
||||
Allowing both a folder and file to have the same pathname can result in ambiguous path problems
|
||||
when using link files and/or Ghidra URLs where only a path is used to identify either a project
|
||||
resource.
|
||||
</P>
|
||||
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<P> </P>
|
||||
<H3>Tree Only Actions</H3>
|
||||
|
||||
@@ -124,7 +138,7 @@
|
||||
<P>To create a new folder,</P>
|
||||
|
||||
<OL>
|
||||
<LI>Select a folder that you own. </LI>
|
||||
<LI>Select a folder which should contain the new folder.</LI>
|
||||
|
||||
<LI>Right mouse click and choose the <I>New Folder</I> option.</LI>
|
||||
|
||||
@@ -133,8 +147,6 @@
|
||||
editing.</LI>
|
||||
</OL>
|
||||
|
||||
<P><IMG src="help/shared/note.png" border="0"> You cannot create
|
||||
a sub-folder of a folder that you do not own.</P>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<H4><A name="Copy"></A><A name="Paste"></A>Copy Folders and Files</H4>
|
||||
@@ -159,7 +171,47 @@
|
||||
time. </LI>
|
||||
</OL>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<H4><A name="Paste_Link"></A><A name="Paste_Relative_Link"></A>Paste Copied Folder or File as a Link</H4>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<P>A Link may be created within the active project to a file or folder within the
|
||||
same project (internal) or to a viewed project/repository (external).
|
||||
Internal links may be defined using either a relative path or an absolute path. Once
|
||||
a link is created its stored path will not change. The link will need to be replaced
|
||||
should the referenced path need to be changed. In addition, file-links are specific
|
||||
to the content-type of the referenced file at the time of link creation (e.g.,
|
||||
ProgramLink).
|
||||
</P>
|
||||
<P>To create a Link use the following steps from the source project data tree:</P>
|
||||
<OL>
|
||||
<LI>Select a single file or folder, right mouse click and choose the <I>Copy</I> option.</LI>
|
||||
|
||||
<LI>Select a destination folder within the active project data tree.</LI>
|
||||
|
||||
<LI>Right mouse click and choose the <I>Paste as Link</I> or <I>Paste as Relative-Link</I>
|
||||
option.</LI>
|
||||
</OL>
|
||||
|
||||
<P>See <A href="#Create_File_Links">Create Linked Folder or File</A> for more information
|
||||
about links and creating external links.
|
||||
</P>
|
||||
|
||||
<P>An internal link in the project tree may indicate a "broken" status for
|
||||
various reasons, including:</P>
|
||||
<ul>
|
||||
<li>The referenced file or folder does not exist,</li>
|
||||
<li>the content-type at the referenced location does not match the link type, or</li>
|
||||
<li>a folder-link results in a circular path reference.</li>
|
||||
</ul>
|
||||
<P>A broken link will have an icon which conveys its type but with a jagged red line
|
||||
through it and a tooltip which conveys the issue detected.</P>
|
||||
|
||||
<P><IMG src="help/shared/note.png" border="0">External links will never show a broken
|
||||
link state since they are not evaluated for such conditions.</P>
|
||||
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<H4><A name="Cut"></A>Move Folders and Files</H4>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
@@ -182,7 +234,7 @@
|
||||
</OL>
|
||||
|
||||
<P><IMG src="help/shared/note.png" border="0">You cannot move a
|
||||
file that is in use.</P>
|
||||
file that is in use or a folder that contains a file that is in use.</P>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<H4>Drag/Drop for Copy</H4>
|
||||
@@ -217,6 +269,9 @@
|
||||
|
||||
<LI>Release the mouse button when you get a valid drop target.</LI>
|
||||
</OL>
|
||||
|
||||
<P><IMG src="help/shared/note.png" border="0">You cannot move a
|
||||
file that is in use or a folder that contains a file that is in use.</P>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<P><IMG src="help/shared/note.png" border="0"> If a folder or file
|
||||
@@ -241,6 +296,24 @@
|
||||
<LI>Right mouse click and choose the <B>Collapse All</B> option.</LI>
|
||||
</OL>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<H4><A name="Follow_Link"></A>Follow Link</H4>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<P>Select the internal or external folder or file referenced by a selected link-file.
|
||||
While internal folders may be expanded directly from a folder-link, following a link
|
||||
to the actual referenced location may be useful at times.
|
||||
</P>
|
||||
|
||||
<OL>
|
||||
<LI>
|
||||
Select a file-link or folder-link, right mouse click and choose the <I>Follow Link</I>
|
||||
option. The referenced file or folder will be selected if possible. If associated
|
||||
with an external project or repository the selection will occur in a READ-ONLY
|
||||
project view once opened.</LI>
|
||||
</OL>
|
||||
|
||||
</BLOCKQUOTE>
|
||||
|
||||
|
||||
<P> </P>
|
||||
@@ -421,9 +494,9 @@
|
||||
<TD style="vertical-align: top; width: 10px;">-<BR>
|
||||
</TD>
|
||||
|
||||
<TD style="vertical-align: top;">A <A href=
|
||||
<TD style="vertical-align: top;"><A href=
|
||||
"help/topics/Program/Ghidra_Programs.htm"><SPAN style=
|
||||
"font-weight: bold;">program</SPAN></A><BR>
|
||||
"font-weight: bold;">Program</SPAN></A><BR>
|
||||
</TD>
|
||||
</TR>
|
||||
|
||||
@@ -434,12 +507,34 @@
|
||||
<TD style="vertical-align: top; width: 10px;">-<BR>
|
||||
</TD>
|
||||
|
||||
<TD style="vertical-align: top;">A <A href=
|
||||
<TD style="vertical-align: top;"><A href=
|
||||
"help/topics/DataTypeManagerPlugin/data_type_manager_description.htm#ProjectDataTypeArchive"><SPAN
|
||||
style="font-weight: bold;">project data type archive</SPAN></A> (a data type file
|
||||
style="font-weight: bold;">Data Type Archive</SPAN></A> (a data type file
|
||||
stored in the project)<BR>
|
||||
</TD>
|
||||
</TR>
|
||||
|
||||
<TR>
|
||||
<TD style="vertical-align: top; width: 20px;"><IMG alt="" src=
|
||||
"images/video-x-generic16.png"></TD>
|
||||
|
||||
<TD style="vertical-align: top; width: 10px;">-<BR>
|
||||
</TD>
|
||||
|
||||
<TD style="vertical-align: top;">Debugger Trace Data<BR>
|
||||
</TD>
|
||||
</TR>
|
||||
|
||||
<TR>
|
||||
<TD style="vertical-align: top; width: 20px;"><IMG alt="" src=
|
||||
"images/start-here_16.png"></TD>
|
||||
|
||||
<TD style="vertical-align: top; width: 10px;">-<BR>
|
||||
</TD>
|
||||
|
||||
<TD style="vertical-align: top;">Version Tracking Session Data<BR>
|
||||
</TD>
|
||||
</TR>
|
||||
</TBODY>
|
||||
</TABLE>
|
||||
|
||||
@@ -521,6 +616,61 @@
|
||||
not under version control, exists only on your local machine, and is not visible to
|
||||
other users.</TD>
|
||||
</TR>
|
||||
|
||||
<TR>
|
||||
<TD width="25%">File Link </TD>
|
||||
|
||||
<TD width="20%"><IMG src="images/AbsoluteFileLinkIcon.png" border="0"></TD>
|
||||
|
||||
<TD width="40%"><A name="FileLink"></A>A file link named "Example" which refers to
|
||||
a Program at <I>/data/example</I>. File links may reference another file using either an
|
||||
1) absolute file path within the same project, 2) a relative file path within
|
||||
the same project, 3) a shared repository Ghidra URL, or 4) a local project Ghidra URL.
|
||||
See <A href="#GhidraURLFormats">Ghidra URL formats</A> below.
|
||||
A file link may appear with various icon states which correspond to version control.
|
||||
File links only support a single version and may not be modified.
|
||||
</TD>
|
||||
</TR>
|
||||
|
||||
<TR>
|
||||
<TD width="25%">File Link (Broken) </TD>
|
||||
|
||||
<TD width="20%"><IMG src="images/AbsoluteBrokenFileLinkIcon.png" border="0"></TD>
|
||||
|
||||
<TD width="40%"><A name="BrokenFileLink"></A>A file link named "Example" which refers to
|
||||
a Program at <I>/data/example</I> and is in a "Broken" state. Hovering the mouse
|
||||
on this node will display a tooltip which indicates the reason for the broken state.
|
||||
External file links will never show a broken link state since they are not evaluated for such conditions.
|
||||
</TD>
|
||||
</TR>
|
||||
|
||||
<TR>
|
||||
<TD width="25%">Folder Link </TD>
|
||||
|
||||
<TD width="20%"><IMG src="images/AbsoluteFolderLinkIcon.png" border="0"></TD>
|
||||
|
||||
<TD width="40%"><A name="FolderLink"></A>A folder link named "Example" which refers
|
||||
to a folder at <I>/data/example</I>. Folder links may reference another folder using either an
|
||||
1) absolute file path within the same project, 2) a relative file path within
|
||||
the same project, 3) a shared repository Ghidra URL, or 4) a local project Ghidra URL.
|
||||
See <A href="#GhidraURLFormats">Ghidra URL formats</A> below.
|
||||
Since a folder link is stored as a file, it may appear with various icon states which
|
||||
correspond to version control. Folder links only support a single version and may not
|
||||
be modified.
|
||||
</TD>
|
||||
</TR>
|
||||
|
||||
<TR>
|
||||
<TD width="25%">Folder Link (Broken) </TD>
|
||||
|
||||
<TD width="20%"><IMG src="images/AbsoluteBrokenFolderLinkIcon.png" border="0"></TD>
|
||||
|
||||
<TD width="40%"><A name="BrokenFolderLink"></A>A folder link named "Example" which refers to
|
||||
a folder at <I>/data/example</I> and is in a "Broken" state. Hovering the mouse
|
||||
on this node will display a tooltip which indicates the reason for the broken state.
|
||||
External folder links will never show a broken link state since they are not evaluated for such conditions.
|
||||
</TD>
|
||||
</TR>
|
||||
|
||||
<TR>
|
||||
<TD width="25%">Hijacked File</TD>
|
||||
@@ -545,9 +695,44 @@
|
||||
</TABLE>
|
||||
</CENTER>
|
||||
</DIV>
|
||||
|
||||
</BLOCKQUOTE>
|
||||
<BLOCKQUOTE>
|
||||
<H3> </H3>
|
||||
<H3><A name="GhidraURLFormats"></A>Ghidra URL Formats</H3>
|
||||
|
||||
<P>The format of a remote <EM>Ghidra Server URL</EM> is distinctly different from a
|
||||
<EM>Local Ghidra Project URL</EM>. These URLs have the following formats:</P>
|
||||
|
||||
<P><STRONG>Remote Ghidra Server Repository</STRONG><BR>
|
||||
</P>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<TABLE border="0" class="simplelist">
|
||||
<TR>
|
||||
<TD><CODE>ghidra://<hostname>[:<port>]/<repository_name>[/<folder_or_file_path>]</CODE></TD>
|
||||
</TR>
|
||||
</TABLE>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<P>If the default Ghidra Server port (13100) is in use it is not specified by the URL.
|
||||
The <EM>hostname</EM> may specify either a Fully Qualified Domain Name (FQDN, e.g.,
|
||||
<EM>host.abc.com</EM>) or IP v4 Address (e.g., <EM>1.2.3.4</EM>).</P>
|
||||
|
||||
<P><STRONG>Local Ghidra Project</STRONG><BR>
|
||||
</P>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<TABLE border="0" class="simplelist">
|
||||
<TR>
|
||||
<TD><CODE>ghidra:[/<directory_path>]/<project_name>[?/<folder_or_file_path>]</CODE></TD>
|
||||
</TR>
|
||||
</TABLE>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<P>For local project URLs, the absolute directory path containing the project
|
||||
<EM>*.gpr</EM> locator file is specified with the project name but excludes any <EM>.gpr/.rep</EM> suffix.
|
||||
The folder or file path within the project is conveyed with a URL query so the '?' is required.</P>
|
||||
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<H2><A name="ReadOnlyProjectDataPanel"></A>Read-Only Project Data</H2>
|
||||
@@ -697,47 +882,54 @@
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<P>This feature allows you to create a folder or file link in your active project to a
|
||||
corresponding folder or file within a read-only viewed project.
|
||||
This is done using a Ghidra URL which references the
|
||||
file in its local or remote storage location. If the viewed project corresponds to a
|
||||
viewed repository a remote URL will be used, while other cases will refer to the
|
||||
locally viewed project. It is possible for links to become broken if the referenced
|
||||
repository, local project or file location are changed.</P>
|
||||
corresponding folder or file within your project or to a read-only viewed project.
|
||||
External links are established using a Ghidra URL which references a
|
||||
file or folder in its local or remote storage location. An external Ghidra URL will
|
||||
be used if a link refers to a viewed project or repository. It is possible for internal links to
|
||||
become broken if the referenced file or folder location has changed (e.g., no longers exists
|
||||
or has the wrong content type). External links may become invalid for various reasons
|
||||
but will not convey an issue until the link is used. The broken link icon does not apply
|
||||
to external link files.
|
||||
</P>
|
||||
<P>To create an external folder or file link the following steps may be used:</P>
|
||||
<ol>
|
||||
<li>Select a single folder or file from a viewed READ-ONLY Project Data tree.</li>
|
||||
<li>Right mouse click on the selected tree node and choose the <I>Copy</I> option.</li>
|
||||
<li>Select a destination folder in the active project tree.</li>
|
||||
<li>Right mouse click on the folder and choose the <I>Paste as Link</I> option.
|
||||
<P><IMG src="help/shared/note.png" border="0">Currently, linked-file types are
|
||||
currently limited to <I>Program</I> and <I>Data Type Archive</I> files
|
||||
only. The <I>Past as Link</I> menu item will be disabled for
|
||||
unsupported file content types or for other unsupported situations such as internal
|
||||
linking within the same project.</P>
|
||||
</li>
|
||||
<li>Select a destination folder in the active project data tree.</li>
|
||||
<li>Right mouse click on the folder and choose the <I>Paste as Link</I> option.</li>
|
||||
</ol>
|
||||
<P>A linked-file may be opened in a tool via the project window in the same fashion that
|
||||
<P>It is important to note that the resulting link is always stored as a file within the
|
||||
project. With the exception of external links to local project content, a link may be
|
||||
added to version control so that it may be shared. Once added to version control it cannot
|
||||
be checked-out, since they are immutable, however they can still be deleted.</P>
|
||||
<P>A file-link may be opened in a tool via the project window in the same fashion that
|
||||
a normal file is opened (e.g., double-left-mouse-click or drag-n-drop onto a tool box icon).
|
||||
Such a project file may also be opened within a Tool using its <B>File->Open...</B> action
|
||||
and selected from the resulting project file selection dilaog.
|
||||
Clicking on a linked-folder in the active project window will open that location in a
|
||||
Clicking on an external folder-link in the active project window will open that location in a
|
||||
<B>READ-ONLY Project Data</B> tree. The user may be prompted for a shared repository
|
||||
connection password when accessing a linked folder or file.</P>
|
||||
<P>Within a project file chooser dialog a linked-folder may be expanded in a similar fashion
|
||||
connection password when accessing an external folder or file link.</P>
|
||||
<P>Within a project file chooser dialog a folder-link may be expanded in a similar fashion
|
||||
to local folders provided any neccessary repository connection can be completed.</P>
|
||||
<P><IMG src="help/shared/note.png" border="0"><B>Add to Version Control...</B> is supported
|
||||
for repository folder and file links only and will be disabled for links to a
|
||||
local project.</P>
|
||||
<P><IMG src="help/shared/note.png" border="0">Currently, linked-files only provide access
|
||||
to the latest file version and do not facilitate access to older file versions.</P>
|
||||
<P><IMG src="help/shared/note.png" border="0">Currently, external file-links only provide access
|
||||
to the latest file version and do not facilitate access to older file versions. An external
|
||||
folder-link will allow access to file versions contained within such a folder.
|
||||
</P>
|
||||
<P><IMG src="help/shared/note.png" border="0">Some file chooser use cases, including the
|
||||
<I>GhidraScript</I> API, are restricted to selecting files and folders within the active
|
||||
project only and will hide all external links.
|
||||
</P>
|
||||
<P>The project window below shows a Program file-link "Program1" which is linked to the
|
||||
same file in the viewed project. Hovering the mouse over a linked-file will show the URL
|
||||
of the linked file or folder. The chain-link icon decoration indicates such a linked
|
||||
file or folder.</P>
|
||||
same file in the viewed project.</P>
|
||||
|
||||
<CENTER>
|
||||
<IMG src= "images/LinkOtherProject.png" border="0">
|
||||
</CENTER>
|
||||
|
||||
<P>A folder or file link will show its referenced location with either
|
||||
same file in the viewed project.</P>
|
||||
|
||||
|
||||
</BLOCKQUOTE>
|
||||
|
||||
</BLOCKQUOTE>
|
||||
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1003 B |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 658 B |
@@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@@ -133,8 +133,9 @@ public class ApplyDataArchiveAnalyzer extends AbstractAnalyzer {
|
||||
OPTION_DESCRIPTION_GDT_FILEPATH,
|
||||
() -> new FileChooserEditor(FileDataTypeManager.GDT_FILEFILTER));
|
||||
options.registerOption(OPTION_NAME_PROJECT_PATH, OptionType.STRING_TYPE, null, null,
|
||||
OPTION_DESCRIPTION_PROJECT_PATH, () -> new ProjectPathChooserEditor(
|
||||
"Choose Data Type Archive", DATATYPEARCHIVE_PROJECT_FILTER));
|
||||
OPTION_DESCRIPTION_PROJECT_PATH,
|
||||
() -> new ProjectPathChooserEditor("Choose Data Type Archive",
|
||||
new DefaultDomainFileFilter(DataTypeArchive.class, false)));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -289,6 +290,4 @@ public class ApplyDataArchiveAnalyzer extends AbstractAnalyzer {
|
||||
.collect(Collectors.toMap(f -> f.getName(), f -> f));
|
||||
}
|
||||
|
||||
private static final DomainFileFilter DATATYPEARCHIVE_PROJECT_FILTER =
|
||||
df -> DataTypeArchive.class.isAssignableFrom(df.getDomainObjectClass());
|
||||
}
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@@ -46,10 +46,6 @@ public class ProjectPathChooserEditor extends PropertyEditorSupport {
|
||||
private String title;
|
||||
private DomainFileFilter filter;
|
||||
|
||||
public ProjectPathChooserEditor() {
|
||||
this(null, null);
|
||||
}
|
||||
|
||||
public ProjectPathChooserEditor(String title, DomainFileFilter filter) {
|
||||
this.title = title;
|
||||
this.filter = filter;
|
||||
@@ -127,8 +123,7 @@ public class ProjectPathChooserEditor extends PropertyEditorSupport {
|
||||
|
||||
private void displayFileChooser() {
|
||||
AtomicReference<String> result = new AtomicReference<>();
|
||||
DataTreeDialog dataTreeDialog =
|
||||
new DataTreeDialog(this, title, OPEN, filter);
|
||||
DataTreeDialog dataTreeDialog = new DataTreeDialog(this, title, OPEN, filter);
|
||||
dataTreeDialog.addOkActionListener(e -> {
|
||||
dataTreeDialog.close();
|
||||
DomainFile df = dataTreeDialog.getDomainFile();
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@@ -92,8 +92,7 @@ class OpenDomainFileTask extends Task {
|
||||
|
||||
private boolean isFileOpen() {
|
||||
List<Archive> dtArchiveList = dtmHandler.getAllArchives();
|
||||
for (int i = 0; i < dtArchiveList.size(); i++) {
|
||||
Archive archive = dtArchiveList.get(i);
|
||||
for (Archive archive : dtArchiveList) {
|
||||
if (archive instanceof ProjectArchive) {
|
||||
ProjectArchive projectArchive = (ProjectArchive) archive;
|
||||
DomainFile archiveDomainFile = projectArchive.getDomainFile();
|
||||
@@ -156,7 +155,7 @@ class OpenDomainFileTask extends Task {
|
||||
}
|
||||
catch (VersionException e) {
|
||||
VersionExceptionHandler.showVersionError(tool.getToolFrame(), domainFile.getName(),
|
||||
contentType, "Open", e);
|
||||
contentType, "Open", false, e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -179,7 +178,7 @@ class OpenDomainFileTask extends Task {
|
||||
}
|
||||
catch (VersionException e) {
|
||||
VersionExceptionHandler.showVersionError(null, domainFile.getName(), contentType,
|
||||
"Open", e);
|
||||
"Open", false, e);
|
||||
}
|
||||
catch (CancelledException e) {
|
||||
// we don't care, the task has been canceled
|
||||
|
||||
@@ -77,7 +77,7 @@ public class DataTypeManagerHandler {
|
||||
private Map<UniversalID, InvalidFileArchive> invalidArchives = new HashMap<>();
|
||||
|
||||
private boolean treeDialogCancelled = false;
|
||||
private DomainFileFilter createArchiveFileFilter;
|
||||
private DomainFileFilter archiveFileFilter;
|
||||
|
||||
private DataTypeIndexer dataTypeIndexer;
|
||||
private List<ArchiveManagerListener> archiveManagerlisteners = new ArrayList<>();
|
||||
@@ -107,18 +107,7 @@ public class DataTypeManagerHandler {
|
||||
dataTypeIndexer.addDataTypeManager(builtInDataTypesManager);
|
||||
openArchives.add(new BuiltInArchive(this, builtInDataTypesManager));
|
||||
|
||||
createArchiveFileFilter = new DomainFileFilter() {
|
||||
|
||||
@Override
|
||||
public boolean accept(DomainFile df) {
|
||||
return DataTypeArchive.class.isAssignableFrom(df.getDomainObjectClass());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean followLinkedFolders() {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
archiveFileFilter = new DefaultDomainFileFilter(DataTypeArchive.class, true);
|
||||
|
||||
folderListener = new MyFolderListener();
|
||||
tool.getProject().getProjectData().addDomainFolderChangeListener(folderListener);
|
||||
@@ -1454,7 +1443,7 @@ public class DataTypeManagerHandler {
|
||||
}
|
||||
|
||||
private DataTreeDialog getSaveDialog() {
|
||||
DataTreeDialog dialog = new DataTreeDialog(null, "Save As", SAVE, createArchiveFileFilter);
|
||||
DataTreeDialog dialog = new DataTreeDialog(null, "Save As", SAVE, archiveFileFilter);
|
||||
|
||||
ActionListener listener = event -> {
|
||||
DomainFolder folder = dialog.getDomainFolder();
|
||||
@@ -1486,7 +1475,7 @@ public class DataTypeManagerHandler {
|
||||
private CreateDataTypeArchiveDataTreeDialog getCreateDialog() {
|
||||
|
||||
CreateDataTypeArchiveDataTreeDialog dialog = new CreateDataTypeArchiveDataTreeDialog(null,
|
||||
"Create", CREATE, createArchiveFileFilter);
|
||||
"Create", CREATE, archiveFileFilter);
|
||||
|
||||
ActionListener listener = event -> {
|
||||
DomainFolder folder = dialog.getDomainFolder();
|
||||
@@ -1726,7 +1715,7 @@ public class DataTypeManagerHandler {
|
||||
}
|
||||
catch (VersionException e) {
|
||||
VersionExceptionHandler.showVersionError(null, newDomainFile.getName(), contentType,
|
||||
"Re-open", e);
|
||||
"Re-open", false, e);
|
||||
}
|
||||
catch (CancelledException e) {
|
||||
throw new AssertException(e);
|
||||
@@ -1766,7 +1755,7 @@ public class DataTypeManagerHandler {
|
||||
Throwable cause = t.getCause();
|
||||
if (cause instanceof VersionException) {
|
||||
VersionExceptionHandler.showVersionError(null, archiveFile.getName(), "Archive",
|
||||
"open", (VersionException) cause);
|
||||
"open", false, (VersionException) cause);
|
||||
}
|
||||
else {
|
||||
Msg.showError(plugin, plugin.getProvider().getComponent(), "Open Archive Failed",
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@@ -78,6 +78,7 @@ public class ExporterDialog extends DialogComponentProvider implements AddressFa
|
||||
private JCheckBox selectionCheckBox; // null for FrontEnd Tool use
|
||||
private JTextField filePathTextField;
|
||||
private JButton fileChooserButton;
|
||||
private List<Exporter> applicableExporters;
|
||||
private GhidraComboBox<Exporter> comboBox;
|
||||
private final DomainFile domainFile;
|
||||
private boolean domainObjectWasSupplied;
|
||||
@@ -86,39 +87,82 @@ public class ExporterDialog extends DialogComponentProvider implements AddressFa
|
||||
private PluginTool tool;
|
||||
|
||||
private JLabel selectionOnlyLabel;
|
||||
private boolean showNoExporterErrorIfNeeded = true;
|
||||
|
||||
/**
|
||||
* Construct a new ExporterDialog for exporting an entire program.
|
||||
* Show a new ExporterDialog for exporting an entire program.
|
||||
* The method {@link #hasNoApplicableExporter()} should be checked before showing the
|
||||
* dilaog. If no exporters are available a popup error will be displayed and the exporter
|
||||
* dialog will not be shown.
|
||||
*
|
||||
* @param tool the tool that launched this dialog.
|
||||
* @param domainFile the program to export
|
||||
*/
|
||||
public ExporterDialog(PluginTool tool, DomainFile domainFile) {
|
||||
this(tool, domainFile, null, null);
|
||||
public static void show(PluginTool tool, DomainFile domainFile) {
|
||||
showExporterDialog(tool, domainFile, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new ExporterDialog for exporting a program, optionally only exported a
|
||||
* selected region.
|
||||
* selected region. The method {@link #hasNoApplicableExporter()} should be checked before
|
||||
* showing the dilaog. If no exporters are available a popup error will be displayed and the
|
||||
* exporter dialog will not be shown.
|
||||
* The {@link #close()} method must always be invoked on the dialog instance even if it
|
||||
* is never shown to ensure any {@link DomainObject} instance held is properly released.
|
||||
*
|
||||
* @param tool the tool that launched this dialog.
|
||||
* @param domainFile the program file to export. (may be proxy)
|
||||
* @param domainObject the program to export if already open, otherwise null.
|
||||
* @param selection the current program selection (ignored for FrontEnd Tool).
|
||||
*/
|
||||
public ExporterDialog(PluginTool tool, DomainFile domainFile, DomainObject domainObject,
|
||||
public static void showExporterDialog(PluginTool tool, DomainFile domainFile,
|
||||
DomainObject domainObject, ProgramSelection selection) {
|
||||
ExporterDialog dialog = new ExporterDialog(tool, domainFile, domainObject, selection);
|
||||
if (dialog.hasNoApplicableExporter()) {
|
||||
dialog.close();
|
||||
}
|
||||
else {
|
||||
tool.showDialog(dialog);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new modal ExporterDialog for exporting a program, optionally only exported a
|
||||
* selected region. The method {@link #hasNoApplicableExporter()} should be checked before
|
||||
* showing the dilaog. If no exporters are available a popup error will be displayed.
|
||||
* The {@link #close()} method must always be invoked on the dialog instance even if it
|
||||
* is never shown to ensure any {@link DomainObject} instance held is properly released.
|
||||
*
|
||||
* @param tool the tool that launched this dialog.
|
||||
* @param domainFile the program file to export. (may be proxy)
|
||||
* @param domainObject the program to export if already open, otherwise null.
|
||||
* @param selection the current program selection (ignored for FrontEnd Tool).
|
||||
*/
|
||||
private ExporterDialog(PluginTool tool, DomainFile domainFile, DomainObject domainObject,
|
||||
ProgramSelection selection) {
|
||||
super("Export " + domainFile.getName());
|
||||
|
||||
if (!Swing.isSwingThread()) {
|
||||
throw new RuntimeException("ExporterDialog must be instantiated within Swing thread");
|
||||
}
|
||||
|
||||
this.tool = tool;
|
||||
this.domainFile = domainFile;
|
||||
this.domainObject = domainObject;
|
||||
this.currentSelection = selection;
|
||||
|
||||
if (domainObject != null) {
|
||||
applicableExporters = getApplicableExporters(false);
|
||||
domainObjectWasSupplied = true;
|
||||
domainObject.addConsumer(this);
|
||||
}
|
||||
else {
|
||||
domainObject = getDomainObjectIfNeeded(TaskMonitor.DUMMY);
|
||||
applicableExporters = getApplicableExporters(true);
|
||||
List<Exporter> applicableDomainFileExporters = getApplicableExporters(false);
|
||||
|
||||
domainObject = getDomainObjectIfNeeded(!applicableDomainFileExporters.isEmpty());
|
||||
|
||||
applicableExporters = getApplicableExporters(false);
|
||||
}
|
||||
|
||||
addWorkPanel(buildWorkPanel());
|
||||
@@ -133,6 +177,11 @@ public class ExporterDialog extends DialogComponentProvider implements AddressFa
|
||||
// need to initialize a few things
|
||||
selectedFormatChanged();
|
||||
validate();
|
||||
|
||||
if (showNoExporterErrorIfNeeded && hasNoApplicableExporter()) {
|
||||
Msg.showError(this, tool.getToolFrame(), "Unable to Export",
|
||||
"No available exporters for content type");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -305,10 +354,9 @@ public class ExporterDialog extends DialogComponentProvider implements AddressFa
|
||||
|
||||
private Component buildFormatChooser() {
|
||||
|
||||
List<Exporter> exporters = getApplicableExporters(false);
|
||||
comboBox = new GhidraComboBox<>(exporters);
|
||||
comboBox = new GhidraComboBox<>(applicableExporters);
|
||||
|
||||
Exporter defaultExporter = getDefaultExporter(exporters);
|
||||
Exporter defaultExporter = getDefaultExporter(applicableExporters);
|
||||
if (defaultExporter != null) {
|
||||
comboBox.setSelectedItem(defaultExporter);
|
||||
}
|
||||
@@ -319,8 +367,8 @@ public class ExporterDialog extends DialogComponentProvider implements AddressFa
|
||||
|
||||
/**
|
||||
* This list generation will be based upon the open domainObject if available, otherwise
|
||||
* the domainFile's content class will be used.
|
||||
* @return list of exporters able to handle content
|
||||
* the domainFile's content class will be used. The {@code applicableExporters} variable
|
||||
* is set to the applicable list of exporters.
|
||||
*/
|
||||
private List<Exporter> getApplicableExporters(boolean preliminaryCheck) {
|
||||
List<Exporter> list = new ArrayList<>(ClassSearcher.getInstances(Exporter.class));
|
||||
@@ -330,15 +378,13 @@ public class ExporterDialog extends DialogComponentProvider implements AddressFa
|
||||
}
|
||||
|
||||
private boolean canExport(Exporter exporter, boolean preliminaryCheck) {
|
||||
if (exporter.canExportDomainFile(domainFile)) {
|
||||
return true;
|
||||
if (domainObject != null) {
|
||||
return exporter.canExportDomainObject(domainObject);
|
||||
}
|
||||
if (domainObject == null) {
|
||||
return preliminaryCheck
|
||||
? exporter.canExportDomainObject(domainFile.getDomainObjectClass())
|
||||
: false;
|
||||
if (preliminaryCheck) {
|
||||
return exporter.canExportDomainObject(domainFile.getDomainObjectClass());
|
||||
}
|
||||
return exporter.canExportDomainObject(domainObject);
|
||||
return exporter.canExportDomainFile(domainFile);
|
||||
}
|
||||
|
||||
private Exporter getDefaultExporter(List<Exporter> list) {
|
||||
@@ -410,6 +456,10 @@ public class ExporterDialog extends DialogComponentProvider implements AddressFa
|
||||
setOkEnabled(true);
|
||||
}
|
||||
|
||||
public boolean hasNoApplicableExporter() {
|
||||
return applicableExporters.isEmpty();
|
||||
}
|
||||
|
||||
private boolean hasOptions() {
|
||||
return options != null && !options.isEmpty();
|
||||
}
|
||||
@@ -450,7 +500,7 @@ public class ExporterDialog extends DialogComponentProvider implements AddressFa
|
||||
}
|
||||
}
|
||||
|
||||
private DomainObject getDomainObjectIfNeeded(TaskMonitor taskMonitor) {
|
||||
private DomainObject getDomainObjectIfNeeded(boolean exportPossibleWithoutOpening) {
|
||||
if (domainObject != null) {
|
||||
return domainObject;
|
||||
}
|
||||
@@ -459,7 +509,7 @@ public class ExporterDialog extends DialogComponentProvider implements AddressFa
|
||||
// direct domain file export. This avoids potential upgrade issues and preserves
|
||||
// database in its current state for those exporters.
|
||||
boolean doOpen = false;
|
||||
for (Exporter exporter : getApplicableExporters(true)) {
|
||||
for (Exporter exporter : applicableExporters) {
|
||||
if (!exporter.canExportDomainFile(domainFile)) {
|
||||
doOpen = true;
|
||||
break;
|
||||
@@ -469,35 +519,28 @@ public class ExporterDialog extends DialogComponentProvider implements AddressFa
|
||||
return null;
|
||||
}
|
||||
|
||||
if (SystemUtilities.isEventDispatchThread()) {
|
||||
TaskLauncher.launchModal("Opening File: " + domainFile.getName(),
|
||||
monitor -> doOpenFile(monitor));
|
||||
}
|
||||
else {
|
||||
doOpenFile(taskMonitor);
|
||||
}
|
||||
TaskLauncher.launchModal("Opening File: " + domainFile.getName(),
|
||||
monitor -> doOpenFile(exportPossibleWithoutOpening, monitor));
|
||||
|
||||
return domainObject;
|
||||
}
|
||||
|
||||
private void doOpenFile(TaskMonitor monitor) {
|
||||
private void doOpenFile(boolean exportPossibleWithoutOpening, TaskMonitor monitor) {
|
||||
showNoExporterErrorIfNeeded = false;
|
||||
String linkedPrefix = domainFile.isLink() ? "linked-" : "";
|
||||
try {
|
||||
if (domainFile.isLinkFile()) {
|
||||
// Linked files are always subject to upgrade if needed and do not support
|
||||
// getImmutableDomainObject
|
||||
domainObject =
|
||||
domainFile.getReadOnlyDomainObject(this, DomainFile.DEFAULT_VERSION, monitor);
|
||||
}
|
||||
else {
|
||||
domainObject =
|
||||
domainFile.getImmutableDomainObject(this, DomainFile.DEFAULT_VERSION, monitor);
|
||||
}
|
||||
domainObject =
|
||||
domainFile.getImmutableDomainObject(this, DomainFile.DEFAULT_VERSION, monitor);
|
||||
}
|
||||
catch (VersionException e) {
|
||||
String msg = "Could not open file: " + domainFile.getName() +
|
||||
"\n\nAvailable export options will be limited.";
|
||||
String msg = "Could not open " + linkedPrefix + "file: " + domainFile.getName();
|
||||
if (exportPossibleWithoutOpening) {
|
||||
msg += "\n\nAvailable export options will be limited.";
|
||||
}
|
||||
if (e.isUpgradable()) {
|
||||
msg += "\n\nA data upgrade is required. You may open file" +
|
||||
"\nin a tool first then Export if a different exporter" + "\nis required.";
|
||||
msg += "\n\nA " + linkedPrefix +
|
||||
"content upgrade is required. You may open file in a" +
|
||||
"\ntool first to complete upgrade then Export if needed.";
|
||||
}
|
||||
else {
|
||||
msg += "\nFile was created with a newer version of Ghidra";
|
||||
@@ -505,8 +548,10 @@ public class ExporterDialog extends DialogComponentProvider implements AddressFa
|
||||
Msg.showError(this, getComponent(), "Error Opening File", msg);
|
||||
}
|
||||
catch (IOException e) {
|
||||
String msg = "Could not open file: " + domainFile.getName() +
|
||||
"\n\nAvailable export options will be limited.";
|
||||
String msg = "Could not open " + linkedPrefix + "file: " + domainFile.getName();
|
||||
if (exportPossibleWithoutOpening) {
|
||||
msg += "\n\nAvailable export options will be limited.";
|
||||
}
|
||||
Msg.showError(this, getComponent(), "Error Opening File", msg, e);
|
||||
}
|
||||
catch (CancelledException e) {
|
||||
@@ -552,7 +597,7 @@ public class ExporterDialog extends DialogComponentProvider implements AddressFa
|
||||
|
||||
boolean exportDomainFile =
|
||||
!domainObjectWasSupplied && exporter.canExportDomainFile(domainFile);
|
||||
if (!exportDomainFile && domainFile == null) {
|
||||
if (!exportDomainFile && (domainFile == null || domainFile.isLink())) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@@ -70,9 +70,8 @@ public class ExporterPlugin extends Plugin implements ApplicationLevelPlugin {
|
||||
protected void actionPerformed(NavigatableActionContext context) {
|
||||
Program program = context.getProgram();
|
||||
DomainFile domainFile = program.getDomainFile();
|
||||
ExporterDialog dialog =
|
||||
new ExporterDialog(tool, domainFile, program, context.getSelection());
|
||||
tool.showDialog(dialog);
|
||||
ExporterDialog.showExporterDialog(tool, domainFile, program,
|
||||
context.getSelection());
|
||||
}
|
||||
};
|
||||
MenuData menuData =
|
||||
@@ -104,8 +103,7 @@ public class ExporterPlugin extends Plugin implements ApplicationLevelPlugin {
|
||||
@Override
|
||||
protected void actionPerformed(ProjectDataContext context) {
|
||||
DomainFile domainFile = context.getSelectedFiles().get(0);
|
||||
ExporterDialog dialog = new ExporterDialog(tool, domainFile);
|
||||
tool.showDialog(dialog);
|
||||
ExporterDialog.show(tool, domainFile);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -118,6 +116,10 @@ public class ExporterPlugin extends Plugin implements ApplicationLevelPlugin {
|
||||
if (selectedFiles.size() != 1) {
|
||||
return false;
|
||||
}
|
||||
DomainFile domainFile = context.getSelectedFiles().get(0);
|
||||
if (domainFile.isLink() && domainFile.getLinkInfo().isFolderLink()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -15,8 +15,6 @@
|
||||
*/
|
||||
package ghidra.app.plugin.core.gotoquery;
|
||||
|
||||
import static ghidra.framework.main.DataTreeDialogType.*;
|
||||
|
||||
import java.util.Stack;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
@@ -32,7 +30,7 @@ import ghidra.app.util.NamespaceUtils;
|
||||
import ghidra.app.util.SymbolPath;
|
||||
import ghidra.app.util.query.TableService;
|
||||
import ghidra.framework.cmd.Command;
|
||||
import ghidra.framework.main.DataTreeDialog;
|
||||
import ghidra.framework.main.ProgramFileChooser;
|
||||
import ghidra.framework.model.DomainFile;
|
||||
import ghidra.framework.model.ProjectData;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
@@ -106,8 +104,10 @@ public class GoToHelper {
|
||||
ExternalLocation externalLoc =
|
||||
program.getExternalManager().getExternalLocation(externalSym);
|
||||
|
||||
// TODO - this seems like a mistake to always pass 'false' here; please doc why we
|
||||
// wish to ignore the user options for when to navigate to external programs
|
||||
// TODO - This seems like a mistake to always pass 'false' here; please doc why we
|
||||
// wish to ignore the user options for when to navigate to external programs.
|
||||
// It appears this was done since this method is invoked on simple external
|
||||
// location node selection within symbol tree where you would not want a popup.
|
||||
return goToExternalLinkage(navigatable, externalLoc, false);
|
||||
}
|
||||
|
||||
@@ -187,10 +187,11 @@ public class GoToHelper {
|
||||
*
|
||||
* @param nav Navigatable
|
||||
* @param externalLoc external location
|
||||
* @param popupAllowed if true a table may be displayed when multiple linkage locations exist,
|
||||
* otherwise navigation to the first linkage location will be performed
|
||||
* @param popupAllowed if true a table may be displayed when multiple linkage locations exist
|
||||
* or navigation to an external program, otherwise navigation to the first linkage
|
||||
* location will be performed
|
||||
* @return true if navigation was successful or a list of possible linkage locations was
|
||||
* displayed.
|
||||
* displayed, false if no navigation was performed.
|
||||
*/
|
||||
protected boolean goToExternalLinkage(Navigatable nav, ExternalLocation externalLoc,
|
||||
boolean popupAllowed) {
|
||||
@@ -205,8 +206,14 @@ public class GoToHelper {
|
||||
NavigationUtils.getExternalLinkageAddresses(program, externalSym.getAddress());
|
||||
if (externalLinkageAddresses.length == 0) {
|
||||
if (externalLoc.isFunction()) {
|
||||
tool.setStatusInfo("Failed to identify external linkage address for " +
|
||||
externalSym.getName(true) + ". Unable to perform navigation.", true);
|
||||
// This assume external functions always require linkage location
|
||||
tool.setStatusInfo("Failed to identify external linkage address for function " +
|
||||
externalSym.getName(true), true);
|
||||
}
|
||||
else if (popupAllowed) {
|
||||
// If there are no linkage location try to navigate to external program if a popup
|
||||
// is tolerated.
|
||||
return goToExternalLocation(nav, externalLoc, false);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -306,7 +313,7 @@ public class GoToHelper {
|
||||
}
|
||||
|
||||
ProjectData pd = tool.getProject().getProjectData();
|
||||
DomainFile domainFile = pd.getFile(pathName);
|
||||
DomainFile domainFile = pd.getFile(pathName, ProgramFileChooser.PROGRAM_FILE_FILTER);
|
||||
ProgramManager service = tool.getService(ProgramManager.class);
|
||||
if (domainFile == null || service == null) {
|
||||
tool.setStatusInfo("Unable to navigate to external location. " +
|
||||
@@ -441,8 +448,8 @@ public class GoToHelper {
|
||||
return;
|
||||
}
|
||||
|
||||
DataTreeDialog dialog = new DataTreeDialog(null,
|
||||
"Choose External Program (" + extProgName + ")", OPEN);
|
||||
ProgramFileChooser dialog =
|
||||
new ProgramFileChooser(null, "Choose External Program (" + extProgName + ")");
|
||||
dialog.setSearchText(extProgName);
|
||||
dialog.setHelpLocation(new HelpLocation("ReferencesPlugin", "ChooseExternalProgram"));
|
||||
tool.showDialog(dialog);
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@@ -79,16 +79,25 @@ public class AboutProgramPlugin extends Plugin implements ApplicationLevelPlugin
|
||||
@Override
|
||||
protected void actionPerformed(ProjectDataContext context) {
|
||||
DomainFile domainFile = context.getSelectedFiles().get(0);
|
||||
showAbout(domainFile, domainFile.getMetadata());
|
||||
Map<String, String> metadata = domainFile.getMetadata();
|
||||
|
||||
showAbout(domainFile, metadata);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isAddToPopup(ProjectDataContext context) {
|
||||
return context.getFileCount() == 1 && context.getFolderCount() == 0;
|
||||
if (context.getFileCount() == 1 && context.getFolderCount() == 0) {
|
||||
// Adjust popup menu text
|
||||
DomainFile domainFile = context.getSelectedFiles().get(0);
|
||||
String contentType = domainFile.getContentType();
|
||||
setPopupMenuData(
|
||||
new MenuData(new String[] { "About " + contentType }, null, "AAA"));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
aboutAction.setPopupMenuData(
|
||||
new MenuData(new String[] { ACTION_NAME }, null, "AAA"));
|
||||
aboutAction.setPopupMenuData(new MenuData(new String[] { ACTION_NAME }, null, "AAA"));
|
||||
|
||||
aboutAction.setEnabled(true);
|
||||
}
|
||||
|
||||
@@ -88,6 +88,10 @@ public final class LanguageProviderPlugin extends Plugin implements ApplicationL
|
||||
return false;
|
||||
}
|
||||
|
||||
if (file.isLink() && file.getLinkInfo().isExternalLink()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return file.isInWritableProject() &&
|
||||
Program.class.isAssignableFrom(file.getDomainObjectClass());
|
||||
}
|
||||
@@ -105,12 +109,12 @@ public final class LanguageProviderPlugin extends Plugin implements ApplicationL
|
||||
}
|
||||
|
||||
};
|
||||
setLanguageAction.setPopupMenuData(
|
||||
new MenuData(new String[] { "Set Language..." }, "Language"));
|
||||
setLanguageAction
|
||||
.setPopupMenuData(new MenuData(new String[] { "Set Language..." }, "Language"));
|
||||
|
||||
setLanguageAction.setEnabled(true);
|
||||
setLanguageAction.setHelpLocation(
|
||||
new HelpLocation("LanguageProviderPlugin", "set language"));
|
||||
setLanguageAction
|
||||
.setHelpLocation(new HelpLocation("LanguageProviderPlugin", "set language"));
|
||||
tool.addAction(setLanguageAction);
|
||||
}
|
||||
|
||||
|
||||
@@ -20,10 +20,10 @@ import java.net.URL;
|
||||
import java.util.Objects;
|
||||
|
||||
import ghidra.framework.data.DomainFileProxy;
|
||||
import ghidra.framework.data.LinkHandler;
|
||||
import ghidra.framework.model.*;
|
||||
import ghidra.framework.protocol.ghidra.GhidraURL;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.util.Msg;
|
||||
|
||||
/**
|
||||
* Programs locations can be specified from either a {@link DomainFile} or a ghidra {@link URL}.
|
||||
@@ -81,11 +81,19 @@ public class ProgramLocator {
|
||||
file = domainFile;
|
||||
}
|
||||
else {
|
||||
try {
|
||||
url = GhidraURL.getNormalizedURL(resolveURL(domainFile));
|
||||
if (domainFile instanceof LinkedDomainFile linkedFile) {
|
||||
try {
|
||||
// Attempt to resolve to actual linked-file to allow for
|
||||
// direct URL reference
|
||||
domainFile = linkedFile.getLinkedFile();
|
||||
}
|
||||
catch (IOException e) {
|
||||
Msg.error(this, "Failed to resolve linked-file", e);
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
file = domainFile;
|
||||
url = domainFile.getLocalProjectURL(null);
|
||||
if (url == null) {
|
||||
url = domainFile.getSharedProjectURL(null);
|
||||
}
|
||||
}
|
||||
this.domainFile = file;
|
||||
@@ -177,25 +185,4 @@ public class ProgramLocator {
|
||||
Objects.equals(ghidraURL, other.ghidraURL) && version == other.version;
|
||||
}
|
||||
|
||||
private URL resolveURL(DomainFile file) throws IOException {
|
||||
if (file.isLinkFile()) {
|
||||
return LinkHandler.getURL(file);
|
||||
}
|
||||
DomainFolder parent = file.getParent();
|
||||
if (file instanceof LinkedDomainFile linkedFile) {
|
||||
return resolveLinkedDomainFile(linkedFile);
|
||||
}
|
||||
if (!parent.getProjectLocator().isTransient()) {
|
||||
return file.getLocalProjectURL(null);
|
||||
}
|
||||
return file.getSharedProjectURL(null);
|
||||
}
|
||||
|
||||
private URL resolveLinkedDomainFile(LinkedDomainFile linkedFile) {
|
||||
URL url = linkedFile.getLocalProjectURL(null);
|
||||
if (url == null) {
|
||||
url = linkedFile.getSharedProjectURL(null);
|
||||
}
|
||||
return url;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@@ -41,23 +41,12 @@ class ProgramSaveManager {
|
||||
private ProgramManager programMgr;
|
||||
private PluginTool tool;
|
||||
private boolean treeDialogCancelled;
|
||||
private DomainFileFilter domainFileFilter;
|
||||
private DomainFileFilter programFileFilter;
|
||||
|
||||
ProgramSaveManager(PluginTool tool, ProgramManager programMgr) {
|
||||
this.tool = tool;
|
||||
this.programMgr = programMgr;
|
||||
domainFileFilter = new DomainFileFilter() {
|
||||
|
||||
@Override
|
||||
public boolean accept(DomainFile df) {
|
||||
return Program.class.isAssignableFrom(df.getDomainObjectClass());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean followLinkedFolders() {
|
||||
return false; // can't save to linked-folder (read-only)
|
||||
}
|
||||
};
|
||||
programFileFilter = new DefaultDomainFileFilter(Program.class, true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -443,8 +432,7 @@ class ProgramSaveManager {
|
||||
}
|
||||
|
||||
private DataTreeDialog getSaveDialog() {
|
||||
DataTreeDialog dialog =
|
||||
new DataTreeDialog(null, "Save As", SAVE, domainFileFilter);
|
||||
DataTreeDialog dialog = new DataTreeDialog(null, "Save As", SAVE, programFileFilter);
|
||||
|
||||
ActionListener listener = event -> {
|
||||
DomainFolder folder = dialog.getDomainFolder();
|
||||
|
||||
@@ -15,8 +15,6 @@
|
||||
*/
|
||||
package ghidra.app.plugin.core.references;
|
||||
|
||||
import static ghidra.framework.main.DataTreeDialogType.*;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.FlowLayout;
|
||||
import java.awt.event.*;
|
||||
@@ -30,7 +28,7 @@ import javax.swing.event.DocumentListener;
|
||||
import docking.widgets.combobox.GhidraComboBox;
|
||||
import docking.widgets.label.GLabel;
|
||||
import ghidra.app.util.AddressInput;
|
||||
import ghidra.framework.main.DataTreeDialog;
|
||||
import ghidra.framework.main.ProgramFileChooser;
|
||||
import ghidra.framework.model.DomainFile;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressSpace;
|
||||
@@ -113,8 +111,8 @@ class EditExternalReferencePanel extends EditReferencePanel {
|
||||
}
|
||||
});
|
||||
|
||||
editButton = new JButton("Edit");
|
||||
editButton.setToolTipText("Edit Link to External Program");
|
||||
editButton = new JButton("Select...");
|
||||
editButton.setToolTipText("Select External Program");
|
||||
editButton.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
@@ -187,10 +185,8 @@ class EditExternalReferencePanel extends EditReferencePanel {
|
||||
* Pop up the data tree dialog so the user can choose the external program.
|
||||
*/
|
||||
private void popupProgramChooser() {
|
||||
DataTreeDialog d =
|
||||
new DataTreeDialog(this.getParent(), "Choose External Program", OPEN);
|
||||
final DataTreeDialog dialog = d;
|
||||
d.addOkActionListener(new ActionListener() {
|
||||
ProgramFileChooser dialog = new ProgramFileChooser(this.getParent(), "Choose External Program");
|
||||
dialog.addOkActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
DomainFile df = dialog.getDomainFile();
|
||||
@@ -206,7 +202,7 @@ class EditExternalReferencePanel extends EditReferencePanel {
|
||||
extLibPath.setText(df.getPathname());
|
||||
}
|
||||
});
|
||||
plugin.getTool().showDialog(d);
|
||||
plugin.getTool().showDialog(dialog);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -15,8 +15,6 @@
|
||||
*/
|
||||
package ghidra.app.plugin.core.references;
|
||||
|
||||
import static ghidra.framework.main.DataTreeDialogType.*;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.util.*;
|
||||
@@ -34,7 +32,7 @@ import generic.theme.GIcon;
|
||||
import ghidra.app.cmd.refs.*;
|
||||
import ghidra.framework.cmd.Command;
|
||||
import ghidra.framework.cmd.CompoundCmd;
|
||||
import ghidra.framework.main.DataTreeDialog;
|
||||
import ghidra.framework.main.*;
|
||||
import ghidra.framework.model.DomainFile;
|
||||
import ghidra.framework.model.DomainObjectListener;
|
||||
import ghidra.framework.plugintool.ComponentProviderAdapter;
|
||||
@@ -236,8 +234,8 @@ public class ExternalReferencesProvider extends ComponentProviderAdapter {
|
||||
private void setExternalProgramAssociation() {
|
||||
List<String> selectedExternalNames = getSelectedExternalNames();
|
||||
String externalName = selectedExternalNames.get(0); // must be exactly one for us to be enabled.
|
||||
DataTreeDialog dialog = new DataTreeDialog(mainPanel,
|
||||
"Choose External Program (" + externalName + ")", OPEN);
|
||||
DataTreeDialog dialog = new ProgramFileChooser(mainPanel,
|
||||
"Choose External Program (" + externalName + ")", AppInfo.getActiveProject());
|
||||
|
||||
dialog.setSearchText(externalName);
|
||||
|
||||
|
||||
@@ -15,8 +15,6 @@
|
||||
*/
|
||||
package ghidra.app.plugin.core.symboltree;
|
||||
|
||||
import static ghidra.framework.main.DataTreeDialogType.*;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.event.ItemListener;
|
||||
import java.util.Arrays;
|
||||
@@ -36,7 +34,7 @@ import docking.widgets.label.GLabel;
|
||||
import ghidra.app.util.AddressInput;
|
||||
import ghidra.app.util.NamespaceUtils;
|
||||
import ghidra.framework.main.AppInfo;
|
||||
import ghidra.framework.main.DataTreeDialog;
|
||||
import ghidra.framework.main.ProgramFileChooser;
|
||||
import ghidra.framework.model.*;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressSpace;
|
||||
@@ -270,9 +268,9 @@ class EditExternalLocationPanel extends JPanel {
|
||||
* Pop up the data tree dialog so the user can choose the external program.
|
||||
*/
|
||||
private void popupProgramChooser() {
|
||||
DataTreeDialog d = new DataTreeDialog(this.getParent(), "Choose External Program", OPEN);
|
||||
final DataTreeDialog dialog = d;
|
||||
d.addOkActionListener(e -> {
|
||||
ProgramFileChooser dialog =
|
||||
new ProgramFileChooser(this.getParent(), "Choose External Program");
|
||||
dialog.addOkActionListener(e -> {
|
||||
DomainFile df = dialog.getDomainFile();
|
||||
if (df == null) {
|
||||
return;
|
||||
@@ -285,7 +283,7 @@ class EditExternalLocationPanel extends JPanel {
|
||||
dialog.close();
|
||||
extLibPathTextField.setText(df.getPathname());
|
||||
});
|
||||
DockingWindowManager.showDialog(this, d);
|
||||
DockingWindowManager.showDialog(this, dialog);
|
||||
}
|
||||
|
||||
private void initialize() {
|
||||
@@ -363,7 +361,8 @@ class EditExternalLocationPanel extends JPanel {
|
||||
|
||||
Project project = AppInfo.getActiveProject();
|
||||
ProjectData projectData = project.getProjectData();
|
||||
DomainFile file = projectData.getFile(extLibPath);
|
||||
DomainFile file =
|
||||
projectData.getFile(extLibPath, ProgramFileChooser.PROGRAM_FILE_FILTER);
|
||||
if (file == null) {
|
||||
showInputErr("Cannot find the program for the specified library 'Path' of " +
|
||||
extLibPath + ".");
|
||||
|
||||
@@ -15,8 +15,6 @@
|
||||
*/
|
||||
package ghidra.app.plugin.core.symboltree.actions;
|
||||
|
||||
import static ghidra.framework.main.DataTreeDialogType.*;
|
||||
|
||||
import javax.swing.Icon;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.tree.TreePath;
|
||||
@@ -29,7 +27,7 @@ import ghidra.app.plugin.core.symboltree.*;
|
||||
import ghidra.app.plugin.core.symboltree.nodes.LibrarySymbolNode;
|
||||
import ghidra.framework.cmd.Command;
|
||||
import ghidra.framework.main.AppInfo;
|
||||
import ghidra.framework.main.DataTreeDialog;
|
||||
import ghidra.framework.main.ProgramFileChooser;
|
||||
import ghidra.framework.model.*;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.symbol.ExternalManager;
|
||||
@@ -82,8 +80,8 @@ public class SetExternalProgramAction extends SymbolTreeContextAction {
|
||||
ExternalManager externalManager = program.getExternalManager();
|
||||
final String externalLibraryPath = externalManager.getExternalLibraryPath(externalName);
|
||||
|
||||
final DataTreeDialog dialog = new DataTreeDialog(provider.getComponent(),
|
||||
"Choose External Program (" + externalName + ")", OPEN);
|
||||
ProgramFileChooser dialog = new ProgramFileChooser(provider.getComponent(),
|
||||
"Choose External Program (" + externalName + ")");
|
||||
|
||||
dialog.setSearchText(externalName);
|
||||
|
||||
|
||||
@@ -2827,8 +2827,10 @@ public abstract class GhidraScript extends FlatProgramAPI {
|
||||
DomainFile choice = loadAskValue(this::parseDomainFile, title);
|
||||
if (!isRunningHeadless()) {
|
||||
choice = doAsk(Program.class, title, "", choice, lastValue -> {
|
||||
|
||||
DataTreeDialog dtd = new DataTreeDialog(null, title, OPEN);
|
||||
// File filter employed limits access to program files within the active project
|
||||
// only to ensure the ability to open for update is possible.
|
||||
DataTreeDialog dtd = new DataTreeDialog(null, title, OPEN,
|
||||
new DefaultDomainFileFilter(Program.class, true));
|
||||
dtd.show();
|
||||
if (dtd.wasCancelled()) {
|
||||
return null;
|
||||
@@ -2932,8 +2934,10 @@ public abstract class GhidraScript extends FlatProgramAPI {
|
||||
|
||||
String message = "";
|
||||
DomainFile choice = doAsk(DomainFile.class, title, message, existingValue, lastValue -> {
|
||||
|
||||
DataTreeDialog dtd = new DataTreeDialog(null, title, OPEN);
|
||||
// File filter employed limits access to files within the active project
|
||||
// only to ensure the ability to open for update is possible.
|
||||
DataTreeDialog dtd = new DataTreeDialog(null, title, OPEN,
|
||||
DomainFileFilter.ALL_FILES_NO_EXTERNAL_FOLDERS_FILTER);
|
||||
dtd.show();
|
||||
if (dtd.wasCancelled()) {
|
||||
throw new CancelledException();
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@@ -47,7 +47,8 @@ public class GdtExporter extends Exporter {
|
||||
|
||||
@Override
|
||||
public boolean canExportDomainFile(DomainFile domainFile) {
|
||||
return canExportDomainObject(domainFile.getDomainObjectClass());
|
||||
// Avoid exporting link-file itself
|
||||
return !domainFile.isLink() && canExportDomainObject(domainFile.getDomainObjectClass());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -42,7 +42,8 @@ public class GzfExporter extends Exporter {
|
||||
|
||||
@Override
|
||||
public boolean canExportDomainFile(DomainFile domainFile) {
|
||||
return canExportDomainObject(domainFile.getDomainObjectClass());
|
||||
// Avoid exporting link-file itself
|
||||
return !domainFile.isLink() && canExportDomainObject(domainFile.getDomainObjectClass());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -43,6 +43,7 @@ import ghidra.framework.model.*;
|
||||
import ghidra.framework.project.DefaultProject;
|
||||
import ghidra.framework.project.DefaultProjectManager;
|
||||
import ghidra.framework.protocol.ghidra.*;
|
||||
import ghidra.framework.protocol.ghidra.GhidraURLQuery.LinkFileControl;
|
||||
import ghidra.framework.remote.User;
|
||||
import ghidra.framework.store.LockException;
|
||||
import ghidra.framework.store.local.LocalFileSystem;
|
||||
@@ -354,7 +355,10 @@ public class HeadlessAnalyzer {
|
||||
}
|
||||
throw new IOException(title + ": " + message);
|
||||
}
|
||||
}, TaskMonitor.DUMMY);
|
||||
|
||||
// Link files are skipped to avoid duplicate processing
|
||||
// Processing should be done on actual folder - not a linked folder
|
||||
}, LinkFileControl.NO_FOLLOW, TaskMonitor.DUMMY);
|
||||
|
||||
}
|
||||
catch (CancelledException e) {
|
||||
@@ -1338,7 +1342,7 @@ public class HeadlessAnalyzer {
|
||||
boolean filesProcessed = false;
|
||||
|
||||
DomainFile domFile = parentFolder.getFile(filename);
|
||||
// Do not follow folder-links or consider program links. Using content type
|
||||
// Do not follow folder-links or program links. Using content type
|
||||
// to filter is best way to control this.
|
||||
if (domFile != null &&
|
||||
ProgramContentHandler.PROGRAM_CONTENT_TYPE.equals(domFile.getContentType())) {
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@@ -27,6 +27,7 @@ import ghidra.framework.client.RepositoryAdapter;
|
||||
import ghidra.framework.main.AppInfo;
|
||||
import ghidra.framework.model.DomainFile;
|
||||
import ghidra.framework.protocol.ghidra.GhidraURLQuery;
|
||||
import ghidra.framework.protocol.ghidra.GhidraURLQuery.LinkFileControl;
|
||||
import ghidra.framework.protocol.ghidra.GhidraURLResultHandlerAdapter;
|
||||
import ghidra.framework.remote.User;
|
||||
import ghidra.framework.store.ExclusiveCheckoutException;
|
||||
@@ -103,13 +104,13 @@ public class ProgramOpener {
|
||||
|
||||
AtomicReference<Program> openedProgram = new AtomicReference<>();
|
||||
try {
|
||||
GhidraURLQuery.queryUrl(ghidraUrl, new GhidraURLResultHandlerAdapter() {
|
||||
GhidraURLQuery.queryUrl(ghidraUrl, Program.class, new GhidraURLResultHandlerAdapter() {
|
||||
@Override
|
||||
public void processResult(DomainFile domainFile, URL url, TaskMonitor m) {
|
||||
Program p = openProgram(locator, domainFile, m); // may return null
|
||||
openedProgram.set(p);
|
||||
}
|
||||
}, monitor);
|
||||
}, LinkFileControl.FOLLOW_EXTERNAL, monitor);
|
||||
}
|
||||
catch (IOException | CancelledException e) {
|
||||
// IOException reported to user by GhidraURLResultHandlerAdapter
|
||||
@@ -148,7 +149,7 @@ public class ProgramOpener {
|
||||
}
|
||||
catch (VersionException e) {
|
||||
String contentType = domainFile.getContentType();
|
||||
VersionExceptionHandler.showVersionError(null, filename, contentType, "Open", e);
|
||||
VersionExceptionHandler.showVersionError(null, filename, contentType, "Open", false, e);
|
||||
}
|
||||
catch (CancelledException e) {
|
||||
// we don't care, the task has been cancelled
|
||||
@@ -197,7 +198,7 @@ public class ProgramOpener {
|
||||
}
|
||||
catch (VersionException e) {
|
||||
VersionExceptionHandler.showVersionError(null, domainFile.getName(), contentType,
|
||||
"Open", e);
|
||||
"Open", false, e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@@ -129,31 +129,37 @@ public class ProgramAnnotatedStringHandler implements AnnotatedStringHandler {
|
||||
serviceProvider.getService(ProjectDataService.class);
|
||||
ProjectData projectData = projectDataService.getProjectData();
|
||||
|
||||
// default folder is the root folder
|
||||
DomainFolder folder = projectData.getRootFolder();
|
||||
|
||||
// Get program name and folder from program comment annotation
|
||||
// handles forward and back slashes and with and without first slash
|
||||
String programText = getProgramText(annotationParts);
|
||||
String programName = FilenameUtils.getName(programText);
|
||||
String path = FilenameUtils.getFullPathNoEndSeparator(programText);
|
||||
|
||||
DomainFolder folder;
|
||||
if (path.length() > 0) {
|
||||
path = StringUtils.prependIfMissing(FilenameUtils.separatorsToUnix(path), "/");
|
||||
folder = projectData.getFolder(path);
|
||||
if (folder == null) {
|
||||
Msg.showInfo(getClass(), null, "Folder Not Found: " + path,
|
||||
"Unable to locate folder by the name \"" + path);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
folder = projectData.getRootFolder();
|
||||
}
|
||||
|
||||
if (folder == null) {
|
||||
Msg.showInfo(getClass(), null, "No Folder: " + path,
|
||||
"Unable to locate folder by the name \"" + path);
|
||||
DomainFile programFile = folder.getFile(programName);
|
||||
if (programFile == null ||
|
||||
!Program.class.isAssignableFrom(programFile.getDomainObjectClass())) {
|
||||
Msg.showInfo(getClass(), null, "Program Not Found: " + programName,
|
||||
"Unable to locate program at path \"" + programText +
|
||||
"\".\nNOTE: File names are case-sensitive.");
|
||||
return true;
|
||||
}
|
||||
|
||||
DomainFile programFile = findProgramByName(programName, folder);
|
||||
|
||||
if (programFile == null) {
|
||||
Msg.showInfo(getClass(), null, "No Program: " + programName,
|
||||
"Unable to locate a program by the name \"" + programName +
|
||||
"\".\nNOTE: Program name is case-sensitive. ");
|
||||
if (!Program.class.isAssignableFrom(programFile.getDomainObjectClass())) {
|
||||
Msg.showInfo(getClass(), null, "Program Not Found: " + programName,
|
||||
"File exists with incorrect content type. ");
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -199,7 +205,7 @@ public class ProgramAnnotatedStringHandler implements AnnotatedStringHandler {
|
||||
return;
|
||||
}
|
||||
|
||||
Msg.showInfo(getClass(), null, "No Symbol: " + symbolName,
|
||||
Msg.showInfo(getClass(), null, "Symbol Not Found: " + symbolName,
|
||||
"Unable to navigate to '" + symbolName + "' in the program '" + programFile.getName() +
|
||||
"'.\nMake sure that the given symbol/address exists.");
|
||||
if (!programManager.isVisible(program)) {
|
||||
@@ -247,27 +253,6 @@ public class ProgramAnnotatedStringHandler implements AnnotatedStringHandler {
|
||||
return address;
|
||||
}
|
||||
|
||||
// recursive program to find a program by the given name within the given folder
|
||||
private DomainFile findProgramByName(String programText, DomainFolder folder) {
|
||||
DomainFile[] files = folder.getFiles();
|
||||
for (DomainFile file : files) {
|
||||
if (file.getName().equals(programText)) {
|
||||
return file;
|
||||
}
|
||||
}
|
||||
|
||||
// not at the current level, then check sub-folders
|
||||
DomainFolder[] folders = folder.getFolders();
|
||||
for (DomainFolder subFolder : folders) {
|
||||
DomainFile domainFile = findProgramByName(programText, subFolder);
|
||||
if (domainFile != null) {
|
||||
return domainFile;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayString() {
|
||||
return "Program";
|
||||
@@ -275,7 +260,7 @@ public class ProgramAnnotatedStringHandler implements AnnotatedStringHandler {
|
||||
|
||||
@Override
|
||||
public String getPrototypeString() {
|
||||
return "{@program program_name.exe@symbol_name}";
|
||||
return "{@program program_path@symbol_name}";
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -291,16 +291,28 @@ public abstract class AbstractDataTreeDialog extends DialogComponentProvider
|
||||
else {
|
||||
domainFile = treePanel.getSelectedDomainFile();
|
||||
if (domainFile != null) {
|
||||
folderNameLabel.setText(domainFile.getParent().getPathname());
|
||||
nameField.setText(domainFile.getName());
|
||||
domainFolder = domainFile.getParent();
|
||||
}
|
||||
else {
|
||||
domainFolder = treePanel.getSelectedDomainFolder();
|
||||
if (domainFolder == null) {
|
||||
domainFolder = project.getProjectData().getRootFolder();
|
||||
LinkFileInfo linkInfo = domainFile.getLinkInfo();
|
||||
if (linkInfo != null && linkInfo.isFolderLink()) {
|
||||
// Ensure we don't have a folder name conflict
|
||||
if (domainFile.getParent().getFolder(domainFile.getName()) == null) {
|
||||
domainFolder = linkInfo.getLinkedFolder();
|
||||
domainFile = null;
|
||||
}
|
||||
}
|
||||
else {
|
||||
folderNameLabel.setText(domainFile.getParent().getPathname());
|
||||
nameField.setText(domainFile.getName());
|
||||
domainFolder = domainFile.getParent();
|
||||
}
|
||||
}
|
||||
|
||||
if (domainFile == null) {
|
||||
if (domainFolder == null) {
|
||||
domainFolder = treePanel.getSelectedDomainFolder();
|
||||
if (domainFolder == null) {
|
||||
domainFolder = project.getProjectData().getRootFolder();
|
||||
}
|
||||
}
|
||||
folderNameLabel.setText(domainFolder.getPathname());
|
||||
if (nameField.isEditable()) {
|
||||
if (nameField.getText().length() > 0) {
|
||||
@@ -349,7 +361,9 @@ public abstract class AbstractDataTreeDialog extends DialogComponentProvider
|
||||
* @param file the file
|
||||
*/
|
||||
public void selectDomainFile(DomainFile file) {
|
||||
Swing.runLater(() -> treePanel.selectDomainFile(file));
|
||||
if (file != null) {
|
||||
Swing.runLater(() -> treePanel.selectDomainFile(file));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -584,20 +598,6 @@ public abstract class AbstractDataTreeDialog extends DialogComponentProvider
|
||||
}
|
||||
}
|
||||
|
||||
protected static DomainFileFilter getDefaultFilter(DataTreeDialogType type) {
|
||||
if (type == CHOOSE_FOLDER || type == OPEN) {
|
||||
// return filter which forces folder selection and allow navigation into linked-folders
|
||||
return new DomainFileFilter() {
|
||||
|
||||
@Override
|
||||
public boolean accept(DomainFile df) {
|
||||
return true; // show all files (legacy behavior)
|
||||
}
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private class FieldKeyListener extends KeyAdapter {
|
||||
@Override
|
||||
public void keyPressed(KeyEvent e) {
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@@ -17,8 +17,7 @@ package ghidra.framework.main;
|
||||
|
||||
import java.awt.Component;
|
||||
|
||||
import ghidra.framework.model.DomainFileFilter;
|
||||
import ghidra.framework.model.Project;
|
||||
import ghidra.framework.model.*;
|
||||
|
||||
/**
|
||||
* Dialog to open or save domain data items to a new location or name.
|
||||
@@ -33,9 +32,9 @@ public class DataTreeDialog extends AbstractDataTreeDialog {
|
||||
|
||||
/**
|
||||
* Construct a new DataTreeDialog for the active project. This chooser will show all project
|
||||
* files. Following linked-folders will only be allowed if a type of CHOOSE_FOLDER
|
||||
* or OPEN is specified. If different behavior is required a filter should
|
||||
* be specified using the other constructor.
|
||||
* files and/or folders within the active project only. Broken and external links will not be
|
||||
* shown. If different behavior is required a filter should be specified using the other
|
||||
* constructor.
|
||||
*
|
||||
* @param parent dialog's parent
|
||||
* @param title title to use
|
||||
@@ -43,7 +42,7 @@ public class DataTreeDialog extends AbstractDataTreeDialog {
|
||||
* @throws IllegalArgumentException if invalid type is specified
|
||||
*/
|
||||
public DataTreeDialog(Component parent, String title, DataTreeDialogType type) {
|
||||
this(parent, title, type, getDefaultFilter(type));
|
||||
this(parent, title, type, DomainFileFilter.ALL_INTERNAL_FILES_FILTER);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -52,7 +51,9 @@ public class DataTreeDialog extends AbstractDataTreeDialog {
|
||||
* @param parent dialog's parent
|
||||
* @param title title to use
|
||||
* @param type specify OPEN, SAVE, CHOOSE_FOLDER, or CREATE
|
||||
* @param filter filter used to control what is displayed in the data tree
|
||||
* @param filter filter used to control what is displayed in the data tree. See static
|
||||
* implementations provided by {@link DomainFileFilter} and a more tailored
|
||||
* {@link DefaultDomainFileFilter}.
|
||||
* @throws IllegalArgumentException if invalid type is specified
|
||||
*/
|
||||
public DataTreeDialog(Component parent, String title, DataTreeDialogType type,
|
||||
@@ -66,7 +67,9 @@ public class DataTreeDialog extends AbstractDataTreeDialog {
|
||||
* @param parent dialog's parent
|
||||
* @param title title to use
|
||||
* @param type specify OPEN, SAVE, CHOOSE_FOLDER, or CREATE
|
||||
* @param filter filter used to control what is displayed in the data tree
|
||||
* @param filter filter used to control what is displayed in the data tree. See static
|
||||
* implementations provided by {@link DomainFileFilter} and a more tailored
|
||||
* {@link DefaultDomainFileFilter}.
|
||||
* @param project the project to browse
|
||||
* @throws IllegalArgumentException if invalid type is specified
|
||||
*/
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@@ -90,9 +90,8 @@ public class OpenVersionedFileDialog<T extends DomainObject> extends AbstractDat
|
||||
*/
|
||||
public OpenVersionedFileDialog(PluginTool tool, String title, Class<T> domainObjectClass,
|
||||
List<T> openDomainObjects) {
|
||||
super(tool.getToolFrame(), title, OPEN, f -> {
|
||||
return domainObjectClass.isAssignableFrom(f.getDomainObjectClass());
|
||||
}, AppInfo.getActiveProject());
|
||||
super(tool.getToolFrame(), title, OPEN,
|
||||
new DefaultDomainFileFilter(domainObjectClass, false), AppInfo.getActiveProject());
|
||||
|
||||
this.tool = tool;
|
||||
this.domainObjectClass = domainObjectClass;
|
||||
@@ -214,8 +213,7 @@ public class OpenVersionedFileDialog<T extends DomainObject> extends AbstractDat
|
||||
tabbedPane = new JTabbedPane();
|
||||
tabbedPane.setName("Tabs");
|
||||
tabbedPane.add("Project Files", projectFilePanel);
|
||||
tabbedPane.add("Open " + domainObjectClass.getSimpleName() + "s",
|
||||
buildOpenObjectsTable());
|
||||
tabbedPane.add("Open " + domainObjectClass.getSimpleName() + "s", buildOpenObjectsTable());
|
||||
|
||||
tabbedPane.addChangeListener(e -> {
|
||||
int selectedTabIndex = tabbedPane.getModel().getSelectedIndex();
|
||||
@@ -254,8 +252,7 @@ public class OpenVersionedFileDialog<T extends DomainObject> extends AbstractDat
|
||||
|
||||
openObjectsTable = new GFilterTable<>(new OpenObjectsTableModel());
|
||||
GTable table = openObjectsTable.getTable();
|
||||
table.getSelectionModel()
|
||||
.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||
table.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||
openObjectsTable.addSelectionListener(e -> {
|
||||
setOkEnabled(true);
|
||||
okButton.setToolTipText("Use the selected " + domainObjectClass.getSimpleName());
|
||||
|
||||