expect Functions

function expect/spawn [argv]

Spawn a process defined by argv

Param argv:

command and arguments

Type argv:

list

Return:

struct-spawn

Rtype:

struct-instance

Raises ^system-error:

spawn will set and return spawn-id.

Tip

spawn-id defaults to #f however if you set it to #n or a list of spawn-id(s) then spawn will prepend any new value to the list.

function expect/exp-case [clauses]

define-template: (exp-case & clauses) (x e)

exp-case looks to find the first match from the clauses:

exp-case operates on the current value of spawn-id (no such value can be passed directly as exp-case is a template).

Each clause in clauses takes one of the forms

([:str-kw]* string body)

(:term-kw body)

Here, :str-kw is one of the string keywords:

  • :re indicating that string should be used for a regular expression match

  • :gl indicating that string should be used for a glob-style pattern match (the default) – see regex-pattern-string for how the string is modified

  • :ex indicating that string should be used for an exact match – see regex-exact-string for how the string is modified

  • :icase indicates that a case-insensitive match should be used

:term-kw is one of the terminal keywords:

  • :eof for matching the End of File indicator

    Note

    If spawn-id is a list then body will be called for each spawn-id for which the condition is true.

    If spawn-id is a list then it is inadvisable to call (exp-continue) in the body of an :eof clause as that will prevent the invocation of any other extant End of File notifications.

    See also the End of File note below.

  • :timeout for matching a time out

    Note

    :timeout will be true for all spawn-ids if spawn-id is a list. The body will be called with the value of spawn-id (which could be a list or a struct instance).

  • :all to match everything in the buffer, ie. in effect, to empty the buffer

    Note

    :all always matches and therefore will not read any more data from the spawned process.

    If spawn-id is a list then for each spawn-id in the list the entire buffer will be matched and the body called.

body will be invoked as though the body of a function with the following arguments:

  • for a successful string match, including :all, the arguments are spawn-id, r, the result of the match as per regexec, and prefix, the contents of the spawn-id’s buffer before the match

  • for a successful terminal match, excluding :all, the argument is spawn-id

body can invoke the function (exp-continue) to loop around again.

body can invoke the function (exp-break [value]) to exit the loop immediately.

Passing no clauses will still attempt to match using any existing clauses from exp-case-before or exp-case-after.

If a clause matches, exp-case returns the value from body. If no clauses match or all spawn-ids have indicated End of File, or a timeout has occurred and there is no :timeout clause exp-case returns #f.

End of File

If the spawned process indicates End of File then the master file descriptor is generally closed although this is not guaranteed.

Call exp-wait to clean up both file descriptors and spawned processes.

Note

All (supported) operating systems can use poll(2). However, some tested operating systems (Mac OS 10.5.8) return POLLNVAL for (pseudo-terminal) devices.

In this case, the code reverts to the uses of select(2) with any associated limits (notably, FD_SETSIZE).

Example:

From the top of the Idio distribution you might try:

import expect

spawn ls -1               ;; minus one !

(expect-case
 (:re "NG[.]" {
   printf ":re '%s' => %s\n" prefix r
   (exp-continue)
 })
 ("doc?" {
   printf ":gl '%s' => %s\n" prefix r
   (exp-continue)
 })
 (:icase "EXT?" {
   printf ":gl '%s' => %s\n" prefix r
   (exp-continue)
 })
 (:ex "NSE." {
   printf ":ex '%s' => %s\n" prefix r
   (exp-continue)
 })
 (:eof {
   printf ":eof\n"
 })
 (:timeout {
   printf ":timeout\n"
 }))

to get:

:re 'bin
CONTRIBUTI' => #[ ("NG." 15 18) ]
:gl 'md
' => #[ ("doc\r" 4 8) ]
:gl '
' => #[ ("ext\r" 1 5) ]
:ex '
lib
LICENSE
LICE' => #[ ("NSE." 19 23) ]
:eof
function expect/exp-continue [value]

define-template: (exp-continue & args) (x e)

exp-continue stops processing the current exp-case clauses and starts the next iteration of the loop. value is ignored and set to #<void> if not supplied.

Warning

exp-continue ignores any protection blocks set up by unwind-protect or dynamic-wind.

function expect/exp-break [value]

define-template: (exp-break & args) (x e)

exp-break returns value from the enclosing exp-case or #<void> if no value is supplied.

Warning

exp-break ignores any protection blocks set up by unwind-protect or dynamic-wind.

function expect/exp-case-before [clauses]

define-template: (exp-case-before & clauses) (x e)

exp-case-before establishes match clauses identically to exp-case except the clauses are tested before those passed in exp-case.

Invoking exp-case-before doesn’t actually perform any matching, you must still call exp-case.

Passing no clauses effectively unsets this behaviour.

Example:

import expect

spawn echo foo

(exp-case-before
 ("foo" {
   'before
 }))

(exp-case
 ("foo" {
   'normally
 }))

to have exp-case return before.

function expect/exp-case-after [clauses]

define-template: (exp-case-after & clauses) (x e)

exp-case-after establishes match clauses identically to exp-case except the clauses are tested after those passed in exp-case.

Invoking exp-case-after doesn’t actually perform any matching, you must still call exp-case.

Passing no clauses effectively unsets this behaviour.

Example:

import expect

spawn echo foo

(exp-case-after
 ("foo" {
   'after
 }))

(exp-case
 ("bar" {
   'bar
 }))

to have exp-case return after.

function expect/exp-close (:spawn-id spawn-id)

close the master file descriptor to the spawned process

Keyword :spawn-id:

the spawn-id(s) to use, defaults to spawn-id

Type :spawn-id:

expect/struct-spawn, optional

Return:

#<unspec>

exp-close will also set the mfd structure element to #f

spawn-id can be a struct-spawn or a list of such struct-spawn s.

function expect/exp-log-file file (:spawn-id spawn-id) (:append #t)

Log the send/expect dialog for spawn-id to file or not

Param file:

the file to log to

Type file:

see below

Keyword :spawn-id:

the spawn-id(s) to use, defaults to spawn-id

Type :spawn-id:

expect/struct-spawn, optional

Keyword :append:

to append or not, defaults to #t

Type :append:

boolean, optional

Return:

#<unspec>

file can be:

  • #f to disable logging to a file

    If the existing log file was passed as a string, ie. it was opened by this module, then the associated log file handle will be closed. If this log file was shared with other spawn-ids then this closure will affect all of them.

    Both log-file and lfh will be set to #f in all cases.

  • a string, indicating the filename to log to

  • an output handle

  • a file descriptor (open for output)

spawn-id can be a struct-spawn or a list of such struct-spawn s.

function expect/exp-log-user on?

Log the send/expect dialog to the user (ie. stdout) or not

Param on?:

to log or not

Type on?:

boolean

Return:

#<unspec>

The default is to have logging to the user enabled.

function expect/exp-send msg (:slow #f) (:human #f) (:spawn-id spawn-id) (:cr #f)

send msg to the spawned process

Param msg:

the string to send

Type msg:

string

Keyword :slow:

send msg slowly, defaults to #f

Type :slow:

boolean, optional

Keyword :human:

send msg humanly, defaults to #f

Type :human:

boolean, optional

Keyword :spawn-id:

the spawn-id to use, defaults to spawn-id

Type :spawn-id:

expect/struct-spawn, optional

Keyword :cr:

to send a ‘carriage return’ or not, defaults to #f

Type :cr:

boolean, optional

Return:

#<unspec>

:slow says to use the value of exp-slow which is a tuple of the number of code points in a burst prefixed by a delay of a number of milliseconds. The default is (600 50) – 600 code points and a 50 ms delay – roughly equivalent to a 115200 baud modem (if the code points were ASCII).

:human says to use the value of exp-human which is a tuple of:

  • the average gap between in-word code points

  • the average gap transitioning from an in-word code point to a non-word code point

  • a moderating factor, K:

    “tiredness” might be represented by a K < 1 and preternaturally consistent typing by a K > 1

  • the minimum inter-code point gap

  • the maximum inter-code point gap

The default is (180 240 1 45 360) roughly equivalent to a 60 wpm typist.

The algorithm used to calculate the inter-code point gap is the inverse cumulative distribution function of the Weibull distribution. The same as Don Libes’ expect(1).

:human is preferred to :slow if both are supplied.

:spawn-id can be passed a C/int representing an open (for output) file descriptor which will be used instead. For example, to send output to the user pass libc/STDOUT_FILENO.

Example:

Suppose we wanted to observe a professional typist attempting a well-known English pangram:

exp-send "The quick brown fox jumps over the lazy dog" :spawn-id libc/STDOUT_FILENO :human #t
function expect/exp-send-human fd msg

send msg slowly as if a human was typing

Param fd:

file descriptor

Type fd:

C/int

Param msg:

message to send

Type msg:

string

Return:

#<unspec>

exp-send-human uses a similar algorithm to expect(1)

See also

exp-send for details

function expect/exp-wait (:spawn-id spawn-id) (:close #t)

wait for the spawned process

Keyword :spawn-id:

the spawn-id(s) to use, defaults to spawn-id

Type :spawn-id:

expect/struct-spawn

Keyword :close:

close the mfd, defaults to #t

Type :close:

boolean

Return:

see below

Rtype:

list

exp-wait will call exp-close if :close is true and exp-close has not already been called.

Otherwise exp-wait will read data from the spawned process until End of File (or some ^system-error) is indicated.

Warning

Neither option is ideal if the spawned process has not completed its processing

  • pro-actively closing the mfd of a running program will probably have the operating system send the spawned process a hangup signal

    This is most likely to result in a failed process state (and presumes the spawned process does not ignore or otherwise handle a SIGHUP).

  • on the other hand, not closing the mfd might have the spawned process block waiting for full output buffers to be emptied, hence reading from the spawned process until End of File is indicated

    However, if the spawned process is waiting for some input before writing any final output then the spawned process will also block.

exp-wait will return a list of the result from waitpid plus a decoding of the status, eg. (exit 0) or (killed 1) (see tip).

spawn-id can be a struct-spawn or a list of such struct-spawn s. If spawn-id is a list the value returned will be a list of individual result lists.

Tip

The per-spawn-id result is also stored in the struct-spawn structure as the status field.

function expect/exp-spawn-sync [spawn-id [timeout]]

wait for the spawned processes to be “ready”

Param spawn-id:

the spawn-id(s) to use, defaults to spawn-id

Type spawn-id:

a list of expect/struct-spawn

Param timeout:

timeout per spawn-id (in seconds), defaults to exp-timeout

Type timeout:

integer

Return:

unspecified

Rtype:

unspecified

If you spawn multiple processes on a transiently/mechanically under-resourced system where a subsequent exp-case is intended to test patterns against all processes it is quite possible that one or more of the spawned processes will not (yet) have been scheduled to run by the operating system before the exp-case starts.

poll(2)/select(2) will quite happily return with what is available from those processes that have been scheduled leaving the possibility of missing data from not yet ready processes.

exp-spawn-sync will test each of the spawn-ids in spawn-id in turn for a response which will give the operating system an opportunity to schedule the process in. The response could be that the is output available to be read, there is no output or an error has occurred – however, at least a response is ready.

There should be no need to use exp-spawn-sync for single-instance spawn-ids as the following exp-case provides the same functionality implicitly.

exp-spawn-sync is only of use where multiple spawn-ids are expected to be tested against simultaneously.

function expect/exp-set-winsize [spawn-id [lines [columns]]]

set the terminal’s window size

Param spawn-id:

the spawn-id to be set, default spawn-id

Type spawn-id:

struct-spawn, optional

Param lines:

terminal lines, default that of Idio’s terminal, if available

Type lines:

fixnum|C/int

Param columns:

terminal columns, default that of Idio’s terminal, if available

Type columns:

fixnum|C/int

Return:

#<unspec>

Last built at 2024-12-21T07:10:38Z+0000 from 62cca4c (dev) for Idio 0.3.b.6