#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <utility/tagitem.h>
/*#include <utility/hooks.h>*/
#include <dos/dostags.h>
#include <clib/dos_protos.h>
#include <dos.h>
#include <time.h>
#include <exec/types.h>
#include <exec/memory.h>
#include <exec/execbase.h>
#include <libraries/dosextens.h>
#include <libraries/locale.h>
#include <proto/exec.h>
#include <clib/locale_protos.h>

/* These are only used in ks1.3 , in 2.0 we use System() */
long SyncRun(char *,char *,BPTR,BPTR);
#pragma libcall ArpBase SyncRun                 021C    109804
struct 	Library *ArpBase=NULL;

#include "/lib/iomodes.h"
#include "/lib/misc.h"
#define UID_NAME_ARRAY
#include "/lib/users.h"

#ifndef FIBF_HIDDEN
#define FIBB_HIDDEN	7        /* hidden from the directory [Not by C=] */
#define FIBF_HIDDEN	(1<<FIBB_HIDDEN)
#endif


#define VERSTAG		"\0$VER: "
#define VERSION 	"FFE 1.82"
#define UPDATED 	"18.5.95"

#define STRINGARRAY
#include "ffelanguage.h"

int		statuswindowsize = 13;	/* Originally 13 */
#define MAXPROTOS	16

#define NAME_LENGTH 32
#define COMM_LENGTH	80
#define MAXFILEDIRS	32
#define PATHSIZE	120
#define MAXLEVELS	64
#define MAXLEN		256

struct Suffix
{
	struct Node sf_node; /* ln_Name points to the suffix */
						 /* ln_Type shows what to do after command (show output) */
	ULONG sf_size;       /* struct size, including strings (size to freemem) */
	char *sf_command;
	/* strings after this */
};

struct List Suffixes;


extern struct ExecBase *SysBase;
extern struct DosLibrary *DOSBase;
struct Locale *localepointer = NULL;

#define OPT_README	(1<<0)

#define DIR_DIR		1
#define DIR_LINK	2

#define FLAG_NEW	(1<<0)
struct base
{
	struct	base *next;
	UWORD	directory;					/* TRUE if directory */
	UWORD	flags;
	LONG	size;						/* size of the file in bytes */
	struct	DateStamp date; 			/* last changed */
	char	filename[NAME_LENGTH];		/* name of the file */
	char	comment[COMM_LENGTH];		/* filenote */
};


struct date
{
	int	day;
	int	month;
	int	year;
};

void __regargs __chkabort(void);
void __regargs __chkabort(void){}

void chkabort(void) {}
/*int CXBRK(void) { return(0);}*/

const static char vers[] = VERSTAG""VERSION" ("UPDATED")";
const static char bar[]  = "                                                                                ";
const char months[12][4]={"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"};
char	home[PATHSIZE],
		filepath[MAXFILEDIRS][PATHSIZE],
		descpath[PATHSIZE],
		xprpath[PATHSIZE] = "axsh:bin/xpr",
		visual[80] = "axsh:bin/editor",
		morecmd[PATHSIZE] = "AXsh:bin/more",
		moreopt[PATHSIZE] = "",
		language[PATHSIZE];
char	datestring[20];
int		paths = 0,
		hideold = 0,
		dsp = 0,
		xrows,
		lastday = 0,
		lastmonth = 0,
		lastyear = 0,
		rows,
		columns = 80,
		lowest = 0,
		curpos,
		curframe,
		numberoffiles,
		numberofdirs;

ULONG	options;
int		maxfiles = 0;
char	*flag = NULL, edit=FALSE, kill=FALSE;
ULONG	numberofbytes;
struct	base *firstnode = NULL;
/*struct	Hook	hook;*/
struct	LocaleBase *LocaleBase;
struct	Catalog *catalog = NULL;


long MyRunCommand(char *,char *,BPTR,BPTR);

void cleanexit(int error);
void openthings(void);
struct base *create_node(void);
struct base *remove_node(struct base *node);
int  getkey(BOOL cursors);
void readconfig(void);
void updatemidstatusline(char *cwd);
void updateheaderscreen(void);
void updatestatusline(char *cwd);
void mainmenu(void);
struct base *gotoframe(int framenumber);
char *convertdate(struct DateStamp *ds,char *temp);
void freefiles(void);
int scanfiles(char *directory,int level);
struct date *gimmedate(struct DateStamp *ds);
BOOL istext(char *filename, char *dir);
char *printout(ULONG);
int ParseCmd(char *com, char *cwd, char *filename);
void __saveds __asm addchr(register __a1 APTR);


void __saveds __asm addchr(register __a1 APTR c)
{
	if(dsp<20)
		datestring[dsp++] = (char)c;
}

int ParseCmd(char *cmd, char *cwd, char *filename)
{
	char a;
	struct Suffix *sf;
	int fn = strlen(filename), fno = 0;
	char *sfxcmd = NULL;

	sf = (struct Suffix *)Suffixes.lh_Head;
	while((sf->sf_node.ln_Succ))
	{
		int i;

		i = strlen(sf->sf_node.ln_Name);
		if(!stricmp(filename+fn-i, sf->sf_node.ln_Name))
		{
			sfxcmd = sf->sf_command;
			break;
		}

		sf = (struct Suffix *)sf->sf_node.ln_Succ;
	}

	if(sfxcmd)
	{
		while((a = *sfxcmd++))
		{
			if(a=='%')
			{
				a = *sfxcmd++;
				if(a=='f') /* Filename */
				{
					cmd += sprintf(cmd, "%s%s", cwd, filename);
					fno = 1;
				}
				else if(a=='s')
				{
					cmd += sprintf(cmd, "%s", sf->sf_node.ln_Name);
				}
				else
				{
					*cmd++ = '%';
					*cmd++ = a;
				}
			}
			else
			{
				*cmd++ = a;
			}
		}
		/* If filename was not already used, append it */
		if(!fno)
			cmd += sprintf(cmd, " %s%s", cwd, filename);
		*cmd = '\0';
		return sf->sf_node.ln_Type;
	}
	return 0;
}

long MyRunCommand(char *command,char *args,BPTR in,BPTR out)
{
	int ret, len=strlen(args);
	char buf2[200], *buf=(char *)((long)(buf2+3)&~3);
	BPTR oldname, oldmodule;
	struct TagItem stags[4];
	BPTR seglist_cmd, lock, oldin=Input(), oldout=Output();
	struct CommandLineInterface *Mycli = (struct CommandLineInterface *)((long)((struct Process *)SysBase->ThisTask)->pr_CLI << 2);
	char *commandbuffer;
	int size;

/*	while((long)buf & 3)
		buf++;*/	/* make it a BPTR:able */

	if(!in)
		in=Input();
	if(!out)
		out=Output();

	if(ArpBase)
		return SyncRun(command, args, in, out);

	SelectInput(in);
	SelectOutput(out);

	oldname = Mycli->cli_CommandName;
	args[len] = '\n';
	args[len+1] = '\0';
	buf[0] = strlen(command);
	strncpy(buf+1, command, 180);

	if((lock = Lock(command, ACCESS_READ)))
	{
	    stags[0].ti_Tag = TAG_DONE;
	    stags[0].ti_Data = 0;

		seglist_cmd = NewLoadSeg(command, stags);

		if(seglist_cmd)
		{
			UnLock(lock);
			Mycli->cli_CommandName = (long)buf/4;	/* SetProgramName(buf); */
			if(Mycli->cli_DefaultStack<1000)
				Mycli->cli_DefaultStack=1000;

			oldmodule = Mycli->cli_Module;
			Mycli->cli_Module = seglist_cmd;

			ret = RunCommand(seglist_cmd,
							 Mycli->cli_DefaultStack * 4,
							 (UBYTE *)args,
							 strlen(args));

			UnLoadSeg(Mycli->cli_Module);
			Mycli->cli_Module = oldmodule;
			Mycli->cli_CommandName = (long)oldname;	/* SetProgramName(oldname*4); */

			args[len] = '\0';		/* remove '\n' from args */

			SelectInput(oldin);
			SelectOutput(oldout);

			return ret;
		}
		UnLock(lock);
	}

	size = strlen(command) + strlen(args) + 2;
	if((commandbuffer = (char *)AllocMem(size, MEMF_PUBLIC | MEMF_CLEAR)))
	{
		sprintf(commandbuffer,"%s %s",command,args);	/* '\n' included */
		args[len]='\0';
		stags[0].ti_Tag = SYS_Input;
		stags[0].ti_Data= in;
		stags[1].ti_Tag = SYS_Output;
		stags[1].ti_Data= out;
		stags[2].ti_Tag = TAG_DONE;

		ret = System(commandbuffer, stags);
		FreeMem(commandbuffer, size);
	}
	SelectInput(oldin);
	SelectOutput(oldout);

	return ret;
}


char *printout(ULONG strnum)
{
	if(catalog)
		return (char *)GetCatalogStr(catalog, strnum, AppStrings[strnum].as_Str);
	else
		return (char *)AppStrings[strnum].as_Str;
}


BOOL istext(char *filename, char *dir)
{
	FILE	*fp;
	char	name[256];
	int		pnt, j, i;

	sprintf(name,"%s%s",dir,filename);
	if((fp=fopen(name,"r")))
	{
		pnt = fread(name, 256, 1, fp);
		for(i=0;i<pnt;i++)
		{
			j = name[i];
			if(j<0x20)
			{
				if(j!='\r' && j!='\n' && j!='\t' && j!='\033' && j!='\b' && j!='\a' && j!=0x0c)
				{
					fclose(fp);
					return FALSE;
				}
			}
			else if(j>0x80 && j<0xa0 && j!=0x9b)
			{
				fclose(fp);
				return FALSE;
			}
		}
		fclose(fp);
	}
	return TRUE;
}


void cleanexit(int error)
{
	struct Suffix *sf;

	while((sf = (struct Suffix *)RemHead(&Suffixes)))
	{
		FreeMem(sf, sf->sf_size);
	}
	if(firstnode)
	{
		freefiles();
	}
	if(flag)
	{
		FreeMem(flag, maxfiles);
	}
	if(LocaleBase)
	{
		if(catalog)
			CloseCatalog(catalog);
		if(localepointer)
			CloseLocale(localepointer);
		CloseLibrary((struct Library *)LocaleBase);
	}
	setmode(MODE_CONSOLE);
	if(ArpBase)
		CloseLibrary(ArpBase);
	printf("\033[%d;1H\n\n", rows);
	exit(error);
}


void openthings(void)
{
	NewList(&Suffixes);
	if(DOSBase->dl_lib.lib_Version<36)
	{
		if(!(ArpBase = (struct Library *)OpenLibrary("arp.library", 39)))
		{
			printf(printout(MSG_CANT_OPEN_ARP));
			cleanexit(10);
		}
	}
	setmode(MODE_RAW);
	if(LocaleBase = (struct LocaleBase *)OpenLibrary("locale.library", 38L))
		localepointer = (struct Locale *)OpenLocale("ENV:Sys/locale.prefs");
}


struct date *gimmedate(struct DateStamp *ds)
{
	long n;
	static struct date t1;

	n = ds->ds_Days-2251;
	t1.year = (4*n+3)/1461;
	n -= 1461*(long)t1.year/4;
	t1.year += 84;
	t1.month = (5*n+2)/153;
	t1.day = n-(153*t1.month+2)/5+1;
	t1.month += 3;
	if(t1.month>12)
	{
		t1.year++;
		t1.month -= 12;
	}
	if(t1.day<1 || t1.day>31 || t1.month<1 || t1.month>12 || t1.year<78)
	{
		t1.day = t1.month = 1;
		t1.year = 78;
	}
	return &t1;
}


char *convertdate(struct DateStamp *ds,char *temp)
{
	struct date *aika = gimmedate(ds);

	sprintf(temp, "%02d-%3s-%02d %02d:%02d",
			aika->day, months[aika->month-1], aika->year, ds->ds_Minute/60, ds->ds_Minute%60);
	return temp;
}


struct base *create_node(void)
{
	return (struct base *)AllocMem(sizeof(struct base), MEMF_CLEAR);
}


struct base *remove_node(struct base *node)
{
	struct base *help;

	if(!node)
		return node;

	help = node->next;
	FreeMem(node, sizeof(struct base));
	return help;
}


int scanfiles(char *directory,int level)
{
	__aligned struct FileInfoBlock fib;
	struct	base *new, *help, *current;
	struct	date *t;
	int		dir, buflen = strlen(directory)+85;
	char	*buffer = (char *)AllocMem(buflen, MEMF_PUBLIC | MEMF_CLEAR);
	BPTR	mylock;

	numberofbytes = numberoffiles = numberofdirs = 0;

	if(!buffer)
	{
		printf("Memory allocation error!\n");
		return 0;
	}

	if((mylock=Lock(directory,ACCESS_READ)))
	{
		Examine(mylock, &fib);
		while(ExNext(mylock, &fib))
		{
			if( strncmp(fib.fib_FileName,".",1) &&
				strcmp(fib.fib_FileName+strlen(fib.fib_FileName)-5, ".info") &&
				fib.fib_FileName[0])
			{
				if( !(options & OPT_README) &&
					!strcmp((fib.fib_FileName+strlen(fib.fib_FileName)-7), ".readme"))
				{
					continue;
				}

				if(kill || !(fib.fib_Protection & (FIBF_EXECUTE | FIBF_HIDDEN)))
				{
					int newflag = 1;

					t = gimmedate(&fib.fib_Date);
					if(t->year<lastyear || (t->year==lastyear && t->month<lastmonth)
						|| (t->year==lastyear && t->month==lastmonth && t->day<lastday))
					{
						newflag = 0;
					}

					if(fib.fib_DirEntryType>0)
					{
						dir = DIR_DIR;
						numberofdirs++;
					}
					else
					{
						if(hideold && !newflag)
							continue;

						dir = 0;
						numberoffiles++;
					}
					if(!(new = create_node()))
					{
						UnLock(mylock);
						freefiles();
						FreeMem(buffer,buflen);
						return 0;
					}
					new->directory = dir;
					strncpy(new->filename, fib.fib_FileName, NAME_LENGTH-1);
					new->filename[NAME_LENGTH-1] = '\0';
					new->size = fib.fib_Size;
					numberofbytes += new->size;
					new->date = fib.fib_Date;
					strncpy(new->comment, fib.fib_Comment, COMM_LENGTH-1);
					new->comment[COMM_LENGTH-1] = '\0';

					if(newflag)
						new->flags = FLAG_NEW;
					else
						new->flags = 0;

					if((current=firstnode))
					{
						if(stricmp(new->filename, current->filename)>0)
						{
							while(current->next && stricmp(new->filename, current->next->filename)>0)
								current=current->next;
							if((help=current->next))	/* ins to middle */
							{
								current->next = new;
								new->next = help;
							}
							else	/* ins to tail */
							{
								current->next = new;
							}
						}
						else	/* add to head */
						{
							new->next = firstnode;
							firstnode = new;
						}
					}
					else
					{
						firstnode = new;
					}
				}
			}
		}
		UnLock(mylock);
	}

	if(!level && paths>1)
	{
		for(dir=0;dir<paths;dir++)
		{
			if((new = create_node()))
			{
				new->directory = DIR_LINK;
				strncpy(new->filename, filepath[dir], NAME_LENGTH-1);
				new->filename[NAME_LENGTH-1] = '\0';
				new->date.ds_Days = 0;
				new->date.ds_Minute = 0;
				new->date.ds_Tick = 0;
				new->size = 0;
				sprintf(new->comment, "%ld", dir);
				if(!strcmp(directory, filepath[dir]))
					new->flags |= FLAG_NEW;
				else
					new->flags &= ~FLAG_NEW;

				new->next = firstnode;
				firstnode = new;
				numberofdirs++;
			}
			else
			{
				freefiles();
				FreeMem(buffer, buflen);
				return 0;
			}
		}
	}

	if(maxfiles < numberoffiles+numberofdirs)
	{
		if(flag)
		{
			FreeMem(flag, maxfiles);
		}
		maxfiles = numberoffiles + numberofdirs;
		if(maxfiles<128)
		{
			maxfiles = 128;
		}

		flag = (char *)AllocMem(maxfiles, MEMF_CLEAR);
		if(!flag)
		{
			freefiles();
			FreeMem(buffer, buflen);
			return 0;
		}
	}
	else
	{
		for(dir=0;dir<maxfiles;dir++)
			flag[dir] = 0;
	}
	FreeMem(buffer, buflen);
	return numberoffiles;
}


void freefiles()
{
	while((firstnode = remove_node(firstnode)));
}

#define M_NOR	0
#define M_ESC	1
#define M_CSI	2

int getkey(BOOL cursors)
{
	int mode=M_NOR;
	unsigned char c;

    while(1)
    {
		c = 0;
		if(WaitForChar(Input(), 10000000))
		{
			if(Read(Input(), &c, 1) < 1)
				return -1;
		}
		else
		{
			mode = M_NOR;
		}

		if(SetSignal(0,0) & SIGBREAKF_CTRL_C)
			return -1;

		if(mode==M_NOR && (c=='n' || c=='p' || c==0x0e || c==0x10))
		{
			mode = M_CSI;
			if(c=='n' || c==0x0e)
				c = 'B';
			else
				c = 'A';
		}

		if(mode==M_NOR)
		{
			switch(c)
			{
			case 0x03:	/* ^C */
				return -1;
			case 0x0a:
			case 0x0d:
				fflush(stdout);
				return 0;

    		case 0x09:	/* TAB */
    			if(flag && curpos<maxfiles)
    			{
					flag[curpos] = !flag[curpos];
					printf("\033[%d;2H%c", curpos-curframe*(xrows-1)+2, flag[curpos]?0x2a:0x20);
				}
				break;
			case 'o':	/* Hide/show old files */
				hideold = 1-hideold;
				printf("\033[%d;0H%s\033[K", xrows+2, hideold?"Show only new files":"Show all files");
				fflush(stdout);
				break;
			case 0x1b:
				mode = M_ESC;
				break;
			case 0x9b:
				mode = M_CSI;
				break;
			default:
				if(c>0x1f && (c<0x80 || c>0x9f))
				{
					fflush(stdout);
					return c;
				}
			}
		}
		else if(mode==M_ESC)
		{
			if(c=='[')
				mode=M_CSI;
			else
				mode=M_NOR;
		}
		else
		{
			if(cursors)
			{
				switch(c)
				{
				case 'A':
					if(--curpos<0)
					{
						curpos = 0;
					}
					else
					{
						if(!((curpos+1)%(xrows-1)))
						{
							updateheaderscreen();
						}
						else
						{
							printf("\033[%d;0H \033[%d;0H>",
									(curpos+3-curframe*(xrows-1)),
									(curpos+2-curframe*(xrows-1)));
						}
					}
					break;
				case 'B':
					if(++curpos>=numberoffiles+numberofdirs)
						curpos = numberoffiles+numberofdirs-1;
					if(curpos+1>(curframe+1)*(xrows-1))
					{
						updateheaderscreen();
					}
					else
					{
						if(curpos+1-curframe*(xrows-1) > 0)
							printf("\033[%d;0H ", (curpos+1-curframe*(xrows-1)));
						printf("\033[%d;0H>", (curpos+2-curframe*(xrows-1)));
					}
					break;
				case 'C':
					fflush(stdout);
					return 'e';
				case 'D':
					fflush(stdout);
					return '.';
				}
			}
			if((c>='a' && c<='z') ||
				(c>='A' && c<='Z'))
				mode = M_NOR;
		}
		fflush(stdout);
	}
	return 0;
}


void readconfig(void)
{
	FILE	*fp;
	char	thisline[PATHSIZE], nextline[PATHSIZE], temp[82];
	BPTR	lock;
	int		len, i, accesslevel;

    /* read user's config */

	/*	This new version works with the shell variables and
		also without AXsh, but it needs "accesslevel" valid
		and the levels spelled correctly..
	 */
#if 1
	if(ArpBase)
	{
		who(COM_GET, NULL, "accesslevel", temp);
	}
	else
	{
		GetVar("accesslevel", temp, sizeof(temp), GVF_LOCAL_ONLY);
	}
	accesslevel = 0; /* Default level (visitor) */
	for(i=0;UID_Name[i];i++)
	{
		if(UID_Name[i])
		{
			if(!stricmp(UID_Name[i], temp))
			{
				accesslevel = i;
				break;
			}
		}
	}
	if((accesslevel<<UID_ACCESSSHIFT)>=UID_PRIVILEGED)
		edit = TRUE;

	if((accesslevel<<UID_ACCESSSHIFT)>=UID_SUPERUSER)
		kill = TRUE;
#else
	accesslevel = ((((struct Process *)(SysBase->ThisTask))->pr_Pad & UID_ACCESSMASK) >> UID_ACCESSSHIFT);
	if((((struct Process *)(SysBase->ThisTask))->pr_Pad & UID_ACCESSMASK)>=UID_PRIVILEGED)
		edit = TRUE;

	if(((struct Process *)(SysBase->ThisTask))->pr_Pad & UID_SUPERUSER)
		kill = TRUE;
#endif

	if(ArpBase)
	{
		who(COM_GET, NULL, "lastlogin", temp);
	}
	else
	{
		GetVar("lastlogin", temp, sizeof(temp), GVF_LOCAL_ONLY);
	}
	len = strlen(temp)-9;
	if(len>9)
	{
		temp[len] = '\0';
		while(strncmp(temp+len-6, months[lastmonth++], 3));
		lastyear = atoi(temp+len-2);
		temp[len-7] = '\0';
		lastday = atoi(temp+len-9);
	}

	if(ArpBase)
	{
		who(COM_GET, NULL, "visual", visual);
		strcpy(temp,"24");
		who(COM_GET, NULL, "home", home);
		who(COM_GET, NULL, "rows", temp);
		rows = atoi(temp);
		if(!rows)
			rows = 24;	/* default */
		who(COM_GET, NULL, "columns", temp);
		columns = atoi(temp);
		if(!columns)
			columns = 80;	/* default */

		who(COM_GET, NULL, "language", language);
	}
	else
	{
		if(GetVar("visual", visual, sizeof(visual), GVF_LOCAL_ONLY) == -1)
		{
			strcpy(visual, "visual");
		}
		if(GetVar("rows", temp, sizeof(temp), GVF_LOCAL_ONLY) != -1)
		{
			rows = atoi(temp);
		}
		if(GetVar("columns", temp, sizeof(temp), GVF_LOCAL_ONLY) != -1)
		{
			columns = atoi(temp);
		}
		if(GetVar("home", home, sizeof(home), GVF_LOCAL_ONLY) == -1)
		{
			strcpy(home, "AXsh:");
		}
		if(GetVar("language", language, sizeof(language), GVF_LOCAL_ONLY) == -1)
		{
			language[0] = '\0';
		}
	}
	if(columns>160)
		columns = 160;

	if(rows<1)
	{
		rows = 24;
		printf(printout(MSG_ROWS_NOT_SET));
		Delay(50);
	}

	if(language[0] && LocaleBase)
	{
		if(!(catalog = (struct Catalog *)OpenCatalog(NULL, "AXsh.ffe.catalog", OC_Language, language, TAG_DONE)))
		{
			printf("Can't access language %s - using English as default\n", language);
			Delay(35);
		}
	}

	for(i=accesslevel;i>=0;i--)
	{
		if(UID_Name[i] && strlen(UID_Name[i]))
		{
			sprintf(temp, "AXsh:etc/rc.ffe.%s", UID_Name[i]);
			if((fp = fopen(temp, "rb")))
			{
				break;
			}
		}
	}
	if(!fp)
	{
		if((fp = fopen("AXsh:etc/rc.ffe","rb")))
		{
			strcpy(temp, "AXsh:etc/rc.ffe");
		}
		else
		{
			printf(printout(MSG_NO_RC));
			cleanexit(0);
		}
	}
	while(!feof(fp))
	{
		if(fgets(thisline, PATHSIZE, fp)==0)
			break;
		if(thisline[0]=='%')
		{
			fgets(nextline, PATHSIZE, fp);
			nextline[strlen(nextline)-1] = '\0';

			if(!strncmp("%statuswindowrows:",thisline,18))
			{
				statuswindowsize = atoi(nextline);
				if(statuswindowsize<7)
					statuswindowsize = 7;
				if(statuswindowsize > rows-5)
					statuswindowsize = rows - 5;
			}
			else if(!strncmp("%filepath:", thisline, 7))
			{
				if(lock=Lock(nextline, ACCESS_READ))
				{
					UnLock(lock);
					if(paths<MAXFILEDIRS)
					{
						if( nextline[strlen(nextline)-1]!=':' &&
							nextline[strlen(nextline)-1]!='/')
							strcat(nextline, "/");
						strcpy(filepath[paths++], nextline);
					}
					else
					{
						printf("Too many filedirs\n");
					}
				}
			}
			else if(!strncmp("%morecmd:", thisline, 9))
			{
				len = 0;
				while(nextline[len] && nextline[len]!='\t' && nextline[len]!=' ')
					len++;
				strncpy(morecmd, nextline, len);
				morecmd[len] = '\0';
				strcpy(moreopt, nextline+len);
			}
			else if(!strncmp("%descpath:", thisline, 10))
			{
				strcpy(descpath, nextline);
			}
			else if(!strncmp("%xprpath:", thisline, 9))
			{
				strcpy(xprpath, nextline);
			}
			else if(!strncmp(thisline, "%suffix:", 8))
			{
				struct Suffix *sf;
				char *a;
				int size = sizeof(struct Suffix) + strlen(nextline) + 1;

				sf = (struct Suffix *)AllocMem(size, MEMF_CLEAR);

				if(sf)
				{
					a = sf->sf_node.ln_Name = ((char *)sf)+sizeof(struct Suffix);
					if(nextline[0]=='!')
					{
						strcpy(sf->sf_node.ln_Name, nextline+1);
						sf->sf_node.ln_Type = 2;
					}
					else
					{
						strcpy(sf->sf_node.ln_Name, nextline);
						sf->sf_node.ln_Type = 1;
					}

					while(*a && *a!='\t' && *a!=' ')
						a++;
					if(*a)
					{
						*a++ = '\0';
						while(*a=='\t' || *a==' ')
							a++;
					}
					sf->sf_command = a;
					sf->sf_size = size;
					AddTail(&Suffixes, (struct Node *)sf);
				}
				else
				{
					printf("Memory allocation error while reading suffixes!\n");
				}
			}
		}
	}
	if(!paths)
	{
		strcpy(filepath[paths++], "AXsh:Info/");
	}
	xrows = rows - statuswindowsize;
	fclose(fp);
}

void updateheaderscreen(void)
{
	int i=0, last, first, longest=0;
	struct base *start;
	char temp[160]; /* Must be 160 */

	while(curpos+1>i)
		i += xrows-1;
	if((curframe = i/(xrows-1)-1)<0)
		curframe = 0;
	if((last = (curframe+1)*(xrows-1)) > numberoffiles+numberofdirs)
		last = numberoffiles+numberofdirs;

	start = gotoframe(curframe);
	for(i=first=curframe*(xrows-1);i<last;i++)
	{
		if(start)
		{
			int k = strlen(start->filename);

			if(start->directory==DIR_DIR)
				++k;

			if(k>longest)
				longest = k;

			start = start->next;
		}
	}

	start = gotoframe(curframe);
	for(i=first=curframe*(xrows-1);i<last;i++)
	{
		printf("\033[%d;0H", (i-first+2));
		if(curpos==i)
			printf(">");
		else
			printf(" ");

		if(flag && i<maxfiles && flag[i])
			printf("*");
		else
			printf(" ");

		printf("%3d", i+1);

		if(start)
		{
			if(start->flags & FLAG_NEW)
				printf("\033[1m");

/*			if(localepointer)
			{
				dsp = 0;
				FormatDate(localepointer, "%d-%b-%y", &start->date,&hook);
			}
			else*/
				convertdate(&start->date, datestring);

			if(start->directory==DIR_LINK)
			{
				printf("%8d %s => %s\033[K", start->size, datestring, start->filename);
			}
			else
			{
				if(start->comment[0])
				{
					if(start->directory==DIR_DIR)
					{
						sprintf(temp, "%8d %s %s/", start->size, datestring, start->filename);
					}
					else
					{
						sprintf(temp, "%8d %s %s ", start->size, datestring, start->filename);
					}
					strcat(temp, "                                ");
					temp[26+longest] = '\"';

					/* Observe: columns is always <= 160 */

					strncpy(temp+27+longest, start->comment, columns-27-longest);

					if(strlen(temp)>columns-9)
					{
						strcpy(temp+columns-11, "..");
					}
					else
					{
						/* Add a terminating NUL to be certain there is one. */
						temp[columns-9] = '\0';
					}
					printf("%s\"\033[K", temp);
				}
				else
				{
					if(start->directory==DIR_DIR)
						printf("%8d %s %s/\033[K", start->size, datestring, start->filename);
					else
						printf("%8d %s %s\033[K", start->size, datestring, start->filename);
				}
			}

			if(start->flags & FLAG_NEW)
				printf("\033[m");
			printf("\n");

			start = start->next;
		}
	}

	if(lowest>xrows+1)
		lowest = xrows+1;
	if(last-first+2<lowest+1)
	{
		for(i=last-first+2;i<lowest;i++)
			printf("\033[%d;0H\033[K", i);
	}
	lowest = last-first+2;

	if(numberoffiles+numberofdirs == 0)
	{
		if(hideold)
			printf("\033[2;0HNo new files");
		else
			printf("\033[2;0HNo files");
	}
	fflush(stdout);
}

struct base *gotoframe(int framenumber)
{
	int i;
	struct base *temp = firstnode;

	for(i=0;i<(xrows-1)*framenumber;i++)
	{
		if(temp)
			temp = temp->next;
	}
	return temp;
}

void updatestatusline(char *cwd)
{
	printf(printout(MSG_STATUS_1), 1);
	if(columns>80)
	{
		printf("\033[7m%s\033[0m", bar+sizeof(bar)-columns+80);
	}
	updatemidstatusline(cwd);
	printf(printout(MSG_STATUS_3), rows);
	if(columns>80)
	{
		printf("\033[7m%s\033[0m", bar+sizeof(bar)-columns+80);
	}
}

void updatemidstatusline(char *cwd)
{
	char *a=cwd;

	if(strlen(a)>27)
	{
		a=cwd+strlen(cwd)-27;
		while(*a && *a!=':' && *a!='/')
			a++;
	}
	printf(printout(MSG_STATUS_2),
			xrows+1,
			numberofdirs,
			numberoffiles,
			numberofbytes/1024,
			a);
	if(columns>80)
	{
		printf("\033[7m%s\033[0m", bar+sizeof(bar)-columns+80);
	}
}


void mainmenu(void)
{
	static int oldcurpos[MAXLEVELS];
	static char protocol[MAXPROTOS][20], protodesc[MAXPROTOS][80];
	int j, i, maxprotos, level = 0, key;
	char com[MAXLEN], temp[MAXLEN], cwd[MAXLEN];
	BOOL show_files=TRUE;
	struct base *ping;
	FILE *fp;
	BPTR file;

	strcpy(cwd, filepath[0]);
	numberoffiles = scanfiles(filepath[0], level);
	if(paths>1)
		curpos = paths;
	updatestatusline(cwd);
	while(1)
	{
		if(!show_files)
		{
			key = getkey(TRUE);
			if(key<0)
				break;
		}

		if((SetSignal(0,0) & SIGBREAKF_CTRL_C) ||
			key=='x' || key=='X' || key=='q' || key=='Q')
		{
			freefiles();
			break;
		}

		if(key=='.')
		{
			if(level)
			{
				i = strlen(cwd)-2;
				while(cwd[i]!=':' && cwd[i]!='/')
					i--;
				cwd[i+1] = '\0';
				freefiles();
				oldcurpos[level] = curpos;
				level--;
				numberoffiles = scanfiles(cwd, level);
				curpos = oldcurpos[level];

				if(curpos >= numberoffiles+numberofdirs)
				{
					curpos = numberoffiles+numberofdirs-1;
				}

				updatemidstatusline(cwd);
				show_files = TRUE;
			}
			else
			{
				printf("\a");
				fflush(stdout);
			}
		}
		else if(key=='?')
		{
			printf("\033[%d;1H\033", xrows+2);
			printf(printout(MSG_HELP_1));
			printf(printout(MSG_HELP_2));
			printf(printout(MSG_HELP_3));
			printf(printout(MSG_HELP_4));
			if(statuswindowsize>12)
			{
				printf(printout(MSG_HELP_5));
				printf(printout(MSG_HELP_6));
				printf(printout(MSG_HELP_7));
				printf(printout(MSG_HELP_8));
				printf(printout(MSG_HELP_9));
				printf(printout(MSG_HELP_10));
			}
			fflush(stdout);
		}

		/* Search the current node */
		ping = firstnode;
		for(j=0;j<curpos;j++)
		{
			if(ping)
				ping = ping->next;
		}

		if(!ping)
		{
			/* No files listed/selection bugged - below commands not available */
			printf("\a");
			fflush(stdout);
		}
		else if(key=='e' || key=='E' || key=='c' || key=='C' || key==' ')
		{
			i=0;
			if(ping->directory)
			{
				if(level<MAXLEVELS && strlen(cwd)+strlen(ping->filename)+1<MAXLEN)
				{
					if(ping->directory==DIR_DIR)
					{
						sprintf(com,"%s%s/", cwd, ping->filename);
						strcpy(cwd, com);
						freefiles();

						if(oldcurpos[level] == curpos)
						{
							curpos = oldcurpos[++level];
						}
						else
						{
							oldcurpos[level++] = curpos;
							curpos = 0;
							for(j=level; j<MAXLEVELS; j++)
								oldcurpos[j] = 0;
						}
						numberoffiles = scanfiles(cwd, level);

						if(curpos >= numberoffiles+numberofdirs)
						{
							curpos = numberoffiles+numberofdirs-1;
						}
						updatemidstatusline(cwd);
						show_files = TRUE;
					}
					else if(ping->directory==DIR_LINK)
					{
						strcpy(cwd, filepath[atoi(ping->comment)]);
						freefiles();
						level = 0;
						numberoffiles = scanfiles(cwd, level);
						if(paths>1)
							curpos = paths;
						else
							curpos = 0;

						if(curpos >= numberoffiles+numberofdirs)
						{
							curpos = numberoffiles+numberofdirs-1;
						}
						updatemidstatusline(cwd);
						show_files = TRUE;
					}
					else
					{
						printf("\a");
						fflush(stdout);
					}
				}
			}
			else
			{
				if(j = ParseCmd(com, cwd, ping->filename))
				{
					sprintf(temp, "%s.ffetmp", home);
					if(file = Open(temp, MODE_NEWFILE))
					{
						Execute(com, NULL, file);
						setmode(MODE_RAW);
						Close(file);
					}
					if(j==1)
					{
						printf("\033[%d;0H\033[J", xrows+2);
						fflush(stdout);
						sprintf(com,"%s %s.ffetmp",moreopt, home);
						MyRunCommand(morecmd, com, NULL, NULL);
						setmode(MODE_RAW);
						printf("\033[7m---EOF---\033[0m");
						fflush(stdout);
						key = getkey(FALSE);
						i=1;
					}
					sprintf(temp, "%s.ffetmp", home);
					DeleteFile(temp);
				}
				else
				{
					if(istext(ping->filename,cwd))
					{
						printf("\033[%d;0H\033[J", xrows+2);
						fflush(stdout);
						sprintf(com,"%s %s%s", moreopt, cwd, ping->filename);
						MyRunCommand(morecmd, com, NULL, NULL);
						setmode(MODE_RAW);
						printf("\033[7m---EOF---\033[0m");
						fflush(stdout);
						key = getkey(FALSE);
						i=1;
					}
					else
					{
						printf("\a");
						fflush(stdout);
					}
				}
			}

			if(i==1)
			{
				updatestatusline(cwd);
				show_files = TRUE;
			}
		}
		else if((key=='w' || key=='W') && edit)
		{
			if(ping->directory)	
				sprintf(com, "\"%s_%s.dsc\"", descpath, ping->filename);
			else
				sprintf(com, "\"%s%s.dsc\"", descpath, ping->filename);
			MyRunCommand(visual, com, NULL, NULL);
			setmode(MODE_RAW);
			updatestatusline(cwd);
			show_files = TRUE;
		}
		else if((key=='K' || key=='k') && kill)
		{
			j=0;
			/*ping=firstnode;*/
			for(i=0;i<numberoffiles+numberofdirs;i++)
			{
				if(flag && i<maxfiles && flag[i])
					j++;
				/*ping=ping->next;*/
			}
			if(!j)
			{
				if(flag && curpos<maxfiles)
				{
					flag[curpos] = 1;
					j++;
				}
			}
			printf(printout(MSG_WANNA_DEL), xrows+2);
			if(j==1)
				printf(printout(MSG_SELECTED_FILE));
			else
				printf(printout(MSG_ALL_SELECTED_FILES), j);
			fflush(stdout);

			key = getkey(TRUE);
			if(key<0)
				break;

			if(key=='y' || key=='Y')
			{
				ping = firstnode;
				for(i=0; i<numberoffiles+numberofdirs; i++)
				{
					if(flag && i<maxfiles && flag[i])
					{
						flag[i] = 0;
						if(i<curpos)
						{
							curpos--;
						}
						if(strlen(cwd)+strlen(ping->filename)<MAXLEN)
						{
							strcpy(com, cwd);
							strcat(com, ping->filename);
							DeleteFile(com);
						}
						if(strlen(descpath)+strlen(ping->filename)<MAXLEN)
						{
							strcpy(com, descpath);
							strcat(com, ping->filename);
							strcat(com, ".dsc");
							DeleteFile(com);
						}
						if(strlen(cwd)+strlen(ping->filename)+6<MAXLEN)
						{
							strcpy(temp, ping->filename);
							j = strlen(temp)-1;
							while(j && temp[j]!='.')
								j--;
							if(temp[j]=='.')
							{
								strcpy(temp+j+1, "readme");
								sprintf(com, "%s%s", cwd, temp);
							}
							else
							{
								sprintf(com, "%s%s.readme", cwd, ping->filename);
							}
							DeleteFile(com);
						}
					}
					ping = ping->next;
				}
				freefiles();
				numberoffiles = scanfiles(cwd, level);
				if(curpos >= numberoffiles+numberofdirs)
				{
					curpos = numberoffiles+numberofdirs-1;
				}
			}
			updatestatusline(cwd);
			show_files=TRUE;
		}
		else if((key=='d' || key=='D'))
		{
			j = 0;
			ping = firstnode;
			for(i=0; i<numberoffiles+numberofdirs; i++)
			{
				if(flag && i<maxfiles)
				{
					if(flag[i] && !ping->directory)
						j++;
					else
						flag[i] = 0;
				}
				ping = ping->next;
			}
			if(!j)
			{
				ping = firstnode;
				for(i=0; i<curpos; i++)
					ping = ping->next;

				if(!ping->directory)
				{
					j++;
					if(flag && i<maxfiles)
						flag[i] = 1;
				}
			}
			if(j)
			{
				printf("\033[%d;0H\033[J", xrows+2);
				printf(printout(MSG_SELECT_PROTOCOL), xrows+2);
				maxprotos = 0;
				printf(printout(MSG_TRANSFER_TO_HOME));
				if((fp = fopen("AXsh:etc/xprotos.tab", "r")))
				{
					while(!feof(fp) && maxprotos<MAXPROTOS)
					{
						if(fgets(com,79,fp)==0)
							break;
						if(com[0]=='\"')
						{
							strncpy(protocol[maxprotos], com+1, 20);
							i = 1;
							while(com[i] && com[i]!='\"' && com[i]!='\n')
								i++;
							protocol[maxprotos][i-1] = 0;
						}
						else
						{
							if(!strncmp(com, "Descrip: ", 8))
							{
								if((i = strlen(com+8)-1)>56)
									i = 56;
								strncpy(protodesc[maxprotos], com+8, i);
								protodesc[maxprotos][i] = 0;
								printf("%d - %-16s %s\n", maxprotos+1, protocol[maxprotos], protodesc[maxprotos++]);
							}
						}
					}
					fclose(fp);
				}

				printf(printout(MSG_SELECT), maxprotos);
				fflush(stdout);

				key = getkey(FALSE);
				if(key<0)
					break;

				j = key-'0';
				if(j>=0 && j<maxprotos)
				{
					ping = firstnode;
					if(j>0 && j<maxprotos)
					{
						sprintf(com, "send %s ", protocol[j-1]);
						for(i=0;i<numberoffiles+numberofdirs;i++)
						{
							if(flag && i<maxfiles && flag[i] && !ping->directory)
							{
								if(strlen(cwd)+strlen(ping->filename)+strlen(com)<MAXLEN)
								{
									strcat(com, "\"");
									strcat(com, cwd);
									strcat(com, ping->filename);
									strcat(com, "\"");
									strcat(com, " ");
								}
								else
								{
									MyRunCommand(xprpath, com, NULL, NULL);
									sprintf(com, "send %s \"%s%s\" ", protocol[j-1], cwd, ping->filename);
								}
								flag[i] = 0;
							}
							ping = ping->next;
						}
						if(strlen(com))
						{
							MyRunCommand(xprpath, com, NULL, NULL);
						}
						setmode(MODE_RAW);
						Delay(100);
					}
					else
					{
						for(i=10;i>=0;i--)
							printf("\033[%d;0H\033[K", i+xrows+2);

						if(!j)
						{
							ping = firstnode;
							printf("Copying");
							fflush(stdout);
							for(i=0;i<numberoffiles+numberofdirs;i++)
							{
								if(flag && i<maxfiles && flag[i] && !ping->directory)
								{
									sprintf(com, "c:copy >NIL: \"%s%s\" %s", cwd, ping->filename, home);
									Execute(com, NULL, NULL);
									printf(".");
									fflush(stdout);
								}
								ping = ping->next;
							}
							for(i=0;i<statuswindowsize-2;i++)
								printf("\033[%d;0H\033[K", i+xrows+2);
							setmode(MODE_RAW);
						}
						fflush(stdout);
					}
				}
				updatestatusline(cwd);
				show_files = TRUE;
				for(i=0;i<numberoffiles+numberofdirs;i++)
					if(flag && i<maxfiles)
						flag[i] = 0;
			}
			else
			{
				printf("\a");
				fflush(stdout);
			}
		}
		else if((!key || key=='s' || key=='S') && !show_files)
		{
			printf("\033[%d;0H", xrows+2);

			if(ping->directory)
				sprintf(com, "%s_%s.dsc", descpath, ping->filename);
			else
				sprintf(com, "%s%s.dsc", descpath, ping->filename);

			if(!(fp=fopen(com, "rb")))
			{
				strcpy(temp, ping->filename);
				i = strlen(temp)-1;
				while(i && temp[i]!='.')
					i--;
				if(temp[i]=='.')
				{
					BPTR newlock;

					strcpy(temp+i+1, "readme");
					i = 0;
					sprintf(com,"%s%s", cwd, temp);
					if(newlock = Lock(com, ACCESS_READ))
					{
						UnLock(newlock);
						printf("\033[%d;0H\033[J", xrows+2);
						fflush(stdout);
						sprintf(com,"%s %s%s", moreopt, cwd, temp);
						MyRunCommand(morecmd, com, NULL, NULL);
						setmode(MODE_RAW);
						printf("\033[7m---EOF---\033[0m");
						fflush(stdout);
						key = getkey(FALSE);

						updatestatusline(cwd);
						show_files = TRUE;
						i = -1;
					}
				}
				if(!i)
				{
					if(!ping->comment[0])
					{
						printf(printout(MSG_NO_DESC));
						if(ping->directory)
							printf(printout(MSG_DIR));
						else
							printf(printout(MSG_FILE));
						printf(printout(MSG_AVAILABLE), ping->filename);
					}
					else
						printf("%s\033[K", ping->comment);
					for(i=0;i<statuswindowsize-3;i++)
						printf("\033[%d;0H\033[K", i+xrows+3);
				}
			}
			else
			{
				j = 0;
				while(j++<statuswindowsize-2 && !feof(fp))
				{
					if(fgets(com, 79, fp)==0)
						break;
					if(strlen(com))
						com[strlen(com)-1] = '\0';
					printf("\033[%d;0H%s\033[K", xrows+j+1, com);
				}
				fclose(fp);
				for(i=j-1;i<statuswindowsize-2;i++)
					printf("\033[%d;0H\033[K", i+xrows+2);
			}
			fflush(stdout);
		}

		if(show_files)
		{
			updateheaderscreen();
			show_files = FALSE;
			for(i=0;i<statuswindowsize-2;i++)
				printf("\033[%d;0H\033[K", i+xrows+2);
			fflush(stdout);
		}
		key = 0;
	}
}


void main(int argc, char *argv[])
{
	int n;

	curpos = curframe = options = 0;
	openthings();
	readconfig();
/*	hook.h_Entry=addchr;*/

	for(n=1;n<argc;n++)
	{
		if(!strcmp(argv[n],"-r"))
		{
			options |= OPT_README;
		}
		else
		{
			fprintf(stderr,"Usage: %s [-r]\n", argv[0]);
			fprintf(stderr,"       -r  Dont hide .readme-files\n");
			exit(10);
		}
	}

	if(columns<80)
	{
		printf("You need 80 columns to run %s\n", argv[0]);
	}
	else if(rows<22)
	{
		printf(printout(MSG_MORE_LINES));
	}
	else
	{
		mainmenu();
	}
	cleanexit(0);
}
