Ticket #4196: InstructionStation_v3.9.patch

File InstructionStation_v3.9.patch, 81.6 KB (added by psprint, 3 years ago)
  • Makefile.am

    From 8a0a1b7ffc4b9570adc71497117d50e9ed8e75e6 Mon Sep 17 00:00:00 2001
    From: Sebastian Gniazdowski <sgniazdowski@gmail.com>
    Date: Wed, 17 Feb 2021 15:02:15 -0600
    Subject: A simple CLI window for MCEdit
    
    ---
     Makefile.am                |  49 +-
     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     |  28 +-
     src/editor/edit.c          | 139 +++---
     src/editor/editcmd.c       |  58 ++-
     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 | 994 +++++++++++++++++++++++++++++++++++++
     src/editor/instr_station.h |  95 ++++
     src/keybind-defaults.c     |   1 +
     20 files changed, 1633 insertions(+), 350 deletions(-)
     create mode 100644 src/editor/instr_station.c
     create mode 100644 src/editor/instr_station.h
    
    diff --git a/Makefile.am b/Makefile.am
    index f86f6ed38..196a474af 100644
    a b CONFIG_STATUS_DEPENDENCIES = $(top_srcdir)/version.h 
    2525        cppcheck-portability \ 
    2626        cppcheck-style \ 
    2727        cppcheck-warning \ 
    28         cppcheck-all 
     28        cppcheck-all \ 
     29        tags \ 
     30        tags-emacs \ 
     31        tags-vim 
    2932 
     33# Invoke a ctags utility to generate Emacs and Vim's format tag file. 
     34# The meaning of the flags: 
     35# - `p` kind enables function prototypes, 
     36# - `x` kind enables extern and forward variable declarations, 
     37# - `S` field enables function signatures, 
     38# - `properties` field enables static, volatile, etc. properties, 
     39# - `macrodef` field enables macro definitions. 
     40# 
     41# Generate two formats at once: Emacs and Vim's. 
     42tags: tags-emacs tags-vim 
     43 
     44# Build only the Emacs-style `TAGS` file. 
     45tags-emacs: 
     46        @if type ctags >/dev/null 2>&1; then \ 
     47                if ctags --version | grep >/dev/null 2>&1 "Universal Ctags"; then \ 
     48                        ctags -e -R --languages=C --langmap=C:.h.c --c-kinds=+px \ 
     49                            --fields=+S --fields-c=+"{properties}" --fields-c=+"{macrodef}" \ 
     50                            --extras=+qr; \ 
     51                else \ 
     52                        ctags -e -R --languages=C --langmap=C:.h.c --c-kinds=+px \ 
     53                            --fields=+S; \ 
     54                fi; \ 
     55                printf "Created the Emacs \`TAGS\` file.\\n"; \ 
     56        else \ 
     57            printf 'Error: Please install a Ctags (e.g.: either the Exuberant or Universal %b' \ 
     58            'version) utility first.\n'; \ 
     59        fi 
     60 
     61# Build only the Vim-style `tags` file. 
     62tags-vim: 
     63        @if type ctags >/dev/null 2>&1; then \ 
     64                if ctags --version | grep >/dev/null 2>&1 "Universal Ctags"; then \ 
     65                        ctags -R --languages=C --langmap=C:.h.c --c-kinds=+px \ 
     66                            --fields=+S --fields-c=+"{properties}" --fields-c=+"{macrodef}" \ 
     67                            --extras=+qr; \ 
     68                else \ 
     69                        ctags -R --languages=C --langmap=C:.h.c --c-kinds=+px \ 
     70                            --fields=+S; \ 
     71                fi; \ 
     72                printf "Created the Vim's style \`tags\` file.\\n"; \ 
     73        else \ 
     74            printf 'Error: Please install a Ctags (e.g.: either the Exuberant or Universal %b' \ 
     75            'version) utility first.\n'; \ 
     76        fi 
    3077 
    3178update-version: 
    3279        @if test -x $(top_srcdir)/maint/utils/version.sh; then \ 
  • lib/global.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..8c34d0c82 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        guint 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..05beca13f 100644
    a b Sort = alt-t 
    350350Mail = alt-m 
    351351ParagraphFormat = alt-p 
    352352MatchBracket = alt-b 
     353InstructionStation = alt-s 
    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..f8af8b03d 100644
    a b Sort = alt-t 
    349349# Mail = 
    350350ParagraphFormat = alt-p 
    351351# MatchBracket = 
     352InstructionStation = alt-s 
    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..9d09f181e 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_move_to_top (WEdit * edit); 
     192void edit_move_to_bottom (WEdit * edit); 
     193void edit_cursor_to_bol (WEdit * edit); 
     194void edit_cursor_to_eol (WEdit * edit); 
     195void edit_insert_string (WEdit * edit, char *str_text, gsize len); 
    172196void edit_insert (WEdit * edit, int c); 
    173197void edit_insert_over (WEdit * edit); 
    174198void edit_cursor_move (WEdit * edit, off_t increment); 
    char *edit_get_write_filter (const vfs_path_t * write_name_vpath, 
    182206gboolean edit_save_confirm_cmd (WEdit * edit); 
    183207gboolean edit_save_as_cmd (WEdit * edit); 
    184208WEdit *edit_init (WEdit * edit, int y, int x, int lines, int cols, 
    185                   const vfs_path_t * filename_vpath, long line); 
     209                  const vfs_path_t * filename_vpath, long line, edit_init_flags_t flags); 
    186210gboolean edit_clean (WEdit * edit); 
    187211gboolean edit_ok_to_exit (WEdit * edit); 
    188212gboolean edit_load_cmd (WDialog * h); 
    void edit_move_to_line (WEdit * e, long line); 
    236260void edit_move_display (WEdit * e, long line); 
    237261void edit_word_wrap (WEdit * edit); 
    238262int edit_sort_cmd (WEdit * edit); 
     263gboolean edit_add_instr_stn_window (WDialog * h, instr_stn_flags_t flags, void *data); 
     264 
    239265int edit_ext_cmd (WEdit * edit); 
    240266 
    241267int edit_store_macro_cmd (WEdit * edit); 
  • src/editor/edit.c

    diff --git a/src/editor/edit.c b/src/editor/edit.c
    index edda1f832..87c4f766b 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_end_page (WEdit * edit) 
    821821} 
    822822 
    823823 
    824 /* --------------------------------------------------------------------------------------------- */ 
    825 /** goto beginning of text */ 
    826  
    827 static void 
    828 edit_move_to_top (WEdit * edit) 
    829 { 
    830     if (edit->buffer.curs_line != 0) 
    831     { 
    832         edit_cursor_move (edit, -edit->buffer.curs1); 
    833         edit_move_to_prev_col (edit, 0); 
    834         edit->force |= REDRAW_PAGE; 
    835         edit->search_start = 0; 
    836         edit_update_curs_row (edit); 
    837     } 
    838 } 
    839  
    840 /* --------------------------------------------------------------------------------------------- */ 
    841 /** goto end of text */ 
    842  
    843 static void 
    844 edit_move_to_bottom (WEdit * edit) 
    845 { 
    846     if (edit->buffer.curs_line < edit->buffer.lines) 
    847     { 
    848         edit_move_down (edit, edit->buffer.lines - edit->curs_row, FALSE); 
    849         edit->start_display = edit->buffer.size; 
    850         edit->start_line = edit->buffer.lines; 
    851         edit_scroll_upward (edit, WIDGET (edit)->lines - 1); 
    852         edit->force |= REDRAW_PAGE; 
    853     } 
    854 } 
    855  
    856 /* --------------------------------------------------------------------------------------------- */ 
    857 /** goto beginning of line */ 
    858  
    859 static void 
    860 edit_cursor_to_bol (WEdit * edit) 
    861 { 
    862     edit_cursor_move (edit, edit_buffer_get_current_bol (&edit->buffer) - edit->buffer.curs1); 
    863     edit->search_start = edit->buffer.curs1; 
    864     edit->prev_col = edit_get_col (edit); 
    865     edit->over_col = 0; 
    866 } 
    867  
    868 /* --------------------------------------------------------------------------------------------- */ 
    869 /** goto end of line */ 
    870  
    871 static void 
    872 edit_cursor_to_eol (WEdit * edit) 
    873 { 
    874     edit_cursor_move (edit, edit_buffer_get_current_eol (&edit->buffer) - edit->buffer.curs1); 
    875     edit->search_start = edit->buffer.curs1; 
    876     edit->prev_col = edit_get_col (edit); 
    877     edit->over_col = 0; 
    878 } 
    879  
    880824/* --------------------------------------------------------------------------------------------- */ 
    881825 
    882826static unsigned long 
    edit_insert_file (WEdit * edit, const vfs_path_t * filename_vpath) 
    20752019 
    20762020WEdit * 
    20772021edit_init (WEdit * edit, int y, int x, int lines, int cols, const vfs_path_t * filename_vpath, 
    2078            long line) 
     2022           long line, edit_init_flags_t flags) 
    20792023{ 
    20802024    gboolean to_free = FALSE; 
    20812025 
    edit_init (WEdit * edit, int y, int x, int lines, int cols, const vfs_path_t * f 
    20972041        edit->fullscreen = fullscreen; 
    20982042        edit->loc_prev = loc_prev; 
    20992043    } 
    2100     else 
     2044    if (has_flag (flags, EDIT_DO_INIT_BASE_CLASS) || edit == NULL) 
    21012045    { 
    21022046        Widget *w; 
    2103         edit = g_malloc0 (sizeof (WEdit)); 
    2104         to_free = TRUE; 
     2047        if (edit == NULL) 
     2048        { 
     2049            edit = g_malloc0 (sizeof (WEdit)); 
     2050            to_free = TRUE; 
     2051        } 
    21052052 
    21062053        w = WIDGET (edit); 
    21072054        widget_init (w, y, x, lines, cols, NULL, NULL); 
    edit_reload_line (WEdit * edit, const vfs_path_t * filename_vpath, long line) 
    22382185    e->fullscreen = edit->fullscreen; 
    22392186    e->loc_prev = edit->loc_prev; 
    22402187 
    2241     if (edit_init (e, w->y, w->x, w->lines, w->cols, filename_vpath, line) == NULL) 
     2188    if (edit_init (e, w->y, w->x, w->lines, w->cols, filename_vpath, line, 0) == NULL) 
    22422189    { 
    22432190        g_free (e); 
    22442191        return FALSE; 
    edit_push_redo_action (WEdit * edit, long c) 
    24952442        edit->redo_stack_bottom = edit->redo_stack_pointer = 0; 
    24962443} 
    24972444 
     2445/* --------------------------------------------------------------------------------------------- */ 
     2446/** goto beginning of text */ 
     2447 
     2448void 
     2449edit_move_to_top (WEdit * edit) 
     2450{ 
     2451    if (edit->buffer.curs_line != 0) 
     2452    { 
     2453        edit_cursor_move (edit, -edit->buffer.curs1); 
     2454        edit_move_to_prev_col (edit, 0); 
     2455        edit->force |= REDRAW_PAGE; 
     2456        edit->search_start = 0; 
     2457        edit_update_curs_row (edit); 
     2458    } 
     2459} 
     2460 
     2461/* --------------------------------------------------------------------------------------------- */ 
     2462/** goto end of text */ 
     2463 
     2464void 
     2465edit_move_to_bottom (WEdit * edit) 
     2466{ 
     2467    if (edit->buffer.curs_line < edit->buffer.lines) 
     2468    { 
     2469        edit_move_down (edit, edit->buffer.lines - edit->curs_row, FALSE); 
     2470        edit->start_display = edit->buffer.size; 
     2471        edit->start_line = edit->buffer.lines; 
     2472        edit_scroll_upward (edit, WIDGET (edit)->lines - 1); 
     2473        edit->force |= REDRAW_PAGE; 
     2474    } 
     2475} 
     2476 
     2477/* --------------------------------------------------------------------------------------------- */ 
     2478/** goto beginning of line */ 
     2479 
     2480void 
     2481edit_cursor_to_bol (WEdit * edit) 
     2482{ 
     2483    edit_cursor_move (edit, edit_buffer_get_current_bol (&edit->buffer) - edit->buffer.curs1); 
     2484    edit->search_start = edit->buffer.curs1; 
     2485    edit->prev_col = edit_get_col (edit); 
     2486    edit->over_col = 0; 
     2487} 
     2488 
     2489/* --------------------------------------------------------------------------------------------- */ 
     2490/** goto end of line */ 
     2491 
     2492void 
     2493edit_cursor_to_eol (WEdit * edit) 
     2494{ 
     2495    edit_cursor_move (edit, edit_buffer_get_current_eol (&edit->buffer) - edit->buffer.curs1); 
     2496    edit->search_start = edit->buffer.curs1; 
     2497    edit->prev_col = edit_get_col (edit); 
     2498    edit->over_col = 0; 
     2499} 
     2500 
     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..de967b3fe 100644
    a b  
    7878#include "spell_dialogs.h" 
    7979#endif 
    8080#include "etags.h" 
     81#include "instr_station.h" 
    8182 
    8283/*** global variables ****************************************************************************/ 
    8384 
    edit_complete_word_insert_recoded_completion (WEdit * edit, char *completion, gs 
    15291530/*** public functions ****************************************************************************/ 
    15301531/* --------------------------------------------------------------------------------------------- */ 
    15311532 
     1533gboolean 
     1534edit_newline_end_check_only (const WEdit * e) 
     1535{ 
     1536    const edit_buffer_t *buf = &e->buffer; 
     1537    return (buf->size > 0 && edit_buffer_get_byte (buf, buf->size - 1) == '\n'); 
     1538} 
     1539 
     1540/* --------------------------------------------------------------------------------------------- */ 
     1541 
    15321542void 
    15331543edit_refresh_cmd (void) 
    15341544{ 
    edit_close_cmd (WEdit * edit) 
    22512261        group_remove_widget (w); 
    22522262        widget_destroy (w); 
    22532263 
    2254         if (edit_widget_is_editor (CONST_WIDGET (g->current->data))) 
     2264        if (edit_widget_is_editor (CONST_WIDGET (g->current->data)) 
     2265            || edit_widget_is_cli (CONST_WIDGET (g->current->data))) 
    22552266            edit = (WEdit *) (g->current->data); 
    22562267        else 
    22572268        { 
     2269            /* Look for a file window or a CLI window */ 
    22582270            edit = find_editor (DIALOG (g)); 
     2271            if (!edit) 
     2272                edit = find_cli (DIALOG (g)); 
    22592273            if (edit != NULL) 
    22602274                widget_select (w); 
    22612275        } 
    edit_ext_cmd (WEdit * edit) 
    32253239{ 
    32263240    char *exp, *tmp, *tmp_edit_temp_file; 
    32273241    int e; 
     3242    gboolean run_in_cli = FALSE, discard_output = FALSE; 
     3243 
     3244    quick_widget_t chbox[5] = { QUICK_SEPARATOR (TRUE), 
     3245        QUICK_CHECKBOX ("&Open in CLI window", &run_in_cli, NULL), 
     3246        QUICK_CHECKBOX ("&Discard output", &discard_output, NULL), 
     3247        QUICK_END 
     3248    }; 
    32283249 
    32293250    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); 
     3251        input_dialog_ext (_("Paste output of external command"), 
     3252                          _("Enter shell command(s):"), MC_HISTORY_EDIT_PASTE_EXTCMD, 
     3253                          INPUT_LAST_TEXT, 
     3254                          INPUT_COMPLETE_FILENAMES | INPUT_COMPLETE_VARIABLES | 
     3255                          INPUT_COMPLETE_USERNAMES | INPUT_COMPLETE_HOSTNAMES | INPUT_COMPLETE_CD | 
     3256                          INPUT_COMPLETE_COMMANDS | INPUT_COMPLETE_SHELL_ESC, chbox); 
    32353257 
    32363258    if (!exp) 
    32373259        return 1; 
    32383260 
    3239     tmp_edit_temp_file = mc_config_get_full_path (EDIT_HOME_TEMP_FILE); 
    3240     tmp = g_strconcat (exp, " > ", tmp_edit_temp_file, (char *) NULL); 
    3241     g_free (tmp_edit_temp_file); 
     3261    /* Should start a CLI window with the command ? */ 
     3262    if (run_in_cli && !discard_output) 
     3263    { 
     3264        edit_add_instr_stn_window (DIALOG (WIDGET (edit)->owner), 
     3265                                   INSTR_STN_NO_STDIN | INSTR_STN_NO_PROMPT, exp); 
     3266        g_free (exp); 
     3267        return 0; 
     3268    } 
     3269 
     3270    /* Should discard output ? */ 
     3271    if (discard_output) 
     3272        tmp = g_strdup_printf ("%s >/dev/null 2>&1", exp); 
     3273    else 
     3274    { 
     3275        tmp_edit_temp_file = mc_config_get_full_path (EDIT_HOME_TEMP_FILE); 
     3276        tmp = g_strconcat (exp, " > ", tmp_edit_temp_file, (char *) NULL); 
     3277        g_free (tmp_edit_temp_file); 
     3278    } 
     3279 
     3280    /* Run instruction */ 
    32423281    e = system (tmp); 
    32433282    g_free (tmp); 
    32443283    g_free (exp); 
    edit_ext_cmd (WEdit * edit) 
    32513290 
    32523291    edit->force |= REDRAW_COMPLETELY; 
    32533292 
     3293    if (!discard_output) 
    32543294    { 
    32553295        vfs_path_t *tmp_vpath; 
    32563296 
  • 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..76c7bab03
    - +  
     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/* --------------------------------------------------------------------------------------------- */ 
     68static void instr_stn_clear_prompt (WInstructionStation * ip); 
     69static void instr_stn_show_prompt (WInstructionStation * ip, gboolean is_ok); 
     70 
     71static char * 
     72instr_stn_get_instruction_text (WInstructionStation * ip) 
     73{ 
     74    char *ret_str; 
     75    GString *instr; 
     76    off_t bol, eol, begin_instr; 
     77    long size; 
     78    int idx, byte; 
     79 
     80    /* Calculate offset of text after prompt */ 
     81    bol = edit_buffer_get_current_bol (&EDIT (ip)->buffer); 
     82    eol = edit_buffer_get_current_eol (&EDIT (ip)->buffer); 
     83    begin_instr = bol + ip->prompt_span; 
     84 
     85    /* Is there anything entered? */ 
     86    size = ((long) eol) - ((long) begin_instr); 
     87    if (size <= 0) 
     88        return NULL; 
     89 
     90    /* Allocate expected size string and fill it */ 
     91    instr = g_string_sized_new (size + 2); 
     92    for (idx = 0; idx < size; idx++) 
     93    { 
     94        byte = edit_buffer_get_byte (&EDIT (ip)->buffer, begin_instr + idx); 
     95        g_string_append_c (instr, byte); 
     96    } 
     97 
     98    /* Append new line if needed */ 
     99    if (instr->str[instr->len - 1] != '\n') 
     100        g_string_append_c (instr, '\n'); 
     101 
     102    /* Return char buffer */ 
     103    ret_str = instr->str; 
     104    g_string_free (instr, FALSE); 
     105    return ret_str; 
     106} 
     107 
     108/* --------------------------------------------------------------------------------------------- */ 
     109 
     110static gboolean 
     111append_text_to_buffer (WInstructionStation * ip, char *buf, gsize count) 
     112{ 
     113    gsize idx = 0, nl_count = 0; 
     114    gboolean is_first = ip->first_response; 
     115 
     116    /* Count number of read lines */ 
     117    char *p = buf; 
     118    while (idx++ < count) 
     119        if (*p++ == '\n') 
     120            nl_count++; 
     121 
     122    /* Move to next line after prompt */ 
     123    if (ip->first_response) 
     124    { 
     125        edit_cursor_to_eol (EDIT (ip)); 
     126        edit_insert (EDIT (ip), '\n'); 
     127        nl_count++; 
     128        ip->first_response = FALSE; 
     129        ip->prompt_shown = FALSE; 
     130    } 
     131    else if (ip->prompt_shown) 
     132        instr_stn_clear_prompt (ip); 
     133 
     134    /* Print whole buffer into file's window  */ 
     135    edit_insert_string (EDIT (ip), buf, count); 
     136 
     137    /* Increase the row pointer */ 
     138    ip->cur_line += nl_count; 
     139 
     140    return is_first; 
     141} 
     142 
     143/* --------------------------------------------------------------------------------------------- */ 
     144 
     145static gboolean 
     146helper_try_append_flow_control_data (WInstructionStation * ip, gboolean force, gboolean restore) 
     147{ 
     148    off_t curs_save = -1; 
     149    long top_line_save = -1; 
     150 
     151    /* Is there any "control flow" bufered data ? Should be at prompt or force should be TRUE */ 
     152    if (ip->flow_ctrl_buf == NULL || ip->flow_ctrl_buf->len == 0 
     153        || ((EDIT (ip)->buffer.curs_line != ip->cur_line) && !force)) 
     154        return FALSE; 
     155 
     156    if (force) 
     157    { 
     158        if (restore) 
     159        { 
     160            top_line_save = EDIT (ip)->start_line; 
     161            curs_save = EDIT (ip)->buffer.curs1; 
     162        } 
     163        edit_move_to_bottom (EDIT (ip)); 
     164        edit_cursor_to_eol (EDIT (ip)); 
     165    } 
     166 
     167    /* First, append text postponed earlier */ 
     168    append_text_to_buffer (ip, (char *) ip->flow_ctrl_buf->data, ip->flow_ctrl_buf->len); 
     169    g_byte_array_free (ip->flow_ctrl_buf, TRUE); 
     170    ip->flow_ctrl_buf = NULL; 
     171 
     172    /* Ensure that prompt is drawn now, when at end */ 
     173    instr_stn_show_prompt (ip, TRUE); 
     174 
     175    if (restore && curs_save != -1) 
     176    { 
     177        edit_cursor_move (EDIT (ip), -1 * (EDIT (ip)->buffer.curs1 - curs_save)); 
     178        edit_move_display (EDIT (ip), top_line_save); 
     179    } 
     180    return TRUE; 
     181} 
     182 
     183/* --------------------------------------------------------------------------------------------- */ 
     184 
     185static gboolean 
     186instr_stn_release (WInstructionStation * ip, gboolean free_all) 
     187{ 
     188    /* Already relased? */ 
     189    if (ip == NULL) 
     190    { 
     191        if (free_all) 
     192            return TRUE; 
     193        else 
     194            return FALSE; 
     195    } 
     196 
     197    /* Already cleaned up (for a restart of program) ? */ 
     198    if (ip->finalized && !free_all) 
     199        return FALSE; 
     200    ip->finalized = TRUE; 
     201 
     202    /* Close process watcher first, suppressing *program_ended_cb() callback */ 
     203    if (ip->proc_src_id != 0) 
     204    { 
     205        GSource *src; 
     206        src = g_main_context_find_source_by_id (NULL, ip->proc_src_id); 
     207        if (src != NULL) 
     208            g_source_destroy (src); 
     209        ip->proc_src_id = 0; 
     210    } 
     211 
     212    /* Append any buffered text first moving to end if needed */ 
     213    helper_try_append_flow_control_data (ip, TRUE, FALSE); 
     214 
     215    /* Release the pipes, channels, etc. */ 
     216    for (int i = 0; i <= STDERR; i++) 
     217    { 
     218        /* Source */ 
     219        if (ip->io[i].src_id != 0) 
     220        { 
     221            GSource *src; 
     222            src = g_main_context_find_source_by_id (NULL, ip->io[i].src_id); 
     223            if (src != NULL) 
     224                g_source_destroy (src); 
     225            ip->io[i].src_id = 0; 
     226        } 
     227        /* Channel */ 
     228        if (ip->io[i].ch != NULL) 
     229        { 
     230            g_io_channel_unref (ip->io[i].ch); 
     231            ip->io[i].ch = NULL; 
     232        } 
     233        /* Pipe */ 
     234        if (ip->io[i].fd >= 0) 
     235        { 
     236            close (ip->io[i].fd); 
     237            ip->io[i].fd = -1; 
     238        } 
     239    } 
     240 
     241    /* Remove prompt guard recurring timeout callback function */ 
     242    if (ip->prompt_timeout_id != 0) 
     243    { 
     244        g_source_remove (ip->prompt_timeout_id); 
     245        ip->prompt_timeout_id = 0; 
     246    } 
     247 
     248    MC_PTR_FREE (ip->instruction); 
     249    /* Release prompt */ 
     250    MC_PTR_FREE (ip->prompt); 
     251    /* Release error prompt */ 
     252    MC_PTR_FREE (ip->eprompt); 
     253 
     254    /* Clear prompt information */ 
     255    ip->prompt_span = 0; 
     256    ip->eprompt_span = 0; 
     257 
     258    /* Set various indicator flags to some inactive state */ 
     259    ip->io_complete = TRUE; 
     260    ip->first_response = TRUE; 
     261    ip->before_first_prompt = TRUE; 
     262    ip->prompt_shown = FALSE; 
     263    ip->flags = INSTR_STN_NO_FLAGS; 
     264 
     265    if (ip->flow_ctrl_buf != NULL) 
     266    { 
     267        g_byte_array_free (ip->flow_ctrl_buf, TRUE); 
     268        ip->flow_ctrl_buf = NULL; 
     269    } 
     270 
     271    /* Clear any GError */ 
     272    if (ip->error != NULL) 
     273    { 
     274        g_error_free (ip->error); 
     275        ip->error = NULL; 
     276    } 
     277 
     278    /* Release space occupied by WInstructionStation object? */ 
     279    if (free_all) 
     280    { 
     281        MC_PTR_FREE (ip->program); 
     282        /* Free main object */ 
     283        g_free (ip); 
     284    } 
     285    else 
     286        /* Value is still accessible in prev_program field */ 
     287        ip->program = NULL; 
     288 
     289    return TRUE; 
     290} 
     291 
     292/* --------------------------------------------------------------------------------------------- */ 
     293 
     294static cb_ret_t 
     295instr_stn_run_novel_state_id (WInstructionStation * ip, long novel_state_id, void *data) 
     296{ 
     297    WGroup *owner = WIDGET (ip)->owner; 
     298    cb_ret_t ret = MSG_NOT_HANDLED; 
     299 
     300    switch (novel_state_id) 
     301    { 
     302    case CK_BackSpace: 
     303        if (ip->cur_col <= ip->prompt_span) 
     304            ret = MSG_HANDLED; 
     305        else 
     306        { 
     307            EDIT (ip)->force |= REDRAW_PAGE; 
     308            edit_update_screen (EDIT (ip)); 
     309        } 
     310        break; 
     311    case CK_Enter: 
     312    case CK_Return: 
     313        if (EDIT (ip)->buffer.curs_line == ip->cur_line) 
     314        { 
     315            ip->instruction = instr_stn_get_instruction_text (ip); 
     316            ip->io_complete = FALSE; 
     317            /* Should dispatch to write callback which will send instruction */ 
     318            g_main_context_iteration (NULL, 0); 
     319            repaint_screen (); 
     320        } 
     321        ret = MSG_HANDLED; 
     322        break; 
     323        /* InstructionStation activity invoked when focus is at a station window behaves differently */ 
     324    case CK_InstructionStation: 
     325        { 
     326            /* Save needed information */ 
     327            char *program_save; 
     328 
     329            /* End previous program */ 
     330            if (kill (ip->process_id, 15) == -1) 
     331                mc_log ("Problem sending TERM signal to %d: %s.", ip->process_id, 
     332                        g_strerror (errno)); 
     333            else 
     334                mc_log ("Ending process %d.", ip->process_id); 
     335            if (!ip->proc_support) 
     336                waitpid (ip->process_id, NULL, 0); 
     337 
     338            /* Allow process watcher callbacks to be called */ 
     339            g_usleep (150000); 
     340            g_main_context_iteration (NULL, FALSE); 
     341            /* Allow the process end message to flash noticeably long */ 
     342            g_usleep (200000); 
     343 
     344            /* Clean WInstructionStation object */ 
     345            instr_stn_release (ip, FALSE); 
     346            /* ...and reinitialize it */ 
     347            program_save = data != NULL ? g_strdup (data) : ip->prev_program; 
     348            if (instr_stn_init (ip, -1, -1, -1, -1, ip->prev_flags, program_save) == NULL) 
     349            { 
     350                /* Init failed -> close faulty station window */ 
     351                ret = 
     352                    edit_dialog_callback (WIDGET (owner), WIDGET (ip), MSG_ACTION, CK_Close, NULL); 
     353                if (ret == MSG_HANDLED) 
     354                    /* Update pointer in case of some future code like widget_draw(ip), etc. */ 
     355                    ip = INSTR_STATION (owner->current->data); 
     356            } 
     357            else 
     358            { 
     359                repaint_screen (); 
     360                ret = MSG_HANDLED; 
     361            } 
     362            g_free (program_save); 
     363        } 
     364        break; 
     365    default: 
     366        break; 
     367    } 
     368    return ret; 
     369} 
     370 
     371/* --------------------------------------------------------------------------------------------- */ 
     372 
     373static void 
     374instr_stn_show_prompt (WInstructionStation * ip, gboolean is_ok) 
     375{ 
     376    char *sel_prompt = is_ok ? ip->prompt : ip->eprompt; 
     377    int sel_prompt_span = is_ok ? ip->prompt_span : ip->eprompt_span; 
     378 
     379    if (has_flag (ip->flags, INSTR_STN_NO_PROMPT)) 
     380        return; 
     381 
     382    /* Print prompt */ 
     383    if (!edit_newline_end_check_only (EDIT (ip)) && !ip->before_first_prompt) 
     384    { 
     385        edit_insert (EDIT (ip), '\n'); 
     386        ip->cur_line += 1; 
     387    } 
     388    else 
     389        ip->before_first_prompt = FALSE; 
     390    edit_insert_string (EDIT (ip), sel_prompt, sel_prompt_span); 
     391 
     392    /* Raise an indicator flag */ 
     393    ip->prompt_shown = TRUE; 
     394 
     395    /* Set cursor position to reflect state */ 
     396    ip->cur_col = sel_prompt_span; 
     397} 
     398 
     399/* --------------------------------------------------------------------------------------------- */ 
     400/* Remove prompt from buffer (basically clear current line). */ 
     401 
     402static void 
     403instr_stn_clear_prompt (WInstructionStation * ip) 
     404{ 
     405    off_t bol, eol, idx; 
     406 
     407    if (has_flag (ip->flags, INSTR_STN_NO_PROMPT)) 
     408        return; 
     409 
     410    edit_cursor_to_eol (EDIT (ip)); 
     411    eol = edit_buffer_get_current_eol (&EDIT (ip)->buffer); 
     412    bol = edit_buffer_get_current_bol (&EDIT (ip)->buffer); 
     413    for (idx = 0; idx < eol - bol; idx++) 
     414        edit_backspace (EDIT (ip), TRUE); 
     415    ip->prompt_shown = FALSE; 
     416} 
     417 
     418/* --------------------------------------------------------------------------------------------- */ 
     419 
     420static gboolean 
     421maintenance_timeout_cb (gpointer data) 
     422{ 
     423    WInstructionStation *ip = INSTR_STATION (data); 
     424    guint64 cur_time; 
     425    gboolean draw = FALSE; 
     426 
     427    cur_time = g_get_real_time (); 
     428 
     429    /* Returned at prompt line? Append pending text */ 
     430    if (EDIT (ip)->buffer.curs_line == ip->cur_line && ip->flow_ctrl_buf != NULL) 
     431        draw |= helper_try_append_flow_control_data (ip, FALSE, FALSE); 
     432 
     433    /* Some time scrolled up? Force temporary jump to the prompt and append pending text */ 
     434    if (cur_time - ip->read_time > 30000000 && ip->flow_ctrl_buf != NULL) 
     435        draw |= helper_try_append_flow_control_data (ip, TRUE, TRUE); 
     436 
     437    /* Draw prompt after 0.5 seconds of no output from program */ 
     438    if (cur_time - ip->read_time > 500000 && !ip->prompt_shown) 
     439    { 
     440        instr_stn_show_prompt (ip, TRUE); 
     441        draw = TRUE; 
     442    } 
     443 
     444    if (draw) 
     445        repaint_screen (); 
     446    return TRUE; 
     447} 
     448 
     449/* --------------------------------------------------------------------------------------------- */ 
     450 
     451#ifdef GLIB_VERSION_2_40 
     452 
     453static void 
     454program_ended_callback (GPid pid, gint exit_code, gpointer user_data) 
     455{ 
     456    WInstructionStation *ip = INSTR_STATION (user_data); 
     457    char *msg; 
     458    gboolean st_ret; 
     459 
     460    /* Release object leaving it uninitialized (no freeing of main object pointer) */ 
     461    instr_stn_release (ip, FALSE); 
     462 
     463    g_spawn_close_pid (pid); 
     464 
     465    /* Examine exit code of closed program */ 
     466#ifdef GLIB_VERSION_2_34 
     467    st_ret = g_spawn_check_exit_status (exit_code, NULL); 
     468#else 
     469    st_ret = (exit_code == 0); 
     470#endif 
     471 
     472    /* Choose message basing on exit code */ 
     473    if (st_ret) 
     474        msg = g_strdup (_("\nProgram closed.\n")); 
     475    else 
     476        msg = g_strdup_printf (_("\nProgram closed abnormally (exit code: %d).\n"), exit_code); 
     477 
     478    /* Insert/print message to buffer and to mc.log */ 
     479    edit_insert_string (EDIT (ip), msg, strlen (msg)); 
     480    mc_log ("%s", msg); 
     481 
     482    /* Release message upon using/printing it */ 
     483    g_free (msg); 
     484    repaint_screen (); 
     485} 
     486 
     487#endif 
     488 
     489/* --------------------------------------------------------------------------------------------- */ 
     490 
     491static gboolean 
     492helper_configure_src_and_ch (GIOChannel ** ch, gint fd, guint * src_id, gboolean write, 
     493                             GIOFunc cb_fun, gpointer data, const char *name) 
     494{ 
     495    /* Initialize the output variables */ 
     496    *ch = NULL; 
     497    *src_id = 0; 
     498 
     499    *ch = g_io_channel_unix_new (fd); 
     500 
     501    /* Channel created? */ 
     502    if (*ch == NULL) 
     503        goto cleanup_and_err; 
     504 
     505    /* Automatic shutdown of channel */ 
     506    g_io_channel_set_close_on_unref (*ch, TRUE); 
     507 
     508    /* Trim down buffering on this channel */ 
     509    g_io_channel_set_buffer_size (*ch, 5); 
     510 
     511    /* Attempt to set non-blocking flag on channel */ 
     512    if (g_io_channel_set_flags (*ch, G_IO_FLAG_NONBLOCK, NULL) != G_IO_STATUS_NORMAL) 
     513        mc_log ("Problem setting a channel to non-blocking state (channel flags: 0x%x)", 
     514                g_io_channel_get_flags (*ch)); 
     515 
     516    /* Create GSource */ 
     517    *src_id = 
     518        g_io_add_watch (*ch, (write ? G_IO_OUT : G_IO_IN) | G_IO_HUP | G_IO_ERR, cb_fun, data); 
     519 
     520    /* Source created OK? */ 
     521    if (*src_id == 0) 
     522        goto cleanup_and_err; 
     523 
     524    /* Configure the sources */ 
     525    g_source_set_name_by_id (*src_id, name); 
     526 
     527    /* Return true */ 
     528    return TRUE; 
     529 
     530  cleanup_and_err: 
     531    if (*src_id != 0) 
     532    { 
     533        GSource *src; 
     534        src = g_main_context_find_source_by_id (NULL, *src_id); 
     535        if (src != NULL) 
     536            /* Triggers also unref of wrapped channel */ 
     537            g_source_destroy (src); 
     538    } 
     539    if (*ch != NULL) 
     540        g_io_channel_unref (*ch); 
     541    *ch = NULL; 
     542    *src_id = 0; 
     543 
     544    return FALSE; 
     545} 
     546 
     547/* --------------------------------------------------------------------------------------------- */ 
     548 
     549static gboolean 
     550helper_read_all (GIOChannel * channel, char *buf, gsize * out_bytes_read_in_len) 
     551{ 
     552    gsize len = *out_bytes_read_in_len; 
     553    gsize count, retries = 10; 
     554    GIOStatus stat; 
     555 
     556    *out_bytes_read_in_len = 0; 
     557 
     558    while (*out_bytes_read_in_len < len) 
     559    { 
     560        count = 0; 
     561        stat = 
     562            g_io_channel_read_chars (channel, buf + *out_bytes_read_in_len, 
     563                                     len - *out_bytes_read_in_len, &count, NULL); 
     564        *out_bytes_read_in_len += count; 
     565 
     566        /* Should end the reading ? */ 
     567        if (stat == G_IO_STATUS_ERROR || (stat == G_IO_STATUS_NORMAL && count == 0) || 
     568            stat == G_IO_STATUS_EOF) 
     569            return (stat != G_IO_STATUS_ERROR); 
     570        else if (stat == G_IO_STATUS_AGAIN && retries-- == 0) 
     571            /* Exhausted retries - there must be no data to read - return OK */ 
     572            return TRUE; 
     573    } 
     574 
     575    return TRUE; 
     576} 
     577 
     578/* --------------------------------------------------------------------------------------------- */ 
     579 
     580static gboolean 
     581helper_write_all (GIOChannel * channel, char *buf, gsize * out_bytes_written_in_len) 
     582{ 
     583    gsize len = *out_bytes_written_in_len; 
     584    gsize count; 
     585    GIOError err; 
     586 
     587    *out_bytes_written_in_len = 0; 
     588 
     589    while (*out_bytes_written_in_len < len) 
     590    { 
     591        count = 0; 
     592        err = 
     593            g_io_channel_write (channel, buf + *out_bytes_written_in_len, 
     594                                len - *out_bytes_written_in_len, &count); 
     595        *out_bytes_written_in_len += count; 
     596 
     597        if (err && err != G_IO_ERROR_AGAIN) 
     598            return FALSE; 
     599    } 
     600 
     601    return TRUE; 
     602} 
     603 
     604/* --------------------------------------------------------------------------------------------- */ 
     605 
     606static gboolean 
     607stdout_read_callback (GIOChannel * source, GIOCondition condition, gpointer data) 
     608{ 
     609    WInstructionStation *ip = INSTR_STATION (data); 
     610    char buf[2048]; 
     611    gsize count = 2047; 
     612    gboolean r_ret; 
     613 
     614    /* Active only when a recognized event of interest occurs */ 
     615    if ((condition & (G_IO_IN | G_IO_HUP | G_IO_ERR)) == 0) 
     616        return TRUE; 
     617    else 
     618        ip->io_complete = FALSE; 
     619 
     620    /* Repeat read of 2KiB-1 maximum bytes until 0 bytes are being read */ 
     621    do 
     622    { 
     623        r_ret = helper_read_all (source, buf, &count); 
     624        buf[count] = '\0'; 
     625        if (count == 0 && !r_ret) 
     626        { 
     627            mc_log ("Error while reading program's output: %s", g_strerror (errno)); 
     628        } 
     629        else 
     630        { 
     631            ip->read_time = g_get_real_time (); 
     632            buf[count] = '\0'; 
     633            if (count > 0 && (EDIT (ip)->buffer.curs_line < ip->cur_line)) 
     634            { 
     635                if (ip->flow_ctrl_buf == NULL) 
     636                    ip->flow_ctrl_buf = g_byte_array_sized_new (2048); 
     637                /* Save text until user scrolls to prompt */ 
     638                g_byte_array_append (ip->flow_ctrl_buf, (guint8 *) buf, count); 
     639            } 
     640            else if (EDIT (ip)->buffer.curs_line == ip->cur_line) 
     641            { 
     642                /* Always check for any buffered data before writing present one */ 
     643                helper_try_append_flow_control_data (ip, FALSE, FALSE); 
     644 
     645                /* Append data read now */ 
     646                if (count > 0) 
     647                    append_text_to_buffer (ip, buf, count); 
     648                else 
     649                    /* No data read? Show prompt */ 
     650                if (!ip->prompt_shown) 
     651                    instr_stn_show_prompt (ip, r_ret); 
     652            } 
     653 
     654            repaint_screen (); 
     655        } 
     656    } 
     657    while (count != 0); 
     658 
     659    ip->io_complete = r_ret; 
     660    return condition != G_IO_HUP; 
     661} 
     662 
     663/* --------------------------------------------------------------------------------------------- */ 
     664 
     665static gboolean 
     666stdin_write_callback (GIOChannel * source, GIOCondition condition, gpointer data) 
     667{ 
     668    WInstructionStation *ip = INSTR_STATION (data); 
     669    gsize count; 
     670    gboolean ret = TRUE; 
     671 
     672    /* Any instruction to send? */ 
     673    if (ip->instruction == NULL) 
     674        return ret; 
     675    else 
     676        ip->io_complete = FALSE; 
     677 
     678    if ((condition & G_IO_OUT) == 0) 
     679        return TRUE; 
     680 
     681    errno = 0; 
     682    count = strlen (ip->instruction); 
     683    ret = helper_write_all (source, ip->instruction, &count); 
     684    if (!ret) 
     685    { 
     686        mc_log ("Error during sending instruction to program: %s", g_strerror (errno)); 
     687    } 
     688    else 
     689    { 
     690        ip->first_response = TRUE; 
     691        MC_PTR_FREE (ip->instruction); 
     692        if (ip->prompt_timeout_id == 0) 
     693            ip->prompt_timeout_id = g_timeout_add (200, maintenance_timeout_cb, ip); 
     694    } 
     695 
     696    g_io_channel_flush (ip->io[STDIN].ch, NULL); 
     697    ip->io_complete = TRUE; 
     698 
     699    return ret; 
     700} 
     701 
     702/* --------------------------------------------------------------------------------------------- */ 
     703 
     704/* --------------------------------------------------------------------------------------------- */ 
     705/*** public functions ****************************************************************************/ 
     706/* --------------------------------------------------------------------------------------------- */ 
     707 
     708WInstructionStation * 
     709instr_stn_create (int y, int x, int lines, int cols, instr_stn_flags_t flags, const char *program) 
     710{ 
     711    WInstructionStation *ip, *iret; 
     712 
     713    ip = g_new0 (WInstructionStation, 1); 
     714    if (ip == NULL) 
     715        return NULL; 
     716 
     717    iret = instr_stn_init (ip, y, x, lines, cols, flags, program); 
     718    if (iret == NULL) 
     719        MC_PTR_FREE (ip); 
     720 
     721    return ip; 
     722} 
     723 
     724/* --------------------------------------------------------------------------------------------- */ 
     725/* Initializes a preallocated object */ 
     726 
     727WInstructionStation * 
     728instr_stn_init (WInstructionStation * ip, int y, int x, int lines, int cols, 
     729                instr_stn_flags_t flags, const char *program) 
     730{ 
     731    WEdit *iret; 
     732    WGroup *owner_save = NULL; 
     733    gboolean tmp_ret = FALSE; 
     734    char **program_cline; 
     735    int rounds = 0; 
     736 
     737    if (ip == NULL) 
     738        goto cleanup_and_err; 
     739 
     740    /* Handle special -1 values indicating reuse of old values */ 
     741    if (ip->finalized) 
     742    { 
     743        y = (y == -1) ? WIDGET (ip)->y : y; 
     744        x = (x == -1) ? WIDGET (ip)->x : x; 
     745        lines = (lines == -1) ? WIDGET (ip)->lines : lines; 
     746        cols = (cols == -1) ? WIDGET (ip)->cols : cols; 
     747        /* Save owner */ 
     748        owner_save = WIDGET (ip)->owner; 
     749        ip->finalized = FALSE; 
     750    } 
     751 
     752    /* Initialize base object (WEdit) */ 
     753    iret = edit_init (EDIT (ip), y, x, lines, cols, NULL, 0, EDIT_DO_INIT_BASE_CLASS); 
     754    if (iret == NULL) 
     755        goto cleanup_and_err; 
     756 
     757    /* Restore owner field value */ 
     758    WIDGET (ip)->owner = owner_save; 
     759    WIDGET (ip)->callback = instr_stn_callback; 
     760    WIDGET (ip)->mouse_callback = edit_mouse_callback; 
     761    EDIT (ip)->fullscreen = 0; 
     762 
     763    ip->io[0].name = g_strdup ("Std-In (WInstructionStation)"); 
     764    ip->io[1].name = g_strdup ("Std-Out (WInstructionStation)"); 
     765    ip->io[2].name = g_strdup ("Std-Error (WInstructionStation)"); 
     766    ip->io[0].fd = ip->io[1].fd = ip->io[2].fd = -1; 
     767 
     768    ip->before_first_prompt = TRUE; 
     769    ip->prompt = g_strdup ("[guest@localhost]# "); 
     770    ip->prompt_span = strlen (ip->prompt); 
     771    ip->eprompt = g_strdup ("[guest@localhost]! "); 
     772    ip->eprompt_span = strlen (ip->eprompt); 
     773    ip->cur_col = 0; 
     774    ip->cur_line = 0; 
     775    ip->flags = flags; 
     776    ip->prev_flags = flags; 
     777 
     778    /* Set initial program (most probably a shell, like Bash) */ 
     779    if (program != NULL) 
     780    { 
     781        if (program[0] == 's' && program[1] == 'h') 
     782            ip->program = g_strdup (program); 
     783        else 
     784            ip->program = g_strdup_printf ("sh -c '%s'", program); 
     785    } 
     786    else 
     787        /* Fallback instruction if no program given */ 
     788        ip->program = g_strdup ("bash"); 
     789 
     790    while (++rounds <= 2 && !tmp_ret) 
     791    { 
     792        int argc = 0; 
     793        if (ip->error != NULL) 
     794        { 
     795            g_error_free (ip->error); 
     796            ip->error = NULL; 
     797        } 
     798        tmp_ret = g_shell_parse_argv (ip->program, &argc, &program_cline, &ip->error); 
     799        if (tmp_ret) 
     800        { 
     801            tmp_ret = g_spawn_async_with_pipes (NULL, program_cline, 
     802                                                NULL, G_SPAWN_SEARCH_PATH | 
     803                                                G_SPAWN_DO_NOT_REAP_CHILD | 
     804                                                G_SPAWN_LEAVE_DESCRIPTORS_OPEN, 
     805                                                NULL, NULL, &ip->process_id, 
     806                                                has_flag (flags, 
     807                                                          INSTR_STN_NO_STDIN) ? NULL : 
     808                                                &ip->io[STDIN].fd, &ip->io[STDOUT].fd, 
     809                                                &ip->io[STDERR].fd, &ip->error); 
     810            g_strfreev (program_cline); 
     811        } 
     812        if (!tmp_ret) 
     813        { 
     814            char *new_program, *msg_text; 
     815            mc_log ("Creating CLI process [instruction:%s] failed: %s", 
     816                    program, ip->error ? ip->error->message : "<no error message>"); 
     817 
     818            /* Create a transatable, parametrized message */ 
     819            msg_text = g_strdup_printf ("%s%s:", _("The specified program has failed to run.\n" 
     820                                                   "Enter a new full path or a program name"), 
     821                                        (rounds >= 2 ? _(" (last try)") : "")); 
     822 
     823            /* Display message asking for a new program */ 
     824            new_program = input_expand_dialog (_("Provide an alternate program to run"), 
     825                                               msg_text, "instruction-station", 
     826                                               alt_prog_first_run ? ip->program : INPUT_LAST_TEXT, 
     827                                               INPUT_COMPLETE_COMMANDS | INPUT_COMPLETE_FILENAMES | 
     828                                               INPUT_COMPLETE_VARIABLES); 
     829            g_free (msg_text); 
     830            alt_prog_first_run = FALSE; 
     831 
     832            /* Obtained a new program? */ 
     833            if (new_program != NULL) 
     834            { 
     835                g_free (ip->program); 
     836                ip->program = new_program; 
     837            } 
     838        } 
     839    } 
     840 
     841    /* If no program to run has been found, then exit. */ 
     842    if (!tmp_ret) 
     843    { 
     844        message (D_ERROR, _("InstructionStation startup failed"), 
     845                 _("Failed to initialize the CLI window\n(reason: %s)."), 
     846                 ip->error ? ip->error->message : "<no error message>"); 
     847        goto cleanup_and_err; 
     848    } 
     849 
     850    ip->prev_program = ip->program; 
     851 
     852    for (int i = 0; i <= STDERR; i++) 
     853    { 
     854        if (has_flag (flags, INSTR_STN_NO_STDIN) && i == STDIN) 
     855        { 
     856            /* There is no standard input pipe for program */ 
     857            ip->io[i].fd = -1; 
     858            continue; 
     859        } 
     860 
     861        if (helper_configure_src_and_ch (&ip->io[i].ch, ip->io[i].fd, &ip->io[i].src_id, 
     862                                         (i == STDIN ? TRUE : FALSE), 
     863                                         (i == STDIN ? stdin_write_callback : stdout_read_callback), 
     864                                         ip, ip->io[i].name) == FALSE) 
     865            goto cleanup_and_err; 
     866    } 
     867 
     868#ifdef GLIB_VERSION_2_40 
     869    /* Set a process watcher and restarter for the program */ 
     870    ip->proc_src_id = g_child_watch_add (ip->process_id, program_ended_callback, ip); 
     871    ip->proc_support = TRUE; 
     872#endif 
     873 
     874    /* Display initial prompt */ 
     875    instr_stn_show_prompt (ip, TRUE); 
     876    ip->io_complete = TRUE; 
     877 
     878    return ip; 
     879 
     880  cleanup_and_err: 
     881 
     882    instr_stn_release (ip, TRUE); 
     883    return NULL; 
     884} 
     885 
     886/* --------------------------------------------------------------------------------------------- */ 
     887 
     888WEdit * 
     889find_cli (const WDialog * h) 
     890{ 
     891    const WGroup *g = CONST_GROUP (h); 
     892 
     893    if (edit_widget_is_cli (CONST_WIDGET (g->current->data))) 
     894        return (WEdit *) g->current->data; 
     895    return (WEdit *) widget_find_by_type (CONST_WIDGET (h), instr_stn_callback); 
     896} 
     897 
     898/* --------------------------------------------------------------------------------------------- */ 
     899/* Check if widget is a WInstructionStation class type. */ 
     900 
     901gboolean 
     902edit_widget_is_cli (const Widget * w) 
     903{ 
     904    return (w != NULL && w->callback == instr_stn_callback); 
     905} 
     906 
     907/* --------------------------------------------------------------------------------------------- */ 
     908 
     909cb_ret_t 
     910instr_stn_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data) 
     911{ 
     912    cb_ret_t ret = MSG_NOT_HANDLED; 
     913    WInstructionStation *ip = INSTR_STATION (w); 
     914    WGroup *owner = w->owner; 
     915 
     916    switch (msg) 
     917    { 
     918 
     919    case MSG_KEY: 
     920        { 
     921            gboolean ext_mode; 
     922            long novel_state_id; 
     923 
     924            /* keep and then extmod flag */ 
     925            ext_mode = w->ext_mode; 
     926            novel_state_id = widget_lookup_key (w, parm); 
     927            w->ext_mode = ext_mode; 
     928 
     929            if (novel_state_id == CK_IgnoreKey) 
     930                w->ext_mode = FALSE; 
     931            else 
     932            { 
     933                /* Treat Station action specially when run from within a station window */ 
     934                if (novel_state_id != CK_InstructionStation) 
     935                    ret = edit_dialog_command_execute (DIALOG (owner), novel_state_id, data); 
     936                if (ret == MSG_NOT_HANDLED) 
     937                    ret = instr_stn_run_novel_state_id (ip, novel_state_id, data); 
     938                /* 
     939                 * If activity was not handled, keep the extended mode 
     940                 * for the further key processing, checking if the 
     941                 * window is still open */ 
     942                if (novel_state_id != CK_Close) 
     943                { 
     944                    if (ret == MSG_HANDLED) 
     945                        w->ext_mode = FALSE; 
     946                } 
     947                else if (ret == MSG_HANDLED) 
     948                    /* Window closed -> update `w` variable */ 
     949                    w = WIDGET (owner->current->data); 
     950            } 
     951 
     952            /* 
     953             * Due to the "end of bracket" escape the editor sees input with is_idle() == false 
     954             * (expects more characters) and hence doesn't yet refresh the screen, but then 
     955             * no further characters arrive (there's only an "end of bracket" which is swallowed 
     956             * by tty_get_event()), so you end up with a screen that's not refreshed after pasting. 
     957             * So let's trigger an IDLE signal. 
     958             */ 
     959            if (!is_idle ()) 
     960                widget_idle (w, TRUE); 
     961        } 
     962        break; 
     963    case MSG_ACTION: 
     964        ret = instr_stn_run_novel_state_id (ip, parm, data); 
     965        break; 
     966    case MSG_DESTROY: 
     967        kill (ip->process_id, 15); 
     968        /* Have to wait directly (no GLib support for watchers) ? */ 
     969        if (!ip->proc_support) 
     970            waitpid (ip->process_id, NULL, 0); 
     971 
     972        /* 
     973         * Message will be passed on to WEdit callback which will g_free(), so passing FALSE (i.e.: 
     974         * requesting cleanup of WInstructionStation object without full release of its main 
     975         * pointer) */ 
     976        instr_stn_release (ip, FALSE); 
     977        /* Not restarting the station, so release program string backup field */ 
     978        g_free (ip->prev_program); 
     979        break; 
     980    default: 
     981        break; 
     982    } 
     983 
     984    /* Redirect message to base object (i.e.: editor window «» WEdit) if needed */ 
     985    if (ret == MSG_NOT_HANDLED) 
     986    { 
     987        ret = edit_callback (w, sender, msg, parm, data); 
     988        if (msg != MSG_DESTROY) 
     989            ip->cur_col = EDIT (w)->curs_col; 
     990    } 
     991    return ret; 
     992} 
     993 
     994/* --------------------------------------------------------------------------------------------- */ 
  • 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..e74409156
    - +  
     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 buffer allocated and filled when user scrolls up blocking output */ 
     69    GByteArray *flow_ctrl_buf; 
     70 
     71    /* A GError placeholder */ 
     72    GError *error; 
     73 
     74    /* Indicator of an idle state – set when there's no data to send and read */ 
     75    gboolean io_complete; 
     76 
     77    /* Indicator of instr_release being already called on WInstructionStation object */ 
     78    gboolean finalized; 
     79} WInstructionStation; 
     80 
     81/*** global variables defined in .c file *********************************************************/ 
     82 
     83/*** declarations of public functions ************************************************************/ 
     84 
     85WInstructionStation *instr_stn_create (int y, int x, int lines, int cols, instr_stn_flags_t flags, 
     86                                       const char *program); 
     87WInstructionStation *instr_stn_init (WInstructionStation * ip, int y, int x, int lines, int cols, 
     88                                     instr_stn_flags_t flags, const char *program); 
     89WEdit *find_cli (const WDialog * h); 
     90gboolean edit_widget_is_cli (const Widget * w); 
     91cb_ret_t instr_stn_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data); 
     92 
     93/*** inline functions ****************************************************************************/ 
     94 
     95#endif /* MC__INSTR_STATION_H */ 
  • src/keybind-defaults.c

    diff --git a/src/keybind-defaults.c b/src/keybind-defaults.c
    index 7b87c2f5a..0b9cf5538 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-s"}, 
    473474    {"ExternalCommand", "alt-u"}, 
    474475#ifdef HAVE_ASPELL 
    475476    {"SpellCheckCurrentWord", "ctrl-p"},