replaced buggy concat_dir_and_file() by mhl_str_dir_plus_file()
[free-mc.git] / edit / syntax.c
blob2cfe976280a2c23900f327d7942bdab098c67afa
1 /* editor syntax highlighting.
3 Copyright (C) 1996, 1997, 1998, 2001, 2002, 2003, 2004, 2005, 2006,
4 2007 Free Software Foundation, Inc.
6 Authors: 1998 Paul Sheer
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 02110-1301, USA.
24 #include <config.h>
25 #include <stdio.h>
26 #include <stdarg.h>
27 #include <sys/types.h>
28 #include <unistd.h>
29 #include <string.h>
30 #include <ctype.h>
31 #include <errno.h>
32 #include <sys/stat.h>
33 #include <stdlib.h>
35 #include <mhl/string.h>
37 #include "../src/global.h"
39 #include "edit.h"
40 #include "edit-widget.h"
41 #include "../src/color.h" /* use_colors */
42 #include "../src/main.h" /* mc_home */
43 #include "../src/wtools.h" /* message() */
45 /* bytes */
46 #define SYNTAX_MARKER_DENSITY 512
49 Mispelled words are flushed from the syntax highlighting rules
50 when they have been around longer than
51 TRANSIENT_WORD_TIME_OUT seconds. At a cursor rate of 30
52 chars per second and say 3 chars + a space per word, we can
53 accumulate 450 words absolute max with a value of 60. This is
54 below this limit of 1024 words in a context.
56 #define TRANSIENT_WORD_TIME_OUT 60
58 #define UNKNOWN_FORMAT "unknown"
60 #define MAX_WORDS_PER_CONTEXT 1024
61 #define MAX_CONTEXTS 128
63 #define RULE_ON_LEFT_BORDER 1
64 #define RULE_ON_RIGHT_BORDER 2
66 #define SYNTAX_TOKEN_STAR '\001'
67 #define SYNTAX_TOKEN_PLUS '\002'
68 #define SYNTAX_TOKEN_BRACKET '\003'
69 #define SYNTAX_TOKEN_BRACE '\004'
71 struct key_word {
72 char *keyword;
73 unsigned char first;
74 char *whole_word_chars_left;
75 char *whole_word_chars_right;
76 int line_start;
77 int color;
80 struct context_rule {
81 char *left;
82 unsigned char first_left;
83 char *right;
84 unsigned char first_right;
85 char line_start_left;
86 char line_start_right;
87 int between_delimiters;
88 char *whole_word_chars_left;
89 char *whole_word_chars_right;
90 char *keyword_first_chars;
91 int spelling;
92 /* first word is word[1] */
93 struct key_word **keyword;
96 struct _syntax_marker {
97 long offset;
98 struct syntax_rule rule;
99 struct _syntax_marker *next;
102 int option_syntax_highlighting = 1;
103 int option_auto_syntax = 1;
104 char *option_syntax_type = NULL;
106 #define syntax_g_free(x) do {g_free(x); (x)=0;} while (0)
108 static gint
109 mc_defines_destroy (gpointer key, gpointer value, gpointer data)
111 char **values = value;
113 (void) data;
115 g_free (key);
116 while (*values)
117 g_free (*values++);
118 g_free (value);
120 return FALSE;
123 /* Completely destroys the defines tree */
124 static inline void
125 destroy_defines (GTree **defines)
127 g_tree_traverse (*defines, mc_defines_destroy, G_POST_ORDER, NULL);
128 g_tree_destroy (*defines);
129 *defines = 0;
132 static void
133 subst_defines (GTree *defines, char **argv, char **argv_end)
135 char **t, **p;
136 int argc;
138 while (*argv && argv < argv_end) {
139 if ((t = g_tree_lookup (defines, *argv))) {
140 int count = 0;
142 /* Count argv array members */
143 argc = 0;
144 for (p = &argv[1]; *p; p++)
145 argc++;
147 /* Count members of definition array */
148 for (p = t; *p; p++)
149 count++;
150 p = &argv[count + argc];
152 /* Buffer overflow or infinitive loop in define */
153 if (p >= argv_end)
154 break;
156 /* Move rest of argv after definition members */
157 while (argc >= 0)
158 *p-- = argv[argc-- + 1];
160 /* Copy definition members to argv */
161 for (p = argv; *t; *p++ = *t++);
163 argv++;
167 static long
168 compare_word_to_right (WEdit *edit, long i, const char *text,
169 const char *whole_left, const char *whole_right,
170 int line_start)
172 const unsigned char *p, *q;
173 int c, d, j;
175 if (!*text)
176 return -1;
177 c = edit_get_byte (edit, i - 1);
178 if (line_start)
179 if (c != '\n')
180 return -1;
181 if (whole_left)
182 if (strchr (whole_left, c))
183 return -1;
185 for (p = (unsigned char *) text, q = p + strlen ((char *) p); p < q; p++, i++) {
186 switch (*p) {
187 case SYNTAX_TOKEN_STAR:
188 if (++p > q)
189 return -1;
190 for (;;) {
191 c = edit_get_byte (edit, i);
192 if (!*p)
193 if (whole_right)
194 if (!strchr (whole_right, c))
195 break;
196 if (c == *p)
197 break;
198 if (c == '\n')
199 return -1;
200 i++;
202 break;
203 case SYNTAX_TOKEN_PLUS:
204 if (++p > q)
205 return -1;
206 j = 0;
207 for (;;) {
208 c = edit_get_byte (edit, i);
209 if (c == *p) {
210 j = i;
211 if (*p == *text && !p[1]) /* handle eg '+' and @+@ keywords properly */
212 break;
214 if (j && strchr ((char *) p + 1, c)) /* c exists further down, so it will get matched later */
215 break;
216 if (c == '\n' || c == '\t' || c == ' ') {
217 if (!*p) {
218 i--;
219 break;
221 if (!j)
222 return -1;
223 i = j;
224 break;
226 if (whole_right)
227 if (!strchr (whole_right, c)) {
228 if (!*p) {
229 i--;
230 break;
232 if (!j)
233 return -1;
234 i = j;
235 break;
237 i++;
239 break;
240 case SYNTAX_TOKEN_BRACKET:
241 if (++p > q)
242 return -1;
243 c = -1;
244 for (;; i++) {
245 d = c;
246 c = edit_get_byte (edit, i);
247 for (j = 0; p[j] != SYNTAX_TOKEN_BRACKET && p[j]; j++)
248 if (c == p[j])
249 goto found_char2;
250 break;
251 found_char2:
252 ; /* dummy command */
254 i--;
255 while (*p != SYNTAX_TOKEN_BRACKET && p <= q)
256 p++;
257 if (p > q)
258 return -1;
259 if (p[1] == d)
260 i--;
261 break;
262 case SYNTAX_TOKEN_BRACE:
263 if (++p > q)
264 return -1;
265 c = edit_get_byte (edit, i);
266 for (; *p != SYNTAX_TOKEN_BRACE && *p; p++)
267 if (c == *p)
268 goto found_char3;
269 return -1;
270 found_char3:
271 while (*p != SYNTAX_TOKEN_BRACE && p < q)
272 p++;
273 break;
274 default:
275 if (*p != edit_get_byte (edit, i))
276 return -1;
279 if (whole_right)
280 if (strchr (whole_right, edit_get_byte (edit, i)))
281 return -1;
282 return i;
285 static inline const char *xx_strchr (const unsigned char *s, int c)
287 while (*s >= '\005' && *s != (unsigned char) c) {
288 s++;
290 return (const char *) s;
293 static inline struct syntax_rule apply_rules_going_right (WEdit * edit, long i, struct syntax_rule rule)
295 struct context_rule *r;
296 int contextchanged = 0, c;
297 int found_right = 0, found_left = 0, keyword_foundleft = 0, keyword_foundright = 0;
298 int is_end;
299 long end = 0;
300 struct syntax_rule _rule = rule;
302 if (!(c = edit_get_byte (edit, i)))
303 return rule;
304 is_end = (rule.end == (unsigned char) i);
306 /* check to turn off a keyword */
307 if (_rule.keyword) {
308 if (edit_get_byte (edit, i - 1) == '\n')
309 _rule.keyword = 0;
310 if (is_end) {
311 _rule.keyword = 0;
312 keyword_foundleft = 1;
316 /* check to turn off a context */
317 if (_rule.context && !_rule.keyword) {
318 long e;
319 r = edit->rules[_rule.context];
320 if (r->first_right == c && !(rule.border & RULE_ON_RIGHT_BORDER) && (e = compare_word_to_right (edit, i, r->right, r->whole_word_chars_left, r->whole_word_chars_right, r->line_start_right)) > 0) {
321 _rule.end = e;
322 found_right = 1;
323 _rule.border = RULE_ON_RIGHT_BORDER;
324 if (r->between_delimiters)
325 _rule.context = 0;
326 } else if (is_end && rule.border & RULE_ON_RIGHT_BORDER) {
328 /* always turn off a context at 4 */
329 found_left = 1;
330 _rule.border = 0;
331 if (!keyword_foundleft)
332 _rule.context = 0;
333 } else if (is_end && rule.border & RULE_ON_LEFT_BORDER) {
335 /* never turn off a context at 2 */
336 found_left = 1;
337 _rule.border = 0;
341 /* check to turn on a keyword */
342 if (!_rule.keyword) {
343 const char *p;
345 p = (r = edit->rules[_rule.context])->keyword_first_chars;
346 if (p)
347 while (*(p = xx_strchr ((unsigned char *) p + 1, c))) {
348 struct key_word *k;
349 int count;
350 long e;
352 count = p - r->keyword_first_chars;
353 k = r->keyword[count];
354 e = compare_word_to_right (edit, i, k->keyword, k->whole_word_chars_left, k->whole_word_chars_right, k->line_start);
355 if (e > 0) {
356 end = e;
357 _rule.end = e;
358 _rule.keyword = count;
359 keyword_foundright = 1;
360 break;
364 /* check to turn on a context */
365 if (!_rule.context) {
366 if (!found_left && is_end) {
367 if (rule.border & RULE_ON_RIGHT_BORDER) {
368 _rule.border = 0;
369 _rule.context = 0;
370 contextchanged = 1;
371 _rule.keyword = 0;
373 } else if (rule.border & RULE_ON_LEFT_BORDER) {
374 r = edit->rules[_rule._context];
375 _rule.border = 0;
376 if (r->between_delimiters) {
377 long e;
378 _rule.context = _rule._context;
379 contextchanged = 1;
380 _rule.keyword = 0;
381 if (r->first_right == c && (e = compare_word_to_right (edit, i, r->right, r->whole_word_chars_left, r->whole_word_chars_right, r->line_start_right)) >= end) {
382 _rule.end = e;
383 found_right = 1;
384 _rule.border = RULE_ON_RIGHT_BORDER;
385 _rule.context = 0;
391 if (!found_right) {
392 int count;
393 struct context_rule **rules = edit->rules;
395 for (count = 1; rules[count]; count++) {
396 r = rules[count];
397 if (r->first_left == c) {
398 long e;
400 e = compare_word_to_right (edit, i, r->left, r->whole_word_chars_left, r->whole_word_chars_right, r->line_start_left);
401 if (e >= end && (!_rule.keyword || keyword_foundright)) {
402 _rule.end = e;
403 found_right = 1;
404 _rule.border = RULE_ON_LEFT_BORDER;
405 _rule._context = count;
406 if (!r->between_delimiters)
407 if (!_rule.keyword) {
408 _rule.context = count;
409 contextchanged = 1;
411 break;
418 /* check again to turn on a keyword if the context switched */
419 if (contextchanged && !_rule.keyword) {
420 const char *p;
422 p = (r = edit->rules[_rule.context])->keyword_first_chars;
423 while (*(p = xx_strchr ((unsigned char *) p + 1, c))) {
424 struct key_word *k;
425 int count;
426 long e;
428 count = p - r->keyword_first_chars;
429 k = r->keyword[count];
430 e = compare_word_to_right (edit, i, k->keyword, k->whole_word_chars_left, k->whole_word_chars_right, k->line_start);
431 if (e > 0) {
432 _rule.end = e;
433 _rule.keyword = count;
434 break;
438 return _rule;
441 static struct syntax_rule edit_get_rule (WEdit * edit, long byte_index)
443 long i;
445 if (byte_index > edit->last_get_rule) {
446 for (i = edit->last_get_rule + 1; i <= byte_index; i++) {
447 edit->rule = apply_rules_going_right (edit, i, edit->rule);
448 if (i > (edit->syntax_marker ? edit->syntax_marker->offset + SYNTAX_MARKER_DENSITY : SYNTAX_MARKER_DENSITY)) {
449 struct _syntax_marker *s;
451 s = edit->syntax_marker;
452 edit->syntax_marker = g_malloc0 (sizeof (struct _syntax_marker));
453 edit->syntax_marker->next = s;
454 edit->syntax_marker->offset = i;
455 edit->syntax_marker->rule = edit->rule;
458 } else if (byte_index < edit->last_get_rule) {
459 struct _syntax_marker *s;
461 for (;;) {
462 if (!edit->syntax_marker) {
463 memset (&edit->rule, 0, sizeof (edit->rule));
464 for (i = -1; i <= byte_index; i++)
465 edit->rule = apply_rules_going_right (edit, i, edit->rule);
466 break;
468 if (byte_index >= edit->syntax_marker->offset) {
469 edit->rule = edit->syntax_marker->rule;
470 for (i = edit->syntax_marker->offset + 1; i <= byte_index; i++)
471 edit->rule = apply_rules_going_right (edit, i, edit->rule);
472 break;
474 s = edit->syntax_marker->next;
475 syntax_g_free (edit->syntax_marker);
476 edit->syntax_marker = s;
479 edit->last_get_rule = byte_index;
480 return edit->rule;
483 static void translate_rule_to_color (WEdit * edit, struct syntax_rule rule, int *color)
485 struct key_word *k;
487 k = edit->rules[rule.context]->keyword[rule.keyword];
488 *color = k->color;
491 void edit_get_syntax_color (WEdit * edit, long byte_index, int *color)
493 if (edit->rules && byte_index < edit->last_byte &&
494 option_syntax_highlighting && use_colors) {
495 translate_rule_to_color (edit, edit_get_rule (edit, byte_index), color);
496 } else {
497 *color = use_colors ? EDITOR_NORMAL_COLOR_INDEX : 0;
503 Returns 0 on error/eof or a count of the number of bytes read
504 including the newline. Result must be free'd.
505 In case of an error, *line will not be modified.
507 static int read_one_line (char **line, FILE * f)
509 GString *p = g_string_new ("");
510 int c, r = 0;
512 for (;;) {
513 c = fgetc (f);
514 if (c == EOF) {
515 if (ferror (f)) {
516 if (errno == EINTR)
517 continue;
518 r = 0;
520 break;
522 r++;
523 /* handle all of \r\n, \r, \n correctly. */
524 if (c == '\r') {
525 if ( (c = fgetc (f)) == '\n')
526 r++;
527 else
528 ungetc (c, f);
529 break;
531 if (c == '\n')
532 break;
534 g_string_append_c (p, c);
536 if (r != 0) {
537 *line = p->str;
538 g_string_free (p, FALSE);
539 } else {
540 g_string_free (p, TRUE);
542 return r;
545 static char *convert (char *s)
547 char *r, *p;
549 p = r = s;
550 while (*s) {
551 switch (*s) {
552 case '\\':
553 s++;
554 switch (*s) {
555 case ' ':
556 *p = ' ';
557 s--;
558 break;
559 case 'n':
560 *p = '\n';
561 break;
562 case 'r':
563 *p = '\r';
564 break;
565 case 't':
566 *p = '\t';
567 break;
568 case 's':
569 *p = ' ';
570 break;
571 case '*':
572 *p = '*';
573 break;
574 case '\\':
575 *p = '\\';
576 break;
577 case '[':
578 case ']':
579 *p = SYNTAX_TOKEN_BRACKET;
580 break;
581 case '{':
582 case '}':
583 *p = SYNTAX_TOKEN_BRACE;
584 break;
585 case 0:
586 *p = *s;
587 return r;
588 default:
589 *p = *s;
590 break;
592 break;
593 case '*':
594 *p = SYNTAX_TOKEN_STAR;
595 break;
596 case '+':
597 *p = SYNTAX_TOKEN_PLUS;
598 break;
599 default:
600 *p = *s;
601 break;
603 s++;
604 p++;
606 *p = '\0';
607 return r;
610 #define whiteness(x) ((x) == '\t' || (x) == '\n' || (x) == ' ')
612 static int get_args (char *l, char **args, int args_size)
614 int argc = 0;
616 while (argc < args_size) {
617 char *p = l;
618 while (*p && whiteness (*p))
619 p++;
620 if (!*p)
621 break;
622 for (l = p + 1; *l && !whiteness (*l); l++);
623 if (*l)
624 *l++ = '\0';
625 args[argc++] = convert (p);
627 args[argc] = (char *) NULL;
628 return argc;
631 #define free_args(x)
632 #define break_a {result=line;break;}
633 #define check_a {if(!*a){result=line;break;}}
634 #define check_not_a {if(*a){result=line;break;}}
636 static int
637 this_try_alloc_color_pair (const char *fg, const char *bg)
639 char f[80], b[80], *p;
641 if (bg)
642 if (!*bg)
643 bg = 0;
644 if (fg)
645 if (!*fg)
646 fg = 0;
647 if (fg) {
648 g_strlcpy (f, fg, sizeof (f));
649 p = strchr (f, '/');
650 if (p)
651 *p = '\0';
652 fg = f;
654 if (bg) {
655 g_strlcpy (b, bg, sizeof (b));
656 p = strchr (b, '/');
657 if (p)
658 *p = '\0';
659 bg = b;
661 return try_alloc_color_pair (fg, bg);
664 static char *error_file_name = 0;
666 static FILE *open_include_file (const char *filename)
668 FILE *f;
670 syntax_g_free (error_file_name);
671 error_file_name = g_strdup (filename);
672 if (*filename == PATH_SEP)
673 return fopen (filename, "r");
675 g_free (error_file_name);
676 error_file_name = g_strconcat (home_dir, PATH_SEP_STR EDIT_DIR PATH_SEP_STR,
677 filename, (char *) NULL);
678 f = fopen (error_file_name, "r");
679 if (f)
680 return f;
682 g_free (error_file_name);
683 error_file_name = g_strconcat (mc_home, PATH_SEP_STR "syntax" PATH_SEP_STR,
684 filename, (char *) NULL);
685 return fopen (error_file_name, "r");
688 /* returns line number on error */
689 static int
690 edit_read_syntax_rules (WEdit *edit, FILE *f, char **args, int args_size)
692 FILE *g = 0;
693 char *fg, *bg;
694 char last_fg[32] = "", last_bg[32] = "";
695 char whole_right[512];
696 char whole_left[512];
697 char *l = 0;
698 int save_line = 0, line = 0;
699 struct context_rule **r, *c = 0;
700 int num_words = -1, num_contexts = -1;
701 int result = 0;
702 int argc;
703 int i, j;
704 int alloc_contexts = MAX_CONTEXTS,
705 alloc_words_per_context = MAX_WORDS_PER_CONTEXT,
706 max_alloc_words_per_context = MAX_WORDS_PER_CONTEXT;
708 args[0] = 0;
710 strcpy (whole_left, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_01234567890");
711 strcpy (whole_right, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_01234567890");
713 r = edit->rules = g_malloc0 (alloc_contexts * sizeof (struct context_rule *));
715 if (!edit->defines)
716 edit->defines = g_tree_new ((GCompareFunc) strcmp);
718 for (;;) {
719 char **a;
721 line++;
722 l = 0;
723 if (!read_one_line (&l, f)) {
724 if (g) {
725 fclose (f);
726 f = g;
727 g = 0;
728 line = save_line + 1;
729 syntax_g_free (error_file_name);
730 syntax_g_free (l);
731 if (!read_one_line (&l, f))
732 break;
733 } else {
734 break;
737 argc = get_args (l, args, args_size);
738 a = args + 1;
739 if (!args[0]) {
740 /* do nothing */
741 } else if (!strcmp (args[0], "include")) {
742 if (g || argc != 2) {
743 result = line;
744 break;
746 g = f;
747 f = open_include_file (args[1]);
748 if (!f) {
749 syntax_g_free (error_file_name);
750 result = line;
751 break;
753 save_line = line;
754 line = 0;
755 } else if (!strcmp (args[0], "wholechars")) {
756 check_a;
757 if (!strcmp (*a, "left")) {
758 a++;
759 g_strlcpy (whole_left, *a, sizeof (whole_left));
760 } else if (!strcmp (*a, "right")) {
761 a++;
762 g_strlcpy (whole_right, *a, sizeof (whole_right));
763 } else {
764 g_strlcpy (whole_left, *a, sizeof (whole_left));
765 g_strlcpy (whole_right, *a, sizeof (whole_right));
767 a++;
768 check_not_a;
769 } else if (!strcmp (args[0], "context")) {
770 check_a;
771 if (num_contexts == -1) {
772 if (strcmp (*a, "default")) { /* first context is the default */
773 break_a;
775 a++;
776 c = r[0] = g_malloc0 (sizeof (struct context_rule));
777 c->left = g_strdup (" ");
778 c->right = g_strdup (" ");
779 num_contexts = 0;
780 } else {
781 /* Terminate previous context. */
782 r[num_contexts - 1]->keyword[num_words] = NULL;
783 c = r[num_contexts] = g_malloc0 (sizeof (struct context_rule));
784 if (!strcmp (*a, "exclusive")) {
785 a++;
786 c->between_delimiters = 1;
788 check_a;
789 if (!strcmp (*a, "whole")) {
790 a++;
791 c->whole_word_chars_left = g_strdup (whole_left);
792 c->whole_word_chars_right = g_strdup (whole_right);
793 } else if (!strcmp (*a, "wholeleft")) {
794 a++;
795 c->whole_word_chars_left = g_strdup (whole_left);
796 } else if (!strcmp (*a, "wholeright")) {
797 a++;
798 c->whole_word_chars_right = g_strdup (whole_right);
800 check_a;
801 if (!strcmp (*a, "linestart")) {
802 a++;
803 c->line_start_left = 1;
805 check_a;
806 c->left = g_strdup (*a++);
807 check_a;
808 if (!strcmp (*a, "linestart")) {
809 a++;
810 c->line_start_right = 1;
812 check_a;
813 c->right = g_strdup (*a++);
814 c->first_left = *c->left;
815 c->first_right = *c->right;
817 c->keyword = g_malloc (alloc_words_per_context * sizeof (struct key_word *));
818 num_words = 1;
819 c->keyword[0] = g_malloc0 (sizeof (struct key_word));
820 subst_defines (edit->defines, a, &args[1024]);
821 fg = *a;
822 if (*a)
823 a++;
824 bg = *a;
825 if (*a)
826 a++;
827 g_strlcpy (last_fg, fg ? fg : "", sizeof (last_fg));
828 g_strlcpy (last_bg, bg ? bg : "", sizeof (last_bg));
829 c->keyword[0]->color = this_try_alloc_color_pair (fg, bg);
830 c->keyword[0]->keyword = g_strdup (" ");
831 check_not_a;
833 alloc_words_per_context = MAX_WORDS_PER_CONTEXT;
834 if (++num_contexts >= alloc_contexts) {
835 struct context_rule **tmp;
837 alloc_contexts += 128;
838 tmp = g_realloc (r, alloc_contexts * sizeof (struct context_rule *));
839 r = tmp;
841 } else if (!strcmp (args[0], "spellcheck")) {
842 if (!c) {
843 result = line;
844 break;
846 c->spelling = 1;
847 } else if (!strcmp (args[0], "keyword")) {
848 struct key_word *k;
850 if (num_words == -1)
851 break_a;
852 check_a;
853 k = r[num_contexts - 1]->keyword[num_words] = g_malloc0 (sizeof (struct key_word));
854 if (!strcmp (*a, "whole")) {
855 a++;
856 k->whole_word_chars_left = g_strdup (whole_left);
857 k->whole_word_chars_right = g_strdup (whole_right);
858 } else if (!strcmp (*a, "wholeleft")) {
859 a++;
860 k->whole_word_chars_left = g_strdup (whole_left);
861 } else if (!strcmp (*a, "wholeright")) {
862 a++;
863 k->whole_word_chars_right = g_strdup (whole_right);
865 check_a;
866 if (!strcmp (*a, "linestart")) {
867 a++;
868 k->line_start = 1;
870 check_a;
871 if (!strcmp (*a, "whole")) {
872 break_a;
874 k->keyword = g_strdup (*a++);
875 k->first = *k->keyword;
876 subst_defines (edit->defines, a, &args[1024]);
877 fg = *a;
878 if (*a)
879 a++;
880 bg = *a;
881 if (*a)
882 a++;
883 if (!fg)
884 fg = last_fg;
885 if (!bg)
886 bg = last_bg;
887 k->color = this_try_alloc_color_pair (fg, bg);
888 check_not_a;
890 if (++num_words >= alloc_words_per_context) {
891 struct key_word **tmp;
893 alloc_words_per_context += 1024;
895 if (alloc_words_per_context > max_alloc_words_per_context)
896 max_alloc_words_per_context = alloc_words_per_context;
898 tmp = g_realloc (c->keyword, alloc_words_per_context * sizeof (struct key_word *));
899 c->keyword = tmp;
901 } else if (*(args[0]) == '#') {
902 /* do nothing for comment */
903 } else if (!strcmp (args[0], "file")) {
904 break;
905 } else if (!strcmp (args[0], "define")) {
906 char *key = *a++;
907 char **argv;
909 if (argc < 3)
910 break_a;
911 if ((argv = g_tree_lookup (edit->defines, key))) {
912 mc_defines_destroy (NULL, argv, NULL);
913 } else {
914 key = g_strdup (key);
916 argv = g_new (char *, argc - 1);
917 g_tree_insert (edit->defines, key, argv);
918 while (*a) {
919 *argv++ = g_strdup (*a++);
921 *argv = NULL;
922 } else { /* anything else is an error */
923 break_a;
925 free_args (args);
926 syntax_g_free (l);
928 free_args (args);
929 syntax_g_free (l);
931 /* Terminate context array. */
932 if (num_contexts > 0) {
933 r[num_contexts - 1]->keyword[num_words] = NULL;
934 r[num_contexts] = NULL;
937 if (!edit->rules[0])
938 syntax_g_free (edit->rules);
940 if (result)
941 return result;
943 if (num_contexts == -1) {
944 return line;
948 char *first_chars, *p;
950 first_chars = g_malloc (max_alloc_words_per_context + 2);
952 for (i = 0; edit->rules[i]; i++) {
953 c = edit->rules[i];
954 p = first_chars;
955 *p++ = (char) 1;
956 for (j = 1; c->keyword[j]; j++)
957 *p++ = c->keyword[j]->first;
958 *p = '\0';
959 c->keyword_first_chars = g_strdup (first_chars);
962 g_free (first_chars);
965 return result;
968 void edit_free_syntax_rules (WEdit * edit)
970 int i, j;
972 if (!edit)
973 return;
974 if (edit->defines)
975 destroy_defines (&edit->defines);
976 if (!edit->rules)
977 return;
979 edit_get_rule (edit, -1);
980 syntax_g_free (edit->syntax_type);
981 edit->syntax_type = 0;
983 for (i = 0; edit->rules[i]; i++) {
984 if (edit->rules[i]->keyword) {
985 for (j = 0; edit->rules[i]->keyword[j]; j++) {
986 syntax_g_free (edit->rules[i]->keyword[j]->keyword);
987 syntax_g_free (edit->rules[i]->keyword[j]->whole_word_chars_left);
988 syntax_g_free (edit->rules[i]->keyword[j]->whole_word_chars_right);
989 syntax_g_free (edit->rules[i]->keyword[j]);
992 syntax_g_free (edit->rules[i]->left);
993 syntax_g_free (edit->rules[i]->right);
994 syntax_g_free (edit->rules[i]->whole_word_chars_left);
995 syntax_g_free (edit->rules[i]->whole_word_chars_right);
996 syntax_g_free (edit->rules[i]->keyword);
997 syntax_g_free (edit->rules[i]->keyword_first_chars);
998 syntax_g_free (edit->rules[i]);
1001 while (edit->syntax_marker) {
1002 struct _syntax_marker *s = edit->syntax_marker->next;
1003 syntax_g_free (edit->syntax_marker);
1004 edit->syntax_marker = s;
1007 syntax_g_free (edit->rules);
1010 /* returns -1 on file error, line number on error in file syntax */
1011 static int
1012 edit_read_syntax_file (WEdit * edit, char ***pnames, const char *syntax_file,
1013 const char *editor_file, const char *first_line,
1014 const char *type)
1016 #define NENTRIES 30
1017 FILE *f, *g = NULL;
1018 regex_t r;
1019 regmatch_t pmatch[1];
1020 char *args[1024], *l = 0;
1021 int line = 0;
1022 int result = 0;
1023 int count = 0;
1024 char *lib_file;
1025 int found = 0;
1026 char **tmpnames = NULL;
1028 f = fopen (syntax_file, "r");
1029 if (!f){
1030 lib_file = mhl_str_dir_plus_file (mc_home, "syntax" PATH_SEP_STR "Syntax");
1031 f = fopen (lib_file, "r");
1032 g_free (lib_file);
1033 if (!f)
1034 return -1;
1037 args[0] = 0;
1038 for (;;) {
1039 line++;
1040 syntax_g_free (l);
1041 if (!read_one_line (&l, f))
1042 break;
1043 (void)get_args (l, args, 1023); /* Final NULL */
1044 if (!args[0])
1045 continue;
1047 /* Looking for `include ...` lines before first `file ...` ones */
1048 if (!found && !strcmp (args[0], "include")) {
1049 if (g)
1050 continue;
1051 if (!args[1] || !(g = open_include_file (args[1]))) {
1052 result = line;
1053 break;
1055 goto found_type;
1058 /* looking for `file ...' lines only */
1059 if (strcmp (args[0], "file")) {
1060 continue;
1062 found = 1;
1064 /* must have two args or report error */
1065 if (!args[1] || !args[2]) {
1066 result = line;
1067 break;
1069 if (pnames && *pnames) {
1071 /* 1: just collecting a list of names of rule sets */
1072 /* Reallocate the list if required */
1073 if (count % NENTRIES == 0) {
1074 if ((tmpnames = (char**) g_realloc (*pnames, (count + NENTRIES
1075 + 1) * sizeof (char*))) != NULL)
1076 *pnames = tmpnames;
1077 else
1078 abort ();
1080 (*pnames)[count++] = g_strdup (args[2]);
1081 (*pnames)[count] = NULL;
1082 } else if (type) {
1084 /* 2: rule set was explicitly specified by the caller */
1085 if (!strcmp (type, args[2]))
1086 goto found_type;
1087 } else if (editor_file && edit) {
1089 /* 3: auto-detect rule set from regular expressions */
1090 int q;
1091 if (regcomp (&r, args[1], REG_EXTENDED)) {
1092 result = line;
1093 break;
1096 /* does filename match arg 1 ? */
1097 q = !regexec (&r, editor_file, 1, pmatch, 0);
1098 regfree (&r);
1099 if (!q && args[3]) {
1100 if (regcomp (&r, args[3], REG_EXTENDED)) {
1101 result = line;
1102 break;
1105 /* does first line match arg 3 ? */
1106 q = !regexec (&r, first_line, 1, pmatch, 0);
1107 regfree (&r);
1109 if (q) {
1110 int line_error;
1111 char *syntax_type;
1112 found_type:
1113 syntax_type = args[2];
1114 line_error = edit_read_syntax_rules (edit, g ? g : f, args, 1023);
1115 if (line_error) {
1116 if (!error_file_name) /* an included file */
1117 result = line + line_error;
1118 else
1119 result = line_error;
1120 } else {
1121 syntax_g_free (edit->syntax_type);
1122 edit->syntax_type = g_strdup (syntax_type);
1124 /* if there are no rules then turn off syntax highlighting for speed */
1125 if (!g && !edit->rules[1])
1126 if (!edit->rules[0]->keyword[1] && !edit->rules[0]->spelling) {
1127 edit_free_syntax_rules (edit);
1128 break;
1131 if (g) {
1132 fclose (g);
1133 g = NULL;
1134 } else {
1135 break;
1140 syntax_g_free (l);
1141 fclose (f);
1142 return result;
1145 static char *get_first_editor_line (WEdit * edit)
1147 int i;
1148 static char s[256];
1150 s[0] = '\0';
1151 if (!edit)
1152 return s;
1153 for (i = 0; i < 255; i++) {
1154 s[i] = edit_get_byte (edit, i);
1155 if (s[i] == '\n') {
1156 s[i] = '\0';
1157 break;
1160 s[255] = '\0';
1161 return s;
1165 * Load rules into edit struct. Either edit or *pnames must be NULL. If
1166 * edit is NULL, a list of types will be stored into names. If type is
1167 * NULL, then the type will be selected according to the filename.
1169 void
1170 edit_load_syntax (WEdit *edit, char ***pnames, const char *type)
1172 int r;
1173 char *f = NULL;
1175 if (option_auto_syntax)
1176 type = NULL;
1178 edit_free_syntax_rules (edit);
1180 if (!use_colors)
1181 return;
1183 if (!option_syntax_highlighting && (!pnames || !*pnames))
1184 return;
1186 if (edit) {
1187 if (!edit->filename)
1188 return;
1189 if (!*edit->filename && !type)
1190 return;
1192 f = mhl_str_dir_plus_file (home_dir, SYNTAX_FILE);
1193 r = edit_read_syntax_file (edit, pnames, f, edit ? edit->filename : 0,
1194 get_first_editor_line (edit), type);
1195 if (r == -1) {
1196 edit_free_syntax_rules (edit);
1197 message (D_ERROR, _(" Load syntax file "),
1198 _(" Cannot open file %s \n %s "), f,
1199 unix_error_string (errno));
1200 } else if (r) {
1201 edit_free_syntax_rules (edit);
1202 message (D_ERROR, _(" Load syntax file "),
1203 _(" Error in file %s on line %d "),
1204 error_file_name ? error_file_name : f, r);
1205 syntax_g_free (error_file_name);
1206 } else {
1207 /* succeeded */
1209 g_free (f);