mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-04-26 09:45:15 +02:00
227 lines
6.9 KiB
Java
227 lines
6.9 KiB
Java
/* ###
|
|
* IP: GHIDRA
|
|
*
|
|
* 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.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
package help;
|
|
|
|
import java.io.*;
|
|
import java.nio.file.*;
|
|
import java.util.Collection;
|
|
import java.util.Date;
|
|
|
|
import ghidra.util.exception.AssertException;
|
|
import help.validator.LinkDatabase;
|
|
import help.validator.location.HelpModuleCollection;
|
|
import help.validator.model.AnchorDefinition;
|
|
import help.validator.model.GhidraTOCFile;
|
|
|
|
/**
|
|
* This class:
|
|
* <ul>
|
|
* <li>Creates a XXX_map.xml file (topic IDs to help files)</li>
|
|
* <li>Creates a XXX_TOC.xml file from a source toc.xml file</li>
|
|
* <li>Finds unused images</li>
|
|
* </ul>
|
|
*/
|
|
public class JavaHelpFilesBuilder {
|
|
private static final String MAP_OUTPUT_FILENAME_SUFFIX = "_map.xml";
|
|
private static final String TOC_OUTPUT_FILENAME_SUFFIX = "_TOC.xml";
|
|
|
|
private static final String LOG_FILENAME = "help.log";
|
|
|
|
private final String moduleName;
|
|
private Path outputDir;
|
|
private String mapOutputFilename;
|
|
private String tocOutputFilename;
|
|
private LinkDatabase linkDatabase;
|
|
|
|
public JavaHelpFilesBuilder(Path outputDir, String moduleName, LinkDatabase linkDatabase) {
|
|
this.moduleName = moduleName;
|
|
this.linkDatabase = linkDatabase;
|
|
this.outputDir = initializeOutputDirectory(outputDir);
|
|
|
|
mapOutputFilename = moduleName + MAP_OUTPUT_FILENAME_SUFFIX;
|
|
tocOutputFilename = moduleName + TOC_OUTPUT_FILENAME_SUFFIX;
|
|
}
|
|
|
|
private Path initializeOutputDirectory(Path outputDirectory) {
|
|
if (!Files.exists(outputDirectory)) {
|
|
try {
|
|
return Files.createDirectories(outputDirectory);
|
|
}
|
|
catch (IOException e) {
|
|
return null;
|
|
}
|
|
}
|
|
return outputDirectory;
|
|
}
|
|
|
|
public void generateHelpFiles(HelpModuleCollection help) throws Exception {
|
|
message("Generating Help Files for: " + help);
|
|
|
|
LogFileWriter errorLog = createLogFile();
|
|
|
|
boolean hasErrors = false;
|
|
StringBuffer shortErrorDescription = new StringBuffer();
|
|
try {
|
|
generateMapFile(help);
|
|
}
|
|
catch (IOException e) {
|
|
hasErrors = true;
|
|
shortErrorDescription.append("Unexpected error generating map file!\n");
|
|
errorLog.append("Failed to generate " + mapOutputFilename + ": ");
|
|
errorLog.append(e.getMessage());
|
|
errorLog.println();
|
|
}
|
|
|
|
try {
|
|
generateTOCFile(linkDatabase, help);
|
|
}
|
|
catch (IOException e) {
|
|
hasErrors = true;
|
|
shortErrorDescription.append("Unexpected error writing TOC file!\n");
|
|
errorLog.append("Failed to generate " + tocOutputFilename + ": ");
|
|
errorLog.append(e.getMessage());
|
|
errorLog.println();
|
|
}
|
|
|
|
if (hasErrors) {
|
|
errorLog.close();
|
|
throw new RuntimeException("Errors Creating Help Files - " + shortErrorDescription +
|
|
"\n\tsee help log for details: " + errorLog.getFile());
|
|
}
|
|
|
|
errorLog.close();
|
|
if (errorLog.isEmpty()) {
|
|
errorLog.delete();
|
|
}
|
|
|
|
message("Done generating help files for module: " + moduleName);
|
|
}
|
|
|
|
private LogFileWriter createLogFile() throws IOException {
|
|
String logFilename = moduleName + "." + LOG_FILENAME;
|
|
Path logFile = outputDir.resolve(logFilename);
|
|
return new LogFileWriter(logFile);
|
|
}
|
|
|
|
private static void message(String message) {
|
|
System.out.println("[" + JavaHelpFilesBuilder.class.getSimpleName() + "] " + message);
|
|
System.out.flush();
|
|
}
|
|
|
|
private void generateMapFile(HelpModuleCollection help) throws IOException {
|
|
Path mapFile = outputDir.resolve(mapOutputFilename);
|
|
message("Generating map file: " + mapFile.toUri() + "...");
|
|
if (Files.exists(mapFile)) {
|
|
Files.delete(mapFile);
|
|
}
|
|
PrintWriter out = new LogFileWriter(mapFile);
|
|
try {
|
|
out.println("<?xml version='1.0' encoding='ISO-8859-1' ?>");
|
|
out.println(
|
|
"<!doctype MAP public \"-//Sun Microsystems Inc.//DTD JavaHelp Map Version 1.0//EN\">");
|
|
out.println("<!-- Auto-generated on " + (new Date()).toString() + " : Do Not Edit -->");
|
|
out.println("<map version=\"1.0\">");
|
|
|
|
Collection<AnchorDefinition> anchors = help.getAllAnchorDefinitions();
|
|
for (AnchorDefinition a : anchors) {
|
|
String anchorTarget = a.getHelpPath();
|
|
|
|
//
|
|
// JavaHelp Note: the JavaHelp system will resolve relative map entries by using
|
|
// this map file that we are generating as the base. So, whatever
|
|
// directory this file lives under is the root directory for the
|
|
// relative path that we are writing here for the 'mapID' entry.
|
|
// Thus, make sure that the relative entry is relative to the
|
|
// directory of this map file.
|
|
//
|
|
|
|
String updatedPath = relativize(outputDir, anchorTarget);
|
|
out.println(" <mapID target=\"" + a.getId() + "\" url=\"" + updatedPath + "\"/>");
|
|
}
|
|
out.println("</map>");
|
|
message("\tfinished generating map file");
|
|
}
|
|
finally {
|
|
out.close();
|
|
}
|
|
}
|
|
|
|
private String relativize(Path parent, String anchorTarget) {
|
|
Path anchorPath = Paths.get(anchorTarget);
|
|
if (anchorPath.isAbsolute()) {
|
|
return anchorTarget; // not a relative path; nothing to do
|
|
}
|
|
|
|
if (!parent.endsWith("help")) {
|
|
throw new AssertException("Map file expected in a directory name 'help'. " +
|
|
"Update the map file generation code.");
|
|
}
|
|
|
|
if (!anchorTarget.startsWith("help")) {
|
|
throw new AssertException("Relative anchor path does not start with 'help'");
|
|
}
|
|
|
|
Path relative = anchorPath.subpath(1, anchorPath.getNameCount());
|
|
String relativePath = relative.toString();
|
|
String normalized = relativePath.replaceAll("\\\\", "/");
|
|
return normalized;
|
|
}
|
|
|
|
private void generateTOCFile(LinkDatabase database, HelpModuleCollection help)
|
|
throws IOException {
|
|
message("Generating TOC file: " + tocOutputFilename + "...");
|
|
GhidraTOCFile sourceTOCFile = help.getSourceTOCFile();
|
|
Path outputFile = outputDir.resolve(tocOutputFilename);
|
|
database.generateTOCOutputFile(outputFile, sourceTOCFile);
|
|
message("\tfinished generating TOC file");
|
|
}
|
|
|
|
//==================================================================================================
|
|
// Inner Classes
|
|
//==================================================================================================
|
|
|
|
private class LogFileWriter extends PrintWriter {
|
|
private final Path file;
|
|
|
|
LogFileWriter(Path logFile) throws IOException {
|
|
super(new BufferedWriter(new OutputStreamWriter(Files.newOutputStream(logFile))));
|
|
this.file = logFile;
|
|
}
|
|
|
|
Path getFile() {
|
|
return file;
|
|
}
|
|
|
|
boolean isEmpty() {
|
|
try {
|
|
return Files.size(file) == 0;
|
|
}
|
|
catch (IOException e) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
void delete() {
|
|
try {
|
|
Files.delete(file);
|
|
}
|
|
catch (IOException e) {
|
|
// ignore
|
|
}
|
|
}
|
|
}
|
|
}
|