Intial commit
[profile/ivi/w3m.git] / func.c
1 /* $Id: func.c,v 1.27 2003/09/26 17:59:51 ukai Exp $ */
2 /*
3  * w3m func.c
4  */
5
6 #include <stdio.h>
7
8 #include "fm.h"
9 #include "func.h"
10 #include "myctype.h"
11
12 #include "funcname.c"
13 #include "functable.c"
14
15 #define KEYDATA_HASH_SIZE 16
16 static Hash_iv *keyData = NULL;
17 static char keymap_initialized = FALSE;
18 static struct stat sys_current_keymap_file;
19 static struct stat current_keymap_file;
20
21 void
22 setKeymap(char *p, int lineno, int verbose)
23 {
24     unsigned char *map = NULL;
25     char *s, *emsg;
26     int c, f;
27
28     s = getQWord(&p);
29     c = getKey(s);
30     if (c < 0) {                /* error */
31         if (lineno > 0)
32             /* FIXME: gettextize? */
33             emsg = Sprintf("line %d: unknown key '%s'", lineno, s)->ptr;
34         else
35             /* FIXME: gettextize? */
36             emsg = Sprintf("defkey: unknown key '%s'", s)->ptr;
37         record_err_message(emsg);
38         if (verbose)
39             disp_message_nsec(emsg, FALSE, 1, TRUE, FALSE);
40         return;
41     }
42     s = getWord(&p);
43     f = getFuncList(s);
44     if (f < 0) {
45         if (lineno > 0)
46             /* FIXME: gettextize? */
47             emsg = Sprintf("line %d: invalid command '%s'", lineno, s)->ptr;
48         else
49             /* FIXME: gettextize? */
50             emsg = Sprintf("defkey: invalid command '%s'", s)->ptr;
51         record_err_message(emsg);
52         if (verbose)
53             disp_message_nsec(emsg, FALSE, 1, TRUE, FALSE);
54         return;
55     }
56     if (c & K_MULTI) {
57         unsigned char **mmap = NULL;
58         int i, j, m = MULTI_KEY(c);
59
60         if (m & K_ESCD)
61             map = EscDKeymap;
62         else if (m & K_ESCB)
63             map = EscBKeymap;
64         else if (m & K_ESC)
65             map = EscKeymap;
66         else
67             map = GlobalKeymap;
68         if (map[m & 0x7F] == FUNCNAME_multimap)
69             mmap = (unsigned char **)getKeyData(m);
70         else
71             map[m & 0x7F] = FUNCNAME_multimap;
72         if (!mmap) {
73             mmap = New_N(unsigned char *, 4);
74             for (i = 0; i < 4; i++) {
75                 mmap[i] = New_N(unsigned char, 128);
76                 for (j = 0; j < 128; j++)
77                     mmap[i][j] = FUNCNAME_nulcmd;
78             }
79             mmap[0][ESC_CODE] = FUNCNAME_escmap;
80             mmap[1]['['] = FUNCNAME_escbmap;
81             mmap[1]['O'] = FUNCNAME_escbmap;
82         }
83         if (keyData == NULL)
84             keyData = newHash_iv(KEYDATA_HASH_SIZE);
85         putHash_iv(keyData, m, (void *)mmap);
86         if (c & K_ESCD)
87             map = mmap[3];
88         else if (c & K_ESCB)
89             map = mmap[2];
90         else if (c & K_ESC)
91             map = mmap[1];
92         else
93             map = mmap[0];
94     }
95     else {
96         if (c & K_ESCD)
97             map = EscDKeymap;
98         else if (c & K_ESCB)
99             map = EscBKeymap;
100         else if (c & K_ESC)
101             map = EscKeymap;
102         else
103             map = GlobalKeymap;
104     }
105     map[c & 0x7F] = f;
106     s = getQWord(&p);
107     if (*s) {
108         if (keyData == NULL)
109             keyData = newHash_iv(KEYDATA_HASH_SIZE);
110         putHash_iv(keyData, c, (void *)s);
111     }
112     else if (getKeyData(c))
113         putHash_iv(keyData, c, NULL);
114 }
115
116 static void
117 interpret_keymap(FILE * kf, struct stat *current, int force)
118 {
119     int fd;
120     struct stat kstat;
121     Str line;
122     char *p, *s, *emsg;
123     int lineno;
124 #ifdef USE_M17N
125     wc_ces charset = SystemCharset;
126 #endif
127     int verbose = 1;
128     extern int str_to_bool(char *value, int old);
129
130     if ((fd = fileno(kf)) < 0 || fstat(fd, &kstat) ||
131         (!force &&
132          kstat.st_mtime == current->st_mtime &&
133          kstat.st_dev == current->st_dev &&
134          kstat.st_ino == current->st_ino && kstat.st_size == current->st_size))
135         return;
136     *current = kstat;
137
138     lineno = 0;
139     while (!feof(kf)) {
140         line = Strfgets(kf);
141         lineno++;
142         Strchop(line);
143         Strremovefirstspaces(line);
144         if (line->length == 0)
145             continue;
146 #ifdef USE_M17N
147         line = wc_Str_conv(line, charset, InnerCharset);
148 #endif
149         p = line->ptr;
150         s = getWord(&p);
151         if (*s == '#')          /* comment */
152             continue;
153         if (!strcmp(s, "keymap")) ;
154 #ifdef USE_M17N
155         else if (!strcmp(s, "charset") || !strcmp(s, "encoding")) {
156             s = getQWord(&p);
157             if (*s)
158                 charset = wc_guess_charset(s, charset);
159             continue;
160         }
161 #endif
162         else if (!strcmp(s, "verbose")) {
163             s = getWord(&p);
164             if (*s)
165                 verbose = str_to_bool(s, verbose);
166             continue;
167         }
168         else {                  /* error */
169             emsg = Sprintf("line %d: syntax error '%s'", lineno, s)->ptr;
170             record_err_message(emsg);
171             if (verbose)
172                 disp_message_nsec(emsg, FALSE, 1, TRUE, FALSE);
173             continue;
174         }
175         setKeymap(p, lineno, verbose);
176     }
177 }
178
179 void
180 initKeymap(int force)
181 {
182     FILE *kf;
183
184     if ((kf = fopen(confFile(KEYMAP_FILE), "rt")) != NULL) {
185         interpret_keymap(kf, &sys_current_keymap_file,
186                          force || !keymap_initialized);
187         fclose(kf);
188     }
189     if ((kf = fopen(rcFile(keymap_file), "rt")) != NULL) {
190         interpret_keymap(kf, &current_keymap_file,
191                          force || !keymap_initialized);
192         fclose(kf);
193     }
194     keymap_initialized = TRUE;
195 }
196
197 int
198 getFuncList(char *id)
199 {
200     return getHash_si(&functable, id, -1);
201 }
202
203 char *
204 getKeyData(int key)
205 {
206     if (keyData == NULL)
207         return NULL;
208     return (char *)getHash_iv(keyData, key, NULL);
209 }
210
211 static int
212 getKey2(char **str)
213 {
214     char *s = *str;
215     int c, esc = 0, ctrl = 0;
216
217     if (s == NULL || *s == '\0')
218         return -1;
219
220     if (strcasecmp(s, "UP") == 0) {     /* ^[[A */
221         *str = s + 2;
222         return K_ESCB | 'A';
223     }
224     else if (strcasecmp(s, "DOWN") == 0) {      /* ^[[B */
225         *str = s + 4;
226         return K_ESCB | 'B';
227     }
228     else if (strcasecmp(s, "RIGHT") == 0) {     /* ^[[C */
229         *str = s + 5;
230         return K_ESCB | 'C';
231     }
232     else if (strcasecmp(s, "LEFT") == 0) {      /* ^[[D */
233         *str = s + 4;
234         return K_ESCB | 'D';
235     }
236
237     if (strncasecmp(s, "ESC-", 4) == 0 || strncasecmp(s, "ESC ", 4) == 0) {     /* ^[ */
238         s += 4;
239         esc = K_ESC;
240     }
241     else if (strncasecmp(s, "M-", 2) == 0 || strncasecmp(s, "\\E", 2) == 0) {   /* ^[ */
242         s += 2;
243         esc = K_ESC;
244     }
245     else if (*s == ESC_CODE) {  /* ^[ */
246         s++;
247         esc = K_ESC;
248     }
249     if (strncasecmp(s, "C-", 2) == 0) { /* ^, ^[^ */
250         s += 2;
251         ctrl = 1;
252     }
253     else if (*s == '^' && *(s + 1)) {   /* ^, ^[^ */
254         s++;
255         ctrl = 1;
256     }
257     if (!esc && ctrl && *s == '[') {    /* ^[ */
258         s++;
259         ctrl = 0;
260         esc = K_ESC;
261     }
262     if (esc && !ctrl) {
263         if (*s == '[' || *s == 'O') {   /* ^[[, ^[O */
264             s++;
265             esc = K_ESCB;
266         }
267         if (strncasecmp(s, "C-", 2) == 0) {     /* ^[^, ^[[^ */
268             s += 2;
269             ctrl = 1;
270         }
271         else if (*s == '^' && *(s + 1)) {       /* ^[^, ^[[^ */
272             s++;
273             ctrl = 1;
274         }
275     }
276
277     if (ctrl) {
278         *str = s + 1;
279         if (*s >= '@' && *s <= '_')     /* ^@ .. ^_ */
280             return esc | (*s - '@');
281         else if (*s >= 'a' && *s <= 'z')        /* ^a .. ^z */
282             return esc | (*s - 'a' + 1);
283         else if (*s == '?')     /* ^? */
284             return esc | DEL_CODE;
285         else
286             return -1;
287     }
288
289     if (esc == K_ESCB && IS_DIGIT(*s)) {
290         c = (int)(*s - '0');
291         s++;
292         if (IS_DIGIT(*s)) {
293             c = c * 10 + (int)(*s - '0');
294             s++;
295         }
296         *str = s + 1;
297         if (*s == '~')
298             return K_ESCD | c;
299         else
300             return -1;
301     }
302
303     if (strncasecmp(s, "SPC", 3) == 0) {        /* ' ' */
304         *str = s + 3;
305         return esc | ' ';
306     }
307     else if (strncasecmp(s, "TAB", 3) == 0) {   /* ^i */
308         *str = s + 3;
309         return esc | '\t';
310     }
311     else if (strncasecmp(s, "DEL", 3) == 0) {   /* ^? */
312         *str = s + 3;
313         return esc | DEL_CODE;
314     }
315
316     if (*s == '\\' && *(s + 1) != '\0') {
317         s++;
318         *str = s + 1;
319         switch (*s) {
320         case 'a':               /* ^g */
321             return esc | CTRL_G;
322         case 'b':               /* ^h */
323             return esc | CTRL_H;
324         case 't':               /* ^i */
325             return esc | CTRL_I;
326         case 'n':               /* ^j */
327             return esc | CTRL_J;
328         case 'r':               /* ^m */
329             return esc | CTRL_M;
330         case 'e':               /* ^[ */
331             return esc | ESC_CODE;
332         case '^':               /* ^ */
333             return esc | '^';
334         case '\\':              /* \ */
335             return esc | '\\';
336         default:
337             return -1;
338         }
339     }
340     *str = s + 1;
341     if (IS_ASCII(*s))           /* Ascii */
342         return esc | *s;
343     else
344         return -1;
345 }
346
347 int
348 getKey(char *s)
349 {
350     int c, c2;
351
352     c = getKey2(&s);
353     if (c < 0)
354         return -1;
355     if (*s == ' ' || *s == '-')
356         s++;
357     if (*s) {
358         c2 = getKey2(&s);
359         if (c2 < 0)
360             return -1;
361         c = K_MULTI | (c << 16) | c2;
362     }
363     return c;
364 }
365
366 char *
367 getWord(char **str)
368 {
369     char *p, *s;
370
371     p = *str;
372     SKIP_BLANKS(p);
373     for (s = p; *p && !IS_SPACE(*p) && *p != ';'; p++) ;
374     *str = p;
375     return Strnew_charp_n(s, p - s)->ptr;
376 }
377
378 char *
379 getQWord(char **str)
380 {
381     Str tmp = Strnew();
382     char *p;
383     int in_q = 0, in_dq = 0, esc = 0;
384
385     p = *str;
386     SKIP_BLANKS(p);
387     for (; *p; p++) {
388         if (esc) {
389             if (in_q) {
390                 if (*p != '\\' && *p != '\'')   /* '..\\..', '..\'..' */
391                     Strcat_char(tmp, '\\');
392             }
393             else if (in_dq) {
394                 if (*p != '\\' && *p != '"')    /* "..\\..", "..\".." */
395                     Strcat_char(tmp, '\\');
396             }
397             else {
398                 if (*p != '\\' && *p != '\'' && /* ..\\.., ..\'.. */
399                     *p != '"' && !IS_SPACE(*p)) /* ..\".., ..\.. */
400                     Strcat_char(tmp, '\\');
401             }
402             Strcat_char(tmp, *p);
403             esc = 0;
404         }
405         else if (*p == '\\') {
406             esc = 1;
407         }
408         else if (in_q) {
409             if (*p == '\'')
410                 in_q = 0;
411             else
412                 Strcat_char(tmp, *p);
413         }
414         else if (in_dq) {
415             if (*p == '"')
416                 in_dq = 0;
417             else
418                 Strcat_char(tmp, *p);
419         }
420         else if (*p == '\'') {
421             in_q = 1;
422         }
423         else if (*p == '"') {
424             in_dq = 1;
425         }
426         else if (IS_SPACE(*p) || *p == ';') {
427             break;
428         }
429         else {
430             Strcat_char(tmp, *p);
431         }
432     }
433     *str = p;
434     return tmp->ptr;
435 }
436
437 #ifdef USE_MOUSE
438 static MouseAction default_mouse_action = {
439     NULL,
440     "<=UpDn",
441     0, 6, FALSE, 0, 0,
442     {{movMs, NULL}, {backBf, NULL}, {menuMs, NULL}},    /* default */
443     {{NULL, NULL}, {NULL, NULL}, {NULL, NULL}}, /* anchor */
444     {{followA, NULL}, {NULL, NULL}, {NULL, NULL}},      /* active */
445     {{tabMs, NULL}, {closeTMs, NULL}, {NULL, NULL}},    /* tab */
446     {NULL, NULL, NULL},         /* menu */
447     {NULL, NULL, NULL}          /* lastline */
448 };
449 static MouseActionMap default_lastline_action[6] = {
450     {backBf, NULL},
451     {backBf, NULL},
452     {pgBack, NULL},
453     {pgBack, NULL},
454     {pgFore, NULL},
455     {pgFore, NULL}
456 };
457
458 static void
459 setMouseAction0(char **str, int *width, MouseActionMap ** map, char *p)
460 {
461     char *s;
462     int b, w, x;
463
464     s = getQWord(&p);
465     if (!*s) {
466         *str = NULL;
467         width = 0;
468         for (b = 0; b < 3; b++)
469             map[b] = NULL;
470         return;
471     }
472     w = *width;
473     *str = s;
474     *width = get_strwidth(s);
475     if (*width >= LIMIT_MOUSE_MENU)
476         *width = LIMIT_MOUSE_MENU;
477     if (*width <= w)
478         return;
479     for (b = 0; b < 3; b++) {
480         if (!map[b])
481             continue;
482         map[b] = New_Reuse(MouseActionMap, map[b], *width);
483         for (x = w + 1; x < *width; x++) {
484             map[b][x].func = NULL;
485             map[b][x].data = NULL;
486         }
487     }
488 }
489
490 static void
491 setMouseAction1(MouseActionMap ** map, int width, char *p)
492 {
493     char *s;
494     int x, x2, f;
495
496     if (!*map) {
497         *map = New_N(MouseActionMap, width);
498         for (x = 0; x < width; x++) {
499             (*map)[x].func = NULL;
500             (*map)[x].data = NULL;
501         }
502     }
503     s = getWord(&p);
504     x = atoi(s);
505     if (!(IS_DIGIT(*s) && x >= 0 && x < width))
506         return;                 /* error */
507     s = getWord(&p);
508     x2 = atoi(s);
509     if (!(IS_DIGIT(*s) && x2 >= 0 && x2 < width))
510         return;                 /* error */
511     s = getWord(&p);
512     f = getFuncList(s);
513     s = getQWord(&p);
514     if (!*s)
515         s = NULL;
516     for (; x <= x2; x++) {
517         (*map)[x].func = (f >= 0) ? w3mFuncList[f].func : NULL;
518         (*map)[x].data = s;
519     }
520 }
521
522 static void
523 setMouseAction2(MouseActionMap * map, char *p)
524 {
525     char *s;
526     int f;
527
528     s = getWord(&p);
529     f = getFuncList(s);
530     s = getQWord(&p);
531     if (!*s)
532         s = NULL;
533     map->func = (f >= 0) ? w3mFuncList[f].func : NULL;
534     map->data = s;
535 }
536
537 static void
538 interpret_mouse_action(FILE * mf)
539 {
540     Str line;
541     char *p, *s;
542     int b;
543
544     while (!feof(mf)) {
545         line = Strfgets(mf);
546         Strchop(line);
547         Strremovefirstspaces(line);
548         if (line->length == 0)
549             continue;
550         p = conv_from_system(line->ptr);
551         s = getWord(&p);
552         if (*s == '#')          /* comment */
553             continue;
554         if (!strcmp(s, "menu")) {
555             setMouseAction0(&mouse_action.menu_str, &mouse_action.menu_width,
556                             mouse_action.menu_map, p);
557             continue;
558         }
559         else if (!strcmp(s, "lastline")) {
560             setMouseAction0(&mouse_action.lastline_str,
561                             &mouse_action.lastline_width,
562                             mouse_action.lastline_map, p);
563             continue;
564         }
565         if (strcmp(s, "button"))
566             continue;           /* error */
567         s = getWord(&p);
568         b = atoi(s) - 1;
569         if (!(b >= 0 && b <= 2))
570             continue;           /* error */
571         SKIP_BLANKS(p);
572         if (IS_DIGIT(*p))
573             s = "menu";
574         else
575             s = getWord(&p);
576         if (!strcasecmp(s, "menu")) {
577             if (!mouse_action.menu_str)
578                 continue;
579             setMouseAction1(&mouse_action.menu_map[b], mouse_action.menu_width,
580                             p);
581         }
582         else if (!strcasecmp(s, "lastline")) {
583             if (!mouse_action.lastline_str)
584                 continue;
585             setMouseAction1(&mouse_action.lastline_map[b],
586                             mouse_action.lastline_width, p);
587         }
588         else if (!strcasecmp(s, "default"))
589             setMouseAction2(&mouse_action.default_map[b], p);
590         else if (!strcasecmp(s, "anchor"))
591             setMouseAction2(&mouse_action.anchor_map[b], p);
592         else if (!strcasecmp(s, "active"))
593             setMouseAction2(&mouse_action.active_map[b], p);
594         else if (!strcasecmp(s, "tab"))
595             setMouseAction2(&mouse_action.tab_map[b], p);
596     }
597 }
598
599 void
600 initMouseAction(void)
601 {
602     FILE *mf;
603
604     bcopy((void *)&default_mouse_action, (void *)&mouse_action,
605           sizeof(default_mouse_action));
606     mouse_action.lastline_map[0] = New_N(MouseActionMap, 6);
607     bcopy((void *)&default_lastline_action,
608           (void *)mouse_action.lastline_map[0],
609           sizeof(default_lastline_action));
610     {
611 #ifdef USE_M17N
612         int w = 0;
613         char **symbol = get_symbol(DisplayCharset, &w);
614 #else
615         char **symbol = get_symbol();
616 #endif
617         mouse_action.lastline_str =
618             Strnew_charp(symbol[N_GRAPH_SYMBOL + 13])->ptr;
619     }
620
621     if ((mf = fopen(confFile(MOUSE_FILE), "rt")) != NULL) {
622         interpret_mouse_action(mf);
623         fclose(mf);
624     }
625     if ((mf = fopen(rcFile(MOUSE_FILE), "rt")) != NULL) {
626         interpret_mouse_action(mf);
627         fclose(mf);
628     }
629 }
630 #endif