Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP Posting-Version: version B 2.10.2 9/18/84; site unmvax.UUCP Path: utzoo!watmath!clyde!burl!ulysses!allegra!mit-eddie!genrad!panda!talcott!harvard!cmcl2!lanl!unm-la!unmvax!wampler From: wampler@unmvax.UUCP Newsgroups: net.sources Subject: TVX: PD Editor (2 of 7) Message-ID: <957@unmvax.UUCP> Date: Mon, 13-Jan-86 16:39:04 EST Article-I.D.: unmvax.957 Posted: Mon Jan 13 16:39:04 1986 Date-Received: Wed, 15-Jan-86 01:31:32 EST Distribution: net Organization: Univ. of New Mexico, Albuquerque Lines: 2744 #--------CUT---------CUT---------CUT---------CUT--------# ######################################################### # # # This is a shell archive file. To extract files: # # # # 1) Make a directory (like tvx) for the files. # # 2) Write a file, such as "filen.shar", containing # # this archive file into the directory. # # 3) Type "sh file.shar". Do not use csh. # # # ######################################################### # # echo Extracting tvx_1.c: sed 's/^X//' >tvx_1.c <<\SHAR_EOF X/* ------------------------ tvx_1.c ------------------------------------- */ X/* ======================================================================== X X TVX - A full screen editor in C X X Originally developed by: X X Dr. Bruce E. Wampler X University of New Mexico X Department of Computer Science X Farris Engineering Center X Albuquerque, NM 87131 X X uucp: ..{ucbvax | gatech | ihnp4!lanl}!unmvax!wampler X X Public Domain version released July 1, 1985 X Direct comments, bug reports, suggestions to X Bruce Wampler at above address. X X Converted from Ratfor to C January 1981 (note: since the editor X was originally in Ratfor, there are certain remnants of the X original structure left over. There are a lot of things that X could have been done better if written in C originally. X Note also that this editor was originally designed in X 1979 when TECO was state of the art. The influence of X TECO on this editor will be apparent to anyone who has X used TECO. X X So it goes. X X X PLEASE! If you are making additional modifications, use the X indentation scheme used here (line up {,}'s!!!) instead X of the unmaintainable indentation used by K&R!. X Also, please mark your changes with initials and date! X X Description of files required: (names lower case on unix) X X TVX_1.C - main part of code (part 1), mostly os/terminal independent X TVX_2.C - main part of code (part 2), mostly os/terminal independent X TVX_LEX.C - defaults, some os dependent stuff in here. Major X changes in defaults can be fixed by recompiling this file. X TVX_IO.C - almost all I/O, including screen, confined to this file. X TVX_LIB.C - misc library routines needed by TVX. X TVX_IBM.C - IBM-PC specific code, specifically the screen driver X (TVX_IBM.ASM - hand optimized version of TVX_IBM.C) X TVX_UNIX.C - contains unix specific code, including termcap driver X TVX_CFG.C - used to build CONFIG.TVX file for -c switch X TVX_PTCH.C - used to permanently patch tvx with config file X X TVX_DEFS.IC - #define's for version, os, terminal, defaults X TVX_GLBL.IC - global data structures X TVX_TERM.IC - definitions for various terminals and systems X X Most distributions will contain other useful files as well. X X============================================================================ */ X X#include "tvx_defs.ic" /* note tv_defs.ic will #include stdio.h */ X#include "tvx_glbl.ic" X X/* =============================>>> MAIN <<<============================= */ X main (argc,argv) X int argc; X char *argv[]; X { X X checkos(); /* check operating system version */ X force_tty = FALSE; /* won't usually force tty mode */ X X tvinit(); X ttinit(); /* initialize tt: */ X trmini(); /* init terminal if needed */ X csrcmd(); /* make cursor command cursor */ X X fopenx(argc,argv); /* open the file, maybe change switches */ X X tvidefs(); /* set defaults */ X opnbak(); /* may or may not be null routine */ X X edit(); /* edit the file */ X X clobak(); /* may be null routine */ X X file_exit(); /* clean up files */ X X ttymode = FALSE; X X if (*dest_file) X remark(dest_file); /* echo final file name */ X else X { X prompt("R/O, no changes: ") ; remark(orig_file); X } X X reset(); /* reset anything necessary */ X quit(); X } X X/* =============================>>> ASK <<<============================= */ X ask(msg,rply,rcnt) X char *msg,*rply; X int rcnt; X { /* get a reply, via tty if necessary */ X int oldtty; X X oldtty = ttymode; X ttymode = FALSE; /* force echo on */ X prompt(msg); X reply(rply,rcnt); X ttymode = oldtty; /* back how it was */ X } X X/* =============================>>> BEGLIN <<<============================= */ X beglin() X { /* beglin - move cursor to beginning of current line */ X X SLOW int xf; X X curchr = *(lines+curlin) + 1; /* point to current character */ X xf = findx(); /* this line needed to make the next */ X /* call eval order independent, if you wondered */ X tvxy(xf,tvy); /* and move cursor */ X } X X/* =============================>>> BOTPAG <<<============================= */ X botpag() X { /* botpag - move cursor to bottom of current page (buffer) */ X X curlin = nxtlin-1; /* the last real line of text */ X curchr = *(lines+curlin) + 1; /* the first char of that line */ X endlin(); /* goto end of the line */ X newscr(); /* update the screen */ X } X X/* ============================>>> CHK_RPT_NR <<<============================ */ X chk_rpt_nr(val) X int val; X { /* see if val is in valid range */ X X if (val < 0 || val > REPEATBUFS) /* out of range */ X { X tverrb("Bad rpt buff # "); X return (FALSE); X } X else X return (TRUE); X } X X/* =============================>>> CMDERR <<<============================= */ X cmderr(chr) X char chr; X { /* cmderr - invalid command entered */ X X static char badcmd[] = "Bad command: "; X X if (chr >= ' ') X { X badcmd[13] = chr; /* stick in after : */ X badcmd[14] = ' '; X } X else X { X badcmd[13] = '^'; X badcmd[14] = chr + '@'; X } X tverrb(badcmd); X } X X/* =============================>>> COMBIN <<<============================= */ X combin() X { /* combin - combine current line with next line X update screen - cursor assumed to be on curlin */ X X SLOW int to,from,xf; X SLOW BUFFINDEX newl,k1,k2; X X if (curlin+1 >= nxtlin) /* can't combine */ X return (FALSE); X if (nxtsav-nxtchr < ALMOSTOUT) /* check if need g.c. */ X if (! gbgcol()) X return (FALSE); X newl = nxtchr; /* where next char goes */ X stcopy(buff,*(lines+curlin),buff,&nxtchr); /* copy over current line */ X curchr = nxtchr; /* update the curchr */ X k1 = *(lines+curlin); /* we will kill this line */ X *(lines+curlin) = newl; /* remember where it is */ X stcopy(buff,*(lines+curlin+1)+1,buff,&nxtchr); /* append the next line */ X ++nxtchr; /* fix nxtchr */ X to = curlin+1; X k2 = *(lines+to); /* we will kill this line */ X for (from=curlin+2; from < nxtlin ; ) /* copy line to end */ X { X *(lines+to++) = *(lines+from++); X } X --nxtlin; /* update line ptr */ X kline(k1); /* kill the old lines now */ X kline(k2); X if (tvdlin <= dsplin) /* not at end of buffer */ X { X tvescr(); /* erase rest of screen */ X tvxy(1,tvy); /* fix it up */ X tvtype(curlin,min(tvlins-tvdlin+1,nxtlin-curlin)); X } X else /* at end of buffer */ X newscr(); X X xf = findx(); X tvxy(xf,tvy); /* home cursor */ X X return (TRUE); X } X X/* =============================>>> CTRLCH <<<============================= */ X ctrlch(chr) X char chr; X { /* ctrlch - echoes a control character for search and lex */ X X if (chr >= ' ') X tvcout(chr); /* echo as is */ X else if (chr == CR) /* carriage return may be special */ X { X tvcout(CR); X#ifdef USELF X tvcout(LF); /*$$$ some machines need LF */ X#endif X } X else if (chr == ESC) /* escape as $ */ X tvcout('$'); X else /* echo most chars as '^x' */ X { X tvcout('^'); X tvcout(chr+'@'); X } X } X X/* =============================>>> DELNXT <<<============================= */ X int delnxt(cnt) X int cnt; X { /* delnxt - delete next n characters */ X X char clower(); X static char chdel; X SLOW int abscnt,newx; X SLOW BUFFINDEX to; X SLOW char ans[2]; X FAST int i; X X abscnt = cnt; /* remember absolute value of cnt */ X if (cnt > 100 || cnt < -100) /* make sure about this! */ X { X tvclr(); X ask("Kill that many for sure? (y/n) ",ans,1); X verify(1); X if (clower(ans[0]) != 'y') X return (TRUE); X } X X if (cnt > 0) /* deleting forewards */ X { X chdel = *(buff+curchr); /* remember the char we are deleting */ X for (i=1; curlin < nxtlin && i <= cnt; ++i) /* don't pass end of buff */ X { X if (*(buff+curchr)==ENDLINE) /* combine when end of line */ X { X if (! combin()) X { X return (FALSE); X } X } X else /* deleting one character */ X { X to=curchr; /* overwrite current line */ X stcopy(buff,curchr+1,buff,&to); /* copy the rest of the line */ X for (++to; *(buff+to) != BEGLINE && to < nxtchr; ++to) X *(buff+to)=GARBAGE; /* mark the garbage characters */ X } X } X } X else if (cnt < 0) /* deleting backwards */ X { X abscnt=(-cnt); X chdel = *(buff+curchr-1); /* remember the char we are deleting */ X for (i=cnt; curlin >= 1 && i<0; ++i) /* don't go past start */ X { X if (*(buff+curchr-1)==BEGLINE) /* deleting line separator */ X { X if (curlin > 1) /* not past beginning */ X { X dwnlin(-1); /* go up one line */ X endlin(); /* get end of the line */ X if (!combin()) /* and combine */ X { X return (FALSE); X } X } X } X else /* killing a normal character */ X { X to=curchr-1; /* overwrite in place */ X stcopy(buff,curchr,buff,&to); /* copy the rest of the line */ X for (++to; *(buff+to)!=BEGLINE && to < nxtchr; ++to) X *(buff+to)=GARBAGE; /* mark the garbage characters */ X --curchr; X } X } X } X newx=findx(); /* where cursor will go */ X tvxy(newx,tvy); /* reposition cursor */ X if (chdel < ' ' || abscnt != 1) X tvelin(); /* erase rest of the line */ X else /* need to check for tabs following */ X { X for (i = curchr ; *(buff+i)!=ENDLINE ; ++i) X if (*(buff+i) < ' ') X { X tvelin(); /* need to erase the line */ X break; X } X } X tvtyln(curchr); /* retype the rest */ X if (chdel >= ' ' && abscnt == 1 && last_col_out < tvcols) X tvcout(' '); /* "erase" last char on line */ X tvxy(newx,tvy); /* restore the cursor */ X X return (TRUE); X } X X/* =============================>>> DWNCOL <<<============================= */ X dwncol(cnt) X int cnt; X { /* dwncol - move down in column */ X X SLOW int curcol,l,oldef,needns; X X needns = FALSE; X if (leftmg > 1) /* handle right virtual screen different */ X { X oldef=echof; X needns = TRUE; X echof = FALSE; X } X X if (oldlex==VDOWNCOL || oldlex==VUPCOL) /* several in a row? */ X curcol=oldcol; /* pick up old value */ X else X { X curcol = curchr - *(lines+curlin); /* calculate the current column */ X oldcol = curcol; X } X dwnlin(cnt); /* go down given lines */ X if (curlin>=1 && curlin1) /* not at ends? */ X { X l = strlen(buff + ((*(lines+curlin)) + 1) ); X right(min(curcol-1,l)); X } X X if (needns) /* needed new screen */ X { X echof=oldef; X newscr(); X } X } X X/* =============================>>> DWNLIN <<<============================= */ X dwnlin(cnt) X int cnt; X { /* dwnlin - move dot down cnt lines */ X X SLOW int oldlin,change; X X if (curlin==nxtlin-1 && cnt > 0) /* down from last line? */ X { X endlin(); X return; X } X oldlin=curlin; /* remember where we started from */ X curlin=max(min(curlin+cnt,nxtlin-1),1); /* move down lines */ X curchr = *(lines+curlin)+1; /* point to the current character */ X change=curlin-oldlin; /* calculate how many lines changed */ X update(change); /* update the screen */ X X } X X/* =============================>>> EDIT <<<============================= */ X edit() X { /* edit - main editing routine */ X X SLOW int lexval,lexcnt,succ, lastln, itmp; X SLOW int noteloc[10], ni, lex_def; X X static int ins_set[] = X { X VINSERT, VOPENLINE, VQUIT, VABORT, VFBEGIN, VGET, VYANK, 0 X }; X X static int jump_set[] = /* commands to not reset jump memory */ X { X VJUMP, VMEMORY, VHELP, VNOTELOC, VPRINTS, 0 X }; X X static char lexchr; X X startm(); X remark("Reading file..."); X X rdpage(); /* read a page into the buffer */ X X tvclr(); /* clear the screen */ X X if (curlin >= 1) X tvtype(curlin,tvlins); /* type out lines */ X X tvxy(1,1); /* and rehome the cursor */ X waserr = FALSE; /* no errors to erase yet */ X X if (curlin<1) X tverr("Buffer empty"); X X lexval = UNKNOWN; /* so can remember 1st time through */ X useprint = FALSE; /* not to printer */ X succ=TRUE; /* assume success initially */ X X lastln = curlin; /* remember where we were */ X for (ni = 0 ; ni < 10 ; noteloc[ni++] = curlin) X ; /* init noteloc */ X do X { X oldlex = lexval; /* remember last command */ X if (! succ) X echof = TRUE; /* resume echo when error */ X lex_def = lex(&lexval,&lexcnt,&lexchr,succ); /* get command input */ X if (waserr) X fixend(); X waserr=FALSE; X succ=TRUE; X if (lexval == UNKNOWN) X { X cmderr(lexchr); X succ = FALSE; /* announce failure to lex */ X } X else X { X if (curlin < 1) /* make sure legal command for empty buffer */ X { X X if (!inset(lexval,ins_set)) X { X tverrb("Can't, buffer empty. Insert 1st "); X succ=FALSE; X continue; X } X } X if (!inset(lexval,jump_set)) X lastln=curlin; /* let user look at help w/o changing */ X X switch (lexval) X { Xcase 1: /* right */ X right(lexcnt); X break; Xcase 2: /* left */ X right(-lexcnt); X break; Xcase 3: /* down line */ X dwnlin(lexcnt); X break; Xcase 4: /* up line */ X dwnlin(-lexcnt); X break; Xcase 5: /* down in column */ X dwncol(lexcnt); X break; Xcase 6: /* up in column */ X dwncol(-lexcnt); X break; Xcase 7: /* delete last character */ X succ = delnxt(-lexcnt); X break; Xcase 8: /* delete next character */ X succ = delnxt(lexcnt); X break; Xcase 9: /* insert */ X succ = insert(lexcnt,lex_def); X break; Xcase 10: /* kill a line */ X killin(lexcnt); X break; Xcase 11: /* kill rest of line */ X krest(); X break; Xcase 12: /* kill previous part of line */ X kprev(); X break; Xcase 13: /* move to beginning of line */ X beglin(); X break; Xcase 14: /* move to end of the line */ X endlin(); X break; Xcase 15: /* search for a pattern */ X succ = search(lexcnt,TRUE); X break; Xcase 16: /* search for next part of a pattern */ X succ = snext(lexcnt,TRUE); X break; Xcase 17: /* flip screen */ X dwnlin(min(lexcnt*tvlins,nxtlin-curlin+1)); X break; Xcase 18: /* goto top of page */ X toppag(); X break; Xcase 19: /* goto to bottom of page */ X botpag(); X break; Xcase 20: /* goto real beginning of the file */ X succ = fbeg(); X break; Xcase 21: /* verify */ X verify(lexcnt); X break; Xcase 22: /* open new line */ X openln(lexcnt); X succ = insert(1,TRUE); /* go into insert mode, insert mode */ X break; Xcase 23: /* delete last thing manipulated */ X succ = rmvlst(); X break; Xcase 24: /* save lines in move buffer */ X succ = save(lexcnt,FALSE); X break; Xcase 25: /* get move buffer */ X succ = getsav(); X break; Xcase 26: /* read in next page of file */ X wtpage(lexcnt); /* write out the current page */ X succ = rdpage(); /* read in the next */ X tvclr(); X if (succ || lexcnt < 0) X verify(1); X break; Xcase 27: /* append external file to save buffer */ X succ = addfil(lexcnt); X break; Xcase 28: /* quit */ X tvclr(); X remark("Exit"); X goto lquit; Xcase 29: /* search again */ X succ = search(lexcnt,FALSE); /* FALSE => don't read search string */ X break; Xcase 30: /* execute repeat buffer again */ X if (lexcnt != 1) X echof=FALSE; /* turn off echo */ X rptcnt[rptuse] = lexcnt > 0 ? lexcnt : (-lexcnt); X break; Xcase 31: /* print memory status, etc. */ X memory(); X break; Xcase 32: /* change a parameter */ X setpar(lexcnt); X break; Xcase 33: /* remove last and enter insert mode */ X if ((succ = rmvlst())) X succ = insert(1,TRUE); X break; Xcase 34: /* unkill last line killed */ X succ = unkill(); X break; Xcase 35: /* jump over a word */ X wordr(lexcnt); X break; Xcase 36: /* neg jump over word */ X wordr(-lexcnt); X break; Xcase 37: /* append to save buffer */ X succ = save(lexcnt,TRUE); X break; Xcase 38: /* print screen */ X scrprint(); X break; Xcase 39: /* show repeat buffer + help*/ X shoset(); X break; Xcase 40: /* flip screen half page */ X dwnlin( min((lexcnt*tvlins)/2 , nxtlin-curlin+1) ); X break; Xcase 41: /* abort */ X abort(); X break; Xcase 42: /* change characters */ X if ((succ = delnxt(lexcnt))) X succ = insert(1,TRUE); X break; Xcase 43: /* jump back to last location */ X itmp = curlin; X curlin = lastln; X curchr = *(lines+curlin)+1; /* point to the current character */ X verify(1); X lastln = itmp; X break; Xcase 44: /* tidy up screen */ X succ = neaten(lexcnt); X break; Xcase 45: /* save current location */ X if (lexcnt < 1 || lexcnt > 9) X lexcnt = 0; X noteloc[lexcnt] = curlin; X break; Xcase 46: /* return to noted location */ X itmp = curlin; X if (lexcnt < 1 || lexcnt > 9) X lexcnt = 0; X if (noteloc[lexcnt] >= nxtlin) X { X tverrb("Line no longer there "); X noteloc[lexcnt] = curlin; X } X else X { X curlin = noteloc[lexcnt]; X curchr = *(lines+curlin)+1; /* point to the current character */ X verify(1); X lastln = itmp; X } X break; X Xcase 47: X opsystem(); /* call operating system */ X break; X Xcase 48: X if (lex_def) /* default 1 passed */ X lexcnt = rptuse + 1; /* use current repeat loop */ X succ = edit_rpt(lexcnt); /* edit repeat buffer */ X break; X Xcase 49: X succ = store_rpt(lexcnt); /* store repeat buffer */ X break; X Xcase 50: X succ = exec_rpt(lexcnt); /* execute repeat buffer */ X break; X Xcase 51: X succ = ins_pat(lexcnt); X break; Xcase 52: X succ = user_1(lexcnt); /* user function 1 */ X break; X Xcase 53: X succ = user_2(lexcnt); /* user function 2 */ X break; X } /* end of switch */ X continue; /* next iteration of do loop */ X } /* end of else */ X } /* end of do loop */ X while (1); X Xlquit: X for ( wtpage(1) ; rdpage() ; wtpage(1) ) /* write whole file */ X ; X tvclr(); X } X X/* =============================>>> EDIT_RPT <<<============================= */ X edit_rpt(val) X int val; X { /* copy repeat buffer val into buffer for editing */ X X SLOW char *cp; X SLOW int start_line; X X if (val == 0) X val = rptuse+1; X X if (!chk_rpt_nr(val)) X return FALSE; X X --val; /* change to relative */ X X beglin(); /* start by moving to beginning of current line */ X start_line = curlin; /* where we started */ X X X ins_chr('#'); ins_chr(val+'1'); ins_chr(':'); X /* start with number */ X ins_chr('<'); /* insert start of repeat loop */ X X for (cp = &rptbuf[val][0] ; *cp ; ++cp) X ins_chr(*cp); X ins_chr(27); ins_chr(27); /* make a way for store_rpt to find end */ X X ins_chr(CR); /* terminate line */ X curlin = start_line; X curchr = *(lines+curlin)+1; X verify(1); X X return (TRUE); X X } X X/* =============================>>> ENDLIN <<<============================= */ X endlin() X { /* endlin - move cursor to end of the line */ X X FAST int cnt; X SLOW BUFFINDEX i; X X cnt=0; X for (i=curchr; *(buff+i)!=ENDLINE; ++i) /* find end of line */ X ++cnt; X right(cnt); /* move to end of line */ X } X X/* =============================>>> EXEC_RPT <<<============================= */ X exec_rpt(knt) X int knt; X { /* this is combination of k:r,n& */ X static char chr; X static int val; X X if (! grptch(&chr)) /* get buffer # (k) to use */ X return (FALSE); X X val = chr - '0'; /* convert to 0 to 9 */ X X if (!chk_rpt_nr(val)) X return FALSE; X X if (val > 0) /* change to specific buffer */ X rptuse=val-1; /* adjust for 0 index int */ X X if (knt != 1) X echof = FALSE; /* turn off echo */ X X rptcnt[rptuse] = knt > 0 ? knt : (-knt); X X return (TRUE); X } X X/* =============================>>> FINDDL <<<============================= */ X finddl(ibeg,cnt) X int *ibeg,*cnt; X { /* finddl - find the display line X known: current line, calculate where it would go on the screen */ X X if (curlin <= dsplin) X { /* it is in first part of the display */ X *ibeg = 1; X *cnt = min(tvlins,nxtlin-1); X tvdlin = curlin; /* update the display line */ X } X else if (nxtlin-curlin <= tvlins-dsplin) /* at bottom of display */ X { X *ibeg = max(1,nxtlin-tvlins); X *cnt = min(tvlins,nxtlin-1); X tvdlin=min(curlin,tvlins-(nxtlin-curlin)+1); X } X else /* normal case: in middle */ X { X *ibeg=max(1,curlin-dsplin+1); X *cnt=min(tvlins,nxtlin-(*ibeg)); X tvdlin=dsplin; X } X } X X/* =============================>>> FINDX <<<============================= */ X int findx() X { /* findx - find the x position of the current character X handles spacing for tabs, control characters etc */ X X SLOW BUFFINDEX i; X SLOW int pos,lmold; X X pos = 0; X for (i = *(lines+curlin)+1; i<=curchr; ++i) X if (*(buff+i-1)<' ' && *(buff+i-1)>0) /* cur pos depends on last chr */ X if (*(buff+i-1)==TAB) /* handle tabs */ X for (++pos ; ((pos-1) % 8)!=0; ++pos) X ; X else /* control characters (echoed as ^X) */ X pos += 2; /* 2 spaces for other control character */ X else /* normal character */ X ++pos; X X lmold = leftmg; /* old left margin */ X for (;;) X { X if (pos < leftmg) /* won't fit on screen */ X leftmg -= 16; /* shift left */ X else if (pos >= tvcols+leftmg) X leftmg += 16; X else X break; X } X X if (leftmg != lmold) /* this handles screen shift */ X newscr(); X X return (pos-leftmg+1); X } X X/* =============================>>> FIXEND <<<============================= */ X fixend() X { /* fixend - fix the error message line */ X X SLOW int lastl; X X lastl = curlin+(tvlins-tvdlin); /* the last line on the display */ X tvxy(1,tvhardlines); /* get to last line */ X tvelin(); X if (lastl < nxtlin && tvlins == tvhardlines) /* only if really there */ X tvtype(lastl,1); /* write it out */ X if (curlin >= 1) X tvhdln(); /* restore cursor */ X else X tvxy(1,1); X } X X/* =============================>>> GBGCOL <<<============================= */ X int gbgcol() X { /* gbgcol - retrieve unused space in buff */ X X FAST int i; X SLOW int lastln; X SLOW BUFFINDEX nxtbad, nxtgud, to, from, whfrom, offset, newlin; X X tverrb("Compacting buffer "); /* let the user know, it might take a while */ X offset = curchr - *(lines+curlin); /* need to reset curchr later */ X X for (nxtbad=1 ; *(buff+nxtbad)!=GARBAGE && nxtbad < nxtchr; ++nxtbad) X ; /* find first space to free */ X nxtgud=nxtbad; X lastln = 1; /* where to start search */ X do X { X to=nxtbad; X for (from=nxtgud; *(buff+from)==GARBAGE && from= nxtchr) X break; /* at the end of the buffer */ X whfrom=from; /* where it came from */ X newlin = to; /* remember start */ X do X { X *(buff+to) = *(buff+from++); /* copy good stuff up */ X } X while (*(buff+to++)!=ENDLINE); X X nxtbad=to ; nxtgud=from; X X/* now find the old line X following algorithm assumes next line is likely to X be near the previous line */ X X for (i=lastln ; i= nxtlin) /* not found in second half */ X { X for (i=1 ; i < lastln ; ++i) X if (*(lines+i)==whfrom) X { X *(lines+i)=newlin; /* point to new position */ X if (curlin==i) X curchr=newlin+offset; /* fix curchr if need be */ X break; X } X if (i >= lastln) /* make sure we really found it */ X { X tverrb("Compactor lost. Quit NOW! "); X for (i=1 ; i < 32000 ; ++i) X ; X return (FALSE); X } X } X lastln = i; /* start at next line down */ X } X while (nxtgud < nxtchr); X X for (to=nxtbad ; to<=nxtchr ; ) X *(buff+to++)=GARBAGE; X X nxtchr=nxtbad; /* update the next free character */ X tverr("Compactor done"); X return (nxtsav-nxtchr >= 50); X } X X/* =============================>>> GETSAV <<<============================= */ X int getsav() X { /* ## getsav - get text from save buffer */ X X FAST int to,from; X SLOW BUFFINDEX fromch; X SLOW int newlin; X X if (mxbuff-nxtsav+savlin >= nxtsav-nxtchr) /* g.c. */ X if (!gbgcol()) X { X tverrb("No get room "); X return (FALSE); X } X X if (nxtsav==mxbuff) /* nothing to save */ X { X return (TRUE); X } X X if (mxbuff-nxtsav+savlin >= nxtsav-nxtchr || mxline-nxtlin <= savlin) X { /* room to get save buffer? */ X tverrb("No get room "); X return (FALSE); /* no room to save */ X } X X/* check if in middle of line */ X if (curchr > lines[curlin]+1) X ins_chr(CR); X X/* # move down line to make space for new */ X from=nxtlin-1; X nxtlin=nxtlin+savlin; X to=nxtlin-1; X while (from >= curlin) /* copy line ptrs down right amnt. */ X *(lines+(to--)) = *(lines+(from--)); X X newlin=curlin; /* will insert new lines here */ X curlin=to+1; X fromch = mxbuff; /* where taking saved stuff from */ X for ( ; newlin < curlin; ++newlin) X { X *(buff+nxtchr)=BEGLINE; /* insert begline character */ X *(lines+newlin) = nxtchr++; /* update line ptrs to new line */ X do /* copy stuff from save buffer */ X { X *(buff+nxtchr++) = *(buff+fromch); X } X while (*(buff+fromch--)); X } X oldlen=0; X savlen=savlin; X newscr(); X return (TRUE); X } X X/* =============================>>> GRPTCH <<<============================= */ X int grptch(chr) X char *chr; X { /* grptch - gets a char from repeat buffer or gkbd */ X X SLOW char tmpchr; X X if (rptcnt[rptuse]>0) /* need to fetch from repeat buffer */ X if (nxtrpt[rptuse] > lstrpt[rptuse]) X { X return (FALSE); X } X else X { X *chr=rptbuf[rptuse][nxtrpt[rptuse]]; X ++nxtrpt[rptuse]; X } X else X { X gkbd(&tmpchr); /* read the character from the keyboard */ X *chr=tmpchr; X } X return (TRUE); X } X X/* =============================>>> ins_pat <<<============================= */ X ins_pat(lexcnt) X int lexcnt; X { X SLOW char *chrp; X X if (!*pat_buff) X return (FALSE); X for (chrp = pat_buff ; *chrp ; ) /* simply insert pattern buffer */ X { X if (!ins_chr(*chrp++)) /* make sure it works */ X return (FALSE); X } X X return (TRUE); X } X X/* =============================>>> save_pat <<<============================= */ X save_pat() X { /* save the find pattern, based on oldlen */ X X SLOW int i; X SLOW char *chrp; X X X if (oldlen <= 0) X { X pat_buff[0] = 0; X return; /* nothing to save */ X } X X for (i = 1 ; i <= oldlen ; ++i) /* first, move left */ X { X --curchr; X if (*(buff+curchr) == BEGLINE) X { X if (curlin > 1) X { X --curlin; X for (curchr = *(lines+curlin) ; *(buff+curchr)!=ENDLINE ; X ++curchr) X ; /* bump curchr to end of the line */ X } X else X { X ++curchr; X break; X } X } X } X X /* now save, go back right */ X X chrp = pat_buff; /* put in pattern buffer */ X X for (i = 1 ; i <= oldlen ; ++i) X { X if (*(buff+curchr)==ENDLINE) X { X if (curlin+1 >= nxtlin) X break; /* don't go beyond end! */ X ++curlin; X curchr = *(lines+curlin)+1; X *chrp++ = CR; /* make a cr */ X } X else X { X if ((chrp - 100) < pat_buff) /* make sure enough room */ X *chrp++ = *(buff+curchr); X ++curchr; X } X } X *chrp = 0; /* terminate */ X } X X/* =============================>>> INSET <<<============================= */ X inset(val,set) X int val,*set; X { X /* return true if val is in set set */ X X while (*set) X if (val == *set++) X return TRUE; X return FALSE; X } X X/* =============================>>> ins_chr <<<============================= */ X ins_chr(ival) X int ival; X { X return insert(ival,FALSE); /* force insert */ X } X X/* =============================>>> INSERT <<<============================= */ X insert(ival,how) X int ival,how; X { /* insert - insert a character X X if how is TRUE, then read characters from keyboard until X get an escape, otherwise insert ival */ X X SLOW BUFFINDEX from,to; X SLOW BUFFINDEX curbuf,curend; X SLOW int lenins, nocins, ityp, xf; X SLOW BUFFINDEX abvchr; X X SLOW char chr; X X X static int ins_msg = TRUE; /* own variable */ X X if (ins_msg) X csrins(); /* change cursor */ X X if (how) /* how = 1 regular insert mode */ X { X if (! grptch(&chr)) /* get char using grptch */ X goto l9999; X if (chr == ESC) /* esc means done */ X { X goto l1000; X } X } X else X chr = ival; /* use the passed value */ X X if (chr==ENDLINE || chr==BEGLINE || chr==GARBAGE || (chr==ENDFILE && usecz)) X goto l9998; /* don't allow this case! */ X X if (curlin < 1) X { /* buffer empty? */ X curlin=1; /* init for initial insert */ X *(lines+1)=nxtchr; X curchr=nxtchr+1; X *(buff+nxtchr)=BEGLINE; X *(buff+nxtchr+1)=ENDLINE; X nxtchr += 2; X nxtlin = 2; X } X X lenins=0; /* remember length of insert for rmvlst */ X X do X { X if (nxtsav-nxtchr < ALMOSTOUT) X if (!gbgcol()) X goto l9999; /* collect garbage if necessary */ X curbuf = *(lines+curlin); /* pick up the pointer to current line */ X for (curend=curbuf; *(buff+curend)!=ENDLINE; ++curend) X ; /* get line length */ X if (curend+1 < nxtchr) /* not using last part of buffer */ X { X if (curend-curbuf >= nxtsav-nxtchr) X goto l9998; /* no more room! */ X curchr=nxtchr+(curchr-curbuf); /* where curchr will be */ X *(lines+curlin)=nxtchr; /* new line goes here */ X stcopy(buff,curbuf,buff,&nxtchr); /* copy the line to the end */ X curend=nxtchr++; /* reset end pointer */ X kline(curbuf); /* kill off the line */ X curbuf = *(lines+curlin); /* update beginning pointer */ X } X X/* # to here, ready to insert the new character at the end of the line */ X X if (chr==' ' && wraplm > 1 && (tvx >= wraplm || leftmg > 1)) /* auto wrap? */ X chr = CR; X#ifdef FILELF X if (chr == LF && how) X ; /* don't insert lfs in CR/LF systems, echo? */ X else if (chr == CR) /* inserting a new line */ X#else X if (chr == CR) /* inserting a new line */ X#endif X { X if (nxtlin >= mxline) /* any room? */ X { X tverrb("No more free lines for insert "); X goto l9999; X } X X for (from=curend; from >= curchr; --from) X *(buff+from+2) = *(buff+from); /* copy chars down */ X nxtchr += 2; /* bump nxtchr to free space */ X X *(buff+curchr) = ENDLINE; /* mark as endline */ X *(buff+curchr+1) = BEGLINE; /* beginning of line */ X ++lenins; X X to=nxtlin; /* move lines down */ X for (from = nxtlin-1; from > curlin; ) X { /* bump the lines down */ X *(lines+to--) = *(lines+from--); X } X ++nxtlin; /* bump to next free line */ X X *(lines+curlin+1)=curchr+1; /* remember where */ X X if (ins_msg) X fixend(); /* fix last line */ X tvelin(); /* erase stuff after cr */ X X nocins = (leftmg > 1); /* ciline no good if left marg > 1 */ X X dwnlin(1); /* go down one line */ X X if (ciline[0] == 0 || nocins) X { X tvescr(); /* erase the rest of the screen */ X ityp = min(tvlins-tvdlin+1,nxtlin-curlin); X } X else X { X tvinsl(); /* insert a line */ X ityp = 1; X } X X tvtype(curlin,ityp); X tvhdln(); X if (ins_msg) X csrins(); /* change cursor */ X X if (autoin && curlin > 2) /* automatic indentation! */ X { X ins_msg = FALSE; /* turn off insert message */ X abvchr = *(lines+curlin-1)+1; /* prevous line */ X while (*(buff+abvchr)==' ' || *(buff+abvchr)==TAB) X if (!insert(*(buff+abvchr++),FALSE) ) X { X ins_msg = TRUE; X goto l9999; X } X else if (ttymode) /* hmm, now what? */ X { X ttymode = FALSE; X ttwt(*(buff+abvchr-1)); X ttymode = TRUE; X } X ins_msg = TRUE; X fixend(); X csrins(); /* change cursor */ X } X } X else if (chr == delkey && how) X { X if (!delnxt(-1)) /* rubbing out last character */ X goto l9999; X --lenins; X } X else /* inserting on the current line */ X { X to = nxtchr; /* will move to nxtchr */ X for (from = curend ; from >= curchr; ) X { X *(buff+to--) = *(buff+from--); X } X curend=nxtchr++; /* end is now at curchr, bump nxtchr */ X *(buff+curchr)=chr; /* stick in the current character */ X ++lenins; X if (tvlins < tvhardlines - 10) X { X tvelin(); X ctrlch(chr); X ctrlch('+'); X } X else X tvtyln(curchr); /* retype rest of the line */ X ++curchr; /* reset the curchr pointer */ X xf = findx(); X tvxy(xf,tvy); /* reset the cursor */ X } X X/* the character has been inserted and displayed, get another maybe */ X X if (how) X if (!grptch(&chr)) X goto l9999; X } X while (how && chr != ESC); /* end of do */ X X if (tvlins < tvhardlines - 10) /* fix for slow baud */ X { X tvelin(); X tvtyln(curchr); /* retype rest of the line */ X xf = findx(); X tvxy(xf,tvy); /* reset the cursor */ X } X X oldlen = lenins; X savlen = (-1); /* haven't saved lines */ X goto l1000; X Xl9998: X tverrb("Can't insert that char "); Xl9999: X csrcmd(); X return FALSE; Xl1000: X X if (ins_msg) X fixend(); X csrcmd(); X return TRUE; X } X/* ------------------------ tvx_1.c ------------------------------------- */ SHAR_EOF echo Extracting tvx_2.c: sed 's/^X//' >tvx_2.c <<\SHAR_EOF X/* -------------------------------- tvx_2.c ------------------------------- */ X/* ======================================================================== X X tvx_2.c - Part 2 of main TVX code X X============================================================================ */ X X#include "tvx_defs.ic" /* note tv_defs will #include stdio.h */ X#include "tvx_glbl.ic" X X/* =============================>>> KILLIN <<<============================= */ X killin(cnt) X int cnt; X { /* ## killin - kill cnt lines */ X X SLOW int i,lim; X SLOW int from,to,ityp,istrt; X X if (cnt+curlin >= nxtlin || (curlin == nxtlin-1 && cnt >= 0)) X { /* special case: deleting rest of buffer */ X svklin(nxtlin-1); X for (i = curlin ; i <= nxtlin-1 ; ) X kline(*(lines+i++)); X nxtlin = curlin--; X if (curlin > 0) X { X curchr = *(lines+curlin)+1; X newscr(); X } X else X { X curchr=0; X tvclr(); X } X return; X } X X if (cnt < 0) /* negative kill */ X { X cnt = min(-cnt,curlin-1); /* all upwards? */ X dwnlin(-cnt); /* go up that far */ X } X X if (cnt != 0) X { X range(cnt,&to,&from); /* calculate the line numbers to kill */ X X curlin=to; /* remember new current line */ X X svklin(from); /* save one line */ X for (i = to ; i <= from ; ) /* mark lines deleted */ X kline(*(lines+i++)); X X lim = min(nxtlin-1,mxline); X for (++from ; from <= lim ; ) X { X *(lines+to++) = *(lines+from++); /* copy next line number */ X } X X nxtlin=to; X if (nxtlin == curlin) X --curlin; /* don't go past end */ X curchr = *(lines+curlin)+1; /* remember new current character */ X X if (cnt >= 0 && curlin+(tvlins-tvdlin) < nxtlin && X tvdlin < tvlins) /* killing down */ X { X tvxy(1,tvy); /* get to start of line */ X ityp=min(tvlins-tvdlin+1,nxtlin-curlin); X if (cnt!=1 || !ckline[0]) X { X tvescr(); /* erase the screen */ X istrt=curlin; X } X else X { X sendcs(ckline); X istrt=curlin+ityp-1; X tvxy(1,tvlins); X ityp=1; X } X tvtype(istrt,ityp); X tvhdln(); /* home to display line */ X } X else if ( cnt != 1) /* neg and > 1 too complicated */ X newscr(); /* kill up, just retype whole screen */ X else if (nxtlin < tvlins) /* top part of screen */ X { X if (*ckline) /* kill line defined */ X { X tvxy(1,tvy); /* get to start of line */ X sendcs(ckline); /* just need to kill the line */ X tvhdln(); X } X else X newscr(); /* rewrite it all */ X } X else if (tvdlin < tvlins) /* must be in last part of buffer */ X { X if (*ckline && *ctopb) /* kill line & topb defined */ X { X tvxy(1,tvy); /* get to start of line */ X sendcs(ckline); /* kill the line */ X if (curlin-tvdlin > 0) /* something to scroll */ X { X tvtopb(1); /* scroll down one line */ X tvtype(curlin-tvdlin,1); /* type the offscreen line */ X tvdlin++; /* will start display on next line */ X } X tvhdln(); X } X else X newscr(); /* rewrite it all */ X } X else /* if all else fails */ X newscr(); X } X } X X/* =============================>>> KLINE <<<============================= */ X kline(ptr) X BUFFINDEX ptr; X { /* kline - kill off the line beginning at buff position ptr */ X X SLOW BUFFINDEX i; X X for (i=ptr; *(buff+i) != ENDLINE ; ) /* insert GARBAGE to kill */ X *(buff+i++)=GARBAGE; X X *(buff+i)=GARBAGE; /* kill the endline */ X } X X/* =============================>>> KPREV <<<============================= */ X kprev() X { /* kprev - kill from cursor to beginning of line */ X X FAST int chrs; X X svklin(curlin); /* save one line */ X chrs = curchr - *(lines+curlin) - 1; /* how much to delete */ X if (chrs > 0) X delnxt(-chrs); /* won't cause a combine, so don't worry */ X } X X/* =============================>>> KREST <<<============================= */ X krest() X { /* krest - kill the rest of the line, not including cursor and ENDLINE */ X X SLOW int chrs; X SLOW BUFFINDEX i; X X svklin(curlin); /* save one line */ X chrs=0; X for (i=curchr; *(buff+i)!=ENDLINE; ++i) X ++chrs; /* count how much to delete */ X if (chrs > 0) X delnxt(chrs); /* won't cause combine, so don't worry */ X } X X/* =============================>>> NEATEN <<<============================= */ X int neaten(count) X int count; X { /* neaten - fill lines to current margin */ X X SLOW int oldef, i; X SLOW BUFFINDEX linbeg; X SLOW int retval; X X retval = TRUE; X oldef = echof; X if (count > 1) X echof = FALSE; X if (wraplm <= 1 || curlin >= nxtlin-1) X goto l900; /* work only if wrap limit turned on */ X X for (i=1 ; i<=count ; ++i) X { X beglin(); /* start at beginning of line */ X if (curlin >= nxtlin-1) X goto l900; X X /* don't neaten leading space, cr, period or tab */ X X if (*(buff+curchr) == '.') X { X dwnlin(1); X continue; /* skip dot commands */ X } X X while (*(buff+curchr)== ' ' || *(buff+curchr)==ENDLINE X || *(buff+curchr) == 9) X { X right(1); /* skip this line */ X } X X do X { X if (*(buff+curchr) == ENDLINE) X { X if (tvx+leftmg < wraplm) /* combine lines! */ X { X linbeg = *(lines+curlin+1)+1; X /* pt to first char of next line */ X if (*(buff+linbeg) == ' ' || *(buff+linbeg) == ENDLINE X || *(buff+linbeg) == 9 || *(buff+linbeg) == '.') X { X dwnlin(1); X break; /* no more combining */ X } X if (! neat1(1,32)) X goto l990; X goto NEATNEXT; /* tab over another word */ X } X else X { X dwnlin(1); /* no more combining on line */ X break; X } X } X XNEATNEXT: X if (*(buff+curchr-1)==' ' && tvx+leftmg >= wraplm) /* change to cr */ X { X if (! neat1(-1,CR)) /* delete the blank */ X goto l990; X break; X } X wordr(1); X } /*# end of the repeat */ X while (1); X } /*# end of the for */ Xl900: X echof = oldef; X if (oldef && count > 1) X newscr(); X return (retval); X Xl990: /* failure return */ X retval = FALSE; X goto l900; X } X X/* =============================>>> NEAT1 <<<============================= */ X neat1(dir, val) X int dir, val; X { /* change character dir to val */ X X SLOW int oldwrp; X X oldwrp = wraplm; X wraplm = 0; X if (! delnxt(dir)) X goto l900; X if (! ins_chr(val)) X goto l900; X wraplm = oldwrp; X return (TRUE); Xl900: X wraplm = oldwrp; X return (FALSE); X } X X/* =============================>>> NEWSCR <<<============================= */ X newscr() X { /* newscr - retype entire screen, updating cursor position if necessary */ X X SLOW int ibeg,cnt; X X if (tvlins != tvhardlines || nxtlin-1 <= tvlins) X /* two kinds of screen rewrite */ X tvclr(); /* clear the screen and home */ X else X tvxy(1,1); X X finddl(&ibeg,&cnt); /* calculate where it will go */ X tvtype(ibeg,cnt); /* type it out */ X tvhdln(); /* home to display line */ X } X X/* =============================>>> OPENLN <<<============================= */ X openln(cnt) X int cnt; X { /* openln - open a new line */ X X FAST int i; X SLOW int pcnt, oldauto; X X oldauto = autoin; autoin = FALSE; /* don't allow autoindent */ X pcnt = cnt >= 0 ? cnt : (-cnt); /* only allow positive opens */ X for (i=1; i<=pcnt; ++i) X ins_chr(CR); /* insert right number of newlines */ X dwnlin(-pcnt); /* and goto beginning of the opened line */ X endlin(); X autoin = oldauto; X } X X/* =============================>>> RANGE <<<============================= */ X range(cnt,lbeg,lend) X int cnt,*lbeg,*lend; X { /* determine a legal line number range given cnt */ X X if (cnt <= 0) X { X *lbeg=max(curlin+cnt,1); X *lend=curlin; X if (cnt < 0) X *lend = (*lend)-1; X } X else X { X *lbeg=curlin; X *lend=min(nxtlin-1,curlin+cnt-1); X } X } X X/* =============================>>> RIGHT <<<============================= */ X right(cnt) X int cnt; X { /* move cursor right cnt characters X newlines count as one character */ X X FAST int change,i; X X change=0; /* nochange yet */ X if (cnt > 0) X { X for (i = 1 ; i <= cnt ; ++i) X { X if (*(buff+curchr)==ENDLINE) X { X if (curlin+1 >= nxtlin) X break; /* don't go beyond end! */ X ++curlin; X ++change; /* we've gone down one line */ X curchr = *(lines+curlin)+1; X } X else X ++curchr; X } X } X else if (cnt < 0) X { X cnt=(-cnt); X for (i = 1 ; i <= cnt ; ++i) X { X --curchr; X if (*(buff+curchr) == BEGLINE) X { X if (curlin > 1) X { X --curlin; X --change; X for (curchr = *(lines+curlin) ; *(buff+curchr)!=ENDLINE ; X ++curchr) X ; /* bump curchr to end of the line */ X } X else X { X ++curchr; X break; X } X } X } X } X if (change != 0) /* don't do unnecessary change */ X update(change); X tvhdln(); X } X X/* =============================>>> RMVLST <<<============================= */ X int rmvlst() X { /* rmvlst - delete the previous thing found or manipulated X length of oldlen is set by insert, find, and save X may also use savlen if set by save */ X X SLOW int oldech; X static int rmv_set[] = X { X VSEARCH, VNEXT, VSAVE, VGET, VSAGAIN, VSAPPEND, VSAPPEND, X VMVWORD, VMVBWORD, 0 X }; X X if (!inset(oldlex,rmv_set)) X return (FALSE); X X if (savlen > 0) X { X if (curlin == nxtlin-1 && slastl != 0) X { X --savlen; /* reduce the count */ X if (savlen > 0) X { X oldech = echof; X echof = FALSE; X killin(-savlen); /* kill off previous lines */ X echof = oldech; X } X killin(1); /* kill the last line */ X } X else X killin(-savlen); /* kill off savlen lines */ X } X else if (oldlen != 0) X { X if (! delnxt(-oldlen)) X return (FALSE); X } X oldlen = 0; /* don't allow multiple deletes! */ X savlen = (-1); X return (TRUE); X } X X/* =============================>>> SAVE <<<============================= */ X int save(cnt,app) X int cnt,app; X { /* save - save cnt lines in save buffer */ X X SLOW int l,lend; X SLOW BUFFINDEX from; X X if (curlin == nxtlin-1 && slastl!=0) X { X tverrb("Can't save last line twice! "); X return (FALSE); X } X if (cnt < 0) X return (FALSE); X X oldlen = 0; /* use savlin instead */ X X if ((oldlex != VSAVE && !app) || cnt == 0) X { /* if new save, cnt == 0 and not appending */ X slastl=0; X savlin=0; /* haven't saved anything */ X savlen=0; X nxtsav=mxbuff; /* start saving at end */ X if (cnt == 0) X { X return (TRUE); X } X } X X if (oldlex != VSAPPEND && app) /* need to reset count for append */ X savlen=0; X X lend=min(curlin+cnt-1 ,nxtlin-1); X for (l=curlin; l <= lend ; ++l) X { X if (nxtsav-nxtchr < ALMOSTOUT) /* make space if need and can */ X if (!gbgcol() || (nxtsav-nxtchr) < ALMOSTOUT) X { X tverrb("No save room "); X return (FALSE); X } X X from = *(lines+l)+1; /* first character of the line */ X do X { X *(buff+nxtsav--) = *(buff+from++); X } X while (*(buff+from-1)!=ENDLINE); X ++savlin; /* keep track of the length */ X ++savlen; /* savlen for rmvlst */ X if (curlin==nxtlin-1) /* don't save last line twice! */ X { X slastl=1; X break; X } X dwnlin(1); /* move to next line on screen for + only */ X } X return (TRUE); X } X X/* =============================>>> SEARCH <<<============================= */ X search(lexcnt,iarg) X int lexcnt,iarg; X { /* search - search down in buffer for a patter */ X X#define SEARCHEND (-30) X SLOW char chr,c0,c1,c2; X static int slines; X SLOW int oldx,oldy,oldlin; X SLOW int change, searchv, lininc, newln, fold_wild; X SLOW int l,lbeg,is; X SLOW BUFFINDEX ib, bbeg, oldpos, nbbeg; X FAST int i; X X SLOW int how_match, set_len; /* how wild card matching happens */ X char *cp, *s_getset(); X SLOW int w_len,inset,extra_len; /* length of match */ X X static int lastsb = 0; X X lininc = (lexcnt >= 0 ) ? 1 : (-1); X searchv = FALSE; X newln = FALSE; /* haven't rubbed out 2nd line */ X X oldpos = curchr; /* need to remember for -f */ X oldx = tvx ; oldy = tvy ; oldlin = curlin; X X ins_mode = TRUE; /* so ttymode can echo right */ X X if (! iarg) X goto l100; /* get arg form search buffer */ X X tvmsg("Find?",FALSE); X X if (! grptch(&chr)) X goto l9000; X X slines=1; /* only one line so far */ X for (i = 0; chr != ESC && i < 100; ++i) /* read in the pattern */ X { X if (chr == delkey && rptcnt[rptuse] <= 0) /* edit interactive input */ X { X --i; /* adjust i for for loop ++i */ X if (i >= 0) /* wipe out chars on screen */ X { X if (sbuff[i] == 0) /* newline */ X { X --slines; tvcout(CR); newln = TRUE; X#ifdef USELF X tvcout(LF); X#endif X } X else X { X if (newln) X { X tvcout('\\'); X ctrlch(sbuff[i]); X } X else X { X tvcout(BACKSPACE); X tvcout(' '); X tvcout(BACKSPACE); X if (sbuff[i] < ' ' && sbuff[i] != 27) X { X tvcout(BACKSPACE); X tvcout(' '); X tvcout(BACKSPACE); X } X } X } X --i; /* wipe the character */ X } X gkbd(&chr); /* get new char */ X continue; X } X sbuff[i]=chr; /* stuff it away */ X if (chr == LF) X { X#ifdef USELF X tvcout(chr); /*$$$ to ignore lfs in cr/lf systems */ X#endif X } X else if (chr == CR) X { X if (rptcnt[rptuse] <= 0) X tvcout(chr); X#ifdef USELF X tvcout(LF); /*$$$ when needed */ X#endif X ++slines; X sbuff[i]=0; /* end of a line */ X } X else X ctrlch(chr); /* echo character, handline control chars */ X X/*# fetch the next character */ X if (! grptch(&chr)) X goto l9000; X } X X tvcout('$'); /* echo the escape */ X tvxy(oldx,oldy); /* return to old location */ X X if (i>0) /* we got a new pattern */ X { X lastsb=i-1; /* last valid character */ X sbuff[i] = 0; /* make sure an EOS */ X } X fixend(); X Xl100: X extra_len = 0; X if (lininc < 0) X endlin(); X bbeg = curchr; /* start from current character first time */ X c0 = sbuff[0]; /* initial char of pattern */ X if (!xcases) /* get initial character of pattern */ X c0 = (c0 >= 'A' && c0 <= 'Z') ? c0+' ' : c0; X X for (l = curlin ; l < nxtlin && l ; l += lininc) /* l is same as l != 0 */ X { X if ( !c0 ) /* !c0 same as c0 == 0 */ X { X if (lastsb == 0) /* matching cr only? */ X { X dwnlin(1); /* go down one line */ X newscr(); /* screen needs updating */ X goto l8000; X } X else X { X for (ib = bbeg; *(buff+ib); ++ib) X ; X goto l1000; X } X } X Xl900: X if (c0 < ' ') /* save time, check if might be w.c. once */ X { X ib = bbeg; X if (*(buff+ib)) X goto l1000; X } X X for (ib=bbeg; *(buff+ib); ++ib) /* search current line */ X { X c2 = *(buff+ib); /* next char of buffer */ X if (!xcases) X c2 = (c2 >= 'A' && c2 <= 'Z') ? c2+' ' : c2; X X if (c2 != c0) X continue; /* first characters don't match */ X else if (lastsb == 0) X { /* one character pattern */ X curchr = ib+1; X curlin = l; X goto l5000; /* successful match */ X } X else X { X if ((c1 = sbuff[1]) < ' ') /* possible wild? */ X goto l1000; X c2 = *(buff+ib+1); X if (! xcases) /* fold to lower case */ X { X c1 = (c1 >= 'A' && c1 <= 'Z') ? c1+' ' : c1; X c2 = (c2 >= 'A' && c2 <= 'Z') ? c2+' ' : c2; /* clower */ X } X if ( c1 != c2 ) X continue; /* first two don't match */ X else X goto l1000; /* first two match, so possibility */ X } X } X X/* # fall thru => no match on this line */ Xl950: X bbeg = *(lines+l+lininc)+1; /* next beginning character */ X continue; /* go check next line */ X Xl1000: /* we have two characters matching! */ X nbbeg = ib; /* beginning of possible match in buff */ X lbeg = l; /* current line we are searching */ X how_match = 1; /* assume exact match */ X for (is = -1 ; ++is <= lastsb ; ) X { X if ((c1 = sbuff[is]) < ' ') /* possible wild card */ X { X if (c1 == W_span) X { X extra_len--; X how_match = 2; /* span match */ X continue; /* keep scanning search pat */ X } X else if (c1 == W_skip) /* skip? */ X { X extra_len--; X how_match = 0; /* skip match */ X continue; /* keep scanning search pat */ X } X else if ((cp = s_getset(c1,&set_len,&fold_wild)) == NULL) /* not wild */ X goto NOT_WILD; /* continue normally */ X X /* ok, to here, then have possible wild card match */ X X w_len = 0; X X for ( ; ; ) X { X chr = *(buff + nbbeg); /* pick up char */ X if (fold_wild) /* fold if not user */ X chr = clower(chr); X if (chr == ENDLINE) /* break on end of line */ X break; /* get out */ X X inset = s_inset(chr,cp,set_len); /* see if in set */ X if ((how_match > 0 && inset) || (how_match == 0 && !inset)) X { X nbbeg++; /* bump to next char */ X ++w_len; X if (how_match == 1) X break; /* only once on mode 1 */ X } X else X break; X } X X if (w_len <= 0) X { X ++bbeg; /* this part of line doesn't match */ X extra_len = 0; X if (c0 == 0) X goto l950; X else X goto l900; /* try rest of current line */ X } X X /* to here, then exit from wild card match */ X extra_len += w_len - 1; X how_match = 1; /* back to 1 again */ X continue; /* leave cursor on 1st unmatched */ X } X XNOT_WILD: X c2 = *(buff+nbbeg); X if (! xcases) /* fold to lower case */ X { X c1 = (c1 >= 'A' && c1 <= 'Z') ? c1+' ' : c1; X c2 = (c2 >= 'A' && c2 <= 'Z') ? c2+' ' : c2; /* clower */ X } X X if ( c1 != c2 ) X { X extra_len = 0; X ++bbeg; /* this part of line doesn't match */ X if (c0 == 0) X goto l950; X else X goto l900; /* try rest of current line */ X } X X /* regular matched sequence */ X X if (*(buff+nbbeg)==0 && lbeg+1 < nxtlin) X { X ++lbeg; X nbbeg = *(lines+lbeg)+1; /* point to next character */ X } X else X ++nbbeg; X } X X/*# fall through => found the pattern */ X curlin = lbeg; X curchr = nbbeg; X Xl5000: X change = curlin-oldlin; /* compute real line change */ X if ((slines > 1 && iarg) || tvdlin == tvlins || newln) X newscr(); X else X update(change); X goto l8000; X } X curchr = oldpos; /* reset things */ X tvxy(oldx, oldy); X if (slines > 1 && iarg) X newscr(); /* patch up screen */ X pat_buff[0] = 0; X tverrb("Not found "); /* announce failure a little */ X goto l9000; X Xl8000: /* success return */ X oldlen = lastsb+1+extra_len; /* remember the length */ X save_pat(); /* save the find pattern */ X savlen = (-1); /* haven't saved lines */ X searchv = TRUE; X Xl9000: X ins_mode = FALSE; X return (searchv); X } X X/* =============================>>> S_GETSET <<<============================= */ X char *s_getset(wildchr,set_len,fold) X char wildchr; /* wild card character */ X int *set_len, *fold; /* length of set, fold flag */ X { X static char sets[] = /* possible sets */ X { X 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', X 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', X 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', X '4', '5', '6', '7', '8', '9', '.', ',', '?', '!', X '[', ']', '{', '}', '(', ')', '<', '>', '\'','"', X '+', '-', '/', '*', '=', '@', '#', '$', '%', '^', X '&', '_', '~', '`', '|', '\\', ' ', 9, ';', ':', 0 X }; X X struct wild_set X { X char wch; X int s_start, s_len; X }; X X static struct wild_set wild_cards[] = X { X { W_letter, 0, 26 }, /* ^L is a letter, starts at 0, 26 long */ X { W_digit, 26, 10 }, /* ^D is digit, starts at 26, 10 long */ X { W_alpha, 0, 36 }, /* ^A is alpha numeric, start at 0, 36 long */ X { W_punc, 36, 4 }, /* ^P is punctuation, at 36, 4 long */ X { W_anything, 0, 70 }, /* ^X is any thing, whole set */ X { W_others, 36, 34 }, /* ^O is non-alphanum, start at 36, 32 long */ X { 0 , 0, 0 } /* end of set */ X }; X X SLOW int i; X X *fold = FALSE; /* assume won't fold */ X if (!use_wild) X return NULL; /* not there if not using! */ X X for (i = 0 ; wild_cards[i].wch ; ++i) /* scan list */ X { X if (wildchr == wild_cards[i].wch) /* found the one we want */ X { X *set_len = wild_cards[i].s_len; X *fold = TRUE; X return (&sets[ wild_cards[i].s_start ]); X } X } X if (wildchr == W_user) X { X *set_len = strlen(user_set); X return user_set; X } X else X return NULL; X X } X X/* =============================>>> S_inset <<<============================= */ X s_inset(c2,cp,set_len) X char c2, *cp; X int set_len; X { X FAST int i; X X for (i = 0 ; i < set_len ; ++i) X if (c2 == *(cp+i)) X return TRUE; X return FALSE; X } X X/* =============================>>> SETPAR <<<============================= */ X setpar(val) X int val; X { /* setpar - reset varoius parameters X syntax for setpar is [val]:, where [val] is the new value X of the parameter, : is the setpar command, and is the X letter of the parameter to set. */ X X static char chr; X X if (! grptch(&chr)) X return; X chr = clower(chr); X X switch (chr) X { X case 'a': /* set auto indent */ X autoin = val > 0; X break; X X case 'e': /* expand tabs */ X tabspc = max(val,0); X verify(1); /* need to redisplay */ X break; X X case 'd': /* display line */ X if (val < 1 || val > tvlins) X tverrb("Bad par val"); X else X { X dsplin=val; X verify(1); X } X break; X X case 'f': /* set find mode */ X xcases = val <= 0; X break; X X case 'm': /* match wild cards */ X use_wild = val; X break; X X case 'o': X if (rdonly) X { X tverrb("Can't :o, R/O"); X break; X } X tvclr(); X ask("New output file name: ",dest_file,FNAMESIZE); X if (*dest_file) X { X expand_name(dest_file); /* expand output name as needed */ X rdonly = FALSE; X } X verify(1); X break; X X case 's': /* scroll lines */ X if (val < 0 || val > dsplin-1) X tverrb("Bad par val"); X else X scroll=val; X break; X X case 't': /* tty mode */ X tvclr(); X ttymode = val; X ttynext = 1000; X verify(1); X break; X X case 'r': /* change repeat buffer in use */ X if (val < 1 || val > REPEATBUFS) X tverrb("Bad par val"); X else X rptuse=val-1; /* adjust for 0 index int */ X break; X X case 'u': X tvclr(); X ask("Enter user wild card set: ",user_set,39); X verify(1); X break; X X case 'v': /* virtual window size */ X if (val < 3 || val > tvhardlines) X tverrb("Bad par val"); X else X { X tvlins = val; /* set to new display line */ X ddline = (tvlins / 2) + 1; /* fix home line */ X setdscrl(); /* set scroll value */ X dsplin = ddline; /* reset these */ X scroll = dscrl; X verify(1); /* update the screen */ X } X break; X X case 'w': /* change wrap width */ X wraplm=val; X break; X X default: X tverrb("Bad par name"); X } X } X X/* =============================>>> SNEXT <<<============================= */ X snext(lexcnt,iarg) X int lexcnt,iarg; X { /* snext - find a text pattern across page boundaries */ X X SLOW int ihow,pagout; X X if (lexcnt < 0) X { X tverrb("Search fails"); X return (FALSE); X } X X ihow=iarg; /* make a local copy */ X pagout=FALSE; X for(;;) X { X if (! search(lexcnt,ihow)) X { X wtpage(1); /* write out current page */ X ihow=FALSE; /* don't reread search pattern */ X pagout=TRUE; X if (! rdpage()) X { X tvclr(); /* no more text */ X tverrb("Search fails"); X return (FALSE); X } X } X else /* found it */ X { X if (pagout) X newscr(); X return (TRUE); X } X } X } X X/* =============================>>> STORE_RPT <<<============================= */ X store_rpt(dummy) X int dummy; X { /* start at current cursor position, insert into repeat buffer X identified until find >$$ or exceed size limit, deleting as it goes */ X X SLOW char chr; X SLOW int saved, i, val; X X beglin(); /* start by moving to beginning of current line */ X X if ((chr = *(buff+curchr)) != '#') /* get current char, which must be # */ X { X tverrb("Not a valid rpt buff"); X return (FALSE); X } X val = *(buff+curchr+1)-'0'; /* pick up buffer number */ X X if (!chk_rpt_nr(val)) X return FALSE; X X delnxt(4); /* delete the #n:< */ X X --val; /* change to relative */ X X saved = 0; /* no previous chars */ X for (i = 0 ; ; ++i) X { X chr = *(buff+curchr); /* get the character */ X X if (chr == ESC && i > 1 && rptbuf[val][i-1] == ESC && X rptbuf[val][i-2] == SLOOPEND) X { X rptbuf[val][i-1] = 0; /* set to 0 */ X lstrpt[val] = i - 2; X nxtrpt[val] = 0; X delnxt(2); /* delete the 27 and following CR */ X return TRUE; X } X if (++saved > 99) X { X tverrb("Only 100 chars in rpt"); X return FALSE; X } X if (chr == ENDLINE) X chr = CR; X rptbuf[val][i] = chr; /* save the char */ X delnxt(1); /* and delete it */ X } X X } X X/* =============================>>> SVKLIN <<<============================= */ X svklin(lin) X int lin; X { /* svklin - save one line that will be killed */ X X SLOW BUFFINDEX from,to; X X to=0; X for (from= *(lines+lin)+1; *(buff+from)!=ENDLINE; ++from) X { X unkbuf[to]= *(buff+from); /* put it in unkill buffer */ X to = min(130,to+1); X } X unkbuf[to]=0; X } X X/* =============================>>> TOPPAG <<<============================= */ X toppag() X { /* toppag - move cursor to top of the page */ X X curlin=1; X curchr = *(lines+1)+1; /* first char of buffer */ X newscr(); X } X X/* =============================>>> TVIDEFS <<<============================= */ X tvidefs() X { /* initialize these AFTER opening, defaults set by -c */ X X dsplin=ddline; X scroll=dscrl; X xcases=dxcase; X } X X/* =============================>>> TVINIT <<<============================= */ X tvinit() X { /* perform initializations needed for tv edit */ X X FAST BUFFINDEX i; X FAST char *chrp; X SLOW char *lim; X char *malloc(); X X#ifdef MSDOS X BUFFINDEX coreleft(); /* !!! cii-86 dependent */ X#endif X X/* This is a time eater if a big buffer -- if your loader inits X mem to some known value, it might be possible to change GARBAGE X to that value (be sure no other conflicts, like EOS == 0) */ X X/* try for maximum size buffer */ X X#ifndef GEMDOS X if ((lines = (BUFFINDEX *) malloc((MAXLINE+1)*sizeof(BUFFINDEX))) X == NULL) /* line pointer array */ X exit(1); X#else Xif ((lines=(BUFFINDEX *)malloc((unsigned int)((MAXLINE+1)*sizeof(BUFFINDEX))) ) X == NULL) /* line pointer array */ X exit(1); X#endif X X#ifdef UNIX X for (mxbuff=MAXBUFF ; (buff = malloc(mxbuff+2))==NULL ; mxbuff -= 1000) X ; /* text buffer pointer */ X#endif X#ifdef CPM X for (mxbuff=MAXBUFF ; (buff = malloc(mxbuff+2))==NULL ; mxbuff -= 1000) X ; /* text buffer pointer */ X#endif X#ifdef GEMDOS X for (mxbuff = 60000L ; (buff = malloc((unsigned int) (mxbuff+2)))==NULL X ; mxbuff -= 1000L) X ; /* text buffer pointer */ X#endif X#ifdef MSDOS /* *** Cii-86 C compiler dependent! */ X X /* cii-86 requires you to manually leave some memory left over X for the I/O routines to use. Sigh. */ X X if ((mxbuff = (coreleft() - 4000) ) > MAXBUFF) X mxbuff = MAXBUFF; X for ( ; (buff = malloc(mxbuff+2))==NULL ; mxbuff -= 1000) X ; /* text buffer pointer */ X#endif X X mxline = MAXLINE; X X lim = buff + mxbuff; X for (chrp = buff ; chrp <= lim ; *chrp++ = GARBAGE ) X ; /* mark as all garbage */ X X curlin = /* init some stuff */ X oldlen = X curchr = 0; X X xoutcm = leftmg = nxtlin = nxtchr = tvdlin = 1; X *(buff+mxbuff)=0; /* needs to be null for save buffer */ X nxtsav=mxbuff; /* point to end of the buffer */ X X pat_buff[0] = 0; /* null pattern buffer */ X X X savlin = savlen = (-1); X for (i = 0 ; i < REPEATBUFS ; ++i) X { /* fix repeat buffers to initial state */ X rptcnt[i]= nxtrpt[i] = lstrpt[i] = rptbuf[i][1] = 0; X rptbuf[i][0]=SLOOPEND; X } X rptuse=0; /* start with first repeat buff */ X bakflg = FALSE; X ineof = X echof = TRUE; X } X X/* =============================>>> TVERR <<<============================= */ X tverr(str) X char str[]; X { /* tverr - display an error message on the last line of the screen X always writes on screen, returns to old position */ X X SLOW int oldx,oldy,oldxot,oldech; X X waserr = TRUE; X oldx=tvx ; oldy=tvy ; oldxot=xoutcm ; oldech=echof; X X ttynext = 1000; /* force new read */ X X echof = TRUE; /* always echo! */ X tvmsg(str,TRUE); /* print the message part */ X tvxy(oldx,oldy); X xoutcm=oldxot; X echof=oldech; /* restore to what it was */ X } X X/* =============================>>> TVERRB <<<============================= */ X tverrb(str) X char str[]; X { /* tverrb - display an error message on the last line of the screen X always writes on screen, returns to old position */ X X sendcs(cerrbg); X tverr(str); X sendcs(cerred); X } X X/* =============================>>> TVHDLN <<<============================= */ X tvhdln() X { /* tvhdln - home to display line */ X X SLOW int xf; X xf = findx(); X tvxy(xf,tvdlin); X } X X/* =============================>>> TVMSG <<<============================= */ X tvmsg(str,intty) X char str[]; X int intty; X { /* tvmsg - display a message on the last line of the screen */ X X FAST int i; X SLOW int oldtty; X X tvxy(1,tvhardlines); X tvelin(); X X oldtty = ttymode; X if (ttymode && intty) X { X ttymode = FALSE; X prompt(">"); X } X X for (i=0; str[i]; ctrlch(str[i++])) X ; X X if (oldtty) /* end with < if was ttymode */ X remark("<"); X X ttymode = oldtty; X } X X/* =============================>>> TVTYLN <<<============================= */ X tvtyln(chrptr) X BUFFINDEX chrptr; X { /* tvtyln - type a line on the screen without cr/lf */ X X#ifdef ULBD X FAST BUFFINDEX i; X X if (cundlb[0] || cboldb[0]) /* check for underline/bold */ X { X for (i = *(lines+curlin)+1 ; *(buff+i)!=ENDLINE ; ++i) X if (*(buff+i)==TOGUNDERLINE || *(buff+i)==TOGBOLD) X { X tvxy(1,tvy); X xoutcm=1; X tvplin(*(lines+curlin)+1); X return; X } X } X#endif X xoutcm=tvx; X tvplin(chrptr); X } X X/* =============================>>> UNKILL <<<============================= */ X int unkill() X { /* unkill the single last line killed */ X X SLOW char chrval; X FAST int i; X X for (i=0; unkbuf[i]; ++i) X { X chrval=unkbuf[i]; X if (! ins_chr(chrval)) /* unkill slowly by using insert */ X { X return (FALSE); X } X } X return (ins_chr(CR)); X } X X/* =============================>>> UPDATE <<<============================= */ X update(change) X int change; X { /* update - update the screen when line position has changed X will not be used if any alterations have been made */ X X SLOW int abschg,bscrol; X X if (! echof) X return; X abschg = change; X X bscrol = (ctopb[0]==0) ? 0 : scroll; X X if (change < 0) /* have to scroll screen down */ X { X abschg = (-change); X if (tvdlin-abschg < 1) X newscr(); X else if (curlin < tvdlin) /* won't fit exactly */ X { X if (tvdlin >= dsplin-scroll && abschg!=1) X { X tvclr(); /* clear the screen */ X tvtype(1,tvlins); /* type out a screen */ X } X tvdlin=curlin; X } X else if (tvdlin-abschg >= dsplin-scroll) X tvdlin -= abschg; X else X { X if (tvdlin > dsplin-scroll) X { /* moving up from below display line */ X abschg=dsplin-scroll-(tvdlin-abschg); X tvdlin=dsplin-scroll; /* update */ X } X if (ctopb[0]==0) /* can't do reverse linefeeds */ X newscr(); /* no choice, redraw screen */ X else X { X tvtopb(abschg); /* make blank lines at top */ X tvtype(curlin-tvdlin+1,abschg); /* fill in */ X } X } X } X else if (change > 0) /* have to scroll screen up */ X if ((tvdlin+change>tvlins && tvdlin=tvlins) X newscr(); X else if (tvdlin < dsplin+bscrol || nxtlin-1 <= tvlins) X if (tvdlin+change > dsplin+bscrol && nxtlin-1 > tvlins) X newscr(); X else X tvdlin += change; X else if (nxtlin-curlin<=tvlins-tvdlin) /* on bottom part */ X { X if (tvdlin<=dsplin+bscrol && abschg!=1) X { X tvclr(); /* rewrite whole screen */ X tvtype(nxtlin-tvlins,tvlins); X } X tvdlin=min(tvlins,nxtlin-1)-(nxtlin-curlin)+1; X } X else X { X tvbotb(abschg); /* make room */ X tvxy(1,tvlins-abschg+1); /* home to right line */ X tvtype(curlin+tvlins-tvdlin-abschg+1,abschg); /* fix up screen */ X if (tvdlin < dsplin+bscrol) X tvdlin=dsplin; X } X tvhdln(); X } X X/* =============================>>> WORDR <<<============================= */ X wordr(cnt) X int cnt; X { /* wordr - move cursor over words */ X X SLOW int lim,words,incr,lenmov; X X lenmov=0; X if (cnt<0) X { X incr = (-1); /* change */ X lim = (-cnt); X } X else if (cnt == 0) X { X incr = -1; X lim = 0; X } X else X { X incr = 1; X lim = cnt; X } X X for (words=1; words<=lim; ++words) X { X if ((*(buff+curchr)==ENDLINE && incr>0) || X (*(buff+curchr-1)==BEGLINE && incr<0) ) X { X if (curlin+incr==nxtlin || curlin+incr<1) X break; /* at a buffer limit, so quit */ X dwnlin(incr); /* move up or down */ X lenmov += incr; X if (cnt<0) X endlin(); X continue; /* move to next word */ X } X X/* ok, first, skip over word characters */ X while (wrdchr(*(buff+curchr))) X { X if (*(buff+curchr-1)==BEGLINE && incr<=0) X goto l100; X else X { X curchr += incr; X lenmov += incr; X } X } X X/* now skip over remaining non word chars */ X while (! wrdchr(*(buff+curchr))) X { X if ((*(buff+curchr)==0 && incr>0) || (*(buff+curchr-1)==BEGLINE && X incr<0)) X break; X else X { X curchr += incr; X lenmov += incr; X } X } Xl100: ; X } X X if (incr < 0) /* move cursor to beginning of word */ X while (wrdchr(*(buff+curchr-1))) X { X curchr += incr; X lenmov += incr; X } X tvhdln(); X oldlen = lenmov ; savlen=(-1) ; X } X X/* =============================>>> WRDCHR <<<============================= */ X int wrdchr(chr) X char chr; X { /* wrdchr - determine if a character is a "word" type character */ X X if ((chr>='a' && chr <= 'z') || (chr >= 'A' && chr <= 'Z') || X (chr >= '0' && chr <= '9')) X return (TRUE); X else X return (FALSE); X } X/* -------------------------------- tvx_2.c ------------------------------- */ SHAR_EOF echo ALL DONE! exit 0