v23i081: ABC interactive programming environment, Part02/25
Rich Salz
rsalz at bbn.com
Tue Dec 18 05:34:47 AEST 1990
Submitted-by: Steven Pemberton <steven at cwi.nl>
Posting-number: Volume 23, Issue 81
Archive-name: abc/part02
#! /bin/sh
# This is a shell archive. Remove anything before this line, then feed it
# into a shell via "sh file" or similar. To overwrite existing files,
# type "sh file -c".
# The tool that generated this appeared in the comp.sources.unix newsgroup;
# send mail to comp-sources-unix at uunet.uu.net if you want that tool.
# Contents: abc/bint1/i1num.c abc/ex/hanoi/suggest.abc
# abc/unix/u1trm.c
# Wrapped by rsalz at litchi.bbn.com on Mon Dec 17 13:27:51 1990
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
echo If this archive is complete, you will see the following message:
echo ' "shar: End of archive 2 (of 25)."'
if test -f 'abc/bint1/i1num.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'abc/bint1/i1num.c'\"
else
echo shar: Extracting \"'abc/bint1/i1num.c'\" \(9745 characters\)
sed "s/^X//" >'abc/bint1/i1num.c' <<'END_OF_FILE'
X/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1986. */
X
X/* B numbers, basic external interface */
X
X#include "b.h"
X#include "feat.h" /* for EXT_RANGE */
X#include "bobj.h"
X#include "i1num.h"
X
X/*
X * This file contains operations on numbers that are not predefined
X * B functions (the latter are defined in `i1fun.c').
X * This includes conversion of numeric B values to C `int's and
X * `double's, (numval() and intval()),
X * but also utilities for comparing numeric values and hashing a numeric
X * value to something usable as initialization for the random generator
X * without chance of overflow (so numval(v) is unusable).
X * It is also possible to build numbers of all types using mk_integer,
X * mk_exact (or mk_rat) and mk_approx. Note the rather irregular
X * (historical!) argument structure for these: mk_approx has a
X * `double' argument, where mk_exact and mk_rat have two values
X * which must be `integer' (not `int's!) as arguments.
X * The random number generator used by the random and choice functions
X * is also in this file.
X */
X
X/*
X * ival is used internally by intval() and large().
X * It converts an integer to double precision, yielding
X * a huge value when overflow occurs (but giving no error).
X */
X
XHidden double ival(u) integer u; {
X double x = 0;
X register int i;
X
X if (IsSmallInt(u)) return SmallIntVal(u);
X for (i = Length(u)-1; i >= 0; --i) {
X if (x >= Maxreal/BASE)
X return Msd(u) < 0 ? -Maxreal : Maxreal;
X x = x*BASE + Digit(u, i);
X }
X
X return x;
X}
X
X
X/* Determine if a value may be converted to an int */
X
X#define NO_INTEGER MESS(1300, "number not an integer")
X
XVisible bool large(v) value v; {
X double r;
X if (!Is_number(v) || !integral(v)) {
X interr(NO_INTEGER);
X return No;
X }
X if (Rational(v)) v= (value) Numerator((rational)v);
X r= ival((integer)v);
X if (r > Maxint) return Yes;
X return No;
X}
X
X/* return the C `int' value of a B numeric value, if it exists. */
X
X#define LARGE_INTEGER MESS(1301, "exceedingly large integer")
X
XVisible int intval(v) value v; {
X /* v must be an Integral number or a Rational with Denominator==1
X which may result from n round x [via mk_exact]!. */
X double i;
X if (IsSmallInt(v)) i= SmallIntVal(v);
X else {
X if (!Is_number(v)) syserr(MESS(1302, "intval on non-number"));
X if (!integral(v)) {
X interr(NO_INTEGER);
X return 0;
X }
X if (Rational(v)) v= (value) Numerator((rational)v);
X i= ival((integer)v);
X }
X if (i > Maxint || i < -Maxint) {
X interr(LARGE_INTEGER);
X return 0;
X }
X return (int) i;
X}
X
X
X/* convert an int to an intlet */
X
XVisible int propintlet(i) int i; {
X if (i > Maxintlet || i < -Maxintlet) {
X interr(LARGE_INTEGER);
X return 0;
X }
X return i;
X}
X
X
X/*
X * determine if a number is integer
X */
X
XVisible bool integral(v) value v; {
X if (Integral(v) || (Rational(v) && Denominator((rational)v) == int_1))
X return Yes;
X else return No;
X}
X
X/*
X * mk_exact makes an exact number out of two integers.
X * The third argument is the desired number of digits after the decimal
X * point when the number is printed (see comments in `i1fun.c' for
X * `round' and code in `i1nuc.c').
X * This printing size (if positive) is hidden in the otherwise nearly
X * unused * 'Size' field of the value struct
X * (cf. definition of Rational(v) etc.).
X */
X
XVisible value mk_exact(p, q, len) integer p, q; int len; {
X rational r = mk_rat(p, q, len, Yes);
X
X if (Denominator(r) == int_1 && len <= 0) {
X integer i = (integer) Copy(Numerator(r));
X Release(r);
X return (value) i;
X }
X
X return (value) r;
X}
X
X#define uSMALL 1
X#define uINT 2
X#define uRAT 4
X#define uAPP 8
X#define vSMALL 16
X#define vINT 32
X#define vRAT 64
X#define vAPP 128
X
XVisible relation numcomp(u, v) value u, v; {
X int tu, tv; relation s;
X
X if (IsSmallInt(u)) tu = uSMALL;
X else if (Length(u) >= 0) tu = uINT;
X else if (Length(u) <= -2) tu = uRAT;
X else tu = uAPP;
X if (IsSmallInt(v)) tv = vSMALL;
X else if (Length(v) >= 0) tv = vINT;
X else if (Length(v) <= -2) tv = vRAT;
X else tv = vAPP;
X
X switch (tu|tv) {
X
X case uSMALL|vSMALL: return SmallIntVal(u) - SmallIntVal(v);
X
X case uSMALL|vINT:
X case uINT|vSMALL:
X case uINT|vINT: return int_comp((integer)u, (integer)v);
X
X case uSMALL|vRAT:
X case uINT|vRAT:
X u = (value) mk_rat((integer)u, int_1, 0, Yes);
X s = rat_comp((rational)u, (rational)v);
X release(u);
X return s;
X
X case uRAT|vRAT:
X return rat_comp((rational)u, (rational)v);
X
X case uRAT|vSMALL:
X case uRAT|vINT:
X v = (value) mk_rat((integer)v, int_1, 0, Yes);
X s = rat_comp((rational)u, (rational)v);
X release(v);
X return s;
X
X case uSMALL|vAPP:
X u = approximate(u);
X s = app_comp((real)u, (real)v);
X release(u);
X return s;
X
X case uINT|vAPP:
X case uRAT|vAPP:
X v = exactly(v);
X s = numcomp(u, v);
X release(v);
X return s;
X
X case uAPP|vAPP:
X return app_comp((real)u, (real)v);
X
X case uAPP|vSMALL:
X v = approximate(v);
X s = app_comp((real)u, (real)v);
X release(v);
X return s;
X
X case uAPP|vINT:
X case uAPP|vRAT:
X u = exactly(u);
X s = numcomp(u, v);
X release(u);
X return s;
X
X default: syserr(MESS(1303, "num_comp")); /* NOTREACHED */
X
X }
X}
X
X#ifdef RANGEPRINT
X/* if ranges are written as {1..10} instead of {1; 2; etc},
X * the following function is used in convert() and wri().
X */
X
XVisible bool is_increment(a, b) value a, b; {
X value v;
X relation c;
X
X if (!(Is_number(a) && integral(a))) {
X return No;
X }
X c= compare(a, v= sum(b, one));
X release(v);
X return c==0;
X}
X#endif /* RANGEPRINT */
X
X
X/*
X * Deliver 10**n, where n is a (maybe negative!) C integer.
X * The result is a value (integer or rational, actually).
X */
X
XVisible value tento(n) int n; {
X /* If int_tento fails, so will tento; caveat invocator */
X if (n < 0) {
X integer i= int_tento(-n); value v;
X if (!i) return Vnil;
X v= (value) mk_exact(int_1, i, 0);
X Release(i);
X return v;
X }
X return (value) int_tento(n);
X}
X
X
X/*
X * numval returns the numerical value of any numeric B value
X * as a C `double'.
X */
X
XVisible double numval(u) value u; {
X double expo, frac;
X
X if (!Is_number(u)) {
X interr(MESS(1304, "value not a number"));
X return 0.0;
X }
X u = approximate(u);
X expo = Expo((real)u), frac = Frac((real)u);
X release(u);
X if (expo > Maxexpo) {
X interr(MESS(1305, "approximate number too large to be handled"));
X return 0.0;
X }
X if(expo < Minexpo) return 0.0;
X return ldexp(frac, (int)expo);
X}
X
X
X/*
X * Random numbers
X */
X
X
X/*
X * numhash produces a `double' number that depends on the value of
X * v, useful for initializing the random generator.
X * Needs rewriting, so that for instance numhash(n) never equals n.
X * The magic numbers here are chosen at random.
X */
X
X/* The following is an auxiliary function for scrambling integers. */
X
XHidden double inthash(v) double v; {
X long i= ((long) v)^0x96696996;
X v= 987.6543210987654321*v;
X return .666*(((long) (v*.543))^((long) v)^i)+.747+v;
X}
X
XVisible double numhash(v) value v; {
X if (Integral(v)) {
X double d = 0;
X register int i;
X
X if (IsSmallInt(v)) return inthash((double)SmallIntVal(v));
X
X for (i = Length(v) - 1; i >= 0; --i) {
X d *= 2;
X d += Digit((integer)v, i);
X }
X
X return d;
X }
X
X if (Rational(v))
X return .777 * numhash((value) Numerator((rational)v)) +
X .123 * numhash((value) Denominator((rational)v));
X
X return numval(v); /* Which fails for HUGE reals. Never mind. */
X}
X
X
X/* Initialize the random generator */
X
XHidden double lastran;
X
XHidden Procedure setran (seed) double seed; {
X double x;
X
X /* Here is a change to make SETRANDOM -x differ from SETRANDOM x: */
X x = seed >= 0 ? seed : .775533-seed;
X /* This gives a small speed-up: */
X while (x >= 10000) x /= 10000;
X /* as before: */
X while (x >= 1) x /= 10;
X lastran = floor(67108864.0*x);
X}
X
XVisible Procedure set_random(v) value v; {
X setran((double) hash(v));
X}
X
X
X/* Return a random number in [0, 1). */
X
X#define AA 29247341.0
X#define CC 66664423.0
X#define MM 67108864.0 /* 2**26 */
X
X#define T21 2097152.0 /* 2**21 */
X
XVisible value random() {
X double p, r;
X
X /* Get three packets of 21 bits.
X * We get the full width of a double as random bits.
X * no group of bits has an obvious cyclic pattern,
X * because we chop off the last 5 bits of each lastran
X * (the last n bits (of the 26) go thru a 2**n cycle).
X * Disadvantage: it is slow -- but someone heavy into using
X * random might prefer a slower good random() over a fast one
X * giving meaningless data.
X */
X
X p = AA * lastran + CC;
X lastran = p - MM*floor(p/MM);
X r= floor(lastran/32.0)/T21;
X
X p = AA * lastran + CC;
X lastran = p - MM*floor(p/MM);
X r= (floor(lastran/32.0)+r)/T21;
X
X p = AA * lastran + CC;
X lastran = p - MM*floor(p/MM);
X r= (floor(lastran/32.0)+r)/T21;
X
X if (r >= 1.0) return random();
X return (value) mk_approx(r, 0.0);
X}
X
XVisible value rndm_limit;
X
XVisible Procedure initnum() {
X rndm_limit= (value) mk_int(RNDM_LIMIT);
X rat_init();
X setran((double) SEED);
X initapp();
X}
X
XVisible Procedure endnum() {
X endapp();
X endrat();
X}
X
X
XVisible value grab_num(len) register int len; {
X integer v;
X register int i;
X
X if (len > Maxintlet) {
X interr(MESS(1306, "exceptionally large number"));
X return Vnil;
X }
X if (len < -Maxintlet) len = -2;
X v = (integer) grab(Num, len);
X for (i = Length(v)-1; i >= 0; --i) Digit(v, i) = 0;
X return (value) v;
X}
X
XVisible value grab_rat(len) register int len; {
X if (len > 0 && len+2 <= Maxintlet);
X else len= 0;
X
X return grab(Num, -2 - len);
X}
X
XVisible value regrab_num(v, len) value v; register int len; {
X uniql(&v);
X regrab(&v, len);
X return v;
X}
X
XVisible unsigned numsyze(len, nptrs)
X intlet len;
X int *nptrs;
X{
X register unsigned syze= 0;
X *nptrs= 0;
X if (len >= 0) syze= len*sizeof(digit); /* Integral */
X else if (len == -1) {
X#ifdef EXT_RANGE
X syze= 2*sizeof(double); /* Approximate */
X#else
X syze= sizeof(double); /* Approximate */
X#endif
X }
X else { /* Rational */
X syze= 2*sizeof(value);
X *nptrs= 2;
X }
X return syze;
X}
X
END_OF_FILE
if test 9745 -ne `wc -c <'abc/bint1/i1num.c'`; then
echo shar: \"'abc/bint1/i1num.c'\" unpacked with wrong size!
fi
# end of 'abc/bint1/i1num.c'
fi
if test -f 'abc/ex/hanoi/suggest.abc' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'abc/ex/hanoi/suggest.abc'\"
else
echo shar: Extracting \"'abc/ex/hanoi/suggest.abc'\" \(8 characters\)
sed "s/^X//" >'abc/ex/hanoi/suggest.abc' <<'END_OF_FILE'
XHANOI ?
END_OF_FILE
if test 8 -ne `wc -c <'abc/ex/hanoi/suggest.abc'`; then
echo shar: \"'abc/ex/hanoi/suggest.abc'\" unpacked with wrong size!
fi
# end of 'abc/ex/hanoi/suggest.abc'
fi
if test -f 'abc/unix/u1trm.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'abc/unix/u1trm.c'\"
else
echo shar: Extracting \"'abc/unix/u1trm.c'\" \(40794 characters\)
sed "s/^X//" >'abc/unix/u1trm.c' <<'END_OF_FILE'
X/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1986. */
X
X#define lenline len_line /* Avoid name conflict with lenline in tex.c */
X
X/*
X * Virtual TeRMinal package.
X * (For a description see at the end of this file.)
X *
X * Big change:
X * output goes to /dev/tty, in case stdout is redirected to a file.
X *
X * TO DO:
X * - add interrupt handling (trminterrupt)
X * - adapt to changed window size when suspended or at SIGWINCH
X * (unfortunately, the calling module must be changed first --
X * it is not prepared for the changed window size...)
X */
X
X/*
X * Includes and data definitions.
X */
X
X#include "b.h"
X
X#ifndef TERMIO
X#include <sgtty.h>
X#else
X#include <termio.h>
X#endif
X
X#include "trm.h"
X
X#ifdef SIGNAL
X#include <signal.h>
X#endif
X#ifdef SETJMP
X#include <setjmp.h>
X#endif
X
Xchar *malloc();
X
Xchar *getenv();
Xint tgetent();
Xint tgetnum();
Xint tgetflag();
Xchar *tgetstr();
X
X#define Min(a,b) ((a) <= (b) ? (a) : (b))
X
X/* tty modes */
X#ifndef TERMIO
X
X/* v7/BSD tty control */
XHidden struct sgttyb oldtty, newtty;
X#ifdef TIOCSETN
X/* Redefine stty to use TIOCSETN, so type-ahead is not flushed */
X#define stty(fd, bp) VOID ioctl(fd, TIOCSETN, (char *) bp)
X#endif
X
X#ifdef TIOCSLTC /* BSD -- local special chars, must all be turned off */
Xstatic struct ltchars oldltchars;
Xstatic struct ltchars newltchars= {-1, -1, -1, -1, -1, -1};
X#endif /* TIOCSLTC */
X
X#ifdef TIOCSETC /* V7 -- standard special chars, some must be turned off too */
Xstatic struct tchars oldtchars;
Xstatic struct tchars newtchars;
X#endif /* TIOCSETC */
X
X#else /* TERMIO */
X
X/* AT&T tty control */
XHidden struct termio oldtty, newtty;
X#define gtty(fd,bp) ioctl(fd, TCGETA, (char *) bp)
X#define stty(fd,bp) VOID ioctl(fd, TCSETAW, (char *) bp)
X
X#endif /* TERMIO */
X
X#ifdef VTRMTRACE
Xextern FILE *vtrmfp;
X#endif
X
XHidden bool know_ttys = No;
X
X/* visible data for termcap */
Xchar PC;
Xchar *BC;
Xchar *UP;
Xshort ospeed;
X
XHidden FILE *fp= NULL;
XForward int outchar(); /* procedure for termcap's tputs */
X#define Putstr(str) tputs((str), 1, outchar)
Xextern char *tgoto();
X
X/* termcap terminal capabilities */
X
XHidden int lines;
XHidden int cols;
X
X/*
X * String-valued capabilities are saved in one big array.
X * Extend this only at the end (even though it disturbs the sorting)
X * because otherwise you have to change all macros...
X */
X
X#define par_al_str strcaps[0] /* parametrized al (AL) */
X#define cap_cm_str strcaps[1] /* screen-relative cursor motion (CM) */
X#define par_dl_str strcaps[2] /* parametrized dl (DL) */
X#define al_str strcaps[3] /* add new blank line */
X#define cd_str strcaps[4] /* clear to end of display */
X#define ce_str strcaps[5] /* clear to end of line */
X#define cl_str strcaps[6] /* cursor home and clear screen */
X#define cm_str strcaps[7] /* cursor motion */
X#define cp_str strcaps[8] /* cursor position sense reply */
X#define cr_str strcaps[9] /* carriage return */
X#define cs_str strcaps[10] /* change scrolling region */
X#define dc_str strcaps[11] /* delete character */
X#define dl_str strcaps[12] /* delete line */
X#define dm_str strcaps[13] /* enter delete mode */
X#define do_str strcaps[14] /* cursor down one line */
X#define ed_str strcaps[15] /* end delete mode */
X#define ei_str strcaps[16] /* end insert mode */
X#define ho_str strcaps[17] /* cursor home */
X#define ic_str strcaps[18] /* insert character (if necessary; may pad) */
X#define im_str strcaps[19] /* enter insert mode */
X#define nd_str strcaps[20] /* cursor right (non-destructive space) */
X#define nl_str strcaps[21] /* newline */
X#define se_str strcaps[22] /* end standout mode */
X#define sf_str strcaps[23] /* scroll text up (from bottom of region) */
X#define so_str strcaps[24] /* begin standout mode */
X#define sp_str strcaps[25] /* sense cursor position */
X#define sr_str strcaps[26] /* scroll text down (from top of region) */
X#define te_str strcaps[27] /* end termcap */
X#define ti_str strcaps[28] /* start termcap */
X#define vb_str strcaps[29] /* visible bell */
X#define ve_str strcaps[30] /* make cursor visible again */
X#define vi_str strcaps[31] /* make cursor invisible */
X#define le_str strcaps[32] /* cursor left */
X#define bc_str strcaps[33] /* backspace character */
X#define up_str strcaps[34] /* cursor up */
X#define pc_str strcaps[35] /* pad character */
X#define ks_str strcaps[36] /* keypad mode start */
X#define ke_str strcaps[37] /* keypad mode end */
X#define us_str strcaps[38] /* start underscore mode */
X#define ue_str strcaps[39] /* end underscore mode */
X/* Insert new entries here only! Don't forget to change the next line! */
X#define NSTRCAPS 40 /* One more than the last entry's index */
X
XHidden char *strcaps[NSTRCAPS];
XHidden char strcapnames[] =
X"ALCMDLalcdceclcmcpcrcsdcdldmdoedeihoicimndnlsesfsospsrtetivbvevilebcuppckskeusue";
X
X/* Same for Boolean-valued capabilities */
X
X#define has_am flagcaps[0] /* has automatic margins */
X#define has_da flagcaps[1] /* display may be retained above screen */
X#define has_db flagcaps[2] /* display may be retained below screen */
X#define has_in flagcaps[3] /* not safe to have null chars on the screen */
X#define has_mi flagcaps[4] /* move safely in insert (and delete?) mode */
X#define has_ms flagcaps[5] /* move safely in standout mode */
X#define has_xs flagcaps[6] /* standout not erased by overwriting */
X#define has_bs flagcaps[7] /* terminal can backspace */
X#define hardcopy flagcaps[8] /* hardcopy terminal */
X#define has_xn flagcaps[9] /* Vt100 / Concept glitch */
X#define NFLAGS 10
X
XHidden char flagcaps[NFLAGS];
XHidden char flagnames[]= "amdadbinmimsxsbshcxn";
X
XHidden Procedure getcaps(parea) register char **parea; {
X register char *capname;
X register char **capvar;
X register char *flagvar;
X
X for (capname= flagnames, flagvar= flagcaps;
X *capname != '\0'; capname += 2, ++flagvar)
X *flagvar= tgetflag(capname);
X
X for (capname= strcapnames, capvar= strcaps;
X *capname != '\0'; capname += 2, ++capvar)
X *capvar= tgetstr(capname, parea);
X}
X
X/* terminal status */
X
X/* calling order of Visible Procs */
XHidden bool started = No;
X
X/* to exports the capabilities mentioned in vtrm.h: */
XHidden int flags = 0;
X
X/* cost for impossible operations */
X#define Infinity 9999
X /* Allow for adding Infinity+Infinity within range */
X /* (Range is assumed at least 2**15 - 1) */
X
X/* The following for all sorts of undefined things (except for UNKNOWN char) */
X#define Undefined (-1)
X
X/* current mode of putting char's */
X#define Normal 0
X#define Insert 1
X#define Delete 2
XHidden short mode = Normal;
X
X/* current standout mode */
X#define Off 0
X#define On 0200
XHidden short so_mode = Off;
X
X/* masks for char's and short's */
X#define NULCHAR '\000'
X#define CHAR 0177
X#define SOBIT On
X#define SOCHAR 0377
X/* if (has_xs) record cookies placed on screen in extra bit */
X/* type of cookie is determined by the SO bit */
X#define XSBIT 0400
X#define SOCOOK 0600
X#define COOKBITS SOCOOK
X#define UNKNOWN 1
X#define NOCOOK UNKNOWN
X
X/* current cursor position */
XHidden short cur_y = Undefined, cur_x = Undefined;
X
X/* "line[y][x]" holds the char on the terminal, with the SOBIT and XSBIT.
X * the SOBIT tells whether the character is standing out, the XSBIT whether
X * there is a cookie on the screen at this position.
X * In particular a standend-cookie may be recorded AFTER the line
X * (just in case some trmputdata will write after that position).
X * "lenline[y]" holds the length of the line.
X * Unknown chars will be 1, so the optimising compare in putline will fail.
X * (Partially) empty lines are distinghuished by "lenline[y] < cols".
X */
XHidden short **line = 0, *lenline = 0;
X
X/* Clear the screen initially iff only memory cursor addressing available */
XHidden bool mustclear = No;
X
X/* Make the cursor invisible when trmsync() tries to move outside the screen */
XHidden bool no_cursor = No;
X
X/* Optimise cursor motion */
XHidden int abs_cost; /* cost of absolute cursor motion */
XHidden int cr_cost; /* cost of carriage return */
XHidden int do_cost; /* cost of down */
XHidden int le_cost; /* cost of left */
XHidden int nd_cost; /* cost of right */
XHidden int up_cost; /* cost of up */
X
X/* Optimise trailing match in put_line, iff the terminal can insert and delete
X * characters; the cost per n characters will be:
X * n * MultiplyFactor + OverHead
X */
XHidden int ins_mf, ins_oh, del_mf, del_oh;
XHidden int ed_cost, ei_cost; /* used in move() */
X
X/* The type of scrolling possible determines which routines get used;
X * these may be:
X * (1) with addline and deleteline (termcap: al_str & dl_str);
X * (2) with a settable scrolling region, like VT100 (cs_str, sr_str, sf_str);
X * (3) no scrolling available. (NOT YET IMPLEMENTED)
X */
XHidden Procedure (*scr_up)();
XHidden Procedure (*scr_down)();
XForward Procedure scr1up();
XForward Procedure scr1down();
XForward Procedure scr2up();
XForward Procedure scr2down();
X/*Forward Procedure scr3up(); */
X/*Forward Procedure scr3down(); */
X
X/*
X * Starting, Ending and (fatal) Error.
X */
X
X/*
X * Initialization call.
X * Determine terminal capabilities from termcap.
X * Set up tty modes.
X * Start up terminal and internal administration.
X * Return 0 if all well, error code if in trouble.
X */
XVisible int trmstart(plines, pcols, pflags) int *plines, *pcols, *pflags; {
X register int err;
X
X#ifdef VTRMTRACE
X if (vtrmfp) putstr(vtrmfp, "\ttrmstart(&li, &co, &fl);\n");
X#endif
X if (started)
X return TE_TWICE;
X err= getttyfp();
X if (err != TE_OK)
X return err;
X err= gettermcaps();
X if (err != TE_OK)
X return err;
X err= setttymode();
X if (err != TE_OK)
X return err;
X err= start_trm();
X if (err != TE_OK) {
X trmend();
X return err;
X }
X
X *plines = lines;
X *pcols = cols;
X *pflags = flags;
X
X started = Yes;
X
X trmsync(lines-1, 0);
X /* position for >ws message from initbws()
X * on vt100's, and for alternate screen buffers
X */
X
X return TE_OK;
X}
X
X/*
X * Termination call.
X * Reset tty modes, etc.
X * Beware that it might be called by a caught interrupt even in the middle
X * of trmstart()!
X */
XVisible Procedure trmend() {
X#ifdef VTRMTRACE
X if (vtrmfp) putstr(vtrmfp, "\ttrmend();\n");
X#endif
X set_mode(Normal);
X if (so_mode != Off)
X standend();
X Putstr(ke_str);
X Putstr(te_str);
X VOID fflush(fp);
X resetttymode();
X
X started = No;
X}
X
X/*
X * Set all internal statuses to undefined, especially the contents of
X * the screen, so a hard redraw will not be optimised to heaven.
X */
XVisible Procedure trmundefined() {
X register int y, x;
X#ifdef VTRMTRACE
X if (vtrmfp) putstr(vtrmfp, "\ttrmundefined();\n");
X#endif
X
X cur_y = cur_x = Undefined;
X mode = so_mode = Undefined;
X
X for (y = 0; y < lines; y++) {
X for (x = 0; x <= cols; x++)
X line[y][x] = 1; /* impossible char, no so bits */
X lenline[y] = cols;
X }
X}
X
X#ifdef VTRMTRACE
X
XHidden Procedure check_started(m) char *m; {
X if (!started) {
X trmend();
X if (vtrmfp) putstr(vtrmfp, "bad VTRM call\n");
X abort();
X }
X}
X#else
X
X#define check_started(m) /*empty*/
X
X#endif /* NDEBUG */
X
XHidden int getttyfp() {
X if (fp != NULL) /* already initialised */
X return TE_OK;
X fp= fopen("/dev/tty", "w");
X if (fp == NULL)
X return TE_NOTTY;
X return TE_OK;
X}
X
XHidden int ccc;
X
X/*ARGSUSED*/
XHidden Procedure countchar(ch) char ch; {
X ccc++;
X}
X
XHidden int strcost(str) char *str; {
X if (str == NULL)
X return Infinity;
X return str0cost(str);
X}
X
XHidden int str0cost(str) char *str; {
X ccc = 0;
X tputs(str, 1, countchar);
X return ccc;
X}
X
X/*
X * Get terminal capabilities from termcap and compute related static
X * properties. Return TE_OK if all well, error code otherwise.
X */
X
XHidden int gettermcaps() {
X string trmname;
X char tc_buf[1024];
X static char strbuf[1024];
X char *area = strbuf;
X int sg;
X static bool tc_initialized = No;
X
X if (tc_initialized)
X return TE_OK;
X
X trmname=getenv("TERM");
X if (trmname == NULL || trmname[0] == '\0')
X return TE_NOTERM;
X if (tgetent(tc_buf, trmname) != 1)
X return TE_BADTERM;
X
X getcaps(&area); /* Read all flag and string type capabilities */
X if (hardcopy)
X return TE_DUMB;
X BC = le_str;
X if (BC == NULL) {
X BC = bc_str;
X if (BC == NULL) {
X if (has_bs)
X BC = "\b";
X else
X return TE_DUMB;
X }
X }
X UP = up_str;
X if (UP == NULL)
X return TE_DUMB;
X PC = (pc_str != NULL? pc_str[0] : NULCHAR);
X
X if (cm_str == NULL) {
X cm_str = cap_cm_str;
X if (cm_str == NULL) {
X if (ho_str == NULL || do_str == NULL || nd_str == NULL)
X return TE_DUMB;
X }
X else
X mustclear = Yes;
X }
X if (al_str && dl_str) {
X scr_up = scr1up;
X scr_down = scr1down;
X flags |= CAN_SCROLL;
X }
X else {
X if (sf_str == NULL)
X sf_str = "\n";
X if (cs_str && sr_str) {
X scr_up = scr2up;
X scr_down = scr2down;
X flags |= CAN_SCROLL;
X }
X else
X return TE_DUMB;
X }
X
X lines = tgetnum("li");
X cols = tgetnum("co");
X if (lines <= 0) lines = 24;
X if (cols <= 0) cols = 80;
X
X if (!ce_str)
X return TE_DUMB;
X if (cr_str == NULL) cr_str = "\r";
X if (do_str == NULL) {
X do_str = nl_str;
X if (do_str == NULL) do_str = "\n";
X }
X le_str = BC;
X up_str = UP;
X if (vb_str == NULL) /* then we will do with the audible bell */
X vb_str = "\007";
X
X /* cursor sensing (non standard) */
X if (cp_str != NULL && sp_str != NULL)
X flags |= CAN_SENSE;
X
X if (so_str != NULL && se_str != NULL && (sg=tgetnum("sg")) <= 0) {
X if (sg == 0)
X has_xs = Yes;
X flags |= HAS_STANDOUT;
X }
X else if (us_str != NULL && ue_str != NULL) {
X so_str = us_str; se_str = ue_str;
X flags |= HAS_STANDOUT;
X }
X else
X return TE_DUMB;
X
X /* calculate costs of local and absolute cursor motions */
X if (cm_str == NULL)
X abs_cost = Infinity;
X else
X abs_cost = strcost(tgoto(cm_str, 0, 0));
X cr_cost = strcost(cr_str);
X do_cost = strcost(do_str);
X le_cost = strcost(le_str);
X nd_cost = strcost(nd_str);
X up_cost = strcost(up_str);
X
X /* cost of leaving insert or delete mode, used in move() */
X ei_cost = str0cost(ei_str);
X ed_cost = str0cost(ed_str);
X
X /* calculate insert and delete cost multiply_factor and overhead */
X if (((im_str && ei_str) || ic_str) && dc_str) {
X flags |= CAN_OPTIMISE;
X ins_mf = 1 + str0cost(ic_str);
X ins_oh = str0cost(im_str) + ei_cost;
X del_mf = str0cost(dc_str);
X del_oh = str0cost(dm_str) + ed_cost;
X }
X
X tc_initialized = Yes;
X return TE_OK;
X}
X
XHidden char intrchar;
X#ifdef SIGTSTP
XHidden char suspchar;
X#endif
X
XHidden int setttymode() {
X if (!know_ttys) {
X if (gtty(0, &oldtty) != 0 || gtty(0, &newtty) != 0)
X return TE_NOTTY;
X#ifndef TERMIO
X ospeed = oldtty.sg_ospeed;
X newtty.sg_flags = (newtty.sg_flags & ~ECHO & ~CRMOD & ~XTABS)
X | CBREAK;
X#ifdef TIOCSLTC
X VOID ioctl(0, TIOCGLTC, (char *) &oldltchars);
X#endif
X#ifdef TIOCSETC
X VOID ioctl(0, TIOCGETC, (char *) &oldtchars);
X#endif
X
X#else /* TERMIO */
X ospeed= oldtty.c_lflag & CBAUD;
X newtty.c_iflag &= ~ICRNL; /* No CR->NL mapping on input */
X newtty.c_oflag &= ~ONLCR; /* NL doesn't output CR */
X#ifndef KEYS
X newtty.c_lflag &= ~(ICANON|ECHO);
X /* No line editing, no echo */
X newtty.c_cc[VQUIT]= 0377; /* no quit signal */
X#ifdef VSUSP
X newtty.c_cc[VSUSP]= 0377; /* no susp signal */
X#endif
X#else
X newtty.c_lflag &= ~(ICANON|ECHO|ISIG);
X /* No line editing, no echo,
X * no quit, intr or susp signals */
X#endif /* KEYS */
X newtty.c_cc[VMIN]= 3; /* wait for 3 characters */
X newtty.c_cc[VTIME]= 1; /* or 0.1 sec. */
X#endif /* TERMIO */
X know_ttys = Yes;
X }
X stty(0, &newtty);
X#ifndef TERMIO
X#ifdef TIOCSLTC
X newltchars.t_suspc= oldltchars.t_suspc;
X#ifdef SIGTSTP
X suspchar= oldltchars.t_suspc;
X#endif
X VOID ioctl(0, TIOCSLTC, (char *) &newltchars);
X#endif
X#ifdef TIOCSETC
X VOID ioctl(0, TIOCGETC, (char *) &newtchars);
X#ifndef KEYS
X intrchar= newtchars.t_intrc;
X#else
X newtchars.t_intrc= -1;
X#endif
X newtchars.t_quitc= -1;
X newtchars.t_eofc= -1;
X newtchars.t_brkc= -1;
X VOID ioctl(0, TIOCSETC, (char *) &newtchars);
X#endif /* TIOCSETC */
X#endif /* TERMIO */
X return TE_OK;
X}
X
XHidden Procedure resetttymode() {
X if (know_ttys) {
X stty(0, &oldtty);
X#ifndef TERMIO
X#ifdef TIOCSLTC
X VOID ioctl(0, TIOCSLTC, (char *) &oldltchars);
X#endif
X#ifdef TIOCSETC
X VOID ioctl(0, TIOCSETC, (char *) &oldtchars);
X#endif
X#endif /* TERMIO */
X know_ttys= No;
X }
X}
X
XHidden int start_trm() {
X register int y;
X#ifdef TIOCGWINSZ
X struct winsize win;
X
X if (ioctl(0, TIOCGWINSZ, (char*)&win) == 0) {
X if (win.ws_col > 0 && ((int) win.ws_col) != cols
X ||
X win.ws_row > 0 && ((int) win.ws_row) != lines) {
X /* Window size has changed.
X Release previously allocated buffers. */
X if (line != NULL) {
X for (y= 0; y < lines; ++y)
X free((char *) line[y]);
X free((char *) line);
X line= NULL;
X }
X if (lenline != NULL) {
X free((char *) lenline);
X lenline= NULL;
X }
X }
X if (((int)win.ws_col) > 0)
X cols = win.ws_col;
X if (((int)win.ws_row) > 0)
X lines = win.ws_row;
X }
X#endif
X if (line == NULL) {
X if ((line = (short**) malloc(lines * sizeof(short*))) == NULL)
X return TE_NOMEM;
X for (y = 0; y < lines; y++) {
X if ((line[y] = (short*)
X malloc((cols+1) * sizeof(short))) == NULL)
X return TE_NOMEM;
X }
X }
X if (lenline == NULL) {
X if ((lenline = (short*) malloc(lines * sizeof(short))) == NULL)
X return TE_NOMEM;
X }
X
X trmundefined();
X
X Putstr(ti_str);
X Putstr(ks_str);
X if (cs_str)
X Putstr(tgoto(cs_str, lines-1, 0));
X if (mustclear)
X clear_lines(0, lines-1);
X VOID fflush(fp);
X
X return TE_OK;
X}
X
X
X/*
X * Sensing and moving the cursor.
X */
X
X/*
X * Sense the current (y, x) cursor position, after a possible manual
X * change by the user with local cursor motions.
X * If the terminal cannot be asked for the current cursor position,
X * or if the string returned by the terminal is garbled,
X * the position is made Undefined.
X */
X
XVisible Procedure trmsense(py, px) int *py, *px; {
X bool get_pos();
X
X#ifdef VTRMTRACE
X if (vtrmfp) putstr(vtrmfp, "\ttrmsense(&yy, &xx);\n");
X#endif
X check_started("trmsense");
X
X *py = *px = Undefined;
X set_mode(Normal);
X if (so_mode != Off)
X standend();
X
X if (flags&CAN_SENSE && get_pos(py, px)) {
X if (*py < 0 || lines <= *py || *px < 0 || cols <= *px)
X *py = *px = Undefined;
X }
X cur_y = Undefined;
X cur_x = Undefined;
X}
X
XHidden bool get_pos(py, px) int *py, *px; {
X char *format = cp_str;
X int fc; /* current format character */
X int ic; /* current input character */
X int num;
X int on_y = 1;
X bool incr_orig = No;
X int i, ni;
X
X Putstr(sp_str);
X VOID fflush(fp);
X
X while (fc = *format++) {
X if (fc != '%') {
X if (trminput() != fc)
X return No;
X }
X else {
X switch (fc = *format++) {
X case '%':
X if (trminput() != '%')
X return No;
X continue;
X case 'r':
X on_y = 1 - on_y;
X continue;
X case 'i':
X incr_orig = Yes;
X continue;
X case 'd':
X ic = trminput();
X if (!isdigit(ic))
X return No;
X num = ic - '0';
X while (isdigit(ic=trminput()))
X num = 10*num + ic - '0';
X trmpushback(ic);
X break;
X case '2':
X case '3':
X ni = fc - '0';
X num = 0;
X for (i=0; i<ni; i++) {
X ic = trminput();
X if (isdigit(ic))
X num = 10*num + ic - '0';
X else
X return No;
X }
X break;
X case '+':
X num = trminput() - *format++;
X break;
X case '-':
X num = trminput() + *format++;
X break;
X default:
X return No;
X }
X /* assign num to parameter */
X if (incr_orig)
X num--;
X if (on_y)
X *py = num;
X else
X *px = num;
X on_y = 1 - on_y;
X }
X }
X
X return Yes;
X}
X
X/*
X * To move over characters by rewriting them, we have to check:
X * (1) that the screen has been initialised on these positions;
X * (2) we do not screw up characters
X * when rewriting line[y] from x_from upto x_to
X */
XHidden bool rewrite_ok(y, xfrom, xto) int y, xfrom, xto; {
X register short *plnyx, *plnyto;
X
X if (xto > lenline[y])
X return No;
X
X plnyto = &line[y][xto];
X for (plnyx = &line[y][xfrom]; plnyx <= plnyto; plnyx++)
X if (*plnyx == UNKNOWN
X ||
X (!has_xs && (*plnyx & SOBIT) != so_mode)
X )
X return No;
X return Yes;
X}
X
X/*
X * Move to position y,x on the screen
X */
X/* possible move types for y and x respectively: */
X#define None 0
X#define Down 1
X#define Up 2
X#define Right 1
X#define ReWrite 2
X#define Left 3
X#define CrWrite 4
X
XHidden Procedure move(y, x) int y, x; {
X int dy, dx;
X int y_cost, x_cost, y_move, x_move;
X int mode_cost;
X int xi;
X
X if (cur_y == y && cur_x == x)
X return;
X
X if (!has_mi || mode == Undefined)
X set_mode(Normal);
X if (!has_xs && ((!has_ms && so_mode != Off) || so_mode == Undefined))
X standend();
X
X if (cur_y == Undefined || cur_x == Undefined)
X goto absmove;
X
X dy = y - cur_y;
X dx = x - cur_x;
X
X if (dy > 0) {
X y_move = Down;
X y_cost = dy * do_cost;
X }
X else if (dy < 0) {
X y_move = Up;
X y_cost = -dy * up_cost;
X }
X else {
X y_move = None;
X y_cost = 0;
X }
X if (y_cost < abs_cost) {
X switch (mode) {
X case Normal:
X mode_cost = 0;
X break;
X case Insert:
X mode_cost = ei_cost;
X break;
X case Delete:
X mode_cost = ed_cost;
X break;
X }
X if (dx > 0) {
X x_cost = dx + mode_cost;
X if (dx*nd_cost < x_cost || !rewrite_ok(y, cur_x, x)) {
X x_cost = dx * nd_cost;
X x_move = Right;
X }
X else
X x_move = ReWrite;
X }
X else if (dx < 0) {
X x_cost = -dx * le_cost;
X x_move = Left;
X }
X else {
X x_cost = 0;
X x_move = None;
X }
X if (cr_cost + x + mode_cost < x_cost && rewrite_ok(y, 0, x)) {
X x_move = CrWrite;
X x_cost = cr_cost + x + mode_cost;
X }
X }
X else
X x_cost = abs_cost;
X
X if (y_cost + x_cost < abs_cost) {
X switch (y_move) {
X case Down:
X while (dy-- > 0) Putstr(do_str);
X break;
X case Up:
X while (dy++ < 0) Putstr(up_str);
X break;
X }
X switch (x_move) {
X case Right:
X while (dx-- > 0) Putstr(nd_str);
X break;
X case Left:
X while (dx++ < 0) Putstr(le_str);
X break;
X case CrWrite:
X Putstr(cr_str);
X cur_x = 0;
X /* FALL THROUGH */
X case ReWrite:
X set_mode(Normal);
X for (xi = cur_x; xi < x; xi++)
X fputc(line[y][xi], fp);
X break;
X }
X }
X else
X {
X absmove:
X if (cm_str == NULL) {
X Putstr(ho_str);
X for (cur_y = 0; cur_y < y; ++cur_y)
X Putstr(do_str);
X /* Should try to use tabs here: */
X for (cur_x = 0; cur_x < x; ++cur_x)
X Putstr(nd_str);
X }
X else
X Putstr(tgoto(cm_str, x, y));
X }
X
X cur_y = y;
X cur_x = x;
X}
X
X
X/*
X * Putting data on the screen.
X */
X
X/*
X * Fill screen area with given data.
X * Characters with the SO-bit (0200) set are put in standout mode.
X */
XVisible Procedure trmputdata(yfirst, ylast, indent, data) int yfirst, ylast;
X register int indent; register string data; {
X register int y;
X int x, len, lendata, space;
X
X#ifdef VTRMTRACE
X if (vtrmfp) put3DSstr(vtrmfp, "\ttrmputdata(%d, %d, %d, \"%s\");\n", yfirst, ylast, indent, data);
X#endif
X check_started("trmputdata");
X
X if (yfirst < 0)
X yfirst = 0;
X if (ylast >= lines)
X ylast = lines-1;
X space = cols*(ylast-yfirst+1) - indent;
X if (space <= 0)
X return;
X yfirst += indent/cols;
X indent %= cols;
X y= yfirst;
X if (!data)
X data= ""; /* Safety net */
X x = indent;
X lendata = strlen(data);
X if (ylast == lines-1 && lendata >= space)
X lendata = space - 1;
X len = Min(lendata, cols-x);
X while (y <= ylast) {
X put_line(y, x, data, len);
X y++;
X lendata -= len;
X if (lendata > 0) {
X x = 0;
X data += len;
X len = Min(lendata, cols);
X }
X else
X break;
X }
X if (y <= ylast)
X clear_lines(y, ylast);
X}
X
X/*
X * We will first try to get the picture:
X *
X * op>>>>>>>>>>>op oq<<<<<<<<<<<<<<<<<<<<<<<<oq
X * ^ ^ ^ ^
X * <xskip><-----m1----><----od-----><-----------m2----------->
X * OLD: "You're in a maze of twisty little pieces of code, all alike"
X * NEW: "in a maze of little twisting pieces of code, all alike"
X * <-----m1----><-----nd------><-----------m2----------->
X * ^ ^ ^ ^
X * np>>>>>>>>>>>np nq<<<<<<<<<<<<<<<<<<<<<<<<nq
X * where
X * op, oq, np, nq are pointers to start and end of Old and New data,
X * and
X * xskip = length of indent to be skipped,
X * m1 = length of Matching part at start,
X * od = length of Differing mid on screen,
X * nd = length of Differing mid in data to be put,
X * m2 = length of Matching trail.
X *
X * Then we will try to find a long blank-or-cleared piece in <nd+m2>:
X *
X * <---m1---><---d1---><---nb---><---d2---><---m2--->
X * ^ ^ ^ ^ ^
X * np bp bq1 nq nend
X * where
X * bp, bq are pointers to start and AFTER end of blank piece,
X * and
X * d1 = length of differing part before blank piece,
X * nb = length of blank piece to be skipped,
X * d2 = length of differing part after blank piece.
X * Remarks:
X * d1 + nb + d2 == nd,
X * and
X * d2 maybe less than 0.
X */
XHidden int put_line(y, xskip, data, len) int y, xskip; string data; int len; {
X register short *op, *oq;
X register char *np, *nq, *nend;
X char *bp, *bq1, *p, *q;
X int m1, m2, od, nd, delta, dd, d1, nb, d2;
X bool skipping;
X int cost, o_cost; /* normal and optimising cost */
X
X /* Bugfix GvR 19-June-87: */
X while (lenline[y] < xskip)
X line[y][lenline[y]++] = ' ';
X
X /* calculate the magic parameters */
X op = &line[y][xskip];
X oq = &line[y][lenline[y]-1];
X np = data;
X nq = nend = data + len - 1;
X m1 = m2 = 0;
X while ((*op&SOCHAR) == (((short)*np)&SOCHAR) && op <= oq && np <= nq)
X op++, np++, m1++;
X /* calculate m2, iff we can optimise or line keeps same length: */
X if (flags & CAN_OPTIMISE || (oq-op) == (nq-np))
X while ((*oq&SOCHAR) == (((short)*nq)&SOCHAR) && op <= oq && np <= nq)
X oq--, nq--, m2++;
X od = oq - op + 1;
X nd = nq - np + 1;
X /* now we have the first picture above */
X
X if (od==0 && nd==0)
X return;
X delta = nd - od;
X
X /* find the blank piece */
X p = q = bp = bq1 = np;
X oq += m2; /* back to current eol */
X if (delta == 0) /* if no change in linelength */
X nend -= m2; /* don't find blanks in m2 */
X if (!has_in) {
X while (p <= nend) {
X while (q<=nend && *q==' ' && (op>oq || *op==' '))
X q++, op++;
X if (q - p > bq1 - bp)
X bp = p, bq1 = q;
X p = ++q;
X op++;
X }
X }
X d1 = bp - np;
X nb = bq1 - bp;
X d2 = nq - bq1 + 1;
X
X /* what is cheapest:
X * ([+m2] means: leave m2 alone if same linelength)
X * normal: put nd[+m2]; (dd = nd[+m2])
X * skipping: put d1, skip nb, put d2[+m2]; (dd = d2[+m2])
X * optimise: put dd, insert or delete delta. (dd = min(od,nd))
X */
X cost = nd + (delta == 0 ? 0 : m2); /* normal cost */
X if (nb > abs_cost || (d1 == 0 && nb > 0)) {
X skipping = Yes;
X cost -= nb - (d1>0 ? abs_cost : 0); /* skipping cost */
X dd = d2;
X }
X else {
X skipping = No;
X dd = nd;
X }
X
X if (m2 != 0 && delta != 0) {
X /* try optimising */
X o_cost = Min(od, nd);
X if (delta > 0)
X o_cost += delta * ins_mf + ins_oh;
X else if (delta < 0)
X o_cost += -delta * del_mf + del_oh;
X if (o_cost >= cost) {
X /* discard m2, no optimise */
X dd += m2;
X m2 = 0;
X }
X else {
X dd = Min(od, nd);
X skipping = No;
X }
X }
X
X /* and now for the real work */
X if (!skipping || d1 > 0)
X move(y, xskip + m1);
X
X if (has_xs)
X get_so_mode();
X
X if (skipping) {
X if (d1 > 0) {
X set_mode(Normal);
X put_str(np, d1, No);
X }
X if (has_xs && so_mode != Off)
X standend();
X set_blanks(y, xskip+m1+d1, xskip+m1+d1+nb);
X if (dd != 0 || delta < 0) {
X move(y, xskip+m1+d1+nb);
X np = bq1;
X }
X }
X
X if (dd > 0) {
X set_mode(Normal);
X put_str(np, dd, No);
X }
X
X if (m2 > 0) {
X if (delta > 0) {
X set_mode(Insert);
X ins_str(np+dd, delta);
X }
X else if (delta < 0) {
X if (so_mode != Off)
X standend();
X /* Some terminals fill with standout spaces! */
X set_mode(Delete);
X del_str(-delta);
X }
X }
X else {
X if (delta < 0) {
X clr_to_eol();
X return;
X }
X }
X
X lenline[y] = xskip + len;
X if (cur_x == cols) {
X if (!has_mi)
X set_mode(Normal);
X if (!has_ms)
X so_mode = Undefined;
X if (has_am) {
X if (has_xn)
X cur_y= Undefined;
X else
X cur_y++;
X }
X else
X Putstr(cr_str);
X cur_x = 0;
X }
X else if (has_xs) {
X if (m2 == 0) {
X if (so_mode == On)
X standend();
X }
X else {
X if (!(line[cur_y][cur_x] & XSBIT)) {
X if (so_mode != (line[cur_y][cur_x] & SOBIT))
X (so_mode ? standend() : standout());
X }
X }
X }
X}
X
XHidden Procedure set_mode(m) int m; {
X if (m == mode)
X return;
X switch (mode) {
X case Insert:
X Putstr(ei_str);
X break;
X case Delete:
X Putstr(ed_str);
X break;
X case Undefined:
X Putstr(ei_str);
X Putstr(ed_str);
X break;
X }
X switch (m) {
X case Insert:
X Putstr(im_str);
X break;
X case Delete:
X Putstr(dm_str);
X break;
X }
X mode = m;
X}
X
XHidden Procedure get_so_mode() {
X if (cur_x >= lenline[cur_y] || line[cur_y][cur_x] == UNKNOWN)
X so_mode = Off;
X else
X so_mode = line[cur_y][cur_x] & SOBIT;
X}
X
XHidden Procedure standout() {
X Putstr(so_str);
X so_mode = On;
X if (has_xs)
X line[cur_y][cur_x] |= SOCOOK;
X}
X
XHidden Procedure standend() {
X Putstr(se_str);
X so_mode = Off;
X if (has_xs)
X line[cur_y][cur_x] = (line[cur_y][cur_x] & ~SOBIT) | XSBIT;
X}
X
XHidden Procedure put_str(data, n, inserting) char *data; int n; bool inserting; {
X register short c, so;
X short *ln_y_x, *ln_y_end;
X
X so = so_mode;
X if (has_xs) {
X ln_y_x = &line[cur_y][cur_x];
X ln_y_end = &line[cur_y][lenline[cur_y]];
X }
X while (n-- > 0) {
X if (has_xs && ln_y_x <= ln_y_end && ((*ln_y_x)&XSBIT))
X so = so_mode = (*ln_y_x)&SOBIT;
X /* this also checks for the standend cookie AFTER */
X /* the line because off the equals sign in <= */
X c = ((short)(*data++))&SOCHAR;
X if ((c&SOBIT) != so) {
X so = c&SOBIT;
X so ? standout() : standend();
X }
X if (inserting)
X Putstr(ic_str);
X put_c(c);
X if (has_xs)
X ln_y_x++;
X }
X}
X
XHidden Procedure ins_str(data, n) char *data; int n; {
X int x;
X
X /* x will start AFTER the line, because there might be a cookie */
X for (x = lenline[cur_y]; x >= cur_x; x--)
X line[cur_y][x+n] = line[cur_y][x];
X put_str(data, n, Yes);
X}
X
XHidden Procedure del_str(n) int n; {
X int x, xto;
X
X xto = lenline[cur_y] - n; /* again one too far because of cookie */
X if (has_xs) {
X for (x = cur_x + n; x >= cur_x; x--) {
X if (line[cur_y][x] & XSBIT)
X break;
X }
X if (x >= cur_x)
X line[cur_y][cur_x+n] =
X (line[cur_y][cur_x+n] & CHAR)
X |
X (line[cur_y][x] & COOKBITS);
X }
X for (x = cur_x; x <= xto; x++)
X line[cur_y][x] = line[cur_y][x+n];
X while (n-- > 0)
X Putstr(dc_str);
X}
X
XHidden Procedure put_c(c) int c; {
X char ch;
X short xs_flag;
X
X ch = c&CHAR;
X if (!isprint(ch) && ch != ' ') /* V7 isprint doesn't include blank */
X ch= '?';
X fputc(ch, fp);
X if (has_xs)
X xs_flag = line[cur_y][cur_x]&XSBIT;
X else
X xs_flag = 0;
X line[cur_y][cur_x] = (c&SOCHAR)|xs_flag;
X cur_x++;
X}
X
XHidden Procedure clear_lines(yfirst, ylast) int yfirst, ylast; {
X register int y;
X
X if (!has_xs && so_mode != Off)
X standend();
X if (cl_str && yfirst == 0 && ylast == lines-1) {
X Putstr(cl_str);
X cur_y = cur_x = 0;
X for (y = 0; y < lines; ++y) {
X lenline[y] = 0;
X if (has_xs) line[y][0] = NOCOOK;
X }
X return;
X }
X for (y = yfirst; y <= ylast; y++) {
X if (lenline[y] > 0) {
X move(y, 0);
X if (ylast == lines-1 && cd_str) {
X Putstr(cd_str);
X while (y <= ylast) {
X if (has_xs) line[y][0] = NOCOOK;
X lenline[y++] = 0;
X }
X break;
X }
X else {
X clr_to_eol();
X }
X }
X }
X}
X
XHidden Procedure clr_to_eol() {
X lenline[cur_y] = cur_x;
X if (!has_xs && so_mode != Off)
X standend();
X Putstr(ce_str);
X if (has_xs) {
X if (cur_x == 0)
X line[cur_y][0] = NOCOOK;
X else if (line[cur_y][cur_x-1]&SOBIT)
X standend();
X }
X}
X
XHidden Procedure set_blanks(y, xfrom, xto) int y, xfrom, xto; {
X register int x;
X
X for (x = xfrom; x < xto; x++) {
X line[y][x] = (line[y][x]&XSBIT) | ' ';
X }
X}
X
X/*
X * outchar() is used by termcap's tputs.
X */
XHidden int outchar(ch) char ch; {
X fputc(ch, fp);
X}
X
X/*
X * Scrolling (part of) the screen up (or down, by<0).
X */
X
XVisible Procedure trmscrollup(yfirst, ylast, by) register int yfirst;
X register int ylast; register int by; {
X#ifdef VTRMTRACE
X if (vtrmfp) put3Dstr(vtrmfp, "\ttrmscrollup(%d, %d, %d);\n", yfirst, ylast, by);
X#endif
X check_started("trmscrollup");
X
X if (yfirst < 0)
X yfirst = 0;
X if (ylast >= lines)
X ylast = lines-1;
X
X if (yfirst > ylast)
X return;
X
X if (!has_xs && so_mode != Off)
X standend();
X
X if (by > 0 && yfirst + by > ylast
X ||
X by < 0 && yfirst - by > ylast)
X {
X clear_lines(yfirst, ylast);
X return;
X }
X
X if (by > 0) {
X (*scr_up)(yfirst, ylast, by);
X scr_lines(yfirst, ylast, by, 1);
X }
X else if (by < 0) {
X (*scr_down)(yfirst, ylast, -by);
X scr_lines(ylast, yfirst, -by, -1);
X }
X}
X
XHidden Procedure scr_lines(yfrom, yto, n, dy) int yfrom, yto, n, dy; {
X register int y;
X short *saveln;
X
X while (n-- > 0) {
X saveln = line[yfrom];
X for (y = yfrom; y != yto; y += dy) {
X line[y] = line[y+dy];
X lenline[y] = lenline[y+dy];
X }
X line[yto] = saveln;
X lenline[yto] = 0;
X if (has_xs) line[yto][0] = NOCOOK;
X }
X}
X
XHidden Procedure scr1up(yfirst, ylast, n) int yfirst, ylast, n; {
X
X move(yfirst, 0);
X dellines(n);
X if (ylast < lines-1) {
X move(ylast-n+1, 0);
X addlines(n);
X }
X}
X
X
XHidden Procedure scr1down(yfirst, ylast, n) int yfirst, ylast, n; {
X if (ylast == lines-1) {
X clear_lines(ylast-n+1, ylast);
X }
X else {
X move(ylast-n+1, 0);
X dellines(n);
X }
X move(yfirst, 0);
X addlines(n);
X}
X
X
XHidden Procedure addlines(n) register int n; {
X if (par_al_str && n > 1)
X Putstr(tgoto(par_al_str, n, n));
X else {
X while (n-- > 0)
X Putstr(al_str);
X }
X}
X
X
XHidden Procedure dellines(n) register int n; {
X if (par_dl_str && n > 1)
X Putstr(tgoto(par_dl_str, n, n));
X else {
X while (n-- > 0)
X Putstr(dl_str);
X }
X}
X
X
XHidden Procedure scr2up(yfirst, ylast, n) int yfirst, ylast, n; {
X Putstr(tgoto(cs_str, ylast, yfirst));
X cur_y = cur_x = Undefined;
X move(ylast, 0);
X while (n-- > 0) {
X Putstr(sf_str);
X if (has_db && ylast == lines-1)
X clr_to_eol();
X }
X Putstr(tgoto(cs_str, lines-1, 0));
X cur_y = cur_x = Undefined;
X}
X
X
XHidden Procedure scr2down(yfirst, ylast, n) int yfirst, ylast, n; {
X Putstr(tgoto(cs_str, ylast, yfirst));
X cur_y = cur_x = Undefined;
X move(yfirst, 0);
X while (n-- > 0) {
X Putstr(sr_str);
X if (has_da && yfirst == 0)
X clr_to_eol();
X }
X Putstr(tgoto(cs_str, lines-1, 0));
X cur_y = cur_x = Undefined;
X}
X
X
X/*
X * Synchronization, move cursor to given position (or previous if < 0).
X */
X
XVisible Procedure trmsync(y, x) int y, x; {
X#ifdef VTRMTRACE
X if (vtrmfp) put2Dstr(vtrmfp, "\ttrmsync(%d, %d);\n", y, x);
X#endif
X check_started("trmsync");
X
X if (0 <= y && y < lines && 0 <= x && x < cols) {
X move(y, x);
X if (no_cursor) {
X Putstr(ve_str);
X no_cursor = No;
X }
X }
X else if (no_cursor == No) {
X Putstr(vi_str);
X no_cursor = Yes;
X }
X VOID fflush(fp);
X}
X
X
X/*
X * Send a bell, visible if possible.
X */
X
XVisible Procedure trmbell() {
X#ifdef VTRMTRACE
X if (vtrmfp) putstr(vtrmfp, "\ttrmbell();\n");
X#endif
X check_started("trmbell");
X
X Putstr(vb_str);
X VOID fflush(fp);
X}
X
X
X#ifdef SHOW
X
X/*
X * Show the current internal statuses of the screen on vtrmfp.
X * For debugging only.
X */
X
XVisible Procedure trmshow(s) char *s; {
X int y, x;
X
X if (!vtrmfp)
X return;
X putSstr(vtrmfp, "<<< %s >>>\n", s);
X for (y = 0; y < lines; y++) {
X for (x = 0; x <= lenline[y] /*** && x < cols-1 ***/ ; x++) {
X putchr(vtrmfp, line[y][x]&CHAR);
X }
X putnewline(vtrmfp);
X for (x = 0; x <= lenline[y] && x < cols-1; x++) {
X if (line[y][x]&SOBIT)
X putchr(vtrmfp, '-');
X else
X putchr(vtrmfp, ' ');
X }
X putnewline(vtrmfp);
X for (x = 0; x <= lenline[y] && x < cols-1; x++) {
X if (line[y][x]&XSBIT)
X putchr(vtrmfp, '+');
X else
X putchr(vtrmfp, ' ');
X }
X putnewline(vtrmfp);
X }
X put2Dstr(vtrmfp, "CUR_Y = %d, CUR_X = %d.\n", cur_y, cur_x);
X VOID fflush(vtrmfp);
X}
X#endif
X
X
X/*
X * Return the next input character, or -1 if read fails.
X * Only the low 7 bits are returned, so reading in RAW mode is permissible
X * (although CBREAK is preferred if implemented).
X * To avoid having to peek in the input buffer for trmavail, we use the
X * 'read' system call rather than getchar().
X * (The interface allows 8-bit characters to be returned, to accomodate
X * larger character sets!)
X */
X
X#ifdef SETJMP
X
XVisible jmp_buf readEinterrupt;
XVisible bool readEcontext;
X
X#endif /* SETJMP */
X
Xextern bool intrflag;
X#ifdef SIGTSTP
Xextern bool suspflag;
X#ifdef SETJMP
XVisible jmp_buf readEsuspend;
X#endif
X#endif
X
XHidden int pushback= -1;
X
Xint trminput() {
X char c;
X int n;
X
X if (intrflag) {
X intrflag= No;
X return intrchar & 0377;
X }
X#ifdef SIGTSTP
X if (suspflag) {
X suspflag= No;
X return suspchar & 0377;
X }
X#endif
X if (pushback >= 0) {
X c= pushback;
X pushback= -1;
X return c;
X }
X#ifdef SETJMP
X if (setjmp(readEinterrupt) != 0) {
X readEcontext= No;
X intrflag= No;
X return intrchar & 0377;
X }
X#ifdef SIGTSTP
X if (setjmp(readEsuspend) != 0) {
X readEcontext= No;
X suspflag= No;
X return suspchar & 0377;
X }
X#endif
X readEcontext= Yes;
X#endif /* SETJMP */
X n= read(0, &c, 1);
X#ifdef SETJMP
X readEcontext= No;
X#endif
X if (n <= 0)
X return -1;
X return c & 0377;
X}
X
Xtrmpushback(c) int c; {
X pushback= c;
X}
X
X
X/*
X * See if there's input available from the keyboard.
X * The code to do this is dependent on the type of Unix you have
X * (BSD, System V, ...).
X * Return value: 0 -- no input; 1 -- input; -1 -- unimplementable.
X * Note that each implementation form should first check pushback.
X *
X * TO DO:
X * - Implement it for other than 4.x BSD! (notably System 5)
X */
X
X#ifdef HAS_SELECT
X
X#include <sys/time.h>
X
Xint dep_trmavail() {
X int nfound, nfds, readfds;
X static struct timeval timeout= {0, 0};
X
X readfds= 1 << 0;
X nfds= 0+1;
X nfound= select(nfds, &readfds, (int*) 0, (int*) 0, &timeout);
X return nfound > 0;
X}
X
X#define TRMAVAIL_DEFINED
X
X#endif /* HAS_SELECT */
X
X#if !defined(TRMAVAIL_DEFINED) && defined(FIONREAD)
X
Xint dep_trmavail() {
X long n;
X
X ioctl(0, FIONREAD, (char *) &n);
X return n > 0;
X}
X
X#define TRMAVAIL_DEFINED
X
X#endif /* FIONREAD */
X
X#ifndef TRMAVAIL_DEFINED
X
Xint dep_trmavail() {
X return -1;
X}
X
X#endif
X
Xint trmavail() {
X if (intrflag)
X return 1;
X if (pushback >= 0)
X return 1;
X return dep_trmavail(); /* dependent code */
X}
X
X#ifdef UNUSED
X/*
X * Suspend the editor.
X * Should be called only after trmend and before trmstart!
X */
X
Xtrmsuspend() {
X#ifdef SIGTSTP
X SIGTYPE (*oldsig)();
X
X oldsig= signal(SIGTSTP, SIG_IGN);
X if (oldsig == SIG_IGN)
X return; /* Could spawn a subshell here... */
X trmend(); /* Safety net */
X signal(SIGTSTP, oldsig);
X kill(0, SIGSTOP);
X#endif /*SIGTSTP*/
X}
X#endif
X
X/*
X * DESCRIPTION.
X *
X * This package uses termcap to determine the terminal capabilities.
X *
X * The lines and columns of our virtual terminal are numbered
X * y = {0...lines-1} from top to bottom, and
X * x = {0...cols-1} from left to right,
X * respectively.
X *
X * The Visible Procedures in this package are:
X *
X * trmstart(&lines, &cols, &flags)
X * Obligatory initialization call (sets tty modes etc.),
X * Returns the height and width of the screen to the integers
X * whose addresses are passed as parameters, and a flag that
X * describes some capabilities.
X * Function return value: 0 if all went well, an error code if there
X * is any trouble. No messages are printed for errors.
X *
X * trmundefined()
X * Sets internal representation of screen and attributes to undefined.
X * This is necessary for a hard redraw, which would get optimised to
X * oblivion,
X *
X * trmsense(&y, &x)
X * Returns the cursor position through its parameters
X * after a possible manual change by the user.
X *
X * trmputdata(yfirst, ylast, indent, data)
X * Fill lines {yfirst..ylast} with data, after skipping the initial
X * 'indent' positions. It is assumed that these positions do not contain
X * anything dangerous (like standout cookies or null characters).
X *
X * trmscrollup(yfirst, ylast, by)
X * Shift lines {yfirst..ylast} up by lines (down |by| if by < 0).
X *
X * trmsync(y, x)
X * Call to output data to the terminal and set cursor position.
X *
X * trmbell()
X * Send a (possibly visible) bell, immediately (flushing fp).
X *
X * trmend()
X * Obligatory termination call (resets tty modes etc.).
X *
X * You may call these as one or more cycles of:
X * + trmstart
X * + zero or more times any of the other routines
X * + trmend
X * Trmend may be called even in the middle of trmstart; this is necessary
X * to make it possible to write an interrupt handler that resets the tty
X * state before exiting the program.
X *
X * ADDITIONAL SPECIFICATIONS (ROUTINES FOR CHARACTER INPUT)
X *
X * trminput()
X * Return the next input character (with its parity bit cleared
X * if any). This value is a nonnegative int. Returns -1 if the
X * input can't be read any more.
X *
X * trmavail()
X * Return 1 if there is an input character immediately available,
X * 0 if not. Return -1 if not implementable.
X *
X * trminterrupt()
X * Return 1 if an interrupt has occurred since the last call to
X * trminput or trmavail, 0 else. [Currently not implemented.]
X *
X * trmsuspend()
X * When called in the proper environment (4BSD with job control
X * enabled), suspends the editor, temporarily popping back to
X * the calling shell. The caller should have called trmend()
X * first, and must call trmstart again afterwards.
X * BUG: there is a timing window where keyboard-generated
X * signals (such as interrupt) can reach the program.
X */
END_OF_FILE
if test 40794 -ne `wc -c <'abc/unix/u1trm.c'`; then
echo shar: \"'abc/unix/u1trm.c'\" unpacked with wrong size!
fi
# end of 'abc/unix/u1trm.c'
fi
echo shar: End of archive 2 \(of 25\).
cp /dev/null ark2isdone
MISSING=""
for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 ; do
if test ! -f ark${I}isdone ; then
MISSING="${MISSING} ${I}"
fi
done
if test "${MISSING}" = "" ; then
echo You have unpacked all 25 archives.
rm -f ark[1-9]isdone ark[1-9][0-9]isdone
else
echo You still must unpack the following archives:
echo " " ${MISSING}
fi
exit 0 # Just in case...
--
Please send comp.sources.unix-related mail to rsalz at uunet.uu.net.
Use a domain-based address or give alternate paths, or you may lose out.
More information about the Comp.sources.unix
mailing list