/*--------------------------------------------------------------------------
  ACCSEL - a simple accessory selector

  Copy the program into a folder called AUTO on the boot disk. It must be 
  called ACCSEL.PRG. When the computer is switched on the names of the 
  accessories on the disk will be listed on the screen. Choose the ones 
  you want and then hit return.

  The source code is Laser C, by Les Kneeling, 1990

--------------------------------------------------------------------------*/
#include <stdio.h>
#include <osbind.h>
#include <strings.h>
#include <ctype.h>
#include <fcntl.h>

/*--------------------------------------------------------------------------
  Structure definitions
--------------------------------------------------------------------------*/
typedef struct dta	{
					char	reserved[21];
					char	fattr;
					int		ftime;
					int		fdate;
					long	fsize;
					char	fname[14];
					};

typedef struct fentry {
					char	fname[20];		/* extra wide to give four
											   names per line			*/
					struct fentry *next;	/* pointer to next struct in
											   the linked list - used when
											   going forwards			*/
					struct fentry *last;	/* pointer to the last struct
											   in the list - used when
											   going forwards			*/
					};

/*--------------------------------------------------------------------------
  Globals
--------------------------------------------------------------------------*/
struct fentry start;
char file_count;
char from[80], to[80];
char resolution;				/* Scaling factor for display			*/
char directory[64];
char path[80];
char sysfile[20] = "\\ACCPATH";
/*-------------------------------------------------------------------------
  Start
--------------------------------------------------------------------------*/

main()
{
get_path();
works();
}

works()
{
start.last = 0l;			/* Initialise struct at beginning of list 	*/
start.next = 0l;
if( Getrez() == 2 ) resolution = 4;		/* Set up scaling factor for	*/
else resolution = 2;					/* colour or mono display		*/
clrscr();
gotoxy( 0, 0 );							/* Go to top left corner		*/
inverted_text();						/* White text on black			*/
if( resolution == 4 ) Cconws("                                        ");
Cconws("             ACCSEL 2 - by Les Kneeling " );
										/* That's me					*/
gotoxy( 23, 0 );						/* Bottom two lines				*/
inverted_text();						/* White on black				*/
if( resolution == 4 )
Cconws("             <+> switch accessory on,     <-> switch accessory off              ");
else Cconws("  <+> accessory on, <-> accessory off   ");
auto_overflow_off();					/* Stop the screen scrolling when
										   the bottom left corner has a
										   character printed in it		*/
gotoxy( 24, 0 );
if( resolution == 4 )
	{
	Cconws("   " );
	Bconout( 5, 4 );
	Cconws( " and " );
	Bconout( 5, 3 ); 
	Cconws( " select file, ESC clears all accessories  <return> finish  H - help   ");
	}
else 
	{
	Cconws( "    + on, - off, H - help, Return end   " );
	}
auto_overflow_on();						/* Back to normal				*/
normal_text(); 							/* and black on white			*/
get_filenames();
show_filenames();
interact();
free_memory();
}
/*--------------------------------------------------------------------------
  If ACCPATH exists in the root directory take the path for the accessory
  folder from it.
--------------------------------------------------------------------------*/
get_path()
{
int fd;
char bytesread;

fd = open( sysfile, 0 );
if( fd > 0 )
	{
	bytesread = read( fd, directory, 64 );
	close( fd );
	if( bytesread == 0 ) strcpy( directory, "" );
	}
}
/*--------------------------------------------------------------------------
  Save the current path to ACCPATH
--------------------------------------------------------------------------*/
put_path()
{
int fd;
char bytes;


fd = creat( sysfile, 1 );
if( fd > 0 )
	{
	bytes = write( fd, directory, 64 );
	if( bytes < strlen( directory ) ) Cconws("\n\rError writing file\n");
	close( fd );
	}
else
	{
	Cconws("Error opening file\n\r" );
	}
}
/*--------------------------------------------------------------------------
  Read the disk directory
--------------------------------------------------------------------------*/
get_filenames()
{
int error;
char temp;
struct dta newdta;
struct fentry *ptr, *holder;

ptr = &start;

Fsetdta( &newdta );						/* User defined disk transfer
										   address	*/

file_count = 0;
strcpy( path, directory );
temp = strlen(path);
if( path[temp] == '\\' ) path[temp] = 0;

strcat( path, "\\*.ACC" );
error = Fsfirst( path, 0x3f );			/* First search for the ACCs	*/
while((!error)&&(file_count < 20 * resolution ))
	{
	file_count++;						/* Make sure files don't over-
										   write the stuff on the screen*/
	strcpy( ptr->fname, newdta.fname );	/* Copy filename into struct	*/
	expand( ptr->fname );				/* Make the string 20 characters
										   wide							*/
	holder = ptr;						/* Keep address for the ->last
										   field in the next struct in
										   the list 					*/
	ptr->next = (struct fentry *)malloc( sizeof( struct fentry ) );
										/* Add a new struct to the list	
										   and link list forwards		*/
	ptr = ptr->next;					/* Move to new struct			*/
	ptr->last = holder;					/* Link the list backwards		*/
	ptr->next = 0l;						/* Put stop value at end of list*/
	*ptr->fname = 0;					/* Zap the filename just in case*/
	error = Fsnext();					/* Any more entries in directory*/
	}

strcpy( path, directory );
strcat( path, "\\*.ACX" );
error = Fsfirst( path, 0x3f );			/* Now for the ACXs				*/
while((!error)&&(file_count < 20 * resolution ))		
	{									/* Exactly the same again		*/
	file_count++;
	strcpy( ptr->fname, newdta.fname );
	expand( ptr->fname );
	holder = ptr;
	ptr->next = (struct fentry *)malloc( sizeof( struct fentry ) );
	ptr = ptr->next;
	ptr->last = holder;
	ptr->next = 0l;
	*ptr->fname = 0;
	error = Fsnext();
	}
}

/*--------------------------------------------------------------------------
  Display the filenames on the screen
--------------------------------------------------------------------------*/
show_filenames()
{
struct fentry *ptr;						/* Local pointer to list		*/
int count = 0;

ptr = &start;							/* Initialise pointer to start	*/
gotoxy( 1, 0 );							/* Next line					*/
inverted_text();						/* White text on black			*/
Cconws( ptr->fname );					/* Print the filename			*/
normal_text();							
while( ptr->next != 0l )				/* while not at end of list		*/
	{
	ptr = ptr->next;					/* advance						*/
	count++;							/* keep count					*/
	gotoxy( count/resolution + 1, (count % resolution) * 20 );
										/* position cursor				*/
	Cconws( ptr->fname );				/* print filename				*/
	}
}

/*--------------------------------------------------------------------------
  Pad the filename with spaces
--------------------------------------------------------------------------*/
expand( filename )
char *filename;
{
register char count;

count = strlen( filename );				/* Add spaces to the end of the	*/
while( count < 20 )						/* filename until it is 20		*/
	*(filename + count++) = 32;			/* characters long to keep the	*/
*(filename + count) = 0;				/* display pretty when it is	*/
}										/* printed white on black		*/

/*--------------------------------------------------------------------------
  Take care of the keystrokes
--------------------------------------------------------------------------*/
interact()
{
struct fentry *ptr;						/* Local pointer for the list	*/
char temp[20];
char count = 0, entry;
union	{								/* Union used to split up the	*/
		long whole;						/* high and low words of the	*/
		struct	{						/* long returned by Bconin()	*/
				int highword;
				int lowword;
				}split;
		} key;

ptr = &start;							/* Initialise pointer to start	*/

gotoxy( 1, 0 );							/* Beginning of display			*/
inverted_text();
Cconws( ptr->fname );					/* Put cursor over first file	*/
normal_text();

/*--------------------------------------------------------------------------
   The Bconin() call returns a long, but the switch statement can only 
   handle an int so it is necessary to go through a few gyrations to
   make it work
--------------------------------------------------------------------------*/
while( key.split.lowword != 13 )		/* Return finishes it			*/
	{
	key.whole = Bconin( 2 );			/* Get the keytroke				*/
	if( key.split.lowword != 0 )
		switch( key.split.lowword )
			{
			case '+':					/* Switch accessory on			*/
				gotoxy( count/resolution + 1, (count % resolution) * 20 );
				inverted_text();
				strcpy( from, directory );
				strcat( from, "\\" );
				strcat( from, ptr->fname );
				strcpy( to, directory );
				strcat( to, "\\" );
				strcat( to, ptr->fname );	/* Build a new filename	*/
				*rindex( to, 'X' ) = 'C';	/* with ACC extension	*/
				if( !Frename( 0, from, to ))
					*rindex( ptr->fname, 'X' ) = 'C';
											/* If the rename doesn't */
											/* work don't change the */
											/* name on the screen	 */
				Cconws( ptr->fname );
				normal_text();
				break;
			case '-':				/* Switch accessory off			*/
				gotoxy( count/resolution + 1, (count % resolution) * 20 );
				inverted_text();
				strcpy( from, directory );
				strcat( from, "\\" );
				strcat( from, ptr->fname );
				strcpy( to, directory );
				strcat( to, "\\" );
				strcat( to, ptr->fname );	/* Build a new filename	*/
				*rindex( to, '.' ) = 0;
				strcat( to, ".ACX" );
				if( !Frename( 0, from, to ))
					*rindex( ptr->fname, 'C' ) = 'X';
											/* If the rename doesn't */
											/* work don't change the */
											/* name on the screen	 */
				Cconws( ptr->fname );
				normal_text();
				break;
			case 27:	
				ptr = &start;		/* Clear all accessories		*/
				count = 0;
				while( ptr->next != 0l )	/* For each filename	*/
					{						/* in turn				*/
					gotoxy( count/resolution + 1, (count % resolution) * 20 );
					inverted_text();
					strcpy( from, directory );
					strcat( from, "\\" );
					strcat( from, ptr->fname );
					strcpy( to, directory );
					strcat( to, "\\" );
					strcat( to, ptr->fname );
					*rindex( to, '.' ) = 0;
					strcat( to, ".ACX" );
					if( !Frename( 0, from, to ))
						*rindex( ptr->fname, 'C' ) = 'X';
					Cconws( ptr->fname );
					normal_text();
					gotoxy( count/resolution + 1, (count % resolution) * 20 );
					Cconws( ptr->fname );
					ptr = ptr->next;
					count++;
					}
				count = 0;			/* Put cursor at beginning again*/
				ptr = &start;
				inverted_text();
				gotoxy( count/resolution + 1, (count % resolution) * 20 );
				Cconws( ptr->fname );
				normal_text();
				break;
			case 'c':
			case 'C':
				get_new_path();
				break;
			case 'w':
			case 'W':
				put_path();
				break;
			case 'h':
			case 'H':
				help();
				break;
			}
	else
		switch( key.split.highword )
			{
			case 0x50:		/* Down arrow */
				gotoxy( count/resolution + 1, (count % resolution) * 20 );
				normal_text();
				Cconws( ptr->fname );
				inverted_text();
				for( entry = 0; entry < resolution; entry++ )
					{
					if( count < file_count - 1 )	/* Keep the cursor	*/
						{							/* inside the list	*/
						count++;
						ptr = ptr->next;		/* Advance through list	*/
						}
					}
				gotoxy( count/resolution + 1, (count % resolution) * 20 );
									/* Position cursor				*/
				Cconws( ptr->fname );
									/* Highlight new filename		*/
				normal_text();
				break;
			case 0x4d:		/* Right arrow */
				gotoxy( count/resolution + 1, (count % resolution) * 20 );
									/* Position cursor				*/
				normal_text();
				Cconws( ptr->fname );
									/* Erase old cursor				*/
				inverted_text();
				if( count < file_count - 1 )	/* Keep the cursor	*/
					{							/* inside the list	*/
					count++;
					ptr = ptr->next;		/* Advance through list	*/
					}
				else
					{
					count = 0;				/* End of list - go back*/
					ptr = &start;			/* to the beginning		*/
					}
				gotoxy( count/resolution + 1, (count % resolution) * 20 );
									/* Position cursor				*/
				Cconws( ptr->fname );
									/* Highlight new filename		*/
				normal_text();
				break;
			case 0x48:
				gotoxy( count/resolution + 1, (count % resolution) * 20 );
				normal_text();
				Cconws( ptr->fname );
				inverted_text();
				for( entry = 0; entry < resolution; entry++ )
					{
					if( count > 0 )
						{
						count--;
						ptr = ptr->last;	/* Going backwards this time*/
						}
					}
				gotoxy( count/resolution + 1, (count % resolution) * 20 );
				Cconws( ptr->fname );
				normal_text();
				break;
			case 0x4b:				/* Last filename				*/
				gotoxy( count/resolution + 1, (count % resolution) * 20 );
				normal_text();
				Cconws( ptr->fname );
				inverted_text();
				if( count > 0 )
					{
					count--;
					ptr = ptr->last;	/* Going backwards this time*/
					}
				gotoxy( count/resolution + 1, (count % resolution) * 20 );
				Cconws( ptr->fname );
				normal_text();
				break;
			}
	}
clrscr();
Dsetpath( "\\" );
}
/*--------------------------------------------------------------------------
  Allow user to change the path for the directory search
--------------------------------------------------------------------------*/
get_new_path()
{
char count, key;

clear_display();
gotoxy( 10, 0 );
enable_cursor();
get_filename();
disable_cursor();
free_memory();
clear_display();
get_filenames();
show_filenames();
}
/*--------------------------------------------------------------------------
  Clear the center of the screen
--------------------------------------------------------------------------*/
clear_display()
{
char count;

for( count = 1; count < 23; count++ )
	{
	gotoxy( count, 0 );
	clreol();
	}
}
/*--------------------------------------------------------------------------
  Release memory reserved in program
--------------------------------------------------------------------------*/
free_memory()
{
struct fentry *ptr;

ptr = &start;

while( ptr->next != 0l )			/* Proceed to the end of the list	*/
	ptr = ptr->next;

while( ptr->last != 0l )			/* Then go back to the beginning	*/
	{								/* releasing the blocks as we go	*/
	ptr = ptr->last;
	free( ptr->next );
	}
}

get_filename()				/* replaces scanf, allows the user 	*/
{                       	/* to abandon the input by hitting 	*/
int count;					/* escape							*/
char key;
char local[80];

strcpy( local, "" );
count = 0;
key = 0;
do	{
	key = Cnecin();
	if( isfile( key ) )				/* If a legal character 		*/
		{
		local[count] = key;
		Cconout( key );
		count++;
		}
	if( (key == 8)&&(count > 0) )	/* Backspace					*/
		{
		count--;
		local[count] = 0;
		Cconout( 8 );
		Cconout( 32 );
		Cconout( 8 );
		}
	if( key == 13 )					/* Return						*/
		local[count] = 0;
	}while( (key != 13)&&(key != 27) );
if( key != 27 ) strcpy( directory, local );
}

isfile( letter )		/* check whether a character can legally */
char letter;			/* be used as part of a filename */
{
if( (letter >= 'a')&&(letter <= 'z'))
	letter &= 0x5f;
return( ((letter >= 'A')&&(letter <= 'Z'))||(letter == '.')||
	(letter == '_')||(letter == ':')||(letter == 0x5c)||
	((letter >= '0')&&(letter <= '9')) );
}

/*--------------------------------------------------------------------------
				Screen handling 
--------------------------------------------------------------------------*/
gotoxy( xpos, ypos )				/* Position VT52 cursor			*/
char xpos, ypos;
{
Bconout( 2, 27 );
Bconout( 2, 'Y' );
Bconout( 2, 32 + xpos );
Bconout( 2, 32 + ypos );
}

clrscr()							/* Clear the screen				*/
{
Bconout( 2, 27 );
Bconout( 2, 'E' );
}

clreol()							/* Clear to end of line			*/
{
Bconout( 2, 27 );
Bconout( 2, 'l' );
}

inverted_text()						/* White print on black 		*/
{
Bconout( 2, 27 );
Bconout( 2, 'p' );
}

normal_text()						/* Black text on white			*/
{
Bconout( 2, 27 );
Bconout( 2, 'q' );
}

auto_overflow_off()					/* Switch off automatic overflow*/
{									/* which normally occurs when 	*/
Bconout( 2, 27 );					/* the end of the screen is 	*/
Bconout( 2, 'w' );					/* reached - this stops the 	*/
}									/* screen scrolling				*/

auto_overflow_on()					/* Restore setting to normal	*/
{
Bconout( 2, 27 );
Bconout( 2, 'v' );
}

enable_cursor()
{
Bconout( 2, 27 );
Bconout( 2, 'e' );
}

disable_cursor()
{
Bconout( 2, 27 );
Bconout( 2, 'f' );
}

help()
{
clear_display();

gotoxy( 1, 0 );
Cconws(" +    switch accessory on\n\r");
Cconws(" -    switch accessory off\n\r");
Cconws("Esc   switch all accessories off\n\n\r");
Cconws(" c    change the directory\n\r");
Cconws("      searched for accessories\n\r");
Cconws(" w    write the change to disk\n\r");
Cconws("      Return accepts the selections\n\n\r");
Cconws(" If a file called ACCPATH exists in the\n\r");
Cconws(" root directory, the path which will\n\r");
Cconws(" searched for accessories is read\n\r");
Cconws(" from it.\n\r");
Cconws(" To create this file, hit c, type the\n\r");
Cconws(" path, then hit w to write it do disk\n\r");
Cconws(" The path must be of the form:\n\r");
Cconws("       c:\\accessry\n\r");
Cconws(" It must not end in \\, and there must\n\r");
Cconws(" be a slash between the drive and the\n\r");
Cconws(" directory name.\n\n\r");
Cconws("          Hit a key to resume");
Cconin();
clear_display();
show_filenames();
} 
