Monitor source part 8 of 9
jct
jct at jct.UUCP
Tue Jul 11 14:33:00 AEST 1989
#! /bin/sh
# This is a shell archive. Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file". To overwrite existing
# files, type "sh file -c". You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g.. If this archive is complete, you
# will see the following message at the end:
# "End of archive 8 (of 9)."
# Contents: lib/scrout1.c
# Wrapped by jct@ on Mon Jul 10 22:48:32 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'lib/scrout1.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'lib/scrout1.c'\"
else
echo shar: Extracting \"'lib/scrout1.c'\" \(37438 characters\)
sed "s/^X//" >'lib/scrout1.c' <<'END_OF_FILE'
X/* module screen output */
X
X/*
X provides UNIX "curses" type routines
X define MMAPPED if output is memory mapped
X*/
X
X/*
X created May 5, 1987 by JCT
X*/
X
X/*
X * Copyright (c) John C. Tompkins 1989
X * All rights reserved.
X *
X * Permission is granted to use this for any personal, noncommercial use.
X * You may not distribute source or executable code for profit, nor
X * may you distribute it with a commercial product without the prior
X * written consent of the author. Please send modifications to the
X * author for inclusion in updates to the program.
X */
X
X#include <stdio.h>
X#include <fcntl.h>
X#include <ctype.h>
X
X#include <km/defs.h>
X#include <km/ascii.h>
X#include <km/scrio.h>
X#include <km/string1.h>
X
X#ifdef DOS /* must come after <km/defs.h> */
X#include <dos.h>
X#endif
X
X/*
X The following are masks and forground, background color bits
X*/
X
X#define A_MASK 0x00ff /* attribute flags mask */
X#define C_MASK 0x00ff /* color flags mask */
X#define F_MASK 0x00f0 /* 16 colors, 4 bits */
X#define B_MASK 0x000f /* 16 colors, 4 bits */
X#define F_BLACK 0x0000
X#define F_RED 0x0010
X#define F_GREEN 0x0020
X#define F_YELLOW 0x0030
X#define F_BLUE 0x0040
X#define F_MAGENTA 0x0050
X#define F_CYAN 0x0060
X#define F_WHITE 0x0070
X#define F_C8 0x0080
X#define F_C9 0x0090
X#define F_C10 0x00a0
X#define F_C11 0x00b0
X#define F_C12 0x00c0
X#define F_C13 0x00d0
X#define F_C14 0x00e0
X#define F_C15 0x00f0
X#define B_BLACK 0x0000
X#define B_RED 0x0001
X#define B_GREEN 0x0002
X#define B_YELLOW 0x0003
X#define B_BLUE 0x0004
X#define B_MAGENTA 0x0005
X#define B_CYAN 0x0006
X#define B_WHITE 0x0007
X#define B_C8 0x0008
X#define B_C9 0x0009
X#define B_C10 0x000a
X#define B_C11 0x000b
X#define B_C12 0x000c
X#define B_C13 0x000d
X#define B_C14 0x000e
X#define B_C15 0x000f
X#define OUTBUF_SIZE 256
X#define IOUTBUF_SIZE 20
X#define COLOR_SIZE 16
X#define BOX_SIZE 11
X
typedef struct
X {
X char *name;
X short value;
X } COLOR_ENTRY;
X
typedef struct
X {
X int fg;
X int bg;
X } COLOR_BITS;
X
int LINES, COLS;
int have_standout, have_blink, have_bold, have_underline;
int have_color, have_graphic, rev_color;
int def_fg = WHITE, def_bg = BLACK;
WINDOW *stdscr = WIN_NULL;
WINDOW *curscr = WIN_NULL;
X#ifdef UNIX
KEY_TBL key_tbl[KEY_TBL_SIZE];
int key_tbl_index;
X#endif
X
X#ifdef UNIX
static char termcap[1024], tc_tbl[1024], *tc_ptr;
X#endif
X#ifndef MMAPPED
static char *_al, *_cd, *_ce, *_ci, *_cl, *_cm, *_dc, *_dl, *_do,
X *_ei, *_im, *_is, *_nd, *_se, *_sf, *_so, *_sr, *_te,
X *_ti, *_ue, *_us, *_ve, *_vs, *_sc, *_gs, *_ge, *_bs,
X *_be, *_hs, *_he;
static char outbuf[OUTBUF_SIZE];
static char *pout = outbuf;
static char *color_tbl[COLOR_SIZE];
static int outbuf_count = 0, pfast = TRUE;
static short *c_color, *c_attrib, *c_y_cur, *c_x_cur, c_x_max;
X#endif
static char *_ke, *_ks, *_gb;
static char ioutbuf[IOUTBUF_SIZE];
static char *piout = ioutbuf;
static int ioutbuf_count = 0;
static char box_tbl[BOX_SIZE];
static char fake_tbl[BOX_SIZE] = {'-', '|', '+', '+', '+', '+', '+', '+', '+', '+', '+'};
static int LAST_y, LAST_x;
X
static COLOR_ENTRY color_table[] =
X {
X "BLACK", BLACK,
X "RED", RED,
X "GREEN", GREEN,
X "YELLOW", YELLOW,
X "BLUE", BLUE,
X "MAGENTA", MAGENTA,
X "CYAN", CYAN,
X "WHITE", WHITE,
X "COLOR_8", COLOR_8,
X "COLOR_9", COLOR_9,
X "COLOR_10", COLOR_10,
X "COLOR_11", COLOR_11,
X "COLOR_12", COLOR_12,
X "COLOR_13", COLOR_13,
X "COLOR_14", COLOR_14,
X "COLOR_15", COLOR_15,
X "UNDETERMINED", UNDETERMINED,
X 0, 0
X };
X
static COLOR_BITS bits_tbl[] =
X {
X F_BLACK, B_BLACK,
X F_RED, B_RED,
X F_GREEN, B_GREEN,
X F_YELLOW, B_YELLOW,
X F_BLUE, B_BLUE,
X F_MAGENTA, B_MAGENTA,
X F_CYAN, B_CYAN,
X F_WHITE, B_WHITE,
X F_C8, B_C8,
X F_C9, B_C9,
X F_C10, B_C10,
X F_C11, B_C11,
X F_C12, B_C12,
X F_C13, B_C13,
X F_C14, B_C14,
X F_C15, B_C15,
X };
X
extern char *malloc(), *getenv(), *tgetstr(), *strcpy();
extern void free();
extern FILE *popen(), *fopen();
X
X/*
X The following are support routines
X*/
X
X#ifndef MMAPPED
X
X#define _puts(s) tputs(s, 0, _putchar)
X
static void _flush_outbuf() /* flush physical output buffer */
X{
X#ifdef DOS
X int i;
X
X for (i = 0; i < outbuf_count; i++)
X bdos(0x06, outbuf[i], 0);
X pout = outbuf;
X outbuf_count = 0;
X#else
X if (outbuf_count > 0)
X {
X write(_tty_ch, outbuf, outbuf_count);
X pout = outbuf;
X outbuf_count = 0;
X }
X#endif
X}
X
static void _putchar(data) /* physical output of data */
X int data;
X{
X *pout++ = data;
X if (++outbuf_count >= OUTBUF_SIZE)
X _flush_outbuf();
X}
X
static void _putstr(data)
X REGISTER char *data;
X{
X if (data)
X {
X while (*data)
X {
X *pout++ = *data++;
X if (++outbuf_count >= OUTBUF_SIZE)
X _flush_outbuf();
X }
X }
X}
X
X#endif
X
static void _iflush_outbuf() /* flush immediate output buffer */
X{
X#ifdef DOS
X int i;
X
X for (i = 0; i < ioutbuf_count; i++)
X bdos(0x06, ioutbuf[i], 0);
X piout = ioutbuf;
X ioutbuf_count = 0;
X#else
X if (ioutbuf_count > 0)
X {
X write(_tty_ch, ioutbuf, ioutbuf_count);
X piout = ioutbuf;
X ioutbuf_count = 0;
X }
X#endif
X}
X
static void _iputchar(data) /* physical output immediate */
X int data;
X{
X *piout++ = data;
X if (++ioutbuf_count >= IOUTBUF_SIZE)
X _iflush_outbuf();
X}
X
static void _iputs(s) /* immediate output string */
X char *s;
X{
X tputs(s, 0, _iputchar);
X _iflush_outbuf();
X}
X
X#ifndef MMAPPED
X
static void _goto(y, x) /* physical cursor positioning */
X int y;
X int x;
X{
X REGISTER WINDOW *p;
X
X p = curscr;
X if (p->y_cur == y)
X {
X if ((p->x_cur - 1) == x)
X {
X if (BC)
X _puts(BC);
X else
X _putchar(BS);
X }
X else if ((p->x_cur + 1) == x)
X {
X if (_nd)
X _puts(_nd);
X else
X _puts(tgoto(_cm, x, y));
X }
X else
X _puts(tgoto(_cm, x, y));
X }
X else if (p->x_cur == x)
X {
X if ((p->y_cur - 1) == y)
X {
X if (UP)
X _puts(UP);
X else
X _puts(tgoto(_cm, x, y));
X }
X else if ((p->y_cur + 1) == y)
X {
X if (_do)
X _puts(_do);
X else
X _puts(tgoto(_cm, x, y));
X }
X else
X _puts(tgoto(_cm, x, y));
X }
X else
X _puts(tgoto(_cm, x, y));
X p->y_cur = y;
X p->x_cur = x;
X}
X
static void _setcolor(color) /* physical set terminal color */
X int color;
X{
X REGISTER char *ptr1, *ptr2;
X
X if (ptr1 = _sc)
X {
X while (*ptr1)
X {
X if (*ptr1 == '%')
X {
X if (*(ptr1 + 1) == 'f')
X {
X ptr2 = color_tbl[(color >> 4) & 0x0f];
X while (*ptr2)
X _putchar(*ptr2++);
X ptr1++;
X }
X else if (*(ptr1 + 1) == 'b')
X {
X ptr2 = color_tbl[color & 0x0f];
X while (*ptr2)
X _putchar(*ptr2++);
X ptr1++;
X }
X else if (*(ptr1 + 1) == '%')
X {
X _putchar('%');
X ptr1++;
X }
X }
X else
X _putchar(*ptr1);
X ptr1++;
X }
X }
X curscr->color = color;
X}
X
static void _setattrib(attrib, color) /* physical set terminal attributes */
X REGISTER int attrib;
X int color;
X{
X REGISTER short *p;
X
X p = &curscr->attrib;
X if ((*p & GRAPHIC) && !(attrib & GRAPHIC))
X _puts(_ge);
X if (!(rev_color && color) && (*p & STANDOUT) && !(attrib & STANDOUT))
X _puts(_se);
X else if (*p & BLINK)
X _puts(_be);
X else if (*p & BOLD)
X _puts(_he);
X else if (*p & UNDERLINE)
X _puts(_ue);
X if (!(*p & GRAPHIC) && (attrib & GRAPHIC))
X _puts(_gs);
X if (!(rev_color && color) && !(*p & STANDOUT) && (attrib & STANDOUT))
X _puts(_so);
X else if (attrib & BLINK)
X _puts(_bs);
X else if (attrib & BOLD)
X _puts(_hs);
X else if (attrib & UNDERLINE)
X _puts(_us);
X *p = attrib;
X#ifdef DOS
X curscr->color = 0;
X#endif
X}
X
static int so_analyze() /* check standout ability */
X{
X char *so_ptr;
X
X if (!_se)
X _so = NULL;
X if (so_ptr = _so)
X {
X while (isdigit(*so_ptr))
X so_ptr++;
X if (!isprint(*so_ptr))
X return(TRUE);
X else
X {
X _so = NULL;
X _se = NULL;
X return(FALSE);
X }
X }
X else
X return(FALSE);
X}
X
static int sc_analyze() /* check and set color parameters */
X{
X char *temp;
X int i, all_em = FALSE, fg = FALSE, bg = FALSE;
X
X if (!_sc)
X return(FALSE);
X temp = _sc;
X for (i = 0; i < COLOR_SIZE; i++)
X color_tbl[i] = NULL;
X while (*temp)
X {
X if (*temp == '%')
X {
X if (*(temp + 1) == 'f')
X {
X fg = TRUE;
X temp++;
X }
X else if (*(temp + 1) == 'b')
X {
X bg = TRUE;
X temp++;
X }
X else if (*(temp + 1) == 'c')
X {
X *temp = '\0';
X temp += 2;
X i = 0;
X while (*temp && (i < COLOR_SIZE))
X {
X color_tbl[i] = temp;
X while (*temp && (*temp != ','))
X temp++;
X *temp++ = '\0';
X i++;
X }
X all_em = (i == COLOR_SIZE);
X }
X }
X temp++;
X }
X if (!fg || !bg || !all_em)
X {
X _sc = NULL;
X return(FALSE);
X }
X else
X return(TRUE);
X}
X
X#endif /* ifndef MMAPPED */
X
static int gb_analyze() /* check and set box parameters */
X{
X char *temp;
X int i;
X
X#ifndef MMAPPED
X if (!_gb || !_gs || !_ge)
X {
X _gs = NULL;
X _ge = NULL;
X return(FALSE);
X }
X#endif
X temp = _gb;
X for (i = 0; i < BOX_SIZE; i++)
X box_tbl[i] = UNDETERMINED;
X i = 0;
X while (*temp && (i < BOX_SIZE))
X {
X box_tbl[i] = *temp;
X temp++;
X i++;
X }
X if (i != BOX_SIZE)
X {
X#ifndef MMAPPED
X _gs = NULL;
X _ge = NULL;
X#endif
X return(FALSE);
X }
X else
X return(TRUE);
X}
X
X#ifndef MMAPPED
X
static int us_analyze()
X{
X if (_us && _ue)
X return(TRUE);
X _us = NULL;
X _ue = NULL;
X return(FALSE);
X}
X
static int bs_analyze()
X{
X if (_bs && _be)
X return(TRUE);
X _bs = NULL;
X _be = NULL;
X return(FALSE);
X}
X
static int hs_analyze()
X{
X if (_hs && _he)
X return(TRUE);
X _hs = NULL;
X _he = NULL;
X return(FALSE);
X}
X
X#endif /* if MMAPPED */
X
X#ifdef UNIX
static void init_key_tbl()
X{
X key_tbl_index = 0;
X}
X
static void add_key_tbl(name, default_seq, value)
X char *name;
X char *default_seq;
X int value;
X{
X
X/*
X adds an ASCII keysequence to the lookup table
X used to decipher arrows keys, function keys etc...
X*/
X
X if (key_tbl_index < KEY_TBL_SIZE)
X {
X if (!name || (!(key_tbl[key_tbl_index].str = tgetstr(name, &tc_ptr))))
X key_tbl[key_tbl_index].str = default_seq;
X key_tbl[key_tbl_index].key = value;
X key_tbl_index++;
X }
X}
X#endif
X
X/*
X The following are standard "curses" like routines that use "static"
X data or functions. All other functions are in other files.
X*/
X
int endwin() /* end screen modes */
X{
X WINDOW *next;
X
X for (next = stdscr->next; next && next->next; next = next->next)
X ;
X for (; next && next->prev; next = next->prev)
X delwin(next, FALSE);
X wendattrib(stdscr);
X wsetcolor(stdscr, def_fg, def_bg);
X werase(stdscr);
X#ifndef MMAPPED
X werase(curscr);
X#endif
X wrefresh(stdscr);
X keypadend();
X#ifndef MMAPPED
X if (_te)
X _puts(_te);
X if (_ve)
X _puts(_ve);
X _flush_outbuf();
X#endif
X resetty();
X return(TRUE);
X}
X
WINDOW *initscr() /* begin screen modes */
X{
X char *ttptr;
X
X if (!stdscr)
X {
X _tty_ch = fileno(stdout);
X#ifdef DOS
X ttptr = Def_term;
X#else
X if (My_term || ((ttptr = getenv("TERM")) == NULL))
X ttptr = Def_term;
X#endif
X if (!setterm(ttptr))
X return(WIN_NULL);
X#ifdef MMAPPED
X if ((curscr = (WINDOW*)malloc(sizeof(WINDOW))) == WIN_NULL)
X return(WIN_NULL);
X curscr->buf_len = 0;
X curscr->y_max = LINES;
X curscr->x_max = COLS;
X curscr->y_org = 0;
X curscr->x_org = 0;
X curscr->y_cur = 0;
X curscr->x_cur = 0;
X curscr->ch_cur = 0;
X curscr->ch_first = 0;
X curscr->ch_last = 0;
X curscr->color = 0;
X curscr->attrib = 0;
X curscr->ch_flags = 0;
X curscr->flags = 0;
X curscr->prev = 0;
X curscr->next = 0;
X#else
X if ((curscr = newwin(LINES, COLS, 0, 0)) == WIN_NULL)
X return(WIN_NULL);
X c_color = &curscr->color;
X c_attrib = &curscr->attrib;
X c_y_cur = &curscr->y_cur;
X c_x_cur = &curscr->x_cur;
X c_x_max = curscr->x_max;
X#endif
X if ((stdscr = newwin(LINES, COLS, 0, 0)) == WIN_NULL)
X {
X#ifndef MMAPPED
X free(curscr->buf);
X free(curscr);
X#endif
X return(WIN_NULL);
X }
X LAST_y = LINES - 1;
X LAST_x = COLS - 1;
X savetty();
X#ifdef DOS
X ospeed = 0;
X#else
X ospeed = _stty.c_cflag & CBAUD;
X#endif
X }
X else
X werase(stdscr);
X#ifndef MMAPPED
X werase(curscr);
X#endif
X raw();
X noecho();
X nonl();
X#ifndef MMAPPED
X if (_is)
X _puts(_is);
X if (_ti)
X _puts(_ti);
X if (_vs)
X _puts(_vs);
X _flush_outbuf(); /* force output before keypad */
X#endif
X keypad(); /* keypad output is immediate */
X wrefresh(stdscr);
X return(stdscr);
X}
X
WINDOW *newwin(lines, cols, y_org, x_org) /* make a new window */
X int lines;
X int cols;
X int y_org;
X int x_org;
X{
X WINDOW *win, *p;
X
X if ((y_org < 0) || (x_org < 0) ||
X (lines <= 0) || ((y_org + lines) > LINES) ||
X (cols <= 0) || ((x_org + cols) > COLS))
X return(WIN_NULL);
X if ((win = (WINDOW*)malloc(sizeof(WINDOW))) == WIN_NULL)
X return(WIN_NULL);
X win->buf_len = lines * cols;
X if ((win->buf = (YX_ELEMENT*)malloc(win->buf_len * sizeof(YX_ELEMENT))) == (YX_ELEMENT*)NULL)
X {
X free((char*)win);
X return(WIN_NULL);
X }
X win->y_max = lines;
X win->x_max = cols;
X win->y_org = y_org;
X win->x_org = x_org;
X win->y_cur = 0;
X win->x_cur = 0;
X win->ch_cur = win->buf;
X win->ch_first = win->buf + win->buf_len - 1;
X win->ch_last = win->buf;
X win->color = 0;
X win->attrib = 0;
X win->flags = 0;
X win->ch_flags = 0;
X if (stdscr == WIN_NULL)
X {
X win->prev = WIN_NULL;
X win->next = WIN_NULL;
X }
X else
X {
X for (p = stdscr; p->next != WIN_NULL; p = p->next)
X ;
X p->next = win;
X win->prev = p;
X win->next = NULL;
X }
X if ((y_org == 0) && (x_org == 0) && (lines == LINES) && (cols == COLS))
X win->flags |= FULLWINDOW;
X#ifndef MMAPPED
X if (((x_org + cols) == COLS) && _ce)
X win->flags |= TSTCLRTOEOL;
X#endif
X wsetcolor(win, def_fg, def_bg);
X werase(win);
X return(win);
X}
X
int setterm(name) /* set terminal parameters for name */
X char *name;
X{
X int stat = TRUE;
X
X#ifdef UNIX
X init_key_tbl();
X#endif
X if (!name || !*name)
X name = Def_term;
X#ifdef MMAPPED
X ini_mmscr();
X#endif
X#ifdef DOS
X#ifndef MMAPPED
X LINES = 25;
X COLS = 80;
X _al = "\033[L";
X BC = 0;
X _cd = "\033[J";
X _ce = "\033[K";
X _cl = "\033[2J\e[H";
X _cm = "\033[%i%d;%dH";
X _dc = "\033[P";
X _dl = "\033[M";
X _do = 0;
X _ei = 0;
X _im = 0;
X _is = 0;
X _ke = 0;
X _ks = 0;
X _nd = "\033[C";
X _se = "\033[m";
X _sf = 0;
X _so = "\033[7m";
X _sr = 0;
X _te = 0;
X _ti = 0;
X _ue = "\033[m";
X UP = "\033[A";
X _us = "\033[4m";
X _ve = 0;
X _vs = 0;
X PC = 0;
X _sc = "\033[3%fm\033[4%bm%c0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15";
X _ci = 0;
X _gs = "";
X _ge = "";
X _bs = "\033[5m";
X _be = "\033[m";
X _hs = "\033[1m";
X _he = "\033[m";
X#endif /* if MMAPPED */
X _gb = "\304\263\305\332\302\277\264\331\301\300\303";
X rev_color = TRUE;
X#else
X if (tgetent(termcap, name) != 1)
X {
X name = "dumb";
X stat = FALSE;
X }
X tc_ptr = tc_tbl;
X LINES = tgetnum("li");
X COLS = tgetnum("co");
X _al = tgetstr("al", &tc_ptr);
X BC = tgetstr("bc", &tc_ptr);
X _cd = tgetstr("cd", &tc_ptr);
X _ce = tgetstr("ce", &tc_ptr);
X _cl = tgetstr("cl", &tc_ptr);
X _cm = tgetstr("cm", &tc_ptr);
X _dc = tgetstr("dc", &tc_ptr);
X _dl = tgetstr("dl", &tc_ptr);
X _do = tgetstr("do", &tc_ptr);
X _ei = tgetstr("ei", &tc_ptr);
X _im = tgetstr("im", &tc_ptr);
X _is = tgetstr("is", &tc_ptr);
X _ke = tgetstr("ke", &tc_ptr);
X _ks = tgetstr("ks", &tc_ptr);
X _nd = tgetstr("nd", &tc_ptr);
X _se = tgetstr("se", &tc_ptr);
X _sf = tgetstr("sf", &tc_ptr);
X _so = tgetstr("so", &tc_ptr);
X _sr = tgetstr("sr", &tc_ptr);
X _te = tgetstr("te", &tc_ptr);
X _ti = tgetstr("ti", &tc_ptr);
X _ue = tgetstr("ue", &tc_ptr);
X UP = tgetstr("up", &tc_ptr);
X _us = tgetstr("us", &tc_ptr);
X _ve = tgetstr("ve", &tc_ptr);
X _vs = tgetstr("vs", &tc_ptr);
X PC = tgetchar("pc");
X _sc = tgetstr("SC", &tc_ptr);
X _ci = tgetstr("CI", &tc_ptr);
X _gs = tgetstr("GS", &tc_ptr);
X _ge = tgetstr("GE", &tc_ptr);
X _gb = tgetstr("GB", &tc_ptr);
X _bs = tgetstr("BS", &tc_ptr);
X _be = tgetstr("BE", &tc_ptr);
X _hs = tgetstr("HS", &tc_ptr);
X _he = tgetstr("HE", &tc_ptr);
X rev_color = tgetflag("RC");
X#endif
X#ifndef MMAPPED
X have_color = sc_analyze();
X have_standout = so_analyze();
X have_underline = us_analyze();
X have_blink = bs_analyze();
X have_bold = hs_analyze();
X#endif
X have_graphic = gb_analyze();
X#ifdef UNIX
X add_key_tbl("ku", "\005", K_UP);
X add_key_tbl("kd", "\030", K_DOWN);
X add_key_tbl("kl", "\023", K_LEFT);
X add_key_tbl("kr", "\004", K_RIGHT);
X add_key_tbl("BT", "\033\t", K_BACKTAB);
X add_key_tbl("BB", "\021\022", K_BEG_BUF);
X add_key_tbl("EB", "\021\003", K_END_BUF);
X add_key_tbl("BS", "\021\005", K_BEG_SCREEN);
X add_key_tbl("ES", "\021\030", K_END_SCREEN);
X add_key_tbl("BL", "\021\023", K_BEG_LINE);
X add_key_tbl("EL", "\021\004", K_END_LINE);
X add_key_tbl("FS", "\003", K_FWD_SCREEN);
X add_key_tbl("RS", "\022", K_BWD_SCREEN);
X add_key_tbl("FW", "\006", K_FWD_WORD);
X add_key_tbl("RW", "\001", K_BWD_WORD);
X add_key_tbl("DL", "\033y", K_DEL_LINE);
X add_key_tbl("DW", "\024", K_DEL_WORD);
X add_key_tbl("DC", "\007", K_DEL_CHAR);
X add_key_tbl("KI", "\026", K_INSERT);
X add_key_tbl("KH", "\033h", K_HELP);
X add_key_tbl("KC", "\033c", K_CANCEL);
X add_key_tbl("KQ", "\033q", K_QUIT);
X add_key_tbl("KB", "\033 ", K_BACKUP);
X add_key_tbl("KP", "\033p", K_PRINT);
X add_key_tbl("k0", "\0331", K_F0);
X add_key_tbl("k1", "\0332", K_F1);
X add_key_tbl("k2", "\0333", K_F2);
X add_key_tbl("k3", "\0334", K_F3);
X add_key_tbl("k4", "\0335", K_F4);
X add_key_tbl("k5", "\0336", K_F5);
X add_key_tbl("k6", "\0337", K_F6);
X add_key_tbl("k7", "\0338", K_F6);
X add_key_tbl("k8", "\0339", K_F8);
X add_key_tbl("k9", "\0330", K_F9);
X#endif
X#ifndef MMAPPED
X if ((COLS == ERROR) || (LINES == ERROR) || streq(tgoto(_cm, 0, 0), "OOPS", TRUE))
X {
X _cm = NULL;
X name = "dumb";
X stat = FALSE;
X }
X#endif
X strcpy(ttytype, name);
X return(stat);
X}
X
static void pos_cursor(win)
X REGISTER WINDOW *win;
X{
X
X/*
X Set physical cursor position and color
X*/
X
X#ifdef MMAPPED
X pos_mmscr(win->y_cur + win->y_org, win->x_cur + win->x_org);
X#else
X _setattrib(0, win->color);
X if ((curscr->y_cur != (win->y_cur + win->y_org)) ||
X (curscr->x_cur != (win->x_cur + win->x_org)))
X _goto(win->y_cur + win->y_org, win->x_cur + win->x_org);
X if (have_color && win->color && (curscr->color != win->color))
X _setcolor(win->color);
X _flush_outbuf();
X#endif
X}
X
X#ifndef MMAPPED
X
static int do_cur()
X{
X
X/*
X Special case for refresh curscr. See wrefresh below.
X This will clean up the entire screen no matter how many subwindows or
X what their order. Used to clean the screen after 'noise' or other
X unwanted chars appear, much like using CTRL-L in vi.
X*/
X
X int i, temp, cur_x, cur_y, color, save_color;
X int weol_x, eol_x, eol_y, chk_color, save_x, save_y;
X REGISTER YX_ELEMENT *cur_yx;
X
X/*
X Initialize the counters and pointers
X*/
X
X cur_y = 0;
X cur_x = 0;
X cur_yx = curscr->buf;
X eol_y = UNDETERMINED;
X eol_x = COLS;
X chk_color = *c_color;
X save_y = *c_y_cur;
X save_x = *c_x_cur;
X save_color = *c_color;
X for (i = curscr->buf_len - 1; i >= 0; i--)
X {
X if (!cur_yx->ch)
X cur_yx->ch = ' ';
X cur_yx++;
X }
X cur_yx = curscr->buf;
X for (i = curscr->buf_len - 1; i >= 0; i--)
X {
X weol_x = 0;
X
X/*
X Check and set physical screen position
X*/
X
X if ((cur_y != *c_y_cur) || (cur_x != *c_x_cur))
X _goto(cur_y, cur_x);
X
X/*
X Check and set physical screen colors and attributes
X*/
X
X#ifdef DOS
X if (cur_yx->attrib != *c_attrib)
X _setattrib(cur_yx->attrib, cur_yx->color);
X if (chk_color && cur_yx->color && (cur_yx->color != *c_color))
X _setcolor(cur_yx->color);
X#else
X if (chk_color && cur_yx->color && (cur_yx->color != *c_color))
X _setcolor(cur_yx->color);
X if (cur_yx->attrib != *c_attrib)
X _setattrib(cur_yx->attrib, cur_yx->color);
X#endif
X
X/*
X This next part is a little tricky. Eol_y contains the current line that
X eol_x is calculated for. If its not the current line, eol_x must be
X recalculated. Eol_x is the first column on a line that contains blanks of
X the same color up to the end of the line. This is the column where CE may
X be used. I calculate eol_x once each line instead of looking forward
X after each character for all blanks as comparing 2 column numbers is much
X faster that comparing several columns of data. Also note that the
X window being refreshed must be full to the end of the physical screen
X else CE does not apply.
X*/
X if (cur_y != eol_y)
X {
X REGISTER YX_ELEMENT *syxp;
X
X eol_y = cur_y;
X eol_x = COLS;
X syxp = cur_yx + (eol_x - cur_x - 1);
X color = syxp->color;
X while ((eol_x > cur_x) && (eol_x > weol_x))
X {
X if ((syxp->ch == ' ') && (!syxp->attrib) &&
X (!have_color || (syxp->color == color)))
X {
X eol_x--;
X syxp--;
X }
X else
X break;
X }
X }
X
X/*
X Test and output CE, update pointers if CE sent
X*/
X
X if ((cur_yx->ch == ' ') && (cur_x >= eol_x))
X {
X _puts(_ce);
X temp = COLS - cur_x;
X i -= temp - 1;
X cur_x += temp - 1;
X cur_yx += temp - 1;
X }
X else
X
X/*
X Output character unless bottom right corner as this may cause screen
X to scroll up a line
X*/
X
X {
X if ((cur_y != (LINES - 1)) || (cur_x != (COLS - 1)))
X {
X *pout++ = cur_yx->ch; /* save putchar */
X if (++outbuf_count >= OUTBUF_SIZE) /* call overhead */
X _flush_outbuf();
X (*c_x_cur)++;
X }
X }
X
X/*
X Current character position complete, update pointers to next position
X*/
X
X cur_yx++;
X cur_x++;
X
X/*
X Test and set new line in window
X*/
X
X if (cur_x >= COLS)
X {
X cur_y++;
X cur_x = 0;
X }
X }
X
X/*
X Window refreshed, reset all flags
X Set physical cursor position and color
X*/
X
X if ((*c_y_cur != save_y) || (*c_x_cur != save_x))
X _goto(save_y, save_x);
X if (have_color && color && (save_color != color))
X _setcolor(save_color);
X
X/*
X Flush and remaining buffer data
X*/
X
X _flush_outbuf();
X return(TRUE);
X}
X
X#endif /* ifndef MMAPPED */
X
int wrefresh(win) /* refresh window */
X WINDOW *win;
X{
X
X/*
X This is the most critical routine in the package in determining speed.
X Function calls, multiplication, division, array subscripting and
X multiple indirection have been minimized for maximum speed. Instead,
X inline code (even if bulkier), addition, subtraction and direct register
X pointers are used as much as possible.
X
X It is responsible for taking a logical window and transferring it to the
X physical device. It only updates portions that are different from a window
X that reflects the physical screen (curscr). This cuts downs on the number
X of output characters and thus increases the speed.
X
X It takes into account if a window is partially hidden by an overlaying
X window or windows.
X
X Currently uses only CM and CE, needs to be improved to include DC, DL, IC
X and so on.
X*/
X
X int i, temp, cur_x, cur_y, win_x, win_y;
X int weol_x, win_y_min, win_y_max, in_range;
X int outside, win_x_max, win_x_org;
X REGISTER YX_ELEMENT *win_yx;
X WINDOW *next;
X#ifndef MMAPPED
X int eol_y, eol_x, last_x, chk_color, tst_clrtoeol, color;
X REGISTER YX_ELEMENT *cur_yx;
X#endif
X
X#ifndef MMAPPED
X if (win == curscr)
X return(do_cur());
X#endif
X if (!(win->flags & NO_CHANGE))
X {
X
X/*
X Initialize the counters and pointers
X*/
X if (win->ch_first > win->ch_last)
X {
X pos_cursor(win);
X return(FALSE);
X }
X win_x_max = win->x_max;
X win_x_org = win->x_org;
X win_y = (win->ch_first - win->buf) / win_x_max;
X win_x = (win->ch_first - win->buf) % win_x_max;
X cur_y = win_y + win->y_org;
X cur_x = win_x + win_x_org;
X win_yx = win->ch_first;
X win_y_min = LINES;
X win_y_max = 0;
X#ifndef MMAPPED
X cur_yx = &curscr->buf[(cur_y * c_x_max) + cur_x];
X eol_y = UNDETERMINED;
X eol_x = win_x_max;
X last_x = c_x_max;
X chk_color = (win->flags & SET_COLOR);
X tst_clrtoeol = (win->flags & TSTCLRTOEOL);
X#endif
X
X/*
X Find the min and max y positions of any windows "on top" of this one
X This simplifys the checking in the main refresh loop when we are out
X of the possible "on top" range.
X*/
X
X for (next = win->next; next; next = next->next)
X {
X if ((temp = next->y_org) < win_y_min)
X win_y_min = temp;
X if ((temp += (next->y_max - 1)) > win_y_max)
X win_y_max = temp;
X }
X next = win->next;
X for (i = win->ch_last - win->ch_first; i >= 0; i--)
X {
X
X/*
X See if character in window is different from physical screen
X If not, skip this character.
X If memory mapped output all.
X*/
X
X#ifndef MMAPPED
X
X if ((win_yx->ch != cur_yx->ch) ||
X (win_yx->attrib != cur_yx->attrib) ||
X (chk_color &&
X ((win_yx->color && (win_yx->color != cur_yx->color)) ||
X (!win_yx->color && cur_yx->color && (cur_yx->color != *c_color)))))
X
X#endif
X
X {
X weol_x = 0;
X
X/*
X See if character position is visable, ie not covered by overlaying window
X*/
X
X outside = TRUE;
X if ((cur_y >= win_y_min) && (cur_y <= win_y_max))
X {
X REGISTER WINDOW *wptr;
X
X outside = FALSE;
X for (wptr = next; wptr; )
X {
X if ((temp = (wptr->x_org + wptr->x_max)) > weol_x)
X weol_x = temp;
X if ((cur_y < wptr->y_org) ||
X (cur_y >= (wptr->y_org + wptr->y_max)) ||
X (cur_x < wptr->x_org) ||
X (cur_x >= temp))
X wptr = wptr->next;
X else
X break;
X }
X in_range = (wptr ? FALSE : TRUE);
X }
X if (outside || in_range)
X {
X
X#ifdef MMAPPED
X
X/*
X Output character irregardless of what screen currently has
X Memory mapping is faster than doing all the checking
X*/
X
X put_mmscr(cur_y, cur_x, win_yx);
X curscr->color = win_yx->color;
X curscr->attrib = win_yx->attrib;
X#else
X
X/*
X Check and set physical screen position
X*/
X
X if ((cur_y != *c_y_cur) || (cur_x != *c_x_cur))
X {
X/*
X Test to see if just need to advance one character ahead, if so output
X character to advance and save cursor motion calculations and output.
X*/
X
X if ((cur_y == *c_y_cur) &&
X (cur_x == (last_x + 1)) &&
X ((win_yx - 1)->attrib == *c_attrib) &&
X (!chk_color ||
X ((win_yx - 1)->color && ((win_yx - 1)->color == *c_color))))
X {
X *pout++ = (win_yx - 1)->ch;
X if (++outbuf_count >= OUTBUF_SIZE)
X _flush_outbuf();
X (*c_x_cur)++;
X }
X else
X _goto(cur_y, cur_x);
X }
X
X/*
X Check and set physical screen colors and attributes
X*/
X
X#ifdef DOS
X if (win_yx->attrib != *c_attrib)
X _setattrib(win_yx->attrib, win_yx->color);
X if (chk_color && win_yx->color && (win_yx->color != *c_color))
X _setcolor(win_yx->color);
X#else
X if (chk_color && win_yx->color && (win_yx->color != *c_color))
X _setcolor(win_yx->color);
X if (win_yx->attrib != *c_attrib)
X _setattrib(win_yx->attrib, win_yx->color);
X#endif
X
X/*
X This next part is a little tricky. Eol_y contains the current line that
X eol_x is calculated for. If its not the current line, eol_x must be
X recalculated. Eol_x is the first column on a line that contains blanks of
X the same color up to the end of the line. This is the column where CE may
X be used. I calculate eol_x once each line instead of looking forward
X after each character for all blanks as comparing 2 column numbers is much
X faster that comparing several columns of data. Also note that the
X window being refreshed must be full to the end of the physical screen
X else CE does not apply.
X*/
X if (tst_clrtoeol)
X {
X if (win_y != eol_y)
X {
X REGISTER YX_ELEMENT *syxp;
X
X eol_y = win_y;
X eol_x = win_x_max;
X syxp = win_yx + (eol_x - win_x - 1);
X color = syxp->color;
X while ((eol_x > win_x) &&
X (outside || (eol_x > weol_x)))
X {
X if ((syxp->ch == ' ') && (!syxp->attrib) &&
X (!have_color || (syxp->color == color)))
X {
X eol_x--;
X syxp--;
X }
X else
X break;
X }
X }
X }
X
X/*
X Test and output CE, update pointers if CE sent
X*/
X
X if (tst_clrtoeol && (win_yx->ch == ' ') && (win_x >= eol_x))
X {
X _puts(_ce);
X temp = win_x_max - win_x;
X i -= temp - 1;
X win_x += temp - 1;
X cur_x += temp - 1;
X while (temp-- > 0)
X *cur_yx++ = *win_yx++;
X win_yx--;
X cur_yx--;
X }
X else
X
X/*
X Output character unless bottom right corner as this may cause screen
X to scroll up a line
X*/
X
X {
X if ((cur_y != LAST_y) || (cur_x != LAST_x))
X {
X *pout++ = win_yx->ch; /* save putchar */
X if (++outbuf_count >= OUTBUF_SIZE) /* call overhead */
X _flush_outbuf();
X *cur_yx = *win_yx;
X (*c_x_cur)++;
X last_x = *c_x_cur;
X }
X }
X
X#endif /* ifdef MMAPPED */
X
X }
X }
X
X/*
X Current character position complete, update pointers to next position
X*/
X
X win_yx++;
X win_x++;
X#ifndef MMAPPED
X cur_yx++;
X#endif
X cur_x++;
X
X/*
X Test and set new line in window
X*/
X
X if (win_x >= win_x_max)
X {
X win_y++;
X win_x = 0;
X#ifndef MMAPPED
X cur_yx += c_x_max - win_x_max;
X#endif
X cur_y++;
X cur_x = win_x_org;
X }
X }
X
X/*
X Window refreshed, reset all flags and flush output
X*/
X
X win->ch_first = win->buf + win->buf_len - 1;
X win->ch_last = win->buf;
X win->flags |= NO_CHANGE;
X pos_cursor(win);
X }
X return(TRUE);
X}
X
X/*
X Extensions to curses
X*/
X
int keypad() /* put terminal in keypad mode */
X{
X if (_ks)
X {
X _iputs(_ks);
X return(TRUE);
X }
X else
X return(FALSE);
X}
X
int keypadend() /* end terminal keypad mode */
X{
X if (_ke)
X {
X _iputs(_ke);
X return(TRUE);
X }
X else
X return(FALSE);
X}
X
int tographic(ch) /* convert to graphic character */
X REGISTER int ch;
X{
X if ((ch >= G_H) && (ch <= G_RT))
X {
X if (have_graphic)
X return(box_tbl[ch - G_H]);
X else
X return(fake_tbl[ch - G_H]);
X }
X else
X return(' ');
X}
X
int waddgraphic(win, ch) /* add graphic character */
X REGISTER WINDOW *win;
X REGISTER int ch;
X{
X int gr_set;
X
X if ((ch >= G_H) && (ch <= G_RT))
X {
X if (have_graphic)
X {
X if ((gr_set = (win->attrib & GRAPHIC)) == 0)
X win->attrib |= GRAPHIC;
X waddch(win, box_tbl[ch - G_H]);
X if (!gr_set)
X win->attrib &= ~GRAPHIC;
X }
X else
X {
X if ((gr_set = (win->attrib & GRAPHIC)) != 0)
X win->attrib &= ~GRAPHIC;
X waddch(win, fake_tbl[ch - G_H]);
X if (gr_set)
X win->attrib |= GRAPHIC;
X }
X }
X else
X waddch(win, ' ');
X return(TRUE);
X}
X
int fg_bits(color)
X REGISTER int color;
X{
X if ((color == UNDETERMINED) || (color < BLACK) || (color > COLOR_15))
X return(UNDETERMINED);
X else
X return(bits_tbl[color].fg);
X}
X
int bg_bits(color)
X REGISTER int color;
X{
X if ((color == UNDETERMINED) || (color < BLACK) || (color > COLOR_15))
X return(UNDETERMINED);
X else
X return(bits_tbl[color].bg);
X}
X
int wsetcolor(win, foreground, background) /* set color on a window */
X REGISTER WINDOW *win;
X int foreground;
X int background;
X{
X int temp;
X
X if (!have_color || (foreground == background))
X return(FALSE);
X if ((temp = fg_bits(foreground)) != UNDETERMINED)
X win->color = (win->color & ~F_MASK) | temp;
X if ((temp = bg_bits(background)) != UNDETERMINED)
X win->color = (win->color & ~B_MASK) | temp;
X win->flags |= SET_COLOR;
X return(TRUE);
X}
X
int get_color(color)
X REGISTER char *color;
X{
X REGISTER COLOR_ENTRY *tbl_ptr;
X
X if (color)
X {
X tbl_ptr = color_table;
X while (tbl_ptr->name)
X {
X if (streq(color, tbl_ptr->name, TRUE))
X return(tbl_ptr->value);
X else
X tbl_ptr++;
X }
X }
X return(UNDETERMINED);
X}
X
X#ifdef UNIX
int tgetchar(id) /* termcap get char spec */
X char *id;
X{
X char *temp;
X
X if (temp = tgetstr(id, &tc_ptr))
X return(*temp);
X else
X return('\0');
X}
X
X#ifndef MMAPPED
int print_screen(parm_printer, parm_file)
X char *parm_printer;
X char *parm_file;
X{
X int i, j, k;
X int p_ch, f_ch, effect, have_effect, f_append;
X int p_uniplex, env_printer, f_uniplex;
X WINDOW *win;
X YX_ELEMENT *ptr;
X FILE *p_file;
X FILE *f_file;
X char sh_cmd[100], file[50], printer[50], ufile[50];
X char *p_buf, *p_ptr, *f_buf, *f_ptr, *e_buf, *e_ptr, *env_ptr;
X
X win = curscr;
X if (!(p_buf = malloc(win->x_max + 5)) ||
X !(f_buf = malloc(win->x_max + 5)) ||
X !(e_buf = malloc(win->x_max + 5)))
X return(FALSE);
X printer[0] = '\0';
X env_printer = FALSE;
X p_uniplex = FALSE;
X f_uniplex = FALSE;
X f_append = FALSE;
X if (parm_printer)
X strncpy(printer, parm_printer, sizeof(printer) - 1);
X else
X {
X if (env_ptr = getenv("SCR_PRINTER"))
X {
X env_printer = TRUE;
X strncpy(printer, env_ptr, sizeof(printer) - 1);
X }
X }
X printer[sizeof(printer) - 1] = '\0';
X if (printer[0] || env_printer)
X {
X if (printer[0])
X {
X if (printer[0] == '#')
X {
X p_uniplex = TRUE;
X strcpy(printer, &printer[1]);
X sprintf(ufile, "/tmp/scr.%d", getuid());
X strcpy(sh_cmd, "cat > ");
X strcat(sh_cmd, ufile);
X }
X else
X {
X strcpy(sh_cmd, "lp -s -d");
X strcat(sh_cmd, printer);
X }
X }
X else
X strcpy(sh_cmd, "lp -s");
X p_file = popen(sh_cmd, "w");
X }
X else
X p_file = NULL;
X file[0] = '\0';
X if (parm_file)
X strncpy(file, parm_file, sizeof(file) - 1);
X else
X {
X if (env_ptr = getenv("SCR_FILE"))
X strncpy(file, env_ptr, sizeof(file) - 1);
X }
X file[sizeof(file) - 1] = '\0';
X if (file[0])
X {
X f_append = FALSE;
X if (file[0] == '+')
X {
X strcpy(file, &file[1]);
X f_append = TRUE;
X }
X if (file[0] == '#')
X {
X strcpy(file, &file[1]);
X f_uniplex = TRUE;
X if (file[0] == '+')
X {
X strcpy(file, &file[1]);
X f_append = TRUE;
X }
X }
X if (f_append)
X f_file = fopen(file, "a");
X else
X f_file = fopen(file, "w");
X }
X else
X f_file = NULL;
X if (!p_file && !f_file)
X return(FALSE);
X j = 0;
X have_effect = FALSE;
X p_ptr = p_buf;
X f_ptr = f_buf;
X e_ptr = e_buf;
X for (i = win->buf_len, ptr = win->buf; i; i--, ptr++)
X {
X p_ch = ptr->ch;
X f_ch = ptr->ch;
X if (ptr->attrib && (p_uniplex || f_uniplex))
X have_effect = TRUE;
X if (ptr->attrib & GRAPHIC)
X {
X for (k = 0; k < BOX_SIZE; k++)
X {
X if (box_tbl[k] == ptr->ch)
X {
X if (p_uniplex)
X p_ch = 'A' + k;
X else
X p_ch = fake_tbl[k];
X if (f_uniplex)
X f_ch = 'A' + k;
X else
X f_ch = fake_tbl[k];
X break;
X }
X }
X effect = '[';
X }
X else if (ptr->attrib & BOLD)
X effect = 'A';
X else if (ptr->attrib & UNDERLINE)
X effect = 'C';
X else if (ptr->attrib & STANDOUT)
X {
X if (p_ch == ' ')
X p_ch = '*';
X if (f_ch == ' ')
X f_ch = '*';
X effect = 'Z';
X }
X else
X effect = ' ';
X j++;
X *p_ptr++ = p_ch;
X *f_ptr++ = f_ch;
X *e_ptr++ = effect;
X if (j == win->x_max)
X {
X while (*(p_ptr - 1) == ' ')
X {
X if (p_uniplex)
X {
X if (*(e_ptr - 1) == ' ')
X e_ptr--;
X else
X break;
X }
X p_ptr--;
X }
X while (*(f_ptr - 1) == ' ')
X {
X if (f_uniplex && !p_uniplex)
X {
X if (*(e_ptr - 1) == ' ')
X e_ptr--;
X else
X break;
X }
X f_ptr--;
X }
X if (p_uniplex && have_effect)
X {
X *p_ptr++ = '@';
X *p_ptr++ = '@';
X }
X if (f_uniplex && have_effect)
X {
X *f_ptr++ = '@';
X *f_ptr++ = '@';
X }
X *p_ptr++ = '\n';
X *p_ptr = '\0';
X *f_ptr++ = '\n';
X *f_ptr = '\0';
X *e_ptr++ = '\n';
X *e_ptr = '\0';
X if (p_file)
X {
X fputs(p_buf, p_file);
X if (p_uniplex && have_effect)
X fputs(e_buf, p_file);
X }
X if (f_file)
X {
X fputs(f_buf, f_file);
X if (f_uniplex && have_effect)
X fputs(e_buf, f_file);
X }
X j = 0;
X have_effect = FALSE;
X p_ptr = p_buf;
X f_ptr = f_buf;
X e_ptr = e_buf;
X }
X }
X free(p_buf);
X free(f_buf);
X free(e_buf);
X if (p_file)
X pclose(p_file);
X if (f_file)
X fclose(f_file);
X if (p_uniplex)
X {
X strcpy(sh_cmd, "/bin/sh -c '/usr/UII/bin/uprint -p ");
X strcat(sh_cmd, printer);
X strcat(sh_cmd, " ");
X strcat(sh_cmd, ufile);
X strcat(sh_cmd, " 2>/dev/null | lp -s 2>/dev/null'");
X return(do_command(sh_cmd, FALSE, (int*)0));
X }
X return(TRUE);
X}
X#endif
X#endif
X
END_OF_FILE
if test 37438 -ne `wc -c <'lib/scrout1.c'`; then
echo shar: \"'lib/scrout1.c'\" unpacked with wrong size!
fi
# end of 'lib/scrout1.c'
fi
echo shar: End of archive 8 \(of 9\).
cp /dev/null ark8isdone
MISSING=""
for I in 1 2 3 4 5 6 7 8 9 ; do
if test ! -f ark${I}isdone ; then
MISSING="${MISSING} ${I}"
fi
done
if test "${MISSING}" = "" ; then
echo You have unpacked all 9 archives.
rm -f ark[1-9]isdone ark[1-9][0-9]isdone
else
echo You still need to unpack the following archives:
echo " " ${MISSING}
fi
## End of shell archive.
exit 0
More information about the Comp.unix.xenix
mailing list