Skip to content

Commit 1578ea9

Browse files
h-eastclaude
authored andcommitted
patch 9.2.0245: xxd: color output detection is broken
Problem: xxd: color output detection is broken (Juergen Weigert) Solution: Fix the issues (Hirohito Higashi) - Disable auto color when output goes to a file (two-argument form) - Check TERM variable: disable color when unset, empty or "dumb" - Add color_forced flag to preserve -R always behavior - Add tests for the new behavior fixes: #19790 closes: #19813 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Hirohito Higashi <h.east.727@gmail.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
1 parent e2cf84d commit 1578ea9

3 files changed

Lines changed: 84 additions & 2 deletions

File tree

src/testdir/test_xxd.vim

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -758,4 +758,68 @@ func Test_xxd_null_dereference()
758758
bw!
759759
endfunc
760760

761+
func Test_xxd_color_outfile_no_color()
762+
CheckUnix
763+
764+
" When output goes to a file (two-argument form), auto color should be
765+
" disabled and produce the same result as redirect to file.
766+
let input = 'Xxd_color_input'
767+
let outfile = 'Xxd_color_outfile'
768+
let outredir = 'Xxd_color_outredir'
769+
770+
call writefile([repeat('A', 100)], input)
771+
772+
" Two-argument form: xxd infile outfile
773+
silent exe '!' . s:xxd_cmd . ' ' . input . ' ' . outfile
774+
775+
" Redirect form: xxd infile > outfile
776+
silent exe '!' . s:xxd_cmd . ' ' . input . ' > ' . outredir
777+
778+
call assert_equal(readfile(outredir), readfile(outfile))
779+
780+
call delete(input)
781+
call delete(outfile)
782+
call delete(outredir)
783+
endfunc
784+
785+
func Test_xxd_color_term_dumb()
786+
CheckUnix
787+
788+
" When TERM is 'dumb', auto color should be disabled.
789+
let input = 'Xxd_term_input'
790+
let outfile = 'Xxd_term_outfile'
791+
792+
call writefile([repeat('A', 100)], input)
793+
794+
silent exe '!' . 'TERM=dumb ' . s:xxd_cmd . ' ' . input . ' > ' . outfile
795+
let result = readfile(outfile)
796+
" Output should not contain escape sequences
797+
for line in result
798+
call assert_equal(-1, stridx(line, "\e"), 'Unexpected color escape in: ' . line)
799+
endfor
800+
801+
call delete(input)
802+
call delete(outfile)
803+
endfunc
804+
805+
func Test_xxd_color_term_unset()
806+
CheckUnix
807+
808+
" When TERM is not set, auto color should be disabled.
809+
let input = 'Xxd_unset_input'
810+
let outfile = 'Xxd_unset_outfile'
811+
812+
call writefile([repeat('A', 100)], input)
813+
814+
silent exe '!' . 'env -u TERM ' . s:xxd_cmd . ' ' . input . ' > ' . outfile
815+
let result = readfile(outfile)
816+
" Output should not contain escape sequences
817+
for line in result
818+
call assert_equal(-1, stridx(line, "\e"), 'Unexpected color escape in: ' . line)
819+
endfor
820+
821+
call delete(input)
822+
call delete(outfile)
823+
endfunc
824+
761825
" vim: shiftwidth=2 sts=2 expandtab

src/version.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -734,6 +734,8 @@ static char *(features[]) =
734734

735735
static int included_patches[] =
736736
{ /* Add new patch number below this line */
737+
/**/
738+
245,
737739
/**/
738740
244,
739741
/**/

src/xxd/xxd.c

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@
7373
* 24.08.2025 avoid NULL dereference with autoskip colorless
7474
* 26.11.2025 update indent in exit_with_usage()
7575
* 19.03.2026 Add -t option to end output with terminating null
76+
* 25.03.2026 Fix color output issues
7677
*
7778
* (c) 1990-1998 by Juergen Weigert (jnweiger@gmail.com)
7879
*
@@ -153,7 +154,7 @@ extern void perror __P((char *));
153154
# endif
154155
#endif
155156

156-
char version[] = "xxd 2026-03-19 by Juergen Weigert et al.";
157+
char version[] = "xxd 2026-03-25 by Juergen Weigert et al.";
157158
#ifdef WIN32
158159
char osver[] = " (Win32)";
159160
#else
@@ -723,7 +724,16 @@ enable_color(void)
723724
mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
724725
return (int)SetConsoleMode(out, mode);
725726
#elif defined(UNIX)
726-
return isatty(STDOUT_FILENO);
727+
char *term;
728+
729+
if (!isatty(STDOUT_FILENO))
730+
return 0;
731+
732+
term = getenv("TERM");
733+
if (term == NULL || *term == '\0' || !strcmp(term, "dumb"))
734+
return 0;
735+
736+
return 1;
727737
#else
728738
return 0;
729739
#endif
@@ -748,6 +758,7 @@ main(int argc, char *argv[])
748758
char *varname = NULL;
749759
int addrlen = 9;
750760
int color = 0;
761+
int color_forced = 0; /* set when -R always is used */
751762
char *no_color;
752763
char cur_color = 0;
753764

@@ -920,6 +931,7 @@ main(int argc, char *argv[])
920931
{
921932
(void)enable_color();
922933
color = 1;
934+
color_forced = 1;
923935
}
924936
else if (!STRNCMP(pw, "never", 5))
925937
color = 0;
@@ -1018,6 +1030,10 @@ main(int argc, char *argv[])
10181030
return 3;
10191031
}
10201032
rewind(fpo);
1033+
1034+
/* Disable auto color when writing to a file. */
1035+
if (!color_forced)
1036+
color = 0;
10211037
}
10221038

10231039
if (revert)

0 commit comments

Comments
 (0)