/* vifm
 * Copyright (C) 2001 Ken Steen.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * 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.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
 */

#if(defined(BSD) && (BSD>=199103)) 
	#include<sys/types.h> /* required for regex.h on FreeBSD 4.2 */
#endif

#include<ncurses.h>
#include<unistd.h> /* chdir() */
#include<stdlib.h> /* malloc  qsort */
#include<sys/stat.h> /* stat */
#include<sys/time.h> /* localtime */
#include<time.h>
#include<regex.h>
#include<dirent.h> /* DIR */
#include<string.h> /* strcat() */
#include<pwd.h>
#include<grp.h>

#include "config.h" /* menu colors */
#include "filelist.h"
#include "keys.h"
#include "menus.h"
#include "sort.h"
#include "status.h"
#include "ui.h"
#include "utils.h" /* update_term_title() */



static void
add_sort_type_info(FileView *view, int y, int x, int current_line)
{
	char buf[24];
	struct passwd *pwd_buf;
	struct group *grp_buf;
	struct tm *tm_ptr;

	switch(view->sort_type)
	{
		 case SORT_BY_OWNER_NAME:
			 if((pwd_buf = getpwuid(view->dir_entry[x].uid)) != NULL)
			 {
				 snprintf(buf, sizeof(buf), " %s", pwd_buf->pw_name);
				 break;
			 }
		 case SORT_BY_OWNER_ID:
			 snprintf(buf, sizeof(buf), " %d", (int) view->dir_entry[x].uid);
			 break;
		 case SORT_BY_GROUP_NAME:
			 if((grp_buf = getgrgid(view->dir_entry[x].gid)) != NULL)
			 {
				 snprintf(buf, sizeof(buf), " %s", grp_buf->gr_name);
				 break;
			 }
		 case SORT_BY_GROUP_ID:
			 snprintf(buf, sizeof(buf), " %d", (int) view->dir_entry[x].gid);
			 break;
		case SORT_BY_MODE:
			 {
				  if (S_ISREG (view->dir_entry[x].mode))
					{
						if((S_IXUSR &view->dir_entry[x].mode)
								|| (S_IXGRP &view->dir_entry[x].mode)
								|| (S_IXOTH &view->dir_entry[x].mode))

							snprintf(buf, sizeof(buf), " exe");
						else
							snprintf(buf, sizeof(buf), " reg");
					}
					else if(S_ISLNK(view->dir_entry[x].mode))
						 snprintf(buf, sizeof(buf), " link");
					else if (S_ISDIR (view->dir_entry[x].mode))
						 snprintf(buf, sizeof(buf), " dir");
					else if (S_ISCHR (view->dir_entry[x].mode))
						 snprintf(buf, sizeof(buf), " char");
					else if (S_ISBLK (view->dir_entry[x].mode))
						 snprintf(buf, sizeof(buf), " block");
					else if (S_ISFIFO (view->dir_entry[x].mode))
						 snprintf(buf, sizeof(buf), " fifo");
					else if (S_ISSOCK (view->dir_entry[x].mode))
						 snprintf(buf, sizeof(buf), " sock");
					else
						 snprintf(buf, sizeof(buf), "  ?  ");
				break;
			 }
		case SORT_BY_TIME_MODIFIED:
			 tm_ptr = localtime(&view->dir_entry[x].mtime);
			 strftime(buf, sizeof(buf), " %m/%d-%H:%M", tm_ptr);
			 break;
		case SORT_BY_TIME_ACCESSED:
			 tm_ptr = localtime(&view->dir_entry[x].atime);
			 strftime(buf, sizeof(buf), " %m/%d-%H:%M", tm_ptr);
			 break;
		case SORT_BY_TIME_CHANGED:
			 tm_ptr = localtime(&view->dir_entry[x].ctime);
			 strftime(buf, sizeof(buf), " %m/%d-%H:%M", tm_ptr);
			 break;
		case SORT_BY_NAME:
		case SORT_BY_EXTENSION:
		case SORT_BY_SIZE:
				snprintf(buf, sizeof(buf), " %d", view->dir_entry[x].size);
			break;
		default:
				snprintf(buf, sizeof(buf), " %d", view->dir_entry[x].size);
			break;
    }
	if (cfg.use_color)
	{
		if (current_line)
			wattron(view->win, COLOR_PAIR(CURR_LINE_COLOR) | A_BOLD);

		mvwaddstr(view->win, y, 
					view->window_width - strlen(buf), buf); 

		if (current_line)
			wattroff(view->win, COLOR_PAIR(CURR_LINE_COLOR) | A_BOLD);
	}
	else
	{
		if (current_line)
			wattron(view->win, A_REVERSE);

		mvwaddstr(view->win, y, 
					view->window_width - strlen(buf), buf); 

	 	if (current_line)
			wattroff(view->win, A_REVERSE);
	}
}

char *
get_current_file_name(FileView *view)
{
		return view->dir_entry[view->list_pos].name;
}

void
free_selected_file_array(FileView *view)
{
	int x;
	if(view->selected_filelist)
	{
		for(x = 0; x < view->selected_files; x++)
		{
			if(view->selected_filelist[x])
			{
				my_free(view->selected_filelist[x]);
			}

		}
		if(view->selected_filelist)
			my_free(view->selected_filelist);
		view->selected_filelist = NULL;
	}
}

/* If you use this function using the free_selected_file_array() 
 * will clean up the allocated memory
 */
void
get_all_selected_files(FileView *view)
{
	size_t namelen;
	int x;
	int y = 0;

	/* No selected files so just use the current file */
	if(!view->selected_files)
		view->dir_entry[view->list_pos].selected = 1;

	view->selected_filelist = 
		(char **)calloc(view->selected_files, sizeof(char *));
	if(view->selected_filelist == NULL)
	{
		show_error_msg(" Memory Error ", "Unable to allocate enough memory");
		return;
	}

	for(x = 0; x < view->list_rows; x++)
	{
		if(view->dir_entry[x].selected)
		{
			namelen = strlen(view->dir_entry[x].name);
			view->selected_filelist[y] = malloc(namelen +1);
			if(view->selected_filelist[y] == NULL)
			{
				show_error_msg(" Memory Error ", "Unable to allocate enough memory");
				return;
			}
			strcpy(view->selected_filelist[y], 
					view->dir_entry[x].name);
			y++;
		}
	}
	view->selected_files = y;
}


int
find_file_pos_in_list(FileView *view, char *file)
{
	int x;
	int found = 0;

	for(x = 0; x < view->list_rows; x++)
	{
		if(!strcmp(view->dir_entry[x].name, file))
		{
			found = 1;
			break;
		}
	}
	if(found)
		return x;
	else
		return -1;
}

void
draw_dir_list(FileView *view, int top, int pos)
{
	int x;
	int y = 0;
	char file_name[view->window_width -2];
	int LINE_COLOR;
	int bold = 1;

	werase(view->win);
	werase(view->title);
	wprintw(view->title, "%s", view->curr_dir);
	wnoutrefresh(view->title);

		/* This is needed for reloading a list that has had files deleted */
	while((view->list_rows - view->list_pos) <= 0)
	{
		view->list_pos--;
		view->curr_line--;
	}
		
	/* Show as much of the directory as possible. */
	if(view->window_rows >= view->list_rows)
		top = 0;
	else if((view->list_rows - top) <= view->window_rows)
	{
		top = view->list_rows - view->window_rows -1;
		view->curr_line++;
	}


	if(cfg.use_color)
	{
		/* Colorize the files */
		for(x = top; x < view->list_rows; x++)
		{
			/* Extra long file names are truncated to fit */
			strncpy(file_name,  view->dir_entry[x].name, sizeof(file_name) -1);
			file_name[view->window_width -2] = '\0';

			wmove(view->win, y, 1);
			if(view->dir_entry[x].selected)
			{
				LINE_COLOR = SELECTED_COLOR;
			}
			else
			{
				switch(view->dir_entry[x].type)
				{
					case DIRECTORY:
						LINE_COLOR = DIRECTORY_COLOR;
						break;
					case LINK:
						LINE_COLOR = LINK_COLOR;
						break;
					case SOCKET:
						LINE_COLOR = SOCKET_COLOR;
						break;
					case DEVICE:
						LINE_COLOR = DEVICE_COLOR;
						break;
					case EXECUTABLE:
						LINE_COLOR = EXECUTABLE_COLOR;
						break;
					default:
						LINE_COLOR = WIN_COLOR;
						bold = 0;
						break;
				}
			}
			if(bold)
			{
				wattrset(view->win, COLOR_PAIR(LINE_COLOR) | A_BOLD);
				wattron(view->win, COLOR_PAIR(LINE_COLOR) | A_BOLD);
				wprintw(view->win, "%s", file_name);
				wattroff(view->win, COLOR_PAIR(LINE_COLOR) | A_BOLD);

				add_sort_type_info(view, y, x, 0);
			}
			else
			{
				wattrset(view->win, COLOR_PAIR(LINE_COLOR));
				wattron(view->win, COLOR_PAIR(LINE_COLOR));
				wprintw(view->win, "%s", file_name);
				wattroff(view->win, COLOR_PAIR(LINE_COLOR) | A_BOLD);

				add_sort_type_info(view, y, x, 0);
				bold = 1;
			}
			y++;
			if(y > view->window_rows)
				break;
		}
	}
	else /* Not using color */
	{
		wattroff(view->win, A_BOLD);
		for(x = top; x < view->list_rows; x++)
		{
			strncpy(file_name,  view->dir_entry[x].name, sizeof(file_name) -1);
			file_name[view->window_width -2] = '\0';
			if(view->dir_entry[x].selected)
			{
				wattrset(view->win, A_REVERSE);
				wattron(view->win, A_REVERSE);
			}
			wmove(view->win, y, 1);

			wprintw(view->win, "%s", file_name);
			wattroff(view->win, A_BOLD);
			wattroff(view->win, A_REVERSE);
			add_sort_type_info(view, y, x, 0);
			y++;
			if(y > view->window_rows)
				break;
		}
	}
	if(view != curr_view)
		mvwaddstr(view->win, view->curr_line, 0, "*");

	view->top_line = top;
}

int
S_ISEXE(mode_t mode)
{
	if((S_IXUSR & mode) || (S_IXGRP & mode) || (S_IXOTH & mode))
		return 1;

	return 0;
}

void
erase_current_line_bar(FileView *view)
{
	int old_cursor = view->curr_line;
	int old_pos = view->top_line + old_cursor;
	char file_name[view->window_width -2];
	int bold = 1;

	if(cfg.use_color)
	{
		int LINE_COLOR;

		/* Extra long file names are truncated to fit */

		wattroff(view->win, COLOR_PAIR(CURR_LINE_COLOR) | A_BOLD);
		if((old_pos > -1)  && (old_pos < view->list_rows))
		{
			strncpy(file_name,  view->dir_entry[old_pos].name, 
					sizeof(file_name) -2);
		}
		else /* The entire list is going to be redrawn so just return. */
			return;



		wmove(view->win, old_cursor, 1);

		wclrtoeol(view->win);

		if(view->dir_entry[old_pos].selected)
		{
			LINE_COLOR = SELECTED_COLOR;
		}
		else
		{
			switch(view->dir_entry[old_pos].type)
			{
				case DIRECTORY:
					LINE_COLOR = DIRECTORY_COLOR;
					break;
				case LINK:
					LINE_COLOR = LINK_COLOR;
					break;
				case SOCKET:
					LINE_COLOR = SOCKET_COLOR;
					break;
				case DEVICE:
					LINE_COLOR = DEVICE_COLOR;
					break;
				case EXECUTABLE:
					LINE_COLOR = EXECUTABLE_COLOR;
					break;
				default:
					LINE_COLOR = WIN_COLOR;
					bold = 0;
					break;
			}
		}
		if(bold)
		{
			wattrset(view->win, COLOR_PAIR(LINE_COLOR) | A_BOLD);
			wattron(view->win, COLOR_PAIR(LINE_COLOR) | A_BOLD);
			mvwaddnstr(view->win, old_cursor, 1, file_name, view->window_width -4);
			wattroff(view->win, COLOR_PAIR(LINE_COLOR) | A_BOLD);

			add_sort_type_info(view, old_cursor, old_pos, 0);
		}
		else
		{
			wattrset(view->win, COLOR_PAIR(LINE_COLOR));
			wattron(view->win, COLOR_PAIR(LINE_COLOR));
			mvwaddnstr(view->win, old_cursor, 1, file_name, view->window_width -4);
			wattroff(view->win, COLOR_PAIR(LINE_COLOR) | A_BOLD);
			bold = 1;
			add_sort_type_info(view, old_cursor, old_pos, 0);
		}
	}
	else
	{
		wattroff(view->win, A_BOLD);
		wattroff(view->win, A_REVERSE);

		strncpy(file_name,  view->dir_entry[old_pos].name, 
					sizeof(file_name) -2);

		wmove(view->win, old_cursor, 1);
		wclrtoeol(view->win);

		if(view->dir_entry[old_pos].selected)
		{
			wattrset(view->win, A_BOLD);
			wattron(view->win, A_BOLD);
		}
		wmove(view->win, view->curr_line, 1);
		wprintw(view->win, "%s", file_name);
		wattroff(view->win, A_BOLD);
		wattroff(view->win, A_REVERSE);
		add_sort_type_info(view, old_cursor, old_pos, 0);
	}
}

void
moveto_list_pos(FileView *view, int pos)
{
	int redraw = 0;
	int old_cursor = view->curr_line;
	char file_name[view->window_width +2];
	int x;

	if(pos < 1)
		pos = 0;

	if(pos > view->list_rows -1)
		pos = (view->list_rows -1);

	
	if(view->curr_line > view->list_rows -1)
		view->curr_line = view->list_rows -1;

	erase_current_line_bar(view);

	if((view->top_line <=  pos) && (pos <= (view->top_line + view->window_rows)))
	{
		view->curr_line = pos - view->top_line;
	}
	else if((pos > (view->top_line + view->window_rows)))
	{
		while(pos > (view->top_line + view->window_rows))
			view->top_line++;

		view->curr_line = view->window_rows;
		redraw = 1;
	}
	else if(pos < view->top_line)
	{
		while(pos < view->top_line)
			view->top_line--;

		view->curr_line = 0;
		redraw = 1;
	}

	view->list_pos = pos;



	if(redraw)
		draw_dir_list(view, view->top_line, view->curr_line);

	if(old_cursor != view->curr_line)
	{
		wattroff(view->win, COLOR_PAIR(CURR_LINE_COLOR));
		mvwaddstr(view->win, old_cursor, 0, " ");

		if(cfg.use_color)
		{

			snprintf(file_name, sizeof(file_name) -1, " %s", 
					view->dir_entry[pos].name);
			wattron(view->win, COLOR_PAIR(CURR_LINE_COLOR) | A_BOLD);

			for (x = strlen(file_name); x < view->window_width ; x++)
				file_name[x] = ' ';

			file_name[view->window_width] = ' ';
			file_name[view->window_width +1 ] = '\0';
			mvwaddstr(view->win, view->curr_line, 0, file_name);

			add_sort_type_info(view, view->curr_line, pos, 1);
		}
		else /* Not using color */
		{
			wattroff(view->win, A_BOLD);

			snprintf(file_name, sizeof(file_name) -1, " %s", 
					view->dir_entry[pos].name);

			wattron(view->win,  A_REVERSE);

			for (x = strlen(file_name); x < view->window_width ; x++)
				file_name[x] = ' ';
			file_name[view->window_width] = ' ';
			file_name[view->window_width +1 ] = '\0';
			mvwaddstr(view->win, view->curr_line, 0, file_name);
			add_sort_type_info(view, view->curr_line, pos, 1);
		}
	}
	else
	{
		if (cfg.use_color)
		{
			int x;
			snprintf(file_name, sizeof(file_name), " %s", 
						view->dir_entry[pos].name);
			wattron(view->win, COLOR_PAIR(CURR_LINE_COLOR) | A_BOLD);
			for (x = strlen(file_name); x < view->window_width; x++)
				file_name[x] = ' ';
			file_name[view->window_width] = ' ';
			file_name[view->window_width +1] = '\0';
			mvwaddstr(view->win, view->curr_line, 0, file_name);

			add_sort_type_info(view, view->curr_line, pos, 1);
		}
		else
		{
			int x;
			snprintf(file_name, sizeof(file_name), " %s", 
						view->dir_entry[pos].name);
			wattrset(view->win, A_REVERSE);
			wattron(view->win,  A_REVERSE);
			for (x = strlen(file_name); x < view->window_width; x++)
				file_name[x] = ' ';
			file_name[view->window_width] = ' ';
			file_name[view->window_width +1] = '\0';
			mvwaddstr(view->win, view->curr_line, 0, file_name);
			wattroff(view->win, A_REVERSE);

			add_sort_type_info(view, view->curr_line, pos, 1);
		}
	}
}


static int
regexp_filter_match(FileView *view,  char *filename)
{
	regex_t re;

	if(regcomp(&re, view->filename_filter, REG_EXTENDED) == 0)
	{
		if(regexec(&re, filename, 0, NULL, 0) == 0)
		{
			regfree(&re);
			return !view->invert;
		}
		regfree(&re);
		return view->invert;
	}
	regfree(&re);
	return 1;
}

void
save_view_history(FileView *view)
{
	int x = 0;
	int found = 0;

	for(x = 0; x < view->history_num; x++)
	{
		if(strlen(view->history[x].dir) < 1)
			break;
		if(!strcmp(view->history[x].dir, view->curr_dir))
		{
			found = 1;
			break;
		}
	}
	if(found)
	{
		snprintf(view->history[x].file, sizeof(view->history[x].file),
				"%s", view->dir_entry[view->list_pos].name);
		return;
	}

	if(x == cfg.history_len)
	{
		int y;
		for(y = 0; y < cfg.history_len -1; y++)
		{
			snprintf(view->history[y].file, sizeof(view->history[y].file),
						"%s", view->history[y +1].file);
			snprintf(view->history[y].dir, sizeof(view->history[y].dir),
					"%s", view->history[y +1].dir);
		}
		snprintf(view->history[cfg.history_len -1].file, 
				sizeof(view->history[cfg.history_len -1].file),
				"%s", view->dir_entry[view->list_pos].name);
		snprintf(view->history[cfg.history_len -1].dir, 
				sizeof(view->history[cfg.history_len -1].dir),
				"%s", view->curr_dir);
	}
	else
	{
		snprintf(view->history[x].dir, sizeof(view->history[x].dir),
			"%s", view->curr_dir);
		snprintf(view->history[x].file, sizeof(view->history[x].file),
				"%s", view->dir_entry[view->list_pos].name);
		view->history_num++;
	}
	return;

}

static void
check_view_dir_history(FileView *view)
{
	int x = 0;
	int found = 0;
	int pos = 0;

	if(curr_stats.is_updir)
	{
		pos = find_file_pos_in_list(view, curr_stats.updir_file);
	}
	else
	{
		for(x = 0; x < view->history_num ; x++)
		{
			if(strlen(view->history[x].dir) < 1)
				break;
			if(!strcmp(view->history[x].dir, view->curr_dir))
			{
				found = 1;
				break;
			}
		}
		if(found)
		{
			pos = find_file_pos_in_list(view, view->history[x].file);
		}
		else
		{
			view->list_pos = 0;
			view->curr_line = 0;
			view->top_line = 0;
			return;
		}
	}

	if(pos < 0)
		pos = 0;
	view->list_pos = pos;
	if(view->list_pos <= view->window_rows)
	{
		view->top_line = 0;
		view->curr_line = view->list_pos;
	}
	else if(view->list_pos > (view->top_line + view->window_rows))
	{
		while(view->list_pos > (view->top_line + view->window_rows))
			view->top_line++;

		view->curr_line = view->window_rows; 
	}
	return;
}

void
clean_selected_files(FileView *view)
{
	int x;

	if(view->selected_files)
	{
		for(x = 0; x < view->list_rows; x++)
			view->dir_entry[x].selected = 0;

		view->selected_files = 0;
	}

}

void
change_directory(FileView *view, char *directory)
{
	DIR *dir;
	struct stat s;

	if(!strcmp(directory, "../"))
	{
		char *str1, *str2;
		curr_stats.is_updir = 1;
		str2 = str1 = view->curr_dir;
		while((str1 = strstr(str1, "/")) != NULL)
		{
			str1++;
			str2 = str1;
		}
		snprintf(curr_stats.updir_file, sizeof(curr_stats.updir_file), "%s/", str2);
	}
	else
		curr_stats.is_updir = 0;



	if(access(directory, F_OK) != 0)
	{
		show_error_msg(" Directory Access Error ", "That directory does not exist.");
		change_directory(view, getenv("HOME"));
		clean_selected_files(view);
		return;
	}
	if(access(directory, R_OK) != 0)
	{
		show_error_msg(" Directory Access Error ", "You do not have read access on that directory");

		clean_selected_files(view);
		return;
	}

	dir = opendir(directory);

	if(dir == NULL)
	{
		clean_selected_files(view);
		return;
	}


	if(chdir(directory) == -1)
	{
		closedir(dir);
		status_bar_message("Couldn't open directory");
		return;
	}

	snprintf(view->last_dir, sizeof(view->last_dir), "%s", view->curr_dir);

	clean_selected_files(view);
		
	save_view_history(view);
	getcwd(view->curr_dir, sizeof(view->curr_dir));

	/* Save the directory modified time to check for file changes */
	stat(view->curr_dir, &s);
	view->dir_mtime = s.st_mtime;
	closedir(dir);
}

static void
reset_selected_files(FileView *view)
{
	int x;
	for(x = 0; x < view->selected_files; x++)
	{
		if(view->selected_filelist[x])
		{
			int pos = find_file_pos_in_list(view, view->selected_filelist[x]);
			if(pos >= 0 && pos < view->list_rows)
				view->dir_entry[pos].selected = 1;
		}
	}
	free_selected_file_array(view);
}


void
load_dir_list(FileView *view, int reload)
{
	DIR *dir;
	struct dirent *d;
	struct stat s;
	int x;
	int namelen = 0;
	int old_list = view->list_rows;

	dir = opendir(view->curr_dir);

	if(dir == NULL)
		return;


	view->filtered = 0;
	
	lstat(view->curr_dir, &s);
	view->dir_mtime = s.st_mtime;

	if(!reload && s.st_size > 2048)
	{
		status_bar_message("Reading Directory...");
	}

	update_all_windows();

	if(reload && view->selected_files)
		get_all_selected_files(view);


	if(view->dir_entry)
	{
		for(x = 0; x < old_list; x++)
		{
			if(view->dir_entry[x].name)
			{
				my_free(view->dir_entry[x].name);
				view->dir_entry[x].name = NULL;
			}
		}

		my_free(view->dir_entry);
		view->dir_entry = NULL;
	}
	view->dir_entry = (dir_entry_t *)malloc(sizeof(dir_entry_t));
	if(view->dir_entry == NULL)
	{
		show_error_msg(" Memory Error ", "Unable to allocate enough memory.");
		return;
	}

	for(view->list_rows = 0; (d = readdir(dir)); view->list_rows++)
	{
		/* Ignore the "." directory. */
		if(strcmp(d->d_name, ".") == 0)
		{
			view->list_rows--;
			continue;
		}
		/* Always include the ../ directory unless it is the root directory. */ 
		if(strcmp(d->d_name, "..") == 0)
		{
			if(!strcmp("/", view->curr_dir))
			{
				view->list_rows--;
				continue;
			}
		}
		if(!regexp_filter_match(view, d->d_name) && strcmp("..", d->d_name))
		{
			view->filtered++;
			view->list_rows--;
			continue;
		}


		if(d->d_name[0] == '.') 
		{
			if((strcmp(d->d_name, "..")) && (view->hide_dot))
			{
				view->filtered++;
				view->list_rows--;
				continue;
			}
		}

		view->dir_entry = (dir_entry_t *)realloc(view->dir_entry,
				(view->list_rows + 1) * sizeof(dir_entry_t));
		if(view->dir_entry == NULL)
		{
			show_error_msg(" Memory Error ", "Unable to allocate enough memory");
			return ;
		}

		namelen = strlen(d->d_name);
		/* Allocate extra for adding / to directories. */
		view->dir_entry[view->list_rows].name = malloc(namelen + 2);
		if(view->dir_entry[view->list_rows].name == NULL)
		{
			show_error_msg(" Memory Error ", "Unable to allocate enough memory");
			return;
		}

		strcpy(view->dir_entry[view->list_rows].name, d->d_name);

		/* All files start as unselected */
		view->dir_entry[view->list_rows].selected = 0;

		/* Load the inode info */ 
		lstat(view->dir_entry[view->list_rows].name, &s);

		view->dir_entry[view->list_rows].size = (int)s.st_size;
		view->dir_entry[view->list_rows].mode = s.st_mode;
		view->dir_entry[view->list_rows].uid = s.st_uid;
		view->dir_entry[view->list_rows].gid = s.st_gid;
		view->dir_entry[view->list_rows].mtime = s.st_mtime;
		view->dir_entry[view->list_rows].atime = s.st_atime;
		view->dir_entry[view->list_rows].ctime = s.st_ctime;

		if(s.st_ino)
		{
			switch(s.st_mode & S_IFMT)
			{
				case S_IFLNK:
					view->dir_entry[view->list_rows].type = LINK;
					break;
				case S_IFDIR:
					namelen = sizeof(view->dir_entry[view->list_rows].name);
					strcat(view->dir_entry[view->list_rows].name, "/");
					view->dir_entry[view->list_rows].type = DIRECTORY;
					break;
				case S_IFCHR:
				case S_IFBLK:
					view->dir_entry[view->list_rows].type = DEVICE;
					break;
				case S_IFSOCK:
					view->dir_entry[view->list_rows].type = SOCKET;
					break;
				case S_IFREG:
					if(S_ISEXE(s.st_mode))
					{
						view->dir_entry[view->list_rows].type = EXECUTABLE;
						break;
					}
					view->dir_entry[view->list_rows].type = REGULAR;
					break;
				default:
					view->dir_entry[view->list_rows].type = UNKNOWN;
				break;
			}
		}
	}

	closedir(dir);
	
	if(!reload && s.st_size > 2048)
	{
		status_bar_message("Sorting Directory...");
	}
	qsort(view->dir_entry, view->list_rows, sizeof(dir_entry_t),
				sort_dir_list);

	for(x = 0; x < view->list_rows; x++)
		view->dir_entry[x].list_num = x;

	/* If reloading the same directory don't jump to 
	 * history position.  Stay at the current line
	 */
	if(!reload)
		check_view_dir_history(view);


	/*
	 * It is possible to set the file name filter so that no files are showing
	 * in the / directory.  All other directorys will always show at least the
	 * ../ file.  This resets the filter and reloads the directory.
	 */
	if(view->list_rows < 1)
	{
		char msg[64];
		snprintf(msg, sizeof(msg), 
				"The %s pattern %s did not match any files. It was reset.", 
				view->filename_filter, view->invert==1 ? "inverted" : "");
		status_bar_message(msg);
		view->filename_filter = (char *)realloc(view->filename_filter,
				strlen("*") +1);
		if(view->filename_filter == NULL)
		{
			show_error_msg(" Memory Error ", "Unable to allocate enough memory");
			return;
		}
		snprintf(view->filename_filter, sizeof(view->filename_filter), "*");
		if(view->invert)
			view->invert = 0;

		load_dir_list(view, 1);
		draw_dir_list(view, view->top_line, view->list_pos);
		return;
	}
	if(reload && view->selected_files)
		reset_selected_files(view);

	draw_dir_list(view, view->top_line, view->list_pos);

	return;
}


