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

core.sh 7.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
  1. #!/bin/bash
  2. ##--------------------##
  3. ## PRINTING FRONT-END ##
  4. ##--------------------##
  5. debug() {
  6. #
  7. # You already know what it does
  8. #
  9. # BTW, following are equivalent:
  10. #
  11. # debug "var1=$var1" "var2=$var2" "result=$result"
  12. # debug -v var1 var2 result
  13. # debug -v "var@ result"
  14. #
  15. $FFOO_DEBUG || return 0
  16. __echo "$@"
  17. }
  18. debug_pipe() {
  19. #
  20. # Debug the whole pipe.
  21. #
  22. grep . \
  23. | while IFS= read line;
  24. do
  25. debug "|$1: '$line'"
  26. echo "$line"
  27. done
  28. }
  29. die() {
  30. #
  31. # A Perl-style death
  32. #
  33. __echo -t
  34. __echo $*
  35. __exits 9
  36. }
  37. usage_is() {
  38. #
  39. # Echo out usage patterns and (by default) exit 2
  40. #
  41. # Specify -e STATUS for exit status, or "-E" to prevent
  42. # exiting. Use "--" to delimit end of arguments processed
  43. # by usage_is.
  44. #
  45. # Recommended usage is to define usage() in your script and
  46. # use this in body.
  47. #
  48. local es=2
  49. while true; do case "$1" in
  50. -e) es="$2"; shift 2 ;;
  51. -E) es=""; shift ;;
  52. *) break ;;
  53. esac done
  54. __echo -u "$@";
  55. test -n "$es" && __exits "$es"
  56. }
  57. think() {
  58. #
  59. # If verose is on, think loud
  60. #
  61. # Use "-l" to split every parameter to separate line, (useful
  62. # or longer warnings)
  63. #
  64. $FFOO_VERBOSE || return 0
  65. __echo "$@"
  66. }
  67. warn() {
  68. #
  69. # Warn them
  70. #
  71. # Use "-l" to split every parameter to separate line, (useful
  72. # or longer warnings)
  73. #
  74. # Resist temptation to prefix "warning" or similar BS here.
  75. # STDERR is *made* for warnings.
  76. #
  77. __echo "$@"
  78. }
  79. ##----------##
  80. ## BACK-END ##
  81. ##----------##
  82. __cat() {
  83. # cat without starting a process
  84. while IFS= read line;
  85. do echo "$line"; done
  86. }
  87. __cut_stack() {
  88. #
  89. # call stack except the head that fits pattern $1
  90. #
  91. local patt=${1:-"^__"}
  92. printf -- '%s\n' "${FUNCNAME[@]}" \
  93. | perl -ne "
  94. my \$checking = 1;
  95. while (<>) {
  96. next if (\$checking and m/$patt/);
  97. \$checking = 0;
  98. print qq/\$_\\n/;
  99. }
  100. "
  101. }
  102. __echo() {
  103. #
  104. # A smarter echo backend
  105. #
  106. # A smarter backend for debug, warn, think, die and
  107. # usage_is.
  108. #
  109. # -c cmd echo output of a command
  110. # -f file echo output of a file (- for stdin)
  111. # -l line [line...] echo each line separately
  112. # -t add stack trace to output
  113. # -u patt [patt...] convert each patt to usage pattern
  114. # -v var [var...] show contents of each var
  115. #
  116. ffoo import mkpretty_${FFOO_MKPRETTY}
  117. local frontend # who (of core.sh) was called (=> prettyprinter choice)
  118. local caller # which user's function (or script) called it
  119. frontend="$(__cut_stack '^__echo' | head -1 )"
  120. caller="$(
  121. __cut_stack \
  122. '^__echo|^warn$|^debug$|^die$|^usage$|^usage_is$|^think$' \
  123. | head -1
  124. )"
  125. test "$frontend" == "main" && frontend=$(basename $0)
  126. test "$caller" == "main" && caller=$(basename $0)
  127. test "$frontend" == "usage" && frontend=usage_is
  128. local prettyfn=__cat
  129. local src
  130. while true; do case $1 in
  131. -c|--cmd) src=cmd; shift; break ;;
  132. -f|--files) src=files; shift; break ;;
  133. -l|--lines) src=lines; shift; break ;;
  134. -t|--trace) src=trace; shift; break ;;
  135. -u|--usage) src=usage; shift; break ;;
  136. -v|--vars) src=vars; shift; break ;;
  137. *) src=args; break ;;
  138. esac done
  139. prettyfn=mkpretty_$frontend
  140. case $frontend in
  141. warn|debug|die|usage_is) __echo_$src "$@" | $prettyfn >&2 ;;
  142. think) __echo_$src "$@" | $prettyfn ;;
  143. *) echo "do not call __echo* directly: $frontend" >&2;
  144. __exits 2 ;;
  145. esac
  146. }
  147. __echo_args() {
  148. echo "$@"
  149. }
  150. __echo_cmd() {
  151. local c=\$
  152. test $(id -u) -eq 0 && c=\#
  153. echo "$c $*"
  154. $@
  155. echo "^ exit status: $?"
  156. }
  157. __echo_files() {
  158. local fp
  159. for fp in "$@";
  160. do
  161. test -s "$fp" || continue
  162. echo "-- $fp --"
  163. cat $fp
  164. done
  165. }
  166. __echo_lines() {
  167. local l;
  168. for l in "$@"; do __echo "$l"; done
  169. }
  170. __echo_trace() {
  171. $FFOO_DEBUG || return 0
  172. echo "== trace =="
  173. caller | sed -e 's/^/-> /g'
  174. }
  175. __echo_usage() {
  176. local u
  177. for u in "$@";
  178. do echo "usage: $caller $u"
  179. done
  180. }
  181. __echo_vars() {
  182. local vn
  183. for vn in "$@";
  184. do
  185. local heel="${vn:0-1}" # last char
  186. local toes="${vn%%$heel}" # all but last char
  187. case "$heel" in
  188. @)
  189. # FIXME: review+fix the eval (even at cost of
  190. # dropping the feature)
  191. local vars=$(eval "echo \"\${!$toes$heel}\"")
  192. __echo_vars $vars
  193. ;;
  194. *)
  195. __echo "$vn='$(echo -n ${!vn})'"
  196. ;;
  197. esac
  198. done
  199. }
  200. ## ---- ##
  201. ## UTIL ##
  202. ## ---- ##
  203. __exits() {
  204. #
  205. # Like exit but will not exit interactive session
  206. #
  207. # fastfoo libs are not intended for use in interactive
  208. # session (one should use fff for that), but for development
  209. # and testing purposes we want to enable it
  210. #
  211. test -z "$PS1" && exit $1
  212. }
  213. echo_hr() {
  214. #
  215. # A horizontal ruler out of char "$1" all across terminal
  216. #
  217. test 0$COLUMNS -gt 0 || return
  218. local char="$1"
  219. local i
  220. for (( i=1; $i<$COLUMNS; i=$i+1 )); do echo -n "$char"; done
  221. echo
  222. }
  223. mute_known() {
  224. #
  225. # Mute known messages
  226. #
  227. # For those yums and rpms that don't know theit manners.
  228. # Use with care!
  229. #
  230. grep -vxf <(iniread -s mute -k $1 mute.ini)
  231. }
  232. append_if_missing() {
  233. #
  234. # Append line to the file but only if it's not already there
  235. #
  236. # Handy for your fstabs, or whatever line-based confs Handy
  237. # for your fstabs, or whatever line-based confs
  238. #
  239. local line=$1
  240. local file=$2
  241. grep -qsx "$line" $file || echo "$line" >> $file
  242. }
  243. collapse_tilde() {
  244. #
  245. # Exchange home back to "~"
  246. #
  247. # Nice to save them users from some eye pain.
  248. #
  249. perl -pe "s|^$HOME|~|"
  250. }
  251. expand_tilde() {
  252. #
  253. # Exchange "~" for home
  254. #
  255. perl -pe "s|^[[:space:]]*~|$HOME|"
  256. }
  257. select_args() {
  258. #
  259. # Filter args based on type, print one per line
  260. #
  261. local want_lopt=false # --long-opt
  262. local want_sopt=false # -s
  263. local want_bopt=false # -bndl
  264. local want_word=false # everything else
  265. local want="$1"
  266. shift
  267. case $want in
  268. word) want_word=true ;;
  269. opt) want_lopt=true
  270. want_sopt=true
  271. want_bopt=true ;;
  272. sopt) want_sopt=true ;;
  273. bopt) want_bopt=true ;;
  274. lopt) want_lopt=true ;;
  275. *) usage_is "word|opt|sopt|bopt|lopt args"
  276. esac
  277. while true; do case "$1" in
  278. "") return; ;;
  279. --*) $want_lopt && echo "$1"; shift ;;
  280. -n) $want_sopt && echo -e "\055n"; shift ;;
  281. -e) $want_sopt && echo -e "\055e"; shift ;;
  282. -E) $want_sopt && echo -e "\055E"; shift ;;
  283. -?) $want_sopt && echo "$1"; shift ;;
  284. -*) $want_bopt && echo "$1"; shift ;;
  285. *) $want_word && echo "$1"; shift ;;
  286. esac done | tr '\n' ' '
  287. }