Notes on the Xvi Source Code Chris Downey John Downey Xvi (pronounced ecks-vee-eye) is a free, portable, multi-window implementation of the popular UNIX(R) editor vi. This document contains information on how to port xvi to systems not currently supported. It also explains how the xvi source code is arranged into modules, and explains some of the data structures which are used, so that modifications may be made if and when necessary to the editor itself. 1. INTRODUCTION Xvi is intended to be portable to just about any system. This is one of the central reasons for its existence; the authors wish to be able to use the same editor everywhere. The main body of the editor is (supposedly) fully portable, relying only on standard facilities defined by the White Book, and on a set of primitives which are provided by a set of one or more modules for each operating system. If __STDC__ is defined, certain ANSI C facilities will be used, but the editor will compile with non-ANSI compilers. Therefore, in order to port xvi to a new system, all that is necessary is to provide the defined set of primitives, and then build the editor. Or at least, that's the idea; we have refined the set of primitives as we port the editor to new environments, and it's getting pretty easy now. The rest of this document is divided into sections as follows: Section 2: System-Specific Modules This section deals with the layout of source files and makefiles which you will have to deal with when porting xvi. Section 3: Primitives Provided by xvi Discusses what primitives are provided by the main body of the editor source code for use by the system interface code. Section 4: System Interface Explains the primitives which need to be provided in order to make xvi work. 25th September 1992 Page 1 2 Xvi Source Code Notes Section 5: Data Structures Details the internal data types used in the editor, and any functions available for operating on those types. Section 6: Source Files Lists the source files comprising the editor, and explains what functionality is provided by each one. 2. SYSTEM-SPECIFIC MODULES The system-specific code normally consists of three (or more) files; a ".c" file, a ".h" file, and a makefile. For example: qnx.c qnx.h makefile.qnx comprise the system-specific module for the QNX operating system. In most cases, the system-specific code is divided into two or more modules, where one (called the system interface module) is concerned with general interactions with the operating system and the other (called the terminal interface module) is designed for a specific interface to a display and keyboard (and possibly, a mouse). For example, the generic UNIX implementation has unix.c and unix.h for the system interface module, and tcap_scr.c and termcap.h for the terminal interface module; this should work reasonably with any full-duplex terminal that can be described by the termcap and terminfo databases. On consoles with memory- mapped displays and systems with graphic user interfaces, it may be possible to achieve faster display updating and perhaps other benefits by replacing the termcap module with another one that makes better use of whatever facilities are available. For instance, there is an experimental version for SunView, which allows mouse input on Sun workstations running the SunView window system. On the other hand, the termcap-specific routines might conceivably be useful on some other operating systems (such as VMS), so in general it seemed a good idea to make the termcap- specific routines a separate module. The current MS-DOS implementation has a separate terminal interface module, which is designed specifically for IBM PC compatible computers. This is in the files ibmpc_a.asm ibmpc_c.c ibmpc.h The first of these is written in assembly language because there are not enough routines common to the various MS-DOS C compilers which reliably access the display and keyboard at a low enough level. Page 2 25th September 1992 Xvi Source Code Notes 3 The hardware-independent system interface module for MS-DOS is in msdos_a.asm msdos_c.c msdos.h The first of these is written in assembly language for the same reason as is ibmpc_a.asm. Theoretically, different terminal interface modules could be written for MS-DOS systems running on hardware which is not IBM- compatible but, unfortunately, such systems seem to be virtually extinct nowadays. Sometimes more than one makefile is provided, as in the case of UNIX, where different versions work in slightly different ways. It is, of couse, not necessary to provide all -- or any -- of these files for a particular implementation; this is just a convention. The makefile(s) for each system determine what files are used in the compilation of the editor. The following porting modules are available at present: 25th September 1992 Page 3 4 Xvi Source Code Notes +-----------------------+--------------+-----------------------------+ | System | Makefile | Source Files | +-----------------------+--------------+-----------------------------+ |UNIX | | | | BSD | makefile.bsd | unix.[ch] tcap*.c termcap.h | | System V - | makefile.usg | unix.[ch] tcap*.c termcap.h | | AIX | makefile.aix | unix.[ch] tcap*.c termcap.h | | ULTRIX | makefile.ult | unix.[ch] tcap*.c termcap.h | | Xenix - | makefile.xen | unix.[ch] tcap*.c termcap.h | | POSIX (e.g. BSDI) | makefile.pos | unix.[ch] tcap*.c termcap.h | | SunOS | makefile.sun | unix.[ch] tcap*.c termcap.h | | SunView | makefile.sv | unix.[ch] sunview.h | | | | sunfront.c sunback.c | | | | xvi.icn | +-----------------------+--------------+-----------------------------+ |MS-DOS | | msdos_c.c msdos.h | | | | ibmpc_c.c ibmpc.h | | Microsoft C 5.* | makefile.msc | 8086mm.inc ibmpc_a.asm | | & MASM 5.* | | msdos_a.asm | | Microsoft Quick C | makefile.qc | 8086mm.inc ibmpc_a.asm | | & MASM 5.* | | msdos_a.asm | | Zortech C++ 2.* | makefile.zc2 | 8086mm.inc ibmpc_a.asm | | & MASM 5.* | | msdos_a.asm | | Zortech C++ 3.* | makefile.zc3 | 8086mm.inc ibmpc_a.asm | | & MASM 5.* | | msdos_a.asm | | Zortech C++ 3.* | | | | 386 protected mode | makefile.386 | pc386.[ch] | +-----------------------+--------------+-----------------------------+ |OS/2 - | | | | Version 1, text mode | | | | Microsoft C 5.1 | makefile.os2 | os2vio.[ch] | | & MASM 5.1 | | i286.asm | +-----------------------+--------------+-----------------------------+ |QNX | | | | Version 2/3 (CII) | makefile.qnx | qnx.[ch] | | Version 4 (Watcom C) | makefile.qn4 | unix.[ch] tcap*.c termcap.h | +-----------------------+--------------+-----------------------------+ |TOS - | | | | Lattice C | makefile.tos | tos.[ch] tos.lnk | +-----------------------+--------------+-----------------------------+ - Versions marked with - probably do not work, as systems have not been recently available to the authors for testing. 3. PRIMITIVES PROVIDED BY XVI 3.1. General Definitions The file xvi.h should be included by all system-specific modules; this file should also be edited so that a system-specific header file (or files), as determined by a predefined keyword, will be included. For instance, under UNIX, the word UNIX is defined by passing the -DUNIX flag to the C compiler from the makefile, and xvi.h contains the following lines: Page 4 25th September 1992 Xvi Source Code Notes 5 #ifdef UNIX # include "unix.h" #endif in order to obtain the UNIX-related definitions from that header file. Among the definitions in xvi.h are the following: bool_t A Boolean type having values TRUE or FALSE. const volatile These are defined out when __STDC__ is not defined, so that it is always safe to use them. xvi.h also includes various other header files which are needed. The following system header files are always included: stdio.h ctype.h signal.h string.h These files are included if __STDC__ is defined: stddef.h stdlib.h limits.h and if __STDC__ is not defined, xvi.h will provide its own definitions for the following: INT_MAX INT_MIN ULONG_MAX FILE *fopen(); char *malloc(); char *getenv(); Finally, one of the following header files will be included: stdarg.h varargs.h depending on whether __STDC__ is defined or not. In order to make coding of varargs functions easier, a macro VA_START() is defined, which takes the same arguments as the ANSI-style va_start(), but which is also available in non-ANSI environments (e.g. BSD). In order to make it possible to use ANSI-style prototypes for function declarations, but still allow compilation under non-ANSI 25th September 1992 Page 5 6 Xvi Source Code Notes environments, the following macro is provided: #ifdef __STDC__ # define P(args) args #else # define P() () #endif so that function declarations may be specified thus: extern FILE *fopen P((const char *, const char *)); Please use this facility when you provide declarations for your system primitives, unless your system always uses an ANSI compiler. 3.2. Parameters An important facility provided for use by system-specific modules is access to the editor's parameter table. This is achieved by means of some apparent functions, and a set of #defined token values. The functions are: void set_param(int n, val) This function sets the indicated parameter to the passed value, which must be of an appropriate type. Parameter values may be obtained by means of the following functions (actually macros): char *Ps(int n) return value of string parameter int Pn(int n) return value of numeric parameter bool_t Pb(int n) return value of boolean parameter char **Pl(int n) return value of list parameter (a NULL-terminated array of character pointers) int Pen(int n) return numeric value (index) of enumerated parameter char **Pes(int n) return string value of enumerated parameter In all cases, the int n argument is the index of the parameter in the table; a set of #defines is provided, of the form: P_name which map the parameter names into integral values. Thus, for example, we might obtain the value of the colour parameter: Page 6 25th September 1992 Xvi Source Code Notes 7 colour = Pn(P_colour); or set the value of the helpfile parameter: set_param(P_helpfile, "/usr/lib/xvi/help"); 4. SYSTEM INTERFACE 4.1. Introduction There follows a list of the primitives which must be provided either by the system interface module or by the underlying OS. Note that it is perfectly acceptable to implement functions or external variables as macros so long as they "look the same" as the definitions below. As a guideline, anything which is (a) in capitals, or (b) is a const variable, will be implemented as a #define for most systems. When you want to actually do the port, it is highly recommended that you copy the system-specific files for the system which seems closest to your own, and modify those files, rather than starting from scratch. All the following symbols should be defined in the system interface module, or by standard header files already included by xvi.h, or by other header files explicitly included by the system-specific header file: const unsigned int MAXPATHLEN The maximum number of characters in a pathname. const unsigned int MAXNAMLEN The maximum number of characters in a filename. int remove(char *filename) Remove the named file as per ANSI. int rename(char *old, char *new) Rename the file old to new as per ANSI. void sleep(unsigned int seconds) Put the process to sleep for the given number of seconds. const char * const DIRSEPS The pathname separators supported for system calls (e.g. "\\/" for MS-DOS). FILE *fopenrb(char *file) FILE *fopenwb(char *file) Like the standard fopen() library call, but they both open files in "binary" mode (i.e. no conversion of cr/lf/crlf is done), for reading and writing respectively. 25th September 1992 Page 7 8 Xvi Source Code Notes bool_t exists(char *filename) Returns TRUE if the named file exists. bool_t can_write(char *filename) Returns TRUE if the named file can be written, i.e. if a fopenwb(filename) will succeed. char *fexpand(char *filename) Returns a filename-expanded version of the passed filename. #define SETVBUF_AVAIL const unsigned int READBUFSIZ const unsigned int WRTBUFSIZ If SETVBUF_AVAIL (or __STDC__) is defined, these constant values are used to set I/O buffer sizes (using the setvbuf() function) for reading and writing files. Note that if buffers of these sizes are unavailable at runtime, the editor will try to allocate smaller buffers by iteratively halving the buffer size until the allocation succeeds. It is therefore acceptable for these values to be quite large. char *tempfname(const char *filename) Create a unique name for a temporary file, possibly using filename as a base (this will be used by do_preserve() to create a backup file for the file named by filename). The string returned must have been allocated using malloc(); NULL can be returned if there is no more memory available. int call_system(char *command) Invoke the given command in a subshell. This is used for shell escapes from xvi. The command string may contain metacharacters which are expected to be expanded by a command interpreter, e.g. UNIX /bin/sh, MS-DOS command.com. Return value is 0 for success. In many environments, this call may safely be #defined as system(command). int call_shell(char *shell) Invoke the named shell. This is used for the :shell command. It may be mapped into call_system(), but is separate on some systems for efficiency reasons (i.e. not invoking two shells to get one). Return value is 0 for success. bool_t sys_pipe(char *cmd, int (*wf)(FILE *), long (*rf)(FILE *)) Used for the ! command. The first parameter is the command to invoke, while the second and third are functions which should be called with an open file pointer in order to write out old, or read in new lines (respectively). Note that if "real" pipes are not available, it is acceptable to implement this function using temporary files, but the wf function must obviously be called before rf. Page 8 25th September 1992 Xvi Source Code Notes 9 void sys_exit(int code) Exit with given exit status. This routine must not return. The editor is considered "dead" once it has been called, and no further calls to editor functions should be made. void delay(void) Delay for a short time, about a fifth of a second. This is used for showing matching brackets when showmatch is set. It is acceptable to just return if implementing this is not easy. 4.2. Screen Control An instance of the following structure must be defined in order to allow screen output to take place: typedef struct virtscr { genptr *pv_window; int pv_rows; int pv_cols; /* public: */ VirtScr *(*v_new)(VirtScr *); void (*v_close)(VirtScr *); int (*v_rows)(VirtScr *); int (*v_cols)(VirtScr *); void (*v_clear_all)(VirtScr *); void (*v_clear_line)(VirtScr *); void (*v_goto)(VirtScr *, int row, int col); void (*v_write)(VirtScr *, int row, int col, char *str); void (*v_putc)(VirtScr *, int row, int col, int ch); void (*v_set_colour)(VirtScr *, int colour); void (*v_flush)(VirtScr *); void (*v_beep)(VirtScr *); /* optional: not used if NULL */ void (*v_insert)(VirtScr *, int row, int col, char *str); int (*v_scroll)(VirtScr *, int start, int end, int nlines); } VirtScr; The first three fields in this structure are "private", for use only within the implementation of the "public" functions. The remaining fields are all function pointers, and are described below. Note that all functions have at least one parameter, which is a pointer to the instance of the VirtScr in question. This is always referred to as vs below. Note also that the top- left-hand corner of the window is taken to be (0,0). 25th September 1992 Page 9 10 Xvi Source Code Notes v_new(vs) Obtain a new VirtScr, and return a pointer to it. This is not used at present, and should return NULL. v_close(vs) Close the window to which vs refers. v_rows(vs) Return the number of rows in vs. v_cols(vs) Return the number of columns in vs. v_clear_all(vs) Clear the window completely. v_clear_line(vs, int row, int col) Clear the specified line, from the given column to the right hand edge of the window, inclusive. v_goto(vs, int row, int col) Move the cursor to the specified row and column. v_write(vs, int row, int col, char *str) Write the specified string of characters into the window, starting at the specified row and column. The parameters will be such that the string will always fit into a single line of the window, i.e. no line-wrapping is necessary; however, it is quite possible for the string to end on the last character of a line, and some implementations will need to take special precautions to handle this correctly. v_putc(vs, int row, int col, int ch) This is like v_write but for a single character. v_set_colour(vs, int colour) Set the colour for all subsequent output (including clearing of lines or the whole window) to the specified colour. The meaning of the value is system-specific. v_colour_cost(vs) Return the number of extra characters which are taken up in the window by a colour change. This is almost always 0, but there exist some terminals for which it is not (see the "sg" termcap capability). v_flush(vs) Flush all screen output, and move the cursor on the screen to the correct position. The screen need not actually be updated until either this function is called, or xvi_handle_event() returns. v_beep(vs) Beep. It is acceptable to flash the screen or window if no audio facility is available. Page 10 25th September 1992 Xvi Source Code Notes 11 v_insert(vs, int row, int col, char *str) This function inserts the given string at the given position, pushing any other characters on the same row to the right. If such a facility is not available, the function pointer should be set to NULL. v_scroll(vs, int start, int end, int nlines) This function scrolls the set of lines between start and end (inclusive) by nlines lines. If nlines is positive, normal scrolling should be done, i.e. the lines should be moved upwards with respect to the window. If nlines is negative, scrolling should be in the reverse direction. The lines which are left by the scrolling should be cleared. The function should return non-zero if the scrolling was successful, otherwise 0. If scrolling is not available, the function pointer should be set to NULL. 4.3. Parameters Default values should be #defined for certain parameters as follows: +---------------+---------+---------------+ |Parameter Name | Type | #define name | +---------------+---------+---------------+ |syscolour | numeric | DEF_SYSCOLOUR | |colour | numeric | DEF_COLOUR | |statuscolour | numeric | DEF_STCOLOUR | |roscolour | numeric | DEF_ROSCOLOUR | |helpfile | string | HELPFILE | |format | string | DEF_TFF | +---------------+---------+---------------+ 4.4. File Formats The functions in xvi which read and write text files are aware of several different newline conventions (for example, "\n" on UNIX, "\r\n" on MS-DOS, and so on), so that any version of the editor can read and write any of the supported formats. The value of the format parameter (which can be set to "unix", "msdos", "macintosh", etc.) determines which format is currently being used. If you are porting xvi to a system with a newline convention which isn't one of those currently supported (see the table called tftable in fileio.c) you may have to add a new entry to the table. Unfortunately, the current design is not as general as it ought to be. If you happen to be porting to VMS, or some other system which doesn't use either a single character or a consecutive pair of characters to represent a newline, you will have quite a lot of work to do if you want to retain the facility for converting between file formats within the editor. 25th September 1992 Page 11 12 Xvi Source Code Notes In any case, your system interface module should define DEF_TFF to be the index of the entry in tftable which represents the default format for your system. This is the value for Pen(P_format) which will be compiled into the parameter table. 4.5. Notes on Termcap Implementation The termcap implementation of the terminal interface is currently only used for the UNIX port. This module could quite easily be re-used for other systems if desired; the following routines would need to be defined by the system module: void foutch(int c) Output a single character to the terminal. This must be implemented as a function, not a macro, because it is passed as a parameter into the termcap library. void moutch(int c) Same as foutch() except that it can be implemented as a macro. This will be used by the termcap interface module to write characters to the screen. void oflush(void) Flush buffered output to the terminal. 4.6. Entering/Leaving Visual Mode Some facility is commonly necessary for the system interface module to be able to tell the terminal interface module to enter or exit visual mode. This might mean changing the terminal state between "raw" and "cooked" modes, or switching display pages. No specific interface for this is defined, although the standard UNIX and MS-DOS implementations do use such a facility, and the interface functions for both systems are identically defined. 4.7. Function Keys/Mouse Handling Function key values are coded into a set of #defined constants in the file ascii.h; e.g. the value K_UARROW might be given as input when the keyboard up-arrow key has been pressed. If the global variable State is not equal to NORMAL, all function keys except for a backspace key are invalid input. If an invalid key is pressed, the safest strategy may be to beep and wait for another key to be pressed. NORMAL is defined in xvi.h. Another facility which may be provided is handling mouse input on systems where it is available. The strategy for interpreting mouse input is controlled by the mouseclick() function (in mouse.c); the idea is to make the strategy independent of any specific device interface. If a mouse button is pressed before a keyboard key is pressed, the following routine should be called: mouseclick(int row, int column); where row and column are the current co-ordinates, counted in Page 12 25th September 1992 Xvi Source Code Notes 13 character positions, of the mouse pointer within the screen or editing window. If the mouse is moved while a button is held down, the routine mousedrag(int startrow, int endrow, int startcolumn, int endcolumn); should be called with co-ordinates describing the movement. If the global variable State is not equal to NORMAL, mouse input can be ignored altogether. All this will be considerably tidied up at a later stage, when we have proper xvEvent types for function keys and mouse actions. 4.8. Main Finally, the system interface module must provide a main() function. This function must call xvi_startup(vs, argc, argv, env) at startup, with parameters as follows: VirstScr *vs; This is a pointer to the VirtScr structure for the first window, or for the terminal screen. int argc, char **argv; These are as for a main() function. char *env; This is an environment string, normally the return value from getenv("XVINIT"). If the concept of environment variables does not exist, a string of the form "source filename" may be passed instead, so as to allow users to localise their usage of the editor. The return value from xvi_startup() is a pointer, which will be used in future to identify the window for input events. For now, it should be stored in the VirtScr's pv_window field. Having called xvi_startup(), input events may then be passed to the editor by calling xvi_handle_event with a pointer to an xvEvent structure as the sole argument. This structure is defined as follows: 25th September 1992 Page 13 14 Xvi Source Code Notes typedef struct event { enum { Ev_char, Ev_timeout } ev_type; union { /* Ev_char: */ int evu_inchar; /* Ev_timeout: */ } ev_u; } xvEvent; #define ev_inchar ev_u.evu_inchar The ev_type field is a tag which identifies the type of event which has occurred. At present, only two events are supported: an input character from the user, and a timeout. The union which follows contains data associated with each event type; currently only the type Ev_char requires data, as may be seen. The #define for ev_inchar is provided purely for convenience. The return value from xvi_handle_event() is a long integer value which is the time in milliseconds for which the editor is prepared to wait for more input. If no input arrives within that time, the function should be called again with an event of type Ev_timeout. The timeout value returned may be 0L, indicating that no timeout is necessary. It is very important that timeouts should actually be implemented because they are needed for the preserve facility. Currently, if a keyboard interrupt is received, xvi_handle_event() need not be called (it should, in any case, never be called from an asynchronous interrupt or signal handler) but the global variable kbdintr should be set to a non-zero value. 5. DATA STRUCTURES Structures used in xvi are all typedef'd, and all begin with a capital letter. They are defined in xvi.h. The following data structures are defined: 5.1. Line This structure is used to hold a single text line. It contains forward and backward pointers which are connected together to form a two-way linked list. It also contains a pointer to an allocated text buffer, an integer recording the number of bytes allocated for the text, and the line number (an unsigned long). The text is null-terminated, and the space allocated for it may be grown but is never shrunk. The maximum size of this space is given by MAX_LINE_LENGTH. Page 14 25th September 1992 Xvi Source Code Notes 15 The line number is used when showing line numbers on screen, but this is secondary to its main purpose of providing an ordering on lines; the ordering of two lines in a list may be established by simply comparing their line numbers (macros are available for this purpose; see later for details). 5.2. Buffer This structure holds the internal representation of a file. It contains pointers to the linked list of lines which comprise the actual text. We always allocate an extra line at the beginning and the end, with line numbers 0 and MAX_LINENO respectively, in order to make the code which deals with this structure easier. The line numbers of Line structures in a Buffer are always maintained by code in undo.c, which is the only module which ever changes the text of a Buffer. The Buffer structure also contains: o flags, including readonly and modified o current filename associated with the buffer o temporary filename for buffer preservation o space for the mark module to store information about marked lines o space for the undo module to store information about the last change o number of windows associated with the buffer The following macros are used to find out certain information about Lines within Buffers: lineno(Buffer *b, Line *l) Returns the line number of the specified Line, which belongs to the specified Buffer. earlier(Line *l1, Line *l2) Returns TRUE if l1 is earlier in the buffer than l2. later(Line *l1, Line *l2) Returns TRUE if l1 is later in the buffer than l2. is_lastline(Line *l1) Returns TRUE if l1 is the last line (i.e. the extra line at the end, not the last text line) of the buffer. is_line0(Line *l1) Returns TRUE if l1 is the 0th line (i.e. the extra line at the start, not the first text line) of the buffer. 25th September 1992 Page 15 16 Xvi Source Code Notes 5.3. Posn This structure is very simple; it contains a Line pointer and an integer index into the line's text, and is used to record a position within a buffer, e.g. the current cursor position. These functions are available for operating on Posn structures: gchar(Posn *) Returns the character which is at the given position. inc(Posn *) Increments the given position, moving past end-of-line to the next line if necessary. The following type is returned: enum mvtype { mv_NOMOVE, /* at beginning or end of buffer */ mv_SAMELINE, /* still within same line */ mv_CHLINE, /* changed to different line */ mv_EOL, /* at terminating '\0' */ }; dec(Posn *) As for inc() but decrements the position. lt(Posn *p1, Posn *p2) Returns TRUE if the position specified by p1 is earlier in the buffer than that specified by p2. 5.4. Xviwin This structure maps a screen window onto a Buffer. It contains: o a pointer to the Buffer structure which it is mapped onto o the cursor's logical position in the buffer (a Posn structure) o the cursor's physical position in the window (row and column) o information about size and location of screen window o current text of status line o forward and backward pointers to other windows Note that there is at least one Xviwin for every Buffer. When the editor was modified to support buffer windows, many global variables were moved into the Buffer and Xviwin structures; some were left as globals. For instance, the undo and mark facilities are obviously buffer-related, but yank is useful if it is global (actually static within its own module); it was decided that search and redo should also be global. Page 16 25th September 1992 Xvi Source Code Notes 17 Some modules have their own internal static data structures; for instance, the search module remembers the last pattern searched for. Also, certain modules use data structures which are included in more global ones; e.g. each Buffer structure contains some data used only within undo.c. This is not very well structured, but in practice it's quite clean because we simply ensure that references to such structures are kept local to the module which "owns" them. 5.5. Mark This data structure records a mark (defined by the m command). It contains a Posn and a character field to hold the letter which defines the mark. Each Buffer contains an array of these structures for holding alphabetic marks, plus one for the previous context mark (as used by the '' and `` commands). The file mark.c deals with marks. 5.6. Change This structure records a single change which has been made to a buffer. It also contains a pointer, so that it may be formed into a list. See the discussion of undo.c below for further details. 5.7. Flexbuf This structure is used to store text strings for which the length is unknown. The following operations are defined for this type. All functions take a Flexbuf pointer as a parameter. flexnew(f) Initialise a Flexbuf; not needed for static Flexbufs. flexclear(f) Truncate a Flexbuf to zero length, but don't free its storage. flexdelete(f) Free all storage belonging to a Flexbuf. flexempty(f) Return TRUE if the Flexbuf is empty. flexlen(f) Return the number of characters in the Flexbuf. flexrmchar(f) Remove the last character from a Flexbuf. flexpopch(f) Remove the first character from a Flexbuf and return it. flexgetstr(f) Return a pointer to the string contained in the Flexbuf. 25th September 1992 Page 17 18 Xvi Source Code Notes flexaddch(f, c) Add the character c to the end of the Flexbuf. lformat(f, fmt, ...) A subset of sprintf() but for Flexbufs. vformat(f, fmt, va_list) A subset of vsprintf() but for Flexbufs. The last two functions are especially useful, since they avoid the usual problems with the lack of bounds-checking in sprintf(). All code in the editor itself now uses Flexbufs to avoid the possibility of buffer overruns, and to reduce the size of the executable. Some OS-specific modules, however, may still use the printf() family. The subset of printf-like format specifiers implemented includes those for integers and strings, but not for floating-point numbers. 5.8. bool_t A simple Boolean type; has values TRUE and FALSE, which are defined as 1 and 0 so as to be compatible with C comparison operators. 5.9. xvEvent This type is defined in the previous section, since it forms part of the porting interface. 5.10. VirtScr This type represents a virtual screen, and is constructed in a similar way to a class. It contains some function pointers which may be used to manipulate the screen in various ways, and some private data which is used by the implementation of the class. The old terminal interface, which consisted of a set of disparate functions, is being replaced by the VirtScr interface; the first step in this process has been accomplished by the provision of a default VirtScr implementation using the old primitive functions. New, native, VirtScr implementations may now be coded, which will increase the efficiency of screen output. As the final stage, a windowing implementation of the VirtScr class will be provided, using the underlying VirtScr implementations, and the window-handling code in the editor will be modified to that each occurrence of an Xviwin references its own VirtScr. It will then be possible to build a version of the editor which operates in a true windowing environment by using a separate screen window for each buffer, instead of the current vertical-split method. A full definition of the VirtScr type will be found in the previous section. Page 18 25th September 1992 Xvi Source Code Notes 19 5.11. Global Variables There are only a few global variables in the editor. These are the important ones: curbuf pointer to the current Buffer curwin pointer to the current Xviwin State the current state of the editor; controls what we do with input characters. The value is one of the following: NORMAL The default state; vi-mode commands may be executed INSERT Insert mode, i.e. characters typed get inserted into the current buffer REPLACE Replace mode, characters in the buffer get overwritten by what is typed CMDLINE Reading a colon-command, regular expression or pipe command DISPLAY Displaying text, i.e. :p command, or :set or :map with no argument echo This variable controls what output is currently displayable. It is used at various points within the editor to stop certain output which is either undesirable or sub-optimal. It must always be restored to its previous value after the code which changed it has finished what it is doing. kbdintr This can be set to a non-zero value to indicate that an asynchronous user-generated interrupt (such as a keyboard interrupt) has occurred. See the discussion of event handling in the previous section. 6. SOURCE FILES The header file xvi.h contains all the type definitions used within the editor, as well as function declarations etc. The following source files form the primary interface to the editor: 25th September 1992 Page 19 20 Xvi Source Code Notes startup.c Entry point for the editor. Deals with argument and option parsing and initial setup, calling module initialisation functions as necessary. events.c Contains the routine xvi_handle_event(), which is the entry point for handling input to the editor; input is passed to different routines according to the State variable. Timeouts on input are also handled here, by calling appropriate routines in map.c or preserve.c. edit.c Deals with insert and replace modes. normal.c Handles normal-mode commands. map.c This file is responsible for all input mapping (both set up by the :map command and internally for function- key mappings; it also implements a stuff-characters- into-the-input-stream function for use within the editor. This is used, for example, to implement command redo (but not to implement "undo" and "put" as in STEVIE). Colon (ex-type) commands are handled by this group: cmdline.c Decodes and executes colon commands. ex_cmds1.c File-, Buffer- and Xviwin-related colon commands. ex_cmds2.c Other colon commands (e.g. shell escape). Screen updating is done within the following files: screen.c Screen updating code, including handling of line-based entry (for colon commands, searches etc) as they are typed in, and display-mode stuff (for parameter displaying, :g/re/p etc). cursor.c This file contains the single function cursupdate(), which is responsible for deciding where the physical screen cursor should be, according to the position of the logical cursor in the buffer and the position of the window onto that buffer. This routine is not very optimal, and will probably disappear in due course. Page 20 25th September 1992 Xvi Source Code Notes 21 defscr.c This file contains the default implementation of the VirtScr class, on top of the old terminal/system interface. status.c Functions to update the status line of a window; there are different functions to display file information (name, position etc.) and error/information messages. These files deal with specific areas of functionality: find.c Search functions: all kinds of searches, including character-based and word-based commands, sections, paragraphs, and the interface to "real" searching (which is actually done in search.c). mark.c Provides primitives to record marks within a Buffer, and to find the marks again. mouse.c Code to handle mice moving the cursor around and resizing windows. param.[ch] Code to handle setting of, and access to, parameters. (These are things like tabstops, autoindent, etc.) pipe.c Handles piping through system commands. preserve.c File preservation routines. search.c Code for pattern-searching in a buffer, and for substitutions and global execution. Uses regexp.[ch] for the actual regular expression stuff. tags.c Routines to handle tags -- for :tag, -t and ^]. undo.c Code to deal with doing and undoing; i.e. making and unmaking changes to a buffer. This is one of the more complex and delicate files. yankput.c Code to deal with yanking and putting text, including named buffers. while these files provide lower-level functions: alloc.c Memory allocation routines. 25th September 1992 Page 21 22 Xvi Source Code Notes ascii.[ch] Deals with the visual representation of special characters on the display (e.g. tabs, control chars). buffers.c Routines dealing with the allocation and freeing of Buffers. fileio.c File I/O routines; reading, writing, re-editing files. Also handling of the format parameter. flexbuf.c Flexible-length character-buffer routines. misccmds.c Miscellaneous functions. movement.c Code to deal with moving the cursor around in the buffer, and scrolling the screen etc. ptrfunc.[ch] Primitives to handle Posn structures; including various operators to compare positions in a text buffer. regexp.[ch], regmagic.h Regular-expression stuff, originally written by Henry Spencer (thanks Henry) and slightly hacked for use within xvi. signal.c Handling of terminal-generated signals in an ANSI environment. virtscr.h Virtual Screen interface definition. This is a new part of xvi, and is not yet fully completed. When it is finished, it will provide the ability to implement "native" versions of xvi under various windowing systems, in a clean and wholesome way. Currently there is a single instance of the VirtScr class, which is defined on top of the old system/terminal interface. windows.c Code to deal with creating, deleting, resizing windows. version.c Contains only the version string. Page 22 25th September 1992