/*********************************

    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			/* 181 * 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)

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 FILE	*tfp=NULL;
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);
    } else {
	strcpy(tmp,drv);
	strcat(tmp,"\\");
    }
    drv = tmp;
    tmp_drive_no = toupper(drv[0]) - 'A';
    sprintf(tmp_file_name,"%c:\\%s",tmp_drive_no+'A',TMPFILE);
    macset("TMP",drv);

    return FALSE;
}
void    BUF_end(void)
{
    if ( tfp != NULL ) {
	fclose(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;
    FILE    *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 = 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(tmp_file_name);
    }

    tfp = fp;
    strcpy(tmp_file_name,tmp);
}
static void    BUF_save(bp)
register BUF_PTR *bp;
{
    if ( tfp == NULL && (tfp = fopen(tmp_file_name,"w+b")) == NULL ) {
	BUF_retry(8);
	goto RETRY;
    }
RETRY:
    if ( fseek(tfp,SEEK_POS(bp->alno),SEEK_SET) ) {
	BUF_retry(0);
	goto RETRY;
    }
    if ( fwrite((char *)bp,sizeof(BUF_PTR),1,tfp) < 1 ) {
	BUF_retry(1);
	goto RETRY;
    }
}
static void BUF_load(no,bp)
int     no;
register BUF_PTR *bp;
{
    BUF_PTR *tp;

    if ( tfp == NULL && (tfp = fopen(tmp_file_name,"w+b")) == NULL ) {
	BUF_retry(8);
	goto RETRY;
    }
RETRY:
    if ( fseek(tfp,SEEK_POS(no),SEEK_SET) ) {
	BUF_retry(3);
	goto RETRY;
    }
    tp = bp->next;
    if ( fread((char *)bp,sizeof(BUF_PTR),1,tfp) < 1 ) {
	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;
}
