
// define to compile test program
#define TEST
// define to compile test program for linux :=P
//#define LINUX

/*
	first test for posix directory routines for the c64
	kludges:
	-   currently uses cbm_open, which conflicts with standard i/o,
		which in turn makes it infact kindof unuseable. this can
		be easily changed however, since the only reason not to use
		open/read was that it currently appends ,u,r to filenames
	-   the offset in current dir stream should better be calculated
		from the values returned by "read".
	-   the type flag isnt filled in atm.	
	-   scandir/alphasort/versionsort is missing
	-   some bits are currently untested (ie, unused in the testprogram)
	27/02/2003 gpz	
*/

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>

#if defined(LINUX)
#include <sys/types.h>
#include <dirent.h>
#else

/*
	contents of dirent.h ...
*/

#include <cbm.h>

/* 16 will only be enough if NO subdirs are supported! */
#define NAME_MAX 16

struct dirent
{
	/* POSIX */
	char d_name[NAME_MAX+1];
	/* linux/bsd extensions */
//	off_t          d_off;
	unsigned short d_off;
	unsigned short d_reclen;
	unsigned char  d_type;

	/* bsd extensions */
	unsigned char  d_namlen;

};

/* fields in the dir struct shouldnt be accessed by user */
typedef struct 
{
	unsigned char fd;
	//off_t		  off;
	unsigned short off;
	char name[NAME_MAX+1];
} DIR;

DIR *opendir(const char *name);
struct dirent *readdir(DIR *dir);
int closedir(DIR *dir);
off_t telldir(DIR *dir);
int dirfd(DIR *dir);
void seekdir(DIR *dir, off_t offset);
void rewinddir(DIR *dir);

/*

int scandir(const char *dir, struct dirent ***namelist,
              int(*select)(const struct dirent *),
              int(*compar)(const struct dirent **, const struct dirent **));

int alphasort(const void *a, const void *b);
int versionsort(const void *a, const void *b);

*/

//-------------------------------------------------------------------------------------------
// functions access the directory via reading the "$" file to provide max. compatibility
//
// disc header:
//
// $07      ignored
// $01      quote
// $10      volume/subdir name
// $01      quote
// $06      disc id
// $01      = $00
//
// file entry:
//
// $02      ignored (basic-line link)
// $02      file length in 254-byte blocks (basic-line nr)
// $xx      spaces
//
// $01      quote if file-entry...                   | = 'b' if "blocks free"...
//          $00-$10  file-name                       |   $xx ignored
//          $01      quote                           |
//          $01      file-closed attrib (' 'or'*')   |   => file-length is free blocks
//          $03      file-type                       |
//          $01      file-protected attr. (' 'or'<') |
//          $xx      ignored                         |
//          $01      = $00 (basic-line end)          |
//-------------------------------------------------------------------------------------------

DIR *opendir(const char *name){
unsigned char buffer[(8+16+1+7)];
unsigned char fd;
DIR *dir;

	dir=(DIR*) malloc(sizeof(DIR));
	if(dir!=NULL)
	{
		/* handle dir name, go into
		   subdirs on 1581 etc... */
		name=name;
		
		// open dir "file"
//		if((fd=dir->fd=open("$",O_RDONLY))==-1)
		if(cbm_open(2, 8, CBM_READ, "$")!=0)
		{
			return(NULL);
		}

		fd=dir->fd=2;
		dir->off=0;
		
		// skip the disc header
		
		//read(fd,buffer,0x08);    // some info
		//read(fd,buffer,0x10);    // disc name
		//read(fd,buffer,0x01);    // quote
		//read(fd,buffer,0x07);    // disc id + $00
	
		cbm_read(fd,buffer,(8+16+1+7));  

	}
	return(dir);
	
}

int closedir(DIR *dir)
{
	cbm_close(dir->fd);
	free(dir);
}

int dirfd(DIR *dir)
{
	return(dir->fd);
}

off_t telldir(DIR *dir)
{
	return((off_t)(dir->off));
}

void seekdir(DIR *dir, off_t offset)
{
static unsigned char ch;
unsigned char fd;
char name[NAME_MAX+1];
	
	if(offset<dir->off)
	{
		/* close and re-open */
		strcpy(name,dir->name);
		closedir(dir);
		dir=opendir(name);
		
	}
	
	fd=dir->fd;
	
	/* scan forward */
	while(dir->off!=offset)
	{
		cbm_read(fd,&ch,0x01);
		dir->off=dir->off+1;    
	}
	
}

void rewinddir(DIR *dir)
{
	seekdir(dir,(off_t)0);
}

struct dirent *readdir(DIR *dir)
{

unsigned char buffer[0x40];
unsigned char temp;
unsigned char i,ii;

static struct dirent entry;
unsigned char fd;
static unsigned char ch;

	 	fd=dirfd(dir);
		entry.d_off=dir->off;

		// basic line-link / file-length
		cbm_read(fd,buffer,0x04);
		dir->off=dir->off+4;    
		/*
		len=(buffer[2]+(buffer[3]<<8));
		blen=len*254;
		*/
		entry.d_reclen=254*(buffer[2]+(buffer[3]<<8));

		// read file entry
		i=0;do
		{
			cbm_read(fd,&ch,0x01);    
			buffer[i]=ch;
			++i;

		} while (ch!=0);
			
		dir->off=dir->off+i;    

		// skip until either quote (file) or b (blocks free => end)
		i=0;ii=0;
		while(i==0){
			temp=buffer[ii];ii++;
			if(ii>16){
				/* something went wrong...this shouldnt happen! */
				return(NULL);
			}
			else if(temp=='\"') i++;
			else if(temp=='b') {
				/* "blocks free" */
				return(NULL);
			}
		}

		/* process file entry */

		i=0;  temp=buffer[ii];ii++;
		while(temp!='\"'){
			entry.d_name[i]=temp;
			i++;
			temp=buffer[ii];ii++;
		}
		entry.d_name[i]=0;
		entry.d_namlen=i;

		/*
		ii=ii+(16-i);                           // pad
		cattr=buffer[ii];ii++;                  // file closed attribute
		ext[0]=buffer[ii];ii++;                 // filetype (text)
		ext[1]=buffer[ii];ii++;
		ext[2]=buffer[ii];ii++;
		pattr=buffer[ii];ii++;                  // file protected attribute
		*/
		
		/* set type flag */

		return(&entry);

}
#endif

#if defined (TEST)
int main(void)
{
char mydirname[NAME_MAX+1]=".";
DIR *mydir;
struct dirent *mydirent;

	mydir=opendir(mydirname);
	if(mydir==NULL)
	{
		printf("error:\n");
	}
	else
	{
		printf("contents of \"%s\" dirfd:%d\n",mydirname,dirfd(mydir));
		while((mydirent=readdir(mydir))!=NULL)
		{
			printf("dirent.d_name[] : \"%s\"\n",mydirent->d_name);
			#if !defined(LINUX)
			printf("dirent.d_namlen : %10d\n",mydirent->d_namlen);
			#endif
			printf("dirent.d_reclen : %10d\n",mydirent->d_reclen);
			printf("dirent.d_type   : %10d\n",mydirent->d_type);
			printf("dirent.d_off    : %10d\n",(unsigned short)mydirent->d_off);
			printf("telldir()       : %10d\n",(unsigned short)telldir(mydir));
			printf("---\n");
		} 
		closedir(mydir);
	}

} 
#endif
