/*----------------------------------------------------------------------------
	dirutil.c - ATARI ST directory reading routines

	ST NOS Version by David Nash, dnash@chaos.demon.co.uk

	21.08.94 DFN - Remove blank padding in short, single column list.

----------------------------------------------------------------------------*/

#include <stdio.h>
#include <ctype.h>
#include <osbind.h>
#include <dos.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <unistd.h>

#include "global.h"
#include "commands.h"

#undef   time										/* remove macro from global.h 	*/

static struct	FILEINFO	info;					/* file information = dta			*/

static void format_dir(char *, struct stat);
static void format_fname(char *, char *, char);
static void diskfree(FILE *, int, int);
static void format_dirent(char *line, char *name, struct stat *st_buf, int full);

       int	getdir(char *, int full, FILE *);
       void path_u2d(char *);
       

#define NULLSORT (struct dirsort *)0

#ifndef FALSE
#define FALSE (0)
#endif

#ifndef TRUE
#define TRUE (!0)
#endif

#define DIR_NAMES	256
#define PATH_LEN	256
#define FILE_L 	13


/*---------------------------------------------------------------------------
	dir - Create a directory listing in a temp file and return the
			resulting file descriptor. If full == 1, give a full listing;
			else return just a list of names.
---------------------------------------------------------------------------*/
 
FILE *dir(char *path, int full) {

	FILE *fp;

	if ((fp = tmpfile()) != NULLFILE) {
		getdir(path, full, fp);
		rewind(fp);
	}

	return fp;
}

/*---------------------------------------------------------------------------
	filedir - wildcard filename lookup
---------------------------------------------------------------------------*/

void filedir(char *name, int times, char *ret_str) {
	
	char *cp, *cp1;
	
	Fsetdta(&info);	/* Set disk transfer address */

	path_u2d(name);								/* convert path seperators			*/
	
	/* Find matching file */

	if (times == 0 ? Fsfirst(name, 0x11) : Fsnext())	/* include r/o and dir  */    
		info.name[0] = '\0';						/* file name not found				*/
		
	/* Copy result to output, forcing to lower case */

	for(cp = ret_str, cp1 = info.name; cp1 < &info.name[13] && *cp1 != '\0'; )
		*cp++ = (char)tolower(*cp1++);
	*cp = '\0';
}


/*---------------------------------------------------------------------------
	getdir - do a directory list to the stream 
				full = 0 -> short form, list of valid file names seperated by '\n'
						 1 -> long form, filenames and statistics
						-1 -> multi-column short
---------------------------------------------------------------------------*/

int getdir(char *path, int full, FILE *file) {

	int 	indx = 0, i = 0;
	int	no_files = 0;
	int	count = 0;
	int	drive_n;
	int   flag = 0;
	int	cols = 1;								
	
	char	drive_c;
	char	*path_p, *path_q;							/* pointer into path string	*/
	char 	line_buf[50];								/* for long dirlist 				*/
	char	fq_path[128];								/* fully quilified path			*/
	char  fq_name[128];								/* fully qualified name			*/

	char  **name_p;									/* pointers to file names		*/
	char  *names;										/* array of file names			*/
	char	*name_ptr;									/* pointer into names array	*/

	struct stat st_buf;								/* buffer for stat results		*/
	struct stat	*st_buf_p = 0;						/* pointer to stat buffer		*/
	
	struct dirent *dp;								/* directory entry pointer		*/
	DIR	*dir;											/* directory stream pointer	*/

	
	path_u2d(path);									/* convert path seperators		*/

	if (full > 0)
		cols = 2;										/* long listing format			*/
	else if (full < 0)
		cols = 5;										/* multi column wide format	*/
	else
		cols = 1;										/* one column short list		*/

		
	/*	Analyse the path components														*/
	
	if (path[1] == ':') {
		drive_c = path[0];							/* set drive letter				*/
		drive_n = toupper(path[0]) - 'A' + 1;	/* set drive number, a=1		*/
		path_p  = path + 2;							/* point past drive letter		*/
	}
	else {
		drive_n = Dgetdrv() + 1;					/* get default drive, a=1		*/
		drive_c = drive_n + 'A' - 1;				/* default drive letter			*/
		path_p  = path;								/* point to full path			*/
	}

	if (*path_p == '.')
		path++;											/* remove .  (current dir)		*/	
	if (*path_p == '.')
		path_p++;										/* remove .. (previous dir)	*/	

	if (path[0] == '\\')								/* process absolute path		*/
		sprintf(fq_path, "%c:%s\0", drive_c, path_p);
	else if (path[1] == ':' && path[2] == '\\')
		sprintf(fq_path, "%s\0", path);
	else {												/* process relative path		*/
		Dgetpath(fq_name, drive_n);				/* get current directory 		*/
			sprintf(fq_path, "%c:%s\\%s\0", drive_c, fq_name, path_p);
	}

	if (fq_path[strlen(fq_path) -1] != '\\')		
		strcat(fq_path, "\\");						/* must end in \					*/


	/* Strip out duplicate '\' characters												*/
	
	for (path_p = path_q = fq_path; *path_p; path_p++) {
		if (flag) 										/* alraedy got a slash			*/
			if (*path_p == '\\')
				continue;								/* we dont't want another		*/
			else
				flag = 0;								/* normal char, clear flag		*/
		else												/* last char was normal			*/
			if (*path_p == '\\')
				flag = 1;								/* got a slash, set flag		*/
		*path_q++ = *path_p;							/* copy the char					*/
	}

	*path_q = '\0';									/* force end of string			*/

	
	/* Process the dirctory       	                               			*/
	
	if ((dir = opendir(fq_path)) == NULL) {	/* open the directory			*/
		fprintf(file, "Path %s not found \n", path); 
		return -1;										/* error, directory not found	*/
	}

	while (readdir(dir))								/* count number of entries		*/
		count++;

	count += 10;										/* add fudge factor				*/
	rewinddir(dir);									/* reset directory pointer		*/

	if ((names = (char *)calloc(count * FILE_L, sizeof(char))) == NULL) {
		closedir(dir);
		tprintf("getdir: calloc failed for names \n");
		return -1;
	}

	if ((name_p = (char **)calloc(count, sizeof(char *))) == NULL) {
		free(names);
		closedir(dir);
		tprintf("getdir: calloc failed for name_p \n");
		return -1;
	}
	
	name_ptr = names;									/* initilise the pointer		*/
	
	while (dp = readdir(dir)) {
		if (dp->d_name[0] == '.') 
			continue;									/* ignore 'hidden' files		*/

		strncpy(name_ptr, dp->d_name, dp->d_reclen);
		name_p[indx] = name_ptr;					/* setup pointer					*/
		name_ptr += dp->d_reclen + 1;
		indx++;
	}														/*	END of while loop				*/

	if (closedir(dir) != 0)							/* close the directory			*/
		tprintf("getdir: closedir failed %s \n", strerror(errno));

	no_files = indx;									/* save number of files			*/

	tqsort(name_p, no_files);						/* sort the pointers				*/


	/* Format and write the output                                         */
	
	for (indx = 0; indx < no_files; indx++) {
		if (full > 0) {
			st_buf_p = &st_buf;						/* may have been reset			*/
			sprintf(fq_name, "%s%s\0", fq_path, name_p[indx]);

			if (stat(fq_name, st_buf_p) == -1) {
				st_buf_p = 0;
			}
		}

		format_dirent(line_buf, name_p[indx], st_buf_p, full);
		fprintf(file, "%s%s", line_buf, (++i % cols) ? "   " : "\n");
	} 

	if (i % cols)
		fprintf(file, "\n");

	if (full > 0)
			diskfree(file, drive_n, no_files);

	free(names);										/* free the file name array	*/
	free(name_p);										/* free the pointer array		*/

	return 0;											/* return OK						*/
}


/*---------------------------------------------------------------------------
	format_dirent - format a directory entry
---------------------------------------------------------------------------*/

static void format_dirent( char *line, 
								   char *name, 
                           struct stat *st_buf, 
                           int full
                         )
{
	char *p, *q;
	char size[12];									/* file size							*/
	char time[15];									/* file modification time			*/
	char fname[14];								/* formatted file name				*/	

	*line = '\0';									/* clear the output buffer			*/
	
	for (p = name, q = fname; *p && *p != ' '; p++, q++)
		*q = tolower(*p);

	*q = '\0';										/* terminate the string				*/

	if (full == 0) {
		strcpy(line, fname);
		return;										/* short, NL terminated list		*/
	}
	
	if (full < 0) {								/* short multi line format 		*/
		sprintf(line, "%-13s\0", fname);		
		return;
	}
					
	if (S_ISDIR(st_buf->st_mode))	{
		*q++ = '/';
		sprintf(size, "  ");		   			/* no size for dir					*/
	}	
	else  		
		sprintf(size, "%ld", st_buf->st_size);

	*q = '\0';										/* set EOS for file name			*/
	
	strftime(time, sizeof(time), "%H:%M %d.%m.%y", localtime(&st_buf->st_mtime));
	sprintf(line, "%8s %14s %-13s", size, time, fname); 
}


/*---------------------------------------------------------------------------
	diskfree - return stats
---------------------------------------------------------------------------*/

void diskfree (FILE *file, int drv, int nfiles)
{
	unsigned long free_bytes  = 0;
	unsigned long total_bytes = 0;
	struct DISKINFO disk_info;
 	char s_free[11];
	char s_total[11];

	fflush(stdout); 								/* function takes a short while... */

	if (Dfree((void *)&disk_info, (short)drv) == 0)	{
		free_bytes  = disk_info.bps * disk_info.spc;
		total_bytes = free_bytes * disk_info.cpd;
		free_bytes *= disk_info.free;
	}

	sprintf(s_free, "%ld", free_bytes);
	sprintf(s_total,"%ld", total_bytes);

	if (nfiles)
		fprintf(file, "\n%d", nfiles);
	else
		fprintf(file, "No");

	fprintf(file, " file%s. %s bytes free. Disk size %s bytes.\r\n",
		      (nfiles == 1 ? "" : "s"), s_free, s_total);
}


/*---------------------------------------------------------------------------
	Change working directory
---------------------------------------------------------------------------*/

docd(int argc, char *argv[], void *p)
{
	char dirname[128];

	if(argc > 1) {
		path_u2d(argv[1]);							/* convert path seperators			*/
		if(chdir(argv[1])){
			printf("Can't change directory\n");
			return 1;
		}
	}

	if (Dgetpath(dirname,0) == 0){
		printf("%c:%s%s\n",(char) Dgetdrv()+'A',
			(*dirname ? "" : "\\"),dirname);
	}

	return 0;
}


/*---------------------------------------------------------------------------
	dodir - List directory to console. [-]w option selects "wide" format
---------------------------------------------------------------------------*/

int __stdargs dodir(int argc, char *argv[], void *p) {
	char path[PATH_LEN];
	int full = 1;

	if (argc > 1 && argv[1][0] == '-' && argv[1][1] == 'w') {
		full = -1;
		argv++;
		argc--;
	}

	if (argc >= 2) {
		strncpy(path, argv[1], PATH_LEN);	
	} else {
		strcpy(path, ".");
	}

	getdir(path, full, stdout);
	return 0;
}


/*---------------------------------------------------------------------------
	domkd - Create directory
---------------------------------------------------------------------------*/

int domkd(int argc, char *argv[], void *p)
{
	if (mkdir(argv[1]) == -1)
		tprintf("Can't make %s: %s\n", argv[1], strerror(errno));
	return 0;
}


/*---------------------------------------------------------------------------
	dormd - Remove directory
---------------------------------------------------------------------------*/

int dormd(int argc, char *argv[], void *p)
{
	if (rmdir(argv[1]) == -1)
		tprintf("Can't remove %s: %s\n", argv[1], strerror(errno));
	return 0;
}


/*---------------------------------------------------------------------------
	path_u2d - convert unix style path seperators to dos style seperators
---------------------------------------------------------------------------*/

void path_u2d(char *name)
{
	char *p;

	for (p = name; *p; p++)
		if (*p == '/')
			*p = '\\';

	return;
}
