vile: a VI Like Editor - WIP
authorAlan Cox <alan@linux.intel.com>
Fri, 23 Feb 2018 00:45:43 +0000 (00:45 +0000)
committerAlan Cox <alan@linux.intel.com>
Fri, 23 Feb 2018 00:45:43 +0000 (00:45 +0000)
Not yet ready for Fuzix as we probably want to avoid stdio and curses
(but maybe not - need to see on size). Either way our cursors lacks some
of the funky features this uses at the moment I believe (eg keypad)

Applications/util/vile.c [new file with mode: 0644]
Applications/util/vile.doc [new file with mode: 0644]

diff --git a/Applications/util/vile.c b/Applications/util/vile.c
new file mode 100644 (file)
index 0000000..1156ea9
--- /dev/null
@@ -0,0 +1,796 @@
+/*
+ *     VILE    -       VI Like Editor
+ *
+ *     Based upon:
+ *
+ *        ae.c                Anthony's Editor  Mar '92
+ *
+ *        Public Domain 1991, 1992 by Anthony Howe.  All rights released.
+ *
+ *     ANSIfied, and extended eventually for Fuzix Alan Cox 2018
+ *
+ *     Copyright 2018 Alan Cox
+ *
+ *     Permission is hereby granted, free of charge, to any person obtaining
+ *     a copy of this software and associated documentation files (the
+ *     "Software"), to deal in the Software without restriction, including
+ *     without limitation the rights to use, copy, modify, merge, publish,
+ *     distribute, sublicense, and/or sell copies of the Software, and to
+ *     permit persons to whom the Software is furnished to do so, subject to
+ *     the following conditions:
+ *
+ *     The above copyright notice and this permission notice shall be included
+ *     in all copies or substantial portions of the Software.
+ *
+ *     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ *     OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ *     MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ *     IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ *     CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ *     TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ *     SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include <ctype.h>
+#include <curses.h>
+#include <stdio.h>
+#include <string.h>
+
+#ifndef BUF
+#define BUF        16384
+#endif /* BUF */
+
+#ifndef HUP
+#define HUP        "ae.hup"
+#endif /* HUP */
+
+typedef struct keytable_t {
+        int key;
+        int flags;
+#define NORPT  1
+#define KEEPRPT        2
+        int (*func)(void);
+} keytable_t;
+
+int done;
+int row, col;
+int indexp, page, epage;
+int input;
+int repeat;
+char buf[BUF];
+char *ebuf;
+char *gap = buf;
+char *egap;
+char *filename;
+keytable_t *table;
+
+/*
+ *        The following assertions must be maintained.
+ *
+ *        o  buf <= gap <= egap <= ebuf
+ *                If gap == egap then the buffer is full.
+ *
+ *        o  point = ptr(indexp) and point < gap or egap <= point
+ *
+ *        o  page <= indexp < epage
+ *
+ *        o  0 <= indexp <= pos(ebuf) <= BUF
+ *
+ *
+ *        Memory representation of the file:
+ *
+ *                low        buf  -->+----------+
+ *                                |  front   |
+ *                                | of file  |
+ *                        gap  -->+----------+<-- character not in file
+ *                                |   hole   |
+ *                        egap -->+----------+<-- character in file
+ *                                |   back   |
+ *                                | of file  |
+ *                high        ebuf -->+----------+<-- character not in file
+ *
+ *
+ *        point & gap
+ *
+ *        The Point is the current cursor position while the Gap is the
+ *        position where the last edit operation took place. The Gap is
+ *        ment to be the cursor but to avoid shuffling characters while
+ *        the cursor moves it is easier to just move a pointer and when
+ *        something serious has to be done then you move the Gap to the
+ *        Point.
+ *
+ *
+ *        Use of stdio for portability.
+ *
+ *        Stdio will handle the necessary conversions of text files to
+ *        and from a machine specific format.  Things like fixed length
+ *        records; CRLF mapping into <newline> (\n) and back again;
+ *        null padding; control-Z end-of-file marks; and other assorted
+ *        bizare issues that appear on many unusual machines.
+ *
+ *        AE is meant to be simple in both code and usage.  With that
+ *        in mind certain assumptions are made.
+ *
+ *        Reading:  If a file can not be opened, assume that it is a
+ *        new file.  If an error occurs, fall back to a safe state and
+ *        assume an empty file.  fread() is typed size_t which is an
+ *        unsigned number.  Zero (0) would indicate a read error or an
+ *        empty file.  A return value less than BUF is alright, since
+ *        we asked for the maximum allowed.
+ *
+ *        Writing:  If the file can not be opened or a write error occurs,
+ *        then we scramble and save the user's changes in a file called
+ *        ae.hup.  If ae.hup fails to open or a write error occurs, then
+ *        we assume that shit happens.
+ *
+ *
+ *     TODO for Fuzix
+ *
+ *     Make more vi like
+ *     Implement regexps
+ *     Add some minimal : commands (:w notably and :n)
+ *     Yank/paste
+ *     Remove stdio and curses use
+ *     ZZ
+ *     Use uint8, uint when we can for speed
+ *     Use memmove not loops
+ *     Macros out of the command blocks we have - may need them all to return
+ *     an error status
+ *     : blank lines vi style
+ *     status bars etc in modeless mode
+ *     long line support (including fixing insert_mode)
+ *
+ *     Can we rewrite most of the editor functions in some kind of bytecode
+ *     / keycode
+ *
+ *     Can we do async redraw (if you type during redraw we skip updating and
+ *     defer the work ?). Need to look at this post curses
+ */
+
+int adjust(int, int);
+int nextline(int);
+int pos(char *);
+int prevline(int);
+int save(char *);
+char *ptr(int);
+
+void display(void);
+void movegap(void);
+int insertch(char);
+int fleft(char);
+int fright(char);
+
+int backsp(void);
+int bottom(void);
+int delete(void);
+int delete_line(void);
+int down(void);
+int file(void);
+int insert(void);
+int insert_mode(void);
+int insert_before(void);
+int append_mode(void);
+int append_end(void);
+int left(void);
+int lnbegin(void);
+int lnend(void);
+int pgdown(void);
+int pgup(void);
+int pgup_half(void);
+int redraw(void);
+int right(void);
+int replace(void);
+int quit(void);
+int flip(void);
+int top(void);
+int up(void);
+int wleft(void);
+int wright(void);
+int noop(void);
+int digit(void);
+int open_after(void);
+int open_before(void);
+int delete_left(void);
+int join(void);
+int eword(void);
+int findleft(void);
+int findright(void);
+
+#undef CTRL
+#define CTRL(x)                ((x) & 0x1f)
+
+keytable_t modeless[] = {
+        { KEY_LEFT, 0, left },
+        { KEY_RIGHT, 0, right },
+        { KEY_DOWN, 0, down },
+        { KEY_UP, 0, up },
+        { CTRL('w'), 0, wleft },
+        { CTRL('e'), 0, wright },
+        { CTRL('n'), 0, pgdown },
+        { CTRL('p'), 0, pgup },
+        { CTRL('a'), NORPT, lnbegin },
+        { CTRL('d'), NORPT, lnend },
+        { CTRL('t'), NORPT, top },
+        { CTRL('b'), NORPT, bottom },
+        { KEY_BACKSPACE, 0, backsp },
+        { '\b', 0, backsp },
+        { KEY_DC, 0, delete },
+        { CTRL('f'), NORPT, file },
+        { CTRL('r'), NORPT, redraw },
+        { CTRL('\\'), NORPT, quit },
+        { CTRL('z'), NORPT, flip },
+        { 0, 0, insert }
+};
+
+/*
+ *     Basic vi commands missing (note not all below exactly match vi yet
+ *     in their behaviour - much testing is needed
+ *
+ *     CTRL-D/CTRL-U   half screen scrolls
+ *     CTRL-F/CTRL-B   implemented but not exactly as vi
+ *     nG              goto line n
+ *     W and S         word skip with punctuation
+ *     [] and ()       setence and paragraph skip
+ *     H M L           top/niddle/bottom of screen
+ *     R               replace mode
+ *     t               swap two characters
+ *     cw/cc/c.        change word, change line, change one char (+c$ etc)
+ *     C               change to end of line
+ *     s               substitute
+ *     u               undo
+ *     dw,dd,de,d$     delete word/space, delete line, delete word, delete
+ *                     to eol (and d. I think delete char) d^ delete to
+ *                     start of line
+ *     D               synonum for d$ (aka c$ = C)
+ *     f/Fc            move forward and back to char c
+ *     %               move to associated bracket pair
+ *     .               repeat last text changing command
+ *
+ *     All : functionality
+ *     All yank/put functionality
+ *     All regexps /?nN
+ *
+ *     Try to write ops as far as possible in terms of each other and a few
+ *     non-command 'ops. The goal is to make a lot of this macrocode for size.
+ */
+
+keytable_t modual[] = {
+        { KEY_LEFT, 0, left },
+        { KEY_RIGHT, 0, right },
+        { KEY_DOWN, 0, down },
+        { KEY_UP, 0, up },
+        {  27, NORPT, noop },
+        { 'h', 0, left },
+        { '\b', 0, left },
+        { 'j', 0, down },
+        { '\n', 0, down },
+        { 'k', 0, up },
+        { 'l', 0, right },
+        { ' ', 0, right },
+        { 'b', 0, wleft },
+        { 'e', 0, eword },
+        { CTRL('F'), 0, pgdown },      /* Need D for half screen too */
+        { CTRL('B'), 0, pgup },
+        { CTRL('U'), 0, pgup_half },
+        { 'w', 0, wright },
+        { '^', NORPT, lnbegin },
+        { '$', NORPT, lnend },
+        { 't', NORPT, top },           /* Should be 0G */
+        { 'b', NORPT, bottom },                /* Should be G  b is begin of word */
+        { 'i', NORPT, insert_mode },
+        { 'I', NORPT, insert_before },
+        { 'J', 0, join },
+        { 'x', 0, delete },
+        { 'X', 0, delete_left },
+        { 'o', 0, open_after },
+        { 'O', 0, open_before },
+        { 'W', NORPT, file },
+        { 'R', NORPT, redraw },
+        { 'Q', NORPT, quit },
+        { 'Z', NORPT, flip },
+        { 'd', 0, delete_line },       /* Should be dd */
+        { 'a', NORPT, append_mode },
+        { 'A', NORPT, append_end },
+        { 'r', 0, replace },
+        { 'F', NORPT, findleft },
+        { 'f', NORPT, findright },
+        { '0', KEEPRPT, digit },
+        { '1', KEEPRPT, digit },
+        { '2', KEEPRPT, digit },
+        { '3', KEEPRPT, digit },
+        { '4', KEEPRPT, digit },
+        { '5', KEEPRPT, digit },
+        { '6', KEEPRPT, digit },
+        { '7', KEEPRPT, digit },
+        { '8', KEEPRPT, digit },
+        { '9', KEEPRPT, digit },
+        { 0, KEEPRPT, noop }
+};
+
+
+char *ptr(int offset)
+{
+        if (offset < 0)
+                return (buf);
+        return (buf+offset + (buf+offset < gap ? 0 : egap-gap));
+}
+
+int pos(char *pointer)
+{
+        return (pointer-buf - (pointer < egap ? 0 : egap-gap));
+}
+
+int top(void)
+{
+        indexp = 0;
+        return 0;
+}
+
+int bottom(void)
+{
+        epage = indexp = pos(ebuf);
+        return 0;
+}
+
+int quit(void)
+{
+        done = 1;
+        return 0;
+}
+
+int redraw(void)
+{
+        clear();
+        display();
+        return 0;
+}
+
+int digit(void)
+{
+        repeat = repeat * 10 + input - '0';
+        return 0;
+}
+
+void movegap(void)
+{
+        char *p = ptr(indexp);
+        while (p < gap)
+                *--egap = *--gap;
+        while (egap < p)
+                *gap++ = *egap++;
+        indexp = pos(egap);
+}
+
+int prevline(int offset)
+{
+        char *p;
+        while (buf < (p = ptr(--offset)) && *p != '\n')
+                ;
+        return (buf < p ? ++offset : 0);
+}
+
+int nextline(int offset)
+{
+        char *p;
+        while ((p = ptr(offset++)) < ebuf && *p != '\n')        
+                ;
+        return (p < ebuf ? offset : pos(ebuf));
+}
+
+int adjust(int offset, int column)
+{
+        char *p;
+        int i = 0;
+        while ((p = ptr(offset)) < ebuf && *p != '\n' && i < column) {
+                i += *p == '\t' ? 8-(i&7) : 1;
+                ++offset;
+        }
+        return (offset);
+}
+
+int left(void)
+{
+        if (0 < indexp) {
+                --indexp;
+                return 0;
+        }
+        return 1;
+}
+
+int right(void)
+{
+        if (indexp < pos(ebuf)) {
+                ++indexp;
+                return 0;
+        }
+        return 1;
+}
+
+int up(void)
+{
+        indexp = adjust(prevline(prevline(indexp)-1), col);
+        return 0;      /* FIXME */
+}
+
+int down(void)
+{
+        indexp = adjust(nextline(indexp), col);
+        return 0;      /* FIXME */
+}
+
+int lnbegin(void)
+{
+        indexp = prevline(indexp);
+        return 0;
+}
+
+int lnend(void)
+{
+        indexp = nextline(indexp);
+        return left();         /* FIXME? if on end of last lie already ? */
+}
+/* We want to the top of the next page: in theory I believe we shouldn't
+   move at all unless we can move this far. Need to save pointers and test ? */
+int pgdown(void)
+{
+        /* Go to bottom of our page */
+        while(row < LINES) {
+                row++;
+                down();
+        }
+        lnbegin();
+        page = indexp;
+        epage = pos(ebuf);
+        return 0;
+}
+
+/* Move to the bottom of the previous page, unless cursor is within 1st page */
+/* Ditto need to save pointers and test ? */
+int pgup(void)
+{
+        int i = LINES;
+        /* Not quite right but will do for now */
+        if (page == 0)
+                return 1;
+        /* Go to the bottom of the page */
+        while(row < LINES) {
+                row++;
+                down();
+        }
+        /* Now go up a page, moving the page marker as we do
+           FIXME: just do the difference!!! */
+        while (0 < --i) {
+                page = prevline(page-1);
+                up();
+        }
+        return 0;
+}
+
+/* TODO FIXME */
+int pgup_half(void)
+{
+        int i = LINES/2;
+        while (0 < --i) {
+                page = prevline(page-1);
+                up();
+        }
+        return 0;
+}
+
+int wleft(void)
+{
+        char *p;
+        while (!isspace(*(p = ptr(indexp))) && buf < p)
+                --indexp;
+        while (isspace(*(p = ptr(indexp))) && buf < p)
+                --indexp;
+        return p == buf;
+}
+
+int eword(void)
+{
+        char *p;
+        while (!isspace(*(p = ptr(indexp))) && p < ebuf)
+                ++indexp;
+        return p == ebuf;
+}
+
+int wright(void)
+{
+        char *p;
+        eword();
+        while (isspace(*(p = ptr(indexp))) && p < ebuf)
+                ++indexp;
+        return p == ebuf;
+}
+
+int fleft(char c)
+{
+        char *p;
+        if (*(p = ptr(indexp)) == c && buf < p)
+                --indexp;
+        while (*(p = ptr(indexp)) != c && buf < p)
+                --indexp;
+        return p == buf;
+}
+
+int fright(char c)
+{
+        char *p;
+        if (*(p = ptr(indexp)) == c && p < ebuf)
+                ++indexp;
+        while (*(p = ptr(indexp)) != c && p < ebuf)
+                ++indexp;
+        return p == ebuf;
+}
+
+int findleft(void)
+{
+        int c = getch();
+        if (c < 0 || c > 255)
+                return 1;
+        return fleft(c);
+}
+
+int findright(void)
+{
+        int c = getch();
+        if (c < 0 || c > 255)
+                return 1;
+        return fright(c);
+}
+
+/* Do we need a filter on this and insert_mode ? */
+int insertch(char ch)
+{
+        movegap();
+        if (gap < egap) {
+                *gap++ = ch == '\r' ? '\n' : ch;
+                indexp = pos(egap);
+                return 0;
+        }
+        return 1;
+}
+
+int insert(void)
+{
+        return insertch(input);
+}
+
+int insert_before(void)
+{
+        lnbegin();
+        display();
+        return insert_mode();
+}
+
+/* Need to do a replace mode version */
+int insert_mode(void)
+{
+        int ch;
+        movegap();
+        while ((ch = getch()) != 27) {
+                if (ch == 0)
+                        continue;
+                if (ch == '\f')
+                        break;
+                if (ch == '\b') {
+                        if (buf < gap)
+                                --gap;
+                } else if (gap < egap) {
+                        *gap++ = ch == '\r' ? '\n' : ch;
+                }
+                indexp = pos(egap);
+                display();
+        }
+        return 0;
+}
+
+int append_mode(void)
+{
+        if (!right()) {
+                display();
+                return insert_mode();
+        }
+        return 0;
+}
+
+int append_end(void)
+{
+        lnend();
+        return append_mode();
+}
+
+int replace(void)
+{
+        int c = getch();
+        /* FIXME: saner filter ? */
+        if (c < 0 || c > 255)
+                return 1;
+        if (!delete())
+                return insertch(c);
+        return 0;
+}
+
+int delete_line(void)
+{
+        lnbegin();
+        while(egap < ebuf - 1 && egap[1] != '\n')
+                indexp = pos(++egap);
+        return 0;
+}
+
+int backsp(void)
+{
+        movegap();
+        if (buf < gap) {
+                --gap;
+                indexp = pos(egap);
+                return 0;
+        }
+        return 1;
+}
+
+int delete(void)
+{
+        movegap();
+        if (egap < ebuf) {
+                indexp = pos(++egap);
+                return 0;
+        }
+        return 1;
+}
+
+int delete_left(void)
+{
+        if (!left())
+                return delete();
+        return 1;
+}
+
+int join(void)
+{
+        lnend();
+        if (egap != ebuf)
+                return delete();
+        return 1;
+}
+
+int open_before(void)
+{
+        lnend();
+        if (!insertch('\n'))
+                return append_mode();
+        return 1;
+}
+
+int open_after(void)
+{
+        lnbegin();
+        if (!insertch('\n') && !up())
+                return append_mode();
+        return 0;
+}
+
+int file(void)
+{
+        if (!save(filename))
+                save(HUP);
+        return 0;
+}
+
+int save(char *fn)
+{
+        FILE *fp;
+        int i, ok;
+        size_t length;
+        fp = fopen(fn, "w");
+        if ((ok = fp != NULL)) {
+                i = indexp;
+                indexp = 0;
+                movegap();
+                length = (size_t) (ebuf-egap);
+                ok = fwrite(egap, sizeof (char), length, fp) == length;
+                (void) fclose(fp);
+                indexp = i;
+        }
+        return (ok);
+}
+
+int flip(void)
+{
+        table = table == modual ? modeless : modual;
+        return 0;
+}
+
+int noop(void)
+{
+        return 0;
+}
+
+void display(void)
+{
+        char *p;
+        int i, j;
+        if (indexp < page)
+                page = prevline(indexp);
+        if (epage <= indexp) {
+                page = nextline(indexp);
+                i = page == pos(ebuf) ? LINES-2 : LINES;
+                while (0 < i--)
+                        page = prevline(page-1);
+        }
+        move(0, 0);
+        i = j = 0;
+        epage = page;
+        while (1) {
+                if (indexp == epage) {
+                        row = i;
+                        col = j;
+                }
+                p = ptr(epage);
+                if (LINES <= i || ebuf <= p)
+                        break;
+                if (*p != '\r') {
+                        addch(*p);
+                        j += *p == '\t' ? 8-(j&7) : 1;
+                }
+                if (*p == '\n' || COLS <= j) {
+                        ++i;
+                        j = 0;
+                }
+                ++epage;
+        }
+        clrtobot();
+        while(++i < LINES)
+                mvaddstr(i, 0, "~");
+        move(row, col);
+        refresh();
+}
+
+int main(int argc, char *argv[])
+{
+        FILE *fp;
+        char *p = *argv;
+        int i = (int) strlen(p);
+        egap = ebuf = buf + BUF;
+        if (argc < 2)
+                return (2);
+        /* Find basename. */
+        while (0 <= i && p[i] != '\\' && p[i] != '/')
+                --i;
+        p += i+1;
+        if (strncmp(p, "vi", 2) == 0)
+                table = modual;
+        else if (strncmp(p, "ev", 2) == 0 || strncmp(p, "ev", 2) == 0)
+                table = modeless;
+        else
+                return (2);
+        if (initscr() == NULL)
+                return (3);
+        raw();
+        noecho();
+        idlok(stdscr, 1);
+        keypad(stdscr, 1);
+        fp = fopen(filename = *++argv, "r");
+        if (fp != NULL) {
+                gap += fread(buf, sizeof (char), (size_t) BUF, fp);
+                fclose(fp);
+        }
+        top();
+        while (!done) {
+                display();
+                i = 0;
+                input = getch();
+                while (table[i].key != 0 && input != table[i].key)
+                        ++i;
+                if (!repeat || (table[i].flags & NORPT))
+                        (*table[i].func)();
+                else while(repeat--)
+                        (*table[i].func)();
+                if (!(table[i].flags & KEEPRPT))
+                        repeat = 0;
+        }
+        endwin();
+        return (0);
+}
diff --git a/Applications/util/vile.doc b/Applications/util/vile.doc
new file mode 100644 (file)
index 0000000..a5e5dc6
--- /dev/null
@@ -0,0 +1,137 @@
+This is now obsolete but retained for the moment
+
+
+NAME
+        ae        Ant's Editor  Mar '92
+
+
+USAGE
+        ae <filename>        
+        ea <filename>
+
+where <filename> is the text file to edit or create.
+
+
+DESCRIPTION
+<< ae >> is a full screen, VI style text editor.  The source should be
+portable to any environment that provides a K&R C compiler and a
+CURSES library.  << ea >> is a full screen, EMACS style text editor.
+
+Text files consists of lines of printable text or tab characters.  
+A line can be of arbitary length and is delimited by either a
+newline or the end of file.  Carriage return is mapped to newline
+on input and ignored on output.  Tab stops are every eight columns.
+Non-printable characters may have unpredictable results depending
+on the implementation of CURSES.
+
+
+COMMANDS
+
+VI STYLE
+h j k l                left, down, up, right cursor movement
+H J K L                word left, page down, page up, word right
+[ ]                beginning and end of line
+t b                top and bottom of file
+i                enter insert mode, formfeed to quit
+x                delete character under the cursor
+W                write buffer to file
+R                refresh the screen
+Q                quit
+Z                switch to EMACS style
+
+EMACS STYLE
+cursor keys        left, down, up, right cursor movement
+^W ^E                word left,  word right
+^N ^P                page down, page up
+^A ^D                beginning and end of line
+^T ^B                top and bottom of file
+backspace        delete character left of the cursor
+delete                delete character under the cursor
+^F                write buffer to file
+^R                refresh the screen
+^\                quit
+^Z                switch to VI style
+
+
+EXIT STATUS
+0        success
+1        general error
+2        usage error
+3        CURSES screen initialization failed
+
+
+INSTALLATION
+Requires K&R C and a CURSES library for the given target machine.
+
+The file creation MODE should be set at compile time to 0600 for
+Unix systems, or 0 for the Atari ST and PC.
+
+The BUF size should be set at compile time to 32767.  This value
+was used because the Sozobon C compiler for the Atari ST has 16
+bit ints and a limit on the size of arrays & structures of 32k.  
+Also the WatCom C compiler for the PC also has 16 bits ints.  On
+machines that have 32 bit ints (most unix boxes), a larger value
+for BUF could be used.
+
+It is recommend that compact memory model be used on PC class
+machines.  Small memory model may work too provided BUF is not
+too large.
+
+The character constants '\b', '\f', '\n', '\r', '\t' are used
+in order to provide more portable code, since the compiler should
+handle the translation of them into the native character set.
+Note that '\f' (formfeed) was used to exit insert mode because
+K&R C had no escape constant for the escape-key.
+
+Note that the "int " definition at the top of the source was for
+the benifit of Turbo C, which returns lots of warnings.  WatCom C
+should return no warnings provided your curses.h has no NULL
+expression defined for functions.  System V and BSD should also
+generate no warnings.  The source should pass basic linting.
+The source still looks like a mess when passed through our
+C beautifier (your milleage may vary).
+
+On EBCDIC machines text files are made up of fixed length records.
+My code should be portable in terms of character set and compiler
+independance, however the Buffer Gap Scheme assumes a stream file
+format used for UNIX text files and most personal computers.  A bit
+of alteration may be required if the C library routinues for read
+and write() do not hide this fact.  Switching to the stream i/o
+library may solve this problem.  One other problem that was not
+addressed concerns block mode terminals.
+
+My goals for this project was to learn and experiment with the
+Buffer Gap Scheme [Fin80][net90], write a useful and *portable*
+programme, and meet the requirements of the IOCCC.  I initially
+planned to have a mini CURSES built-in like the IOCCC Tetris entry
+from a previous year, however this was not as portable as using a
+CURSES library with TERMINFO/TERMCAP support.  
+
+
+BUGS
+This editor will display a file with long lines, but has trouble
+scrolling the screen with long lines.  Paging up and down should
+work correctly, however.
+
+
+REFERENCES
+[Fin80]        Craig A. Finseth, "Theory and Practice of Text Editors or
+        A Cookbook For An EMACS", TM-165, MIT Lab. for Computer
+        Science
+
+[KeP81]        Kernighan & Plauger, "Software Tools in Pascal",
+        Addison-Wesley, 81, chapter 6
+
+[Mil86]        Eugene W. Myers & Webb Miller, "Row-replacement Algorithums
+        for Screen Editors", TR 86-19, Dept. of Compter Science,
+        U. of Arizona
+
+[MyM86]        Eugene W. Myers & Webb Miller, "A simple row-replacement
+        method", TR 86-28, Dept. of Compter Science, U. of Arizona
+
+[Mil87]        Webb Miller, "A Software Tools Sampler", Prentice Hall, 87
+        ISBN 0-13-822305-X, chapter 5
+
+[net90]        "Editor 101/102" articles from comp.editors
+
+