/*
    buffer I/O
*/
#include    <stdio.h>
#include    <stdlib.h>
#include    <string.h>

#define TRUE    1
#define FALSE   0
#define ERR     (-1)

#define PTR(p)  (unsigned int)(p)
#define unlink  remove
#define MAX_BUF (16384-24)

#define HEAD_SIZ    (sizeof(BUF_PTR)-MAX_BUF)
#define SEEK_POS(n) ((long)(n)*sizeof(BUF_PTR))

#define WRITE_ERR   1
#define READ_ERR    2

typedef struct _BP {
    struct _BP  *next;
    int         alno;
    int         left;
    int         right;
    int         len;
    int		lfc;
    char        buf[MAX_BUF];
} BUF_PTR;

typedef struct _EP {
    struct _EP  *next;
    int         actno;
    int         actpos;
    long        pos;
    long 	line;
} EDT_PTR;

extern void	wrtstr();
extern int	Input();
extern char     *Tmp_device_name;

EDT_PTR *edt_top=NULL;

static EDT_PTR	*undo_edt=NULL;
static long	undo_sz=0L;
static FILE	*tfp=NULL;
static int	free_no=ERR;
static BUF_PTR	*top_ptr=NULL;
static char	TMPFILE[80];

unsigned long int Freemem()
{
    int     mx;
    unsigned long int sz,fm;
    char    *dmy[128];

    for ( sz = 0x10000,fm = mx = 0 ; mx < 128 ; mx++ ) {
	while ( (dmy[mx] = malloc(sz)) == NULL ) {
	    if ( (sz /= 2) < 0x1000 )
		break;
	}
	if ( dmy[mx] == NULL )
	    break;
	fm += sz;
    }
    while ( mx > 0 )
	free(dmy[--mx]);
    return fm;
}
int     Bfp_init()
{
    int     i;
    BUF_PTR *bp;
    char    *p;
    unsigned long int l;

    l = Freemem() / 2;
    i = l / sizeof(BUF_PTR);

    if ( (bp = (BUF_PTR *)malloc(sizeof(BUF_PTR)*i)) == NULL )
	return ERR;

    top_ptr = bp;
    for ( i-- ; i > 0 ; i-- ) {
        bp->next = bp + 1;
        bp->alno = ERR;
        bp++;
    }
    bp->next = NULL;
    bp->alno = ERR;

    if ( (p = Tmp_device_name) != NULL ||
         (p = getenv("TMP")) != NULL )
	sprintf(TMPFILE,"%s\\",p);
    else
        TMPFILE[0] = '\0';
    strcat(TMPFILE,"WINK.TMP");

    New_link();
    undo_edt = edt_top;
    New_link();

    return FALSE;
}
void    Bfp_end()
{
    if ( tfp != NULL ) {
	fclose(tfp);
	unlink(TMPFILE);
    }
}
static void    retry(no)
int	no;
{
    static char *msg[]={
	"Save Temp Seek Error        ",
	"Save Temp Write Header Error",
	"Save Temp Write Buffer Error",
	"Load Temp Seek Error        ",
	"Load Temp Read Header Error ",
	"Save Temp Read Buffer Error ",
	"New Temp File Can't open    ",
	"New Temp Write Error        ",
	"Temp File Can't open        "
    };
    int     i;
    FILE    *fp;
    char    tmp[80];
    char    buf[512];

RETRY:
    do {
	wrtstr(msg[no],30,1,0x12);
	tmp[0] = '\0';
        while ( (short int)Input(tmp,"ÃÝÎß×ØÄÞ×²ÌÞ–¼ ") != FALSE );
	if ( strcmp(tmp,"EXIT") == 0 )
	    exit(10);
	strcat(tmp,"\\WINK.TMP");
	wrtstr("                            ",30,1,0x1F);
	no = 6;
    } while ( (fp = fopen(tmp,"w+b")) == NULL );

    if ( tfp != NULL ) {
	fseek(tfp,0L,SEEK_SET);
	while ( (i = fread(buf,1,512,tfp)) > 0 )
	    fwrite(buf,1,i,fp);
    }

    if ( ferror(fp) ) {
	fclose(fp);
	unlink(tmp);
	no = 7;
	goto RETRY;
    }

    if ( tfp != NULL ) {
	fclose(tfp);
	unlink(TMPFILE);
    }

    tfp = fp;
    strcpy(TMPFILE,tmp);
}
static void    save_buf(bp)
register BUF_PTR *bp;
{
    if ( tfp == NULL && (tfp = fopen(TMPFILE,"w+b")) == NULL ) {
	retry(8);
	goto RETRY;
    }
RETRY:
    if ( fseek(tfp,SEEK_POS(bp->alno),SEEK_SET) ) {
	retry(0);
	goto RETRY;
    }
    if ( fwrite((char *)bp,HEAD_SIZ,1,tfp) < 1 ) {
	retry(1);
	goto RETRY;
    }
    if ( bp->len > 0 && fwrite(bp->buf,1,bp->len,tfp) < bp->len ) {
	retry(2);
	goto RETRY;
    }
}
static void    load_buf(no,bp)
int     no;
register BUF_PTR *bp;
{
    BUF_PTR *tp;

    if ( tfp == NULL && (tfp = fopen(TMPFILE,"w+b")) == NULL ) {
	retry(8);
	goto RETRY;
    }
RETRY:
    if ( fseek(tfp,SEEK_POS(no),SEEK_SET) ) {
	retry(3);
	goto RETRY;
    }
    tp = bp->next;
    if ( fread((char *)bp,HEAD_SIZ,1,tfp) < 1 ) {
	bp->next = tp;
	retry(4);
	goto RETRY;
    }
    bp->next = tp;
    if ( bp->len > 0 && fread(bp->buf,1,bp->len,tfp) < bp->len ) {
	retry(5);
	goto RETRY;
    }
}
static BUF_PTR *get_buf(no)
int     no;
{
    BUF_PTR *tp;
    register BUF_PTR *bp;

    bp = top_ptr;
    while ( bp->alno != no ) {
	if ( bp->next == NULL ) {
            if ( bp->alno != ERR )
                save_buf(bp);
            if ( no != ERR )
                load_buf(no,bp);
            break;
	}
	tp = bp;
	bp = bp->next;
    }
    if ( bp != top_ptr ) {
	tp->next = bp->next;
	bp->next = top_ptr;
	top_ptr = bp;
    }
    return bp;
}
static int     xalloc()
{
    static int   alno=0;
    register BUF_PTR *bp;

    if ( free_no != ERR ) {
        bp = get_buf(free_no);
        free_no = bp->left;
    } else {
	bp = get_buf(ERR);
	bp->alno = alno++;
    }
    bp->left = bp->right = ERR;
    bp->len = 0;
    bp->lfc = ERR;
    return bp->alno;
}
static void    xfree(no)
int     no;
{
    register BUF_PTR *bp;

    bp = get_buf(no);
    bp->left = free_no;
    free_no = no;
    bp->len = 0;
}
static int     link_buf(no)
int     no;
{
    int    new;
    BUF_PTR *tp;
    BUF_PTR *bp;

    new = xalloc();
    tp = get_buf(no);                   /* 1 */
    bp = get_buf(new);                  /* 2 */

    bp->left = no;
    bp->right = tp->right;
    tp->right = new;

    if ( bp->right != ERR ) {
        tp = get_buf(bp->right);        /* 3 */
        tp->left = new;
    }
    return new;
}
static void    del_buf(no)
int     no;
{
    BUF_PTR *tp;
    BUF_PTR *bp;

    bp = get_buf(no);
    if ( bp->left != ERR ) {
        tp = get_buf(bp->left);
        tp->right = bp->right;
    }
    if ( bp->right != ERR ) {
        tp = get_buf(bp->right);
        tp->left = bp->left;
    }
    xfree(no);
}
static int     line_count(bp)
register BUF_PTR *bp;
{
    int     i,n;
    register char *p;

    if ( bp->lfc == ERR ) {
        p = bp->buf;
        n = 0;
        for ( i = bp->len ; i > 0 ; i-- ) {
            if ( *(p++) == '\n' )
                n++;
        }
        bp->lfc = n;
    }
    return bp->lfc;
}
static char    *chk_ptr(ep,pos)
register EDT_PTR *ep;
long	pos;
{
    BUF_PTR *bp;

    bp = get_buf(ep->actno);
    while ( (ep->pos + bp->len) < pos ) {
        if ( bp->right == ERR )
            return &(bp->buf[bp->len]);
        ep->actno = bp->right;
        ep->pos += bp->len;
        ep->line += line_count(bp);
        bp = get_buf(ep->actno);
    }
    while ( ep->pos > pos ) {
	if ( bp->left == ERR )
	    return bp->buf;
	ep->actno = bp->left;
	bp = get_buf(ep->actno);
	ep->pos -= bp->len;
	ep->line -= line_count(bp);
    }
    return &(bp->buf[(int)(pos - ep->pos)]);
}
static long	get_line(ep,line)
register EDT_PTR *ep;
int	*line;
{
    int     t;
    int     n,i;
    char    *p;
    BUF_PTR *bp;

    bp = get_buf(ep->actno);
    while ( (ep->line + line_count(bp)) < *line ) {
        if ( bp->right == ERR ) {
	    *line = ep->line + line_count(bp);
	    break;
	}
        ep->actno = bp->right;
        ep->pos += bp->len;
        ep->line += line_count(bp);
        bp = get_buf(ep->actno);
    }
    while ( ep->line >= *line ) {
	if ( bp->left == ERR ) {
	    *line = ep->line;
	    break;
	}
	ep->actno = bp->left;
	bp = get_buf(ep->actno);
	ep->pos -= bp->len;
	ep->line -= line_count(bp);
    }

    p = bp->buf;
    t = n = *line - ep->line;
    i = 0;
    while ( n > 0 && i < bp->len ) {
	if ( *(p++) == '\n' )
	    n--;
	i++;
    }
    *line -= n;

    return (ep->pos + (long)i);
}
char    *Cnv_ptr(pos)
long	pos;
{
    register EDT_PTR *ep;
    register BUF_PTR *bp;

    ep = edt_top;
    bp = get_buf(ep->actno);
    while ( (ep->pos + bp->len) <= pos ) {
        if ( bp->right == ERR )
            return &(bp->buf[bp->len]);
        ep->actno = bp->right;
        ep->pos += bp->len;
        ep->line += line_count(bp);
        bp = get_buf(ep->actno);
    }
    while ( ep->pos > pos ) {
	if ( bp->left == ERR )
	    return bp->buf;
	ep->actno = bp->left;
	bp = get_buf(ep->actno);
	ep->pos -= bp->len;
	ep->line -= line_count(bp);
    }
    return &(bp->buf[(int)(pos - ep->pos)]);
}
static int     ins_str(ep,pos,str,n)
EDT_PTR *ep;
long	pos;
char    *str;
int     n;
{
    int     tn,i,len;
    BUF_PTR *tp;
    BUF_PTR *bp;
    char    *p;
    static char tmp[MAX_BUF];

    p = chk_ptr(ep,pos);
    bp = get_buf(ep->actno);
    if ( (tn = &bp->buf[bp->len] - p) > 0 ) {
	memcpy(tmp,p,tn);
	bp->len -= tn;
    }

    len = n + tn;
    for ( ; len > 0 ; ) {
	if ( bp->len >= MAX_BUF ) {
	    bp = get_buf(link_buf(bp->alno));
	    p = bp->buf;
        }

	if ( n == 0 ) {
	    n = tn;
	    str = tmp;
        }

	i = PTR(&(bp->buf[MAX_BUF])) - PTR(p);
	if ( i > n ) i = n;

	memcpy(p,str,i);
	p += i;
	str += i;
	n -= i;
	len -= i;
	bp->len += i;
	bp->lfc = ERR;
    }

    if ( bp->right != ERR ) {
        tp = get_buf(bp->right);
	if ( (bp->len + tp->len) < MAX_BUF ) {
            memcpy(&bp->buf[bp->len],tp->buf,tp->len);
            bp->len += tp->len;
            bp->lfc = ERR;
            del_buf(bp->right);
        }
    }

    return FALSE;
}
static int     del_str(ep,pos,len)
EDT_PTR *ep;
long	pos;
int     len;
{
    int     i,n;
    BUF_PTR *tp;
    BUF_PTR *bp;
    char    *p;

    p = chk_ptr(ep,pos);
    bp = get_buf(ep->actno);

    for ( ; len > 0 ; ) {
	n = &bp->buf[bp->len] - p;
	if ( n <= len ) {
            bp->len -= n;
            len -= n;
	} else {
	    memcpy(p,p+len,n-(int)len);
	    bp->len -= len;
	    len = 0;
        }
	bp->lfc = ERR;
	if ( bp->len == 0 && bp->right != ERR ) {
            if ( (i = bp->alno) == ep->actno )
                ep->actno = bp->right;
            bp = get_buf(bp->right);
            del_buf(i);
	} else if ( len > 0 && bp->right != ERR ) {
	    bp = get_buf(bp->right);
        } else
	    break;
        p = bp->buf;
    }

    if ( bp->right != ERR ) {
	tp = get_buf(bp->right);
	if ( (bp->len + tp->len) < MAX_BUF ) {
	    memcpy(&bp->buf[bp->len],tp->buf,tp->len);
	    bp->len += tp->len;
	    bp->lfc = ERR;
	    del_buf(bp->right);
        }
    }

    return FALSE;
}
static int     cut_str(ep,pos,str,n)
EDT_PTR *ep;
long	pos;
char    *str;
int     n;
{
    int     i,len;
    BUF_PTR *bp;
    char    *p;

    p = chk_ptr(ep,pos);
    bp = get_buf(ep->actno);

    for ( len = 0 ; n > 0 ; ) {
        i = &bp->buf[bp->len] - p;
        if ( i > n ) i = n;
        memcpy(str,p,i);
        str += i;
        n -= i;
        len += i;
        if ( n > 0 ) {
	    if ( bp->right == ERR )
		break;
	    bp = get_buf(bp->right);
            p = bp->buf;
        }
    }

    return len;
}
long	Line_ptr(n)
int	*n;
{
    return get_line(edt_top,n);
}
void    Ins_buf(ptr,sz,str)
long	ptr;
int     sz;
char    *str;
{
    ins_str(edt_top,ptr,str,sz);
}
void    Del_buf(ptr,sz)
long	ptr;
long	sz;
{
    int     n;

    while ( sz > 0 ) {
	n = (sz > 16384 ? 16384:sz);
	del_str(edt_top,ptr,n);
	sz -= n;
    }
}
void    Cut_buf(str,sz,ptr)
char    *str;
int     sz;
long	ptr;
{
    cut_str(edt_top,ptr,str,sz);
}
int	New_link()
{
    EDT_PTR *ep;

    if ( (ep = (EDT_PTR *)malloc(sizeof(EDT_PTR))) == NULL )
        return ERR;

    ep->next = edt_top;
    edt_top = ep;

    ep->actno = xalloc();
    ep->pos = 0L;
    ep->line = 0;

    return FALSE;
}
void    Kill_link(ep)
EDT_PTR	*ep;
{
    int     no;
    BUF_PTR *bp;

    no = ep->actno;
    for ( ; ; ) {
	bp = get_buf(no);
	if ( bp->left == ERR )
	    break;
	no = bp->left;
    }
    while ( no != ERR ) {
	bp = get_buf(no);
	no = bp->right;
	xfree(bp->alno);
    }
    free(ep);
}
long	Nxt_line(pos)
long	pos;
{
    int     i;
    char    *p;
    register EDT_PTR *ep;
    register BUF_PTR *bp;
    extern long btm_ptr;

    p = Cnv_ptr(pos);
    ep = edt_top;
    bp = get_buf(ep->actno);
    do {
        while ( p >= &(bp->buf[bp->len]) ) {
            if ( bp->right == ERR ) {
		p = &(bp->buf[bp->len]);
		goto ENDOF;
	    }
	    i = PTR(p) - PTR(&(bp->buf[bp->len]));
            ep->actno = bp->right;
            ep->pos += bp->len;
            ep->line += line_count(bp);
            bp = get_buf(ep->actno);
	    p = &(bp->buf[i]);
        }
    } while ( *(p++) != '\n' );
ENDOF:
    pos = (ep->pos + (long)(PTR(p) - PTR(bp->buf)));
    return (pos > btm_ptr ? btm_ptr : pos);
}
void	Undo_put(ptr,sz)
long	ptr,sz;
{
    int     n;
    char    tmp[256];

    while ( undo_sz > 0 ) {
	n = (undo_sz > 16384 ? 16384:undo_sz);
	del_str(undo_edt,0L,n);
	undo_sz -= n;
    }
    while ( sz > 0 ) {
	n = (sz > 256 ? 256:sz);
	cut_str(edt_top,ptr,tmp,n);
	ins_str(undo_edt,undo_sz,tmp,n);
	sz -= n;
	ptr += n;
	undo_sz += n;
    }
}
long	Undo_get(ptr)
long	ptr;
{
    int     n;
    long    sz,pos;
    char    tmp[256];

    sz = undo_sz;
    pos = 0L;
    while ( sz > 0 ) {
	n = (sz > 256 ? 256:sz);
	cut_str(undo_edt,pos,tmp,n);
	ins_str(edt_top,ptr,tmp,n);
	sz -= n;
	pos += n;
	ptr += n;
    }
    return undo_sz;
}
int	Undo_cpy(buf,sz,pos)
char	*buf;
int	sz;
long	pos;
{
    return cut_str(undo_edt,pos,buf,sz);
}
