File: ia.interactive

package info (click to toggle)
shellia 5.10
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 792 kB
  • sloc: sh: 7,840; makefile: 34
file content (447 lines) | stat: -rw-r--r-- 15,029 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
#!/bin/sh
# vim: set filetype=sh :
#        file: /usr/share/shellia/ia.interactive
#   copyright: Bernd Schumacher <bernd.schumacher@hpe.com> (2007-2021)
#     license: GNU General Public License, version 3
# description: ia.interactive - interactive functions used by shellia

# ia_find_lib <lib>
# example:
#   program "/nfs/host1/usr/local/bin/program" with line "ia_source_lib /usr/share/shellia/ia" called
#   from directory "/tmp" will try "/tmp/ia", ... "/nfs/host1/usr/share/shellia/ia", ... and
#   "/usr/share/shellia/ia" in the listed order.
ia_find_lib()
{
  local i
  local p

  for i in "$@"; do
    [ -f "./$(basename "$i")" ] && /bin/echo "./$(basename "$i")" && return 0
    p="$0"
    while :; do
      [ "$p" != "$(dirname p)" ] || break
      p="$(dirname "$p")"
      [ -f "$p/$i" ] && /bin/echo "$p/$i" && return 0
    done
    [ -f "$i" ] && /bin/echo "$i" && return 0
    return 1
  done
}

if [ "${ia_prefix:=ia}" != "ia" ] || [ "${dbg_prefix:=dbg}" != "dbg" ]; then
  [ -n "$(eval "/bin/echo \${${ia_prefix}_interactive_already_sourced:=}")" ] && return
  eval "$(sed -e "s/ia_/${ia_prefix}_/g" -e "s/ia()/${ia_prefix}()/g" \
    -e "s/dbg/${dbg_prefix}/g" "$(ia_find_lib /usr/share/shellia/ia.interactive)")"
  return
fi

[ "${ia_interactive_already_sourced:=}" ] && return || ia_interactive_already_sourced=1

. "$(ia_find_lib /usr/share/shellia/ia.basic)"
. "$(ia_find_lib /usr/share/shellia/ia.array)"

# Global Variables used:
#   interactive_already_sourced
#
# Functions used:
#   dbcheck
#   dbgoff
#   dbgon
#   dbgtoggle
#   interactive
#   todo_add

# ia_init will run in context of users shell function (and not in sub-function ia)
ia_init_details="$( cat <<END

if [ -n "\${ia_head:=}" ]; then
  # we are in a sub function
  # we save some variables from parent to own local variables
  local ia_head_TMP
  local ia_use_interactive_tmp
  ia_head_TMP="\$ia_head"
  ia_use_interactive_tmp="\$ia_use_interactive"
  local ia_head
  local ia_use_interactive
  ia_head="\$ia_head_TMP"
  ia_use_interactive="\$ia_use_interactive_tmp"
  local ia_todo
  ia_todo=""
  local ia_opts
fi

[ -n "\${ia_init_called:-}" ] && ia_init_called_wrong ia_init "\$ia_head" || ia_init_called="1"
ia_opts=""
while [ \$# -gt 0 ]; do
  if [ "\$1" = "-i" ] || [ "\$1" = "-s" ] || [ "\$1" = "-m" ] || [ "\$1" = "-a" ]; then
    [ "\$1" = "-s" ] && ia_use_silent="-s"
    [ -n "\$ia_opts" ] && ia_opts="\$ia_opts \$1" || ia_opts="\$1"
    shift
  elif [ "\$1" = "-d" ]; then
      if [ -z "\${ia_debug_already_sourced:=}" ]; then
        ia_err "To use option \$1 ia.debug has to be loaded first."
        exit 1
      else
        dbgon "\$2"
        shift 2
      fi
  elif [ "\$1" = "--" ]; then
    shift
    break
  else
    break
  fi
done

END
)"

ia_init="{ set +x; } 2>/dev/null; eval \"\$ia_init_details\""

# restore the return value of the last command that uses shellia
ia_restore_return()
{
  #ia_dev_dbg "ia_restore_return: ia_return=<$ia_return>"
  return "$ia_return"
}

# save the return value of the last command that uses shellia
ia_save_return()
{
  set +x
  ia_return=$1
  #ia_dev_dbg "ia_save_return: ia_return=<$ia_return>"
}

ia_add()
{
  # global ia_init_called
  local a
  #ia_dev_dbg "ia_add: *=<$*> ia_ignore=<$ia_ignore> ia_stdout=<$ia_stdout>"
  [ -n "${ia_init_called:-}" ] || ia_init_called_wrong ia_add "${ia_head:-}"
  a="$(ia_arr_mk "${ia_ignore:=}" "${ia_stdout:=}" "$*")"
  [ -n "${ia_todo:=}" ] && ia_todo="$ia_todo $a" || ia_todo="$a"
  ia_ignore=""
  ia_stdout=""
}

ia()
{
  # posh does not like empty "$@"
  if [ $# -ge 1 ]; then ia_interactive "$@" ${ia_opts:-} ${ia_todo:=}; else ia_interactive ${ia_opts:-} ${ia_todo:=}; fi
  ia_restore_return
}

ia_debug_cmdarr()
{
  local cmd

  ia_dev_dbg "cmdarr=<$ia_cmdarr> cmdarr=<"
  for cmd in $ia_cmdarr; do
    ia_dev_dbg "  <$(ia_arr_get 1 "$cmd")> <$(ia_arr_get 2 "$cmd")> <$(ia_arr_get 3 "$cmd")>"
  done
  ia_dev_dbg ">"
}

# ia_init_called_wrong ia_interactive|ia_init|ia_add
ia_init_called_wrong()
{
  # global ia_cmd_header
  # local_in_parent ia_head
  local what
  local info

  what="$1"
  info=${ia_head:-}
  [ -n "$info" ] || info="$(basename "$0")"
  info="$info/${ia_cmd_header:-<outside-of-function>}"

  case "$what" in
    ia_interactive)
      ia_exiterr 1 "shellia: script error: $info: <ia> is called without previous calling <eval \"\$ia_init\">"
      ;;
    ia_init)
      ia_exiterr 1 "shellia: script error: $info: <eval \"\$ia_init\"> is called again, before calling <ia>"
      ;;
    ia_add)
      ia_exiterr 1 "shellia: script error: $info: <ia_add> is called without previous calling <eval \"\$ia_init\">"
      ;;
    *)
      ia_exiterr 1 "shellia: ia_init_called_wrong: internal error"
      ;;
  esac
}

ia_run()
{
  ia_early_exit=1
  eval "[ -n \"$ia_use_trace\" ] && set -x
    { ia_restore_return; } 2>/dev/null
    $ia_cmd"
  { ia_save_return $?; } 2>/dev/null
  ia_premature_exit=""
  ia_early_exit=0
}

# ia_call_run [<stdout_file> [<stderr_file>]]
# global output: ia_out, ia_return
ia_call_run()
{
  local ia_early_exit # 1 if ia_cmd exited early, 0 else
  local ia_early_exit_ret

  ia_premature_exit="$ia_cmd" # global variable used by trap

  if [ $# -eq 1 ]; then
    # next line needs a file for output, because if you try to
    # use something like x="$(eval ...)" the eval command will
    # no longer be executed in the actual function but in a
    # sub function
    #ia_dev_dbg "ia_check: -c: ia_use_trace=<$ia_use_trace>"
    #ia_dev_dbg "ia_check1: -c: ia_cmd=<$(/bin/echo "$ia_cmd" | head -1)> ignore=<$ia_ign> stdout=<$ia_std>"
    ia_run >"$1" 2>&1
  elif [ $# -eq 2 ]; then
    # If we use a pipe, we can not set local variables:
    #   $ sh -c "a=0; eval \"a=1\" | sleep 1; echo \"a=<\$a>\"" # => a=<0>
    #   $ sh -c "a=0; eval \"a=1\"; echo \"a=<\$a>\"" # => a=<1>
    # This is the reason why the following command does not work with all shellia tests  (e.g example.multipy):
    #   { { mycmd | stdbuf -oL sed "s/^/s:/" >> /tmp/mylog; } 2>&1 1>&5 | stdbuf -oL sed "s/^/e:/" >> /tmp/mylog; } 5>&1 1>&2
    ia_run >"$1" 2>"$2"
  else
    ia_premature_exit="" # do not complain about premature exit
    # no logfile, because this would change order of stdout and stderr
    ia_run "$ia_cmd"
  fi
  ia_early_exit_ret=$?
  if [ $ia_early_exit = 1 ]; then
    # the command ended earlier, maybe because of executing return
    ia_return=$ia_early_exit_ret
  fi
  return $ia_early_exit
}

ia_cmd_calc_variable()
{
  #ia_cmd="$(/bin/echo "$ia_cmd" | ia_easy_backslash | sed "s/$<\(\w\+\)>/\${\1:-}/g")"
  ia_cmd="$(/bin/echo "$ia_cmd" | ia_easy_backslash |
    sed "s/$<\(\w\+\)>/\\\$(\/bin\/echo \"\${\1:-}\" | ia_easy_backslash)/g")"
  ia_cmd="$(eval "/bin/echo \"$ia_cmd\"")"
}
#v="a \"b b\""; ia_cmd="tst $<v>"; ia_cmd_calc_variable; exp="tst a \\\"b b\\\""
#[ "$ia_cmd" = "$exp" ] && echo "OK ia_cmd_calc_variable-1" || echo "ERROR ia_cmd_calc_variable-1 ia_cmd=<$ia_cmd> exp=<$exp>"
#v="a \"b b\" \"c \\\"c c\\\"\""; ia_cmd="tst $<v>"; ia_cmd_calc_variable; exp="tst a \\\"b b\\\" \\\"c \\\\\\\"c c\\\\\\\"\\\""
#[ "$ia_cmd" = "$exp" ] && echo "OK ia_cmd_calc_variable-2" || echo "ERROR ia_cmd_calc_variable-2 ia_cmd=<$ia_cmd> exp=<$exp>"
#exit 0

#      syntax: interactive [-i] [-c] <functioncall with params> ...
# description: interactive evaluates the listed functioncalls. If the first
#              argument is -i an interactive menu will be given.
#              The <functioncall with params> can contain the special
#              character sequence "<-i>". "<-i>" will be translated
#              either to "-i" or to "". This can be toggled with the key "i".
#   copyright: Bernd Schumacher <bernd.schumacher@hp.com> (2007-2018)
#     license: GNU General Public License, version 3
#     comment: use ia_answer to change the next point from external
#
ia_interactive()
{
  # global ia_cmd_header
  # global ia_use_silent # never initialized
  # global ia_use_minctrl # never initialized
  # global ia_direct_stderr_fd
  # global ia_return # exit code from last command
  # global ia_init_called
  # local_in_parent ia_head
  local ia_use_check
  # local_in_parent ia_use_interactive
  local ia_answer
  local ia_input
  local ia_i
  local ia_found_i
  local ia_ignore
  local ia_stdout
  local ia_now_ignore
  local ia_now_stdout
  local ia_cmd
  local ia_use_interactive_sub
  local ia_show_variable
  local ia_cmdarr
  local ia_cmdarrlen
  local ia_question
  local ia_use_trace

  # init variables to allow use of: set -u
  ia_input=""

  ia_return=0
  ia_use_check=""
  ia_use_interactive=""
  ia_use_trace=""
  ia_found_i=""
  : "${ia_use_minctrl:=}"
  : "${ia_use_silent:=}"

  while [ $# -gt 0 ]; do
    if [ "$1" = "-i" ]; then
      [ -z "$ia_use_minctrl" ] || ia_exiterr 1 "either use -m or -i"
      [ -z "$ia_use_silent" ] || ia_exiterr 1 "either use -s or -i"
      ia_use_interactive="$1"
      shift
    elif ( [ "$1" = "-c" ] || [ "$1" = "-C" ] || [ "$1" = "--learn" ] ) && [ -z "$ia_use_check" ]; then
      ia_use_check="$1"
      shift
    elif ( [ "$1" = "-c" ] || [ "$1" = "-C" ] ) && [ "$ia_use_check" = "--learn" ]; then
      ia_use_check="$1 $ia_use_check"
      shift
    elif [ "$1" = "--learn" ] && ( [ "$ia_use_check" = "-c" ] || [ "$ia_use_check" = "-C" ] ); then
      ia_use_check="$ia_use_check $1"
      shift
    elif [ "$1" = "-x" ]; then
      ia_use_trace="$1"
      shift
    elif [ "$1" = "-m" ]; then
      [ -z "$ia_use_interactive" ] || ia_exiterr 1 "either use -i or -m"
      [ -z "$ia_use_silent" ] || ia_exiterr 1 "either use -s or -m"
      ia_use_minctrl="$1"
      shift
    elif [ "$1" = "-s" ]; then
      [ -z "$ia_use_interactive" ] || ia_exiterr 1 "either use -i or -s"
      [ -z "$ia_use_minctrl" ] || ia_exiterr 1 "either use -m or -s"
      ia_use_silent="$1"
      shift
    elif [ "$1" = "-a" ]; then
      ia_red=""
      ia_green=""
      ia_orange=""
      ia_no_color=""
      shift
    elif [ "$1" = "--" ]; then
      shift
      break
    else
      break
    fi
  done

  ia_use_interactive_sub=""
  ia_show_variable=""
  [ -n "$ia_use_interactive" ] && ia_use_interactive_sub=" -i" || ia_use_interactive_sub=""
  ia_cmdarr="$*"
  ia_cmdarrlen="$#"
  #ia_debug_cmdarr

  # ia_head is a local variable in parent function of ia_interactive
  if [ -n "${ia_head:-}" ]; then
    ia_head="$ia_head/$ia_cmd_header"
  else
    # ia_interactive is called the first time
    if [ -n "${ia_prefix_head:-}" ]; then
      ia_head="${ia_prefix_head}$(basename "$0")"
    else
      ia_head="$(basename "$0")"
    fi
    ia_check_logfile
  fi
  [ -z "${ia_init_called:-}" ] && ia_init_called_wrong ia_interactive "$ia_head" || ia_init_called=""
  #[ -n "$ia_use_trace" -a ! -n "$ia_use_interactive" ] && ia_log "$ia_head"
  [ -z "$ia_use_interactive" ] && ia_log "$ia_head"
  #ia_log "$ia_head"

  ia_answer="1"
  [ "$ia_answer" -gt "$ia_cmdarrlen" ] && ia_answer=q
  while :; do
    if [ -n "$ia_use_interactive" ]; then
      # print header
      ia_question="${ia_orange}=== $ia_head ===${ia_no_color}"
      # print all functions

      ia_i=1
      for ia_array in $ia_cmdarr; do
        ia_cmd="$(ia_arr_get 3 "$ia_array")"
        if [ -n "$(/bin/echo "$ia_cmd" | grep "<-i>")" ]; then
          ia_found_i=1
          ia_cmd="$(/bin/echo "$ia_cmd" | sed "s/[[:space:]]*<-i>/${ia_use_interactive_sub}/g")"
        fi
        if [ -n "$ia_show_variable" ]; then
          ia_cmd_calc_variable
        fi
        if [ "$ia_answer" = "$ia_i" ];then
          # in next line /bin/echo is needed, because not each shell has an echo that supports option -e
          ia_question="$ia_question${ia_nl}${ia_green}$ia_i ${ia_cmd}${ia_no_color}"
        else
          ia_question="$ia_question${ia_nl}$ia_i ${ia_cmd}"
        fi
        ia_i=$((ia_i + 1))
      done

      # print debug toggle
      #ia_dev_dbg "ia_interactive: dbg_display=<$(dbg_display)> dbg_levels=<$dbg_levels>"
      [ -n "${ia_debug_already_sourced:=}" ] &&
        ia_question="$ia_question${ia_nl}d ... change dbg: $(dbg_display)"

      # print iativity toggle
      [ -n "$ia_found_i" ] && ia_question="$ia_question${ia_nl}i toggle -i flag"

      # print input less option if not at the end
      [ -z "$(/bin/echo "$ia_answer" | sed 's/[0-9]//g')" ] && [ $ia_answer -lt "$ia_cmdarrlen" ] &&
        ia_question="$ia_question${ia_nl}c continue without questions"

      # print exit
      if [ "$ia_answer" = "q" ];then
        ia_question="$ia_question${ia_nl}${ia_green}q quit${ia_no_color}"
      else
        ia_question="$ia_question${ia_nl}q quit"
      fi

      # ia_ask <question> <default> <eof> <prompt> <regex>
      ia_input="$(ia_ask "$(/bin/echo "$ia_question")" "" "q" \
      "? ${ia_green}[$ia_answer]${ia_no_color} " \
      "^(d.*|c|i|v|q|[0-9]+|)$")"

      if [ -n "$(/bin/echo "$ia_input" | grep -v -e "^c$" -e "^i$" -e "^v$" -e "^d")" ]; then
        ia_answer="$ia_input"
      fi
    fi

    # debug toggle
    if [ -n "$(/bin/echo "$ia_input" | grep "^d")" ]; then
      [ -n "${ia_debug_already_sourced:=}" ] && dbgtoggle "$(/bin/echo "$ia_input" | sed -n "s/^d\s*//p")"
    # interactive toggle
    elif [ "$ia_input" = "i" ]; then
      [ "${ia_use_interactive_sub}" ] && ia_use_interactive_sub="" || ia_use_interactive_sub=" -i"
    # show $<variable>
    elif [ "$ia_input" = "v" ]; then
      [ "${ia_show_variable}" ] && ia_show_variable="" || ia_show_variable="v"
    # quit
    elif [ "$ia_answer" = "q" ]; then
      return
    # invalid input
    elif [ -n "$(/bin/echo "$ia_answer" | sed 's/[0-9]//g')" ]; then
      ia_answer="?"
    # function execution
    elif [ "$ia_answer" -ge 1 ] && [ "$ia_answer" -le "$ia_cmdarrlen" ]; then
      [ "$ia_input" = "c" ] && ia_use_interactive=""
      ia_array="$(/bin/echo "$ia_cmdarr" | cut -d' ' -f "$ia_answer")"

      # because shell command substitution deletes trailing newlines, but we may need it, we add a comment
      ia_cmd="$(ia_arr_get 3 "$ia_array" | sed -e "s/<-i>\([[:space:]]*\)/${ia_use_interactive_sub}\1/g")"
      ia_cmd_calc_variable
      ia_cmd_header="$(/bin/echo "$ia_cmd" | head -1 | gawk '{print $1}')"
      ia_now_ignore="$(ia_arr_get 1 "$ia_array")"
      ia_now_stdout="$(ia_arr_get 2 "$ia_array")"
      #ia_dev_dbg "ia_interactive: ia_use_check=<$ia_use_check> ia_now_ignore=<$ia_now_ignore> ia_now_stdout=<$ia_now_stdout>"

      if [ "$ia_now_ignore" = "-f" ] && [ "$ia_now_stdout" = "-f" ] || [ -z "$ia_use_check" ] ; then
        # we do not use check at all or in this case we uses ia_nocheck -f
        ia_call_run
        [ $? = 0 ] || return
      elif [ "$ia_now_ignore" = "-1" ] && [ "$ia_now_stdout" = "-1" ] ; then
        ia_check_and_result "--exitcode" "$ia_cmd" "$ia_now_ignore" "$ia_now_stdout" || return
      else # "$ia_use_check" = "-c" or "-C" or "-c -l" or "-C -l"
        ia_check_and_result $ia_use_check "$ia_cmd" "$ia_now_ignore" "$ia_now_stdout" || return
      fi
      ia_answer=$((ia_answer+1))
      [ "$ia_answer" -gt "$ia_cmdarrlen" ] && ia_answer=q
    fi
  done
}