paginated-output.scm 3.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889
  1. (library (paginated-output)
  2. (export output-paginated)
  3. (import (except (rnrs base) let-values)
  4. (only (guile)
  5. ;; lambdas
  6. lambda* λ
  7. ;; control structures
  8. when
  9. ;; display
  10. display
  11. simple-format
  12. ;; strings
  13. string-join
  14. string-append
  15. string-tokenize
  16. ;; characters
  17. char-set
  18. char-set-complement
  19. ;; environment variables
  20. getenv
  21. ;; path stuff
  22. search-path)
  23. ;; pipes
  24. (ice-9 popen))
  25. ;; `path-as-string->list`'s logic is copied from GNU
  26. ;; Guix. Some comments added. See:
  27. ;; https://git.savannah.gnu.org/cgit/guix.git/tree/guix/build/utils.scm?id=c0bc08d82c73e464a419f213d5ae5545bc67e2bf#n573.
  28. (define path-as-string->list
  29. (lambda* (path #:optional (separator #\:))
  30. (if separator
  31. (string-tokenize path
  32. ;; Match everything except the
  33. ;; separator.
  34. (char-set-complement
  35. (char-set separator)))
  36. ;; Otherwise simply return a list containing the
  37. ;; path to be sure to always return a list.
  38. (list path))))
  39. ;; `find-executable-on-path` is adapted from GNU Guix's
  40. ;; `which` procedure. See:
  41. ;; https://git.savannah.gnu.org/cgit/guix.git/tree/guix/build/utils.scm?id=c0bc08d82c73e464a419f213d5ae5545bc67e2bf#n617
  42. (define (find-executable-on-path executable)
  43. "Return the complete file name for EXECUTABLE as found in
  44. ${PATH}, or #f if EXECUTABLE could not be found."
  45. ;; search-path is a procedure defined in GNU Guile
  46. (search-path
  47. ;; Check the PATH for the executable.
  48. (path-as-string->list (getenv "PATH"))
  49. executable))
  50. (define find-pager
  51. (λ ()
  52. (or (getenv "PAGER")
  53. (find-executable-on-path "more")
  54. (find-executable-on-path "less"))))
  55. ;;; Now onto the actual matter of using open-pipe ...
  56. (define open-output-pipe*
  57. (λ (command . args)
  58. (open-output-pipe
  59. (string-join (cons command args) " "))))
  60. (define output-paginated
  61. (lambda* (message #:optional (lines-per-page #f))
  62. (let* ([pager-args
  63. (if lines-per-page
  64. ;; Here we assume, that the pager will support
  65. ;; an argument "-<number>". This might not always be
  66. ;; true.
  67. (list (string-append "-" (number->string lines-per-page)))
  68. '())]
  69. [pager-pipe
  70. ;; Execute the pager command in a subprocess
  71. ;; with its arguments and return an output
  72. ;; pipe to the pager.
  73. (apply open-output-pipe* (find-pager) pager-args)])
  74. (display (simple-format #f "~a\n" message)
  75. pager-pipe)
  76. ;; Ultimately close pipe after being done with
  77. ;; writing to it.
  78. (close-pipe pager-pipe)))))