shell dot on steroids https://pagure.io/shellfu

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275
  1. #!/bin/bash
  2. ffoo import exits
  3. #
  4. # Name of prerry-printer module
  5. #
  6. # Friendly name of module used for decorating output. For
  7. # example, if the value is NAME, a _pretty_NAME module must
  8. # exist and be importable. Otherwise pretty will fall back
  9. # to plain, which is also the default value.
  10. #
  11. FFOO_PRETTY=${FFOO_PRETTY:-plain}
  12. __ffoo_pretty__init() {
  13. #
  14. # Import proper submodule
  15. #
  16. if ffoo try_import _pretty_${FFOO_PRETTY};
  17. then
  18. ffoo import _pretty_${FFOO_PRETTY}
  19. return 0
  20. else
  21. warn "falling back to _pretty_plain"
  22. FFOO_PRETTY=plain
  23. ffoo import _pretty_${FFOO_PRETTY}
  24. fi
  25. }
  26. ##--------------------##
  27. ## PRINTING FRONT-END ##
  28. ##--------------------##
  29. debug() {
  30. #
  31. # You already know what it does
  32. #
  33. # BTW, following are equivalent:
  34. #
  35. # debug "var1=$var1" "var2=$var2" "result=$result"
  36. # debug -v var1 var2 result
  37. # debug -v "var@ result"
  38. #
  39. $FFOO_DEBUG || return 0
  40. __echo "$@"
  41. }
  42. debug_pipe() {
  43. #
  44. # Debug the whole pipe.
  45. #
  46. grep . \
  47. | while IFS= read line;
  48. do
  49. debug "|$1: '$line'"
  50. echos "$line"
  51. done
  52. }
  53. die() {
  54. #
  55. # A fatal error
  56. #
  57. __echo -t
  58. __echo "$@"
  59. exits_error
  60. }
  61. echos() {
  62. #
  63. # Safer version of echo able to echo "-n"
  64. #
  65. # Traditional echo is broken in that it does not
  66. # distinguish between string to print and its own switches
  67. # (-e, -E or -n), leading to unexpected behavior.
  68. #
  69. # This echo version circumvents this by using printf.
  70. #
  71. printf -- '%s\n' "$@"
  72. }
  73. usage_is() {
  74. #
  75. # Echo out usage patterns and (by default) exit 2
  76. #
  77. # Specify -e STATUS for exit status, or "-E" to prevent
  78. # exiting. Use "--" to delimit end of arguments processed
  79. # by usage_is.
  80. #
  81. # Recommended usage is to define usage() in your script and
  82. # use this in body.
  83. #
  84. local es=$FFOO_EXITS_OK
  85. while true; do case "$1" in
  86. -e) es="$2"; shift 2 ;;
  87. -E) es=""; shift ;;
  88. *) break ;;
  89. esac done
  90. __echo -u "$@";
  91. test -n "$es" && exits "$es"
  92. }
  93. think() {
  94. #
  95. # If verose is on, think loud
  96. #
  97. # Use "-l" to split every parameter to separate line, (useful
  98. # or longer warnings)
  99. #
  100. $FFOO_VERBOSE || return 0
  101. __echo "$@"
  102. }
  103. warn() {
  104. #
  105. # Warn them
  106. #
  107. # Use "-l" to split every parameter to separate line, (useful
  108. # or longer warnings)
  109. #
  110. # Resist temptation to prefix "warning" or similar BS here.
  111. # STDERR is *made* for warnings.
  112. #
  113. __echo "$@"
  114. }
  115. ##----------##
  116. ## BACK-END ##
  117. ##----------##
  118. __cat() {
  119. # cat without starting a process
  120. while IFS= read line;
  121. do echos "$line"; done
  122. }
  123. __cut_stack() {
  124. #
  125. # call stack except the head that fits pattern $1
  126. #
  127. local patt=${1:-"^__"}
  128. echos "${FUNCNAME[@]}" \
  129. | perl -ne "
  130. my \$checking = 1;
  131. while (<>) {
  132. next if (\$checking and m/$patt/);
  133. \$checking = 0;
  134. print qq/\$_\\n/;
  135. }
  136. "
  137. }
  138. __echo() {
  139. #
  140. # A smarter echo backend
  141. #
  142. # A smarter backend for debug, warn, think, die and
  143. # usage_is.
  144. #
  145. # -c cmd echo output of a command
  146. # -f file echo output of a file (- for stdin)
  147. # -l line [line...] echo each line separately
  148. # -t add stack trace to output
  149. # -u patt [patt...] convert each patt to usage pattern
  150. # -v var [var...] show contents of each var
  151. #
  152. local frontend # who (of pretty.sh) was called (=> prettyprinter choice)
  153. local caller # which user's function (or script) called it
  154. frontend="$(__cut_stack '^__echo' | head -1 )"
  155. caller="$(
  156. __cut_stack \
  157. '^__echo|^warn$|^debug$|^die$|^usage$|^usage_is$|^think$' \
  158. | head -1
  159. )"
  160. test "$frontend" == "main" && frontend=$(basename $0)
  161. test "$caller" == "main" && caller=$(basename $0)
  162. test "$frontend" == "usage" && frontend=usage_is
  163. local prettyfn=__cat
  164. local src # which source, or "producer" (__echo_*()) to use
  165. while true; do case $1 in
  166. -c|--cmd) src=cmd; shift; break ;;
  167. -f|--files) src=files; shift; break ;;
  168. -l|--lines) src=lines; shift; break ;;
  169. -t|--trace) src=trace; shift; break ;;
  170. -u|--usage) src=usage; shift; break ;;
  171. -v|--vars) src=vars; shift; break ;;
  172. *) src=args; break ;;
  173. esac done
  174. prettyfn=_pretty_$frontend
  175. case $frontend in
  176. warn|debug|die|usage_is) __echo_$src "$@" | $prettyfn >&2 ;;
  177. think) __echo_$src "$@" | $prettyfn ;;
  178. *) echo "do not call __echo* directly: $frontend" >&2;
  179. exits_usage ;;
  180. esac
  181. }
  182. __echo_args() {
  183. echos "$@"
  184. }
  185. __echo_cmd() {
  186. local c=\$
  187. test $(id -u) -eq 0 && c=\#
  188. echo "$c $*"
  189. $@
  190. echo "^ exit status: $?"
  191. }
  192. __echo_files() {
  193. local fp
  194. for fp in "$@";
  195. do
  196. test -s "$fp" || continue
  197. echo "-- $fp --"
  198. cat $fp
  199. done
  200. }
  201. __echo_lines() {
  202. local l;
  203. for l in "$@"; do __echo "$l"; done
  204. }
  205. __echo_trace() {
  206. $FFOO_DEBUG || return 0
  207. echo "== trace =="
  208. caller | sed -e 's/^/-> /g'
  209. }
  210. __echo_usage() {
  211. local u
  212. for u in "$@";
  213. do echo "usage: $caller $u"
  214. done
  215. }
  216. __echo_vars() {
  217. local vn
  218. for vn in "$@";
  219. do
  220. local heel="${vn:0-1}" # last char
  221. local toes="${vn%%$heel}" # all but last char
  222. case "$heel" in
  223. @)
  224. # FIXME: review+fix the eval (even at cost of
  225. # dropping the feature)
  226. local vars="$(eval "echos \"\${!$toes$heel}\"")"
  227. __echo_vars "$vars"
  228. ;;
  229. *)
  230. __echo "$vn='$(echos "${!vn}")'"
  231. ;;
  232. esac
  233. done
  234. }