#include "ftptool.h"

#ifdef USE_PROTOTYPES
struct dirlist *new_dirlist(char *name, char *date, char *owner, 
	char *group, mode_t mode, size_t size)
#else
struct dirlist *new_dirlist(name, date, owner, group, mode, size)
char	*name;
char	*date;
char	*owner;
char	*group;
mode_t	mode;
size_t	size;
#endif
{
	struct dirlist *tmp;

	tmp = (struct dirlist *)malloc(sizeof(struct dirlist));
	if (tmp == NULL)
		return NULL;
	bzero((char *)tmp, sizeof(struct dirlist));
	tmp->name = (char *)malloc((unsigned int)(strlen(name) + 1));
	if (tmp->name == NULL) {
		free_dirlist(tmp);
		return NULL;
	}

	tmp->date = (char *)malloc((unsigned int)(strlen(date) + 1));
	if (tmp->date == NULL) {
		free_dirlist(tmp);
		return NULL;
	}

	tmp->owner = (char *)malloc((unsigned int)(strlen(owner) + 1));
	if (tmp->owner == NULL) {
		free_dirlist(tmp);
		return NULL;
	}

	tmp->group = (char *)malloc((unsigned int)(strlen(group) + 1));
	if (tmp->group == NULL) {
		free_dirlist(tmp);
		return NULL;
	}

	strcpy(tmp->name, name);
	strcpy(tmp->date, date);
	strcpy(tmp->owner, owner);
	strcpy(tmp->group, group);
	tmp->mode = mode;
	tmp->size = size;
	tmp->next = NULL;
	return tmp;
}

#ifdef USE_PROTOTYPES
struct dirlist *add_dirname(struct dirlist *head, char *name, char *date, 
	char *owner, char *group, mode_t mode, size_t size, int sort_mode, 
	int sort_direction)
#else
struct dirlist *add_dirname(head, name, date, owner, group, mode, 
	size, sort_mode, sort_direction)
struct dirlist *head;
char	*name;
char	*date;
char	*owner;
char	*group;
mode_t	mode;
size_t	size;
int sort_mode;
int	sort_direction;
#endif
{
	struct dirlist *tmp;
	struct dirlist *oldnext = NULL;

	if (non_unix)
		sort_mode = SORTBYNAME;
	switch(sort_mode) {
	default:
		fprintf(stderr, "Unknown sort mode in add_dirname.\n");
		/* Fall through */
	case SORTBYNAME:
		if (sort_direction == ASCENDING)
			tmp = sortupbyname(head, name);
		else
			tmp = sortdownbyname(head, name);
		break;
	case SORTBYDATE:
		if (sort_direction == ASCENDING)
			tmp = sortupbydate(head, date);
		else
			tmp = sortdownbydate(head, date);
		break;
	case SORTBYSIZE:
		if (sort_direction == ASCENDING)
			tmp = sortupbysize(head, size);
		else
			tmp = sortdownbysize(head, size);
		break;
	}
	oldnext = tmp->next;
	tmp->next = new_dirlist(name, date, owner, group, mode, size);
	if (tmp->next == NULL) {
		tmp->next = oldnext;
		return NULL;
	}

	tmp->next->next = oldnext;
	return head;
}

#ifdef USE_PROTOTYPES
struct dirlist *add_dirlist_struct(struct dirlist *head, 
	struct dirlist *dlist, int sort_mode, int sort_direction)
#else
struct dirlist *add_dirlist_struct(head, dlist, sort_mode, sort_direction)
struct dirlist *head;
struct dirlist *dlist;
int sort_mode;
int	sort_direction;
#endif
{
	struct dirlist *tmp;

	if (non_unix)
		sort_mode = SORTBYNAME;
	switch(sort_mode) {
	default:
		fprintf(stderr, "Unknown sort mode in add_dirlist_struct.\n");
		/* Fall through */
	case SORTBYNAME:
		if (sort_direction == ASCENDING)
			tmp = sortupbyname(head, dlist->name);
		else
			tmp = sortdownbyname(head, dlist->name);
		break;
	case SORTBYDATE:
		if (sort_direction == ASCENDING)
			tmp = sortupbydate(head, dlist->date);
		else
			tmp = sortdownbydate(head, dlist->date);
		break;
	case SORTBYSIZE:
		if (sort_direction == ASCENDING)
			tmp = sortupbysize(head, dlist->size);
		else
			tmp = sortdownbysize(head, dlist->size);
		break;
	}
	dlist->next = tmp->next;
	tmp->next = dlist;
	return head;
}

#ifdef USE_PROTOTYPES
void free_dirlist(struct dirlist *head)
#else
void free_dirlist(head)
struct dirlist *head;
#endif
{
	struct dirlist *tmp;

	while (head) {
		tmp = head->next;
		if (head->name)
			free(head->name);
		if (head->date)
			free(head->date);
		if (head->owner)
			free(head->owner);
		if (head->group)
			free(head->group);
		free((char *)head);
		head = tmp;
	}
}

/* alphabetical order */
#ifdef USE_PROTOTYPES
struct dirlist *sortupbyname(struct dirlist *head, char *name)
#else
struct dirlist *sortupbyname(head, name)
struct dirlist *head;
char	*name;
#endif
{
	struct dirlist *tmp;
	int		rval;

	for (tmp = head; tmp->next != NULL; tmp = tmp->next) {
		if (ignore_case)
			rval = strcasecmp(name, tmp->next->name);
		else
			rval = strcmp(name, tmp->next->name);
		if (rval < 0)
			break; /* need to go before next entry. */
	}
	return tmp;
}

/* least recently modified */
#ifdef USE_PROTOTYPES
struct dirlist *sortupbydate(struct dirlist *head, char *date)
#else
struct dirlist *sortupbydate(head, date)
struct dirlist *head;
char	*date;
#endif
{
	struct dirlist *tmp;

	for (tmp = head; tmp->next != NULL; tmp = tmp->next) {
		if (isearlier(date, tmp->next->date)) 
			break; /* need to go before next entry. */
	}
	return tmp;
}

/* smallest to largest */
#ifdef USE_PROTOTYPES
struct dirlist *sortupbysize(struct dirlist *head, size_t size)
#else
struct dirlist *sortupbysize(head, size)
struct dirlist *head;
size_t	size;
#endif
{
	struct dirlist *tmp;

	for (tmp = head; tmp->next != NULL; tmp = tmp->next)
		if (size <= tmp->next->size) 
			break; /* need to go before next entry. */
	return tmp;
}

int cummonthdays[] = {
	0,
	31,
	59,
	90,
	120,
	151,
	181,
	212,
	243,
	273,
	304,
	334
};

#ifdef USE_PROTOTYPES
long	datetotime(char *date)
#else
long	datetotime(date)
char	*date;
#endif
{
	struct tm tm;

	/* "Aug 19 19:47" */
	/* "Jan 10  1990" */

	if (index(date, ':')) {
		hour_time(date, &tm);
		if (tm.tm_mon > current_month) 
			tm.tm_year = current_year - 1;
		else
			tm.tm_year = current_year;
	} else {
		year_time(date, &tm);
		tm.tm_hour = 0;
		tm.tm_min = 0;
	}
	return (tm.tm_min + tm.tm_hour * 60
		+ (tm.tm_year * 365 + tm.tm_mday + cummonthdays[tm.tm_mon]) * 1440);
}

#ifdef USE_PROTOTYPES
int isearlier(char *date1, char *date2)
#else
int isearlier(date1, date2)
char	*date1, *date2;
#endif
{
	long	time1;
	long	time2;


	time1 = datetotime(date1);
	time2 = datetotime(date2);

	if (time1 < time2)
		return 1;
	return 0;
}

/* reverse alphabetical order */
#ifdef USE_PROTOTYPES
struct dirlist *sortdownbyname(struct dirlist *head, char *name)
#else
struct dirlist *sortdownbyname(head, name)
struct dirlist *head;
char	*name;
#endif
{
	struct dirlist *tmp;
	int		rval;

	for (tmp = head; tmp->next != NULL; tmp = tmp->next) {
		if (ignore_case)
			rval = strcasecmp(name, tmp->next->name);
		else
			rval = strcmp(name, tmp->next->name);
		if (rval > 0)
			break; /* need to go before next entry. */
	}
	return tmp;
}

/* most recently modified */
#ifdef USE_PROTOTYPES
struct dirlist *sortdownbydate(struct dirlist *head, char *date)
#else
struct dirlist *sortdownbydate(head, date)
struct dirlist *head;
char	*date;
#endif
{
	struct dirlist *tmp;

	for (tmp = head; tmp->next != NULL; tmp = tmp->next) {
		if (!isearlier(date, tmp->next->date)) {
			break; /* need to go before next entry. */
		}
	}
	return tmp;
}

/* largest to smallest */
#ifdef USE_PROTOTYPES
struct dirlist *sortdownbysize(struct dirlist *head, size_t size)
#else
struct dirlist *sortdownbysize(head, size)
struct dirlist *head;
size_t	size;
#endif
{
	struct dirlist *tmp;

	for (tmp = head; tmp->next != NULL; tmp = tmp->next)
		if (size >= tmp->next->size) 
			break; /* need to go before next entry. */
	return tmp;
}

#ifdef USE_PROTOTYPES
void clear_slist(Panel panel_list)
#else
void clear_slist(panel_list)
Panel panel_list;
#endif
{
	int		nitems, row;
	struct dirlist *dotdot;

	nitems = xv_get(panel_list, PANEL_LIST_NROWS);
	if (nitems && (dotdot = (struct dirlist *)xv_get(panel_list, 
		PANEL_LIST_CLIENT_DATA, 0))) {
		dotdot->next = NULL;
		free_dirlist(dotdot);
	}
	for (row = nitems - 1; row >= 0; row--) {
		xv_set(panel_list,
			PANEL_LIST_DELETE, row,
			PANEL_PAINT, PANEL_NONE,
			NULL);
	}
	panel_paint(panel_list, PANEL_CLEAR);
}

#ifdef USE_PROTOTYPES
void actual_dirlist_to_slist(Panel panel_list, struct dirlist *head, 
	mode_t type, int showdotfiles)
#else
void actual_dirlist_to_slist(panel_list, head, type, showdotfiles)
Panel	panel_list;
struct dirlist *head;
mode_t	type;
int		showdotfiles;
#endif
{
	struct dirlist *tmp;
	int	row;
	Xv_font	entry_font;
	char *d;
	Server_image entry_glyph;

	row = 0;
	for (tmp = head->next; tmp != NULL; tmp = tmp->next) {
		if (type && ((type & S_IFMT) != (tmp->mode & S_IFMT)))
			continue;
		if ((tmp != head->next) && (tmp->name[0] == '.' && !showdotfiles))
			continue;
		d = tmp->date;
		if (!strcmp(tmp->date, "unknown"))
			d = "";
		if (tmp->size == -1)
			sprintf(scratch, "  %12s  %10s  %s", d, "", tmp->name);
		else
			sprintf(scratch, "  %12s  %10d  %s", d, tmp->size, tmp->name);

		switch(tmp->mode & S_IFMT) {
		case S_IFDIR:
			entry_font = bold_list_font;
			if (row != 0)
				entry_glyph = directory_glyph;
			else
				entry_glyph = dotdot_glyph;
			strcat(scratch, "/");
			break;
		case S_IFREG:
			entry_font = list_font;
			entry_glyph = file_glyph;
			break;
		case S_IFLNK:
			entry_font = list_font;
			entry_glyph = link_glyph;
			break;
		default:
			entry_font = list_font;
			entry_glyph = unknown_glyph;
			break;
		}
		xv_set(panel_list,
			PANEL_LIST_INSERT, row,
			PANEL_LIST_STRING, row, scratch,
			PANEL_LIST_FONT, row, entry_font,
			PANEL_LIST_GLYPH, row, entry_glyph,
			PANEL_LIST_CLIENT_DATA, row, tmp,
			PANEL_PAINT, PANEL_NONE,
			NULL);
		row++;
	} 

}

#ifdef USE_PROTOTYPES
void dirlist_to_slist(Panel panel_list, struct dirlist *head)
#else
void dirlist_to_slist(panel_list, head)
Panel	panel_list;
struct dirlist *head;
#endif
{
	struct dirlist *tmp;
	int	dotypesort = 0;
	int	showdotfiles = 0;

	add_dotdot(head);

	if (panel_list == local_window.list) {
		showdotfiles = local_showdotfiles;
		if (group_local_files)
			dotypesort = 1;
	} else {
		/* panel_list == base_window.list */
		showdotfiles = remote_showdotfiles;
		if (group_remote_files)
			dotypesort = 1;
	}
			
	if (dotypesort) {
		/* have to insert backwards, since the routine inserts at 0 */
#ifdef S_IFIFO
		actual_dirlist_to_slist(panel_list, head, S_IFIFO, showdotfiles);
#endif
#ifdef S_IFSOCK
		actual_dirlist_to_slist(panel_list, head, S_IFSOCK, showdotfiles);
#endif
		actual_dirlist_to_slist(panel_list, head, S_IFBLK, showdotfiles);
		actual_dirlist_to_slist(panel_list, head, S_IFCHR, showdotfiles);
		actual_dirlist_to_slist(panel_list, head, S_IFLNK, showdotfiles);
		actual_dirlist_to_slist(panel_list, head, S_IFREG, showdotfiles);
		actual_dirlist_to_slist(panel_list, head, S_IFDIR, showdotfiles);
	} else {
		actual_dirlist_to_slist(panel_list, head, 0, showdotfiles);
	}

	/* now remove '..' from list */
	tmp = head->next;
	head->next = head->next->next;
	tmp->next = NULL;

	/* all entries attached as client_data */
	xv_set(panel_list, 
		XV_SHOW, TRUE, 
		NULL);
}

#ifdef USE_PROTOTYPES
void add_dotdot(struct dirlist *head)
#else
void add_dotdot(head)
struct dirlist *head;
#endif
{
	struct dirlist *tmp;

	tmp = new_dirlist("..", "unknown", "", "", S_IFDIR, (size_t)-1);
	if (tmp == NULL)
		return;

	tmp->next = head->next;
	head->next = tmp;
}

#ifdef USE_PROTOTYPES
struct dirlist *sort_dirlist(struct dirlist *head, int sort_mode, 
	int sort_direction)
#else
struct dirlist *sort_dirlist(head, sort_mode, sort_direction)
struct dirlist *head;
int	sort_mode;
int	sort_direction;
#endif
{
	struct dirlist *tmp;
	struct dirlist *next;

	if (head == NULL)
		return NULL;
	/* sort current list into new one */
	tmp = head->next;
	head->next = NULL;
	while (tmp) {
		next = tmp->next;
		tmp->next = NULL;
		(void)add_dirlist_struct(head, tmp, sort_mode, sort_direction);
		tmp = next;
	}
	return head;
}

static char *abbrev_month[] = {
	"Jan", "Feb", "Mar", "Apr", "May", "Jun",
	"Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
};

#ifdef USE_PROTOTYPES
void hour_time(char *date, struct tm *tm)
#else
void hour_time(date, tm)
char	*date;
struct tm *tm;
#endif
{
	char	month[10];
	int		i;

	bzero((char *)tm, sizeof(struct tm));
	sscanf(date, "%s%d%d:%d", month, &tm->tm_mday, 
		&tm->tm_hour, &tm->tm_min);
	for (i = 0; i < 12; i++)
		if (!strncmp(month, abbrev_month[i], 3))
			break;
	if (i != 12)
		tm->tm_mon = i;
}

#ifdef USE_PROTOTYPES
void year_time(char *date, struct tm *tm)
#else
void year_time(date, tm)
char	*date;
struct tm *tm;
#endif
{
	char	month[10];
	int		i;

	bzero((char *)tm, sizeof(struct tm));
	sscanf(date, "%s%d%d", month, &tm->tm_mday, &tm->tm_year);
	for (i = 0; i < 12; i++)
		if (!strncmp(month, abbrev_month[i], 3))
			break;
	if (i != 12)
		tm->tm_mon = i;
	if (tm->tm_year > 1900)
		tm->tm_year -= 1900;
}
