home *** CD-ROM | disk | FTP | other *** search
- /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1984. */
- static char rcsid[] = "$Header: cell.c,v 2.4 85/02/12 11:18:00 timo Exp $";
-
- /*
- * B editor -- Screen management package, cell list manipulation routines.
- */
-
- #include "b.h"
- #include "bobj.h"
- #include "node.h"
- #include "eval.h"
- #include "cell.h"
-
-
- extern bool dflag;
- extern bool noscroll;
-
- /*
- * Definitions for internals of cell manipulations.
- */
-
- Hidden cell *freelist;
-
- #define CELLSIZE (sizeof(cell))
-
- #ifndef PAGESIZE /* 4.2 BSD freaks compile with -DPAGESIZE='getpagesize()' */
- #define PAGESIZE 1024
- #endif
-
- #ifndef MALLOCLOSS
- #define MALLOCLOSS (sizeof(char*))
- /* number of bytes taken by malloc administration per block */
- #endif
-
-
- /*
- * Replace `oldlcnt' cells from `tops', starting at the one numbered `oldlno',
- * by the list `rep'.
- * Returns a pointer to the deleted chain (with a Nil end pointer).
- */
-
- Visible cell *
- replist(tops, rep, oldlno, oldlcnt)
- cell *tops;
- cell *rep;
- int oldlno;
- register int oldlcnt;
- {
- cell head;
- register cell *p;
- register cell *q;
- register cell *old;
- register cell *end;
- register int diff;
- int i;
- int replcnt;
-
- if (!tops) /* Start with empty list */
- return rep;
- head.c_link = tops;
- p = &head;
- for (diff = oldlno; diff > 0; --diff) {
- p = p->c_link;
- Assert(p);
- }
- q = p;
- for (i = oldlcnt; i > 0 && p; --i)
- p = p->c_link;
- if (i > 0) {
- #ifndef NDEBUG
- debug("[replist jackpot]");
- #endif NDEBUG
- oldlcnt -= i;
- }
- old = q->c_link;
- q->c_link = rep;
- if (p) {
- end = p->c_link;
- p->c_link = Cnil;
- }
- for (replcnt = 0; q->c_link; ++replcnt, q = q->c_link)
- ;
- dupmatch(old, rep, oldlcnt, replcnt);
- discard(old);
- if (p)
- q->c_link = end;
- return head.c_link;
- }
-
-
- /*
- * Allocate a new cell.
- */
-
- Hidden cell *
- newcell()
- {
- register cell *p;
-
- if (!freelist)
- feedfreelist();
- p = freelist;
- freelist = p->c_link;
- p->c_link = Cnil;
- return p;
- }
-
-
- /*
- * Feed the free list with a block of new entries.
- * We try to keep them together on a page
- * to keep consecutive accesses fast.
- */
-
- Hidden Procedure
- feedfreelist()
- {
- register int n = (PAGESIZE-MALLOCLOSS) / CELLSIZE;
- register cell *p = (cell*) malloc((unsigned)(n*CELLSIZE));
-
- Assert(n > 0);
- if (!p)
- syserr("feedfreelist: malloc");
- freelist = p;
- for (; n > 1; --n, ++p)
- p->c_link = p+1;
- p->c_link = Cnil;
- }
-
-
- /*
- * Discard all entries of a list of cells.
- */
-
- Visible Procedure
- discard(p)
- register cell *p;
- {
- register cell *savefreelist;
-
- if (!p)
- return;
- savefreelist = p;
- for (;;) {
- noderelease(p->c_data);
- p->c_data = Nnil;
- if (!p->c_link)
- break;
- p = p->c_link;
- }
- p->c_link = freelist;
- freelist = savefreelist;
- }
-
-
- /*
- * Replace the `onscreen' fields in the replacement chain by those
- * in the old chain, if they match.
- */
-
- Hidden Procedure
- dupmatch(old, rep, oldcnt, repcnt)
- register cell *old;
- register cell *rep;
- int oldcnt;
- int repcnt;
- {
- register int diff = repcnt - oldcnt;
-
- #ifndef NDEBUG
- if (dflag)
- debug("[dupmatch(oldcnt=%d, newcnt=%d)]", oldcnt, repcnt);
- #endif NDEBUG
- while (rep && old) {
- if (old->c_length == rep->c_length
- && eqlines(old->c_data, rep->c_data)) {
- if (old->c_onscreen != Nowhere) {
- rep->c_onscreen = old->c_onscreen;
- rep->c_oldindent = old->c_oldindent;
- rep->c_oldvhole = old->c_oldvhole;
- rep->c_oldfocus = old->c_oldfocus;
- }
- rep = rep->c_link;
- old = old->c_link;
- }
- else {
- if (diff >= 0) {
- --diff;
- rep = rep->c_link;
- }
- if (diff < 0) {
- ++diff;
- old = old->c_link;
- }
- }
- }
- }
-
-
- /*
- * Build a list of cells consisting of the first `lcnt' lines of the tree.
- */
-
- Visible cell *
- build(p, lcnt)
- /*auto*/ path p;
- register int lcnt;
- {
- cell head;
- register cell *q = &head;
-
- p = pathcopy(p);
- for (;;) {
- q = q->c_link = newcell();
- q->c_onscreen = Nowhere;
- q->c_data = nodecopy(tree(p));
- q->c_length = linelen(q->c_data);
- q->c_newindent = Level(p) * TABS;
- q->c_oldindent = 0;
- q->c_oldvhole = q->c_newvhole = q->c_oldfocus = q->c_newfocus = No;
- --lcnt;
- if (lcnt <= 0)
- break;
- nextline(&p) || Abort();
- }
- q->c_link = Cnil;
- pathrelease(p);
- return head.c_link;
- }
-
-
- /*
- * Decide which line is to be on top of the screen.
- * We slide a window through the list of lines, recognizing
- * lines of the focus and lines already on the screen,
- * and stop as soon as we find a reasonable focus position.
- *
- * - The focus must always be on the screen completely;
- * if it is larger than the screen, its first line must be
- * on top of the screen.
- * - When old lines can be retained, at least one line above
- * and below the focus must be shown; the retained lines
- * should be moved as little as possible.
- * - As little as possible blank space should be shown at the
- * bottom, even if the focus is at the end of the unit.
- * - If no rule applies, try to center the focus on the screen.
- * - If noscroll is Yes (the terminal can't scroll), and the top
- * line can't be retained, also try to center the focus on the
- * screen.
- */
-
- Visible cell *
- gettop(tops)
- cell *tops;
- {
- register cell *pfwa = tops; /* First line of sliding window */
- register cell *plwa = tops; /* Last+1 line of sliding window */
- register cell *pffocus = Cnil; /* First line of focus */
- cell *pscreen = Cnil; /* First line still on screen */
- register int nfwa = 0; /* Corresponding line numbers in parse tree */
- register int nlwa = 0;
- register int nffocus;
- int nlfocus;
- int nscreen;
- int size;
-
- for (;;) { /* plwa is the current candidate for top line. */
- if (!pfwa) {
- #ifndef NDEBUG
- debug("[Lost the focus!]");
- #endif NDEBUG
- return tops; /* To show *something*... */
- }
- while (plwa && nlwa < nfwa+winheight) {
- /* Find first line *not* in window */
- size = Space(plwa);
- if (plwa->c_newfocus) { /* Hit a focus line */
- if (!pffocus) { /* Note first focus line */
- pffocus = plwa;
- nffocus = nlwa;
- }
- nlfocus = nlwa + size;
- }
- if (plwa->c_onscreen != Nowhere) { /* Hello old chap */
- if (!pscreen) { /* Note first line on screen */
- pscreen = plwa;
- nscreen = nlwa;
- }
- }
- nlwa += size;
- plwa = plwa->c_link;
- }
- if (pffocus) {
- /* Focus in sight; stop at first reasonable opportunity */
- if (pffocus == pfwa)
- break; /* Grab last chance! */
- if (!noscroll && nlwa - nfwa <= winheight - winheight/3)
- break; /* Don't show too much white space at bottom */
- if (pffocus == pfwa->c_link && nlfocus < nfwa+winheight)
- break; /* Near top line */
- if (pscreen && (!noscroll || nffocus > nscreen)) {
- /* Conservatism may succeed */
- if (pscreen->c_onscreen >= nscreen - nfwa
- && (nlfocus < nfwa+winheight
- || !plwa && nlfocus == nfwa+winheight))
- break; /* focus entirely on screen */
- }
- else { /* No comrades seen */
- if (nffocus - nfwa <= nfwa+winheight - nlfocus
- || !plwa && nlwa <= nfwa+winheight)
- break; /* Nicely centered focus or end of unit */
- }
- }
- if (pfwa == pscreen) { /* Say farewell to oldest comrade */
- pscreen->c_onscreen = Nowhere;
- do { /* Find next in age */
- nscreen += Space(pscreen);
- pscreen = pscreen->c_link;
- if (pscreen == plwa) {
- pscreen = Cnil;
- break;
- }
- } while (pscreen->c_onscreen == Nowhere);
- }
- nfwa += Space(pfwa);
- pfwa = pfwa->c_link; /* Pass the buck */
- }
- return pfwa; /* This is what all those breaks aim at */
- }
-