/*
    UIT main file
    Copyright (c) 1993, 1994 Tudor Hulubei & Andrei Pitis

This file is part of UIT (UNIX Interactive Tools)

UIT 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, or (at your option) any later version.

UIT 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
UIT; see the file COPYING.  If not, write to the Free Software Foundation,
675 Mass Ave, Cambridge, MA 02139, USA.  */


#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <ctype.h>
#include <signal.h>
#include <time.h>
#include <pwd.h>
#include <grp.h>
#include <limits.h>
#include <termios.h>
#include <sys/wait.h>

#include "xmalloc.h"
#include "tty.h"
#include "window.h"
#include "edit.h"
#include "status.h"
#include "panel.h"
#include "config.h"
#include "signals.h"
#include "tilde.h"
#include "misc.h"


extern int FrameDisplay;
extern int suspended;
extern int suspend_allowed;
extern int signals_status;
extern int UserHeartAttack;


#define MAX_STATIC_SIZE 60


#ifdef HAVE_LINUX
extern int LinuxConsole;
#endif /* HAVE_LINUX */


#ifdef HAVE_LINUX
int AnsiColorSequences = ON;
#else	/* not HAVE_LINUX */
int AnsiColorSequences = OFF;
#endif	/* not HAVE_LINUX */


char *home;
char *program;
char *tty_name;
int  tty_name_len;
char *login_name;
int  login_name_len;
edit *edt = NULL;
int  SCREEN_X, SCREEN_Y;
char *stdout_log_name = NULL;
char *stderr_log_name = NULL;
char cSection[]  = "[UIT-Color]";
char bwSection[] = "[UIT-Monochrome]";
char title[] = " UNIX Interactive Tools 4.3.1";
char login[] = "User:";
char tty[] = "tty:";
char exit_msg[] = "*** (EXIT) Exit UNIX Interactive Tools ? \
(ENTER to confirm, TAB to cancel) ***";
char PS1[4] = " $ ";
char *screen = NULL;

static char *TempDirectory       = "";
static char *NormalModeHelp      = "";
static char *CommandLineModeHelp = "";


#define DO_FORK 0
#define DO_EXEC	1


#define BUILTIN_OPERATIONS		  48

#define BUILTIN_Copy			 -1
#define BUILTIN_RenameMove		 -2
#define BUILTIN_MkDir			 -3
#define BUILTIN_Delete			 -4
#define BUILTIN_Exit			 -5
#define BUILTIN_HistoryBack		 -6
#define BUILTIN_ShowTty			 -7
#define BUILTIN_Refresh			 -8
#define BUILTIN_GotoRoot		 -9
#define BUILTIN_SwitchPanels		 -10
#define BUILTIN_HistoryNext		 -11
#define BUILTIN_FileDisplayNextMode	 -12
#define BUILTIN_FileDisplayOwnerGroup	 -13
#define BUILTIN_FileDisplayDateTime	 -14
#define BUILTIN_FileDisplaySize		 -15
#define BUILTIN_FileDisplayMode		 -16
#define BUILTIN_FileDisplayFullName	 -17
#define BUILTIN_FileSortNextMethod	 -18
#define BUILTIN_FileSortByName		 -19
#define BUILTIN_FileSortByExtension	 -20
#define BUILTIN_FileSortBySize		 -21
#define BUILTIN_FileSortByDate		 -22
#define BUILTIN_FileSortByMode		 -23
#define BUILTIN_FileSortByOwnerId	 -24
#define BUILTIN_FileSortByGroupId	 -25
#define BUILTIN_FileSortByOwnerName	 -26
#define BUILTIN_FileSortByGroupName	 -27
#define BUILTIN_DeleteCmdLn		 -28
#define BUILTIN_FileSelect		 -29
#define BUILTIN_CopyFileToCmdLn		 -30
#define BUILTIN_Home			 -31
#define BUILTIN_End			 -32
#define BUILTIN_PageUp			 -33
#define BUILTIN_PageDown		 -34
#define BUILTIN_CursorLeft		 -35
#define BUILTIN_CursorRight		 -36
#define BUILTIN_CursorUp		 -37
#define BUILTIN_CursorDown		 -38
#define BUILTIN_ChangePanel		 -39
#define BUILTIN_ChangeDir		 -40
#define BUILTIN_HardRefresh		 -41
#define BUILTIN_FilePatternSelect	 -42
#define BUILTIN_FilePatternUnselect	 -43
#define BUILTIN_DeleteCmdLnWord		 -44
#define BUILTIN_ConformCurrentDir	 -45
#define BUILTIN_ConformOtherDir		 -46
#define BUILTIN_CopyPathToCmdLn		 -47
#define BUILTIN_CopySelectedFilesToCmdLn -48


#define MAX_BUILTIN_NAME	30


char built_in[BUILTIN_OPERATIONS][MAX_BUILTIN_NAME] =
{
    "<Copy>",
    "<RenameMove>",
    "<MkDir>",
    "<Delete>",
    "<Exit>",
    "<HistoryBack>",
    "<ShowTty>",
    "<Refresh>",
    "<GotoRoot>",
    "<SwitchPanels>",
    "<HistoryNext>",
    "<FileDisplayNextMode>",
    "<FileDisplayOwnerGroup>",
    "<FileDisplayDateTime>",
    "<FileDisplaySize>",
    "<FileDisplayMode>",
    "<FileDisplayFullName>",
    "<FileSortNextMethod>",
    "<FileSortByName>",
    "<FileSortByExtension>",
    "<FileSortBySize>",
    "<FileSortByDate>",
    "<FileSortByMode>",
    "<FileSortByOwnerId>",
    "<FileSortByGroupId>",
    "<FileSortByOwnerName>",	
    "<FileSortByGroupName>",
    "<DeleteCmdLn>",
    "<FileSelect>",
    "<CopyFileToCmdLn>",
    "<Home>",
    "<End>",
    "<PageUp>",
    "<PageDown>",
    "<CursorLeft>",
    "<CursorRight>",
    "<CursorUp>",
    "<CursorDown>",
    "<ChangePanel>",
    "<ChangeDir>",
    "<HardRefresh>",
    "<FilePatternSelect>",
    "<FilePatternUnselect>",
    "<DeleteCmdLnWord>",
    "<ConformCurrentDir>",
    "<ConformOtherDir>",
    "<CopyPathToCmdLn>",
    "<CopySelectedFilesToCmdLn>",
};


typedef struct
{
    char *OpName;
    char *OpCommand;
    char *OpNewDir;
    char  OpSaveScreen;
    char  OpPause;
    char  OpHide;
    char  OpBuiltin;
} ConfigKey;


#define MAX_KEYS        2048      /* enough ?   :-) */
#define KEYSDATA_FIELDS	   8


#define TITLE_FIELDS	8
 
static char *TitleFields[TITLE_FIELDS] =
{
    "TitleForeground",
    "TitleBackground",
    "TitleBrightness",
    "UserName",
    "TtyName",
    "ClockForeground",
    "ClockBackground",
    "ClockBrightness"
};

#ifdef HAVE_LINUX
static int TitleColors[TITLE_FIELDS] = 
{
    CYAN, BLUE, ON, YELLOW, YELLOW, BLACK, CYAN, OFF
};
#else	/* not HAVE_LINUX */
static int TitleColors[TITLE_FIELDS] = 
{
    WHITE, BLACK, ON, WHITE, WHITE, BLACK, WHITE, OFF
};
#endif	/* not HAVE_LINUX */

#define TitleForeground	TitleColors[0]
#define TitleBackground	TitleColors[1]
#define TitleBrightness	TitleColors[2]
#define UserName 	TitleColors[3]
#define TtyName 	TitleColors[4]
#define ClockForeground TitleColors[5]
#define ClockBackground TitleColors[6]
#define ClockBrightness TitleColors[7]


void uitclock(int);


int start(char *cmd, int hide, int start_type)
{
    char *fname;
    int child_exit_code;
    FILE *stdout_log, *stderr_log;

    if (hide)
    {
        uitclock(OFF);
        close(1);
        close(2);
        stdout_log = fopen(stdout_log_name, "w");
	stderr_log = fopen(stderr_log_name, "w");
	
	restore_signals();
	fname = xmalloc(16 + strlen(cmd) + 1);
 	sprintf(fname, "exec %s", cmd);
	signals_dfl();
        child_exit_code = system(fname);
        free(fname);
	signals(signals_status);
	ignore_signals();
	
	fclose(stderr_log);
        fclose(stdout_log);
        open(tty_name, O_RDWR);
        open(tty_name, O_RDWR);
    }
    else
    {
        uitclock(OFF);
        tty_set_mode(TTY_CANONIC);
        tty_putscreen(screen);

	restore_signals();
        fname = xmalloc(16 + strlen(cmd) + 1);
 	sprintf(fname, (start_type == DO_EXEC) ? "exec %s" : "%s", cmd);
	signals_dfl();
        child_exit_code = system(fname);
        free(fname);
        signals(signals_status);
	ignore_signals();

	write(1, "\n\n", 2);
        tty_set_mode(TTY_NONCANONIC);
    }

    return child_exit_code;
}


void removelog(void)
{
    if (stdout_log_name) remove(stdout_log_name);
    if (stderr_log_name) remove(stderr_log_name);
}


void clean_up(void)
{
    uitclock(OFF);
    tty_exit();
    if (edt) edit_end(edt);
    status_end();
    removelog();
}


void fatal(char *postmsg)
{
    clean_up();
    fprintf(stderr, "%s: fatal error: %s.\n", program, postmsg);
    exit(1);
}


void settitle(void)
{
    int len;
    char *buf;
    tty_status status;
    window *title_win = window_init(0, 0, 1, SCREEN_X);

    tty_save(&status);
    tty_cursor(OFF);

    tty_bright(TitleBrightness);
    tty_foreground(TitleForeground);
    tty_background(TitleBackground);

    window_cursormove_notify(title_win, 0, 0);
    window_write(title, strlen(title));

    buf = xmalloc(SCREEN_X + 1);

    len = (sizeof(login) - 1) + 1 + login_name_len + 2 +
    	  (sizeof(tty)   - 1) + 1 + tty_name_len;

    memset(buf, ' ', len = SCREEN_X - strlen(title) - len - 2 - 6 - 1);
    window_cursormove_notify(title_win, 0, strlen(title));
    window_write(buf, len);

    free(buf);

    window_cursormove_notify(title_win, 0, strlen(title) + len);
    window_write(login, sizeof(login) - 1);
    window_putch(' ');
    tty_foreground(UserName);
    window_write(login_name, login_name_len);
    window_putch(' ');
    window_putch(' ');
    tty_foreground(TitleForeground);
    window_write(tty, sizeof(tty) - 1);
    window_putch(' ');
    tty_foreground(TtyName);
    window_write(tty_name, tty_name_len);

    tty_foreground(TitleForeground);
    window_putch(' ');
    window_putch(' ');
    window_cursormove_notify(title_win, 0, SCREEN_X - 1);
    window_putch(' ');
    
    tty_restore(&status);
}


void clock_on(int signum)
{
    struct tm *_tm;
    int hour;
    time_t _time;
    char buf[16];
    tty_status status;

    signal(SIGALRM, clock_on);
    _time = time(NULL);
    _tm = localtime(&_time);
    alarm(60 - _tm->tm_sec);
    tty_save(&status);
    tty_cursor(OFF);
    if ((hour = _tm->tm_hour % 12) == 0) hour = 12;
    sprintf(buf, "%2d:%02d%c",
	    hour, _tm->tm_min, (_tm->tm_hour < 12) ? 'a' : 'p');
    tty_cursor(OFF);
    tty_cursormove_notify(0, SCREEN_X - 7);
    tty_bright(ClockBrightness);
    tty_foreground(ClockForeground);
    tty_background(ClockBackground);
    tty_write(buf, strlen(buf));
    tty_restore(&status);
    edit_setcursor(edt);
}


void clock_off(void)
{
    signal(SIGALRM, SIG_IGN);
}


void uitclock(int status)
{
    if (status == ON)
        clock_on(0);
    else
        clock_off();
}


void uitclock_status(int status)
{
    if (status)
	signal(SIGALRM, clock_on);
    else
	signal(SIGALRM, SIG_IGN);
}


/*
 * This function is a mess. Don't try to understand what it does ... :-(
 */

int getOpCommand(char *CommandName, char *src, char **dest, panel *p, panel *l)
{
    panel *t;
    struct group *grp;
    struct passwd *pwd;
    static int busy = 0;
    char *answer = NULL;
    char *question = NULL;
    int retval, uid, gid, len;
    int entry, oldtmplen, tmplen;
    char *ptr, *tmp = NULL, *d, *flag;


    d = *dest = xmalloc(len = (strlen(src) + 1));

    while (*src)
    {
        if (*src != '%')
	    *d++ = *src++;
        else
        {
	    t = islower(*++src) ? p : l;

            switch(*src)
            {
                case 's':

		    if (busy) { busy = 0; goto bad_command; }
		    if (*++src != '{') goto bad_command;
		    if ((ptr = strchr(++src, ',')) == NULL) goto bad_command;
		    *ptr = 0;
		    retval = getOpCommand(NULL, src, &answer, p, l);

		    if (retval != 1)
		        if (retval == 0)
		            goto bad_command;
		        else
		            goto string_dont_match;

		    question = xmalloc(16 + strlen(CommandName) +
		    			    strlen(answer) + 1);
		    sprintf(question, "(%s) %s", CommandName, answer);
		    free(answer);
		    answer =  NULL;
		    *ptr++ = ',';
		    if ((src = strchr(ptr, '}')) == NULL) goto bad_command;
		    *src = 0;

		    if (strlen(question) > MAX_STATIC_SIZE)
		        question[MAX_STATIC_SIZE] = 0;

		    retval = getOpCommand(NULL, ptr, &answer, p, l);

		    if (retval != 1)
		    {
			*src = '}';
			free(question);
			question = NULL;
			if (retval == 0)
			    goto bad_command;
			goto string_dont_match;
		    }

		    flag = edit_gets(edt, question, &tmp, answer);
		    
		    free(question);
		    free(answer);
		    question = answer = NULL;

		    if (flag == NULL)
		    {
			*src = '}';
			goto string_dont_match;
		    }

		    *src = '}';
		    break;

                case 'f':
                case 'F':

		    if (panel_getcurrentfiletype(t) != FILE_ENTRY)
			goto string_dont_match;
		  get_file_name:
		    ptr = panel_getcurrentfilename(t);
		    tmp = xmalloc(1 + strlen(ptr) + 1 + 1);
		    sprintf(tmp, "\"%s\"", ptr);
		    break;

                case 'd':
                case 'D':

		    if (panel_getcurrentfiletype(t) != DIR_ENTRY)
			goto string_dont_match;
		    goto get_file_name;

                case 'l':
                case 'L':

		    if (panel_getcurrentfiletype(t)!= SYMLINK_ENTRY)
			goto string_dont_match;
		    goto get_file_name;

                case 't':
                case 'T':

		    if (panel_getcurrentfiletype(t) != FIFO_ENTRY)
			goto string_dont_match;
		    goto get_file_name;

                case 'z':
                case 'Z':

		    if (panel_getcurrentfiletype(t) != SOCKET_ENTRY)
			goto string_dont_match;
		    goto get_file_name;

                case 'a':
                case 'A':

		    goto get_file_name;

                case 'm':
                case 'M':

		    tmp = xmalloc(16);
		    sprintf(tmp, "%o", panel_getcurrentfilemode(t) & 07777);
		    break;

                case 'o':
                case 'O':

		    uid = panel_getcurrentfileuid(t);
		    pwd = getpwuid(uid);

		    if (pwd)
		    {
			tmp = xmalloc(strlen(pwd->pw_name) + 1);
			strcpy(tmp, pwd->pw_name);
		    }
		    else
		    {
			tmp = xmalloc(16);
		        sprintf(tmp, "%o", uid);
		    }

		    break;

                case 'g':
                case 'G':

		    gid = panel_getcurrentfilegid(t);
		    grp = getgrgid(gid);

		    if (grp)
		    {
		        tmp = xmalloc(strlen(grp->gr_name) + 1);
		        strcpy(tmp, grp->gr_name);
		    }
		    else
		    {
			tmp = xmalloc(16);
		        sprintf(tmp, "%o", gid);
		    }

		    break;

                case 'p':
                case 'P':

		    tmp = xmalloc(1 + strlen(t->path) + 1 + 1);
		    sprintf(tmp, "\"%s\"", t->path);
		    break;

                case 'b':
                case 'B':

		    ptr = strrchr(t->path, '/');
		    ptr = (*++ptr) ? ptr : "/";
		    tmp = xmalloc(1 + strlen(ptr) + 1 + 1);
		    sprintf(tmp, "\"%s\"", ptr);
		    break;

		case 'i':
		case 'I':

		    tmp = NULL;
		    tmplen = 0;

		    while ((entry = panel_getnext(t)) != -1)
		    {
			oldtmplen = tmplen;
			tmplen += strlen(t->dir_entry[entry].name) + 1;
			tmp = xrealloc(tmp, tmplen + 1);
			strcpy(tmp + oldtmplen, t->dir_entry[entry].name);
			tmp[tmplen - 1] = ' ';
			tmp[tmplen    ] = 0;

			/* this should *NOT* be here because is *VERY* ugly */

			if (t->dir_entry[entry].selected)
			{
			    t->dir_entry[entry].selected = 0;
			    t->selected_files--;
			}
		    }

		    break;

                default :

		    goto bad_command;
            }
	    src++;
	    *d = 0;
	    *dest = xrealloc(*dest, len += strlen(tmp));
	    strcat(*dest, tmp);
	    d = *dest + strlen(*dest);
	    free(tmp);
	    tmp = NULL;
        }
    }

    *d = 0;
    return 1;

  bad_command:

    free(*dest);
    *dest = NULL;
    return 0;

  string_dont_match:

    if (tmp) free(tmp);
    *dest = NULL;
    return -1;
}


int main(int argc, char *argv[])
{
    FILE *stderr_log;
    char key_seq[16];
    struct key_struct *ks;
    ConfigKey *ConfigKeyInfo;
    char *StartupLeftPanelPath;
    char *StartupRightPanelPath;
    char PWD[MAX_STATIC_SIZE + 1];
    char *contents[KEYSDATA_FIELDS - 2];
    int euid, child_exit_code, repeat_count;
    int panel_no = 0, action_status, i, j, retval, len;
    char *data = NULL, *cmdln = NULL, *input = NULL, *ptr;
    int entry, index, key, app_end = 0, wait = 0, need_clrscr = 0;
    panel *left_panel, *right_panel, *current_panel, *other_panel, *temp_panel;


    signal(SIGSEGV, fatal_signal);
    signal(SIGHUP,  fatal_signal);
    signals(OFF);
    ignore_signals();

    printf("UNIX Interactive Tools 4.3.1\n");
    printf("Copyright (c) 1993, 1994 Tudor Hulubei & Andrei Pitis\n");

    program = argv[0];

    home = getenv("HOME");
    if (!home) home = ".";

    get_tty_name();
    get_login_name();

    tty_get_capabilities();
    tty_kbdinit(TTY_RESTRICTED_INPUT);

    configuration_check();


    use_section("[Setup]");

    configuration_getvarinfo("TempDirectory", &data, 1, DO_SEEK);
    TempDirectory = data ? tilde_expand(data) : "/tmp";

    AnsiColorSequences = get_flag_var("AnsiColorSequences", OFF);


    use_section("[UIT-Setup]");

    NormalModeHelp      = get_string_var("NormalModeHelp",      "");
    CommandLineModeHelp = get_string_var("CommandLineModeHelp", "");

    configuration_getvarinfo("StartupLeftPanelPath", &data, 1, DO_SEEK);
    StartupLeftPanelPath = data ? tilde_expand(data) : ".";

    configuration_getvarinfo("StartupRightPanelPath", &data, 1, DO_SEEK);
    StartupRightPanelPath = data ? tilde_expand(data) : ".";


    use_section(AnsiColorSequences ? cSection : bwSection);

    get_colorset_var(TitleColors, TitleFields, TITLE_FIELDS);


    use_section("[UIT-Keys]");

    for (i = 0; i < MAX_KEYS; i++)
    {
        configuration_getvarinfo(key_seq, contents,
        			 KEYSDATA_FIELDS - 2, NO_SEEK);

	if (*key_seq == 0) break;

	ConfigKeyInfo = (ConfigKey *)xmalloc(sizeof(ConfigKey));

	if (contents[0])
	{
            ConfigKeyInfo->OpName = xmalloc(strlen(contents[0]) + 1);
            strcpy(ConfigKeyInfo->OpName, contents[0]);
        }
        else
        {
            ConfigKeyInfo->OpName = NULL;
            continue;
        }

	if (contents[1])
	{
            ConfigKeyInfo->OpCommand = xmalloc(strlen(contents[1]) + 1);
            strcpy(ConfigKeyInfo->OpCommand, contents[1]);
        }
        else
        {
            ConfigKeyInfo->OpCommand = NULL;
            goto insert;
        }

        if (contents[2])
        {
            ConfigKeyInfo->OpNewDir = xmalloc(strlen(contents[2]) + 1);
            strcpy(ConfigKeyInfo->OpNewDir, contents[2]);
        }
        else
	    ConfigKeyInfo->OpNewDir = NULL;

	if (contents[3])
	    ConfigKeyInfo->OpSaveScreen = ((tolower(contents[3][0])=='y')?1:0);
        else
	    ConfigKeyInfo->OpSaveScreen = 1;

	if (contents[4])
	    ConfigKeyInfo->OpPause = ((tolower(contents[4][0]) == 'y') ? 1:0);
        else
	    ConfigKeyInfo->OpPause = 0;

	if (contents[5])
	    ConfigKeyInfo->OpHide = ((tolower(contents[5][0]) == 'y') ? 1:0);
        else
	    ConfigKeyInfo->OpHide = 0;

      insert:

        ConfigKeyInfo->OpBuiltin = 0;

        for (j = 0; j < BUILTIN_OPERATIONS; j++)
            if (strcmp(ConfigKeyInfo->OpName, built_in[j]) == 0)
            {
                free(ConfigKeyInfo->OpName);
                ConfigKeyInfo->OpName = built_in[j];
                ConfigKeyInfo->OpBuiltin = 1;
                break;
            }

	if (ConfigKeyInfo->OpBuiltin || ConfigKeyInfo->OpCommand)
	    if (tty_key_convert(key_seq))
	        tty_key_list_insert(key_seq, (void *)ConfigKeyInfo);
    }

    if (i == MAX_KEYS)
        fprintf(stderr,	"%s: too many key sequences; only %d are allowed.\n",
		program, MAX_KEYS);

    tty_getsize(&SCREEN_X, &SCREEN_Y);
    tty_startup();

    stdout_log_name = xmalloc(32 + strlen(TempDirectory) + 1);
    stderr_log_name = xmalloc(32 + strlen(TempDirectory) + 1);
    sprintf(stdout_log_name, "%s/uit.1.%d", TempDirectory, getpid());
    sprintf(stderr_log_name, "%s/uit.2.%d", TempDirectory, getpid());

#ifdef HAVE_LINUX
    if (LinuxConsole == ON)
	screen = xmalloc(2 + SCREEN_X * SCREEN_Y);
#endif	/* HAVE_LINUX */

    status_init(SCREEN_X, SCREEN_Y - 1, NormalModeHelp);

    if (getuid() == 0) PS1[1] = '#';
    edt = edit_init(SCREEN_X, SCREEN_Y - 2);

    left_panel  = panel_init(SCREEN_Y - 3, SCREEN_X >> 1, 0, 1,
			     StartupLeftPanelPath,  edt, &UserHeartAttack);
    right_panel = panel_init(SCREEN_Y - 3, SCREEN_X >> 1, SCREEN_X >> 1, 1,
			     StartupRightPanelPath, edt, &UserHeartAttack);

    configuration_end();

    tty_getscreen(screen);
    tty_set_mode(TTY_NONCANONIC);

    if (FrameDisplay == OFF)
    {
	tty_defaults();
	tty_clrscr();
    }

restart:

    signals(OFF);
    signal(SIGTSTP, suspend);

    if (wait)
    {
	tty_getkey(NULL);
	wait = 0;
    }

    if (need_clrscr)
    {
	tty_defaults();
	tty_clrscr();
	need_clrscr = 0;
    }

    settitle();
    uitclock(ON);
    uitclock(OFF);

    current_panel = panel_no ? right_panel : left_panel;
    other_panel   = panel_no ?  left_panel : right_panel;

    panel_action(current_panel, act_REFRESH, other_panel,   (void *)-1, 1);
    panel_action(other_panel,   act_REFRESH, current_panel, (void *)-1, 1);

    panel_setfocus(current_panel, ON, other_panel);

    status(NULL, 0, 0, 1, MSG_OK);

    edit_gettext(edt, &cmdln);
    edit_reset(edt);
    edit_puts(edt, strcat(panel_getpath(current_panel, PWD, MAX_STATIC_SIZE),
    			  PS1));
    edit_eos(edt);
    edit_puts(edt, cmdln);
    edit_update(edt);
    edit_setcursor(edt);

    signals(ON);
    uitclock(ON);

    while(!app_end)
    {
	UserHeartAttack = 0;
	suspend_allowed = ON;
	ks  = tty_getkey(&repeat_count);
	key = ks->key_seq[0];
	suspend_allowed = OFF;

	if (suspended)
	{
            tty_set_mode(TTY_NONCANONIC);
	    suspended = OFF;
	    panel_nooptimizations(current_panel);
	    panel_nooptimizations(other_panel);
	    tty_touch();
            goto restart;
	}

	signals(OFF);

	ConfigKeyInfo = (ConfigKey *)ks->aux_data;
	if (ConfigKeyInfo)
	 if (ConfigKeyInfo->OpBuiltin)
	    key = - 1 - (ConfigKeyInfo->OpName-built_in[0]) / MAX_BUILTIN_NAME;
	 else
	 {
	    if (ConfigKeyInfo->OpName)
	    {
		panel_nooptimizations(current_panel);
		panel_nooptimizations(other_panel);

		if (ConfigKeyInfo->OpCommand)
		{
		    char *cmd = NULL;

		    if (retval = getOpCommand(ConfigKeyInfo->OpName,
					      ConfigKeyInfo->OpCommand, &cmd,
					      current_panel, other_panel))
		    {
			if (retval == 1)
			{
			    int msglen = 32 + strlen(ConfigKeyInfo->OpName) +
			    		 strlen(cmd) + 1;
			    char *msg  = xmalloc(msglen);
			    
			    sprintf(msg, "Executing %s: %s",
				    ConfigKeyInfo->OpName, cmd);
			    status(msg, 0, 0, 0, MSG_WARNING);
			    free(msg);

			    child_exit_code = start(cmd,
			    			    ConfigKeyInfo->OpHide,
			    			    DO_EXEC);

			    free(cmd);

			    if (ConfigKeyInfo->OpHide)
			    {
				stderr_log = fopen(stderr_log_name, "r");

				if (child_exit_code) tty_beep();

				if (stderr_log == NULL)
				   status("*** Can't open stderr log file ***",
					  1, 1, 1, MSG_ERROR);
				else
				{
				    char *buf = xmalloc(SCREEN_X + 1);

				    while (fgets(buf, SCREEN_X+1, stderr_log))
				        if (status(buf, 1, 0, 0, MSG_ERROR) !=
					    key_ENTER)
					    break;

				    free(buf);
				}

				fclose(stderr_log);
			    }
			    else
			    {
				if (ConfigKeyInfo->OpSaveScreen)
				    tty_getscreen(screen);

				tty_touch();

				if (FrameDisplay == OFF) need_clrscr = 1;
				if (ConfigKeyInfo->OpPause) wait = 1;
			    }

			    if (child_exit_code == 0 &&
				ConfigKeyInfo->OpNewDir)
			    {
				char *expanded_dir =
					tilde_expand(ConfigKeyInfo->OpNewDir);

				panel_action(current_panel, act_CHDIR,
					     other_panel,
					     expanded_dir, 1);
				
				free(expanded_dir);
			    }
			    goto restart;
			}
			else
			    continue;
		    }
		    else
		    {
			char *msg = xmalloc(80 + strlen(ks->key_seq) + 1);
			sprintf(msg, "Bad configured command for key %s !",
				ks->key_seq);
			status(msg, 1, 1, 1, MSG_ERROR);
			free(msg);
			continue;
		    }
		}
	     }
	}

        signals(ON);
        
        switch (key)
        {
	    case key_TAB:
            case BUILTIN_ChangePanel:

 		signals(OFF);
		uitclock_status(OFF);
		if ((repeat_count & 1) == 0) break;
		panel_setfocus(current_panel, OFF, NULL);
		temp_panel = current_panel;
		current_panel = other_panel;
		other_panel = temp_panel;
		panel_no = !panel_no;
		panel_setfocus(current_panel, ON, other_panel);

	    restore:

		edit_gettext(edt, &cmdln);
		edit_reset(edt);
		edit_puts(edt, strcat(panel_getpath(current_panel, PWD,
						    MAX_STATIC_SIZE),
			  PS1));
		edit_eos(edt);
		edit_puts(edt, cmdln);
		edit_update(edt);
		uitclock_status(ON);
		break;

            case BUILTIN_CursorUp:

		signals(OFF);
		uitclock_status(OFF);
		panel_action(current_panel, act_UP, other_panel,
			     NULL, repeat_count);
		uitclock_status(ON);
		break;

            case BUILTIN_CursorDown:

		signals(OFF);
		uitclock_status(OFF);
		panel_action(current_panel, act_DOWN, other_panel,
			     NULL, repeat_count);
		uitclock_status(ON);
		break;

            case key_ENTER:

		action_status = 0;
		if (*edit_gettext(edt, &cmdln))
		    switch (*cmdln)
		    {
			case '+':

		            action_status = panel_action(current_panel,
							 act_SELECT_ALL,
							 other_panel, NULL, 1);
			    break;

		        case '-':

			    action_status = panel_action(current_panel,
							 act_UNSELECT_ALL,
							 other_panel, NULL, 1);
			    break;

		        case '*':
			
			    action_status = panel_action(current_panel,
							 act_TOGGLE,
							 other_panel, NULL, 1);
			    break;

			default:

			    start(cmdln, 0, DO_FORK);
		            panel_nooptimizations(current_panel);
			    panel_nooptimizations(other_panel);
			    tty_touch();
			    action_status = -1; 
			    break;
		    }
		else
              	{
		    signals(OFF);
		    uitclock_status(OFF);
		    action_status = panel_action(current_panel, act_ENTER,
						 other_panel, screen, 1);
		    uitclock_status(ON);
		}

		if (action_status == 1)
		{
		    uitclock_status(OFF);
		    edit_reset(edt);
		    edit_puts(edt, strcat(panel_getpath(current_panel, PWD,
							MAX_STATIC_SIZE),PS1));
		    edit_eos(edt);
		    edit_update(edt);
		    uitclock_status(ON);
		}
		else
		    if (action_status == -1) 
		    {
			uitclock_status(OFF);
			tty_getscreen(screen);
			edit_history(edt, EDIT_RECORD);
			edit_reset(edt);
			uitclock_status(ON);
			if (FrameDisplay == OFF) need_clrscr = 1;
			goto restart;
		    }
		break;

            case BUILTIN_FileSelect:

		signals(OFF);
		uitclock_status(OFF);
		for (i = 0; i < repeat_count; i++)
		    panel_action(current_panel, act_SELECT,
				 other_panel, NULL, 1);
		uitclock_status(ON);
		break;

            case BUILTIN_CursorLeft:
            case BUILTIN_PageUp:

		signals(OFF);
		uitclock_status(OFF);

		for (i = 0; i < repeat_count; i++)
		    panel_action(current_panel, act_PGUP, other_panel, NULL,1);
		
		uitclock_status(ON);
		break;

            case BUILTIN_CursorRight:
            case BUILTIN_PageDown:  

		signals(OFF);
		uitclock_status(OFF);

		for (i = 0; i < repeat_count; i++)
		    panel_action(current_panel, act_PGDOWN,
				 other_panel, NULL, 1);

		uitclock_status(ON);
		break;

            case BUILTIN_Home:

		signals(OFF);
		uitclock_status(OFF);
		panel_action(current_panel, act_HOME, other_panel, NULL, 1);
		uitclock_status(ON);
		break;

            case BUILTIN_End:

		signals(OFF);
		uitclock_status(OFF);
		panel_action(current_panel, act_END, other_panel, NULL, 1);
		uitclock_status(ON);
		break;

            case BUILTIN_GotoRoot:  

		signals(OFF);
		uitclock_status(OFF);
		panel_action(current_panel, act_CHDIR, other_panel, "/", 1);

		goto restore;

	    case BUILTIN_HardRefresh:
	    
	        tty_touch();

            case BUILTIN_Refresh:

		panel_nooptimizations(current_panel);
		panel_nooptimizations(other_panel);
		if (FrameDisplay == OFF) need_clrscr = 1;
		goto restart;

            case BUILTIN_ShowTty:

		if ((repeat_count & 1) == 0) break;
		uitclock(OFF);
		tty_putscreen(screen);
		status(CommandLineModeHelp, 0, 0, 0, MSG_OK);
		edit_update(edt);
		edit_setcursor(edt);

		while (1)
		{
		    suspend_allowed = ON;
		    ks  = tty_getkey(&repeat_count);
		    key = ks->key_seq[0];

		    if (suspended)
		    {
			tty_set_mode(TTY_NONCANONIC);
			signal(SIGTSTP, suspend);
			status(CommandLineModeHelp, 0, 0, 0, MSG_OK);
			edit_update(edt);
			edit_setcursor(edt);
			suspended = OFF;
		    }

		    ConfigKeyInfo = (ConfigKey *)ks->aux_data;
		    if (ConfigKeyInfo && ConfigKeyInfo->OpBuiltin)
		        key = - 1 - (ConfigKeyInfo->OpName - built_in[0]) /
			             MAX_BUILTIN_NAME;

		    if (key == BUILTIN_ShowTty && (repeat_count & 1)) break;

		    switch (key)
		    {
			case key_ENTER:

			    if (*edit_gettext(edt, &cmdln))
			    {
				tty_putscreen(screen);
				start(cmdln, 0, DO_FORK);
				tty_getscreen(screen);
				edit_history(edt, EDIT_RECORD);
				edit_reset(edt);
				edit_puts(edt,
					  strcat(panel_getpath(current_panel,
							       PWD,
							       MAX_STATIC_SIZE)
						 , PS1));
				edit_eos(edt);
				edit_update(edt);
				status(CommandLineModeHelp, 0, 0, 0, MSG_OK);
			    }
			    break;

			case BUILTIN_HistoryBack:
			case BUILTIN_CursorUp:

			    for (i = 0; i < repeat_count; i++)
			        edit_history(edt, EDIT_PREVIOUS);

			    break;

			case BUILTIN_HistoryNext:
			case BUILTIN_CursorDown:

			    for (i = 0; i < repeat_count; i++)
			        edit_history(edt, EDIT_NEXT);

			    break;

                        case BUILTIN_DeleteCmdLn:

			    edit_del(edt);
			    break;

			case BUILTIN_DeleteCmdLnWord:

			    edit_delete_last_word(edt);
			    break;

			case BUILTIN_Refresh:

			    panel_nooptimizations(current_panel);
			    panel_nooptimizations(other_panel);
			    tty_putscreen(screen);
			    edit_update(edt);
			    status(CommandLineModeHelp, 0, 0, 0, MSG_OK);
			    break;

			case BUILTIN_Exit:

			    if (status(exit_msg, 1, 1, 1, MSG_ERROR) ==
				key_ENTER)
			    {
				app_end = 1;
				goto end_show_tty;
			    }
			    status(CommandLineModeHelp, 0, 0, 0, MSG_OK);
			    break;

			default:

			    if (key) edit_putch(edt, key);
			    break;
		    }
		    edit_setcursor(edt);
		}

	      end_show_tty:

		panel_nooptimizations(current_panel);
		panel_nooptimizations(other_panel);
		tty_touch();
		status(NULL, 0, 0, 1, MSG_OK);

		if (app_end)
		    continue;
		else
		{
		    if (FrameDisplay == OFF) need_clrscr = 1;
		    goto restart;
		}

	    case BUILTIN_DeleteCmdLn:

		signals(OFF);
		uitclock_status(OFF);
		edit_del(edt);
		uitclock_status(ON);
		break;

	    case BUILTIN_DeleteCmdLnWord:

		signals(OFF);
		uitclock_status(OFF);
		edit_delete_last_word(edt);
		uitclock_status(ON);
		break;

            case BUILTIN_Copy:

		uitclock_status(OFF);
		panel_action(current_panel, act_COPY, other_panel, NULL, 1);
		uitclock_status(ON);
		break;

            case BUILTIN_RenameMove:

		uitclock_status(OFF);
		panel_action(current_panel, act_MOVE, other_panel, NULL, 1);
		uitclock_status(ON);
		break;

            case BUILTIN_MkDir:

		signals(OFF);
		uitclock_status(OFF);
		panel_action(current_panel, act_MKDIR, other_panel, NULL, 1);
		uitclock_status(ON);
		break;

            case BUILTIN_Delete:

		uitclock_status(OFF);
		panel_action(current_panel, act_DELETE, other_panel, NULL, 1);
		uitclock_status(ON);
		break;

	    case BUILTIN_FileDisplayNextMode:

	    case BUILTIN_FileDisplayOwnerGroup:
	    case BUILTIN_FileDisplayDateTime:
	    case BUILTIN_FileDisplaySize:
	    case BUILTIN_FileDisplayMode:
	    case BUILTIN_FileDisplayFullName:

		signals(OFF);
		uitclock_status(OFF);
		panel_action(current_panel,
			     act_DISPLAY_NEXT_MODE -
			     (key - BUILTIN_FileDisplayNextMode),
			     NULL, NULL, 1);
		uitclock_status(ON);
		break;

	    case BUILTIN_FileSortNextMethod:

	    case BUILTIN_FileSortByName:
	    case BUILTIN_FileSortByExtension:
	    case BUILTIN_FileSortBySize:
	    case BUILTIN_FileSortByDate:
	    case BUILTIN_FileSortByMode:
	    case BUILTIN_FileSortByOwnerId:
	    case BUILTIN_FileSortByGroupId:
	    case BUILTIN_FileSortByOwnerName:
	    case BUILTIN_FileSortByGroupName:

		signals(OFF);
		uitclock_status(OFF);
		panel_action(current_panel,
			     act_SORT_NEXT_METHOD -
			     (key - BUILTIN_FileSortNextMethod),
			     NULL, NULL, 1);
		uitclock_status(ON);
		break;

            case BUILTIN_Exit:

		signals(OFF);
		uitclock_status(OFF);
		if (status(exit_msg, 1, 1, 1, MSG_ERROR) == key_ENTER)
		    app_end = 1;
		edit_setcursor(edt);
		uitclock_status(ON);
		continue;

            case BUILTIN_CopyFileToCmdLn:

		signals(OFF);
		uitclock_status(OFF);
		edit_gettext(edt, &cmdln);
		len = strlen(cmdln);

		if (len && isalnum(cmdln[len - 1]))
		{
		    uitclock_status(ON);
		    break;
		}

		ptr = panel_getcurrentfilename(current_panel);
		ptr = strcpy(xmalloc(strlen(ptr) + 1 + 1), ptr);
		cmdln = xrealloc(cmdln, len + strlen(ptr) + 1 + 1);

	      copy_to_cmdln:

		for (i = 0; ptr[i]; i++)
		    if (!is_print(ptr[i]))
		    {
			tty_beep();
			ptr[i] = 0;
			break;
		    }

		ptr[i  ] = ' ';
		ptr[i+1] = 0;
		strcpy(&cmdln[len], ptr);
		edit_puts(edt, ptr);
		free(ptr);
		edit_update(edt);
		uitclock_status(ON);
		break;

            case BUILTIN_CopyPathToCmdLn:

		signals(OFF);
		uitclock_status(OFF);
		edit_gettext(edt, &cmdln);
		len = strlen(cmdln);

		if (len && isalnum(cmdln[len - 1]))
		{
		    uitclock_status(ON);
		    break;
		}

		ptr = strcpy(xmalloc(other_panel->pathlen + 1 + 1),
			     other_panel->path);
		cmdln = xrealloc(cmdln, len + other_panel->pathlen + 1 + 1);

		goto copy_to_cmdln;

            case BUILTIN_CopySelectedFilesToCmdLn:

		signals(OFF);
		uitclock_status(OFF);
		edit_gettext(edt, &cmdln);
		len = strlen(cmdln);

		if (len && isalnum(cmdln[len - 1]))
		{
		    uitclock_status(ON);
		    break;
		}

		while ((entry = panel_getnext(current_panel)) != -1)
		{
		    ptr = current_panel->dir_entry[entry].name;
		    ptr = strcpy(xmalloc(strlen(ptr) + 1 + 1), ptr);
		    cmdln = xrealloc(cmdln, len + strlen(ptr) + 1 + 1);

		    for (i = 0; ptr[i]; i++)
			if (!is_print(ptr[i]))
			{
			    tty_beep();
			    ptr[i] = 0;
			    break;
			}

		    ptr[i  ] = ' ';
		    ptr[i+1] = 0;
		    strcpy(&cmdln[len], ptr);

		    if (!edit_puts(edt, ptr))
		        break;

		    free(ptr);
		}

		edit_update(edt);
		uitclock_status(ON);
		break;

	    case BUILTIN_HistoryBack:

		signals(OFF);
		uitclock_status(OFF);

		for (i = 0; i < repeat_count; i++)
		    edit_history(edt, EDIT_PREVIOUS);

		uitclock_status(ON);
		break;

	    case BUILTIN_HistoryNext:

		signals(OFF);
		uitclock_status(OFF);

		for (i = 0; i < repeat_count; i++)
		    edit_history(edt, EDIT_NEXT);

		uitclock_status(ON);
		break;
		
	    case BUILTIN_SwitchPanels:

		if ((repeat_count & 1) == 0) break;
		signals(OFF);
		uitclock_status(OFF);
		panel_nooptimizations(current_panel);
		panel_nooptimizations(other_panel);
		panel_action(current_panel, act_SWITCH, other_panel, NULL, 1);
		panel_action(other_panel, act_REFRESH,
			     current_panel, (void *)-1, 1);
		panel_action(current_panel, act_REFRESH,
			     other_panel, (void *)-1, 1);
		uitclock_status(ON);
		break;

	    case BUILTIN_ChangeDir:

		signals(OFF);
		uitclock_status(OFF);

		if (edit_gets(edt, "(CHDIR) Change to directory: ",
			      &input, NULL))
		{
		    char *expanded_input;

		    if (input[0] == 0) break;

		    panel_action(current_panel, act_CHDIR, other_panel,
		    		 expanded_input = tilde_expand(input), 1);

		    free(expanded_input);
		    free(input);
		    input = NULL;
		}

		goto restore;


	    case BUILTIN_FilePatternSelect:

		signals(OFF);
		uitclock_status(OFF);

		if (edit_gets(edt, "(SELECT) Pattern: ",
			      &input, NULL))
		{
		    if (input[0] == 0) break;

		    panel_action(current_panel, act_PATTERN_SELECT,
		    		 other_panel, input, 1);

		    free(input);
		    input = NULL;
		}

		goto restore;

	    case BUILTIN_FilePatternUnselect:

		signals(OFF);
		uitclock_status(OFF);

		if (edit_gets(edt, "(UNSELECT) Pattern: ",
			      &input, NULL))
		{
		    if (input[0] == 0) break;

		    panel_action(current_panel, act_PATTERN_UNSELECT,
		    		 other_panel, input, 1);

		    free(input);
		    input = NULL;
		}

		goto restore;

	    case BUILTIN_ConformCurrentDir:

		signals(OFF);
		uitclock_status(OFF);

		panel_action(current_panel, act_CHDIR, other_panel,
			     other_panel->path, 1);

		goto restore;

	    case BUILTIN_ConformOtherDir:

		signals(OFF);
		uitclock_status(OFF);

		panel_action(other_panel, act_CHDIR, current_panel,
			     current_panel->path, 1);

		goto restore;

            default:

		if (key)
		{
		    uitclock_status(OFF);
		    edit_putch(edt, key);
		    uitclock_status(ON);
		}

		break;
	}

	signals(ON);
        edit_setcursor(edt);
    }

    signals(OFF);
    panel_end(left_panel);
    panel_end(right_panel);
    uitclock(OFF);
    tty_set_mode(TTY_CANONIC);
    edit_end(edt);
    status_end();
    removelog();

#ifdef HAVE_LINUX
    if (LinuxConsole == ON)
	tty_putscreen(screen);
    else
    {
	tty_defaults();
	tty_clrscr();
    }
#else	/* not HAVE_LINUX */
    tty_defaults();
    tty_clrscr();
#endif	/* not HAVE_LINUX */

    return 0;
}
