1 /* cooledit.bindings file parser
3 Authors: 2005 Vitja Makarov
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
29 #include <sys/types.h>
33 #include <mhl/types.h>
34 #include <mhl/memory.h>
35 #include <mhl/string.h>
37 #include "../src/global.h"
40 #include "edit-widget.h"
41 #include "editcmddef.h" /* list of commands */
43 #include "../src/key.h" /* KEY_M_* */
44 #include "../src/tty.h" /* keys */
45 #include "../src/wtools.h"
46 #include "../src/widget.h" /* buttonbar_redraw() */
48 typedef struct NameMap
{
53 typedef struct Config
{
54 time_t mtime
; /* mtime at the moment we read config file */
60 typedef struct Command
{
62 bool (*handler
) (config_t
*cfg
, int argc
, char *argv
[]);
65 static char error_msg
[200] = "Nobody see this";
67 static const name_map_t key_names
[] = {
68 {"backspace", KEY_BACKSPACE
},
82 {"scancel", KEY_SCANCEL
},
99 static const name_map_t command_names
[] = {
100 {"No-Command", CK_Ignore_Key
},
101 {"Ignore-Key", CK_Ignore_Key
},
102 {"BackSpace", CK_BackSpace
},
103 {"Delete", CK_Delete
},
105 {"Page-Up", CK_Page_Up
},
106 {"Page-Down", CK_Page_Down
},
109 {"Word-Left", CK_Word_Left
},
110 {"Word-Right", CK_Word_Right
},
117 {"Beginning-Of-Text", CK_Beginning_Of_Text
},
118 {"End-Of-Text", CK_End_Of_Text
},
119 {"Scroll-Up", CK_Scroll_Up
},
120 {"Scroll-Down", CK_Scroll_Down
},
121 {"Return", CK_Return
},
122 {"Begin-Page", CK_Begin_Page
},
123 {"End-Page", CK_End_Page
},
124 {"Delete-Word-Left", CK_Delete_Word_Left
},
125 {"Delete-Word-Right", CK_Delete_Word_Right
},
126 {"Paragraph-Up", CK_Paragraph_Up
},
127 {"Paragraph-Down", CK_Paragraph_Down
},
131 {"Save-as", CK_Save_As
},
135 {"Remove", CK_Remove
},
136 {"Unmark", CK_Unmark
},
137 {"Save-Block", CK_Save_Block
},
138 {"Column-Mark", CK_Column_Mark
},
140 {"Find-Again", CK_Find_Again
},
141 {"Replace", CK_Replace
},
142 {"Replace-Again", CK_Replace_Again
},
143 {"Complete-Word", CK_Complete_Word
},
144 {"Debug-Start", CK_Debug_Start
},
145 {"Debug-Stop", CK_Debug_Stop
},
146 {"Debug-Toggle-Break", CK_Debug_Toggle_Break
},
147 {"Debug-Clear", CK_Debug_Clear
},
148 {"Debug-Next", CK_Debug_Next
},
149 {"Debug-Step", CK_Debug_Step
},
150 {"Debug-Back-Trace", CK_Debug_Back_Trace
},
151 {"Debug-Continue", CK_Debug_Continue
},
152 {"Debug-Enter-Command", CK_Debug_Enter_Command
},
153 {"Debug-Until-Curser", CK_Debug_Until_Curser
},
154 {"Insert-File", CK_Insert_File
},
156 {"Toggle-Insert", CK_Toggle_Insert
},
159 {"Refresh", CK_Refresh
},
161 {"Delete-Line", CK_Delete_Line
},
162 {"Delete-To-Line-End", CK_Delete_To_Line_End
},
163 {"Delete-To-Line-Begin", CK_Delete_To_Line_Begin
},
164 {"Man-Page", CK_Man_Page
},
167 {"Cancel", CK_Cancel
},
168 {"Complete", CK_Complete
},
169 {"Paragraph-Format", CK_Paragraph_Format
},
171 {"Type-Load-Python", CK_Type_Load_Python
},
172 {"Find-File", CK_Find_File
},
174 {"Match-Bracket", CK_Match_Bracket
},
175 {"Terminal", CK_Terminal
},
176 {"Terminal-App", CK_Terminal_App
},
177 {"ExtCmd", CK_ExtCmd
},
178 {"User-Menu", CK_User_Menu
},
179 {"Save-Desktop", CK_Save_Desktop
},
180 {"New-Window", CK_New_Window
},
183 {"Save-And-Quit", CK_Save_And_Quit
},
184 {"Run-Another", CK_Run_Another
},
185 {"Check-Save-And-Quit", CK_Check_Save_And_Quit
},
186 {"Maximize", CK_Maximize
},
187 {"Begin-Record-Macro", CK_Begin_Record_Macro
},
188 {"End-Record-Macro", CK_End_Record_Macro
},
189 {"Delete-Macro", CK_Delete_Macro
},
190 {"Toggle-Bookmark", CK_Toggle_Bookmark
},
191 {"Flush-Bookmarks", CK_Flush_Bookmarks
},
192 {"Next-Bookmark", CK_Next_Bookmark
},
193 {"Prev-Bookmark", CK_Prev_Bookmark
},
194 {"Page-Up-Highlight", CK_Page_Up_Highlight
},
195 {"Page-Down-Highlight", CK_Page_Down_Highlight
},
196 {"Left-Highlight", CK_Left_Highlight
},
197 {"Right-Highlight", CK_Right_Highlight
},
198 {"Word-Left-Highlight", CK_Word_Left_Highlight
},
199 {"Word-Right-Highlight", CK_Word_Right_Highlight
},
200 {"Up-Highlight", CK_Up_Highlight
},
201 {"Down-Highlight", CK_Down_Highlight
},
202 {"Home-Highlight", CK_Home_Highlight
},
203 {"End-Highlight", CK_End_Highlight
},
204 {"Beginning-Of-Text-Highlight", CK_Beginning_Of_Text_Highlight
},
205 {"End-Of-Text_Highlight", CK_End_Of_Text_Highlight
},
206 {"Begin-Page-Highlight", CK_Begin_Page_Highlight
},
207 {"End-Page-Highlight", CK_End_Page_Highlight
},
208 {"Scroll-Up-Highlight", CK_Scroll_Up_Highlight
},
209 {"Scroll-Down-Highlight", CK_Scroll_Down_Highlight
},
210 {"Paragraph-Up-Highlight", CK_Paragraph_Up_Highlight
},
211 {"Paragraph-Down-Highlight", CK_Paragraph_Down_Highlight
},
212 {"XStore", CK_XStore
},
214 {"XPaste", CK_XPaste
},
215 {"Selection-History", CK_Selection_History
},
217 {"Select-Codepage", CK_Select_Codepage
},
218 {"Insert-Literal", CK_Insert_Literal
},
219 {"Execute-Macro", CK_Execute_Macro
},
220 {"Begin-or-End-Macro", CK_Begin_End_Macro
},
221 {"Ext-mode", CK_Ext_Mode
},
223 {"Focus-Next", CK_Focus_Next
},
224 {"Focus-Prev", CK_Focus_Prev
},
225 {"Height-Inc", CK_Height_Inc
},
226 {"Height-Dec", CK_Height_Dec
},
228 {"Error-Next", CK_Error_Next
},
229 {"Error-Prev", CK_Error_Prev
},
235 cfg_free_maps(config_t
*cfg
)
240 g_array_free(cfg
->keymap
, TRUE
);
243 g_array_free(cfg
->ext_keymap
, TRUE
);
244 cfg
->ext_keymap
= NULL
;
246 for (i
= 0; i
< 10; i
++)
247 mhl_mem_free(cfg
->labels
[i
]);
250 /* Returns an array containing the words in str. WARNING: As long as
251 * the result is used, str[...] must be valid memory. This function
252 * modifies str[...] and uses it, so the caller should not use it until
253 * g_ptr_array_free() is called for the returned result.
256 split_line(char *str
)
258 bool inside_quote
= false;
262 args
= g_ptr_array_new();
265 while (isspace((unsigned char) *str
))
268 g_ptr_array_add(args
, str
);
272 case '#': /* cut off comments */
277 case '\n': /* end of line */
280 if (str
== g_ptr_array_index(args
, args
->len
- 1))
281 g_ptr_array_remove_index(args
, args
->len
- 1);
283 *(str
- move
) = '\0';
286 case '"': /* quote */
288 inside_quote
= !inside_quote
;
292 case ' ': /* spaces */
297 *(str
++ - move
) = '\0';
301 while (isspace((unsigned char) *str
))
304 g_ptr_array_add(args
, str
--);
323 *(str
- move
) = *str
;
329 keymap_add(GArray
*keymap
, int key
, int cmd
)
331 edit_key_map_type new_one
, *map
;
334 map
= &(g_array_index(keymap
, edit_key_map_type
, 0));
335 for (i
= 0; i
< keymap
->len
; i
++) {
336 if (map
[i
].key
== key
) {
337 map
[i
].command
= cmd
;
343 new_one
.command
= cmd
;
344 g_array_append_val(keymap
, new_one
);
347 /* bind <key> <command> */
349 cmd_bind(config_t
*cfg
, int argc
, char *argv
[])
351 char *keyname
, *command
;
352 const name_map_t
*key
= key_names
;
353 const name_map_t
*cmd
= command_names
;
354 int mod
= 0, k
= -1, m
= 0;
357 snprintf(error_msg
, sizeof(error_msg
),
358 _("bind: Wrong argument number, bind <key> <command>"));
366 switch (*keyname
++) {
378 if (!m
) { /* incorrect key */
379 snprintf(error_msg
, sizeof(error_msg
),
380 _("bind: Bad key value `%s'"), keyname
);
392 if (keyname
[0] == '\0') {
393 snprintf(error_msg
, sizeof(error_msg
), _("bind: Ehh...no key?"));
398 if (keyname
[1] == '\0') {
401 while (key
->name
&& strcasecmp(key
->name
, keyname
) != 0)
405 if (k
< 0 && !key
->name
) {
406 snprintf(error_msg
, sizeof(error_msg
),
407 _("bind: Unknown key: `%s'"), keyname
);
411 while (cmd
->name
&& strcasecmp(cmd
->name
, command
) != 0)
415 snprintf(error_msg
, sizeof(error_msg
),
416 _("bind: Unknown command: `%s'"), command
);
420 if (mod
& KEY_M_CTRL
) {
430 if (mod
& KEY_M_SHIFT
)
433 if (!strcasecmp("bind-ext", argv
[0]))
434 keymap_add(cfg
->ext_keymap
, k
, cmd
->val
);
436 keymap_add(cfg
->keymap
, k
, cmd
->val
);
443 static void cmd_F ## x (WEdit * edit) \
445 send_message ((Widget *) edit, WIDGET_KEY, KEY_F (x)); \
459 void (*cmd_Fx
[]) (WEdit
*) = {
460 cmd_F1
, cmd_F2
, cmd_F3
, cmd_F4
, cmd_F5
,
461 cmd_F6
, cmd_F7
, cmd_F8
, cmd_F9
, cmd_F10
465 static void edit_my_define (Dlg_head
* h
, int idx
, const char *text
,
466 void (*fn
) (WEdit
*), WEdit
* edit
)
468 /* function-cast ok */
469 buttonbar_set_label_data (h
, idx
, text
, (buttonbarfn
) fn
, edit
);
473 /* label <number> <command> <label> */
475 cmd_label(config_t
*cfg
, int argc
, char *argv
[])
477 const name_map_t
*cmd
= command_names
;
478 const char *command
, *label
;
482 snprintf(error_msg
, sizeof(error_msg
),
483 _("%s: Syntax: %s <n> <command> <label>"),
488 fn
= strtol(argv
[1], NULL
, 0);
492 while (cmd
->name
&& strcasecmp(cmd
->name
, command
) != 0)
496 snprintf(error_msg
, sizeof(error_msg
),
497 _("%s: Unknown command: `%s'"), argv
[0], command
);
501 if (fn
< 1 || fn
> 10) {
502 snprintf(error_msg
, sizeof(error_msg
),
503 _("%s: fn should be 1-10"), argv
[0]);
507 keymap_add(cfg
->keymap
, KEY_F(fn
), cmd
->val
);
508 cfg
->labels
[fn
- 1] = mhl_str_dup(label
);
515 parse_file(config_t
*cfg
, const char *file
, const command_t
*cmd
)
520 FILE *fp
= fopen(file
, "r");
523 snprintf(error_msg
, sizeof(error_msg
), _("%s: fopen(): %s"),
524 file
, strerror(errno
));
528 while (fgets(buf
, sizeof(buf
), fp
)) {
529 const command_t
*c
= cmd
;
535 args
= split_line(buf
);
536 argv
= (char **) args
->pdata
;
538 if (args
->len
== 0) {
539 g_ptr_array_free(args
, TRUE
);
543 while (c
->name
!= NULL
&& strcasecmp(c
->name
, argv
[0]) != 0)
546 if (c
->name
== NULL
) {
547 snprintf(error_msg
, sizeof(error_msg
),
548 _("%s:%d: unknown command `%s'"), file
, line
,
550 g_ptr_array_free(args
, TRUE
);
555 if (!(c
->handler(cfg
, args
->len
, argv
))) {
556 char *ss
= mhl_str_dup(error_msg
);
557 snprintf(error_msg
, sizeof(error_msg
),
558 _("%s:%d: %s"), file
, line
, ss
);
560 g_ptr_array_free(args
, TRUE
);
564 g_ptr_array_free(args
, TRUE
);
572 load_user_keymap(config_t
*cfg
, const char *file
)
574 const command_t cmd
[] = {
576 {"bind-ext", cmd_bind
},
577 {"label", cmd_label
},
581 cfg
->keymap
= g_array_new(TRUE
, FALSE
, sizeof(edit_key_map_type
));
582 cfg
->ext_keymap
= g_array_new(TRUE
, FALSE
, sizeof(edit_key_map_type
));
584 if (!parse_file(cfg
, file
, cmd
)) {
592 edit_load_user_map(WEdit
*edit
)
599 if (edit_key_emulation
!= EDIT_KEY_EMULATION_USER
)
602 file
= mhl_str_dir_plus_file(home_dir
, MC_USERMAP
);
604 if (stat(file
, &s
) < 0) {
605 char *msg
= g_strdup_printf(_("%s not found!"), file
);
606 edit_error_dialog(_("Error"), msg
);
612 if (s
.st_mtime
!= cfg
.mtime
) {
613 memset(&new_cfg
, 0, sizeof(new_cfg
));
614 new_cfg
.mtime
= s
.st_mtime
;
616 if (!load_user_keymap(&new_cfg
, file
)) {
617 edit_error_dialog(_("Error"), error_msg
);
618 cfg_free_maps(&new_cfg
);
627 edit
->user_map
= (edit_key_map_type
*) cfg
.keymap
->data
;
628 edit
->ext_map
= (edit_key_map_type
*) cfg
.ext_keymap
->data
;
629 memcpy(edit
->labels
, cfg
.labels
, sizeof(edit
->labels
));