/*********************************

    buffer I/O

**********************************/
#include    <stdio.h>
#include    <stdlib.h>
#include    <string.h>
#include    <ctype.h>

#define TRUE    1
#define FALSE   0
#define ERR     (-1)

#define unlink  remove
#define	TMPFILE	"WHISTMP.$$$"

#define LIN_SIZ	82
#define	BLK_SIZ		16384
#define BUF_SIZ		181			/* 90 * 90 = 16290 */
#define	BUF_LIN(n)	(n*BUF_SIZ)
#define	LIN_BUF(n)	(n/BUF_SIZ)
#define	LIN_MASK(n)	(n%BUF_SIZ)
#define SEEK_POS(n)	((long)(n)*BLK_SIZ)

int	creato(char *file,int mode,int attr);
int	read(int f,void *buf,int sz);
int	write(int f,void *buf,int sz);
long	lseek(int f,long pos,int sk);

typedef struct {
    int		left;
    int		right;
    char	lin[LIN_SIZ];
} LIN_PTR;

typedef struct _BP {
    struct _BP  *next;
    int         alno;
    LIN_PTR	buf[BUF_SIZ];
} BUF_PTR;

static int	tfp=ERR;
static int	free_no=ERR;
static BUF_PTR	*top_buf=NULL;
static int	tmp_drive_no=0;
static char	tmp_file_name[80];

char	*macget(char *mac);

unsigned long int Freemem(void)
{
    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     BUF_init(char *drv)
{
    int     i;
    BUF_PTR *bp;
    unsigned long int l;
    char    tmp[80];

    l = Freemem() / 3;
    i = l / sizeof(BUF_PTR);
    macvalset("FREEMEM",l);

    if ( (bp = (BUF_PTR *)malloc(sizeof(BUF_PTR)*i)) == NULL )
	return ERR;

    top_buf = bp;
    for ( i-- ; i > 0 ; i-- ) {
        bp->next = bp + 1;
        bp->alno = ERR;
        bp++;
    }
    bp->next = NULL;
    bp->alno = ERR;

    if ( drv == NULL ) {
	getdir(tmp);
	drv = tmp;
    }
    tmp_drive_no = toupper(drv[0]) - 'A';
    sprintf(tmp_file_name,"%c:\\%s",tmp_drive_no+'A',TMPFILE);

    return FALSE;
}
void    BUF_end(void)
{
    if ( tfp != ERR ) {
	close(tfp);
	unlink(tmp_file_name);
    }
}
static void BUF_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,rt=0;
    int     fp;
    char    tmp[80];
    char    buf[512];

RETRY:

    do {
	macset("ERROR",msg[no]);
	sprintf(tmp,"%c:",tmp_drive_no+'A');
	macset("TMP",tmp);
	MSG_wind("RETRY");
	strcpy(buf,macget("DRIVE"));
	tmp_drive_no = toupper(buf[0]) - 'A';
	sprintf(tmp,"%c:\\%s",tmp_drive_no+'A',TMPFILE);
    } while ( (fp = creato(tmp,2,0)) == ERR );

    if ( tfp != ERR ) {
	lseek(tfp,0L,0);
	while ( (i = read(tfp,buf,512)) > 0 ) {
	    if ( write(fp,buf,i) < 0 )
		break;
	}
	if ( i < 0 ) {
	    close(fp);
	    unlink(tmp);
	    no = 7;
	    goto RETRY;
	}
	close(tfp);
	unlink(tmp_file_name);
    }

    tfp = fp;
    strcpy(tmp_file_name,tmp);
}
static void    BUF_save(bp)
register BUF_PTR *bp;
{
    if ( tfp == ERR && (tfp = creato(tmp_file_name,2,0)) == ERR ) {
	BUF_retry(8);
	goto RETRY;
    }
RETRY:
    if ( lseek(tfp,SEEK_POS(bp->alno),0) < 0 ) {
	BUF_retry(0);
	goto RETRY;
    }
    if ( write(tfp,(char *)bp,sizeof(BUF_PTR)) != sizeof(BUF_PTR) ) {
	BUF_retry(1);
	goto RETRY;
    }
}
static void BUF_load(no,bp)
int     no;
register BUF_PTR *bp;
{
    BUF_PTR *tp;

    if ( tfp == ERR && (tfp = creato(tmp_file_name,2,0)) == ERR ) {
	BUF_retry(8);
	goto RETRY;
    }
RETRY:
    if ( lseek(tfp,SEEK_POS(no),0) < 0 ) {
	BUF_retry(3);
	goto RETRY;
    }
    tp = bp->next;
    if ( read(tfp,(char *)bp,sizeof(BUF_PTR)) != sizeof(BUF_PTR) ) {
	bp->next = tp;
	BUF_retry(4);
	goto RETRY;
    }
    bp->next = tp;
}
static BUF_PTR *get_buf(register int no)
{
    register BUF_PTR *tp;
    register BUF_PTR *bp;

    bp = top_buf;
    while ( bp->alno != no ) {
	if ( bp->next == NULL ) {
            if ( bp->alno != ERR )
                BUF_save(bp);
            if ( no != ERR )
                BUF_load(no,bp);
            break;
	}
	tp = bp;
	bp = bp->next;
    }
    if ( bp != top_buf ) {
	tp->next = bp->next;
	bp->next = top_buf;
	top_buf = bp;
    }
    return bp;
}
LIN_PTR *get_lin(register int no)
{
    register BUF_PTR *bp;

    bp = get_buf(LIN_BUF(no));
    return &(bp->buf[LIN_MASK(no)]);
}
int	xalloc(void)
{
    static int alno=0;

    int     i,n;
    register LIN_PTR *lp;
    register BUF_PTR *bp;

    if ( free_no == ERR ) {
	bp = get_buf(ERR);
	bp->alno = alno++;
	n = BUF_LIN(bp->alno);
	for ( i = 0 ; i < BUF_SIZ ; i++,n++ ) {
	    lp = &(bp->buf[i]);
	    lp->left = free_no;
	    free_no = n;
	}
    }
    n = free_no;
    lp = get_lin(free_no);
    free_no = lp->left;
    lp->left = lp->right = ERR;
    lp->lin[0] = '\0';
    return n;
}
int	xfree(int no)
{
    register LIN_PTR *lp;

    lp = get_lin(no);
    lp->left = free_no;
    free_no = no;
}
