ustel@well.UUCP (Mark Hargrove) (02/11/88)
Comp.sources.misc: Volume 2, Issue 52 Submitted-By: "Mark Hargrove" <ustel@well.UUCP> Archive-Name: accell-text/Part3 [Nice to know someone else posts Unify/Accell tools. ++bsa] #!/bin/sh # to extract, remove the header and type "sh filename" if `test ! -s ./cinfo.c` then echo "writing ./cinfo.c" cat > ./cinfo.c << '\Rogue\Monster\' /* * Character class tables. * Do it yourself character classification * macros, that understand the multinational character set, * and let me ask some questions the standard macros (in * ctype.h) don't let you ask. */ #include "def.h" /* * This table, indexed by a character drawn * from the 256 member character set, is used by my * own character type macros to answer questions about the * type of a character. It handles the full multinational * character set, and lets me ask some questions that the * standard "ctype" macros cannot ask. */ char cinfo[256] = { _C, _C, _C, _C, /* 0x0X */ _C, _C, _C, _C, _C, _C, _C, _C, _C, _C, _C, _C, _C, _C, _C, _C, /* 0x1X */ _C, _C, _C, _C, _C, _C, _C, _C, _C, _C, _C, _C, 0, _P, 0, 0, /* 0x2X */ _W, _W, 0, _W, 0, 0, 0, 0, 0, 0, _P, 0, _W, _W, _W, _W, /* 0x3X */ _W, _W, _W, _W, _W, _W, 0, 0, 0, 0, 0, _P, 0, _U|_W, _U|_W, _U|_W, /* 0x4X */ _U|_W, _U|_W, _U|_W, _U|_W, _U|_W, _U|_W, _U|_W, _U|_W, _U|_W, _U|_W, _U|_W, _U|_W, _U|_W, _U|_W, _U|_W, _U|_W, /* 0x5X */ _U|_W, _U|_W, _U|_W, _U|_W, _U|_W, _U|_W, _U|_W, 0, 0, 0, 0, 0, 0, _L|_W, _L|_W, _L|_W, /* 0x6X */ _L|_W, _L|_W, _L|_W, _L|_W, _L|_W, _L|_W, _L|_W, _L|_W, _L|_W, _L|_W, _L|_W, _L|_W, _L|_W, _L|_W, _L|_W, _L|_W, /* 0x7X */ _L|_W, _L|_W, _L|_W, _L|_W, _L|_W, _L|_W, _L|_W, 0, 0, 0, 0, _C, 0, 0, 0, 0, /* 0x8X */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x9X */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xAX */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xBX */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, _U|_W, _U|_W, _U|_W, _U|_W, /* 0xCX */ _U|_W, _U|_W, _U|_W, _U|_W, _U|_W, _U|_W, _U|_W, _U|_W, _U|_W, _U|_W, _U|_W, _U|_W, 0, _U|_W, _U|_W, _U|_W, /* 0xDX */ _U|_W, _U|_W, _U|_W, _U|_W, _U|_W, _U|_W, _U|_W, _U|_W, _U|_W, _U|_W, 0, _W, _L|_W, _L|_W, _L|_W, _L|_W, /* 0xEX */ _L|_W, _L|_W, _L|_W, _L|_W, _L|_W, _L|_W, _L|_W, _L|_W, _L|_W, _L|_W, _L|_W, _L|_W, 0, _L|_W, _L|_W, _L|_W, /* 0xFX */ _L|_W, _L|_W, _L|_W, _L|_W, _L|_W, _L|_W, _L|_W, _L|_W, _L|_W, _L|_W, 0, 0 }; #ifdef __50SERIES /* Primos C blows up without this - wierd, really wierd... */ static void dummy(){} #endif \Rogue\Monster\ else echo "will not over write ./cinfo.c" fi if `test ! -s ./display.c` then echo "writing ./display.c" cat > ./display.c << '\Rogue\Monster\' /* * The functions in this file handle redisplay. The * redisplay system knows almost nothing about the editing * process; the editing functions do, however, set some * hints to eliminate a lot of the grinding. There is more * that can be done; the "vtputc" interface is a real * pig. Two conditional compilation flags; the GOSLING * flag enables dynamic programming redisplay, using the * algorithm published by Jim Gosling in SIGOA. The MEMMAP * changes things around for memory mapped video. With * both off, the terminal is a VT52. */ #include "def.h" /* * You can change these back to the types * implied by the name if you get tight for space. If you * make both of them "int" you get better code on the VAX. * They do nothing if this is not Gosling redisplay, except * for change the size of a structure that isn't used. * A bit of a cheat. */ /* These defines really belong in sysdef.h */ #ifndef XCHAR # define XCHAR int # define XSHORT int #endif #ifdef STANDOUT_GLITCH extern int SG; /* number of standout glitches */ #endif /* * A video structure always holds * an array of characters whose length is equal to * the longest line possible. Only some of this is * used if "ncol" isn't the same as "NCOL". */ typedef struct { short v_hash; /* Hash code, for compares. */ short v_flag; /* Flag word. */ short v_color; /* Color of the line. */ XSHORT v_cost; /* Cost of display. */ char v_text[NCOL]; /* The actual characters. */ } VIDEO; #define VFCHG 0x0001 /* Changed. */ #define VFHBAD 0x0002 /* Hash and cost are bad. */ /* * SCORE structures hold the optimal * trace trajectory, and the cost of redisplay, when * the dynamic programming redisplay code is used. * If no fancy redisplay, this isn't used. The trace index * fields can be "char", and the score a "short", but * this makes the code worse on the VAX. */ typedef struct { XCHAR s_itrace; /* "i" index for track back. */ XCHAR s_jtrace; /* "j" index for trace back. */ XSHORT s_cost; /* Display cost. */ } SCORE; int sgarbf = TRUE; /* TRUE if screen is garbage. */ int vtrow = 0; /* Virtual cursor row. */ int vtcol = 0; /* Virtual cursor column. */ int tthue = CNONE; /* Current color. */ int ttrow = HUGE; /* Physical cursor row. */ int ttcol = HUGE; /* Physical cursor column. */ int tttop = HUGE; /* Top of scroll region. */ int ttbot = HUGE; /* Bottom of scroll region. */ VIDEO *vscreen[NROW-1]; /* Edge vector, virtual. */ VIDEO *pscreen[NROW-1]; /* Edge vector, physical. */ VIDEO video[2*(NROW-1)]; /* Actual screen data. */ VIDEO blanks; /* Blank line image. */ /* * Some predeclerations to make ANSI compilers happy */ VOID vtinit(); VOID vttidy(); VOID vtmove(); VOID vtputc(); VOID vteeol(); VOID update(); VOID ucopy(); VOID uline(); VOID modeline(); VOID hash(); VOID setscores(); VOID traceback(); #ifdef GOSLING /* * This matrix is written as an array because * we do funny things in the "setscores" routine, which * is very compute intensive, to make the subscripts go away. * It would be "SCORE score[NROW][NROW]" in old speak. * Look at "setscores" to understand what is up. */ SCORE score[NROW*NROW]; #endif /* * Initialize the data structures used * by the display code. The edge vectors used * to access the screens are set up. The operating * system's terminal I/O channel is set up. Fill the * "blanks" array with ASCII blanks. The rest is done * at compile time. The original window is marked * as needing full update, and the physical screen * is marked as garbage, so all the right stuff happens * on the first call to redisplay. */ VOID vtinit() { register VIDEO *vp; register int i; ttopen(); ttinit(); vp = &video[0]; for (i=0; i<NROW-1; ++i) { vscreen[i] = vp; ++vp; pscreen[i] = vp; ++vp; } blanks.v_color = CTEXT; for (i=0; i<NCOL; ++i) blanks.v_text[i] = ' '; } /* * Tidy up the virtual display system * in anticipation of a return back to the host * operating system. Right now all we do is position * the cursor to the last line, erase the line, and * close the terminal channel. */ VOID vttidy() { ttcolor(CTEXT); ttnowindow(); /* No scroll window. */ ttmove(nrow-1, 0); /* Echo line. */ tteeol(); tttidy(); ttflush(); ttclose(); } /* * Move the virtual cursor to an origin * 0 spot on the virtual display screen. I could * store the column as a character pointer to the spot * on the line, which would make "vtputc" a little bit * more efficient. No checking for errors. */ VOID vtmove(row, col) { vtrow = row; vtcol = col; } /* * Write a character to the virtual display, * dealing with long lines and the display of unprintable * things like control characters. Also expand tabs every 8 * columns. This code only puts printing characters into * the virtual display image. Special care must be taken when * expanding tabs. On a screen whose width is not a multiple * of 8, it is possible for the virtual cursor to hit the * right margin before the next tab stop is reached. This * makes the tab code loop if you are not careful. * Three guesses how we found this. */ VOID vtputc(c) register int c; { register VIDEO *vp; vp = vscreen[vtrow]; if (vtcol >= ncol) vp->v_text[ncol-1] = '$'; else if ( #ifdef NOTAB !(mode&MNOTAB) && #endif c == '\t') { do { vtputc(' '); } while (vtcol<ncol && (vtcol&0x07)!=0); } else if (ISCTRL(c) != FALSE) { vtputc('^'); vtputc(c ^ 0x40); } else vp->v_text[vtcol++] = c; } /* * Erase from the end of the * software cursor to the end of the * line on which the software cursor is * located. The display routines will decide * if a hardware erase to end of line command * should be used to display this. */ VOID vteeol() { register VIDEO *vp; vp = vscreen[vtrow]; while (vtcol < ncol) vp->v_text[vtcol++] = ' '; } /* * Make sure that the display is * right. This is a three part process. First, * scan through all of the windows looking for dirty * ones. Check the framing, and refresh the screen. * Second, make sure that "currow" and "curcol" are * correct for the current window. Third, make the * virtual and physical screens the same. */ VOID update() { register LINE *lp; register WINDOW *wp; register VIDEO *vp1; VIDEO *vp2; register int i; register int j; register int c; register int hflag; register int currow; register int curcol; register int offs; register int size; VOID traceback (); VOID uline (); if (typeahead()) return; if (sgarbf) { /* must update everything */ wp = wheadp; while(wp != NULL) { wp->w_flag |= WFMODE | WFHARD; wp = wp->w_wndp; } } hflag = FALSE; /* Not hard. */ wp = wheadp; while (wp != NULL) { if (wp->w_flag != 0) { /* Need update. */ if ((wp->w_flag&WFFORCE) == 0) { lp = wp->w_linep; for (i=0; i<wp->w_ntrows; ++i) { if (lp == wp->w_dotp) goto out; if (lp == wp->w_bufp->b_linep) break; lp = lforw(lp); } } i = wp->w_force; /* Reframe this one. */ if (i > 0) { --i; if (i >= wp->w_ntrows) i = wp->w_ntrows-1; } else if (i < 0) { i += wp->w_ntrows; if (i < 0) i = 0; } else i = wp->w_ntrows/2; lp = wp->w_dotp; while (i!=0 && lback(lp)!=wp->w_bufp->b_linep) { --i; lp = lback(lp); } wp->w_linep = lp; wp->w_flag |= WFHARD; /* Force full. */ out: lp = wp->w_linep; /* Try reduced update. */ i = wp->w_toprow; if ((wp->w_flag&~WFMODE) == WFEDIT) { while (lp != wp->w_dotp) { ++i; lp = lforw(lp); } vscreen[i]->v_color = CTEXT; vscreen[i]->v_flag |= (VFCHG|VFHBAD); vtmove(i, 0); for (j=0; j<llength(lp); ++j) vtputc(lgetc(lp, j)); vteeol(); } else if ((wp->w_flag&(WFEDIT|WFHARD)) != 0) { hflag = TRUE; while (i < wp->w_toprow+wp->w_ntrows) { vscreen[i]->v_color = CTEXT; vscreen[i]->v_flag |= (VFCHG|VFHBAD); vtmove(i, 0); if (lp != wp->w_bufp->b_linep) { for (j=0; j<llength(lp); ++j) vtputc(lgetc(lp, j)); lp = lforw(lp); } vteeol(); ++i; } } if ((wp->w_flag&WFMODE) != 0) modeline(wp); wp->w_flag = 0; wp->w_force = 0; } wp = wp->w_wndp; } lp = curwp->w_linep; /* Cursor location. */ currow = curwp->w_toprow; while (lp != curwp->w_dotp) { ++currow; lp = lforw(lp); } curcol = 0; i = 0; while (i < curwp->w_doto) { c = lgetc(lp, i++); if ( #ifdef NOTAB !(mode&MNOTAB) && #endif c == '\t') curcol |= 0x07; else if (ISCTRL(c) != FALSE) ++curcol; ++curcol; } if (curcol >= ncol) /* Long line. */ curcol = ncol-1; if (sgarbf != FALSE) { /* Screen is garbage. */ sgarbf = FALSE; /* Erase-page clears */ epresf = FALSE; /* the message area. */ tttop = HUGE; /* Forget where you set */ ttbot = HUGE; /* scroll region. */ tthue = CNONE; /* Color unknown. */ ttmove(0, 0); tteeop(); for (i=0; i<nrow-1; ++i) { uline(i, vscreen[i], &blanks); ucopy(vscreen[i], pscreen[i]); } ttmove(currow, curcol); ttflush(); return; } #ifdef GOSLING if (hflag != FALSE) { /* Hard update? */ for (i=0; i<nrow-1; ++i) { /* Compute hash data. */ hash(vscreen[i]); hash(pscreen[i]); } offs = 0; /* Get top match. */ while (offs != nrow-1) { vp1 = vscreen[offs]; vp2 = pscreen[offs]; if (vp1->v_color != vp2->v_color || vp1->v_hash != vp2->v_hash) break; uline(offs, vp1, vp2); ucopy(vp1, vp2); ++offs; } if (offs == nrow-1) { /* Might get it all. */ ttmove(currow, curcol); ttflush(); return; } size = nrow-1; /* Get bottom match. */ while (size != offs) { vp1 = vscreen[size-1]; vp2 = pscreen[size-1]; if (vp1->v_color != vp2->v_color || vp1->v_hash != vp2->v_hash) break; uline(size-1, vp1, vp2); ucopy(vp1, vp2); --size; } if ((size -= offs) == 0) /* Get screen size. */ panic("Illegal screen size in update"); setscores(offs, size); /* Do hard update. */ traceback(offs, size, size, size); for (i=0; i<size; ++i) ucopy(vscreen[offs+i], pscreen[offs+i]); ttmove(currow, curcol); ttflush(); return; } #endif for (i=0; i<nrow-1; ++i) { /* Easy update. */ vp1 = vscreen[i]; vp2 = pscreen[i]; if ((vp1->v_flag&VFCHG) != 0) { uline(i, vp1, vp2); ucopy(vp1, vp2); } } ttmove(currow, curcol); ttflush(); } /* * Update a saved copy of a line, * kept in a VIDEO structure. The "vvp" is * the one in the "vscreen". The "pvp" is the one * in the "pscreen". This is called to make the * virtual and physical screens the same when * display has done an update. */ VOID ucopy(vvp, pvp) register VIDEO *vvp; register VIDEO *pvp; { vvp->v_flag &= ~VFCHG; /* Changes done. */ pvp->v_flag = vvp->v_flag; /* Update model. */ pvp->v_hash = vvp->v_hash; pvp->v_cost = vvp->v_cost; pvp->v_color = vvp->v_color; bcopy(vvp->v_text, pvp->v_text, ncol); } /* * Update a single line. This routine only * uses basic functionality (no insert and delete character, * but erase to end of line). The "vvp" points at the VIDEO * structure for the line on the virtual screen, and the "pvp" * is the same for the physical screen. Avoid erase to end of * line when updating CMODE color lines, because of the way that * reverse video works on most terminals. */ VOID uline(row, vvp, pvp) VIDEO *vvp; VIDEO *pvp; { #ifdef MEMMAP putline(row+1, 1, &vvp->v_text[0]); #else register char *cp1; register char *cp2; register char *cp3; char *cp4; char *cp5; register int nbflag; if (vvp->v_color != pvp->v_color) { /* Wrong color, */ ttmove(row, 0); /* do a full redraw. */ #ifdef STANDOUT_GLITCH if (pvp->v_color != CTEXT && SG >= 0) tteeol(); #endif ttcolor(vvp->v_color); #ifdef STANDOUT_GLITCH cp1 = &vvp->v_text[SG > 0 ? SG : 0]; /* the odd code for SG==0 is to avoid putting the invisable * glitch character on the next line. * (Hazeltine executive 80 model 30) */ cp2 = &vvp->v_text[ncol - (SG >= 0 ? (SG!=0 ? SG : 1) : 0)]; #else cp1 = &vvp->v_text[0]; cp2 = &vvp->v_text[ncol]; #endif while (cp1 != cp2) { ttputc(*cp1++); ++ttcol; } #ifndef MOVE_STANDOUT ttcolor(CTEXT); #endif return; } cp1 = &vvp->v_text[0]; /* Compute left match. */ cp2 = &pvp->v_text[0]; while (cp1!=&vvp->v_text[ncol] && cp1[0]==cp2[0]) { ++cp1; ++cp2; } if (cp1 == &vvp->v_text[ncol]) /* All equal. */ return; nbflag = FALSE; cp3 = &vvp->v_text[ncol]; /* Compute right match. */ cp4 = &pvp->v_text[ncol]; while (cp3[-1] == cp4[-1]) { --cp3; --cp4; if (cp3[0] != ' ') /* Note non-blanks in */ nbflag = TRUE; /* the right match. */ } cp5 = cp3; /* Is erase good? */ if (nbflag==FALSE && vvp->v_color==CTEXT) { while (cp5!=cp1 && cp5[-1]==' ') --cp5; /* Alcyon hack */ if ((int)(cp3-cp5) <= tceeol) cp5 = cp3; } /* Alcyon hack */ ttmove(row, (int)(cp1-&vvp->v_text[0])); #ifdef STANDOUT_GLITCH if (vvp->v_color != CTEXT && SG > 0) { if(cp1 < &vvp->v_text[SG]) cp1 = &vvp->v_text[SG]; if(cp5 > &vvp->v_text[ncol-SG]) cp5 = &vvp->v_text[ncol-SG]; } else if (SG < 0) #endif ttcolor(vvp->v_color); while (cp1 != cp5) { ttputc(*cp1++); ++ttcol; } if (cp5 != cp3) /* Do erase. */ tteeol(); #endif } #define LAST_ROT 5 static int rotat_pos = 1; /* * The mode line reflects the the Function-Key help * information, rather than the state of the current window/buffer * (since there is only one window in the TINY version). */ VOID modeline(wp) register WINDOW *wp; { register int row, /* display row */ col; /* current column */ register BUFFER *bp; row = wp->w_toprow+wp->w_ntrows; /* Location. */ vscreen[row]->v_color = CMODE; /* Mode line color. */ vscreen[row]->v_flag |= (VFCHG|VFHBAD); /* Recompute, display. */ vtmove(row, 0); /* Seek to right line. */ bp = wp->w_bufp; if ((mode & MOVRSTK) == 0) col = vtputs(" Insert |"); else col = vtputs(" Replace|"); switch (rotat_pos) { case 1: /* Function Keys 1-4 */ col += vtputs("F1 -Prv Form |"); #ifdef MISLOG col += vtputs("F2 -Time Stmp|"); #else col += vtputs(" |"); #endif col += vtputs("F3 -Prv Line |"); col += vtputs("F4 -Nxt Line |"); break; case 2: /* Function Keys 5-8 */ col += vtputs("F5 -Open Line|"); col += vtputs("F6 -Fix Para |"); #ifdef MENU_INSERT col += vtputs("F7 -Ins File |"); #else col += vtputs(" |"); #endif col += vtputs("F8 -Rep/Ins |"); break; case 3: /* Function Keys 9,11-13 */ col += vtputs("F9 -Save |"); col += vtputs("F11-Restart |"); col += vtputs("F12-Print |"); col += vtputs("F13-Search |"); break; case 4: /* Function Keys 14-17 */ col += vtputs("F14-Prv Para |"); col += vtputs("F15-Nxt Para |"); col += vtputs("F16-Beg Line |"); col += vtputs("F17-End Line |"); break; case 5: /* Function Keys 18-19+ */ col += vtputs("F18-Del Line |"); col += vtputs("F19-Zoom Can |"); col += vtputs(" |"); col += vtputs(" |"); break; default: /* shouldn't get here. set to known state */ rotat_pos = 1; break; } /* end switch */ while (col < 60) { vtputc(' '); ++col; } col += vtputs("F10- More Key"); while (col < ncol) { /* Pad out. */ vtputc(' '); ++col; } } /* * This routine does the actual rotating of the fields * of the mode-line help message. */ /*ARGSUSED*/ rotatmode( f, n, k) { rotat_pos += 1; if (rotat_pos > LAST_ROT) rotat_pos = 1; modeline(wheadp); /* NOTE: this only works because only */ wheadp->w_flag &= WFHARD; /* one window allowed in TINY version */ return TRUE; } /* * output a string to the mode line, report how long it was. */ vtputs(s) register char *s; { register int n = 0; while (*s != '\0') { vtputc(*s++); ++n; } return n; } #ifdef GOSLING /* * Compute the hash code for * the line pointed to by the "vp". Recompute * it if necessary. Also set the approximate redisplay * cost. The validity of the hash code is marked by * a flag bit. The cost understand the advantages * of erase to end of line. Tuned for the VAX * by Bob McNamara; better than it used to be on * just about any machine. */ VOID hash(vp) register VIDEO *vp; { register int i; register int n; register char *s; if ((vp->v_flag&VFHBAD) != 0) { /* Hash bad. */ s = &vp->v_text[ncol-1]; for (i=ncol; i!=0; --i, --s) if (*s != ' ') break; n = ncol-i; /* Erase cheaper? */ if (n > tceeol) n = tceeol; vp->v_cost = i+n; /* Bytes + blanks. */ for (n=0; i!=0; --i, --s) n = (n<<5) + n + *s; vp->v_hash = n; /* Hash code. */ vp->v_flag &= ~VFHBAD; /* Flag as all done. */ } } /* * Compute the Insert-Delete * cost matrix. The dynamic programming algorithm * described by James Gosling is used. This code assumes * that the line above the echo line is the last line involved * in the scroll region. This is easy to arrange on the VT100 * because of the scrolling region. The "offs" is the origin 0 * offset of the first row in the virtual/physical screen that * is being updated; the "size" is the length of the chunk of * screen being updated. For a full screen update, use offs=0 * and size=nrow-1. * * Older versions of this code implemented the score matrix by * a two dimensional array of SCORE nodes. This put all kinds of * multiply instructions in the code! This version is written to * use a linear array and pointers, and contains no multiplication * at all. The code has been carefully looked at on the VAX, with * only marginal checking on other machines for efficiency. In * fact, this has been tuned twice! Bob McNamara tuned it even * more for the VAX, which is a big issue for him because of * the 66 line X displays. * * On some machines, replacing the "for (i=1; i<=size; ++i)" with * i = 1; do { } while (++i <=size)" will make the code quite a * bit better; but it looks ugly. */ VOID setscores(offs, size) { register SCORE *sp; SCORE *sp1; register int tempcost; register int bestcost; register int j; register int i; register VIDEO **vp; VIDEO **pp, **vbase, **pbase; vbase = &vscreen[offs-1]; /* By hand CSE's. */ pbase = &pscreen[offs-1]; score[0].s_itrace = 0; /* [0, 0] */ score[0].s_jtrace = 0; score[0].s_cost = 0; sp = &score[1]; /* Row 0, inserts. */ tempcost = 0; vp = &vbase[1]; for (j=1; j<=size; ++j) { sp->s_itrace = 0; sp->s_jtrace = j-1; tempcost += tcinsl; tempcost += (*vp)->v_cost; sp->s_cost = tempcost; ++vp; ++sp; } sp = &score[NROW]; /* Column 0, deletes. */ tempcost = 0; for (i=1; i<=size; ++i) { sp->s_itrace = i-1; sp->s_jtrace = 0; tempcost += tcdell; sp->s_cost = tempcost; sp += NROW; } sp1 = &score[NROW+1]; /* [1, 1]. */ pp = &pbase[1]; for (i=1; i<=size; ++i) { sp = sp1; vp = &vbase[1]; for (j=1; j<=size; ++j) { sp->s_itrace = i-1; sp->s_jtrace = j; bestcost = (sp-NROW)->s_cost; if (j != size) /* Cd(A[i])=0 @ Dis. */ bestcost += tcdell; tempcost = (sp-1)->s_cost; tempcost += (*vp)->v_cost; if (i != size) /* Ci(B[j])=0 @ Dsj. */ tempcost += tcinsl; if (tempcost < bestcost) { sp->s_itrace = i; sp->s_jtrace = j-1; bestcost = tempcost; } tempcost = (sp-NROW-1)->s_cost; if ((*pp)->v_color != (*vp)->v_color || (*pp)->v_hash != (*vp)->v_hash) tempcost += (*vp)->v_cost; if (tempcost < bestcost) { sp->s_itrace = i-1; sp->s_jtrace = j-1; bestcost = tempcost; } sp->s_cost = bestcost; ++sp; /* Next column. */ ++vp; } ++pp; sp1 += NROW; /* Next row. */ } } /* * Trace back through the dynamic programming cost * matrix, and update the screen using an optimal sequence * of redraws, insert lines, and delete lines. The "offs" is * the origin 0 offset of the chunk of the screen we are about to * update. The "i" and "j" are always started in the lower right * corner of the matrix, and imply the size of the screen. * A full screen traceback is called with offs=0 and i=j=nrow-1. * There is some do-it-yourself double subscripting here, * which is acceptable because this routine is much less compute * intensive then the code that builds the score matrix! */ VOID traceback(offs, size, i, j) { register int itrace; register int jtrace; register int k; register int ninsl; register int ndraw; register int ndell; if (i==0 && j==0) /* End of update. */ return; itrace = score[(NROW*i) + j].s_itrace; jtrace = score[(NROW*i) + j].s_jtrace; if (itrace == i) { /* [i, j-1] */ ninsl = 0; /* Collect inserts. */ if (i != size) ninsl = 1; ndraw = 1; while (itrace!=0 || jtrace!=0) { if (score[(NROW*itrace) + jtrace].s_itrace != itrace) break; jtrace = score[(NROW*itrace) + jtrace].s_jtrace; if (i != size) ++ninsl; ++ndraw; } traceback(offs, size, itrace, jtrace); if (ninsl != 0) { ttcolor(CTEXT); ttinsl(offs+j-ninsl, offs+size-1, ninsl); } do { /* B[j], A[j] blank. */ k = offs+j-ndraw; uline(k, vscreen[k], &blanks); } while (--ndraw); return; } if (jtrace == j) { /* [i-1, j] */ ndell = 0; /* Collect deletes. */ if (j != size) ndell = 1; while (itrace!=0 || jtrace!=0) { if (score[(NROW*itrace) + jtrace].s_jtrace != jtrace) break; itrace = score[(NROW*itrace) + jtrace].s_itrace; if (j != size) ++ndell; } if (ndell != 0) { ttcolor(CTEXT); ttdell(offs+i-ndell, offs+size-1, ndell); } traceback(offs, size, itrace, jtrace); return; } traceback(offs, size, itrace, jtrace); k = offs+j-1; uline(k, vscreen[k], pscreen[offs+i-1]); } #endif \Rogue\Monster\ else echo "will not over write ./display.c" fi if `test ! -s ./echo.c` then echo "writing ./echo.c" cat > ./echo.c << '\Rogue\Monster\' /* * Echo line reading and writing. * * Common routines for reading * and writing characters in the echo line area * of the display screen. Used by the entire * known universe. */ #include "def.h" #ifdef VARARGS # include <varargs.h> static veread(); VOID ewprintf(); #endif static VOID eformat(); static VOID eputi(); static VOID eputl(); static VOID eputs(); static VOID eputc(); int epresf = FALSE; /* Stuff in echo line flag. */ /* * Erase the echo line. */ VOID eerase() { ttcolor(CTEXT); ttmove(nrow-1, 0); tteeol(); ttflush(); epresf = FALSE; } /* * Ask "yes" or "no" question. * Return ABORT if the user answers the question * with the abort ("^G") character. Return FALSE * for "no" and TRUE for "yes". No formatting * services are available. No newline required. */ eyorn(sp) char *sp; { register KEY s; if (kbdmop == NULL) ewprintf("%s? (y or n) ", sp); for (;;) { s = getkey(0); if (s == 'y' || s == 'Y') return (TRUE); if (s == 'n' || s == 'N') return (FALSE); if (s == (KCTRL|'G') || s == (KCTLX|KCTRL|'G') || s == (KMETA|KCTRL|'G')) { (VOID) ctrlg(FALSE, 1, KRANDOM); return ABORT; } if (kbdmop == NULL) ewprintf("Please answer y or n. %s? (y or n) ", sp); } /*NOTREACHED*/ } /* * Like eyorn, but for more important question. User must type either all of * "yes" or "no", and the trainling newline. */ eyesno(sp) char *sp; { register int s; char buf[64]; s = ereply("%s? (yes or no) ", buf, sizeof(buf), sp); for (;;) { if (s == ABORT) return ABORT; if (s != FALSE) { if ((buf[0] == 'y' || buf[0] == 'Y') && (buf[1] == 'e' || buf[1] == 'E') && (buf[2] == 's' || buf[2] == 'S')) return TRUE; if ((buf[0] == 'n' || buf[0] == 'N') && (buf[1] == 'o' || buf[0] == 'O')) return FALSE; } s = ereply("Please answer yes or no. %s? (yes or no) ", buf, sizeof(buf), sp); } /*NOTREACHED*/ } /* * Write out a prompt, and read back a * reply. The prompt is now written out with full "ewprintf" * formatting, although the arguments are in a rather strange * place. This is always a new message, there is no auto * completion, and the return is echoed as such. */ #ifdef VARARGS ereply(va_alist) va_dcl { register int i; va_list pvar; register char *fp, *buf; register int nbuf; va_start(pvar); fp = va_arg(pvar, char *); buf = va_arg(pvar, char *); nbuf = va_arg(pvar, int); i = veread(fp, buf, nbuf, EFNEW|EFCR, &pvar); va_end(pvar); return i; } #else /* VARARGS3 */ ereply(fp, buf, nbuf, arg) char *fp, *buf; int nbuf; long arg; { return (eread(fp, buf, nbuf, EFNEW|EFCR, (char *)&arg)); } #endif /* * This is the general "read input from the * echo line" routine. The basic idea is that the prompt * string "prompt" is written to the echo line, and a one * line reply is read back into the supplied "buf" (with * maximum length "len"). The "flag" contains EFNEW (a * new prompt), an EFFUNC (autocomplete), or EFCR (echo * the carriage return as CR). */ /* VARARGS4 */ #ifdef VARARGS eread(va_alist) va_dcl { va_list pvar; char *fp, *buf; int nbuf, flag, i; va_start(pvar); fp = va_arg(pvar, char *); buf = va_arg(pvar, char *); nbuf = va_arg(pvar, int); flag = va_arg(pvar, int); i = veread(fp, buf, nbuf, flag, &pvar); va_end(pvar); return i; } #endif #ifdef VARARGS static veread(fp, buf, nbuf, flag, ap) char *fp; char *buf; va_list *ap; { #else eread(fp, buf, nbuf, flag, ap) char *fp; char *buf; char *ap; { #endif register int cpos; register int i; register KEY c; cpos = 0; if (kbdmop != NULL) { /* In a macro. */ while ((c = *kbdmop++) != '\0') buf[cpos++] = (char) c; buf[cpos] = '\0'; goto done; } if ((flag&EFNEW)!=0 || ttrow!=nrow-1) { ttcolor(CTEXT); ttmove(nrow-1, 0); epresf = TRUE; } else eputc(' '); eformat(fp, ap); tteeol(); ttflush(); for (;;) { c = getkey(KQUOTE|KNOMAC); if ((flag&EFAUTO) != 0 && (c == ' ' || c == CCHR('I'))) { cpos += complete(flag, c, buf, cpos); continue; } switch (c) { case 0x0A: case 0x0D: /* Return, done. */ if ((flag&EFFUNC) != 0) { if ((i = complete(flag, c, buf, cpos)) == 0) continue; if (i > 0) cpos += i; } buf[cpos] = '\0'; if ((flag&EFCR) != 0) { ttputc(0x0D); ttflush(); } if (kbdmip != NULL) { if (kbdmip+cpos+1 > &kbdm[NKBDM-3]) { ewprintf("Keyboard macro overflow"); ttflush(); return FALSE; } for (i = 0; i <= cpos; ++i) *kbdmip++ = (KEY) buf[i]; } goto done; case CCHR('G'): /* Bell, abort. */ eputc(CCHR('G')); (VOID) ctrlg(FALSE, 0, KRANDOM); ttflush(); return (ABORT); case 0x7F: /* Rubout, erase. */ if (cpos != 0) { ttputc('\b'); ttputc(' '); ttputc('\b'); --ttcol; if (ISCTRL(buf[--cpos]) != FALSE) { ttputc('\b'); ttputc(' '); ttputc('\b'); --ttcol; } ttflush(); } break; case CCHR('X'): /* C-X */ case CCHR('U'): /* C-U, kill line. */ while (cpos != 0) { ttputc('\b'); ttputc(' '); ttputc('\b'); --ttcol; if (ISCTRL(buf[--cpos]) != FALSE) { ttputc('\b'); ttputc(' '); ttputc('\b'); --ttcol; } } ttflush(); break; case CCHR('Q'): /* C-Q, quote next */ c = getkey(KQUOTE|KNOMAC) ; default: /* All the rest. */ if (cpos < nbuf-1) { buf[cpos++] = (char) c; eputc((char) c); ttflush(); } } } done: if (buf[0] == '\0') return (FALSE); return (TRUE); } /* * do completion on a list of objects. */ complete(flags, c, buf, cpos) register char *buf; register int cpos; { register LIST *lh, *lh2; int i, nxtra; int nhits, bxtra; int wflag = FALSE; int msglen, nshown; char *msg; if ((flags&EFFUNC) != 0) lh = &(symbol[0]->s_list); else if ((flags&EFBUF) != 0) lh = &(bheadp->b_list); else panic("broken complete call: flags"); if (c == ' ') wflag = TRUE; else if (c != '\t' && c != 0x0D && c != 0x0A) panic("broken complete call: c"); nhits = 0; nxtra = HUGE; while (lh != NULL) { for (i=0; i<cpos; ++i) { if (buf[i] != lh->l_name[i]) break; } if (i == cpos) { if (nhits == 0) lh2 = lh; ++nhits; if (lh->l_name[i] == '\0') nxtra = -1; else { bxtra = getxtra(lh, lh2, cpos, wflag); if (bxtra < nxtra) nxtra = bxtra; lh2 = lh; } } lh = lh->l_next; } if (nhits == 0) msg = " [No match]"; else if (nhits > 1 && nxtra == 0) msg = " [Ambiguous]"; else { /* Got a match, do it to it */ /* * Being lazy - ought to check length, but all things * autocompleted have known types/lengths. */ if (nxtra < 0 && nhits > 1 && c == ' ') nxtra = 1; for (i = 0; i < nxtra; ++i) { buf[cpos] = lh2->l_name[cpos]; eputc(buf[cpos++]); } ttflush(); if (nxtra < 0 && c != 0x0D && c != 0x0A) return 0; return nxtra; } /* Set up backspaces, etc., being mindful of echo line limit */ msglen = strlen(msg); nshown = (ttcol + msglen + 2 > ncol) ? ncol - ttcol - 2 : msglen; eputs(msg); ttcol -= (i = nshown); /* update ttcol! */ while (i--) /* move back before msg */ ttputc('\b'); ttflush(); /* display to user */ i = nshown; while (i--) /* blank out on next flush */ eputc(' '); ttcol -= (i = nshown); /* update ttcol on BS's */ while (i--) ttputc('\b'); /* update ttcol again! */ return 0; } /* * The "lp1" and "lp2" point to list structures. The * "cpos" is a horizontal position in the name. * Return the longest block of characters that can be * autocompleted at this point. Sometimes the two * symbols are the same, but this is normal. */ getxtra(lp1, lp2, cpos, wflag) register LIST *lp1, *lp2; register int wflag; { register int i; i = cpos; for (;;) { if (lp1->l_name[i] != lp2->l_name[i]) break; if (lp1->l_name[i] == '\0') break; ++i; if (wflag && !ISWORD(lp1->l_name[i-1])) break; } return (i - cpos); } /* * Special "printf" for the echo line. * Each call to "ewprintf" starts a new line in the * echo area, and ends with an erase to end of the * echo line. The formatting is done by a call * to the standard formatting routine. */ #ifdef VARARGS VOID ewprintf(va_alist) va_dcl { va_list pvar; register char *fp; va_start(pvar); fp = va_arg(pvar, char *); #else /* VARARGS1 */ ewprintf(fp, arg) char *fp; { #endif ttcolor(CTEXT); ttmove(nrow-1, 0); #ifdef VARARGS eformat(fp, &pvar); va_end(pvar); #else eformat(fp, (char *)&arg); #endif tteeol(); ttflush(); epresf = TRUE; } /* * Printf style formatting. This is * called by both "ewprintf" and "ereply" to provide * formatting services to their clients. The move to the * start of the echo line, and the erase to the end of * the echo line, is done by the caller. * Note: %c works, and prints the "name" of the key. However * the key must be cast to an int to avoid tripping over * various oddities in C argument passing. */ static VOID eformat(fp, ap) register char *fp; #ifdef VARARGS register va_list *ap; #else register char *ap; #endif { register int c; char kname[NKNAME]; while ((c = *fp++) != '\0') { if (c != '%') eputc(c); else { c = *fp++; switch (c) { case 'c': #ifdef VARARGS keyname(kname, va_arg(*ap, int)); #else /*NOSTRICT*/ keyname(kname, *(int *)ap); ap += sizeof(int); #endif eputs(kname); break; case 'd': #ifdef VARARGS eputi(va_arg(*ap, int), 10); #else /*NOSTRICT*/ eputi(*(int *)ap, 10); ap += sizeof(int); #endif break; case 'o': #ifdef VARARGS eputi(va_arg(*ap, int), 8); #else /*NOSTRICT*/ eputi(*(int *)ap, 8); ap += sizeof(int); #endif break; case 's': #ifdef VARARGS eputs(va_arg(*ap, char *)); #else /*NOSTRICT*/ eputs(*(char **)ap); ap += sizeof(char *); #endif break; case 'l':/* explicit longword */ c = *fp++; switch(c) { case 'd': #ifdef VARARGS eputl((long)va_arg(*ap, long), 10L); #else /*NOSTRICT*/ eputl(*(long *)ap, 10L); ap += sizeof(long); #endif break; default: eputc(c); break; } break; default: eputc(c); } } } } /* * Put integer, in radix "r". */ static VOID eputi(i, r) register int i; register int r; { register int q; if ((q=i/r) != 0) eputi(q, r); eputc(i%r+'0'); } /* * Put long, in radix "r". */ static VOID eputl(l, r) register long l; register long r; { register long q; if ((q=l/r) != 0) eputl(q, r); eputc((int)(l%r)+'0'); } /* * Put string. */ static VOID eputs(s) register char *s; { register int c; while ((c = *s++) != '\0') eputc(c); } /* * Put character. Watch for * control characters, and for the line * getting too long. */ static VOID eputc(c) register char c; { if (ttcol+2 < ncol) { if (ISCTRL(c) != FALSE) { eputc('^'); c ^= 0x40; } ttputc(c); ++ttcol; } } \Rogue\Monster\ else echo "will not over write ./echo.c" fi if `test ! -s ./extend.c` then echo "writing ./extend.c" cat > ./extend.c << '\Rogue\Monster\' /* * Extended (M-X) commands. */ #include "def.h" /* * Extended command. Call the message line * routine to read in the command name and apply autocompletion * to it. When it comes back, look the name up in the symbol table * and run the command if it is found and has the right type. * Print an error if there is anything wrong. */ /*ARGSUSED*/ extend(f, n, k) { register SYMBOL *sp; register int s; char xname[NXNAME]; if (f == FALSE) s = eread("M-x ", xname, NXNAME, EFNEW|EFFUNC #ifndef VARARGS , (char *) NULL #endif ) ; else s = eread("%d M-x ", xname, NXNAME, EFNEW|EFFUNC, #ifdef VARARGS n #else (char *) &n, (char *) NULL #endif ) ; if (s != TRUE) return (s); if ((sp=symlookup(xname)) != NULL) return ((*sp->s_funcp)(f, n, KRANDOM)); ewprintf("[No match]"); return FALSE; } /* * This function will erase the current buffer, re-read the file, and * cancel the modification flag. It has to remember the file name. */ /*ARGSUSED*/ startover( f, n, k) { BUFFER *bp; bp = wheadp->w_bufp; if (bclear(bp) != FALSE) /* user did not abort it */ { insertfile( bp->b_fname, (char *)NULL); notmodified( FALSE, 0, KRANDOM); sgarbf = TRUE; } } /* * This function will quit Emacs without saving any files, * or asking about saving them. Just call quit with an argument. */ /*ARGSUSED*/ reallyquit( f, n, k) { extern int quit(); quit( TRUE, 1, KRANDOM); } /* * This function will delete the current line. It is easy because * all of the work is done in "killline". The cursor may be anywhere * on it, not just at the beginning. */ /*ARGSUSED*/ deleteline( f, n, k) { killline( TRUE, 0, KRANDOM); /* kill beginning of line */ killline( TRUE, 1, KRANDOM); /* kill end including nl */ } #ifdef STARTUP /* * Define the commands needed to do startup-file processing. * This code is mostly a kludge just so we can get startup-file processing. * * If you're serious about having this code, you should rewrite it. * To wit: * It has lots of funny things in it to make the startup-file look * like a GNU startup file; mostly dealing with parens and semicolons. * This should all vanish. * * It uses the same buffer as keyboard macros. The fix is easy (make * a new function "execmacro" that takes a pointer to char and * does what ctlxe does on it. Make ctlxe and excline both call it.) * but would slow down the non-micro version. * * We define eval-expression because it's easy. It's pretty useless, * since it duplicates the functionality of execute-extended-command. * All of this is just to support startup files, and should be turned * off for micros. */ /* * evalexpr - get one line from the user, and run it. Identical in function * to extend, but easy. */ /*ARGSUSED*/ evalexpr(f, n, k) { register int s; char exbuf[NKBDM]; if ((s = ereply("Eval: ", exbuf, NKBDM)) != TRUE) return s; return excline(exbuf); } /* * evalbuffer - evaluate the current buffer as line commands. Useful * for testing startup files. */ /*ARGSUSED*/ evalbuffer(f, n, k) { register LINE *lp; register BUFFER *bp = curbp; register int s; static char excbuf[NKBDM]; char *strncpy(); for (lp = lforw(bp->b_linep); lp != bp->b_linep; lp = lforw(lp)) { if (llength(lp) >= NKBDM + 1) return FALSE ; (VOID) strncpy(excbuf, ltext(lp), NKBDM); if ((s = excline(excbuf)) != TRUE) return s; } return TRUE; } /* * evalfile - go get a file and evaluate it as line commands. You can * go get your own startup file if need be. */ /*ARGSUSED*/ evalfile(f, n, k) { register int s; char fname[NFILEN]; if ((s = ereply("Load file: ", fname, NFILEN)) != TRUE) return s; return load(fname); } /* * load - go load the file name we got passed. */ load(fname) char *fname; { register int s; char excbuf[NKBDM]; if (((s = ffropen(fname)) == FIOERR) || (s == FIOFNF)) return FALSE; while ((s = ffgetline(excbuf, NKBDM)) == FIOSUC) if (excline(excbuf) != TRUE) break; (VOID) ffclose(); return s == FIOEOF; } /* * excline - run a line from a load file or eval-expression. */ excline(line) register char *line; { register char *funcp, *argp = NULL; char *skipwhite(), *parsetoken(), *backquote(); int status; /* Don't know if it works; don't care - mwm */ if (kbdmip != NULL || kbdmop != NULL) { ewprintf("Not now!") ; return FALSE; } funcp = skipwhite(line); if (*funcp == '\0') return TRUE; /* No error on blank lines */ line = parsetoken(funcp); if (*line != '\0') { *line++ = '\0'; line = skipwhite(line); if ((*line >= '0' && *line <= '9') || *line == '-') { argp = line; line = parsetoken(line); } } kbdmip = &kbdm[0]; if (argp != NULL) { *kbdmip++ = (KEY) (KCTRL|'U'); *kbdmip++ = (KEY) atoi(argp); } *kbdmip++ = (KEY) (KMETA|'X'); /* Pack in function */ while (*funcp != '\0') if (kbdmip+1 <= &kbdm[NKBDM-3]) *kbdmip++ = (KEY) *funcp++; else { ewprintf("eval-expression macro overflow"); ttflush(); return FALSE; } *kbdmip++ = '\0'; /* done with function */ /* Pack away all the args now... */ while (*line != '\0') { argp = skipwhite(line); if (*argp == '\0') break ; line = parsetoken(argp) ; /* Slightly bogus for strings. But they should be SHORT! */ if (kbdmip+(line-argp)+1 > &kbdm[NKBDM-3]) { ewprintf("eval-expression macro overflow"); ttflush(); return FALSE; } if (*line != '\0') *line++ = '\0'; if (*argp != '"') { if (*argp == '\'') ++argp; while (*argp != '\0') *kbdmip++ = (KEY) *argp++; *kbdmip++ = '\0'; } else { /* Quoted strings special again */ ++argp; while (*argp != '"' && *argp != '\0') if (*argp != '\\') *kbdmip++ = (KEY) *argp++; else argp = backquote(++argp, TRUE); /* Quotes strings are gotkey'ed, so no trailing null */ } } *kbdmip++ = (KEY) (KCTLX|')'); *kbdmip++ = '\0'; kbdmip = NULL; status = ctlxe(FALSE, 1, KRANDOM); kbdm[0] = (KCTLX|')'); return status; } /* * a pair of utility functions for the above */ char * skipwhite(s) register char *s; { while ((*s == ' ' || *s == '\t' || *s == ')' || *s == '(') && *s != '\0') if (*s == ';') *s = '\0' ; else s++; return s; } char * parsetoken(s) register char *s; { if (*s != '"') while (*s != ' ' && *s != '\t' && *s != '(' && *s != ')' && *s != '\0') { if (*s == ';') *s = '\0'; else s++; } else /* Strings get special treatment */ do { /* Beware: You can \ out the end of the string! */ if (*s == '\\') ++s; if (ISLOWER(*s)) *s = TOUPPER(*s); } while (*++s != '"' && *s != '\0'); return s; } /* * Put a backquoted string element into the keyboard macro. Return pointer * to char following backquoted stuff. */ /* Don't want to get the objects in isdigit.c just for this */ #define isdigit(c) (((c) >= '0') && ((c) <= '9')) char * backquote(in, flag) char *in; { register KEY keycode; switch (*in++) { case 'T': *kbdmip++ = (KEY) (KCTRL|'I'); break; case 'N': *kbdmip++ = (KEY) (KCTRL|'J'); break; case 'R': *kbdmip++ = (KEY) (KCTRL|'M'); break; case '^': *kbdmip = (KEY) (KCTRL|*in++); if (flag != FALSE && *kbdmip == (KEY) (KCTRL|'X')) { if (*in == '\\') in = backquote(++in, FALSE); else *kbdmip++ = (KEY) *in++; kbdmip[-1] |= (KEY) KCTLX; } else ++kbdmip; break; case 'E': if (flag != TRUE) *kbdmip++ = (KEY) (KCTRL|'['); else if (*in != '\\') *kbdmip++ = (KEY) (KMETA|*in++); else { in = backquote(++in, FALSE); kbdmip[-1] |= (KEY) KMETA; } break; /* (L. Frenkel) Convert "\Fd" and "\Fdd" to a function * key code between KFIRST and KLAST. "dd" should be * decimal; codes > KLAST are mapped to KLAST, for want * of a better idea of what to do with them. */ case 'F': keycode = 0; if (isdigit(*in)) keycode += *in++ - '0'; if (isdigit(*in)) keycode = (10 * keycode) + *in++ - '0'; if ( (keycode += KFIRST) > KLAST) keycode = KLAST; *kbdmip++ = (KEY) keycode; break; } return in; } #endif STARTUP \Rogue\Monster\ else echo "will not over write ./extend.c" fi if `test ! -s ./file.c` then echo "writing ./file.c" cat > ./file.c << '\Rogue\Monster\' /* * File commands. */ #include "def.h" BUFFER *findbuffer(); VOID makename(); VOID upmodes(); /* * insert a file into the current buffer. Real easy - just call the * insertfile routine with the file name. */ /*ARGSUSED*/ fileinsert(f, n, k) { register int s; char fname[NFILEN]; if ((s=ereply("Insert file: ", fname, NFILEN)) != TRUE) return (s); adjustcase(fname); return (insertfile(fname, (char *) NULL)); /* don't set buffer name */ } /* * Select a file for editing. * Look around to see if you can find the * fine in another buffer; if you can find it * just switch to the buffer. If you cannot find * the file, create a new buffer, read in the * text, and switch to the new buffer. */ /*ARGSUSED*/ filevisit(f, n, k) { register BUFFER *bp; int s; char fname[NFILEN]; if ((s=ereply("Find file: ", fname, NFILEN)) != TRUE) return (s); if ((bp = findbuffer(fname, &s)) == NULL) return s; curbp = bp; if (showbuffer(bp, curwp, WFHARD) != TRUE) return FALSE; if (bp->b_fname[0] == 0) return (readin(fname)); /* Read it in. */ return TRUE; } /* * given a file name, either find the buffer it uses, or create a new * empty buffer to put it in. */ BUFFER * findbuffer(fname, s) char *fname; int *s; { register BUFFER *bp; char bname[NBUFN]; adjustcase(fname); for (bp=bheadp; bp!=NULL; bp=bp->b_bufp) { if (strcmp(bp->b_fname, fname) == 0) { return bp; } } makename(bname, fname); /* New buffer name. */ while ((bp=bfind(bname, FALSE)) != NULL) { *s = ereply("Buffer name: ", bname, NBUFN); if (*s == ABORT) /* ^G to just quit */ return NULL; if (*s == FALSE) { /* CR to clobber it */ bp->b_fname[0] = '\0'; break; } } if (bp == NULL) bp = bfind(bname, TRUE); *s = FALSE; return bp; } /* * Read the file "fname" into the current buffer. * Make all of the text in the buffer go away, after checking * for unsaved changes. This is called by the "read" command, the * "visit" command, and the mainline (for "uemacs file"). */ readin(fname) char *fname; { register int status; register WINDOW *wp; if (bclear(curbp) != TRUE) /* Might be old. */ return TRUE; status = insertfile(fname, fname) ; curbp->b_flag &= ~BFCHG; /* No change. */ for (wp=wheadp; wp!=NULL; wp=wp->w_wndp) { if (wp->w_bufp == curbp) { wp->w_linep = lforw(curbp->b_linep); wp->w_dotp = lforw(curbp->b_linep); wp->w_doto = 0; wp->w_markp = NULL; wp->w_marko = 0; } } return status; } /* * insert a file in the current buffer, after dot. Set mark * at the end of the text inserted, point at the beginning. * Return a standard status. Print a summary (lines read, * error message) out as well. If the * BACKUP conditional is set, then this routine also does the read * end of backup processing. The BFBAK flag, if set in a buffer, * says that a backup should be taken. It is set when a file is * read in, but not on a new file (you don't need to make a backup * copy of nothing). * * Warning: Adds a trainling nl to files that don't end in one! * Need to fix, but later (I suspect that it will require a change * in the fileio files for all systems involved). */ insertfile(fname, newname) char fname[], newname[]; { register LINE *lp1; register LINE *lp2; LINE *olp; /* Line we started at */ int opos; /* and offset into it */ register WINDOW *wp; register int i; register int nbytes; int s, nline; BUFFER *bp; char line[NLINE]; bp = curbp; /* Cheap. */ if (newname != (char *) NULL) (VOID) strcpy(bp->b_fname, newname); if ((s=ffropen(fname)) == FIOERR) /* Hard file open. */ goto out; if (s == FIOFNF) { /* File not found. */ if (kbdmop == NULL) if (newname != NULL) ewprintf("(New file)"); else ewprintf("(File not found)"); goto out; } opos = curwp->w_doto; /* Open a new line, at point, and start inserting after it */ (VOID) lnewline(); olp = lback(curwp->w_dotp); nline = 0; /* Don't count fake line at end */ while ((s=ffgetline(line, NLINE)) == FIOSUC) { nbytes = strlen(line); if ((lp1=lalloc((RSIZE) nbytes)) == NULL) { s = FIOERR; /* Keep message on the */ break; /* display. */ } lp2 = lback(curwp->w_dotp); lp2->l_fp = lp1; lp1->l_fp = curwp->w_dotp; lp1->l_bp = lp2; curwp->w_dotp->l_bp = lp1; for (i=0; i<nbytes; ++i) lputc(lp1, i, line[i]); ++nline; } (VOID) ffclose(); /* Ignore errors. */ if (s==FIOEOF && kbdmop==NULL) { /* Don't zap an error. */ if (nline == 1) ewprintf("(Read 1 line)"); else ewprintf("(Read %d lines)", nline); } /* Set mark at the end of the text */ curwp->w_markp = curwp->w_dotp; curwp->w_marko = curwp->w_doto; /* Now, delete the results of the lnewline we started with */ curwp->w_dotp = olp; curwp->w_doto = opos; (VOID) ldelnewline(); curwp->w_doto = opos; /* and dot is right */ #ifdef BACKUP if (newname != NULL) bp->b_flag |= BFCHG | BFBAK; /* Need a backup. */ else bp->b_flag |= BFCHG; #else bp->b_flag |= BFCHG; #endif /* if the insert was at the end of buffer, set lp1 to the end of * buffer line, and lp2 to the beginning of the newly inserted * text. (Otherwise lp2 is set to NULL.) This is * used below to set pointers in other windows correctly if they * are also at the end of buffer. */ lp1 = bp->b_linep; if (curwp->w_markp == lp1) lp2 = curwp->w_dotp; else { out: lp2 = NULL; } for (wp=wheadp; wp!=NULL; wp=wp->w_wndp) { if (wp->w_bufp == curbp) { wp->w_flag |= WFMODE|WFEDIT; if (wp != curwp && lp2 != NULL) { if (wp->w_dotp == lp1) wp->w_dotp = lp2; if (wp->w_markp == lp1) wp->w_markp = lp2; if (wp->w_linep == lp1) wp->w_linep = lp2; } } } if (s == FIOERR) /* False if error. */ return (FALSE); return (TRUE); } /* * Take a file name, and from it * fabricate a buffer name. This routine knows * about the syntax of file names on the target system. * BDC1 left scan delimiter. * BDC2 optional second left scan delimiter. * BDC3 optional right scan delimiter. */ VOID makename(bname, fname) char bname[]; char fname[]; { register char *cp1; register char *cp2; cp1 = &fname[0]; while (*cp1 != 0) ++cp1; #ifdef BDC2 while (cp1!=&fname[0] && cp1[-1]!=BDC1 && cp1[-1]!=BDC2) --cp1; #else while (cp1!=&fname[0] && cp1[-1]!=BDC1) --cp1; #endif cp2 = &bname[0]; #ifdef BDC3 while (cp2!=&bname[NBUFN-1] && *cp1!=0 && *cp1!=BDC3) *cp2++ = *cp1++; #else while (cp2!=&bname[NBUFN-1] && *cp1!=0) *cp2++ = *cp1++; #endif *cp2 = 0; } /* * Ask for a file name, and write the * contents of the current buffer to that file. * Update the remembered file name and clear the * buffer changed flag. This handling of file names * is different from the earlier versions, and * is more compatable with Gosling EMACS than * with ITS EMACS. */ /*ARGSUSED*/ filewrite(f, n, k) { register int s; char fname[NFILEN]; if ((s=ereply("Write file: ", fname, NFILEN)) != TRUE) return (s); adjustcase(fname); if ((s=writeout(curbp, fname)) == TRUE) { #ifdef BACKUP curbp->b_flag &= ~(BFBAK | BFCHG); #else curbp->b_flag &= ~BFCHG; #endif upmodes(curbp); } return (s); } /* * Save the contents of the current buffer back into * its associated file. Do nothing if there have been no changes * (is this a bug, or a feature). Error if there is no remembered * file name. If this is the first write since the read or visit, * then a backup copy of the file is made. * Allow user to select whether or not to make backup files * by looking at the value of makebackup. */ #ifdef BACKUP static int makebackup = 0; #endif /*ARGSUSED*/ filesave(f, n, k) { register int s; if ((curbp->b_flag&BFCHG) == 0) { /* Return, no changes. */ if (kbdmop == NULL) ewprintf("(No changes need to be saved)"); return (TRUE); } if (curbp->b_fname[0] == 0) { /* Must have a name. */ ewprintf("No file name"); return (FALSE); } #ifdef BACKUP if (makebackup && ((curbp->b_flag&BFBAK) != 0)) { s = fbackupfile(curbp->b_fname); if (s == ABORT) /* Hard error. */ return FALSE; if (s == FALSE /* Softer error. */ && (s=eyesno("Backup error, save anyway")) != TRUE) return (s); } #endif if ((s=writeout(curbp, curbp->b_fname)) == TRUE) { #ifdef BACKUP curbp->b_flag &= ~(BFCHG | BFBAK); #else curbp->b_flag &= ~BFCHG; #endif upmodes(curbp); } return (s); } #ifdef BACKUP /* Since we don't have variables (we probably should) * this is a command processor for changing the value of * the make backup flag. If no argument is given, * sets makebackup to true, so backups are made. If * an argument is given, no backup files are made when * saving a new version of a file. Only used when BACKUP * is #defined. */ /*ARGSUSED*/ makebkfile(f, n, k) { makebackup = !f; /* make backup if no argument given */ ewprintf(makebackup ? "Backup files enabled" : "Disabling backup files"); return (TRUE); } #endif /* * This function performs the details of file * writing; writing the file in buffer bp to * file fn. Uses the file management routines * in the "fileio.c" package. Most of the grief * is checking of some sort. */ writeout(bp, fn) register BUFFER *bp; char *fn; { register int s; register LINE *lp; if ((s=ffwopen(fn)) != FIOSUC) /* Open writes message. */ return (FALSE); lp = lforw(bp->b_linep); /* First line. */ while (lp != bp->b_linep) { if ((s=ffputline(&(ltext(lp))[0], llength(lp))) != FIOSUC) break; lp = lforw(lp); } if (s == FIOSUC) { /* No write error. */ s = ffclose(); if (s==FIOSUC && kbdmop==NULL) ewprintf("Text Saved", fn); } else /* Ignore close error */ (VOID) ffclose(); /* if a write error. */ if (s != FIOSUC) /* Some sort of error. */ return (FALSE); return (TRUE); } /* * Tag all windows for bp (all windows if bp NULL) as needing their * mode line updated. */ VOID upmodes(bp) register BUFFER *bp; { register WINDOW *wp; for (wp = wheadp; wp != NULL; wp = wp->w_wndp) if (bp == NULL || curwp->w_bufp == bp) wp->w_flag |= WFMODE; } \Rogue\Monster\ else echo "will not over write ./file.c" fi echo "Finished archive 3 of 5" exit -- =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- Mark A. Hargrove U.S. TeleCenters Voice: 408-496-1800 Santa Clara, CA uucp : {dual, hoptoad, hplabs, portal, ptsfa}!well!ustel