Skip to content

Instantly share code, notes, and snippets.

@pgaskin
Last active December 31, 2021 06:54
Show Gist options
  • Select an option

  • Save pgaskin/4c1a7ff8e0f752d0d1cf262f08afbd7c to your computer and use it in GitHub Desktop.

Select an option

Save pgaskin/4c1a7ff8e0f752d0d1cf262f08afbd7c to your computer and use it in GitHub Desktop.
Go function to intercept escape sequences to set the terminal title.
package main
// interceptsl intercepts escape sequences to set the terminal title.
func interceptsl(ctx context.Context, w io.Writer, r io.Reader, title chan<- string) error {
state := 0
ibuf := make([]byte, 256)
tbuf := make([]byte, 256)
obuf := make([]byte, 256) // will use up to cap(ibuf) + cap(tbuf) + 4 in certain situations
for {
select {
case <-ctx.Done():
return ctx.Err()
default:
}
// note: Go will handle EAGAIN, EINTR, and polling internally while reading files
// note: for a pty, VMIN=0 and VTIME=1 seems to work well (EAGAIN will be returned when nothing is available)
// note: for writes, we don't need to check the count since Go io.Writers are supposed to always do full writes
n, err := r.Read(ibuf)
if err != nil {
return err
} else if n == 0 {
continue
}
// fast path when no escape sequences in the buffer
if state == 0 {
for _, c := range ibuf[:n] {
if c == 0x1B {
goto slowpath
}
}
_, _ = w.Write(ibuf[:n])
continue
}
slowpath:
obuf = obuf[:0]
for _, c := range ibuf[:n] {
switch state {
case 0: // normal output
switch c {
default:
state = 0
obuf = append(obuf, c)
case 0x1B:
state = 1
}
case 1: // at \x1B
switch c {
default:
state = 0
obuf = append(obuf, 0x1B, c)
case ']':
state = 2
}
case 2: // at \x1B]
switch c {
default:
state = 0
obuf = append(obuf, 0x1B, ']', c)
case '0':
state = 3
}
case 3: // at \x1B]0
switch c {
default:
state = 0
obuf = append(obuf, 0x1B, ']', '0', c)
case ';':
state = 4
tbuf = tbuf[:0]
}
case 4: // in \x1B]0;
switch c {
default:
// next title char
if len(tbuf) < cap(tbuf) {
tbuf = append(tbuf, c)
break // out of the switch
}
fallthrough
case 0x07:
// end of title || overflow
select {
case title <- string(tbuf):
default:
}
if len(tbuf) == cap(tbuf) {
state = 5
} else {
state = 0
}
tbuf = tbuf[:0]
case 0x1B:
// start of a new escape sequence (this shouldn't happen)
state = 1
}
case 5: // in title
switch c {
default:
// overflowing title character
case 0x07:
// end of the overflowing title
state = 0
case 0x1B:
// start of a new escape sequence (this shouldn't happen)
state = 1
}
}
}
_, _ = w.Write(obuf)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment