/***   [getfiles.c]
*
*	ディレクトリ情報 関連		(C)ささがわ
*
*	For GNU C Compiler (GCC)   Version 1.39
*
***/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dos.h>
#include "jstring.h"
#include "getfiles.h"

#define DEFAULT_BUF	30
#define ADD_BUF		50

#define STBUF_RETRY	-1
#define STBUF_FATAL	-2

typedef unsigned char	Uchar;

static files *buf = NULL;

static int	Set_buf(unsigned *);
static int	Set_buf_again(int);
static void Sort_ext(files **, unsigned);
static void Sort_date(files **, unsigned);
static void Sort_name(files **, unsigned);
static void Sort_non(files **, unsigned);
static int cmp_dirornot(files **, files **);
static int cmp_base(files **, files **);
static int cmp_ext(files **, files **);
static int cmp_date(files **, files **);
static int cmp_name(files **, files **);
static int cmp_non(files **, files **);
static Uchar *get_extptr(Uchar *);

int Getfiles(int type, get_f *gf, int mode) {
	int			n;
	unsigned	n_file;
	unsigned	n_dir;
	unsigned	mf = DEFAULT_BUF;
	files		**ptr;
	
	if (mode == 0) {
		while ((n = Set_buf(&mf)) == STBUF_RETRY)
			mf += ADD_BUF;
		if (n == STBUF_FATAL) {
			gf->pbuf = NULL;
			return 1;
		}
		ptr = (files **)(buf + mf);
		n_file = n;
		n_dir = 0;
		
		for (n = 0;n < mf;n++)
			ptr[n] = buf + n;
	} else {
		ptr = gf->pbuf;
		n_file = gf->nfiles;
		n_dir = gf->ndir;
	}
	if (n_file == 0) {
		gf->pbuf = ptr;
		gf->nfiles = n_file;
		gf->ndir = n_dir;
		return 0;
	}
	
	/* ディレクトリ分離 */
	qsort((void *)ptr, (size_t)n_file, (size_t)sizeof(files *), (int (*)(const void *, const void *))cmp_dirornot);
	
	n = 0;
	while (ptr[n]->attr & _A_SUBDIR && n < n_file)
		n++;
	n_dir = n;
	
	switch (type) {
		case GF_SEXT:
			Sort_name(ptr, n_dir);
			Sort_ext(ptr + n_dir, n_file - n_dir);
			break;
		
		case GF_SDATE:
			Sort_date(ptr, n_dir);
			Sort_date(ptr + n_dir, n_file - n_dir);
			break;
		
		case GF_SNAME:
			Sort_name(ptr, n_dir);
			Sort_name(ptr + n_dir, n_file - n_dir);
			break;
		
		default:
			Sort_non(ptr, n_dir);
			Sort_non(ptr + n_dir, n_file - n_dir);
			break;
	}
	
	gf->pbuf = ptr;
	gf->nfiles = n_file;
	gf->ndir = n_dir;
	return 0;
}

static int Set_buf(unsigned *cap_buf) {
	int			n_file;
	unsigned	atr = _A_NORMAL | _A_RDONLY | _A_SUBDIR | _A_ARCH;
	struct find_t	ft;
	
	Gf_bufrel();
	if ((buf = (files *)malloc(*cap_buf * (sizeof(files) + sizeof(files *)))) == NULL)
		return STBUF_FATAL;
	
	n_file = 0;
	if (!_dos_findfirst("*.*", atr, &ft)) {
		if (strcmp(".", ft.name) || !(ft.attrib & _A_SUBDIR)) {
			strcpy((char *)buf[n_file].name, (const char *)ft.name);
			buf[n_file].attr = ft.attrib;
			buf[n_file].dt = ft.wr_date;
			buf[n_file].tm = ft.wr_time;
			buf[n_file].ord = n_file;
			buf[n_file++].sz = ft.size;
		}
		if (n_file >= *cap_buf) {
			if (Set_buf_again(*cap_buf))
				return STBUF_RETRY;
			*cap_buf += ADD_BUF;
		}
		
		while (!_dos_findnext(&ft)) {
			if (strcmp(".", ft.name) || !(ft.attrib & _A_SUBDIR)) {
				strcpy((char *)buf[n_file].name, (const char *)ft.name);
				buf[n_file].attr = ft.attrib;
				buf[n_file].dt = ft.wr_date;
				buf[n_file].tm = ft.wr_time;
				buf[n_file].ord = n_file;
				buf[n_file++].sz = ft.size;
			}
			if (n_file >= *cap_buf) {
				if (Set_buf_again(*cap_buf))
					return STBUF_RETRY;
				*cap_buf += ADD_BUF;
			}
		}
	}
	
	return n_file;
}

static int Set_buf_again(int n) {
	files	*buf2;
	
	if ((buf2 = (files *)malloc((n + ADD_BUF) * (sizeof(files) + sizeof(files *)))) == NULL)
		return -1;
	memcpy(buf2, buf, n * (sizeof(files) + sizeof(files *)));
	free(buf);
	buf = buf2;
	
	return 0;
}

/*   拡張子でソートし、そのうえでベース名でソートする関数   */
static void Sort_ext(files **ptr, unsigned no) {
	int	n = 0;
	
	qsort((void *)ptr, (size_t)no, (size_t)sizeof(files *), (int (*)(const void *, const void *))cmp_ext);
	
	while (n < no) {
		Uchar	*startptr;
		unsigned		start;
		
		start = n;
		startptr = get_extptr((Uchar *)ptr[n++]->name);
		while (!strcmp((const char *)startptr, (const char *)get_extptr((Uchar *)(ptr[n]->name))) && n < no)
			n++;
		qsort((void *)(ptr + start), (size_t)(n - start), (size_t)sizeof(files *), (int (*)(const void *, const void *))cmp_base);
	}
}

/*   作成日時でソートする関数   */
static void Sort_date(files **ptr, unsigned no) {
	int		n = 0;
	
	qsort((void *)ptr, (size_t)no, (size_t)sizeof(files *), (int (*)(const void *, const void *))cmp_date);
	
	while (n < no) {
		int		t, d, st;
		
		st = n;
		d = ptr[n]->dt;	t = ptr[n++]->tm;
		while (d == ptr[n]->dt && t == ptr[n]->tm && n < no)
			n++;
		qsort((void *)(ptr + st), (size_t)(n - st), (size_t)sizeof(files *), (int (*)(const void *, const void *))cmp_name);
	}
}

/*   ファイル名全体でソートする関数   */
static void Sort_name(files **ptr, unsigned no) {
	qsort((void *)ptr, (size_t)no, (size_t)sizeof(files *), (int (*)(const void *, const void *))cmp_name);
}

/*   ディレクトリ順にソートする関数   */
static void Sort_non(files **ptr, unsigned no) {
	qsort((void *)ptr, (size_t)no, (size_t)sizeof(files *), (int (*)(const void *, const void *))cmp_non);
}

/*   qsort関数用、サブディレクトリを識別する関数   */
static int cmp_dirornot(files **a, files **b) {
	if ((*a)->attr & _A_SUBDIR && (*b)->attr & _A_SUBDIR)
		return 0;
	else if ((*a)->attr & _A_SUBDIR)
		return -1;
	else if ((*b)->attr & _A_SUBDIR)
		return 1;
	else
		return 0;
}

/*   qsort関数用、ベース名を比較する関数   */
static int cmp_base(files **a, files **b) {
	Uchar c[13], d[13], *e;
	
	strcpy((char *)c, (const char *)(*a)->name);
	if ((e = jstrchr(c, '.')) != NULL)
		*e = '\0';
	strcpy((char *)d, (const char *)(*b)->name);
	if ((e = jstrchr(d, '.')) != NULL)
		*e = '\0';
	return jstrcmp(c, d);
}

/*   qsort関数用、拡張子を比較する関数   */
static int cmp_ext(files **a, files **b) {
	return jstrcmp(get_extptr((Uchar *)(*a)->name), get_extptr((Uchar *)(*b)->name));
}

/*   qsort関数用、作成日時を比較する関数   */
static int cmp_date(files **a, files **b) {
	unsigned long	d1, d2;
	
	d1 = ((*a)->dt << 16) + (*a)->tm;
	d2 = ((*b)->dt << 16) + (*b)->tm;
	if (d1 > d2)
		return 1;
	else if (d1 < d2)
		return -1;
	else
		return 0;
}

/*   qsort関数用、ファイル名全体を比較する関数   */
static int cmp_name(files **a, files **b) {
	if ((*a)->name[0] == '.' || (*b)->name[0] == '.')
		return jstrcmp((Uchar *)(*a)->name, (Uchar *)(*b)->name);
	else
		return jstrcmp((Uchar *)(*a)->name, (Uchar *)(*b)->name);
}

/*   qsort関数用、ディレクトリ順を比較する関数   */
static int cmp_non(files **a, files **b) {
	if ((*a)->ord > (*b)->ord)
		return 1;
	else if ((*a)->ord < (*b)->ord)
		return -1;
	else
		return 0;
}

/*   拡張子へのポインタを返す関数   */
static Uchar *get_extptr(Uchar *a) {
	Uchar *b;

	if ((b = jstrchr(a, '.')) == NULL)
		return (a + strlen((char *)a));
	else
		return (b + 1);
}

void Gf_bufrel(void) {
	free(buf);
	buf = NULL;
}

int Split_fname(const char *f_name, unsigned atr, char *buf_base, char *buf_ext) {
	int		ret;
	char	*a;
	static const char	*exec_ext[] = {
		".COM", ".EXE", ".EXP", ".BAT", ""
	};
	
	if ((a = strchr(f_name, '.')) == NULL) {
		strcpy(buf_base, f_name);
		buf_ext[0] = '\0';
	} else {
		buf_base[0] = '\0';
		strncat(buf_base, f_name, a - f_name);
		strcpy(buf_ext, a);
	}
	
	if (atr & _A_SUBDIR) {
		ret = EX_DIR;
	} else {
		int		i;
		
		ret = EX_OTHER;
		for (i = 0; exec_ext[i][0]; i++) {
			if (!stricmp(buf_ext, exec_ext[i])) {
				ret = EX_EXE;
				break;
			}
		}
	}
	
	return ret;
}
