home *** CD-ROM | disk | FTP | other *** search
- /* This file is EM.CC */
- #include "em.h"
- /*-----*/
- char CW[strsize],disp[256]; jmp_buf *bad=0; /* FILE *debug; */
- /*----- val==string? */
- int val::operator==(reg char*t){if(!t?:!s) return 0; reg int i,m=n;
- reg char*u=s; for(i=0;i<m;i++) if(u[i]!=t[i]) return 0; return t[m]==0;}
- /*----- is char in string? */
- int val::operator>>(reg char c){reg char*S=s,*T;
- for(T=S+n;T>S;) if(c==*--T) return 1; return 0;}
- /*-----*/
- Text killring[16]; short nkill=0;
- gp_cur cursor(0,0); /* the cursor */
- char *Moan=0,*Display=0; line*dustbin=0;
- /*----- if line is blank */
- int line::blank(){reg int i,m=n; char*t=s,j;
- for(i=0;i<m;i++) if((j=t[i])==' '?0:j!=9) return 0; return 1;};
- /*----- new line with specified text */
- line::line(char*s){nullline(); *this=val(s,strlen(s));};
- line::line(char*s,int n){nullline(); *this=val(s,n);};
- line::line(line*L){nullline(); *this=val(L->s,L->n); no_cr(L->no_cr());};
- /*-----*/
- line *line0,*modeline; /* dummy lines */
- screenline Sl[256];
- /*----- initialize buffer */
- void buffer::initbuffer(){next=0; nrows=ncols=0; row1=lastrow=refresh=0; name=0;
- sortcol=truncated=invisible=readonly=changed=oldch=lastrow=stback=0;
- rmargin=71; startc=0; bound=val(); dotcc=dotcc2=-1; col.init();
- tabtype=Case=1; twod=overlay=wrap=longlines=skip_after_word=0;
- text.line(); text.next=text.prev=new line();
- start=mark(text.next,0); dot=start; dot1=start; dot2=start; olddot=start;
- start.prev=0; start^olddot; olddot^dot1; dot1^dot2; dot2^dot; dot.next=0;}
- /*-----*/
- buffer *buf0, *bhead=0, *B=0; int bcount=0; /* B -> current buffer */
- line *T; /* entry point of circle of lines currently being handled */
- char T1w[magicsize]={0},T2w[magicsize]={0}; val T1t(T1w,0),T2t(T2w,0);
- int needswrap=0,obtype,prevobtype=ob_other; region lastyank;
- buffer*window[16]; int nwindows=1,currentwindow=0; byte ccc;
- /*---------- interface with screen */
- long _ax,_bx,_cx,_dx,_si,_di,_bp,_es; short _flags; int nextch=0;
- byte gp_Mode,gp_Rows,gp_Cols;
- short gp_Attr=White,gp_Attr_def=White; /* on black */
- /*-----*/
- void int10() {interrupt(10);}
- void int16() {interrupt(16);}
- void int21() {interrupt(21);}
- void int31() {interrupt(31);}
- void int33() {interrupt(33);}
- /*----- is a key waiting to be read? */
- int typahead() {if(nextch) return 1; _ax=0xb00; int21(); return _ax&255;}
- /*----- swop foreground and background colours at a screen position */
- void counterchange(c_short x){uns short y=x.val();
- x=(y&0x88ff)|((y&0x0700)<<4)|((y&0x7000)>>4);}
- /*-----*/
- byte get__key(){_ax=0x700; int21(); return _ax&255;}
- /*-----*/
- int get_key(){return get__key()?:-get__key();}
- /*-----*/
- int loseip(){A: _ax=0xb00; int21(); if(_ax&255) {get__key(); goto A;} return 1;}
- /*-----*//* Put a char into DOS's input buffer. Returns 0 if fail */
- int inject(int ch){_ax=0x500;
- _cx=ch<0?((-ch)&0xff)<<8:((ch&0xff)<<8)+(ch&0xff); int16(); return !(_ax&1);}
- /*-----*/
- int getkey(){int i; uns short s; c_short_addr t,u=scr(cursor.r,cursor.c).adr();
- if(nextch) {i=nextch; nextch=0; return i;}
- if(B) {t=scr(B->lastrow+1,-1).adr(); s=(*t).val();
- if(Jerry.mc) *t=sch('m',Orange+(White<<4)); counterchange(*t);}
- if(ccc) counterchange(*u);
- A: i=get_key(); if(!Jerry.mc) if(i==-mousemove) goto A;
- if(ccc) counterchange(*u);
- if(B) *t=s;
- if(B) if(i) {line*L=B->dot.r,*M; while(M=L->prev) L=M;
- if(L!=B->text.next) Moan="BUG: buffer.dot.r pointing to wrong buffer";}
- return i;}
- /*-----*/
- int getkey_nowait(){return typahead()?getkey():0;}
- /*----- print char on screen, obeying ctrl chars */
- void printch(char c) {if(c=='C'-64) return;
- if(c==LF) {_ax=0x200; _dx=CR; int21();} _ax=0x200; _dx=c; int21();}
- /*-----*/
- void wr(char*s){while(*s) printch(*s++);}
- /*-----*/
- int beep(){_ax=0x0200; _dx=7; int21(); return 0;}
- /*----- = current screen mode */
- int gp_mode(void) {_ax=0xf00; int10(); return _ax&255;}
- /*----- screen mode := m */
- void gp_mode(char m) {_ax=m&255; int10(); gp_Attr=gp_Attr_def;
- if(m!=gp_mode()) ps("\007error: this computer has no mode 0x%1x\n",m);}
- /*----- move cursor to c */
- void gp_cursor(gp_cur c) {_ax=0x200; _bx=0; _dx=*((short*)&c); int10();}
- /*----- = where cursor is on screen */
- gp_cur gp_cursor(void) {_ax=0x300; _bx=0; int10(); return *((gp_cur*)&_dx);}
- /*----- clear screen line i cols j to J */
- void gp_clear(int i,int j/*=0*/,reg int J/*=gp_Cols-1*/){
- reg int k; c_short_addr L=Sl[i].sa; for(k=j;k<=J;k++) L[k]=sch(' ',White);}
- /*----- video-reverse from gp_Attr */
- inline byte revattr(char a){return ((a&7)<<4)|((a>>4)&7);}
- /*----------*//* error exit */
- volatile void MOAN(char *m) {beep(); gp_Attr=White; Moan=m; longjmp(*bad,1);}
- /*----- get current drive */
- byte drive(){_ax=0x1900; int21(); return _ax+'a';}
- /*----- get current directory for drive c ('@' = current drive, here) */
- void getcurrentdir(char c,char*s) {_dx=c&31; _si=(long)s; _ax=0x4700; int21();
- if(_carry)MOAN("no such drive, or bad drive, or no good floppy on this drive");}
- /*-----make a full pathname, replace / by \, convert to lowercase */
- char* fullfilename(char*full,char*name) {int i,j,k,l,m,n,p,q,net; byte c;
- char *s,*t,Dir[strsize],cw[strsize],*pf[128];
- if(!name[0]) {full[0]=0; return full;}
- if(name[0]=='(') {strcpy(full,name); return full;}
- for(s=cw,t=name;c=*t;t++) if(c>32) if(c<'A' ?0: c<='Z') *s++=c+32; else *s++=c;
- *s=0; n=strlen(cw); /**** relies on ctrl chars=0-31, space=32, lc=uc+32 **/
- s=cw; i=*s; j=s[1];
- if(net=i=='\\'?j=='\\':0) Dir[0]=0; /* net drive */
- else {if(i<'a'?0:i>'z'?0:j==':') s+=2; else i=drive();
- Dir[0]=i; Dir[1]=':';
- if(*s=='/'?:*s=='\\') Dir[2]=0;
- else {Dir[2]='\\'; for(j=3;j<256+2;j++) Dir[j]=0; getcurrentdir(i,Dir+3);}}
- i=strlen(Dir); if(net) s=cw; else Dir[i++]='\\'; strcpy(Dir+i,s);
- for(pf[0]=s=Dir,q=1;*s;s++) if(*s=='/'?1:*s=='\\') {*s=0; pf[q++]=s+1;}
- for(i=0;i<q;i++) if(!(s=pf[i])[0] ?1: (s[0]!='.'?0:!s[1])) pf[i]=0; /* \\ \.\ */
- SQUEEZE: for(p=i=0;i<q;i++) {if(s=pf[i]) pf[p++]=s; /* remove nulls */} q=p;
- for(i=0;i<q;i++) if((s=pf[i])[0]=='.') if(s[1]=='.') if(!s[2]) { /* \..\ */
- pf[i]=0; if(i>1) pf[i-1]=0; goto SQUEEZE;}
- for(i=1;i<q;i++) { /* check each name segment, but not drive or server name */
- k=m=-1; l=strlen(s=pf[i]);
- if(s[0]=='.') continue; /* starts with dot, illegal on PC */
- for(j=0;j<l;j++) if(s[j]=='.') {k=j; break;} /* find first dot */
- if(k<0) {if(l>8) s[8]=0; continue;} /* no dot: shorten to 8 chars */
- for(j++;j<l;j++) if(s[j]=='.') {m=j; break;} /* find second dot */
- if(m>0) continue; /* >1 dots, illegal on PC */
- if(l>k+4) s[l=k+4]=0; /* shorten suffix to 3 chars */
- if(k>8) for(m=k-8,j=k;j<=l;j++) s[j-m]=s[j];} /* shorten stem to 8 chars */
- /*for(i=0;i<q;i++){DEBUG;fprintf(debug,"pf[%1d]='%s'\n",i,pf[i]?:"(null)");GUBED;}*/
- if(net) strcpy(full,"\\\\"); strcpy(full+(net?2:0),pf[0]); for(i=1;i<q;i++) {
- n=strlen(full); full[n]='\\'; strcpy(full+n+1,pf[i]);}
- return full;}
- /*----- compare n chars from t to s */
- int comptext(reg char*s,reg char*t,int n,int nocase/*=0*/){reg char*u;
- if(!n) return 1; if(!s) return 0; if(!t) return 0;
- if(nocase) {for(u=s+n;s<u;) if(to_upper(*s++)!=to_upper(*t++)) return 0;}
- else {for(u=s+n;s<u;) if(*s++!=*t++) return 0;} return 1;}
- /*---------- <marks> */
- /*----- how many marks currently defined on this buffer */
- int buffer::nmarks(){
- reg mark *M; reg int n=0; for(M=dot.next;M;M=M->next,n++); return n;}
- /*----- find mark N. -ve N = count from bottom of stack */
- mark&buffer::Mark(int N){reg mark *M; reg int i; if(!N) N=1;
- if(N==999) return dot2;
- if(N>0) {for(M=B->dot.next ,i= 1;i<N;i++,M=M->next) if(!M) break;}
- else {for(M=B->start.prev,i=-1;i>N;i--,M=M->prev) if(!M) break;}
- if(!M) if(N>0) if(N==i) if(N<17) {B->dot.hsup(); return *B->start.prev;}
- if(!M) {pr(CW,"mark %d not defined yet",N); MOAN(CW);} return *M;}
- /*----- pushmark */
- void mark::push(){reg mark *M,*P; M=new mark(*this); M->prev=0;
- if(!(P=B->dot.next)) {B->dot.next=B->start.prev=M; M->next=0;}
- else {*M^*P; B->dot.next=M;}}
- /*----- push mark onto bottom of mark stack */
- void mark::hsup(){reg mark *M,*P; M=new mark(*this); M->next=0;
- if(!(P=B->start.prev)) {B->dot.next=B->start.prev=M; M->prev=0;}
- else {*P^*M; B->start.prev=M;}}
- /*----- popmark */
- void mark::pop(){reg mark *M;
- if(!(M=B->dot.next)) MOAN("this buffer's mark stack is already empty");
- *this=*M; if(B->dot.next=M->next) M->next->prev=0; else B->start.prev=0;
- delete M; return;}
- /*----- remove a mark */
- void buffer::rem1mark(){reg mark *M;
- if(!(M=dot.next)) MOAN("this buffer's mark stack is already empty");
- if(dot.next=M->next) M->next->prev=0; else start.prev=0; delete M; return;}
- /*---------- <lines> */
- /*----- count lines from start to this line */
- int line::lineno(){reg int i; reg line *L;
- for(i=0,L=this;L;i++,L=L->prev); return i;}
- /*----- insert line after L */
- line* operator/(line &L,val t) {reg line *N,*X,*T=&B->text; N=new line();
- X=L.next?:T; L-*N; *N-*X; T->next->prev=T->prev->next=0; *N=t; return N;}
- /*----- insert line before L */
- line* operator/(val t,line &L) {reg line *N,*X,*T=&B->text; N=new line();
- X=L.prev?:T; *X-*N; *N-L; T->next->prev=T->prev->next=0; *N=t; return N;}
- /*----- how to delete a line */
- void line::del(){if(dustbin) *this-*dustbin; else next=0; dustbin=this;}
- line::~line() {if(s) delete s;}
- void emptydustbin(){reg line *J,*K;
- for(J=dustbin;J;J=K) {K=J->next; delete J;} dustbin=0;}
- /*----- line=val : line gets new text */
- void line::operator=(val t){mark *M; int j; char *ss;
- if(!t.n) {delete s; s=0; n=0; la(1); return;}
- ss=new char[j=roundup(t.n)]; la(1); delete s; s=ss; n=t.n;
- memcpy(ss,t.s,t.n); forallmarks(M) if(M->r==this) M->c<?=t.n;}
- /*----- line=int : change line.n */
- void line::operator=(int nn) {mark *M; int i,j; char *t;
- nn>?=0; if(n==nn) return; la(1);
- if(nn<n) forallmarks(M) if(M->r==this) M->c<?=nn;
- if(!nn) {empty(); return;}
- if((i=roundup(nn))!=roundup(n)) {t=new char[i]; j=n<?nn;
- if(!t) {pr(CW,"needs a line to be too long (%d bytes)",nn); MOAN(CW);}
- if(s) {if(j) memcpy(t,s,j); delete s;} s=t;}
- n=nn; /* if line lengthened, added part not initialized */}
- /*----- line+=int : lengthen line */
- inline void line::operator+=(reg int nn) {*this=n+nn;}
- /*----- line-=int : shorten line */
- inline void line::operator-=(reg int nn) {*this=n-nn;}
- /*----- line+=char : append char to line */
- void line::operator+=(reg char C){*this+=1; s[n-1]=C; la(1);}
- /*----- line+=line2 : append text of line2 to line */
- void line::operator+=(reg line&L) {int nn=n; *this=nn+L.n;
- memcpy(s+nn,L.s,L.n); la(1); return;}
- /*----- -line : remove eol at start of this line */
- void line::operator-(){
- if(!prev) MOAN("chain lines: no previous line"); --*prev;}
- /*----- line-- : remove eol at end of this line */
- void line::operator--(){reg int i; reg mark *m; reg line *L=next,*M;
- if(!L) MOAN("chain lines: no next line"); i=n; *this+=*next; M=L->next;
- forallmarks(m) if(m->r==L) {m->r=this; m->c+=i;} no_cr(L->no_cr());
- if(M) *this-*M; else {next=0; B->text.prev=this;} L->del();}
- /*----- line-Text : remove eol at end of this line, in the Text */
- void line::operator-(reg Text&K){reg line *L=next,*M; if(!L) return;
- *this+=*next; M=L->next; if(M) *this-*M; else {next=0; K.end=this;} L->del();}
- /*----- mark>>=int : move rest of line right n chars */
- void mark::operator>>=(reg int n){reg char*s,*t; if(n<0) *this<<=-n;
- else if(n) for(s=r->s+r->n-1-n,t=r->s+c;s>=t;s--) *(s+n)=*s;}
- /*----- mark<<=int : move rest of line left n chars */
- void mark::operator<<=(reg int n){reg char*s,*t; if(n<0) *this>>=-n;
- else if(n) for(s=r->s+r->n-n,t=r->s+c;t<s;t++) *t=*(t+n);}
- /*----- !mark : delete char at mark */
- void mark::operator!(){reg mark*M; c<?=r->n; r->la(1); B->changed=1;
- if(c==r->n) --*r;
- else {*this<<=1; *r-=1; forallmarks(M) if(M->r==r) if(M->c>c) M->c--;}}
- /*----- backspace-delete char at mark */
- void mark::bs() {reg mark*M; c<?=r->n; B->changed=1;
- if(!c) -(*r);
- else {c--; *this<<=1; *r-=1; r->la(1);
- forallmarks(M) if(M->r==r) if(M->c>c) M->c--;}}
- /*----- mark=char : replace char at mark */
- void mark::operator=(reg char C) {c<?=r->n; r->la(1); B->changed=1;
- if(C==LF) *this/1;
- else if(c>=r->n) *r+=C;
- else r->s[c]=C;}
- /*----- mark+=char : insert char at mark */
- void mark::operator+=(reg char C){reg mark *M; c<?=r->n; r->la(1); B->changed=1;
- if(C==LF) *this/1;
- else if(c>=r->n) {*r+=C; c++;}
- else {*r+=1; (*this)>>=1; r->s[c]=C;
- forallmarks(M) if(M->r==r) if(M->c>=c) M->c++;}}
- /*----- mark+=char* : insert string at mark */
- void mark::operator+=(reg char *C) {reg int i; reg char j;
- for(i=0;j=C[i];i++) (*this)+=j;}
- /*----- mark/int : insert eol. If i==0, mark at cut stays at eol before cut */
- line* mark::operator/(int i) {reg int cut,j; reg line *L,*N; L=r; reg mark *M;
- c<?=L->n; cut=c; N=*L/val(L->s+c,L->n-c); B->changed=1;
- j=N->no_cr(); N->no_cr(L->no_cr()); L->no_cr(j);
- if(i) {forallmarks(M) if(M->r==L) if(M->c>=cut) {M->r=N; M->c-=cut;}}
- else {forallmarks(M) if(M->r==L) if(M->c> cut) {M->r=N; M->c-=cut;}}
- if(i!=2) if(B->start.r==N) B->start.r=L; *L=cut; L->la(1); return N;}
- /*----- line[int] : nth char in line */
- char line::operator[](reg int nn){return nn<0?' ':nn<n?s[nn]:LF;}
- /*----- change color of jth char of ith screen line */
- void recolor(reg int i,reg int j,byte col){c_short_addr SL=Sl[i].sa;
- if(i>=0) if(i<gp_Rows) if(j>=0) if(j<gp_Cols) (*SL).color()=col;}
- /*-----*/
- void line::empty(){delete s; s=0; n=0; la(1);}
- /*-----*/
- char chwidth[256]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,5,3,5,5,5,6,6,3,4,4,5,5,3,5,3,5,5,5,5,5,5,5,5,5,5,5,3,3,5,5,5,5,6,6,6,6,6,6,
- 6,6,6,4,5,6,6,7,6,6,6,6,6,6,6,7,6,7,6,6,5,4,5,4,5,5,3,5,6,5,6,5,4,6,6,3,4,6,3,7,
- 6,5,6,6,5,5,4,6,6,7,5,6,5,4,3,4,5,0,6,6,5,5,5,5,5,5,5,5,5,3,3,3,6,6,6,7,7,5,5,5,
- 6,6,6,6,7,5,5,6,7,5,5,3,5,6,6,6,5,5,5,5,5,5,5,5,5,5,-6,-6,-6,-6,-6,-6,-6,-6,-6,
- -6,-6,-6,-6,-6,-6,-6,-6,-6,-6,-6,-6,-6,-6,-6,-6,-6,-6,-6,-6,-6,-6,-6,-6,-6,-6,
- -6,-6,-6,-6,-6,-6,-6,-6,-6,-6,-6,-6,-6,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,-6,-6,-6,
- -6,-6,-6,-6,-6,-6,-6,-6,-6,-6,-6,-6,0}; /* -6 = width=30 always */
- /* width = this*6 normal, this*4 if subscript/superscript. 0 = not printing */
- /* for LQ1050+: normal/condensed chars/line: 10cpi 136/233; 12cpi 164/272, 15cpi
- 204/NA, proportional 4890/9780 units as above (= 136/272 'n''s) */
- /*----- convert char # to screen col # */
- int line::to_tabexp(reg int c,int proportional/*=0*/) {
- reg int i,j,J,k; reg char*S=s;
- if(!proportional) {c<?=n; if(!B->tabtype) return c;
- for(j=k=0;k<c;) if(S[k++]==9) j=totab(j); else j++; return j;}
- else {for(J=j=k=0;k<c;)
- if((i=S[k++])==9) {i=j; j=totab(j); J+=(j-i)*chwidth[' '];}
- else {j++; J+=abs(chwidth[i&127]);} return j;}}
- /*-----*/
- int line::to_scrcolno(int c) {return to_tabexp(c)-B->startc;}
- /*----- convert screen col # to char # */
- int line::from_tabexp(int p,int proportional/*=0*/) {
- reg int i,j,J,k; reg char *S=s;
- if(!proportional) {if(!B->tabtype) return p;
- for(j=k=0;k<n?j<p:0;) if(S[k++]==9) j=totab(j); else j++; return k;}
- else {reg int P=p*6; for(J=j=k=0;k<n;) {if(J>=P) return k>0?k-1:0;
- if((i=S[k++])==9) {i=j; j=totab(j); J+=(j-i)*chwidth[' '];}
- else {j++; J+=abs(chwidth[i&127]);}} return k;}}
- /*-----*/
- int line::from_scrcolno(int p) {return from_tabexp(p+B->startc);}
- /*----- correlate screen cursor with buffer cursor positions in line */
- void line::dotty() {
- if(B->dotcc<0) {B->dot.c<?=B->dot.r->n; B->dotcc=to_scrcolno(B->dot.c);}
- else B->dot.c=from_scrcolno(B->dotcc);}
- /*----- clear whole screen */
- void clearscreen(){reg short int i,c=sch(' ',White),n=gp_Cols*gp_Rows;
- for(i=0;i<n;i++) screen[i]=c; for(i=0;i<gp_Rows;i++) Sl[i].ok=0;}
- /*----- clear this buffer's window only */
- void buffer::clearscreen(){reg int i; for(i=row1;i<=lastrow;i++) gp_clear(i);}
- /*----- buffer[int]: nth line in buffer */
- line* buffer::operator[](reg int n){reg int i; reg line *L;
- for(i=0,L=text.next;L;L=L->next,i++) if(i==n) return L; return 0;}
- /*----- go to this buffer */
- void buffer::go_to(){
- if(B==this) return; if(nrows) MOAN("this buffer already has a window");
- row1=B->row1; nrows=B->nrows; lastrow=B->lastrow; B->row1=B->nrows=B->lastrow=0;
- window[currentwindow]=B=this; B->redraw_info();}
- /*----- split window with this buffer, old window keeps N screen lines */
- void buffer::split_window_with(int N){reg int i,P=B->nrows-N;
- if(P<2 ?1: N<2) MOAN("window must have at least 2 lines");
- if(nrows) MOAN("this buffer already has a window");
- for(i=B->row1;i<=B->lastrow;i++) Sl[i].ok=0;
- B->nrows=N; nrows=P; lastrow=B->lastrow; B->lastrow-=P; row1=B->row1+N;
- for(i=nwindows++;i>currentwindow;i--) window[i]=window[i-1];
- for(i=row1;i<=lastrow;i++) Sl[i].buf=this; window[++currentwindow]=B=this;}
- /*----- read a Text from file */
- void Text::read(char*f){int F,b=0,i,j,k,m,n=1,z=0; line*K,*L=new line(),*N;K=L;
- if((F=open(f,0x8001))<0) {pr(disp,"new buffer '%s'",f); Display=disp; goto Z;}
- BLOCK: if((m=::read(F,CW,strsize))<0) {close(F); MOAN("file fault on reading");}
- if(!m) goto Y; z+=m; i=-1;
- LINE: i++; if(i) n++; for(k=i;i<m;i++) if(CW[i]==LF) break;
- if(!(n%50)) {pr(disp,"%5d lines = %6d bytes of %s read",n,b,f);
- display(disp,B->lastrow,0,Blue+8);}
- N=new line(); *L-*N; j=i; *N=val(CW+k,j-k); L=N;
- N->la(!k); if(i<m) {b+=N->n+1; goto LINE;} goto BLOCK;
- Y: pr(disp,"file '%s' read, %1d lines, %1d bytes",f,n,z); Display=disp;
- Z: if(F>=0) close(F); beg=K; end=L; beg->la(1);
- for(L=beg;K=L->next;L=K) if(K->la()) {(*L)-(*this); K=L;} else K->la(1);
- for(L=beg;L->next;L=L->next)
- if(L->n?L->s[L->n-1]==CR:0) {if(!(--L->n)) {delete L->s; L->s=0;}}
- else L->no_cr(1);}
- /*-----*/
- val Text::asstring(){line*L; int c=0,i,n=0; for(L=beg;L;L=L->next) c+=L->n+1;
- char*s; val v; v.n=(c-1)>?0; v.s=new char[c>?1];
- for(L=beg;L;L=L->next) {s=L->s;
- for(i=0;i<L->n;i++) v.s[n++]=s[i];
- v.s[n++]=LF;}
- v.s[(--n)>?0]=0; return v;}
- /*----- read buffer from file */ /*** also moan if >1 buffers on this file ***/
- void buffer::read(){Text T;
- if(this!=B) MOAN("BUG: tried to read non-current buffer");
- if(!name) MOAN("BUG: buffer has no file"); T.read(name);
- changed=0; text.next=T.beg; text.prev=T.end; dot=bof();}
- /*----- write buffer to file */
- void buffer::write(){register int i; int F; line *L;
- if(name[0]=='(') MOAN("error in savebuffer: buffer has no file");
- if(attrib(name)==3*256) MOAN("bad filename, or can't find file's directory");
- char crlf[2]={CR,LF};
- pr(CW,"shall I write to file '%s'?",name); if(!yesno(CW)) goto AA;
- for(L=text.next;L;i++,L=L->next) if(L->n) if(!L->s) MOAN("BUG: corrupted line");
- if((F=open(name,0x8302))<0) MOAN("can't open file");
- for(i=1,L=text.next;L;i++,L=L->next) {
- if(L->n) if(::write(F,L->s,L->n)<0) goto FAIL;
- if(!L->next) break;
- if(!(i%50)){pr(CW,"%5d lines written",i);::display(CW,B->lastrow,0,Blue+8);}
- if((L->no_cr()?::write(F,crlf+1,1): ::write(F,crlf,2))<0) goto FAIL;}
- ::close(F); changed=0; AA: Sl[lastrow].ok=0; return;
- FAIL: ::close(F); MOAN("file fault on writing");}
- /*----- empty this buffer */
- void buffer::deltext(){text.prev->del(); dustbin=text.next;
- text.next=text.prev=new line(); /* leave one empty line */}
- /*-----*/
- int buffer::exists(){buffer*BB;
- for(BB=bhead;BB;BB=BB->next) if(BB==this) return 1; return 0;}
- /*-----*/
- buffer::~buffer(){while(dot.next) rem1mark(); delete name; del_every(val(this));
- deltext(); if(bound.n) delete bound.s; buffer**I;
- for(I=&bhead;*I;I=&((*I)->next)) if(*I==this) {*I=(*I)->next; break;}}
- /*----- put buffer into buffer chain */
- buffer*buffer::linkin(){line *L=new line(); text.next=text.prev=L;
- next=bhead; bhead=this; dot=mark(text.next,0); start=dot; return this;}
- /*----- move dot one in each direction */
- void buffer::left(){reg line *L; if(dot.c>0) {--dot.c; return;}
- if(B->twod) MOAN("hit start of line");
- if(L=dot.r->prev) dot.c=(dot.r=L)->n; else MOAN("hit start of buffer");}
- /*-----*/
- void buffer::right(){reg line *L; if(dot.c<dot.r->n) {++dot.c; return;}
- if(B->twod) {(*dot.r)+=' '; dot.c++; B->changed=1; return;}
- if(L=dot.r->next) {dot.c=0; dot.r=L;} else MOAN("hit end of buffer");}
- /*-----*/
- void buffer::up (){if(!(dot.Up ())) MOAN("hit start of buffer");}
- /*-----*/
- void buffer::down (){if(!(dot.Down ())) MOAN("hit end of buffer") ;}
- /*----- move mark one in each direction */
- int mark::Left() {reg line *L; if(c>0 ) {--c; return 1;}
- if(B->twod) return 0;
- if(L=r->prev) {c=(r=L)->n; return 1;} return 0;}
- /*-----*/
- int mark::Right(){reg line *L; if(c<r->n) {++c; return 1;}
- if(B->twod) {(*r)+=' '; c++; B->changed=1; return 1;}
- if(L=r->next) {c=0; r=L; return 1;} return 0;}
- /*-----*//*** beware of dot left off end of line after up and down */
- int mark::Up() {reg line *L=r->prev; if(!L) return 0; r=L; return 1;}
- /*-----*/
- int mark::Down() {reg line *L=r->next; if(!L) return 0; r=L; return 1;}
- /*----- copy a Text and all of its text */
- Text Text::copy(){line *J,*L,*M,*N,*Z=end;
- jmp_buf *oldbad=bad,killos; bad=&killos; if(setjmp(killos)) {
- clear(); bad=oldbad; MOAN("memory got full in copying kill");}
- for(L=0,M=beg;M;M=M->next) {
- N=new line(M); if(L) *L-*N; else J=N; L=N; if(M==Z) break;}
- J->prev=N->next=0; bad=oldbad; return Text(J,N);}
- /*----- empty a Text */
- void Text::clear(){if(beg) if(end) {end->del(); dustbin=beg;} beg=end=0;}
- /*----- mark1<mark2 : find what text order marks are in */
- int mark::operator<(mark&N){return r==N.r?c<N.c:r->lineno()<N.r->lineno();}
- /*----- print a Text to debug file */
- /* void Text::print(){line *L; int i;
- for(L=beg;L;L=L->next) {
- for(i=0;i<L->n;i++) putc(L->s[i],debug); putc('¬',debug);}
- putc(LF,debug);} */
- /*----- tell display to print this text color f on color b */
- void region::color(int f,int b){B->col.Reg=*this; B->col.fg=f; B->col.bg=b;};
- /*----- if region is back to front, reverse its ends */
- void region::right_order(){
- if(!beg.r) MOAN("BUG: right_order() with zero left argument");
- if(!end.r) MOAN("BUG: right_order() with zero right argument");
- if(beg<end) return; mark W(beg); beg=end; end=W;};
- /*----- also counts (lines on screen) which are new since last time displayed */
- int region::ok_to_delete(){reg line*L; reg int n; char s[64];
- for(n=0,L=beg.r;L;L=L==end.r?0:L->next) if(L->sl==line_notshown) n++;
- if(n) {B->display();
- for(n=0,L=beg.r;L;L=L==end.r?0:L->next) if(L->sl==line_notshown) n++;}
- if(!n) return 1; pr(s,"OK to delete or kill %1d lines which are off screen?",n);
- return yesno(s);}
- /*----- delete text between the marks */
- mark region::Delete(int query/*=1*/){reg line *I,*J,*K,*L; reg mark *Z;
- if(beg==end) return beg;
- if(query) if(!ok_to_delete()) MOAN("user abort"); B->changed=1;
- if((I=beg.r)==end.r) {reg int x=end.c-beg.c;
- forallmarks(Z) if(Z->r==I) if(Z->c>beg.c) Z->c=beg.c>?(Z->c-x);
- beg<<=x; *I-=x; return mark(I,beg.c);}
- *I=beg.c; J=I->next; K=end.r; L=end/1; *I-*L; --*I;
- for(K->next=0;J;J=J->next) {forallmarks(Z) if(Z->r==J) {Z->r=beg.r;Z->c=beg.c;}}
- if(dustbin) *K-*dustbin; else K->next=0; dustbin=J; return mark(I,beg.c);}
- /*----- kill text between the marks */
- mark region::kill(int query/*=1*/){reg line *I,*J,*K,*L; mark *Z;
- if(query) if(!ok_to_delete()) MOAN("user abort");
- static mark prevkill(0,0); B->changed=1; int C=beg.c;
- int z=prevobtype!=ob_kill ? 0 : prevkill==beg ? 1 : prevkill==end ? -1 : 0;
- if(!z) {nkill++; nkill&=15; killring[nkill].clear();} Text&W=killring[nkill];
- if(beg.r==end.r) {int x; I=beg.r; J=K=new line(I->s+C,x=end.c-C);
- forallmarks(Z) if(Z->r==I) if(Z->c>C) Z->c=C>?(Z->c-x);
- beg<<=x; *I-=x; beg.r->la(1);}
- else {I=beg.r; J=beg/0; K=end.r; L=end/1; *I-*L; --*I;}
- J->prev=K->next=0; /* seal ends of cut-out bit */
- for(L=J;L;L=L->next) forallmarks(Z) if(Z->r==L) *Z=mark(I,C);
- if(!z ?1: !W.beg) {W.beg=J; W.end=K;}
- else if(z<0) {*K-*W.beg; W.beg=J; *K-W;}
- else /* if(z>0) */ {*W.end-*J; W.end=K; J=J->prev; *J-W;}
- prevkill=mark(I,C); return prevkill;}
- /*----- copy text between marks to Text */
- void region::copykill(){line *J,*K; char c=B->changed;
- nkill++; nkill&=15; Text&W=killring[nkill]; W.clear(); if(beg==end) return;
- if(beg.r==end.r) {K=new line(beg.r->s+beg.c,end.c-beg.c); W=Text(K,K);}
- else {J=beg/0; K=end.r; end/1; W=Text(J,K).copy(); -*J; --*K; B->changed=c;}}
- /*----- copy region to mark, with fudge to save & restore an affected kill */
- region region::copyto(mark&m){if(beg==end) return m-m;
- if(m.isin(*this)) MOAN("can't copy text to within itself");
- Text *K=&killring[nkill],T=*K; K->beg=K->end=0; nkill--; copykill();
- region r=m.yank(killring[nkill],0); killring[nkill]=T; return r;}
- /*----- move region to mark */ /* push & pop m & end to be corrected for edit */
- region region::moveto(mark&m){if(beg==end) return m-m; B->changed=1;
- mark M; if(m==end?1:m==beg) return *this;
- if(m.isin(*this))MOAN("can't move text to within itself"); m.push(); end.push();
- line *A1=beg.r,*A2=beg/0; end.pop(); line *B1=end.r,*B2=end/1;
- *A1-*B2; --*A1; M.pop(); return M.yank(Text(A2,B1),0);}
- /*----- insert (copy of) Text at mark */
- region mark::yank(const Text&k,int copy/*=1*/){
- line *L=r,*M; Text K; mark Snip=*this,Z; K=k;
- if(!K.beg) return Snip-Snip; if(copy) K=K.copy(); M=Snip/1; B->changed=1;
- *L-*K.beg; *K.end-*M; mark(M,0).push(); --*L; -*M; Z.pop(); return Snip-Z;}
- /*-----*/
- int mark::isin(reg region &RR){reg line*L,*A=RR.beg.r,*R=r,*Z=RR.end.r;
- if(R==A) if(c<RR.beg.c) return 0; if(R==Z) return c<RR.end.c; if(R==A) return 1;
- if(A==Z) return 0; for(L=A->next;L!=Z;L=L->next) if(L==R) return 1; return 0;}
- /*-----*/
- byte to__upper[256],to__lower[256]; char chtype[256];
- region Found;
- /*-----*/
- /* void pq(char*s,int n){int i;
- putc('"',debug); for(i=0;i<n;i++) putc(s[i],debug); putc('"',debug);} */
- /*-----*/
- /* Micro-Emacs has : [ = char class start, - = range in [], ] = end of char
- class, ^ = negate char class, * = "closure, does not extend past newline", & =
- use matched string in replacement */
- /* try * = any chars: limit how many?: magic number = count & count limits? */
- /* try a symbol for `skip a word' & similar? */
- /*-----*//* returns region -> string found */
- int heremagic(mark A,val T){if(!T.magic(0)) if(T.s[0]!=*A) return 0;
- int b=0,d,sc; mark M(A),N; char sk,C=*M;
- char c,*t=T.s,*te=T.s+(T.n&0x00ffffff); byte D; Found=A-=mark(0,0);
- if(!M.r) return 0; mark B[17]; B[0]=A;
- if(T.magic(0)) if(*t=='/') if(!M.c) t++; /* leading magic '/', at bol already */
- CH: if(Breakin()) MOAN("user abort"); if(t>=te) goto OK;
- if(!T.magic(t-T.s)) if(C!=*t++) goto NO; else goto NEXT;
- switch(D=*t++) {
- default: if(isalpha(D))if(D==to_lower(D)){if(D==to_lower(C))goto NEXT; goto NO;}
- Moan="illegal magic character in search: ' '"; Moan[36]=t[-1]; MOAN(Moan);
- case '[': if(b>=16) MOAN("magic [] > 16 deep"); B[++b]=M; goto CH;
- case '|': if(b<1) MOAN("magic | outside [ ]");
- for(d=b;t<te;t++) if(T.magic(t-T.s)) if((c=*t)=='[') b++;
- else if(c==']') if(--b<d) {t++; goto CH;} goto OK; /* OK to |, skip to ] */
- case ']': if(--b<0) MOAN("unpaired magic ]"); goto CH;
- case '{': Found.beg=M; goto CH;
- case '}': Found.end=M; goto CH;
- case '=': M.c=M.r->n; C=LF; goto CH;
- case '#': if(isalnum(C)) goto NO; goto NEXT;
- case ',': if(!isalnum(C)) goto NO; goto NEXT;
- case '/': if(M.c==M.r->n) goto NEXT; /* falls thru */
- case '\\': if(!M.r->prev) if(!M.c) goto CH;
- if(!M.r->next) if(M.c==M.r->n) goto CH; goto NO;
- case 9: sk=9; goto SKIP;
- case ' ': sk=' '; goto SKIP;
- SKIP: sc=0; SK: if(C==' '?:C==9) {++M; C=*M; sc++; goto SK;}
- if(C==LF) if(sk==9) if(N=M,++M,M.r) {sc++; C=*M; goto SK;} else M=N;
- if(!sc) goto NO; goto CH;
- case '.': if(C==LF) goto NO; goto NEXT;} goto CH;
- NEXT: N=M; ++M; if(!M.r) {M=N; goto NO;} C=*M; goto CH;
- NO: if(b>0) for(d=b;t<te;t++) if(T.magic(t-T.s)) /* skip to | for alt to try */
- if(*t=='[') b++; /* allow omitted initial [ and final ] */
- else if(*t=='|') {if(b<=d) {M=B[b]; C=*M; t++; goto CH;}}
- else if(*t==']') {if(b) d<?=--b;} return 0;
- OK: if(b>0) MOAN("unpaired magic ["); if(!Found.end.r) Found.end=M;
- if(Found.end<Found.beg) MOAN("magic {range} back-to-front"); return 1;}
- /*-----*//* returns mark -> end of string found */
- int here(mark A,val T,int word/*=0*/){
- char*s=A.r->s+A.c,*se,*t=T.s,*te=t+T.n;
- if(word) if(A.c>=word) if(isalnum(s[-word])) return 0; Found.beg=A;
- if(Breakin()) MOAN("user abort"); L: se=(A.r->s+A.r->n)<?(s+(te-t));
- if(B->Case) while(s<se) {if(*s++!=*t++) return 0;}
- else while(s<se) if(to_upper(*s++)!=to_upper(*t++)) return 0;
- if(t>=te) {Found.end=mark(A.r,s-A.r->s); return word?!isalnum(*Found.end):1;}
- if(!(A.r=A.r->next)) return -1; if(*t++!=LF) return 0; s=A.r->s; goto L;}
- /*----- search forwards for text in region */
- int region::huntf(val T,int word/*=0*/) {if(!T.n) return 0;
- reg char *s,*se,C; line*A=beg.r,*D; if(!A) return 0; int a=beg.c,v=0,i,n; val U;
- line*e=end.r; line*E=e->next; e->next=0; int en=e->n; e->n=end.c; /*pseudo eof*/
- jmp_buf*oldbad=bad,failed; bad=&failed; if(setjmp(*bad)) goto F;
- if(T.magic()) if(T.magic(0)?0:T.s[0]==LF) {for(;A;A=A->next)
- if(heremagic(mark(A,A->n),T)) {v=1; goto F;} goto F;}
- else {for(;A;A=A->next,a=0) for(n=A->n;a<=n;a++)
- if(heremagic(mark(A,a ),T)) {v=1; goto F;} goto F;}
- C=B->Case?T.s[0]:to_upper(T.s[0]); U=val(T.s+1,T.n-1); if(word) word=2;
- if(C==LF) {for(;D=A->next;A=D) if(i=here(mark(D,0),U),i>0) {
- Found.beg=mark(A,A->n); v=1; goto F;} else if(i<0) goto F;}
- else if(B->Case) {for(;A;A=A->next,a=0) for(se=A->s+A->n,s=A->s+a;s<se;s++)
- if( *s == C) if(i=here(mark(A,s-A->s+1),U,word),i>0) {
- Found.beg.c--; v=1; goto F;} else if(i<0) goto F;}
- else {for(;A;A=A->next,a=0) for(se=A->s+A->n,s=A->s+a;s<se;s++)
- if(to_upper(*s)==(byte)C) if(i=here(mark(A,s-A->s+1),U,word),i>0) {
- Found.beg.c--; v=1; goto F;} else if(i<0) goto F;}
- F: bad=oldbad; e->next=E; e->n=en; /* remove pseudo eof */ if(Moan) MOAN(Moan);
- return v;}
- /*----- search backwards for text in region */
- int region::huntb(val T,int word/*=0*/) {if(!T.n) return 0;
- reg char *s,*sb; line*e=end.r,*D,*A,*b=beg.r?:B->text.next; if(!e) return 0;
- int a=end.c,v=0; val U; reg char C; static char X;
- line*E=e->next; e->next=0; int en=e->n; e->n=end.c; /* pseudo eof */
- line*BB=b->prev; b->prev=0; b->s+=beg.c; b->n-=beg.c; /* make a pseudo bof */
- jmp_buf*oldbad=bad,failed; bad=&failed; if(setjmp(*bad)) goto F;
- if(T.magic()) if(T.magic(0)?0:T.s[0]==LF) {for(A=e;A;A=A->prev)
- if(heremagic(mark(A,A->n),T)) {v=1; goto F;} goto F;}
- else {for(A=e;A;A=A->prev) for(a=A->n;a>=0;a--)
- if(heremagic(mark(A,a ),T)) {v=1; goto F;} goto F;}
- C=B->Case?T.s[0]:to_upper(T.s[0]); U=val(T.s+1,T.n-1); if(word) word=2;
- if(C==LF) {for(A=e;D=A,A=A->prev;) if(here(mark(D,0),U,word)>0) {
- Found.beg=mark(A,A->n); v=1; goto F;}}
- else if(B->Case) {for(A=e;A;A=A->prev) for(s=(sb=A->s?:&X)+A->n-1;s>=sb;s--)
- if( *s == C) if(here(mark(A,s-A->s+1),U,word)>0) {
- Found.beg.c--; v=1; goto F;}}
- else {for(A=e;A;A=A->prev) for(s=(sb=A->s?:&X)+A->n-1;s>=sb;s--)
- if(to_upper(*s)==(byte)C) if(here(mark(A,s-A->s+1),U,word)>0) {
- Found.beg.c--; v=1; goto F;}}
- F: bad=oldbad; e->next=E; e->n=en; b->prev=BB; b->s-=beg.c; b->n+=beg.c;
- /* remove pseudo bof & eof */ if(Moan) MOAN(Moan); return v;}
- /*** treats e.g. 'fi' as word 'i' if mark beg is at the i. Similarly huntf. */
- /*-----*/
- void line::de_trailing_space(){mark M(this,n),N(this,n);
- N.skip_white(1,1); (N-M).Delete(0);}
- /*-----*/
- void mark::skip_white(int back/*=0*/,int Eol/*=0*/){
- if(back) while(!c ?(Eol?r->prev!=0:0):mark(r,c-1).is_white()) --*this;
- else while(eol()?(Eol?r->next!=0:0): is_white()) ++*this;}
- /*-----*/
- void mark::find_white(int back/*=0*/){
- if(back) while(!c ?0:!mark(r,c-1).is_white()) --*this;
- else while(eol()?0:! is_white()) ++*this;}
- /*-----*/
- void mark::skip_alnum(int back/*=0*/){
- if(back) while(!c ?0:mark(r,c-1).is_alnum()) --*this;
- else while(eol()?0: is_alnum()) ++*this;}
- /*-----*/
- void mark::find_alnum(int back/*=0*/,int Eol/*=0*/){
- if(back) while(!c ?(Eol?r->prev!=0:0):!mark(r,c-1).is_alnum()) --*this;
- else while(eol()?(Eol?r->next!=0:0):! is_alnum()) ++*this;}
- /*-----*/
- void line::split_if_long(){int nn; line *L=this,*N; mark M;
- B->dotcc=-1;
- AGAIN: nn=L->from_tabexp(abs(B->rmargin)-1,B->rmargin<0); if(L->n<=nn) return;
- M=mark(L,nn); if(M.is_white()) M.skip_white(0); else M.find_white(1);
- if(!M.c) {M.find_white(0); M.skip_white();}
- if(M.c<L->n) {N=M/2; L->de_trailing_space(); L=N; goto AGAIN;}}
- /*----- turn val into Text */
- Text::Text(val&T){line *J,*K,*L; int i=0,j,n=T.n; char *t=T.s;
- K=new line(); J=K; AGAIN: for(j=i;i<n;i++) if(t[i]==LF) break;
- *K=val(t+j,i-j); if(i>=n) goto OUT; L=new line(); *K-*L; K=L; i++; goto AGAIN;
- OUT: beg=J; end=K;}
- /*----- replace text between the marks */
- region region::repl(Text&k,int copy/*=1*/){B->changed=1;
- line *I,*J,*K,*L; mark M,*Z; int x; Text T(k);
- if((I=beg.r)==end.r) {L=beg/0; x=end.c-beg.c;
- forallmarks(Z) if(Z->r==L) Z->c=(Z->c-x)>?0; mark(L,0)<<=x; *L-=x;}
- else {*I=beg.c; J=I->next; K=end.r; L=end/1; K->next=0;
- for(;J;J=J->next) {forallmarks(Z) if(Z->r==J) *Z=beg;}
- K->del(); dustbin=J;}
- if(!T.beg) {*I-*L; --*I; return beg-beg;} if(copy) T=T.copy();
- *I-*T.beg; *T.end-*L; mark(L,0).push(); --*I; -*L; M.pop(); return beg-M;}
- /*----- replace all occurences of Old by New in region */
- void region::replace(val Old,val New,int ask/*=0*/,int word/*=0*/){
- int i,j,k; mark Q;
- if(!Old.n) MOAN("tried to search-&-replace nil");
- static char*notword="object searched for is not a word";
- static char*Z="\
- space = yes, N = no, . = yes & exit, alt-end = no & exit, end = no & stay here";
- if(word>0) {if(Old.magic()) MOAN(notword);
- for(i=0;i<Old.n;i++) if(!isalnum(Old.s[i])) MOAN(notword);}
- int c=' '; B->dot.push(); j=k=0; Q=beg;
- NO: if(Q==end?1:!(Q-=end).huntf(Old,word)) goto OUT; Q=Found.end; j++;
- if(ask) {B->dot=Found.beg; B->dotcc=-1; Found.color(White,Blue);
- B->display(); showmoan();
- X: display(Z,B->lastrow,0,Cyan); Sl[B->lastrow].ok=1;
- switch(c=getkey()) {
- case -end_: Q.pop(); goto EXIT;
- case -alt_end: goto OUT;
- case 'N': case 'n': case -mbutton: case -rbutton: goto NO;
- case ' ': case '.': case -lbutton: goto F;
- default: if(!Breakin()) goto X;}}
- F: end.push(); Q=Found.repl(Text(New)).end; k++; end.pop(); if(c!='.') goto NO;
- OUT: B->dot.pop(); EXIT: B->dotcc=-1; Sl[B->lastrow].ok=0;
- pr(disp,"%1d found, %1d replaced",j,k); Display=disp;}
- /*-----*/
- val&getkeyseq(val P){int z=1,c=1; keyarray *keytable=&keys;
- val *Key; display(P,B->lastrow,0,Blue+8); c=getkey();
- ARRAY: if(c<0) {keyseqc[z++]=0; keyseqc[z++]=-c; Key=&(*keytable)[0][-c];}
- else {keyseqc[z++]=c; Key=&(*keytable)[c];} keyseqc[0]=z;
- if(Key->n!=_keyarray) return *Key;
- if(c){pr(CW,"%v %k (more reqd)",&P,&keyseq); display(CW,B->lastrow,0,Blue+8);}
- c=getkey(); if(c>0) c=to_upper(c); keytable=Key->k; goto ARRAY;}
- /*----- look up val in key table, if bound to a keysequence */
- val&val::keyseq(){int j=1,nn; val*Key; keyarray *kt=&keys; static val X(0,_bad);
- if(n==_char) {if(i&~255) Key=&keys[0][i&255]; else Key=&keys[i&255];
- if(Key->n==_keyarray) {X.s="end of keysequence missing"; return X;}
- return *Key;}
- if(n!=_keyseq?:(nn=s[0])<2) {X.s="subr arg is not a keysequence"; return X;}
- byte C=s[1],D; ARRAY: keyseqc[j++]=C; Key=&(*kt)[C];
- if(Key->n!=_keyarray) {if(j!=nn) {X.s="extra keys after keysequence"; return X;}
- keyseqc[0]=j; return *Key;}
- if(j>=nn) {X.s="end of keysequence missing"; return X;}
- D=C; C=s[j]; if(D) C=to_upper(C); kt=Key->k; goto ARRAY;}
- /*----- ask user for string arg if necessary */
- val val::getifn(val&W,char*prompt,int type/*=0*/,char*start/*=0*/){
- char*w,*zap="magic string not allowed here"; int max; buffer*C; val Z;
- if(n?0:!s) {max=strsize;
- if(start) {max=-max; strcpy(W.s,start); W.n=strlen(W.s);}
- getstring(prompt,B->lastrow,W,max,type); n=W.n; s=W.s;}
- else if(n<0) if(!magic()) MOAN("subroutine arg is not a string");
- if(type!=_magic) if(magic()) MOAN(zap);
- switch(type) {default: return val();
- case _subr:
- if(!n) {strcpy(s=W.s,(Z=subrmenu(prompt),Z.S->name)); n=W.n=strlen(s);}
- else Z=named(*this);
- if(Z.n!=_Subr) if(Z.n!=_macro) MOAN("not subroutine and not macro");
- return Z;
- case _buffer: C=0;
- if(W.n==1) if(W.s[0]=='=') {strcpy(W.s,B->name); W.n=strlen(W.s);}
- w=s; s=W.s;
- if(!n) strcpy(s,(C=buffer_menu(prompt))->name); else fullfilename(s,w);
- W.n=strlen(s); if(attrib(s)==3*256)MOAN("can't find this file's directory");
- if(attrib(s)&16) filefromdirmenu(W,prompt,B->name); n=W.n;
- return C?val(C):val();}}
- /*-----*/
- struct {int n; char*s;} smh[20]={
- {-alt_R, " copy 1st arg of replace command into 2nd arg"},
- {-alt_K, " insert name of key sequence"},
- {-alt_C, " insert the name of the current buffer"},
- {-alt_B, " insert name of buffer which is bound to a key"},
- {-alt_Y, " insert last kill"},
- {-ctrl_tab, " menu of characters"},
- {-del_, " delete a character (forwards if possible)"},
- {-alt_del, " delete a character backwards"},
- {-alt_ret, " insert a carriage-return"},
- {-leftarrow, " move one position left"},
- {-rightarrow, " move one position right"},
- {-home, " move to start of string"},
- {-end_, " move to end of string"},
- {-f1, " switch this char between magic and non-magic"},
- {-f2, " switch previous char between magic and non-magic"},
- {-f3, " start or stop typing magic"},
- {-alt_end, " abort the command that wanted this string arg"},
- {-alt_minus, " help"},
- {-pagedown, " accept string arg as it is"},
- {-2, " exit from this submenu"}}; enum{smhn=20};
- /*-----*/
- void getstring(char*prompt,int sl,val &T,int max,int type/*=0*/){
- #define MoaN(s) ({Moan=s; goto BAD;})
- int c=0,i,j,k=-1,l,M,N=T.n&0x00ffffff,n=0,prmag=0,ptl=strlen(prompt); short*sd;
- int pp,C,Q,I; val*f,g; char *s,*SC; mousestate ms,MS; ms=Jerry;
- extern char*getstringhelp[],*magichelp[]; if(max<0) {max=-max; n=N; k++;}
- if(!T.s) T.s=new char[max]; c_short_addr Sc=Sl[sl].sa; uns short S[strsize+1];
- for(i=0;i<N;i++) S[i]=T.s[i]&255; pp=(gp_Cols-ptl)/2; Jerry.mc=1;
- if(T.magic()) {s=T.s+N+1; for(i=0;i<N;i++) if(bit(s,i)) S[i]|=256;}
- GETCHAR: for(i=0;i<ptl;i++) Sc[i]=sch(prompt[i],Magenta);
- Jerry.range(4,(N+1)<?2048);
- C=n<=2*pp?0:((n-pp/2)/pp)*pp; M=N; X: j=((N-C)<?(gp_Cols-ptl));
- for(i=0;i<j;i++) Sc[i+ptl]=sch(S[C+i]&255,((S[C+i]&256)?Orange+8:Cyan));
- for(i=0;i<j;i++) if(l=S[C+i]-256,l==' '?:l==255?:!l) Sc[i+ptl]=254|(Orange<<12);
- if(C) Sc[ptl]=sch(17,Red); if(N-C>gp_Cols-ptl) Sc[gp_Cols-1]=sch(16,Red);
- gp_clear(sl,ptl+N-C);
- Y: cursor.r=sl; cursor.c=((n-C+ptl)>?0)<?(gp_Cols-1);
- if(k<0) display("(alt-minus for help)",sl,(ScreenCols()-21)>?cursor.c,Green+8);
- if(c!=-mousemove) Jerry.move(0,n); gp_cursor(cursor); c=getkey(); if(!++k) N=0;
- switch(c){ /* special char */
- case -alt_R: if(T.s!=T2w ?: T1t.n<=0 ?: T1t.s!=T1w) break;
- if(max-N<T1t.n) MoaN("text is too long to fit in reply");
- for(i=N-1;i>=n;i--) S[i+T1t.n]=S[i]; for(i=0;i<T1t.n;i++) S[n+i]=T1w[i]&255;
- n+=T1t.n; N+=T1t.n; break;
- case -alt_K: f=&getkeyseq("type key sequence whose name you want to insert: ");
- pr(CW,"%k",&keyseq); g=CW; goto Z;
- case -alt_C: g=B->name; goto Z;
- case -alt_B: f=&getkeyseq("key sequence bound to a buffer:");
- if(f->n!=_buffer) {pr(CW,"%k is not bound to a buffer",&keyseq); MoaN(CW);}
- g=f->b->name; goto Z;
- case -alt_Y: g=killring[nkill].asstring(); goto Z; /* insert last kill */
- Z: if(max-N<g.n) MoaN("too long to fit in reply");
- for(i=N-1;i>=n;i--) S[i+g.n]=S[i]; for(i=0;i<g.n;i++) S[n+i]=g.s[i]&255;
- n+=g.n; N+=g.n; if(c==-alt_Y) delete g.s; break;
- case -del_: if(N) {for(i=n+1;i<N;i++) S[i-1]=S[i]; --N; n<?=N;} break;
- case -alt_del: if(n) {for(i=n--;i<N;i++) S[i-1]=S[i]; --N; n<?=N;} break;
- case -leftarrow: if(n>0) n--; break;
- case -rightarrow: n++; n<?=N; break;
- case -home: n=0; break;
- case -end_: if(!k) N=M; n=N; break; /* if end first, keep the old reply */
- case -f1: if(n<N) S[n]^=256; break;
- case -f2: if(n>0) S[n-1]^=256; break;
- case -f3: prmag=256-prmag; break;
- case -alt_ret: c=CR; goto C;
- case -alt_end: case 0: T.n=0; MoaN("user abort");
- case -alt_minus: sd=screendump();
- for(i=0;SC=getstringhelp[i];i++) display(SC,i,0,Cyan);
- display("(type any key to continue)",0,52,Cyan); getkey();
- screenrestore(sd); sd=screendump();
- for(i=0;SC=magichelp[i];i++) display(SC,i,0,Cyan);
- display("(type any key to continue)",0,52,Cyan); getkey();
- screenrestore(sd); break;
- case -ctrl_tab: i=B->dotcc; j=B->dot.c; c=charfrommenu();
- B->dotcc=i; B->dot.c=j; /* cancel effect of dotty() */ if(c<0)break; goto C;
- case -mousemove: k--; n=Jerry.x; j=C; C=n<=2*pp?0:((n-pp/2)/pp)*pp;
- if(j==C) goto Y; else goto X;
- case -lbuttond: case -mbuttond: case -rbuttond: k--; goto X;
- case -lbutton: if(!k) if(type==_buffer) {S[0]='.'; S[1]=0; N=1; k=0;} /*run on*/
- else break; /* get a dir menu */
- case -pagedown: Sl[sl].ok=0; T.n=N=k?N:M; s=T.s+N+1;
- for(i=0;i<N;i++) T.s[i]=S[i]; for(i=lmagic(N)-1;i>=N;i--) T.s[i]=0; k=0;
- for(i=0;i<N;i++) if(S[i]&256) {s[i>>3]|=(128>>(i&7)); k=1;}
- if(k) T.n=N|0x80000000; Jerry=ms; return; /* exit */
- case -f4: case -rbutton: case -mbutton: {short ss[gp_Cols*gp_Rows]; Q=0; k--;
- c_get(ss,screen,gp_Cols*gp_Rows);
- for(i=0;i<smhn;i++) display(smh[i].s,i,31,Cyan);
- MS=Jerry; Jerry.move(0,0); Jerry.range(smhn,4); i=0; scr(0,31)=1+256*White;
- MENU: I=i; scr(i,31)=1+256*White; if(Q!=-mousemove) Jerry.move(i,0);
- switch(Q=getkey()) {
- case -uparrow: i=(i+smhn-1)%smhn; break;
- case -downarrow: i=(i+1)%smhn; break;
- case -mousemove: i=Jerry.y; break;
- case -mbutton: case -rbutton: i=19;
- case CR: case -lbutton: inject(smh[i].n);
- Jerry=MS; for(i=0;i<smhn;i++) display(" ",i+1,47,White);
- c_put(screen,ss,gp_Cols*gp_Rows); goto Y;}
- if(I!=i) scr(I,31)=' '+256*White; goto MENU;}
- default: if(c&~255) goto GETCHAR; if(c==CR) c=LF; /* so RET makes end-of-line */
- C: if(N<max) {for(i=N++;i>n;i--) S[i]=S[i-1]; S[n++]=(c&255)+prmag;}}
- N>?=n; N<?=max; goto GETCHAR; BAD: Jerry=ms; MOAN(Moan);}
- /*-----*/
- char*getstringhelp[]={
- "[SPECIAL KEYS USED WHEN INPUTTING STRING ARGUMENTS]",
- "printing & control chars & space & newline go into the string argument",
- "alt-B then keysequence bound to buffer, inserts that buffer's name",
- "alt-C inserts the current buffer's name",
- "alt-K then keysequence: inserts that keysequence's name",
- "alt-R in 2nd arg of replace commands, insert 1st arg just entered",
- "alt-Y insert last kill",
- "delete delete this character (at end of string, previous character)",
- "alt_delete delete previous character",
- "\033 go left one character",
- "\032 go right one character",
- "home go to start of string",
- "end go to end of string. If first keypunch, do not lose old string",
- "alt_end abort the command that asked for this string argument",
- "ctrl_tab menu to input a character",
- "F1 change char from normal (cyan) to magic (yellow) or vice-versa",
- "F2 ditto but previous char (usually the character just typed)",
- "F3 start or stop typing chars as magic",
- "F4 get menu of these options",
- "pagedown accept string as it is",
- "alt_ret put a ctrl-M (CR) (carriage-return) into the string as a byte",
- "ret or ctrlJ or ctrlM put a ctrl-J (LF) (end-of-line) into the string",
- 0};
- /*-----*/
- byte esc_alt[26]={alt_A,alt_B,alt_C,alt_D,alt_E,alt_F,alt_G,alt_H,alt_I,
- alt_J,alt_K,alt_L,alt_M,alt_N,alt_O,alt_P,alt_Q,alt_R,alt_S,alt_T,alt_U,
- alt_V,alt_W,alt_X,alt_Y,alt_Z};
- /*-----*/
- void out_of_store(){Moan="out of store"; longjmp(*bad,1);}
- /*----- replace all tabs by spaces in region */
- void region::expandtabs(){B->changed=1;
- reg int i,j,k,m; int b,e,D,E,n,N; mark *M; line *L; reg char*s,*t,c; int *mc;
- for(L=beg.r,b=beg.c;L;L=L->next,b=0) {
- L->la(1); s=L->s; n=L->n; e=L==end.r?end.c:n; m=0;
- for(D=i=0 ;i<b;i++) if(s[i]==9) D=totab(D); else D++;
- for(E=D,i=b;i<e;i++) if(s[i]==9) {E=totab(E); m=1;} else E++;
- if(!m) goto NEXT;
- if(!(j=E-D-e+b)) {for(i=b;i<e;i++) if(s[i]==9) s[i]=' '; goto NEXT;}
- t=new char[roundup(N=n+j)]; mc=new int[n+1];
- for(i=0;i<b;i++) t[mc[i]=i]=s[i];
- for(j=D,m=i=b;i<e;i++) {mc[i]=m; if((c=s[i])==9)
- for(k=j,j=totab(j);k<j;k++) t[m++]=' '; else {j++; t[m++]=c;}}
- for(i=e;i<n;i++) t[mc[i]=m++]=s[i]; mc[n]=m;
- forallmarks(M) if(M->r==L) M->c=mc[M->c];
- delete mc; L->s=t; delete s; L->n=N;
- NEXT: if(L==end.r) return;}}
- /*----- replace spaces by tabs in region */
- void region::maketabs(){B->changed=1;
- reg int i,j,k,m; int b,e,D,E,n,N; mark *M; line *L; reg char*s,*t; int *mc;
- for(L=beg.r,b=beg.c;L;L=L->next,b=0) {
- L->la(1); s=L->s; n=L->n; e=L==end.r?end.c:n; m=0;
- for(D=i=0 ;i<b;i++) if(s[i]==9) D=totab(D); else D++;
- for(E=D,i=b;i<e;i++) if(s[i]==9) E=totab(E); else E++;
- j=E-D-e+b; t=new char[roundup(N=n+j)]; mc=new int[n+1];
- for(j=D,i=b;i<e;i++) {mc[i]=j; if(s[i]==9) j=totab(j); else j++;}
- for(i=b;i<e;i++) if(s[i]==' ') if(!sptotab(mc[i])) s[i]=9;
- for(i=e-1;i>b;i--) if(s[i]==9 ?1: mc[i]==-2) if(s[i-1]==' ') mc[i-1]=-2;
- for(i=b>?1;i<e;i++) if(s[i]==9) if(!sptotab(mc[i])) if(mc[i-1]!=-2)s[i]=' ';
- for(i=0;i<b;i++) t[mc[i]=i]=s[i]; m=b;
- for(i=b;i<e;i++) {k=mc[i]; mc[i]=m; if(k>=0) t[m++]=s[i];}
- for(i=e;i<n;i++) t[mc[i]=m++]=s[i]; mc[n]=m;
- forallmarks(M) if(M->r==L) M->c=mc[M->c]; delete mc;
- L->s=t; delete s; L->n=m; if(L==end.r) return;}}
- /*-----*/
- short*screendump(){int i=ScreenRows()*ScreenCols(); short*s=new short[i];
- c_get(s,screen,i); return s;}
- void screenrestore(short*s){int i=ScreenRows()*ScreenCols();
- c_put(screen,s,i); delete s;}
- /*-----*/
-