Ticket #4196: InstructionStation_v3.8.patch

File InstructionStation_v3.8.patch, 71.6 KB (added by psprint, 3 years ago)
  • lib/global.h

    From 4d62b9b1d16ff99a9d9cbc6033d0b58a24c2696b Mon Sep 17 00:00:00 2001
    From: Sebastian Gniazdowski <sgniazdowski@gmail.com>
    Date: Wed, 17 Feb 2021 15:02:15 -0600
    Subject: CLI window for MCEdit
    
    ---
     lib/global.h               |   3 +
     lib/keybind.c              |   1 +
     lib/keybind.h              |   1 +
     lib/tty/key.c              |  10 +-
     lib/widget/wtools.c        |  40 +-
     lib/widget/wtools.h        |   4 +
     misc/mc.default.keymap     |   1 +
     misc/mc.emacs.keymap       |   1 +
     src/editor/Makefile.am     |   1 +
     src/editor/edit-impl.h     |  24 +-
     src/editor/edit.c          |  27 +-
     src/editor/editcmd.c       |  41 +-
     src/editor/editdraw.c      |   1 -
     src/editor/editmenu.c      |   3 +
     src/editor/editwidget.c    | 550 +++++++++++-----------
     src/editor/editwidget.h    |   3 +
     src/editor/instr_station.c | 902 +++++++++++++++++++++++++++++++++++++
     src/editor/instr_station.h |  92 ++++
     src/keybind-defaults.c     |   1 +
     19 files changed, 1416 insertions(+), 290 deletions(-)
     create mode 100644 src/editor/instr_station.c
     create mode 100644 src/editor/instr_station.h
    
    diff --git a/lib/global.h b/lib/global.h
    index c60063faa..05d88cbb4 100644
    a b  
    103103#include "lib/logging.h" 
    104104#endif 
    105105 
     106#define has_flag(x,y) (((x) & (y)) != 0) 
     107#define set_flag_in(x,y) ((x) |= (y)) 
     108 
    106109/* Just for keeping Your's brains from invention a proper size of the buffer :-) */ 
    107110#define BUF_10K 10240L 
    108111#define BUF_8K  8192L 
  • lib/keybind.c

    diff --git a/lib/keybind.c b/lib/keybind.c
    index abd44d3e2..40e25466c 100644
    a b static name_keymap_t command_names[] = { 
    290290    ADD_KEYMAP_NAME (EditMail), 
    291291    ADD_KEYMAP_NAME (ParagraphFormat), 
    292292    ADD_KEYMAP_NAME (MatchBracket), 
     293    ADD_KEYMAP_NAME (InstructionStation), 
    293294    ADD_KEYMAP_NAME (ExternalCommand), 
    294295    ADD_KEYMAP_NAME (MacroStartRecord), 
    295296    ADD_KEYMAP_NAME (MacroStopRecord), 
  • lib/keybind.h

    diff --git a/lib/keybind.h b/lib/keybind.h
    index af019df09..e99181ec6 100644
    a b enum 
    314314    CK_SyntaxOnOff, 
    315315    CK_SyntaxChoose, 
    316316    CK_InsertLiteral, 
     317    CK_InstructionStation, 
    317318    CK_ExternalCommand, 
    318319    CK_Date, 
    319320    CK_EditMail, 
  • lib/tty/key.c

    diff --git a/lib/tty/key.c b/lib/tty/key.c
    index 4abfc71d7..8876fbf82 100644
    a b tty_get_event (struct Gpm_Event *event, gboolean redo_event, gboolean block) 
    20312031            time_out.tv_sec = 0; 
    20322032            time_out.tv_usec = 0; 
    20332033        } 
    2034  
     2034        else if (time_addr == NULL) 
     2035        { 
     2036            /* Standard timeout to call GLib main loop */ 
     2037            time_addr = &time_out; 
     2038            time_addr->tv_sec = 0; 
     2039            time_addr->tv_usec = 200000; 
     2040        } 
    20352041        tty_enable_interrupt_key (); 
    20362042        flag = select (nfd, &select_set, NULL, NULL, time_addr); 
    20372043        tty_disable_interrupt_key (); 
    tty_get_event (struct Gpm_Event *event, gboolean redo_event, gboolean block) 
    20432049         */ 
    20442050        if (flag == 0) 
    20452051        { 
     2052            /* Provide CPU time to GLib main loop's default context */ 
     2053            g_main_context_iteration (NULL, FALSE); 
    20462054            if (redo_event) 
    20472055                return EV_MOUSE; 
    20482056            if (!block || tty_got_winch ()) 
  • lib/widget/wtools.c

    diff --git a/lib/widget/wtools.c b/lib/widget/wtools.c
    index 5c5dc4487..bc5937111 100644
    a b bg_message (int dummy, int *flags, char *title, const char *text) 
    184184static char * 
    185185fg_input_dialog_help (const char *header, const char *text, const char *help, 
    186186                      const char *history_name, const char *def_text, gboolean strip_password, 
    187                       input_complete_t completion_flags) 
     187                      input_complete_t completion_flags, quick_widget_t * add_w) 
    188188{ 
    189189    char *p_text; 
    190190    char histname[64] = "inp|"; 
    fg_input_dialog_help (const char *header, const char *text, const char *help, 
    209209    } 
    210210 
    211211    { 
    212         quick_widget_t quick_widgets[] = { 
     212        int i = 1, norm_i = 0, add_i = 0; 
     213        quick_widget_t quick_widgets[15] = { 
    213214            /* *INDENT-OFF* */ 
    214215            QUICK_LABELED_INPUT (p_text, input_label_above, def_text, histname, &my_str, 
    215                                  NULL, is_passwd, strip_password, completion_flags), 
    216             QUICK_BUTTONS_OK_CANCEL, 
    217             QUICK_END 
     216                                 NULL, is_passwd, strip_password, completion_flags) 
    218217            /* *INDENT-ON* */ 
    219218        }; 
    220  
     219        quick_widget_t press_end[] = { QUICK_BUTTONS_OK_CANCEL, QUICK_END }; 
    221220        quick_dialog_t qdlg = { 
    222221            -1, -1, COLS / 2, header, 
    223222            help, quick_widgets, NULL, NULL 
    224223        }; 
    225224 
     225        /* Handling of additional widgets optionally passed via last argument `add_w` */ 
     226 
     227        /* Copy/append any extra widgets (max 10) */ 
     228        while (add_w != NULL && add_w[add_i].widget_type != quick_end && i < 15) 
     229            quick_widgets[i++] = add_w[add_i++]; 
     230 
     231        /* Copy normal buttons */ 
     232        while (norm_i < sizeof (press_end) / sizeof (quick_widget_t) && i < 15) 
     233            quick_widgets[i++] = press_end[norm_i++]; 
     234 
    226235        ret = quick_dialog (&qdlg); 
    227236    } 
    228237 
    input_dialog_help (const char *header, const char *text, const char *help, 
    479488        { 
    480489            void *p; 
    481490            char *(*f) (const char *, const char *, const char *, const char *, const char *, 
    482                         gboolean, input_complete_t); 
     491                        gboolean, input_complete_t, quick_widget_t * add_w); 
    483492        } func; 
    484493        func.f = fg_input_dialog_help; 
    485         return wtools_parent_call_string (func.p, 7, 
     494        return wtools_parent_call_string (func.p, 8, 
    486495                                          strlen (header), header, strlen (text), 
    487496                                          text, strlen (help), help, 
    488497                                          strlen (history_name), history_name, 
    489498                                          strlen (def_text), def_text, 
    490499                                          sizeof (gboolean), strip_password, 
    491                                           sizeof (input_complete_t), completion_flags); 
     500                                          sizeof (input_complete_t), completion_flags, 
     501                                          sizeof (quick_widget_t *), NULL); 
    492502    } 
    493503    else 
    494504#endif /* ENABLE_BACKGROUND */ 
    495505        return fg_input_dialog_help (header, text, help, history_name, def_text, strip_password, 
    496                                      completion_flags); 
     506                                     completion_flags, NULL); 
    497507} 
    498508 
    499509/* --------------------------------------------------------------------------------------------- */ 
    input_dialog (const char *header, const char *text, const char *history_name, co 
    509519 
    510520/* --------------------------------------------------------------------------------------------- */ 
    511521 
     522char * 
     523input_dialog_ext (const char *header, const char *text, const char *history_name, 
     524                  const char *def_text, input_complete_t completion_flags, quick_widget_t * add_w) 
     525{ 
     526    return fg_input_dialog_help (header, text, "[Input Line Keys]", history_name, def_text, FALSE, 
     527                                 completion_flags, add_w); 
     528} 
     529 
     530/* --------------------------------------------------------------------------------------------- */ 
     531 
    512532char * 
    513533input_expand_dialog (const char *header, const char *text, 
    514534                     const char *history_name, const char *def_text, 
  • lib/widget/wtools.h

    diff --git a/lib/widget/wtools.h b/lib/widget/wtools.h
    index cd0bc3253..d5a39971b 100644
    a b struct simple_status_msg_t 
    6565char *input_dialog (const char *header, const char *text, 
    6666                    const char *history_name, const char *def_text, 
    6767                    input_complete_t completion_flags); 
     68/* The input dialogs */ 
     69char *input_dialog_ext (const char *header, const char *text, 
     70                        const char *history_name, const char *def_text, 
     71                        input_complete_t completion_flags, quick_widget_t * add_w); 
    6872char *input_dialog_help (const char *header, const char *text, const char *help, 
    6973                         const char *history_name, const char *def_text, gboolean strip_password, 
    7074                         input_complete_t completion_flags); 
  • misc/mc.default.keymap

    diff --git a/misc/mc.default.keymap b/misc/mc.default.keymap
    index 2931ddd0a..af2ddc59d 100644
    a b Sort = alt-t 
    350350Mail = alt-m 
    351351ParagraphFormat = alt-p 
    352352MatchBracket = alt-b 
     353InstructionStation = alt-i 
    353354ExternalCommand = alt-u 
    354355UserMenu = f11 
    355356Menu = f9 
  • misc/mc.emacs.keymap

    diff --git a/misc/mc.emacs.keymap b/misc/mc.emacs.keymap
    index 7cc305db7..e0e1a9173 100644
    a b Sort = alt-t 
    349349# Mail = 
    350350ParagraphFormat = alt-p 
    351351# MatchBracket = 
     352InstructionStation = alt-i 
    352353ExternalCommand = alt-u 
    353354UserMenu = f11 
    354355Menu = f9 
  • src/editor/Makefile.am

    diff --git a/src/editor/Makefile.am b/src/editor/Makefile.am
    index 235ed76af..0ac4b4af7 100644
    a b libedit_la_SOURCES = \ 
    1818        editmenu.c \ 
    1919        editoptions.c \ 
    2020        editwidget.c editwidget.h \ 
     21        instr_station.c instr_station.h \ 
    2122        etags.c etags.h \ 
    2223        format.c \ 
    2324        syntax.c 
  • src/editor/edit-impl.h

    diff --git a/src/editor/edit-impl.h b/src/editor/edit-impl.h
    index 3ad04dbea..8f8dce2f1 100644
    a b typedef enum 
    8787    LB_MAC 
    8888} LineBreaks; 
    8989 
     90typedef enum 
     91{ 
     92    EDIT_DO_INIT_BASE_CLASS = 1 << 0, 
     93 
     94} edit_init_flags_t; 
     95 
     96typedef enum instr_stn_flags 
     97{ 
     98    INSTR_STN_NO_FLAGS = 0, 
     99    INSTR_STN_NO_STDIN = 1 << 17, 
     100    INSTR_STN_NO_PROMPT = 1 << 18 
     101} instr_stn_flags_t; 
     102 
    90103typedef enum 
    91104{ 
    92105    EDIT_QUICK_SAVE = 0, 
    extern char *edit_window_state_char; 
    135148extern char *edit_window_close_char; 
    136149 
    137150/*** declarations of public functions ************************************************************/ 
     151cb_ret_t edit_dialog_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data); 
     152cb_ret_t edit_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data); 
     153cb_ret_t edit_dialog_command_execute (WDialog * h, long command, void *data); 
     154 
     155void edit_mouse_callback (Widget * w, mouse_msg_t msg, mouse_event_t * event); 
    138156 
    139157gboolean edit_add_window (WDialog * h, int y, int x, int lines, int cols, 
    140158                          const vfs_path_t * f, long fline); 
    void edit_move_to_prev_col (WEdit * edit, off_t p); 
    158176long edit_get_col (const WEdit * edit); 
    159177void edit_update_curs_row (WEdit * edit); 
    160178void edit_update_curs_col (WEdit * edit); 
     179gboolean edit_newline_end_check_only (const WEdit * edit); 
    161180void edit_find_bracket (WEdit * edit); 
    162181gboolean edit_reload_line (WEdit * edit, const vfs_path_t * filename_vpath, long line); 
    163182void edit_set_codeset (WEdit * edit); 
    void edit_delete_line (WEdit * edit); 
    169188 
    170189int edit_delete (WEdit * edit, gboolean byte_delete); 
    171190int edit_backspace (WEdit * edit, gboolean byte_delete); 
     191void edit_insert_string (WEdit * edit, char *str_text, gsize len); 
    172192void edit_insert (WEdit * edit, int c); 
    173193void edit_insert_over (WEdit * edit); 
    174194void edit_cursor_move (WEdit * edit, off_t increment); 
    char *edit_get_write_filter (const vfs_path_t * write_name_vpath, 
    182202gboolean edit_save_confirm_cmd (WEdit * edit); 
    183203gboolean edit_save_as_cmd (WEdit * edit); 
    184204WEdit *edit_init (WEdit * edit, int y, int x, int lines, int cols, 
    185                   const vfs_path_t * filename_vpath, long line); 
     205                  const vfs_path_t * filename_vpath, long line, edit_init_flags_t flags); 
    186206gboolean edit_clean (WEdit * edit); 
    187207gboolean edit_ok_to_exit (WEdit * edit); 
    188208gboolean edit_load_cmd (WDialog * h); 
    void edit_move_to_line (WEdit * e, long line); 
    236256void edit_move_display (WEdit * e, long line); 
    237257void edit_word_wrap (WEdit * edit); 
    238258int edit_sort_cmd (WEdit * edit); 
     259gboolean edit_add_instr_stn_window (WDialog * h, instr_stn_flags_t flags, void *data); 
     260 
    239261int edit_ext_cmd (WEdit * edit); 
    240262 
    241263int edit_store_macro_cmd (WEdit * edit); 
  • src/editor/edit.c

    diff --git a/src/editor/edit.c b/src/editor/edit.c
    index edda1f832..56653700b 100644
    a b edit_modification (WEdit * edit) 
    621621{ 
    622622    edit->caches_valid = FALSE; 
    623623 
    624     /* raise lock when file modified */ 
    625     if (!edit->modified && !edit->delete_file) 
     624    /* raise lock when file is about to be modified */ 
     625    if (edit->filename_vpath && !edit->modified && !edit->delete_file) 
    626626        edit->locked = lock_file (edit->filename_vpath); 
    627627    edit->modified = 1; 
    628628} 
    edit_insert_file (WEdit * edit, const vfs_path_t * filename_vpath) 
    20752075 
    20762076WEdit * 
    20772077edit_init (WEdit * edit, int y, int x, int lines, int cols, const vfs_path_t * filename_vpath, 
    2078            long line) 
     2078           long line, edit_init_flags_t flags) 
    20792079{ 
    20802080    gboolean to_free = FALSE; 
    20812081 
    edit_init (WEdit * edit, int y, int x, int lines, int cols, const vfs_path_t * f 
    20972097        edit->fullscreen = fullscreen; 
    20982098        edit->loc_prev = loc_prev; 
    20992099    } 
    2100     else 
     2100    if (has_flag (flags, EDIT_DO_INIT_BASE_CLASS) || edit == NULL) 
    21012101    { 
    21022102        Widget *w; 
    2103         edit = g_malloc0 (sizeof (WEdit)); 
    2104         to_free = TRUE; 
     2103        if (edit == NULL) 
     2104        { 
     2105            edit = g_malloc0 (sizeof (WEdit)); 
     2106            to_free = TRUE; 
     2107        } 
    21052108 
    21062109        w = WIDGET (edit); 
    21072110        widget_init (w, y, x, lines, cols, NULL, NULL); 
    edit_reload_line (WEdit * edit, const vfs_path_t * filename_vpath, long line) 
    22382241    e->fullscreen = edit->fullscreen; 
    22392242    e->loc_prev = edit->loc_prev; 
    22402243 
    2241     if (edit_init (e, w->y, w->x, w->lines, w->cols, filename_vpath, line) == NULL) 
     2244    if (edit_init (e, w->y, w->x, w->lines, w->cols, filename_vpath, line, 0) == NULL) 
    22422245    { 
    22432246        g_free (e); 
    22442247        return FALSE; 
    edit_push_redo_action (WEdit * edit, long c) 
    24952498        edit->redo_stack_bottom = edit->redo_stack_pointer = 0; 
    24962499} 
    24972500 
     2501/* --------------------------------------------------------------------------------------------- */ 
     2502 
     2503void 
     2504edit_insert_string (WEdit * edit, char *str_text, gsize len) 
     2505{ 
     2506    size_t i; 
     2507    for (i = 0; i < len; i++) 
     2508        edit_insert (edit, str_text[i]); 
     2509} 
     2510 
    24982511/* --------------------------------------------------------------------------------------------- */ 
    24992512/** 
    25002513   Basic low level single character buffer alterations and movements at the cursor. 
  • src/editor/editcmd.c

    diff --git a/src/editor/editcmd.c b/src/editor/editcmd.c
    index 0d2caa923..080bfad24 100644
    a b edit_complete_word_insert_recoded_completion (WEdit * edit, char *completion, gs 
    15291529/*** public functions ****************************************************************************/ 
    15301530/* --------------------------------------------------------------------------------------------- */ 
    15311531 
     1532gboolean 
     1533edit_newline_end_check_only (const WEdit * e) 
     1534{ 
     1535    const edit_buffer_t *buf = &e->buffer; 
     1536    return (buf->size > 0 && edit_buffer_get_byte (buf, buf->size - 1) == '\n'); 
     1537} 
     1538 
     1539/* --------------------------------------------------------------------------------------------- */ 
     1540 
    15321541void 
    15331542edit_refresh_cmd (void) 
    15341543{ 
    edit_close_cmd (WEdit * edit) 
    22512260        group_remove_widget (w); 
    22522261        widget_destroy (w); 
    22532262 
    2254         if (edit_widget_is_editor (CONST_WIDGET (g->current->data))) 
     2263        if (edit_widget_is_editor (CONST_WIDGET (g->current->data)) 
     2264            || edit_widget_is_cli (CONST_WIDGET (g->current->data))) 
    22552265            edit = (WEdit *) (g->current->data); 
    22562266        else 
    22572267        { 
     2268            /* Look for a file window or a CLI window */ 
    22582269            edit = find_editor (DIALOG (g)); 
     2270            if (!edit) 
     2271                edit = find_cli (DIALOG (g)); 
    22592272            if (edit != NULL) 
    22602273                widget_select (w); 
    22612274        } 
    edit_ext_cmd (WEdit * edit) 
    32253238{ 
    32263239    char *exp, *tmp, *tmp_edit_temp_file; 
    32273240    int e; 
     3241    gboolean run_in_cli = FALSE; 
     3242 
     3243    quick_widget_t chbox[3] = { QUICK_SEPARATOR (TRUE), 
     3244        QUICK_CHECKBOX ("&Open in CLI window", &run_in_cli, NULL), 
     3245        QUICK_END 
     3246    }; 
    32283247 
    32293248    exp = 
    3230         input_dialog (_("Paste output of external command"), 
    3231                       _("Enter shell command(s):"), MC_HISTORY_EDIT_PASTE_EXTCMD, INPUT_LAST_TEXT, 
    3232                       INPUT_COMPLETE_FILENAMES | INPUT_COMPLETE_VARIABLES | INPUT_COMPLETE_USERNAMES 
    3233                       | INPUT_COMPLETE_HOSTNAMES | INPUT_COMPLETE_CD | INPUT_COMPLETE_COMMANDS | 
    3234                       INPUT_COMPLETE_SHELL_ESC); 
     3249        input_dialog_ext (_("Paste output of external command"), 
     3250                          _("Enter shell command(s):"), MC_HISTORY_EDIT_PASTE_EXTCMD, 
     3251                          INPUT_LAST_TEXT, 
     3252                          INPUT_COMPLETE_FILENAMES | INPUT_COMPLETE_VARIABLES | 
     3253                          INPUT_COMPLETE_USERNAMES | INPUT_COMPLETE_HOSTNAMES | INPUT_COMPLETE_CD | 
     3254                          INPUT_COMPLETE_COMMANDS | INPUT_COMPLETE_SHELL_ESC, chbox); 
    32353255 
    32363256    if (!exp) 
    32373257        return 1; 
    32383258 
     3259    /* Should start a CLI window with the command ? */ 
     3260    if (run_in_cli) 
     3261    { 
     3262        edit_add_instr_stn_window (DIALOG (WIDGET (edit)->owner), 
     3263                                   INSTR_STN_NO_STDIN | INSTR_STN_NO_PROMPT, exp); 
     3264        g_free (exp); 
     3265        return 0; 
     3266    } 
     3267 
    32393268    tmp_edit_temp_file = mc_config_get_full_path (EDIT_HOME_TEMP_FILE); 
    32403269    tmp = g_strconcat (exp, " > ", tmp_edit_temp_file, (char *) NULL); 
    32413270    g_free (tmp_edit_temp_file); 
  • src/editor/editdraw.c

    diff --git a/src/editor/editdraw.c b/src/editor/editdraw.c
    index 248128b68..d2fddc13d 100644
    a b edit_draw_this_line (WEdit * edit, off_t b, long row, long start_col, long end_c 
    820820    } 
    821821 
    822822    p->ch = 0; 
    823  
    824823    print_to_widget (edit, row, start_col, start_col_real, end_col, line, line_stat, book_mark); 
    825824} 
    826825 
  • src/editor/editmenu.c

    diff --git a/src/editor/editmenu.c b/src/editor/editmenu.c
    index 489893849..02bf43aa3 100644
    a b create_window_menu (void) 
    236236    entries = g_list_prepend (entries, menu_entry_create (_("&Next"), CK_WindowNext)); 
    237237    entries = g_list_prepend (entries, menu_entry_create (_("&Previous"), CK_WindowPrev)); 
    238238    entries = g_list_prepend (entries, menu_entry_create (_("&List..."), CK_WindowList)); 
     239    entries = 
     240        g_list_prepend (entries, 
     241                        menu_entry_create (_("Instr&uction Station"), CK_InstructionStation)); 
    239242 
    240243    return g_list_reverse (entries); 
    241244} 
  • src/editor/editwidget.c

    diff --git a/src/editor/editwidget.c b/src/editor/editwidget.c
    index 18ac00e66..2e49fe3ed 100644
    a b  
    6767#ifdef HAVE_ASPELL 
    6868#include "spell.h" 
    6969#endif 
     70#include "src/editor/instr_station.h" 
    7071 
    7172/*** global variables ****************************************************************************/ 
    7273 
    static unsigned int edit_dlg_init_refcounter = 0; 
    8586 
    8687/*** file scope functions ************************************************************************/ 
    8788 
    88 static cb_ret_t edit_dialog_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, 
    89                                       void *data); 
    90  
    9189/* --------------------------------------------------------------------------------------------- */ 
    9290/** 
    9391 * Init the 'edit' subsystem 
    edit_get_title (const WDialog * h, size_t len) 
    376374    return g_strconcat (_("Edit: "), modified, file_label, (char *) NULL); 
    377375} 
    378376 
    379 /* --------------------------------------------------------------------------------------------- */ 
    380  
    381 static cb_ret_t 
    382 edit_dialog_command_execute (WDialog * h, long command) 
    383 { 
    384     WGroup *g = GROUP (h); 
    385     Widget *wh = WIDGET (h); 
    386     cb_ret_t ret = MSG_HANDLED; 
    387  
    388     switch (command) 
    389     { 
    390     case CK_EditNew: 
    391         edit_add_window (h, wh->y + 1, wh->x, wh->lines - 2, wh->cols, NULL, 0); 
    392         break; 
    393     case CK_EditFile: 
    394         edit_load_cmd (h); 
    395         break; 
    396     case CK_History: 
    397         edit_load_file_from_history (h); 
    398         break; 
    399     case CK_EditSyntaxFile: 
    400         edit_load_syntax_file (h); 
    401         break; 
    402     case CK_EditUserMenu: 
    403         edit_load_menu_file (h); 
    404         break; 
    405     case CK_Close: 
    406         /* if there are no opened files anymore, close MC editor */ 
    407         if (edit_widget_is_editor (CONST_WIDGET (g->current->data)) && 
    408             edit_close_cmd ((WEdit *) g->current->data) && find_editor (h) == NULL) 
    409             dlg_stop (h); 
    410         break; 
    411     case CK_Help: 
    412         edit_help (); 
    413         /* edit->force |= REDRAW_COMPLETELY; */ 
    414         break; 
    415     case CK_Menu: 
    416         edit_menu_cmd (h); 
    417         break; 
    418     case CK_Quit: 
    419     case CK_Cancel: 
    420         /* don't close editor due to SIGINT, but stop move/resize window */ 
    421         { 
    422             Widget *w = WIDGET (g->current->data); 
    423  
    424             if (edit_widget_is_editor (w) && ((WEdit *) w)->drag_state != MCEDIT_DRAG_NONE) 
    425                 edit_restore_size ((WEdit *) w); 
    426             else if (command == CK_Quit) 
    427                 dlg_stop (h); 
    428         } 
    429         break; 
    430     case CK_About: 
    431         edit_about (); 
    432         break; 
    433     case CK_SyntaxOnOff: 
    434         edit_syntax_onoff_cmd (h); 
    435         break; 
    436     case CK_ShowTabTws: 
    437         edit_show_tabs_tws_cmd (h); 
    438         break; 
    439     case CK_ShowMargin: 
    440         edit_show_margin_cmd (h); 
    441         break; 
    442     case CK_ShowNumbers: 
    443         edit_show_numbers_cmd (h); 
    444         break; 
    445     case CK_Refresh: 
    446         edit_refresh_cmd (); 
    447         break; 
    448     case CK_Shell: 
    449         toggle_subshell (); 
    450         break; 
    451     case CK_LearnKeys: 
    452         learn_keys (); 
    453         break; 
    454     case CK_WindowMove: 
    455     case CK_WindowResize: 
    456         if (edit_widget_is_editor (CONST_WIDGET (g->current->data))) 
    457             edit_handle_move_resize ((WEdit *) g->current->data, command); 
    458         break; 
    459     case CK_WindowList: 
    460         edit_window_list (h); 
    461         break; 
    462     case CK_WindowNext: 
    463         group_select_next_widget (g); 
    464         break; 
    465     case CK_WindowPrev: 
    466         group_select_prev_widget (g); 
    467         break; 
    468     case CK_Options: 
    469         edit_options_dialog (h); 
    470         break; 
    471     case CK_OptionsSaveMode: 
    472         edit_save_mode_cmd (); 
    473         break; 
    474     case CK_SaveSetup: 
    475         save_setup_cmd (); 
    476         break; 
    477     default: 
    478         ret = MSG_NOT_HANDLED; 
    479         break; 
    480     } 
    481  
    482     return ret; 
    483 } 
    484  
    485377/* --------------------------------------------------------------------------------------------- */ 
    486378/* 
    487379 * Translate the keycode into either 'command' or 'char_for_insertion'. 
    edit_update_cursor (WEdit * edit, const mouse_event_t * event) 
    739631    return done; 
    740632} 
    741633 
    742 /* --------------------------------------------------------------------------------------------- */ 
    743 /** Callback for the edit dialog */ 
    744  
    745 static cb_ret_t 
    746 edit_dialog_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data) 
    747 { 
    748     WGroup *g = GROUP (w); 
    749     WDialog *h = DIALOG (w); 
    750  
    751     switch (msg) 
    752     { 
    753     case MSG_INIT: 
    754         edit_dlg_init (); 
    755         return MSG_HANDLED; 
    756  
    757     case MSG_RESIZE: 
    758         dlg_default_callback (w, NULL, MSG_RESIZE, 0, NULL); 
    759         menubar_arrange (find_menubar (h)); 
    760         return MSG_HANDLED; 
    761  
    762     case MSG_ACTION: 
    763         { 
    764             /* Handle shortcuts, menu, and buttonbar. */ 
    765  
    766             cb_ret_t result; 
    767  
    768             result = edit_dialog_command_execute (h, parm); 
    769  
    770             /* We forward any commands coming from the menu, and which haven't been 
    771                handled by the dialog, to the focused WEdit window. */ 
    772             if (result == MSG_NOT_HANDLED && sender == WIDGET (find_menubar (h))) 
    773                 result = send_message (g->current->data, NULL, MSG_ACTION, parm, NULL); 
    774  
    775             return result; 
    776         } 
    777  
    778     case MSG_KEY: 
    779         { 
    780             Widget *we = WIDGET (g->current->data); 
    781             cb_ret_t ret = MSG_NOT_HANDLED; 
    782  
    783             if (edit_widget_is_editor (we)) 
    784             { 
    785                 gboolean ext_mode; 
    786                 long command; 
    787  
    788                 /* keep and then extmod flag */ 
    789                 ext_mode = we->ext_mode; 
    790                 command = widget_lookup_key (we, parm); 
    791                 we->ext_mode = ext_mode; 
    792  
    793                 if (command == CK_IgnoreKey) 
    794                     we->ext_mode = FALSE; 
    795                 else 
    796                 { 
    797                     ret = edit_dialog_command_execute (h, command); 
    798                     /* if command was not handled, keep the extended mode 
    799                        for the further key processing */ 
    800                     if (ret == MSG_HANDLED) 
    801                         we->ext_mode = FALSE; 
    802                 } 
    803             } 
    804  
    805             /* 
    806              * Due to the "end of bracket" escape the editor sees input with is_idle() == false 
    807              * (expects more characters) and hence doesn't yet refresh the screen, but then 
    808              * no further characters arrive (there's only an "end of bracket" which is swallowed 
    809              * by tty_get_event()), so you end up with a screen that's not refreshed after pasting. 
    810              * So let's trigger an IDLE signal. 
    811              */ 
    812             if (!is_idle ()) 
    813                 widget_idle (w, TRUE); 
    814             return ret; 
    815         } 
    816  
    817         /* hardcoded menu hotkeys (see edit_drop_hotkey_menu) */ 
    818     case MSG_UNHANDLED_KEY: 
    819         return edit_drop_hotkey_menu (h, parm) ? MSG_HANDLED : MSG_NOT_HANDLED; 
    820  
    821     case MSG_VALIDATE: 
    822         edit_quit (h); 
    823         return MSG_HANDLED; 
    824  
    825     case MSG_END: 
    826         edit_dlg_deinit (); 
    827         return MSG_HANDLED; 
    828  
    829     case MSG_IDLE: 
    830         widget_idle (w, FALSE); 
    831         return send_message (g->current->data, NULL, MSG_IDLE, 0, NULL); 
    832  
    833     default: 
    834         return dlg_default_callback (w, sender, msg, parm, data); 
    835     } 
    836 } 
    837  
    838634/* --------------------------------------------------------------------------------------------- */ 
    839635 
    840636/** 
    edit_dialog_bg_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm 
    929725 
    930726/* --------------------------------------------------------------------------------------------- */ 
    931727 
    932 static cb_ret_t 
     728/** 
     729 * Handle move/resize mouse events. 
     730 */ 
     731static void 
     732edit_mouse_handle_move_resize (Widget * w, mouse_msg_t msg, mouse_event_t * event) 
     733{ 
     734    WEdit *edit = (WEdit *) (w); 
     735    Widget *h = WIDGET (w->owner); 
     736    int global_x, global_y; 
     737 
     738    if (msg == MSG_MOUSE_UP) 
     739    { 
     740        /* Exit move/resize mode. */ 
     741        edit_execute_cmd (edit, CK_Enter, -1); 
     742        edit_update_screen (edit);      /* Paint the buttonbar over our possibly overlapping frame. */ 
     743        return; 
     744    } 
     745 
     746    if (msg != MSG_MOUSE_DRAG) 
     747        /** 
     748         * We ignore any other events. Specifically, MSG_MOUSE_DOWN. 
     749         * 
     750         * When the move/resize is initiated by the menu, we let the user 
     751         * stop it by clicking with the mouse. Which is why we don't want 
     752         * a mouse down to affect the window. 
     753         */ 
     754        return; 
     755 
     756    /* Convert point to global coordinates for easier calculations. */ 
     757    global_x = event->x + w->x; 
     758    global_y = event->y + w->y; 
     759 
     760    /* Clamp the point to the dialog's client area. */ 
     761    global_y = CLAMP (global_y, h->y + 1, h->y + h->lines - 2); /* Status line, buttonbar */ 
     762    global_x = CLAMP (global_x, h->x, h->x + h->cols - 1);      /* Currently a no-op, as the dialog has no left/right margins. */ 
     763 
     764    if (edit->drag_state == MCEDIT_DRAG_MOVE) 
     765    { 
     766        w->y = global_y; 
     767        w->x = global_x - edit->drag_state_start; 
     768    } 
     769    else if (edit->drag_state == MCEDIT_DRAG_RESIZE) 
     770    { 
     771        w->lines = MAX (WINDOW_MIN_LINES, global_y - w->y + 1); 
     772        w->cols = MAX (WINDOW_MIN_COLS, global_x - w->x + 1); 
     773    } 
     774 
     775    edit->force |= REDRAW_COMPLETELY;   /* Not really needed as WEdit's MSG_DRAW already does this. */ 
     776 
     777    /* We draw the whole dialog because dragging/resizing exposes area beneath. */ 
     778    widget_draw (WIDGET (w->owner)); 
     779} 
     780 
     781/* --------------------------------------------------------------------------------------------- */ 
     782/*** public functions ****************************************************************************/ 
     783/* --------------------------------------------------------------------------------------------- */ 
     784 
     785cb_ret_t 
     786edit_dialog_command_execute (WDialog * h, long command, void *data) 
     787{ 
     788    WGroup *g = GROUP (h); 
     789    Widget *wh = WIDGET (h); 
     790    cb_ret_t ret = MSG_HANDLED; 
     791 
     792    switch (command) 
     793    { 
     794    case CK_EditNew: 
     795        edit_add_window (h, wh->y + 1, wh->x, wh->lines - 2, wh->cols, NULL, 0); 
     796        break; 
     797    case CK_EditFile: 
     798        edit_load_cmd (h); 
     799        break; 
     800    case CK_History: 
     801        edit_load_file_from_history (h); 
     802        break; 
     803    case CK_EditSyntaxFile: 
     804        edit_load_syntax_file (h); 
     805        break; 
     806    case CK_EditUserMenu: 
     807        edit_load_menu_file (h); 
     808        break; 
     809    case CK_Close: 
     810        /* if there are no opened files anymore, close MC editor */ 
     811        if ((edit_widget_is_editor (CONST_WIDGET (g->current->data)) || 
     812             edit_widget_is_cli (CONST_WIDGET (g->current->data))) && 
     813            edit_close_cmd ((WEdit *) g->current->data) && find_editor (h) == NULL) 
     814            dlg_stop (h); 
     815        break; 
     816    case CK_Help: 
     817        edit_help (); 
     818        /* edit->force |= REDRAW_COMPLETELY; */ 
     819        break; 
     820    case CK_Menu: 
     821        edit_menu_cmd (h); 
     822        break; 
     823    case CK_Quit: 
     824    case CK_Cancel: 
     825        /* don't close editor due to SIGINT, but stop move/resize window */ 
     826        { 
     827            Widget *w = WIDGET (g->current->data); 
     828 
     829            if (edit_widget_is_editor (w) && ((WEdit *) w)->drag_state != MCEDIT_DRAG_NONE) 
     830                edit_restore_size ((WEdit *) w); 
     831            else if (command == CK_Quit) 
     832                dlg_stop (h); 
     833        } 
     834        break; 
     835    case CK_About: 
     836        edit_about (); 
     837        break; 
     838    case CK_SyntaxOnOff: 
     839        edit_syntax_onoff_cmd (h); 
     840        break; 
     841    case CK_ShowTabTws: 
     842        edit_show_tabs_tws_cmd (h); 
     843        break; 
     844    case CK_ShowMargin: 
     845        edit_show_margin_cmd (h); 
     846        break; 
     847    case CK_ShowNumbers: 
     848        edit_show_numbers_cmd (h); 
     849        break; 
     850    case CK_Refresh: 
     851        edit_refresh_cmd (); 
     852        break; 
     853    case CK_InstructionStation: 
     854        edit_add_instr_stn_window (h, INSTR_STN_NO_FLAGS, data); 
     855        break; 
     856    case CK_Shell: 
     857        toggle_subshell (); 
     858        break; 
     859    case CK_LearnKeys: 
     860        learn_keys (); 
     861        break; 
     862    case CK_WindowMove: 
     863    case CK_WindowResize: 
     864        if (edit_widget_is_editor (CONST_WIDGET (g->current->data))) 
     865            edit_handle_move_resize ((WEdit *) g->current->data, command); 
     866        break; 
     867    case CK_WindowList: 
     868        edit_window_list (h); 
     869        break; 
     870    case CK_WindowNext: 
     871        group_select_next_widget (g); 
     872        break; 
     873    case CK_WindowPrev: 
     874        group_select_prev_widget (g); 
     875        break; 
     876    case CK_Options: 
     877        edit_options_dialog (h); 
     878        break; 
     879    case CK_OptionsSaveMode: 
     880        edit_save_mode_cmd (); 
     881        break; 
     882    case CK_SaveSetup: 
     883        save_setup_cmd (); 
     884        break; 
     885    default: 
     886        ret = MSG_NOT_HANDLED; 
     887        break; 
     888    } 
     889 
     890    return ret; 
     891} 
     892 
     893/* --------------------------------------------------------------------------------------------- */ 
     894/** Callback for the edit dialog */ 
     895 
     896cb_ret_t 
     897edit_dialog_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data) 
     898{ 
     899    WGroup *g = GROUP (w); 
     900    WDialog *h = DIALOG (w); 
     901 
     902    switch (msg) 
     903    { 
     904    case MSG_INIT: 
     905        edit_dlg_init (); 
     906        return MSG_HANDLED; 
     907 
     908    case MSG_RESIZE: 
     909        dlg_default_callback (w, NULL, MSG_RESIZE, 0, NULL); 
     910        menubar_arrange (find_menubar (h)); 
     911        return MSG_HANDLED; 
     912 
     913    case MSG_ACTION: 
     914        { 
     915            /* Handle shortcuts, menu, and buttonbar. */ 
     916 
     917            cb_ret_t result; 
     918 
     919            result = edit_dialog_command_execute (h, parm, data); 
     920 
     921            /* We forward any commands coming from the menu, and which haven't been 
     922               handled by the dialog, to the focused WEdit window. */ 
     923            if (result == MSG_NOT_HANDLED && sender == WIDGET (find_menubar (h))) 
     924                result = send_message (g->current->data, NULL, MSG_ACTION, parm, NULL); 
     925 
     926            return result; 
     927        } 
     928 
     929    case MSG_KEY: 
     930        { 
     931            Widget *we = WIDGET (g->current->data); 
     932            cb_ret_t ret = MSG_NOT_HANDLED; 
     933 
     934            if (edit_widget_is_editor (we)) 
     935            { 
     936                gboolean ext_mode; 
     937                long command; 
     938 
     939                /* keep and then extmod flag */ 
     940                ext_mode = we->ext_mode; 
     941                command = widget_lookup_key (we, parm); 
     942                we->ext_mode = ext_mode; 
     943 
     944                if (command == CK_IgnoreKey) 
     945                    we->ext_mode = FALSE; 
     946                else 
     947                { 
     948                    ret = edit_dialog_command_execute (h, command, data); 
     949                    /* if command was not handled, keep the extended mode 
     950                       for the further key processing */ 
     951                    if (ret == MSG_HANDLED) 
     952                        we->ext_mode = FALSE; 
     953                } 
     954            } 
     955 
     956            /* 
     957             * Due to the "end of bracket" escape the editor sees input with is_idle() == false 
     958             * (expects more characters) and hence doesn't yet refresh the screen, but then 
     959             * no further characters arrive (there's only an "end of bracket" which is swallowed 
     960             * by tty_get_event()), so you end up with a screen that's not refreshed after pasting. 
     961             * So let's trigger an IDLE signal. 
     962             */ 
     963            if (!is_idle ()) 
     964                widget_idle (w, TRUE); 
     965            return ret; 
     966        } 
     967 
     968        /* hardcoded menu hotkeys (see edit_drop_hotkey_menu) */ 
     969    case MSG_UNHANDLED_KEY: 
     970        return edit_drop_hotkey_menu (h, parm) ? MSG_HANDLED : MSG_NOT_HANDLED; 
     971 
     972    case MSG_VALIDATE: 
     973        edit_quit (h); 
     974        return MSG_HANDLED; 
     975 
     976    case MSG_END: 
     977        edit_dlg_deinit (); 
     978        return MSG_HANDLED; 
     979 
     980    case MSG_IDLE: 
     981        widget_idle (w, FALSE); 
     982        return send_message (g->current->data, NULL, MSG_IDLE, 0, NULL); 
     983 
     984    default: 
     985        return dlg_default_callback (w, sender, msg, parm, data); 
     986    } 
     987} 
     988 
     989/* --------------------------------------------------------------------------------------------- */ 
     990 
     991cb_ret_t 
    933992edit_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data) 
    934993{ 
    935994    WEdit *e = (WEdit *) w; 
    edit_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *da 
    9991058 
    10001059/* --------------------------------------------------------------------------------------------- */ 
    10011060 
    1002 /** 
    1003  * Handle move/resize mouse events. 
    1004  */ 
    1005 static void 
    1006 edit_mouse_handle_move_resize (Widget * w, mouse_msg_t msg, mouse_event_t * event) 
    1007 { 
    1008     WEdit *edit = (WEdit *) (w); 
    1009     Widget *h = WIDGET (w->owner); 
    1010     int global_x, global_y; 
    1011  
    1012     if (msg == MSG_MOUSE_UP) 
    1013     { 
    1014         /* Exit move/resize mode. */ 
    1015         edit_execute_cmd (edit, CK_Enter, -1); 
    1016         edit_update_screen (edit);      /* Paint the buttonbar over our possibly overlapping frame. */ 
    1017         return; 
    1018     } 
    1019  
    1020     if (msg != MSG_MOUSE_DRAG) 
    1021         /** 
    1022          * We ignore any other events. Specifically, MSG_MOUSE_DOWN. 
    1023          * 
    1024          * When the move/resize is initiated by the menu, we let the user 
    1025          * stop it by clicking with the mouse. Which is why we don't want 
    1026          * a mouse down to affect the window. 
    1027          */ 
    1028         return; 
    1029  
    1030     /* Convert point to global coordinates for easier calculations. */ 
    1031     global_x = event->x + w->x; 
    1032     global_y = event->y + w->y; 
    1033  
    1034     /* Clamp the point to the dialog's client area. */ 
    1035     global_y = CLAMP (global_y, h->y + 1, h->y + h->lines - 2); /* Status line, buttonbar */ 
    1036     global_x = CLAMP (global_x, h->x, h->x + h->cols - 1);      /* Currently a no-op, as the dialog has no left/right margins. */ 
    1037  
    1038     if (edit->drag_state == MCEDIT_DRAG_MOVE) 
    1039     { 
    1040         w->y = global_y; 
    1041         w->x = global_x - edit->drag_state_start; 
    1042     } 
    1043     else if (edit->drag_state == MCEDIT_DRAG_RESIZE) 
    1044     { 
    1045         w->lines = MAX (WINDOW_MIN_LINES, global_y - w->y + 1); 
    1046         w->cols = MAX (WINDOW_MIN_COLS, global_x - w->x + 1); 
    1047     } 
    1048  
    1049     edit->force |= REDRAW_COMPLETELY;   /* Not really needed as WEdit's MSG_DRAW already does this. */ 
    1050  
    1051     /* We draw the whole dialog because dragging/resizing exposes area beneath. */ 
    1052     widget_draw (WIDGET (w->owner)); 
    1053 } 
    1054  
    1055 /* --------------------------------------------------------------------------------------------- */ 
    1056  
    10571061/** 
    10581062 * Handle mouse events of editor window 
    10591063 * 
    edit_mouse_handle_move_resize (Widget * w, mouse_msg_t msg, mouse_event_t * even 
    10611065 * @param msg mouse event message 
    10621066 * @param event mouse event data 
    10631067 */ 
    1064 static void 
     1068void 
    10651069edit_mouse_callback (Widget * w, mouse_msg_t msg, mouse_event_t * event) 
    10661070{ 
    10671071    WEdit *edit = (WEdit *) w; 
    edit_mouse_callback (Widget * w, mouse_msg_t msg, mouse_event_t * event) 
    11761180} 
    11771181 
    11781182/* --------------------------------------------------------------------------------------------- */ 
    1179 /*** public functions ****************************************************************************/ 
     1183/* Creates a CLI special window, windowed (not fullscreen) */ 
     1184 
     1185gboolean 
     1186edit_add_instr_stn_window (WDialog * h, instr_stn_flags_t flags, void *data) 
     1187{ 
     1188    WInstructionStation *ip; 
     1189 
     1190    /* Note passing of data - ability to alter program that's run in this station */ 
     1191    ip = instr_stn_create (3, 10, 17, 80, flags, (const char *) data); 
     1192 
     1193    if (ip == NULL) 
     1194        return FALSE; 
     1195 
     1196    /* Add CLI-extended editor window (extending WEdit) to front dialog */ 
     1197    group_add_widget_autopos (GROUP (h), ip, WPOS_KEEP_ALL, NULL); 
     1198    widget_draw (WIDGET (h)); 
     1199    return TRUE; 
     1200} 
     1201 
    11801202/* --------------------------------------------------------------------------------------------- */ 
    11811203/** 
    11821204 * Edit one file. 
    edit_add_window (WDialog * h, int y, int x, int lines, int cols, const vfs_path_ 
    13691391    WEdit *edit; 
    13701392    Widget *w; 
    13711393 
    1372     edit = edit_init (NULL, y, x, lines, cols, f, fline); 
     1394    edit = edit_init (NULL, y, x, lines, cols, f, fline, 0); 
    13731395    if (edit == NULL) 
    13741396        return FALSE; 
    13751397 
  • src/editor/editwidget.h

    diff --git a/src/editor/editwidget.h b/src/editor/editwidget.h
    index 446ef07ac..e8d1586e2 100644
    a b  
    1313 
    1414/*** typedefs(not structures) and defined constants **********************************************/ 
    1515 
     16#define EDIT(x) ((WEdit *)(x)) 
     17#define CONST_EDIT(x) ((const WEdit *)(x)) 
     18 
    1619#define N_LINE_CACHES 32 
    1720 
    1821/*** enums ***************************************************************************************/ 
  • new file src/editor/instr_station.c

    diff --git a/src/editor/instr_station.c b/src/editor/instr_station.c
    new file mode 100644
    index 000000000..e4bb6ad65
    - +  
     1/* 
     2   Implementation of a CLI window for MCEdit. 
     3 
     4   Copyright (C) 2021 
     5   Free Software Foundation, Inc. 
     6 
     7   Written by: 
     8   Sebastian Gniazdowski <sgniazdowski@gmail.com>, 2021. 
     9 
     10   This file is part of the Midnight Commander. 
     11 
     12   The Midnight Commander is free software: you can redistribute it 
     13   and/or modify it under the terms of the GNU General Public License as 
     14   published by the Free Software Foundation, either version 3 of the License, 
     15   or (at your option) any later version. 
     16 
     17   The Midnight Commander is distributed in the hope that it will be useful, 
     18   but WITHOUT ANY WARRANTY; without even the implied warranty of 
     19   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
     20   GNU General Public License for more details. 
     21 
     22   You should have received a copy of the GNU General Public License 
     23   along with this program.  If not, see <http://www.gnu.org/licenses/>. 
     24 */ 
     25 
     26/** \file instr_station.c 
     27 *  \brief Implementation of a CLI special window for MCEdit. 
     28 *  \author Sebastian Gniazdowski 
     29 *  \date 2021 
     30 * 
     31 *  Such window runs a set up program (/bin/bash by default) allowing to 
     32 *  provide an input to it and read its output. 
     33 */ 
     34 
     35#include <config.h> 
     36 
     37#include "lib/global.h" 
     38 
     39#include <sys/types.h> 
     40#include <sys/wait.h> 
     41 
     42#include "src/editor/editwidget.h" 
     43 
     44#include "lib/util.h" 
     45#include "lib/tty/key.h" 
     46#include "lib/widget.h" 
     47#include "src/editor/instr_station.h" 
     48 
     49/*** global variables ****************************************************************************/ 
     50 
     51/*** file scope macro definitions ****************************************************************/ 
     52 
     53/*** file scope type declarations ****************************************************************/ 
     54 
     55typedef enum instr_stn_io_type 
     56{ 
     57    STDIN = 0, 
     58    STDOUT, 
     59    STDERR 
     60} instr_stn_io_type_t; 
     61 
     62/*** file scope variables ************************************************************************/ 
     63 
     64static gboolean alt_prog_first_run = TRUE; 
     65 
     66/*** file scope functions ************************************************************************/ 
     67/* --------------------------------------------------------------------------------------------- */ 
     68 
     69static char * 
     70instr_stn_get_instruction_text (WInstructionStation * ip) 
     71{ 
     72    char *ret_str; 
     73    GString *instr; 
     74    off_t bol, eol, begin_instr; 
     75    long size; 
     76    int idx, byte; 
     77 
     78    /* Calculate offset of text after prompt */ 
     79    bol = edit_buffer_get_current_bol (&EDIT (ip)->buffer); 
     80    eol = edit_buffer_get_current_eol (&EDIT (ip)->buffer); 
     81    begin_instr = bol + ip->prompt_span; 
     82 
     83    /* Is there anything entered? */ 
     84    size = ((long) eol) - ((long) begin_instr); 
     85    if (size <= 0) 
     86        return NULL; 
     87 
     88    /* Allocate expected size string and fill it */ 
     89    instr = g_string_sized_new (size + 2); 
     90    for (idx = 0; idx < size; idx++) 
     91    { 
     92        byte = edit_buffer_get_byte (&EDIT (ip)->buffer, begin_instr + idx); 
     93        g_string_append_c (instr, byte); 
     94    } 
     95 
     96    /* Append new line if needed */ 
     97    if (instr->str[instr->len - 1] != '\n') 
     98        g_string_append_c (instr, '\n'); 
     99 
     100    /* Return char buffer */ 
     101    ret_str = instr->str; 
     102    g_string_free (instr, FALSE); 
     103    return ret_str; 
     104} 
     105 
     106/* --------------------------------------------------------------------------------------------- */ 
     107 
     108static gboolean 
     109instr_stn_release (WInstructionStation * ip, gboolean free_all) 
     110{ 
     111    /* Already relased? */ 
     112    if (ip == NULL) 
     113    { 
     114        if (free_all) 
     115            return TRUE; 
     116        else 
     117            return FALSE; 
     118    } 
     119 
     120    /* Already cleaned up (for a restart of program) ? */ 
     121    if (ip->finalized && !free_all) 
     122        return FALSE; 
     123    ip->finalized = TRUE; 
     124 
     125    /* Close process watcher first, suppressing *program_ended_cb() callback */ 
     126    if (ip->proc_src_id != 0) 
     127    { 
     128        GSource *src; 
     129        src = g_main_context_find_source_by_id (NULL, ip->proc_src_id); 
     130        if (src != NULL) 
     131            g_source_destroy (src); 
     132        ip->proc_src_id = 0; 
     133    } 
     134 
     135    /* Release the pipes, channels, etc. */ 
     136    for (int i = 0; i <= STDERR; i++) 
     137    { 
     138        /* Source */ 
     139        if (ip->io[i].src_id != 0) 
     140        { 
     141            GSource *src; 
     142            src = g_main_context_find_source_by_id (NULL, ip->io[i].src_id); 
     143            if (src != NULL) 
     144                g_source_destroy (src); 
     145            ip->io[i].src_id = 0; 
     146        } 
     147        /* Channel */ 
     148        if (ip->io[i].ch != NULL) 
     149        { 
     150            g_io_channel_unref (ip->io[i].ch); 
     151            ip->io[i].ch = NULL; 
     152        } 
     153        /* Pipe */ 
     154        if (ip->io[i].fd >= 0) 
     155        { 
     156            close (ip->io[i].fd); 
     157            ip->io[i].fd = -1; 
     158        } 
     159    } 
     160 
     161    /* Remove prompt guard recurring timeout callback function */ 
     162    if (ip->prompt_timeout_id != 0) 
     163    { 
     164        g_source_remove (ip->prompt_timeout_id); 
     165        ip->prompt_timeout_id = 0; 
     166    } 
     167 
     168    MC_PTR_FREE (ip->instruction); 
     169    /* Release prompt */ 
     170    MC_PTR_FREE (ip->prompt); 
     171    /* Release error prompt */ 
     172    MC_PTR_FREE (ip->eprompt); 
     173 
     174    /* Clear prompt information */ 
     175    ip->prompt_span = 0; 
     176    ip->eprompt_span = 0; 
     177 
     178    /* Set various indicator flags to some inactive state */ 
     179    ip->io_complete = TRUE; 
     180    ip->first_response = TRUE; 
     181    ip->before_first_prompt = TRUE; 
     182    ip->prompt_shown = FALSE; 
     183    ip->flags = INSTR_STN_NO_FLAGS; 
     184 
     185    /* Clear any GError */ 
     186    if (ip->error != NULL) 
     187    { 
     188        g_error_free (ip->error); 
     189        ip->error = NULL; 
     190    } 
     191 
     192    /* Release space occupied by WInstructionStation object? */ 
     193    if (free_all) 
     194    { 
     195        MC_PTR_FREE (ip->program); 
     196        /* Free main object */ 
     197        g_free (ip); 
     198    } 
     199    else 
     200        /* Value is still accessible in prev_program field */ 
     201        ip->program = NULL; 
     202 
     203    return TRUE; 
     204} 
     205 
     206/* --------------------------------------------------------------------------------------------- */ 
     207 
     208static cb_ret_t 
     209instr_stn_run_novel_state_id (WInstructionStation * ip, long novel_state_id, void *data) 
     210{ 
     211    WGroup *owner = WIDGET (ip)->owner; 
     212    cb_ret_t ret = MSG_NOT_HANDLED; 
     213 
     214    switch (novel_state_id) 
     215    { 
     216    case CK_BackSpace: 
     217        if (ip->cur_col <= ip->prompt_span) 
     218            ret = MSG_HANDLED; 
     219        else 
     220        { 
     221            EDIT (ip)->force |= REDRAW_PAGE; 
     222            edit_update_screen (EDIT (ip)); 
     223        } 
     224        break; 
     225    case CK_Enter: 
     226    case CK_Return: 
     227        if (EDIT (ip)->buffer.curs_line == ip->cur_line) 
     228        { 
     229            ip->instruction = instr_stn_get_instruction_text (ip); 
     230            ip->io_complete = FALSE; 
     231            /* Should dispatch to write callback which will send instruction */ 
     232            g_main_context_iteration (NULL, 0); 
     233            repaint_screen (); 
     234        } 
     235        ret = MSG_HANDLED; 
     236        break; 
     237        /* InstructionStation activity invoked when focus is at a station window behaves differently */ 
     238    case CK_InstructionStation: 
     239        { 
     240            /* Save needed information */ 
     241            char *program_save; 
     242 
     243            /* End previous program */ 
     244            if (kill (ip->process_id, 15) == -1) 
     245                mc_log ("Problem sending TERM signal to %d: %s.", ip->process_id, 
     246                        g_strerror (errno)); 
     247            else 
     248                mc_log ("Ending process %d.", ip->process_id); 
     249            if (!ip->proc_support) 
     250                waitpid (ip->process_id, NULL, 0); 
     251 
     252            /* Allow process watcher callbacks to be called */ 
     253            g_usleep (150000); 
     254            g_main_context_iteration (NULL, FALSE); 
     255            /* Allow the process end message to flash noticeably long */ 
     256            g_usleep (200000); 
     257 
     258            /* Clean WInstructionStation object */ 
     259            instr_stn_release (ip, FALSE); 
     260            /* ...and reinitialize it */ 
     261            program_save = data != NULL ? g_strdup (data) : ip->prev_program; 
     262            if (instr_stn_init (ip, -1, -1, -1, -1, ip->prev_flags, program_save) == NULL) 
     263            { 
     264                /* Init failed -> close faulty station window */ 
     265                ret = 
     266                    edit_dialog_callback (WIDGET (owner), WIDGET (ip), MSG_ACTION, CK_Close, NULL); 
     267                if (ret == MSG_HANDLED) 
     268                    /* Update pointer in case of some future code like widget_draw(ip), etc. */ 
     269                    ip = INSTR_STATION (owner->current->data); 
     270            } 
     271            else 
     272            { 
     273                repaint_screen (); 
     274                ret = MSG_HANDLED; 
     275            } 
     276            g_free (program_save); 
     277        } 
     278        break; 
     279    default: 
     280        break; 
     281    } 
     282    return ret; 
     283} 
     284 
     285/* --------------------------------------------------------------------------------------------- */ 
     286 
     287static void 
     288instr_stn_show_prompt (WInstructionStation * ip, gboolean is_ok) 
     289{ 
     290    char *sel_prompt = is_ok ? ip->prompt : ip->eprompt; 
     291    int sel_prompt_span = is_ok ? ip->prompt_span : ip->eprompt_span; 
     292 
     293    if (has_flag (ip->flags, INSTR_STN_NO_PROMPT)) 
     294        return; 
     295 
     296    /* Print prompt */ 
     297    if (!edit_newline_end_check_only (EDIT (ip)) && !ip->before_first_prompt) 
     298    { 
     299        edit_insert (EDIT (ip), '\n'); 
     300        ip->cur_line += 1; 
     301    } 
     302    else 
     303        ip->before_first_prompt = FALSE; 
     304    edit_insert_string (EDIT (ip), sel_prompt, sel_prompt_span); 
     305 
     306    /* Raise an indicator flag */ 
     307    ip->prompt_shown = TRUE; 
     308 
     309    /* Set cursor position to reflect state */ 
     310    ip->cur_col = sel_prompt_span; 
     311} 
     312 
     313/* --------------------------------------------------------------------------------------------- */ 
     314/* Remove prompt from buffer (basically clear current line). */ 
     315 
     316static void 
     317instr_stn_clear_prompt (WInstructionStation * ip) 
     318{ 
     319    off_t bol, eol, idx; 
     320 
     321    if (has_flag (ip->flags, INSTR_STN_NO_PROMPT)) 
     322        return; 
     323 
     324    eol = edit_buffer_get_current_eol (&EDIT (ip)->buffer); 
     325    bol = edit_buffer_get_current_bol (&EDIT (ip)->buffer); 
     326    for (idx = 0; idx < eol - bol; idx++) 
     327        edit_backspace (EDIT (ip), TRUE); 
     328    ip->prompt_shown = FALSE; 
     329} 
     330 
     331/* --------------------------------------------------------------------------------------------- */ 
     332 
     333static gboolean 
     334prompt_drawing_timeout_callback (gpointer data) 
     335{ 
     336    WInstructionStation *ip = INSTR_STATION (data); 
     337    guint64 cur_time; 
     338    cur_time = g_get_real_time (); 
     339    /* Draw prompt after 0.5 seconds of no output from program */ 
     340    if (cur_time - ip->read_time > 500000 && !ip->prompt_shown) 
     341    { 
     342        instr_stn_show_prompt (ip, TRUE); 
     343        repaint_screen (); 
     344    } 
     345    return TRUE; 
     346} 
     347 
     348/* --------------------------------------------------------------------------------------------- */ 
     349 
     350#ifdef GLIB_VERSION_2_40 
     351 
     352static void 
     353program_ended_callback (GPid pid, gint exit_code, gpointer user_data) 
     354{ 
     355    WInstructionStation *ip = INSTR_STATION (user_data); 
     356    char *msg; 
     357    gboolean st_ret; 
     358 
     359    /* Release object leaving it uninitialized (no freeing of main object pointer) */ 
     360    instr_stn_release (ip, FALSE); 
     361 
     362    g_spawn_close_pid (pid); 
     363 
     364    /* Examine exit code of closed program */ 
     365#ifdef GLIB_VERSION_2_34 
     366    st_ret = g_spawn_check_exit_status (exit_code, NULL); 
     367#else 
     368    st_ret = (exit_code == 0); 
     369#endif 
     370 
     371    /* Choose message basing on exit code */ 
     372    if (st_ret) 
     373        msg = g_strdup (_("\nProgram closed.\n")); 
     374    else 
     375        msg = g_strdup_printf (_("\nProgram closed abnormally (exit code: %d).\n"), exit_code); 
     376 
     377    /* Insert/print message to buffer and to mc.log */ 
     378    edit_insert_string (EDIT (ip), msg, strlen (msg)); 
     379    mc_log ("%s", msg); 
     380 
     381    /* Release message upon using/printing it */ 
     382    g_free (msg); 
     383    repaint_screen (); 
     384} 
     385 
     386#endif 
     387 
     388/* --------------------------------------------------------------------------------------------- */ 
     389 
     390static gboolean 
     391helper_configure_src_and_ch (GIOChannel ** ch, gint fd, guint * src_id, gboolean write, 
     392                             GIOFunc cb_fun, gpointer data, const char *name) 
     393{ 
     394    /* Initialize the output variables */ 
     395    *ch = NULL; 
     396    *src_id = 0; 
     397 
     398    *ch = g_io_channel_unix_new (fd); 
     399 
     400    /* Channel created? */ 
     401    if (*ch == NULL) 
     402        goto cleanup_and_err; 
     403 
     404    /* Automatic shutdown of channel */ 
     405    g_io_channel_set_close_on_unref (*ch, TRUE); 
     406 
     407    /* Trim down buffering on this channel */ 
     408    g_io_channel_set_buffer_size (*ch, 5); 
     409 
     410    /* Attempt to set non-blocking flag on channel */ 
     411    if (g_io_channel_set_flags (*ch, G_IO_FLAG_NONBLOCK, NULL) != G_IO_STATUS_NORMAL) 
     412        mc_log ("Problem setting a channel to non-blocking state (channel flags: 0x%x)", 
     413                g_io_channel_get_flags (*ch)); 
     414 
     415    /* Create GSource */ 
     416    *src_id = 
     417        g_io_add_watch (*ch, (write ? G_IO_OUT : G_IO_IN) | G_IO_HUP | G_IO_ERR, cb_fun, data); 
     418 
     419    /* Source created OK? */ 
     420    if (*src_id == 0) 
     421        goto cleanup_and_err; 
     422 
     423    /* Configure the sources */ 
     424    g_source_set_name_by_id (*src_id, name); 
     425 
     426    /* Return true */ 
     427    return TRUE; 
     428 
     429  cleanup_and_err: 
     430    if (*src_id != 0) 
     431    { 
     432        GSource *src; 
     433        src = g_main_context_find_source_by_id (NULL, *src_id); 
     434        if (src != NULL) 
     435            /* Triggers also unref of wrapped channel */ 
     436            g_source_destroy (src); 
     437    } 
     438    if (*ch != NULL) 
     439        g_io_channel_unref (*ch); 
     440    *ch = NULL; 
     441    *src_id = 0; 
     442 
     443    return FALSE; 
     444} 
     445 
     446/* --------------------------------------------------------------------------------------------- */ 
     447 
     448static gboolean 
     449helper_read_all (GIOChannel * channel, char *buf, gsize * out_bytes_read_in_len) 
     450{ 
     451    gsize len = *out_bytes_read_in_len; 
     452    gsize count, retries = 10; 
     453    GIOStatus stat; 
     454 
     455    *out_bytes_read_in_len = 0; 
     456 
     457    while (*out_bytes_read_in_len < len) 
     458    { 
     459        count = 0; 
     460        stat = 
     461            g_io_channel_read_chars (channel, buf + *out_bytes_read_in_len, 
     462                                     len - *out_bytes_read_in_len, &count, NULL); 
     463        *out_bytes_read_in_len += count; 
     464 
     465        /* Should end the reading ? */ 
     466        if (stat == G_IO_STATUS_ERROR || (stat == G_IO_STATUS_NORMAL && count == 0) || 
     467            stat == G_IO_STATUS_EOF) 
     468            return (stat != G_IO_STATUS_ERROR); 
     469        else if (stat == G_IO_STATUS_AGAIN && retries-- == 0) 
     470            /* Exhausted retries - there must be no data to read - return OK */ 
     471            return TRUE; 
     472    } 
     473 
     474    return TRUE; 
     475} 
     476 
     477/* --------------------------------------------------------------------------------------------- */ 
     478 
     479static gboolean 
     480helper_write_all (GIOChannel * channel, char *buf, gsize * out_bytes_written_in_len) 
     481{ 
     482    gsize len = *out_bytes_written_in_len; 
     483    gsize count; 
     484    GIOError err; 
     485 
     486    *out_bytes_written_in_len = 0; 
     487 
     488    while (*out_bytes_written_in_len < len) 
     489    { 
     490        count = 0; 
     491        err = 
     492            g_io_channel_write (channel, buf + *out_bytes_written_in_len, 
     493                                len - *out_bytes_written_in_len, &count); 
     494        *out_bytes_written_in_len += count; 
     495 
     496        if (err && err != G_IO_ERROR_AGAIN) 
     497            return FALSE; 
     498    } 
     499 
     500    return TRUE; 
     501} 
     502 
     503/* --------------------------------------------------------------------------------------------- */ 
     504 
     505static gboolean 
     506stdout_read_callback (GIOChannel * source, GIOCondition condition, gpointer data) 
     507{ 
     508    WInstructionStation *ip = INSTR_STATION (data); 
     509    char buf[2048]; 
     510    gsize count = 2047; 
     511    gboolean r_ret; 
     512 
     513    /* Active only when a recognized event of interest occurs */ 
     514    if ((condition & (G_IO_IN | G_IO_HUP | G_IO_ERR)) == 0) 
     515        return TRUE; 
     516    else 
     517        ip->io_complete = FALSE; 
     518 
     519    /* Repeat read of 2KiB-1 maximum bytes until 0 bytes are being read */ 
     520    do 
     521    { 
     522        r_ret = helper_read_all (source, buf, &count); 
     523        buf[count] = '\0'; 
     524        if (count == 0 && !r_ret) 
     525        { 
     526            mc_log ("Error while reading program's output: %s", g_strerror (errno)); 
     527        } 
     528        else 
     529        { 
     530            ip->read_time = g_get_real_time (); 
     531            buf[count] = '\0'; 
     532            if (count > 0) 
     533            { 
     534                gsize idx = 0, nl_count = 0; 
     535 
     536                /* Count number of read lines */ 
     537                char *p = buf; 
     538                while (idx++ < count) 
     539                    if (*p++ == '\n') 
     540                        nl_count++; 
     541 
     542                /* Move to next line after prompt */ 
     543                if (ip->first_response) 
     544                { 
     545                    edit_insert (EDIT (ip), '\n'); 
     546                    nl_count++; 
     547                    ip->first_response = FALSE; 
     548                    ip->prompt_shown = FALSE; 
     549                } 
     550                else if (ip->prompt_shown) 
     551                    instr_stn_clear_prompt (ip); 
     552 
     553                /* Print whole buffer into file's window  */ 
     554                edit_insert_string (EDIT (ip), buf, count); 
     555 
     556                /* Increase the row pointer */ 
     557                ip->cur_line += nl_count; 
     558            } 
     559            else if (!ip->prompt_shown) 
     560                instr_stn_show_prompt (ip, r_ret); 
     561 
     562            repaint_screen (); 
     563        } 
     564    } 
     565    while (count != 0); 
     566 
     567    ip->io_complete = r_ret; 
     568    return condition != G_IO_HUP; 
     569} 
     570 
     571/* --------------------------------------------------------------------------------------------- */ 
     572 
     573static gboolean 
     574stdin_write_callback (GIOChannel * source, GIOCondition condition, gpointer data) 
     575{ 
     576    WInstructionStation *ip = INSTR_STATION (data); 
     577    gsize count; 
     578    gboolean ret = TRUE; 
     579 
     580    /* Any instruction to send? */ 
     581    if (ip->instruction == NULL) 
     582        return ret; 
     583    else 
     584        ip->io_complete = FALSE; 
     585 
     586    if ((condition & G_IO_OUT) == 0) 
     587        return TRUE; 
     588 
     589    errno = 0; 
     590    count = strlen (ip->instruction); 
     591    ret = helper_write_all (source, ip->instruction, &count); 
     592    if (!ret) 
     593    { 
     594        mc_log ("Error during sending instruction to program: %s", g_strerror (errno)); 
     595    } 
     596    else 
     597    { 
     598        ip->first_response = TRUE; 
     599        MC_PTR_FREE (ip->instruction); 
     600        if (ip->prompt_timeout_id == 0) 
     601            ip->prompt_timeout_id = g_timeout_add (200, prompt_drawing_timeout_callback, ip); 
     602    } 
     603 
     604    g_io_channel_flush (ip->io[STDIN].ch, NULL); 
     605    ip->io_complete = TRUE; 
     606 
     607    return ret; 
     608} 
     609 
     610/* --------------------------------------------------------------------------------------------- */ 
     611 
     612/* --------------------------------------------------------------------------------------------- */ 
     613/*** public functions ****************************************************************************/ 
     614/* --------------------------------------------------------------------------------------------- */ 
     615 
     616WInstructionStation * 
     617instr_stn_create (int y, int x, int lines, int cols, instr_stn_flags_t flags, const char *program) 
     618{ 
     619    WInstructionStation *ip, *iret; 
     620 
     621    ip = g_new0 (WInstructionStation, 1); 
     622    if (ip == NULL) 
     623        return NULL; 
     624 
     625    iret = instr_stn_init (ip, y, x, lines, cols, flags, program); 
     626    if (iret == NULL) 
     627        MC_PTR_FREE (ip); 
     628 
     629    return ip; 
     630} 
     631 
     632/* --------------------------------------------------------------------------------------------- */ 
     633/* Initializes a preallocated object */ 
     634 
     635WInstructionStation * 
     636instr_stn_init (WInstructionStation * ip, int y, int x, int lines, int cols, 
     637                instr_stn_flags_t flags, const char *program) 
     638{ 
     639    WEdit *iret; 
     640    WGroup *owner_save = NULL; 
     641    gboolean tmp_ret = FALSE; 
     642    char **program_cline; 
     643    int rounds = 0; 
     644 
     645    if (ip == NULL) 
     646        goto cleanup_and_err; 
     647 
     648    /* Handle special -1 values indicating reuse of old values */ 
     649    if (ip->finalized) 
     650    { 
     651        y = (y == -1) ? WIDGET (ip)->y : y; 
     652        x = (x == -1) ? WIDGET (ip)->x : x; 
     653        lines = (lines == -1) ? WIDGET (ip)->lines : lines; 
     654        cols = (cols == -1) ? WIDGET (ip)->cols : cols; 
     655        /* Save owner */ 
     656        owner_save = WIDGET (ip)->owner; 
     657        ip->finalized = FALSE; 
     658    } 
     659 
     660    /* Initialize base object (WEdit) */ 
     661    iret = edit_init (EDIT (ip), y, x, lines, cols, NULL, 0, EDIT_DO_INIT_BASE_CLASS); 
     662    if (iret == NULL) 
     663        goto cleanup_and_err; 
     664 
     665    /* Restore owner field value */ 
     666    WIDGET (ip)->owner = owner_save; 
     667    WIDGET (ip)->callback = instr_stn_callback; 
     668    WIDGET (ip)->mouse_callback = edit_mouse_callback; 
     669    EDIT (ip)->fullscreen = 0; 
     670 
     671    ip->io[0].name = g_strdup ("Std-In (WInstructionStation)"); 
     672    ip->io[1].name = g_strdup ("Std-Out (WInstructionStation)"); 
     673    ip->io[2].name = g_strdup ("Std-Error (WInstructionStation)"); 
     674    ip->io[0].fd = ip->io[1].fd = ip->io[2].fd = -1; 
     675 
     676    ip->before_first_prompt = TRUE; 
     677    ip->prompt = g_strdup ("[guest@localhost]# "); 
     678    ip->prompt_span = strlen (ip->prompt); 
     679    ip->eprompt = g_strdup ("[guest@localhost]! "); 
     680    ip->eprompt_span = strlen (ip->eprompt); 
     681    ip->cur_col = 0; 
     682    ip->cur_line = 0; 
     683    ip->flags = flags; 
     684    ip->prev_flags = flags; 
     685 
     686    /* Set initial program (most probably a shell, like Bash) */ 
     687    if (program != NULL) 
     688    { 
     689        if (program[0] == 's' && program[1] == 'h') 
     690            ip->program = g_strdup (program); 
     691        else 
     692            ip->program = g_strdup_printf ("sh -c '%s'", program); 
     693    } 
     694    else 
     695        /* Fallback instruction if no program given */ 
     696        ip->program = g_strdup ("bash"); 
     697 
     698    while (++rounds <= 2 && !tmp_ret) 
     699    { 
     700        int argc = 0; 
     701        if (ip->error != NULL) 
     702        { 
     703            g_error_free (ip->error); 
     704            ip->error = NULL; 
     705        } 
     706        tmp_ret = g_shell_parse_argv (ip->program, &argc, &program_cline, &ip->error); 
     707        if (tmp_ret) 
     708        { 
     709            tmp_ret = g_spawn_async_with_pipes (NULL, program_cline, 
     710                                                NULL, G_SPAWN_SEARCH_PATH | 
     711                                                G_SPAWN_DO_NOT_REAP_CHILD | 
     712                                                G_SPAWN_LEAVE_DESCRIPTORS_OPEN, 
     713                                                NULL, NULL, &ip->process_id, 
     714                                                has_flag (flags, 
     715                                                          INSTR_STN_NO_STDIN) ? NULL : 
     716                                                &ip->io[STDIN].fd, &ip->io[STDOUT].fd, 
     717                                                &ip->io[STDERR].fd, &ip->error); 
     718            g_strfreev (program_cline); 
     719        } 
     720        if (!tmp_ret) 
     721        { 
     722            char *new_program, *msg_text; 
     723            mc_log ("Creating CLI process [instruction:%s] failed: %s", 
     724                    program, ip->error ? ip->error->message : "<no error message>"); 
     725 
     726            /* Create a transatable, parametrized message */ 
     727            msg_text = g_strdup_printf ("%s%s:", _("The specified program has failed to run.\n" 
     728                                                   "Enter a new full path or a program name"), 
     729                                        (rounds >= 2 ? _(" (last try)") : "")); 
     730 
     731            /* Display message asking for a new program */ 
     732            new_program = input_expand_dialog (_("Provide an alternate program to run"), 
     733                                               msg_text, "instruction-station", 
     734                                               alt_prog_first_run ? ip->program : INPUT_LAST_TEXT, 
     735                                               INPUT_COMPLETE_COMMANDS | INPUT_COMPLETE_FILENAMES | 
     736                                               INPUT_COMPLETE_VARIABLES); 
     737            g_free (msg_text); 
     738            alt_prog_first_run = FALSE; 
     739 
     740            /* Obtained a new program? */ 
     741            if (new_program != NULL) 
     742            { 
     743                g_free (ip->program); 
     744                ip->program = new_program; 
     745            } 
     746        } 
     747    } 
     748 
     749    /* If no program to run has been found, then exit. */ 
     750    if (!tmp_ret) 
     751    { 
     752        message (D_ERROR, _("InstructionStation startup failed"), 
     753                 _("Failed to initialize the CLI window\n(reason: %s)."), 
     754                 ip->error ? ip->error->message : "<no error message>"); 
     755        goto cleanup_and_err; 
     756    } 
     757 
     758    ip->prev_program = ip->program; 
     759 
     760    for (int i = 0; i <= STDERR; i++) 
     761    { 
     762        if (has_flag (flags, INSTR_STN_NO_STDIN) && i == STDIN) 
     763        { 
     764            /* There is no standard input pipe for program */ 
     765            ip->io[i].fd = -1; 
     766            continue; 
     767        } 
     768 
     769        if (helper_configure_src_and_ch (&ip->io[i].ch, ip->io[i].fd, &ip->io[i].src_id, 
     770                                         (i == STDIN ? TRUE : FALSE), 
     771                                         (i == STDIN ? stdin_write_callback : stdout_read_callback), 
     772                                         ip, ip->io[i].name) == FALSE) 
     773            goto cleanup_and_err; 
     774    } 
     775 
     776#ifdef GLIB_VERSION_2_40 
     777    /* Set a process watcher and restarter for the program */ 
     778    ip->proc_src_id = g_child_watch_add (ip->process_id, program_ended_callback, ip); 
     779    ip->proc_support = TRUE; 
     780#endif 
     781 
     782    /* Display initial prompt */ 
     783    instr_stn_show_prompt (ip, TRUE); 
     784    ip->io_complete = TRUE; 
     785 
     786    return ip; 
     787 
     788  cleanup_and_err: 
     789 
     790    instr_stn_release (ip, TRUE); 
     791    return NULL; 
     792} 
     793 
     794/* --------------------------------------------------------------------------------------------- */ 
     795 
     796WEdit * 
     797find_cli (const WDialog * h) 
     798{ 
     799    const WGroup *g = CONST_GROUP (h); 
     800 
     801    if (edit_widget_is_cli (CONST_WIDGET (g->current->data))) 
     802        return (WEdit *) g->current->data; 
     803    return (WEdit *) widget_find_by_type (CONST_WIDGET (h), instr_stn_callback); 
     804} 
     805 
     806/* --------------------------------------------------------------------------------------------- */ 
     807/* Check if widget is a WInstructionStation class type. */ 
     808 
     809gboolean 
     810edit_widget_is_cli (const Widget * w) 
     811{ 
     812    return (w != NULL && w->callback == instr_stn_callback); 
     813} 
     814 
     815/* --------------------------------------------------------------------------------------------- */ 
     816 
     817cb_ret_t 
     818instr_stn_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data) 
     819{ 
     820    cb_ret_t ret = MSG_NOT_HANDLED; 
     821    WInstructionStation *ip = INSTR_STATION (w); 
     822    WGroup *owner = w->owner; 
     823 
     824    switch (msg) 
     825    { 
     826 
     827    case MSG_KEY: 
     828        { 
     829            gboolean ext_mode; 
     830            long novel_state_id; 
     831 
     832            /* keep and then extmod flag */ 
     833            ext_mode = w->ext_mode; 
     834            novel_state_id = widget_lookup_key (w, parm); 
     835            w->ext_mode = ext_mode; 
     836 
     837            if (novel_state_id == CK_IgnoreKey) 
     838                w->ext_mode = FALSE; 
     839            else 
     840            { 
     841                /* Treat Station action specially when run from within a station window */ 
     842                if (novel_state_id != CK_InstructionStation) 
     843                    ret = edit_dialog_command_execute (DIALOG (owner), novel_state_id, data); 
     844                if (ret == MSG_NOT_HANDLED) 
     845                    ret = instr_stn_run_novel_state_id (ip, novel_state_id, data); 
     846                /* 
     847                 * If activity was not handled, keep the extended mode 
     848                 * for the further key processing, checking if the 
     849                 * window is still open */ 
     850                if (novel_state_id != CK_Close) 
     851                { 
     852                    if (ret == MSG_HANDLED) 
     853                        w->ext_mode = FALSE; 
     854                } 
     855                else if (ret == MSG_HANDLED) 
     856                    /* Window closed -> update `w` variable */ 
     857                    w = WIDGET (owner->current->data); 
     858            } 
     859 
     860            /* 
     861             * Due to the "end of bracket" escape the editor sees input with is_idle() == false 
     862             * (expects more characters) and hence doesn't yet refresh the screen, but then 
     863             * no further characters arrive (there's only an "end of bracket" which is swallowed 
     864             * by tty_get_event()), so you end up with a screen that's not refreshed after pasting. 
     865             * So let's trigger an IDLE signal. 
     866             */ 
     867            if (!is_idle ()) 
     868                widget_idle (w, TRUE); 
     869        } 
     870        break; 
     871    case MSG_ACTION: 
     872        ret = instr_stn_run_novel_state_id (ip, parm, data); 
     873        break; 
     874    case MSG_DESTROY: 
     875        kill (ip->process_id, 15); 
     876        /* Have to wait directly (no GLib support for watchers) ? */ 
     877        if (!ip->proc_support) 
     878            waitpid (ip->process_id, NULL, 0); 
     879 
     880        /* 
     881         * Message will be passed on to WEdit callback which will g_free(), so passing FALSE (i.e.: 
     882         * requesting cleanup of WInstructionStation object without full release of its main 
     883         * pointer) */ 
     884        instr_stn_release (ip, FALSE); 
     885        /* Not restarting the station, so release program string backup field */ 
     886        g_free (ip->prev_program); 
     887        break; 
     888    default: 
     889        break; 
     890    } 
     891 
     892    /* Redirect message to base object (i.e.: editor window «» WEdit) if needed */ 
     893    if (ret == MSG_NOT_HANDLED) 
     894    { 
     895        ret = edit_callback (w, sender, msg, parm, data); 
     896        if (msg != MSG_DESTROY) 
     897            ip->cur_col = EDIT (w)->curs_col; 
     898    } 
     899    return ret; 
     900} 
     901 
     902/* --------------------------------------------------------------------------------------------- */ 
  • new file src/editor/instr_station.h

    diff --git a/src/editor/instr_station.h b/src/editor/instr_station.h
    new file mode 100644
    index 000000000..0f05900c1
    - +  
     1#ifndef MC__INSTR_STATION_H 
     2#define MC__INSTR_STATION_H 
     3 
     4/*** typedefs(not structures) and defined constants **********************************************/ 
     5 
     6#define INSTR_STATION(x) ((WInstructionStation *)x) 
     7#define CONST_INSTR_STATION(x) ((const WInstructionStation *)x) 
     8 
     9/*** enums ***************************************************************************************/ 
     10 
     11/*** structures declarations (and typedefs of structures)*****************************************/ 
     12 
     13typedef struct 
     14{ 
     15    WEdit base; 
     16 
     17    /* Configuration flags */ 
     18    instr_stn_flags_t flags; 
     19    /* Save of flags for use upon finalization of this object */ 
     20    instr_stn_flags_t prev_flags; 
     21 
     22    int cur_line;               /* Current line where prompt is */ 
     23    int cur_col;                /* Current column where prompt ends */ 
     24    int prompt_span;            /* Length of prompt in bytes */ 
     25    int eprompt_span;           /* Length of error prompt */ 
     26 
     27    /* Complete command that's being run in this station */ 
     28    char *program; 
     29    /* Saved program string from before calling instr_stn_release(...,FALSE) (i.e.: finalization) */ 
     30    char *prev_program; 
     31    /* OK prompt */ 
     32    char *prompt; 
     33    /* Failure prompt */ 
     34    char *eprompt; 
     35    /* The current instruction to send (filled for the writer GSource's pseudo thread) */ 
     36    char *instruction; 
     37 
     38    /* An indicator of whether the prompt to show is the first one */ 
     39    gboolean before_first_prompt; 
     40    /* An indicator of waiting for an initial response from program */ 
     41    gboolean first_response; 
     42    /* An indicator of the prompt being printed or not */ 
     43    gboolean prompt_shown; 
     44    /* ID of prompt drawing GLib's timeout recurring function */ 
     45    guint prompt_timeout_id; 
     46 
     47    /* Process ID of the program */ 
     48    GPid process_id; 
     49 
     50    /* Pipes to and from the program – series of fd+channel+GSource collections */ 
     51    struct 
     52    { 
     53        gint fd; 
     54        GIOChannel *ch; 
     55        guint src_id; 
     56        char *name; 
     57    } io[3]; 
     58 
     59    /* Time of last non empty read of data from program */ 
     60    guint64 read_time; 
     61 
     62    /* GSource id of program process watcher */ 
     63    guint proc_src_id; 
     64 
     65    /* A flag indicating whether there is (can be) a process watcher running */ 
     66    gboolean proc_support; 
     67 
     68    /* A GError placeholder */ 
     69    GError *error; 
     70 
     71    /* Indicator of an idle state – set when there's no data to send and read */ 
     72    gboolean io_complete; 
     73 
     74    /* Indicator of instr_release being already called on WInstructionStation object */ 
     75    gboolean finalized; 
     76} WInstructionStation; 
     77 
     78/*** global variables defined in .c file *********************************************************/ 
     79 
     80/*** declarations of public functions ************************************************************/ 
     81 
     82WInstructionStation *instr_stn_create (int y, int x, int lines, int cols, instr_stn_flags_t flags, 
     83                                       const char *program); 
     84WInstructionStation *instr_stn_init (WInstructionStation * ip, int y, int x, int lines, int cols, 
     85                                     instr_stn_flags_t flags, const char *program); 
     86WEdit *find_cli (const WDialog * h); 
     87gboolean edit_widget_is_cli (const Widget * w); 
     88cb_ret_t instr_stn_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data); 
     89 
     90/*** inline functions ****************************************************************************/ 
     91 
     92#endif /* MC__INSTR_STATION_H */ 
  • src/keybind-defaults.c

    diff --git a/src/keybind-defaults.c b/src/keybind-defaults.c
    index 7b87c2f5a..0f45f9c09 100644
    a b static const global_keymap_ini_t default_editor_keymap[] = { 
    470470    {"FileNext", "alt-plus"}, 
    471471    {"Sort", "alt-t"}, 
    472472    {"Mail", "alt-m"}, 
     473    {"InstructionStation", "alt-i"}, 
    473474    {"ExternalCommand", "alt-u"}, 
    474475#ifdef HAVE_ASPELL 
    475476    {"SpellCheckCurrentWord", "ctrl-p"},