/**********************************************************************
 
	Copyright (C) 2003 Hirohisa MORI <joshua@nichibun.ac.jp>
 
	This program is free software; you can redistribute it 
	and/or modify it under the terms of the GLOBALBASE 
	Library General Public License (G-LGPL) as published by 

	http://www.globalbase.org/
 
	This program is distributed in the hope that it will be 
	useful, but WITHOUT ANY WARRANTY; without even the 
	implied warranty of MERCHANTABILITY or FITNESS FOR A 
	PARTICULAR PURPOSE.

**********************************************************************/



#include	<sys/stat.h>
#include	"machine/include.h"
#include	"xl.h"
#include	"memory_debug.h"
#include	"xldir.h"

void
print_dir_list(DIR_LIST * d)
{
	for ( ; d ; d = d->next )
		printf("> %s\n",d->name);
}

DIR_LIST *
add_dir_list(DIR_LIST * dl,char * name,int flags)
{
DIR_LIST * new;
	new = d_alloc(sizeof(*new),123);
	new->name = name;
	new->flags = flags;
	new->next = dl;
	return new;
}

int
match_check(char * name,char * match)
{
char * m;
char * p;
char ** argv;
int cnt,argc;
int start_flag,end_flag;
int i;
int len;
int total;
int ret;
	m = copy_str(match);
	cnt = 1;
	for ( p = m ; *p ; p ++ ) {
		if ( *p == '*' )
			cnt ++;
	}
	argv = d_alloc(sizeof(char*)*(cnt+2),111);
	argc = 0;
	p = m;
	if ( *p == '*' ) {
		start_flag = 0;
		p ++;
	}
	else	start_flag = 1;
	if ( m[strlen(m)-1] != '*' )
		end_flag = 1;
	else	end_flag = 0;
	p = m;
	for ( ; *p && *p == '*' ; p ++ );
	if ( *p == 0 )
		return 0;
	for ( ; *p ; ) {
		argv[argc++] = p;
		for ( ; *p && *p != '*' ; p ++ );
		if ( *p == 0 )
			break;
		*p++ = 0;
		for ( ; *p && *p == '*' ; p ++ );
	}
	if ( start_flag ) {
		if ( memcmp(name,argv[0],strlen(argv[0])) )
			goto no_hit;
	}
	if ( end_flag ) {
		if ( memcmp(&name[strlen(name)-strlen(argv[argc-1])],
				argv[argc-1],strlen(argv[argc-1])) )
			goto no_hit;
	}
	p = name;
	total = strlen(name);
	for ( i = 0 ; i < argc ; i ++ ) {
		len = strlen(argv[i]);
		for ( ; *p ; p ++ , total -- ) {
			if ( total < len )
				break;
			if ( memcmp(p,argv[i],len) == 0 )
				goto ok;
		}
		goto no_hit;
	ok:
		p += len;
		total -= len;
	}
	ret = 0;
	goto finish;
no_hit:
	ret = -1;
finish:
	d_f_ree(argv);
	d_f_ree(m);
	return ret;
}

DIR_LIST *
get_file_star(
	char * down_path,
	int target_type,
	char * match,
	char * up_path,
	int up_path_type)
{
DIR * dd;
struct dirent * d;
struct stat sbuf;
char * dir_name, * total_path;
int len1,len2,len3,len_t;
int flags;
DIR_LIST * ret;

/*
if ( down_path )
printf("gfs %s %x %s\n",down_path,target_type,match);
else
printf("gfs %x %s\n",target_type,match);
if ( up_path )
printf("up %s %x\n",up_path,up_path_type);
*/

	if ( down_path ) {
		dd = opendir(down_path);
		if ( dd == 0 ) {
			perror("");
			return 0;
		}
		len1 = strlen(down_path);
	}
	else {
		dd = opendir(".");
		if ( dd == 0 ) {
			return 0;
		}
		len1 = 0;
	}
	if ( up_path )
		len3 = strlen(up_path);

	ret = 0;
	for ( ; ; ) {
		d = u_readdir(dd);
		if ( d == 0 )
			break;
		if ( d->d_name[0] == '.' )
			continue;
		if ( match_check(d->d_name,match) )
			continue;
		len2 = strlen(d->d_name);
		dir_name = d_alloc(len1+len2+2,123);
		if ( down_path ) {
			strcpy(dir_name,down_path);
			dir_name[len1] = '/';
			strcpy(&dir_name[len1+1],d->d_name);
		}
		else {
			strcpy(dir_name,d->d_name);
		}
		if ( stat(dir_name,&sbuf) < 0 ) {
			fprintf(stderr,"%s",dir_name);
			perror(" stat");
			goto next;
		}
		switch (sbuf.st_mode & S_IFMT) {
		case S_IFREG:
			if ( up_path )
				goto next;
			if ( (target_type & DLF_FILE) == 0 )
				goto next;
			flags = DLF_FILE;
			break;
		case S_IFDIR:
			if ( (target_type & DLF_DIR) == 0 )
				goto next;
			flags = DLF_DIR;
			if ( up_path ) {
				len_t = strlen(dir_name);
				total_path = d_alloc(len1+len2+len3+5,
						213);
				strcpy(total_path,dir_name);
				total_path[len_t] = '/';
				strcpy(&total_path[len_t+1],
						up_path);
				if ( stat(total_path,&sbuf) < 0 ) {
					d_f_ree(total_path);
					goto next;
				}
				switch ( sbuf.st_mode & S_IFMT ) {
				case S_IFREG:
					if ( (up_path_type & DLF_FILE)
							== 0 )
						goto next;
					break;
				case S_IFDIR:
					if ( (up_path_type & DLF_DIR)
							== 0 )
						goto next;
					break;
				default:
					goto next;
				}
			}
			break;
		default:
			goto next;
		}
		ret = add_dir_list(ret,dir_name,flags);
		continue;
	next:
		d_f_ree(dir_name);
	}
	closedir(dd);
	return ret;
}

DIR_LIST *
get_double_star(
	DIR_LIST * ret,
	char * down_path,
	int target_type,
	char * up_path,
	int up_path_type)
{
DIR * dd;
struct dirent * d;
struct stat sbuf;
char * dir_name, * total_path;
int len1,len2,len3;
int flags;


	if ( down_path ) {
		dd = opendir(down_path);
		if ( dd == 0 )
			return 0;
		len1 = strlen(down_path);
	}
	else {
		dd = opendir(".");
		len1 = 0;
	}
	if ( up_path )
		len3 = strlen(up_path);
	for ( ; ; ) {
		d = u_readdir(dd);
		if ( d == 0 )
			break;
		if ( d->d_name[0] == '.' )
			continue;
		len2 = strlen(d->d_name);
		dir_name = d_alloc(len1+len2+2,123);
		if ( down_path ) {
			strcpy(dir_name,down_path);
			dir_name[len1] = '/';
			strcpy(&dir_name[len1+1],d->d_name);
		}
		else {
			strcpy(dir_name,d->d_name);
		}
		if ( stat(dir_name,&sbuf) < 0 ) {
			fprintf(stderr,"%s",dir_name);
			perror("stat");
			goto next;
		}
		switch (sbuf.st_mode & S_IFMT) {
		case S_IFREG:
			if ( (target_type & DLF_FILE) == 0 )
				goto next;
			flags = DLF_FILE;
			break;
		case S_IFDIR:
			if ( (target_type & DLF_DIR) == 0 )
				goto next;
			flags = DLF_DIR;
			if ( up_path ) {
				total_path = d_alloc(len1+len2+len3+1,
						213);
				strcpy(total_path,dir_name);
				strcpy(&total_path[len1+len2],
						up_path);
				if ( stat(total_path,&sbuf) < 0 ) {
					d_f_ree(total_path);
					goto dir_next;
				}
				switch ( sbuf.st_mode & S_IFMT ) {
				case S_IFREG:
					if ( (up_path_type & DLF_FILE)
							== 0 )
						goto dir_next;
					break;
				case S_IFDIR:
					if ( (up_path_type & DLF_DIR)
							== 0 )
						goto dir_next;
					break;
				default:
					goto dir_next;
				}
			}
		dir_next:
			ret = get_double_star(
				ret,
				dir_name,
				target_type,
				up_path,
				up_path_type);
			break;
		default:
			goto next;
		}
		ret = add_dir_list(ret,dir_name,flags);
		continue;
	next:
		d_f_ree(dir_name);
	}
	closedir(dd);
	return ret;
}

void
free_dir_list(DIR_LIST * d)
{
DIR_LIST * d1;
	for ( ; d ; ) {
		d1 = d->next;
		d_f_ree(d->name);
		d_f_ree(d);
		d = d1;
	}
}

DIR_LIST *
get_no_star(
	DIR_LIST * d,
	char * path,
	int condition,
	char * base_dir,
	char * target_dir,
	char * prefix)
{
char * new_path,* target_path;
DIR_LIST * ret;
int len1,len2,target_len;
int c;
int base_len;
struct stat sbuf_new,sbuf_target;
int flags;
int prefix_len;
int p,ll;
	ret = 0;
	if ( path )
		len2 = strlen(path);
	else	len2 = 0;
	if ( prefix )
		prefix_len = strlen(prefix);
	else	prefix_len = 0;
	if ( base_dir ) {
		base_len = strlen(base_dir);
		target_len = strlen(target_dir);
	}
	for ( ; d ; d = d->next ) {
		len1 = strlen(d->name);
		new_path = d_alloc(len1+len2+1,123);
		strcpy(new_path,d->name);
		if ( path )
			strcpy(&new_path[len1],path);
		if ( stat(new_path,&sbuf_new) < 0 ) {
			d_f_ree(new_path);
			continue;
		}
		switch (sbuf_new.st_mode & S_IFMT) {
		case S_IFREG:
			if ( (condition & DLF_FILE) == 0 ) {
				d_f_ree(new_path);
				continue;
			}
			flags = DLF_FILE;
			break;
		case S_IFDIR:
			if ( (condition & DLF_DIR) == 0 ) {
				d_f_ree(new_path);
				continue;
			}
			flags = DLF_DIR;
			break;
		default:
			goto no_hit;
		}
		if ( base_dir == 0 ) {
			target_path = 0;
			goto hit;
		}
		if ( base_len > len1+len2 ) {
			d_f_ree(new_path);
			continue;
		}
		if ( memcmp(base_dir,new_path,base_len) ) {
			d_f_ree(new_path);
			continue;
		}
		target_path 
			= d_alloc(len1+len2-base_len+target_len+
				+prefix_len+1,123);
		strcpy(target_path,target_dir);
		strcpy(&target_path[target_len],
			&new_path[base_len]);
		if ( prefix ) {
			ll = strlen(target_path);
			for ( p = ll - 1 ; 
					p >= 0 ; p -- )
				if ( target_path[p] == '.' )
					break;
			if ( p < 0 ) {
				strcpy(&target_path[ll],prefix);
			}
			else {
				strcpy(&target_path[p],prefix);
			}
		}
		if ( stat(target_path,&sbuf_target) < 0 ) {
			if ( condition&DLF_T_NOTEXIST )
				goto hit;
			goto no_hit;
		}
		switch (sbuf_target.st_mode & S_IFMT) {
		case S_IFREG:
			if ( (flags & DLF_FILE) == 0 )
				goto no_hit;
			break;
		case S_IFDIR:
			if ( (flags & DLF_DIR) == 0 )
				goto no_hit;
			break;
		default:
			goto no_hit;
		}
		if ( (condition&DLF_T_EXIST) == 0 )
			goto no_hit;
		if ( sbuf_new.st_mtime <= sbuf_target.st_mtime ) {
			if ( (condition&DLF_T_TOUCH) )
				goto hit;
			goto no_hit;
		}
		else {
			if ( (condition&DLF_T_NOTOUCH) )
				goto hit;
			goto no_hit;
		}
	hit:
		ret = add_dir_list(ret,new_path,flags);
		if ( target_path )
			d_f_ree(target_path);
		continue;
	no_hit:
		d_f_ree(new_path);
		d_f_ree(target_path);
	}
	return ret;
}

DIR_LIST *
marge_dir_list(DIR_LIST * d1,DIR_LIST * d2)
{
DIR_LIST ** dp;
	for ( dp = &d1 ; *dp ; dp = &(*dp)->next );
	*dp = d2;
	return d1;
}

DIR_LIST *
reverse_dir_list(DIR_LIST * d)
{
DIR_LIST * ret, * d2;
	ret = 0;
	for ( ; d ; ) {
		d2 = d;
		d = d->next;
		d2->next = ret;
		ret = d2;
	}
	return ret;
}

int
get_wildcard_list(char *** argv,char ** argt,char ** data,char * w)
{
int argc;
char * p, * q, * r;
char * pp;
	*data = copy_str(w);
	argc = 0;
	for ( p = w ; *p ; p ++ )
		if ( *p == '/' )
			argc ++;
	argc += 2;

	*argv = d_alloc(sizeof(char*)*argc,123);
	*argt = d_alloc(sizeof(char)*argc,123);
	argc = 0;
	for ( pp = *data ; *pp ; ) {
		for ( p = pp ; *p && *p != '*' ; p ++ );
		if ( *p == 0 ) {
			(*argv)[argc] = pp;
			(*argt)[argc++] = 0;
			break;
		}
		for ( q = p ; *q &&
			((unsigned int)q) > ((unsigned int)pp) &&
			*q != '/' ; q -- );
		if ( q == pp ) {
			(*argv)[argc] = pp;
			(*argt)[argc++] = 1;
		}
		else if ( *q == '/' ) {
			*q ++ = 0;
			(*argv)[argc] = pp;
			(*argt)[argc++] = 0;

			(*argv)[argc] = q;
			(*argt)[argc++] = 1;
		}
		else {
			er_panic("get_wildcard_list(1)");
		}
		for ( r = p ; *r && *r != '/' ; r ++ );
		if ( *r == '/' )
			*r ++ = 0;
		pp = r;
	}

	return argc;
}


DIR_LIST *
set_path_dir_list(DIR_LIST * d,char * path)
{
char * new;
int len1,len2;
struct stat sbuf;
DIR_LIST * ret;
	len2 = strlen(path);
	if ( d == 0 ) {
		if ( stat(path,&sbuf) < 0 )
			return 0;
		switch ( sbuf.st_mode & S_IFMT ) {
		case S_IFREG:
			return add_dir_list(0,path,DLF_FILE);
		case S_IFDIR:
			return add_dir_list(0,path,DLF_DIR);
		default:
			return 0;
		}
	}
	ret = d;
	for ( ; d ; d = d->next ) {
		len1 = strlen(d->name);
		new = d_alloc(len1+len2+2,123);
		strcpy(new,d->name);
		new[len1] = '/';
		strcpy(&new[len1+1],path);
		d_f_ree(d->name);
		d->name = new;
	}
	return ret;
}

void
print_wlist(char ** argv,char * argt,char * w,int argc)
{
int i;
	printf("== ");
	for ( i = 0 ; i < argc ; i ++ )
		printf("(%s %i)",argv[i],argt[i]);
	printf("\n");
}

DIR_LIST *
get_dir(
	char * wildcard,
	char * base_dir,
	char * target_dir,
	char * prefix,
	int condition)
{
char * w;
char ** argv;
char * argt;
int argc;
int i;
char * p;
DIR_LIST * ret, * ret2, * ret3, * ret4;
char * down_path;
int target_type;
int up_path_type;
char * up_path;
int double_star;
int last_match;


	argc = get_wildcard_list(&argv,&argt,&w,wildcard);

	ret = 0;
	last_match = -1;
	for ( i = 0 ; i < argc ; i ++ ) {
		if ( argt[i] == 0 ) {
			ret = set_path_dir_list(ret,argv[i]);
			continue;
		}
		last_match = i;
		if ( argc - i == 1 ) {
			up_path = 0;
			up_path_type = condition;
			target_type = condition;
		}
		else {
			target_type = DLF_DIR;
			if ( argt[i+1] )
				up_path = 0;
			else 	up_path = argv[i+1];
			if ( argc - i == 2 )
				up_path_type = condition;
			else	up_path_type = DLF_DIR;
		}
		if ( strcmp(argv[i],"**") == 0 ) {
			switch ( i ) {
			case 0:
				ret = get_double_star(
					0,0,
					target_type,
					up_path,
					up_path_type);
				break;
			case 1:
				if ( argt[0] )
					goto loop_ret;
				ret = get_double_star(
					0,argv[0],
					target_type,
					up_path,
					up_path_type);
				break;
			default:
			loop_ret:
				ret2 = ret3 = ret;
				ret = 0;
				for ( ; ret2 ; ret2 = ret2->next ) {
					ret4 = get_double_star(
						0,
						ret2->name,
						target_type,
						up_path,
						up_path_type);
					ret = marge_dir_list(ret,ret4);
				}
				free_dir_list(ret3);
			}
		}
		else {
			switch ( i ) {
			case 0:
				ret = get_file_star(
					0,
					target_type,
					argv[i],
					up_path,
					up_path_type);
				break;
			case 1:
				if ( argt[0] )
					goto loop_ret2;
				ret = get_file_star(
					argv[0],
					target_type,
					argv[i],
					up_path,
					up_path_type);
				break;
			default:
			loop_ret2:
				ret2 = ret3 = ret;
				ret = 0;
				for ( ; ret2 ; ret2 = ret2->next ) {
					ret4 = get_file_star(
						ret2->name,
						target_type,
						argv[i],
						up_path,
						up_path_type);
					ret = marge_dir_list(ret,ret4);
				}
				free_dir_list(ret3);
			}
		}
	}
	ret2 = get_no_star(ret,0,condition,
			base_dir,target_dir,prefix);
	free_dir_list(ret);
	return ret2;
}
