@@ -4,7 +4,8 @@ |
| 4 | 4 | ! ============================================================================== |
| 5 | 5 | module better_errors |
| 6 | 6 | use iso_fortran_env, only: error_unit |
| 7 | | - use system_interface, only: get_environment_var |
| 7 | + use system_interface, only: get_environment_var, c_isatty |
| 8 | + use iso_c_binding, only: c_int |
| 8 | 9 | implicit none |
| 9 | 10 | private |
| 10 | 11 | |
@@ -31,19 +32,34 @@ contains |
| 31 | 32 | character(len=*), intent(in) :: command |
| 32 | 33 | character(len=:), allocatable :: suggestions(:) |
| 33 | 34 | integer :: num_suggestions, i |
| 35 | + character(len=10) :: shell_name |
| 34 | 36 | |
| 35 | | - ! Print main error message in red |
| 36 | | - write(error_unit, '(a,a,a,a)') & |
| 37 | | - color_code(COLOR_RED), & |
| 38 | | - "fortsh: Unknown command '", trim(command), "'" |
| 39 | | - write(error_unit, '(a)') color_code(COLOR_RESET) |
| 37 | + ! Use "sh" for POSIX compliance in non-interactive mode |
| 38 | + if (stderr_is_tty()) then |
| 39 | + shell_name = "fortsh" |
| 40 | + else |
| 41 | + shell_name = "sh" |
| 42 | + end if |
| 43 | + |
| 44 | + ! Print main error message in red (POSIX format) |
| 45 | + write(error_unit, '(a,a,a,a,a)') & |
| 46 | + trim(color_code(COLOR_RED)), & |
| 47 | + trim(shell_name), ": line 1: ", trim(command), ": command not found" |
| 48 | + |
| 49 | + ! Only write color reset if using colors |
| 50 | + if (stderr_is_tty()) then |
| 51 | + write(error_unit, '(a)') trim(color_code(COLOR_RESET)) |
| 52 | + end if |
| 53 | + |
| 54 | + ! Only show suggestions if stderr is a TTY (interactive mode) |
| 55 | + if (.not. stderr_is_tty()) return |
| 40 | 56 | |
| 41 | 57 | ! Try to find similar commands |
| 42 | 58 | call suggest_similar_commands(command, suggestions, num_suggestions) |
| 43 | 59 | |
| 44 | 60 | if (num_suggestions > 0) then |
| 45 | 61 | ! Print suggestions |
| 46 | | - write(error_unit, '(a)', advance='no') color_code(COLOR_CYAN) |
| 62 | + write(error_unit, '(a)', advance='no') trim(color_code(COLOR_CYAN)) |
| 47 | 63 | write(error_unit, '(a)', advance='no') "Did you mean" |
| 48 | 64 | |
| 49 | 65 | if (num_suggestions == 1) then |
@@ -54,12 +70,12 @@ contains |
| 54 | 70 | write(error_unit, '(a)') ":" |
| 55 | 71 | do i = 1, num_suggestions |
| 56 | 72 | write(error_unit, '(a)', advance='no') " " |
| 57 | | - write(error_unit, '(a)', advance='no') color_code(COLOR_GREEN) |
| 73 | + write(error_unit, '(a)', advance='no') trim(color_code(COLOR_GREEN)) |
| 58 | 74 | write(error_unit, '(a)', advance='no') trim(suggestions(i)) |
| 59 | | - write(error_unit, '(a)') color_code(COLOR_CYAN) |
| 75 | + write(error_unit, '(a)') trim(color_code(COLOR_CYAN)) |
| 60 | 76 | end do |
| 61 | 77 | end if |
| 62 | | - write(error_unit, '(a)') color_code(COLOR_RESET) |
| 78 | + write(error_unit, '(a)') trim(color_code(COLOR_RESET)) |
| 63 | 79 | end if |
| 64 | 80 | |
| 65 | 81 | ! Cleanup |
@@ -279,11 +295,28 @@ contains |
| 279 | 295 | deallocate(matrix) |
| 280 | 296 | end function |
| 281 | 297 | |
| 298 | + ! Check if stderr is a TTY (terminal) |
| 299 | + function stderr_is_tty() result(is_tty) |
| 300 | + logical :: is_tty |
| 301 | + integer(c_int) :: result |
| 302 | + |
| 303 | + ! error_unit is typically 0 or 2 depending on implementation |
| 304 | + ! Standard error is file descriptor 2 |
| 305 | + result = c_isatty(int(2, c_int)) |
| 306 | + is_tty = (result /= 0) |
| 307 | + end function |
| 308 | + |
| 282 | 309 | ! Generate ANSI color code |
| 283 | 310 | function color_code(color) result(code) |
| 284 | 311 | integer, intent(in) :: color |
| 285 | 312 | character(len=16) :: code |
| 286 | 313 | |
| 314 | + ! Only use colors if stderr is a TTY |
| 315 | + if (.not. stderr_is_tty()) then |
| 316 | + code = '' |
| 317 | + return |
| 318 | + end if |
| 319 | + |
| 287 | 320 | if (color == COLOR_RESET) then |
| 288 | 321 | code = char(27) // '[0m' |
| 289 | 322 | else |