Jul 16

    DOS下虽然有edit编辑器和更加原始的EDLIN行编辑器,但近几天我把《THE CRAFT OF C》中关于Screen-Editor的代码全部输机,想自己再弄出个新的文本编辑器。

代码如下:(在turboc下成功编译通过,但无法正常运行)

/* A Screen Editor Subsystem*/

#define TURBOC

#include <stdio.h>
#include <dos.h>
#include <string.h>
#include <bios.h>
#include <conio.h>

#define BUF_SIZE 32000
#define LINE_LEN 79
#define MAX_LINES 24
#define KILL_BUF_SIZE 4*LINE_LEN
char buf[BUF_SIZE];
char *curloc,*endloc;
int scrnx,scrny;
char killbuf[KILL_BUF_SIZE];

char *helpline=
    "F1:save  F2:load  F3:find  F4:replace\
    ^K:Kill line  ^Y:Yank  ^Z:Quit ";

void edit(char *fname),help();
void gotoxy(int x,int y),clrline(int y);
void edit_clr_eol(int x,int y),clrscr();
void printline(char *p),delete_char(void);
void search(),kill_line();
void upline();
void downline(),left(),right();
void scrolldn (int x,int y);
void scrollup(int topx,int topy,int endx,int endy);
void display_scrn(int x,int y,char *p);
void pagedown(),pageup(),replace();
void home(),gotoend(),yank();
int load(char *fname),save(char *fname);
void edit_gets(char *str);

main(int argc,char *argv[])
{
    if (argc==2) edit(argv[1]);
    clrscr();
    return 0;
}

/*  This is the editor's entry function and contains
    its main loop.

    Call it with the name of the file you want to edit.
*/

void edit(char *fname)
{
    union k{
        char ch[2];
        unsigned i;
    }key;
    char name[80];

    /*try to load the file*/
    if(!load(fname)) curloc=endloc=buf;
    strcpy(name,fname);

    clrscr();

    scrnx=scrny=0;

    display_scrn(0,0,curloc);
    help();
    gotoxy(0,0);

    /*Editor main loop*/
    do{
        key.i=bioskey(0);
        if(!key.ch[0]){
            switch(key.ch[1]){
                case 59: /*F1 save*/
                    save (name);
                    break;
                case 60: /*F2 load*/
                    clrline(MAX_LINES);
                    gotoxy(0,MAX_LINES);
                    printf ("Enter filename:");
                    edit_gets(name);
                    if (*name) load (name);
                    help();
                    display_scrn(0,0,curloc);
                    scrnx=scrny=0;
                    break;
                case 61: /*F3 search*/
                    search();
                    break;
                case 62: /*F4 relace*/
                    replace();
                    break;
                case 71: /*home*/
                    home();
                    break;
                case 79: /*end*/
                    gotoend();
                    break;
                case 75: /* left*/
                    left();
                    break;
                case 77: /*right*/
                    right();
                    break;
                case 72: /*up*/
                    upline();
                    break;
                case 80: /*down*/
                    downline();
                    break;
                case 73: /*page up*/
                    pageup();
                    break;
                case 81: /*page down*/
                    pagedown();
                    break;
                case 83: /*del*/
                    if(curloc<endloc) delete_char();
                    break;
            }
	    if(curloc<buf){
		scrnx=scrny=0;
                curloc=buf;
            }
            gotoxy(scrnx,scrny);
        }
        else{   /* enter keystroke into file*/
            switch(key.ch[0]){
                case '\r': /* Carriage return*/
                    /*see if buffer is full*/
                    if(endloc==buf+BUF_SIZE-2) break;

                    /* move contents of file below current
                       location down one byte to make room
                       for the RETURN
                    */
                    memmove(curloc+1,curloc,endloc-curloc+1);

                    *curloc=key.ch[0];
                    curloc++;

                    /*clear rest of line*/
                    edit_clr_eol(scrnx,scrny);
                    scrnx=0;
                    scrny++;

                    /*move text on the screen down*/
                    if (scrny==MAX_LINES){
                        scrny=MAX_LINES-1;
                        scrollup(0,0,LINE_LEN,scrny);
                    }
                    else scrolldn(scrnx,scrny);

                    gotoxy(scrnx,scrny);
                    printline(curloc);
                    endloc++;
                    break;

                case '\b': /*backspace*/
                    if(curloc==buf) break;
                    left ();
                    delete_char();
                    break;

                case 11: /*C-K:kiil line*/
                    kill_line();
                    break;
		case 25: /*C-Y:yank kill buffer*/
                    yank();
                    break;
                default:
                    if(endloc==buf+BUF_SIZE-2) break;

                    /*can't type past end of line*/
                    if(scrnx==LINE_LEN) break;
                    memmove(curloc+1,curloc,endloc-curloc+1);
                    *curloc=key.ch[0];
                    putch(*curloc);
                    scrnx++;
                    gotoxy(scrnx,scrny);
                    printline(curloc+1);
                    curloc++;
                    endloc++;
            }
            gotoxy(scrnx,scrny);
        }
    }while(key.ch[0]!=26);
}


/* Display a line pointed to by P.*/
void printline(register char *p)
{
    register int i;
    i=scrnx;
    while(*p!='\r'&& *p && i<LINE_LEN){
        putch(*p);
        p++;
        i++;
    }
}

/*Insert previously killed line*/
void yank()
{
    char *p;

    p=killbuf;
    while(*p){
        memmove(curloc+1,curloc,endloc-curloc+1);
        *curloc=*p;
        if(scrnx<LINE_LEN){
            putch(*curloc);
            scrnx++;
        }
        curloc++;
        endloc++;
        p++;
    }
    printline (curloc);
}

/*Delete the line at the current location*/
void kill_line()
{
    register int i;
    char *p,*killbufptr;
    if(*curloc=='\r'){
        delete_char();
        return;
    }

    edit_clr_eol(scrnx,scrny);

    /*find out how many characters are in the line*/
    p=curloc;
    i=0;
    killbufptr=killbuf;
    while(*p!='\r' && p<endloc){
        i++;
        *killbufptr=*p;
        p++;
        if(killbufptr<killbuf+KILL_BUF_SIZE-2) killbufptr++;
    }
    *killbufptr='\0';

    /*remove the line*/
    memmove(curloc,curloc+1,endloc-curloc);
    endloc-=i;
}


/*Global search and replace*/
void replace()
{
    register int len1;
    char str1[80],str2[80];
    char *p,*p2;

    clrline(MAX_LINES);
    gotoxy(0,MAX_LINES);
    printf("enter string to replace: ");
    edit_gets(str1);

    clrline(MAX_LINES);
    gotoxy(0,MAX_LINES);
    printf ("enter replacement: ");
    edit_gets(str2);

    p=curloc;
    len1=strlen(str1);

    while(*str1){
        /*search for a string*/
	while(*p && strncmp(str1,p,len1)) p++;
        if (!p) break;

        /*remove old string*/
        memmove(p,p+len1,endloc-p);
        endloc-=len1;

        /*insert new string*/
        p2=str2;
        while(*p2){
            memmove(p+1,p,endloc-p+1);
            *p=*p2;
            p++;
            endloc++;
            p2++;
        }
    }
    clrscr();

    /*find location of top of screen*/
    p=curloc;
    for(len1=scrny;len1>=0&&p>buf;){
        p--;
        if(*p=='\r') len1--;
    }
    if(*p=='\r') p++;

    /*redisplay current screen*/
    display_scrn(0,0,p);
    help();
}


/*Delete character at the current location.*/
void delete_char()
{
    gotoxy(scrnx,scrny);
    if(*curloc=='\r'){
        scrollup(0,scrny+1,LINE_LEN,MAX_LINES-1);
        memmove(curloc,curloc+1,endloc-curloc);
        endloc--;
        display_scrn(scrnx,scrny,curloc);
        help();
    }
    else{
        memmove(curloc,curloc+1,endloc-curloc);
        endloc--;
	printline(curloc); printf(" ");
    }
}


/*Display help line*/
void help()
{
    gotoxy(0,MAX_LINES);
    printf(helpline);
}

/*Move current location left*/
void left()
{
    if(curloc==buf) return;
    scrnx--;
    if(scrnx<0){
        scrnx=0;
        upline();
        /*find end of line*/
        while(*curloc!='\r'){
            curloc++;
            scrnx++;
        }
    }
    else curloc--;
}


/*Move currrent position right*/
void right()
{
    /*can't move rihgt*/
    if(curloc+1>endloc) return;

    scrnx++;

    /*if at end of line,go to next one*/
    if(scrnx>LINE_LEN||*curloc=='\r'){
        scrnx=0;
        scrny++;
        if(scrny==MAX_LINES){
            scrny=MAX_LINES-1;
            downline();

            /*move cursor and current loc to start
             of new line*/
            curloc--;
            while(*curloc!='\r') curloc--;
            curloc++;
            scrnx=0;
        }
        else curloc++;
    }
    else curloc++;
}

/*Find a string*/
void search()
{
    char str[80];
    register char *p;
    int len,i;

    clrline(MAX_LINES);
    gotoxy(0,MAX_LINES);
    printf("search string: ");
    edit_gets(str);
    if(!*str) return;

    p=curloc;
    len=strlen(str);

    /*search for the string*/
    while(*p && strncmp(str,p,len)) p++;
    if(!*p) return;

    /*back up to start of line*/
    i=0;
    while(p>buf&&*p!='\r'){
        p--;
        i++;
    }
    p++;
    i--;

    /*reposition current location to start of match*/
    curloc=p+i;
    scrnx=i;
    scrny=0;

    /*diplay screenof text at location of match*/
    clrscr();
    display_scrn(0,0,p);
    help();
}

/*Move up one line.*/
void upline()
{
    register int i;
    char *p;
    if(curloc==buf) return;

    p=curloc;

    if(*p=='\r') p--;

    /*back up loacator to start of current line.*/
    for(;*p!='\r'&&p>buf;p--);
    if(*p!='\r') return;
    curloc=p;
    curloc--;
    i=scrnx;

    /*find start of next line*/
    while(*curloc!='\r' && curloc>=buf) curloc--;
    scrny--;scrnx=0;
    curloc++;

    /*at top of screen,must scroll up*/
    if(scrny<0){
        scrolldn(0,0);
        scrny=0;
        gotoxy(0,0);
        printline(curloc);
    }

    /* position the cursor and current location at
     same scrnx position as previous line if possible*/
    while(i&& *curloc!='\r'){
        curloc++;
        scrnx++;
        i--;
    }
}


/* Move down one line. */
void downline()
{
    register int i;
    char *p;
    i=scrnx;
    p=curloc;

    /* advance current location to start to next line*/
    while(*p!='\r' && p<endloc) p++;
    if(p==endloc) return;
    p++;
    curloc=p;
    scrny++;scrnx=0;

    /*if moving down past the bottom of the screen*/
    if(scrny==MAX_LINES){
        scrny=MAX_LINES-1;
        scrollup(0,0,LINE_LEN,MAX_LINES-1);
        gotoxy(scrnx,scrny);
        printline(curloc);
    }

    /* advance to corresponding character in next line*/
    while(i && *curloc!='\r' && curloc<endloc){
        curloc++;
        scrnx++;
        i--;
    }
}


/* Display a screen full of text(up to 24lines)
   starting at the specified location.
*/
void display_scrn(int x,int y,char *p)
{
    register int i;
    gotoxy(x,y);

    i=0;
    while(y<MAX_LINES && *p){
        switch(*p){
            case '\r':printf("\n");
                y++;
                i=0;
                break;
            default:if(i<LINE_LEN) putch(*p);
                i++;
        }
        p++;
    }
}


/* Page down MAX_LINES lines.*/
void pagedown()
{
    register int i;

    clrscr();

    /*count down MAX_LINES lines.*/
    for(i=0;i<MAX_LINES && curloc<endloc;){
        if (*curloc=='\r') i++;
        curloc++;
    }
    help();
    scrnx=0;scrny=0;
    display_scrn(0,0,curloc);
}

/* Page up MAX_LINES lines*/
void pageup()
{
    register int i;

    clrscr();
    if (*curloc=='\r' && curloc>buf) curloc--;

    /* go back MAX_LINES in buffer*/
    for(i=0;i<MAX_LINES+1 && curloc>buf;){
        if(*curloc=='\r') i++;
        curloc--;
    }
    if(i==MAX_LINES+1) curloc+=2;

    help();
    scrnx=0;scrny=0;
    display_scrn(0,0,curloc);
}


/* Go to the top of the file.*/
void home()
{
    clrscr();
    curloc=buf;
    scrnx=scrny=0;
    display_scrn(0,0,curloc);
    help();
}

/*  Go to the end of the file. */
void gotoend()
{
    clrscr();
    curloc=endloc;
    pageup();
}

/* Load a file.*/
load(char *fname)
{
    FILE *fp;
    char ch,*p;

    if((fp=fopen(fname,"rb"))==NULL)
        return 0;

    p=buf;
    while(!feof(fp) && p!=buf+BUF_SIZE-2){
        ch=getc(fp);
        if(ch!='\n' && ch!=EOF){
	    *p=ch;
            p++;
        }
    }
    *p='\0';
    fclose (fp);
    curloc=buf;
    endloc=p;
    return 1;
}

/* Save a file.*/
save(char *fname)
{
    FILE *fp;
    char *p,name[80];

    if(!*fname){
        printf("filename: ");
        gets(name);
    }
    else strcpy(name,fname);

    if((fp=fopen(name,"wb"))==NULL)
        return 0;

    p=buf;
    while(p!=endloc){
        if(*p!='\r')
            putc(*p,fp);
        else{
            putc('\r',fp);
            putc('\n',fp);
        }
        p++;
    }
    fclose(fp);
    return 1;
}


/* Read a string from the keyboard,but do not
   scroll the display when a RETURN is entered.
*/
void edit_gets(char *str)
{
    char *p;

    p=str;

    for(;;){
        *str=getch();
        if(*str=='\r'){
            *str='\0';
            return;
        }
        if(*str=='\b'){
            if(str>p){
		str--;
		putch('\b');
		putch(' ');
		putch('\b');
            }
        }
        else{
            putch(*str);
            str++;
        }
    }
}


/* Read and save cursor coordinates.*/
void cursor_pos()
{
    union REGS i,o;
    i.h.bh=0;
    i.h.ah=3;
    int86(16,&i,&o);
}

/* Send cursor to specified X,Y (0,0 is
   Upper left corner.
*/
void gotoxy(int x,int y)
{
    union REGS i;

    i.h.dh=y;
    i.h.dl=x;
    i.h.ah=2;
    i.h.bh=0;
    int86(16,&i,&i);
}

/* Clear entire line given its Y coordinate.*/
void clrline(int y)
{
    register int i;

    gotoxy(0,y);
    for(i=0;i<LINE_LEN;i++) putch(' ');
}

/* Clear to end of specified line.*/
void edit_clr_eol(int x,int y)
{
    char *p;

    p=curloc;
    gotoxy(x,y);
    for(;x<LINE_LEN && *p!='\r' && *p;x++,p++){
        printf(" ");
    }
}

/* Clear the screen.*/
void clrscr()
{
    union REGS r;
    r.h.ah=6;
    r.h.al=0;
    r.h.ch=0;
    r.h.cl=0;
    r.h.dh=MAX_LINES;
    r.h.dl=LINE_LEN;
    r.h.bh=7;
    int86(0x10,&r,&r);
}

/*Scroll down the screen.*/
void scrolldn(int x,int y)
{
    union REGS r;

    r.h.ah=7;
    r.h.al=1;
    r.h.ch=y;
    r.h.cl=x;
    r.h.dh=MAX_LINES-1;
    r.h.dl=LINE_LEN;
    r.h.bh=7;
    int86(0x10,&r,&r);
}

/* Scroll up the screen using the specified coordinate.*/
void scrollup(int topx,int topy,int endx,int endy)
{
    union REGS r;

    r.h.ah=6;
    r.h.al=1;
    r.h.ch=topy;
    r.h.cl=topx;
    r.h.dh=endy;
    r.h.dl=endx;
    r.h.bh=7;
    int86(0x10,&r,&r);
}