####################################################### 120
From martind Thu Sep 28 21:37:10 1989
To: stevewo
Subject: ansi.c
Date: Thu Sep 28 21:34:28 1989


#include <stdio.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/machdep.h>
#include <signal.h>
#include <termio.h>
#include <ctype.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <errno.h>
#include "../include/message.h"
#include "../include/client.h"

/*
** - character definitions for ANSI 3.64
*/
#define ESC     0x1b    /* escape */
#define CBT     0x5a    /* backward tabulation */               /* M001 */
#define CPL     0x46    /* cursor to previous line */
#define CNL     0x45    /* cursor to next line */
#define CUB     0x44    /* cursor back */
#define CUF     0x43    /* cursor forward */
#define CUU     0x41    /* cursor up */
#define CUD     0x42    /* cursor down */
#define CUP     0x48    /* cursor position */
#define DCH     0x50    /* delete character */
#define DL      0x4d    /* delete line */
#define ECH     0x58    /* erase character */
#define ED      0x4a    /* erase display */
#define EL      0x4b    /* erase line */
#define ICH     0x40    /* insert character */
#define IL      0x4c    /* insert line */
#define SU      0x53    /* scroll up */
#define SD      0x54    /* scroll down */
#define SGR     0x6d    /* select graphic rendition */

/* states of the finite state machine */
#define NOCMD  1        /* type of crt state - most chars will go onto screen */
#define ESCED   2       /* we've seen an ESC, waiting for rest of CSI */
#define PARAMS  3       /* we're building the parameter list */


#define NROWS   LYSIZE  /* number of rows */
#define NCOLS   LXSIZE  /* number of columns */

#define NPARMS 3        /* max # of params */

int pt_cy = 1, pt_cx = 1;       /* current active position */
int pt_param[NPARMS];           /* parameter list */
int pt_pnum;                    /* index of parameter we're building */
int pt_state = NOCMD;           /* state of machine */

/*
** Basic concepts:
**      The screen consists of rows and columns
**      columns are numbered from the left, starting with one.
**      rows on the screen are numbered from the top, starting with one.
**      Thus, the home position in the upper left corner is row one, column one.
**
**      Associated with each screen is the 'current active position'. 
**      It corresponds roughly to the cursor; in fact, after each call to screen
**      the cursor will indicate the active position.  Thus, 
**      the cursor movements really change the active position, and 
**      the cursor follows the change.
**
**      This code implements a finite state machine that reads a stream of 
**      characters, and emits commands that alter the screen.  All of these
**      commands are issued via calls through the 'crtsw' array.  Each element
**      of this array consists of an aggregate of functions which are 
**      responsible for making the appropriate changes to the actual screen.
**
**      The functions in the aggregate and their responsibities are:
**
**      v_scroll(i)     
**              scroll the text on the screen i lines.
**              This will move some lines off the screen, and some blank lines
**              onto the screen.  i may be negative, indicating that the text
**              moves downward, and blank lines appear at the top.
**      v_copy(sr, sc, dr, dc, cnt)
**              sr and sc specify a source row and column.
**              dr and dc specify a destination row and column.
**              Count characters are copied from the source to the dest,
**              with the copy proceeding from left to right, and top to bottom.
**              If the source and destination overlap, the copy is done 
**              correctly.
**      v_clear(r, c, cnt)
**              Characters starting at row r and column are cleared to the 
**              space character. 
**      v_pchar(r, c, ch)
**              The character ch is placed on the screen at row r, column c,
**              using the current graphic rendition.
**              return value is number of character positions to adjust active
**              position by - zero means the character has no graphic 
**              representation.
**      v_scurs(r, c)
**              The cursor is moved to row r, column c.
**      v_init()
**              The screen and all data structures are initialized.
**      v_sgr(i)
**              The current graphic rendition (e. g. font, color) is set to
**              that specified by i. See ANSI x3.64 for encoding.
*/

/*
** screen(cp, cnt) - pass characters to the finite state machine
**      cp points to the array of characters
**      cnt indicates how many characters are being passed.
*/      
static  unsigned short ignore_next_char = 0;

screen(cs, lp, cp, cnt)
struct handle *cs;
int lp;
char *cp;
int cnt;
{
        register char c;
        int work;
        char *ep, *ip;
        int ix, iy;

        ip = cp;
        ep = cp;
        ix = pt_cx;
        iy = pt_cy;

        while ( cnt-- ) {
                c = *cp++;
                                                /* M001 */
                if (ignore_next_char)
                {
                        ignore_next_char = 0;
                        continue;
                }
                switch ( pt_state ) {
                case NOCMD:
                        if ( c == ESC ) {
                                /* Flush */
                                lputs(cs, lp, ix-1, iy-1, ip, (ep-ip), SA_BONW);
                                pt_state = ESCED;
                                break; 
                        } else {
                                if( c >= ' ' ){
                                        ep++;
                                        pt_cx++; 
                                }else{
                                        /* Flush */
                                        if( ep > ip )
                                                lputs(cs, lp, ix-1, iy-1, ip, (ep-ip), SA_BONW);

                                        switch(c) {
                                        case '\n':
                                                pt_cy += 1;
                                                break;
                                        case '\r':
                                                pt_cx = 1;
                                                break;
                                        case '\b':
                                                if ( pt_cx > 1 )
                                                        pt_cx -= 1;
                                                break;
                                        case '\t':
                                                pt_cx += (8 - ((pt_cx - 1) % 8));
                                                break;
                                        case '\07':
                                                lbeep(cs);
                                                break;
                                        default:
                                                break;
                                        } /* end switch */
                                        ip = ep = cp;
                                        ix = pt_cx;
                                        iy = pt_cy;
                                }
                        } 
                        break;
                case ESCED:
                        switch(c) {
                        case '[':
                                pt_state = PARAMS;
                                clrparam();
                                break;
                        default:
                                pt_state = NOCMD;
                                ip = ep = cp;
                                ix = pt_cx;
                                iy = pt_cy;
                        }
                        break;
                case PARAMS:
                        if ( c >= '0' && c <= '9' ) {
                                pt_param[pt_pnum] *= 10;
                                pt_param[pt_pnum] += (c - '0');
                                break;
                        } else if (c == ';') {
                                if ( pt_pnum < (NPARMS - 1) )
                                        pt_pnum += 1;
                                else{
                                        pt_state = NOCMD;
                                        ip = ep = cp;
                                        ix = pt_cx;
                                        iy = pt_cy;
                                }
                                break;
                        } else {
                                ansicmd(cs, lp, c);
                                pt_state = NOCMD;
                                ip = ep = cp;
                                ix = pt_cx;
                                iy = pt_cy;
                        }
                        break;
                }
                /* if past right hand edge, move left and down */
                if ( pt_cx > NCOLS ) {
                        pt_cy += (pt_cx / NCOLS);
                        pt_cx = ((pt_cx - 1) % NCOLS) + 1;
                } else if ( pt_cx < 1 ) {
                        pt_cx = 1;
                }

                /* if off screen, scroll */
                if ( pt_cy < 1 ) {
                        lscroll(cs, lp, pt_cy - 1, SA_BONW);
                        pt_cy = 1;
                } else if ( pt_cy > NROWS ) {
                        if( ep > ip )
                                lputs(cs, lp, ix-1, iy-1, ip, (ep-ip), SA_BONW);
                        lscroll(cs, lp, pt_cy - NROWS, SA_BONW);
                        pt_cy = NROWS;
                        ip = ep = cp;
                        ix = pt_cx;
                        iy = pt_cy;
                }
        }
        /* Flush */
        if( ep > ip )
                lputs(cs, lp, ix-1, iy-1, ip, (ep-ip), SA_BONW);
        lcursor(cs, lp, pt_cx-1, pt_cy-1);
}

/*
** clrparam(lp) - clear the parameters for a screen
**      lp points to the screen's crt struct
*/
clrparam()
{
        register int i;

        for ( i = 0; i < NPARMS; i += 1)
                pt_param[i] = 0;
        pt_pnum = 0;
}
                

/*
** ansicmd(c, lp) - perform some ANSI 3.64 function, using the parameters
**                                       we've just gathered.
**      c is the character that indicates the function to be performed
**      lp indicates the screen to perform the function on.
*/
ansicmd(cs, lp, c)
struct handle *cs;
int lp;
register char c;
{
        register int    i, col;

        switch ( c ) {

        case CPL:       /* cursor to previous line */
                pt_cy -= range(pt_param[0], 1, 1, NROWS);
                pt_cx = 1;
                break;

        case CNL:       /* cursor to next line */
                pt_cy += range(pt_param[0], 1, 1, NROWS);
                pt_cx = 1;
                break;

        case CUB:       /* cursor backward */
                pt_cx -= range(pt_param[0], 1, 1, pt_cx - 1);
                break;

        case CUF:       /* cursor forward */
                pt_cx += range(pt_param[0], 1, 1, NCOLS - pt_cx);
                break;

        case CUU:       /* cursor up */
                pt_cy -= range(pt_param[0], 1, 1, pt_cy - 1);
                break;

        case CUD:       /* cursor down */
                pt_cy += range(pt_param[0], 1, 1, NROWS - pt_cy);
                break;

        case CUP:       /* cursor position */
                pt_cy = range(pt_param[0], 1, 1, NROWS);
                pt_cx = range(pt_param[1], 1, 1, NCOLS);
                break;

                                                        /* M001 */
        case CBT:       /* tab backwards */
                col = pt_cx - 1;
                i = range(pt_param[0], 1, 1, (col + 7) >> 3);
                if (col & 7)
                {
                        pt_cx = (col & ~7) + 1;
                        --i;
                }
                pt_cx -= (i << 3);
                break;
        case DCH:       /* delete character */
                pt_param[0] = range(pt_param[0], 1, 1, (NCOLS - pt_cx) + 1);
                if ( pt_cx + pt_param[0] <= NCOLS ) {
                        lcopy(cs, lp, pt_cx+pt_param[0]-1, pt_cy-1,
                                pt_cx-1, pt_cy-1, NCOLS-(pt_cx+pt_param[0]-1));
                }
                lclear(cs, lp, NCOLS-pt_param[0], pt_cy-1, 
                                                pt_param[0], SA_BONW);
                break;
                
        case DL:        /* delete line */
                pt_param[0] = range(pt_param[0], 1, 1, (NROWS - pt_cy) + 1);
                /* copy lines up */
                if ( pt_cy + pt_param[0] <= NROWS ) {
                        lcopy(cs, lp, 0, pt_cy+pt_param[0]-1, 0, pt_cy-1, 
                                        NCOLS*(NROWS-(pt_cy+pt_param[0]-1)));
                }
                /* clear new stuff */
                lclear(cs, lp,  0, NROWS-pt_param[0], 
                                        NCOLS*pt_param[0], SA_BONW);
                break;

        case ECH:       /* erase character */
                pt_param[0] = range( pt_param[0], 1, 1, (NCOLS - pt_cx) + 1);
                lclear(cs, lp, pt_cx-1, pt_cy-1, pt_param[0], SA_BONW);
                break;
        case ED:        /* erase display */
                switch(pt_param[0]) {
                case 0:
                        lclear(cs, lp, pt_cx-1, pt_cy-1, 
                                ((NROWS - pt_cy) * NCOLS) +
                                ((NCOLS - pt_cx) + 1 ), SA_BONW);
                        break;
                case 1:
                        lclear(cs, lp, 0, 0, (pt_cy-1)*NCOLS+pt_cx, SA_BONW);
                        break;
                case 2:
                        lfill(cs, lp, SA_BONW);
                        pt_cy = 1;
                        pt_cx = 1;
                        break;
                }
                break;
        case EL:
                switch(pt_param[0]) {
                case 0: /* ap to end */
                        lclear(cs, lp, pt_cx-1, pt_cy-1,
                                        (NCOLS - pt_cx) + 1, SA_BONW);
                        break;
                case 1: /* start to ap */
                        lclear(cs, lp, 0, pt_cy-1, pt_cx, SA_BONW);
                        break;
                case 2: /* whole line */
                        lclear(cs, lp, 0, pt_cy-1, NCOLS, SA_BONW);
                        break;
                }
                break;
                                
        case ICH:       /* insert character */
                pt_param[0] = range( pt_param[0], 1, 1, (NCOLS - pt_cx) + 1);
                if ( pt_cx + pt_param[0] <= NCOLS ) {
                        lcopy(cs, lp, pt_cx-1, pt_cy-1, pt_cx+pt_param[0]-1, 
                                        pt_cy-1, NCOLS-(pt_cx+pt_param[0]-1));
                }
                lclear(cs, lp, pt_cx-1, pt_cy-1, pt_param[0], SA_BONW);
                break;
        case IL:        /* insert line */
                pt_param[0] = range(pt_param[0], 1, 1, (NROWS - pt_cy) + 1);
                /* copy lines down */
                if ( pt_cy + pt_param[0] <= NROWS ) {
                        lcopy(cs, lp, 0, pt_cy-1, 0, pt_cy+pt_param[0]-1, 
                                NCOLS * ( NROWS-(pt_cy+pt_param[0]-1)));
                }
                /* clear new stuff */
                lclear(cs, lp, 0, pt_cy-1, NCOLS * pt_param[0], SA_BONW);
                break;
        case SU:        /* scroll up */
                pt_param[0] = range(pt_param[0], 1, 1, NROWS);
                lscroll(cs, lp, pt_param[0], SA_BONW);
                break;
        case SD:        /* scroll down */
                pt_param[0] = -range(pt_param[0], 1, 1, NROWS);
                lscroll(cs, lp, pt_param[0], SA_BONW);
                break;
        case SGR:
#ifdef ENSGR
                lsgr(pt_param[0]);
                                                        /* M002 */
                for (i = 1; i < NPARMS; i++)
                {
                        if (pt_param[i])
                                lsgr(pt_param[i]);
                        else
                                break;
                }
#endif
                break;
        }
}

/*
** range(val, default, min, max) - restrict a value to a range, or supply a
**                                                                 default
**      val is the value to be restricted.
**      default is the value to be returned if val is zero
**      min is the minimum value
**      max is the maximum value
*/
range( val, def, min, max)
register int val, def;
int min, max;
{
        if ( val == 0 )
                return def;
        if ( val < min )
                return min;
        if ( val > max )
                return max;
        return val;
}

