#ifdef	ournix
#include "ournix.h"
#endif
char sccsID[] = "@(#) ls.c V2.0 Copyright VSL(pjc+jhs) 1987\n" ;

/* ls - Unix like ls for MSDOS */
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>

#ifdef	MSDOS
#include "ndir.h"
#else
#include <dir.h>
#endif
extern	DIR     *opendir() ;
extern	struct	direct	*readdir() ;
extern	void	closedir() ;

#define ENOUGH	128
#define MAXDIR	1024

struct	mlist	{
	char	m_name[12+2];
	long	m_size;
	short	m_mode;
	long	m_time;
};

struct	mlist	*m_tab[MAXDIR];
struct	mlist	**m_end;
struct	mlist	**m_start;

struct	mlist	*m_stat();
int	tom_stat;

char	*malloc();
char	aflag;
char	Rflag;
char	rflag;
char	lflag;
char	tflag;
char	xflag;
char	nomem;
char	oneflag;
char	Cflag;
char	dflag;
char	Fflag;
char	Xflag;
char	Dflag;

char	**ARGV ;

main(argc, argv)
	char	**argv;
	{
	char	*opt;
	int	status = 0;
	int	flag = 0;

	ARGV = argv ;
#ifdef	VSL	/* { */
#include	"../../include/vsl.h"
#endif		/* } */

	for(argv++, argc--; argc ; argc--, argv++){
		if(*(opt = *argv) != '-')
			break;
		while(*++opt)
			switch(*opt){
			case 'D':
				Dflag = 1;
				break;
			case 'd':
				dflag = 1;
				break;
			case 'X':	/* NOT UNIX */
				Xflag = 1;
				break;
			case 'r':
				rflag = 1;
				break;
			case 'F':
				Fflag = 1;
				break;
			case '1':
				oneflag = 1;
				break;
			case 'C':
				Cflag = 1;
				break;
			case 'a':
			case 'A':
				aflag = 1;
				break;
			case 'R':
				Rflag = 1;
				break;
			case 'l':
				lflag = 1;
				break;
			case 't':
				tflag = 1;
				break;
			case 'x':
				xflag = 1;
				break;
			}
	}
	if(!oneflag){
		if(!Cflag && !isatty(1))
			oneflag = 1;
	}
	if(lflag)
		oneflag = 1;
	if(Xflag || lflag)
		xflag = 1;
	if(oneflag)
		xflag = 1;

	tom_stat = (lflag || Rflag || Xflag || Dflag || tflag || Fflag);
	if(argc <= 0)
		status = doit(".", 0);
	else
		for(flag = (argc > 1); argc ; argc--, argv++)
			status |= doit(*argv, flag);
	exit(status);
}

doit(d, flag)
char	*d;
{
	struct	mlist	*mp;
	struct	stat	statbuf;
	int	status;

	if(stat(d, &statbuf) < 0){
		status = rootchk(d, flag);
		if(status >= 0)
			return(status);
		mp = m_stat(d, 0, (struct stat *)0, 1);
	}
	else
		mp = m_stat(d, 0, &statbuf, 1);
	if(mp == NULL)
		return(1);
	if((mp->m_mode & S_IFMT) != S_IFDIR || dflag){
		if(!Dflag || dflag){
			if(lflag)
				lprint(mp, d);
			else
				printit(mp, d, 1);
		}
		free( (char *)mp);
		return(0);
	}
	free( (char *)mp);
	if(flag)
		printf("%s:\n", d);
	m_start = m_tab;	/* put start at start of table */
	if(status = recurse(d))
		return(status);
	if(flag)
		printf("\n");
	return(0);
}

/*
 * check to see if directory is root
 */

rootchk(d, flag)
char	*d;
{
	DIR	*dirp;
	static	char	NULLS[] = "";
	char	**xp;
	static	char	*matches[] = { ".", "/", "\\", (char *)0, };

	for(xp = matches ; *xp ; xp++)
		if(strcmp(d, *xp) == 0){
			dirp = opendir(NULLS);	/* "" for ROOT ?? */
			if(dirp == NULL)
				return(-1);
			closedir(dirp);
			if(flag)
				printf("%s\n", d);
			m_start = m_tab;	/* get start right */
			return(recurse(NULLS));
		}
	return(-1);
}

recurse(d)
char	*d;
{
	int	nentries;
	int	nc;
	struct	mlist	*mp, **xmp;
	struct	mlist	**Mp;
	char	*tbuf;
	int	nlp;
	int	nlines;

	nentries = dirbuild(d);
	if(nentries < 0)
		return(1);
	else if(nentries == 0)
		return(0);
	nc = 0;
	if(xflag){
		for(Mp = m_start ; Mp < m_end ;){
			mp = *Mp++;
			if(Dflag && (mp->m_mode & S_IFMT ) != S_IFDIR){
				if(!Rflag)
					free( (char *)mp);
				continue;
			}
			nc = ++nc % 5;
			if(Xflag){
				if( (mp->m_mode & S_IFMT) != S_IFDIR)
#ifdef	unix	/* { */
#define DIR_DELIM '/'
#endif		/* } */
#ifdef	MSDOS	/* { */
#define DIR_DELIM '\\'
#endif		/* } */
					printf("%s%c%s\n", d,DIR_DELIM,
						mp->m_name);
			}
			else if(lflag)
				lprint(mp, mp->m_name);
			else
				printit(mp, mp->m_name,
						oneflag || !nc || Mp == m_end);
			if(!Rflag)
				free( (char *)mp);
		}
	}
	else {
		/* must sort into 5 column output */
		nlines = (nentries + 4) / 5;
		for(nlp = 0; nlp < nlines ; nlp++){
			for(nc = 0 ; nc < 5 ; nc++){
				Mp = m_start + nlp + (nlines*nc);
				if(Mp >= m_end)
					break;
				mp = *Mp;
				nentries--;
				if(Dflag && (mp->m_mode & S_IFMT ) != S_IFDIR){
					if(!Rflag)
						free( (char *)mp);
					continue;
				}
				printit(mp, mp->m_name, (nc==4) || !nentries);
				if(!Rflag)
					free( (char *)mp);
			}
		}
	}
		
	if(!Rflag)
		return(0);
	for(Mp = m_start ; Mp < m_end ;){
		mp = *Mp++;
		if((mp->m_mode & S_IFMT) == S_IFDIR && !dot(mp->m_name)){
			tbuf = malloc(strlen(d) + strlen(mp->m_name) + 2);
			if(tbuf != 0){
				sprintf(tbuf, "%s%c%s", d, DIR_DELIM,
					mp->m_name);
				if(!Xflag && !Dflag)
					printf("\n%s:\n",tbuf);
				xmp = m_start;
				m_start = m_end;
				if(recurse(tbuf)){
					m_end = m_start;
					m_start = xmp;
					free(tbuf);
					return(1);
				}
				m_end = m_start;
				m_start = xmp;
				free(tbuf);
			}
		}
		free( (char *)mp);
	}
	return(0);
}

dirbuild(d)
char	*d;
{
	DIR	*dirp;
	struct	direct	*dp;
	struct	mlist	*mp;
	static	char	tbuf[ENOUGH];
	int	n = 0;

	m_end = m_start;
	dirp = opendir(d);
	if(dirp == 0){
		fprintf(stderr, "Cannot open '%s'\n", d);
		return(-1);
	}
	strcpy(tbuf, d);
	d = &tbuf[strlen(d)];
	*d++ = DIR_DELIM ;
	while((dp = readdir(dirp)) != NULL){
		if(!aflag && dot(dp->d_name))
			continue;
		strcpy(d, dp->d_name);
		mp = m_stat(tbuf, dp->d_name, (struct stat *)0, tom_stat);
		if(mp == 0){
			if(nomem)
				break;
			continue;
		}
		*m_end++ = mp;
		if(m_end > &m_tab[MAXDIR-1]){
			fprintf(stderr, "Out of memory\n");
			nomem = 1;
			break;
		}
	}
	closedir(dirp);
	n = m_end - m_start;
	sortit(m_start, n);
	return(n);
}

dot(name)
char	*name;
{
	if(*name != '.')
		return(0);
	if(name[1] == '\0' || (name[1] == '.' && name[2] == '\0'))
		return(1);
	return(0);
}

struct	mlist	*
m_stat(nam, pnam, statp, tostat)
char	*nam, *pnam;
struct	stat	*statp;
{
	struct	stat	statbuf;
	struct	mlist	*mp;

	if(statp == (struct stat *)0 && tostat){
		statp = &statbuf;
		if(stat(nam, statp) < 0){
			fprintf(stderr, "Can't stat '%s'\n", nam);
			return(0);
		}
	}
	mp = (struct mlist *)malloc(sizeof(struct mlist));
	if(mp == 0){
		if(!nomem)
			fprintf(stderr, "Out of memory\n");
		nomem = 1;
		return(0);
	}
	if(pnam)
		strcpy(mp->m_name, pnam);
	else
		mp->m_name[0] = '\0';
	if(tostat){
		mp->m_size = statp->st_size;
		mp->m_mode = statp->st_mode;
		mp->m_time = statp->st_mtime;
	}
	return(mp);
}

asccomp(a1, a2)
struct	mlist	**a1, **a2;
{
	if(rflag)
		return(strcmp((*a2)->m_name, (*a1)->m_name));
	else
		return(strcmp((*a1)->m_name, (*a2)->m_name));
}

timcomp(a1, a2)
struct	mlist	**a1, **a2;
{
	if((*a1)->m_time == (*a2)->m_time)
		return(0);
	if(rflag){
		if((*a1)->m_time > (*a2)->m_time)
			return(1);
	}
	else if((*a1)->m_time < (*a2)->m_time)
		return(1);
	return(-1);
}

sortit(m_tab, n)
struct	mlist	**m_tab;
{
	int	(*cmpfunc)();

	if(!n)
		return;
	if(tflag)
		cmpfunc = timcomp;
	else
		cmpfunc = asccomp;
	qsort((char *)m_tab, n, sizeof(struct mlist *), cmpfunc);
}

/*
 * do an ls -l type of printing
 */

lprint(mp, nam)
struct	mlist	*mp;
char	*nam;
{
	char	tbuf[12];
	char	*sp;
	int	i, mode;
	char	*ctime();

	if(nam == 0)
		nam = mp->m_name;
	sp = tbuf;
	mode = mp->m_mode;
	if((mode & S_IFMT) == S_IFDIR)
		*sp++ = 'd';
	else
		*sp++ = '-';
	for(i = 0 ; i < 3 ; i++, mode<<=3){
		if(mode & S_IREAD)
			*sp++ = 'r';
		else
			*sp++ = '-';
		if(mode & S_IWRITE)
			*sp++ = 'w';
		else
			*sp++ = '-';
		if(mode & S_IEXEC)
			*sp++ = 'x';
		else
			*sp++ = '-';
	}
	*sp = 0;
	sp = ctime(&mp->m_time) + 4;
	printf("%s %-16s %-7ld %20.20s\n", tbuf, nam, mp->m_size, sp);
}

printit(mp, xnam, flag)
struct	mlist	*mp;
char	*xnam;
{
	char	*cstr = "";
	char	lbuf[20];

	if(Fflag){
		switch(mp->m_mode & S_IFMT){
		case S_IFDIR:
			cstr = "/";
			break;
		default:
			if(mp->m_mode & S_IEXEC)
				cstr = "*";
			break;
		}
	}

	if(xnam == 0) xnam = mp->m_name;
	sprintf(lbuf, "%s%s", xnam, cstr);
	if(flag) printf("%s\n", lbuf);
	else printf("%-16s", lbuf);
}
