#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <ncurses.h>
#include <dirent.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <signal.h>
#include <time.h>
#include <pwd.h>
#include <grp.h>
#include <glob.h>
#include "sel.h"

// Global variables --------------------------------------------------------

file_struct *file_list;
history_struct *history;
int   file_count = -1;         // How many files found
int   history_count = -1;      // How many directories in history
int   len_page;                // How many lines to display information (0 - n)
int   lines_page;              // How many lines your terminal has got (0 - n)
int   cols_page;               // How many columns we have
int   pointer;                 // The actually marked filename
int   pointer_pos;             // Absolute Pointer position
int   start_dsp;               // beginning of display of file_list
int   end_dsp;                 // end of display of file_list
short color_dsp;               // color yes=1 no=0
int   search_start;            // pointer-position to start search at
short search_success;          // last search ok=1 failed=0
int   tags = 0;                // number of tagged files
char  working_dir[len_path];   // current directory
char  last_dir[len_path];      // dir before last cd, used to jump back
char  home_dir[len_path];      // users home
char  start_dir[len_path];     // Start directory (when sel was started)
char  search_term[len_line];   // searchterm, used by /

// Settings ----------------------------------------------------------------

char  set_cmd[len_cmd];
char  set_path[len_path];
char  set_mask[len_path];
char  set_type[len_types];
char  set_banner[len_line];
char  set_file_sub;
short set_ask_before_cmd;
short set_blank_before_cmd;
short set_use_mask;
short set_use_full_path;
short set_long_listing;
short set_pause_after_cmd;
short set_quit_after_cmd;
short set_read_after_cmd;
short set_allow_multiple_selection;
short set_allow_change_command = TRUE;
short set_color_status_fg;
short set_color_status_bg;
short set_color_pointer_fg;
short set_color_pointer_bg;
short set_color_backgnd_fg;
short set_color_backgnd_bg;
short set_color_msg_fg;
short set_color_msg_bg;
short set_color_backgnd_tag_fg;
short set_color_backgnd_tag_bg;
short set_color_pointer_tag_fg;
short set_color_pointer_tag_bg;

// Signal + Error handling -------------------------------------------------

void sig_int(int sig) {
    signal(SIGINT, SIG_IGN);
    sel_exit(error_noaction);
}

void sig_term(int sig) {
    signal(SIGTERM, SIG_IGN);
    sel_exit(error_noaction);
}

void sig_winch(int sig) {
    signal(SIGWINCH, SIG_IGN);
    error("Can't change window-size while running sel", error_terminal);
}

void sig_init() {
    if (signal(SIGINT, sig_int) == SIG_ERR) {
        printf("Error: can't handle SIGINT\n");
        exit(error_signal);
    }
    if (signal(SIGTERM, sig_term) == SIG_ERR) {
        printf("Error: can't handle SIGTERM\n");
        exit(error_signal);
    }
    if (signal(SIGWINCH, sig_winch) == SIG_ERR) {
        printf("Error: can't handle SIGWINCH\n");
        exit(error_signal);
    }
}

void error(char *errtxt, int rc) {
    if (curscr != NULL) endwin();
    printf("%s\n", errtxt);
    exit(rc);
}

// the normal way to leave sel

void sel_exit(int rc) {
    if (curscr != NULL) {
        endwin();
        printf(lf);
    }
    exit(rc);
}

// User interaction --------------------------------------------------------

void help(void) {
    printf(version);
    printf(lf);
    printf(lf);
    printf("Syntax: select [options] [files]\n");
    printf(lf);
    printf("Options\n");
    printf(lf);
    printf("-a Ask before executing a command\n");
    printf("-b Text to display as headline\n");
    printf("-c Command to execute. 'percent' will be replaced by selected entry\n");
    printf("-C Filename-Substitutor (default is \"percent\")\n");
    printf("-d Start directory\n");
    printf("-f Use complete (absolute) filename with full path in executed commands\n");
    printf("-h Show this help\n");
    printf("-l Long file-listing with uid, gid, size, date and permissions\n");
    printf("-m filemask\n");
    printf("-w Wait after executing a command\n");
    printf("-q Quit after executing a command\n");
    printf("-u Read directory after executing a command\n");
    printf("-s Blank screen before executing a command\n");
    printf("-t File-type filter-string. Possible values: f,l,d,c,b,p,s (file, link,\n");
    printf("   directory, char-dev., block-dev., fifo, socket) Combinations such as\n");
    printf("   -t fdl are allowed\n");
    printf(lf);
    printf("--no-tagging Don't allow tagging of more than one file\n");
    printf("--no-command-entry Don't allow changing the command to be executed\n");
    printf(lf);
    printf("Color setting options\n");
    printf(lf);
    printf("--color-status_fg color      Set foreground-color of statusline\n");
    printf("--color-status_bg color      Set background-color of statusline\n");
    printf("--color-pointer-fg color     Set foreground-color of pointer\n");
    printf("--color-pointer-bg color     Set background-color of pointer\n");
    printf("--color-backgnd-fg color     Set background-color of file-list\n");
    printf("--color-backgnd-bg color     Set foreground-color of file-list\n");
    printf("--color-msg-fg color         Set foreground-color of warnings\n");
    printf("--color-msg-bg color         Set background-color of warnings\n");
    printf("--color-backgnd-tag-fg color Set foreground-color of tagged file\n");
    printf("--color-backgnd-tag-bg color Set background-color of tagged file\n");
    printf("--color-pointer-tag-fg color Set foreground-color of tagged pointer\n");
    printf("--color-pointer-tag-bg color Set background-color of tagged pointer\n");
    printf(lf);
    printf("Availiable color-values are:\n");
    printf(lf);
    printf("black\n");
    printf("red\n");
    printf("green\n");
    printf("yellow\n");
    printf("blue\n");
    printf("magenta\n");
    printf("cyan\n");
    printf("white\n");
    printf(lf);
    printf("[files] can be wildcards such as *.c; if you don't specify [files], sel\n");
    printf("displays all files (*).\n");
    sel_exit(error_ok);
}

void help_online(void) {
    endwin();
    if (debug) system("less help.txt");
    else system("less /usr/local/share/sel/help.txt");
}

void display_settings(void) {
    char input;

    clear();
    move(0,0);
    printw("Current settings\n\n");
    printw("Command:                   %s\n", set_cmd);

    printw("Path:                      %s\n", set_path);

    printw("Filemask:                  %s\n", set_mask);

    printw("Type:                      %s\n", set_type);

    printw("Banner:                    %s\n", set_banner);

    printw("Filename-Substition:       %c\n", set_file_sub);
    
    printw("Ask before execution:      ");
    if (set_ask_before_cmd) printw("on\n");
    else printw("off\n");

    printw("Blank before execution:    ");
    if (set_blank_before_cmd) printw("on\n");
    else printw("off\n");

    printw("Use full path:             ");
    if (set_use_full_path) printw("on\n");
    else printw("off\n");

    printw("Display file details       ");
    if (set_long_listing) printw("on\n");
    else printw("off\n");

    printw("Wait after execution       ");
    if (set_pause_after_cmd) printw("on\n");
    else printw("off\n");

    printw("Exit after execution       ");
    if (set_quit_after_cmd) printw("on\n");
    else printw("off\n");
    
    printw("Rescan dir after execution ");
    if (set_read_after_cmd) printw("on\n");
    else printw("off\n");

    printw("Allow multiple selection   ");
    if (set_allow_multiple_selection) printw("on\n");
    else printw("off\n");

    printw("Allow change command       ");
    if (set_allow_change_command) printw("on\n");
    else printw("off\n");

    printw("\n\nHit any key to continue ");
    
    input = getch();
    clear();
    return;
}

void status_put(char instr[]) {
    attron(A_BOLD);
    if (color_dsp) attron(COLOR_PAIR(COLOR_STATUS));
    else attron(A_REVERSE);
    mvprintw(lines_page + 1,0,"%-79s", instr);
    attroff(A_BOLD);
    if (color_dsp) attroff(COLOR_PAIR(COLOR_STATUS));
    else attroff(A_REVERSE);
}

// Initializations ---------------------------------------------------------

int term_getlines(int fd) {
    struct winsize size;
    
    if (ioctl(fd, TIOCGWINSZ, (char *)&size) < 0) return 0;
    return size.ws_row;
}

int term_getcols(int fd) {
    struct winsize size;
    
    if (ioctl(fd, TIOCGWINSZ, (char *)&size) < 0) return 0;
    return size.ws_col;
}

void init(void) {
    // initialize signals
    sig_init();
    
    // set defaults
    strcpy(set_banner, version);
    strcpy(set_path, ".");
    strcpy(set_mask, "*");
    strcpy(set_type, "*");
    set_cmd[0]                   = '\0';
    set_file_sub                 = DEF_FILE_SUB;
    set_use_mask                 = OFF;
    set_use_full_path            = OFF;
    set_ask_before_cmd           = OFF;
    set_blank_before_cmd         = OFF;
    set_pause_after_cmd          = OFF;
    set_quit_after_cmd           = OFF;
    set_read_after_cmd           = OFF;
    set_color_status_fg          = DEF_COLOR_STATUS_FG;
    set_color_status_bg          = DEF_COLOR_STATUS_BG;
    set_color_pointer_fg         = DEF_COLOR_POINTER_FG;
    set_color_pointer_bg         = DEF_COLOR_POINTER_BG;
    set_color_backgnd_fg         = DEF_COLOR_BACKGND_FG;
    set_color_backgnd_bg         = DEF_COLOR_BACKGND_BG;
    set_color_msg_fg             = DEF_COLOR_MSG_FG;
    set_color_msg_bg             = DEF_COLOR_MSG_BG;
    set_color_backgnd_tag_fg     = DEF_COLOR_BACKGND_TAG_FG;
    set_color_backgnd_tag_bg     = DEF_COLOR_BACKGND_TAG_BG;
    set_color_pointer_tag_fg     = DEF_COLOR_POINTER_TAG_FG;
    set_color_pointer_tag_bg     = DEF_COLOR_POINTER_TAG_BG;
    set_allow_multiple_selection = ON;
    search_term[0] = '\0';
    
    // Find out number of lines on your console, xterm or whatever
    lines_page = term_getlines(STDIN_FILENO) - 2;
    if (lines_page < 3) error("Window too small", error_terminal);

    // Check columns
    cols_page = term_getcols(STDIN_FILENO);
    if (cols_page < 64) error("Window too small", error_terminal);
    
    // Find out home-directory
    strcpy(home_dir, getenv("HOME"));

    // Initialize the file_list-array
    file_list = (file_struct *)malloc(0);

    // Initialize the history-array
    history = (history_struct *)malloc(0);
}

void display_init(void) {
    file_count  = -1;
    pointer     =  0;
    pointer_pos =  0;
    start_dsp   =  0;
    len_page    =  lines_page - 1;
    end_dsp     =  len_page;
}

void display_limit(void) {
    if (file_count < len_page) {
        len_page = file_count;
        end_dsp = file_count;
    }
}

// Color functions ---------------------------------------------------------

short color_get (char instr[]) {
    if (strcmp(instr, "black")   == 0) return COLOR_BLACK;
    if (strcmp(instr, "red")     == 0) return COLOR_RED;
    if (strcmp(instr, "green")   == 0) return COLOR_GREEN;
    if (strcmp(instr, "yellow")  == 0) return COLOR_YELLOW;
    if (strcmp(instr, "blue")    == 0) return COLOR_BLUE;
    if (strcmp(instr, "magenta") == 0) return COLOR_MAGENTA;
    if (strcmp(instr, "cyan")    == 0) return COLOR_CYAN;
    if (strcmp(instr, "white")   == 0) return COLOR_WHITE;
    return(0);
}

void color_init(void) {
    color_dsp = FALSE;
    if (has_colors()) {
        color_dsp = TRUE;
        start_color();
        init_pair(COLOR_STATUS,      set_color_status_fg,  set_color_status_bg);
        init_pair(COLOR_POINTER,     set_color_pointer_fg, set_color_pointer_bg);
        init_pair(COLOR_BACKGND,     set_color_backgnd_fg, set_color_backgnd_bg);
        init_pair(COLOR_MSG,         set_color_msg_fg,     set_color_msg_bg);
        init_pair(COLOR_BACKGND_TAG, set_color_backgnd_tag_fg, set_color_backgnd_tag_bg);
        init_pair(COLOR_POINTER_TAG, set_color_pointer_tag_fg, set_color_pointer_tag_bg);
        bkgdset(COLOR_PAIR(COLOR_BACKGND) | ' ');
        clear();
    }
}

// Helpers for listing_get -------------------------------------------------

void listing_sort(void) {
    int i, j;
    file_struct temp;
    
    for (i = 0; i < file_count; i++) {
        for (j = 0; j < file_count; j++) {
            if (strcmp(file_list[j].name, file_list[j + 1].name) > 0) {
                temp = file_list[j];
                file_list[j] = file_list[j + 1];
                file_list[j + 1] = temp;
            }
        }
    }
}

char *get_uname(int uid) {
    struct passwd *pw_entry;
    static char tmp[len_line];

    pw_entry = getpwuid(uid);
    if (pw_entry != NULL) return pw_entry->pw_name;
    else {
        sprintf(tmp, "%d", uid);
        return tmp;
    }
}

char *get_gname(int gid) {
    struct group *grp_entry;
    static char tmp[len_line];

    grp_entry = getgrgid(gid);
    if (grp_entry != NULL) return grp_entry->gr_name;
    else {
        sprintf(tmp, "%d", gid);
        return tmp;
    }
}

char *timestring(time_t t, char * buf) {
    struct tm * local;
    char tmp[len_line];

    local = localtime(&t);
    strftime(tmp, len_time, "%x ", local);
    strcpy(buf,tmp);
    strftime(tmp, len_time, "%X", local);
    strcat(buf,tmp);
    return buf;
}

// type_file: returns one character representing the type of the file

char type_file(char *filename) {
    struct stat file_stat;
    char type;

    if (stat(filename, &file_stat) == 1) {
        error("Can't stat file", error_intern);
    } else {
        if      (S_ISREG(file_stat.st_mode))  type = 'f'; // regular file
        else if (S_ISDIR(file_stat.st_mode))  type = 'd'; // directory
        else if (S_ISCHR(file_stat.st_mode))  type = 'c'; // char-device
        else if (S_ISBLK(file_stat.st_mode))  type = 'b'; // block-device
        else if (S_ISLNK(file_stat.st_mode))  type = 'l'; // link
        else if (S_ISFIFO(file_stat.st_mode)) type = 'p'; // Named pipe (fifo)
        else if (S_ISSOCK(file_stat.st_mode)) type = 's'; // Socket
        else type = '?';
    }
    return(type);
}

char type_file_l(char *filename) {
    struct stat file_lstat;
    char type;

    if (lstat(filename, &file_lstat) == 1) {
        error("Can't stat file", error_intern);
    } else {
        if      (S_ISREG(file_lstat.st_mode))  type = 'f'; // regular file
        else if (S_ISDIR(file_lstat.st_mode))  type = 'd'; // directory
        else if (S_ISCHR(file_lstat.st_mode))  type = 'c'; // char-device
        else if (S_ISBLK(file_lstat.st_mode))  type = 'b'; // block-device
        else if (S_ISLNK(file_lstat.st_mode))  type = 'l'; // link
        else if (S_ISFIFO(file_lstat.st_mode)) type = 'p'; // Named pipe (fifo)
        else if (S_ISSOCK(file_lstat.st_mode)) type = 's'; // Socket
        else type = '?';
    }
    return(type);
}

// stat_file: Get file-information by stat(), used by get_listing and main

void stat_file(int file_number) {
    struct stat file_lstat;

    if (lstat(file_list[file_number].name, &file_lstat) == 1) {
        error("Can't stat file", error_intern);
    } else {
        strcpy(file_list[file_number].uname, get_uname(file_lstat.st_uid));
        strcpy(file_list[file_number].gname, get_gname(file_lstat.st_gid));
        sprintf(file_list[file_number].size, "%ld", file_lstat.st_size);
        timestring(file_lstat.st_mtime, file_list[file_number].time);
        file_list[file_number].perm[0] = '\0';

        // put together permission-string
        
        if (S_ISREG(file_lstat.st_mode)) strcat(file_list[file_number].perm, "-");
        else if (S_ISDIR(file_lstat.st_mode)) strcat(file_list[file_number].perm, "d");
        else if (S_ISCHR(file_lstat.st_mode)) strcat(file_list[file_number].perm, "c");
        else if (S_ISBLK(file_lstat.st_mode)) strcat(file_list[file_number].perm, "b");
        else if (S_ISLNK(file_lstat.st_mode)) strcat(file_list[file_number].perm, "l");
        else if (S_ISFIFO(file_lstat.st_mode)) strcat(file_list[file_number].perm, "p");
        else if (S_ISSOCK(file_lstat.st_mode)) strcat(file_list[file_number].perm, "s");

        // file-type

        file_list[file_number].type = type_file(file_list[file_number].name);

        // permissions
        
        if (file_lstat.st_mode & S_IRUSR) strcat(file_list[file_number].perm, "r");
        else strcat(file_list[file_number].perm, "-");
        if (file_lstat.st_mode & S_IWUSR) strcat(file_list[file_number].perm, "w");
        else strcat(file_list[file_number].perm, "-");
        if(file_lstat.st_mode & S_ISUID) strcat(file_list[file_number].perm, "s");
        else if (file_lstat.st_mode & S_IXUSR) strcat(file_list[file_number].perm, "x");
        else strcat(file_list[file_number].perm, "-");
        if (file_lstat.st_mode & S_IRGRP) strcat(file_list[file_number].perm, "r");
        else strcat(file_list[file_number].perm, "-");
        if (file_lstat.st_mode & S_IWGRP) strcat(file_list[file_number].perm, "w");
        else strcat(file_list[file_number].perm, "-");
        if (file_lstat.st_mode & S_ISGID) strcat(file_list[file_number].perm, "s");
        else if (file_lstat.st_mode & S_IXGRP) strcat(file_list[file_number].perm, "x");
        else strcat(file_list[file_number].perm, "-");
        if (file_lstat.st_mode & S_IROTH) strcat(file_list[file_number].perm, "r");
        else strcat(file_list[file_number].perm, "-");
        if (file_lstat.st_mode & S_IWOTH) strcat(file_list[file_number].perm, "w");
        else strcat(file_list[file_number].perm, "-");
        if (file_lstat.st_mode & S_IXOTH) strcat(file_list[file_number].perm, "x");
        else strcat(file_list[file_number].perm, "-");
    }
}

// listing_get: read directory ---------------------------------------------

int listing_get(char *path) {
    struct dirent *direntz;
    DIR *dirz;
    int n = -1;

    tags = 0;
    dirz = opendir(path);
    if (dirz != NULL) {
        while((direntz = readdir(dirz)) != NULL) {
            if (strchr(set_type, '*') == NULL) {
                if (strchr(set_type, type_file_l(direntz->d_name)) == NULL) continue;
            }
            n++;
            file_list = (file_struct *)realloc(file_list, ((sizeof(file_struct)) * (n + 1)));
            strcpy(file_list[n].name,direntz->d_name);
            file_list[n].tagged = 0;
            stat_file(n);
        }
        closedir(dirz);
        file_count = n;
        listing_sort();
        display_limit();
        return(0);
    } else {
        closedir(dirz);
        return(1);
    }
}

// listing_glob: expand wildcards passed to sel with the "-m" argument

int listing_glob(void) {
    glob_t result;
    int flags;
    int i;
    int n = -1;

    tags = 0;
    flags = 0;
    if (glob(set_mask, flags, NULL, &result) != 0)
        error("no such file or directory", error_cmdline);
    for (i = 0; i < result.gl_pathc; i++) {
        if (strchr(set_type, '*') == NULL) {
            if (strchr(set_type, type_file_l(result.gl_pathv[i])) == NULL) continue;
        }
        n++;
        file_list = (file_struct *)realloc(file_list, ((sizeof(file_struct)) * (n + 1)));
        strcpy(file_list[n].name,result.gl_pathv[i]);
        file_list[n].tagged = 0;
        stat_file(n);
    }
    file_count = n;
    listing_sort();
    display_limit();
    return(0);
}

// listing_put: show a file-listing ----------------------------------------

char* limit_str(char instr[len_line], int maxlen) {
    char *buf;
    
    if ((buf = strdup(instr)) == NULL) error("strdup", error_intern);
    if (strlen(buf) > maxlen) {
        buf[maxlen - 1] = '~';
        buf[maxlen] = '\0';
    }
    return buf;
}

void listing_put(void) {
    char status_line[len_line];
    char format_string[len_line];
    char slength[len_line];
    int i;

    // Display headline

    attron(A_BOLD);
    if (color_dsp) attron(COLOR_PAIR(COLOR_STATUS));
    else attron(A_REVERSE);
    strcpy(format_string, "%-");
    sprintf(slength, "%d", cols_page);
    strcat(format_string, slength);
    strcat(format_string, "s");
    mvprintw(0,0,format_string, set_banner);
    attroff(A_BOLD);
    if (color_dsp) attroff(COLOR_PAIR(COLOR_STATUS));
    else attroff(A_REVERSE);

    // Display the file-list (or not...)
    
    if (file_count < 0) {
        move(2,1);
        attron(A_BOLD);
        if (color_dsp) attron(COLOR_PAIR(COLOR_MSG));
        else attron(A_REVERSE);
        printw("No files / no permission. Go back with <d> or <r>");
        attroff(A_BOLD);
        if (color_dsp) attroff(COLOR_PAIR(COLOR_MSG));
        else attroff(A_REVERSE);
    } else {
        move(1,0);
        for(i = start_dsp; i <= end_dsp; i++) {
            if (i == pointer) {
                if (color_dsp) attrset(A_BOLD|COLOR_PAIR(COLOR_POINTER));
                else attron(A_REVERSE);
            } else if (file_list[i].type == 'd') attrset(A_BOLD);
            if (file_list[i].tagged) {
                if (color_dsp) {
                    if (i == pointer) attrset(A_BOLD|COLOR_PAIR(COLOR_POINTER_TAG));
                    else attrset(A_BOLD|COLOR_PAIR(COLOR_BACKGND_TAG));
                } else attron(A_REVERSE);
            }
            if (set_long_listing) {
                strcpy(format_string, "%-");
                sprintf(slength, "%d", (cols_page - 62));
                strcat(format_string, slength);
                strcat(format_string, "s %-10s %-10s %-10s %-10s %-10s");
                printw(format_string,
                       limit_str(file_list[i].name, (cols_page - 62)),
                       file_list[i].perm,
                       limit_str(file_list[i].uname, 10),
                       limit_str(file_list[i].gname, 10),
                       limit_str(file_list[i].size, 10),
                       file_list[i].time);
            } else {
                strcpy(format_string, "%-");
                sprintf(slength, "%d", cols_page);
                strcat(format_string, slength);
                strcat(format_string, "s");
                printw(format_string, limit_str(file_list[i].name, (cols_page - 1)));
            }
            attrset(A_NORMAL);
        }
        if (debug) {
            mvprintw(0,40,"|P:%d PP:%d S:%d E:%d FC:%d LP:%d|",
                     pointer,
                     pointer_pos,
                     start_dsp,
                     end_dsp,
                     file_count,
                     len_page);
        }
    }
    
    // Build and display the status-line

    status_line[0] = '\0';
    strcat(status_line, working_dir);
    if (status_line[strlen(status_line) - 1] != '/') strcat(status_line, "/");
    strcat(status_line, file_list[pointer].name);
    strcpy(status_line, limit_str(status_line, (cols_page - 8)));
    strcpy(format_string, "%-");
    sprintf(slength, "%d", (cols_page - 8));
    strcat(format_string, slength);
    strcat(format_string, "s");
    sprintf(status_line, format_string, status_line);
    strcat(status_line, " h=help");
    status_put(status_line);
    refresh();
}

// History-related functions -----------------------------------------------

int history_exist(char input[]) {
    int i;

    for (i = 0; i <= history_count; i++) {
        if (strcmp(history[i].path, input) == 0) return 1;
    }
    return(0);
}

void history_add(void) {
    history_count++;
    history = (history_struct *)realloc(history, ((sizeof(history_struct)) * (history_count + 1)));
    strcpy(history[history_count].path, working_dir);
    history[history_count].pointer = pointer;
    history[history_count].pointer_pos = pointer_pos;
    history[history_count].start_dsp = start_dsp;
    history[history_count].end_dsp = end_dsp;
    return;
}

void history_update(void) {
    int i;

    for (i = 0; i <= history_count; i++) {
        if (strcmp(history[i].path, working_dir) == 0) {
            history[i].pointer = pointer;
            history[i].pointer_pos = pointer_pos;
            history[i].start_dsp = start_dsp;
            history[i].end_dsp = end_dsp;
        }
    }
    return;
}

void history_restore(void) {
    char cwd[len_path];
    int i;
    
    getcwd(cwd, len_path);
    for (i = 0; i <= history_count; i++) {
        if (strcmp(history[i].path, cwd) == 0) {
            pointer = history[i].pointer;
            pointer_pos = history[i].pointer_pos;
            start_dsp = history[i].start_dsp;
            end_dsp = history[i].end_dsp;
        }
    }
    return;
}

// Functions concerning command-execution ----------------------------------

int ask_before_cmd(char cmd[]) {
    char input;

    clear();
    move(0,0);
    printw("The following command will be executed:\n\n");
    printw(cmd);
    printw(lf);
    printw(lf);
    printw("Continue? (y/n) ");
    input = getch();
    if (input == 'y') return(1);
    else return(0);
}

int ask_before_cmd_group(void) {
    char input;
    int i;
    
    clear();
    move(0,0);
    printw("Selected files:\n\n");
    for (i = 0; i <= file_count; i++) {
        if (file_list[i].tagged) printw("%s\n", file_list[i].name);
    }
    printw(lf);
    printw("The following command will be executed:\n\n");
    printw(set_cmd);
    printw(lf);
    printw(lf);
    printw("Continue? (y/n) ");
    input = getch();
    if (input == 'y') return(1);
    else return(0);
}

void pause_after_cmd(void) {
    char input;
    
    printf("Hit <RETURN> to continue");
    FLUSH;
    input = getch();
    clear();
    return;
}

void execute(int file_nr) {
    int i;
    char cmd[len_cmd];
    char filename[len_path];
    char token[2];

    if (set_use_full_path) {
        strcpy(filename, working_dir);
        strcat(filename, "/");
        strcat(filename, file_list[file_nr].name);
    } else strcpy(filename, file_list[file_nr].name);
    if (strchr(set_cmd, set_file_sub) == NULL) {
        strcpy(cmd, "");
        if (strlen(set_cmd) > 0) {
            strcat(cmd, set_cmd);
            strcat(cmd, " ");
        }
        strcat(cmd, filename);
    } else {
        cmd[0] = '\0';
        i = 0;
        while(set_cmd[i] != '\0') {
            if(set_cmd[i] == set_file_sub) {
                strcat(cmd, filename);
            } else {
                token[0] = set_cmd[i];
                token[1] = '\0';
                strcat(cmd, token);
            }
            i++;
        }
    }
    if (set_ask_before_cmd) {
        if ((ask_before_cmd(cmd)) == 0) {
            clear();
            return;
        }
    }
    status_put("Running...");
    refresh();
    endwin();
    if (set_blank_before_cmd) system("clear");
    system(cmd);
    if (file_list[file_nr].tagged) {
        file_list[file_nr].tagged = 0;
        tags--;
    }
    if (set_pause_after_cmd) pause_after_cmd();
    if (set_quit_after_cmd) exit(0);

    // Re-read directory

    if (set_read_after_cmd) {
        display_init();
        if (set_use_mask) listing_glob();
        else listing_get(".");
        clear();
        listing_put();
    }
}

void execute_group(void) {
    int i;
    int n;
    char cmd[len_cmd];
    char filename[len_path];
    char token[2];

    if (set_ask_before_cmd) {
        if (ask_before_cmd_group() == 0) {
            clear();
            return;
        }
    }
    status_put("Running...");
    refresh();
    endwin();
    if (set_blank_before_cmd) system("clear");
    for (n = 0; n <= file_count; n++) {
        if (file_list[n].tagged) {
            file_list[n].tagged = 0;
            tags--;
            if (set_use_full_path) {
                strcpy(filename, working_dir);
                strcat(filename, "/");
                strcat(filename, file_list[n].name);
            } else strcpy(filename, file_list[n].name);
            if (strchr(set_cmd, set_file_sub) == NULL) {
                strcpy(cmd, "");
                if (strlen(set_cmd) > 0) {
                    strcat(cmd, set_cmd);
                    strcat(cmd, " ");
                }
                strcat(cmd, filename);
            } else {
                cmd[0] = '\0';
                i = 0;
                while(set_cmd[i] != '\0') {
                    if(set_cmd[i] == set_file_sub) {
                        strcat(cmd, filename);
                    } else {
                        token[0] = set_cmd[i];
                        token[1] = '\0';
                        strcat(cmd, token);
                    }
                    i++;
                }
            }
            system(cmd);
        }
    }
    if (set_pause_after_cmd) pause_after_cmd();
    if (set_quit_after_cmd) exit(0);

    // Re-read directory

    if (set_read_after_cmd) {
        display_init();
        if (set_use_mask) listing_glob();
        else listing_get(".");
        clear();
        listing_put();
    }
}

void changedir(char dir[]) {
    char *new_dir;

    if ((new_dir = strdup(dir)) == NULL) error("strdup", error_intern);
    if (access(new_dir, X_OK) == 0) {
        strcpy(last_dir, working_dir);
        chdir(new_dir);
        getcwd(working_dir, len_path);
        display_init();
        history_restore();
        if (set_use_mask) listing_glob();
        else listing_get(".");
        clear();
    }
}

void dir_enter(char dir[]) {
    char *new_dir;

    if ((new_dir = strdup(dir)) == NULL) error("strdup", error_intern);
    if (history_exist(working_dir) == 0) history_add();
    else history_update();
    if (access(new_dir, X_OK) == 0) {
        strcpy(last_dir, working_dir);
        chdir(new_dir);
        getcwd(working_dir, len_path);
        display_init();
        if (set_use_mask) listing_glob();
        else listing_get(".");
        clear();
    }
}

void dir_leave(void) {
    strcpy(last_dir, working_dir);
    chdir("..");
    getcwd(working_dir, len_path);
    display_init();
    history_restore();
    if (set_use_mask) listing_glob();
    else listing_get(".");
    clear();
    return;
}

// scrolling ---------------------------------------------------------------

void scrollup(void) {
    start_dsp--;
    end_dsp--;
}

void scrolldown(void) {
    start_dsp++;
    end_dsp++;
}

// code for online-commands not included in main() --------------------------

int find_next(void) {
    int i, result;

    result = -1;
    echo();
    nocbreak();
    attron(A_BOLD);
    if (color_dsp) attron(COLOR_PAIR(COLOR_STATUS));
    else attron(A_REVERSE);
    mvprintw(lines_page + 1,0,"%s", "Searchterm:");
    clrtoeol();
    attroff(A_BOLD);
    if (color_dsp) attroff(COLOR_PAIR(COLOR_STATUS));
    else attroff(A_REVERSE);
    printw(" ");
    scanw("%80s", search_term);
    noecho();
    cbreak();
    if (search_term[0] != '\0') {
        search_success = 1;
        for (i = search_start; i <= file_count; i++) {
            if (strstr(file_list[i].name, search_term) != NULL) {
                result = i;
                break;
            }
        }
    }
    return(result);
}

void get_command(void) {
    echo();
    nocbreak();
    attron(A_BOLD);
    if (color_dsp) attron(COLOR_PAIR(COLOR_STATUS));
    else attron(A_REVERSE);
    mvprintw(lines_page + 1,0,"%s", "Command:");
    clrtoeol();
    attroff(A_BOLD);
    if (color_dsp) attroff(COLOR_PAIR(COLOR_STATUS));
    else attroff(A_REVERSE);
    printw(" ");
    getstr(set_cmd);
    noecho();
    cbreak();
    return;
}

void get_file_substitute(void) {
    echo();
    nocbreak();
    attron(A_BOLD);
    if (color_dsp) attron(COLOR_PAIR(COLOR_STATUS));
    else attron(A_REVERSE);
    mvprintw(lines_page + 1,0,"%s", "Substitute for filename:");
    clrtoeol();
    attroff(A_BOLD);
    if (color_dsp) attroff(COLOR_PAIR(COLOR_STATUS));
    else attroff(A_REVERSE);
    printw(" ");
    set_file_sub = getch();
    getch();
    noecho();
    cbreak();
    return;
}

// main --------------------------------------------------------------------

int main(int argc, char *argv[]) {
    int input;
    int go = 1;
    int i  = 0;
    int n  = -1;
    int entries = 0;
    int file_input = 0; // 0: Use internal listing_get 1: Files given on the command-line
    int search_result;

    init();

    // Read commandline-arguments
    
    for (i = 1; i <= argc; i++) {
        if (argv[i] != NULL) {
            if (argv[i][0] != '-') { // Filenames, given on the cmd-line
                file_input = 1;
                if (access(argv[i], F_OK) == 0) {
                    if (strchr(set_type, '*') == NULL) {
                        if (strchr(set_type, type_file_l(argv[i])) == NULL) continue;
                    }
                    n++;
                    file_list = (file_struct *)realloc(file_list, ((sizeof(file_struct)) * (n + 1)));
                    strcpy(file_list[n].name,argv[i]);
                    stat_file(n);
                }
            } else if (argv[i][1] != '-') {

                // Options without arguments
                
                if (strchr(argv[i], 'a') != NULL) set_ask_before_cmd = ON;
                if (strchr(argv[i], 'f') != NULL) set_use_full_path = ON;
                if (strchr(argv[i], 'h') != NULL) help();
                if (strchr(argv[i], 'l') != NULL) set_long_listing = ON;
                if (strchr(argv[i], 's') != NULL) set_blank_before_cmd = ON;
                if (strchr(argv[i], 'w') != NULL) set_pause_after_cmd = ON;
                if (strchr(argv[i], 'q') != NULL) set_quit_after_cmd = ON;
                if (strchr(argv[i], 'u') != NULL) set_read_after_cmd = ON;

                // Options needing an argument
                
                if (strchr(argv[i], 'b') != NULL) {
                    if (argv[i][strlen(argv[i]) - 1] == 'b') {
                        if ((argv[i+1] != NULL) && (argv[i+1][0] != '-')) {
                            strcpy(set_banner,limit_str(argv[i+1], len_line - 1));
                            i++;
                        } else error("-b without value. Use -b \"My files\"", error_cmdline);
                    } else error("the b option can only be used at the end of argument-list", error_cmdline);
                    continue;
                }
                if (strchr(argv[i], 'c') != NULL) {
                    if (argv[i][strlen(argv[i]) - 1] == 'c') {
                        if ((argv[i+1] != NULL) && (argv[i+1][0] != '-')) {
                            strcpy(set_cmd,argv[i+1]);
                            i++;
                        } else error("-c without value. Use -c \"command(s)\"", error_cmdline);
                    } else error("the c option can only be used at the end of argument-list", error_cmdline);
                    continue;
                }
                if (strchr(argv[i], 'C') != NULL) {
                    if (argv[i][strlen(argv[i]) - 1] == 'C') {
                        if ((argv[i+1] != NULL) && (argv[i+1][0] != '-')) {
                            set_file_sub = *argv[i+1];
                            i++;
                        } else error("-C without value. Use -C \"character\"", error_cmdline);
                    } else error("the C option can only be used at the end of argument-list", error_cmdline);
                    continue;
                }
                if (strchr(argv[i], 'd') != NULL) {
                    if (argv[i][strlen(argv[i]) - 1] == 'd') {
                        if ((argv[i+1] != NULL) && (argv[i+1][0] != '-')) {
                            strcpy(set_path,argv[i+1]);
                            i++;
                        } else error("-d without value. Use -d /directory", error_cmdline);
                    } else error("the d option can only be used at the end of argument-list", error_cmdline);
                    continue;
                }
                if (strchr(argv[i], 'm') != NULL) {
                    if (argv[i][strlen(argv[i]) - 1] == 'm') {
                        if ((argv[i+1] != NULL) && (argv[i+1][0] != '-')) {
                            set_use_mask = ON;
                            strcpy(set_mask,argv[i+1]);
                            i++;
                        } else error("-m without value. Use -m \"something.*\"", error_cmdline);
                    } else error("the m option can only be used at the end of argument-list", error_cmdline);
                    continue;
                }
                if (strchr(argv[i], 't') != NULL) {
                    if (argv[i][strlen(argv[i]) - 1] == 't') {
                        if ((argv[i+1] != NULL) && (argv[i+1][0] != '-')) {
                            strcpy(set_type, argv[i+1]);
                            i++;
                        } else error("-t without value. Use -t [fldcbps]", error_cmdline);
                    } else error("the t option can only be used at the end of argument-list", error_cmdline);
                    continue;
                }
            }

            // Long options

            else if (argv[i][1] == '-') {
                if (strcmp(argv[i],"--no-tagging") == 0) set_allow_multiple_selection = OFF;
                if (strcmp(argv[i],"--no-command-entry") == 0) set_allow_change_command = OFF;
                if (strcmp(argv[i],"--color-status-fg") == 0) {
                    if ((argv[i+1] != NULL) && (argv[i+1][0] != '-')) {
                        set_color_status_fg = color_get(argv[i+1]);
                        i++;
                    } else error("--color-status-fg without value", error_cmdline);
                    continue;
                }
                if (strcmp(argv[i],"--color-status-bg") == 0) {
                    if ((argv[i+1] != NULL) && (argv[i+1][0] != '-')) {
                        set_color_status_bg = color_get(argv[i+1]);
                        i++;
                    } else error("--color-status-bg without value", error_cmdline);
                    continue;
                }
                if (strcmp(argv[i],"--color-pointer-fg") == 0) {
                    if ((argv[i+1] != NULL) && (argv[i+1][0] != '-')) {
                        set_color_pointer_fg = color_get(argv[i+1]);
                        i++;
                    } else error("--color-pointer-fg without value", error_cmdline);
                    continue;
                }
                if (strcmp(argv[i],"--color-pointer-bg") == 0) {
                    if ((argv[i+1] != NULL) && (argv[i+1][0] != '-')) {
                        set_color_pointer_bg = color_get(argv[i+1]);
                        i++;
                    } else error("--color-pointer-bg without value", error_cmdline);
                    continue;
                }
                if (strcmp(argv[i],"--color-backgnd-fg") == 0) {
                    if ((argv[i+1] != NULL) && (argv[i+1][0] != '-')) {
                        set_color_backgnd_fg = color_get(argv[i+1]);
                        i++;
                    } else error("--color-backgnd-fg without value", error_cmdline);
                    continue;
                }
                if (strcmp(argv[i],"--color-backgnd-bg") == 0) {
                    if ((argv[i+1] != NULL) && (argv[i+1][0] != '-')) {
                        set_color_backgnd_bg = color_get(argv[i+1]);
                        i++;
                    } else error("--color-backgnd-bg without value", error_cmdline);
                    continue;
                }
                if (strcmp(argv[i],"--color-msg-fg") == 0) {
                    if ((argv[i+1] != NULL) && (argv[i+1][0] != '-')) {
                        set_color_msg_fg = color_get(argv[i+1]);
                        i++;
                    } else error("--color-msg-fg without value", error_cmdline);
                    continue;
                }
                if (strcmp(argv[i],"--color-msg-bg") == 0) {
                    if ((argv[i+1] != NULL) && (argv[i+1][0] != '-')) {
                        set_color_msg_bg = color_get(argv[i+1]);
                        i++;
                    } else error("--color-msg-bg without value", error_cmdline);
                    continue;
                }
                if (strcmp(argv[i],"--color-backgnd-tag-fg") == 0) {
                    if ((argv[i+1] != NULL) && (argv[i+1][0] != '-')) {
                        set_color_backgnd_tag_fg = color_get(argv[i+1]);
                        i++;
                    } else error("--color-backgnd-tag-fg without value", error_cmdline);
                    continue;
                }
                if (strcmp(argv[i],"--color-backgnd-tag-bg") == 0) {
                    if ((argv[i+1] != NULL) && (argv[i+1][0] != '-')) {
                        set_color_backgnd_tag_bg = color_get(argv[i+1]);
                        i++;
                    } else error("--color-backgnd-tag-bg without value", error_cmdline);
                    continue;
                }
                if (strcmp(argv[i],"--color-pointer-tag-fg") == 0) {
                    if ((argv[i+1] != NULL) && (argv[i+1][0] != '-')) {
                        set_color_pointer_tag_fg = color_get(argv[i+1]);
                        i++;
                    } else error("--color-pointer-tag-fg without value", error_cmdline);
                    continue;
                }
                if (strcmp(argv[i],"--color-pointer-tag-bg") == 0) {
                    if ((argv[i+1] != NULL) && (argv[i+1][0] != '-')) {
                        set_color_pointer_tag_bg = color_get(argv[i+1]);
                        i++;
                    } else error("--color-pointer-tag-bg without value", error_cmdline);
                    continue;
                }
            }
        }
    }

    display_init();
    chdir(set_path);
    getcwd(working_dir, len_path);
    strcpy(start_dir, working_dir);
    strcpy(last_dir, working_dir);

    if (file_input == ON) {
        file_count = n;
        listing_sort();
        display_limit();
    } else {
        if (set_use_mask) {
            listing_glob();
        } else listing_get(".");
    }
    
    initscr();
    keypad(stdscr, 1);
    cbreak();
    noecho();
    color_init();
    listing_put();

    // Input-Loop
    
    while (go) {
        input = getch();
        switch (input) {
        case 'p':
        case KEY_UP: {
            if (pointer > 0) {
                pointer--;
                if (pointer_pos > 0) pointer_pos--;
                else if (pointer_pos == 0)
                    if (start_dsp > 0) scrollup();
            }
            listing_put();
            break;
        }
        case 'n':
        case KEY_DOWN: {
            pointer++;
            if (pointer > file_count) pointer--;
            if (file_count < len_page) {
                if (pointer > file_count) pointer--;
            } else if (file_count >= len_page) {
                if (pointer_pos < len_page) pointer_pos++;
                else if (pointer_pos == len_page) {
                    if (end_dsp < file_count) scrolldown();
                }
            }
            listing_put();
            break;
        }
        case 'P':
        case KEY_PPAGE: {
            if (file_count > len_page) {
                if (start_dsp >= len_page) {
                    start_dsp -= len_page;
                    end_dsp -= len_page;
                    pointer -= len_page;
                } else {
                    entries = start_dsp;
                    start_dsp -= entries;
                    end_dsp -= entries;
                    pointer -= entries;
                }
            } else {
                pointer = 0;
                pointer_pos = 1;
            }
            listing_put();
            break;
        }
        case 'N':
        case KEY_NPAGE: {
            if (file_count > len_page) {
                if ((file_count - end_dsp) >= len_page) {
                    start_dsp += len_page;
                    end_dsp += len_page;
                    pointer += len_page;
                } else {
                    entries = file_count - end_dsp;
                    start_dsp += entries;
                    end_dsp += entries;
                    pointer += entries;
                }
            } else {
                pointer = file_count;
                start_dsp = 0;
                end_dsp = len_page;
            }
            listing_put();
            break;
        }
        case 't': {
            if (file_count <= len_page) {
                pointer = 0;
                pointer_pos = 0;
            } else {
                pointer = 0;
                pointer_pos = 0;
                start_dsp = 0;
                end_dsp = len_page;
            }
            listing_put();
            break;
        }
        case 'b': {
            if (file_count <= len_page) {
                pointer = file_count;
                pointer_pos = file_count;
                start_dsp = 0;
                end_dsp = len_page;
            } else {
                pointer = file_count;
                pointer_pos = len_page;
                start_dsp = file_count - len_page;
                end_dsp = file_count;
            }
            listing_put();
            break;
        }
        case ' ':
        case '\n': {
            if (file_list[pointer].type == 'd') {
                if (input == '\n') {
                    if (strcmp(file_list[pointer].name, "..") == 0) dir_leave();
                    else dir_enter(file_list[pointer].name);
                }
                else if (input == ' ') execute(pointer);
            }
            else execute(pointer);
            listing_put();
            break;
        }
        case 'x': {
            if (set_allow_multiple_selection && (tags > 0)) {
                for (i = 0; i <= file_count; i++) {
                    if (file_list[i].tagged) {
                        execute(i);
                    }
                }
                listing_put();
            }
            break;
        }
        case 'X': {
            if ((set_allow_multiple_selection) && (tags > 0)) {
                execute_group();
                listing_put();
            }
            break;
        }
        case 'c': {
            if (set_allow_change_command) get_command();
            listing_put();
            break;
        }
        case 'C': {
            if (set_allow_change_command) get_file_substitute();
            listing_put();
            break;
        }
        case KEY_RIGHT: {
            if (file_list[pointer].type == 'd') {
                if (strcmp(file_list[pointer].name, "..") == 0) dir_leave();
                else dir_enter(file_list[pointer].name);
                listing_put();
            }
            break;
        }
        case KEY_LEFT: {
            if (strcmp(working_dir, "/") != 0) {
                dir_leave();
                listing_put();
            }
            break;
        }
        case '/': {
            if (search_success) search_start = pointer + 1;
            else search_start = 0;
            search_result = find_next();
            if (search_result >= 0) {
                if (file_count <= len_page) {
                    pointer = search_result;
                    pointer_pos = search_result;
                } else {
                    if ((search_result >= start_dsp) && (search_result <= end_dsp)) {
                        pointer = search_result;
                        pointer_pos = pointer - start_dsp;
                    } else {
                        if ((file_count-search_result) >= len_page) {
                            pointer = search_result;
                            pointer_pos = 0;
                            start_dsp = search_result;
                            end_dsp = start_dsp + len_page;
                        } else {
                            pointer = search_result;
                            end_dsp = file_count;
                            start_dsp = end_dsp - len_page;
                            pointer_pos = pointer - start_dsp;
                        }
                    }
                }
                listing_put();
            } else {
                status_put("term not found, next search starts from the top");
                search_success = 0;
            }
            break;
        }
        case 'd': {
            changedir(start_dir);
            listing_put();
            break;
        }
        case 'r': {
            changedir(last_dir);
            listing_put();
            break;
        }
        case 'R': {
            changedir("/");
            listing_put();
            break;
        }
        case 'H':
        case '~': {
            changedir(home_dir);
            listing_put();
            break;
        }
        case 'U': {
            display_init();
            if (set_use_mask) listing_glob();
            else listing_get(".");
            clear();
            listing_put();
            break;
        }
        case 'h': {
            help_online();
            listing_put();
            break;
        }
        case '?': {
            display_settings();
            listing_put();
            break;
        }
        case 'a': {
            if (set_ask_before_cmd) {
                set_ask_before_cmd = OFF;
                status_put("ask-before-cmd now off");
            } else {
                set_ask_before_cmd = ON;
                status_put("ask-before-cmd now on");
            }
            break;
        }
        case 'f': {
            if (set_use_full_path) {
                set_use_full_path = OFF;
                status_put("use-full-path now off");
            } else {
                set_use_full_path = ON;
                status_put("use-full-path now on");
            }
            break;
        }
        case 'w': {
            if (set_pause_after_cmd) {
                set_pause_after_cmd = OFF;
                status_put("wait-after-exec now off");
            } else {
                set_pause_after_cmd = ON;
                status_put("wait-after-exec now on");
            }
            break;
        }
        case 'l': {
            if (set_long_listing) {
                set_long_listing = OFF;
                listing_put();
                status_put("long-listing now off");
            } else {
                set_long_listing = ON;
                listing_put();
                status_put("long-listing now on");
            }
            break;
        }
        case 'Q': {
            if (set_quit_after_cmd) {
                set_quit_after_cmd = OFF;
                status_put("quit-after-exec now off");
            } else {
                set_quit_after_cmd = ON;
                status_put("quit-after-exec now on");
            }
            break;
        }
        case 's': {
            if (set_blank_before_cmd) {
                set_blank_before_cmd = OFF;
                status_put("blank-before-exec now off");
            } else {
                set_blank_before_cmd = ON;
                status_put("blank-before-exec now on");
            }
            break;
        }
        case 'u': {
            if(set_read_after_cmd) {
                set_read_after_cmd = OFF;
                status_put("update-after-cmd now off");
            } else {
                set_read_after_cmd = ON;
                status_put("update-after-cmd now on");
            }
            break;
        }
        case 'q': {
            sel_exit(error_noaction);
            break;
        }
        case '+': {
            if (set_allow_multiple_selection) {
                if (file_list[pointer].tagged == 0) tags++;
                file_list[pointer].tagged = 1;
                pointer++;
                if (pointer > file_count) pointer--;
                if (file_count < len_page) {
                    if (pointer > file_count) pointer--;
                } else if (file_count >= len_page) {
                    if (pointer_pos < len_page) pointer_pos++;
                    else if (pointer_pos == len_page) {
                        if (end_dsp < file_count) scrolldown();
                    }
                }
                listing_put();
            }
            break;
        }
        case '-': {
            if (set_allow_multiple_selection) {
                if (file_list[pointer].tagged == 1) tags--;
                file_list[pointer].tagged = 0;
                pointer++;
                if (pointer > file_count) pointer--;
                if (file_count < len_page) {
                    if (pointer > file_count) pointer--;
                } else if (file_count >= len_page) {
                    if (pointer_pos < len_page) pointer_pos++;
                    else if (pointer_pos == len_page) {
                        if (end_dsp < file_count) scrolldown();
                    }
                }
                listing_put();
            }
            break;
        }
        case '*': {
            if (set_allow_multiple_selection) {
                for (i = 0; i <= file_count; i++) {
                    if (strcmp(file_list[i].name, ".") != 0 && strcmp(file_list[i].name, "..") != 0) file_list[i].tagged = ON;
                }
                listing_put();
            }
            break;
        }
        case '#': {
            for (i = 0; i <= file_count; i++) {
                file_list[i].tagged = 0;
            }
            listing_put();
            break;
        }
        case '!': {
            for (i = 0; i <= file_count; i++) {
                if (file_list[i].tagged) file_list[i].tagged = OFF;
                else {
                    if (strcmp(file_list[i].name, ".") != 0 && strcmp(file_list[i].name, "..") != 0) file_list[i].tagged = ON;
                }
            }
            listing_put();
            break;
        }
        default: break;
        }
    }
    exit(error_ok);
}

// the end
