Files
Arquivotheca.SunOS-4.1.4/usr.lib/libcurses/screen/insdelline.c
seta75D ff309bfe1c Init
2021-10-11 18:37:13 -03:00

519 lines
14 KiB
C

/* Copyright (c) 1984 AT&T */
/* All Rights Reserved */
/* THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF AT&T */
/* The copyright notice above does not evidence any */
/* actual or intended publication of such source code. */
#ifndef lint
static char sccsid[] = "@(#)insdelline.c 1.1 94/10/31 SMI"; /* from S5R3 1.4 */
#endif
/* Routines to deal with calculation of how to do ins/del line optimally */
#include "curses.ext"
int InputPending;
struct st { /* symbol table */
int hash; /* hashed value of line ("text") */
char oc, nc; /* 0, 1, or many: # times this value occurs */
short olno; /* line # of line in "old" array */
};
/*
* Update the Screen. Consider using insert and delete line.
* Using Heckel's algorithm from April 1978 CACM.
*
* This algorithm is based on two observations:
* (1) A line that occurs once and only once in each file must be
* the same line (unchanged but possibly moved).
* (2) If i each file immediately adjacent to a "found" line pair
* there are lines identical to each other, these lines must be
* the same line. Repeated application will "find" sequences
* of unchanged lines.
*
* We view the old and new screens' hashed values as the old and new "files".
* Since crossing match lines cannot be taken advantage of with real insert
* and delete line, we ignore those matches - this is the one real disadvantage
* of this algorithm.
*
* first and last are the integer line numbers (first line is 1) of the
* portion of the screen to consider.
*/
_id_line(first, last)
register int first, last;
{
register int i, j, k, n;
int base, ndel;
int idl_offset = 0; /* number of extra del lines done so far. */
#ifdef NONSTANDARD
static
#endif /* NONSTANDARD */
struct st symtab[256];
/*
* oa and na represent the old and new files (SP->cur_body and
* SP->std_body, respectively.) If they are > 0 they are
* subscripts in each other (e.g. na[4] = 3 means that na[4]
* points to oa[3]). If they are <= 0 their negative is taken
* as a symbol table index into the symtab array.
*/
#ifdef NONSTANDARD
static
#endif /* NONSTANDARD */
short oa[256], na[256];
for (i=0; i<256; i++)
symtab[i].hash = symtab[i].oc = symtab[i].nc = 0;
#ifdef DEBUG
if(outf) fprintf(outf, "_id_line(%d, %d)\n", first, last);
#endif
/* Pass 1: enter old file into symtab */
for (i=first; i<=last; i++) {
n = _getst(SP->std_body[i]->hash, symtab);
symtab[n].nc++;
na[i] = -n;
}
#ifdef LONGDEBUG
if(outf) fprintf(outf, "Pass 2\n");
if(outf) fprintf(outf, "na[12] = %d\n", na[12]);
#endif
/* Pass 2: enter new into symtab */
for (i=first; i<=last; i++) {
n = _getst(SP->cur_body[i]->hash, symtab);
symtab[n].oc++;
symtab[n].olno = i;
oa[i] = -n;
}
#ifdef LONGDEBUG
if(outf) fprintf(outf, "Pass 3\n");
if(outf) fprintf(outf, "\nsymtab oc nc olno hash\n");
for (i=0; i<256; i++)
if (symtab[i].hash)
if(outf) fprintf(outf, "%d %d %d %d %d\n",
i, symtab[i].oc, symtab[i].nc,
symtab[i].olno, symtab[i].hash);
#endif
/*
* Pass 3: Match all lines with exactly one match.
* Matching lines in oa and na point at each other.
*/
for (i=first; i<=last; i++) {
n = -na[i];
if (symtab[n].oc == 1 && symtab[n].nc == 1) {
na[i] = symtab[n].olno;
oa[na[i]] = i;
}
}
oa[first-1] = na[first-1] = first-1;
oa[last+1] = na[last+1] = last+1;
/* Pass 4: Find following implied matches based on sequence */
#ifdef DEBUG
if(outf) fprintf(outf, "Pass 4\n");
#endif
for (i=first; i<=last; i++) {
if (na[i] > 0 && na[i+1] <= 0) {
j = na[i];
if (na[i+1] == oa[j+1]) {
oa[j+1] = i+1;
na[i+1] = j+1;
}
}
}
/* Pass 5: Find preceding implied matches based on sequence */
#ifdef DEBUG
if(outf) fprintf(outf, "Pass 5\n");
if(outf) fprintf(outf, "na[12] = %d\n", na[12]);
#endif
for (i=last; i>=first; i--) {
if (na[i] > 0 && na[i-1] <= 0) {
j = na[i];
if (na[i-1] == oa[j-1]) {
oa[j-1] = i-1;
na[i-1] = j-1;
}
}
}
#ifdef DEBUG
if(outf) fprintf(outf, "\ni oa na After pass 5\n");
for (i=first; i<=last; i++)
if(outf) fprintf(outf, "%d %d %d\n", i, oa[i], na[i]);
if(outf) fprintf(outf, "\n");
#endif
/* Pass 6: Find matches from more than one line, in order. */
for (i=first; i<=last; i++) {
if (na[i] < -1 && symtab[-na[i]].nc > 1) {
j = na[i];
#ifdef DEBUG
if(outf) fprintf(outf, "i %d, na[i] %d\n", i, na[i]);
#endif
for (k=first; k<last; k++) {
if (j == oa[k]) {
#ifdef DEBUG
if(outf) fprintf(outf, "k %d, oa[i] %d, matching them up\n", k, oa[i]);
#endif
na[i] = k;
oa[k] = i;
break;
}
}
}
}
#ifdef DEBUG
if(outf) fprintf(outf, "\ni oa na After pass 6\n");
for (i=first; i<=last; i++)
if(outf) fprintf(outf, "%d %d %d\n", i, oa[i], na[i]);
if(outf) fprintf(outf, "\n");
#endif
/*
* Passes 7abcd: Remove any match lines that cross backwards.
* (Draw lines connecting the matching lines on debug output
* to see what I mean about crossing lines.)
* The na's must be monotonically increasing except for those < 0
* which say there is no match. k counts the number of matches
* we have to throw away.
*/
n = k = 0; /* k is the added cost to throw away nw-se matches */
/* 7a: get nw-se cost in k */
for (i=first; i<=last; i++) {
if (na[i] > 0 && na[i] < n) {
k += SP->std_body[i]->bodylen;
}
if (na[i] > n)
n = na[i];
}
/* 7b: get sw-ne cost in j */
/* Consider throwing away in the other direction. */
if (k > 0) {
j = 0; /* j is the added cost to throw away sw-ne matches */
n = last+1;
for (i=last; i>=first; i--) {
if (na[i] > 0 && na[i] > n) {
j += SP->std_body[i]->bodylen;
}
if (na[i] > 0 && na[i] < n)
n = na[i];
}
}
else
j = 1; /* will be > 0 for sure */
#ifdef DEBUG
if(outf) fprintf(outf, "forward, k is %d, backward, j is %d\n", k, j);
#endif
/* Remove whichever is cheapest. */
if (k < j) {
/* 7c: remove nw-se */
n = 0;
for (i=first; i<=last; i++) {
if (na[i] > 0 && na[i] < n) {
oa[na[i]] = -1;
na[i] = -1;
}
if (na[i] > n)
n = na[i];
}
} else {
/* 7d: remove sw-ne */
n = last+1;
for (i=last; i>=first; i--) {
if (na[i] > 0 && na[i] > n) {
oa[na[i]] = -1;
na[i] = -1;
}
if (na[i] > 0 && na[i] < n)
n = na[i];
}
}
#ifdef DEBUG
if(outf) fprintf(outf, "\ni oa na After pass 7\n");
for (i=first; i<=last; i++)
if(outf) fprintf(outf, "%d %d %d\n", i, oa[i], na[i]);
if(outf) fprintf(outf, "\nPass 7\n");
if(outf) fprintf(outf, "ILmf %d/32, ILov %d\n",
SP->term_costs.ilvar, SP->term_costs.ilfixed);
if(outf) fprintf(outf, "i base j k DC n\n");
#endif
/*
* Pass 8: we go through and remove all matches if the
* overall ins/del lines would be too expensive to capatilize on.
* j is cost with ins/del line, k is cost without it. base is the
* logical beginning of the oa array, as the array would be after
* inserts and deletes, ignoring anything before row i. This
* approximates things by not taking into account SP->curptr motion,
* funny insert line costs, or lines that are partially equal.
* This macro is the cost to insert or delete n lines at screen line i.
* It is approximated for speed and simplicity, but shouldn't be.
* The approximation assumes each line is deleted separately.
*/
#define idlcost(n, i) n * \
(((SP->term_costs.ilvar * (lines-i))>>5) + SP->term_costs.ilfixed)
base = ndel = 0;
j = k = 0;
for (i=first; i<=last; i++) {
#ifdef DEBUG
n = 0;
#endif
if (oa[i] != na[i]) {
/* Cost to turn Phys[i] into Des[i] on same line */
if (SP->cur_body[i] == 0 || SP->cur_body[i]->length == 0) {
k += SP->std_body[i]->bodylen;
} else if (SP->std_body[i] == 0 ||
SP->std_body[i]->length == 0) {
k += 2; /* guess at cost of clr to eol */
} else {
register chtype *p0, *p1, *p2;
n = SP->cur_body[i]->length -
SP->std_body[i]->length;
if (n > 0) {
k += n;
n = SP->std_body[i]->length;
} else {
k += -n;
n = SP->cur_body[i]->length;
}
p0 = &SP->std_body[i]->body[0];
p1 = &SP->std_body[i]->body[n];
p2 = &SP->cur_body[i]->body[n];
while (--p1 >= p0)
if (*p1 != *--p2)
k++;
}
}
/* cost to do using ins/del line */
if (na[i] <= 0) {
/* totally different - plan on redrawing whole thing
* (even though chances are good they are partly the
* same - _id_char will take care of this, if it
* happens to get the same line on the screen).
* Should probably figure out what will be there on
* the screen and do above k algorithm on it.
*/
j += SP->std_body[i]->bodylen;
} else if ((n = (na[i] - base) - i) > 0) {
/* delete line */
j += idlcost(n, i);
ndel += n;
base += n;
} else if (n < 0) {
/* insert line */
j += idlcost((-n), i);
ndel += n;
base += n;
}
/* else they are the same line: no cost */
#ifdef DEBUG
if (outf) {
fprintf(outf, "%d %d %d %d %d", i, base, j, k, SP->std_body[i]->bodylen);
if (n < 0)
fprintf(outf, " %d lines inserted", -n);
else if (n > 0)
fprintf(outf, " %d lines deleted", n);
fprintf(outf, "\n");
}
#endif
}
if (j >= k) {
/* It's as cheap to just redraw, so do it. */
for (i=first; i<=last; i++)
na[i] = oa[i] = -1;
} else if (ndel < 0) {
ndel = -ndel;
#ifdef DEBUG
if(outf) fprintf(outf, "will do %d extra inserts, so del now.\n", ndel);
#endif
if (SP->des_bot_mgn - last >= 0) {
_pos(last-ndel, 0);
idl_offset += ndel;
_dellines(ndel);
}
}
#ifdef DEBUG
if(outf) fprintf(outf, "\ni oa na After pass 8\n");
for (i=first; i<=last; i++)
if(outf) fprintf(outf, "%d %d %d\n", i, oa[i], na[i]);
if(outf) fprintf(outf, "\n");
#endif
/* Pass 9: Do any delete lines that are needed */
k = first-1; /* previous matching spot in oa */
ndel = 0;
for (i=first; i<=last; i++) {
if (oa[i] > 0) {
n = (i-k) - (oa[i]-oa[k]);
#ifdef DEBUG
if(outf) fprintf(outf,
"del loop, i %d, k %d, oa[i] %d, oa[k] %d, n %d\n",
i, k, oa[i], oa[k], n);
#endif
if (n > 0) {
if (i-n == SP->des_top_mgn+1) {
_scrollf(n);
} else {
_pos(i-n-1, 0);
_dellines(n);
}
idl_offset += n;
ndel += n;
for (j=i-n; j<=last-n; j++) {
if (oa[j] > 0 && na[oa[j]] > 0)
na[oa[j]] -= n;
_line_free(SP->cur_body[j]);
SP->cur_body[j] = SP->cur_body[j+n];
oa[j] = oa[j+n];
}
for ( ; j<=last; j++) {
if (oa[j] > 0 && na[oa[j]] > 0)
na[oa[j]] -= n;
_line_free(SP->cur_body[j]);
SP->cur_body[j] = NULL;
oa[j] = -1;
}
i -= n;
}
k = i;
}
}
#ifdef DEBUG
if(outf) fprintf(outf, "\ni oa na After pass 9\n");
for (i=first; i<=last; i++)
if(outf) fprintf(outf, "%d %d %d\n", i, oa[i], na[i]);
if(outf) fprintf(outf, "\n");
#endif
/* Half way through - check for typeahead */
_chk_typeahead();
if (idl_offset == 0 && InputPending) {
#ifdef DEBUG
if (outf) fprintf(outf, "InputPending %d, aborted after dellines\n", InputPending);
#endif
return;
}
/* for pass 10, j is the next line above i that will be used */
for (j=first; na[j] <= 0; j++)
;
/* Pass 10: insert and fix remaining lines */
for (i=first; i<=last; i++) {
if (j <= i)
for (j++; na[j] <= 0; j++)
;
#ifdef DEBUG
if(outf) {
fprintf(outf, "i %d, j %d, na[i] %d, na[j] %d\n",
i, j, na[i], na[j]);
if (na[i] > 0 && na[i] != i)
fprintf(outf, "OOPS: na[%d] is %d\n", i, na[i]);
}
#endif
/* There are two cases: na[i] <= 0 or na[i] == i */
if (na[i] <= 0) {
/*
* This line must be fixed from scratch. First
* check to see if the line physically there is
* going to be used later, if so, move it down.
*/
if (na[j]==i || ndel+i>last && last<SP->des_bot_mgn+1) {
_pos(i-1, 0);
n = j - i;
idl_offset -= n;
ndel -= n;
_inslines(n);
_chk_typeahead();
if (idl_offset == 0 && InputPending) {
#ifdef DEBUG
if (outf) fprintf(outf, "InputPending %d, aborted after dellines\n", InputPending);
#endif
return;
}
for (k=last; k>=j; k--) {
if (na[k] > 0)
na[k] += n;
_line_free(SP->cur_body[k]);
SP->cur_body[k] = SP->cur_body[k-n];
oa[k] = oa[k-n];
}
for ( ; k>=i; k--) {
if (na[k] > 0)
na[k] += n;
_line_free(SP->cur_body[k]);
SP->cur_body[k] = NULL;
oa[k] = 0;
}
}
}
/* Now transform line i to new line j */
if (!InputPending) {
_id_char (SP->cur_body[i], SP->std_body[i], i-1);
if (SP->cur_body[i] != SP->std_body[i])
_line_free (SP->cur_body[i]);
SP->cur_body[i] = SP->std_body[i];
}
}
#ifdef DEBUG
if(outf) fprintf(outf, "\ni oa na After pass 10: _id_line completed\n");
for (i=first; i<=last; i++)
if(outf) fprintf(outf, "%d %d %d\n", i, oa[i], na[i]);
if(outf) fprintf(outf, "\n");
#endif
}
static int
_getst(val, symtab)
register struct st *symtab;
{
register int i;
register int h;
register int hopcount = 256;
i = val & 0377;
while ((h=symtab[i].hash) && h != val) {
if (++i >= 256)
i = 0;
if (--hopcount <= 0) {
_ec_quit("Hash table full in dispcalc", "");
}
}
symtab[i].hash = val;
#ifdef LONGDEBUG
if(outf) fprintf(outf, "_getst, val %d, init slot %d, return %d\n",
val, val & 0377, i);
#endif
return i;
}
/*
* If it's been long enough, check to see if we have any typeahead
* waiting. If so, we quit this update until next time.
*/
_chk_typeahead()
{
#ifdef DEBUG
if(outf) fprintf(outf, "end of _id_char: --SP->check_input %d, InputPending %d, chars buffered %d: ", SP->check_input-1, InputPending, (SP->term_file->_ptr-SP->term_file->_base));
#endif
if(--SP->check_input<0 && !InputPending &&
((SP->term_file->_ptr - SP->term_file->_base) > 20)) {
/* __cflush(); */
if (SP->check_fd >= 0)
InputPending = _chk_input();
else
InputPending = 0;
SP->check_input = SP->baud / 2400;
#ifdef DEBUG
if(outf) fprintf(outf, "flush, ioctl returns %d, SP->check_input set to %d\n", InputPending, SP->check_input);
#endif
}
#ifdef DEBUG
if(outf) fprintf(outf, ".\n");
#endif
}