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