mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-06-02 05:38:17 +08:00
Merge remote-tracking branch 'origin/GP-6505_Dan_fixTraceRmiPythonClientTestsWindows--RB20260325--SQUASHED'
This commit is contained in:
+7
@@ -94,6 +94,7 @@ public class TerminalLayoutModel implements LayoutModel, VtHandler {
|
|||||||
// Flags for what's been enabled
|
// Flags for what's been enabled
|
||||||
protected boolean showCursor;
|
protected boolean showCursor;
|
||||||
protected boolean bracketedPaste;
|
protected boolean bracketedPaste;
|
||||||
|
protected boolean win32InputMode; // not implemented
|
||||||
protected boolean reportMousePress;
|
protected boolean reportMousePress;
|
||||||
protected boolean reportMouseRelease;
|
protected boolean reportMouseRelease;
|
||||||
protected boolean reportFocus;
|
protected boolean reportFocus;
|
||||||
@@ -139,6 +140,7 @@ public class TerminalLayoutModel implements LayoutModel, VtHandler {
|
|||||||
buffer = bufPrimary;
|
buffer = bufPrimary;
|
||||||
|
|
||||||
bracketedPaste = false;
|
bracketedPaste = false;
|
||||||
|
win32InputMode = false;
|
||||||
reportMousePress = false;
|
reportMousePress = false;
|
||||||
reportMouseRelease = false;
|
reportMouseRelease = false;
|
||||||
reportFocus = false;
|
reportFocus = false;
|
||||||
@@ -498,6 +500,11 @@ public class TerminalLayoutModel implements LayoutModel, VtHandler {
|
|||||||
this.bracketedPaste = en;
|
this.bracketedPaste = en;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleWin32InputMode(boolean en) {
|
||||||
|
this.win32InputMode = en;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleSaveCursorPos() {
|
public void handleSaveCursorPos() {
|
||||||
buffer.saveCursorPos();
|
buffer.saveCursorPos();
|
||||||
|
|||||||
+18
-40
@@ -28,10 +28,8 @@ import ghidra.util.Msg;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* The handler of parsed ANSI VT control sequences
|
* The handler of parsed ANSI VT control sequences
|
||||||
*
|
|
||||||
* <p>
|
* <p>
|
||||||
* Here are some of the resources where I found useful documentation:
|
* Here are some of the resources where I found useful documentation:
|
||||||
*
|
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li><a href="https://invisible-island.net/xterm/ctlseqs/ctlseqs.html">XTerm Control
|
* <li><a href="https://invisible-island.net/xterm/ctlseqs/ctlseqs.html">XTerm Control
|
||||||
* Sequences</a></li>
|
* Sequences</a></li>
|
||||||
@@ -39,13 +37,11 @@ import ghidra.util.Msg;
|
|||||||
* Terminal Control Escape Sequences</a></li>
|
* Terminal Control Escape Sequences</a></li>
|
||||||
* <li><a href="https://en.wikipedia.org/wiki/ANSI_escape_code">Wikipedia: ANSI escape code</a></li>
|
* <li><a href="https://en.wikipedia.org/wiki/ANSI_escape_code">Wikipedia: ANSI escape code</a></li>
|
||||||
* </ul>
|
* </ul>
|
||||||
*
|
|
||||||
* <p>
|
* <p>
|
||||||
* They were incredibly useful, even when experimentation was required to fill in details, because
|
* 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
|
* 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
|
* documents and within this documentation, the following abbreviations are used for escape
|
||||||
* sequences:
|
* sequences:
|
||||||
*
|
|
||||||
* <table>
|
* <table>
|
||||||
* <tr>
|
* <tr>
|
||||||
* <th>Abbreviation</th>
|
* <th>Abbreviation</th>
|
||||||
@@ -78,13 +74,11 @@ import ghidra.util.Msg;
|
|||||||
* <td>{@code "\007"}</td>
|
* <td>{@code "\007"}</td>
|
||||||
* </tr>
|
* </tr>
|
||||||
* </table>
|
* </table>
|
||||||
*
|
|
||||||
* <p>
|
* <p>
|
||||||
* The separation between the parser and the handler deals in state management. The parser manages
|
* 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 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.,
|
* state machine. The state of the terminal, e.g., the current attributes, cursor position, etc.,
|
||||||
* are managed by the handler and its delegates.
|
* are managed by the handler and its delegates.
|
||||||
*
|
|
||||||
* <p>
|
* <p>
|
||||||
* For example, the Cursor Position sequence is documented as:
|
* For example, the Cursor Position sequence is documented as:
|
||||||
* <p>
|
* <p>
|
||||||
@@ -97,7 +91,6 @@ import ghidra.util.Msg;
|
|||||||
* It will thus invoke the abstract {@link #handleMoveCursor(int, int)} method passing 12 and 39.
|
* 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
|
* Note that 1 is subtracted from both parameters, because ANSI specifies 1-up indexing while Java
|
||||||
* lends itself to 0-up indexing.
|
* lends itself to 0-up indexing.
|
||||||
*
|
|
||||||
* <p>
|
* <p>
|
||||||
* The XTerm documentation, which is arguably the most thorough, presents the CSI commands
|
* 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
|
* 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[] Q1048 = ascii("?1048");
|
||||||
public static final byte[] Q1049 = ascii("?1049");
|
public static final byte[] Q1049 = ascii("?1049");
|
||||||
public static final byte[] Q2004 = ascii("?2004");
|
public static final byte[] Q2004 = ascii("?2004");
|
||||||
|
public static final byte[] Q9001 = ascii("?9001");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An ANSI color specification
|
* An ANSI color specification
|
||||||
*
|
|
||||||
* <p>
|
* <p>
|
||||||
* We avoid going straight to AWT colors, 1) Because it provides better separation between the
|
* 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
|
* 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
|
* A singleton representing the default color
|
||||||
*
|
|
||||||
* <p>
|
* <p>
|
||||||
* The actual color selected will depend on context and use. Most notably, the default color
|
* 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.
|
* 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
|
* One of the eight standard ANSI colors
|
||||||
*
|
|
||||||
* <p>
|
* <p>
|
||||||
* The actual color may be modified by other SGR attributes, notably {@link Intensity}. For
|
* 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
|
* 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
|
* Get the standard color for the given numerical code
|
||||||
*
|
|
||||||
* <p>
|
* <p>
|
||||||
* For example, the sequence {@code CSI [ 34 m} would use code 4 (blue).
|
* 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
|
* One of the eight ANSI intense colors
|
||||||
*
|
|
||||||
* <p>
|
* <p>
|
||||||
* Note that intense colors may also be specified using the standard color with the
|
* Note that intense colors may also be specified using the standard color with the
|
||||||
* {@link Intensity#BOLD} attribute, depending on the command sequence.
|
* {@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
|
* Get the intense color for the given numerical code
|
||||||
*
|
|
||||||
* <p>
|
* <p>
|
||||||
* For example, the sequence {@code CSI [ 94 m} would use code 4 (blue).
|
* 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
|
* Get the dim color for the given numerical code
|
||||||
*
|
|
||||||
* <p>
|
* <p>
|
||||||
* For example, the sequence {@code CSI [ 34 m} would use code 4 (blue).
|
* 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
|
* For 8-bit colors, one of the 216 colors from the RGB cube
|
||||||
*
|
|
||||||
* <p>
|
* <p>
|
||||||
* The r, g, and b fields give the "step" number from 0 to 5, dimmest to brightest.
|
* 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
|
* For 8-bit colors, one of the 24 grays
|
||||||
*
|
|
||||||
* <p>
|
* <p>
|
||||||
* The v field is a value from 0 to 23, 0 being the dimmest, but not true black, and 23 being
|
* 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.
|
* the brightest, but not true white.
|
||||||
@@ -366,7 +351,6 @@ public interface VtHandler {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* A 24-bit color
|
* A 24-bit color
|
||||||
*
|
|
||||||
* <p>
|
* <p>
|
||||||
* The r, g, and b fields are values from 0 to 255 dimmest to brightest.
|
* 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.
|
* Modifies the intensity of the character either by color or by font weight.
|
||||||
*
|
|
||||||
* <p>
|
* <p>
|
||||||
* The renderer may choose a combination of strategies. For example, {@link #NORMAL} may be
|
* 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
|
* 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
|
* Causes text to blink
|
||||||
*
|
|
||||||
* <p>
|
* <p>
|
||||||
* If implemented, renderers should take care not to irritate the user. One option is to make
|
* 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
|
* {@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
|
* For cursor and keypad, specifies normal or application mode
|
||||||
*
|
|
||||||
* <p>
|
* <p>
|
||||||
* This affects the codes sent by the terminal.
|
* 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
|
* Handle normal character output, i.e., place the character on the display
|
||||||
*
|
|
||||||
* <p>
|
* <p>
|
||||||
* This excludes control sequences and control characters, e.g., tab, line feed. While we've not
|
* 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
|
* 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.
|
* Handle a character not part of an escape sequence.
|
||||||
*
|
|
||||||
* <p>
|
* <p>
|
||||||
* This may include control characters, which are displatched appropriately by this method.
|
* This may include control characters, which are displatched appropriately by this method.
|
||||||
* Additionally, this handles any exception thrown by {@link #handleChar(byte)}.
|
* 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> ;} ....
|
* Parse a sequence of integers in the form {@code <em>n</em> ; <em>m</em> ;} ....
|
||||||
*
|
|
||||||
* <p>
|
* <p>
|
||||||
* This is designed to replace the {@link String#split(String)} and
|
* This is designed to replace the {@link String#split(String)} and
|
||||||
* {@link Integer#parseInt(String)} pattern, which should avoid some unnecessary object
|
* {@link Integer#parseInt(String)} pattern, which should avoid some unnecessary object
|
||||||
@@ -799,6 +777,9 @@ public interface VtHandler {
|
|||||||
else if (bufEq(csiParam, Q2004)) {
|
else if (bufEq(csiParam, Q2004)) {
|
||||||
handleBracketedPasteMode(en);
|
handleBracketedPasteMode(en);
|
||||||
}
|
}
|
||||||
|
else if (bufEq(csiParam, Q9001)) {
|
||||||
|
handleWin32InputMode(en);
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
throw new UnknownCsiException();
|
throw new UnknownCsiException();
|
||||||
}
|
}
|
||||||
@@ -1189,7 +1170,6 @@ public interface VtHandler {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Decode the 8-bit ANSI color.
|
* Decode the 8-bit ANSI color.
|
||||||
*
|
|
||||||
* <p>
|
* <p>
|
||||||
* Colors 0-15 are the standard + high-intensity. Colors 16-231 come from a 6x6x6 RGB cube.
|
* 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.
|
* Finally, colors 232-255 are 24 steps of gray scale.
|
||||||
@@ -1413,7 +1393,6 @@ public interface VtHandler {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle toggling of reverse video
|
* Handle toggling of reverse video
|
||||||
*
|
|
||||||
* <p>
|
* <p>
|
||||||
* This can be a bit confusing with default colors. In general, this means swapping the
|
* 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
|
* foreground and background color specifications (not inverting the colors or mirroring or some
|
||||||
@@ -1449,7 +1428,6 @@ public interface VtHandler {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle toggling insert mode
|
* Handle toggling insert mode
|
||||||
*
|
|
||||||
* <p>
|
* <p>
|
||||||
* In insert mode, characters at and to the right of the cursor are shifted right to make room
|
* 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
|
* 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
|
* Toggle blinking of the cursor
|
||||||
*
|
|
||||||
* <p>
|
* <p>
|
||||||
* Renderers should take care not to irritate the user. Some possibilities are to blink slowly,
|
* 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
|
* 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
|
* Switch to and from the alternate screen buffer, optionally clearing it
|
||||||
*
|
|
||||||
* <p>
|
* <p>
|
||||||
* This will never clear the normal buffer. If the buffer does not change as a result of this
|
* 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.
|
* call, then the alternate buffer is not cleared, even if clearAlt is specified.
|
||||||
@@ -1545,6 +1521,20 @@ public interface VtHandler {
|
|||||||
*/
|
*/
|
||||||
void handleBracketedPasteMode(boolean en);
|
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
|
* Handle a request to save the cursor position
|
||||||
*/
|
*/
|
||||||
@@ -1573,7 +1563,6 @@ public interface VtHandler {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle an absolute cursor row movement command
|
* Handle an absolute cursor row movement command
|
||||||
*
|
|
||||||
* <p>
|
* <p>
|
||||||
* The column should remain the same, i.e., do <em>not</em> reset the column to 0.
|
* 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
|
* Handle a request to save the terminal window's icon title
|
||||||
*
|
|
||||||
* <p>
|
* <p>
|
||||||
* "Icon titles" are a concept from the X Windows system. Do the closest equivalent, if anything
|
* "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.
|
* 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
|
* Handle a request to save the terminal window's title
|
||||||
*
|
|
||||||
* <p>
|
* <p>
|
||||||
* Window titles are fairly applicable to all desktop windowing systems. The current title is
|
* Window titles are fairly applicable to all desktop windowing systems. The current title is
|
||||||
* pushed to a stack of limited size.
|
* 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
|
* Handle a request to restore the terminal window's icon title
|
||||||
*
|
|
||||||
* <p>
|
* <p>
|
||||||
* The title is set to the one popped from the stack of saved window icon titles.
|
* 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
|
* Handle a request to restore the terminal window's title
|
||||||
*
|
|
||||||
* <p>
|
* <p>
|
||||||
* The title is set to the one popped from the stack of saved window titles.
|
* 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
|
* Insert n lines at and below the cursor
|
||||||
*
|
|
||||||
* <p>
|
* <p>
|
||||||
* Lines within the viewport are shifted down or deleted to make room for the new lines.
|
* 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
|
* Delete n lines at and below the cursor
|
||||||
*
|
|
||||||
* <p>
|
* <p>
|
||||||
* Lines within the viewport are shifted up, and new lines inserted at the bottom.
|
* 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.
|
* Set the range of rows (viewport) involved in scrolling.
|
||||||
*
|
|
||||||
* <p>
|
* <p>
|
||||||
* This applies not only to {@link #handleScrollUp()} and {@link #handleScrollDown()}, but also
|
* 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
|
* 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.
|
* Scroll the display n lines down, considering only those lines in the scrolling range.
|
||||||
*
|
|
||||||
* <p>
|
* <p>
|
||||||
* To be unambiguous, this of movement of the viewport. The viewport scrolls down, so the lines
|
* 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.
|
* 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.
|
* Scroll the lines n slots down, considering only those lines in the scrolling range.
|
||||||
*
|
|
||||||
* <p>
|
* <p>
|
||||||
* This is equivalent to scrolling the <em>viewport</em> n lines <em>up</em>. This method exists
|
* 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
|
* 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.
|
* Scroll the lines n slots up, considering only those lines in the scrolling range.
|
||||||
*
|
|
||||||
* <p>
|
* <p>
|
||||||
* The is equivalent to scrolling the <em>viewport</em> n lines <em>down</em>. This method
|
* 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,
|
* 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
|
* Handle a request to fully reset the terminal
|
||||||
*
|
|
||||||
* <p>
|
* <p>
|
||||||
* All buffers should be cleared and all state variables, positions, attributes, etc., should be
|
* All buffers should be cleared and all state variables, positions, attributes, etc., should be
|
||||||
* reset to their defaults.
|
* reset to their defaults.
|
||||||
|
|||||||
+114
-142
@@ -21,7 +21,6 @@ import java.io.InputStream;
|
|||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Set;
|
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import ghidra.util.Msg;
|
import ghidra.util.Msg;
|
||||||
@@ -122,34 +121,17 @@ public class AnsiBufferedInputStream extends InputStream {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
byte c = (byte) ci;
|
byte c = (byte) ci;
|
||||||
//printDebugChar(c);
|
// printDebugChar(c);
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case CHARS:
|
case CHARS -> processChars(c);
|
||||||
processChars(c);
|
case ESC -> processEsc(c);
|
||||||
break;
|
case CSI -> processCsi(c);
|
||||||
case ESC:
|
case CSI_p -> processCsiParamOrCommand(c);
|
||||||
processEsc(c);
|
case CSI_Q -> processCsiQ(c);
|
||||||
break;
|
case OSC -> processOsc(c);
|
||||||
case CSI:
|
case WINDOW_TITLE -> processWindowTitle(c);
|
||||||
processCsi(c);
|
case WINDOW_TITLE_ESC -> processWindowTitleEsc(c);
|
||||||
break;
|
default -> throw new AssertionError();
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
countIn++;
|
countIn++;
|
||||||
return c;
|
return c;
|
||||||
@@ -191,180 +173,151 @@ public class AnsiBufferedInputStream extends InputStream {
|
|||||||
|
|
||||||
protected void processChars(byte c) {
|
protected void processChars(byte c) {
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case 0x08:
|
default -> appendChar(c);
|
||||||
|
case '\b' -> {
|
||||||
if (lineBuf.get(lineBuf.position() - 1) == ' ') {
|
if (lineBuf.get(lineBuf.position() - 1) == ' ') {
|
||||||
lineBuf.position(lineBuf.position() - 1);
|
lineBuf.position(lineBuf.position() - 1);
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
case '\n':
|
case '\n' -> bakeLine();
|
||||||
//appendChar(c);
|
case '\r' -> lineBuf.position(0);
|
||||||
bakeLine();
|
case 0x1b -> mode = Mode.ESC;
|
||||||
break;
|
|
||||||
case 0x1b:
|
|
||||||
mode = Mode.ESC;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
appendChar(c);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void processEsc(byte c) {
|
protected void processEsc(byte c) {
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case '[':
|
case '[' -> mode = Mode.CSI;
|
||||||
mode = Mode.CSI;
|
case ']' -> mode = Mode.OSC;
|
||||||
break;
|
default -> throw new AssertionError("Saw 'ESC " + c + "' at " + countIn);
|
||||||
case ']':
|
|
||||||
mode = Mode.OSC;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new AssertionError("Saw 'ESC " + c + "' at " + countIn);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void processCsi(byte c) {
|
protected void processCsi(byte c) {
|
||||||
switch (c) {
|
switch (c) {
|
||||||
default:
|
default -> processCsiParamOrCommand(c);
|
||||||
processCsiParamOrCommand(c);
|
case '?' -> mode = Mode.CSI_Q;
|
||||||
break;
|
|
||||||
case '?':
|
|
||||||
mode = Mode.CSI_Q;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void processCsiParamOrCommand(byte c) {
|
protected void processCsiParamOrCommand(byte c) {
|
||||||
switch (c) {
|
switch (c) {
|
||||||
default:
|
default -> escBuf.put(c);
|
||||||
escBuf.put(c);
|
case 'A' -> {
|
||||||
break;
|
|
||||||
case 'A':
|
|
||||||
execCursorUp();
|
execCursorUp();
|
||||||
mode = Mode.CHARS;
|
mode = Mode.CHARS;
|
||||||
break;
|
}
|
||||||
case 'B':
|
case 'B' -> {
|
||||||
execCursorDown();
|
execCursorDown();
|
||||||
mode = Mode.CHARS;
|
mode = Mode.CHARS;
|
||||||
break;
|
}
|
||||||
case 'C':
|
case 'C' -> {
|
||||||
execCursorForward();
|
execCursorForward();
|
||||||
mode = Mode.CHARS;
|
mode = Mode.CHARS;
|
||||||
break;
|
}
|
||||||
case 'D':
|
case 'D' -> {
|
||||||
execCursorBackward();
|
execCursorBackward();
|
||||||
mode = Mode.CHARS;
|
mode = Mode.CHARS;
|
||||||
break;
|
}
|
||||||
case 'G':
|
case 'G' -> {
|
||||||
execCursorCharAbsolute();
|
execCursorCharAbsolute();
|
||||||
mode = Mode.CHARS;
|
mode = Mode.CHARS;
|
||||||
break;
|
}
|
||||||
case 'H':
|
case 'H' -> {
|
||||||
execCursorPosition();
|
execCursorPosition();
|
||||||
mode = Mode.CHARS;
|
mode = Mode.CHARS;
|
||||||
break;
|
}
|
||||||
case 'J':
|
case 'J' -> {
|
||||||
execEraseInDisplay();
|
execEraseInDisplay();
|
||||||
mode = Mode.CHARS;
|
mode = Mode.CHARS;
|
||||||
break;
|
}
|
||||||
case 'K':
|
case 'K' -> {
|
||||||
execEraseInLine();
|
execEraseInLine();
|
||||||
mode = Mode.CHARS;
|
mode = Mode.CHARS;
|
||||||
break;
|
}
|
||||||
case 'X':
|
case 'X' -> {
|
||||||
execEraseCharacter();
|
execEraseCharacter();
|
||||||
mode = Mode.CHARS;
|
mode = Mode.CHARS;
|
||||||
break;
|
}
|
||||||
case 'm':
|
case 'm' -> {
|
||||||
execSetGraphicsRendition();
|
execSetGraphicsRendition();
|
||||||
mode = Mode.CHARS;
|
mode = Mode.CHARS;
|
||||||
break;
|
}
|
||||||
case 'h':
|
case 'h' -> {
|
||||||
execPrivateSequence(true);
|
execPrivateSequence(true);
|
||||||
mode = Mode.CHARS;
|
mode = Mode.CHARS;
|
||||||
break;
|
}
|
||||||
case 'l':
|
case 'l' -> {
|
||||||
execPrivateSequence(false);
|
execPrivateSequence(false);
|
||||||
mode = Mode.CHARS;
|
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) {
|
protected void processCsiQ(byte c) {
|
||||||
String buf;
|
|
||||||
switch (c) {
|
switch (c) {
|
||||||
default:
|
default -> escBuf.put(c);
|
||||||
escBuf.put(c);
|
case 'h' -> {
|
||||||
break;
|
switch (readAndClearEscBuf()) {
|
||||||
case 'h':
|
case PRIV_12 -> execTextCursorEnableBlinking();
|
||||||
buf = readAndClearEscBuf();
|
case PRIV_25 -> execTextCursorEnableModeShow();
|
||||||
if ("12".equals(buf)) {
|
case PRIV_1004 -> execEnableFocusReport();
|
||||||
execTextCursorEnableBlinking();
|
case PRIV_2004 -> execEnableBracketedPasteMode();
|
||||||
escBuf.clear();
|
case PRIV_9001 -> execEnableWin32InputMode();
|
||||||
mode = Mode.CHARS;
|
case String buf -> throw new AssertionError("Got CsiQ(h): %s".formatted(buf));
|
||||||
}
|
}
|
||||||
else if ("25".equals(buf)) {
|
mode = Mode.CHARS;
|
||||||
execTextCursorEnableModeShow();
|
}
|
||||||
escBuf.clear();
|
case 'l' -> {
|
||||||
mode = Mode.CHARS;
|
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 {
|
mode = Mode.CHARS;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void processOsc(byte c) {
|
protected void processOsc(byte c) {
|
||||||
switch (c) {
|
switch (c) {
|
||||||
default:
|
default -> escBuf.put(c);
|
||||||
escBuf.put(c);
|
case ';' -> {
|
||||||
break;
|
switch (readAndClearEscBuf()) {
|
||||||
case ';':
|
case "0", "2" -> mode = Mode.WINDOW_TITLE;
|
||||||
if (Set.of("0", "2").contains(readAndClearEscBuf())) {
|
default -> throw new AssertionError();
|
||||||
mode = Mode.WINDOW_TITLE;
|
|
||||||
escBuf.clear();
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
throw new AssertionError();
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void processWindowTitle(byte c) {
|
protected void processWindowTitle(byte c) {
|
||||||
switch (c) {
|
switch (c) {
|
||||||
default:
|
default -> titleBuf.put(c);
|
||||||
titleBuf.put(c);
|
case 0x07 -> { // bell, even though MSDN says longer form preferred
|
||||||
break;
|
|
||||||
case 0x07: // bell, even though MSDN says longer form preferred
|
|
||||||
execSetWindowTitle();
|
execSetWindowTitle();
|
||||||
mode = Mode.CHARS;
|
mode = Mode.CHARS;
|
||||||
break;
|
}
|
||||||
case 0x1b:
|
case 0x1b -> mode = Mode.WINDOW_TITLE_ESC;
|
||||||
mode = Mode.WINDOW_TITLE_ESC;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void processWindowTitleEsc(byte c) {
|
protected void processWindowTitleEsc(byte c) {
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case '\\':
|
case '\\' -> {
|
||||||
execSetWindowTitle();
|
execSetWindowTitle();
|
||||||
mode = Mode.CHARS;
|
mode = Mode.CHARS;
|
||||||
break;
|
}
|
||||||
default:
|
default -> throw new AssertionError("Saw <ST> ... ESC " + c + " at " + countIn);
|
||||||
throw new AssertionError("Saw <ST> ... ESC " + c + " at " + countIn);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -459,6 +412,30 @@ public class AnsiBufferedInputStream extends InputStream {
|
|||||||
// Don't care
|
// 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() {
|
protected void execEraseInDisplay() {
|
||||||
// Because I have only one line, right?
|
// Because I have only one line, right?
|
||||||
execEraseInLine();
|
execEraseInLine();
|
||||||
@@ -466,15 +443,10 @@ public class AnsiBufferedInputStream extends InputStream {
|
|||||||
|
|
||||||
protected void execEraseInLine() {
|
protected void execEraseInLine() {
|
||||||
switch (parseNumericBuffer()) {
|
switch (parseNumericBuffer()) {
|
||||||
case 0:
|
case 0 -> Arrays.fill(lineBuf.array(), lineBuf.position(), lineBuf.capacity(),
|
||||||
Arrays.fill(lineBuf.array(), lineBuf.position(), lineBuf.capacity(), (byte) 0);
|
(byte) 0);
|
||||||
break;
|
case 1 -> Arrays.fill(lineBuf.array(), 0, lineBuf.position() + 1, (byte) 0);
|
||||||
case 1:
|
case 2 -> Arrays.fill(lineBuf.array(), (byte) 0);
|
||||||
Arrays.fill(lineBuf.array(), 0, lineBuf.position() + 1, (byte) 0);
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
Arrays.fill(lineBuf.array(), (byte) 0);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+23
-4
@@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package agent;
|
package agent;
|
||||||
|
|
||||||
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
@@ -47,6 +48,7 @@ import ghidra.framework.plugintool.util.*;
|
|||||||
import ghidra.pty.*;
|
import ghidra.pty.*;
|
||||||
import ghidra.pty.PtyChild.Echo;
|
import ghidra.pty.PtyChild.Echo;
|
||||||
import ghidra.pty.testutil.DummyProc;
|
import ghidra.pty.testutil.DummyProc;
|
||||||
|
import ghidra.pty.windows.AnsiBufferedInputStream;
|
||||||
import ghidra.trace.model.Trace;
|
import ghidra.trace.model.Trace;
|
||||||
import ghidra.trace.model.target.schema.PrimitiveTraceObjectSchema.MinimalSchemaContext;
|
import ghidra.trace.model.target.schema.PrimitiveTraceObjectSchema.MinimalSchemaContext;
|
||||||
import ghidra.trace.model.target.schema.TraceObjectSchema.SchemaName;
|
import ghidra.trace.model.target.schema.TraceObjectSchema.SchemaName;
|
||||||
@@ -107,11 +109,14 @@ public class TraceRmiPythonClientTest extends AbstractGhidraHeadedDebuggerTest {
|
|||||||
|
|
||||||
protected Path getPathToPython() {
|
protected Path getPathToPython() {
|
||||||
try {
|
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) {
|
catch (RuntimeException e) {
|
||||||
return Paths.get(DummyProc.which("python"));
|
|
||||||
}
|
}
|
||||||
|
return Paths.get(DummyProc.which("python"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
@@ -119,6 +124,7 @@ public class TraceRmiPythonClientTest extends AbstractGhidraHeadedDebuggerTest {
|
|||||||
traceRmi = addPlugin(tool, TraceRmiPlugin.class);
|
traceRmi = addPlugin(tool, TraceRmiPlugin.class);
|
||||||
|
|
||||||
pathToPython = getPathToPython();
|
pathToPython = getPathToPython();
|
||||||
|
Msg.info(this, "Using python: %s".formatted(pathToPython));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void addAllDebuggerPlugins() throws PluginException {
|
protected void addAllDebuggerPlugins() throws PluginException {
|
||||||
@@ -180,6 +186,14 @@ public class TraceRmiPythonClientTest extends AbstractGhidraHeadedDebuggerTest {
|
|||||||
protected ExecInPy execInPy(String script) throws IOException {
|
protected ExecInPy execInPy(String script) throws IOException {
|
||||||
Map<String, String> env = new HashMap<>(System.getenv());
|
Map<String, String> env = new HashMap<>(System.getenv());
|
||||||
setPythonPath(env);
|
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();
|
Pty pty = PtyFactory.local().openpty();
|
||||||
|
|
||||||
PtySession session =
|
PtySession session =
|
||||||
@@ -187,7 +201,7 @@ public class TraceRmiPythonClientTest extends AbstractGhidraHeadedDebuggerTest {
|
|||||||
|
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
new Thread(() -> {
|
new Thread(() -> {
|
||||||
InputStream is = pty.getParent().getInputStream();
|
InputStream is = new AnsiBufferedInputStream(pty.getParent().getInputStream());
|
||||||
byte[] buf = new byte[1024];
|
byte[] buf = new byte[1024];
|
||||||
while (true) {
|
while (true) {
|
||||||
try {
|
try {
|
||||||
@@ -203,7 +217,12 @@ public class TraceRmiPythonClientTest extends AbstractGhidraHeadedDebuggerTest {
|
|||||||
}).start();
|
}).start();
|
||||||
|
|
||||||
PrintWriter stdin = new PrintWriter(pty.getParent().getOutputStream());
|
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();
|
stdin.flush();
|
||||||
return new ExecInPy(session, stdin, CompletableFuture.supplyAsync(() -> {
|
return new ExecInPy(session, stdin, CompletableFuture.supplyAsync(() -> {
|
||||||
try {
|
try {
|
||||||
|
|||||||
Reference in New Issue
Block a user