Getting Started Documentation Glish Learn More Programming Contact Us
Version 1.9 Build 1556
News FAQ
Search Home


next up previous contents index
Next: Shared Clients Up: Events Previous: Point-to-Point Communication

Subsections



Creating Clients

Clients form the heart of the Glish system. They can be created in two ways. You can use either the predefined client function to execute a new instance of a client program (i.e., a program linked with the Glish Client Library; see Chapter 15, page [*]), or the predefined shell function to run an unmodified UNIX program as a simple type of Glish client. These two alternatives are discussed below.


The client Function

The client function takes the name of a program and an optional set of arguments, invokes the program with the arguments, and returns a Glish agent value. You can then use this value to manipulate the program via events, as discussed in § 7.4, page [*], and § 7.5, page [*], above.

A call to client requires at least one argument, a string giving the name of the program to execute. If the name begins with a slash (/) or a period (.) then it is interpreted as giving the complete pathname to the program; otherwise system.path.bin.hostname is used to locate clients. hostname in this case is the host on which the client is being started. If no field matching the hostname is found in system.path.bin, system.path.bin.default is used (if it exists). A field for the local host is created in system.path.bin at startup time using the $PATH environment variable.

Additional arguments to client are converted to string values, if they are not already strings, and passed to the program. For example,

    c := client("tester", 1:3, "hi")
invokes the program tester and passes it two string arguments, ``[1 2 3]'' and ``hi''. The conversion of non-string arguments to strings is done in the same mannar as when they are printed. An element by element conversion can be done using as_string:
    c := client("tester", as_string(1:3), "hi")
would invoke tester with four arguments ``1'', ``2'', ``3'', and ``hi''. Starting tester with:
    c := client("tester", 'hello there')
would invoke tester with a single argument ``hello there'' whereas:
    c := client("tester", "hello there")
invokes tester with a two arguments ``hello'' and ``there''.

When a single multi-element string is supplied, the first element is taken to be the name of the program to invoke. The last example can also be written:

    c := client("tester hello there")
This invokes the program ``tester'', not ``tester hello there''. However,
    c := client('tester hello there')
fails because there is no program called ``tester hello there''.

The client function also takes a number of optional, named parameters:

host=
specifies on which host to run the client, as a string scalar. For example,
    c := client("tester", host="mars")
runs tester on the remote host mars. If missing, the client runs on the local host.

input=
takes a string value and makes it the client's standard input. The value is split into lines at each occurrence of a newline ('\n'):
    c := client("tester", input="hello there")
results in tester seeing a single line on its standard input, namely the string ``hello there'', while
    c := client("tester", input='how\nare\nyou?')
results in three lines appearing on tester's standard input.

Note that presently non-string values are converted to strings as though they were being printed, so

    c := client("tester", input=1:3)
results in the single line ``[1 2 3]'' appearing on the standard input. This may change in the future.

If no input= argument appears then the client inherits the Glish interpreter's standard input.

suspend=
takes a boolean value. If true then when the client runs it will first suspend itself, allowing a debugger to attach. Suspending clients generate a message like:
    tester @ myhost, pid 18915: suspending ...
indicating that the client tester running on host myhost with process ID 18915 has suspended. (See § 13.2, page [*], for more information on debugging suspended clients.)

ping=
takes a boolean value. If true then whenever an event is sent to the client, the client is ``pinged" by also sending it a SIGIO signal. Use of ping= is discouraged, because it is error-prone (in particular, receipt of a single SIGIO signal may indicate more than one new event has arrived for the client). I'm interested in hearing from users who find ping= indispensable.

async=
takes a boolean value. A true value specifies an asynchronous client. In this case, Glish does not execute the client process. Instead it assumes the user has arranged a separate mechanism for invoking the process, perhaps because the mechanism Glish uses to invoke remote clients (Chapter 16, page [*]) is unavailable for this particular client.

For asynchronous clients, the client function still returns an agent value. The record associated with this value has its activate field set to a string giving special command-line arguments that need to be passed to the client. Once the client is executed by some means, these arguments will then be interpreted on the client's behalf by the Glish Client Library so that the client knows how to connect to the Glish interpreter.

For example, executing:

    a := client("special", async=T)
    print "executing special using:", a.activate
assigns to a an agent value and then prints out the arguments that need to be used to invoke special and associate it with a's agent. Note that a host= option should not be used even if special is running on a remote host.

When the asynchronous client runs and ``joins" the current Glish script it generates an established event, just as do regular clients (see § 7.11, page [*], below). Even before this happens, though, you can send events to a and execute whenever statements for responding to a's events.


The shell Function

You can use the shell function to incorporate unmodified UNIX programs into a Glish program. As noted in § 4.10, page [*], above, a standard use of shell is to run a UNIX program, wait for it to terminate, and collect its output as a string vector. For example:

    sources := shell("ls *.c")
returns in sources a list of all of the files in the current directory that end in ``.c". The return status of the command which the shell executes is returned as an attribute:
    print sources::status
to allow you to check on the exit status of the UNIX command.

Similar to client (see preceding section), shell takes a number of optional arguments. Of these, host=, input=, and ping= behave identically (suspend= is useful only for debugging Glish itself).

The async= option behaves differently, though. It runs the shell command asynchronously; that is, it instructs Glish not to wait for the command to complete but instead to use an event-oriented interface for the shell command. The asynchronous shell commands are called ``shell clients".

The asynchronous interface works as follows. First, when async=T is used, shell returns not a string value but instead an agent value, as client does. You then use the agent value to send stdin events to make text appear on the shell client's standard input, EOF events to close the standard input, and terminate events to terminate the client. Furthermore, each line of text the shell client writes to its standard output becomes a stdout event.

To illustrate, here's a Glish script that uses awk to print the numbers from 1 to 32 in hexadecimal, each appearing as a separate event:

    cvt := "awk '{ printf(\"%x\\n\", $1) }'"
hex := shell(cvt, async=T)

    count := 1
    hex->stdin(count)

    whenever hex->stdout do
        {
        print count, "=", $value
        if ( count < 32 )
            {
            count +:= 1
            hex->stdin(count)
            }
        else
            hex->EOF()
        }

The first two statements associate an asynchronous shell client with the variable hex. The next line initializes the global count to 1 and sends that value to hex, making it appear on awk's standard input.

The whenever body prints out the current count and its hexadecimal equivalent, and then either increments the count and sends awk a new input line or closes its standard input.

You might think that a race exists between sending the first stdin event to hex's client and setting up the whenever to deal with the client's response. This problem does not arise, however, because the Glish interpreter does not read events generated by clients until it is done executing all of the statements in a script. (See § 13.1.2, page [*], below.)

One final note regarding asynchronous shell commands. Glish uses a technique called ``pseudo-ttys" for communicating with shell clients. This makes the shell clients' standard output be line-buffered (instead of block-buffered, the default). Without this technique, in the above example awk would buffer up its output until either it had filled an entire block (a lot of text) or its standard input was closed and it exited. In this case you would not immediately get a new stdout event for each stdin event, and the program would not work correctly. One drawback of using ``pseudo-ttys", though, is that the shell command truly believes that it is writing to a terminal. Programs that alter their behavior depending on whether they're writing to a terminal will engage the altered behavior. For example, on BSD systems the following Glish program:

    a := shell("ls", async=T)
    whenever a->stdout do print $value
prints out several filenames at a time, because ls writes its output in columns when writing to a terminal.


next up previous contents index
Next: Shared Clients Up: Events Previous: Point-to-Point Communication   Contents   Index
Please send questions or comments about AIPS++ to aips2-request@nrao.edu.
Copyright © 1995-2000 Associated Universities Inc., Washington, D.C.

Return to AIPS++ Home Page
2006-10-15