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