- Description of the terminal interface to the portable Infocom datafile interpreter.
#include <stdio.h>
#include "infocom.h"
extern gflags_t gflags;
extern char     *ti_location;
extern char     *ti_status;
const char *scr_usage;
const char *scr_long_usage;
const char *scr_opt_list;
int scr_cmdarg( int argc, char ***argv_p );
int scr_getopt( int c, const char *arg );
void scr_setup( int margin,
                int indent,
                int lines,
                int context );
void scr_begin();
void scr_putline( const char *buffer );
void scr_putscore();
void scr_putsound( int number,
                   int action,
                   int volume,
                   int argc );
void scr_putmesg( const char *buffer, Bool is_error );
int scr_getline( const char *prompt,
                 int length,
                 char *buffer );
void scr_window( int size );
void scr_set_win( int win );
FILE *scr_open_sf( int length, char *buffer, int type );
void scr_close_sf( const char *buffer, FILE *fp, int is_save );
void scr_end();
void scr_shutdown();
These routines form the interface between the portable Infocom datafile interpreter (pinfocom) and a particular type of terminal. The pinfocom package comes with interfaces to the following terminal types: This terminal type supports all(?) UNIX systems via an interface to either termcap or terminfo terminal capability and either termio, termios, or sgtty line discipline control. Optional support for the GNU readline line editing and history library is also available. This terminal type supports an Amiga interface, including sound and command line editing. See amiga.c for full details. This terminal type supports MS-DOS based systems. See msdos.c for full details. This terminal type should support any system with a C compiler; it uses simple C stdio routines that all C libraries will have. Nothing fancy, but it works. Also in the works is an X11-based interface. This man page describes the interface between the pinfo interpreter and the terminal support packages so that programmers can create their own interfaces to new systems. will be abbreviated to below. The interpreter operates in two modes: game-playing mode (default) and information mode (if any of the command-line options and/or are given), where the game is not played but information about it is instead displayed on the screen. All input and output in game-playing mode is done via the functions described below. Output during information mode is via simple C stdio library functions. The only interface between the interpreter and the terminal handling code is through the functions and global variables described below; no other symbols in the TI code should be externally visible and any other external symbols in the interpreter code are subject to change. Some general issues to be considered: The interpreter registers a signal handler for the SIGINT signal and sets to ignore the SIGQUIT signal (if defined). Any other signals or other asynchronous events are free to be handled by the TI as it sees fit. Note, however, that none of these handler routines may modify or access any interpreter variables: interpreter variables listed below are only guaranteed to be valid when one of the scr_*() functions below has been properly called from within the interpreter code. If the TI supports multiple fonts it is perfectly reasonable to use proportional-width fonts for printing general game text if you wish. However, at certain points in the game fixed-width fonts are required, such as when printing maps, etc. To accomodate this, before printing to the screen (see below) should invoke the macro If this macro returns non-0 then the current buffer should be printed in a fixed-width font; if it returns 0 then a proportional-width font may be used if desired. All Infocom games support the command, which is supposed to begin a transcript of the user's adventure. Normally this initialized the printer and all subsequent text and commands were printed to the printer as well as to the screen. The TI may support this feature as well. There's no Z-Code primitive for turning on and off scripting, however: instead a flag is set or reset. In and the macro call should be used; if it returns non-0 then the output line or prompt and command should be printed to the script file as well as the display. The interface will call with a file type of when a script file is to be opened and when a script file is to be closed. Only makes use of the fixed window feature in Standard Series games, but it's nice to have nevertheless. If the TI can support a fixed window in addition to the normal scrolling text window then should notify the interpreter by calling In this case the and functions described below will be used to create/manage/delete the fixed window. If the TI cannot support a fixed window then the above macro should not be called and and may be dummy functions. These global variables are defined in the interpreter code and may be used by the TI: Contains global flags and other general variables of interest. The only field which may be modified is the field, if the TI doesn't support status line printing. This nul-terminated string is a short description of the current location of the player, for printing on the status line. It is usually displayed on the left-hand side of the status line. This nul-terminated string contains the status of the player. Depending on the game this may be score, score and turn number, or the current time in a 12-hour clock. This string is usually printed on the right-hand side of the status line. These global variables should be defined in the TI .c file; they will be declared external where used in the interpreter code. The variable should be initialized to a string containing info about any extra command-line options that the TI makes available. Here's an example from the TI:
    const char *scr_usage = "[-H file] [-C file]";
If there are no extra command-line options the variable should be set to the empty string (""), NULL. The variable should be initialized to be a string containing a one-line per option description of any extra command-line options that the TI makes available. Here's an example from the TI:
    const char *scr_long_usage = "\
    \t-H file\tread/store command history in file\n\
    \t-C file\tread/store user command completions in file\n";
If there are no extra command-line options the variable should be set to NULL. The variable should be initialized to be a string containing getopt(3)-style descriptions of any extra command-line options that the TI makes available. Briefly, each option character is placed in the string; if the option has an argument (no optional arguments allowed) then it should be immediately followed by a colon (:). Here's an example from the TI:
    const char *scr_opt_list = "C:H:";
If there are no extra command-line options the variable should be set to the nul string (""). The following macros are defined in and available to the TI writer for access to certain data structures. The flags available with macros are: If set them time is displayed on the status line, otherwise score/moves is displayed. This is a readonly value; the TI cannot change it. The Tandy license flag; if set then Tandy licensing mode is turned on, otherwise it's turned off. The alternate prompt flag; if set then alternate prompting is enabled, otherwise it's disabled. If set the TI supports fixed windows (for if not set it doesn't. The flags available with macros are: If set then scripting mode is currently enabled, otherwise it's disabled. If set then the current buffer must be printed in a fixed-width font if possible; otherwise it may be printed in a proportional font. If set the interpreter may issue requests; otherwise it won't. Note this flag is readonly and may not be set by the TI. This macro returns a 0 if the specified game header flag is not set, and a non-0 (note necessarily 1!) if it is not set. This macro sets the specified game header flag. This macro resets (un-sets) the specified game header flag.

Interpreter Functions
The following functions are defined in the interpreter and available to the TI writer for use (no other functions in the interpreter should be called). This function calls and exits with an error if there is no heap left. This function calls and exits with an error if there is no heap left. Buffer to be chopped. Max number of chars wanted This function locates the longest section of less than or equal to characters containing at least one complete word. It does not modify the buffer. It returns a pointer to the space where the line should be broken, or to the nul character ('\0') if is less than or equal to chars long. This function is useful with fixed-width font TIs for chopping up the buffer passed to into sections which fit on the screen. See examples of its use in The following functions need to be provided by the TI code. Number of command-line args. Pointer to an array of strings; each one is a command-line argument. This function will be called before any other scr_*() function, and before the command-line arguments are examined by the interpreter. It allows the TI to add (and possibly remove) arguments to the command line. If the interface has the ability to determine command-line options from an alternate source, such as a configuration file, an environment variable, windowing resources, or saved game resources, then it should add these options to so they will be noticed by the interpreter. Note that when adding options they should be inserted between and shifting the existing options over, so that they will be properly overridden by any options given on the command line. This function should return the number of elements in after modification (the new argument count). It is expected that after this function is called, remains the name of the interpreter program, to are command-line arguments, and is 0 (NULL). Although it is perfectly legal to remove arguments from as well, this is trickier because you must follow all UNIX getopt(1) conventions. It is suggested that is used to process arguments on the command line, and used only to add extra arguments if needed. The command-line option character. The command-line option argument (if used). This function will be called each time an option specified in is found on the command line. It is guaranteed that only options contained in will be passed, and each option which requires a command-line argument will have one. Error messages for poorly formed options will be printed by the interpreter and this function will not be called. The number of spaces in the right margin. The number of spaces in the left margin. Note that normally only the output text is indented, not the command prompts. The number of lines per screenful. If this value is non-0 then the TI must make every effort to use this value as the number of lines per screenful. If it is 0 then the interface must infer the screen size as best it can. The number of lines that should be kept at the top of the screen when scrolling; this many lines should be left at the top of each page of output if a paged output mode is in effect. This function is called after the game has initialized and examined its command-line arguments but before anything else is done. It is called for both game-playing and information modes, so it should perform any setup necessary for general stdio printing. This function should return the number of characters that may be printed on one line in informational mode. If game-playing mode is to be used then this value is ignored. This function should perform any general end processing appropriate for both game-playing and informational modes. This function will be called just before the interpreter goes into game-playing mode. If the interpreter is running in information mode this function will never be called. Any game-playing mode specific processing should be done here, such as opening a new window, clearing the screen, setting terminal characteristics, etc. After this function returns the screen should be set up and the cursor should be positioned at the lower-left hand corner of the screen. Additionally, if the TI supports the creation of a fixed status window (in addition to the normal status line) it should call the macro here to notify the game Z-Code so that it will properly call the and functions (see below). This function should perform any end processing appropriate for game-playing mode only, such as deleting windows, resetting terminal characteristics, etc. A nul-terminated string containing any number of characters. The string may be empty, but the pointer will not be NULL. There will be no newline character ('\n') at the end of the string. This function should print to the screen, performing whatever line wrapping, paging, etc. is necessary. Processing should proceed as follows: For each lineful of characters in print whatever indent was specified. print a lineful along with a newline and whatever scrolling is necessary. if the interface supports paging, and it's enabled, and it's been (screenful - context) lines since the last prompt was printed, then perform whatever paging is necessary. If the TI supports proportional- and fixed-width fonts then the macro call should be used before printing line; if the macro returns non-0 then that line must be printed in a fixed-width font if possible (maps, etc.). If the macro returns 0 then a proportional font may be used if desired. If the TI supports scripting then the macro call should be checked; if the macro returns non-0 then should be printed to the script file as well. This function is called whenever the values on the status line are to be displayed. If the interface supports status-line printing and it is enabled, then this function should use the values of and to display the status line on the screen in whatever manner it chooses (these variables always contain the proper values; they may be used by any TI function). This function should be also be called whenever the TI code might have disturbed the status line. The sound number to be played. The action to be taken. The volume at which to play the sound (between 1 and 8). The number of arguments (1 to 3) passed to the function. This function will be called by the interpreter if the game being played contains sound support. If the interface doesn't support sound then a simple output line may be printed by this function stating that fact. Unfortunately different versions of Infocom games require different methods of sound support, but generally a separate sounds file is supplied with the datafile and the sounds must be retrieved and played from there. A nul-terminated string containing a message from the interpreter. Set to 1 if the message is an error message, or 0 if it's an informational message only. This function is called whenever the interpreter needs to print a message for some reason. Messages from the game will be printed via scr_putline(). The TI should attempt to exit the program or anything else; the interpreter will decide what to do about the message. A nul-terminated string which is the prompt to be printed when requesting a command. The maximum number of characters (including the nul character) which can be placed in A string of characters, in which the command should be returned. This function is called when the interpreter needs command input from the user. The function should accept up to characters of input and place them, along with a terminating nul character, into All command history, editing, shell escaping, etc. etc. must be done internal to this function. Also if the user types more than characters the function should flush the rest of the input and notify the user that it is doing so. If this function causes the status line to be messed up (i.e., a shell escape causes extra lines to be printed, etc.) care should be taken to rewrite the status line correctly. The interpreter supports an "interpreter escape mode", where interpreter flags and options may be modified. This mode is invoked by the user typing the escape character (by default ``@'') as the first character on the command line. If this function sees that as the first character, it should call with the remainder of the command line ( the escape character). Once that function returns the prompt should be reprinted and more input should be obtained without returning. If the TI wishes to provide its own escape commands it should process them itself and not call for those commands. The escape char by itself will list the available options. If the TI wishes to supply options in addition to the interpreter options, it should call first, then print its own options. Note that the TI may provide alternative ways of manipulating these options, such as pull-down or pop-up menus on windowing systems; can be called whenever control is properly passed to the TI; not from a signal handler, etc. If the TI supports scripting, then it should check the command to see if it was "script". If it was and scripting is not already enabled then it should be enabled here. If the enabling fails then an error should be printed and the TI should ask for another command without returning to the interpreter. The function should return the number of characters placed into not counting the terminating nul character. Create a fixed status window lines high. If is 0, then delete the current fixed status window (if any). This function is called if the game supports a fixed status window (currently is the only such Standard Series game), and the TI has registered that it supports such functionality by calling in scr_begin() (see above). The function will be passed the number of vertical lines needed for the fixed status window. When the window is to be deleted it will be passed an argument of 0. Note it may be passed a 0 even if the window currently doesn't exist; in this case the function should just silently return. If the TI doesn't support a fixed status window then just provide a dummy function which does nothing. Determines which window to start printing in. This function is called if the game supports a fixed status window (currently is the only such Standard Series game), and the TI has registered that it supports such functionality by calling in scr_begin() (see above). After the function has been called (see above), this function will be used to switch back and forth between the two windows. If called with set to 0, then the TI should set up to start printing in the regular text window. If called with set to 1, then set up to start printing in the fixed status window. All actual printing is done with the normal function. The maximum number of characters (including the nul character) which can be placed in A string of characters, in which the filename should be returned. The type of save file to be opened: SF_SAVE, SF_RESTORE, or SF_SCRIPT. This function is called when the interpreter needs to open a save game file for either saving or restoring, or to open a script file. When is called, will contain a default filename (initially it will be a static name, after the first call it will contain the last filename entered). If is 0, then the file should just be opened and the file pointer returned. If is greater than 0, the function should print an appropriate prompt, then accept up to characters of input and place them, along with a terminating nul character, into then open that file and return the file pointer. If is SF_SAVE or SF_SCRIPT, then the file is being opened to save the game or write script to and so should be opened for writing (binary mode for SF_SAVE and plain text mode for SF_SCRIPT). The function should test if the file already exists and if so, ask the user to confirm overwriting it. If is SF_RESTORE, then the file is being opened to restore a game and so should be opened for reading in binary mode. Any command history, editing, filename completion, shell escaping, etc. etc. must be done internal to this function. Also if the user types more than characters the function should flush the rest of the input and notify the user that it is doing so. If this function causes the status line to be messed up (i.e., a shell escape causes extra lines to be printed, etc.) care should be taken to rewrite the status line correctly. If the TI wishes to use this function for other purposes, the argument can be used: the interpreter will only call this function with values >=0 for so the TI is free to call it with special values <0 if it wishes. The function should return a FILE pointer to the open file. If the user cancels the operation somehow or the open fails, then the function should return 0 (NULL). If the return value is NULL then the interpreter will examine if is 0 then the interpreter will assume the operation was canceled; if it's non-0 then the interpreter will assume there was an error during opening of the file and print an appropriate error message. If the function returns non-NULL, the interpreter will attempt to read/write data from the current position in the file without rewinding or other manipulation. This means that you may store TI-specific information at the beginning of saved game files and read them back in when restoring if you like. The name of the game file just saved/restored FILE pointer of the game just saved/restored The type of save file to be opened: SF_SAVE, SF_RESTORE, or SF_SCRIPT. This function is called immediately after a game save file is saved or restored or a script file is to be closed. At the very least this function should perform an to close the file. Additionally it may add more terminal-specific data, create special configuration files, change file privileges, etc. If the TI wishes to use this function for other purposes, the argument can be used: the interpreter will only call this function with values >=0 for so the TI is free to call it with special values <0 if it wishes. If you wish to create a new TI, I suggest that you start with stream.c as the simplest current interface. Be sure you examine all of this code so you understand in detail what each function does. You should then create your own .c file and add functionality as you like. After you create your new TI you should name the .c file something relevant, such as for an interface to an IBM PC-compatible in DOS, or for an interface to an IBM PC-compatible running MS-Windows, or something. Then you should create a new TERMTYPE section in the Makefile. Decide what, if any, flags you need to give to the compiler and fill in TERMFLAGS; likewise for any extra libraries you need and TERMLIB. If you'd like your interface distributed with the pinfocom distribution, and an interface doesn't already exist, then send it to the pinfocom maintainer and he/she'll incorporate it in. Note you must place your interface code under the GNU Public License for it to be distributed (see the copyright notices in the other files), and provide an address for people to contact you about bugs, enhancements, etc. in a comment in the .c file. If an interface already exists then you'll have to contact the author and the two of you can hash it out between you :-).