Completely removed MHL stuff
[midnight-commander.git] / edit / usermap.c
blob1f5a8b7d52963a0c0f871b5c7f80950361b79d12
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
18 02111-1307, USA.
21 #include <config.h>
23 #include <ctype.h>
24 #include <errno.h>
25 #include <stdarg.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <unistd.h>
33 #include "../src/global.h"
35 #include "edit.h"
36 #include "edit-widget.h"
37 #include "editcmddef.h" /* list of commands */
38 #include "usermap.h"
39 #include "../src/key.h" /* KEY_M_* */
40 #include "../src/tty.h" /* keys */
41 #include "../src/wtools.h"
42 #include "../src/widget.h" /* buttonbar_redraw() */
44 typedef struct NameMap {
45 const char *name;
46 int val;
47 } name_map_t;
49 typedef struct Config {
50 time_t mtime; /* mtime at the moment we read config file */
51 GArray *keymap;
52 GArray *ext_keymap;
53 gchar *labels[10];
54 } config_t;
56 typedef struct Command {
57 const char *name;
58 int (*handler) (config_t *cfg, int argc, char *argv[]);
59 } command_t;
61 static char error_msg[200] = "Nobody see this";
63 static const name_map_t key_names[] = {
64 {"backspace", KEY_BACKSPACE},
65 {"end", KEY_END},
66 {"up", KEY_UP},
67 {"down", KEY_DOWN},
68 {"left", KEY_LEFT},
69 {"right", KEY_RIGHT},
70 {"home", KEY_HOME},
71 {"a1", KEY_A1},
72 {"c1", KEY_C1},
73 {"npage", KEY_NPAGE},
74 {"ppage", KEY_PPAGE},
75 {"ic", KEY_IC},
76 {"enter", KEY_ENTER},
77 {"dc", KEY_DC},
78 {"scancel", KEY_SCANCEL},
79 {"btab", KEY_BTAB},
80 {"f11", KEY_F(11)},
81 {"f12", KEY_F(12)},
82 {"f13", KEY_F(13)},
83 {"f14", KEY_F(14)},
84 {"f15", KEY_F(15)},
85 {"f16", KEY_F(16)},
86 {"f17", KEY_F(17)},
87 {"f18", KEY_F(18)},
88 {"f19", KEY_F(19)},
89 {"f20", KEY_F(20)},
90 {"tab", '\t'},
91 {"return", '\n'},
92 {NULL, 0}
95 static const name_map_t command_names[] = {
96 {"No-Command", CK_Ignore_Key},
97 {"Ignore-Key", CK_Ignore_Key},
98 {"BackSpace", CK_BackSpace},
99 {"Delete", CK_Delete},
100 {"Enter", CK_Enter},
101 {"Page-Up", CK_Page_Up},
102 {"Page-Down", CK_Page_Down},
103 {"Left", CK_Left},
104 {"Right", CK_Right},
105 {"Word-Left", CK_Word_Left},
106 {"Word-Right", CK_Word_Right},
107 {"Up", CK_Up},
108 {"Down", CK_Down},
109 {"Home", CK_Home},
110 {"End", CK_End},
111 {"Tab", CK_Tab},
112 {"Undo", CK_Undo},
113 {"Beginning-Of-Text", CK_Beginning_Of_Text},
114 {"End-Of-Text", CK_End_Of_Text},
115 {"Scroll-Up", CK_Scroll_Up},
116 {"Scroll-Down", CK_Scroll_Down},
117 {"Return", CK_Return},
118 {"Begin-Page", CK_Begin_Page},
119 {"End-Page", CK_End_Page},
120 {"Delete-Word-Left", CK_Delete_Word_Left},
121 {"Delete-Word-Right", CK_Delete_Word_Right},
122 {"Paragraph-Up", CK_Paragraph_Up},
123 {"Paragraph-Down", CK_Paragraph_Down},
124 {"Save", CK_Save},
125 {"Load", CK_Load},
126 {"New", CK_New},
127 {"Save-as", CK_Save_As},
128 {"Mark", CK_Mark},
129 {"Copy", CK_Copy},
130 {"Move", CK_Move},
131 {"Remove", CK_Remove},
132 {"Unmark", CK_Unmark},
133 {"Save-Block", CK_Save_Block},
134 {"Column-Mark", CK_Column_Mark},
135 {"Find", CK_Find},
136 {"Find-Again", CK_Find_Again},
137 {"Replace", CK_Replace},
138 {"Replace-Again", CK_Replace_Again},
139 {"Complete-Word", CK_Complete_Word},
140 {"Debug-Start", CK_Debug_Start},
141 {"Debug-Stop", CK_Debug_Stop},
142 {"Debug-Toggle-Break", CK_Debug_Toggle_Break},
143 {"Debug-Clear", CK_Debug_Clear},
144 {"Debug-Next", CK_Debug_Next},
145 {"Debug-Step", CK_Debug_Step},
146 {"Debug-Back-Trace", CK_Debug_Back_Trace},
147 {"Debug-Continue", CK_Debug_Continue},
148 {"Debug-Enter-Command", CK_Debug_Enter_Command},
149 {"Debug-Until-Curser", CK_Debug_Until_Curser},
150 {"Insert-File", CK_Insert_File},
151 {"Exit", CK_Exit},
152 {"Toggle-Insert", CK_Toggle_Insert},
153 {"Help", CK_Help},
154 {"Date", CK_Date},
155 {"Refresh", CK_Refresh},
156 {"Goto", CK_Goto},
157 {"Delete-Line", CK_Delete_Line},
158 {"Delete-To-Line-End", CK_Delete_To_Line_End},
159 {"Delete-To-Line-Begin", CK_Delete_To_Line_Begin},
160 {"Man-Page", CK_Man_Page},
161 {"Sort", CK_Sort},
162 {"Mail", CK_Mail},
163 {"Cancel", CK_Cancel},
164 {"Complete", CK_Complete},
165 {"Paragraph-Format", CK_Paragraph_Format},
166 {"Util", CK_Util},
167 {"Type-Load-Python", CK_Type_Load_Python},
168 {"Find-File", CK_Find_File},
169 {"Ctags", CK_Ctags},
170 {"Match-Bracket", CK_Match_Bracket},
171 {"Terminal", CK_Terminal},
172 {"Terminal-App", CK_Terminal_App},
173 {"ExtCmd", CK_ExtCmd},
174 {"User-Menu", CK_User_Menu},
175 {"Save-Desktop", CK_Save_Desktop},
176 {"New-Window", CK_New_Window},
177 {"Cycle", CK_Cycle},
178 {"Menu", CK_Menu},
179 {"Save-And-Quit", CK_Save_And_Quit},
180 {"Run-Another", CK_Run_Another},
181 {"Check-Save-And-Quit", CK_Check_Save_And_Quit},
182 {"Maximize", CK_Maximize},
183 {"Begin-Record-Macro", CK_Begin_Record_Macro},
184 {"End-Record-Macro", CK_End_Record_Macro},
185 {"Delete-Macro", CK_Delete_Macro},
186 {"Toggle-Bookmark", CK_Toggle_Bookmark},
187 {"Flush-Bookmarks", CK_Flush_Bookmarks},
188 {"Next-Bookmark", CK_Next_Bookmark},
189 {"Prev-Bookmark", CK_Prev_Bookmark},
190 {"Page-Up-Highlight", CK_Page_Up_Highlight},
191 {"Page-Down-Highlight", CK_Page_Down_Highlight},
192 {"Left-Highlight", CK_Left_Highlight},
193 {"Right-Highlight", CK_Right_Highlight},
194 {"Word-Left-Highlight", CK_Word_Left_Highlight},
195 {"Word-Right-Highlight", CK_Word_Right_Highlight},
196 {"Up-Highlight", CK_Up_Highlight},
197 {"Down-Highlight", CK_Down_Highlight},
198 {"Home-Highlight", CK_Home_Highlight},
199 {"End-Highlight", CK_End_Highlight},
200 {"Beginning-Of-Text-Highlight", CK_Beginning_Of_Text_Highlight},
201 {"End-Of-Text_Highlight", CK_End_Of_Text_Highlight},
202 {"Begin-Page-Highlight", CK_Begin_Page_Highlight},
203 {"End-Page-Highlight", CK_End_Page_Highlight},
204 {"Scroll-Up-Highlight", CK_Scroll_Up_Highlight},
205 {"Scroll-Down-Highlight", CK_Scroll_Down_Highlight},
206 {"Paragraph-Up-Highlight", CK_Paragraph_Up_Highlight},
207 {"Paragraph-Down-Highlight", CK_Paragraph_Down_Highlight},
208 {"XStore", CK_XStore},
209 {"XCut", CK_XCut},
210 {"XPaste", CK_XPaste},
211 {"Selection-History", CK_Selection_History},
212 {"Shell", CK_Shell},
213 {"Select-Codepage", CK_Select_Codepage},
214 {"Insert-Literal", CK_Insert_Literal},
215 {"Execute-Macro", CK_Execute_Macro},
216 {"Begin-or-End-Macro", CK_Begin_End_Macro},
217 {"Ext-mode", CK_Ext_Mode},
218 #if 0
219 {"Focus-Next", CK_Focus_Next},
220 {"Focus-Prev", CK_Focus_Prev},
221 {"Height-Inc", CK_Height_Inc},
222 {"Height-Dec", CK_Height_Dec},
223 {"Make", CK_Make},
224 {"Error-Next", CK_Error_Next},
225 {"Error-Prev", CK_Error_Prev},
226 #endif
227 {NULL, 0}
230 static void
231 cfg_free_maps(config_t *cfg)
233 int i;
235 if (cfg->keymap)
236 g_array_free(cfg->keymap, TRUE);
237 cfg->keymap = NULL;
238 if (cfg->ext_keymap)
239 g_array_free(cfg->ext_keymap, TRUE);
240 cfg->ext_keymap = NULL;
242 for (i = 0; i < 10; i++)
243 g_free(cfg->labels[i]);
246 /* Returns an array containing the words in str. WARNING: As long as
247 * the result is used, str[...] must be valid memory. This function
248 * modifies str[...] and uses it, so the caller should not use it until
249 * g_ptr_array_free() is called for the returned result.
251 static GPtrArray *
252 split_line(char *str)
254 gboolean inside_quote = FALSE;
255 int move = 0;
256 GPtrArray *args;
258 args = g_ptr_array_new();
260 /* skip spaces */
261 while (isspace((unsigned char) *str))
262 str++;
264 g_ptr_array_add(args, str);
266 for (;; str++) {
267 switch (*str) {
268 case '#': /* cut off comments */
269 if (inside_quote)
270 break;
271 /* FALLTHROUGH */
273 case '\n': /* end of line */
274 case '\r':
275 case '\0':
276 if (str == g_ptr_array_index(args, args->len - 1))
277 g_ptr_array_remove_index(args, args->len - 1);
278 else
279 *(str - move) = '\0';
280 return args;
282 case '"': /* quote */
283 case '\'':
284 inside_quote = !inside_quote;
285 move++;
286 continue;
288 case ' ': /* spaces */
289 case '\t':
290 if (inside_quote)
291 break;
293 *(str++ - move) = '\0';
294 move = 0;
296 /* skip spaces */
297 while (isspace((unsigned char) *str))
298 str++;
300 g_ptr_array_add(args, str--);
301 break;
303 case '\\':
304 switch (*(++str)) {
305 case 'n':
306 *str = '\n';
307 break;
308 case 'r':
309 *str = '\r';
310 break;
311 case 't':
312 *str = '\t';
313 break;
315 move++;
316 break;
318 if (move != 0)
319 *(str - move) = *str;
321 /* never be here */
324 static void
325 keymap_add(GArray *keymap, int key, int cmd)
327 edit_key_map_type new_one, *map;
328 guint i;
330 map = &(g_array_index(keymap, edit_key_map_type, 0));
331 for (i = 0; i < keymap->len; i++) {
332 if (map[i].key == key) {
333 map[i].command = cmd;
334 return;
338 new_one.key = key;
339 new_one.command = cmd;
340 g_array_append_val(keymap, new_one);
343 /* bind <key> <command> */
344 static gboolean
345 cmd_bind(config_t *cfg, int argc, char *argv[])
347 char *keyname, *command;
348 const name_map_t *key = key_names;
349 const name_map_t *cmd = command_names;
350 int mod = 0, k = -1, m = 0;
352 if (argc != 3) {
353 snprintf(error_msg, sizeof(error_msg),
354 _("bind: Wrong argument number, bind <key> <command>"));
355 return FALSE;
358 keyname = argv[1];
359 command = argv[2];
361 while (*keyname) {
362 switch (*keyname++) {
363 case 'C':
364 m = KEY_M_CTRL;
365 continue;
366 case 'M':
367 case 'A':
368 m = KEY_M_ALT;
369 continue;
370 case 'S':
371 m = KEY_M_SHIFT;
372 continue;
373 case '-':
374 if (!m) { /* incorrect key */
375 snprintf(error_msg, sizeof(error_msg),
376 _("bind: Bad key value `%s'"), keyname);
377 return FALSE;
379 mod |= m;
380 m = 0;
381 continue;
383 keyname--;
384 break;
387 /* no key */
388 if (keyname[0] == '\0') {
389 snprintf(error_msg, sizeof(error_msg), _("bind: Ehh...no key?"));
390 return FALSE;
393 /* ordinary key */
394 if (keyname[1] == '\0') {
395 k = keyname[0];
396 } else {
397 while (key->name && strcasecmp(key->name, keyname) != 0)
398 key++;
401 if (k < 0 && !key->name) {
402 snprintf(error_msg, sizeof(error_msg),
403 _("bind: Unknown key: `%s'"), keyname);
404 return FALSE;
407 while (cmd->name && strcasecmp(cmd->name, command) != 0)
408 cmd++;
410 if (!cmd->name) {
411 snprintf(error_msg, sizeof(error_msg),
412 _("bind: Unknown command: `%s'"), command);
413 return FALSE;
416 if (mod & KEY_M_CTRL) {
417 if (k < 256)
418 k = XCTRL(k);
419 else
420 k |= KEY_M_CTRL;
423 if (mod & KEY_M_ALT)
424 k |= KEY_M_ALT;
426 if (mod & KEY_M_SHIFT)
427 k |= KEY_M_SHIFT;
429 if (!strcasecmp("bind-ext", argv[0]))
430 keymap_add(cfg->ext_keymap, k, cmd->val);
431 else
432 keymap_add(cfg->keymap, k, cmd->val);
434 return TRUE;
437 #if 0
438 #define CMD_F(x) \
439 static void cmd_F ## x (WEdit * edit) \
441 send_message ((Widget *) edit, WIDGET_KEY, KEY_F (x)); \
444 CMD_F(1)
445 CMD_F(2)
446 CMD_F(3)
447 CMD_F(4)
448 CMD_F(5)
449 CMD_F(6)
450 CMD_F(7)
451 CMD_F(8)
452 CMD_F(9)
453 CMD_F(10)
455 void (*cmd_Fx[]) (WEdit *) = {
456 cmd_F1, cmd_F2, cmd_F3, cmd_F4, cmd_F5,
457 cmd_F6, cmd_F7, cmd_F8, cmd_F9, cmd_F10
460 /* move me! */
461 static void edit_my_define (Dlg_head * h, int idx, const char *text,
462 void (*fn) (WEdit *), WEdit * edit)
464 /* function-cast ok */
465 buttonbar_set_label_data (h, idx, text, (buttonbarfn) fn, edit);
467 #endif
469 /* label <number> <command> <label> */
470 static gboolean
471 cmd_label(config_t *cfg, int argc, char *argv[])
473 const name_map_t *cmd = command_names;
474 const char *command, *label;
475 int fn;
477 if (argc != 4) {
478 snprintf(error_msg, sizeof(error_msg),
479 _("%s: Syntax: %s <n> <command> <label>"),
480 argv[0], argv[0]);
481 return FALSE;
484 fn = strtol(argv[1], NULL, 0);
485 command = argv[2];
486 label = argv[3];
488 while (cmd->name && strcasecmp(cmd->name, command) != 0)
489 cmd++;
491 if (!cmd->name) {
492 snprintf(error_msg, sizeof(error_msg),
493 _("%s: Unknown command: `%s'"), argv[0], command);
494 return FALSE;
497 if (fn < 1 || fn > 10) {
498 snprintf(error_msg, sizeof(error_msg),
499 _("%s: fn should be 1-10"), argv[0]);
500 return FALSE;
503 keymap_add(cfg->keymap, KEY_F(fn), cmd->val);
504 cfg->labels[fn - 1] = g_strdup(label);
506 return TRUE;
510 static gboolean
511 parse_file(config_t *cfg, const char *file, const command_t *cmd)
513 char buf[200];
514 int line = 0;
516 FILE *fp = fopen(file, "r");
518 if (!fp) {
519 snprintf(error_msg, sizeof(error_msg), _("%s: fopen(): %s"),
520 file, strerror(errno));
521 return FALSE;
524 while (fgets(buf, sizeof(buf), fp)) {
525 const command_t *c = cmd;
526 GPtrArray *args;
527 char **argv;
529 line++;
531 args = split_line(buf);
532 argv = (char **) args->pdata;
534 if (args->len == 0) {
535 g_ptr_array_free(args, TRUE);
536 continue;
539 while (c->name != NULL && strcasecmp(c->name, argv[0]) != 0)
540 c++;
542 if (c->name == NULL) {
543 snprintf(error_msg, sizeof(error_msg),
544 _("%s:%d: unknown command `%s'"), file, line,
545 argv[0]);
546 g_ptr_array_free(args, TRUE);
547 fclose(fp);
548 return FALSE;
551 if (!(c->handler(cfg, args->len, argv))) {
552 char *ss = g_strdup(error_msg);
553 snprintf(error_msg, sizeof(error_msg),
554 _("%s:%d: %s"), file, line, ss);
555 g_free(ss);
556 g_ptr_array_free(args, TRUE);
557 fclose(fp);
558 return FALSE;
560 g_ptr_array_free(args, TRUE);
563 fclose(fp);
564 return TRUE;
567 static gboolean
568 load_user_keymap(config_t *cfg, const char *file)
570 const command_t cmd[] = {
571 {"bind", cmd_bind},
572 {"bind-ext", cmd_bind},
573 {"label", cmd_label},
574 {0, 0}
577 cfg->keymap = g_array_new(TRUE, FALSE, sizeof(edit_key_map_type));
578 cfg->ext_keymap = g_array_new(TRUE, FALSE, sizeof(edit_key_map_type));
580 if (!parse_file(cfg, file, cmd)) {
581 return FALSE;
584 return TRUE;
587 gboolean
588 edit_load_user_map(WEdit *edit)
590 static config_t cfg;
591 config_t new_cfg;
592 char *file;
593 struct stat s;
595 if (edit_key_emulation != EDIT_KEY_EMULATION_USER)
596 return TRUE;
598 file = concat_dir_and_file(home_dir, MC_USERMAP);
600 if (stat(file, &s) < 0) {
601 char *msg = g_strdup_printf(_("%s not found!"), file);
602 edit_error_dialog(_("Error"), msg);
603 g_free(msg);
604 g_free(file);
605 return FALSE;
608 if (s.st_mtime != cfg.mtime) {
609 memset(&new_cfg, 0, sizeof(new_cfg));
610 new_cfg.mtime = s.st_mtime;
612 if (!load_user_keymap(&new_cfg, file)) {
613 edit_error_dialog(_("Error"), error_msg);
614 cfg_free_maps(&new_cfg);
615 g_free(file);
616 return FALSE;
617 } else {
618 cfg_free_maps(&cfg);
619 cfg = new_cfg;
623 edit->user_map = (edit_key_map_type *) cfg.keymap->data;
624 edit->ext_map = (edit_key_map_type *) cfg.ext_keymap->data;
625 memcpy(edit->labels, cfg.labels, sizeof(edit->labels));
627 g_free(file);
629 return TRUE;