56 Commits

Author SHA1 Message Date
  Alois Mahdal f872308193 Bump version to 0.10.27 4 years ago
  Alois Mahdal 411a359a0e Update TFKit to v0.0.18 4 years ago
  Alois Mahdal d0f12a2382 Mute ShellCheck warning about temp var 4 years ago
  Alois Mahdal a0fdf26b0f Enable Vornet Gladness pipeline 4 years ago
  Alois Mahdal 72bee24d1e Clarify list format for PRETTY_DEBUG_EXCLUDE 4 years ago
  Alois Mahdal ce4c42a9dc Update MKit to v0.0.40 4 years ago
  Alois Mahdal 04bbe6879e Bump version to 0.10.26 4 years ago
  Alois Mahdal dcdcd52728 Use leagacy mode for shellfu-sh-coerce also for RHEL-7 4 years ago
  Alois Mahdal 2d34f223af Bump version to 0.10.25 4 years ago
  Alois Mahdal 477cd50b82 Switch to less cynical branch naming convention 4 years ago
  Alois Mahdal f40b74432d Update MKit to v0.0.39 4 years ago
  Alois Mahdal 82a1c25930 Add -h to enable overriding definition of "hidden" 4 years ago
  Alois Mahdal 32bdba11b9 Add SFDOC_HIDE_REGEX to enable overriding definition of "hidden" 4 years ago
  Alois Mahdal 0922e6f4e6 Bump version 4 years ago
  Alois Mahdal e96616b77e Enable reloading modules 4 years ago
  Alois Mahdal cfb2e0f7cd Don't enforce debuging and verbosity modes on import 4 years ago
  Alois Mahdal 380eda671e Bump version 4 years ago
  Alois Mahdal aaa36c1b0a Fix bug when sfpi__key() would not ever return anything 4 years ago
  Alois Mahdal 5caf0cfba9 Bump version 4 years ago
  Alois Mahdal 93fa1abb5a Fix typo in usage error template 4 years ago
  Alois Mahdal fc20097925 Mention usage error templates in mkusage() docstring 4 years ago
  Alois Mahdal 83a4c9785d Bump version 4 years ago
  Alois Mahdal 171848a5fa Add extra shorthand arguments for common usage errors 4 years ago
  Alois Mahdal 42621995c2 Fortify command, file and pipe delimiters 4 years ago
  Alois Mahdal 089d0a76d0 Bump version 4 years ago
  Alois Mahdal d80a3cdc58 Add arr.sh, couple of utilities for array manipulation 4 years ago
  Alois Mahdal 47316ab8b7 Update TFKit to v0.0.17 4 years ago
  Alois Mahdal fb18926932 Bump version 4 years ago
  Alois Mahdal db7e27520b Add 'forcecolor' module 4 years ago
  Alois Mahdal 8e0cfadb13 Bump version 5 years ago
  Alois Mahdal 7eee5bb2fc Update MKit to v0.0.37 5 years ago
  Alois Mahdal 8875772d4d Bump version 5 years ago
  Alois Mahdal 4750ad76f3 Elaborate more in the docstring 5 years ago
  Alois Mahdal d7193295d0 Replace, don't remove control characters 5 years ago
  Alois Mahdal b1f3060143 Don't (mis)use tr/// to remove non-printables 5 years ago
  Alois Mahdal b00d06dfe4 Bump version 5 years ago
  Alois Mahdal 4112218fac Add legacy mode to avoid Term::ANSIColor dependency on RHEL-6 5 years ago
  Alois Mahdal e75c4b45d6 Bump version 5 years ago
  Alois Mahdal e5c2c211f9 Fix syntax error in debian/control 5 years ago
  Alois Mahdal fcd7da8d05 Bump version 5 years ago
  Alois Mahdal 76ccd1c093 Add missing file listings 5 years ago
  Alois Mahdal 7b7cc4b4a9 Add missing Perl dependencies 5 years ago
  Alois Mahdal 4e2cc49d13 Fix typo in .spec file 5 years ago
  Alois Mahdal 75dd46520b Bump version 5 years ago
  Alois Mahdal 0bec8ed937 Jump to first object mention in object mode 5 years ago
  Alois Mahdal e5f1740095 Add third import group for consistency with PEP8 5 years ago
  Alois Mahdal 13ac645e1e Remove leftover debug junk 5 years ago
  Alois Mahdal 508911f0b1 Fix typo 5 years ago
  Alois Mahdal e2214d4c0f Add coerce.sh for character set coercion 5 years ago
  Alois Mahdal 72a248a0c7 Bump version 5 years ago
  Alois Mahdal a11b72dea4 Improve an update tab completion for sfdoc 5 years ago
  Alois Mahdal ff4a9a4745 Stop stomping 'mname' global 5 years ago
  Alois Mahdal c788a63ddc Fix --which treated as if no value was needed 5 years ago
  Alois Mahdal 64964d7049 Avoid grep ambiguities 5 years ago
  Alois Mahdal ef4887123b Update MKit to v0.0.36 5 years ago
  Alois Mahdal 085aea4e87 Drop the 'draft' weasel-word 5 years ago

+ 5
- 0
.gitlab-ci.yml View File

1
+---
2
+include:
3
+  - project: 'vornet/infra/gladness'
4
+    ref: master
5
+    file: '/pipelines/shellfu.yaml'

+ 1
- 1
README.md View File

87
 Next, if you have also installed some modules, you can look around
87
 Next, if you have also installed some modules, you can look around
88
 using `sfdoc`:
88
 using `sfdoc`:
89
 
89
 
90
-    sfdoc --lsmods
90
+    sfdoc --lsmod
91
     sfdoc inigrep
91
     sfdoc inigrep
92
 
92
 
93
 This is how you access documentation of modules available on the system.
93
 This is how you access documentation of modules available on the system.

+ 2
- 2
mkit.ini View File

1
 [project]
1
 [project]
2
-    version     = 0.10.11
2
+    version     = 0.10.27
3
     codename    = FloatingPointError
3
     codename    = FloatingPointError
4
     name        = Shellfu
4
     name        = Shellfu
5
     tagline     = Shell dot on steroids
5
     tagline     = Shell dot on steroids
7
     maintainer  = Alois Mahdal <netvor@vornet.cz>
7
     maintainer  = Alois Mahdal <netvor@vornet.cz>
8
     vcs_browser = https://pagure.io/shellfu
8
     vcs_browser = https://pagure.io/shellfu
9
     relsrc      = master
9
     relsrc      = master
10
-    reldst      = last
10
+    reldst      = latest
11
 
11
 
12
 [dist]
12
 [dist]
13
     tarball  = LICENSE.md
13
     tarball  = LICENSE.md

+ 2
- 1
notes/style.md View File

1
-Coding style - draft
1
+Coding style
2
 ============
2
 ============
3
 
3
 
4
 This is the official coding style for Shellfu project.
4
 This is the official coding style for Shellfu project.
84
 
84
 
85
  1. standard library modules,
85
  1. standard library modules,
86
  2. related third party modules,
86
  2. related third party modules,
87
+ 3. local application/library specific imports,
87
 
88
 
88
 with empty line between the groups, and each group being sorted
89
 with empty line between the groups, and each group being sorted
89
 alphabetically.
90
 alphabetically.

+ 38
- 1
packaging/debian/control View File

48
  This sub-package contains Bash-specific parts of library infrastructure.
48
  This sub-package contains Bash-specific parts of library infrastructure.
49
  Shellfu modules written for Bash should depend on this.
49
  Shellfu modules written for Bash should depend on this.
50
 
50
 
51
+Package: shellfu-bash-arr
52
+Architecture: all
53
+Depends: shellfu-bash, shellfu-bash-pretty, shellfu-sh-isa
54
+Description: Array utilities
55
+ Shellfu is an attempt to add modularity to your shell scripts.
56
+ .
57
+ With Shellfu you can develop your shell modules separately from your
58
+ scripts, and test, use, explore or study them without need to be aware
59
+ of details such as where the actual files are placed.
60
+ .
61
+ Shellfu is mostly intended for cases when there's need for non-trivial
62
+ amount of reliable body of shell scripts, and access to advanced modular
63
+ languages such as Python or Perl is limited.
64
+ .
65
+ This sub-package contains 'arr', Shellfu/Bash module with few utilities
66
+ for manipulating Bash arrays
67
+
51
 Package: shellfu-bash-inigrep
68
 Package: shellfu-bash-inigrep
52
 Architecture: all
69
 Architecture: all
53
 Depends: shellfu-bash, shellfu-bash-pretty
70
 Depends: shellfu-bash, shellfu-bash-pretty
172
  This sub-package contains 'charmenu', a Shellfu/sh module for building
189
  This sub-package contains 'charmenu', a Shellfu/sh module for building
173
  interactive terminal menus
190
  interactive terminal menus
174
 
191
 
192
+Package: shellfu-sh-coerce
193
+Architecture: all
194
+Depends:
195
+ shellfu-sh,
196
+ perl,
197
+ perl-modules,
198
+Description: Shellfu/sh module with data coercion helpers
199
+ Shellfu is an attempt to add modularity to your shell scripts.
200
+ .
201
+ With Shellfu you can develop your shell modules separately from your
202
+ scripts, and test, use, explore or study them without need to be aware
203
+ of details such as where the actual files are placed.
204
+ .
205
+ Shellfu is mostly intended for cases when there's need for non-trivial
206
+ amount of reliable body of shell scripts, and access to advanced modular
207
+ languages such as Python or Perl is limited.
208
+ .
209
+ This sub-package contains 'coerce', Shellfu module containing few
210
+ data coercion helpers.
211
+
175
 Package: shellfu-sh-exit
212
 Package: shellfu-sh-exit
176
 Architecture: all
213
 Architecture: all
177
 Depends: shellfu-sh
214
 Depends: shellfu-sh
245
  common ANSI color names.
282
  common ANSI color names.
246
 
283
 
247
 
284
 
248
-# control file built with MKit __MKIT_SELF_VERSION__
285
+# control file built with MKit __MKIT_MKIT_VERSION__

+ 1
- 0
packaging/debian/shellfu-bash-arr.install View File

1
+/usr/share/shellfu/include-bash/arr.sh

+ 1
- 0
packaging/debian/shellfu-bash-pretty.install View File

1
 /usr/share/shellfu/include-bash/_pretty_color.sh
1
 /usr/share/shellfu/include-bash/_pretty_color.sh
2
+/usr/share/shellfu/include-bash/_pretty_forcecolor.sh
2
 /usr/share/shellfu/include-bash/_pretty_html.sh
3
 /usr/share/shellfu/include-bash/_pretty_html.sh
3
 /usr/share/shellfu/include-bash/_pretty_journald.sh
4
 /usr/share/shellfu/include-bash/_pretty_journald.sh
4
 /usr/share/shellfu/include-bash/_pretty_notify.sh
5
 /usr/share/shellfu/include-bash/_pretty_notify.sh

+ 1
- 0
packaging/debian/shellfu-sh-coerce.install View File

1
+/usr/share/shellfu/include-sh/coerce.sh

+ 36
- 1
packaging/template.spec View File

4
 %global pspkg procps-ng
4
 %global pspkg procps-ng
5
 %endif
5
 %endif
6
 
6
 
7
+%if ( 0%{?rhel} && 0%{?rhel} < 8 ) || ( 0%{?centos} && 0%{?centos} < 8 )
8
+%global legacy_coerce true
9
+%else
10
+%global legacy_coerce false
11
+%endif
12
+
7
 Name:           __MKIT_PROJ_PKGNAME__
13
 Name:           __MKIT_PROJ_PKGNAME__
8
 Version:        __MKIT_PROJ_VERSION__
14
 Version:        __MKIT_PROJ_VERSION__
9
 Release:        1%{?dist}
15
 Release:        1%{?dist}
38
 This sub-package contains Bash-specific parts of library infrastructure.
44
 This sub-package contains Bash-specific parts of library infrastructure.
39
 Shellfu modules written for Bash should depend on this.
45
 Shellfu modules written for Bash should depend on this.
40
 
46
 
47
+%package bash-arr
48
+Summary: Array utilities
49
+Requires: shellfu-bash
50
+Requires: shellfu-bash-pretty
51
+Requires: shellfu-sh-isa
52
+%description bash-arr
53
+This sub-package contains 'arr', Shellfu/Bash module with few utilities
54
+for manipulating Bash arrays
55
+
41
 %package bash-inigrep
56
 %package bash-inigrep
42
 Summary: INI grepping Shellfu/Bash module
57
 Summary: INI grepping Shellfu/Bash module
43
 Requires: perl
58
 Requires: perl
97
 This sub-package contains 'charmenu', a Shellfu/sh module for building
112
 This sub-package contains 'charmenu', a Shellfu/sh module for building
98
 interactive terminal menus
113
 interactive terminal menus
99
 
114
 
115
+%package sh-coerce
116
+Summary: Shellfu/sh module with data coercion helpers
117
+Requires: perl
118
+%if %{legacy_coerce} == "false"
119
+Requires: perl-Term-ANSIColor
120
+%endif
121
+Requires: shellfu-sh
122
+%description sh-coerce
123
+This sub-package contains 'coerce', Shellfu module containing few
124
+data coercion helpers.
125
+
100
 %package sh-exit
126
 %package sh-exit
101
 Summary: Shellfu/sh module for standard exit statuses
127
 Summary: Shellfu/sh module for standard exit statuses
102
 Requires: shellfu-sh
128
 Requires: shellfu-sh
129
 
155
 
130
 %prep
156
 %prep
131
 %setup -q
157
 %setup -q
158
+/bin/sed -i '/^__COERCE__LEGACY=/s/:-false/:-%{legacy_coerce}/' ./src/include-sh/coerce.sh
159
+
132
 
160
 
133
 %build
161
 %build
134
 make %{?_smp_mflags} PREFIX=/usr
162
 make %{?_smp_mflags} PREFIX=/usr
155
 %files bash
183
 %files bash
156
 %dir %{_datadir}/%{name}/include-bash
184
 %dir %{_datadir}/%{name}/include-bash
157
 
185
 
186
+%files bash-arr
187
+%{_datadir}/%{name}/include-bash/arr.sh
188
+
158
 %files bash-inigrep
189
 %files bash-inigrep
159
 %{_datadir}/%{name}/include-bash/inigrep.sh
190
 %{_datadir}/%{name}/include-bash/inigrep.sh
160
 
191
 
161
 %files bash-pretty
192
 %files bash-pretty
162
 %{_datadir}/%{name}/include-bash/_pretty_color.sh
193
 %{_datadir}/%{name}/include-bash/_pretty_color.sh
194
+%{_datadir}/%{name}/include-bash/_pretty_forcecolor.sh
163
 %{_datadir}/%{name}/include-bash/_pretty_html.sh
195
 %{_datadir}/%{name}/include-bash/_pretty_html.sh
164
 %{_datadir}/%{name}/include-bash/_pretty_journald.sh
196
 %{_datadir}/%{name}/include-bash/_pretty_journald.sh
165
 %{_datadir}/%{name}/include-bash/_pretty_notify.sh
197
 %{_datadir}/%{name}/include-bash/_pretty_notify.sh
181
 %files sh-charmenu
213
 %files sh-charmenu
182
 %{_datadir}/%{name}/include-sh/charmenu.sh
214
 %{_datadir}/%{name}/include-sh/charmenu.sh
183
 
215
 
216
+%files sh-coerce
217
+%{_datadir}/%{name}/include-sh/coerce.sh
218
+
184
 %files sh-exit
219
 %files sh-exit
185
 %{_datadir}/%{name}/include-sh/exit.sh
220
 %{_datadir}/%{name}/include-sh/exit.sh
186
 
221
 
195
 
230
 
196
 %changelog
231
 %changelog
197
 
232
 
198
-# specfile built with MKit __MKIT_SELF_VERSION__
233
+# specfile built with MKit __MKIT_MKIT_VERSION__

+ 13
- 5
src/bin/sfdoc View File

12
         "[options] MODULE"                                              \
12
         "[options] MODULE"                                              \
13
         "[options] --ls [MODULE]"                                       \
13
         "[options] --ls [MODULE]"                                       \
14
         "[options] --ls{var|fun} MODULE"                                \
14
         "[options] --ls{var|fun} MODULE"                                \
15
-        "[options] --which"                                             \
15
+        "[options] --which MODULE"                                      \
16
         "[options] --lsmod"                                             \
16
         "[options] --lsmod"                                             \
17
         "[options] -s|--src MODULE"                                     \
17
         "[options] -s|--src MODULE"                                     \
18
         "[options] --export FMT MODULE"                                 \
18
         "[options] --export FMT MODULE"                                 \
31
             "-O, --object       Treat MODULE as function or variable"   \
31
             "-O, --object       Treat MODULE as function or variable"   \
32
             "                   name and find respective module first." \
32
             "                   name and find respective module first." \
33
             "-d, --debug        Turn on debugging"                      \
33
             "-d, --debug        Turn on debugging"                      \
34
+            "-h, --hide REGEX   Use REGEX to determine what is hidden." \
35
+            "                   (See also -a and 'sfdoc sfdoc'.)"       \
34
             "-a, --all          Don't ignore hidden (starting with"     \
36
             "-a, --all          Don't ignore hidden (starting with"     \
35
             "                   underscore) modules or objects"         \
37
             "                   underscore) modules or objects"         \
36
             "--encoding ENC     Override encoding of the source text"   \
38
             "--encoding ENC     Override encoding of the source text"   \
93
     local RealModuleName    # name to override eg. if accessing via filename
95
     local RealModuleName    # name to override eg. if accessing via filename
94
     local encoding          # encoding
96
     local encoding          # encoding
95
     local mpath             # path to module file
97
     local mpath             # path to module file
98
+    local jump              # jump to first occurence of this
96
     action=man
99
     action=man
97
     format=markdown
100
     format=markdown
98
     encoding=utf8
101
     encoding=utf8
102
         -O|--object)    spectype=obj;               shift ;;
105
         -O|--object)    spectype=obj;               shift ;;
103
         -d|--debug)     PRETTY_DEBUG=true;          shift ;;
106
         -d|--debug)     PRETTY_DEBUG=true;          shift ;;
104
         -a|--all)       SFDOC_SHOW_HIDDEN=true;     shift ;;
107
         -a|--all)       SFDOC_SHOW_HIDDEN=true;     shift ;;
108
+        -h|--hide)      SFDOC_SHOW_HIDDEN=false; SFDOC_HIDE_REGEX="$2"; shift 2 || usage -w "no REGEX?" ;;
105
         -I|--include)   SHELLFU_PATH="$2:$SHELLFU_PATH"; shift 2 || usage -w "no PTH?" ;;
109
         -I|--include)   SHELLFU_PATH="$2:$SHELLFU_PATH"; shift 2 || usage -w "no PTH?" ;;
106
         -s|--src)       action=src;                 shift; break ;;
110
         -s|--src)       action=src;                 shift; break ;;
107
         -l|--ls)        action=lsx;                 shift; break ;;
111
         -l|--ls)        action=lsx;                 shift; break ;;
120
     debug -v SHELLFU_INCLUDE SHELLFU_PATH SFDOC_SHOW_HIDDEN
124
     debug -v SHELLFU_INCLUDE SHELLFU_PATH SFDOC_SHOW_HIDDEN
121
     debug -v action format module RealModuleName
125
     debug -v action format module RealModuleName
122
     case $action:$mspec in
126
     case $action:$mspec in
123
-        lsx:*|lsm:*|wch:*) true ;;
127
+        lsx:*|lsm:*)    true ;;
124
         *:)             usage -w "no MODULE?" ;;
128
         *:)             usage -w "no MODULE?" ;;
125
     esac
129
     esac
126
     case $spectype in
130
     case $spectype in
127
         obj) module=$(find_by "$mspec") \
131
         obj) module=$(find_by "$mspec") \
128
-              || die "no module found with: $mspec" ;;
132
+              || die "no module found with: $mspec"
133
+             jump="$mspec"
134
+             ;;
129
         *)   module=$mspec ;;
135
         *)   module=$mspec ;;
130
     esac
136
     esac
131
     case $action in
137
     case $action in
134
     esac
140
     esac
135
     case $action in
141
     case $action in
136
         exp)    # --export
142
         exp)    # --export
137
-            grep -qw "$format" <<<manpage,markdown,pod \
143
+            grep -qwe "$format" <<<manpage,markdown,pod \
138
              || die "unknown format: $format"
144
              || die "unknown format: $format"
139
             sfdoc__export "$format" "${RealModuleName:-$module}" "$mpath" "$encoding"
145
             sfdoc__export "$format" "${RealModuleName:-$module}" "$mpath" "$encoding"
140
             ;;
146
             ;;
164
              || die "pod2man is missing; cannot use man page mode"
170
              || die "pod2man is missing; cannot use man page mode"
165
             manfile=$(mktemp -t "sfdoc.manfile.XXXXXXXX")
171
             manfile=$(mktemp -t "sfdoc.manfile.XXXXXXXX")
166
             sfdoc__export manpage "${RealModuleName:-$module}" "$mpath" "$encoding" >"$manfile"
172
             sfdoc__export manpage "${RealModuleName:-$module}" "$mpath" "$encoding" >"$manfile"
167
-            man "$manfile"
173
+            manless="$LESS"
174
+            test -n "$jump" && manless+=" +/$jump"
175
+            LESS="$manless" man "$manfile"
168
             rm "$manfile"
176
             rm "$manfile"
169
             ;;
177
             ;;
170
         src)
178
         src)

+ 69
- 6
src/complete.bash View File

1
 
1
 
2
 __sfpath() {
2
 __sfpath() {
3
-    local cur prev opts
3
+    local cur       # current word
4
+    local prev      # previous word
5
+    local lopts     # long options
4
     COMPREPLY=()
6
     COMPREPLY=()
5
     cur="${COMP_WORDS[COMP_CWORD]}"
7
     cur="${COMP_WORDS[COMP_CWORD]}"
6
     prev="${COMP_WORDS[COMP_CWORD-1]}"
8
     prev="${COMP_WORDS[COMP_CWORD-1]}"
7
-    opts="--version --version-semver"
9
+    lopts="--version --version-semver"
8
     COMPREPLY=(
10
     COMPREPLY=(
9
-        $(compgen -W "$opts" -- ${cur})
11
+        $(compgen -W "$lopts" -- "$cur")
10
     )
12
     )
11
 }
13
 }
12
 
14
 
13
 __sfdoc() {
15
 __sfdoc() {
14
-    local cur prev opts
16
+    local cur       # current word
17
+    local prev      # previous word
18
+    local opts      # options
19
+    local rest      # rest of options
20
+    local cmds      # commands
21
+    local crtext    # text for COMPREPLY
15
     COMPREPLY=()
22
     COMPREPLY=()
16
     cur="${COMP_WORDS[COMP_CWORD]}"
23
     cur="${COMP_WORDS[COMP_CWORD]}"
17
     prev="${COMP_WORDS[COMP_CWORD-1]}"
24
     prev="${COMP_WORDS[COMP_CWORD-1]}"
18
-    args="--help --debug --include --ls --lsmod --lsfun --lsvar --export --name"
25
+    opts="-d -O -a -I -o --debug --object --all --include --only-from --encoding --name"
26
+    cmds="-l -L -s -e --ls --lsvar --lsfun --which --lsmod --src --export"
27
+    rest="${opts/$prev/}"
28
+    crtext=$(
29
+        case $prev in
30
+            sfdoc)                  echo "$opts $cmds"; __sfdoc_compgen_M2 ;;
31
+            -d|--debug)             echo "$rest $cmds"; __sfdoc_compgen_M3 ;;
32
+            -O|--object)            echo "$rest $cmds"; __sfdoc_compgen_M3 ;;
33
+            -a|--all)               echo "$rest $cmds"; __sfdoc_compgen_M3 ;;
34
+            -I|--include)           compgen -o nospace -d -- "$cur" ;;
35
+            -o|--only-from)         compgen -o nospace -d -- "$cur" ;;
36
+            --encoding)             iconv -l | tr '[:upper:]' '[:lower:]' | sed s://$:: ;;
37
+            --name)                 echo "" ;;
38
+            -e|--export)            echo "markdown manpage pod" ;;
39
+            -s|--src)               __sfdoc_compgen_M3 ;;
40
+            -l|--ls)                __sfdoc_compgen_M2 ;;
41
+            --lsfun|--lsvar)        __sfdoc_compgen_M2 ;;
42
+            --which)                sfdoc --lsmod ;;
43
+            markdown|manpage|pod)   __sfdoc_cwhas "-e" "--export" \
44
+                                      && __sfdoc_compgen_M3  ;;
45
+        esac
46
+    )
19
     COMPREPLY=(
47
     COMPREPLY=(
20
-        $(compgen -W "$args" -- ${cur})
48
+        $(compgen -W "$crtext" -- "$cur")
21
     )
49
     )
22
 }
50
 }
23
 
51
 
52
+__sfdoc_compgen_M2() {
53
+    #
54
+    # Produce list of possible MODULE values
55
+    #
56
+    case $cur in
57
+        */*)    compgen -f -o nospace -- "$cur" ;;
58
+        *)      sfdoc --lsmod ;;
59
+    esac
60
+}
61
+
62
+__sfdoc_cwhas() {
63
+    #
64
+    # True if COMP_WORDS already has word $1 or $2...
65
+    #
66
+    local word
67
+    local want
68
+    for want in "$@"; do
69
+        for word in "${COMP_WORDS[@]}"; do
70
+            test "$word" == "$want" && return 0
71
+        done
72
+    done
73
+    return 1
74
+}
75
+
76
+__sfdoc_compgen_M3() {
77
+    #
78
+    # Produce list of possible MODULE values
79
+    #
80
+    if __sfdoc_cwhas "-O" "--object"; then
81
+        sfdoc --ls | cut -d: -f2
82
+    else
83
+        __sfdoc_compgen_M2
84
+    fi
85
+}
86
+
24
 complete -F __sfpath sfpath
87
 complete -F __sfpath sfpath
25
 complete -F __sfdoc sfdoc
88
 complete -F __sfdoc sfdoc

+ 62
- 0
src/include-bash/_pretty_forcecolor.sh View File

1
+#!/bin/bash
2
+
3
+shellfu import termcolors
4
+
5
+
6
+_PRETTY_FORCECOLOR_DEBUG="${TERMCOLORS_LBLUE}"
7
+_PRETTY_FORCECOLOR_DIE="${TERMCOLORS_LRED}"
8
+_PRETTY_FORCECOLOR_USAGE_IS="${TERMCOLORS_YELLOW}"
9
+_PRETTY_FORCECOLOR_THINK="${TERMCOLORS_LBLACK}"
10
+_PRETTY_FORCECOLOR_WARN="${TERMCOLORS_LRED}"
11
+_PRETTY_FORCECOLOR_OFF="${TERMCOLORS_NONE}"
12
+
13
+
14
+_pretty__debug() {
15
+    local decor="()"
16
+    local caller_is_main=${caller_is_main:-false}
17
+    local caller=${caller:-UNKNOWN}
18
+    $caller_is_main && decor=
19
+    while IFS= read -r line;
20
+    do echo -ne "${_PRETTY_FORCECOLOR_DEBUG}debug:$caller$decor:$_PRETTY_FORCECOLOR_OFF"
21
+       echo "$line"; done
22
+}
23
+
24
+
25
+_pretty__die() {
26
+    echo -ne "$_PRETTY_FORCECOLOR_DIE"
27
+    while IFS= read -r line;
28
+    do echo "$line"; done
29
+    echo -ne "$_PRETTY_FORCECOLOR_OFF"
30
+}
31
+
32
+
33
+_pretty__mkhelp() {
34
+    echo -ne "$_PRETTY_FORCECOLOR_USAGE_IS"
35
+    while IFS= read -r line;
36
+    do echo -e "$line"; done
37
+    echo -ne "$_PRETTY_FORCECOLOR_OFF"
38
+}
39
+
40
+
41
+_pretty__mkusage() {
42
+    echo -ne "$_PRETTY_FORCECOLOR_USAGE_IS"
43
+    while IFS= read -r line;
44
+    do echo -e "$line"; done
45
+    echo -ne "$_PRETTY_FORCECOLOR_OFF"
46
+}
47
+
48
+
49
+_pretty__think() {
50
+    echo -ne "$_PRETTY_FORCECOLOR_THINK"
51
+    while IFS= read -r line;
52
+    do echo "$line"; done
53
+    echo -ne "$_PRETTY_FORCECOLOR_OFF"
54
+}
55
+
56
+
57
+_pretty__warn() {
58
+    echo -ne "$_PRETTY_FORCECOLOR_WARN"
59
+    while IFS= read -r line;
60
+    do echo "$line"; done
61
+    echo -ne "$_PRETTY_FORCECOLOR_OFF"
62
+}

+ 98
- 0
src/include-bash/arr.sh View File

1
+#!/bin/bash
2
+
3
+shellfu import isa
4
+shellfu import pretty
5
+
6
+#
7
+# Array utilities
8
+#
9
+# Several utilities for loading and printing Bash arrays.
10
+#
11
+
12
+arr__fromcmd() {
13
+    #
14
+    # Save output lines from command $2.. to array named $1
15
+    #
16
+    # Usage:
17
+    #     arr__fromcmd ARRAY CMD [ARG]..
18
+    #
19
+    # Unlike `mapfile -t <<<"$(command)", this produces zero
20
+    # items if command output was empty.
21
+    #
22
+    local __arr__fromcmd__oarr=$1; shift
23
+    local __arr__fromcmd__cmd=("$@")
24
+    local __arr__fromcmd__tmp
25
+    local es
26
+    __arr__val_oarr "$__arr__fromcmd__oarr" || return 2
27
+    __arr__fromcmd__tmp=$(mktemp arr__fromcmd.__arr__fromcmd__tmp.XXXXX)
28
+    "${__arr__fromcmd__cmd[@]}" > "$__arr__fromcmd__tmp"; es=$?
29
+    mapfile -t "$__arr__fromcmd__oarr" <"$__arr__fromcmd__tmp"
30
+    rm "$__arr__fromcmd__tmp"
31
+    return $es
32
+}
33
+
34
+arr__has() {
35
+    #
36
+    # True if value $1 is in array $2
37
+    #
38
+    # Usage:
39
+    #     arr__has VALUE ARRAY
40
+    #
41
+    local want=$1
42
+    local iarr=$2
43
+    local copy
44
+    local value
45
+    local code
46
+    __arr__val_iarr "$iarr" || return 2
47
+    code=$(declare -p "$iarr"); code=${code/ $iarr=/ copy=}
48
+    eval "$code"
49
+    for value in "${copy[@]}"; do
50
+        test "$value" == "$want" && return 0
51
+    done
52
+    return 1
53
+}
54
+
55
+arr__join() {
56
+    #
57
+    # Join items in array $2 by string $1
58
+    #
59
+    # Usage:
60
+    #     arr__join DELIM ARRAY
61
+    #
62
+    # Example:
63
+    #
64
+    #     names=( Alice Bob )
65
+    #     arr__join , names
66
+    #     # prints 'Alice,Bob'
67
+    #
68
+    local delim=$1
69
+    local iarr=$2
70
+    local first=true
71
+    local item
72
+    local copy
73
+    local code
74
+    __arr__val_iarr "$iarr" || return 2
75
+    code=$(declare -p "$iarr"); code=${code/ $iarr=/ copy=}
76
+    eval "$code"
77
+    for item in "${copy[@]}"; do
78
+        $first || echo -n "$delim"
79
+        first=false
80
+        echo -n "$item"
81
+    done
82
+}
83
+
84
+__arr__val_iarr() {
85
+    #
86
+    # Validate input array named $1
87
+    #
88
+    local name=$1
89
+    set | grep -q "^$name=" || { warn "no such array: $name"; return 2; }
90
+}
91
+
92
+__arr__val_oarr() {
93
+    #
94
+    # Validate output array named $1
95
+    #
96
+    local name=$1
97
+    isa__name "$name" || { warn "invalid array name: $name"; return 2; }
98
+}

+ 26
- 13
src/include-bash/pretty.sh View File

5
 #
5
 #
6
 # Application debug mode
6
 # Application debug mode
7
 #
7
 #
8
-PRETTY_DEBUG=${PRETTY_DEBUG:-false}
8
+PRETTY_DEBUG=${PRETTY_DEBUG:-}
9
 
9
 
10
 #
10
 #
11
-# List of module/function names to exclude from debuging
11
+# Comma-separated list of module/function names to exclude from debuging
12
 #
12
 #
13
 # For the sake of readability of your debug dumps, you can set this
13
 # For the sake of readability of your debug dumps, you can set this
14
 # variable to comma-separated list of module or function names that
14
 # variable to comma-separated list of module or function names that
24
 #
24
 #
25
 # Application verbosity mode
25
 # Application verbosity mode
26
 #
26
 #
27
-PRETTY_VERBOSE=${PRETTY_VERBOSE:-false}
27
+PRETTY_VERBOSE=${PRETTY_VERBOSE:-}
28
 
28
 
29
 #
29
 #
30
 # Name of pretty-printer module
30
 # Name of pretty-printer module
76
     #     debug "var1=$var1" "var2=$var2" "result=$result"
76
     #     debug "var1=$var1" "var2=$var2" "result=$result"
77
     #     debug -v var1 var2 result
77
     #     debug -v var1 var2 result
78
     #
78
     #
79
-    $PRETTY_DEBUG || return 0
79
+    test "$PRETTY_DEBUG" == true || return 0
80
     _pretty__echo "$@"
80
     _pretty__echo "$@"
81
 }
81
 }
82
 
82
 
135
     #
135
     #
136
     # Optionally, you can add -w MSG to add clarifying message
136
     # Optionally, you can add -w MSG to add clarifying message
137
     # (presented as warning) to help user understand what is
137
     # (presented as warning) to help user understand what is
138
-    # wrong with arguments they have passed.
138
+    # wrong with arguments they have passed.  Alternatively,
139
+    # -m SUBJ, -M SUBJ, -u SUBJ, and -U SUBJ are shorthands for
140
+    # error messages describing missing positional argument (-m),
141
+    # missing value to a parametrized argument (-M), unknown
142
+    # argument (-u) or unknown command (-U).  Using these
143
+    # shorthands you don't need  to provide whole message, but
144
+    # only subject in question; rest of the message is provided
145
+    # by pretty.sh.
139
     #
146
     #
140
     # Use "--" to delimit end of arguments processed by mkusage.
147
     # Use "--" to delimit end of arguments processed by mkusage.
141
     #
148
     #
154
         -E) doexit=false;   shift ;;
161
         -E) doexit=false;   shift ;;
155
         -k) es=$EXIT_OK;    shift ;;
162
         -k) es=$EXIT_OK;    shift ;;
156
         -w) cmsg="$2";      shift 2 || return 2 ;;
163
         -w) cmsg="$2";      shift 2 || return 2 ;;
164
+        -m) cmsg="no $2?";  shift 2 || return 2 ;;
165
+        -M) cmsg="missing value for: $2";    shift 2 || return 2 ;;
166
+        -u) cmsg="unknown argument: $2";     shift 2 || return 2 ;;
167
+        -U) cmsg="unknown command: $2";      shift 2 || return 2 ;;
157
         --)                 shift; break ;;
168
         --)                 shift; break ;;
158
         *)                  break ;;
169
         *)                  break ;;
159
     esac done
170
     esac done
203
     # Use "-l" to split every parameter to separate line, (useful
214
     # Use "-l" to split every parameter to separate line, (useful
204
     # or longer warnings)
215
     # or longer warnings)
205
     #
216
     #
206
-    $PRETTY_VERBOSE || return 0
217
+    test "$PRETTY_VERBOSE" == true || return 0
207
     _pretty__echo "$@"
218
     _pretty__echo "$@"
208
 }
219
 }
209
 
220
 
378
     #
389
     #
379
     # Print command line, launch it and report exit status
390
     # Print command line, launch it and report exit status
380
     #
391
     #
381
-    local c=\$
382
-    test "$(id -u)" -eq 0 && c="#"
383
-    echo "$c $*"
384
-    "$@"
385
-    echo "^ exit status: $?"
392
+    local es
393
+    echo "-- begin command $* --"
394
+    "$@"; es=$?
395
+    echo "-- end command ($es) $* --"
386
 }
396
 }
387
 
397
 
388
 
398
 
393
     local fp
403
     local fp
394
     for fp in "$@"; do
404
     for fp in "$@"; do
395
         if test "$fp" = "-"; then
405
         if test "$fp" = "-"; then
406
+            echo "-- begin pipe --"
396
             cat
407
             cat
408
+            echo "-- end pipe --"
397
         elif test -s "$fp" || test "$fp" = "/dev/stdin"; then
409
         elif test -s "$fp" || test "$fp" = "/dev/stdin"; then
398
-            echo "-- $fp --"
410
+            echo "-- begin file $fp --"
399
             cat "$fp"
411
             cat "$fp"
412
+            echo "-- end file $fp --"
400
         fi
413
         fi
401
     done
414
     done
402
 }
415
 }
415
     #
428
     #
416
     # Print "decorated" call trace (only in debug mode)
429
     # Print "decorated" call trace (only in debug mode)
417
     #
430
     #
418
-    $PRETTY_DEBUG || return 0
431
+    test "$PRETTY_DEBUG" == true || return 0
419
     local depth
432
     local depth
420
     echo "== trace =="
433
     echo "== trace =="
421
     for depth in $(seq 0 ${#FUNCNAME}); do
434
     for depth in $(seq 0 ${#FUNCNAME}); do

+ 18
- 3
src/include-bash/sfdoc.sh View File

23
 # with `_` (underscore).  These are ignored by default.  Set this to 'true' to show
23
 # with `_` (underscore).  These are ignored by default.  Set this to 'true' to show
24
 # them.
24
 # them.
25
 #
25
 #
26
+# See also $SFDOC_HIDE_REGEX.
27
+#
26
 SFDOC_SHOW_HIDDEN=${SFDOC_SHOW_HIDDEN:-false}
28
 SFDOC_SHOW_HIDDEN=${SFDOC_SHOW_HIDDEN:-false}
27
 
29
 
30
+#
31
+# Regex to override what is hidden
32
+#
33
+# Override definition of which objects are considered "private", thus hidden
34
+# by default.  Regular expression is used as Extended Regular Expressions,
35
+# is anchored on both sides and case-sensitive, e setting 'foo|bAr' matches
36
+# only objects whose entire name is either 'foo' or 'bAr'.
37
+#
38
+# See also $SFDOC_SHOW_HIDDEN.
39
+#
40
+SFDOC_HIDE_REGEX=${SFDOC_HIDE_REGEX:-_.*}
41
+
28
 sfdoc__export() {
42
 sfdoc__export() {
29
     #
43
     #
30
     # Export docs of module $2 from file $3 as format $3
44
     # Export docs of module $2 from file $3 as format $3
345
     #
359
     #
346
     local what=$1
360
     local what=$1
347
     $SFDOC_SHOW_HIDDEN && cat && return
361
     $SFDOC_SHOW_HIDDEN && cat && return
362
+    debug -v SFDOC_HIDE_REGEX
348
     case $what in
363
     case $what in
349
-        m)  grep -v ^_ ;;
350
-        v)  grep -v :_ ;;
351
-        f)  grep -v :_ ;;
364
+        m)  grep -vE "^$SFDOC_HIDE_REGEX$" ;;
365
+        v)  grep -vE ":$SFDOC_HIDE_REGEX$" ;;
366
+        f)  grep -vE ":$SFDOC_HIDE_REGEX$" ;;
352
         *)  warn "bug: invalid object type: $otype" ;;
367
         *)  warn "bug: invalid object type: $otype" ;;
353
     esac
368
     esac
354
 }
369
 }

+ 11
- 8
src/include-bash/sfpi.sh View File

226
     local key=$2    # meta-data key
226
     local key=$2    # meta-data key
227
     local value     # ^^ value
227
     local value     # ^^ value
228
     local fun="${SFPI__PREFIX}_${plg}__sfpimeta" # plg getter function name
228
     local fun="${SFPI__PREFIX}_${plg}__sfpimeta" # plg getter function name
229
-    if type -t "$fun" >/dev/null; then
230
-        value=$("$fun" "$key")
231
-        debug -v fun key value
232
-        test -n "$value" || value="(none)"
233
-    else
234
-        value='(undefined)'
235
-    fi
236
-    echo "$value"
229
+    (
230
+        sfpi__import "$plg"
231
+        if type -t "$fun" >/dev/null; then
232
+            value=$("$fun" "$key")
233
+            debug -v fun key value
234
+            test -n "$value" || value="(none)"
235
+        else
236
+            value='(undefined)'
237
+        fi
238
+        echo "$value"
239
+    )
237
 }
240
 }
238
 
241
 
239
 sfpi__ls_all() {
242
 sfpi__ls_all() {

+ 95
- 0
src/include-sh/coerce.sh View File

1
+#!/bin/sh
2
+
3
+#
4
+# Data coercion helpers
5
+#
6
+# This module provides several simple functions to help transform
7
+# data to fit certain constraints.
8
+#
9
+
10
+#
11
+# Replacement character
12
+#
13
+COERCE__REPCHAR=${COERCE__REPCHAR:-�}
14
+
15
+coerce__nocolor() {
16
+    #
17
+    # Remove ANSI color codes
18
+    #
19
+    if $__COERCE__LEGACY; then
20
+        sed 's/\x1b\[[0-9;]*m//g'
21
+    else
22
+        perl -CS -Mutf8 -MTerm::ANSIColor=colorstrip -ne 'print colorstrip $_;'
23
+    fi
24
+}
25
+
26
+coerce__noctl() {
27
+    #
28
+    # Replace non-printable characters with $COERCE__REPCHAR
29
+    #
30
+    # Keep only characters that have grapgical representation ([:graph:] POSIX
31
+    # class), newline, tab and space.  Replace rest with $COERCE__REPCHAR.
32
+    #
33
+    perl -CS -Mutf8 -pe "s|[^[:graph:] \t\n]|$COERCE__REPCHAR|g"
34
+}
35
+
36
+coerce__nofdraw() {
37
+    #
38
+    # Replace frame-drawing characters with ASCII
39
+    #
40
+    # Replace frame-drawing characters according
41
+    # to following mapping:
42
+    #
43
+    #     ┌ ┬ ┐ └ ┴ ┘ ├ ┤ │ ┼ ─
44
+    #
45
+    #     ' ' ' . . . | | | | -
46
+    #
47
+    # This converts frame-drawing to ASCII, making it
48
+    # safer when fonts on terminals are not rendering
49
+    # properly.
50
+    #
51
+    perl -CS -Mutf8 -pe "
52
+        tr{┌┬┐}{.};
53
+        tr{└┴┘}{'};
54
+        tr{├┼┤│}{|};
55
+        tr{─}{-};
56
+    "
57
+}
58
+
59
+coerce__for_yaml() {
60
+    #
61
+    # Replace yaml-invalid characters
62
+    #
63
+    # Yaml won't allow all characters:
64
+    #
65
+    # > [...] The allowed character range explicitly excludes the C0 control
66
+    # > block #x0-#x1F (except for TAB #x9, LF #xA, and CR #xD which are
67
+    # > allowed), DEL #x7F, the C1 control block #x80-#x9F (except for NEL
68
+    # > #x85 which is allowed), the surrogate block #xD800-#xDFFF, #xFFFE,
69
+    # > and #xFFFF.
70
+    #
71
+    # so take stdin and replace all unacceptable characters with '�'.
72
+    #
73
+    perl -CS -Mutf8 -pe "tr/$(__coerce__for_yaml_bad)/$COERCE__REPCHAR/"
74
+}
75
+
76
+#
77
+# Legacy mode
78
+#
79
+# If 'true', avoids using Term::ANSIColor in favor of a local hacky
80
+# sed expression yanked from stackoverflow.  Use if Term::ANSIColor
81
+# is not available (as is case of eg. RHEL-6).
82
+#
83
+__COERCE__LEGACY=${__COERCE__LEGACY:-false}
84
+
85
+__coerce__for_yaml_bad() {
86
+    #
87
+    # Print all YAML-bad chars
88
+    #
89
+    printf '\N{U+0}-\N{U+8}\N{U+B}\N{U+C}\N{U+E}-\N{U+1F}'    # C0 with some gaps
90
+    printf '\N{U+7F}'                                         # DEL alone
91
+    printf '\N{U+80}-\N{U+84}\N{U+86}-\N{U+9F}'               # C1 with NEL gap
92
+    # printf -n '\N{U+D800}-\N{U+DFFF}'                        # surrogates
93
+    # printf -n '\N{U+FFFE}-\N{U+FFFF}'                        # 0xFFFE and 0xFFFF
94
+    #FIXME: for some reasons perl complains about these ^^
95
+}

+ 1
- 0
src/include-sh/mdfmt.sh View File

78
     test "$Elem" == h2 && { cat; return; }
78
     test "$Elem" == h2 && { cat; return; }
79
     test "$Elem" == h3 && { cat; return; }
79
     test "$Elem" == h3 && { cat; return; }
80
     pfx=$(
80
     pfx=$(
81
+        #shellcheck disable=SC2034
81
         for n in $(seq 1 "$Nest"); do
82
         for n in $(seq 1 "$Nest"); do
82
             printf '    '
83
             printf '    '
83
         done
84
         done

+ 15
- 1
src/shellfu.sh.skel View File

173
                 shellfu __die "cannot import module: $mname"
173
                 shellfu __die "cannot import module: $mname"
174
             fi
174
             fi
175
             ;;
175
             ;;
176
+        reload)
177
+            #
178
+            # Reload module named $1 (unsafe!)
179
+            #
180
+            local mname=$1
181
+            shellfu _is_imported "$mname" || {
182
+                shellfu __warn "cannot reload; not imported yet: $mname"
183
+                return 2
184
+            }
185
+            if ! shellfu _do_import "$mname"; then
186
+                shellfu __die "cannot reload module: $mname"
187
+            fi
188
+            ;;
176
         try_import)
189
         try_import)
177
             #
190
             #
178
             # Try if module $1 could be imported
191
             # Try if module $1 could be imported
185
             # Show version of module named $1
198
             # Show version of module named $1
186
             #
199
             #
187
             local mname=$1
200
             local mname=$1
201
+            local mpath
188
             mpath=$(shellfu _select_mfile "$mname") || {
202
             mpath=$(shellfu _select_mfile "$mname") || {
189
                 shellfu __warn "cannot find module: $mname"
203
                 shellfu __warn "cannot find module: $mname"
190
                 return 3
204
                 return 3
230
             # True if module $1 is already imported
244
             # True if module $1 is already imported
231
             #
245
             #
232
             local mname=$1
246
             local mname=$1
233
-            echo "$__SHELLFU_IMPORTED" | tr : \\n | grep -qx "$mname"
247
+            echo "$__SHELLFU_IMPORTED" | tr : \\n | grep -qxe "$mname"
234
             ;;
248
             ;;
235
 
249
 
236
         _list_mfiles)
250
         _list_mfiles)

+ 229
- 0
tests/arr/TF_RUN View File

1
+#!/bin/bash
2
+#shellcheck disable=SC1090
3
+
4
+. "$TF_DIR/include/subtest.sh"
5
+. "$TF_DIR/include/tools.sh"
6
+
7
+. "$(sfpath)" || tf_exit_error "failed to init shellfu"
8
+export PRETTY=plain
9
+shellfu import arr    || tf_exit_error "failed to import arr"
10
+
11
+
12
+tf_enum_subtests() {
13
+    echo arr__fromcmd,inval
14
+    echo arr__fromcmd,c_null
15
+    echo arr__fromcmd,c_zlz
16
+    echo arr__fromcmd,c_lzl
17
+    echo arr__fromcmd,c_lz
18
+    echo arr__fromcmd,c_zl
19
+    echo arr__fromcmd,c_zz
20
+    echo arr__fromcmd,c_zzz
21
+    echo arr__fromcmd,c_l
22
+    echo arr__fromcmd,c_ll
23
+    echo arr__fromcmd,c_lll
24
+    echo arr__has,nsuch
25
+    echo arr__has,c_null
26
+    echo arr__has,w_null_yes
27
+    echo arr__has,w_null_not
28
+    echo arr__has,m_not
29
+    echo arr__has,m_mid
30
+    echo arr__has,m_a
31
+    echo arr__has,m_z
32
+    echo arr__join,nsuch
33
+    echo arr__join,d_null
34
+    echo arr__join,c_null
35
+    echo arr__join,d_mult
36
+    echo arr__join,c_zz
37
+    echo arr__join,c_lll
38
+    echo arr__join,c_z
39
+    echo arr__join,c_l
40
+}
41
+
42
+bashfix() {
43
+    local x=${BASH_VERSION%%.*}
44
+    local yz=${BASH_VERSION#$x.}
45
+    local y=${yz%%.**}
46
+    if test "$x" -eq 4 && test "$y" -lt 4; then
47
+        tr -d "'"
48
+    else
49
+        cat
50
+    fi
51
+}
52
+
53
+cmd_printing() {
54
+    local what=$1
55
+    case $1 in
56
+        c_null)   : ;;
57
+        c_z)      echo ;;
58
+        c_zz)     echo; echo ;;
59
+        c_zzz)    echo; echo; echo ;;
60
+        c_l)      echo all ;;
61
+        c_ll)     echo first; echo last ;;
62
+        c_lll)    echo first; echo mid; echo last ;;
63
+        c_zlz)    echo; echo mid; echo ;;
64
+        c_lzl)    echo first; echo; echo last ;;
65
+        c_lz)     echo left; echo  ;;
66
+        c_zl)     echo; echo right  ;;
67
+    esac
68
+}
69
+
70
+mkadecl() {
71
+    local name=$1
72
+    local shape=$2
73
+    echo -n "declare -a $name="
74
+    case $shape in
75
+        inval)    echo '()' ;;
76
+        c_null)   echo '()' ;;
77
+        c_z)      echo '([0]="")' ;;
78
+        c_zz)     echo '([0]="" [1]="")' ;;
79
+        c_zzz)    echo '([0]="" [1]="" [2]="")' ;;
80
+        c_l)      echo '([0]="all")' ;;
81
+        c_ll)     echo '([0]="first" [1]="last")' ;;
82
+        c_lll)    echo '([0]="first" [1]="mid" [2]="last")' ;;
83
+        c_zlz)    echo '([0]="" [1]="mid" [2]="")' ;;
84
+        c_lzl)    echo '([0]="first" [1]="" [2]="last")' ;;
85
+        c_lz)     echo '([0]="left" [1]="")' ;;
86
+        c_zl)     echo '([0]="" [1]="right")' ;;
87
+    esac
88
+}
89
+
90
+getprop() {
91
+    #
92
+    # Make artifact or oracle $what for $TestName
93
+    #
94
+    local what=$1
95
+    case $TestName:$what in
96
+
97
+        # arr__fromcmd
98
+
99
+        arr__fromcmd,inval:args)    echo bad=array=name; echo echo; echo hello ;;
100
+        arr__fromcmd,c_*:args)      echo cout; echo cmd_printing; echo "${TestName#*,}" ;;
101
+
102
+        arr__fromcmd,*:o_decl)      mkadecl cout "${TestName#*,}" ;;
103
+
104
+        arr__fromcmd,inval:o_es)    echo 2 ;;
105
+        arr__fromcmd,*:o_es)        echo 0 ;;
106
+
107
+        # arr__has
108
+
109
+        arr__has,w_null_yes:a_decl) mkadecl cin c_lzl ;;
110
+        arr__has,w_null_not:a_decl) mkadecl cin c_lll ;;
111
+        arr__has,c_null:a_decl)     mkadecl cin c_null ;;
112
+        arr__has,m_not:a_decl)      mkadecl cin c_lll ;;
113
+        arr__has,m_mid:a_decl)      mkadecl cin c_lll ;;
114
+        arr__has,m_a:a_decl)        mkadecl cin c_lll ;;
115
+        arr__has,m_z:a_decl)        mkadecl cin c_lll ;;
116
+
117
+        arr__has,nsuch:args)        echo foo; echo nonesuch ;;
118
+        arr__has,c_null:args)       echo foo;   echo cin ;;
119
+        arr__has,w_null*:args)      echo "";    echo cin ;;
120
+        arr__has,m_not:args)        echo bar;   echo cin ;;
121
+        arr__has,m_mid:args)        echo mid;   echo cin ;;
122
+        arr__has,m_a:args)          echo first; echo cin ;;
123
+        arr__has,m_z:args)          echo last;  echo cin ;;
124
+
125
+        arr__has,nsuch:o_es)        echo 2 ;;
126
+        arr__has,m_not:o_es)        echo 1 ;;
127
+        arr__has,c_null:o_es)       echo 1 ;;
128
+        arr__has,w_null_not:o_es)   echo 1 ;;
129
+        arr__has,*:o_es)            echo 0 ;;
130
+
131
+        # arr__join
132
+
133
+        arr__join,nsuch:a_decl)     mkadecl cin c_lll ;;
134
+        arr__join,d_null:a_decl)    mkadecl cin c_lll ;;
135
+        arr__join,d_mult:a_decl)    mkadecl cin c_lll ;;
136
+        arr__join,c_null:a_decl)    mkadecl cin c_null ;;
137
+        arr__join,c_zz:a_decl)      mkadecl cin c_zz ;;
138
+        arr__join,c_zzz:a_decl)     mkadecl cin c_zzz ;;
139
+        arr__join,c_lll:a_decl)     mkadecl cin c_lll ;;
140
+        arr__join,c_z:a_decl)       mkadecl cin c_z ;;
141
+        arr__join,c_l:a_decl)       mkadecl cin c_l ;;
142
+
143
+        arr__join,nsuch:args)       echo oxo;  echo nonesuch ;;
144
+        arr__join,d_null:args)      echo "";   echo cin ;;
145
+        arr__join,d_mult:args)      echo oxo;  echo cin ;;
146
+        arr__join,c_null:args)      echo .;    echo cin ;;
147
+        arr__join,c_zz:args)        echo .;    echo cin ;;
148
+        arr__join,c_zzz:args)       echo .;    echo cin ;;
149
+        arr__join,c_lll:args)       echo .;    echo cin ;;
150
+        arr__join,c_z:args)         echo .;    echo cin ;;
151
+        arr__join,c_l:args)         echo .;    echo cin ;;
152
+
153
+        arr__join,nsuch:o_out)      echo -n ;;
154
+        arr__join,d_null:o_out)     echo -n firstmidlast ;;
155
+        arr__join,d_mult:o_out)     echo -n firstoxomidoxolast ;;
156
+        arr__join,c_null:o_out)     echo -n '' ;;
157
+        arr__join,c_zz:o_out)       echo -n . ;;
158
+        arr__join,c_zzz:o_out)      echo -n .. ;;
159
+        arr__join,c_lll:o_out)      echo -n first.mid.last ;;
160
+        arr__join,c_z:o_out)        echo -n ;;
161
+        arr__join,c_l:o_out)        echo -n all ;;
162
+
163
+        arr__join,nsuch:o_es)       echo 2 ;;
164
+        arr__join,*:o_es)           echo 0 ;;
165
+
166
+    esac
167
+}
168
+
169
+mkresult() {
170
+    local sut
171
+    local ttype
172
+    local args=()
173
+    local cout=()
174
+    local cin=()
175
+    local r_es
176
+    ttype=${TestName##*,}
177
+    sut=${TestName%,$ttype}
178
+    type -t "$sut" >/dev/null || {
179
+        tf_exit_error "no such function: $sut"
180
+    }
181
+    readarray -t args <<<"$(getprop args)"
182
+    tf_debugv args
183
+    case $sut in
184
+        arr__fromcmd)
185
+            cout=()
186
+            "$sut" "${args[@]}"; r_es=$?
187
+            declare -p cout | bashfix
188
+            ;;
189
+        *)
190
+            eval "$(getprop a_decl)"
191
+            "$sut" "${args[@]}"; r_es=$?
192
+            ;;
193
+    esac
194
+    return "$r_es"
195
+}
196
+
197
+mkoracle() {
198
+    case $TestName in
199
+        *,nsuch)
200
+            echo 'no such array: nonesuch' >&2
201
+            ;;
202
+        arr__fromcmd,inval)
203
+            getprop o_decl
204
+            echo 'invalid array name: bad=array=name' >&2
205
+            ;;
206
+        arr__fromcmd,*)
207
+            getprop o_decl
208
+            ;;
209
+        arr__join,*)
210
+            getprop o_out
211
+            ;;
212
+    esac >"oracle/$TestName.out" 2>"oracle/$TestName.err"
213
+}
214
+
215
+tf_do_subtest() {
216
+    local TestName=$1
217
+    local o_es
218
+    mkdir -p oracle result
219
+    o_es=$(getprop o_es)
220
+    mkoracle
221
+    tf_testflt \
222
+        -n "$TestName" \
223
+        -O "oracle/$TestName.out" \
224
+        -E "oracle/$TestName.err" \
225
+        -S "$o_es" \
226
+        "mkresult"
227
+}
228
+
229
+tf_do_subtests

+ 2
- 0
tests/shellfu_api/TF_RUN View File

12
         -e _pretty_journald \
12
         -e _pretty_journald \
13
         -e _pretty_html \
13
         -e _pretty_html \
14
         -e sync \
14
         -e sync \
15
+        -e _pretty_forcecolor \
15
         -e _pretty_color \
16
         -e _pretty_color \
16
         -e inigrep \
17
         -e inigrep \
17
         -e _pretty_notify \
18
         -e _pretty_notify \
22
         -e sfdoc \
23
         -e sfdoc \
23
         -e sfpi \
24
         -e sfpi \
24
         -e mdfmt \
25
         -e mdfmt \
26
+        -e arr \
25
     #FIXME: remove the filter when TFKit learns to test in sandbox
27
     #FIXME: remove the filter when TFKit learns to test in sandbox
26
 }
28
 }
27
 
29
 

+ 11
- 0
tests/shellfu_api/oracle/functions.stdout View File

5
 _pretty_color:_pretty__mkusage
5
 _pretty_color:_pretty__mkusage
6
 _pretty_color:_pretty__think
6
 _pretty_color:_pretty__think
7
 _pretty_color:_pretty__warn
7
 _pretty_color:_pretty__warn
8
+_pretty_forcecolor:_pretty__debug
9
+_pretty_forcecolor:_pretty__die
10
+_pretty_forcecolor:_pretty__mkhelp
11
+_pretty_forcecolor:_pretty__mkusage
12
+_pretty_forcecolor:_pretty__think
13
+_pretty_forcecolor:_pretty__warn
8
 _pretty_html:_pretty__debug
14
 _pretty_html:_pretty__debug
9
 _pretty_html:_pretty__die
15
 _pretty_html:_pretty__die
10
 _pretty_html:_pretty__mkhelp
16
 _pretty_html:_pretty__mkhelp
29
 _pretty_plain:_pretty__mkusage
35
 _pretty_plain:_pretty__mkusage
30
 _pretty_plain:_pretty__think
36
 _pretty_plain:_pretty__think
31
 _pretty_plain:_pretty__warn
37
 _pretty_plain:_pretty__warn
38
+arr:__arr__val_iarr
39
+arr:__arr__val_oarr
40
+arr:arr__fromcmd
41
+arr:arr__has
42
+arr:arr__join
32
 charmenu:charmenu
43
 charmenu:charmenu
33
 exit:exit_error
44
 exit:exit_error
34
 exit:exit_no
45
 exit:exit_no

+ 2
- 0
tests/shellfu_api/oracle/modules.stdout View File

1
 _pretty_color
1
 _pretty_color
2
+_pretty_forcecolor
2
 _pretty_html
3
 _pretty_html
3
 _pretty_journald
4
 _pretty_journald
4
 _pretty_notify
5
 _pretty_notify
5
 _pretty_plain
6
 _pretty_plain
7
+arr
6
 charmenu
8
 charmenu
7
 exit
9
 exit
8
 inigrep
10
 inigrep

+ 7
- 0
tests/shellfu_api/oracle/variables.stdout View File

4
 _pretty_color:_PRETTY_COLOR_THINK
4
 _pretty_color:_PRETTY_COLOR_THINK
5
 _pretty_color:_PRETTY_COLOR_USAGE_IS
5
 _pretty_color:_PRETTY_COLOR_USAGE_IS
6
 _pretty_color:_PRETTY_COLOR_WARN
6
 _pretty_color:_PRETTY_COLOR_WARN
7
+_pretty_forcecolor:_PRETTY_FORCECOLOR_DEBUG
8
+_pretty_forcecolor:_PRETTY_FORCECOLOR_DIE
9
+_pretty_forcecolor:_PRETTY_FORCECOLOR_OFF
10
+_pretty_forcecolor:_PRETTY_FORCECOLOR_THINK
11
+_pretty_forcecolor:_PRETTY_FORCECOLOR_USAGE_IS
12
+_pretty_forcecolor:_PRETTY_FORCECOLOR_WARN
7
 charmenu:CHARMENU_FILE
13
 charmenu:CHARMENU_FILE
8
 exit:EXIT_ERROR
14
 exit:EXIT_ERROR
9
 exit:EXIT_NO
15
 exit:EXIT_NO
16
 pretty:PRETTY_DEBUG_EXCLUDE
22
 pretty:PRETTY_DEBUG_EXCLUDE
17
 pretty:PRETTY_USAGE
23
 pretty:PRETTY_USAGE
18
 pretty:PRETTY_VERBOSE
24
 pretty:PRETTY_VERBOSE
25
+sfdoc:SFDOC_HIDE_REGEX
19
 sfdoc:SFDOC_SHOW_HIDDEN
26
 sfdoc:SFDOC_SHOW_HIDDEN
20
 sfpi:SFPI__PREFIX
27
 sfpi:SFPI__PREFIX
21
 termcolors:TERMCOLORS_BLACK
28
 termcolors:TERMCOLORS_BLACK

+ 30
- 9
utils/mkit/include/build.sh View File

71
     local line          # each line on stdin
71
     local line          # each line on stdin
72
     local mname         # each macro name
72
     local mname         # each macro name
73
     local -A MacroMap   # macro value map
73
     local -A MacroMap   # macro value map
74
+    MacroMap[__MKIT_MKIT_VERSION__]=$MKIT_VERSION
74
     MacroMap[__MKIT_PROJ_NAME__]=$(ini 1value project:name)
75
     MacroMap[__MKIT_PROJ_NAME__]=$(ini 1value project:name)
75
     MacroMap[__MKIT_PROJ_CODENAME__]=$(ini 1value project:codename)
76
     MacroMap[__MKIT_PROJ_CODENAME__]=$(ini 1value project:codename)
76
     MacroMap[__MKIT_PROJ_LICENSE__]=$(ini 1value project:license)
77
     MacroMap[__MKIT_PROJ_LICENSE__]=$(ini 1value project:license)
79
     MacroMap[__MKIT_PROJ_MAINTAINER__]=$(ini 1value project:maintainer)
80
     MacroMap[__MKIT_PROJ_MAINTAINER__]=$(ini 1value project:maintainer)
80
     MacroMap[__MKIT_PROJ_VCS_BROWSER__]=$(ini 1value project:vcs_browser)
81
     MacroMap[__MKIT_PROJ_VCS_BROWSER__]=$(ini 1value project:vcs_browser)
81
     MacroMap[__MKIT_PROJ_GIT_LASTHASH__]=$(__cached git_lasthash)
82
     MacroMap[__MKIT_PROJ_GIT_LASTHASH__]=$(__cached git_lasthash)
83
+    MacroMap[__MKIT_PROJ_GIT_LASTSUMMARY__]=$(__cached git_lastsummary)
82
     MacroMap[__MKIT_PROJ_VERSION__]=$(__cached semver)
84
     MacroMap[__MKIT_PROJ_VERSION__]=$(__cached semver)
83
-    MacroMap[__MKIT_SELF_VERSION__]=$MKIT_VERSION
84
     for section in "$@"; do
85
     for section in "$@"; do
85
         for mname in $(ini lskeys "$section"); do
86
         for mname in $(ini lskeys "$section"); do
86
             MacroMap[$mname]=$(ini values "$section:$mname")
87
             MacroMap[$mname]=$(ini values "$section:$mname")
161
     #
162
     #
162
     local file=$1
163
     local file=$1
163
     mkdir -p "$MKIT_LOCAL"
164
     mkdir -p "$MKIT_LOCAL"
164
-    echo "$file" >> "$MKIT_LOCAL/built.lst"
165
+    echo "1:$file" >> "$MKIT_LOCAL/built.lst"
165
 }
166
 }
166
 
167
 
167
 _mkit_data() {
168
 _mkit_data() {
176
     sections+=( $(ini lssect | grep ':macros$') )
177
     sections+=( $(ini lssect | grep ':macros$') )
177
     {
178
     {
178
         echo "(builtin):"
179
         echo "(builtin):"
180
+        echo "  x_MKIT_MKIT_VERSION__ => '__MKIT_MKIT_VERSION__'"
179
         echo "  x_MKIT_PROJ_NAME__ => '__MKIT_PROJ_NAME__'"
181
         echo "  x_MKIT_PROJ_NAME__ => '__MKIT_PROJ_NAME__'"
180
         echo "  x_MKIT_PROJ_CODENAME__ => '__MKIT_PROJ_CODENAME__'"
182
         echo "  x_MKIT_PROJ_CODENAME__ => '__MKIT_PROJ_CODENAME__'"
181
         echo "  x_MKIT_PROJ_LICENSE__ => '__MKIT_PROJ_LICENSE__'"
183
         echo "  x_MKIT_PROJ_LICENSE__ => '__MKIT_PROJ_LICENSE__'"
184
         echo "  x_MKIT_PROJ_MAINTAINER__ => '__MKIT_PROJ_MAINTAINER__'"
186
         echo "  x_MKIT_PROJ_MAINTAINER__ => '__MKIT_PROJ_MAINTAINER__'"
185
         echo "  x_MKIT_PROJ_VCS_BROWSER__ => '__MKIT_PROJ_VCS_BROWSER__'"
187
         echo "  x_MKIT_PROJ_VCS_BROWSER__ => '__MKIT_PROJ_VCS_BROWSER__'"
186
         echo "  x_MKIT_PROJ_GIT_LASTHASH__ => '__MKIT_PROJ_GIT_LASTHASH__'"
188
         echo "  x_MKIT_PROJ_GIT_LASTHASH__ => '__MKIT_PROJ_GIT_LASTHASH__'"
189
+        echo "  x_MKIT_PROJ_GIT_LASTSUMMARY__ => '__MKIT_PROJ_GIT_LASTSUMMARY__'"
187
         echo "  x_MKIT_PROJ_VERSION__ => '__MKIT_PROJ_VERSION__'"
190
         echo "  x_MKIT_PROJ_VERSION__ => '__MKIT_PROJ_VERSION__'"
188
-        echo "  x_MKIT_SELF_VERSION__ => '__MKIT_SELF_VERSION__'"
189
         for section in "${sections[@]}"; do
191
         for section in "${sections[@]}"; do
190
             echo "$section:"
192
             echo "$section:"
191
             for macro in $(ini lskeys "$section"); do
193
             for macro in $(ini lskeys "$section"); do
212
     #
214
     #
213
     # Clean up tree after building
215
     # Clean up tree after building
214
     #
216
     #
215
-    test -f "$MKIT_LOCAL/built.lst" && {
216
-        <"$MKIT_LOCAL/built.lst" grep -v -e '\.\.' -e ^/ \
217
-          | xargs -r rm -rf
218
-        rm -f "$MKIT_LOCAL/built.lst"
219
-        rmdir --ignore-fail-on-non-empty "$MKIT_LOCAL"
220
-    }
217
+    local path
218
+    local line
219
+    local depth
220
+    test -f "$MKIT_LOCAL/built.lst" || return 0
221
+    {
222
+        cat "$MKIT_LOCAL/built.lst"
223
+        echo "1:$MKIT_LOCAL/built.lst"
224
+        echo "1:$MKIT_LOCAL"
225
+    } \
226
+      | grep -v -e '\.\.' -e ^/ -e '^~' \
227
+      | while IFS=: read -r depth path; do
228
+            test -e "$path" || continue
229
+            case $depth in
230
+                1)  warn "removing: $path"
231
+                    test -d "$path" \
232
+                     && rmdir -p --ignore-fail-on-non-empty "$path"
233
+                    test -f "$path" && rm "$path"
234
+                    ;;
235
+                r)  warn "removing recursively: $path"
236
+                    rm -r "$path"
237
+                    ;;
238
+                *)  warn "invalid built.lst format!"
239
+                    ;;
240
+            esac
241
+        done
221
     true
242
     true
222
 }
243
 }
223
 
244
 

+ 1
- 1
utils/mkit/include/deploy.sh View File

35
         find "$src" -type f \
35
         find "$src" -type f \
36
           | while read -r item; do
36
           | while read -r item; do
37
                 [[ $item =~ .skel$ ]] \
37
                 [[ $item =~ .skel$ ]] \
38
-                 && grep -q "${item%.skel}" "$MKIT_LOCAL/built.lst" \
38
+                 && grep -qe "${item%.skel}" "$MKIT_LOCAL/built.lst" \
39
                  && continue
39
                  && continue
40
                 __deploy_item "$item" "$dst${item#$src}" "$mode"
40
                 __deploy_item "$item" "$dst${item#$src}" "$mode"
41
             done
41
             done

+ 42
- 17
utils/mkit/include/facts.sh View File

103
     git_bool dirty && echo -n ".dirty"
103
     git_bool dirty && echo -n ".dirty"
104
 }
104
 }
105
 
105
 
106
+git_lastsummary() {
107
+    #
108
+    # Show last commit summary
109
+    #
110
+    # We can't do it outside git repo (or without git) but we should
111
+    # not be asked to; targets that don't require git should make use
112
+    # of cache built by dist target.
113
+    #
114
+    git_present || {
115
+        echo UNKNOWN_SUMMARY
116
+        warn "no git present; could not determine last summary"
117
+        return 3
118
+    }
119
+    git_bool dirty && {
120
+        echo "(index is dirty)"
121
+        return
122
+    }
123
+    git log -1 --format=oneline HEAD \
124
+      | cut -d' ' -f2-
125
+}
126
+
106
 semver() {
127
 semver() {
107
     #
128
     #
108
     # Build proper SemVer version string
129
     # Build proper SemVer version string
141
     local xyz           # base version string
162
     local xyz           # base version string
142
     local prerl         # pre-release keyword (from mkit.ini, eg. 'beta')
163
     local prerl         # pre-release keyword (from mkit.ini, eg. 'beta')
143
     local latest_tag    # latest git tag
164
     local latest_tag    # latest git tag
144
-    local commit        # commit indicator (CURRENT_BRANCH.gHASH)
145
-    local dirty=F       # F if dirty, T if clean
146
-    local btime         # timestamp or nothing (see $MKIT_TTAG)
165
+    local brname        # current branch name
166
+    local ghash         # current commit short hash
167
+    local is_tagged=T   # T if tagged (clean, no metadata), F if devel
168
+    local is_dirty=F    # F if dirty, T if clean
169
+    local stamp         # timestamp or nothing (see $MKIT_TSTAMP)
147
     local suffix        # version suffix
170
     local suffix        # version suffix
148
     prerl=$(ini 1value project:prerl)
171
     prerl=$(ini 1value project:prerl)
149
-    case $MKIT_TTAG in
150
-        none)   btime= ;;
151
-        btime)  btime=$(date -u +%Y%m%d%H%M%S) ;;
152
-        ctime)  btime=$(date -d @"$(git_fact latest_cdate)" -u +%Y%m%d%H%M%S) ;;
172
+    case $MKIT_TSTAMP in
173
+        none)   stamp= ;;
174
+        btime)  stamp=$(date -u +%Y%m%d%H%M%S) ;;
175
+        ctime)  stamp=$(date -d @"$(git_fact latest_cdate)" -u +%Y%m%d%H%M%S) ;;
153
     esac
176
     esac
154
     grep ":" <<<"$prerl" \
177
     grep ":" <<<"$prerl" \
155
      && warn "colon in project:prerl may corrupt version data: $prerl"
178
      && warn "colon in project:prerl may corrupt version data: $prerl"
168
     esac
191
     esac
169
     if ! git describe --tags --exact-match HEAD >&/dev/null;
192
     if ! git describe --tags --exact-match HEAD >&/dev/null;
170
     then    # we are at a later commit than the last tag
193
     then    # we are at a later commit than the last tag
171
-        commit="$(git_fact current_branch).g$(git_fact latest_sha)"
194
+        is_tagged=F
195
+        brname=$(git_fact current_branch | sed 's/[^[:alnum:]]/_/g')
196
+        ghash=$(git_fact latest_sha)
172
     fi
197
     fi
173
-    git_bool dirty && dirty=T
174
-    case "$dirty:$btime:$commit" in
175
-        F:*:)   suffix=""                       ;;
176
-        T::)    suffix="+dirty"                 ;;
177
-        T:*:)   suffix="+t$btime.dirty"           ;;
178
-        F::*)   suffix="+$commit"               ;;
179
-        F:*:*)  suffix="+t$btime.$commit"         ;;
180
-        T::*)   suffix="+$commit.dirty"         ;;
181
-        T:*:*)  suffix="+t$btime.$commit.dirty"   ;;
198
+    git_bool dirty && is_dirty=T
199
+    case "$is_dirty:$is_tagged:$stamp" in
200
+        F:T:*)  suffix=""                                 ;;
201
+        T:T:)   suffix="+dirty"                           ;;
202
+        T:T:*)  suffix="+t$stamp.dirty"                   ;;
203
+        F:F:)   suffix="+$brname.g$ghash"                 ;;
204
+        F:F:*)  suffix="+t$stamp.$brname.g$ghash"         ;;
205
+        T:F:)   suffix="+$brname.g$ghash.dirty"           ;;
206
+        T:F:*)  suffix="+t$stamp.$brname.g$ghash.dirty"   ;;
182
         *)      suffix=MKIT_BUG
207
         *)      suffix=MKIT_BUG
183
                 warn "MKIT_BUG: bad dirt/commit detection" ;;
208
                 warn "MKIT_BUG: bad dirt/commit detection" ;;
184
     esac
209
     esac

+ 53
- 15
utils/mkit/include/ini.sh View File

8
     #
8
     #
9
     local line      # each line
9
     local line      # each line
10
     while read -r line; do
10
     while read -r line; do
11
-        printf -- "%s\n" "$line"
11
+        printf -- '%s\n' "$line"
12
     done
12
     done
13
 }
13
 }
14
 
14
 
17
     # Expand reference value (prefix only)
17
     # Expand reference value (prefix only)
18
     #
18
     #
19
     local line      # each input line
19
     local line      # each input line
20
-    local suffix    # tail of the line
21
-    local ref       # reference
22
-    local value     # value if reference
23
     while read -r line; do                  # [foo:bar]/path
20
     while read -r line; do                  # [foo:bar]/path
24
-        suffix="${line#\[*\]}"              # /path
25
-        ref="${line%$suffix}"               # [foo:bar]
26
-        ref="${ref%\]}"                     # [foo:bar
27
-        ref="${ref#\[}"                     # foo:bar
28
-        value="$(ini 1value "$ref")"        # foo_bar_value
29
-        printf -- "%s\n" "$value$suffix"    # foo_bar_value/path
21
+        __ini_expandln "$line"
30
     done
22
     done
31
 }
23
 }
32
 
24
 
25
+__ini_expandln() {
26
+    #
27
+    # Fully expand references in line $1
28
+    #
29
+    local line_orig=$1          # original line
30
+    local line_todo=$line_orig  # current state
31
+    local line_done             # next state
32
+    local Depth=0               # current depth
33
+    local MaxDepth=10           # maximum depth
34
+    while true; do
35
+        ((Depth++))
36
+        debug_var line_todo
37
+        test "$Depth" -le "$MaxDepth" || {
38
+            warn "expansion error: reached maximum depth: $Depth > $MaxDepth"
39
+            warn "    original line: $line_orig"
40
+            warn "    expanded line: $line_todo"
41
+            return 3
42
+        }
43
+        line_done=$(__ini_expandln_once "$line_todo")
44
+        debug_var line_done
45
+        test "$line_done" == "$line_todo" && break
46
+        line_todo=$line_done
47
+    done
48
+    echo "$line_done"
49
+}
50
+
51
+__ini_expandln_once() {
52
+    #
53
+    # Run through line $1 once and replace all references
54
+    #
55
+    local line=$1   # line to expand
56
+    local ref       # full reference (incl. brackets)
57
+    local ipath     # just ini path from ^^ (stripped brackets)
58
+    local value     # value of reference
59
+    local refs=()   # all references found in line
60
+    mapfile -t refs <<<"$(grep -Eo '[[][^]]+[]]' <<< "$line_todo")"
61
+    debug_var refs
62
+    for ref in "${refs[@]}"; do
63
+        test -n "$ref" || continue
64
+        ipath=${ref#[}; ipath=${ipath%]}
65
+        value=$(ini 1value "$ipath")
66
+        debug_var line ref ipath value
67
+        line=$(sed "s|\\[$ipath\\]|$value|" <<<"$line")
68
+    done
69
+    echo "$line"
70
+}
71
+
33
 __ini_grepcmt() {
72
 __ini_grepcmt() {
34
     #
73
     #
35
     # Remove comments from INI file on stdin
74
     # Remove comments from INI file on stdin
85
                 \[*\])    ok=false; continue ;;
124
                 \[*\])    ok=false; continue ;;
86
             esac
125
             esac
87
             $ok || continue
126
             $ok || continue
88
-            printf -- "%s\n" "$line"
127
+            printf -- '%s\n' "$line"
89
         done \
128
         done \
90
       | sed -e 's/ *= */=/; s/ +$//; s/^//;'
129
       | sed -e 's/ *= */=/; s/ +$//; s/^//;'
91
 }
130
 }
102
     #
141
     #
103
     # List all section names
142
     # List all section names
104
     #
143
     #
105
-    local arg=$1    # unused argument
106
     grep -x '\[.*\]' | sed 's/^.//; s/.$//'
144
     grep -x '\[.*\]' | sed 's/^.//; s/.$//'
107
 }
145
 }
108
 
146
 
126
     local inc                       # file to include
164
     local inc                       # file to include
127
     local incre='\[INCLUDE:.*\]'    # include directive regex
165
     local incre='\[INCLUDE:.*\]'    # include directive regex
128
     local iline                     # include directive line
166
     local iline                     # include directive line
129
-    if iline=$(grep -m1 -x "$incre" "$MKIT_INI"); then
167
+    if iline=$(grep -m1 -xe "$incre" "$MKIT_INI"); then
130
         inc=${iline#*:}; inc=${inc%]}
168
         inc=${iline#*:}; inc=${inc%]}
131
-        grep -vx "$incre" "$inc"
132
-        grep -vx "$incre" "$MKIT_INI"
169
+        grep -vxe "$incre" "$inc"
170
+        grep -vxe "$incre" "$MKIT_INI"
133
     else
171
     else
134
         cat "$MKIT_INI"
172
         cat "$MKIT_INI"
135
     fi | __ini_grepcmt
173
     fi | __ini_grepcmt

+ 5
- 4
utils/mkit/include/release.sh View File

44
         at_relsrc)
44
         at_relsrc)
45
             oracle=$(ini 1value project:relsrc)
45
             oracle=$(ini 1value project:relsrc)
46
             git_fact current_branch \
46
             git_fact current_branch \
47
-              | grep -qFx "$oracle" \
47
+              | grep -qFxe "$oracle" \
48
              || die "you are not on release source branch: $oracle"
48
              || die "you are not on release source branch: $oracle"
49
             ;;
49
             ;;
50
         not_dirty)
50
         not_dirty)
70
         ini_version)
70
         ini_version)
71
             oracle=$(git_fact latest_version | __bump_version "$rlevel")
71
             oracle=$(git_fact latest_version | __bump_version "$rlevel")
72
             ini 1value project:version  \
72
             ini 1value project:version  \
73
-              | grep -qFx "$oracle" \
73
+              | grep -qFxe "$oracle" \
74
              || die "new version not in mkit.ini: $oracle"
74
              || die "new version not in mkit.ini: $oracle"
75
             ;;
75
             ;;
76
         *)
76
         *)
154
     git add mkit.ini \
154
     git add mkit.ini \
155
       || die "failed to add mkit.ini to the index"
155
       || die "failed to add mkit.ini to the index"
156
     cache=$(mktemp -t "mkit._vbump_gitmsg.XXXXXXXX")
156
     cache=$(mktemp -t "mkit._vbump_gitmsg.XXXXXXXX")
157
-    _vbump_gitmsg > "$cache"
157
+    _vbump_gitmsg "$nextver" > "$cache"
158
     git commit -e -F "$cache"   # note: reading from stdin will break vim
158
     git commit -e -F "$cache"   # note: reading from stdin will break vim
159
     rm "$cache"
159
     rm "$cache"
160
 }
160
 }
163
     #
163
     #
164
     # Compose git message template for 'Bump version' commit
164
     # Compose git message template for 'Bump version' commit
165
     #
165
     #
166
-    echo "Bump version"
166
+    local nextver=$1
167
+    echo "Bump version to $nextver"
167
     echo ""
168
     echo ""
168
     echo "Overview of changes:"
169
     echo "Overview of changes:"
169
     echo ""
170
     echo ""

+ 3
- 3
utils/mkit/include/vars.sh View File

71
 # For example, old yum version (as of RHEL-6) will not let you install
71
 # For example, old yum version (as of RHEL-6) will not let you install
72
 # version that it deems older than is installed, making it hard to
72
 # version that it deems older than is installed, making it hard to
73
 # continually upgrade during active development.  While packaging
73
 # continually upgrade during active development.  While packaging
74
-# systems have their own rukes (and SemVer says both versions should be
74
+# systems have their own rules (and SemVer says both versions should be
75
 # considered same) this tag will make it more likely to "win" the build
75
 # considered same) this tag will make it more likely to "win" the build
76
 # you made later.
76
 # you made later.
77
 #
77
 #
81
 # Also note that 'btime' makes the version non-deterministic: merely
81
 # Also note that 'btime' makes the version non-deterministic: merely
82
 # initiating the build a second later will result in different version.
82
 # initiating the build a second later will result in different version.
83
 #
83
 #
84
-MKIT_TTAG=${MKIT_TTAG:-ctime}
84
+MKIT_TSTAMP=${MKIT_TSTAMP:-ctime}
85
 
85
 
86
 #
86
 #
87
 # This MKit version
87
 # This MKit version
88
 #
88
 #
89
-MKIT_VERSION=0.0.35
89
+MKIT_VERSION=0.0.40

+ 3
- 3
utils/mkit/stub View File

162
             echo ''
162
             echo ''
163
             echo '%changelog'
163
             echo '%changelog'
164
             echo ''
164
             echo ''
165
-            echo '# specfile built with MKit __MKIT_SELF_VERSION__'
165
+            echo '# specfile built with MKit __MKIT_MKIT_VERSION__'
166
             ;;
166
             ;;
167
 
167
 
168
         packaging/debian/copyright)
168
         packaging/debian/copyright)
185
             echo 'Description: __MKIT_PROJ_NAME__ - __MKIT_PROJ_TAGLINE__'
185
             echo 'Description: __MKIT_PROJ_NAME__ - __MKIT_PROJ_TAGLINE__'
186
             echo ' MKIT_STUB_DESCRIPTION'
186
             echo ' MKIT_STUB_DESCRIPTION'
187
             echo ''
187
             echo ''
188
-            echo '# control file built with MKit __MKIT_SELF_VERSION__'
188
+            echo '# control file built with MKit __MKIT_MKIT_VERSION__'
189
             ;;
189
             ;;
190
 
190
 
191
         packaging/debian/changelog)
191
         packaging/debian/changelog)
686
     esac
686
     esac
687
     updating && init_from_existing
687
     updating && init_from_existing
688
     if test -n "$License"; then
688
     if test -n "$License"; then
689
-        known_licenses | grep -qxF "$License" \
689
+        known_licenses | grep -qxFe "$License" \
690
          || die "unknown license (use -L to get list): $License"
690
          || die "unknown license (use -L to get list): $License"
691
         MkLicense=true
691
         MkLicense=true
692
     fi
692
     fi

+ 4
- 1
utils/tfkit/include/common.sh View File

1
+#!/bin/bash
2
+#shellcheck disable=SC2034,SC1117
3
+
1
 #
4
 #
2
 # Variables to hold exit status semantic
5
 # Variables to hold exit status semantic
3
 #
6
 #
123
     for msg in "$@";
126
     for msg in "$@";
124
     do
127
     do
125
         $TF_COLOR && echo -ne "$TF_COLOR_LBLACK" >&2
128
         $TF_COLOR && echo -ne "$TF_COLOR_LBLACK" >&2
126
-        echo "$pfx$msg$sfx" >&2;
129
+        echo "$msg" >&2;
127
         $TF_COLOR && echo -ne "$TF_COLOR_NONE" >&2
130
         $TF_COLOR && echo -ne "$TF_COLOR_NONE" >&2
128
     done
131
     done
129
 }
132
 }

+ 4
- 2
utils/tfkit/include/harness.sh View File

1
 #!/bin/bash
1
 #!/bin/bash
2
+#shellcheck disable=SC1090
2
 # tfkit - Shellfu's movable test framework
3
 # tfkit - Shellfu's movable test framework
3
 # See LICENSE file for copyright and license details.
4
 # See LICENSE file for copyright and license details.
4
 
5
 
6
+#shellcheck disable=SC2153
5
 . "$TF_DIR/include/common.sh"
7
 . "$TF_DIR/include/common.sh"
6
 
8
 
7
 
9
 
104
         tmpdir=$(mktemp -d)
106
         tmpdir=$(mktemp -d)
105
         stamp=$(date "+relics-$tname-%Y%m%d-%H%M%S")
107
         stamp=$(date "+relics-$tname-%Y%m%d-%H%M%S")
106
         cp -r "$TF_SUITE/$tname/"* "$tmpdir"
108
         cp -r "$TF_SUITE/$tname/"* "$tmpdir"
107
-        pushd "$tmpdir" >/dev/null
109
+        pushd "$tmpdir" >/dev/null || tf_exit_panic "could not chdir to: $tmpdir"
108
             TF_DEBUG=$TF_DEBUG TF_VERBOSE=$TF_VERBOSE \
110
             TF_DEBUG=$TF_DEBUG TF_VERBOSE=$TF_VERBOSE \
109
             TF_DIR="$tf_dir" TF_TEST="$tname" \
111
             TF_DIR="$tf_dir" TF_TEST="$tname" \
110
             TF_FILTER_SUBTEST=$TF_FILTER_SUBTEST \
112
             TF_FILTER_SUBTEST=$TF_FILTER_SUBTEST \
112
             tes=$?
114
             tes=$?
113
             __tf_collect_if_needed $tes
115
             __tf_collect_if_needed $tes
114
             test $tes -gt $es && es=$tes
116
             test $tes -gt $es && es=$tes
115
-        popd >/dev/null
117
+        popd >/dev/null || tf_exit_panic "could not chdir from: $tmpdir to $OLDPWD"
116
         rm -rf "$tmpdir"
118
         rm -rf "$tmpdir"
117
         if test $tes -eq 0;
119
         if test $tes -eq 0;
118
         then
120
         then

+ 1
- 0
utils/tfkit/include/subtest.sh View File

1
 #!/bin/bash
1
 #!/bin/bash
2
+#shellcheck disable=SC1090
2
 
3
 
3
 . "$TF_DIR/include/common.sh"
4
 . "$TF_DIR/include/common.sh"
4
 
5
 

+ 19
- 18
utils/tfkit/include/tools.sh View File

1
 #!/bin/bash
1
 #!/bin/bash
2
+#shellcheck disable=SC1090
2
 
3
 
3
 . "$TF_DIR/include/common.sh"
4
 . "$TF_DIR/include/common.sh"
4
 
5
 
10
     # Run a simple test for a unix filter
11
     # Run a simple test for a unix filter
11
     #
12
     #
12
     #     tf_testflt -n foo [-i foo.stdin] \
13
     #     tf_testflt -n foo [-i foo.stdin] \
13
-    #                [-O foo.stdout] [-E foo.stderr] [-S 3] \
14
+    #                [-O foo.out] [-E foo.err] [-S 3] \
14
     #                cmd arg...
15
     #                cmd arg...
15
     #
16
     #
16
-    # Will drop *result/NAME.stdout* and *result/NAME.stderr* (intentionally
17
+    # Will drop *result/NAME.out* and *result/NAME.err* (intentionally
17
     # not cleaning up).
18
     # not cleaning up).
18
     #
19
     #
19
 
20
 
44
         -S) o_es="$2";          shift 2 || { arg_err=true; break; } ;;
45
         -S) o_es="$2";          shift 2 || { arg_err=true; break; } ;;
45
         --)                     shift; break ;;
46
         --)                     shift; break ;;
46
         "")                            break ;;
47
         "")                            break ;;
47
-        -*) tf_warn "wrong testcli arg: $1"; return $TF_ES_BAILOUT ;;
48
+        -*) tf_warn "wrong testcli arg: $1"; return "$TF_ES_BAILOUT" ;;
48
         *)                             break ;;
49
         *)                             break ;;
49
     esac done
50
     esac done
50
-    $arg_err && { tf_warn "error parsing arguments: $orig_args"; return $TF_ES_BAILOUT; }
51
+    $arg_err && { tf_warn "error parsing arguments: $orig_args"; return "$TF_ES_BAILOUT"; }
51
     tf_debug "t_in='$t_in'"
52
     tf_debug "t_in='$t_in'"
52
     tf_debug "t_name='$t_name'"
53
     tf_debug "t_name='$t_name'"
53
     tf_debug "o_out='$o_out'"
54
     tf_debug "o_out='$o_out'"
55
     tf_debug "o_es='$o_es'"
56
     tf_debug "o_es='$o_es'"
56
     tf_debug "test command: $*"
57
     tf_debug "test command: $*"
57
     test "$t_in" = "-" && t_in=/dev/stdin   # works better for check below
58
     test "$t_in" = "-" && t_in=/dev/stdin   # works better for check below
58
-    test -z "$t_name"  && { tf_warn "missing test name"             ; return $TF_ES_BAILOUT; }
59
-    test -z "$1"       && { tf_warn "missing test command"          ; return $TF_ES_BAILOUT; }
60
-    test -r "$t_in"    || { tf_warn "missing input file: $t_in"     ; return $TF_ES_BAILOUT; }
61
-    test -e "$o_out"   || { tf_warn "missing oracle stdout: $o_out" ; return $TF_ES_BAILOUT; }
62
-    test -e "$o_err"   || { tf_warn "missing oracle stderr: $o_err" ; return $TF_ES_BAILOUT; }
63
-    test "$o_es" -ge 0 || { tf_warn "invalid oracle status: $o_es"  ; return $TF_ES_BAILOUT; }
59
+    test -z "$t_name"  && { tf_warn "missing test name"             ; return "$TF_ES_BAILOUT"; }
60
+    test -z "$1"       && { tf_warn "missing test command"          ; return "$TF_ES_BAILOUT"; }
61
+    test -r "$t_in"    || { tf_warn "missing input file: $t_in"     ; return "$TF_ES_BAILOUT"; }
62
+    test -e "$o_out"   || { tf_warn "missing oracle stdout: $o_out" ; return "$TF_ES_BAILOUT"; }
63
+    test -e "$o_err"   || { tf_warn "missing oracle stderr: $o_err" ; return "$TF_ES_BAILOUT"; }
64
+    test "$o_es" -ge 0 || { tf_warn "invalid oracle status: $o_es"  ; return "$TF_ES_BAILOUT"; }
64
 
65
 
65
     # prepare
66
     # prepare
66
     #
67
     #
67
     mkdir -p result
68
     mkdir -p result
68
-    r_out="result/$t_name.stdout"
69
-    r_err="result/$t_name.stderr"
69
+    r_out="result/$t_name.out"
70
+    r_err="result/$t_name.err"
70
     tf_debug "r_out='$r_out'"
71
     tf_debug "r_out='$r_out'"
71
     tf_debug "r_err='$r_err'"
72
     tf_debug "r_err='$r_err'"
72
-    touch "$r_out" || { tf_warn "cannot create tmp file: $r_out" ; return $TF_ES_BAILOUT; }
73
-    touch "$r_err" || { tf_warn "cannot create tmp file: $r_err" ; return $TF_ES_PANIC; }
73
+    touch "$r_out" || { tf_warn "cannot create tmp file: $r_out" ; return "$TF_ES_BAILOUT"; }
74
+    touch "$r_err" || { tf_warn "cannot create tmp file: $r_err" ; return "$TF_ES_PANIC"; }
74
 
75
 
75
     # run
76
     # run
76
     #
77
     #
79
 
80
 
80
     # eval/report/exit
81
     # eval/report/exit
81
     #
82
     #
82
-    test $r_es = $o_es || { tf_warn "bad exit status: $r_es (need $o_es)" ; t_es=$TF_ES_FAIL; }
83
-    $diff -u "$o_err" "$r_err" || t_es=$TF_ES_FAIL
84
-    $diff -u "$o_out" "$r_out" || t_es=$TF_ES_FAIL
85
-    return $t_es
83
+    test "$r_es" = "$o_es" || { tf_warn "bad exit status: $r_es (need $o_es)" ; t_es=$TF_ES_FAIL; }
84
+    $diff -u "$r_err" "$o_err" || t_es=$TF_ES_FAIL
85
+    $diff -u "$r_out" "$o_out" || t_es=$TF_ES_FAIL
86
+    return "$t_es"
86
 }
87
 }

+ 4
- 3
utils/tfkit/runtests View File

1
 #!/bin/bash
1
 #!/bin/bash
2
+#shellcheck disable=SC1090
2
 # tfkit - Shellfu's movable test framework
3
 # tfkit - Shellfu's movable test framework
3
 # See LICENSE file for copyright and license details.
4
 # See LICENSE file for copyright and license details.
4
 
5
 
5
-TF_VERSION="0.0.16"
6
+TF_VERSION="0.0.18"
6
 
7
 
7
 die() {
8
 die() {
8
     echo "$@" && exit 9
9
     echo "$@" && exit 9
75
         -c|--collect)           TF_COLLECT=always;          shift ;;
76
         -c|--collect)           TF_COLLECT=always;          shift ;;
76
         -C|--no-collect)        TF_COLLECT=never;           shift ;;
77
         -C|--no-collect)        TF_COLLECT=never;           shift ;;
77
         -d|--debug)             TF_DEBUG=true;              shift ;;
78
         -d|--debug)             TF_DEBUG=true;              shift ;;
78
-        -p|--prefix)            export PATH="$(readlink -f "$2")/bin:$PATH"
79
-                                                            shift 2 || usage ;;
79
+        -p|--prefix)            PATH="$(readlink -f "$2")/bin:$PATH"
80
+                                export PATH;                shift 2 || usage ;;
80
         -s|--filter-subtest)    TF_FILTER_SUBTEST="$2";     shift 2 || usage ;;
81
         -s|--filter-subtest)    TF_FILTER_SUBTEST="$2";     shift 2 || usage ;;
81
         -t|--filter-test)       TF_FILTER_TEST="$2";        shift 2 || usage ;;
82
         -t|--filter-test)       TF_FILTER_TEST="$2";        shift 2 || usage ;;
82
         -v|--verbose)           TF_VERBOSE=true;            shift ;;
83
         -v|--verbose)           TF_VERBOSE=true;            shift ;;