mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-04-25 17:25:17 +02:00
GP-6505: Fix TraceRmiPythonClientTest on Windows
This commit is contained in:
@@ -94,6 +94,7 @@ public class TerminalLayoutModel implements LayoutModel, VtHandler {
|
||||
// Flags for what's been enabled
|
||||
protected boolean showCursor;
|
||||
protected boolean bracketedPaste;
|
||||
protected boolean win32InputMode; // not implemented
|
||||
protected boolean reportMousePress;
|
||||
protected boolean reportMouseRelease;
|
||||
protected boolean reportFocus;
|
||||
@@ -139,6 +140,7 @@ public class TerminalLayoutModel implements LayoutModel, VtHandler {
|
||||
buffer = bufPrimary;
|
||||
|
||||
bracketedPaste = false;
|
||||
win32InputMode = false;
|
||||
reportMousePress = false;
|
||||
reportMouseRelease = false;
|
||||
reportFocus = false;
|
||||
@@ -497,6 +499,11 @@ public class TerminalLayoutModel implements LayoutModel, VtHandler {
|
||||
public void handleBracketedPasteMode(boolean en) {
|
||||
this.bracketedPaste = en;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleWin32InputMode(boolean en) {
|
||||
this.win32InputMode = en;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleSaveCursorPos() {
|
||||
|
||||
@@ -28,10 +28,8 @@ import ghidra.util.Msg;
|
||||
|
||||
/**
|
||||
* The handler of parsed ANSI VT control sequences
|
||||
*
|
||||
* <p>
|
||||
* Here are some of the resources where I found useful documentation:
|
||||
*
|
||||
* <ul>
|
||||
* <li><a href="https://invisible-island.net/xterm/ctlseqs/ctlseqs.html">XTerm Control
|
||||
* Sequences</a></li>
|
||||
@@ -39,13 +37,11 @@ import ghidra.util.Msg;
|
||||
* Terminal Control Escape Sequences</a></li>
|
||||
* <li><a href="https://en.wikipedia.org/wiki/ANSI_escape_code">Wikipedia: ANSI escape code</a></li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>
|
||||
* They were incredibly useful, even when experimentation was required to fill in details, because
|
||||
* they at least described the sort of behavior I should be looking for. Throughout the referenced
|
||||
* documents and within this documentation, the following abbreviations are used for escape
|
||||
* sequences:
|
||||
*
|
||||
* <table>
|
||||
* <tr>
|
||||
* <th>Abbreviation</th>
|
||||
@@ -78,13 +74,11 @@ import ghidra.util.Msg;
|
||||
* <td>{@code "\007"}</td>
|
||||
* </tr>
|
||||
* </table>
|
||||
*
|
||||
* <p>
|
||||
* The separation between the parser and the handler deals in state management. The parser manages
|
||||
* state only of the control sequence parser itself, i.e., the current node in the token parsing
|
||||
* state machine. The state of the terminal, e.g., the current attributes, cursor position, etc.,
|
||||
* are managed by the handler and its delegates.
|
||||
*
|
||||
* <p>
|
||||
* For example, the Cursor Position sequence is documented as:
|
||||
* <p>
|
||||
@@ -97,7 +91,6 @@ import ghidra.util.Msg;
|
||||
* It will thus invoke the abstract {@link #handleMoveCursor(int, int)} method passing 12 and 39.
|
||||
* Note that 1 is subtracted from both parameters, because ANSI specifies 1-up indexing while Java
|
||||
* lends itself to 0-up indexing.
|
||||
*
|
||||
* <p>
|
||||
* The XTerm documentation, which is arguably the most thorough, presents the CSI commands
|
||||
* alphabetically by the final byte, in ASCII order. For sanity and consistency, we adopt the same
|
||||
@@ -134,10 +127,10 @@ public interface VtHandler {
|
||||
public static final byte[] Q1048 = ascii("?1048");
|
||||
public static final byte[] Q1049 = ascii("?1049");
|
||||
public static final byte[] Q2004 = ascii("?2004");
|
||||
public static final byte[] Q9001 = ascii("?9001");
|
||||
|
||||
/**
|
||||
* An ANSI color specification
|
||||
*
|
||||
* <p>
|
||||
* We avoid going straight to AWT colors, 1) Because it provides better separation between the
|
||||
* terminal logic and the rendering framework, and 2) Because some specifications, e.g., default
|
||||
@@ -149,7 +142,6 @@ public interface VtHandler {
|
||||
|
||||
/**
|
||||
* A singleton representing the default color
|
||||
*
|
||||
* <p>
|
||||
* The actual color selected will depend on context and use. Most notably, the default color
|
||||
* used for foreground should greatly contrast the default color used for the background.
|
||||
@@ -160,7 +152,6 @@ public interface VtHandler {
|
||||
|
||||
/**
|
||||
* One of the eight standard ANSI colors
|
||||
*
|
||||
* <p>
|
||||
* The actual color may be modified by other SGR attributes, notably {@link Intensity}. For
|
||||
* colors that are described by hue, some thought should be given to how the standard and
|
||||
@@ -219,7 +210,6 @@ public interface VtHandler {
|
||||
|
||||
/**
|
||||
* Get the standard color for the given numerical code
|
||||
*
|
||||
* <p>
|
||||
* For example, the sequence {@code CSI [ 34 m} would use code 4 (blue).
|
||||
*
|
||||
@@ -233,7 +223,6 @@ public interface VtHandler {
|
||||
|
||||
/**
|
||||
* One of the eight ANSI intense colors
|
||||
*
|
||||
* <p>
|
||||
* Note that intense colors may also be specified using the standard color with the
|
||||
* {@link Intensity#BOLD} attribute, depending on the command sequence.
|
||||
@@ -279,7 +268,6 @@ public interface VtHandler {
|
||||
|
||||
/**
|
||||
* Get the intense color for the given numerical code
|
||||
*
|
||||
* <p>
|
||||
* For example, the sequence {@code CSI [ 94 m} would use code 4 (blue).
|
||||
*
|
||||
@@ -335,7 +323,6 @@ public interface VtHandler {
|
||||
|
||||
/**
|
||||
* Get the dim color for the given numerical code
|
||||
*
|
||||
* <p>
|
||||
* For example, the sequence {@code CSI [ 34 m} would use code 4 (blue).
|
||||
*
|
||||
@@ -349,7 +336,6 @@ public interface VtHandler {
|
||||
|
||||
/**
|
||||
* For 8-bit colors, one of the 216 colors from the RGB cube
|
||||
*
|
||||
* <p>
|
||||
* The r, g, and b fields give the "step" number from 0 to 5, dimmest to brightest.
|
||||
*/
|
||||
@@ -357,7 +343,6 @@ public interface VtHandler {
|
||||
|
||||
/**
|
||||
* For 8-bit colors, one of the 24 grays
|
||||
*
|
||||
* <p>
|
||||
* The v field is a value from 0 to 23, 0 being the dimmest, but not true black, and 23 being
|
||||
* the brightest, but not true white.
|
||||
@@ -366,7 +351,6 @@ public interface VtHandler {
|
||||
|
||||
/**
|
||||
* A 24-bit color
|
||||
*
|
||||
* <p>
|
||||
* The r, g, and b fields are values from 0 to 255 dimmest to brightest.
|
||||
*/
|
||||
@@ -374,7 +358,6 @@ public interface VtHandler {
|
||||
|
||||
/**
|
||||
* Modifies the intensity of the character either by color or by font weight.
|
||||
*
|
||||
* <p>
|
||||
* The renderer may choose a combination of strategies. For example, {@link #NORMAL} may be
|
||||
* rendered using the standard color and bold type. Then {@link #BOLD} would use the intense
|
||||
@@ -434,7 +417,6 @@ public interface VtHandler {
|
||||
|
||||
/**
|
||||
* Causes text to blink
|
||||
*
|
||||
* <p>
|
||||
* If implemented, renderers should take care not to irritate the user. One option is to make
|
||||
* {@link #FAST} actually slow, and {@link #SLOW} even slower. Another option is to only blink
|
||||
@@ -567,7 +549,6 @@ public interface VtHandler {
|
||||
|
||||
/**
|
||||
* For cursor and keypad, specifies normal or application mode
|
||||
*
|
||||
* <p>
|
||||
* This affects the codes sent by the terminal.
|
||||
*/
|
||||
@@ -628,7 +609,6 @@ public interface VtHandler {
|
||||
|
||||
/**
|
||||
* Handle normal character output, i.e., place the character on the display
|
||||
*
|
||||
* <p>
|
||||
* This excludes control sequences and control characters, e.g., tab, line feed. While we've not
|
||||
* tested, in theory, this can instead buffer the byte for decoding from UTF-8. Still, the
|
||||
@@ -641,7 +621,6 @@ public interface VtHandler {
|
||||
|
||||
/**
|
||||
* Handle a character not part of an escape sequence.
|
||||
*
|
||||
* <p>
|
||||
* This may include control characters, which are displatched appropriately by this method.
|
||||
* Additionally, this handles any exception thrown by {@link #handleChar(byte)}.
|
||||
@@ -683,7 +662,6 @@ public interface VtHandler {
|
||||
|
||||
/**
|
||||
* Parse a sequence of integers in the form {@code <em>n</em> ; <em>m</em> ;} ....
|
||||
*
|
||||
* <p>
|
||||
* This is designed to replace the {@link String#split(String)} and
|
||||
* {@link Integer#parseInt(String)} pattern, which should avoid some unnecessary object
|
||||
@@ -799,6 +777,9 @@ public interface VtHandler {
|
||||
else if (bufEq(csiParam, Q2004)) {
|
||||
handleBracketedPasteMode(en);
|
||||
}
|
||||
else if (bufEq(csiParam, Q9001)) {
|
||||
handleWin32InputMode(en);
|
||||
}
|
||||
else {
|
||||
throw new UnknownCsiException();
|
||||
}
|
||||
@@ -1189,7 +1170,6 @@ public interface VtHandler {
|
||||
|
||||
/**
|
||||
* Decode the 8-bit ANSI color.
|
||||
*
|
||||
* <p>
|
||||
* Colors 0-15 are the standard + high-intensity. Colors 16-231 come from a 6x6x6 RGB cube.
|
||||
* Finally, colors 232-255 are 24 steps of gray scale.
|
||||
@@ -1413,7 +1393,6 @@ public interface VtHandler {
|
||||
|
||||
/**
|
||||
* Handle toggling of reverse video
|
||||
*
|
||||
* <p>
|
||||
* This can be a bit confusing with default colors. In general, this means swapping the
|
||||
* foreground and background color specifications (not inverting the colors or mirroring or some
|
||||
@@ -1449,7 +1428,6 @@ public interface VtHandler {
|
||||
|
||||
/**
|
||||
* Handle toggling insert mode
|
||||
*
|
||||
* <p>
|
||||
* In insert mode, characters at and to the right of the cursor are shifted right to make room
|
||||
* for each new character. In replace mode (default), the character at the cursor is replaced
|
||||
@@ -1482,7 +1460,6 @@ public interface VtHandler {
|
||||
|
||||
/**
|
||||
* Toggle blinking of the cursor
|
||||
*
|
||||
* <p>
|
||||
* Renderers should take care not to irritate the user. Some possibilities are to blink slowly,
|
||||
* blink only for a short period of time after it moves, and/or blink only when the terminal has
|
||||
@@ -1523,7 +1500,6 @@ public interface VtHandler {
|
||||
|
||||
/**
|
||||
* Switch to and from the alternate screen buffer, optionally clearing it
|
||||
*
|
||||
* <p>
|
||||
* This will never clear the normal buffer. If the buffer does not change as a result of this
|
||||
* call, then the alternate buffer is not cleared, even if clearAlt is specified.
|
||||
@@ -1545,6 +1521,20 @@ public interface VtHandler {
|
||||
*/
|
||||
void handleBracketedPasteMode(boolean en);
|
||||
|
||||
/**
|
||||
* Toggle Win32 input mode
|
||||
*
|
||||
* <p>
|
||||
* See the Windows Terminal specification:
|
||||
* https://github.com/microsoft/terminal/blob/main/doc/specs/%234999%20-%20Improved%20keyboard%20handling%20in%20Conpty.md.
|
||||
* It should be safe to ignore this, but could provide us options if we'd like to forward more
|
||||
* detailed keyboard events to a Windows Console application than is permitted with standard VT
|
||||
* sequences.
|
||||
*
|
||||
* @param en true to enable Win32 input mode
|
||||
*/
|
||||
void handleWin32InputMode(boolean en);
|
||||
|
||||
/**
|
||||
* Handle a request to save the cursor position
|
||||
*/
|
||||
@@ -1573,7 +1563,6 @@ public interface VtHandler {
|
||||
|
||||
/**
|
||||
* Handle an absolute cursor row movement command
|
||||
*
|
||||
* <p>
|
||||
* The column should remain the same, i.e., do <em>not</em> reset the column to 0.
|
||||
*
|
||||
@@ -1595,7 +1584,6 @@ public interface VtHandler {
|
||||
|
||||
/**
|
||||
* Handle a request to save the terminal window's icon title
|
||||
*
|
||||
* <p>
|
||||
* "Icon titles" are a concept from the X Windows system. Do the closest equivalent, if anything
|
||||
* applies at all. The current title is pushed to a stack of limited size.
|
||||
@@ -1604,7 +1592,6 @@ public interface VtHandler {
|
||||
|
||||
/**
|
||||
* Handle a request to save the terminal window's title
|
||||
*
|
||||
* <p>
|
||||
* Window titles are fairly applicable to all desktop windowing systems. The current title is
|
||||
* pushed to a stack of limited size.
|
||||
@@ -1613,7 +1600,6 @@ public interface VtHandler {
|
||||
|
||||
/**
|
||||
* Handle a request to restore the terminal window's icon title
|
||||
*
|
||||
* <p>
|
||||
* The title is set to the one popped from the stack of saved window icon titles.
|
||||
*
|
||||
@@ -1623,7 +1609,6 @@ public interface VtHandler {
|
||||
|
||||
/**
|
||||
* Handle a request to restore the terminal window's title
|
||||
*
|
||||
* <p>
|
||||
* The title is set to the one popped from the stack of saved window titles.
|
||||
*
|
||||
@@ -1647,7 +1632,6 @@ public interface VtHandler {
|
||||
|
||||
/**
|
||||
* Insert n lines at and below the cursor
|
||||
*
|
||||
* <p>
|
||||
* Lines within the viewport are shifted down or deleted to make room for the new lines.
|
||||
*
|
||||
@@ -1657,7 +1641,6 @@ public interface VtHandler {
|
||||
|
||||
/**
|
||||
* Delete n lines at and below the cursor
|
||||
*
|
||||
* <p>
|
||||
* Lines within the viewport are shifted up, and new lines inserted at the bottom.
|
||||
*
|
||||
@@ -1702,7 +1685,6 @@ public interface VtHandler {
|
||||
|
||||
/**
|
||||
* Set the range of rows (viewport) involved in scrolling.
|
||||
*
|
||||
* <p>
|
||||
* This applies not only to {@link #handleScrollUp()} and {@link #handleScrollDown()}, but also
|
||||
* to when the cursor moves far enough down that the display must scroll. Normally, start is 0
|
||||
@@ -1721,7 +1703,6 @@ public interface VtHandler {
|
||||
|
||||
/**
|
||||
* Scroll the display n lines down, considering only those lines in the scrolling range.
|
||||
*
|
||||
* <p>
|
||||
* To be unambiguous, this of movement of the viewport. The viewport scrolls down, so the lines
|
||||
* themselves scroll up. The default range is the whole display. The cursor is not moved.
|
||||
@@ -1743,7 +1724,6 @@ public interface VtHandler {
|
||||
|
||||
/**
|
||||
* Scroll the lines n slots down, considering only those lines in the scrolling range.
|
||||
*
|
||||
* <p>
|
||||
* This is equivalent to scrolling the <em>viewport</em> n lines <em>up</em>. This method exists
|
||||
* in attempt to reflect "up" and "down" correctly in the documentation. Unfortunately, the
|
||||
@@ -1759,7 +1739,6 @@ public interface VtHandler {
|
||||
|
||||
/**
|
||||
* Scroll the lines n slots up, considering only those lines in the scrolling range.
|
||||
*
|
||||
* <p>
|
||||
* The is equivalent to scrolling the <em>viewport</em> n lines <em>down</em>. This method
|
||||
* exists in attempt to reflect "up" and "down" correctly in the documentation. Unfortunately,
|
||||
@@ -1784,7 +1763,6 @@ public interface VtHandler {
|
||||
|
||||
/**
|
||||
* Handle a request to fully reset the terminal
|
||||
*
|
||||
* <p>
|
||||
* All buffers should be cleared and all state variables, positions, attributes, etc., should be
|
||||
* reset to their defaults.
|
||||
|
||||
@@ -19,7 +19,6 @@ import java.io.*;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Arrays;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
// TODO: I shouldn't have to do any of this.
|
||||
@@ -118,34 +117,17 @@ public class AnsiBufferedInputStream extends InputStream {
|
||||
return -1;
|
||||
}
|
||||
byte c = (byte) ci;
|
||||
//printDebugChar(c);
|
||||
// printDebugChar(c);
|
||||
switch (mode) {
|
||||
case CHARS:
|
||||
processChars(c);
|
||||
break;
|
||||
case ESC:
|
||||
processEsc(c);
|
||||
break;
|
||||
case CSI:
|
||||
processCsi(c);
|
||||
break;
|
||||
case CSI_p:
|
||||
processCsiParamOrCommand(c);
|
||||
break;
|
||||
case CSI_Q:
|
||||
processCsiQ(c);
|
||||
break;
|
||||
case OSC:
|
||||
processOsc(c);
|
||||
break;
|
||||
case WINDOW_TITLE:
|
||||
processWindowTitle(c);
|
||||
break;
|
||||
case WINDOW_TITLE_ESC:
|
||||
processWindowTitleEsc(c);
|
||||
break;
|
||||
default:
|
||||
throw new AssertionError();
|
||||
case CHARS -> processChars(c);
|
||||
case ESC -> processEsc(c);
|
||||
case CSI -> processCsi(c);
|
||||
case CSI_p -> processCsiParamOrCommand(c);
|
||||
case CSI_Q -> processCsiQ(c);
|
||||
case OSC -> processOsc(c);
|
||||
case WINDOW_TITLE -> processWindowTitle(c);
|
||||
case WINDOW_TITLE_ESC -> processWindowTitleEsc(c);
|
||||
default -> throw new AssertionError();
|
||||
}
|
||||
countIn++;
|
||||
return c;
|
||||
@@ -187,180 +169,151 @@ public class AnsiBufferedInputStream extends InputStream {
|
||||
|
||||
protected void processChars(byte c) {
|
||||
switch (c) {
|
||||
case 0x08:
|
||||
default -> appendChar(c);
|
||||
case '\b' -> {
|
||||
if (lineBuf.get(lineBuf.position() - 1) == ' ') {
|
||||
lineBuf.position(lineBuf.position() - 1);
|
||||
}
|
||||
break;
|
||||
case '\n':
|
||||
//appendChar(c);
|
||||
bakeLine();
|
||||
break;
|
||||
case 0x1b:
|
||||
mode = Mode.ESC;
|
||||
break;
|
||||
default:
|
||||
appendChar(c);
|
||||
break;
|
||||
}
|
||||
case '\n' -> bakeLine();
|
||||
case '\r' -> lineBuf.position(0);
|
||||
case 0x1b -> mode = Mode.ESC;
|
||||
}
|
||||
}
|
||||
|
||||
protected void processEsc(byte c) {
|
||||
switch (c) {
|
||||
case '[':
|
||||
mode = Mode.CSI;
|
||||
break;
|
||||
case ']':
|
||||
mode = Mode.OSC;
|
||||
break;
|
||||
default:
|
||||
throw new AssertionError("Saw 'ESC " + c + "' at " + countIn);
|
||||
case '[' -> mode = Mode.CSI;
|
||||
case ']' -> mode = Mode.OSC;
|
||||
default -> throw new AssertionError("Saw 'ESC " + c + "' at " + countIn);
|
||||
}
|
||||
}
|
||||
|
||||
protected void processCsi(byte c) {
|
||||
switch (c) {
|
||||
default:
|
||||
processCsiParamOrCommand(c);
|
||||
break;
|
||||
case '?':
|
||||
mode = Mode.CSI_Q;
|
||||
break;
|
||||
default -> processCsiParamOrCommand(c);
|
||||
case '?' -> mode = Mode.CSI_Q;
|
||||
}
|
||||
}
|
||||
|
||||
protected void processCsiParamOrCommand(byte c) {
|
||||
switch (c) {
|
||||
default:
|
||||
escBuf.put(c);
|
||||
break;
|
||||
case 'A':
|
||||
default -> escBuf.put(c);
|
||||
case 'A' -> {
|
||||
execCursorUp();
|
||||
mode = Mode.CHARS;
|
||||
break;
|
||||
case 'B':
|
||||
}
|
||||
case 'B' -> {
|
||||
execCursorDown();
|
||||
mode = Mode.CHARS;
|
||||
break;
|
||||
case 'C':
|
||||
}
|
||||
case 'C' -> {
|
||||
execCursorForward();
|
||||
mode = Mode.CHARS;
|
||||
break;
|
||||
case 'D':
|
||||
}
|
||||
case 'D' -> {
|
||||
execCursorBackward();
|
||||
mode = Mode.CHARS;
|
||||
break;
|
||||
case 'G':
|
||||
}
|
||||
case 'G' -> {
|
||||
execCursorCharAbsolute();
|
||||
mode = Mode.CHARS;
|
||||
break;
|
||||
case 'H':
|
||||
}
|
||||
case 'H' -> {
|
||||
execCursorPosition();
|
||||
mode = Mode.CHARS;
|
||||
break;
|
||||
case 'J':
|
||||
}
|
||||
case 'J' -> {
|
||||
execEraseInDisplay();
|
||||
mode = Mode.CHARS;
|
||||
break;
|
||||
case 'K':
|
||||
}
|
||||
case 'K' -> {
|
||||
execEraseInLine();
|
||||
mode = Mode.CHARS;
|
||||
break;
|
||||
case 'X':
|
||||
}
|
||||
case 'X' -> {
|
||||
execEraseCharacter();
|
||||
mode = Mode.CHARS;
|
||||
break;
|
||||
case 'm':
|
||||
}
|
||||
case 'm' -> {
|
||||
execSetGraphicsRendition();
|
||||
mode = Mode.CHARS;
|
||||
break;
|
||||
case 'h':
|
||||
}
|
||||
case 'h' -> {
|
||||
execPrivateSequence(true);
|
||||
mode = Mode.CHARS;
|
||||
break;
|
||||
case 'l':
|
||||
}
|
||||
case 'l' -> {
|
||||
execPrivateSequence(false);
|
||||
mode = Mode.CHARS;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static final String PRIV_12 = "12";
|
||||
public static final String PRIV_25 = "25";
|
||||
public static final String PRIV_1004 = "1004";
|
||||
public static final String PRIV_2004 = "2004";
|
||||
public static final String PRIV_9001 = "9001";
|
||||
|
||||
protected void processCsiQ(byte c) {
|
||||
String buf;
|
||||
switch (c) {
|
||||
default:
|
||||
escBuf.put(c);
|
||||
break;
|
||||
case 'h':
|
||||
buf = readAndClearEscBuf();
|
||||
if ("12".equals(buf)) {
|
||||
execTextCursorEnableBlinking();
|
||||
escBuf.clear();
|
||||
mode = Mode.CHARS;
|
||||
default -> escBuf.put(c);
|
||||
case 'h' -> {
|
||||
switch (readAndClearEscBuf()) {
|
||||
case PRIV_12 -> execTextCursorEnableBlinking();
|
||||
case PRIV_25 -> execTextCursorEnableModeShow();
|
||||
case PRIV_1004 -> execEnableFocusReport();
|
||||
case PRIV_2004 -> execEnableBracketedPasteMode();
|
||||
case PRIV_9001 -> execEnableWin32InputMode();
|
||||
case String buf -> throw new AssertionError("Got CsiQ(h): %s".formatted(buf));
|
||||
}
|
||||
else if ("25".equals(buf)) {
|
||||
execTextCursorEnableModeShow();
|
||||
escBuf.clear();
|
||||
mode = Mode.CHARS;
|
||||
mode = Mode.CHARS;
|
||||
}
|
||||
case 'l' -> {
|
||||
switch (readAndClearEscBuf()) {
|
||||
case PRIV_12 -> execTextCursorDisableBlinking();
|
||||
case PRIV_25 -> execTextCursorDisableModeShow();
|
||||
case PRIV_1004 -> execDisableFocusReport();
|
||||
case PRIV_2004 -> execDisableBracketedPasteMode();
|
||||
case PRIV_9001 -> execDisableWin32InputMode();
|
||||
case String buf -> throw new AssertionError("Got CsiQ(l): %s".formatted(buf));
|
||||
}
|
||||
else {
|
||||
throw new AssertionError();
|
||||
}
|
||||
break;
|
||||
case 'l':
|
||||
buf = readAndClearEscBuf();
|
||||
if ("12".equals(buf)) {
|
||||
execTextCursorDisableBlinking();
|
||||
escBuf.clear();
|
||||
mode = Mode.CHARS;
|
||||
}
|
||||
else if ("25".equals(buf)) {
|
||||
execTextCursorDisableModeShow();
|
||||
escBuf.clear();
|
||||
mode = Mode.CHARS;
|
||||
}
|
||||
break;
|
||||
mode = Mode.CHARS;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void processOsc(byte c) {
|
||||
switch (c) {
|
||||
default:
|
||||
escBuf.put(c);
|
||||
break;
|
||||
case ';':
|
||||
if (Set.of("0", "2").contains(readAndClearEscBuf())) {
|
||||
mode = Mode.WINDOW_TITLE;
|
||||
escBuf.clear();
|
||||
break;
|
||||
default -> escBuf.put(c);
|
||||
case ';' -> {
|
||||
switch (readAndClearEscBuf()) {
|
||||
case "0", "2" -> mode = Mode.WINDOW_TITLE;
|
||||
default -> throw new AssertionError();
|
||||
}
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void processWindowTitle(byte c) {
|
||||
switch (c) {
|
||||
default:
|
||||
titleBuf.put(c);
|
||||
break;
|
||||
case 0x07: // bell, even though MSDN says longer form preferred
|
||||
default -> titleBuf.put(c);
|
||||
case 0x07 -> { // bell, even though MSDN says longer form preferred
|
||||
execSetWindowTitle();
|
||||
mode = Mode.CHARS;
|
||||
break;
|
||||
case 0x1b:
|
||||
mode = Mode.WINDOW_TITLE_ESC;
|
||||
break;
|
||||
}
|
||||
case 0x1b -> mode = Mode.WINDOW_TITLE_ESC;
|
||||
}
|
||||
}
|
||||
|
||||
protected void processWindowTitleEsc(byte c) {
|
||||
switch (c) {
|
||||
case '\\':
|
||||
case '\\' -> {
|
||||
execSetWindowTitle();
|
||||
mode = Mode.CHARS;
|
||||
break;
|
||||
default:
|
||||
throw new AssertionError("Saw <ST> ... ESC " + c + " at " + countIn);
|
||||
}
|
||||
default -> throw new AssertionError("Saw <ST> ... ESC " + c + " at " + countIn);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -455,6 +408,30 @@ public class AnsiBufferedInputStream extends InputStream {
|
||||
// Don't care
|
||||
}
|
||||
|
||||
protected void execEnableFocusReport() {
|
||||
// Don't care
|
||||
}
|
||||
|
||||
protected void execDisableFocusReport() {
|
||||
// Don't care
|
||||
}
|
||||
|
||||
protected void execEnableBracketedPasteMode() {
|
||||
// Don't care
|
||||
}
|
||||
|
||||
protected void execDisableBracketedPasteMode() {
|
||||
// Don't care
|
||||
}
|
||||
|
||||
protected void execEnableWin32InputMode() {
|
||||
// Don't care
|
||||
}
|
||||
|
||||
protected void execDisableWin32InputMode() {
|
||||
// Don't care
|
||||
}
|
||||
|
||||
protected void execEraseInDisplay() {
|
||||
// Because I have only one line, right?
|
||||
execEraseInLine();
|
||||
@@ -462,15 +439,10 @@ public class AnsiBufferedInputStream extends InputStream {
|
||||
|
||||
protected void execEraseInLine() {
|
||||
switch (parseNumericBuffer()) {
|
||||
case 0:
|
||||
Arrays.fill(lineBuf.array(), lineBuf.position(), lineBuf.capacity(), (byte) 0);
|
||||
break;
|
||||
case 1:
|
||||
Arrays.fill(lineBuf.array(), 0, lineBuf.position() + 1, (byte) 0);
|
||||
break;
|
||||
case 2:
|
||||
Arrays.fill(lineBuf.array(), (byte) 0);
|
||||
break;
|
||||
case 0 -> Arrays.fill(lineBuf.array(), lineBuf.position(), lineBuf.capacity(),
|
||||
(byte) 0);
|
||||
case 1 -> Arrays.fill(lineBuf.array(), 0, lineBuf.position() + 1, (byte) 0);
|
||||
case 2 -> Arrays.fill(lineBuf.array(), (byte) 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package agent;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.io.*;
|
||||
@@ -47,6 +48,7 @@ import ghidra.framework.plugintool.util.*;
|
||||
import ghidra.pty.*;
|
||||
import ghidra.pty.PtyChild.Echo;
|
||||
import ghidra.pty.testutil.DummyProc;
|
||||
import ghidra.pty.windows.AnsiBufferedInputStream;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.target.schema.PrimitiveTraceObjectSchema.MinimalSchemaContext;
|
||||
import ghidra.trace.model.target.schema.TraceObjectSchema.SchemaName;
|
||||
@@ -107,11 +109,14 @@ public class TraceRmiPythonClientTest extends AbstractGhidraHeadedDebuggerTest {
|
||||
|
||||
protected Path getPathToPython() {
|
||||
try {
|
||||
return Paths.get(DummyProc.which("python3"));
|
||||
String py3path = DummyProc.which("python3");
|
||||
if (py3path != null && !py3path.contains("msys")) {
|
||||
return Paths.get(py3path);
|
||||
}
|
||||
}
|
||||
catch (RuntimeException e) {
|
||||
return Paths.get(DummyProc.which("python"));
|
||||
}
|
||||
return Paths.get(DummyProc.which("python"));
|
||||
}
|
||||
|
||||
@Before
|
||||
@@ -119,6 +124,7 @@ public class TraceRmiPythonClientTest extends AbstractGhidraHeadedDebuggerTest {
|
||||
traceRmi = addPlugin(tool, TraceRmiPlugin.class);
|
||||
|
||||
pathToPython = getPathToPython();
|
||||
Msg.info(this, "Using python: %s".formatted(pathToPython));
|
||||
}
|
||||
|
||||
protected void addAllDebuggerPlugins() throws PluginException {
|
||||
@@ -180,6 +186,14 @@ public class TraceRmiPythonClientTest extends AbstractGhidraHeadedDebuggerTest {
|
||||
protected ExecInPy execInPy(String script) throws IOException {
|
||||
Map<String, String> env = new HashMap<>(System.getenv());
|
||||
setPythonPath(env);
|
||||
/**
|
||||
* A new REPL was instroduced in Python 3.13. Unfortunately, the REPL is in play when we use
|
||||
* a PTY, because it assumes that is a human. It will automatically insert indentation after
|
||||
* pressing ENTER, which really goofs up our input. (It's worth noting, this happens even
|
||||
* when copy-pasting a code block from notepad, which seems like a bug on their part.) This
|
||||
* environment variable (at least for the moment) disables that new REPL.
|
||||
*/
|
||||
env.put("PYTHON_BASIC_REPL", "1");
|
||||
Pty pty = PtyFactory.local().openpty();
|
||||
|
||||
PtySession session =
|
||||
@@ -187,7 +201,7 @@ public class TraceRmiPythonClientTest extends AbstractGhidraHeadedDebuggerTest {
|
||||
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
new Thread(() -> {
|
||||
InputStream is = pty.getParent().getInputStream();
|
||||
InputStream is = new AnsiBufferedInputStream(pty.getParent().getInputStream());
|
||||
byte[] buf = new byte[1024];
|
||||
while (true) {
|
||||
try {
|
||||
@@ -203,7 +217,12 @@ public class TraceRmiPythonClientTest extends AbstractGhidraHeadedDebuggerTest {
|
||||
}).start();
|
||||
|
||||
PrintWriter stdin = new PrintWriter(pty.getParent().getOutputStream());
|
||||
script.lines().forEach(stdin::println); // to transform newlines.
|
||||
/**
|
||||
* Because we're using a pty, we need to use CR instead of LF, i.e., to simulate the user
|
||||
* pressing ENTER.
|
||||
*/
|
||||
script = script.replace("\n", "\r");
|
||||
stdin.write(script);
|
||||
stdin.flush();
|
||||
return new ExecInPy(session, stdin, CompletableFuture.supplyAsync(() -> {
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user