Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/test-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ jobs:
- name: run tests
timeout-minutes: 2
run: |
make prove
make prove-j2
build-static:
runs-on: ubuntu-latest
container: alpine:latest
Expand Down Expand Up @@ -89,7 +89,7 @@ jobs:
- name: run tests [with iTerm2 palettes]
timeout-minutes: 1
run: |
prove -v tests/*.sh
prove -v -j 2 tests/*.sh
- name: static compilation [no iTerm2 palettes] with mingw32-w64
timeout-minutes: 2
run: |
Expand Down Expand Up @@ -154,7 +154,7 @@ jobs:
- name: run tests
timeout-minutes: 2
run: |
make prove
make prove-j2
- name: "Compress for ${{matrix.arch}} [with iTerm2 palettes]"
timeout-minutes: 1
run: |
Expand Down
4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -93,3 +93,7 @@ run-tests:
.PHONY: prove
prove:
@prove tests/*.sh

.PHONY: prove-j2
prove-j2:
@prove -j 2 tests/*.sh
95 changes: 95 additions & 0 deletions src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,8 @@ just_strip_it(bool ignore_sgr_errors)
STATE_TEXT,
STATE_GOT_ESC,
STATE_GOT_ESC_BRACKET,
STATE_GOT_ESC_BRACKET_EQUALS,
STATE_GOT_ESC_BRACKET_QUESTION,
} state = STATE_TEXT;

char buffer[INPUT_BUFFER_SIZE];
Expand Down Expand Up @@ -269,6 +271,19 @@ just_strip_it(bool ignore_sgr_errors)
// Ignore "Erase in Line" sequence.
state = STATE_TEXT;
}
else if (c == 'J')
{
// Ignore "Erase in Display" sequence.
state = STATE_TEXT;
}
else if (c == '=')
{
state = STATE_GOT_ESC_BRACKET_EQUALS;
}
else if (c == '?')
{
state = STATE_GOT_ESC_BRACKET_QUESTION;
}
else
{
if (ignore_sgr_errors)
Expand All @@ -282,6 +297,42 @@ just_strip_it(bool ignore_sgr_errors)
);
}
}
else if (state == STATE_GOT_ESC_BRACKET_EQUALS)
{
// Read numbers until "h" or "l", ignore everything in between.
if (c == 'h' || c == 'l')
state = STATE_TEXT;
else if (c < '0' || c > '9')
{
if (ignore_sgr_errors)
state = STATE_TEXT;
else
ERROR(
"SGR ]= sequence contains invalid character "
"'%c' at %zu characters read / %zu in SGR sequence "
"which begun at %zu characters read.\n",
c, read, sgr_chars_len, begun_at
);
}
}
else if (state == STATE_GOT_ESC_BRACKET_QUESTION)
{
// Read numbers until "h" or "l", ignore everything in between.
if (c == 'h' || c == 'l')
state = STATE_TEXT;
else if (c < '0' || c > '9')
{
if (ignore_sgr_errors)
state = STATE_TEXT;
else
ERROR(
"SGR ]? sequence contains invalid character "
"'%c' at %zu characters read / %zu in SGR sequence "
"which begun at %zu characters read.\n",
c, read, sgr_chars_len, begun_at
);
}
}
else
{
ERROR("Invalid state %d.\n", (int)state);
Expand Down Expand Up @@ -328,6 +379,8 @@ static inline __attribute__((always_inline)) void ansi2html(
STATE_TEXT,
STATE_GOT_ESC,
STATE_GOT_ESC_BRACKET,
STATE_GOT_ESC_BRACKET_EQUALS,
STATE_GOT_ESC_BRACKET_QUESTION,
} state = STATE_TEXT;

char buffer[INPUT_BUFFER_SIZE];
Expand Down Expand Up @@ -534,6 +587,24 @@ static inline __attribute__((always_inline)) void ansi2html(
span_outputted = true;
}
}
else if (c == 'J')
{
// Ignore "Erase in Display" sequence.
state = STATE_TEXT;
if (span && !span_outputted)
{
append_to_buffer(span);
span_outputted = true;
}
}
else if (c == '=')
{
state = STATE_GOT_ESC_BRACKET_EQUALS;
}
else if (c == '?')
{
state = STATE_GOT_ESC_BRACKET_QUESTION;
}
else
{
// The character should be a digit, or semicolon.
Expand Down Expand Up @@ -588,6 +659,30 @@ static inline __attribute__((always_inline)) void ansi2html(
);
}
break;
case STATE_GOT_ESC_BRACKET_EQUALS:
// Read numbers until "h" or "l", ignore everything in between.
if (c == 'h' || c == 'l')
state = STATE_TEXT;
else if (c < '0' || c > '9')
ERROR(
"SGR ]= sequence contains invalid character "
"'%c' at %zu characters read / %zu in SGR sequence "
"which begun at %zu characters read.\n",
c, read, sgr_chars_len, begun_at
);
break;
case STATE_GOT_ESC_BRACKET_QUESTION:
// Read numbers until "h" or "l", ignore everything in between.
if (c == 'h' || c == 'l')
state = STATE_TEXT;
else if (c < '0' || c > '9')
ERROR(
"SGR ]? sequence contains invalid character "
"'%c' at %zu characters read / %zu in SGR sequence "
"which begun at %zu characters read.\n",
c, read, sgr_chars_len, begun_at
);
break;
}
}
} while (1);
Expand Down
21 changes: 21 additions & 0 deletions tests/csi-equals.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#!/bin/bash

set -e

. "$(dirname "$0")/".functions.sh

# CSI = 0 ... 19 h and CSI = ... l are no-op
for l in h l; do
for k in $(seq 0 19); do
str=$(printf 'Hello, \e[0;1mBold\e[=%s%s\e[0m world!' "$k" "$l")
want=$'Hello, <span style="font-weight:bold;">Bold</span> world!'
got=$(printf '%s' "$str" | ./ansi2html -p vga)
str_eq_html "$str" "$want" "$got"

want=$'Hello, Bold world!'
got=$(printf '%s' "$str" | ./ansi2html -S)
str_eq_html "$str" "$want" "$got"
done
done

done_testing
29 changes: 29 additions & 0 deletions tests/csi-j.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#!/bin/bash

set -e

. "$(dirname "$0")/".functions.sh

# CSI J is a no-op when stripping OR converting to HTML
str=$'Hello, \e[0;1mBold\e[J\e[0m world!'
want=$'Hello, <span style="font-weight:bold;">Bold</span> world!'
got=$(printf '%s' "$str" | ./ansi2html -p vga)
str_eq_html "$str" "$want" "$got"

want=$'Hello, Bold world!'
got=$(printf '%s' "$str" | ./ansi2html -S)
str_eq_html "$str" "$want" "$got"

# CSI 0 J is also a no-op, same for CSI 1 J, CSI 2 J and CSI 3 J
for k in 0 1 2 3; do
str=$(printf 'Hello, \e[0;1mBold\e[%sJ\e[0m world!' "$k")
want=$'Hello, <span style="font-weight:bold;">Bold</span> world!'
got=$(printf '%s' "$str" | ./ansi2html -p vga)
str_eq_html "$str" "$want" "$got"

want=$'Hello, Bold world!'
got=$(printf '%s' "$str" | ./ansi2html -S)
str_eq_html "$str" "$want" "$got"
done

done_testing
12 changes: 12 additions & 0 deletions tests/csi-k.sh
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,16 @@ want=$'Hello, Bold world!'
got=$(printf '%s' "$str" | ./ansi2html -S)
str_eq_html "$str" "$want" "$got"

# CSI 0 K is also a no-op, same for CSI 1 K and CSI 2 K
for k in 0 1 2; do
str=$(printf 'Hello, \e[0;1mBold\e[%sK\e[0m world!' "$k")
want=$'Hello, <span style="font-weight:bold;">Bold</span> world!'
got=$(printf '%s' "$str" | ./ansi2html -p vga)
str_eq_html "$str" "$want" "$got"

want=$'Hello, Bold world!'
got=$(printf '%s' "$str" | ./ansi2html -S)
str_eq_html "$str" "$want" "$got"
done

done_testing
21 changes: 21 additions & 0 deletions tests/csi-question.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#!/bin/bash

set -e

. "$(dirname "$0")/".functions.sh

# CSI ? ... h and CSI ? ... l are no-op
for l in h l; do
for k in 25 47 1049; do
str=$(printf 'Hello, \e[0;1mBold\e[?%s%s\e[0m world!' "$k" "$l")
want=$'Hello, <span style="font-weight:bold;">Bold</span> world!'
got=$(printf '%s' "$str" | ./ansi2html -p vga)
str_eq_html "$str" "$want" "$got"

want=$'Hello, Bold world!'
got=$(printf '%s' "$str" | ./ansi2html -S)
str_eq_html "$str" "$want" "$got"
done
done

done_testing