/*	ls.c for AXsh by Juha 20-Feb-92
	Copyright (C) 1992 Digital Design
	This version of ls supports Commodore network and AXsh 2.0 external
	protection bits (group/other) */

#include <exec/types.h>
#include <exec/execbase.h>
#include <exec/memory.h>
#include <libraries/dos.h>
#include <libraries/dosextens.h>
#include <proto/exec.h>
#include <proto/dos.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>


#define NAME_LENGTH	32
#define COMM_LENGTH	80
#define OUTPUTWIDTH 78
#define MAXCOLS		20

void __regargs __chkabort(void);
void __regargs __chkabort(void){}
void chkabort(void) {}
int CXBRK(void){return 0;}

char *vers="\0$VER: ls 1.31 (06.2.93)\n";
char *usage=
"Usage: ls [-hRaldncC] [directory/file list]\n"\
"           -h, -?  help\n"\
"           -R      recursive\n"\
"           -a      show all\n"\
"           -l      long listing\n"\
"           -d      show the directory itself\n"\
"           -o      show file owners\n"\
"           -n      no sorting\n"\
"           -c      show comments (sets -l too)\n"\
"           -C      one-column mode\n";


struct base
{
	struct  Node node;
	char	filename[NAME_LENGTH];		/* name of the file */
	LONG	dirtype;					/* dirtype */
	LONG	protection;					/* extented protection bits */
	LONG	size;						/* size of the file in bytes */
	LONG	numblocks;					/* size of the file in blocks */
	struct	DateStamp date;				/* last changed */
	char	comment[COMM_LENGTH];		/* comment line */
	UWORD	OwnerUID;
	UWORD	OwnerGID;
};


#define FLAG_HELP		(1<<0)
#define FLAG_ALL		(1<<1)
#define FLAG_LONG		(1<<2)
#define FLAG_OWNER		(1<<3)
#define FLAG_RECURSIVE	(1<<4)
#define FLAG_FILENOTES	(1<<5)
#define FLAG_ONECOLUMN	(1<<6)
#define FLAG_DIRECTORY	(1<<7)
#define FLAG_NOSORT		(1<<8)


int  readdir(char *directory,long flags);
char *convertdate(struct DateStamp *ds,char *string);


int readdir(char *directory,long flags)
{	struct	FileInfoBlock *fib=(struct FileInfoBlock *)AllocMem(sizeof(*fib),MEMF_PUBLIC | MEMF_CLEAR);
	struct	base *new,*help;
	int		i,buflen=strlen(directory)+85+180;
	char	*buffer=(char *)AllocMem(buflen,MEMF_PUBLIC | MEMF_CLEAR);
	char	*temp,*apu;
	BPTR	mylock;
	struct  List filelist;

	if(!fib || !buffer)
	{
		Write(Output(),"Memory allocation error!\n",25);
		goto errorexit;
	}
	temp=buffer+buflen-180;
	apu=temp+160;
	NewList(&filelist);

	if((mylock=Lock(directory,ACCESS_READ))	&&	Examine(mylock,fib))
	{
		if(fib->fib_DirEntryType==ST_FILE || (flags & FLAG_DIRECTORY))
		{
			if(!(help=(struct base *)AllocMem(sizeof(struct base),MEMF_CLEAR)))
			{
				Write(Output(),"Memory allocation error!\n",25);
				UnLock(mylock);
				goto errorexit;
			}
			strncpy(help->filename,fib->fib_FileName,NAME_LENGTH-2);
			help->filename[NAME_LENGTH-2]=0;
			help->protection=fib->fib_Protection;
			help->size=fib->fib_Size;
			help->numblocks=fib->fib_NumBlocks;
			help->date=fib->fib_Date;
			strcpy(help->comment,fib->fib_Comment);
			help->dirtype=fib->fib_DirEntryType;
			help->OwnerUID=fib->fib_OwnerUID;
			help->OwnerGID=fib->fib_OwnerGID;
			AddHead(&filelist,(struct Node *)help);
		}
		else
		{
			while(ExNext(mylock,fib))
			{
				if( ((flags & FLAG_ALL) && fib->fib_FileName[0]) ||
					(!(flags & FLAG_ALL) && (strncmp(fib->fib_FileName,".",1) && strcmp((fib->fib_FileName+strlen(fib->fib_FileName)-5),".info") && fib->fib_FileName[0] && !(fib->fib_Protection & (1<<7)))))
				{
					if(fib->fib_DirEntryType==ST_ROOT || fib->fib_DirEntryType==ST_USERDIR)
					{
						if(flags & FLAG_RECURSIVE)
						{
							i=*(directory+strlen(directory)-1);
							if(*directory && i!='/' && i!=':')
								sprintf(buffer,"%s/%s",directory,fib->fib_FileName);
							else
								sprintf(buffer,"%s%s",directory,fib->fib_FileName);
							if(readdir(buffer,flags))
							{	UnLock(mylock);
								goto errorexit;
							}
							continue;
						}
					}

					if(!(new=(struct base *)AllocMem(sizeof(struct base),MEMF_CLEAR)))
					{
						UnLock(mylock);
						goto errorexit;
					}
					new->dirtype=fib->fib_DirEntryType;
					strncpy(new->filename,fib->fib_FileName,30);
					new->protection=fib->fib_Protection;
					new->size=fib->fib_Size;
					new->numblocks=fib->fib_NumBlocks;
					new->date=fib->fib_Date;
					strcpy(new->comment,fib->fib_Comment);
					new->OwnerUID=fib->fib_OwnerUID;
					new->OwnerGID=fib->fib_OwnerGID;

					if(flags & FLAG_NOSORT)
					{
						AddTail(&filelist,(struct Node *)new);
					}
					else
					{
						help=(struct base *)filelist.lh_Head;
						if(help->node.ln_Succ)
						{
							if(strcmp(new->filename,help->filename)>0)
							{
								while(help->node.ln_Succ->ln_Succ && strcmp(new->filename,((struct base *)(help->node.ln_Succ))->filename)>0)
									help=(struct base *)help->node.ln_Succ;

								if(help->node.ln_Succ->ln_Succ)
								{
									Insert(&filelist,(struct Node *)new,(struct Node *)help);
								}
								else
									AddTail(&filelist,(struct Node *)new);
							}
							else
								AddHead(&filelist,(struct Node *)new);
						}
						else
							AddHead(&filelist,(struct Node *)new);
					}

				}
				if(SetSignal(0L,SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C)
				{
					UnLock(mylock);
					Write(Output(),"\n***Break\n",10);
					goto errorexit;
				}
			}
			if(*directory)
			{
				Write(Output(),directory,strlen(directory));
				Write(Output()," (dir):\n",8);
			}
		}
		UnLock(mylock);
	}
	else
	{
		Write(Output(),"Can't open ",11);
		Write(Output(),directory,strlen(directory));
		Write(Output(),"\n",1);
		FreeMem(buffer,buflen);
		return FALSE;
	}

	help=(struct base *)filelist.lh_Head;
	if((flags & FLAG_LONG))
	{
		while(help->node.ln_Succ)
		{
			strcpy(buffer,"dchsparwedrwedrwed");
			for(i=0;i<4;i++)
			{	if(help->protection & (1<<i))
					buffer[9-i]='-';
				if(!(help->protection & (1L<<(i+4))))
					buffer[5-i]='-';
				if(!(help->protection & (1L<<(i+8))))
					buffer[13-i]='-';
				if(!(help->protection & (1L<<(i+12))))
					buffer[17-i]='-';
			}
			if(help->dirtype==ST_FILE)
				buffer[0]='-';
			else
			if(help->dirtype==ST_LINKFILE)
				buffer[0]='l';
			else
			if(help->dirtype==ST_LINKDIR)
				buffer[0]='D';
			else
			if(help->dirtype==ST_SOFTLINK)
				buffer[0]='s';

			if(!help->comment[0])
				buffer[1]='-';

			buffer[19]=0;
			if(flags & FLAG_OWNER)
				sprintf(temp,"%18s uid=%-5d %8d %s %s",buffer,help->OwnerUID,help->size,convertdate(&help->date,apu),help->filename);
			else
				sprintf(temp,"%18s %5d %8d %s %s",buffer,help->numblocks,help->size,convertdate(&help->date,apu),help->filename);
			Write(Output(),temp,strlen(temp));

			if(help->dirtype==ST_SOFTLINK)
			{
				if(*directory)
					sprintf(temp,"%s/%s",directory,help->filename);
				else
					strcpy(temp,help->filename);
				if((mylock=Lock(temp,ACCESS_READ)) && Examine(mylock,fib))
				{
					UnLock(mylock);
					sprintf(temp," » %s\n",fib->fib_FileName);
					if(fib->fib_DirEntryType>0)
						strcat(temp,"/");
					Write(Output(),temp,strlen(temp));
				}
				else
					Write(Output()," » (Can't examine)\n",19);
			}
			else
			{
				Write(Output(),"\n",1);
			}

			if((flags & FLAG_FILENOTES) && buffer[1]!='-')
			{
				sprintf(temp,"\"%s\"\n",help->comment);
				Write(Output(),temp,strlen(temp));
			}

			help=(struct base *)help->node.ln_Succ;
			if(SetSignal(0L,SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C)
			{
				Write(Output(),"\n***Break\n",10);
				goto errorexit;
			}
		}
		Write(Output(),"\n",1);
	}
	else
	if(flags & FLAG_ONECOLUMN)
	{
		while(help->node.ln_Succ)
		{
			strcpy(buffer,help->filename);
			if(help->dirtype>=0)
			{
				if(help->dirtype==ST_SOFTLINK)
					strcat(buffer,"»");
				else
					strcat(buffer,"/");
			}
			Write(Output(),buffer,strlen(buffer));
			Write(Output(),"\n",1);
			if(SetSignal(0L,SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C)
			{
				Write(Output(),"\n***Break\n",10);
				goto errorexit;
			}
			help=(struct base *)help->node.ln_Succ;
		}
	}
	else
	{	char *temp2="                                ";
		int	longest=0,files=0,cols,rows,j,len=strlen(temp2);
		struct	base *rs[MAXCOLS];

		while(help->node.ln_Succ)
		{
			if(strlen(help->filename)>longest)
				longest=strlen(help->filename);
			files++;
			help=(struct base *)help->node.ln_Succ;
		}
		longest+=2;

		if((cols=OUTPUTWIDTH/longest)>MAXCOLS)
			cols=MAXCOLS;
		rows=files/cols+1;

		for(i=0;i<MAXCOLS;rs[i++]=NULL);

		help=(struct base *)filelist.lh_Head;
		for(j=i=0;i<rows*cols;i++)
		{
			if(help->node.ln_Succ)
			{
				if(!(i%rows))
				{
					rs[j++]=help;
				}
				help=(struct base *)help->node.ln_Succ;
			}
			else
			{
				if(!(i%rows))
				{
					rs[j++]=NULL;
				}
			}
		}

		for(i=0;i<rows;i++)
		{
			j=0;
			buffer[0]='\0';
			while(rs[j] && rs[j]->node.ln_Succ && j<cols)
			{
				strcat(buffer,rs[j]->filename);
				if(rs[j]->dirtype>=0)
				{
					if(rs[j]->dirtype==ST_SOFTLINK)
						strcat(buffer,"»");
					else
						strcat(buffer,"/");
					if(j<cols-1 && rs[j+1])
						strcat(buffer,temp2+len-longest+(strlen(rs[j]->filename)+1));
				}
				else
				{	if(j<cols-1 && rs[j+1])
						strcat(buffer,temp2+len-longest+strlen(rs[j]->filename));
				}
				rs[j]=(struct base *)rs[j]->node.ln_Succ;
				j++;
			}
			Write(Output(),buffer,strlen(buffer));
			Write(Output(),"\n",1);
			if(SetSignal(0L,SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C)
			{
				Write(Output(),"\n***Break\n",10);
				goto errorexit;
			}
		}
		Write(Output(),"\n",1);
	}
	while(help=(struct base *)RemHead(&filelist))
		FreeMem(help,sizeof(*help));
	FreeMem(buffer,buflen);
	FreeMem(fib,sizeof(*fib));
	return FALSE;

errorexit:
	while(help=(struct base *)RemHead(&filelist))
		FreeMem(help,sizeof(*help));
	if(buffer)
		FreeMem(buffer,buflen);
	if(fib)
		FreeMem(fib,sizeof(*fib));
	return TRUE;
}


char *convertdate(struct DateStamp *ds,char *temp)
{	long n;
	int months,days,years;
	static char *m[]={"","Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"};

	n=ds->ds_Days-2251;
	years=(4*n+3)/1461;
	n-=1461*(long)years/4;
	years+=84;
	months=(5*n+2)/153;
	days=n-(153*months+2)/5+1;
	months+=3;
	if(months>12)
	{	years++;
		months-=12;
	}
	if(days<1 || days>31 || months<1 || months>12 || years<78)
	{	days=months=1;
		years=78;
	}

	sprintf(temp,"%02d-%3s-%02d %02d:%02d",days,m[months],years,ds->ds_Minute/60,ds->ds_Minute%60);
	return temp;
}


void main(int argc, char *argv[])
{	register int i,ulostettu=FALSE;
	long flags=0;
	char *str;

	for(i=1;i<argc;i++)
	{
		if(argv[i][0]=='-')
		{
			str=argv[i]+1;
			while(*str)
			{
				switch(*str++)
				{
				case 'R':
					flags|=FLAG_RECURSIVE;
					break;
				case 'a':
					flags|=FLAG_ALL;
					break;
				case 'l':
					flags|=FLAG_LONG;
					break;
				case 'o':
					flags|=FLAG_OWNER;
					break;
				case 'c':
					flags|=FLAG_FILENOTES | FLAG_LONG;
					break;
				case 'C':
					flags|=FLAG_ONECOLUMN;
					break;
				case 'd':
					flags|=FLAG_DIRECTORY;
					break;
				case 'n':
					flags|=FLAG_NOSORT;
					break;
				case 'h':
				case '?':
				default:
					flags|=FLAG_HELP;
					break;
				}
			}
		}
		else
		{
			if(flags & FLAG_HELP)
			{
				break;
			}
			readdir(argv[i],flags);
			ulostettu=~FALSE;
		}
	}

	if(flags & FLAG_HELP)
	{
		Write(Output(),usage,strlen(usage));
	}
	if(!ulostettu && !(flags & FLAG_HELP))
		readdir("",flags);
}
