123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. #!/bin/bash
  2. # MKit - simple install helper
  3. # See LICENSE file for copyright and license details.
  4. __ini_cat() {
  5. #
  6. # A no-op for text stream
  7. #
  8. local line # each line
  9. while read -r line; do
  10. printf -- '%s\n' "$line"
  11. done
  12. }
  13. __ini_expand() {
  14. #
  15. # Expand reference value (prefix only)
  16. #
  17. local line # each input line
  18. while read -r line; do # [foo:bar]/path
  19. __ini_expandln "$line"
  20. done
  21. }
  22. __ini_expandln() {
  23. #
  24. # Fully expand references in line $1
  25. #
  26. local line_orig=$1 # original line
  27. local line_todo=$line_orig # current state
  28. local line_done # next state
  29. local Depth=0 # current depth
  30. local MaxDepth=10 # maximum depth
  31. while true; do
  32. ((Depth++))
  33. debug_var line_todo
  34. test "$Depth" -le "$MaxDepth" || {
  35. warn "expansion error: reached maximum depth: $Depth > $MaxDepth"
  36. warn " original line: $line_orig"
  37. warn " expanded line: $line_todo"
  38. return 3
  39. }
  40. line_done=$(__ini_expandln_once "$line_todo")
  41. debug_var line_done
  42. test "$line_done" == "$line_todo" && break
  43. line_todo=$line_done
  44. done
  45. echo "$line_done"
  46. }
  47. __ini_expandln_once() {
  48. #
  49. # Run through line $1 once and replace all references
  50. #
  51. local line=$1 # line to expand
  52. local ref # full reference (incl. brackets)
  53. local ipath # just ini path from ^^ (stripped brackets)
  54. local value # value of reference
  55. local refs=() # all references found in line
  56. mapfile -t refs <<<"$(grep -Eo '[[][^]]+[]]' <<< "$line_todo")"
  57. debug_var refs
  58. for ref in "${refs[@]}"; do
  59. test -n "$ref" || continue
  60. ipath=${ref#[}; ipath=${ipath%]}
  61. value=$(ini 1value "$ipath")
  62. debug_var line ref ipath value
  63. line=$(sed "s|\\[$ipath\\]|$value|" <<<"$line")
  64. done
  65. echo "$line"
  66. }
  67. __ini_grepcmt() {
  68. #
  69. # Remove comments from INI file on stdin
  70. #
  71. grep -v '^[[:space:]]*#'
  72. }
  73. __ini_grepkey() {
  74. #
  75. # Read key from a section
  76. #
  77. local wnt=$1 # wanted key
  78. grep '.' \
  79. | sed -e 's/ *= */=/; s/ +$//; s/^//;' \
  80. | grep -e "^$wnt=" \
  81. | cut -d= -f2- \
  82. | __ini_maybe_expand
  83. }
  84. __ini_greppath() {
  85. #
  86. # Read key from the right section
  87. #
  88. # E.g. `files:share:my/lib.sh` should read
  89. #
  90. # [files:share]
  91. # my/lib.sh = proj/my/lib.sh
  92. #
  93. local wnt=$1 # wanted path
  94. local wntkey=${wnt##*:} # ^^ key part
  95. local wntsec=${wnt%:$wntkey} # ^^ section part
  96. local override # ENV override (only ENV section)
  97. if test "$wntsec" = 'ENV'; then
  98. override=${!wntkey}
  99. test -n "$override" \
  100. && echo "$override" \
  101. && return
  102. fi
  103. __ini_grepsec "$wntsec" | __ini_grepkey "$wntkey"
  104. }
  105. __ini_grepsec() {
  106. #
  107. # Read one INI section
  108. #
  109. local wnt=$1 # wanted section name
  110. local ok=false # are we in the section?
  111. local line # each input line
  112. grep '.' \
  113. | while read -r line; do
  114. case "$line" in
  115. \[$wnt\]) ok=true; continue ;;
  116. \[*\]) ok=false; continue ;;
  117. esac
  118. $ok || continue
  119. printf -- '%s\n' "$line"
  120. done \
  121. | sed -e 's/ *= */=/; s/ +$//; s/^//;'
  122. }
  123. __ini_lskeys() {
  124. #
  125. # List keys from a section
  126. #
  127. local sct=$1 # section of interest
  128. __ini_grepsec "$sct" | cut -d= -f1 | awk '!x[$0]++'
  129. }
  130. __ini_lssect() {
  131. #
  132. # List all section names
  133. #
  134. grep -x '\[.*\]' | sed 's/^.//; s/.$//'
  135. }
  136. __ini_maybe_expand() {
  137. #
  138. # Decide whether or not to expand
  139. #
  140. if test "$MKIT_INI_EXPAND" -gt 0; then
  141. MKIT_INI_EXPAND=$(( --MKIT_INI_EXPAND )) __ini_expand
  142. else
  143. __ini_cat
  144. fi
  145. }
  146. __ini_body() {
  147. #
  148. # Produce mkit.ini body including INCLUDE
  149. #
  150. # Note: recursive includes are not supported.
  151. #
  152. local inc # file to include
  153. local incre='\[INCLUDE:.*\]' # include directive regex
  154. local iline # include directive line
  155. if iline=$(grep -m1 -xe "$incre" "$MKIT_INI"); then
  156. inc=${iline#*:}; inc=${inc%]}
  157. grep -vxe "$incre" "$inc"
  158. grep -vxe "$incre" "$MKIT_INI"
  159. else
  160. cat "$MKIT_INI"
  161. fi | __ini_grepcmt
  162. }
  163. ini() {
  164. #
  165. # do ini operation
  166. #
  167. local op=$1 # operator
  168. local arg=$2 # argument
  169. local fn # internal function implementing $op
  170. local limit=__ini_cat # limiting internal function
  171. case $op in
  172. lskeys) fn=__ini_lskeys ;;
  173. lssect) fn=__ini_lssect ;;
  174. sec) fn=__ini_grepsec ;;
  175. values) fn=__ini_greppath ;;
  176. 1value) fn=__ini_greppath; limit="tail -1" ;;
  177. *) die "incorrect use of \`ini()\`"
  178. esac
  179. __ini_body | $fn "$arg" | $limit
  180. }
  181. update_version() {
  182. #
  183. # Change project:version in mkit.ini at path $2 to value $1
  184. #
  185. local version=$1 # new version
  186. local inifile=$2 # mkit.ini path
  187. local tmp # mkit.ini cache
  188. tmp=$(mktemp -t mkit.update_version.XXXXXXXX)
  189. <"$inifile" perl -e '
  190. my $hit = 0;
  191. my $done = 0;
  192. foreach (<STDIN>) {
  193. if ($done) { print; next; }
  194. elsif (m/\[project\]/) { $hit++; print; next; }
  195. elsif (m/\[/) { $hit = 0; print; next; }
  196. elsif ($hit) { s/\bversion\b( *)=( *).*/version$1=$2$ARGV[0]/ and $done++; print; }
  197. else { print; next; }
  198. }
  199. ' "$version" >"$tmp" || die "failed to update version in mkit.ini"
  200. mv "$tmp" "$inifile"
  201. }