about summary refs log tree commit diff
path: root/Functions/TCP/tcp_expect
blob: eef39ad060d65d949535b1bd970b79d9474e1458 (plain) (blame)
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
# Expect one of a series of regular expressions from $TCP_SESS.
# Can make backreferences to be handled by $match.  Returns 0 for
# successful match, 1 for error, 2 for timeout.
#
# This function has no facility for conditionally calling code based
# the regular expression found.  This should be done in the calling code
# by testing $TCP_LINE, which contains the line which matched the
# regular expression.  The complete set of lines read while waiting for
# this line is available in the array $tcp_expect_lines (including $TCP_LINE
# itself which will be the final element).  Alternatively, use -p pind
# which sets $pind to the index of the pattern which matched.  It
# will be set to 0 otherwise.
#
# Many of the options are passed straight down to tcp_read.
#
# Options:
#   -a     Run tcp_expect across all sessions; the first pattern matched
#          from any session is used.  The TCP output prompt can be
#          used to decide which session matched.
#   -l list
#          Comma-separated list of sessions as for tcp_read.
#   -p pv  If the Nth of a series of patterns matches, set the parameter
#          whose name is given by $pv to N; in the case of a timeout,
#          set it to -1; otherwise (unless the function exited prematurely),
#	   set it to 0.
#	   To avoid namespace clashes, the parameter's name must
#	   not begin with `_expect'.
#   -P pv  This is similar to -p, however in this case the
#          arguments to tcp_expect following the options are expected
#          to start with a prefix "<tag>:".  The parameter $pv is
#          then set to the value "<tag>" rather than the numeric
#          index of the parameter.  The string "timeout" is used
#          as the tag for a timeout specified by -t and -T and
#          on a failed match the variable is set to the empty string.
#          It is not an error for multiple arguments to have
#          the same tag or to use a reserved value of the tag.
#   -q     Quiet, passed down to tcp_read.  Bad option and argument
#          usage is always reported.
#   -s sess
#          Expect from session sess.  May be repeated for multiple sessions.
#   -t to  Timeout in seconds (may be floating point) per read operation.
#          tcp_expect will only time out if every read operation takes longer
#          than to
#   -T TO  Overall timeout; tcp_expect will time out if the overall operation
#          takes longer than this many seconds.
emulate -L zsh
setopt extendedglob

if [[ ${(t)SECONDS} != float* ]]; then
    # If called from another function, use that
    typeset -F TCP_SECONDS_START=$SECONDS
    # Get extra accuracy by making SECONDS floating point locally
    typeset -F SECONDS
fi

# Variables are all named _expect_* to avoid problems with the -p param.
local _expect_opt _expect_pvar _expect_state _expect_arg _expect_ind
local -a _expect_read_args
float _expect_to1 _expect_to_all _expect_to _expect_new_to
integer _expect_i _expect_stat _expect_states

while getopts "al:p:P:qs:t:T:" _expect_opt; do
  case $_expect_opt in
    (a) _expect_read_args+=(-a)
	;;
    (l) _expect_read_args+=(-l $OPTARG)
        ;;
    ([pP]) _expect_pvar=$OPTARG
	if [[ $_expect_pvar != [a-zA-Z_][a-zA-Z_0-9]# ]]; then
	  print "invalid parameter name: $_expect_pvar" >&2
	  return 1
	fi
	if [[ $_expect_pvar = _expect* ]]; then
	  print "$0: parameter names staring \`_expect' are reserved."
	  return 1
	fi
	if [[ $_expect_opt = "P" ]]; then
	  eval "$_expect_pvar=0"
	  _expect_states=1
	else
	  eval "$_expect_pvar="
	fi
	;;
    (q) _expect_read_args+=(-q)
        ;;
    (s) _expect_read_args+=(-s $OPTARG)
        ;;
    (t) _expect_to1=$OPTARG
	;;
    (T) _expect_to_all=$(( SECONDS + $OPTARG ))
        ;;
    (\?) return 1
	 ;;
    (*) print Unhandled option $_expect_opt, complain >&2
	return 1
	;;
  esac
done
(( OPTIND > 1 )) && shift $(( OPTIND - 1 ))

typeset -ga tcp_expect_lines
tcp_expect_lines=()
while true; do
  if (( _expect_to_all || _expect_to1 )); then
    _expect_to=0
    (( _expect_to1 )) && (( _expect_to = _expect_to1 ))
    if (( _expect_to_all )); then
      # overall timeout, see if it has already triggered
      if (( (_expect_new_to = (_expect_to_all - SECONDS)) <= 0 )); then
	[[ -n $_expect_pvar ]] && eval "$_expect_pvar=-1"
	return 2
      fi
      if (( _expect_to <= 0 || _expect_new_to < _expect_to )); then
	_expect_to=$_expect_new_to
      fi
    fi
    tcp_read $_expect_read_args -t $_expect_to
    _expect_stat=$?
  else
    tcp_read $_expect_read_args -b
    _expect_stat=$?
  fi
  if (( _expect_stat )); then
    [[ -n $_expect_pvar ]] && eval "$_expect_pvar=-1"
    return $_expect_stat
  fi
  tcp_expect_lines+=($TCP_LINE)
  for (( _expect_i = 1; _expect_i <= $#; _expect_i++ )); do
    if [[ _expect_states -ne 0 && $argv[_expect_i] = (#b)([^:]#):(*) ]]; then
      _expect_ind=$match[1]
      _expect_arg=$match[2]
    else
      _expect_ind=$_expect_i
      _expect_arg=$argv[_expect_i]
    fi
    if [[ "$TCP_LINE" = ${~_expect_arg} ]]; then
      [[ -n $_expect_pvar ]] && eval "$_expect_pvar=\$_expect_ind"
      return 0
    fi
  done
done