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

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 <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <signal.h>
#include <time.h>
#include <pwd.h>
#include <grp.h>
#include <malloc.h>
#include <limits.h>
#include <termios.h>
#include <sys/wait.h>
#include "tty.h"
#include "window.h"
#include "edit.h"
#include "status.h"
#include "panel.h"
#include "config.h"
#include "termcap.h"


#define MAX_STATIC_SIZE 60

#ifdef HAVE_LINUX
int  LinuxConsole = ON;
int  ColorMonitor = ON;
#else
int  LinuxConsole = OFF;
int  ColorMonitor = OFF;
#endif

edit *edt;
int  SignalsStatus;
int  SCREEN_X, SCREEN_Y;
int  suspended = OFF;
int  suspend_allowed = OFF;
char cSection[]  = "[UIT-Color]";
char bwSection[] = "[UIT-Monochrome]";
char title[] = "UNIX Interactive Tools 4.2b";
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;
char configfile[PATH_MAX] = "";
char TempDirectory[PATH_MAX];
static char NormalModeHelp[256];
static char CommandLineModeHelp[256];

extern int FrameDisplay;
extern char tty_type[];
static int UserHeartAttack;

#define BUILTIN_OPERATIONS		25

#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_FileDisplayMode		-12
#define BUILTIN_DeleteCmdLn		-13
#define BUILTIN_FileSelect		-14
#define BUILTIN_CopyFileToCmdLn		-15
#define BUILTIN_Home			-16
#define BUILTIN_End			-17
#define BUILTIN_PageUp			-18
#define BUILTIN_PageDown		-19
#define BUILTIN_CursorLeft		-20
#define BUILTIN_CursorRight		-21
#define BUILTIN_CursorUp		-22
#define BUILTIN_CursorDown		-23
#define BUILTIN_ChangePanel		-24
#define BUILTIN_ChangeDir		-25


#define MAX_BUILTIN_NAME		20


char built_in[BUILTIN_OPERATIONS][MAX_BUILTIN_NAME] =
{
    "<Copy>",
    "<RenameMove>",
    "<MkDir>",
    "<Delete>",
    "<Exit>",
    "<HistoryBack>",
    "<ShowTty>",
    "<Refresh>",
    "<GotoRoot>",
    "<SwitchPanels>",
    "<HistoryNext>",
    "<FileDisplayMode>",
    "<DeleteCmdLn>",
    "<FileSelect>",
    "<CopyFileToCmdLn>",
    "<Home>",
    "<End>",
    "<PageUp>",
    "<PageDown>",
    "<CursorLeft>",
    "<CursorRight>",
    "<CursorUp>",
    "<CursorDown>",
    "<ChangePanel>",
    "<ChangeDir>",
};


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][40] = 
{
    "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
static int TitleColors[TITLE_FIELDS] = 
{
    WHITE, BLACK, ON, WHITE, WHITE, BLACK, WHITE, OFF
};
#endif

#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);


void user_panic(int signum)
{
    signal(signum, user_panic);
    tcdrain(1);
    tcflush(0, TCIFLUSH);
    UserHeartAttack = 1; 
}


void removelog(void)
{
    char name[PATH_MAX];
    
    sprintf(name, "%s/uit.1.%d", TempDirectory, getpid());
    remove(name);
    sprintf(name, "%s/uit.2.%d", TempDirectory, getpid());
    remove(name);
}


void fatal_signal(int signum)
{
    tty_end();
    tty_clrscr();

    switch (signum)
    {
	case SIGTERM:
	case SIGQUIT:

	    fprintf(stderr, "Got %s !\nbye\n",
	           (signum == SIGTERM) ? "SIGTERM" : "SIGQUIT");
	    break;

	case SIGSEGV:

	    fprintf(stderr, "Got SIGSEGV !\n");
	    fprintf(stderr, "Please report to tudor@ulise.cs.pub.ro\n");
	    break;

	case SIGHUP:

	    /* :-) */
	    fprintf(stderr, "Got SIGHUP !\n");
	    break;

	default:

	    fprintf(stderr, "Got bad signal !\n");
	    fprintf(stderr, "Please report to tudor@ulise.cs.pub.ro\n");
	    break;
    }
    removelog();
    exit(1);
}


void signals_dfl(void)
{
    signal(SIGTERM, SIG_DFL);
    signal(SIGQUIT, SIG_DFL);
    signal(SIGINT , SIG_DFL);
}


void signals(int status)
{
    SignalsStatus = status;
    signal(SIGTERM, status == ON ? fatal_signal : SIG_IGN);
    signal(SIGQUIT, status == ON ? fatal_signal : SIG_IGN);
    signal(SIGINT , status == ON ? user_panic   : SIG_IGN);
}


void ignore_signals(void)
{
    signal(SIGILL,  SIG_IGN);
    signal(SIGTRAP, SIG_IGN);
    signal(SIGABRT, SIG_IGN);
    signal(SIGUSR1, SIG_IGN);
    signal(SIGUSR2, SIG_IGN);
    signal(SIGTTIN, SIG_IGN);
    signal(SIGTTOU, SIG_IGN);
    signal(SIGPIPE, SIG_IGN);
    signal(SIGFPE,  SIG_IGN);
}


void restore_signals(void)
{
    signal(SIGILL,  SIG_DFL);
    signal(SIGTRAP, SIG_DFL);
    signal(SIGABRT, SIG_DFL);
    signal(SIGUSR1, SIG_DFL);
    signal(SIGUSR2, SIG_DFL);
    signal(SIGTTIN, SIG_DFL);
    signal(SIGTTOU, SIG_DFL);
    signal(SIGPIPE, SIG_DFL);
    signal(SIGFPE,  SIG_DFL);
}


void settitle(void)
{
    char buf[256], *login_name = getlogin(), *tty_name = ttyname(1);
    window *title_win = window_init(0, 0, 1, SCREEN_X);

    if (login_name == NULL) login_name = "";
    if (tty_name   == NULL) tty_name   = "";

    tty_bright(TitleBrightness);
    tty_foreground(TitleForeground);
    tty_background(TitleBackground);
    window_cursormove(title_win, 0, 0);
    memset(buf, ' ', SCREEN_X);
    window_write(buf, SCREEN_X);

    sprintf(buf, "%s %s  %s %s        ", login, login_name, tty, tty_name);
    window_cursormove(title_win, 0, 1);
    window_write(title, strlen(title));
    window_cursormove(title_win, 0, SCREEN_X - strlen(buf)  - 1);
    window_write(login, strlen(login));
    window_putch(' ');
    tty_foreground(UserName);
    window_write(login_name, strlen(login_name));
    window_putch(' ');
    window_putch(' ');
    tty_foreground(TitleForeground);
    window_write(tty, strlen(tty));
    window_putch(' ');
    tty_foreground(TtyName);
    window_write(tty_name, strlen(tty_name));
}


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

    signal(SIGALRM, clock_on);
    _time = time(NULL);
    _tm = localtime(&_time);
    alarm(60 - _tm->tm_sec);
    tty_save(&status);
    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_cursormove(0, SCREEN_X - 7);
    tty_foreground(ClockForeground);
    tty_background(ClockBackground);
    tty_bright(ClockBrightness);
    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);
}


void Suspend(int signum)
{
    if (suspend_allowed)
    {
	suspended = ON;
	uitclock(OFF);
	tty_end();
	tty_putscreen(screen);
	signal(SIGTSTP, SIG_DFL);
	kill(getpid(), SIGTSTP);
    }
    else
	signal(SIGTSTP, Suspend);
}


int start(char *cmd, int hide)
{
    int child_exit_code;
    FILE *stdout_log, *stderr_log;
    char fname[PATH_MAX], *tty_name;
    
    if (hide)
    {
        uitclock(OFF);
        tty_name = ttyname(1);
        close(1);
        close(2);
        sprintf(fname, "%s/uit.1.%d", TempDirectory, getpid());
        stdout_log = fopen(fname, "w");
        sprintf(fname, "%s/uit.2.%d", TempDirectory, getpid());
	stderr_log = fopen(fname, "w");
	
	restore_signals();
	signals_dfl();
        child_exit_code = system(cmd);
	signals(SignalsStatus);
	ignore_signals();
	
	fclose(stderr_log);
        fclose(stdout_log);
        open(tty_name, O_RDWR);
        open(tty_name, O_RDWR);
    }
    else
    {
        uitclock(OFF);
        tty_end();
        tty_putscreen(screen);

	restore_signals();
	signals_dfl();
        child_exit_code = system(cmd);
        signals(SignalsStatus);
	ignore_signals();

	write(1, "\n\n", 2);
        tty_init();
    }
    return child_exit_code;
}


void fatal(char *postmsg)
{
    uitclock(OFF);
    tty_end();
    tty_clrscr();
    fprintf(stderr, "Fatal error : %s !\n", postmsg);
    removelog();
    exit(1);
}


int getOpCommand(char *CommandName, char *src, char *dest,
		 panel *p, panel *l, edit *e)
{
    char *ptr;
    struct group *grp;
    struct passwd *pwd;
    static int busy = 0;
    int  retval, uid, gid, len;
    char question[MAXLINE];
    char formatted[MAXLINE];


    while (*src)
    {
        if (*src != '%') *dest++ = *src++;
        else
        {
            switch(*++src)
            {
                case 's':

		    if (busy) { busy = 0; return 0; }
		    if (*++src != '{') return 0;
		    if ((ptr = strchr(++src, ',')) == NULL) return 0;
		    *ptr = 0;
		    retval = getOpCommand(NULL, src, formatted, p, l, NULL);
		    if (retval != 1) return retval;
		    sprintf(question, "(%s) %s", CommandName, formatted);
		    *ptr++ = ',';
		    if ((src = strchr(ptr, '}')) == NULL) return 0;
		    *src = 0;
		    question[MAX_STATIC_SIZE] = 0;
		    retval = getOpCommand(NULL, ptr, formatted, p, l, NULL);

		    if (retval != 1)
		    {
			*src = '}';
			return retval;
		    }

		    if (!edit_gets(e, question, dest, formatted))
		    {
			*src = '}';
			return -1;
		    }

		    *src = '}';
		    break;

                case 'f':

		    if (panel_getcurrentfiletype(p) != FILE_ENTRY) return -1;
		    sprintf(dest, "\"%s\"", panel_getcurrentfilename(p));
		    break;

                case 'd':

		    if (panel_getcurrentfiletype(p) != DIR_ENTRY) return -1;
		    sprintf(dest, "\"%s\"", panel_getcurrentfilename(p));
		    break;

                case 'l':

		    if (panel_getcurrentfiletype(p)!= SYMLINK_ENTRY) return -1;
		    sprintf(dest, "\"%s\"", panel_getcurrentfilename(p));
		    break;

                case 't':

		    if (panel_getcurrentfiletype(p) != FIFO_ENTRY) return -1;
		    sprintf(dest, "\"%s\"", panel_getcurrentfilename(p));
		    break;

                case 'z':

		    if (panel_getcurrentfiletype(p) != SOCKET_ENTRY) return -1;
		    sprintf(dest, "\"%s\"", panel_getcurrentfilename(p));
		    break;

                case 'a':

		    sprintf(dest, "\"%s\"", panel_getcurrentfilename(p));
		    break;

                case 'm':

		    sprintf(dest, "%o", panel_getcurrentfilemode(p) & 07777);
		    break;

                case 'o':

		    uid = panel_getcurrentfileuid(p);
		    pwd = getpwuid(uid);
		    if (pwd)
		        sprintf(dest, "%s", pwd->pw_name);
		    else
		        sprintf(dest, "%o", uid);
		    break;

                case 'g':

		    gid = panel_getcurrentfilegid(p);
		    grp = getgrgid(gid);
		    if (grp)
		        sprintf(dest, "%s", grp->gr_name);
		    else
		        sprintf(dest, "%o", gid);
		    break;

                case 'p':

		    dest[0] = '\"';
		    panel_getpath(p, dest + 1, PATH_MAX);
		    len = strlen(dest);
		    dest[len] = '"';
		    dest[len + 1] = 0;
		    break;

                case 'b':

		    dest[0] = '\"';
		    panel_getpath(p, dest + 1, PATH_MAX);
		    ptr = strrchr(dest + 1, '/');
		    strcpy(dest + 1, (*++ptr) ? ptr : "root");
		    len = strlen(dest);
		    dest[len] = '"';
		    dest[len + 1] = 0;
		    break;

                case 'F':

		    if (panel_getcurrentfiletype(l) != FILE_ENTRY) return -1;
		    sprintf(dest, "\"%s\"", panel_getcurrentfilename(l));
		    break;

                case 'D':

		    if (panel_getcurrentfiletype(l) != DIR_ENTRY) return -1;
		    sprintf(dest, "\"%s\"", panel_getcurrentfilename(l));
		    break;

                case 'L':

		    if (panel_getcurrentfiletype(l)!= SYMLINK_ENTRY) return -1;
		    sprintf(dest, "\"%s\"", panel_getcurrentfilename(l));
		    break;

                case 'T':

		    if (panel_getcurrentfiletype(p) != FIFO_ENTRY) return -1;
		    sprintf(dest, "\"%s\"", panel_getcurrentfilename(p));
		    break;

                case 'Z':

		    if (panel_getcurrentfiletype(p) != SOCKET_ENTRY) return -1;
		    sprintf(dest, "\"%s\"", panel_getcurrentfilename(p));
		    break;

                case 'A':

		    sprintf(dest, "\"%s\"", panel_getcurrentfilename(l));
		    break;

                case 'M':

		    sprintf(dest, "%o", panel_getcurrentfilemode(l) & 07777);
		    break;

                case 'O':

		    uid = panel_getcurrentfileuid(l);
		    pwd = getpwuid(uid);
		    if (pwd)
		        sprintf(dest, "%s", pwd->pw_name);
		    else
		        sprintf(dest, "%o", uid);
		    break;

                case 'G':

		    gid = panel_getcurrentfilegid(l);
		    grp = getgrgid(gid);
		    if (grp)
		        sprintf(dest, "%s", grp->gr_name);
		    else
		        sprintf(dest, "%o", gid);
		    break;

                case 'P':

		    dest[0] = '\"';
		    panel_getpath(l, dest + 1, PATH_MAX);
		    len = strlen(dest);
		    dest[len] = '\"';
		    dest[len + 1] = 0;
		    break;

		case 'B':

		    dest[0] = '\"';
		    panel_getpath(l, dest + 1, PATH_MAX);
		    ptr = strrchr(dest + 1, '/');
		    strcpy(dest + 1, (*++ptr) ? ptr : "root");
		    len = strlen(dest);
		    dest[len] = '"';
		    dest[len + 1] = 0;
		    break;

                default :

		    return 0;
            }
            src++;
            dest += strlen(dest);
        }
    }
    *dest = 0;
    return 1;
}


int main(void)
{
    FILE *stderr_log;
    char cmdln[PATH_MAX];
    struct key_struct *ks;
    configuration *config;
    char cmdname[PATH_MAX];
    ConfigKey *ConfigKeyInfo;
    char *homedirectory = cmdln;
    char temppath[2][PATH_MAX];
    char *home, *data = NULL, *section;
    int child_exit_code, repeat_count;
    int panel_no = 0, action_status, i, j, retval, len;
    int sectionptr = 0, 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.2b\n");
    printf("Copyright (c) May 1994 by Tudor Hulubei & Andrei Pitis\n");

    termcap_init();
    tty_kbdinit(0);

    strcpy(homedirectory, "/");
    strcpy(temppath[0], ".");
    strcpy(temppath[1], ".");

    if (home = getenv("HOME"))
    {
        strcpy(homedirectory, home);
        strcpy(configfile, home);
        if (configfile[strlen(configfile) - 1] != '/') strcat(configfile, "/");
    }

    strcat(configfile, ".uitrc.");
    strcat(configfile, tty_type);

    config = configuration_init(configfile);

    if (configuration_getstatus(config) == STATUS_OK)
    {
	sectionptr = configuration_getsectionptr(config,"[Setup]");
        if (sectionptr == -1)
	    fprintf(stderr, "[Setup] section missing.");
	else
	{
	    configuration_getfielddata(config, sectionptr, "TempDirectory",
				       &data, 1, DO_SEEK);
	    strncpy(TempDirectory, data ? data : homedirectory, PATH_MAX);
#ifdef HAVE_LINUX
	    configuration_getfielddata(config, sectionptr, "LinuxConsole",
				       &data, 1, DO_SEEK);
	    LinuxConsole = data && strcmp(data, "ON") == 0;
	    configuration_getfielddata(config, sectionptr, "ColorMonitor",
				       &data, 1, DO_SEEK);
	    ColorMonitor = data && strcmp(data, "ON") == 0;
#endif
	}

	sectionptr = configuration_getsectionptr(config, "[UIT-Setup]");
        if (sectionptr == -1)
	    fprintf(stderr, "[UIT-Setup] section missing.");
	else
	{
	    configuration_getfielddata(config, sectionptr,
				       "NormalModeHelp",
				       &data, 1, DO_SEEK);
	    if (data) strncpy(NormalModeHelp, data, 256);
	    configuration_getfielddata(config, sectionptr,
				       "CommandLineModeHelp",
				       &data, 1, DO_SEEK);
	    if (data) strncpy(CommandLineModeHelp, data, 256);
	    configuration_getfielddata(config, sectionptr,
				       "StartupLeftPanelPath",
				       &data, 1, DO_SEEK);
	    strncpy(temppath[0], data ? data : ".", PATH_MAX);
	    configuration_getfielddata(config, sectionptr,
				       "StartupRightPanelPath",
				       &data, 1, DO_SEEK);
	    strncpy(temppath[1], data ? data : ".", PATH_MAX);
	}

	section = (LinuxConsole && ColorMonitor) ? cSection : bwSection;

	sectionptr = configuration_getsectionptr(config, section);
        if (sectionptr == -1)
	    fprintf(stderr, "%s section missing.", section);
	else
            for (i = 0; i < TITLE_FIELDS; i++)
            {
                configuration_getfielddata(config, sectionptr, TitleFields[i],
					   &data, 1, DO_SEEK);
                if (!data || (index  = tty_getcolorindex(data))== -1)
                    fprintf(stderr, "Invalid %s (%s).\n", TitleFields[i],data);
	        else
                    TitleColors[i] = index;
            }

        sectionptr = configuration_getsectionptr(config, "[UIT-Keys]");
        if (sectionptr == -1)
	    fprintf(stderr, "[UIT-Keys] section missing.");
	else
	{
	    char key_seq[16];
	    ConfigKey *ConfigKeyInfo;
	    char *contents[KEYSDATA_FIELDS - 2];

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

		if (*key_seq == 0) break;

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

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

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

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

		if (contents[3])
		    ConfigKeyInfo->OpSaveScreen = atoi(contents[3]);
	        else
		    ConfigKeyInfo->OpSaveScreen = 1;

		if (contents[4])
		    ConfigKeyInfo->OpPause = atoi(contents[4]);
	        else
		    ConfigKeyInfo->OpPause = 0;

		if (contents[5])
		    ConfigKeyInfo->OpHide = atoi(contents[5]);
	        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,
			"Too many key sequences. Only %d are allowed.\n",
			MAX_KEYS);
	}
    }
    else
    {
        fprintf(stderr, "\nCannot open configuration file %s\n", configfile);
	fprintf(stderr, "See the manual page for details.\n");
	fprintf(stderr, "If your TERM environment variable is, let's say,\n");
	fprintf(stderr, "vt102, your configuration file name should be\n");
	fprintf(stderr, ".uitrc.vt102 . Try renaming .uitrc.console ...\n");
	exit(1);
    }

    tty_getsize(&SCREEN_X, &SCREEN_Y);

#ifdef HAVE_LINUX
    if (LinuxConsole)
    {
	screen = (char *)malloc(2 + SCREEN_X * SCREEN_Y);
	if (screen == NULL)
	    fatal("not enough memory");
    }
#endif

    status_init(SCREEN_X, SCREEN_Y - 1, NormalModeHelp, config);

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

    left_panel  = panel_init(SCREEN_Y - 3, SCREEN_X >> 1, 0, 1,
			     temppath[0], edt, &UserHeartAttack, config);
    right_panel = panel_init(SCREEN_Y - 3, SCREEN_X >> 1, SCREEN_X >> 1, 1,
			     temppath[1], edt, &UserHeartAttack, config);

    configuration_end(config);
    tty_getscreen(screen);
    tty_init();

    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,
					temppath[0], 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_init();
	    suspended = OFF;
	    panel_nooptimizations(current_panel);
	    panel_nooptimizations(other_panel);
            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)
		{
		    if (retval = getOpCommand(ConfigKeyInfo->OpName,
					      ConfigKeyInfo->OpCommand, cmdln,
					      current_panel, other_panel, edt))
		    {
			if (retval == 1)
			{
			    sprintf(cmdname, "Executing %s: %s",
				    ConfigKeyInfo->OpName, cmdln);
			    status(cmdname, 0, 0, 0, MSG_WARNING);
			    child_exit_code = start(cmdln,
						    ConfigKeyInfo->OpHide);
			    if (ConfigKeyInfo->OpHide)
			    {
				sprintf(cmdln, "%s/uit.2.%d",
					TempDirectory, getpid());
				stderr_log = fopen(cmdln, "r");
				if (child_exit_code) tty_beep();
				if (stderr_log == NULL)
				   status("*** Can't open stderr log file ***",
					  1, 1, 1, MSG_ERROR);
				else
				{
				    while (fgets(cmdln, SCREEN_X, stderr_log))
				        if (status(cmdln,1,0,0,MSG_ERROR) !=
					    KEY_ENTER)
					    break;
				}
				fclose(stderr_log);
			    }
			    else
			    {
				if (ConfigKeyInfo->OpSaveScreen)
				    tty_getscreen(screen);
				if (FrameDisplay == OFF) need_clrscr = 1;
				if (ConfigKeyInfo->OpPause) wait = 1;
			    }

			    if (child_exit_code == 0 &&
				ConfigKeyInfo->OpNewDir)
			        panel_action(current_panel, act_CHDIR,
					     other_panel,
					     ConfigKeyInfo->OpNewDir, 1);

			    goto restart;
			}
			else
			    continue;
		    }
		    else
		    {
			sprintf(cmdln, "Bad configured command for key %s !",
				ks->key_seq);
			status(cmdln, 1, 1, 1, MSG_ERROR);
			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);
		edit_gettext(edt, cmdln);
		edit_reset(edt);
		edit_puts(edt,
			  strcat(panel_getpath(current_panel, temppath[0],
					       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_SELECTALL,
							 other_panel, NULL, 1);
			    break;

		        case '-':

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

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

			default :

		            panel_nooptimizations(current_panel);
			    panel_nooptimizations(other_panel);
			    start(cmdln, 0);
			    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,
							temppath[0],
							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);
		edit_gettext(edt, cmdln);
		edit_reset(edt);
		edit_puts(edt, strcat(panel_getpath(current_panel,
						    temppath[0],
						    MAX_STATIC_SIZE),
				      PS1));
		edit_eos(edt);
		edit_puts(edt, cmdln);
		edit_update(edt);
		uitclock_status(ON);
		break;

            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_init();
			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);
				tty_getscreen(screen);
				edit_history(edt, EDIT_RECORD);
				edit_reset(edt);
				edit_puts(edt,
					  strcat(panel_getpath(current_panel,
							       temppath[0],
							       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_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_ctrlo;
			    }
			    status(CommandLineModeHelp, 0, 0, 0, MSG_OK);
			    break;

			default:

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

	      end_ctrlo:

		panel_nooptimizations(current_panel);
		panel_nooptimizations(other_panel);
		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_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_FileDisplayMode:

		signals(OFF);
		uitclock_status(OFF);

		for (i = 0; i < repeat_count; i++)
		    panel_action(current_panel, act_MODE, 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;
		}

		strcpy(&cmdln[len], panel_getcurrentfilename(current_panel));

		while (cmdln[len])
		{
		    if (!is_print(cmdln[len]))
		    {
			tty_beep();
			cmdln[len] = 0;
			break;
		    }
		    else
		        edit_putch(edt, cmdln[len++]);
		}

		edit_putch(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);
		edit_gets(edt, "(CHDIR) Change to directory: ", cmdname, NULL);
		panel_action(current_panel, act_CHDIR, other_panel, cmdname,1);
		edit_gettext(edt, cmdln);
		edit_reset(edt);
		edit_puts(edt, strcat(panel_getpath(current_panel,
						    temppath[0],
						    MAX_STATIC_SIZE),
				      PS1));
		edit_eos(edt);
		edit_puts(edt, cmdln);
		edit_update(edt);
		uitclock_status(ON);
		break;

            default:

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

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

#ifdef HAVE_LINUX
    if (LinuxConsole)
    {
	tty_putscreen(screen);
	free(screen);
    }
    else
    {
	tty_defaults();
	tty_clrscr();
    }
#else
    tty_defaults();
    tty_clrscr();
#endif

    return 0;
}
