Browse Source

Convert to pure shellfu library

Move satcmd to other repo and distribute saturnin as a stand-alone
shellfu library.  Users can import just the library instead of
embedding it inside their source tree, which enables them to delegate
part of the job to package manager.
Alois Mahdal 8 years ago
parent
commit
c252ad53ed

+ 1
- 1
.gitignore View File

1
 .mkit/autoclean
1
 .mkit/autoclean
2
-satcmd-*.tar.gz
2
+shellfu-bash-saturnin-*.tar.gz
3
 artifacts
3
 artifacts

+ 4
- 27
mkit.ini View File

3
 [project]
3
 [project]
4
     version     = 0.3.3
4
     version     = 0.3.3
5
     codename    = StrycF
5
     codename    = StrycF
6
-    name        = SATCMD
6
+    name        = Saturnin
7
     tagline     = Spirit of your toolbox
7
     tagline     = Spirit of your toolbox
8
-    pkgname     = satcmd
8
+    pkgname     = shellfu-bash-saturnin
9
     relsrc      = master
9
     relsrc      = master
10
     reldst      = last
10
     reldst      = last
11
 
11
 
25
     PREFIX = /usr/local
25
     PREFIX = /usr/local
26
 
26
 
27
 [roots]
27
 [roots]
28
-    bin     = [ENV:PREFIX]/bin
29
-    libexec = [ENV:PREFIX]/libexec/satcmd
30
-    share   = [ENV:PREFIX]/share/satcmd
31
-    doc     = [ENV:PREFIX]/share/doc/satcmd
32
-    etc     = /etc/satcmd
33
-    etc_bc  = /etc/bash_completion.d
34
-
35
-[tokens]
36
-    __SATURNIN_CACHE_HOME__     = $HOME/.cache/satcmd
37
-    __SATURNIN_CONFIG_USER__    = $HOME/.satcmd
38
-    __SATURNIN_CONFIG_LOCAL__   = [roots:etc]
39
-    __SATURNIN_LIBEXEC__        = [roots:libexec]
40
-    __SATURNIN_SHARE__          = [roots:share]
41
-    __SATURNIN_SHELLFU_DIR__    = [roots:share]/shellfu
42
-
43
-[modes]
44
-    bin     = 755
45
-    libexec = 755
46
-    share   = 644
28
+    sfmod   = [ENV:PREFIX]/share/shellfu/include-bash
47
 
29
 
48
 [files]
30
 [files]
49
-    bin = src/bin/app satcmd
50
     doc = README.md
31
     doc = README.md
51
-    etc_bc = src/complete.bash          satcmd.bash
52
-    libexec = src/libexec/satcmd-dump
53
-    libexec = src/libexec/satcmd-echo
54
-    share = src/ini.d/main/echo.ini     ini.d/main/echo.ini
55
-    share = src/shellfu/saturnin.sh     shellfu/saturnin.sh
32
+    sfmod = src/saturnin.sh

+ 1
- 1
packaging/debian/changelog View File

1
-satcmd (__MKIT_PROJ_VERSION__-1) UNRELEASED; urgency=medium
1
+shellfu-bash-saturnin (__MKIT_PROJ_VERSION__-1) UNRELEASED; urgency=medium
2
 
2
 
3
   * Initial release. (Closes: #XXXXXX)
3
   * Initial release. (Closes: #XXXXXX)
4
 
4
 

+ 3
- 6
packaging/debian/control View File

1
-Source: satcmd
1
+Source: shellfu-bash-saturnin
2
 Maintainer: Alois Mahdal <netvor@vornet.cz>
2
 Maintainer: Alois Mahdal <netvor@vornet.cz>
3
-Vcs-Browser: http://git.vornet.cz/cgit/saturnin.git/
3
+Vcs-Browser: http://git.vornet.cz/cgit/shellfu-bash-saturnin.git/
4
 Section: misc
4
 Section: misc
5
 Priority: extra
5
 Priority: extra
6
 Standards-Version: 3.9.2
6
 Standards-Version: 3.9.2
7
 Build-Depends: debhelper (>= 9)
7
 Build-Depends: debhelper (>= 9)
8
 
8
 
9
-Package: satcmd
9
+Package: shellfu-bash-saturnin
10
 Architecture: all
10
 Architecture: all
11
 Depends: bash, shellfu-bash, shellfu-bash-core, shellfu-bash-extras
11
 Depends: bash, shellfu-bash, shellfu-bash-core, shellfu-bash-extras
12
 Description: __MKIT_PROJ_TAGLINE__
12
 Description: __MKIT_PROJ_TAGLINE__
15
  herd of untamed undocumented and hard-to-share scripts, with help of
15
  herd of untamed undocumented and hard-to-share scripts, with help of
16
  saturnin you can easily create and package a meta-command with a set
16
  saturnin you can easily create and package a meta-command with a set
17
  of discoverable and maintainable sub-commands.
17
  of discoverable and maintainable sub-commands.
18
- .
19
- This package contains a demo meta-command built with saturnin; you can
20
- also re-use the source as template for your new toolkit.
21
 
18
 
22
 # control file built with MKit __MKIT_SELF_VERSION__
19
 # control file built with MKit __MKIT_SELF_VERSION__

+ 0
- 7
packaging/debian/satcmd.install View File

1
-/etc/bash_completion.d/satcmd.bash
2
-/usr/local/bin/satcmd
3
-/usr/local/libexec/satcmd/satcmd-dump
4
-/usr/local/libexec/satcmd/satcmd-echo
5
-/usr/local/share/doc/satcmd/README.md
6
-/usr/local/share/satcmd/ini.d/main/echo.ini
7
-/usr/local/share/satcmd/shellfu/saturnin.sh

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

1
+/usr/local/share/shellfu/include-bash/saturnin.sh

+ 1
- 15
packaging/template.spec View File

19
 saturnin you can easily create and package a meta-command with a set
19
 saturnin you can easily create and package a meta-command with a set
20
 of discoverable and maintainable sub-commands.
20
 of discoverable and maintainable sub-commands.
21
 
21
 
22
-This package contains a demo meta-command built with saturnin; you can
23
-also re-use the source as template for your new toolkit.
24
-
25
 %prep
22
 %prep
26
 %setup -q
23
 %setup -q
27
 
24
 
34
 %make_install
31
 %make_install
35
 
32
 
36
 %files
33
 %files
37
-%config %{_sysconfdir}/bash_completion.d/satcmd.bash
38
-%dir /usr/local/libexec/satcmd
39
-%dir /usr/local/share/satcmd
40
-%dir /usr/local/share/satcmd/ini.d
41
-%dir /usr/local/share/satcmd/ini.d/main
42
-%dir /usr/local/share/satcmd/shellfu
43
-%doc /usr/local/share/doc/satcmd/README.md
44
-/usr/local/bin/satcmd
45
-/usr/local/libexec/satcmd/satcmd-dump
46
-/usr/local/libexec/satcmd/satcmd-echo
47
-/usr/local/share/satcmd/ini.d/main/echo.ini
48
-/usr/local/share/satcmd/shellfu/saturnin.sh
34
+/usr/local/share/shellfu/include-bash/saturnin.sh
49
 
35
 
50
 
36
 
51
 %changelog
37
 %changelog

+ 0
- 39
src/bin/app.skel View File

1
-#!/bin/bash
2
-
3
-. "$(shellfu-get path)" || exit 3
4
-
5
-#
6
-# =====BEGIN BUILT PART=====
7
-#
8
-
9
-#shellcheck disable=SC2034
10
-{
11
-    SATURNIN_APP_CODENAME="__MKIT_PROJ_CODENAME__"
12
-    SATURNIN_APP_TAGLINE="__MKIT_PROJ_TAGLINE__"
13
-    SATURNIN_APP_VERSION="__MKIT_PROJ_VERSION__"
14
-    SATURNIN_APP_GIT_HASH="__MKIT_PROJ_GIT_LASTHASH__"
15
-    SATURNIN_CACHE_HOME="__SATURNIN_CACHE_HOME__"
16
-    SATURNIN_LIBEXEC="__SATURNIN_LIBEXEC__"
17
-    SATURNIN_LIBEXEC_PREFIX="__MKIT_PROJ_PKGNAME__-"
18
-    SHELLFU_PATH="__SATURNIN_SHELLFU_DIR__"
19
-    PRETTY_USAGE="subcommand"
20
-}
21
-
22
-shellfu import saturnin
23
-
24
-SATURNIN_CONF_PATH="$(
25
-    saturnin__conf_mkpath \
26
-        __SATURNIN_CONFIG_USER__/ini.d \
27
-        __SATURNIN_CONFIG_USER__ \
28
-        __SATURNIN_CONFIG_LOCAL__ \
29
-        __SATURNIN_SHARE__/ini.d
30
-)"
31
-
32
-#
33
-# =====END BUILT PART=====
34
-#
35
-
36
-export SATURNIN_CONF_PATH SHELLFU_PATH PRETTY_USAGE \
37
-       SATURNIN_CACHE_HOME
38
-
39
-saturnin__main "$@"

+ 0
- 38
src/complete.bash View File

1
-#!/bin/bash
2
-
3
-__SATURNIN_COMPLETE_CMDNAME=satcmd
4
-
5
-#shellcheck disable=SC2016
6
-__SATURNIN_COMPLETE_CODETMPL='
7
-__%s() {
8
-    local cur opts sopts
9
-    COMPREPLY=()
10
-    cur="${COMP_WORDS[COMP_CWORD]}"
11
-    sopts="-d -h -v"
12
-    opts="--help --verbose --debug --version --version-semver"
13
-    case "$cur" in
14
-        -*)
15
-            COMPREPLY=(
16
-                $(compgen -W "$sopts $opts" -- "${cur}")
17
-            )
18
-            ;;
19
-        --*)
20
-            COMPREPLY=(
21
-                $(compgen -W "$opts" -- "${cur}")
22
-            )
23
-            ;;
24
-        *)
25
-            COMPREPLY=(
26
-                $(compgen -W "$(%s _ls_sc)" "${cur}")
27
-            )
28
-    esac
29
-}'
30
-
31
-eval "$(
32
-    #shellcheck disable=SC2059
33
-    printf "$__SATURNIN_COMPLETE_CODETMPL" \
34
-        $__SATURNIN_COMPLETE_CMDNAME \
35
-        $__SATURNIN_COMPLETE_CMDNAME
36
-)"
37
-
38
-complete -F "__$__SATURNIN_COMPLETE_CMDNAME" "$__SATURNIN_COMPLETE_CMDNAME"

+ 0
- 7
src/ini.d/main/echo.ini View File

1
-
2
-[echo]
3
-
4
-    #
5
-    # default prefix
6
-    #
7
-    prefix = so:

+ 0
- 5
src/libexec/satcmd-dump View File

1
-#!/bin/bash
2
-
3
-. "$(shellfu-get path)" || exit 3
4
-
5
-env | sort | xargs -L 1 satcmd echo

+ 0
- 10
src/libexec/satcmd-echo View File

1
-#!/bin/bash
2
-
3
-. "$(shellfu-get path)" || exit 3
4
-
5
-shellfu import saturnin
6
-
7
-SATCMD_ECHO__PREFIX="${SATCMD_ECHO__PREFIX:-$(saturnin__conf "echo.prefix")}"
8
-
9
-echo -n "$SATCMD_ECHO__PREFIX"
10
-echo "$@"

src/shellfu/saturnin.sh → src/saturnin.sh View File

4
 shellfu import inigrep
4
 shellfu import inigrep
5
 shellfu import pretty
5
 shellfu import pretty
6
 
6
 
7
+
8
+#
9
+# Git commit hash of application source tree
10
+#
11
+# This is supposed to be set by your build scripts when building your
12
+# application.  The string is returned by calling your meta-command
13
+# with option --saturnin-app-git-hash.
14
+#
15
+# Look for 'satcmd' template for a working example.
16
+#
17
+SATURNIN_APP_GIT_HASH=${SATURNIN_APP_GIT_HASH:-}
18
+
19
+#
20
+# Your application version
21
+#
22
+# This is supposed to be set by your build scripts when building your
23
+# application.  The string is returned by calling your meta-command
24
+# with options --version, --version-semver or  --saturnin-app-version.
25
+#
26
+# Look for 'satcmd' template for a working example.
27
+#
28
+SATURNIN_APP_VERSION=${SATURNIN_APP_VERSION:-}
29
+
30
+#
31
+# Path to user cache
32
+#
33
+SATURNIN_CACHE_HOME=${SATURNIN_CACHE_HOME:-}
34
+
7
 #
35
 #
8
 # Path where saturnin__conf should look for files
36
 # Path where saturnin__conf should look for files
9
 #
37
 #
23
 #
51
 #
24
 SATURNIN_CONF_SUFFIX="${SATURNIN_CONF_SUFFIX:-.ini}"
52
 SATURNIN_CONF_SUFFIX="${SATURNIN_CONF_SUFFIX:-.ini}"
25
 
53
 
54
+#
55
+# Directory where to look for subcommands
56
+#
57
+# Files here starting with $SATURNIN_LIBEXEC_PREFIX are considered subcommands
58
+#
59
+SATURNIN_LIBEXEC="${SATURNIN_LIBEXEC:-}"
60
+
61
+#
62
+# Subcommand file prefix
63
+#
64
+# This is recommended to be set to meta-command name plus dash.  For example,
65
+# if your meta-command is `mykit`, this should be set to `mykit-`.
66
+#
67
+SATURNIN_LIBEXEC_PREFIX="${SATURNIN_LIBEXEC_PREFIX:-}"
68
+
69
+
26
 saturnin__conf() {
70
 saturnin__conf() {
27
     #
71
     #
28
     # inigrep smart loader
72
     # inigrep smart loader
130
     #
174
     #
131
     local key=${1#--saturnin-get-}
175
     local key=${1#--saturnin-get-}
132
     case "$key" in
176
     case "$key" in
133
-        shellfu-path)   echo "$SHELLFU_PATH"                ;;
134
         saturnin-conf-path) echo "$SATURNIN_CONF_PATH"      ;;
177
         saturnin-conf-path) echo "$SATURNIN_CONF_PATH"      ;;
135
         app-git-hash)   echo "$SATURNIN_APP_GIT_HASH"       ;;
178
         app-git-hash)   echo "$SATURNIN_APP_GIT_HASH"       ;;
136
         app-version)    echo "$SATURNIN_APP_VERSION"        ;;
179
         app-version)    echo "$SATURNIN_APP_VERSION"        ;;
157
 }
200
 }
158
 
201
 
159
 saturnin__main() {
202
 saturnin__main() {
203
+    #
204
+    # Main meta-command entry function
205
+    #
206
+    # After setting all mandatory environment variables, call this from
207
+    # your main meta-command script.
208
+    #
160
     local subcommand
209
     local subcommand
210
+    test -n "$SATURNIN_CACHE_HOME"     || die "SATURNIN_CACHE_HOME is not set"
211
+    test -n "$SATURNIN_LIBEXEC"        || die "SATURNIN_LIBEXEC is not set"
212
+    test -n "$SATURNIN_LIBEXEC_PREFIX" || die "SATURNIN_LIBEXEC_PREFIX is not set"
161
     while true; do case $1 in
213
     while true; do case $1 in
162
         -d|--debug)     export PRETTY_DEBUG=true;   shift ;;
214
         -d|--debug)     export PRETTY_DEBUG=true;   shift ;;
163
         -v|--verbose)   export PRETTY_VERBOSE=true; shift ;;
215
         -v|--verbose)   export PRETTY_VERBOSE=true; shift ;;
172
         *)              break;                      ;;
224
         *)              break;                      ;;
173
     esac done
225
     esac done
174
     subcommand="$1"; shift
226
     subcommand="$1"; shift
175
-    debug -v SHELLFU_PATH SATURNIN_LIBEXEC SATURNIN_CONF_PATH
227
+    debug -v SATURNIN_LIBEXEC SATURNIN_CONF_PATH
176
     case "$subcommand" in
228
     case "$subcommand" in
177
         conf)    saturnin__conf "$@"                ;;
229
         conf)    saturnin__conf "$@"                ;;
178
         _ls_sc)  saturnin__lssc                     ;;
230
         _ls_sc)  saturnin__lssc                     ;;

+ 0
- 3
tests/TF_HEADER View File

1
-#!/bin/sh
2
-
3
-echo "version = $(satcmd --version-semver)"

+ 12
- 1
tests/cli/TF_RUN View File

3
 . "$TF_DIR/include/subtest.sh"
3
 . "$TF_DIR/include/subtest.sh"
4
 . "$TF_DIR/include/tools.sh"
4
 . "$TF_DIR/include/tools.sh"
5
 
5
 
6
+PRETTY=plain
7
+
8
+. "$(shellfu-get path)" || tf_exit_error "failed to init shellfu"
9
+shellfu import saturnin  || tf_exit_error "failed to import saturnin"
10
+
6
 tf_enum_subtests() {
11
 tf_enum_subtests() {
7
     echo usage
12
     echo usage
8
 }
13
 }
11
     local name=$1
16
     local name=$1
12
     local cmd
17
     local cmd
13
     local o_err="oracle/$name.stderr"
18
     local o_err="oracle/$name.stderr"
19
+    SATURNIN_CACHE_HOME=chm
20
+    SATURNIN_LIBEXEC=lex
21
+    SATURNIN_LIBEXEC_PREFIX=lexpfx-
22
+    mkdir lex
23
+    touch lex/lexpfx-{foo,bar,baz}
24
+    chmod +x lex/lexpfx-{foo,bar,baz}
14
     case $name in
25
     case $name in
15
-        usage)  tf_testflt -n "$name" -E "$o_err" -S 2 satcmd ;;
26
+        usage)  tf_testflt -n "$name" -E "$o_err" -S 2 saturnin__main ;;
16
     esac
27
     esac
17
 }
28
 }
18
 
29
 

+ 4
- 3
tests/cli/oracle/usage.stderr View File

1
 usage:
1
 usage:
2
-  satcmd [options] COMMAND [ARG...]
2
+  saturnin__usage [options] COMMAND [ARG...]
3
 
3
 
4
 options:
4
 options:
5
   -d, --debug    turn on debugging
5
   -d, --debug    turn on debugging
9
 
9
 
10
 commands:
10
 commands:
11
   conf
11
   conf
12
-  dump
13
-  echo
12
+  bar
13
+  baz
14
+  foo

+ 0
- 1
tests/saturnin-conf/TF_RUN View File

4
 . "$TF_DIR/include/tools.sh"
4
 . "$TF_DIR/include/tools.sh"
5
 
5
 
6
 . "$(shellfu-get path)" || tf_exit_error "failed to init shellfu"
6
 . "$(shellfu-get path)" || tf_exit_error "failed to init shellfu"
7
-SHELLFU_PATH=$(satcmd --saturnin-get-shellfu-path)
8
 shellfu import saturnin  || tf_exit_error "failed to import saturnin"
7
 shellfu import saturnin  || tf_exit_error "failed to import saturnin"
9
 
8
 
10
 tf_enum_subtests() {
9
 tf_enum_subtests() {

+ 28
- 17
tests/saturnin-get/TF_RUN View File

3
 . "$TF_DIR/include/subtest.sh"
3
 . "$TF_DIR/include/subtest.sh"
4
 . "$TF_DIR/include/tools.sh"
4
 . "$TF_DIR/include/tools.sh"
5
 
5
 
6
+PRETTY=plain
7
+
8
+. "$(shellfu-get path)" || tf_exit_error "failed to init shellfu"
9
+shellfu import saturnin  || tf_exit_error "failed to import saturnin"
10
+
6
 tf_enum_subtests() {
11
 tf_enum_subtests() {
7
     echo unknown
12
     echo unknown
8
     echo unknown_dash1
13
     echo unknown_dash1
9
     echo unknown_dash2
14
     echo unknown_dash2
10
     echo unknown_none
15
     echo unknown_none
11
     echo unknown_same
16
     echo unknown_same
12
-    echo shellfu_path
13
     echo saturnin_conf_path
17
     echo saturnin_conf_path
14
     echo app_version
18
     echo app_version
15
     echo cache_home
19
     echo cache_home
18
 # warn "unknown devel key: $key"
22
 # warn "unknown devel key: $key"
19
 }
23
 }
20
 
24
 
25
+rndtoken() {
26
+    head -c 100 /dev/urandom | md5sum | head -c 7
27
+}
28
+
21
 ckfuzzy() {
29
 ckfuzzy() {
22
     local es
30
     local es
23
     local out="result/$name.stdout"
31
     local out="result/$name.stdout"
24
     mkdir -p result
32
     mkdir -p result
25
     cat > "$out"
33
     cat > "$out"
26
     case "$name" in
34
     case "$name" in
27
-        shellfu_path)   grep -q '^/usr/'                  ;;
28
-        saturnin_conf_path)  tr : '\n' | grep -q '^/usr/' ;;
35
+        saturnin_conf_path)  grep -qx "$SATURNIN_CONF_PATH" ;;
29
         app_version)    grep -q '[0-9]'                   ;;
36
         app_version)    grep -q '[0-9]'                   ;;
30
-        cache_home)     grep -qx '/home/.*/.cache/satcmd' ;;
31
-        libexec)        grep -q '^/usr/'                  ;;
32
-        libexec_prefix) grep -q '.'                       ;;
37
+        cache_home)     grep -qx "$SATURNIN_CACHE_HOME"   ;;
38
+        libexec)        grep -qx "$SATURNIN_LIBEXEC"      ;;
39
+        libexec_prefix) grep -qx "$SATURNIN_LIBEXEC_PREFIX" ;;
33
     esac <"$out"; es=$?
40
     esac <"$out"; es=$?
34
     if test $es -gt 0;
41
     if test $es -gt 0;
35
     then
42
     then
42
 tf_do_subtest() {
49
 tf_do_subtest() {
43
     local name=$1
50
     local name=$1
44
     local o_err="oracle/$name.stderr"
51
     local o_err="oracle/$name.stderr"
52
+    SATURNIN_APP_VERSION=1.2.3
53
+    SATURNIN_CACHE_HOME=chm_$(rndtoken)
54
+    SATURNIN_CONF_PATH=cp_$(rndtoken)
55
+    SATURNIN_LIBEXEC=lex_$(rndtoken)
56
+    SATURNIN_LIBEXEC_PREFIX=lexpfx_$(rndtoken)
45
     case $name in
57
     case $name in
46
-        unknown)        tf_testflt -n "$name" -E "$o_err" -S 2 satcmd --saturnin-get-foo             ;;
47
-        unknown_dash1)  tf_testflt -n "$name" -E "$o_err" -S 2 satcmd --saturnin-get--               ;;
48
-        unknown_dash2)  tf_testflt -n "$name" -E "$o_err" -S 2 satcmd --saturnin-get---              ;;
49
-        unknown_none)   tf_testflt -n "$name" -E "$o_err" -S 2 satcmd --saturnin-get-                ;;
50
-        unknown_same)   tf_testflt -n "$name" -E "$o_err" -S 2 satcmd --saturnin-get---saturnin-get  ;;
51
-        shellfu_path)   satcmd --saturnin-get-shellfu-path    | ckfuzzy ;;
52
-        saturnin_conf_path)   satcmd --saturnin-get-saturnin-conf-path | ckfuzzy ;;
53
-        app_version)    satcmd --saturnin-get-app-version     | ckfuzzy ;;
54
-        cache_home)     satcmd --saturnin-get-cache-home      | ckfuzzy ;;
55
-        libexec)        satcmd --saturnin-get-libexec         | ckfuzzy ;;
56
-        libexec_prefix) satcmd --saturnin-get-libexec-prefix  | ckfuzzy ;;
58
+        unknown)        tf_testflt -n "$name" -E "$o_err" -S 2 saturnin__main --saturnin-get-foo             ;;
59
+        unknown_dash1)  tf_testflt -n "$name" -E "$o_err" -S 2 saturnin__main --saturnin-get--               ;;
60
+        unknown_dash2)  tf_testflt -n "$name" -E "$o_err" -S 2 saturnin__main --saturnin-get---              ;;
61
+        unknown_none)   tf_testflt -n "$name" -E "$o_err" -S 2 saturnin__main --saturnin-get-                ;;
62
+        unknown_same)   tf_testflt -n "$name" -E "$o_err" -S 2 saturnin__main --saturnin-get---saturnin-get  ;;
63
+        saturnin_conf_path)   saturnin__main --saturnin-get-saturnin-conf-path | ckfuzzy ;;
64
+        app_version)    saturnin__main --saturnin-get-app-version     | ckfuzzy ;;
65
+        cache_home)     saturnin__main --saturnin-get-cache-home      | ckfuzzy ;;
66
+        libexec)        saturnin__main --saturnin-get-libexec         | ckfuzzy ;;
67
+        libexec_prefix) saturnin__main --saturnin-get-libexec-prefix  | ckfuzzy ;;
57
     esac
68
     esac
58
 }
69
 }
59
 
70