Skip to content

Instantly share code, notes, and snippets.

@TruncatedDinoSour
Created March 5, 2023 13:35
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save TruncatedDinoSour/e2034cf470f268596235a5c88ffcd048 to your computer and use it in GitHub Desktop.
Save TruncatedDinoSour/e2034cf470f268596235a5c88ffcd048 to your computer and use it in GitHub Desktop.
concept for GNU BASH syntax highlighing in python using linux procFS by Ari Archer
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""concept for GNU BASH syntax highlighing in python
author : Ari Archer <ari.web.xyz@gmail.com> / <https://ari-web.xyz/>
license : GPLv3
this concept works ... almost, theres a problem, we need to somehow
take ownership of the stdin fd, but we cannot, the reason we need to take
ownership or priority over that fd is to make sure were the only ones reading
it and not giving bash a chance to steal a byte, currently bash sometimes,
especially if youre writing fast, steals a read() from us meaning we miss a byte.
this script use the linux procFS
theres also an issue with ansi, but its not as important as of now, the script is
also quite messy, but i dont have the motivation to fix it rn, so deal with it
if you feel like it -- comment with ideas
stuff i tried so far :
- using LD_PRELOAD to overwrite the read() syscall
- making always return 0
- redirecting it to a FIFO
- closing it
- using os.write / read
- using C++
- using C"""
import distutils.spawn
import os
import signal
import sys
from warnings import filterwarnings as filter_warnings
FDS: list[int] = []
def open_fd(fd: int, mode: int) -> int:
nfd: int = os.open(
f"/proc/{sys.argv[1]}/fd/{fd}", os.O_RDWR | os.O_CREAT, mode=mode
)
FDS.append(nfd)
return nfd
def cleanup_fds() -> None:
for fd in FDS:
os.close(fd)
def interpret_backspace(string: str) -> str:
new_string: str = ""
for char in string:
if char == "\x7f":
new_string = new_string[:-1]
else:
new_string += char
return new_string
def highlight_line(line: str) -> str:
if not line:
return ""
line_cmd: str = line.split(maxsplit=1)[0]
return f"\033[{31 + bool(distutils.spawn.find_executable(line_cmd))}m{line_cmd}\033[0m{line.removeprefix(line_cmd)}"
def logread(fd: int) -> bytes:
print("reading ...")
return os.read(fd, 1)
def main() -> int:
"""entry / main function"""
if len(sys.argv) < 2:
print(
"no target bash PID supplied, you can get it by running `echo $$` in your target bash shell instance"
)
return 1
stdin, stderr = open_fd(0, 0o400), open_fd(2, 0o200)
out = open(f"/tmp/{sys.argv[1]}.bash", "w", buffering=1)
out.write("")
out.flush()
while os.fstat(stdin):
s: bytes = b""
c: bytes = b""
os.write(stderr, b"\033[s")
while (c := logread(stdin)) not in b"\r\n":
print(f"{c = }; {s = }")
s = interpret_backspace((s + c).decode()).encode()
os.write(stderr, ("\033[u\033[K" + highlight_line(s.decode())).encode())
print("newline", repr(c))
os.write(stderr, b"\n")
if s:
out.seek(0)
out.write(s.decode())
out.flush()
print("running", s)
os.kill(int(sys.argv[1]), signal.SIGINT)
cleanup_fds()
out.close()
return 0
if __name__ == "__main__":
assert main.__annotations__.get("return") is int, "main() should return an integer"
filter_warnings("error", category=Warning)
raise SystemExit(main())
@TruncatedDinoSour
Copy link
Author

  • use export PROMPT_COMMAND="source /tmp/$$.bash" if youre using this, if you alrd have it set -- export PROMPT_COMMAND="$PROMPT_COMMAND; source /tmp/$$.bash"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment