3    The Emacs OLCR Client

The Emacs OLCR client (EOLCR) was written as a replacement of the standard text-based OLCR client, olcr. It was developed by a consultant, for consultants, out of frustration with the limitations of the simple interface provided by olcr, and out of a need for greater power and flexibility.

The first section of this chapter will discuss the motivation behind writing the EOLCR client. The second section will focus on the use and capabilities of EOLCR. Ideas and details of its implementation will be investigated in the third section. Finally, the fourth section will present a brief analysis of the more salient aspects of EOLCR as a whole.

Note that EOLCR is a large, comprehensive system; for the sake of economy, only the most distinctive and important features of EOLCR will be mentioned. Those interested in more complete documentation of EOLCR's capabilities are referred to the Emacs OLCR User's Manual and the Emacs OLCR Programmer's Manual.

3.1    Motivation

On-duty consultants typically face an OLC queue of about 50 questions. Experienced consultants are connected to anywhere from one to 10 questions at a time [1], many of them to logged in users with whom the consultants must maintain interactive dialog.

However, the olcr client provides only a very primitive, scrolling ASCII interface with which to handle this load. Consultants with multiple questions need to constantly monitor the replay logs of their users, the OLC queue, and their status in OLC. With olcr, consultants have to continually replay logs and relist the queue. They are also hindered by the fact that they can only do one thing at a time, which is frustrating since olcr commands are far from instantaneous.[2] Also, as mentioned in the previous chapter, consultants use many special tools on-line. However, these tools are not integrated with olcr,[3] resulting in more inefficiency. The standard ``solution'' was to run several xterm programs, each one dedicated to running a separate olcr. This, however, provided a very limited solution to only some of these problems.

Out of this frustration arose the Emacs OLCR client.[4] EOLCR caches the OLC queue, replay logs, and the consultant's status, making them instantly available within a keystroke or two. All EOLCR commands are run in the background, so several commands can be executed one after the after the other without waiting for one to terminate before starting another. EOLCR listens to Zephyrgrams from the OLC daemon regarding OLC state changes, so that the consultant is kept as up to date as possible. EOLCR works particularly well with the consulting tools mentioned in the previous chapter; they were written with EOLCR in mind. EOLCR also has an extended set of commands, a convenient and customizable user interface, and complete on-line documentation. Over the last two years, EOLCR has replaced olcr as the standard OLCR interface, and is currently used by over 70 Athena consultants, volunteers, and staff.

Emacs was chosen as the user interface for several reasons. Initially, the ease and power of elisp allowed development of a client which was actually useful very quickly. In fact, throughout EOLCR's development the inherent power of Lisp as an extension language was crucial, since it allowed many invaluable features to be added in a short amount of time and with relatively small amounts of code.

Furthermore, the Emacs interface provided a familiar, well-liked, and easily customizable environment. The fact that Emacs itself is a text editor was essential, since much of a consultant's job involves writing text. Finally, Emacs could also be used on a plain text terminal, which benefited the many consultants which poll the queue remotely by dialing in to Athena.

3.2    Using EOLCR

A consultant fires up EOLCR by typing M-x olcr-list in Emacs. This loads EOLCR, retrieves the OLC message of the day, and displays the current OLC queue.

EOLCR creates three main types of buffers: the List buffer, Replay buffers, and the Status buffer. The List buffer is created automatically on startup; it holds the current OLC queue. The Status buffer displays all users to which the consultant is connected.[5] Replay buffers are created when the consultant replays logs. All three types of buffers share a common set of consulting commands, most of which are single keystroke, for example g to grab a user, r to replay a question, s to send a message to the user, and so on.

EOLCR provides several other commands to ease manipulation of the various buffers. Typing RET switches to a Replay buffer; typing RET several times cycles among all the Replay buffers in EOLCR. ESC n and ESC p cycle among the consultant's connected Replay buffers, i.e. the Replay logs of users to which the consultant is connected. ESC n goes to the user with the next highest instance, and ESC p goes to the user with the next lowest instance. Alternatively, typing n i switches to the log of user with instance n.

In addition to the standard set of consulting commands available in olcr, EOLCR provides commands to perform other common consulting tasks. The Emacs version of Athena's On-Line Help module, OLH, and the stock answers are available with C-c a. Similarly, CRef, Dig, and Klook are available via C-c c, C-c d, and C-c k.

Other additional EOLCR commands include z to zlocate a user, C-c h to obtain a user's Hesiod information, C-c f to finger a user @Athena and @MIT, C-c H to run hostcheck, a program retrieving useful information about a user's workstation, and C-c p to check the status (via /etc/ping) of a user's file server.

The zlocate command of EOLCR---olcr-zlocate, invoked by typing z---deserves some special attention. A common nuisance consultant's face is users who are logged in, but idle at their workstation. This is frustrating when the consultant attempts to communicate with the user, but is not aware that the user is idle and thus probably not at the workstation. olcr-zlocate runs zlocate on the user, and if the user is logged in, fingers the user's workstation so the consultant can see if the user is idle. Furthermore, the result is displayed in the consultant's Status buffer---discussed in detail below---so the consultant is aware of whether the user is logged in or idle.

3.2.1    The Zephyr Client

EOLCR takes advantage of OLC's use of Zephyr by listening to OLC Zephyrgrams. It does this by running a small Zephyr client which subscribes itself to personal messages from the OLC daemon and to class ``OLC'' messages. The Zephyr client listens for and acts accordingly with the following OLC daemon messages:

The Zephyr client updates EOLCR according to the type of Zephyrgrams it receives. For example, if a new question arrives, the Zephyr client will automatically add the question to the List buffer. Likewise, if a question is forwarded, the List buffer will be updated to reflect the new status of the question. For other types of Zephyrgrams---for example those informing the consultant that a user has logged out---the Status buffer and the corresponding Replay buffer are updated.

3.2.2    The Status Buffer and Mode Line

Typing S from the List buffer or any Replay buffer creates the EOLCR Status buffer. The Status buffer shows all the users to which the consultant running EOLCR is connected. See Figure 3-1.
 
                             OLCR Status for Gabriel

                          You are not signed on to OLC.

                                   Last      Last
Inst.   User       Idle?  Status  Update  Transaction  Topic   Description
  0   jfc      [0]   N     Done    7:00p       ?       unix    [kernel debugging]
  1   jik      [0]   N    Active   7:03p     6:49p     zephyr  [manual pages    ]
  2   wdc      [0]   N    Active   7:05p     7:02p     ez      [can't print     ]

Figure 3-1: A Sample EOLCR Status Buffer

 
The Status buffer displays the following information, in addition to the username, instance, topic and description data available in the List buffer, regarding each connected user:

Idle?
This field shows if the user is logged in, and if so, if the user is idle. ``L'' means that the user is logged out, ``Y'' that the user is idle, ``N'' that the user is logged in but not idle. EOLCR normally keeps track of whether a user is logged in; the decision of whether the user is idle comes from running olcr-zlocate on the user.

Status
This field serves as a useful summary of the consultant's current interaction with the user. The possible entries are listed in Table 3-2.

Last Update
The last time the consultant updated the user's log.

Last Transaction
The last time there was a message transaction between the user and the consultant; i.e. the last time the user or the consultant sent a message, or the consultant sent mail.

Any of these fields may have a ``?'', meaning EOLCR does not have the information.

The Emacs mode line of EOLCR List, Status, and Replay buffers displays a summary of the consultant's status in OLC by showing the ``Status'' column of the Status buffer. Initially, the right portion of the mode line looks like this:
 
[..... ..... ..... ..... ..... ..... ..... ..... .....]
 
Each position on the mode line, except for the blank spaces, represents one of the consultant's instances in OLC, starting with zero. A period means that the consultant is not connected to any users on that instance. Once the consultant is connected to a user, the position on the mode line corresponding to that consultant's instance with that user is replaced by one of the following letters, representing the ``Status'' column in the Status buffer:
 
W    Waiting. The user has sent a message, but the consultant has not responded.
L    Logged out. The user is logged out.
I    Idle. The user is logged in, but idle.
A    Active. User is logged in, but not idle.
M    Mail. The consultant has sent mail to the user.
N    New Messages. The user has sent the consultant messages which the consultant has not yet read.
U    Unseen Messages. The consultant has sent messages to the user which the user has not yet seen.
D    Done. The user has typed done in olc.
C    Cancel. The user has typed cancel in olc.

Table 3-2: Status Field of Status Buffer

 
For example, the mode line for a consultant connected to users as in Figure 3-1 will show the following:
 
[DAA.. ..... ..... ..... ..... ..... ..... ..... .....]
 
because the consultant is connected on instances zero through two. This mechanism provides a quick and simple overview of a consultant's status, without having to actually switch to the Status buffer.

3.2.3    Input Buffers

Input buffers are used by commands requiring input of more than a few characters. Input mode is very similar to Emacs' Text mode, but provides some additional consulting-related commands; these usually operate on the user involved in the command being executed. C-c u and C-c q insert the user's unseen messages---i.e. those messages from the consultant which the user has not seen yet---and the user's question, respectively, into the buffer, and are useful when composing mail to a user. C-c c, C-c k, C-c d, and C-c a access CRef, Klook, Dig, and the stock answers and OLH as in other EOLCR modes.

Input mode provides another very useful command for consultants referring users to the stock answers: C-c p (olh-insert-path[6]). Suppose a consultant wishes to refer a user to the stock answer on including PostScript figures in LaTeX documents. The consultant types C-c a to enter the stock answers browser, finds the stock answer, then enters the Input buffer and types C-c p, which inserts the following into the buffer:
 
12. Answers to common questions
 9. LATEX Answers
 2. FIGURES Answers
 1. Including POSTSCRIPT pictures into a LaTeX file
 
This informs the user of the menus leading to the desired answer. Because consultants constantly refer users to the stock answers, C-c p provides an excellent shortcut to a task which consultants used to perform manually.

3.2.4    Getting Help

EOLCR has an extensive built-in help system. Help on any EOLCR command or major mode is available. Also, the entire Emacs OLCR User's Manual is accessible in Emacs' Info format.

Typing ? enters EOLCR help. The initial Help buffer gives an overview of how EOLCR help works (see Figure 3-3). At this point, the consultant can request command help by typing c, major mode help by typing m, or ``general'' help via the EOLCR user's manual by typing g.
 
                           OLCR Help on Help

Hi!  You have entered OLCR's Help system.  From here, you can get help on
an OLCR command, any OLCR mode, or some general help on OLCR by looking
at the on-line version of the Emacs OLCR manual.  Once you have entered
OLCR Help (you are in it right now), you can `move around' in this
buffer with these commands:

SPC             Scroll up (same as C-v)
DEL             Scroll down (same as ESC v)
,               Go to the beginning of the buffer
.               Go to the end of the buffer
q               Remove this window (exit Help)

Now, to getting help on OLCR itself.  The following 3 sections tell you
how to get help on either a particular OLCR command, an OLCR major mode,
or how to access the on-line version of the OLCR manual.  You get to this
information by hitting one of `c', `m', or `g' (for Command, Mode, or
General help respectively).  Note that you can always get back to this
text you are reading now by hitting `?' within OLCR Help, and get
additional Command, Mode, or General help by hitting `c', `m', or `g'
again.

Figure 3-3: EOLCR Help Main Buffer (Partial Display)

 
Command help prompts the consultant for any of EOLCR's commands, and displays the following information about the selected command:

Major mode help is similar in concept to Emacs' C-h m command, but provides additional information. For example, for Status mode the full meaning of the columns in the Status buffer is explained. Also, the key bindings for all major EOLCR modes, and not just for those in that particular mode, are displayed in a more organized format than that provided by C-h m.[7]

General help enters the Emacs Info documentation browser and loads the user's manual. Info is an on-line documentation system allowing perusal of on-line documents with chapters, sections, and cross references. The GNU Emacs, Texinfo, and Regex library manuals are available on-line in Info format.

The EOLCR user's manual is a very extensive document covering almost every imaginable aspect of EOLCR, from basic operation to advanced commands to specialized customization. It contains several indices and a special section on the changes made to the most recent EOLCR version. It is also available in DVI format on-line, and as a printed manual in the OLC office.

3.2.5    Customization

One of the main advantages of EOLCR is its customizability. Consultants can modify many aspects of EOLCR to suite their particular needs and tastes. Consultants customize EOLCR by the usual Emacs customization methods: changing elisp variables and major mode hooks.[8] These changes go in the consultant's $HOME/.emacs file, along with all other Emacs customizations.

Some of the customizations in this section refer to a data structure called an olcr-user. This is a data type defined by EOLCR which represents a user in OLC. For this section, how it is defined and used will not be important; these will be discussed in detail later. For now, consider the olcr-user type as a structure representing a user, and containing all information EOLCR knows about the user.

This section will cover the more important features of EOLCR which can be customized by the consultant. Many more options are available, but for economy only the two most widely used will be discussed. The rest are covered in the Emacs OLCR User's Manual.

3.2.5.1    Changing the List and Status Buffers
The format of the List and Status buffers can be completely specified by the consultant. EOLCR takes two elisp print functions---one for the List buffer and the other for the Status buffer---whose input is an olcr-user, and which return a string that is inserted in the List or Status buffer when that user is displayed in the corresponding buffer. The default functions yield the List and Status buffers shown in Figure 3-1 and Chapter 2, Figure 2-7. The print function is called when the List and Status buffers are created, once for each user, and when EOLCR needs to update a user.

For both buffers, the consultant can also specify the order in which users appear. Another two functions, called insert functions, take an olcr-user, and a list of olcr-user, and insert the first into the second. The order in which users appear in the list is the order in which they appear in the List or Status buffer. (There is a separate list for each.) The default List buffer insert function orders users according to when the question was asked, from latest to most recent, and the Status buffer insert function orders them in increasing order according to their instance with the consultant.

The List buffer allows specification of which queues should be displayed in the buffer, and in what order. The default is to display all six queues---active, pending, unseen, pickup, refer, and on-duty. This is useful for volunteer consultants or consultants performing queue management, neither of which need to see the entire OLC queue. Also, the actual name of the queue as displayed in the buffer is customizable; for example one can have ``New Questions'' appear instead of ``unseen''.

Finally, because many of the consultants using EOLCR are volunteers, it is unnecessary to always consider all the questions in the queue. For example, certain volunteers may only be interested in answering questions under the ``matlab'' or ``fortran'' topics. For these consultants, EOLCR specifies a filter predicate which takes an olcr-user and returns t if the user should be displayed in the List buffer, or nil otherwise.[9] For consultants which must consider the entire queue, but pay special attention to certain types of questions, the ESC TAB command in the List buffer jumps to the next user in OLC having a question in one of these ``specialty'' topics. The default list of specialty topics is not set, but the consultant can easily set it in the $HOME/.emacs file.

3.2.5.2    Sending Mail
Consultants frequently send mail to users who have logged out before their question could be answered. Conventions among consultants for sending mail vary greatly. A few common characteristics are almost standard:

In EOLCR, a consultant simply specifies a header file and a signature file in their home directory, whose contents are the header and signature of all OLC mail. The automatic insertion of unseen messages and the user's question is also variable. For unseen messages, because it is not always the case that a consultant wants to have them inserted automatically, the consultant can tell EOLCR to display them first before actually inserting them.

3.3    Implementation

3.3.1    Overall Structure

EOLCR consists of several modules, each implementing either a major mode and its related commands, or a set of common commands. While several of the major modules are highly interdependent and are strictly necessary to run EOLCR, most are independent of each other and are loaded on demand using elisp's autoload function. The following files define some of the modules essential to EOLCR operation:

olcr.el
This is the initial file loaded by EOLCR; its main function is simply to load all other files.

autoloads.el
Sets up all the EOLCR modules which are not loaded on startup to be loaded on demand. Consists mostly of autoload instructions for the various commands.

main.el
Defines some essential functions of EOLCR: manipulation of users, basic subprocess execution, automatic updating, and parsing of the OLC queue.

misc.el
Various miscellaneous functions needed by EOLCR. This file is actually about twice the size of main.el because it contains many functions used throughout the rest of the code.

list-mode.el
Defines List mode and its associated functions. This includes code to obtain the queue listing from the OLC daemon and to implement the various commands available in List mode.

commands.el
Defines the core set of EOLCR commands. These are the most basic commands almost always used, for example olcr-change-description and olcr-motd.

The main modules consist of the List module, the Status module, the Input module, and the Replay module, corresponding to the List, Status, Input and Replay buffers and their associated functions. The List and Replay modules are always loaded. The Status module is loaded only when the olcr-status command, which displays the Status buffer, is executed. Once loaded, the Status module recreates the Status buffer whenever the List buffer is updated.

Most other modules form orthogonal sets of commands which are loaded on demand. The Zephyr client and Help modules are good examples. Other EOLCR commands are also grouped according to overall functionality, and loaded on demand as a unit. For example, all commands interacting with the OLC daemon which the consultant can only use once connected to a user are defined in olc-commands.el. Other commands, such as olcr-finger and olcr-hesinfo, are defined in other-commands.el. Finally, specialized minor modes for the Replay, List, and Status buffers are each treated individually, rather than as a unit. Thus, the Description minor mode for the List and Status buffers is defined in description-mode.el, Indent mode for Replay buffers in indent-mode.el, and so on.

3.3.2    The olcr-user Data Type

EOLCR defines an olcr-user data type to represent a user in OLC. The olcr-user type is manipulated in similar fashion to structures created with Common Lisp's defstruct. An olcr-user instance is created with the make-olcr-user macro. Slots are accessed and modified via olcr-user-slotname and olcr-user-set-slotname macros respectively.

Because elisp does not have a defstruct facility, EOLCR defines a very simple DEFSTRUCT macro in defstruct.el.[10] It provides the essential functionality of defining a structure, creating instances of the structure, and accessing and modifying the fields of an instance.

Many of the slots defined for an olcr-user correspond to the obvious values relating to the user: username, machine, user instance, queue, status, and so on. Others hold EOLCR-specific information, such as the Replay buffer associated with the user (if any), and the last update and last transaction times as displayed in the Status buffer.

To facilitate updating of other EOLCR state when slots of an olcr-user structure are modified, EOLCR provides the olcr-with-user-redisplay macro. This macro takes as arguments an olcr-user structure and a body of elisp code. The code is executed; afterwards, the macro examines which slots of the olcr-user structure were modified, and updates other EOLCR state as necessary. The macro is actually far from all-inclusive and handles only a few simple situations, but is nevertheless very useful in many circumstances.

3.3.3    Internal State

The only state needed by EOLCR is that of the OLC queue. The queue listing is stored internally in six different variables, one for each of the active, pending, unseen, pickup, on-duty and refer queues. The order of the users under each queue in each of these variables is the same order they appear in the List buffer, and indeed the List buffer's user insert function receives the value of one of these variables when adding a new user. The Status buffer, displaying users in a different order, must keep its own list of users. (The actual user instances are not copied, so no data is duplicated.) Note that this data representation scheme directly reflects the organization of users in the OLC queue, which greatly facilitates EOLCR's data manipulation.

The above scheme is actually slightly more complicated. EOLCR also keeps an association list of pointers to these variables, because often a user must be looked up given an arbitrary queue. (Using only the above variables works well only if EOLCR knows a priori in what queue the user is going to be in, which is not true.) The association list is indexed given a queue name, and the resulting entry contains the symbol name of the variable containing users in that queue. The symbol-value and set functions can then be used to modify the variable.

EOLCR keeps the users in the variables, rather than in the entry directly, for two reasons. First, it greatly simplifies some operations in which the queues involved are known a priori. Second, elisp unfortunately does not provide an equivalent for Common Lisp's setf macro; thus, modifying an element of a list (other than the first, which is easily handled by setcar) requires using nthcdr followed by setcar, which is less convenient.

Because users must be often looked up without knowing what queue they are in, EOLCR also maintains a hash table of pointers to users, implemented as an elisp obarray. An obarray is an elisp data type which serves as a very simple hash table: it only works with strings as keys and symbols as values.[11] The idea is to indirectly keep each user in the obarray by constructing a unique Lisp symbol for that user based on the user's username and user instance. Because obarrays can only store symbols hashed by strings, the symbol's property list stores the actual user structure, under the olcr-user property. Thus, to look up a user structure, given the username and user instance, EOLCR first constructs the string used to hash into the obarray from the username and user instance. This returns a unique symbol, whose print name is the string used to hash into the obarray. The user structure is then retrieved from the property list of this symbol.

Lisp hackers may wonder why EOLCR needs to create its own obarray to do this (instead of using the global Emacs obarray containing all elisp symbols), or even why any obarray is necessary, since once the symbol is created, it does not need to be ``looked up'' anywhere. One problem is that symbols in elisp do need to be stored somewhere; most are interned in the global Emacs obarray. EOLCR uses its own obarray for two reasons: first, hash look up is faster, since the EOLCR obarray will be much smaller and with many less symbols. Second, there is no way to remove a symbol from an obarray. If EOLCR were to use Emacs' global obarray, there would be no way to remove its entries once EOLCR finished or when users were removed from the queue. Keeping its own obarray allows EOLCR to simply discard the obarray when reading in a new queue listing, thus letting garbage collection clean up the mess.

3.3.4    Automatic Updating

One important function of EOLCR is that of automatic updating. This is simply the process by which EOLCR attempts, as much as possible, to keep all its main buffers constantly updated. For example, when the Zephyr client receives notice of a new question, the List buffer is automatically updated. EOLCR tries to stay up to date as much as possible, but it can not always reflect an exact picture of the state of OLC. For example, EOLCR has no way of knowing when a consultant (other than the one running EOLCR) has grabbed a question; it can only know if this has happened when the consultant running EOLCR updates the List buffer. Other types of transactions it can keep track of, the most obvious being those broadcast by the OLC daemon over Zephyr, since the Zephyr client finds out about them immediately. Also, EOLCR obviously knows about any transactions executed by the consultant running EOLCR.

Besides using the Zephyr client and the OLC queue to stay up to date, EOLCR examines the contents of replay logs, since all transactions between the user and OLC are recorded in the replay log for the user. When EOLCR replays a question, it examines the contents of the Replay buffer to determine the latest status of the question: the topic and description, whether the user is connected to a consultant, whether the user is logged in, and so on. The new information is then reflected in the List buffer. For connected users, the information displayed in the various fields of the Status buffer is also obtained from parsing the log.

EOLCR making sure that it is constantly up to date serves a very important function, since consultants do not have to worry as much about repeatedly querying the OLC server for information. It also helps reduce the load on the server. However, as already mentioned, EOLCR cannot know about all transactions, so it is still necessary to occasionally retrieve the queue listing to obtain a complete picture.

3.3.5    User Display in the List and Status Buffers

Because the List and Status buffers allow the consultant to specify an arbitrary print function for a user, EOLCR is presented with the interesting problem of knowing what areas of the buffer correspond to what user. For example, consider the sample queue listing in Chapter 2, Figure 2-7. Suppose the Emacs cursor is on the line displaying user ``jik'', and that the consultant types r to replay that question. How does EOLCR know what user the consultant is referring to?

If the users were all displayed in a uniform format, EOLCR would know where on that line the username of the user is found, and could thus extract the information directly from the buffer.[12] Or, if the user print function were forced to always return a string of predetermined length, EOLCR could easily calculate what user should be present at that line. But the print function has no such restrictions. It can display each user in the same format, or use a different format for each user. It can return a string of any length for a user, with or without newlines. How then can EOLCR know which user the cursor is on?

EOLCR solves this problem by keeping track of the start and end positions in the buffer where a user is displayed. This approach would not normally work, because if a user is removed or inserted in the queue, these start and end numbers would have to be manually updated to reflect the new positions of all subsequent users. Fortunately, EOLCR can let elisp keep track of this automatically by using markers---the same type of structure used to hold ``the mark'' defining a region for many Emacs editing commands---to hold the start and end positions, instead of plain numbers. Markers accomplish this because the positions in the buffer they refer to are automatically updated, by Emacs, when text in the buffer is deleted or inserted. Use of many markers in a buffer can slow down editing in the buffer, but the range at which this becomes a problem is much larger than the number of markers typically needed by EOLCR.

EOLCR uses markers in the above fashion as follows: when a user is inserted in the buffer, the start and end positions of the user in the buffer are stored in markers. These markers are stored in the olcr-user structure of the user, under olcr-user-lbuf-marks for the List buffer, and olcr-user-sbuf-marks for the Status buffer. Now, given an arbitrary cursor position in for example the List buffer, EOLCR can find out which user, if any, is displayed at that position by using a binary search through the lbuf-marks of each user in the queue. Note that a binary search assumes that the marks it is searching through are numbered incrementally; but this is the case, because users are stored in the order in which they are displayed in the List buffer. The Status buffer is slightly different, because it keeps a separate ordered list of the users displayed in the buffer, but the idea is the same.

3.3.6    Subprocesses

EOLCR makes intensive use of subprocesses in Emacs. Indeed, EOLCR makes no direct communication with the OLC server via elisp; it instead relies on the following programs, which it runs in the background using elisp's start-process function:

olist
Actually written for use with EOLCR, it is a convenient and fast client which can be used elsewhere. It interacts directly with the OLC query daemon to retrieve the OLC queue.

oreplay
Like olist, in that it is a fast client interacting directly with the OLC query daemon. oreplay retrieves replay logs.

olcr
The text-based olcr client is used for all other OLC daemon transactions. It is somewhat ironic that EOLCR uses the very program it is intended to replace to perform its dirty work. But even though the way in which EOLCR is forced to interface with olcr is far from ideal---mainly because olcr was not intended to server as a subclient---it is exactly what EOLCR needs: a general purpose OLC client which can perform all the actual OLC daemon transactions.

zclient
The Zephyr client is implemented as a small C program, run as a subprocesses, which does the actual interaction with Zephyr. The Zephyr client will be discussed separately in the Section Zephyr Client Interaction.

EOLCR also relies on other programs, such as zlocate and finger, to execute commands not interacting with the OLC daemon.[13] Because EOLCR must handle a variety of subprocesses, each of which must be handled differently, EOLCR has four different mechanisms for interacting with subprocesses. Two of these deal with olist and oreplay respectively; EOLCR has a unique method for each because of their specialized use. The rest of the processes (including olcr and those not interacting directly with the daemon, like zlocate) are treated differently depending on whether the command executing the process requires the consultant provide input via an Input buffer.

3.3.6.1    Running olist and oreplay
olist and oreplay are treated individually. olist is run by the List buffer module, and oreplay by the Replay buffer module. The interface is very simple: they are run in the background with start-process, and their output is collected in a file. The output file is removed when the consultant quits EOLCR, or in the case of oreplay, if the consultant is not connected to the user.[14] Note that the oreplay output file is used directly as the actual Replay buffer. However, while the olist output file is parsed internally, what is actually displayed in the List buffer is the result of calling the user print function on each user in the queue.

The Replay module performs some extensive parsing of the log to obtain information about the user, as already discussed. The List buffer module is more thorough since it must reconstruct most of the internal state of EOLCR from the output of olist. However, because EOLCR maintains some information about each user that is not available from olist's queue listing---namely the information displayed in the Status buffer---it does not throw away its previous state. In particular, it updates only the new information in its database, discarding just those users no longer in the queue.

3.3.6.2    Other Commands
Two related commands handle the execution of all other subprocesses: olcr-execute and olcr-start-input. They are very similar in concept and functionality. The main difference is that olcr-start-input is meant for use by commands requiring an Input buffer in which the consultant can provide the input to be given to the process. Both take similar arguments, but while olcr-execute starts the subprocess immediately, olcr-start-input creates an Input buffer for the consultant, and starts the subprocess only when the consultant types C-c C-c to actually execute the command.

The general policy followed by both olcr-execute and olcr-start-input allows for a flexible process handling mechanism which must accommodate the varying needs of EOLCR commands using subprocesses. In particular, these functions take care of the following:

Checking if the process is already running is easy using elisp's get-process function, since each process is given a unique name. Error checking is done in two ways. First, the exit status of the process is examined. Second, the output of the process is examined; if it does not match what the command calling olcr-execute or olcr-start-input expected, an error is assumed. In either case, an error is handled by immediately displaying the buffer to the consultant.

If the process terminated normally, the done function is called with the buffer holding the process output. This done function performs whatever task the command initiating the subprocess needs performed; for example, the done function for the olcr-grab command actually updates the user's olcr-user structure to reflect the fact that the user is now connected, places the user in the active queue, and updates the List and Status buffers.

If no done function is specified, EOLCR assumes that the result should be displayed to the consultant. Because subprocesses are run asynchronously, the consultant may have switched buffers since the command was originally executed. Thus, EOLCR displays the buffer only if the consultant is in the same buffer in which the command was executed. Otherwise the consultant is notified the process terminated via the Emacs minibuffer; the buffer can then be examined at the consultant's own leisure when desired.

3.3.7    Zephyr Client Interaction

The EOLCR Zephyr client performs very critical functions. It is one of the chief advantages of EOLCR, since it is one of the main mechanisms via which EOLCR keeps the consultant up to date.

The zclient program is a very small C client whose only job is to subscribe to certain types of Zephyrgrams, and to print such Zephyrgrams when they arrive. It subscribes to personal messages of the consultant so that it can catch personal messages from the OLC daemon. It also subscribes to the following instances of class ``OLC'', recipient ``*'':

new_question
Announces new OLC questions.

nol
Broadcasts when a consultant has signed on or off duty.

aloha
Broadcasts when a user has logged out.

resurrection
Broadcasts when a user has logged in.

forward
Announces users that are being forwarded by a consultant.

zclient registers its subscriptions with the Zephyr ZSubscribeToSansDefault function, then waits for a Zephyrgram via ZReceiveNotice. Because of a problem with Zephyr in which the Zephyr servers occasionally and quite randomly lose track of client subscriptions, zclient sets up the SIGALRM interrupt to recheck its subscriptions every few minutes, resubscribing and continuing if they have been lost.[15]

On the EOLCR side, the Zephyr client module manages the zclient subprocess. Its tasks consist of starting the process, handling any errors, and taking appropriate action when zclient receives a Zephyrgram.

Errors are handled in a straightforward manner: they are directly reported to the consultant by displaying the zclient process buffer. The error message is inserted into the buffer, including instructions on how to restart the Zephyr client. (This consists of simply typing M-x olcr-start-zclient.)

When the Zephyr client module receives output from zclient, it checks to see if it is any of the known Zephyrgrams which it is prepared to handle. If so, appropriate action is taken; for example, if a ``new_question'' message was received, an olcr-user structure is created (obtaining the necessary information about the username and topic from the Zephyrgram), and the question is inserted in the queue. If the output does not match a known Zephyrgram, the zclient process is stopped, and an error is reported.

3.4    Analysis

EOLCR presents an interesting collection of programming concepts: subprocess management, user interface design, and state maintenance. The EOLCR user interface's main priority is to provide a powerful and efficient environment for consultants facing a heavy load of questions. EOLCR thus presents a rich set of useful commands, many implemented as separate and independent modules, which interact well with one another.

EOLCR, however, faces some awkward situations when dealing with inferior processes. The elisp subprocess interface provided by Emacs has proved adequate for EOLCR use. But the actual communication protocol between EOLCR and its subprocesses is not wholly satisfactory. In particular, the fact that olcr---EOLCR's chief subprocess---was not written to run as an inferior to a parent with whom it must communicate has made EOLCR's general handling of subprocesses less than ideal. EOLCR must rely heavily on regular expressions to parse the output of virtually all its subprocesses. This is not a bad thing in and of itself; for example olist's output is tailored for easy parsing by EOLCR. However, for other processes, such as olcr and oreplay, output is formatted for human viewing rather than EOLCR parsing. EOLCR must therefore parse the output making assumptions based on experimental analysis rather than a well-defined protocol.

Another somewhat more complicated problem is that of EOLCR's insistence on asynchrony among its subprocesses, in particular with olcr. Currently, olcr is loaded for every daemon request (other than list and replay log updates, which are handled by olist and oreplay). This is wasteful, especially considering that olcr is not a trivial program. Ideally, the asynchrony could be maintained without running a new olcr process for every command. Some ideas on resolving this are:

Among these solutions, the last is the more realistic (though not necessarily the more preferable), since it requires no changes in the OLC protocol and little work in terms of writing the actual clients and of the changes that would have to be made to EOLCR.

Finally, EOLCR attempts to resolve problems of state maintenance in interesting ways. EOLCR grabs bits and pieces of OLC state from wherever it can: Zephyrgrams, replay logs, and command output. EOLCR's view of the world is always an approximation, although a very good one. Even after reading the OLC queue, its picture is not guaranteed to be accurate, for two reasons. First, the OLC poll daemon, which keeps track of the login status of users, is itself not always completely up to date, because it checks every user periodically, not constantly. Second, the OLC query daemon from which olist obtains its output is likewise not guaranteed to always be up to date. In particular, the query daemon does not obtain the queue listing directly from the main lock daemon: it instead retrieves a dumped backup copy made by the lock daemon after only certain types of transactions.[17]

EOLCR's use of Zephyr as an updating mechanism is an innovative approach which seems natural but has actually not been widely used elsewhere.[18] Zephyr as a sole communication medium between a client and a server exchanging significant amounts of information is probably not a good idea. But to the limited extent in which EOLCR uses Zephyr in this respect, it does serve as a practical and simple relayer of state information without requiring a hefty protocol specification or a complicated communication model.