| 1 | module preview_ops |
| 2 | use filesystem_ops, only: MAX_PATH, join_path |
| 3 | implicit none |
| 4 | private |
| 5 | |
| 6 | public :: get_file_preview, check_bat_available |
| 7 | public :: MAX_PREVIEW_LINES |
| 8 | |
| 9 | integer, parameter :: MAX_PREVIEW_LINES = 100 |
| 10 | logical, save :: bat_available = .false. |
| 11 | logical, save :: bat_checked = .false. |
| 12 | |
| 13 | contains |
| 14 | |
| 15 | function check_bat_available() result(has_bat) |
| 16 | logical :: has_bat |
| 17 | integer :: stat |
| 18 | |
| 19 | if (.not. bat_checked) then |
| 20 | call execute_command_line("which bat > /dev/null 2>&1", exitstat=stat, wait=.true.) |
| 21 | bat_available = (stat == 0) |
| 22 | bat_checked = .true. |
| 23 | end if |
| 24 | has_bat = bat_available |
| 25 | end function check_bat_available |
| 26 | |
| 27 | subroutine get_file_preview(filepath, is_dir, lines, line_count, max_lines) |
| 28 | character(len=*), intent(in) :: filepath |
| 29 | logical, intent(in) :: is_dir |
| 30 | character(len=*), dimension(:), intent(out) :: lines |
| 31 | integer, intent(out) :: line_count |
| 32 | integer, intent(in) :: max_lines |
| 33 | character(len=MAX_PATH) :: temp_file, cmd |
| 34 | integer :: unit, ios, i |
| 35 | logical :: has_bat |
| 36 | |
| 37 | line_count = 0 |
| 38 | |
| 39 | ! Skip special files |
| 40 | if (trim(filepath) == "." .or. trim(filepath) == "..") then |
| 41 | lines(1) = "(special directory)" |
| 42 | line_count = 1 |
| 43 | return |
| 44 | end if |
| 45 | |
| 46 | ! Create temp file |
| 47 | call get_environment_variable("HOME", temp_file) |
| 48 | temp_file = trim(temp_file) // "/.fortress_preview" |
| 49 | |
| 50 | if (is_dir) then |
| 51 | ! Preview directory with ls |
| 52 | write(cmd, '(a,a,a,i0,a,a)') "ls -lh '", trim(filepath), "' 2>/dev/null | head -", & |
| 53 | max_lines, " > ", trim(temp_file) |
| 54 | else |
| 55 | ! Check if file is likely binary |
| 56 | call execute_command_line("file -b '" // trim(filepath) // "' | grep -q text", exitstat=ios, wait=.true.) |
| 57 | |
| 58 | if (ios == 0) then |
| 59 | ! Text file - use bat or cat |
| 60 | has_bat = check_bat_available() |
| 61 | if (has_bat) then |
| 62 | ! Use bat with syntax highlighting, no line numbers, limited lines |
| 63 | write(cmd, '(a,i0,a,a,a,a)') "bat --style=plain --color=always --line-range :", & |
| 64 | max_lines, " '", trim(filepath), "' 2>/dev/null > ", trim(temp_file) |
| 65 | else |
| 66 | ! Fallback to cat with head |
| 67 | write(cmd, '(a,a,a,i0,a,a)') "cat '", trim(filepath), "' 2>/dev/null | head -", & |
| 68 | max_lines, " > ", trim(temp_file) |
| 69 | end if |
| 70 | else |
| 71 | ! Binary file - show file type info |
| 72 | cmd = "file -b '" // trim(filepath) // "' > " // trim(temp_file) |
| 73 | end if |
| 74 | end if |
| 75 | |
| 76 | ! Execute preview command |
| 77 | call execute_command_line(trim(cmd), wait=.true.) |
| 78 | |
| 79 | ! Read preview into lines array |
| 80 | open(newunit=unit, file=temp_file, status='old', iostat=ios) |
| 81 | if (ios == 0) then |
| 82 | do i = 1, max_lines |
| 83 | read(unit, '(a)', iostat=ios) lines(i) |
| 84 | if (ios /= 0) exit |
| 85 | line_count = line_count + 1 |
| 86 | end do |
| 87 | close(unit) |
| 88 | end if |
| 89 | |
| 90 | ! Cleanup |
| 91 | call execute_command_line("rm -f " // trim(temp_file) // " 2>/dev/null") |
| 92 | |
| 93 | ! Handle empty result |
| 94 | if (line_count == 0) then |
| 95 | lines(1) = "(empty or unreadable)" |
| 96 | line_count = 1 |
| 97 | end if |
| 98 | end subroutine get_file_preview |
| 99 | |
| 100 | end module preview_ops |
| 101 |