output.bash 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. #
  2. # output.bash
  3. # -----------
  4. #
  5. # Private functions implementing output formatting. Used by public
  6. # helper functions.
  7. #
  8. # Print a message to the standard error. When no parameters are
  9. # specified, the message is read from the standard input.
  10. #
  11. # Globals:
  12. # none
  13. # Arguments:
  14. # $@ - [=STDIN] message
  15. # Returns:
  16. # none
  17. # Inputs:
  18. # STDIN - [=$@] message
  19. # Outputs:
  20. # STDERR - message
  21. batslib_err() {
  22. { if (( $# > 0 )); then
  23. echo "$@"
  24. else
  25. cat -
  26. fi
  27. } >&2
  28. }
  29. # Count the number of lines in the given string.
  30. #
  31. # TODO(ztombol): Fix tests and remove this note after #93 is resolved!
  32. # NOTE: Due to a bug in Bats, `batslib_count_lines "$output"' does not
  33. # give the same result as `${#lines[@]}' when the output contains
  34. # empty lines.
  35. # See PR #93 (https://github.com/sstephenson/bats/pull/93).
  36. #
  37. # Globals:
  38. # none
  39. # Arguments:
  40. # $1 - string
  41. # Returns:
  42. # none
  43. # Outputs:
  44. # STDOUT - number of lines
  45. batslib_count_lines() {
  46. local -i n_lines=0
  47. local line
  48. while IFS='' read -r line || [[ -n $line ]]; do
  49. (( ++n_lines ))
  50. done < <(printf '%s' "$1")
  51. echo "$n_lines"
  52. }
  53. # Determine whether all strings are single-line.
  54. #
  55. # Globals:
  56. # none
  57. # Arguments:
  58. # $@ - strings
  59. # Returns:
  60. # 0 - all strings are single-line
  61. # 1 - otherwise
  62. batslib_is_single_line() {
  63. for string in "$@"; do
  64. (( $(batslib_count_lines "$string") > 1 )) && return 1
  65. done
  66. return 0
  67. }
  68. # Determine the length of the longest key that has a single-line value.
  69. #
  70. # This function is useful in determining the correct width of the key
  71. # column in two-column format when some keys may have multi-line values
  72. # and thus should be excluded.
  73. #
  74. # Globals:
  75. # none
  76. # Arguments:
  77. # $odd - key
  78. # $even - value of the previous key
  79. # Returns:
  80. # none
  81. # Outputs:
  82. # STDOUT - length of longest key
  83. batslib_get_max_single_line_key_width() {
  84. local -i max_len=-1
  85. while (( $# != 0 )); do
  86. local -i key_len="${#1}"
  87. batslib_is_single_line "$2" && (( key_len > max_len )) && max_len="$key_len"
  88. shift 2
  89. done
  90. echo "$max_len"
  91. }
  92. # Print key-value pairs in two-column format.
  93. #
  94. # Keys are displayed in the first column, and their corresponding values
  95. # in the second. To evenly line up values, the key column is fixed-width
  96. # and its width is specified with the first parameter (possibly computed
  97. # using `batslib_get_max_single_line_key_width').
  98. #
  99. # Globals:
  100. # none
  101. # Arguments:
  102. # $1 - width of key column
  103. # $even - key
  104. # $odd - value of the previous key
  105. # Returns:
  106. # none
  107. # Outputs:
  108. # STDOUT - formatted key-value pairs
  109. batslib_print_kv_single() {
  110. local -ir col_width="$1"; shift
  111. while (( $# != 0 )); do
  112. printf '%-*s : %s\n' "$col_width" "$1" "$2"
  113. shift 2
  114. done
  115. }
  116. # Print key-value pairs in multi-line format.
  117. #
  118. # The key is displayed first with the number of lines of its
  119. # corresponding value in parenthesis. Next, starting on the next line,
  120. # the value is displayed. For better readability, it is recommended to
  121. # indent values using `batslib_prefix'.
  122. #
  123. # Globals:
  124. # none
  125. # Arguments:
  126. # $odd - key
  127. # $even - value of the previous key
  128. # Returns:
  129. # none
  130. # Outputs:
  131. # STDOUT - formatted key-value pairs
  132. batslib_print_kv_multi() {
  133. while (( $# != 0 )); do
  134. printf '%s (%d lines):\n' "$1" "$( batslib_count_lines "$2" )"
  135. printf '%s\n' "$2"
  136. shift 2
  137. done
  138. }
  139. # Print all key-value pairs in either two-column or multi-line format
  140. # depending on whether all values are single-line.
  141. #
  142. # If all values are single-line, print all pairs in two-column format
  143. # with the specified key column width (identical to using
  144. # `batslib_print_kv_single').
  145. #
  146. # Otherwise, print all pairs in multi-line format after indenting values
  147. # with two spaces for readability (identical to using `batslib_prefix'
  148. # and `batslib_print_kv_multi')
  149. #
  150. # Globals:
  151. # none
  152. # Arguments:
  153. # $1 - width of key column (for two-column format)
  154. # $even - key
  155. # $odd - value of the previous key
  156. # Returns:
  157. # none
  158. # Outputs:
  159. # STDOUT - formatted key-value pairs
  160. batslib_print_kv_single_or_multi() {
  161. local -ir width="$1"; shift
  162. local -a pairs=( "$@" )
  163. local -a values=()
  164. local -i i
  165. for (( i=1; i < ${#pairs[@]}; i+=2 )); do
  166. values+=( "${pairs[$i]}" )
  167. done
  168. if batslib_is_single_line "${values[@]}"; then
  169. batslib_print_kv_single "$width" "${pairs[@]}"
  170. else
  171. local -i i
  172. for (( i=1; i < ${#pairs[@]}; i+=2 )); do
  173. pairs[$i]="$( batslib_prefix < <(printf '%s' "${pairs[$i]}") )"
  174. done
  175. batslib_print_kv_multi "${pairs[@]}"
  176. fi
  177. }
  178. # Prefix each line read from the standard input with the given string.
  179. #
  180. # Globals:
  181. # none
  182. # Arguments:
  183. # $1 - [= ] prefix string
  184. # Returns:
  185. # none
  186. # Inputs:
  187. # STDIN - lines
  188. # Outputs:
  189. # STDOUT - prefixed lines
  190. batslib_prefix() {
  191. local -r prefix="${1:- }"
  192. local line
  193. while IFS='' read -r line || [[ -n $line ]]; do
  194. printf '%s%s\n' "$prefix" "$line"
  195. done
  196. }
  197. # Mark select lines of the text read from the standard input by
  198. # overwriting their beginning with the given string.
  199. #
  200. # Usually the input is indented by a few spaces using `batslib_prefix'
  201. # first.
  202. #
  203. # Globals:
  204. # none
  205. # Arguments:
  206. # $1 - marking string
  207. # $@ - indices (zero-based) of lines to mark
  208. # Returns:
  209. # none
  210. # Inputs:
  211. # STDIN - lines
  212. # Outputs:
  213. # STDOUT - lines after marking
  214. batslib_mark() {
  215. local -r symbol="$1"; shift
  216. # Sort line numbers.
  217. set -- $( sort -nu <<< "$( printf '%d\n' "$@" )" )
  218. local line
  219. local -i idx=0
  220. while IFS='' read -r line || [[ -n $line ]]; do
  221. if (( ${1:--1} == idx )); then
  222. printf '%s\n' "${symbol}${line:${#symbol}}"
  223. shift
  224. else
  225. printf '%s\n' "$line"
  226. fi
  227. (( ++idx ))
  228. done
  229. }
  230. # Enclose the input text in header and footer lines.
  231. #
  232. # The header contains the given string as title. The output is preceded
  233. # and followed by an additional newline to make it stand out more.
  234. #
  235. # Globals:
  236. # none
  237. # Arguments:
  238. # $1 - title
  239. # Returns:
  240. # none
  241. # Inputs:
  242. # STDIN - text
  243. # Outputs:
  244. # STDOUT - decorated text
  245. batslib_decorate() {
  246. echo
  247. echo "-- $1 --"
  248. cat -
  249. echo '--'
  250. echo
  251. }