/************************************************************************** * This program is Copyright (C) 1986-2002 by Jonathan Payne. JOVE is * * provided by Jonathan and Jovehacks without charge and without * * warranty. You may copy, modify, and/or distribute JOVE, provided that * * this notice is included in all the source files and documentation. * **************************************************************************/ #include "jove.h" #include "jctype.h" #include "chars.h" #include "fp.h" #include "disp.h" #include "ask.h" #include "extend.h" #include "fmt.h" #include "insert.h" /* #include "io.h" */ /* for pwd() */ #ifdef IPROCS # include "sysprocs.h" # include "iproc.h" #endif #include "move.h" #include "macros.h" #include "screen.h" #include "term.h" #include "wind.h" #ifdef MAC # include "mac.h" #else # include #endif /* define a couple of unique daddrs */ #define NOWHERE_DADDR (~NULL_DADDR | DDIRTY) /* not in tmp file */ #define UNSAVED_CURLINE_DADDR ((NOWHERE_DADDR - 1) | DDIRTY) /* not yet in tmp file */ struct screenline *Screen = NULL, /* the screen (a bunch of screenline) */ *Curline = NULL; /* current line */ private void DeTab proto((char *, int, char *, char *, bool)), DoIDline proto((int)), do_cl_eol proto((int)), ModeLine proto((Window *, char *, int)), GotoDot proto((void)), UpdLine proto((int)), UpdWindow proto((Window *, int)); #ifdef ID_CHAR private void DelChar proto((int, int, int)), InsChar proto((int, int, int, char *)); private bool IDchar proto ((char *, int)), OkayDelete proto ((int, int, bool)), OkayInsert proto ((int, int)); private int NumSimilar proto ((char *, char *, int)), IDcomp proto ((char *, char *, int)); #endif /* ID_CHAR */ private bool AddLines proto((int, int)), DelLines proto((int, int)); bool DisabledRedisplay = NO; /* Kludge windows gets called by the routines that delete lines from the * buffer. If the w->w_line or w->w_top are deleted and this procedure * is not called, the redisplay routine will barf. */ void ChkWindows(line1, line2) LinePtr line1, line2; { register Window *w = fwind; register LinePtr lp, lend = line2->l_next; do { if (w->w_bufp == curbuf) { for (lp = line1->l_next; lp != lend; lp = lp->l_next) { if (lp == w->w_top) w->w_flags |= W_TOPGONE; if (lp == w->w_line) w->w_flags |= W_CURGONE; } } w = w->w_next; } while (w != fwind); } #ifdef WINRESIZE volatile bool ResizePending = NO; /* asynch request for screen resize */ private void resize() { bool oldDisabledRedisplay = DisabledRedisplay; int oldILI = ILI, oldCO = CO; DisabledRedisplay = YES; /* prevent tragedy */ ResizePending = NO; /* early & safe */ ttsize(); /* update line (LI and ILI) and col (CO) info. */ if (oldILI != ILI || oldCO != CO) { register int total; register Window *wp; /* Go through the window list, changing each window size in * proportion to the resize. If the window would become too * small, we delete it. * * Actually, we must do the deletion in * a separate pass because del_wind donates the space to either * neighbouring window. We test the windows in a funny order: * top-most, then bottom-up. Although it isn't necessary * for correctness, it means that we consider any donated * space, cutting down the number of windows we decide to delete. * Loop termination is tricky: fwind may have changed due to a * del_wind. As a simple fix, we start over whenever we * delete a window. Although this is O(n**2), it can't * really be expensive. * * After scaling all the windows, we * give any space remaining to the current window (which would * have changed if the old current window had been deleted). * * This seems fairer than just resizing the current window. */ wp = fwind; for (;;) { int newsize = ILI * wp->w_height / oldILI; if (newsize < 2) { del_wind(wp); wp = fwind; } else { wp = wp->w_prev; if (wp == fwind) break; } } total = 0; do { int newsize = ILI * wp->w_height / oldILI; wp->w_height = newsize; total += newsize; wp = wp->w_next; } while (wp != fwind); curwind->w_height += ILI - total; /* Make a new screen structure */ make_scr(); /* Do a 'hard' update on the screen - clear and redraw */ ClAndRedraw(); #ifdef WIN32 ResizeWindow(); #endif } DisabledRedisplay = oldDisabledRedisplay; } #endif /* WINRESIZE */ private bool RingBell; /* So if we have a lot of errors ... ring the bell only ONCE */ void redisplay() { if (DisabledRedisplay) return; #ifdef WINRESIZE do #endif { register Window *w; int lineno, i; bool done_ID = NO, old_UpdModLine; register struct scrimage *des_p, *phys_p; #ifdef WINRESIZE if (ResizePending) resize(); #endif curwind->w_line = curwind->w_bufp->b_dot; curwind->w_char = curwind->w_bufp->b_char; #ifdef MAC /* To avoid calling redisplay() recursively, * we must avoid calling CheckEvent(), * so we must avoid calling charp(). */ InputPending = NO; #else if (PreEmptOutput()) return; #endif if (RingBell) { dobell(1); RingBell = NO; } AbortCnt = ScrBufSize; /* initialize this now */ if (UpdMesg) DrawMesg(YES); for (lineno = 0, w = fwind; lineno < ILI; w = w->w_next) { UpdWindow(w, lineno); lineno += w->w_height; } /* Now that we've called update window, we can * assume that the modeline will be updated. But * if while redrawing the modeline the user types * a character, ModeLine() is free to set this on * again so that the modeline will be fully drawn * at the next redisplay. Furthermore, if output * is preempted, we'll restore the old value because * we can't be sure that the updating has happened. */ old_UpdModLine = UpdModLine; UpdModLine = NO; des_p = DesiredScreen; phys_p = PhysScreen; for (i = 0; i < ILI; i++, des_p++, phys_p++) { if (!done_ID && (des_p->s_id != phys_p->s_id)) { DoIDline(i); done_ID = YES; } if ((des_p->s_flags & (s_DIRTY | s_L_MOD)) || des_p->s_id != phys_p->s_id || des_p->s_vln != phys_p->s_vln || des_p->s_offset != phys_p->s_offset) UpdLine(i); if (CheapPreEmptOutput()) { if (old_UpdModLine) UpdModLine = YES; goto suppress; } } if (Asking) { int p = calc_pos(mesgbuf, AskingWidth); jdbg("redisplay CO=%d aw=%d p=%d \"%s\"\n", CO, AskingWidth, p, mesgbuf); Placur(ILI, jmin(CO - 2, p)); /* Nice kludge */ flushscreen(); } else { GotoDot(); } suppress: ; } #ifdef WINRESIZE /**/ while (ResizePending); #endif #ifdef MAC if (Windchange) docontrols(); #endif } /* find_pos() returns the position on the line, that C_CHAR represents * in LINE */ private int find_pos(line, c_char) LinePtr line; int c_char; { return calc_pos(lcontents(line), c_char); } /* calc_pos calculates the screen column of character c_char. * * Note: the calc_pos, how_far, and DeTab must be in synch -- * each thinks it knows how characters are displayed. */ int calc_pos(lp, c_char) register char *lp; register int c_char; { register int pos = 0; register ZXchar c; while ((--c_char >= 0) && (c = ZXC(*lp++)) != 0) { if (c == '\t' && tabstop != 0) { pos += TABDIST(pos); } else if (jisprint(c)) { pos += 1; } else { if (c <= DEL) pos += 2; else pos += 4; } } return pos; } volatile bool UpdModLine = NO; bool UpdMesg = NO; private void DoIDline(start) int start; { register struct scrimage *des_p = &DesiredScreen[start]; struct scrimage *phys_p = &PhysScreen[start]; register int i, j; /* Some changes have been made. Try for insert or delete lines. * If either case has happened, Addlines and/or DelLines will do * necessary scrolling, also CONVERTING PhysScreen to account for the * physical changes. The comparison continues from where the * insertion/deletion takes place; this doesn't happen very often, * usually it happens with more than one window with the same * buffer. */ #ifdef TERMCAP if (!CanScroll) return; /* We should never have been called! */ #endif for (i = start; i < ILI; i++, des_p++, phys_p++) if (des_p->s_id != phys_p->s_id) break; for (; i < ILI; i++) { for (j = i + 1; j < ILI; j++) { des_p = &DesiredScreen[j]; phys_p = &PhysScreen[j]; if (des_p->s_id != NULL_DADDR && des_p->s_id == phys_p->s_id) break; if (des_p->s_id == PhysScreen[i].s_id) { if (des_p->s_id == NULL_DADDR) continue; if (AddLines(i, j - i)) { DoIDline(j); return; } break; } if ((des_p = &DesiredScreen[i])->s_id == phys_p->s_id) { if (des_p->s_id == NULL_DADDR) continue; if (DelLines(i, j - i)) { DoIDline(i); return; } break; } } } } /* Make DesiredScreen reflect what the screen should look like when we are done * with the redisplay. This deals with horizontal scrolling. Also makes * sure the current line of the Window is in the window. */ bool ScrollAll = NO; /* VAR: when current line scrolls, scroll whole window? */ int ScrollWidth = 10; /* VAR: unit of horizontal scrolling */ private void UpdWindow(w, start) register Window *w; int start; { LinePtr lp; long i, upper, /* top of window */ lower; /* bottom of window */ int strt_col, /* starting print column of current line */ ntries = 0; /* # of tries at updating window */ register struct scrimage *des_p, *phys_p; Buffer *bp = w->w_bufp; do { if (w->w_flags & W_CURGONE) { w->w_line = bp->b_dot; w->w_char = bp->b_char; } if (w->w_flags & W_TOPGONE) CentWind(w); /* reset topline of screen */ w->w_flags &= ~(W_CURGONE | W_TOPGONE); /* make sure that the current line is in the window */ upper = start; lower = upper + WSIZE(w); for (i = upper, lp = w->w_top; ; lp = lp->l_next, i++) { if (i == lower || lp == NULL) { /* we've run out of window without finding dot */ ntries += 1; if (ntries == 1) { CalcWind(w); } else if (ntries == 2) { w->w_top = w->w_line = w->w_bufp->b_first; writef("\rERROR in redisplay: I got hopelessly lost!"); dobell(2); } else { writef("\n\rOops, still lost, quitting ...\r\n"); finish(-1); /* die! */ /*NOTREACHED*/ } break; } if (lp == w->w_line) { ntries = 0; /* happiness: dot is in window */ break; } } } while (ntries != 0); /* first do some calculations for the current line */ { int nw = W_NUMWIDTH(w), dot_col, end_col; strt_col = ScrollAll? w->w_LRscroll : PhysScreen[i].s_offset; end_col = strt_col + (CO - 1) - (nw + SIWIDTH(strt_col)); /* Right now we are displaying from strt_col to * end_col of the buffer line. These are PRINT * columns, not actual characters. */ dot_col = w->w_dotcol = find_pos(w->w_line, w->w_char); /* if the new dotcol is out of range, reselect * a horizontal window */ if (PhysScreen[i].s_offset == -1 || !(strt_col <= dot_col && dot_col < end_col)) { /* If dot_col is within first step left of screen, step left. * Otherwise, if ditto for right. * Otherwise, if it is in first screenwidth, start from begining. * Otherwise, center dot_col. * Fudge: if a scroll left would work except for the necessary * appearance of an ! on the left, we scroll an extra column. */ int step = jmin(ScrollWidth, end_col - strt_col); strt_col = strt_col > dot_col && strt_col - step <= dot_col ? jmax(strt_col - step, 0) : dot_col >= end_col && dot_col < end_col + step ? jmin(strt_col + step + (strt_col == 0 && dot_col == end_col + step - 1? 1 : 0) , dot_col) : dot_col < ((CO - 1) - nw) ? 0 : dot_col - ((CO - nw) / 2); if (ScrollAll) { if (w->w_LRscroll != strt_col) UpdModLine = YES; w->w_LRscroll = strt_col; } } w->w_dotline = i; w->w_dotcol = dot_col + nw + SIWIDTH(strt_col); } lp = w->w_top; des_p = &DesiredScreen[upper]; phys_p = &PhysScreen[upper]; for (i = upper; i < lower; i++, des_p++, phys_p++) { if (lp != NULL) { des_p->s_offset = (lp == w->w_line)? strt_col : w->w_LRscroll; des_p->s_flags = isdirty(lp) ? s_L_MOD : 0; des_p->s_vln = (w->w_flags & W_NUMLINES)? w->w_topnum + (i - upper) : 0; des_p->s_id = (lp == curline && DOLsave)? UNSAVED_CURLINE_DADDR : lp->l_dline & ~DDIRTY; des_p->s_lp = lp; lp = lp->l_next; } else { /* display line beyond end of buffer */ static const struct scrimage clean_plate = { 0, 0, 0, NULL_DADDR, NULL, NULL }; *des_p = clean_plate; if (phys_p->s_id != NULL_DADDR) des_p->s_flags = s_DIRTY; } des_p->s_window = w; } /* mode line: */ /* ??? The following assignment to des_p->s_id is tricky: * * We count on the cast pointer value being distinct from * any other daddr, but equal to itself. Turning * the "DDIRTY" bit on should ensure that it is distinct * from legitimate daddr values (except for NOWHERE_DADDR * and UNSAVED_CURLINE_DADDR). * If sizeof(Buffer *)>sizeof(daddr), nothing ensures that * these pointers are even distinct from each other. * * There also seems to be an assumption that every modeline * for a particular buffer will be the same. This is not * always the case: the last modeline on the screen is usually * different from any other modeline, even for the same buffer. * Currently, I think that only very contrived cases could cause * problems (probably involving window resizing). * Further problems will arise if JOVE is changed so that there are * other ways in which a modeline can reflect the window state * (instead of just the buffer state). * * -- DHR */ des_p->s_window = w; des_p->s_id = (jwid_t) w->w_bufp | DDIRTY; des_p->s_flags = (des_p->s_id != phys_p->s_id || UpdModLine)? s_MODELINE | s_DIRTY : 0; des_p->s_offset = 0; des_p->s_vln = 0; des_p->s_lp = NULL; #ifdef MAC if (UpdModLine) Modechange = YES; if (w == curwind && w->w_control != NULL) SetScrollBar(w); #endif } /* Write whatever is in mesgbuf (maybe we are Asking, or just printed * a message). Turns off the UpdateMesg line flag. */ void DrawMesg(abortable) bool abortable; { char outbuf[MAXCOLS + PPWIDTH]; /* assert(CO <= MAXCOLS); */ #ifndef MAC /* same reason as in redisplay() */ if (PreEmptOutput()) return; #endif i_set(ILI, 0); jdbg("DrawMesg aw=%d \"%s\"\n", AskingWidth, mesgbuf); DeTab(mesgbuf, 0, outbuf, outbuf + CO, NO); if (swrite(outbuf, NOEFFECT, abortable)) { cl_eol(); UpdMesg = NO; } flushscreen(); } /* Goto the current position in the current window. Presumably redisplay() * has already been called, and curwind->{w_dotline,w_dotcol} have been set * correctly. */ private void GotoDot() { if (!CheapPreEmptOutput()) { Placur(curwind->w_dotline, curwind->w_dotcol - PhysScreen[curwind->w_dotline].s_offset); flushscreen(); } } private int UntilEqual(start) register int start; { register struct scrimage *des_p = &DesiredScreen[start], *phys_p = &PhysScreen[start]; while ((start < ILI) && (des_p->s_id != phys_p->s_id)) { des_p += 1; phys_p += 1; start += 1; } return start; } /* Calls the routine to do the physical changes, and changes PhysScreen to * reflect those changes. */ private bool AddLines(at, num) register int at, num; { register int i; int bottom = UntilEqual(at + num); if (num == 0 || num >= ((bottom - 1) - at)) return NO; /* we did nothing */ v_ins_line(num, at, bottom - 1); /* Now change PhysScreen to account for the physical change. */ for (i = bottom - 1; i - num >= at; i--) PhysScreen[i] = PhysScreen[i - num]; for (i = 0; i < num; i++) PhysScreen[at + i].s_id = NULL_DADDR; return YES; /* we did something */ } private bool DelLines(at, num) register int at, num; { register int i; int bottom = UntilEqual(at + num); if (num == 0 || num >= ((bottom - 1) - at)) return NO; v_del_line(num, at, bottom - 1); for (i = at; num + i < bottom; i++) PhysScreen[i] = PhysScreen[num + i]; for (i = bottom - num; i < bottom; i++) PhysScreen[i].s_id = NULL_DADDR; return YES; } bool MarkHighlighting = YES; /* VAR: highlight mark when visible */ /* Update line linenum in window w. Only set PhysScreen to DesiredScreen * if the swrite or cl_eol works, that is nothing is interrupted by * characters typed. */ private void UpdLine(linenum) register int linenum; { register struct scrimage *des_p = &DesiredScreen[linenum]; register Window *w = des_p->s_window; char outbuf[MAXCOLS + PPWIDTH]; /* assert(CO <= MAXCOLS); */ i_set(linenum, 0); if (des_p->s_flags & s_MODELINE) { ModeLine(w, outbuf, linenum); } else if (des_p->s_id != NULL_DADDR) { char *lptr; int fromcol = W_NUMWIDTH(w); #ifdef HIGHLIGHTING static struct LErange lr = {0, 0, LENULLPROC, US_effect}; Mark *mark = b_curmark(w->w_bufp); bool marked_line = (MarkHighlighting # ifdef TERMCAP && US != NULL # endif && mark != NULL && mark->m_line == des_p->s_lp); #endif /* HIGHLIGHTING */ des_p->s_lp->l_dline &= ~DDIRTY; des_p->s_flags &= ~(s_DIRTY | s_L_MOD); if (w->w_flags & W_NUMLINES) { outbuf[fromcol-2] = ' '; swritef(outbuf, sizeof(outbuf), "%6D ", des_p->s_vln); if (outbuf[fromcol-2] != ' ') { outbuf[fromcol-2] = '*'; outbuf[fromcol-1] = ' '; } } if (des_p->s_offset != 0) { outbuf[fromcol++] = '!'; outbuf[fromcol] = '\0'; } lptr = lcontents(des_p->s_lp); DeTab(lptr, des_p->s_offset, outbuf + fromcol, outbuf + CO, (w->w_flags & W_VISSPACE) != 0); #ifdef HIGHLIGHTING if (marked_line) { lr.start = calc_pos(lptr, mark->m_char) - des_p->s_offset + fromcol; lr.width = 1; if (lr.start < sizeof(outbuf) - 1 && outbuf[lr.start] == '\0') { outbuf[lr.start] = ' '; outbuf[lr.start + 1] = '\0'; } } #endif /* HIGHLIGHTING */ #ifdef ID_CHAR /* REMIND: This code, along with the rest of the * ID_CHAR, belongs in the screen driver for * termcap based systems. mac and pc's and other * window-based drivers don't give a hoot about * ID_CHAR. */ /* attempt to exploit insert or delete character capability * but only if not highlighting some part of the line */ if (UseIC && Curline->s_effects == NOEFFECT # ifdef HIGHLIGHTING && !marked_line # endif /* HIGHLIGHTING */ ) { if (IDchar(outbuf, linenum)) { /* success: clean up and go home */ PhysScreen[linenum] = *des_p; return; } /* failure: re-initialize various cursors */ i_set(linenum, 0); } #endif /* ID_CHAR */ if (swrite(outbuf, #ifdef HIGHLIGHTING marked_line ? &lr : NOEFFECT, #else NOEFFECT, #endif YES)) { do_cl_eol(linenum); } else { /* interrupted: mark indeterminate state */ PhysScreen[linenum].s_id = NOWHERE_DADDR; } } else if (PhysScreen[linenum].s_id != NULL_DADDR) { /* not the same ... make sure */ do_cl_eol(linenum); } } private void do_cl_eol(linenum) register int linenum; { cl_eol(); PhysScreen[linenum] = DesiredScreen[linenum]; } /* Expand tabs (and other funny characters) of a section of "buf" * into "outbuf". * * Note: outbuf must allow for at least PPWIDTH extra characters. * This is sufficient room for one extra character to be displayed, * streamlining the code. * * Note: the calc_pos, how_far, and DeTab must be in synch -- * each thinks it knows how characters are displayed. */ private void DeTab(src, start_offset, dst, dst_limit, visspace) char *src; int start_offset; char *dst; char *dst_limit; bool visspace; { ZXchar c; int offset = start_offset; /* At any time, the number of characters we've output is * start_offset - offset. This is needed to correctly * calculate TABDIST() without having to add another * variable (pos) to be incremented for each call to addc. */ #define addc(ch) { if (--offset < 0) *dst++ = (ch); } while ((c = ZXC(*src++)) != '\0') { if (c == '\t' && tabstop != 0) { int nchars = TABDIST(start_offset - offset); c = visspace? '>' : ' '; while (--nchars > 0 && dst < dst_limit) { addc(c); c = ' '; } } else if (jisprint(c)) { if (visspace && c == ' ') c = '_'; } else { char buf[PPWIDTH]; char *p; PPchar(c, buf); /* assert(buf[0] != '\0'); */ for (p = buf; (c = *p++), *p != '\0'; ) addc(c); } if (--offset < 0) { *dst++ = c; if (dst >= dst_limit) { /* we've run out of real estate: truncate and flag it */ dst = dst_limit-1; *dst++ = '!'; break; } } } #undef addc *dst = '\0'; } #ifdef ID_CHAR /* From here to the end of the file is code that tries to utilize the * insert/delete character feature on some terminals. It is very confusing * and not so well written code, AND there is a lot of it. You may want * to use the space for something else. */ bool IN_INSmode = NO; void INSmode(on) bool on; { if (on != IN_INSmode) { putpad(on? IM : EI, 1); IN_INSmode = on; } } /* ID character routines full of special cases and other fun stuff like that. * It actually works though ... * * Returns Non-Zero if you are finished (no differences left). */ private bool IDchar(new, lineno) register char *new; int lineno; { register int col = 0; struct screenline *sline = &Screen[lineno]; register char *old = sline->s_line; int newlen = strlen(new); for (;;) { int oldlen = sline->s_roof - old; int i; for (; ; col++) { if (col == oldlen || col == newlen) return oldlen == newlen; /* one ended; happy if both ended */ if (old[col] != new[col]) break; } /* col now is first difference, and not the end of either */ /* see if an insertion will help */ for (i = col + 1; i < newlen; i++) { if (new[i] == old[col]) { /* The number of saved characters is (roughly) * the number of characters we can retain after * the insertion, minus the number that we * could have salvaged without moving them. */ int NumSaved = IDcomp(new + i, old + col, oldlen-col) - NumSimilar(new + col, old + col, jmin(i, oldlen)-col); if (OkayInsert(NumSaved, i - col)) { InsChar(lineno, col, i - col, new); col = i; break; } } } if (i != newlen) continue; /* see if a deletion will help */ for (i = col + 1; i < oldlen; i++) { if (new[col] == old[i]) { int NumSaved = IDcomp(new + col, old + i, oldlen - i); if (OkayDelete(NumSaved, i - col, newlen == oldlen)) { DelChar(lineno, col, i - col); break; } } } if (i != oldlen) continue; return NO; } } private int NumSimilar(s, t, n) register char *s, *t; int n; { register int num = 0; while (n--) if (*s++ == *t++) num += 1; return num; } private int IDcomp(s, t, len) register char *s, /* NUL terminated */ *t; /* len chars */ int len; { register int i; int num = 0, nonspace = 0; for (i = 0; i < len; i++) { char c = *s++; if (c == '\0' || c != *t++) break; if (c != ' ') nonspace = 1; num += nonspace; } return num; } private bool OkayDelete(Saved, num, samelength) int Saved, num; bool samelength; { /* If the old and the new have different lengths, then the competition * will have to clear to end of line. We take that into consideration. * Note: we must avoid multiplying by INFINITY. */ return Saved + (samelength ? 0 : CElen) > jmin(MDClen, DC != NULL? DClen * num : INFINITY); } private bool OkayInsert(Saved, num) int Saved, num; { # ifdef NCURSES_BUG /* Note: with the ncurses version of termcap/terminfo, we must use * any one of insert mode, insert character, or multiple insert character. */ register int n = INFINITY; if (IC != NULL) /* Per character prefixes */ n = num * IClen; n = jmin(n, MIClen); n = jmin(n, IMEIlen); # else /* !NCURSES_BUG */ /* Note: the way termcap/terminfo is defined, we must use *both* * IC and IM to insert, but normally only one will be defined. * See terminfo(5), under the heading "Insert/Delete Character". */ register int n = 0; if (IC != NULL) /* Per character prefixes */ n = jmin(num * IClen, MIClen); if (!IN_INSmode) n += IMEIlen; # endif /* !NCURSES_BUG */ n += num; /* The characters themselves */ return Saved > n; } private void DelChar(lineno, col, num) int lineno, col, num; { struct screenline *sp = (&Screen[lineno]); Placur(lineno, col); putmulti(DC, M_DC, num, 1); /* Shift the remaining characters left in the buffer to close the gap. * byte_copy cannot be used since the destination overlaps the source. */ { register char *to = sp->s_line + col, *from = to + num; register size_t len = sp->s_roof - from; byte_move(from, to, len); } clrline(sp->s_roof - num, sp->s_roof); sp->s_roof -= num; } private void InsChar(lineno, col, num, new) int lineno, col, num; char *new; { register char *sp1, *sp2, /* To push over the array. */ *sp3; /* Last character to push over. */ int i; i_set(lineno, 0); sp2 = Curline->s_roof + num; if (sp2 > cursend) { i_set(lineno, CO - num - 1); cl_eol(); sp2 = cursend; } Curline->s_roof = sp2; sp1 = sp2 - num; sp3 = Curline->s_line + col; while (sp1 > sp3) *--sp2 = *--sp1; new += col; byte_copy(new, sp3, (size_t) num); /* The internal screen is correct, and now we have to do * the physical stuff. */ Placur(lineno, col); # ifdef NCURSES_BUG /* Note: with the ncurses version of termcap/terminfo, we must use * any one of insert mode, insert character, or multiple insert character. */ if (IN_INSmode || ((IC == NULL || IMEIlen < num * IClen) && IMEIlen < MIClen)) INSmode(YES); else putmulti(IC, M_IC, num, 1); # else /* !NCURSES_BUG */ /* Note: the way termcap/terminfo is defined, we must use *both* * IC and IM, but normally only one will be defined. * See terminfo(5), under the heading "Insert/Delete Character". */ if (IC != NULL) putmulti(IC, M_IC, num, 1); if (IM != NULL) INSmode(YES); # endif /* !NCURSES_BUG */ for (i = 0; i < num; i++) { scr_putchar(new[i]); if (IN_INSmode) putpad(IP, 1); } CapCol += num; } #endif /* ID_CHAR */ #ifdef UNIX /* obviously ... no mail today if not Unix*/ /* chkmail() returns YES if there is new mail since the * last time we checked. */ char Mailbox[FILESIZE]; /* VAR: mailbox name */ int MailInt = 60; /* VAR: mail check interval (seconds) */ bool chkmail(force) bool force; { time_t now; static bool state = NO; /* assume unknown */ static time_t last_chk = 0, mbox_time = 0; struct stat stbuf; if (MailInt == 0 || Mailbox[0] == '\0') return NO; time(&now); if ((force == NO) && (now < last_chk + MailInt)) return state; last_chk = now; if (stat(Mailbox, &stbuf) < 0) { state = NO; /* no mail */ return NO; } if ((stbuf.st_atime > stbuf.st_mtime && stbuf.st_atime > mbox_time) || stbuf.st_size == 0) { mbox_time = stbuf.st_atime; state = NO; } else if (stbuf.st_mtime > mbox_time) { if (mbox_time > 0) dobell(2); /* announce the change */ mbox_time = stbuf.st_mtime; state = YES; } return state; } #endif /* UNIX */ /* Print the mode line. */ private char *mode_p, *mend_p; bool BriteMode = YES; /* VAR: make the mode line inverse? */ private void mode_app(str) register const char *str; { ZXchar c; while (mode_p < mend_p && (c = ZXC(*str++)) != '\0') { /* don't expand tabs: treat them as suspects */ if (jisprint(c)) { *mode_p++ = c; } else { char buf[PPWIDTH]; PPchar(c, buf); mode_app(buf); } } } /* VAR: mode line format string */ char ModeFmt[MAXCOLS] = "%[Jove%]%w%w%c(%M)%3c[%b:%n]%2c\"%f\"%c%i# %m*-%2c%p%2s%(%d%e(%t)%)"; private void ModeLine(w, line, linenum) register Window *w; char *line; /* scratch space of at least CO chars */ int linenum; { int n, glue = 0; bool ign_some = NO; bool td = NO; /* is time (kludge: or mail status) displayed? */ char *fmt = ModeFmt, fillc, c; register Buffer *thisbuf = w->w_bufp; register Buffer *bp; LineEffects highlighting; mode_p = line; mend_p = &line[CO - 1]; #ifdef TERMCAP if (SO == NULL) BriteMode = NO; /* we can't do it */ #endif /* ??? On Mac, perhaps '_' looks better than '-' */ fillc = BriteMode? ' ' : '-'; while ((c = *fmt++)!='\0' && mode_p= '0' && c <= '9') { n = 0; while (c >= '0' && c <= '9') { n = n * 10 + (c - '0'); c = *fmt++; } if (c == '\0') break; } switch (c) { case '%': mode_app("%"); break; case '(': if (w->w_next != fwind) /* Not bottom window. */ ign_some = YES; break; case ')': ign_some = NO; break; case '[': case ']': for (n=RecDepth; n>0 && mode_pb_minor; const char *const *p; mode_app(majname[thisbuf->b_major]); for (p = minname; minors != 0; p++) { if (minors & 01) mode_app(*p); minors >>= 1; } if (InMacDefine) mode_app(" Def"); break; } case 'c': while (--n>=0 && mode_pb_name); break; case 'f': case 'F': if (thisbuf->b_fname == NULL) mode_app("[No file]"); else { if (c == 'f') mode_app(pr_name(thisbuf->b_fname, YES)); else mode_app(jbasename(thisbuf->b_fname)); } break; case 'i': { char yea = (*fmt == '\0') ? '#' : *fmt++; char nay = (*fmt == '\0') ? ' ' : *fmt++; *mode_p++ = w->w_bufp->b_diverged ? yea : nay; break; } case 'm': { char yea = (*fmt == '\0') ? '*' : *fmt++; char nay = (*fmt == '\0') ? ' ' : *fmt++; *mode_p++ = IsModified(w->w_bufp) ? yea : nay; break; } case 'n': { char tmp[16]; for (bp = world, n = 1; bp != NULL; bp = bp->b_next, n++) if (bp == thisbuf) break; swritef(tmp, sizeof(tmp), "%d", n); mode_app(tmp); break; } case 'p': #ifdef IPROCS if (thisbuf->b_type == B_PROCESS) { char tmp[40]; swritef(tmp, sizeof(tmp), "(%s)", pstate(thisbuf->b_process)); mode_app(tmp); } #endif break; case 's': if (mode_p[-1] != ' ') *mode_p++ = ' '; break; case 't': { char timestr[12]; td = YES; mode_app(get_time((time_t *)NULL, timestr, 11, 16)); break; } case 'w': if (w->w_LRscroll > 0) mode_app(">"); break; default: mode_app("?"); break; } } /* Glue (Knuth's term) is a field that expands to fill * any leftover space. Multiple glue fields compete * on an equal basis. This is a generalization of a * mechanism to allow centring and right-justification. * The original meaning of %e (fill the rest of the * line) has also been generalized. %e can now * meaningfully be used 0 or more times. */ if (glue) { /* 1 space unused, plus padding for magic cookies */ register char *to = &line[CO - 1 - (4 * SG)], *from = mode_p; if (to < from) to = from; mode_p = to; while (from != line) { if ((*--to = *--from) == '\0') { register int portion = (to-from) / glue; glue--; *to = fillc; while (--portion >= 0) *--to = fillc; } } } else { while (mode_p < &line[CO - 1 - (4 * SG)]) *mode_p++ = fillc; } *mode_p = '\0'; /* Highlight mode line. */ highlighting = NOEFFECT; if (BriteMode) { highlighting = WindowRange(w); #ifdef HIGHLIGHTING { char *p = &line[highlighting->start], *e = p + highlighting->width; for (; p != e; p++) if (*p == ' ') *p = '-'; } #endif } if (w->w_next == fwind && TimeDisplayed != td) { TimeDisplayed = td; #ifdef UNIX SetClockAlarm(YES); #endif } #ifdef ID_CHAR INSmode(NO); #endif if (swrite(line, highlighting, YES)) do_cl_eol(linenum); else UpdModLine = YES; } /* This tries to place the current line of the current window in the * center of the window, OR to place it at the arg'th line of the window. * This also causes the horizontal position of the line to be centered, * if the line needs scrolling, or moved all the way back to the left, * if that's possible. */ void RedrawDisplay() { int line; LinePtr newtop = prev_line((curwind->w_line = curline), arg_or_default(WSIZE(curwind)/2)); if ((line = in_window(curwind, curwind->w_line)) != -1) PhysScreen[line].s_offset = -1; if (newtop == curwind->w_top) ClAndRedraw(); else SetTop(curwind, newtop); } void ClAndRedraw() { cl_scr(YES); } void NextPage() { LinePtr newline; if (Asking) { /* don't do it */ } else if (arg_value() < 0) { negate_arg(); PrevPage(); } else if (is_non_minus_arg()) { UpScroll(); } else { if (in_window(curwind, curwind->w_bufp->b_last) != -1) { rbell(); return; } newline = next_line(curwind->w_top, jmax(1, WSIZE(curwind) - 1)); SetTop(curwind, curwind->w_line = newline); if (curwind->w_bufp == curbuf) SetLine(newline); } } void PrevPage() { LinePtr newline; if (Asking) { /* don't do it */ } else if (arg_value() < 0) { negate_arg(); NextPage(); } else if (is_non_minus_arg()) { DownScroll(); } else { newline = prev_line(curwind->w_top, jmax(1, WSIZE(curwind) - 1)); SetTop(curwind, curwind->w_line = newline); if (curwind->w_bufp == curbuf) SetLine(newline); } } void UpScroll() { SetTop(curwind, next_line(curwind->w_top, arg_value())); if (curwind->w_bufp == curbuf && in_window(curwind, curline) == -1) SetLine(curwind->w_top); } void DownScroll() { SetTop(curwind, prev_line(curwind->w_top, arg_value())); if (curwind->w_bufp == curbuf && in_window(curwind, curline) == -1) SetLine(curwind->w_top); } bool VisBell = NO; /* VAR: use visible bell (if possible) */ void rbell() { RingBell = YES; } /* Message prints the null terminated string onto the bottom line of the * terminal. */ void message(str) const char *str; { if (InJoverc) return; UpdMesg = YES; stickymsg = NO; if (str != mesgbuf) truncstr(mesgbuf, str); } /* End of Window */ void Eow() { if (Asking) return; SetLine(next_line(curwind->w_top, WSIZE(curwind) - 1 - jmin(WSIZE(curwind) - 1, arg_value() - 1))); if (!is_an_arg()) Eol(); } /* Beginning of Window */ void Bow() { if (Asking) return; SetLine(next_line(curwind->w_top, jmin(WSIZE(curwind) - 1, arg_value() - 1))); } /* Typeout Mechanism */ bool UseBuffers = NO, /* VAR: use buffers with Typeout() */ TOabort = NO; private int LineNo; /* screen line for Typeout (if not UseBuffers) */ private Window *old_wind; /* curwind before preempted by typeout to buffer */ /* This initializes the typeout. If send-typeout-to-buffers is set * the buffer NAME is created (emptied if it already exists) and output * goes to the buffer. Otherwise output is drawn on the screen and * erased by TOstop() */ void TOstart(name) const char *name; { if (UseBuffers) { old_wind = curwind; pop_wind(name, YES, B_SCRATCH); } else DisabledRedisplay = YES; TOabort = NO; LineNo = 0; } private void TOlineFits(s) char *s; { i_set(LineNo, 0); (void) swrite(s, NOEFFECT, NO); PhysScreen[LineNo].s_id = NOWHERE_DADDR; cl_eol(); flushscreen(); } private void TOprompt(s) char *s; { if (!TOabort) { register ZXchar c; TOlineFits(s); c = kbd_getch(); TOlineFits(""); if (c != ' ') { TOabort = YES; if (c != AbortChar) kbd_ungetch(c); } } } #ifdef STDARGS void Typeout(const char *fmt, ...) #else /*VARARGS1*/ void Typeout(fmt, va_alist) const char *fmt; va_dcl #endif { char string[MAX_TYPEOUT+1]; va_list ap; va_init(ap, fmt); format(string, sizeof string, fmt, ap); va_end(ap); if (UseBuffers) { ins_str(string); ins_str("\n"); } else { char outbuf[MAXCOLS + PPWIDTH]; /* assert(CO <= MAXCOLS); */ if (LineNo == ILI - 2) { TOprompt("--more--"); LineNo = 0; } if (!TOabort) { DeTab(string, 0, outbuf, outbuf + CO, NO); TOlineFits(outbuf); LineNo += 1; } } } void TOstop() { if (UseBuffers) { ToFirst(); SetWind(old_wind); } else { TOprompt("--end--"); DisabledRedisplay = NO; } }