/* Mac buffered IO functions

*/
#include <ToolUtils.h>
#include <Files.h>
#include <Errors.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <errno.h>

#include "macbf.h"
#ifndef FALSE
#define FALSE (0)
#define TRUE (!FALSE)
#endif

/* Valid mode strings
	a  append, create if doesn't exist
	r  read, must exist
	w  create file, write only access
	w+ create file, read and write access
	r+ Reading and writing, file must exist
	a+ (not supported) allow seeks but always write at end.
*/
int fflush_raw(FILE *f);
int fseek_raw( FILE *f, long offset, int origin );
int flength_raw(FILE *f);
int fread_raw( void *buffer, long inout, FILE *f );
int fwrite_raw( const void *buffer, long inout , FILE *f );
char *m_showhex(char *s, int len);


extern int errno; 
unsigned char *pstr(const char *s);
unsigned char *pstr(const char *s)
{
	static char bf[260];
	strcpy(bf+1,s);
	bf[0] = strlen(s);
	return (unsigned char *) bf;
}
#define BSIZE 10000
FILE *fopen( const char *filename, char *xmode )
{
	FILE *t;
	int r;
	int i;
	int isbin = FALSE;
	char mode[10];
	int docreate=FALSE;
	int dodel = FALSE;

//imsg("mac_fopen(%s,%s)\n",filename,xmode);
	for (i=0;*xmode!=0;xmode++) if (*xmode!='b') mode[i++]=*xmode ; else isbin = TRUE;
	if (mode[0] == 'w') {docreate = TRUE; dodel = TRUE;}
	if (mode[0] == 'a') docreate = TRUE;
	t = malloc(sizeof(FILE));
	if (t==NULL) {
		errno = ENOMEM;
		return NULL;
	}
	t->islog = FALSE;
/*if (strstr(filename,"dbi.idx")!=NULL) t->islog = TRUE;*/
	t->readonly = FALSE;
	t->at = 0;
	t->f_at = t->at;
	t->bf = NULL;
	t->bflen = 0;
	t->bfat = 0;
	t->bfdirty = FALSE;
	t->f_eof = FALSE;
	t->eof = FALSE;
	t->isbin = isbin;
	t->length = t->f_length = 0;
		
	if (strcmp(mode,"r")==0) t->readonly = TRUE; 
	if (strcmp(mode,"r+")==0) t->readonly = FALSE; 
	r = FSMakeFSSpec(0,0,pstr(filename),&t->fs);
	if (r==0) { /* File exists */
		if (dodel) {
			r = FSpDelete(&t->fs);
			if (r!=0) {
				errno = r;

			}
		}
	} else if (r== -43) { /* File doesn't exit*/
		dodel = TRUE;
		if (!docreate) {
			errno = ENOENT;
			return NULL;
		}
	} else { 
		errno = r; goto rnull;
	}
	
	if (dodel) {
		r = FSpCreate(&t->fs, 'TEXT', 'TEXT', 0);
		if (r!=0) {errno=r; goto rnull;}
	}
	
	r = FSpOpenDF(&t->fs,fsCurPerm,&t->ref);
	if (r!=0) { errno = r; goto rnull; }
	t->bf = malloc(BSIZE);	
	t->bflen = 0;
	if (t->bf==NULL) {
		errno = ENOMEM;
		goto rnull;
	}
	t->length = t->f_length = flength_raw(t);

//imsg("mac_fopen(%s,%s) ref=%d\n",filename,xmode,t->ref);
	if (mode[0] == 'a') {
		t->at = t->length;
		t->f_at = t->at;
		SetFPos(t->ref,2,0);
/*		fseek(t,0,SEEK_END);*/
	}
	return t;
rnull:
	free(t);
	return NULL;
}
int remove(const char *filename)
{
	FSSpec fs;
	int r;

	r = FSMakeFSSpec(0,0,pstr(filename),&fs);
	if (r==0) { /* File exists */
		r = FSpDelete(&fs);
		if (r!=0) {
			errno = r;
			return 1;
		}
	} else if (r== -43) { /* File doesn't exit*/
		errno = ENOENT;
		return 1;
	} else { 
		errno = r; 
		return 1;
	}
	return 0;
}
int fclose(FILE *f)
{
	int r;
	int readonly=FALSE;
	if (f==NULL) {
		errno = EINVAL;
		return 1;
	}
	if (!f->readonly) fflush_raw(f);
if (f->islog) imsg("mac_fclose ref=%d\n",f->ref);
	r = FSClose(f->ref);
	readonly = f->readonly;
	f->ref = 0;
	if (f->bf!=NULL) free(f->bf);
	f->bf = NULL;
	free(f);
	if (r!=0) {
		errno = r;
		return 1;
	}
	if (!readonly) {
		r = FlushVol(NULL,0); /* Should be flushfile but can't figure that out */
		if (r!=0) { errno = r; return 1;}
	}
	return 0;
}
size_t fread( void *buffer, size_t size, size_t count, FILE *f )
{
	int offset;
	int r; 
	int long inout=size*count;
	if (inout<=0) return 0;

again:
	if (f->at >= f->bfat) if (f->at+inout <= f->bfat+f->bflen) {
		offset = f->at - f->bfat;
		memcpy(buffer,f->bf + offset, inout);
if (f->islog) imsg("mac_fread_cache bfat=%d bflen=%d offset=%d len=%d (%s)\n",f->bfat,f->bflen,offset,inout,m_showhex(buffer,inout));

		f->at += inout;
		return count;
	}
	if (inout<BSIZE) {
		if (f->bfdirty) fflush_raw(f); /* Write buffer if necessary */ 
		if (f->at != f->f_at) fseek_raw(f,f->at,SEEK_SET);
		f->bflen = fread_raw(f->bf,BSIZE,f);
if (f->islog) imsg("mac_fread_raw bfat=%d bflen=%d \n",f->bfat,f->bflen);
		f->bfat = f->at;
		if (f->bflen<inout) {
			memcpy(buffer,f->bf, f->bflen);
			f->bflen = 0;
			f->eof = TRUE;
			return (f->bflen / size);
		}
		goto again;
	}
	if (f->at != f->f_at) fseek_raw(f,f->at,SEEK_SET);
	r = fread_raw(buffer,inout,f);
if (f->islog) imsg("mac_fread_raw2 len=%d %s \n",r,m_showhex(buffer,r));
if (f->islog) imsg("mac_fread_raw2 bfat=%d bflen=%d \n",f->bfat,f->bflen);
	f->at += inout;
	if (r<inout) f->eof = TRUE;
	return r/size;
}
char *m_hexbyte(int c)
{
        static char bf[20];
        sprintf(bf,"%02.2x",(unsigned int) c);
        if (strlen(bf)>2) return bf + strlen(bf) - 2; 
        return bf;
}

char *m_showhex(char *s, int len)
{
	int i;
	static char bf[1000];
	char *out=bf;
	if (len>400) len = 400;
	for (i=0; i<len; i++) {
		if ((i % 20)==0) *out++ = ' ';
		strcpy(out,m_hexbyte(*s));
		out += strlen(out);
		s++;
	}
	return bf;	
}
int fread_raw( void *buffer, long inout, FILE *f )
{
	int r;
	r = FSRead(f->ref,&inout,buffer);
if (f->islog) imsg("mac FSRead ref=%d len=%d %s\n",f->ref,inout,m_showhex(buffer,inout));
	if (r==eofErr) f->f_eof = TRUE;
	else if (r!=0) errno = r;
	f->f_at += inout;
	return inout;
}
int fileno(FILE *f)
{
	return (int) f->ref;
}
static char vbf[30000];
int fprintf( FILE *f, const char *format,...)
{
	char *s;
	va_list ap;
	va_start(ap,format);
	vsprintf(vbf,format,ap)
	va_end(ap);
	if (!f->isbin) {
		for (s=vbf;*s!=0;s++) {
			if (*s=='\n') *s = '\r';
		}
	}
	return fwrite(vbf,1,strlen(vbf),f);
}

size_t fwrite( const void *buffer, size_t size, size_t count, FILE *f )
{
	int offset;
	int r; 
	int long inout=size*count;
	if (inout<=0) return 0;
	

	if (f->islog) imsg("mac_fwrite at=%d len=%d %s\n",f->at,inout,m_showhex(buffer,inout));
again:
	if (f->at >= f->bfat) if (f->at+inout <= f->bfat+BSIZE) 
	  if (f->at <= f->bfat + f->bflen) 
	{
		offset = f->at - f->bfat;
		memcpy(f->bf + offset,buffer, inout);
	if (f->islog) 	imsg("mac_write_cache bfat=%d bflen=%d,  offset=%d, len=%d\n",f->bfat,f->bflen,offset,inout);
		f->at += inout;
		f->bfdirty = TRUE;
		if (f->at > (f->bfat + f->bflen)) f->bflen = f->at - f->bfat; 
		if (f->length<f->at) f->length = f->at;
	/*	printf("wrote to buffer %d bytes, bfat = %d len %d\n",inout,f->bfat,f->bflen); */
		return count;
	}
	if (inout<BSIZE) {
		if (f->bfdirty) fflush_raw(f); /* Write buffer if necessary */ 
		f->bfat = f->at;
		f->bflen = 0;
if (f->islog) imsg("mac_move_bf  bfat=%d bflen=%d,  \n",f->bfat,f->bflen);
/*	printf("xxbfat = %d at=%d\n",f->bfat,f->at);*/
		goto again;
	}
	if (f->bfdirty) fflush_raw(f);

	if (f->at != f->f_at) fseek_raw(f,f->at,SEEK_SET);

//imsg("mac_write ref=%d at=%d len=%d\n",f->ref,f->at,inout);
	r = fwrite_raw(buffer,inout,f);
	if (r<0) r = 0;
	f->at += r;
	if (f->at > f->length) f->length = f->at;
	return r/size;
}
int fwrite_raw( const void *buffer, long inout , FILE *f )
{
	int r; 
	if (inout<=0) return 0;
if (f->islog) imsg("mac_FSWrite ref=%d len=%d %s\n",f->ref,inout,m_showhex(buffer,inout));
	r = FSWrite(f->ref,&inout,buffer);
	if (r!=0) {errno = r;}
	f->f_at += inout;
	if (f->f_at > f->f_length) f->f_length = f->f_at;
	return (size_t) inout;	
}
int fflush_raw(FILE *f)
{
	int r;
	if (f->readonly) return 0;
	if (!f->bfdirty) return 0;
	f->bfdirty = FALSE;
	if (f->f_at != f->bfat) fseek_raw(f,f->bfat,SEEK_SET);
	r = fwrite_raw(f->bf,f->bflen,f);
	if (r<f->bflen) return -1;
	return 0;
}
char *fgets( char *out, int n, FILE *f )
{
	char *s;
	int i;
	char *xout=out;
again:
	for (s=f->bf,i=0;i<f->bflen;i++) {
		if (*s=='\r') {*s = '\n';} 
		*out++ = *s;
		if (*s=='\n') goto gotline;
		if (out-xout>=(n-1)) goto gotline;
		s++;
	}
	
	f->bfat = f->f_at;
	f->bflen = fread_raw(f->bf,BSIZE-1,f);

	if (f->bflen<=0) {
		f->bflen = 0;
		if (f->f_eof) if (out==xout) { f->eof = TRUE; *out = 0; return NULL;}
		*out++ = 0;
		return xout;
	}
	goto again;
gotline:
	memmove(f->bf,s+1,strlen(s));
	if (f->f_eof) if (f->bflen<=0) f->eof = TRUE;
	f->bflen -= i+1;
	out[0] = 0;
	return xout;
}
int fseek( FILE *f, long offset, int origin )
{
	int pmode=0;
	f->eof = FALSE;
	f->f_eof = FALSE;
	if (origin==SEEK_CUR) {pmode = fsFromMark; f->at += offset; }
	if (origin==SEEK_END) {pmode = fsFromLEOF; f->at = f->length + offset;}
	if (origin==SEEK_SET) {pmode = fsFromStart; f->at = offset; }
	return 0;
}
int fseek_raw( FILE *f, long offset, int origin )
{
	int r;
	int pmode=0;
	if (origin==SEEK_CUR) {pmode = fsFromMark; f->f_at += offset;  }
	if (origin==SEEK_END) {pmode = fsFromLEOF; f->f_at = f->f_length + offset;}
	if (origin==SEEK_SET) {pmode = fsFromStart; f->f_at = offset; }

if (f->islog) imsg("mac_SetFPos ref=%d at=%d from=%d\n",f->ref,offset,origin);

	r = SetFPos(f->ref,pmode,offset);
	if (r!=0) errno = r;
	return r;
}
long ftell( FILE *f )
{
	return f->at;
/*	int r;
	long pos;
	r = GetFPos(f->ref,&pos);
	if (r!=0) { errno = r; return -1;}
	return pos;
	*/
}

int fflush( FILE *stream )
{
	int r;
	fflush_raw(stream);
	return 0;
	r = FlushVol(NULL,0);
	 /* Should be flushfile but can't figure that out */
	if (r!=0) { errno = r; return EOF;}
	return 0;
}
int feof( FILE *f )
{
	return f->eof;
}
int flength(FILE *f)
{
	return f->length;
} 
int flength_raw(FILE *f)
{
	long eof;
	int r;
	r = GetEOF(f->ref,&eof);
	if (r!=0) {errno = r; return -1;}
	return eof;
}
char *strerror(int x)
{
	static char bf[200];
	if (x==ENOENT) return "File not found";
	if (x== -0) return "MAC No Error";
	if (x== -35) return "MAC No such volume";
	if (x== ioErr) return "MAC I/O error";
	if (x== -37) return "MAC Bad Name";
	if (x== -43) return "MAC File not found";
	if (x== -44) return "MAC Write protected";
	if (x== -45) return "MAC File is locked";	
	if (x== -46) return "MAC Volume is locked";
	if (x== -47) return "MAC File is busy";
	if (x== -48) return "MAC Duplicate Filename (rename)";
	if (x== -49) return "MAC File already open with write permissions";
	
	if (x== -120) return "MAC Dir not found or incomplete pathname";
	sprintf(bf,"MAC Unknown error (%d)",x);
	return bf;
}

