Portable Windowed Applications: Pollsters, Onlookers, and Others Leonard J. Harding OG Software, Inc. Ann Arbor, MI 48104 USA Copyright 1998 by OG Software, Inc. =================================================================== This monograph, including the software contained in it, is provided AS IS with no warranties of any kind, either express or implied. The liability of OG Software, Inc., is limited to the replacement of defective distribution media returned to OG Software, Inc., within ninety (90) days of purchase together with a receipt evidencing such purchase. The purchaser of this monograph is authorized to distribute executables of his/her POO applications that contain object code of the POO software. All other rights are reserved to OG Software, Inc. =================================================================== PREFACE I wrote this monograph for people who program, people whose real business is not programming but who are not spectators. There was a time when our numbers were legion, but complex circumstances have thinned our ranks. There are lots of programmers, of course, but this monograph is for people who program. The advances that moved computers onto every desktop, e.g., windowing technology, served to disenfranchise many people who program because the advances require that they invest more knowledge and effort in their programming than they can afford. Consequently, many people were cast up on the shore amongst a flotsam of xterms and DOS windows. But there neither was nor is any fundamental reason for this, the necessary software may not be very exciting and may no longer have a large market, but it is quite straightforward to write. This monograph contains the minimal, basic software that I regard as essential. This basic software, the POO software, provides you the ability to write applications that use multiple, windowed, text I/O streams based on a familiar interface that is consistent with the basic I/O capabilities of the ANSI C and Fortran languages. In short, you can open and close windowed text streams, can append text to an output stream, and can obtain text from an input stream, which is sufficient for the rudimentary text I/O capabilities required by most applications. As the user of a POO application, you can manage the attributes of its windows, the strings assigned to the function keys, the relationships between the output and input streams associated with its windows and the underlying file system, and the execution of the application itself. To manage the windows created by the application, you can interact with the windowing system directly or with the POO software. You can open windows to view files or directories in the file system. Scrolling, input editing, typeahead input, and line re-entry are supported. In general, your activities are transparent to the application. If these basic services are sufficient, you may be able to start programming your computer again and, as your application's user, have access to your computer's inherent capabilities in the context of some current windowing systems. There are, of course, some limitations. First, this monograph contains versions of the POO software only for the X Window System commonly supported in Unix, Microsoft's Windows, and IBM's OS/2 Presentation Manager. Thus, your POO applications can be run only in these windowing systems. Second, whether you program in C or not, you will need an ANSI standard C or C++ compiler that supports the POSIX.1 I/O library, which is provided with most C compilers. If you usually write Fortran applications, you will need to learn how to build mixed-language applications. Third, you will need to learn how to tell your compiler that your applications are both multithreaded and windowed, which may require that you replace or upgrade your software; your DOS C compiler may be found wanting. Although not recommended, the POO software can be run single threaded in any of the supported windowing systems. The X version of the POO software is predicated on the POSIX threads standard. Portability and familiarity played prominent roles in the design of both the application and user interfaces. The five API functions use only standard data types and take at most three arguments. The four I/O functions support the basic capabilities in the language standards and are analogous to the C stream I/O functions fopen, fclose, fputs, and fgets and the Fortran OPEN, CLOSE, WRITE, and READ statements. The user interface supports many of the keyboard and mouse functions that are standard in the supported systems and implements a set of commands for managing window attributes, the function keys, I/O streams, and application. Additional commands provide windowed access to the file system as well as the ability to perform some rudimentary functions on files. Most people who use computers fall in one of two groups: those who try their newest product first and read as necessary and those who read first and then try the product. The POO software is a tool rather than a product, but you can use either of these strategies. Regardless of which strategy you follow, you will need a working knowledge of windowing systems: how to get them started and how to run applications in them. If you fall in the first group, the natural step is to run one or more of the examples. The examples are portable so that there is only one examples file; which of the seven examples is compiled is determined by which constant of the form POO_... is defined at the beginning of the file. The POO software itself, of course, is not portable so that you must use the version that is provided for your system. Depending on what system and compiler you use, you may have to change the values assigned to one or more of the constants that are defined at the beginning of the POO software. These constants are intended to obviate inconsistencies that stem from the system when using X and from the C compiler when using Windows or OS/2. The prose at the beginning of the POO source file provides more details. The prose in the examples file contains some information you may need to run the examples. The examples themselves, of course, illustrate the POO application programming interface. If you follow the suggestions in the prose accompanying each example, running them will provide you an introduction to the POO user interface. This may take an hour or so depending on your experience and spare you having to read the text of this monograph, which inherently cannot have the spellbinding quality of a Clancy novel. If you generally read first, this monograph consists of technical prose that, aside from some commentary, is relatively terse so that you can read through it fairly quickly, particularly if you skip sections that seem to provide familiar information. If you commonly use C when writing your applications, the software will follow a familiar refrain. If you use Fortran, you may have a somewhat more difficult time because the Fortran language is oriented more to statements than functions and the POO software is basically a new I/O library for transmitting textual data, a capability firmly embedded in the Fortran library and normally invisible. The POO software is not intended to provide you the ability to develop products, but this may be only a matter of perspective. Today, a windowed application is more than an application that creates and uses windows. The POO software does not support such common features as menus, either static or pop-up or pull-down, buttons of any kind, or power or speed bars containing an array of inscrutable pictographs, etc. In short, it does not support a user interface consistent with the desktop applications that are commonly available today. On the other hand, the applications for which the POO software is intended are unlikely to appear on a store shelf in a shrink-wrapped box. My requirement that this book could be published electronically on a single diskette containing standard files had a significant impact. Most notably, it became a monograph on windowed text streams. The single diskette requirement conflicts with portability and many other aspects of the design of this software. Thus, errors get little attention. The first draft was too long, but the brutal editing that ensued appears to have succeeded without material impact. Considerable space could have been saved by packaging the three versions of the software differently, e.g., in multiple files or by eliminating comments from the code. I chose to provide each version in a single file for your convenience and to retain all important commentary so that each version would be self-contained. This monograph consists of five files: this text file, a C source file containing the example applications, and a C source file for each of the supported windowing systems. The pure text in this monograph is relatively short; the bulk of the text is contained in the source files. I would apologize for providing the text in a plain text file if there were other options. HTML would be the obvious choice, but none of the supported systems comes from its vendor armed with an HTML viewer, let alone editor, and I am not aware of any C/C++ compiler that accepts source files in HTML. The examples file is about 100KB long, and each of the POO source files is about 350KB long. All of these files are one-third code and two-thirds comments. The commentary in the source files is roughly the same in all versions. Indeed, much of the source code is the same in all versions. Consequently, much of the POO source code has been successfully compiled by at least three different compilers and tested in three windowing systems. I have tested the X Window System version of the POO software in Linux, SunOS, AIX, and HP-UX. I have used the multithreaded version only in SunOS because it was the only Unix system current enough to support POSIX threads. Fortran tests were limited to AIX and Linux, but Linux Fortran is C so that it doesn't really count. The Windows version has been run in Windows 3.1 using the Borland compiler, Windows 95 using both the Borland and Watcom compilers, and NT with the Microsoft compiler. The OS/2 version has been run only in OS/2 Warp 3.0 using the Watcom compiler. In each case, all of the examples contained in this monograph were run in addition to the basic test programs, which include multithreaded test applications when appropriate. Despite this testing and the amount of code common to all versions, all three most likely contain bugs of some sort, which you can likely fix if they are sufficiently annoying to warrant your attention. Finally, I would offer some retrospective commentary about the POO software. The X version is less friendly than the Windows and OS/2 versions, most of which can be traced to the underlying philosophy of the designers of X: they were trying to provide a framework rather than a monolith and, as in many other situations, having few options readily translates to ease of use. In addition, apparently there is sufficient ambiguity in the specifications of the X Window System that its various implementations differ; depending on one's viewpoint, some of these differences could be termed errors. The POO software handles most of these issues, but window geometry appears to depend on the server, Xlib, and the window manager; I was unable to find any common ground in this area. Multithreading support is crucial if you want to write CPU intensive applications using the POO software. Threads are readily available in Windows and OS/2 Warp and less so in Unix. The vendors of C compilers for Windows and OS/2 pay only lip service to the ANSI and POSIX standards. Having spent many years writing anonymous documents in the third person and now being retired and thus free to exercise my First Amendment rights, I have chosen to write this monograph in the first person, perhaps with some backsliding. I have tried to limit the text to the necessary and sufficient information you need to start using the software and have relegated much of the documentation to the comments in the source code, which you should consult for detailed information. If you're reading this monograph, you're probably more interested in exploiting the software than reading my prose anyway, and prose is inherently less authoritative than source code. Those who at least browse the source code will discover that the text of this monograph is not complete; the POO software contains capabilities that are not described in the text, which provides you some incentive to at least glance at the source code. Leonard J. Harding August 1998 TABLE OF CONTENTS INTRODUCTION Introduction The Windowing Systems The Virtual Memory File Facility The Memory File Facility Portability Considerations Other Design Considerations USING A POO APPLICATION Introduction Keyboard Input Mouse Input Command Overview Window Attribute Commands Scrolling Commands: COLUMN and POSITION Function Key Commands: Fn I/O Direction Commands: CD, <, <<, >, and >> Window Creation Commands: POLLSTER, ONLOOKER, and VMF Termination Commands: EXIT, STOP, and MEMORY NB Command: Inserting Notes FIND Command: Word/String Search CF Command: File Comparison WRITING A POO APPLICATION Introduction The Initialization/Termination Function Initialization Termination The Open Function: (n/t string, size, extra size) The ECHO Modifier The LINE Modifier The CLOSE Modifier The STET Modifier The Close Function: (identifier) The Put Function: (identifier, text, length) Control Character Processing The Get Function: (identifier, buffer, length) A Noncommittal C I/O Model A Fortran I/O Model REFERENCES EXAMPLE APPLICATIONS C/C++ SOURCE Introduction Pollster Onlooker Pseudorandom Number Generators Windowing Systems and Threads Floating-Point Mapping Polish Notation Calculator Convergence Map for Newton's Iteration Nonsymmetric Eigenproblems X WINDOW SYSTEM C/C++ SOURCE MICROSOFT WINDOWS C/C++ SOURCE OS/2 PRESENTATION MANAGER C/C++ SOURCE Introduction Headers and Associated Additions System-Dependent Defined Constants Memory Management Macros System-Dependent Thread Management Macros Data Structures and Associated Defined Constants The API Function Event Struct The VM File Struct The Window Struct Function Prototypes and Macros Miscellaneous Macro Functions Windowing System Macros Internal Variables and Tables Key and Button Tables Variables and Tables Associated with Command Processing POO Main Program, API Functions, and Threads The POO Main Program cappl, copen, cclose, cgets, and cputs nappl, nopen, nclose, ngets, and nputs WaitEvent and WindThread API Co-conspirators Open, Put, Get and Ctrl Windowing System Interface CreateWND, SetAttribute, GetFontSize, Activate, EventProc, KeyFcn, ButtonFcn, LineBefore, LineAfter, DrawWindow, HexDump, DrawInput, and InputMgr Command Processing Command, Annotate, ParseCommand, and ParseGeometry Virtual Memory File Facility CreateVMF, DestroyVMF, LoadVMF, StoreVMF, ExpandVMF, AppendVMF, and AssociateVMF Memory File Facility MemoryLoad, MemoryStore, MemoryCmd, MemoryKey, and MemoryVMF INTRODUCTION This monograph is intended for people who program in conjunction with their professional or leisure activities and would like to write portable windowed applications in C/C++ or Fortran for the X Window System commonly available in Unix, Microsoft's Windows, or IBM's OS/2 Presentation Manager. I assume that you would find delving into windowing technology for a period of several weeks or months to expand your computing skills an interesting but unwelcome diversion from your principal interests. I further assume that portability is an important consideration for your colleagues, if not for yourself. If you have experience programming in nonwindowed environments, such as xterms and DOS windows, and using windowed environments but have no background in constructing windowed applications, the software contained in this monograph may enable you to write windowed applications that are portable among the supported windowing environments. The POO software has been designed to build on your familiarity with the I/O functionality of C and Fortran so that you can write windowed applications with little more effort than you now invest in writing your nonwindowed applications. The software contained in this monograph is an outgrowth of an effort to provide the full capabilities of today's windowed environments to people who program, which includes myself, but is limited to supporting text windows. Windowing systems involve some unusual elements, at least in comparison to those common in numerical applications, and the knowledge necessary to construct even simple windowed applications is neither easily acquired nor retained. People who program, people whose principal interests lie elsewhere, are unlikely to have either the time to acquire the requisite knowledge of one or more windowing systems or to require it often enough to retain it. The POO software is designed to allow you to construct windowed applications but does not require that you know much about windowing systems let alone be able to write windowing software. This monograph contains versions of the POO software for the X Window System commonly available in Unix systems, Microsoft's Windows, and OS/2 Presentation Manager. The three versions of the POO software are identical in design and organization and contain a great deal of common code; for the most part, differences among the three versions emerge only when dealing with the windowing system itself, e.g., when creating a window or changing its attributes. The POO Application Programming Interface (API) does not depend on the windowing system so that a properly written POO application can be ported among these systems without change; the examples in this monograph are portable among these systems. The POO User Interface (UI) is almost independent of the windowing system so that users of POO applications are quite portable. Indeed, many aspects of the user interface were designed to encourage user portability: the keyboard functions are an admixture of those usually provided by windowed applications for these systems, and many functions are assigned to both the usual Windows key and the usual OS/2 key. Many other keyboard functions fall in the legacy category and should be familiar. The ability of an application to provide textual output to its user and to obtain textual input from its user are fundamental, and you may view the I/O capabilities provided by your C/C++ or Fortran compiler as sufficient. If your applications require only one I/O stream, many systems may suffice for your needs because many create a window for the standard I/O stream. Although running an application in an xterm or DOS window, a command window, or some window created by the compiler for handling standard I/O may suffice, it likely falls far short of providing convenient access to the actual capabilities of your computer system. You may or may not be able to scroll the output provided in the window, enter typeahead input, re-enter input, use the function keys, direct output to a disk file, or control the execution of the application. Further, even if provided, these capabilities may not have the flexibility that you desire. My objective in writing this software was to free myself from command windows, to enter the world of windowed applications with the proviso that my applications be portable, and to gain access to the actual capabilities of the computer's user interface. Indeed, the real issue is not whether the I/O streams are windowed but rather the environment that is provided to the user while running an application. The components of an appropriate solution are clear: a portable interface for the application itself, a simple and familiar programming interface, a familiar user interface, a command interface with the appropriate capabilities, and an interface to the underlying file system. The first objective is achieved by embedding the actual main program in the POO software so that a portable interface that is consistent with the C and Fortran standards could be provided for POO applications. The second led to an API that is consistent and, in most cases, compatible with the C and Fortran standards, namely the ANSI C stream I/O functions fopen, fclose, fputs, and fgets and the Fortran OPEN, CLOSE, WRITE, and READ statements when used to transmit textual data. The user interface supports an admixture of the usual keyboard and mouse controls: some may be very familiar because all of the supported windowing systems have many in common, but some may be unusual when viewed in the context of just one of the supported windowing systems. The command interface provides the user many of the capabilities of command windows as well as the ability to manage window attributes and the function keys. The virtual memory file facility embedded in the POO software is adept at dealing with both Unix and PC text files, binary files, and directories and can be exploited by the user to obtain windowed access to the underlying file system. Finally, for the user's convenience, the memory file facility automatically retains window attributes, function key string assignments, and other data between invocations of a POO application. The POO software is designed to provide people who program with the ability to write portable windowed applications using a simple and sparse application programming interface sufficient for handling the output and input text streams commonly required in numerical applications as well as a rich and powerful user interface that supports an admixture of the keyboard and mouse functionality of the supported windowing environments and an appropriate command interface predicated on the notation commonly used in command windows. Depending on your experience, you should find both the programming and user interfaces familiar. The POO Application Programming Interface (API) is small and easy to use. It is data oriented and contains no suggestion that the corresponding I/O streams opened by your application are windowed. It employs only standard C data types that can also be represented in Fortran and contains only five functions, none of which take more than three arguments. The API contains one function that should be used to identify your application but can also be used to terminate it. There are four functions for handling textual data that are analogous to the facilities provided by the ANSI C stream I/O functions fopen, fputs, fgets, and fclose or the Fortran OPEN, WRITE, READ, and CLOSE statements. The API functions provide only for the transmission of data; it is assumed that you will use the standard I/O formatting capabilities of the language to produce output text and to convert input text. You probably write your applications using the integrated I/O capabilities of C or Fortran that combine the formatting and transmission in a single function or statement. I would have preferred to support these integrated capabilities, but doing so would have sundered portability. The POO User Interface (UI) is driven by the keyboard and mouse just as for any windowed application. Keyboard input will likely elicit the responses you expect, but many keyboard functions are duplicated to foster portability of the user among the supported environments. Mouse input may or may not elicit the responses that you expect because there is minimal standardization of mouse input; indeed, even different X window managers treat mouse input differently. Scrolling, typeahead, line re-entry, and many input editing features are supported. The UI includes a command interface so that you can change window attributes, manage the strings assigned to the function keys, manage the disposition of a window's output stream to a file, establish a file or portion thereof as a window's input stream, open pollsters to update logs and open onlookers to view files and directories, create virtual memory files, create or change files in the file system, and suspend or terminate the application. In general, the actions taken by the UI in response to your requests are transparent to the application. The application, however, can detect that you closed one its windows or suspended the automatic updating of a window, which usually occurs when text is added to an output stream. The POO software is quite permissive and not at all paternalistic. The API is more or less bulletproof but is, of course, an easy target for a bad pointer or an unterminated string. Keyboard and mouse input that is not defined is ignored, in most cases quietly. Unless some action requested by the user would lead to problems in most situations, it is not prohibited. Confirmation for performing a command is never requested. When processing commands, alphabetic case is usually ignored and any initial substring abbreviation suffices, but unique initial substrings are required for the command itself. Since the POO software is a tool rather than a product and since the application is the central issue, I did not try to provide detailed or even necessarily clear error messages; there are no terminal errors. In general, the POO software attempts to cope with the situations that arise on the theory that you will notice the problem and correct your application or that you will cease and desist your mischievous behavior. In short, continuation of your application is given highest priority because your application's results are more important than whether the POO software is being used properly. Malicious behavior by either an application or its user may have consequences; this software is specifically not designed to protect either itself or its data. The underlying systems require that an application either take full responsibility for interrupts or have them processed by the system. In short, they do not provide an application environment in which it is convenient to handle the obvious interrupts with relatively simple code and leave the more significant problems to the system. If systems designers, both hardware and software, spent more time using their creations, I suspect dramatic changes would occur in interrupt processing mechanisms, which have remained substantially unchanged for decades. Effective interrupt processing in current systems is not consistent with portability, and conversely. You may find that windowed applications are somewhat more delicate than nonwindowed ones: errors in a windowed application may have bizarre consequences. Based on my experience, the underlying systems are considerably more robust than the windowing systems that they support. Thus, if you run an erroneous nonwindowed application in the system, it may fail without causing collateral damage. But, running the windowed version may engender considerable damage with spectacular consequences. The X Window System has not been integrated into the operating system, and the underlying operating system, usually Unix, is quite robust. I have never killed either Unix or OS/2 accidently but have killed the X server on occasion, a "broken pipe" is almost always fatal. The behavior of Windows depends on both the incarnation, 3.1 being rather delicate, and the compiler. Some Windows environments are delicate, while others are of sturdier stock. Nevertheless, if you try modifying the POO software, I suggest that you test it thoroughly when nothing else important is running. At least one of the supported systems, namely OS/2, provides automatic restart, which should be turned off. Automatically restarting a failing application is usually circular and may be deadly from the system's perspective. Automatic restart is appropriate only when running production applications in industrial strength systems. Although you may view the central focus of the POO software as supporting multiple, windowed I/O streams, the central issue is the user environment it provides when running an application. The POO software acts as an intermediary among the various active and passive participants: the application, the user, the windowing system, the file system, and the operating system. An application's responsibilities and prerogatives are quite limited and necessarily independent of the windowing system; an application should be concerned only with its I/O streams, which is reflected in the API. The user is expected to take an active role in the management of the execution of the application by managing the windows and their associated I/O streams. The remaining participants are passive or dictatorial. Portability of both the application and user played a prominent role in establishing the rights and responsibilities of POO applications and their users. For those who write their applications in C, the principal POO I/O functions are direct analogues of the ANSI C stream I/O functions fopen, fputs, fgets, and fclose. The four POO functions are named copen, cputs, cgets, and cclose and deal with windowed I/O streams in almost precisely the same manner as the C stream I/O functions. Indeed, using C macros, it is possible to write C code that can be compiled to use either the C stream I/O or POO functions for data transmission so that the application can be run nonwindowed or windowed. The Fortran POO functions are named nopen, nputs, ngets, and nclose and deal with windowed I/O streams in much the same manner as the OPEN, WRITE, READ, and CLOSE statements. The fifth POO API function provides initialization that is optional but strongly recommended and also provides for system-independent termination of your application; the C (cappl) and Fortran (nappl) versions are distinct only so that you can deal with mixed-language issues and leave the C interface intact. Your application should call cappl (nappl) for termination only when something has gone terribly wrong; in general, POO applications are terminated by their user. In summary, your application can open and close windowed I/O streams, can append text to an open I/O stream, and can obtain text from an open I/O stream. All windowed I/O streams and access to the underlying file system are based on the virtual memory file facility embedded in the POO software. This facility is discussed in more detail later in this introduction because some aspects of it are visible both to the writer and to the user of a POO application. The VM file facility is adept at managing data in VM files and handles all interactions with the underlying file system. All windows created by the POO software are standard, top-level windows as provided by the window manager. The window manager is not part of the X Window System but is an integral part of Windows and OS/2 Presentation Manager. There are numerous X window managers in use, and the POO software attempts to ignore the window manager to avoid dependencies on them. Consequently, you may see subtle differences when running POO applications under different X window managers. As standard windows, each POO window has some decorations provided by the window manager: a title bar, a sizing border, and perhaps some menus or buttons. The system may provide a standard system menu, but menus and buttons are not supported otherwise. As top- level windows, your desktop is the parent of each POO window, and each is an independent window even though some of the supported windowing systems seem to put great stock in top-level windows. The user is responsible for managing the attributes of the windows created by the POO software; colors, font, size, and location can be managed using the POO attribute commands or interacting with the windowing system. Those accustomed to working in Windows and OS/2 may find this unusual, but X users expect to be able to control window attributes statically if not dynamically. The POO software classifies the windows it creates as pollsters, onlookers, or others. Pollsters and onlookers can be created by either the application or the user, while others are created only to display an I/O stream opened by a POO application. Pollsters are rather special and are used to maintain a log with entries that automatically include a date/time stamp; pollsters can be opened only in conjunction with 'poll' files, which is for the user's protection because, once opened, the poll file is always updated with a new entry. Onlookers display files, usually a file or directory loaded from the file system, but the user can create an onlooker for a file that does not exist in the file system. The VM file associated with a window can be displayed as either text or in hexadecimal dump format. In onlookers, files that look like text files are displayed as text and otherwise are displayed in hexadecimal dump format; directories are displayed as a list of file names. The user can toggle the display mode in any window at any time. The POO software is window oriented in that the I/O streams created by a POO application are maintained in the VM file associated with an other (window) only while the window exists. If the user closes the window, the I/O stream for which it was created is destroyed. The application can detect that the window was closed because the API functions generate error returns when requested to append data to or to obtain data from a nonexistent window. The application can ignore the error returns, of course, but is terminated by the POO software when the last window associated with it is closed. In addition to closing all of its windows, the user has multiple mechanisms for terminating a POO application, e.g., a STOP command, but closing its windows is frequently convenient. In summary, when a POO application is invoked, it will generally create one or more others (windows) and append text to the output streams that are associated with them. As new text is appended, the window is updated, usually with the last line appended at the bottom of the window. The output lines are never wrapped in the window, and no caret is provided because the user is not allowed to edit the application's output. The usual scrolling keys, e.g., the arrow keys, can be used to display any portion of the output stream in the window and always have immediate effect because there is no delay until the caret hits an edge of the window. Most applications will eventually solicit input. All input entered through a window is done using the input rectangle. To provide high visibility, the input rectangle is always drawn in reverse video and, by default, is two lines high. As necessary, the window is restored or raised to the top when input is solicited; the focus shifts automatically in Windows and OS/2 but not necessarily in X. If input is entered before being solicited, it is typeahead and is saved until received by the application or deleted by the user. Either before being solicited or when solicited, you can direct input or paste into the input stream using the VM file copy/paste capability. Input echoing to the output stream is an optional feature that can be elected by the application when it opens an I/O stream. Nevertheless, input to the application is echoed in the output stream only when and as the application receives it. Thus, typeahead input simply disappears until read and may never appear if the application elects to interpret the input before providing its own acknowledgment. Whether solicited or not, you can enter input through a window at any time by simply typing. If the application is soliciting input, pressing Enter terminates your input line and returns it to the application. If the application is not soliciting input, pressing Enter appends your input line to the current typeahead input. If you are in the process of entering some input and the application tries to solicit input, the application is given the right of way and your input (incomplete) is discarded. To avoid such conflicts, you can suspend the application, which is strongly suggested while you enter a note, an N.B., in an active output stream. Regardless of the circumstances, your input can be either normal input intended for the application or a POO command. Traditionally, when normal input and commands can be intermixed in an input stream, commands are distinguished by a command character that must be typed as the first character of the input line. This software, however, distinguishes normal input and commands by context and by how you terminate your input line. Unsolicited input is interpreted as a command in pollsters and onlookers and as typeahead input in others. Input terminated by Ctrl+Enter is always interpreted as a command. Thus, all input that is terminated by Enter constitutes a pure text stream that is returned to the application when and as it is requested. The window through which a command is entered is always an implicit operand, the window for which the attribute is changed. The command word itself is case insensitive and can be abbreviated to any initial substring provided that the abbreviation is unique; in the operands, the treatment of case and abbreviations depends on the windowing system. In X, colors and fonts are case sensitive and cannot be abbreviated, and path names are case sensitive. In Windows and OS/2, colors and fonts are table driven so that they are case insensitive and any initial substring is sufficient, and path names depend on the underlying file system. A command with no operand displays the current value, while a command with an operand generally sets the pertinent value. This tactic avoids the more common implementation based on set and display commands and provides each characteristic that can be managed its own command. Thus, "<{filename}" directs input, while "<" displays the current status of input direction. The role of the POO software as an intermediary among the various participants involved when running an application played a role in its design and organization, and this perspective has some merit when trying to understand the software because it creates a user- friendly environment for running windowed applications and, therefore, necessarily interacts with the system, file system, windowing system, application, and user. In this context, the system is somewhat ambiguous because it is used here to connote whatever is underneath everything else, which includes the compiler and even some aspects of the windowing system. This monograph necessarily includes information for both the user and writer of a POO application so that, if organized in terms of its role as an intermediary, most sections would contain information needed by or of interest to both. Thus, even though the code is organized along these lines, the text is organized into just two major sections, one for users and one for writers of POO applications. Writers, of course, will have to read both sections because it makes little sense to write a POO application unless you know something about the environment created by the POO software. Writers of POO applications can avoid duplicating capabilities provided by the POO software and can take advantage of many of these capabilities to simplify their design. For example, it is quite straightforward for a user to provide one or more portions of one or more files as a single continuous input stream. In many applications, this capability may serve no purpose; in others, it may allow considerable simplification in the application itself; and, in still others, it may simply impact user behavior with respect to data file organization. Nevertheless, the virtual memory file and memory file facilities embedded in the POO software necessarily involve both the user and writer of POO applications so that it is constructive to cover these two facilities in this introduction. All interactions with the underlying file system by the POO software use the VM file facility. The memory file facility is used to retain window attributes and function key assignments in the application's memory file in the file system between invocations but depends on the application for critical information, its name. These facilities directly impact both the application and user. THE WINDOWING SYSTEMS The windowing systems in Windows and OS/2 are thoroughly integrated into the underlying operating systems, but the X Window System is simply a product that runs on top of an operating system, typically Unix. The X Window System is based on a client-server model. In this model, the display, keyboard, and mouse are part of the server and driven by the X server software; the X application runs on the client system and calls functions in the X library; and the X library functions communicate with the X server using the X protocol and the 'network' connecting the client and server. The client-server model is used in all situations, even when both the server and client systems are running on the workstation on your desk. If you have run the X Window System, you are likely familiar with 'xinit'. In effect, it starts the X server in your workstation and then one or more X applications, including a few xterms. When you run a POO application in X, this software uses the display specified in the environmental variable DISPLAY, which should have the form 'host:server.screen' where host is a network address and both server and screen are numbers. In a workstation, DISPLAY is often set to unix:0.0 when the X server is started by the xinit script. If you are running on a remote client, you should set the DISPLAY variable in your client session after you login so that X applications create their windows on your display. The value should be the network address of your workstation with a suffix of :0.0. In addition, you will have to issue an xhost command on your workstation so that the remote system can open windows on your display; normally, your X server should not be willing to interact with X clients. Technically, an X application can use multiple servers and multiple displays on a server; the POO software supports only one display. In the context of the system on your desktop, the differences among the supported windowing systems usually have little impact. If you run an application, you may notice some response time issues in X that are not apparent in Windows and OS/2. If the X server and X client are separated by an ocean or continent, you should expect to see some timing differences, but these sorts of circumstances use X capabilities that are not available in Windows and OS/2. You may also see some timing differences in a local workstation environment because the client and server in X are distinct processes so that at least one task switch is required before the client's request to the X library can be acted on by the server, e.g., a draw. Timing issues arising in the integrated environments of Windows and OS/2 should stem only from workstation performance and the overhead of these windowing systems. The POO software is intended to allow you to write applications that are portable among the supported windowing systems and that may be processor intensive. In the context of Windows and OS/2, running such applications does not add potential points of failure to your application: either your workstation functions correctly for the duration or not. But, if you run an X application on a remote system, you are adding a new point of failure, namely the network connecting the client and server systems. For this reason, processor intensive applications that run for an extended period of time should be run nonwindowed to avoid this new point of failure. If you use the POO software and the network fails, your application will most likely die of a "broken pipe." The X Window System does not contain a window manager; there are numerous X window managers in widespread use, e.g., Tom's window manager, twm, and the Motif window manager, mwm. In contrast, the window manager is an integral part of the windowing system in both Windows and OS/2. In most circumstances, the POO software ignores features or practices that might lead to dependencies on the window manager. Thus, for example, although all top-level windows in OS/2 or Windows are the same, the appearance of top-level windows in X varies with the window manager. The Motif window manager is fairly consistent with Windows and OS/2. You do not need to change your window manager to run POO applications; but, if you start running windowed applications routinely, you may discover your own reasons to change it. In addition to the appearance of top-level windows, a different X window manager may support a different user interface, e.g., the process for moving or resizing a window. Despite the preceding comments, the supported windowing systems are fundamentally the same. After a windowed application creates its first window, the windowing system starts queuing events that it expects the application to process; Windows and OS/2 use the term message rather than event. Although the event processing provided by the POO software is crucial, this software has only superficial dependencies on the windowing system. The amount of code in the POO software that depends on the windowing system is quite small. The important point is that the events be processed in a timely manner even if your application is busy performing computations for an extended period of time. The POO software deals with this issue by using multithreading. If you have used windowing systems, you have almost certainly seen the pointer that indicates the mouse position change from an arrow or cross to an hourglass or clock. At the same time, you may have noticed that some or all of the windows on your display become nonresponsive. In general, this type of situation arises when an application is busy doing something and is not looking for events. Because the POO software is intended to support processor intensive applications, it clearly had to contain a mechanism for continuing to process events regardless of the activity of your application. Multithreading is used to resolve this issue. When an application requests that the first window be opened, the POO software creates another thread automatically: the original thread continues to run your application, while the newly created thread continuously looks for and processes events. The threadedness of the POO software is more thoroughly discussed in the comments for the Random example and in the POO source files. Both Windows and OS/2 contain the thread support required by the POO software, but you can choose to run your application single threaded. The system in which you run X may or may not support the POSIX threads standard on which the POO software is based: it is a relatively new standard, and some Unix vendors have not implemented it as yet. If not, you must either run single threaded or construct the code for running multithreaded in your system, presuming it supports multiple threads. When single threaded, your application must ensure that the POO API functions are called frequently enough to maintain the timely processing of events. The POO API functions are conscientious about looking for events to process. The Random example provides both additional information and a context for you to gain experience with single threaded and multithreaded applications. THE VIRTUAL MEMORY FILE FACILITY A mechanism for managing the data displayed in the windows created by the application and user as well as an appropriate interface to the underlying file system are essential to support the spectrum of services provided by the POO software, e.g., scrolling through an output stream or browsing the file system. The virtual memory file facility embedded in the POO software contains the requisite data management capabilities and handles all interactions with the underlying file system. The design of this facility entails a number of issues, some pertinent to the writer of a POO application and some to its users. The output streams produced by an application are retained in virtual memory files, VM files, in a form entirely consistent with the underlying file system. VM files loaded from the file system are retained as provided by the file system. Directories are held as text files, one file name per line. The VM file facility allocates a single block of virtual memory for each VM file and uses it to hold both the data and a small block of information used to manage the VM file. In effect, this assumes that virtual memory is flat; otherwise, VM files are restricted in size because a contiguous block of memory must fit in a portion of virtual memory that is flat. Other designs for the VM file facility could avoid this issue, but I am not willing to regress to such memory models. Thus, for example, VM files are severely restricted in Windows 3.1, which uses a segmented memory model that is flat only in 64KB chunks. This design may stress the paging capacity of the system, which clearly must have sufficient paging capacity to deal with not only the application's needs but also all files held in virtual memory, which includes the VM files used to retain its output streams and any files used for input direction. This places a system-dependent limit on the aggregate size of VM files, but one that should not be significant. The POO software is designed to handle the output streams from an application that the user is expected to read or at least browse since there is little reason to display an output stream in a window otherwise. A user cannot be expected to browse hundreds of pages of output while an application is running so that output streams measured in megabytes should not occur. Likewise, this software is not designed for applications that expect the user to direct input from a file containing the census data or the human genome data base. For all practical purposes, therefore, the VM file facility should only require a few megabytes, and this is well within the capabilities of the supported systems. In a windowed environment, the concept of a current working directory is anachronistic. Each application icon on the screen has an implicit current directory, namely, the path of the associated executable, and may have one that can be assigned to the icon explicitly. Although a single current directory may suffice in the context of some applications, it would be naive to assume that all windowed applications could be predicated on a current directory that could be set once and remain unchanged for the life of the application. For each invocation of a windowed application, the application may require a directory for accessing fixed data files, while the user may need one or more to associate disk files with the application's I/O streams. In addition, the use of a current directory generally precludes the provision of complete and accurate information to the user of an application, which immediately excludes relative path names. If the current directory can be changed while an application is running, any prior relative file references would be left flapping in the breeze. To provide flexibility to both the application and the user and to provide complete and accurate information to the user, the POO software maintains a current directory for the application, a current directory for each window, supports a CD command, and generates and retains complete path names. The VM file facility includes a couple user portability features when generating complete path names. First, it accepts either a slash (/) or backslash (\) as the path name separation character, which some of the supported systems also do in some circumstances. Second, it processes environmental variables that are specified in the form commonly supported by either Windows and OS/2 or Unix, specifically, %...% or ${...}, where the braces may be omitted if the name is followed by a path name separation character. In both Windows and OS/2, environmental variables are converted to upper case because these systems appear to ignore case in such names; in Unix, environmental variable names are case sensitive. The decision to convert case in environmental variables is based on whether the current directory starts with a drive letter, i.e., looks PC-ish. In path names, the treatment of alphabetic case is controlled by a constant, which should be defined in the software to be consistent with the underlying file system. Windows and OS/2 can be installed with a DOS-like file system in which alphabetic case in path names is ignored or with a more modern file system in which path names are case sensitive. In Unix, path names are always case sensitive. The VM file facility treats path names as case sensitive but can be changed to ignore case in file names. If you alter the POO software to ignore case, path names are converted to upper case so that you will have recognition problems if the file system treats path names as case sensitive. The identification of VM files is based on both the complete path names generated by the VM file facility and their last modification times. The last modification time of a VM file is either the value provided by the file system or the application starting time for VM files that are not loaded from the file system. Consequently, if a file in the file system is changed between references, you can load both the old and new versions as distinct VM files. Likewise, when you direct an output stream, the file in the file system to which it is directed can be loaded as a VM file and does not conflict with the VM file that is used to retain the output stream even though they both have the same path name. In Unix, the lines in a text file are terminated by a new line character (\n), while Windows and OS/2 terminate lines with both a carriage return and new line character, the two character sequence \r\n. When dealing with text files, the POO software adapts to whatever line termination is encountered so that it automatically handles both Unix text files and Windows and OS/2 text files. A VM file used to retain an output stream is maintained in the form used by the underlying file system. When appending data to a file loaded from the file system, line termination is based on how the first line of the file is terminated: lines appended to the file use this apparent line termination. In general, this means that the POO software can deal with files that use either line termination so that there is no need to convert files when moving among the supported systems even if data is appended to a foreign file. Most Unix systems contain a conversion facility for text files, while Windows and OS/2 ignore this issue. The line termination issue engenders an incompatibility between POO input streams and ANSI input streams because ANSI input streams do not adjust for the apparent line termination, at least in general. When an input stream is directed to a file in the file system, the POO software adjusts for whatever line termination is encountered in the data being read and returns only the new line character to the application. Consequently, the line termination in a file need not be consistent but will be transparent to the application in any case. Generally, line termination is not transparent in an ANSI input stream, which is why foreign text files must be converted. When viewing a VM file in a window, the user can determine the line termination of the VM file by switching to hex dump format. In hex dump format, the VM file is displayed in hexadecimal based on 16 character lines. If a VM file loaded from the file system appears to contain binary data rather than text, it is displayed in hex dump format automatically; but, regardless of how the VM file facility types a file, any file can be displayed as text or in hex dump format. Ctrl+O toggles the display format. How a VM file is displayed in a window does not affect its representation; pressing Ctrl+O only affects how the POO software draws the data. When appending data to a VM file, the VM file facility supports two management strategies: expansion or truncation. For VM files used to retain output streams generated by the application, the strategy is specified by the application when the output stream is opened. If an application requests that the VM file for an output stream be expanded but space is not available, the management strategy is changed to truncation and execution continues without interruption. For all other VM files, the file is expanded as necessary. Thus, you can copy/paste VM files as desired. If additional space is required but not available, the requested operation is ignored. If the system runs out of virtual memory, it is likely in deep trouble anyway and termination is probable. The expansionist strategy requires little explanation: the VM file grows until it fills virtual memory. The truncation strategy can be used only for a VM file used to retain an output stream generated by the application and is based on an initial size and a truncation size provided by the application when the output stream is opened. After creating the initial VM file, its size is fixed; and, when data is appended to the output stream, the oldest data in the VM file is discarded as necessary based on the truncation size. Thus, the oldest data is discarded in chunks, which cannot be too large because the truncation size cannot exceed half the initial size. For a text file, lines are discarded because the truncation point is moved to the beginning of the next line after the chunk to be discarded. Clearly, the two size values provided by the application should be selected so that the data retained in the VM file is meaningful in the context of its intended use of the output stream. The VM file facility is not paternalistic. Thus, although generally ill-advised, you can append a binary file to a text file, and conversely. When you use the VM file copy/paste capability, the VM file facility software looks only at the type of the file to which the data is appended and, for text files, the line termination. The POO software is only slightly more circumspect if you direct an input stream to a file. Most applications will expect textual data so that a binary input stream might engender some difficulty for the application. Finally, the implementation of I/O direction in the context of VM files requires some explanation. When input is directed to a file in the file system, the file is loaded into a VM file and all input requests are satisfied from the VM file. Thus, subsequent changes in the file do not affect the input stream; and, as implied by earlier comments, the modified file can be loaded into a VM file and used for input direction to another window or displayed in a window. When an output stream is directed to a file in the file system, the data in the VM file is transferred only as necessary. Thus, a VM file managed by expansion is usually not written until the application terminates, while a VM file managed by truncation is written whenever data must be discarded. The user can manually save an output stream at any time by creating a VM file (VMF command), doing a copy/paste from the output stream to the VM file, and then storing the VM file (STORE command); this works because output direction in an onlooker stores the VM file associated with it immediately. Both input and output direction can be cancelled at any time, but cancelling input direction can be a bit troublesome because there is no way for you to tell exactly what the application is doing and cancellation may leave the application in an awkward position. Input direction can be changed by cancelling the current request and then directing it again. Output direction can be changed without cancelling the current direction first. The new file to which output is directed starts with the first line of the VM file when the change is made. The original design of the VM file facility was predicated on the POSIX I/O standard. In the Unix environment, this I/O library is quite old and both reliable and robust. In the PC environment, the POSIX I/O library is available but was discovered to be neither reliable nor robust. In theory, this design decision allowed me to write one and only one implementation of the VM file facility that could be used in all of the supported environments, which is the whole idea of a standard. And, indeed, the implementation was successfully ported to at least one rendition of each supported windowing system. Unfortunately, the POSIX implementation of the VM file facility contains, in the immediate vicinity of almost every reference to a POSIX function, a patch of inelegant code that was designed to get around an apparent problem in one or more functions in some vendor's rendition of the POSIX I/O library. When this monograph was almost finished, my frustration level was finally exceeded while testing the POO software in some new environments. Consequently, the Windows and OS/2 versions can be compiled to use either the POSIX version or the native version that is based on the I/O library supported by the underlying system. The POSIX version, of course, contains all the little inelegant pieces of code that were developed for C/C++ compilers that I used but may not contain the patch necessary for your compiler. I recommend you avoid this whole issue, which has no redeeming value, and use the native I/O library supported by the system. Unless you explicitly change the Windows and OS/2 versions, they will run native. THE MEMORY FILE FACILITY Over the years, I have come to appreciate software that can at least remember if not learn from experience. For example, I like an editor that remembers where I was working in a file and, when I edit the file again, automatically positions it, which it can do much more quickly and efficiently than I. Similarly, application development systems that retain pertinent information from one invocation to the next are more user friendly and improve one's productivity. Unfortunately, many applications fail to provide this kind of user service and perpetually start with a blank slate. Most high-level languages support only two types of storage, code and constants, both of which are immutable in theory and hopefully in practice. In addition, of course, languages provide for the allocation of data storage, but the data is not retained between invocations. The windowing systems do not support any additional data storage capability. The resource management facility embedded in the X Window System, which many X applications use to provide an often daunting set of options to the user, supports a sophisticated mechanism for obtaining values but depends on the user to change the resource file(s) manually. In Windows and OS/2, there is no analogue of the X resource management facility; indeed, a Windows resource is something entirely different. Nevertheless, many applications use a file for retaining pertinent data between invocations, often a .INI file, but these files are frequently not text files and not very user friendly. The memory file facility embedded in the POO software provides the required bidirectional capability: dynamic data that is retained between invocations. The memory file is implemented as a plain text file so that the user can modify it with a text editor and uses a very abbreviated version of the syntax employed in the X resource manager. But, unlike X resource management, the memory file is managed by the POO software: it is created the first time the POO application is run, changed by the software to retain the user's resource requests, and updated when the application is terminated. The POO software uses the memory file to retain window attributes and function key string assignments between invocations and, while executing an application, a record of all files accessed in the file system. Since the memory file often contains system dependent data, such as color names, memory files are generally not portable among the supported systems. The POO software is not designed to handle multiple invocations of an application or multiple applications using the same memory file. In these situations, you can set memory file updating off, which can be done by inserting an appropriate command in the memory file, or you can use the STOP command to terminate those for which the updated memory file should be discarded. If the application calls the appropriate API function with the application name, which loads the memory file, memory file updating is set on; if not, the POO software uses the application name "Other" and sets memory file updating off before creating the first window. The MEMORY command provides the user control over memory updating and the ability to open a window to view the memory file (no operands). If a MEMORY command with no operands is issued in an onlooker that is showing the memory file, the update status of the memory file is displayed. While an application is running, the POO software maintains a copy of the memory file in virtual memory. The primary purpose of the API function named cappl, nappl in Fortran, is to load or create the virtual memory copy of the memory file. If the virtual memory copy of the memory file does not exist when an application tries to open its first window, cappl is called with an application name of "Other" before the window is opened and memory file updating is set off. Normally, memory file updating is set on when the memory file is loaded. When the POO software terminates a POO application and memory file updating is set on, the copy of the memory file in the file system is updated to be consistent with the virtual memory copy. The memory file facility necessarily depends on a mechanism for generating the name of the memory file, on a strategy for locating the memory file in the file system, and on a naming convention for the data retained in the memory file. The name of the memory file is generated by appending ".mem" to the application name, which is limited to eight alphanumeric characters so that the memory file name is portable among the supported windowing systems. For this reason, I recommend that you select unique application names even though, with careful management, one can conveniently use the same name for all of one's applications. Three attempts are made to locate the memory file: 1) on the path name in the environmental variable POOMEMDIR, 2) on the path name in the environmental variable HOME, and 3) in the system's current directory. The path names assigned to these environmental variables may be either absolute or relative to the system's current directory when the application was started. If a suitable memory file cannot be loaded, an empty memory file is created on the path of highest precedence that does not cause a conflict with an existing file or directory, and the application starts with a blank slate. Conflict occurs if the memory file corresponds to a directory or a disk file that appears to contain binary data. Environmental variables are commonly used in Unix but not particularly well supported in either Windows or OS/2 despite the fact that these systems use them. Thus, the environmental variables used to locate the memory file may not seem either natural or terribly convenient in Windows and OS/2. --------------------------------------------------------------- -Pollster POO Memory File The first line simply identifies the file and is maintained by the software. It's followed by all comments with their relative order maintained, then all function key string assignments, and finally all other commands. For Onlooker, the file information is placed last. F01 "Crash and burn!" F12 "exit^M" Pollster*foreground dblue Pollster*geometry 72x15c memory off --------------------------------------------------------------- Figure 1: An illustrative memory file for Pollster. The memory file can be viewed in an onlooker by entering a MEMORY command with no operand. In the memory file, all lines beginning with a blank are viewed as comments. All such lines are retained in the VM copy of the memory file so that, for example, you can place help information in the memory file. When updating the memory file, comments are collected at the beginning of the file, but their relative order in the file is maintained. Lines beginning with a minus (-) are ignored and deleted when the memory file is updated; this feature is used to delete redundant commands and commands containing invalid operands. Lines beginning with a less than (<) or at-sign (@) contain file information, the former if the file is still loaded and the latter if it has been deleted (as a VM file). Normally, file information is not retained when the memory file is updated, but it is retained by the Onlooker application so that Onlooker can restore its state when invoked the next time. All remaining lines should be of the form [ { window name | window type } {.|*}] .... Elsewhere, "Pollster" and "Onlooker" are described as the window names of pollsters and onlookers, respectively. In fact, however, they are the window types and have the precedence of the window type. For others, the application provides the window name, and "Other" is the window type; if the application does not provide a name, "Other" is used for the name but has the precedence of the window type. As you might expect, commands qualified with the window name have the highest precedence, with the window type coming second, and unqualified commands coming last. In general, the last line of highest precedence is used, the only exception being CD commands, which are always processed cumulatively. For the user's convenience, unique window names are recommended. If an application uses unique window names, then the window attributes saved in the memory file for each window will be applied only to that window because window attributes are qualified by the window name. If an application uses the same name for multiple windows, the user may set a window attribute for one window only to have it used for two or more windows the next time the application is invoked. With proper management, you can use a single memory file for all of your applications and then follow a set of naming conventions for windows to establish your own window classes with consistent attributes. Aside from the optional qualifier, commands in the memory file are processed just as commands entered through a window: alphabetic case is ignored except for window attributes in X and possibly in conjunction with file name operands depending on the underlying file system, commands can be abbreviated to any unique initial substring, and extra operands are ignored. In general, commands with erroneous operands are marked for deletion. When the memory file is loaded into a VM file, it is scanned for function key commands, unqualified CD commands, and MEMORY commands. Unqualified CD commands are processed cumulatively starting with the system's current directory, and the final result is saved as the application's current directory, which is the default directory for each window that is created. Only MEMORY commands with an operand are processed. Since memory file updating is on by default, your only real option is to insert a "memory off" command in the memory file. Although this may be useful in come circumstances, it can be very frustrating if you forget that you turned it off. If an application accepts attribute changes but consistently forgets them, the memory file is a good place to look. When a window is opened, the memory file is scanned for applicable window attribute commands and, for others, for CD and I/O direction commands that are qualified by the window name. Qualified CDs are processed cumulatively starting with the application's current directory and the result is used to set the window's current directory. Qualified I/O direction commands have limited use because they are permanent until you delete them manually. For example, input direction in the memory file would likely yield a boring application except that an initial << command, which directs input and resumes input through the window when the end of the file is encountered, can be useful to provide initial data. Inserting output direction commands in the memory file may be convenient if you want to ensure that the output stream is saved in the file system. Because unique window names are not required, I/O direction commands in the memory file can be dangerous even though they must be qualified by the window name. For example, output direction may direct multiple output streams to the same file. The memory file is automatically updated for successful BACKGROUND, FOREGROUND, FONT, GEOMETRY, and MODE commands when memory file updating is on and for all function key string assignments. All other commands must be inserted manually. When saved in the memory file, attribute commands are qualified by the window name, which may affect the attributes of windows that are opened subsequently or when the application is invoked the next time. Changing the attributes of a pollster or onlooker affects all pollsters and onlookers that are opened subsequently because their names are the same as their window types. Thus, for example, an onlooker geometry that includes both size and position would usually be ill-advised because each new onlooker would hide the previous ones. Changing the attributes of an other may or may not have side effects depending on whether the application uses unique window names. Function key string assignments are not window specific and cannot be qualified. The memory file is a plain text file so that you can edit it. In general, the memory file facility ignores erroneous data, which it may delete, so that you should feel free to make manual changes in the memory file. The memory file can be displayed in an onlooker by issuing a MEMORY command, and you can append data to it while the application is running using the NB command. This latter tactic, however, may not work in some cases. For example, if you have already opened an onlooker and append new commands qualified by 'Onlooker', you will be disappointed because the memory file is scanned for attribute commands for onlookers only once: when the first onlooker is opened. The situation for pollsters is similar. The POO software does not provide an interface to the memory file that can be used by a POO application. PORTABILITY CONSIDERATIONS Software is portable if it can be moved between systems with little or no change, which generally means that it contains no significant system dependencies and is predicated on system and language standards. Portability of numerical software has been a valued attribute for many years, and there are numerous libraries and collections of portable mathematical software in the public domain or commercially available. But portability of numerical software has been based primarily on language standards, commonly the ANSI Fortran standard, and is superficial in that it has had little or no impact on the design of the software but only its expression. Thus, for example, such software can be readily translated to other languages when required, which is illustrated in the last example application in this monograph. Portable applications have been more elusive, frequently because of their unique I/O requirements, which are absent in the context of pure numerical software. Some software products are available for multiple systems but seem to arise from multiple efforts rather than portable software. In the past, only zealots could write portable applications; fortunately, it has come to be somewhat less demanding. This monograph contains versions of the POO software for the X Window System, for some incarnations of Microsoft's Windows, and for IBM's OS/2 Presentation Manager so that a POO application that is itself portable can be ported among these systems. Using the POO software, of course, does not confer portability on an application; you must design and implement your application to be portable by following the pertinent standards, both language and library. The example applications contained in this monograph are portable. If you write a portable POO application, you can run it in any of the supported windowing systems. Thus, for better or worse, you may be able to run it on your Unix workstation at work as well as on your PC at home; and you can send it to colleagues who have a suitable system and the POO software. Many users would undoubtably feel at least uncomfortable, if not lost, when faced with a new operating system. Nevertheless, if the appropriate applications are available, it is really quite easy to move among the supported windowing systems. After all, the keyboard and mouse are the only input devices, windows are the principal output mechanism, and the idiom is point-and-click even though there are few standards in this context. To the majority of users, I suspect that the operating system would be entirely transparent if they could obtain equivalent products, with strong emphasis on 'equivalent' because the production of equivalent products for different environments appears to be a major stumbling block for many vendors. For some vendors, the creation of user dependencies as barriers to change has been standard business practice. When moving among systems, editors are significant impediments for many people, quite likely because of the significant impact they can have on one's productivity in the near-term or long-term, either real or perceived. Although text editors share a great deal of functionality, often provided using the same mechanisms, it is very easy to become critically dependent on small features of exceptional value. In this context, you might view the software contained in this monograph as providing you the ability to develop your applications in the most convenient windowing environment, the one containing your favorite editor, without restricting their subsequent use. Computing platforms are or should be targets of opportunity: it is advantageous to be able to use whatever platform provides adequate performance and is available or costs less. Although neither a PC nor a Unix workstation are well defined, they have the same basic capabilities: one or more of several different operating systems that support a windowed user environment can be run on them. Indeed, some rendition of each of the supported windowing systems can be run on a PC. Nevertheless, many users are reticent to change computing platforms. Ironically, even though many people value portability and routinely write software that is portable, they view themselves as not portable. If you write POO applications, I think you will come to view computing platforms as targets of opportunity rather than barriers or impediments to be overcome because your applications will be portable among the supported windowing systems. Further, a number of user portability features have been incorporated in the POO software so that, regardless of the system in which they are run, the user interface created by the POO software is very similar. Portability in the broadest sense played a pervasive role in the design and development of this software. The POO software was designed to provide you a familiar interface for writing windowed applications that could be ported among the supported windowing systems. In addition, the design includes a number of user portability features intended to obviate various inconsistencies and incompatibilities of the supported windowing systems so that the user interface of a POO application appears the same regardless of the windowing system in which it is running. Finally, the POO software itself was designed to be portable because the plan for this monograph would tolerate nothing less. The overall structure of the three versions of the POO software is the same: each version consists of almost exactly the same set of functions, each function has the same essential role in each version, and many of these functions are identical in all three versions or, if not identical, quite portable. The POO functions that are substantially different are those that must interface directly to the windowing system: window creation and attributes, the interpretation of window events (messages), and drawing. But these functions are designed to handle system dependencies as rapidly as possible and then use common code to actually perform the processing that is required. Thus, although each of the supported windowing systems presents key and button events generated by the user in its own inimitable style, this software implements the functions that such events trigger in code that is common to all three versions. Consequently, the POO software clearly delineates the similarities and differences among the supported systems, e.g., the common heritage of Windows and OS/2 is evident. The supported windowing systems are similar in a gross sense but neither consistent nor compatible when one is trying to implement windowed applications. The POO software was designed to deal with some inconsistencies and incompatibilities of these systems so that you could ignore such issues when writing your applications. For example, window attributes are a user responsibility. Your application is not provided any mechanism for setting window attributes because such values are system dependent, but users are quite flexible and usually aware of the basic characteristics of the hardware that they are using. In this context, the POO software builds on your familiarity with your system rather compatibility among the supported windowing systems. If you commonly use both Unix workstations and PCs, you are quite likely aware that these systems commonly use different path name separation characters, differ in the syntax used for referring to environmental variables in path names, and represent text files differently. The first is a minor annoyance; the second likewise except that environmental variables are poorly supported on PCs and, therefore, not widely used. The third has given rise to a Unix utility that is usually provided to convert text files between the two formats; PC systems generally do not provide a conversion tool. In the interests of user portability, the POO software accepts either a slash or backslash character as a path name separation character and processes environmental variables in path names whether specified with Unix or PC syntax. Although intended for your convenience, these features do mean that the POO software may interpret your path names somewhat differently than you expect. For example, if you embed $HOME in a path name, it will quite likely be interpreted as a reference to the environmental variable HOME rather than literally. The POO software objects to environmental variables that are referenced and not defined, but HOME is usually defined in Unix. How the POO software handles the file format issue has already been described in the section on the virtual memory file facility: it adapts to the apparent file format so that foreign files are handled automatically. Because the POO software always adapts to the apparent line termination, you can append new text to a foreign text file; you can also create a foreign text file. In most circumstances, a sequence of characters is processed by the POO put and get functions in the same manner as it would be if the analogous ANSI C functions were used. But POO I/O streams and ANSI C I/O streams are not equivalent. Specifically, the implementations of the seven C control characters \t, \a, \f, \v, \b, \r, and \n (the "new-line character" in C) differ: they are processed by the POO software only in some circumstances, and their definitions have been changed to be appropriate in the context of a windowed output stream. The POO put function, which appends text to an output stream, only processes C control characters that occur at the beginning of the text to be appended to the output stream except that \r and \n are processed even if embedded in the text. Also, the POO definitions of \b and \r extend the ANSI definitions, and those of \f and \v are quite different than their ANSI counterparts. The POO get function is designed for a more general environment than its ANSI analogue but is equivalent to it in circumstances that are similar. The user is responsible for managing the input stream associated with each window: the user can intermingle characters streams or portions thereof from at least three distinct sources to create what appears to be a single C input stream to the application. The POO implementation, unlike its ANSI counterpart, adapts to the apparent line termination of an input stream so that the user need not distinguish between Unix and PC text files. Thus, a Unix file can serve as input to a POO application running on a PC, and conversely. The portion of an input stream that you enter through a window by typing on the keyboard and pressing Enter (Return) generates a standard C input stream, one that would be interpreted the same way by both the POO and ANSI C get functions if the circumstances were similar. But input entered through a POO window can be either normal input or a POO command. In such circumstances, a command character is often used to distinguish between input and commands, but this strategy would invalidate the statement above. The POO software resolves this portability issue by interpreting your input based on how you terminate it: Enter or Shift+Enter for normal input, and Ctrl+Enter or Ctrl+Shift+Enter for commands. The preceding touches on several features of the POO software that were influenced by portability. The idea of portable software is well known, while the concept of portable users may be somewhat unusual. The availability of the POO software may encourage you to develop POO applications. If so, you may find the user portability features described above advantageous. OTHER DESIGN CONSIDERATIONS Issues arising from the desire to support both C and Fortran also had some impact on the overall design and, like portability, necessarily entailed both costs and benefits. Thus, for example, the POO API is not predicated on null terminated strings and supports both stream and record-oriented I/O in either language. The POO software contains very few prohibitions and is not very paternalistic. In general, if allowing something would not routinely lead to problems, I elected to allow it. Thus, for example, unique application and window names are recommended but not required, and the user can insert I/O direction commands in the memory file. I would have preferred to catch background and foreground colors with insufficient contrast, but no limitations are imposed on the window colors because I was not able to find or create a completely satisfactory test for insufficient contrast. The POO software does not quit. All error situations are handled in some fashion based on the premise that the most important issue is the application and the results that it is attempting to produce. It is assumed that the application writer, you, will notice that something is wrong and correct it as necessary. If not, the POO software will simply continue to cope. On those rare occasions when the user makes an error, the error comments are quite cryptic and the user's request is ignored. Similarly, if an error of any kind is encountered while attempting to provide a user service, the request is ignored or suspended with the expectation that the user will notice that it was not completed successfully. The POO software will not terminate an application on its own. The only exception is that the application is always terminated if the user successfully closes all windows associated with it, which suggests the user's expectations are low so that termination is probably not a bad idea. Because the POO software perseveres, a POO application is fertile ground for a mischievous or malicious user. The POO software is a tool for people in search of reliable and effective tools. From the application's perspective, it is a very simply tool; from the user's, a perhaps overly permissive and flexible one. It is not a sophisticated, polished product in the context of the current computing industry and does not follow some of the de facto standards for windowing software. Thus, the user interface is based on commands rather than the pictographs and menus in widespread use, partly because it is easier from the programming perspective and partly because it is better suited for use with voice recognition software. In this regard, this software is designed for the future rather than the past. The performance of the POO software itself has been assumed to be immaterial. In the context of numerical applications that require at least minutes if not hours or days of processing time, the performance of that part of the application dealing with output that must be read or browsed by the user or input that the user must provide should be irrelevant. If not, the application does not really fit the model for which this software was designed. In any case, the code in the POO software itself is almost certainly only the tip of the iceberg given the size and complexity of the windowing system. In short, any reasonable assessment of the return on investment in optimization of the POO software itself should show it to be negligible. To make the code more readable, particularly for those familiar with Fortran, I have chosen to avoid some standard C constructs even though, at some time in the past, they may have improved performance. Optimization at the statement level cannot adversely impact the performance of the POO software when one considers the larger setting. In actuality, some decisions on coding style were made to avoid warning messages from paternalistic compilers. Some warning messages do no harm, but I have never appreciated receiving code that elicited compiler commentary and assume you may feel the same. In browsing the source code, you will find that most of the POO functions harbor considerable distrust of their callers as well as the contents of the POO data structures. I adopted this attitude to enhance the modifiability of the code, as protection against ill- considered modifications. Despite the situation, most of the internal functions do something constructive when faced with adversity. The POO software is highly modifiable in some areas and less so in others. For example, key and button processing is almost entirely table driven and can be modified by changing table entries. Adding new functions that can be associated with keys or buttons and new commands is relatively easy because the framework is already in place. Most current functions and commands are independent of the system and implemented in pure C code, which implies that many additions and modifications may be made without any knowledge of the windowing system. Modifying the basic orientation of the POO software would be a bit more difficult because its role in the overall structure of the application is that of an intermediary. If you want to learn more about window events or need to debug changes in the POO software, you may find it helpful to insert something like the following static int tml; static char tmb[256]; #define BTE(lw) if (MemoryFile && lw->dout != MemoryFile) \ { tml = sprintf(tmb,"-%s#%li ",lw->wndname,lw->SSN); \ tml += sprintf(tmb+tml, #define BTM if (MemoryFile) \ { tmb[0] = '-'; tml = 1 + sprintf(tmb+1, #define ETM ); tmb[tml++] = '\n'; \ O_AppendVMF(NULL,MemoryFile,tmb,tml); } at the beginning of the POO source. The static variables are used to hold the messages that are generated using the BTE...ETM or BTM...ETM macro pairs, where the ellipsis is replaced by a format and variable list consistent with the C function printf. The ETM macro appends a \n to the message and then appends the message the memory file. You may also note that the first character of the message is always a minus (-) so that these messages are deleted from the memory file when the application terminates; to retain these records in the updated memory file, the initial minus should be changed to a blank. In conjunction with the MEMORY command and Ctrl+U, these macros provide a mechanism for viewing sequences of events dynamically. When the memory file must be expanded, the onlooker created by the last MEMORY command remains associated with the old memory file and a new memory file is used. Thus, you must periodically issue a new MEMORY command; you should, but need not, close the onlooker for the old memory file. Pressing Ctrl+U updates the onlooker, which is necessary because appending the messages does not update it. The BTE macro was designed to trace window events (messages). Even though the available documentation provides detailed information about the sequences of events that occur in certain circumstances, event tracing served to validate the documentation, particularly with respect to the parameter values provided as part of each event, and to fine tune the event processing functions. Tracing keyboard and mouse events can produce huge quantities of data and should be avoided; tracing these events is not very instructive anyway. The BTM macro was used to trace the flow of control through the POO software. Using these macros can be exciting; it can also be deadly: do NOT insert these macros in the VM file facility. USING A POO APPLICATION You, the user of a POO application, can interact directly with the windowing system or can use the POO User Interface (UI) to interact with the windowing system, file system, or system. You can manage the windows created by the application using whatever controls are normally supported by the windowing system, e.g., move, resize, or close a window. The application may restrict your ability to close its windows. You can set and display the colors, font, and geometry of a window, which are your prerogative, indeed, they are your responsibility. You can set, display, and unset the strings assigned to the function keys. You can set and display each window's current directory, issue I/O direction commands to save output in a file or to establish input from a file, establish input from a subfile of an existing file, open additional windows to view directories or files in the file system, or create files in the file system. You can suspend or terminate the application. You can use the keyboard and mouse for scrolling and data entry and editing; both typeahead and line re-entry are supported. The application opens one or more windows and appends text to the output streams that are associated with them. As text is added, the windows are updated to show the most recent output, normally with the last line of the output stream at the bottom of the window. For some windows, the application may solicit input, hopefully preceded by an appropriate message so that you will have some idea of how to respond. This is the extent of the application's role; all other aspects of its windows and their associated I/O streams fall under your purview. The principal limitation placed on you is intended to maintain the integrity of the application: you cannot edit its output streams. Further, if there is a conflict, the application always has the right of way because it is less adaptable than you and because you can suspend it at any time and, therefore, avoid conflicts. All windows created by the application, whether at your request or the application's, are standard, top-level windows as provided by the window manager. In Windows and OS/2, the window manager is an integral part of the windowing system, and the standard windows provided in these two systems are almost identical. In X, the window manager is not part of the X Window System, and there are numerous window managers in widespread use. Thus, there is some variation in a standard window in X. If you are happy with your X window manager, there is no reason to change it. On the other hand, if you run windowed applications routinely, you may choose to use a different window manager. As top-level windows, your desktop is the parent of each window, and all windows are independent of each other even though they are all associated with the application. Although undisciplined in this respect, the application's windows are not unruly: you can manage them using either the windowing system or POO commands. As standard windows, each likely has a system menu box, title bar, a maximize, minimize, and/or restore box, and a sizing border. You can use any features provided by the windowing system to manage the windows but should not close an X window in this manner. You are responsible for managing the colors, font, and geometry of each window using the appropriate attribute commands. These POO commands follow the rule that the current value is displayed if a new operand is not provided and is set if an valid operand is provided. In general, window attributes and function key string assignments are retained in the memory file between invocations so that, once you establish them, they are retained until you change them. The POO software uses a command-driven user interface rather than the pictographs and menus commonly used in commercial products. You can enter commands at any time. Most commands consist of the command word followed by a single operand, e.g., 'background blue'. The current setting is displayed if the command is entered without an operand, and most commands ignore extraneous operands. If you terminate an input line by pressing Ctrl or Ctrl and Shift in conjunction with Enter (Return), your input is always processed as a command. Windows generally emits a noise for Ctrl+Enter but is quiet for Ctrl+Shift+Enter, and OS/2 ignores Enter if Shift is pressed so that Ctrl+Enter must be used. Although all POO windows are alike as far as the windowing system is concerned, the POO software classifies them as either pollsters, onlookers, or others. Thus, typographical errors aside, the phrases "an other window" and "another window" are not the same because the former is more specific than the latter. This classification is based primarily on how a window is used. Regardless of the window type, the data displayed in a window is held in a virtual memory file, the VM file associated with the window. Pollsters can be created by either the user or the application and provide a mechanism for maintaining a diary or log, a poll file. Because the poll file is always updated, a pollster can be opened only for a file that looks like a poll file. Each entry consists of a header line followed by the text you enter through the window. The header contains the current date and time and an unique index. If you edit a poll file, the indexes in the header lines will no longer be correct because they are the displacements of the header lines from the beginning of the file. Onlookers can be created by either the user or the application and provide a convenient mechanism for rummaging through the file system or creating new files. Regular files, both text and binary, and directories can be viewed in an onlooker. An onlooker can have only one file associated with it at a time, but a VM file can be associated with numerous onlookers at the same time. Usually, the VM file associated with a pollster or an onlooker is a copy of the corresponding disk file. As a VM file, a directory is represented as a text file consisting of the unsorted file names in the directory, one file name per line; the first line is the path name of the directory. When loading a regular file into a VM file, the VM file facility determines whether the file appears to contain text or binary data. When displaying a text file, the software adapts to the apparent line termination and does not wrap lines too long to fit in the window. By default, onlookers display binary files in hex dump format, but you can toggle (F6) between text and hex dump format, which affects only how the data is rendered in the window. The VM file associated with a pollster or onlooker is automatically expanded if you append data to it. Others can be created only by the application and display an output stream provided by the application. The size of the VM file and the management strategy, expansion or truncation, are determined by the application. If expansion is required but virtual memory space is not available, the management strategy is changed to truncation. When managed by truncation, the oldest data in the VM file is discarded to make room for new data. As the application appends text to the output stream, the window is updated to show the most recent output. For some others, the application may solicit input through the window. You can enter it through the window, issue an input direction command, or paste using the VM file copy/paste keys F7 and F5. How you provide the input is transparent to the application. If you close a window, the associated VM file is deleted. If it is a pollster, the disk file is always updated; if an onlooker, the disk file, if any, is unchanged even if you have changed the copy in the VM file. If you close an other and have directed the output to a disk file, the file is updated before the VM file is deleted. When an application opens a window, it can specify that you are not allowed to close it. Further, the application can detect that you have closed a window that it opened. The application is terminated by the POO software if you close the last window associated with it; in this regard, windows that you create count. Pollsters and onlookers that you create are transparent to the application. You can scroll the text displayed in a window by using the usual scrolling keys, by dragging the text around the window with the mouse, by using the implicit vertical scroll bar, or by issuing COLUMN or POSITION commands. Vertically, scrolling is limited by the first and last lines of the output stream. Horizontally, it is unrestricted so that it is possible to lose the text and have a blank window. Lines of text are not wrapped; each line of text occupies one line in the window appropriately truncated at either or both the left or right. Because you cannot edit the text displayed in a window, a caret or cursor is not provided. Thus, the arrow keys (Up, Down, Left, and Right) have immediate effect. The vertical and horizontal scroll bars common in Windows and OS/2 are not supported. You can request that a vertical scroll be drawn at the right edge of the window, but the implicit scroll bar that is activated when the Shift key is used in conjunction with the left mouse button is more convenient. The software adapts to the size of the window but is not fond of puny windows. Thus, the result of pressing the Page Up and Page Down keys varies with the window size, but windows too small to hold a single line get short shrift. For pollsters, the window normally displays the entry being added at the top of the window; for onlookers, the window is positioned over the beginning of the file or the previous position depending on whether file position information is available in the memory file; and, for others, the window normally displays the most recent data at the bottom. All of the supported windowing systems send keyboard input to the window that has the focus, which is not necessarily related to the stacking order. The window with the keyboard focus is on top of other windows in Windows and OS/2, but some X window managers do not correlate focus and stacking order. In general, the color of the title bar of the focus window will be unique among the windows on the screen. If you press a mouse button in a window, the window is raised to the top and given the focus. Some X window managers award focus based on pointer location so that it suffices to simply move the pointer into the window; such window managers do not raise the focus window to the top. If the application solicits input through an other, the window is raised to the top, and the input rectangle is drawn. If you have minimized a window and the application requests input through it, it is restored and raised to the top. Thus, you need not worry about missing an input solicitation because you minimize a window. A window soliciting input is given the focus in Windows and OS/2 but may or may not be given the focus by your X window manager. All input through a window is done in the input rectangle in reverse video, but the input rectangle is not drawn unless you are being solicited for input or in the process of entering input. Within specific limits, you can control both the location and the size of the input rectangle: it can be one or two lines high, full or partial width, and drawn at the top, middle, or bottom of the window. By default, the input rectangle is at the bottom of a pollster, in the middle of an onlooker, and at the top of an other; it is two lines high to increase its visibility. Like the window attributes, you can change these values dynamically and the new values are saved in the memory file so that they are available the next time the application is invoked. The input rectangle is also used to display information if requested. If the input rectangle is displaying a message and you type something, appropriate changes are made in the message and it becomes input. Three carets are used in conjunction with the input rectangle, but the caret is displayed only if the window has the focus. A thick vertical bar to the left of the caret character indicates insert mode, while a pillow covering it indicates overstrike. The third caret is a thin vertical bar that may be almost indistinguishable from the insert caret and is used only when a message is being displayed. If a message is being displayed, pressing Enter deletes it. If you enter an erroneous command, a cryptic note is appended to your input and little else happens except that the thin caret is used. In this situation, you can correct your error, which converts the message to input automatically, and enter your command again. Most commands ignore extraneous operands so that you need not delete the cryptic commentary when correcting a command. Input is terminated when you press Enter (Return) or Enter in conjunction with Shift or Ctrl. Some of the supported windowing systems ignore Enter when Shift is pressed, and some enhance it with audio effects when Ctrl is pressed, which you may find alarming or just annoying. In any case, to the extent that they are accepted by the windowing system, this software treats Enter and Shift+Enter the same, and Ctrl+Enter and Shift+Ctrl+Enter the same. The former terminates your input normally, while the latter causes it to be interpreted as a command. Traditionally, when both data and commands can be entered, they are distinguished by a command character, usually the first character. This software uses both context and your method of termination to distinguish between data and commands. Unsolicited input is interpreted as a command in pollsters and onlookers but as typeahead input in others. Typeahead input is never inspected for commands. If you mistakenly Enter a command in an other, you can retrieve it using Ctrl+Delete, which inserts the last line of typeahead as input and deletes it from the typeahead input. An unsolicited Ctrl+Z, an EOF, is valid typeahead input and can be withdrawn by Ctrl+Delete. In practice, using Ctrl with Enter for commands is no less error prone than using a command character; you will quite likely use Ctrl+Delete. If you have assigned a string to a function key, pressing the key has the same effect as typing the assigned string, but strings ending in either "\n" or "^M" receive special treatment so that the string can include the effect of pressing Enter or Ctrl+Enter, respectively. When browsing the file system, you will find it very convenient to assign "on ^M" to a function key so that you can use the right mouse button to select a file name in a directory and then press the function key to open an onlooker for the file. Line re-entry is provided by the right mouse button. If the input rectangle is not drawn or the pointer is not in it, the line under the pointer becomes the current input when you press the right mouse button. If the pointer is within the input rectangle when you press the right mouse button, it has the same effect as pressing Enter. Strictly speaking, this capability is somewhat more general than line re-entry in that the line may be any line displayed in the window. For example, the application may provide you a list of choices and expect you to use the right mouse button to select one, a process that requires two clicks, one on the line you want and one in the input rectangle. The cut/copy/paste capability for input being entered through a window allows you to move input between windows associated with an invocation of the application but not between different invocations or other applications because it is not interfaced to the analogous capability of the windowing system. It is implemented by both the usual Windows and OS/2 keys, namely, Ctrl+X, Ctrl+C, and Ctrl+V and Shift+Delete, Ctrl+Insert, and Shift+Insert, respectively. Input direction from a disk file is supported using the < and << (LOAD and CONTINUE) commands. If you establish input direction with < (a LOAD command), a single EOF is returned to the application when the end of the file is encountered and subsequent input requests are processed normally. If you establish input direction with << (a CONTINUE command), no EOF is returned to the application and the input request that would normally receive the EOF is processed normally. The POO software treats an EOF is a transitory signal rather than a permanent condition. You can also establish input direction for an other using the file copy/paste capability provided by function keys F7 (copy) and F5 (paste). A paste is treated the same as a CONTINUE. Because this capability is NOT interfaced to the analogous capability of the windowing systems, you can copy and paste only between windows opened by an invocation of an application. This form of input direction is useful primarily when only a subset of the lines in a file is desired; subfiles can be associated with an onlooker by using Ctrl+B and Ctrl+A to restrict the portion of the VM file associated with the window. In pollsters and onlookers, pasting input appends the data to the associated VM file. The user interface consists of four components: the processing of keyboard and mouse input, the processing of commands entered through a window, and the memory file. In general, key and button input is quadrupled based on the states of the Shift and Ctrl keys; the Alt key state is ignored but may be examined by the windowing system. The majority of these combinations are ignored. Commands are used to manage window attributes, function key assignments, I/O direction, as well as the application itself. The memory file is used to retain data between invocations of an application and is a plain, text file so that you can insert additional commands into the memory file manually. The keys produce an admixture of the functions commonly provided by applications in the supported windowing systems. Because the keys are engraved with something more or less definitive, there is considerable standardization. The functions assigned to the keys are the same in all versions of the POO software. Consequently, keys that are normally passive or ignored in your windowing system may acquire some functionality because they are commonly assigned a function in one of the other supported systems. Likewise, some familiar keys may provide a different function than the one you are accustomed to in your systems. There are a few functions that are useful but uncommon. Some functions depend on whether you are or are not entering input through the window and are applied either to the input caret or the window, respectively. Hopefully, you will not be astonished by the sequence of events that unfolds when you press a key, but mild surprise is possible. There is almost no standardization with regard to mouse input; even different X window managers assign different functions to mouse input. As usual, the left mouse button is heavily oversubscribed, and little use is made of the other buttons. Only a few of the eight (two button mouse) or more mouse buttons solicit a response. Dragging with the left mouse button pressed commonly provides text selection for cutting and pasting but this capability is moot because you are not allowed to edit the text displayed in a window. Instead, this action moves the text within the window so that you can drag a portion of text under the window. Shift plus the left mouse button implements the vertical scroll bar regardless of the pointer location. The command interface is entirely nonstandard. This software uses commands to provide the services that commercial applications usually provide using menus, proprietary pictographs, or adjunct windows. Many of the commands simply provide the ability to set or to display something, which includes the window attribute and I/O direction commands. For such commands, the associated value is set if an operand is provided and displayed otherwise, which avoids the additional layer that would be needed to support explicit set and display commands. The remaining commands result in some specific action, e.g., open an onlooker or terminate the application. The memory file is fundamentally a user facility that is maintained automatically by the software for your convenience. Although the name of the memory file is determined by the application, you can manage its location in the file system using the environmental variables POOMEMDIR or HOME. In effect, the memory file contains the part of an application that languages do not support: dynamic data storage that is retained between invocations. This software uses the memory file to retain window attributes and function key string assignments. The memory file is a plain text file so that you can edit it easily, and the software is very tolerant of its contents. KEYBOARD INPUT A computer keyboard is commonly described as consisting of the standard typewriter keys, the extended keys, and the function keys. This software does not address the usual key pad directly, but the supported windowing systems generally map these keys to their counterparts on the standard keyboard. The keys are quadrupled based on the states of the Shift and Ctrl keys. Although each key is quadrupled and available to be assigned its own function, some combinations are not distinguished and many others are ignored. For example, the Ctrl+alphabetic and Shift+Ctrl+alphabetic are treated the same by the windowing system, and only three function keys are assigned built-in functions. In summary, there is not a large table of key functions that must be memorized. The extended keys generally follow common usage, see Table 1. Thus, Insert toggles between insert and overstrike modes, and Delete and Backspace do so. Some of the scrolling keys are context dependent: they either scroll the window or move the input caret. To provide complete functionality while entering input, the scrolling keys in combination with Shift always provide the usual scrolling function. Scrolling with the keyboard is based on the perspective that the data is fixed and that the window moves, which provides the obvious result. Up, Down, Page Up and Page Down move the window over the text and can be used with or without the Shift key. In this context, a page consists of the lines that fit in the window and, therefore, varies with the window size. Vertical movement of a window is restricted by the first and last lines of the VM file associated with it. The Left, Right, Home, and End keys move the caret when entering input and move the window over the text otherwise but always have their usual scrolling function in combination with the Shift key. Lines are never wrapped, and the horizontal position of a window is not limited so that it is possible to lose the data. Ctrl+Left and Ctrl+Right reset the horizontal shift so that each line starts at the left edge of the window. Home and End scroll to the beginning or end of the VM file, respectively, but do not set the horizontal shift to zero. Ctrl+Home and Ctrl+End move the window and set the horizontal shift to zero, which appears common in many Windows and OS/2 applications. --------------------------------------------------------------- Key Normal Shift+ Ctrl+ Shift+Ctrl+ --------- ------ ------ ------- ----------- Up Scroll Scroll Down Scroll Scroll Page Up Scroll Scroll Page Down Scroll Scroll Left Scroll/Caret Scroll Reset Right Scroll/Caret Scroll Reset Home Scroll/Caret Scroll Scroll & Reset End Scroll/Caret Scroll Scroll & Reset Delete Delete OS/2 Cut Typeahead Backspace Backspace Insert Toggle OS/2 Paste OS/2 Copy Enter Normal Normal Command Command Pause Toggle Toggle Stop Stop --------------------------------------------------------------- Table 1: Keys assigned their normal scrolling function are labelled Scroll, while the Scroll/Caret label implies a context dependency. As already noted, the Insert, Delete, and Backspace keys do the obvious. In deference to OS/2, Shift+Delete cuts (copies and deletes) the input to the cut/copy/paste input buffer, a function that is also assigned to Ctrl+X in deference to Windows. Similarly, the inverse function is assigned to Shift+Insert and Ctrl+V, and Ctrl+Insert and Ctrl+C copy the input to the cut/copy/paste input buffer. All of these key combinations are ignored if you are not entering input. The Ctrl+Delete key provides an unusual but valuable function. It inserts or overstrikes the input with the last line of typeahead input and deletes it from the typeahead input. If the last line is an EOF (Ctrl+Z), "EOF" is used. Input is interpreted as normal input or as a command depending on how you terminate it, Enter or Ctrl+Enter, respectively. This unusual feature is handy if you mistakenly press Enter to terminate a command in an other or if you need to delete some or all of your typeahead input. Neither the Esc nor Tab keys are quadrupled and, in combination with the Shift and/or Ctrl keys, may be usurped by the windowing system and result in some action. The Esc key is assigned a function, while the Tab key is ignored. If you are in the process of entering input, pressing Esc discards the input and deletes the input rectangle even if the application is soliciting input. If the input rectangle is not displayed, pressing Esc closes the window. As in all other situations, the process is terminated automatically and without option when the last window is closed. Thus, pressing Esc twice in an application with only one window terminates the application! It is wise to avoid the Esc key when frustrated or if you are naturally lead fingered. The Enter key is used to terminate an input line. Some of the supported windowing systems ignore Enter when Shift is pressed; and some enhance Enter with sound effects when Ctrl is pressed. To the extent that they are not ignored by the windowing system, Enter and Shift+Enter are treated the same and, likewise, Ctrl+Enter and Shift+Ctrl+Enter. Because it is well known, Ctrl+M is treated the same as Enter. Input termination, however, is context dependent. If input is being solicited through a window, pressing Enter terminates your input line and returns it to the application, while Ctrl+Enter causes your input line to be interpreted as a command. If input is not being solicited, your input is interpreted as a command in a pollster or onlooker and as normal input (typeahead) in an other. In general, therefore, Enter suffices, but Ctrl+Enter must be used to terminate commands in others. If you mistakenly press Enter in an other to terminate a command, you may be able to recover with a Ctrl+Delete. When the input rectangle is not being displayed in a window but the application is soliciting input through one of its windows, pressing Enter shifts the focus to the window through which input is being solicited and displays the input rectangle in this window. When the application solicits input, you may choose to peruse other windows or to enter commands in other windows. This special processing of an Enter provides you a quick method to get back to the real business at hand and saves you having to remember which window solicited input. If you press Enter while a message is being displayed in the input rectangle, the message is deleted. Usually, the input rectangle disappears when a message is deleted in this manner, but it remains displayed if the application is soliciting input. For example, if the application solicits input, you might type bg Ctrl+Enter Enter Enter to look at the background color before entering your input. The Ctrl+Enter displays the background color because bg is a valid abbreviation for the BACKGROUND command and there is no operand, the first Enter deletes this display, and the second Enter returns your input to the application. On the other hand, if you want to return the complete path name of a file as your input to the application, you might type yo Ctrl+Enter Enter. The Ctrl+Enter terminates the YO command, which displays the path name of the file and other information; the editing converts this message to input and presumably deletes the extraneous information provided by the YO command; and the Enter returns the edited message to the application as input. ----------------------------------------------------------------- A deletes the input after the caret OR the file after the bottom line. B deletes the input before the caret OR the file before the top line. C copies the input to the cut/copy/paste input buffer. D terminates the application. H the same as Backspace. I ignored (Tab character). M the same as Enter. O toggles between text and hex dump format. Q resumes application execution. S suspends application execution. U deletes all input OR restores the whole file. V pastes the cut/copy/paste input buffer into the input. X cuts the input to the cut/copy/paste input buffer. Z generates an EOF. ----------------------------------------------------------------- Table 2: Most of the Ctrl+alphabetic functions have some basis in common usage in a current system or in history. Only A, B, and U are context dependent. When entering input, Ctrl+A, Ctrl+B, and Ctrl+U delete the end, the beginning, or the whole line, respectively. In this situation, the text is actually deleted and is not placed in the cut/copy/paste input buffer. When not entering input, Ctrl+A, Ctrl+B, and Ctrl+U affect the portion of the file associated with the window but do not actually delete any data from the file. Ctrl+A inhibits access to the portion of the file after the bottom line in the window, Ctrl+B inhibits access to the portion before the top line, and Ctrl+U (re)associates the whole file with the window. This capability is useful primarily in association with the file copy/paste capability provided by functions keys F7 and F5 and with output direction in an onlooker, both of which deal only with the viewable portion of the VM file associated with the window. Some of the supported windowing systems pre-empt one or more of the function keys, perhaps including the Shift and Ctrl states as well, which effectively precludes their use because they are not passed to the application. Windows pre-empts F10. The function keys are quadrupled based on the states of the Shift and Ctrl keys so that Shift+F5 is F17, Ctrl+F5 is F29, and Shift+Ctrl+F5 is F41. The commands F1,...,F48 can be used to set, display, or delete a string assignment to the function keys. The function keys F5, F6, and F7 and their progeny are assigned built-in functions, which have lower priority than your string assignments. The built-in functions are assigned to all of their progeny so that, until you assign a string to each, the built-in function remains available. ------------------------------------------------------------- F5 pastes into the input stream or to the end of the file. F6 toggles STATIC mode (others only). F7 copies the VM file associated with the window. ------------------------------------------------------------- Table 3: Two of the built-in function key assignments provide the copy/paste file capability; the other, control of window updating. Normally, others are updated whenever data is appended to the output stream. In some cases, this automatic updating can be time consuming. To deal with this issue, F6 toggles the STATIC flag, which prevents the automatic updating. When the STATIC flag is toggled off, the window is always updated to show the end of the output stream at the bottom of the window. The STATIC flag does not impact scrolling and can be overridden by the application. When used in others, a copy operation (F7) actually makes a copy of the data because the VM file used to retain the output stream may be changed before the data is pasted. In pollsters and onlookers, the portion of the VM file copied is simply recorded. The paste operation (F5) destroys the copy/paste VM file. Thus, if you want to paste a file twice, you must copy and paste and then copy and paste again. In a pollster or an onlooker, the paste function appends the copy/paste file to the associated VM file; of course, each line is prefixed with two blanks when appending to a pollster. In an other, pasting establishes an unterminated input stream so that when the input is exhausted, input through the window is resumed without interruption or notification to the application. After a function key is assigned a string, pressing the function key inserts or overstrikes the current input line with the assigned string just as if you typed the string. Thus, the result depends on whether you are in insert or overstrike modes unless the caret is at the end of the your input. If the string assigned to a function key ends with either of the two-character sequences "\n" or "^M", the key is accorded special processing. First, these two suffixes are not considered when updating the input. Second, "\n" is treated as an Enter, and "^M" is treated as a Ctrl+Enter. Thus, the command "f12 stop ^M" would make F12 a potential killer. MOUSE INPUT Mouse input is processed when you press a button or, in one case, when you drag with the button pressed; your release of a button is ignored. There are no standards in this regard, some applications process button presses and releases, and some ignore presses and process releases. This is pertinent because if you press a button in one window and release it in another, you may suffer collateral damage depending on what applications own the windows. Further, since the POO software may move the window as a consequence of your press, the window may be moved out from under the pointer by the press so that the release may go to a window belonging to another application, and it may process the release. Clearly, you can avoid this minor issue. On the other hand, if this piques your interest, you can use this software to determine which applications process button releases by issuing GEOMETRY commands that move the window out from underneath the pointer with the right mouse button and releasing the right button without moving the pointer back into the window. In addition to the usual functionality of the mouse provided by the window manager, the mouse can be used for scrolling and input entry. Further, if the pointer is in a POO window, pressing a mouse button raises the window and awards it the keyboard focus. This latter service is provided either by the window manager or the POO software depending on the windowing system. Finally, just as for the keyboard, the mouse buttons are quadrupled based on the Shift and Ctrl keys. The scrolling services provided by the mouse take the perspective that the window is fixed and that the text is moved, which is the converse of the perspective used for the keyboard. If the desired movement is small, you can drag the text around the window. In effect, when you press the left mouse button, the software attaches the pointer to the character under it, and the text is moved to maintain this attachment. The attachment is loose and accelerated a bit so that the text moves faster than the pointer; it is possible to move the character at least to the edge, if not outside, the window. For larger vertical distances, the mouse can be used with the vertical scroll bar. If one or more components of the scroll bar is drawn and the pointer is within the scroll bar area or if you simply press the Shift key, a point-and-click with the left mouse button moves the text so that the top line displayed in the window is proportional to the position of the pointer relative to the window. Thus, if you click in the middle of the window, the top line will be the line roughly half way through the VM file. The MODE command can be used to control what parts of the scroll bar are drawn at the right edge of the window. By default, no scroll bar components are drawn in others and pollsters, and only the tic line is drawn in onlookers. The right button point-and-click mechanism can be used to enter input and contains context dependencies. If the input rectangle is displayed and the pointer is within it, pressing the right button has the same effect as pressing the Enter key. Otherwise, the line containing the pointer is inserted as or replaces the input. Only the vertical position of the pointer is considered; the horizontal is ignored. COMMAND OVERVIEW You enter commands to manage the window attributes, function keys, current directory, I/O direction, and the updating of the memory file; there are also commands for setting the window position and displaying the status of the application or a file. The usual set and display commands are not provided; rather, each value that you can manage is represented as a command, and all such commands take the form " ." If you provide no operand, the current value is displayed; otherwise, the value is changed to the operand value. If the operand in a function key or I/O direction command is "-", the new value is none, which means the function key is not assigned a string or the I/O direction is cancelled. ------------------------------------------------------ Set/Display Type Window Creation ---------------- --------------- BACKGROUND aka BG [color] POLLSTER filename FOREGROUND aka FG [color] ONLOOKER filename FONT aka FN [name] VMF [size] [type] GEOMETRY [=wxh+x+y] MEMORY MODE [+|-]flag{+|-}... F [text | -] Action < aka LOAD [filename | - ] ------ << aka CONTINUE [filename | - ] EXIT aka XIT aka QUIT > aka STORE [filename | - ] STOP >> aka APPEND [filename | - ] MEMORY { off | on } CD [pathname] CLOSE YO [filename] NB COLUMN [n] FIND [?]string[?] [b] POSITION [n|fraction] CF number ? ------------------------------------------------------ Table 4: Command summary by category. The MEMORY command is special and contains a context dependency. In general, it opens an onlooker for the memory file, but it also acts like a set and display command for controlling whether the memory file is updated when the application terminates. The VMF command creates a new, unnamed VM file and displays it in an onlooker. The effect of an NB depends on whether the window is a pollster, onlooker, or other. A FIND provides the obvious service, and CF compares two files. When normal input and commands are obtained from a single input stream, it is common to use a command character, usually the first character, to distinguish between the two types of input. Although very familiar, this strategy either limits normal input or requires a literal next character that must be used to enter normal input that begins with the selected command character. This software distinguishes normal input and commands by how you terminate your input: Enter (Return) for normal input and Ctrl+Enter for commands. Commands must be entered through a window and are executed when entered; there is no provision for processing commands in typeahead input or in a directed input stream. In general, the window is an implicit operand, and there is no mechanism for entering a command through one window and applying it to another window. In the text of this monograph, commands are always in uppercase to enhance their visibility; when entered through a window or obtained from the memory file, case is ignored and any unique initial substring is sufficient. In general, after successfully executing a command, the input rectangle disappears; but, if input is being solicited through the window, the window is re-initialized for input. Further, the FIND and CF commands are retained as input so that they can be re-issued conveniently. Except for the function key commands, the command is parsed as the first blank-delimited word, and the operands as the subsequent blank-delimited words. Operands that contain blanks must be enclosed in quotes (") or primes ('). Additional operands are ignored so that it is not necessary to delete extraneous text when entering a command. In function key commands, the string to be assigned to the key is the only operand, and it is parsed as the longest blank-delimited string following the command. Thus, the operand is usually the rest of the input line, and a leading (trailing) quote or prime is necessary only if the operand begins (ends) with a blank, quote, or prime. Operands are always viewed as case sensitive even though the eventual processing applied to them may not distinguish case. For example, although command processing does not convert file names to uppercase, they may be converted to uppercase before they are passed to the file system. Color and font names are case sensitive in X, but not in Windows and OS/2. If you enter an erroneous command, a cryptic but suggestive note is appended to your input and displayed in the input rectangle as a message. If you enter a display command, the consequences are very similar. These two situations must be distinguished by the context. When displaying values, the message consists of the complete command name and current value. When a message is displayed in the input rectangle (thin caret), it can be deleted by pressing Enter or Esc but becomes input if you change it. Thus, for example, if you enter an erroneous command, you can generally correct it and re-enter it because your corrections change the error message to input and the cryptic error comment is ignored as extraneous when the command is parsed. Similarly, displaying the current value may be the most convenient method for changing it. WINDOW ATTRIBUTE COMMANDS The BACKGROUND, FOREGROUND, FONT, GEOMETRY, and MODE commands provide control over the obvious attributes of the window; the usual X abbreviations BG, FG, and FN are accepted. If successfully executed, these commands update the memory file with a line of the form * if memory file updating is currently on. If you want to issue an attribute command and do not want it saved in the memory file, issue a "memory off" first, the attribute command, and then restore memory updating with a "memory on." This feature is quite useful in conjunction with GEOMETRY commands. The window names are "Pollster" or "Onlooker" or "Other" or the name provided by the application. Consequently, attribute commands entered through a pollster or onlooker affect all subsequent such windows that are opened because the memory file is used to set their initial attributes. The names of others may or may not be unique so that changing the attributes of an other may or may not affect windows that are opened subsequently. The color and font values are highly system dependent; a value of "default" is supported by the POO software in all systems but is itself system dependent. The color names "black" and "white" are safe assumptions; while the usual VGA color names red, green, blue, yellow, purple aka magenta, cyan, and gray have roughly the same probability of success. If the usual VGA color names are accepted, then prefixing "Dark" to the capitalized color name will likely work in all supported systems. In the X Window System, the file "/usr/X11/.../rgb.txt" should contain a list of the valid color names, which are more or less standard but may vary somewhat with the system. The rgb.txt file is usually quite large. X color names may contain both upper and lower case characters, and the color names must be entered exactly as specified in the rgb.txt file. The font operands are very system dependent. In Windows, FIXED, VARIABLE, ANSIFIXED, ANSIVAR, DEVICE, and OEM elicit the so-called stock fonts; case is ignored and abbreviations are accepted. In Windows and OS/2, the font names Courier, Helvetica, Times Roman aka Tms Roman aka Roman are supported with an optional size of the form [{ x | X }]. Font size is normally measured by its height, but the POO software tries to match the width and generally ignores the height because the legitimate width values are more or less consecutive integers while the heights vary considerably. The font setting only affects how text is displayed in the window: it does not affect an output stream that is saved in a file. In Windows and OS/2, case in font names is ignored, and any initial substring is accepted; since the first characters of the font names are distinct, a single character is sufficient. In X, font operands are case sensitive and may not be abbreviated. The file /usr/X11/.../fonts.alias should contain a short list of X font values, but they are usually only the tip of the iceberg. The X Window System is often installed with hundreds of fonts. The GEOMETRY command sets the size and/or position of a window and accepts a few minor extensions of the standard X geometry but may be quite foreign to those familiar only with Windows and OS/2 in which window geometry is usually left to the windowing system. The syntax of the geometry operand is [=]x{+|-}{+|-}. The operand is parsed in a relatively permissive manner. Almost all fields are optional, but there is clearly no way to specify the y origin without giving the x origin. Components that are not specified remain unchanged. The width and height values are interpreted in units of characters and lines, respectively, if they are less than 100 and in pixels otherwise. This makes it a bit difficult to create puny windows, but then this software does not care much for puny windows anyway. Either or both sizes may be 'm' or 'M' meaning maximum. The origin values are always pixel values but may be either 'c' or 'C' meaning centered. The interpretation of the sign characters used with the origin values follows the usual X convention: if the plus sign is used, the origin value is the displacement of the left (upper) edge of the window from the left (upper) edge of the screen; if negative, the displacement of the right (lower) edge from the right (lower) edge. The operand in a MODE command consists of any sequence of the legal values separated by sign characters, which indicate whether the option should be on (+) or off (-). A plus sign is assumed before the first word in the operand. Rather than try to remember the legal values, I suggest you issue the display version, which will show all values, and then modify the signs as desired. MODE commands saved in the memory file always include all values. The position and size of the input rectangle can be controlled using BOTTOM, TOP, HIGH, and WIDE. SB and TIC control the components of the transparent scroll bar that are drawn at the right edge of the window. BD causes lines to be drawn around the text to distinguish blank space within the text from the nothingness surrounding it; at most three lines are drawn because text lines are always viewed as unlimited in length. INSERT sets the input mode to insert or overstrike but is useful only to set the initial state. SCROLLING COMMANDS The COLUMN and POSITION commands provide no new functionality but do provide more precise mechanisms and the ability to display the current values. The COLUMN operand may be any integer and sets the horizontal position of the window over the text; the values 0 and 1 are the same. Thus "col 10" shifts the window right 10 characters, which means that the 10-th character in each line is displayed at the left edge of the window; while "col -10" shifts the window left, which means there is blank space to the left of the text. The POSITION operand may be either a positive integer displacement or a fraction. The display version includes the displacements of the top and bottom lines from the beginning of the file as well as the size of the VM file. FUNCTION KEY COMMANDS The commands F1,...,F48 set, display, or delete string assignments to the 48 function keys. The operand, the string to be assigned to the key, starts with the first nonblank character after the command and ends with the last nonblank character in the line. To allow for the inclusion of leading and trailing blanks, an initial or trailing quote (") or prime (') is deleted; aside from this trimming, the string itself is not examined so that no special notation is required for embedded quotes or primes. Nevertheless, the price for this tactic is that leading or trailing quotes or primes must be doubled. An operand of "-" deletes the current string assignment. If entered with no operand, the current string assignment, if any, is displayed; there is no provision for displaying the built-in functions. As in similar situations, the information is discarded if you press the Enter key without making any changes but is converted to input if you make a change, which is convenient when you wish to edit the current string assignment. Function key string assignments are maintained in the memory file. Thus, the application must be properly terminated to retain new string assignments. In X, the key rebinding capability supported by X is not used to implement string assignments to the function keys. Function key string assignments do not affect the X server and, until saved in the memory file, apply only to the current invocation of the application. I/O DIRECTION COMMANDS Although I/O direction commands are not saved in the memory file, I/O direction commands in the memory file that are qualified by the window name are processed when creating others. In short, you can insert such commands in the memory file manually. Because this software provides no mechanism for changing such commands, they will continue to be used until you delete them. This may be useful when initially developing an application, but software is usually as deterministic as hardware so that eventually the application will be boring. Since window names are not required to be unique, output direction commands in the memory file may direct multiple streams to the same disk file, which is likely counterproductive at best. A current directory is associated with each window created by this software. Initially, the current directory of a pollster is set to the path of the poll file, that of an onlooker to the path of the associated file, and that of an other to the application's current directory. You can display or set the current directory of a window using the CD command. The new current directory, the first and only operand, is interpreted relative to the current directory and must correspond to a directory in the file system. A CD with no operand displays the current directory. You can insert CDs in the memory file. If unqualified, such CDs are processed when the memory file is loaded and set the current directory of the application. Such CDs are applied cumulatively starting with the system's current directory when the application is invoked. If qualified, such CDs are processed when creating the named window and set the window's current directory; they are applied cumulatively starting with the application's current directory. When a file or directory in the file system is associated with a pollster or onlooker, its current directory is changed to the path name of the file or directory. The YO command is display-only whether entered with or without an operand. The information includes the complete path name and may include additional information. If no operand is provided, the file information corresponds to the VM file associated with the window; otherwise, it corresponds to the first operand, a file name that is interpreted relative to the window's current directory. The LOAD, CONTINUE, STORE, and APPEND commands are aliases for the usual notation based on the less than and greater than characters: <, <<, >, and >>, respectively. The word versions are used in this monograph because they are more visible; in practice, you will likely prefer the usual notation. When entered, the word versions require one or more blanks between the command word and the file name, but the usual notation does not. Each of these commands expects a single operand, a file name that is interpreted relative to the window's current directory. I/O direction commands cannot be applied to a pollster. In onlookers, input direction changes the associated file, and output direction stores the viewable portion of the associated file in the specified disk file, where the viewable portion of the file can be set using Ctrl+A and Ctrl+B (Ctrl+U makes the whole file viewable). All appropriate input and output I/O operations are performed immediately. LOAD and CONTINUE differ only in that the former deletes the VM file currently associated with the onlooker, while the latter retains it because it is assumed that the VM file will be needed subsequently. STORE replaces or creates the disk file as necessary, while APPEND appends the VM file to the end of an existing disk file. In the Onlooker application, you are not allowed to STORE or APPEND to an existing disk file unless you enter "Marvelous" after the file name. In others, STORE and APPEND have their standard meaning, namely the output stream is either stored in or appended to the disk file. But, aside from setting the name of the VM file associated with the window, these commands have no immediate effect. The disk file is written only when and as necessary: if the VM file must be truncated to obtain space, is rewound by the application, or the window is closed. When data must be transferred to the file, the file is opened, the data is transferred, and the file is closed. Thus, the specified disk file does not remain open continuously. If you do not enter the output direction command before data must be discarded, the disk file will not contain the complete output stream. For example, part of the output stream may have been discarded if you wait until the application is done. If the output stream has been directed to a file and you enter a new output direction command, the existing portion of the output stream that has not been saved in the old file will be saved in the new file. If the operand in the new output direction command is a minus (-), there is no new file and output direction is cancelled. If you want to save part or all of the current output stream immediately, you must create a VM file (VMF command), copy/paste the output stream into it, and then use output direction in the onlooker created by the VMF command. LOAD and CONTINUE direct the input stream and are identical until the end of the file is reached. At that point, an EOF return is generated if LOAD was used, while input continues though the window if CONTINUE was used. Thus, you can generate an input stream using multiple files or portions of files interspersed with input through the window. Input direction can be cancelled by entering a new input direction command with an operand of minus (-) but is quite dangerous. This should be done with caution unless you can know exactly what the application is doing because there is no attempt to time the cancellation. For example, input direction could be cancelled after the application has read the first part of a line but before it can read the remainder. When you issue an input direction command, the disk file is loaded into a VM file, and all data needs are satisfied from the VM file. Thus, subsequent changes in the disk file have no impact on the window's input stream. Because changes in the disk file change the last modification time, which this software includes when deciding whether two files are the same, you can load the modified file even while the original is being used for input. The VM file is deleted when the end of the file is reached, which may or may not return an EOF depending on how input was directed. The display versions of LOAD and CONTINUE provide the name of the input file, the number of bytes read, the size of the file, and its last modification time. The display versions of STORE and APPEND provide the name of the file to which the output has been directed, the current and maximum size of the output stream, and the last modification time of the disk file. Further, display commands will be corrected for the actual situation regardless of which command you enter. Thus, for example, you may enter ">" and the display may show either "> ..." or ">> ..." depending on whether some data has already been written to the file. WINDOW CREATION COMMANDS The POLLSTER, ONLOOKER, VMF, and MEMORY commands create windows, the latter only if no operand is specified. POLLSTER and ONLOOKER accept a single operand that is evaluated as a file name relative to the current directory of the window through which the command is entered; while VMF accepts up to two operands. Windows that you create are transparent to the application. The operand in a POLLSTER command must be the name of a poll file. A pollster always updates its poll file. Consequently, the file must look like a poll file, a requirement that is imposed primarily for your protection. Most files will not pass the test. The operand in an ONLOOKER command may be a regular file containing either text or binary data or a directory. By default, text files are displayed as text, while files that appear to contain binary data are displayed in hexadecimal dump format. You can toggle between text and hexadecimal dump format by pressing Ctrl+O, which affects only how the data in the VM file is displayed in the window. Directories are displayed as a text file, one file name per line; the first line is the complete path name of the directory. The MEMORY command with no operand creates an onlooker for the memory file and is, therefore, just shorthand that saves you having to know the name of the memory file. The window title will always be the application name followed by "Memory File" rather than the usual onlooker title. If you enter a MEMORY command with no operand in this window, the update status of the memory file is displayed. Because the memory file is dynamic and because changes are made without consideration as to whether it is associated with a window, the onlooker created by a MEMORY command may become outdated over time in that either the information shown in the window is outdated or the file associated with the window is outdated and is no longer the memory file. In the former case, you can force an update by pressing Ctrl+U; in the latter, you should close the onlooker and issue a new MEMORY command. The latter, of course, will give the desired result regardless of the situation. If you enter a VMF command with no operand, an unnamed and minimal size VM file is created and displayed in a new onlooker. A VMF does not create a disk file; you must use output direction in the onlooker to create the disk file. The optional operands, in any order, are the file size and type. The integer size of the file may be terminated with P (pages), B (bytes), or K (kilobytes), which is the default. The type may be { \r\n | crlf | pc | \n | lf | unix | hex | binary }, which allows three aliases for creating a text file in PC format, three for a text file in Unix format, and two for a binary file. You will probably want the file format consistent with the file system because it is more convenient, but this feature allows you to create Unix text files in Windows or OS/2, and conversely. Cutting and pasting between VM files automatically performs any conversions that are necessary. TERMINATION COMMANDS EXIT aka XIT aka QUIT and STOP terminate the application. The first three are the same and update the memory file if memory file updating is on, which is the usual situation. The STOP command sets memory file updating off, which can also be done with the MEMORY command, and then terminates the application. Before the application is terminated, all windows with outstanding output direction are handled appropriately. This includes all pollsters since their output is always directed and cannot be changed. Using these commands provides graceful termination. If the windowing system provides you the ability to terminate an application and you use it, your output direction requests are ignored, and the memory file is not updated. The MEMORY command with an operand of either "on" or "off" sets the updating of the memory file, which is updated when the application is being terminated by this software. In general, the memory file is set to be updated if this software terminates the application; but, if you set memory updating off and enter an EXIT, the memory file is not updated. If you terminate with a STOP, the memory file is not updated. CLOSE closes the window. When opening a window, the application can specify that you are not allowed to close it. If output direction has been specified, the file is updated as appropriate. The ? command is a display-only command that ignores any operands and generates a message containing the application name and status; a double colon; the window name, number, and I/O status; a double colon; and the date and time that the application started. If running single threaded, "ST" immediately follows the application name; the Pollster and Onlooker applications are interactive and always run single threaded. The status may include "suspended" if you have done so, "done" if the primary application thread has returned to the POO main program, or "API" if an application thread is awaiting input. The I/O status may include "static" if you have requested STATIC operation (F6) or "awaiting input" if input is being solicited through the window in which the ? command is entered. THE NB COMMAND The NB command may seem a bit cryptic, but N.B. is the standard abbreviation of "nota bene" or "notez bien" depending on one's language orientation. Its role depends on the type of the window through which it is entered. It ignores any operands. For pollsters, the NB command is implicit in that, when created, a pollster will solicit input and append it to the associated (poll) file with two initial blanks until you enter an EOF or press the Esc key. Pressing Ctrl+Z, an EOF, updates the poll file and closes the pollster. Pressing Esc suspends input solicitation, but it can be resumed for the current poll file entry by entering an NB. If you want a new header line, you must enter an EOF and issue a new POLLSTER command. When not soliciting input, input through a pollster is interpreted as a command. For onlookers, the NB command acts much the same as for a pollster except that the input is appended to the associated file with no changes. Thus, it can be used to append text to any VM file associated with an onlooker and, in this regard, is quite uninhibited. If the VM file corresponds to a disk file, the disk file is not changed unless you explicitly STORE the VM file. Ctrl+Z or Esc terminate the NB but do not close the onlooker. When not soliciting input, input through an onlooker is interpreted as a command. For others, NB is quite aptly named because it provides a mechanism for you to insert proper notes in the output stream associated with the window. If the output stream is currently being written by the application, you should suspend the application before issuing the NB because the application always has the right of way. Thus, if it writes some output while you are in the middle of entering your note, the resulting output stream may be a bit messy because the lines of your note and the lines added by the application will be intermixed. Even though you suspend the application, your note may corrupt the output stream if the last text appended to it by the application was not terminated by a new line character. When an NB is entered, a new line character is appended to the end of the output stream if it does not end with one, a line of the form N.B. is appended to the output stream, and input is solicited through the window. Each line you enter is appended to the note prefixed with two blanks, just as in a pollster. Either an Esc or a Ctrl+Z terminates your note. This capability is an exception to the rule that you cannot edit an output stream, but the insertion of a stylized note is somewhat less intrusive than editing even though it may perturb the output stream. THE FIND COMMAND The FIND command locates the next occurrence of a string but, unlike most other commands, does not go away after it is executed. Thus you can find successive occurrences by simply pressing Enter. If an occurrence of the string is not found, "failed" is appended to your command and it is displayed as a message so that pressing Enter deletes it. But there are a few rules with respect to both the search process and the string operand that require some explanation. Each FIND command moves the window so that the line containing the next occurrence of the string is at the top of the window. Thus, the top line in the window is always ignored when the file is searched regardless of the search direction. If you provide a second operand that starts with a lowercase 'b', the search is backward; otherwise, the search is forward, towards the end of the file. To perform a standard string search, the first operand must be the desired string enclosed in question marks (?). In processing the first operand, an initial or terminal question mark is discarded but not ignored. If present, an initial question mark indicates that you are indifferent to the character preceding the first character of the string; otherwise, the character preceding the first character of the string must be unlike the first character of the string. A terminal question mark has an analogous effect with respect to the character following the last character of the string. In this case, 'unlike' means that the two characters in question cannot both be alphanumeric, punctuation characters, or white space characters. In the FIND command, an underscore (_) is classed as alphanumeric. Thus, for example, "find the?" will succeed before "find the" because the former is more permissive than the latter. In a text file, this mechanism generally provides a word search because the first and last characters are alphabetic and words are separated by punctuation or white space. In source files, it usually results in a symbol search for much the same reason but is also quite reliable for operator searches, e.g., find -> or find &&. This strategy is not standard but quite effective in many circumstances and easy to use. THE CF COMMAND The CF command compares two files character by character and, like FIND, does not go away after it is executed. Usually, it must be deleted explicitly. When executed, the comparison starts with the second line in each window. When a mismatch is found, a message indicating the outcome of the comparison is appended to the command, and each window is moved to show the line containing the mismatch at the top of the window. Consequently, you can resume the file comparison by simply pressing Enter. Before resuming the comparison, you may position the windows to resync the files or to skip sections as desired. If a mismatch is found, the message has the form < char != char> @ COL n>, where the char's are the characters that do not match and n is their position within the line, which is suitable fodder for a COLUMN command. If the end of one of the files is encountered, the message is "EOF", and your input has message status so that pressing Enter deletes it. When the EOF message is given, the last lines that matched are displayed at the top of the windows, which will be blank otherwise. This command clearly violates the idea that a command entered in one window cannot affect another. Further, although the file in the window through which the CF is entered is clearly an operand, you should be wondering how the other file is specified. All windows are assigned numbers, and the CF operand is the number of the window in which the other comparand is displayed. For onlookers, the number is apparent because it is contained in the window title. For pollsters and others, it can be displayed by issuing a ? command. WRITING A POO APPLICATION You, as the writer of a POO application, must be familiar with the POO Application Programming Interface (API) and aware of the rules that must be followed when writing and compiling a POO application. The POO API is very primitive: your application can open and close windowed I/O streams, append data to an output stream, and obtain data from an input stream. Your application starts with a function named 'appl' that accepts no arguments and returns an integer. Your application is responsible for providing its name and the names of its I/O streams, all of which should be unique for the user's sake. Window management is a user responsibility so that no mechanisms are provided to your application for managing the windows that are created to display its output streams. A C stream is described in section 7.9.2 of the ANSI C standard as "an ordered sequence of characters composed into lines, each line consisting of one or more characters plus a terminating new-line character." But this is a reasonable description for I/O streams processed using Fortran formatted WRITE and READ statements and, for that matter, is equally valid for a text file in most systems. The external representation of an I/O stream is not specified by either the C or the Fortran standard. The POO software is designed to interface among the participants: your application processing I/O streams consistent with the description above, a file system using one of the two common external representations, and the windowing system. In effect, the POO software replaces the usual I/O transmission capabilities of C and Fortran with a windowed interface that provides the user power and flexibility in managing the applications I/O streams. --------------------------------------------------------- int cappl (char *application_name) void *copen (char *name_title, int size, int extra_size) int cclose (void *id) int cputs (void *id, char *text, int length) int cgets (void *id, char *buffer, int length) --------------------------------------------------------- Figure 2: POO application programming interface. The POO API is summarized in Figure 2 and should be familiar. Four of the five API functions are direct analogues of the ANSI C stream I/O functions fopen, fclose, fputs, and fgets. The C and Fortran APIs are identical: the Fortran names start with an 'n' rather than a 'c' so that their return values are INTEGERs by default. The nopen, nclose, nputs, and ngets functions are direct analogues of the Fortran OPEN, CLOSE, WRITE, and READ statements. The Fortran and C API functions are identical except that ngets can be used to obtain the C control characters, which is clearly superfluous in C. The API uses only ANSI C types that are consistent with Fortran types: a C (char *) is a Fortran CHARACTER*1 array, and a C (int) is a Fortran INTEGER, which is a 32-bit integer if one is using an adult computer. The representation of an I/O identifier for the POO software is a void * pointer in C and a 32-bit INTEGER in Fortran. If you have the knowledge and experience required to write your application using the standard I/O capabilities of C or Fortran, you can write a POO application. The POO software provides a new set of functions for transmitting textual data that are generally consistent with the analogous functions in the language standards. Because one of the principal design objectives of the POO software is to enable you to write windowed applications that are portable among the supported windowing systems, the API must be independent of these systems. Based on the assumption that people are more portable than software, the user, rather than the application, is responsible for any necessary or desirable interactions with the windowing system. You should know how to run an application in the windowing system but need not know more about the windowing system. The POO software is written in ANSI standard C, but I am not aware of any C compiler that would verify this statement. C compilers appear to adopt the view that standards apply only to the aggregate of all information required for compilation, which is a reasonable perspective but not particularly informative since it includes the contents of all include files, e.g., the windowing systems. On the other hand, most people writing an application are interested in whether their code satisfies the standard since they have little or no control over the required include files, which likely change when porting the software anyway. Thus, the POO software may not actually satisfy the ANSI standard. The POO software cannot be compiled using a C compiler based on Kernighan and Ritchie because such a compiler will balk on the type declarations for the arguments contained in the declaration of each function; in this case, the diagnostics offered by such compilers are generally wholly unrelated to the actual problem. If you normally write your applications in Fortran, you will have to become adept at writing mixed-language applications. Within the environment created by your system and compiler, for example, you may have to deal explicitly with some library and COMMON issues. Fortunately, this is a problem that you will likely need to resolve only once. One might assume, quite reasonably, that Fortran and C compilers from the same vendor would make the resolution of any problems easier. Some Fortran compilers are actually Fortran to C translators, which usually simplifies mixed-language applications. For example, when using the Linux Fortran compiler f77, you must change the POO source by appending an underscore (_) to 'appl' and to the names of the Fortran API functions and then, when writing your Fortran code, forget that you did so. A POO application is always 'windowed' and usually 'multithreaded' so that both of the these options must be specified when building a POO application. How one specifies these options depends on both the system and compiler. When using X in a Unix system, both of these options are generally specified as library options, e.g., "-lX11" and "-lpthreads." In Windows and OS/2, they are specified just like other options but may have low visibility: you may have trouble figuring out how to set them. Additional information on these options is provided in the file that contains the example applications. From the system's perspective, the main program, e.g., 'main', for all POO applications is contained in the POO software so that all POO applications are C applications, at least technically. This tactic avoids a portability issue in that some of the supported windowing systems do not start execution with the function named 'main'. In any case, the code that you write for your application starts with a function named 'appl' so that, in all supported systems, a POO C application starts with int appl (void) and a POO Fortran application starts with INTEGER FUNCTION appl (). In addition to resolving a portability issue, this tactic allows the POO software to gain control when your application returns so that your application can be terminated gracefully. In particular, if the 'appl' function returns, the POO software gains control and simply services your application's windows until terminated by the user, which gives the user an opportunity to browse and to direct output streams to files. The initialization/termination API function, cappl or nappl, serves both of these purposes. This API function provides initialization by loading the memory file when you provide the name you select for your application but terminates your application when the string argument does not start with an alphanumeric character. The first is recommended but not required; otherwise, when you try to create the first window, the application name is set to "Other" and memory file updating is set off. Explicit termination is not recommended except when the application detects errors; termination should be a user prerogative. The only essential use of the application name is to construct the name of the memory file, which contains the last piece of your application, the piece that languages and systems do not support, permanent dynamic data storage. It is used to retain function key string assignments and window attributes between invocations. Thus, the real name of your application, rather than what it is being called, is fairly critical, and only your application is viewed as a trustworthy agent. In some respects, the supported windowing systems assume that they know an application's name; fortunately, they do not use it for anything significant. In X, it's nominally argv[0], but there is nothing sacrosanct about the argv[0] string: one can rename the file, make a copy with a different name, or enter redundant path components when invoking the application. In both Windows and OS/2, the name available from the windowing system appears to depend on how the application is invoked. In general, the names of your applications should be unique so that each has its own memory file. Likewise, because window attributes saved in the memory file are qualified by the window name, these names should also be unique. Although unique names are suggested, they are not required. Indeed, one alternative is to let the name of all of your applications default to "Other" and use window names that are unique with respect to all of your applications or unique with respect to a class of windows that you commonly use. The only drawback of this strategy is related to the updating of the memory file. Like X, the POO software establishes a framework rather than a set of rules that must be followed. Both ANSI C and Fortran contain functions named 'exit', neither of which are connected in any way to the POO software nor necessarily to each other. You should not use either of these functions in your application. Although these exit functions will likely terminate your application, neither will do so with style or grace. The ANSI C standard specifies that a return in main is "equivalent to calling the 'exit' function with the value returned by the 'main' function as its argument." In a POO application, this statement is not correct. Calling exit will have a very different effect than a return in your 'appl' function. If you return to the POO main program, the POO software is more considerate of the user than the 'exit' function. If your application detects an error that requires that it be terminated before anything else can go wrong, use cappl(NULL) in C and nappl(0) in Fortran. It would be worthwhile trying to open a new window with a message, but this may not be possible. The user is provided numerous mechanisms to terminate an application, and there is no reason to usurp the user's prerogative unless your application is totally lost. Termination by the POO software, whether instigated by your application or its user, honors output direction established by the user, updates the memory file if appropriate, and then gracefully closes the windows associated with your application. Until your application opens a window, it is beyond the control of the user. In general, therefore, your application should create its first window soon: before initiating any activity that could be nonterminating would be a good practice. Unless you change the POO software, your application will run multithreaded so that, once a window exists, the POO software receives key and button events for any window associated with your application. Thus the user can terminate the application at any time by pressing Ctrl+D. The POO software automatically terminates a POO application when the last window associated with your application is closed. In short, after your application creates its first window, the user, you, can control it. The remaining API functions provide for opening and closing POO I/O streams, for appending data to an output stream, and for acquiring data from an input stream. The names of the C API functions are copen, cclose, cputs, and cgets. These functions are analogues of the ANSI functions fopen, fclose, fputs, and fgets, respectively. The names of the Fortran API functions are nopen, nclose, nputs, and ngets. These functions are analogous to the Fortran OPEN, CLOSE, WRITE, and READ statements with the caveat that WRITE and READ are restricted to transferring character strings. If you are familiar with both C and Fortran, you likely suspect that the C and Fortran API functions differ because C uses stream I/O and Fortran has always used record oriented I/O. In fact, the C and Fortran API functions are identical with one caveat: ngets returns the C control characters when passed a zero I/O identifier, which cgets would regard as invalid. Thus, the C and Fortran API functions support both character and record oriented I/O so that you can use either or both character and record oriented I/O in your application regardless of which language you use. The desired orientation is established when you open an I/O stream, the LINE modifier. The orientation of one I/O stream is independent of that of your other I/O streams. It is not possible to do both character and record oriented I/O with the same I/O stream; you cannot switch the orientation of an open stream. The POO open function creates a named window and a VM file for the output stream associated with the window, while the close function closes the window and deletes the VM file associated with it. The put function appends text to the output stream and normally updates the window to show the most recent output. The get function returns text from the input stream associated with a window and updates the window only if the input must be echoed to the output stream, which is not the default. Windows inherently provide both an output and an input stream, and no restrictions are imposed on others, the windows of type 'other' that are created by the open function. Your application can also open pollsters and onlookers. Although the user is free to do anything with these pollsters and onlookers, the only thing your application can do with them is close them. The POO put function includes the length of the output string as an optional argument with the proviso that the string must be null terminated if the length is not positive, while the ANSI function fputs requires a null terminated string. Thus, cputs(*,string,0) is analogous to fputs(string,*) and returns a similar value. Under appropriate circumstances, i = nputs(*,string,0) is analogous to WRITE (*,format) string, where the format consists of appropriate A descriptors. The put function processes an initial substring of C control characters and ignores embedded \r's, which allows you to ring bells, backspace characters or records, empty the VM file, and exert some control over the window updating. The initial substring processing can be turned off but should not cause problems because these control characters are legacies from a now, nonexistent environment and rarely used. Accepting \r\n for a \n in the text provided by your application should not cause a problem because people are rarely verbose when programming. The POO get function returns the number of characters returned in the buffer rather than the buffer pointer, and the argument order differs, but it is functionally consistent with the ANSI function fgets. Thus, cgets(*,buffer,len) is roughy an fgets(buffer,len,*), and i = ngets(*,buffer,len) is similar to a READ (*,format) buffer that uses an A descriptor. There are some minor incompatibilities between POO and ANSI input streams. First, the POO get function does not process C control characters. Second, it treats \r\n sequences the same as a \n so that it is possible to create a file that would produce different input streams when read using the POO get function and fgets. On the other hand, when a file is read using the POO get function, line termination is transparent so that the file can be in either Unix or PC format. In Unix, text files have a \n at the end of each line; while, in Windows and OS/2, each line is terminated by a \r\n sequence. The POO software replaces only the transmission function of the language I/O facilities, and it is assumed that you will use the standard I/O formatting capabilities of the language to produce output text and to convert input text. Because they are much more convenient when programming, you probably use the integrated I/O capabilities that combine the formatting and transmission functions in a single function or statement. In C, printf or fprintf calls are more common than sprintf calls followed by an fputs call. In Fortran, many people are not even aware of the language support for separating the formatting and transmission steps. Separating the formatting and transmission functions is inconvenient from the programmer's perspective, but tinkering with the standard libraries would have precluded portability. The noncommittal I/O models for C and Fortran that are outlined at the end of this section provide some programming techniques for simplifying the coding required and address the portability issue, namely, writing code that can target either the language standards or the POO software with minimal changes. In C, this is relatively straightforward using simple macros. In Fortran, the situation is less satisfactory, but the structure of the Fortran I/O statements can be exploited to alleviate much of the work needed to re-target an application. The brute force approach, of course, can be used in either C or Fortran. In C, the brute force approach requires that you replace each call to printf or fprintf with a call to sprintf followed by a call to cputs and each call to scanf or fscanf with a call to cgets and then a call to sscanf. The sprintf function provides the same formatting capabilities as printf/fprintf but places the output in a buffer and returns the length of the output, which happens to be precisely the length required by cputs. Similarly, the sscanf function provides the formatting capabilities of scanf. Although quite satisfactory for output, this approach changes the basic behavior for input. Both scanf and fscanf require that the user enter values for all variables in the I/O list: they continue reading input until enough values are obtained from the input stream. But, after cgets or fgets, sscanf converts and stores only the values contained in the input obtained by cgets. In summary, input loses the stream quality of C. In Fortran, the changes are analogous: each WRITE with a unit becomes a WRITE with a buffer followed by a call to nputs, and each READ with a unit becomes a call to ngets followed by a READ with a buffer. Most people write Fortran I/O statements using positional parameters for the unit and format, e.g., WRITE (unit,'(...)'), but the language also allows the positional parameters to be a buffer and format, e.g., WRITE (buffer,'(...)'). The similarity between these two is striking and justifies my describing them using the phrases "with a unit" and "with a buffer." In effect, the Fortran language allows one to convert all I/O statements in a program by changing the type of a single variable. From the perspective of the user, the resulting behavior is consistent with Fortran record oriented I/O for both output and input if the LINE modifier is specified when the I/O stream is opened. In Fortran, handling the EOF and ERR parameters adds some minor complexity. In general, both the EOF and ERR returns can be triggered by issues arising from either the transmission or the formatting. But, when using the POO software, transmission errors must be detected by looking at the values returned by the POO functions. Null terminated strings are routinely produced in C so that the length argument for the put function is extraneous; on the other hand, if the length is known, you might as well provide it to save the function the trouble of computing it. Null terminated strings, however, are not common in Fortran, and the length of formatted text is somewhat elusive. Further, in a Fortran format that is used to produce an output line in a buffer, a slash does not terminate a line; for this purpose, the C new line character is needed. Indeed, these are the basic reasons that ngets can be used to obtain the C control characters. Both the new line and null character can be used in conjunction with an A1 format to produce new lines and null terminated strings using standard Fortran formats. String arguments to API functions need not be null terminated, which avoids a lot of duplicate code when one is trying to serve both the C and Fortran environments. If the API function is expecting the application or window name, the name is taken as the longest initial substring that satisfies the appropriate criterion. This tactic has two side effects. First, it automatically enforces the restrictions placed on the characters allowed in such names; and second, it allows such names to be terminated by any character that doesn't satisfy the criterion, which obviates the need for null terminated strings in a very natural way. Case is always significant; no case conversions are performed on string arguments to API functions except that path names may be converted to upper case if the file system does not distinguish case in file names. THE INITIALIZATION/TERMINATION FUNCTION This API function, cappl or nappl, has two roles: to initialize your application by loading the memory file and to terminate it gracefully. The single argument to this function is a character string, the name you select for your application, and the action taken depends on whether the first character of this string is or is not alphanumeric. Because the argument is a character string, there is no technical reason for having distinct C and Fortran versions. Two versions are provided because there is at least one system, namely, Linux, in which the names of the Fortran API functions must be modified by appending an underscore (_). By providing both cappl and nappl, this kind of change can be made without impacting the C API. Initialization If the first character of the string argument is alphanumeric and the memory file has not been loaded, this function performs the following initialization: 1) the application name is set to the initial alphanumeric substring of the argument, 2) the memory file name is constructed by appending ".mem" to the application name, 3) the memory file is loaded or created based on the memory path table, and 4) the memory file is scanned for initialization commands. Zero is returned if a suitable memory file is successfully loaded. If this function returns -1, there is no virtual memory available so that your application stands little chance of success, but it is not terminated. If the memory file has been loaded, this function takes no action and returns the number of windows that have been opened by the POO software since the application started. Because the user may have opened numerous windows, the return value may exceed the number of windows opened by your application. The user may have closed some or all of the windows opened by your application. The application names "Pollster" and "Onlooker" are reserved. In both cases, the argument string should be of the form {Pollster | Onlooker}[:]{null | blank | ;}, where the file name may be omitted if the defaults, "poll" and "." respectively, are desired. These two applications are interactive and always run single threaded. For Pollster, a pollster is created for the specified file, which must look like a poll file. For Onlooker, an onlooker is created for each open file noted in the memory file when Onlooker last terminated; an onlooker is opened for the specified file/directory only if no files are noted in the memory file. Usually, the title associated with a pollster or onlooker is the application name followed by either "Pollster" or "Onlooker", respectively, and the window's SSN; but there will be no Pollster Pollsters or Onlooker Onlookers even though Pollster Onlookers and Onlooker Pollsters are possible. The name of your application is parsed from the string argument as the initial substring of at most eight (8) alphanumeric characters. The length limit is imposed so that the name of the memory file can be constructed with impunity by adding a ".mem" suffix, a necessary concession to portability. The POO software uses the memory file to retain window attributes and function key string assignments between invocations. The user can insert additional commands in the memory file, e.g., to direct an I/O stream. Unique application names are recommended so that the corresponding memory files are distinct. Likewise, unique window names are recommended because the window attribute information in the memory file is qualified by the window name. Neither recommendation is enforced. Normally, the copy of the memory file in the file system is updated whenever your application is terminated by the POO software; memory file updating can be controlled by the user. Aside from the initialization stemming from the memory file, this function performs no essential role in initializing the software. The POO software would run without a memory file, and you are not required to call cappl/nappl. On the other hand, without a memory file, the user is really quite hampered. Consequently, if you call the open function and the memory file is not loaded, then the open function issues a cappl("Other") before opening the window; and the open fails if cappl fails. In this case, memory file updating is set off; normally, it is on. Thus, if you do not provide a name, the name "Other" is used so that all unnamed applications share the same memory file. Termination If the first character of the string argument is not alphanumeric, your application is terminated gracefully: established output direction is handled, the memory file is updated as appropriate, and all windows associated with your application are closed. If an application is terminated in this manner, it is immediate so that the user does not have an opportunity to browse the output streams or to establish output direction for them. In effect, issuing a cappl("") or nappl(0) is much like calling the standard 'exit' function. The exit function should not be called in a POO application. The C and Fortran exit functions are not connected to the POO software and are likely unrelated to each other. It would be convenient to use the exit/atexit capability of C, but there is no analogue of atexit in Fortran. The exit function is a necessary casualty of supporting both C and Fortran and, in some instances, using the windowing system. Its replacement is cappl("") or nappl(0), which terminates the application immediately but handles output direction requests and updates the memory file if appropriate. As with the exit function, the user gets no notification; all of the windows associated with the application simply disappear. This method of termination is provided for situations in which an error has been detected and continuation is ill-advised. Because the user can terminate your application at any time with a single keystroke, there is little reason to use this function on the user's behalf. For example, you could terminate in this manner if the user responds to an input request with an EOF (Ctrl+Z), but the user could have responded with a Ctrl+D. In general, considerably more functionality is provided to the user than your application so that usurping the user's prerogatives is restrictive rather than helpful. Because the POO software terminates any application when the last window associated with it is closed, you could try to terminate by closing each of the windows created by your application. But the user may have opened additional windows, which are also associated with your application, so that you cannot be certain that closing your windows would actually cause termination. THE OPEN FUNCTION The open function, copen or nopen, takes three arguments: (1) a string of the form ":;" (2) the initial size in bytes of the VM file to be created for retaining the output stream, and (3) the extra size in bytes. The open function creates a window and an associated VM file for an output stream and returns the I/O identifier that is required as the first argument to the other API functions. If unsuccessful, NULL (zero) is returned. A POO identifier is a void * pointer in C and a 32-bit integer in Fortran. The open modifiers at the beginning of the first argument string must be of the form "@...." Each modifier consists of a single letter followed by an at-sign (@). Blanks after the last modifier are ignored. The legal modifiers are ECHO, LINE, CLOSE, and STET, based on the letters E, L, C and X, respectively. The modifiers are described below. The window name may consist of any characters other than those implied by the syntax, but alphanumeric characters are recommended and neither a period nor an asterisk should be used since they will cause problems in the memory file. The name is truncated to 15 characters if necessary. If the colon is not found, "Other" is used for the window name. The window names "Pollster" and "Onlooker" are reserved. If you use either as the window name, the window title is interpreted as a file name, and a pollster or onlooker, respectively, is opened for the specified file. When opening a pollster, the file must look like a poll file if it exists and is created if it does not exist. When opening an onlooker, the file or directory must exist. When opening a pollster or onlooker, the last two arguments are ignored, and the I/O identifier returned cannot be used to call the put or get functions. Unique window names are strongly recommended because they are used to identify applicable window attributes in the memory file, but there are circumstances in which duplicate window names may be convenient. Because the window name determines the attributes selected from the memory file, you can open a new window with the same attributes as an existing one. For example, if the memory file contains an explicit geometry for the window, the new window will overlay the existing one, and you can use the new window to obtain input, then destroy the new window, and explicitly echo the input in the old window. This is a handy mechanism when you want to provide a list of options to the user: the user can employ the line re-entry feature to select the desired option. The window title starts with the first character after the optional modifiers or the colon, and the semicolon is optional if the string is terminated by a null character. The title appears in the title bar and is not used otherwise. The title Other is used if none is provided. The open function creates a VM file to retain the output stream so that the output stream can be scrolled. The VM file is allocated at least the number of bytes specified by the initial size but not less than 2048 bytes. If additional space is required, the extra size is used: if positive, at least [extra size] additional bytes are allocated; while if negative, at least [extra size] bytes of the oldest data in the VM file are discarded. Here, [extra size] denotes the absolute value of the extra size argument rounded up to the nearest kilobyte but limited to half the size of the initial VM file. The sign of the extra size argument determines the buffer management policy: expansion if positive and truncation otherwise. If you select expansion and expansion of the VM file fails, the strategy is changed to truncation quietly, i.e., the software copes with the situation. When a file is truncated, the truncation point is adjusted to a line boundary. There are a number of considerations involved in selecting the size values and the buffer management policy. The magnitudes of the two size values impact primarily the efficiency of the software, while the buffer management policy also impacts how much of the output stream the user can view at any given time. The size values should exceed the length of the longest output string that will be passed to the put function, but this is solely an efficiency issue. For the same reason, the initial and extra sizes should be related to the anticipated size of the output stream: using a small VM file for a small output stream and a large VM file for a large output stream seem obvious. The ramifications of selecting truncation rather than expansion are related to the consumption of virtual memory and output direction. Truncation clearly limits the amount of virtual memory needed for the VM file, the initial size specified in the open. At the same time, the extra size determines the amount of output that the user can view at any time. Truncation is useful principally with a large output stream in which the user will probably have interest only in the most recent output, where "recent" is determined by the two size values and the output stream. Truncation may also be useful in connection with a small output stream that is rewound by your application because each rewind empties the VM file. The buffer management strategy impacts output direction because output is appended to the file to which it is directed only if it is about to be discarded: (1) if the VM file must be truncated to make room for new data, (2) if the VM file is rewound by the application, or (3) if the VM file is destroyed because the window is closed. When a user establishes output direction for an output stream, no output operations are actually performed; rather, the POO software simply notes the user's request so that, when data is about to be discarded, it can be appended to the file established by the user. Thus, when used in conjunction with a VM file managed by expansion, the output may not be written to the file until the application terminates. This implementation strategy cannot be altered by your application. Although it involves some additional effort, the user can manually save part or all of an output stream in a disk file at any time and can manually insert output direction commands in the memory file. The ECHO Modifier By default, input is not echoed to the output stream even through this is commonly done in command windows, e.g., xterms and DOS windows. If the ECHO modifier is specified, input is echoed to the output stream when and as it is returned to the application. You should take into account the "when and as" part of this statement because this is not consistent with the input echoing normally provided in command windows, which echo the entire input line when it is entered. If get is provided a small input buffer or the user is overly verbose, multiple calls may be required to obtain the entire input line. Since the input is echoed only "when and as" it is actually returned, interspersing put calls with these get calls may create a mess. The interactions between the ECHO and LINE modifiers are discussed in conjunction with the LINE modifier. The LINE Modifier By default, the put and get functions are consistent with ANSI C stream I/O, the descriptions of the ANSI C functions fputs and fgets in section 7.9.7. The LINE modifier makes the put and get functions consistent with record-oriented I/O and is intended primarily for Fortran. By providing this capability in this manner, you can use either or both I/O mechanisms from either C or Fortran. In the put function, if the last character of your text is not a new line character, one is appended to the output stream after your text. In effect, this treatment provides the line termination normally associated with the end of a Fortran format. The treatment of new line characters embedded in your text is not impacted by the LINE modifier. In the get function, input that will not fit in the buffer provided by your application is discarded, and new line characters are never returned. Normally, input that will not fit in your buffer is retained until your next get request and new line characters are returned. In Fortran, the format used to convert an input record cannot be longer than the record, and characters not referenced by the format are discarded. If you provide the get function with a buffer that is longer than your Fortran format, the truncation of the input records provided by the LINE modifier mimics Fortran input. The LINE and ECHO modifiers interact. If you select both, the LINE modifier causes excess input to be discarded so that the "when and as" condition of the ECHO modifier is moot. Further, a new line character is appended to the input when it is echoed by the get function even though it is not returned by this function. The CLOSE Modifier The CLOSE modifier is somewhat of a misnomer because, if specified, it prevents the user from closing the window. In the X version, this modifier only prevents the user from closing the window by pressing Esc or using the CLOSE command; it does not prevent an X window manager from closing the window. In the Windows and OS/2 versions, even the window manager is prevented from closing the window. This modifier is a bit user unfriendly but may be appropriate in some situations since the VM file for the output stream is deleted if the window is closed. If the user closes a window, then all subsequent put and get calls return -2 because the I/O identifier is no longer valid. The STET Modifier By default, the put function processes any initial substring of C control characters in your text and then appends the remainder of your text to the output stream. To a great extent, the C control character definitions in the ANSI standard are a legacy from the era of teletypes. Because the processing accorded these characters has been modified to serve the windowed environment supported by the POO software, the STET modifier is provided so that you can disable the processing of the C control characters. If the STET modifier is specified, the text you pass to the put function is not examined for C control characters. THE CLOSE FUNCTION The close function, cclose or nclose, closes the window identified by its only argument, a POO I/O identifier, and returns 0 (zero) if successful and -2 otherwise, e.g., the I/O identifier is invalid. If the user has already closed the window, the I/O identifier will be invalid so that a return of -2 should not necessarily be viewed as an error. As usual, when the window is closed, the VM file associated with it is destroyed provided that it is not being used otherwise. If the user directed the output to a file, the file is updated before the VM file used to retain the output stream is deleted. THE PUT FUNCTION The put function, cputs or nputs, takes three arguments: (1) an I/O identifier for an other, (2) a text pointer, and (3) the length of the text. The put function appends your text to the output stream associated with the identified window, updates the window to show the end of the output stream unless requested otherwise, and returns a non- negative integer if successful or -2 if the identifier is invalid or corresponds to a pollster or onlooker. The put function is consistent with the C function fputs, which is described in section 7.9.7.4 of the ANSI standard. The length is optional: if your text is a null terminated string, a nonpositive value (zero) may be given for the length. Thus, for example, cputs(*,text,0) provides the same functionality as fputs(text,*), where the asterisk corresponds to the appropriate identifier. If the length is available, you might as well provide it and save the cost of computing it. The C function sprintf, which is often used to format the text passed to cputs, returns the length of the text that it constructs. Your text may consist of multiple lines of output separated by either a \n or by the two-character sequence \r\n; the line termination within your text need not be consistent. When using a Fortran WRITE statement to format output text into a buffer, a slash cannot be used to terminate a line. When using the nputs function, you must either format and transmit each line, or you must replace each slash by an A1 format descriptor and add to the I/O list an appropriate CHARACTER*1 variable that is set to the value of a C new line character. In Fortran, the end of a format terminates an output line implicitly and does not generate a \n, a C new line character. The LINE modifier is provided to handle this implicit new line character: if you specify the LINE modifier when you open a stream, each call to nputs appends a new line character to the output stream after your text if your text does not end with a new line. Text output streams are held in VM files using the line termination format of the underlying file system: \n in X (Unix) or \r\n in Windows and OS/2. Nevertheless, you can use either a \n or the two character sequence \r\n within your text to generate a new line character. If the LINE modifier is set, a new line is appended to the output stream after your text if and only if your text does not end with a new line character; otherwise, the LINE modifier has no effect on this function. Normally, after appending your text to the VM file, the window is updated to show the last line of the VM file at the bottom of the window. You can change the normal updating by using the C control characters \f or \v (see below). The user can suspend automatic updating of the window by requesting STATIC operation, which is toggled by F6. If STATIC is toggled off, the window is always updated to show the end of the output stream. By calling with a NULL data pointer or a pointer to a null string and nonpositive length, the put function can be used to update the window and to determine whether the user has requested STATIC operation. The window is updated to show the end of the output stream if the length argument is nonzero, i.e., negative. Thus, regardless of whether the user has requested STATIC operation, you can ensure that the user can see the end of the output stream. The return is normally -1 or 0 depending on whether the user has or has not requested STATIC operation, respectively, but -2 is possible if the window has been closed. Thus, cputs(*,"",-1) or nputs(*,0,-1) update the window and obtain its status, while cputs(*,"",0) or nputs(*,0,0) just obtain its status. Control Character Processing When appending text to an output stream, any initial substring of the ANSI C control characters \a, \r if not immediately followed by a \n, \b, \f, \v, and \0 is processed; any character other than one of these characters terminates the initial substring and is the first character appended to the VM file. Because this processing engenders incompatibilities between POO and ANSI output streams, the STET modifier is provided so that you can disable this feature. In general, the descriptions of these control characters in the ANSI standard (5.2.2) are legacies from the era of teletypes. Thus, it is implicit in the definitions of \b and \r that once a line has been terminated it cannot be revisited. These control characters "are intended to produce actions on display devices..." according to the standard and are processed by the POO software in this spirit. The "active position" is the location on a display device where the next output character is displayed; the phrases enclosed in quotes are from the ANSI C standard. \a "produces an audible...alert." \r "moves the active position to the initial position of the current line" or deletes the previous line. \b "moves the active position to the previous position on the current line" or deletes the previous character. \f updates the window to show the text being appended at the top of the window. \v skips the automatic window update. \0 empties the VM file used to retain the output stream. If embedded in the text, these control characters are transmitted unchanged except that \r's are deleted. In all situations, a \n generates line termination appropriate for the VM file. If you want to empty the VM file, you must specify the length of your text explicitly; otherwise, this function assumes that your text is null terminated and computes the length, which would not produce the desired result in this situation. The implementations of \b and \r are not compatible with the ANSI standard and may not be faithful. One incompatibility arises if the "active position" is just after a new line character: the POO implementations of \b and \r move the active position backwards, whereas an ANSI compatible implementation would not move the active position. Second, the POO software regards the active position as the end of the output stream associated with the display device so that characters are actually deleted from the output stream when the active position is changed by a \b or \r. In the context of the teletype implicit in the ANSI standard, \b was and continues to be used to produce bold type. The POO implementations of \b and \r are not faithful in that the output stream viewable in the window may not be consistent with the file created by output direction. For example, if you transmit a lot of data to a VM file that is being managed by truncation and then try to delete the data with backspaces, the VM file and disk file may not be consistent. The potential inconsistency arises if truncation caused some of the data to be deleted from the VM file before it could be deleted with backspaces; the text truncated from the VM file will have been written to the file. Thus a mischievous application can elicit mischief. If used judiciously, the output stream and file should be consistent. Finally, because the context is not considered when \b and \r are implemented, your application may backspace over notes entered in the output stream by the user. In short, you may delete them inadvertently if you use \b or \r. There is no way to avoid this issue because the user is free to enter N.B.'s in an output stream at any time. Clearly, the processing of \f and \v, which are described as a form feed and a vertical tab, respectively, do not embody much of the ANSI spirit. On the other hand, they provide functionality that is very useful in the context of windowed text. For example, within a loop you may want to be able to watch the value of one or more variables without retaining the whole sequence of output lines in the output stream. Placing a \r at the beginning the line will produce the desired result, but the window updates will be costly; placing \f\r at the beginning has the same effect but reduces the update costs because less text must be drawn. The primary reason for using a \v is to save the cost of updating the window. Thus, when writing a long sequence of related lines, you might choose to place a \v at the beginning of each line except the last. Depending on the capabilities of your system, the window updating may be time consuming. As the writer of an application, you can ameliorate the situation by writing multiple lines in a single call or by using \v's appropriately. As the user of a POO application, you can press F6, which toggles the STATIC flag: when this flag is set, normal window updating is skipped. THE GET FUNCTION The get function, cgets or ngets, takes three arguments: (1) an I/O identifier for an other, (2) a buffer pointer, and (3) the length of the buffer and returns the number of characters placed in the buffer, -1 if the user responded with an EOF, or -2 if the I/O identifier is invalid or corresponds to a pollster or onlooker. If a negative value is returned and the buffer exists, the null string (0) is returned in the buffer. Normally, the get function is consistent with ANSI C stream input, see the description of the fgets function in section 7.9.7.2 of the ANSI C standard. Consequently, this function returns at most (length - 1) characters from the input stream up to the next new line character and appends a null character. If the input stream contains more than (length - 1) characters before the next new line character, the remainder of the line is retained in the stream and accessed the next time this function is called. Thus, for example, you can get one character at a time by specifying a buffer length of 2, which only provides space for the next input character and the terminating null character. The get function, however, is not entirely compatible with the ANSI C function fgets when the input is being obtained from a file. The incompatibilities arise because this function tries to deal with text files in either Unix or PC format. In Windows and OS/2, a new line character is represented in a file as a \r\n sequence; while, in Unix, it is represented in a file by a \n. In general, the Unix version of fgets cannot deal with PC text files, and the PC version of fgets cannot deal with Unix text files. The POO get function tries to handle both file formats by treating each occurrence of a \n or \r\n sequence as a new line character. Consequently, the get function can read files in either Unix or PC format. Otherwise, POO and ANSI input streams are compatible unless you specify the LINE modifier when you open the I/O stream. If you specify the LINE modifier when you open the I/O stream, the get function returns the next input record. If the LINE modifier is set, the get function returns at most (length - 1) characters from the input stream up to, but not including, the next new line character and appends a null character. In this case, the get function returns up to (length - 1) characters followed by a null character and discards the remainder of the input line, if any. Because new line characters are not returned, the return value is zero if the user responds by simply pressing Enter. Strictly speaking, the LINE modifier is not consistent with Fortran record-oriented input. When running Fortran applications, the user is generally responsible for providing, implicitly or explicitly, the maximum input record length to the underlying I/O system and Fortran I/O library, and it is the format used to process the input that effectively truncates the record. If you provide a buffer consistent with the Fortran format that you use to process the input, the net effect will mimic Fortran record-oriented input. Although the LINE modifier is intended for Fortran applications, you can clearly omit the LINE modifier and do C stream input in Fortran, or you can insert the LINE modifier in C and do Fortran record-oriented input. The POO software treats an EOF as a signal rather than a condition. In general, whether in C or Fortran, the mechanism for associating an input stream and a disk file is quite primitive, and it is natural to regard the end of a file as a condition because there is no mechanism for continuing an input stream after the end of the file is encountered. But, when obtaining input through a window, there is no inherent end because additional input is possible as long as the window exists. If your application solicits input by calling the get function and the user closes the window, a single EOF return is provided; subsequently, the get function returns -2. The POO software does not distinguish between a window that was never opened and one that has been closed. Because the POO software provides the user multiple mechanisms for dynamically managing the input stream associated with a window, what constitutes a file is a matter of perspective. For example, although your application may open an input stream, read it until an EOF is received, and then close it, this does not restrict the user to providing an input stream that consists of a single file. The mechanisms for associating input with a window are transparent to your application, and the user can construct a single file from various portions of multiple files punctuated by input entered through the window. Thus, the POO user interface may simplify the design of your application. How the get function obtains the input that it returns in your buffer is transparent to your application. The user may have directed input from a file or pasted input into the input stream, the user may have entered typeahead input, there may be residual input from a previous get, or the user may be solicited to enter input through the window. If the user is actually solicited for input through the window, the window will be raised and updated if necessary. Unless input is obtained through the window, the get function updates the window only if the ECHO modifier is set. If the ECHO modifier is set, the characters returned in the buffer are appended to the output stream associated with the window. You should note that this statement implies that input appears in the output stream only when and as it is actually returned to your application. If you use multiple get calls to obtain an input line, interspersing these gets with puts may produce a mess or, if you are very clever, exactly what you want. Using an input buffer of length 2 with the ECHO modifier can be quite tiresome since each character then requires that the window be updated. If the LINE modifier is set, a \n is appended to the output stream after the characters returned in the buffer even though it is not returned in the buffer. Because input is echoed to the output stream only when and as it is returned to your application, any part of an input line that is discarded because the LINE modifier is set never appears in the output stream even though it was read from the file or through the window. Both cgets and ngets provide functionality in ostensibly erroneous situations. First, if the buffer is the NULL pointer or if the length is less than 2, they return -1 if the window exists and -2 if not. Thus, like the put function, you can use the get function to determine whether the user has closed the window. Second, ngets(0,*,8) places the character string '\t\a\f\v\b\r\n\0' in the buffer and returns 8, which provides a system-independent mechanism for obtaining the seven C control characters and the C null character. The C control characters can be exploited in Fortran by placing them in CHARACTER*1 variables and using them with A1 format descriptors. A NONCOMMITTAL C I/O MODEL In the example applications, the API functions are generally called explicitly, which is likely how you use the ANSI I/O functions printf and scanf. The noncommittal model outlined here is used in the Random, NICMap, and NEigen examples. The principal advantage of this model is that it vests in code only the format and I/O list and leaves the transmission issues to be resolved by macros, which can be easily changed. In this respect, this model could also be described as portable but is more aptly termed noncommittal. ---------------------------------------------------------------- #define BFO cputs(id,buf,sprintf(buf, #define EFO )); #define BFI cgets(id,buf,buflen); sscanf(buf, #define EFI ); #define BFO fprintf(id, #define EFO ); #define BFI fgets(buf,buflen,id); sscanf(buf, #define EFI ); ---------------------------------------------------------------- Figure 3: Illustrative definitions of macros for a noncommittal I/O model. The noncommittal I/O model is based on macros such as those illustrated in Figure 3. These definitions assume that the I/O buffer named 'buf' of length 'buflen' is defined and that the variable 'id' is appropriately defined. When using C stream I/O, 'id' has the type FILE * and is set by calling fopen; when using the POO software, it has type void * and is set by calling copen. Implicitly, these illustrations assume that only one I/O stream is required. For multiple I/O streams, there are a couple options. First, the macros can be replicated with different names for the I/O identifier, id. Second, if you are confident that all of the I/O environments of interest can be described in terms of a single parameter, you can incorporate the parameter in the macros and define BFO(id) and BFI(id). Before discussing some potentially crucial issues concerning these macros, a few examples seem appropriate. If you choose to use these macros, the production of textual output always takes the form BFO format, list EFO, and the acquisition of input takes the form BFI format, list EFI. As when using the ANSI functions, scalars in an input list must be preceded by an ampersand (&) since you must pass a pointer rather than the value. In the Random example application, you will find statements like BFO "\v%11.0lf %8.3lf",cnt,t*clock() EFO and BFO "Oops, buckets only have %.0lf != %.0lf!\n",t,cnt EFO. Hopefully, if you run this example, you will not see any lines produced by the latter. If you use these macros, it is relatively easy to convert existing codes to use the POO software, and conversely. In particular, I would suggest a two step process. First, change your code to use the ANSI versions of the macros, which will allow you to test these changes in a familiar setting. Second, change to the POO versions of the macros and change the I/O identifier(s). If these latter changes are made using the preprocessor, you should be able to use the POO or ANSI macros by simply changing a defined constant or two. The output macros are quite satisfactory in that both the ANSI and POO versions produce the same output stream and apparent behavior. The input macros, however, are somewhat less satisfactory. First, the input macros ignore the end-of-file issue and other errors, but this issue can be easily resolved by appropriate changes in the macros. In this context, cgets is quite careful to return the null string in the buffer if EOF (-1) is returned. Thus, after an EOF return, sscanf does nothing and returns zero because the buffer contains the null string. Normally, sscanf returns the number of values converted and stored. The input macro, however, raises a second issue that is more fundamental. Both scanf and fscanf are stream oriented and continue reading input until values have been obtained for all variables in the I/O list, a feature that cannot be easily mimicked using the POO software. Indeed, even though sscanf returns the number of variables read, it is not straightforward to write a replacement macro or function for fscanf that obtains input using cgets. But I find this feature of scanf and fscanf irksome in any case. My orientation is that I would have entered a value for it if I wanted it changed and that when I press Enter, that's it. In short, I prefer the behavior commonly followed in interactive Fortran environments in which record-oriented I/O is the norm and values not specified are left unchanged. The example applications illustrate a number of techniques for handling input conversion but reflect my bias: if I didn't type it, don't change it. Indeed, the ill-conditioned eigenproblem example looks for commas (,) as separators when reading vectors so that the user can change some elements and leave others unchanged. In all of the examples, pressing Enter often enough will eventually lead to the default problem, which obviously requires that all variables be preset and that the input process leave variables unchanged when no explicit value is provided This is handy when you are running someone else's application for the first time and when you are running one of your own that you have not had occasion to use for some time because simply pressing Enter often enough allows you to observe the entire course of the application, a sort of tutorial. For much the same reason, it is also convenient when you are in the process of writing an application; but, in this situation, you might choose to use input direction, which could be inserted in the memory file if you use the POO software. A FORTRAN I/O MODEL A convenient model for writing POO applications in Fortran is somewhat more elusive and must overcome a number of minor issues. First, Fortran does not provide a mechanism for defining macros. Second, Fortran formatted output does not explicitly generate new line characters because Fortran I/O is record oriented. Thus, a slash (/) in a format usually causes an I/O operation rather than the generation of a character. Third, Fortran does not produce null terminated strings and the length of a string is not readily available. Fourth, Fortran does not know about the C control characters. Indeed, about the only feature of Fortran that can be exploited is the inherent structure of the Fortran WRITE and READ statements. Because there is no mechanism for associating a Fortran unit and a POO I/O stream, the integrated capabilities of the WRITE and READ statements cannot be used. Instead, they are used only for their formatting capability, while the POO API functions are called to transmit the data. Thus, the usual WRITE with a unit must be changed to a WRITE with a buffer followed by a call to nputs, while a the usual READ with a unit becomes a call to ngets followed by a READ with a buffer. The I/O buffer or buffers, of course, must be provided by you, e.g., a CHARACTER*200 variable. When READ and WRITE statements are used only for formatting, the ERR parameter corresponds only to formatting errors; transmission errors must be detected by looking at the returns from the POO API functions. Likewise, the EOF parameter in a READ statement may not apply because this is a transmission issue. If you use the ERR and EOF parameters, appropriate changes are clearly required. The structural similarities of WRITEs and READs with a unit and with a buffer should not be ignored because they allow the same code to be used for standard Fortran I/O and with the POO software, or any other transmission service. For example, I would normally write READ (unit, format,...) list for standard Fortran input from a unit but write k = ngets (wnd, buffer, 200) READ (buffer, format,...) list to use ngets for data transmission and the READ statement only for its formatting capability. The only real difference between these two READ statements, however, is the type of the first variable, which is either an INTEGER variable or constant or a CHARACTER variable of appropriate length. Thus, by changing the type of a single variable, the meaning of all WRITE and READ statements in a program can be changed. The data transmission can be handled by the POO API functions, some other transmission functions, or dummies that do nothing. ---------------------------------------------------------------- CHARACTER*1 TAB, BELL, TOP, UPD, BCHR, BKSP, NL, NUL CHARACTER EOL*2, UB*200 COMMON /POOS/ TAB, BELL, TOP, UPD, BCHR, BKSP, NL, NUL, EOL, UB .......... INTEGER FUNCTION appl () .......... k = ngets(0,TAB,8) EOL(1:1) = NL EOL(2:2) = NUL C Replacement for WRITE (UB,format,list). WRITE (UB, format + A2) list, EOL k = nputs (id, UB, 0) C Replacement for READ (UB,format,list,EOF=label). k = ngets (id, UB, 199) IF (k .LT. 0) GOTO label READ (UB, format) list ---------------------------------------------------------------- Figure 4: Basic structure for a POO Fortran application. Finally, you will want to be able to use C control characters, which are not intrinsic to the Fortran language. To avoid embedding dependencies on their representation in your Fortran code, you should use ngets to obtain them from the POO software. Technically, the code in Figure 4 may not work because it assumes that the eight single character variables occupy successive bytes in the common block. In any case, I think you will find it convenient to have some or all of the C control characters in CHARACTER*1 variables so that they can be used with an A1 format descriptor. Of course, these variables can be used directly, e.g., k = nputs(id,BELL,1). Although you can clearly use the LINE modifier to terminate output lines, the new line character is handy for creating output consisting of multiple lines, which cannot be done with a slash (/) in a format that is placing output in a buffer. A CHARACTER*2 variable consisting of a C new line character followed by a null character used in conjunction with an A2 format descriptor, however, is even more convenient than the individual characters. Specifically, by appending it to each I/O list and appending an A2 to each format, a WRITE can be used to create a line of textual output that is null terminated, which eliminates the need to deal with the length of the output text produced by a WRITE. Indeed, you might note that if you change this variable to contain two blanks, its inclusion has no material effect if you convert the WRITE to use a unit. With some Fortran compilers, you may find that a READ with a buffer requires a full buffer when using some formats: the implementation of the input conversion may look at all characters in the buffer regardless of the input format. You can resolve this problem by inserting code to add blanks to the buffer between the call to ngets and the READ statement, but this is not a very convenient solution. It is much easier to modify ngets so that it always pads your input with blanks and returns the number of characters specified by the third argument, the buffer length. This is not very elegant but quite effective. I would have provided a more finished solution, e.g., another modifier, but this issue arises only in some cases and there are no bits left to implement another modifier. REFERENCES Butenhof, David R. 1997. Programming with POSIX Threads. Reading, MA: Addison-Wesley. Harbison, Samuel P., and Steele, Guy L., Jr. 1991. C, A Reference Manual. Englewood Cliffs, NJ: Prentice-Hall. Kernighan, Brian W., and Ritchie, Dennis M. 1978. The C Programming Language. Englewood Cliffs, NJ: Prentice-Hall. Levine, Donald. 1991. POSIX Programmer's Guide. Sebastopol, CA: O'Reilly and Associates. The Linux Bible, Third Edition. 1995. San Jose, CA: Yggdrasil. Microsoft, Inc. 1993. Win32 Programmer's Reference, Volume 1. Redmond, WA: Microsoft Press. Microsoft, Inc. 1993. Win32 Programmer's Reference, Volume 2. Redmond, WA: Microsoft Press. Nichols, Bradford; Buttlar, Dick; and Farrell, Jackie. 1996. Pthreads Programming. Sebastopol, CA: O'Reilly and Associates. Nye, Adrian. 1990. Xlib Programming Manual, X Window System Series, Volume One. Sebastopol, CA: O'Reilly and Associates. Nye, Adrian., ed. 1990. Xlib Reference Manual, X Window System Series, Volume Two. Sebastopol, CA: O'Reilly and Associates. Panov, Kathleen; Salomon, Larry, Jr.; and Panov Arthur. 1995. The Art of OS/2 Warp Programming. NY: Wiley. Petzold, Charles. 1992. Programming Windows 3.1. Redmond, WA: Microsoft Press. Petzold, Charles. 1994. OS/2 Presentation Manager Programming. Emeryville, CA: Ziff-Davis Press. Schildt, Herbert. 1990. The Annotated ANSI C Standard. Berkeley, CA: Osborne McGraw-Hill. Schildt, Herbert. 1991. C++, The Complete Reference. Berkeley, CA: Osborne McGraw-Hill. The help facilities and online documentation provided with numerous C/C++ compilers and operating systems have been omitted as explicit references but, of course, were sources for critical information in many cases.