/* 
   elmo - ELectronic Mail Operator

   Copyright (C) 2002, 2003, 2004 rzyjontko

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; version 2.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software Foundation,
   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  

   ----------------------------------------------------------------------

   handles command loop
   
*/
/****************************************************************************
 *    IMPLEMENTATION HEADERS
 ****************************************************************************/

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/time.h>
#include <unistd.h>
#include <ctype.h>

#include "ecurses.h"
#include "cmd.h"
#include "status.h"
#include "folder.h"
#include "mailreader.h"
#include "error.h"
#include "elmo.h"
#include "box_selection.h"
#include "xmalloc.h"
#include "fetch.h"
#include "confread.h"
#include "compose.h"
#include "sender.h"
#include "attach.h"
#include "abook.h"
#include "read.h"
#include "exec.h"
#include "keymap.h"
#include "hook.h"
#include "choose.h"
#include "ask.h"
#include "bayes.h"
#include "gettext.h"
#include "help.h"
#include "color.h"
#include "interface.h"
#include "debug.h"
#include "smtp.h"
#include "pgp.h"
#include "select.h"

/****************************************************************************
 *    IMPLEMENTATION PRIVATE DEFINITIONS / ENUMERATIONS / SIMPLE TYPEDEFS
 ****************************************************************************/

#define STACK_SIZE 10

#ifndef DEBUG
# define DEBUG 0
#endif

/****************************************************************************
 *    IMPLEMENTATION PRIVATE CLASS PROTOTYPES / EXTERNAL CLASS REFERENCES
 ****************************************************************************/
/****************************************************************************
 *    IMPLEMENTATION PRIVATE STRUCTURES / UTILITY CLASSES
 ****************************************************************************/

struct fd_handler {
        struct fd_handler  *next;
        int                 fd;
        void              (*handler)(int);
};

/****************************************************************************
 *    IMPLEMENTATION REQUIRED EXTERNAL REFERENCES (AVOID)
 ****************************************************************************/
/****************************************************************************
 *    IMPLEMENTATION PRIVATE DATA
 ****************************************************************************/

/* Each keymap is bound to a particular state.  Opening a window usually
   pushes new state on the stack, and closing - pops the state from the top
   of the stack.  Switching between windows works in the same manner.

   Elmo determines action by watching at a keymap bound to the state on the
   top of the stack. */
static cmd_state_t  state_stack[STACK_SIZE];
static cmd_state_t *state_top;

/* Colors used to display messages in echo area. */
static chtype text_color;
static chtype error_color;

/* Lists of handlers for read, and write descriptors used in select call. */
static struct fd_handler *readfd_list  = NULL;
static struct fd_handler *writefd_list = NULL;
/* List of functions to execute on timeout. */
static struct fd_handler *timeout_list = NULL;

/* This is the last character - input from user. */
static int last_char  = 0;

/* We use digits as prefix arguments to functions. */
static int prefix_arg = 0;

/****************************************************************************
 *    INTERFACE DATA
 ****************************************************************************/

/* Echo area window.  It is not static because error.c uses this window
   to display error messages, and read.c uses this window to read the
   input string. */
WINDOW *cmd_win = NULL;

/* These are keymaps, and hooks bindings.  The array is indexed with state
   number. */
keymap_t keymaps[CMD_STATE_COUNT];

/****************************************************************************
 *    IMPLEMENTATION PRIVATE FUNCTION PROTOTYPES
 ****************************************************************************/

static void destroy_keymaps (void);
static void destroy_list (struct fd_handler *list);

/****************************************************************************
 *    IMPLEMENTATION PRIVATE FUNCTIONS
 ****************************************************************************/

static void
quit (void)
{
        destroy_keymaps ();
        destroy_list (readfd_list);
        destroy_list (writefd_list);
        destroy_list (timeout_list);

        readfd_list  = NULL;
        writefd_list = NULL;
        timeout_list = NULL;

        elmo_finish (0);
        exit (EXIT_SUCCESS);
}



static void
init_keymaps (void)
{
        int i;

        for (i = 0; i < CMD_STATE_COUNT; i++)
                keymap_init (keymaps + i);
	keymap_init (&keymap_default);

        for (i = '0'; i < '9'; i++)
                keymap_add (& keymap_default, i, 1, interface_num_window);

        keymap_add (&keymap_default, 12, 0,        interface_redraw);
        keymap_add (&keymap_default, '\t', 0,      interface_next_window);
	keymap_add (&keymap_default, 'q', 0,       cmd_quit);
	keymap_add (&keymap_default, '?', 0,       help_open);
	keymap_add (&keymap_default, 'h', 0,       help_open);
	keymap_add (&keymap_default, KEY_F (1), 0, help_open);
        keymap_add (&keymap_default, 6, 0,         pgp_forget_passphrase);
	
	keymap_add (keymaps + CMD_LIST, KEY_PPAGE, 0, folder_page_prev);
        keymap_add (keymaps + CMD_LIST, KEY_NPAGE, 0, folder_page_next);
        keymap_add (keymaps + CMD_LIST, KEY_HOME, 0,  folder_bar_first);
        keymap_add (keymaps + CMD_LIST, KEY_END, 0,   folder_bar_last);
        keymap_add (keymaps + CMD_LIST, KEY_UP, 0,    folder_bar_prev);
        keymap_add (keymaps + CMD_LIST, KEY_DOWN, 0,  folder_bar_next);
        keymap_add (keymaps + CMD_LIST, KEY_DC, 0,    folder_delete_mail);
        keymap_add (keymaps + CMD_LIST, 'K', 0,       folder_kill_mail);
        keymap_add (keymaps + CMD_LIST, 'd', 0,       folder_sort_date);
        keymap_add (keymaps + CMD_LIST, 't', 0,       folder_sort_threads);
        keymap_add (keymaps + CMD_LIST, 'A', 0,       folder_sort_from);
        keymap_add (keymaps + CMD_LIST, 's', 0,       folder_sort_subject);
        keymap_add (keymaps + CMD_LIST, 'a', 0,       abook_show);
        keymap_add (keymaps + CMD_LIST, '\r', 0,      mailreader_show);
        keymap_add (keymaps + CMD_LIST, 'F', 0,       fetch_open);
        keymap_add (keymaps + CMD_LIST, 'm', 0,       sender_open_new);
        keymap_add (keymaps + CMD_LIST, 'r', 0,       sender_open_reply);
        keymap_add (keymaps + CMD_LIST, 'R', 0,       sender_open_reply_all);
        keymap_add (keymaps + CMD_LIST, 'f', 0,       sender_open_fwd);
        keymap_add (keymaps + CMD_LIST, 'e', 0,       sender_open_edit);
        keymap_add (keymaps + CMD_LIST, '$', 0,       folder_flush);
        keymap_add (keymaps + CMD_LIST, ' ', 0,       folder_toggle_flag);
        keymap_add (keymaps + CMD_LIST, '*', 0,       folder_flag_invert);
        keymap_add (keymaps + CMD_LIST, '+', 0,       folder_flag_all);
        keymap_add (keymaps + CMD_LIST, '-', 0,       folder_unflag_all);
        keymap_add (keymaps + CMD_LIST, '=', 0,       folder_flag_duplicates);
        keymap_add (keymaps + CMD_LIST, 'N', 0,       folder_scroll_down);
        keymap_add (keymaps + CMD_LIST, 'P', 0,       folder_scroll_up);
        keymap_add (keymaps + CMD_LIST, 'c', 0,       folder_recenter);
        keymap_add (keymaps + CMD_LIST, 'M', 0,       folder_move_mail);
        keymap_add (keymaps + CMD_LIST, 'n', 0,       folder_next_unread);
        keymap_add (keymaps + CMD_LIST, 'p', 0,       folder_prev_unread);
        keymap_add (keymaps + CMD_LIST, 'S', 0,       smtp_flush_outbox);
        keymap_add (keymaps + CMD_LIST, 18,  0,       folder_search_backward);
        keymap_add (keymaps + CMD_LIST, 19,  0,       folder_search_forward);

        keymap_add (keymaps + CMD_SELECT_BOX, KEY_PPAGE, 0, box_selection_prev_page);
        keymap_add (keymaps + CMD_SELECT_BOX, KEY_NPAGE, 0, box_selection_next_page);
        keymap_add (keymaps + CMD_SELECT_BOX, KEY_HOME, 0,  box_selection_first);
        keymap_add (keymaps + CMD_SELECT_BOX, KEY_END, 0,   box_selection_last);
        keymap_add (keymaps + CMD_SELECT_BOX, KEY_UP, 0,    box_selection_prev);
        keymap_add (keymaps + CMD_SELECT_BOX, KEY_DOWN, 0,  box_selection_next);
        keymap_add (keymaps + CMD_SELECT_BOX, '\r', 0,      box_selection_hit);
        keymap_add (keymaps + CMD_SELECT_BOX, 18,  0,       box_selection_search_backward);
        keymap_add (keymaps + CMD_SELECT_BOX, 19,  0,       box_selection_search_forward);
  
        keymap_add (keymaps + CMD_ABOOK, KEY_PPAGE, 0, abook_prev_page);
        keymap_add (keymaps + CMD_ABOOK, KEY_NPAGE, 0, abook_next_page);
        keymap_add (keymaps + CMD_ABOOK, KEY_HOME, 0,  abook_first);
        keymap_add (keymaps + CMD_ABOOK, KEY_END, 0,   abook_last);
        keymap_add (keymaps + CMD_ABOOK, KEY_DOWN, 0,  abook_next);
        keymap_add (keymaps + CMD_ABOOK, KEY_UP, 0,    abook_prev);
        keymap_add (keymaps + CMD_ABOOK, KEY_DC, 0,    abook_remove);
        keymap_add (keymaps + CMD_ABOOK, 'n', 0,       abook_change_name);
        keymap_add (keymaps + CMD_ABOOK, 'e', 0,       abook_change_email);
        keymap_add (keymaps + CMD_ABOOK, 'g', 0,       abook_change_groups);
        keymap_add (keymaps + CMD_ABOOK, 's', 0,       abook_change_sex);
        keymap_add (keymaps + CMD_ABOOK, 'o', 0,       abook_change_official);
        keymap_add (keymaps + CMD_ABOOK, 'f', 0,       abook_change_foreign);
        keymap_add (keymaps + CMD_ABOOK, 'a', 0,       abook_insert);
        keymap_add (keymaps + CMD_ABOOK, 'q', 0,       abook_hide);
        keymap_add (keymaps + CMD_ABOOK, ' ', 0,       abook_hit);
        keymap_add (keymaps + CMD_ABOOK, '*', 0,       abook_hit_all);
        keymap_add (keymaps + CMD_ABOOK, '+', 0,       abook_set_all);
        keymap_add (keymaps + CMD_ABOOK, '-', 0,       abook_unset_all);
        keymap_add (keymaps + CMD_ABOOK, 'm', 0,       abook_compose);
        keymap_add (keymaps + CMD_ABOOK, 18,  0,       abook_search_backward);
        keymap_add (keymaps + CMD_ABOOK, 19,  0,       abook_search_forward);

        keymap_add (keymaps + CMD_READ_MAIL, 'q', 0,       mailreader_close);
        keymap_add (keymaps + CMD_READ_MAIL, KEY_PPAGE, 0, mailreader_page_up);
        keymap_add (keymaps + CMD_READ_MAIL, KEY_NPAGE, 0, mailreader_page_down);
        keymap_add (keymaps + CMD_READ_MAIL, KEY_HOME, 0,  mailreader_top);
        keymap_add (keymaps + CMD_READ_MAIL, KEY_END, 0,   mailreader_bottom);
        keymap_add (keymaps + CMD_READ_MAIL, KEY_DOWN, 0,  mailreader_line_down);
        keymap_add (keymaps + CMD_READ_MAIL, KEY_UP, 0,    mailreader_line_up);
        keymap_add (keymaps + CMD_READ_MAIL, 18,   0,      mailreader_search_backward);
        keymap_add (keymaps + CMD_READ_MAIL, 19,   0,      mailreader_search_forward);
        keymap_add (keymaps + CMD_READ_MAIL, 'h', 0,       mailreader_header_switch);
        keymap_add (keymaps + CMD_READ_MAIL, 'b', 0,       abook_add_show);
        keymap_add (keymaps + CMD_READ_MAIL, 'a', 0,       attach_show);
        keymap_add (keymaps + CMD_READ_MAIL, 'r', 0,       sender_open_reply);
        keymap_add (keymaps + CMD_READ_MAIL, 'R', 0,       sender_open_reply_all);
        keymap_add (keymaps + CMD_READ_MAIL, 'f', 0,       sender_open_fwd);

        keymap_add (keymaps + CMD_FETCH, KEY_PPAGE, 0, fetch_prev_page);
        keymap_add (keymaps + CMD_FETCH, KEY_NPAGE, 0, fetch_next_page);
        keymap_add (keymaps + CMD_FETCH, KEY_HOME, 0,  fetch_first);
        keymap_add (keymaps + CMD_FETCH, KEY_END, 0,   fetch_last);
        keymap_add (keymaps + CMD_FETCH, KEY_DOWN, 0,  fetch_next);
        keymap_add (keymaps + CMD_FETCH, KEY_UP, 0,    fetch_prev);
        keymap_add (keymaps + CMD_FETCH, 'q', 0,       fetch_close);
        keymap_add (keymaps + CMD_FETCH, 'f', 0,       fetch_get_mail);
        keymap_add (keymaps + CMD_FETCH, 'd', 0,       fetch_del_mail);
        keymap_add (keymaps + CMD_FETCH, 'r', 0,       fetch_rset);

        keymap_add (keymaps + CMD_ATTACH, KEY_PPAGE, 0, attach_prev_page);
        keymap_add (keymaps + CMD_ATTACH, KEY_NPAGE, 0, attach_next_page);
        keymap_add (keymaps + CMD_ATTACH, KEY_HOME, 0,  attach_first);
        keymap_add (keymaps + CMD_ATTACH, KEY_END, 0,   attach_last);
        keymap_add (keymaps + CMD_ATTACH, KEY_DOWN, 0,  attach_next);
        keymap_add (keymaps + CMD_ATTACH, KEY_UP, 0,    attach_prev);
        keymap_add (keymaps + CMD_ATTACH, 's', 0,       attach_save);
        keymap_add (keymaps + CMD_ATTACH, 'S', 0,       attach_save_all);
        keymap_add (keymaps + CMD_ATTACH, '\r', 0,      attach_hit);
        keymap_add (keymaps + CMD_ATTACH, 'q', 0,       attach_hide);

        keymap_add (keymaps + CMD_ABOOK_ADD, KEY_PPAGE, 0, abook_add_prev_page);
        keymap_add (keymaps + CMD_ABOOK_ADD, KEY_NPAGE, 0, abook_add_next_page);
        keymap_add (keymaps + CMD_ABOOK_ADD, KEY_HOME, 0,  abook_add_first);
        keymap_add (keymaps + CMD_ABOOK_ADD, KEY_END, 0,   abook_add_last);
        keymap_add (keymaps + CMD_ABOOK_ADD, KEY_DOWN, 0,  abook_add_next);
        keymap_add (keymaps + CMD_ABOOK_ADD, KEY_UP, 0,    abook_add_prev);
        keymap_add (keymaps + CMD_ABOOK_ADD, 'q', 0,       abook_add_hide);
        keymap_add (keymaps + CMD_ABOOK_ADD, '\r', 0,      abook_add_hit);

        keymap_add (keymaps + CMD_SENDER, KEY_PPAGE, 0, sender_prev_page);
        keymap_add (keymaps + CMD_SENDER, KEY_NPAGE, 0, sender_next_page);
        keymap_add (keymaps + CMD_SENDER, KEY_HOME, 0,  sender_first);
        keymap_add (keymaps + CMD_SENDER, KEY_END, 0,   sender_last);
        keymap_add (keymaps + CMD_SENDER, KEY_DOWN, 0,  sender_next);
        keymap_add (keymaps + CMD_SENDER, KEY_UP, 0,    sender_prev);
        keymap_add (keymaps + CMD_SENDER, 'q', 0,       sender_close);
        keymap_add (keymaps + CMD_SENDER, 'y', 0,       sender_go);
        keymap_add (keymaps + CMD_SENDER, 'S', 0,       sender_change_smtp);
        keymap_add (keymaps + CMD_SENDER, 'f', 0,       sender_change_from);
        keymap_add (keymaps + CMD_SENDER, 'r', 0,       sender_change_reply_to);
        keymap_add (keymaps + CMD_SENDER, 't', 0,       sender_change_to);
        keymap_add (keymaps + CMD_SENDER, 'c', 0,       sender_change_cc);
        keymap_add (keymaps + CMD_SENDER, 'b', 0,       sender_change_bcc);
        keymap_add (keymaps + CMD_SENDER, 's', 0,       sender_change_subject);
        keymap_add (keymaps + CMD_SENDER, 'a', 0,       sender_add_attachment);
        keymap_add (keymaps + CMD_SENDER, KEY_DC, 0,    sender_delete_attachment);
        keymap_add (keymaps + CMD_SENDER, 'T', 0,       sender_change_type);

        keymap_add (keymaps + CMD_ASK, KEY_HOME, 0,  choose_first);
        keymap_add (keymaps + CMD_ASK, KEY_END, 0,   choose_last);
        keymap_add (keymaps + CMD_ASK, KEY_DOWN, 0,  choose_next);
        keymap_add (keymaps + CMD_ASK, KEY_RIGHT, 0, choose_next);
        keymap_add (keymaps + CMD_ASK, KEY_UP, 0,    choose_prev);
        keymap_add (keymaps + CMD_ASK, KEY_LEFT, 0,  choose_prev);

        keymap_add (keymaps + CMD_HELP, KEY_HOME, 0,  help_beg);
        keymap_add (keymaps + CMD_HELP, KEY_END, 0,   help_end);
        keymap_add (keymaps + CMD_HELP, KEY_DOWN, 0,  help_scroll_down);
        keymap_add (keymaps + CMD_HELP, KEY_UP, 0,    help_scroll_up);
        keymap_add (keymaps + CMD_HELP, KEY_NPAGE, 0, help_next_page);
        keymap_add (keymaps + CMD_HELP, KEY_PPAGE, 0, help_prev_page);
        keymap_add (keymaps + CMD_HELP, 'q', 0,       help_close);
        keymap_add (keymaps + CMD_HELP, 27, 0,        help_close);
        keymap_add (keymaps + CMD_HELP,  7, 0,        help_close);
        keymap_add (keymaps + CMD_HELP, '?', 0,       cmd_nothing);

        keymap_add (keymaps + CMD_READ, 7, 0,             read_abort);
        keymap_add (keymaps + CMD_READ, '\r', 0,          read_accept);
        keymap_add (keymaps + CMD_READ, KEY_HOME, 0,      read_begin);
        keymap_add (keymaps + CMD_READ, 1, 0,             read_begin);
        keymap_add (keymaps + CMD_READ, KEY_END, 0,       read_end);
        keymap_add (keymaps + CMD_READ, 5, 0,             read_end);
        keymap_add (keymaps + CMD_READ, KEY_LEFT, 0,      read_backward_char);
        keymap_add (keymaps + CMD_READ, 2, 0,             read_backward_char);
        keymap_add (keymaps + CMD_READ, KEY_RIGHT, 0,     read_forward_char);
        keymap_add (keymaps + CMD_READ, 6, 0,             read_forward_char);
        keymap_add (keymaps + CMD_READ, 'b', 1,           read_backward_word);
        keymap_add (keymaps + CMD_READ, 'f', 1,           read_forward_word);
        keymap_add (keymaps + CMD_READ, 4, 0,             read_del_char_fwd);
        keymap_add (keymaps + CMD_READ, KEY_DC, 0,        read_del_char_fwd);
        keymap_add (keymaps + CMD_READ, KEY_BACKSPACE, 0, read_del_char_back);
        keymap_add (keymaps + CMD_READ, '\b', 0,          read_del_char_back);
        keymap_add (keymaps + CMD_READ, 127, 0,           read_del_char_back);
        keymap_add (keymaps + CMD_READ, 'd', 1,           read_del_word_fwd);
        keymap_add (keymaps + CMD_READ, KEY_BACKSPACE, 1, read_del_word_back);
        keymap_add (keymaps + CMD_READ, '\b', 1,          read_del_word_back);
        keymap_add (keymaps + CMD_READ, 127, 1,           read_del_word_back);
        keymap_add (keymaps + CMD_READ, 11, 0,            read_kill_line);
        keymap_add (keymaps + CMD_READ, '\t', 0,          read_complete);
        keymap_add (keymaps + CMD_READ, 14, 0,            read_choose_next);
        keymap_add (keymaps + CMD_READ, KEY_DOWN, 0,      read_choose_next);
        keymap_add (keymaps + CMD_READ, 16, 0,            read_choose_prev);
        keymap_add (keymaps + CMD_READ, KEY_UP, 0,        read_choose_prev);

        keymap_add (keymaps + CMD_SEARCH, 18,   0, select_search_backward);
        keymap_add (keymaps + CMD_SEARCH, 19,   0, select_search_forward);
        keymap_add (keymaps + CMD_SEARCH, '\b', 0, select_search_backspace);
        keymap_add (keymaps + CMD_SEARCH, 127,  0, select_search_backspace);
        keymap_add (keymaps + CMD_SEARCH, KEY_BACKSPACE, 0, select_search_backspace);

#if DEBUG
        keymap_add (keymaps + CMD_DEBUG, KEY_HOME, 0,  debug_first);
        keymap_add (keymaps + CMD_DEBUG, KEY_END, 0,   debug_last);
        keymap_add (keymaps + CMD_DEBUG, KEY_UP, 0,    debug_prev);
        keymap_add (keymaps + CMD_DEBUG, KEY_DOWN, 0,  debug_next);
        keymap_add (keymaps + CMD_DEBUG, KEY_PPAGE, 0, debug_prev_page);
        keymap_add (keymaps + CMD_DEBUG, KEY_NPAGE, 0, debug_next_page);
        keymap_add (keymaps + CMD_DEBUG, ' ', 0,       debug_show);
#endif
}


static void
destroy_keymaps (void)
{
        int i;

        for (i = 0; i < CMD_STATE_COUNT; i++){
                keymap_destroy (keymaps + i);
        }
        keymap_destroy (&keymap_default);
}


static void
destroy_list (struct fd_handler *list)
{
        if (list == NULL)
                return;

        destroy_list (list->next);
        xfree (list);
}



static void
key_action (int nothing)
{
        int   meta;
        int   c;
        char *arg;

        meta = 0;
        c    = wgetch (cmd_win);
        if (c == 27){
                meta = 1;
                c    = wgetch (cmd_win);
        }

        last_char = c;
        
        if (c == ':'){
                arg = read_argument (": ", NULL, COMPLETE_FUNS, HIDE_NO);
                if (arg)
                        exec_run_name (arg);
                return;
        }

        if (! meta && isdigit (c)){
                prefix_arg = c - '0';
                return;
        }

        if (keymap_action (keymaps + *state_top, c, meta))
                fprintf (stderr, "%c", '\a');

        prefix_arg = 0;
}



/* This file is generated by interface.pl script from interface.desc,
   and inc.in. */
static WINDOW *interface_init (void);
#include "cmd.inc"

static void
init (void)
{
        cmd_win = interface_init ();

        keypad (cmd_win, TRUE);
        meta (cmd_win, TRUE);

        init_keymaps ();

        state_top  = state_stack;
        *state_top = CMD_LIST;

        cmd_add_readfd_handler (0, key_action);
}



static void
put_fds (fd_set *set, struct fd_handler *list)
{
        if (list == NULL)
                return;

        FD_SET (list->fd, set);
        put_fds (set, list->next);
}



/* The reversed order of taking actions is *very* important.  The action
   may change the list by removing or adding new elements. */
static void
take_actions (fd_set *set, struct fd_handler *list)
{
        if (list == NULL)
                return;

        take_actions (set, list->next);

        if (set == NULL || FD_ISSET (list->fd, set))
                list->handler (list->fd);
}



static struct fd_handler *
list_rem (struct fd_handler *list, int fd)
{
        struct fd_handler *result;
        
        if (list == NULL)
                return NULL;

        if (list->fd != fd){
                list->next = list_rem (list->next, fd);
                return list;
        }

        result = list->next;
        xfree (list);
        return result;
}


/****************************************************************************
 *    INTERFACE FUNCTIONS
 ****************************************************************************/


void
cmd_text_color (void)
{
        wattrset (cmd_win, text_color);
}



void
cmd_error_color (void)
{
        wattrset (cmd_win, error_color);
}



void
cmd_init (void)
{
        init ();
}



void
cmd_quit (void)
{
        exec_t *exec;

        exec = exec_lookup_fun (cmd_quit);

        hook_execute (exec->hook);
        quit ();
}


void
cmd_nothing (void)
{
        
}


void
cmd_state_push (cmd_state_t requested_state)
{
        if (state_top == state_stack + STACK_SIZE){
                error_ (0, _("state stack overflow, please send bug report"));
        }
        state_top++;
        *state_top = requested_state;
}


int
cmd_state_pop (void)
{
        if (state_top == state_stack){
                error_ (0, _("state stack underflow, "
                             "please send bug report"));
        }
        state_top--;
        return 0;
}



cmd_state_t
cmd_state_get (int from_top)
{
        return *(state_top - from_top);
}



void
cmd_add_writefd_handler (int fd, void (*handler)(int))
{
        struct fd_handler *elem = xmalloc (sizeof (struct fd_handler));

        elem->fd      = fd;
        elem->handler = handler;
        elem->next    = writefd_list;
        writefd_list  = elem;
}



void
cmd_add_readfd_handler (int fd, void (*handler)(int))
{
        struct fd_handler *elem = xmalloc (sizeof (struct fd_handler));

        elem->fd      = fd;
        elem->handler = handler;
        elem->next    = readfd_list;
        readfd_list   = elem;
}



void
cmd_add_timeout_handler (void (*handler)(int))
{
        struct fd_handler *elem = xmalloc (sizeof (struct fd_handler));

        elem->fd      = 0;
        elem->handler = handler;
        elem->next    = timeout_list;
        timeout_list  = elem;
}



void
cmd_del_writefd_handler (int fd)
{
        writefd_list = list_rem (writefd_list, fd);
}



void
cmd_del_readfd_handler (int fd)
{
        readfd_list = list_rem (readfd_list, fd);
}



void
cmd_read_loop (void)
{
        struct timeval tv;
        fd_set readfds;
        fd_set writefds;
        int    ret;

        interface_redraw ();
  
        cmd_after_startup ();
        
        while (1){

                doupdate ();

                tv.tv_sec  = 0;
                tv.tv_usec = 3000;

                FD_ZERO (& readfds);
                FD_ZERO (& writefds);
                put_fds (& readfds, readfd_list);
                put_fds (& writefds, writefd_list);

                ret = select (FD_SETSIZE, & readfds, & writefds, NULL, & tv);

                if (ret > 0){
                        take_actions (& readfds, readfd_list);
                        take_actions (& writefds, writefd_list);
                }
                else if (ret == 0){
                        take_actions (NULL, timeout_list);
                }
                else {
                        error_critical (EXIT_FAILURE, errno, "select");
                }
        }
}


void
cmd_choose (void)
{
        int meta;
        int c;
  
        while (1){

                doupdate ();
    
                meta = 0;
                c    = wgetch (cmd_win);
                if (c == 27){
                        meta = 1;
                        c    = wgetch (cmd_win);
                }
                if (c == '\r'){
                        break;
                }
                if (c == 7){
                        choose_cancel ();
                        break;
                }
                keymap_action (keymaps + CMD_ASK, c, meta);
        }

        interface_redraw ();
}


int
cmd_last_char (void)
{
        return last_char;
}


int
cmd_prefix_arg (void)
{
        return prefix_arg;
}


void
cmd_after_startup (void)
{
        exec_t *exec;

        exec = exec_lookup_fun (cmd_after_startup);

        hook_execute (exec->hook);
}

/****************************************************************************
 *    INTERFACE CLASS BODIES
 ****************************************************************************/
/****************************************************************************
 *
 *    END MODULE cmd.c
 *
 ****************************************************************************/


syntax highlighted by Code2HTML, v. 0.9.1