Reverted some more GString removals. (See f235b1976ee6dd7aa2be7e75c870784c424e3de3)
[midnight-commander.git] / edit / syntax.c
blob479f3c5cc86c49200626c36d7c2ec6609a8abc8f
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/memory.h>
36 #include <mhl/string.h>
38 #include "../src/global.h"
40 #include "edit.h"
41 #include "edit-widget.h"
42 #include "../src/color.h" /* use_colors */
43 #include "../src/main.h" /* mc_home */
44 #include "../src/wtools.h" /* message() */
46 /* bytes */
47 #define SYNTAX_MARKER_DENSITY 512
50 Mispelled words are flushed from the syntax highlighting rules
51 when they have been around longer than
52 TRANSIENT_WORD_TIME_OUT seconds. At a cursor rate of 30
53 chars per second and say 3 chars + a space per word, we can
54 accumulate 450 words absolute max with a value of 60. This is
55 below this limit of 1024 words in a context.
57 #define TRANSIENT_WORD_TIME_OUT 60
59 #define UNKNOWN_FORMAT "unknown"
61 #define MAX_WORDS_PER_CONTEXT 1024
62 #define MAX_CONTEXTS 128
64 #define RULE_ON_LEFT_BORDER 1
65 #define RULE_ON_RIGHT_BORDER 2
67 #define SYNTAX_TOKEN_STAR '\001'
68 #define SYNTAX_TOKEN_PLUS '\002'
69 #define SYNTAX_TOKEN_BRACKET '\003'
70 #define SYNTAX_TOKEN_BRACE '\004'
72 struct key_word {
73 char *keyword;
74 unsigned char first;
75 char *whole_word_chars_left;
76 char *whole_word_chars_right;
77 int line_start;
78 int color;
81 struct context_rule {
82 char *left;
83 unsigned char first_left;
84 char *right;
85 unsigned char first_right;
86 char line_start_left;
87 char line_start_right;
88 int between_delimiters;
89 char *whole_word_chars_left;
90 char *whole_word_chars_right;
91 char *keyword_first_chars;
92 int spelling;
93 /* first word is word[1] */
94 struct key_word **keyword;
97 struct _syntax_marker {
98 long offset;
99 struct syntax_rule rule;
100 struct _syntax_marker *next;
103 int option_syntax_highlighting = 1;
104 int option_auto_syntax = 1;
105 char *option_syntax_type = NULL;
107 static gint
108 mc_defines_destroy (gpointer key, gpointer value, gpointer data)
110 char **values = value;
112 (void) data;
114 mhl_mem_free (key);
115 while (*values)
116 mhl_mem_free (*values++);
117 mhl_mem_free (value);
119 return FALSE;
122 /* Completely destroys the defines tree */
123 static inline void
124 destroy_defines (GTree **defines)
126 g_tree_traverse (*defines, mc_defines_destroy, G_POST_ORDER, NULL);
127 g_tree_destroy (*defines);
128 *defines = 0;
131 static void
132 subst_defines (GTree *defines, char **argv, char **argv_end)
134 char **t, **p;
135 int argc;
137 while (*argv && argv < argv_end) {
138 if ((t = g_tree_lookup (defines, *argv))) {
139 int count = 0;
141 /* Count argv array members */
142 argc = 0;
143 for (p = &argv[1]; *p; p++)
144 argc++;
146 /* Count members of definition array */
147 for (p = t; *p; p++)
148 count++;
149 p = &argv[count + argc];
151 /* Buffer overflow or infinitive loop in define */
152 if (p >= argv_end)
153 break;
155 /* Move rest of argv after definition members */
156 while (argc >= 0)
157 *p-- = argv[argc-- + 1];
159 /* Copy definition members to argv */
160 for (p = argv; *t; *p++ = *t++);
162 argv++;
166 static long
167 compare_word_to_right (WEdit *edit, long i, const char *text,
168 const char *whole_left, const char *whole_right,
169 int line_start)
171 const unsigned char *p, *q;
172 int c, d, j;
174 if (!*text)
175 return -1;
176 c = edit_get_byte (edit, i - 1);
177 if (line_start)
178 if (c != '\n')
179 return -1;
180 if (whole_left)
181 if (strchr (whole_left, c))
182 return -1;
184 for (p = (unsigned char *) text, q = p + strlen ((char *) p); p < q; p++, i++) {
185 switch (*p) {
186 case SYNTAX_TOKEN_STAR:
187 if (++p > q)
188 return -1;
189 for (;;) {
190 c = edit_get_byte (edit, i);
191 if (!*p)
192 if (whole_right)
193 if (!strchr (whole_right, c))
194 break;
195 if (c == *p)
196 break;
197 if (c == '\n')
198 return -1;
199 i++;
201 break;
202 case SYNTAX_TOKEN_PLUS:
203 if (++p > q)
204 return -1;
205 j = 0;
206 for (;;) {
207 c = edit_get_byte (edit, i);
208 if (c == *p) {
209 j = i;
210 if (*p == *text && !p[1]) /* handle eg '+' and @+@ keywords properly */
211 break;
213 if (j && strchr ((char *) p + 1, c)) /* c exists further down, so it will get matched later */
214 break;
215 if (c == '\n' || c == '\t' || c == ' ') {
216 if (!*p) {
217 i--;
218 break;
220 if (!j)
221 return -1;
222 i = j;
223 break;
225 if (whole_right)
226 if (!strchr (whole_right, c)) {
227 if (!*p) {
228 i--;
229 break;
231 if (!j)
232 return -1;
233 i = j;
234 break;
236 i++;
238 break;
239 case SYNTAX_TOKEN_BRACKET:
240 if (++p > q)
241 return -1;
242 c = -1;
243 for (;; i++) {
244 d = c;
245 c = edit_get_byte (edit, i);
246 for (j = 0; p[j] != SYNTAX_TOKEN_BRACKET && p[j]; j++)
247 if (c == p[j])
248 goto found_char2;
249 break;
250 found_char2:
251 ; /* dummy command */
253 i--;
254 while (*p != SYNTAX_TOKEN_BRACKET && p <= q)
255 p++;
256 if (p > q)
257 return -1;
258 if (p[1] == d)
259 i--;
260 break;
261 case SYNTAX_TOKEN_BRACE:
262 if (++p > q)
263 return -1;
264 c = edit_get_byte (edit, i);
265 for (; *p != SYNTAX_TOKEN_BRACE && *p; p++)
266 if (c == *p)
267 goto found_char3;
268 return -1;
269 found_char3:
270 while (*p != SYNTAX_TOKEN_BRACE && p < q)
271 p++;
272 break;
273 default:
274 if (*p != edit_get_byte (edit, i))
275 return -1;
278 if (whole_right)
279 if (strchr (whole_right, edit_get_byte (edit, i)))
280 return -1;
281 return i;
284 static inline const char *xx_strchr (const unsigned char *s, int c)
286 while (*s >= '\005' && *s != (unsigned char) c) {
287 s++;
289 return (const char *) s;
292 static inline struct syntax_rule apply_rules_going_right (WEdit * edit, long i, struct syntax_rule rule)
294 struct context_rule *r;
295 int contextchanged = 0, c;
296 int found_right = 0, found_left = 0, keyword_foundleft = 0, keyword_foundright = 0;
297 int is_end;
298 long end = 0;
299 struct syntax_rule _rule = rule;
301 if (!(c = edit_get_byte (edit, i)))
302 return rule;
303 is_end = (rule.end == (unsigned char) i);
305 /* check to turn off a keyword */
306 if (_rule.keyword) {
307 if (edit_get_byte (edit, i - 1) == '\n')
308 _rule.keyword = 0;
309 if (is_end) {
310 _rule.keyword = 0;
311 keyword_foundleft = 1;
315 /* check to turn off a context */
316 if (_rule.context && !_rule.keyword) {
317 long e;
318 r = edit->rules[_rule.context];
319 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) {
320 _rule.end = e;
321 found_right = 1;
322 _rule.border = RULE_ON_RIGHT_BORDER;
323 if (r->between_delimiters)
324 _rule.context = 0;
325 } else if (is_end && rule.border & RULE_ON_RIGHT_BORDER) {
327 /* always turn off a context at 4 */
328 found_left = 1;
329 _rule.border = 0;
330 if (!keyword_foundleft)
331 _rule.context = 0;
332 } else if (is_end && rule.border & RULE_ON_LEFT_BORDER) {
334 /* never turn off a context at 2 */
335 found_left = 1;
336 _rule.border = 0;
340 /* check to turn on a keyword */
341 if (!_rule.keyword) {
342 const char *p;
344 p = (r = edit->rules[_rule.context])->keyword_first_chars;
345 if (p)
346 while (*(p = xx_strchr ((unsigned char *) p + 1, c))) {
347 struct key_word *k;
348 int count;
349 long e;
351 count = p - r->keyword_first_chars;
352 k = r->keyword[count];
353 e = compare_word_to_right (edit, i, k->keyword, k->whole_word_chars_left, k->whole_word_chars_right, k->line_start);
354 if (e > 0) {
355 end = e;
356 _rule.end = e;
357 _rule.keyword = count;
358 keyword_foundright = 1;
359 break;
363 /* check to turn on a context */
364 if (!_rule.context) {
365 if (!found_left && is_end) {
366 if (rule.border & RULE_ON_RIGHT_BORDER) {
367 _rule.border = 0;
368 _rule.context = 0;
369 contextchanged = 1;
370 _rule.keyword = 0;
372 } else if (rule.border & RULE_ON_LEFT_BORDER) {
373 r = edit->rules[_rule._context];
374 _rule.border = 0;
375 if (r->between_delimiters) {
376 long e;
377 _rule.context = _rule._context;
378 contextchanged = 1;
379 _rule.keyword = 0;
380 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) {
381 _rule.end = e;
382 found_right = 1;
383 _rule.border = RULE_ON_RIGHT_BORDER;
384 _rule.context = 0;
390 if (!found_right) {
391 int count;
392 struct context_rule **rules = edit->rules;
394 for (count = 1; rules[count]; count++) {
395 r = rules[count];
396 if (r->first_left == c) {
397 long e;
399 e = compare_word_to_right (edit, i, r->left, r->whole_word_chars_left, r->whole_word_chars_right, r->line_start_left);
400 if (e >= end && (!_rule.keyword || keyword_foundright)) {
401 _rule.end = e;
402 found_right = 1;
403 _rule.border = RULE_ON_LEFT_BORDER;
404 _rule._context = count;
405 if (!r->between_delimiters)
406 if (!_rule.keyword) {
407 _rule.context = count;
408 contextchanged = 1;
410 break;
417 /* check again to turn on a keyword if the context switched */
418 if (contextchanged && !_rule.keyword) {
419 const char *p;
421 p = (r = edit->rules[_rule.context])->keyword_first_chars;
422 while (*(p = xx_strchr ((unsigned char *) p + 1, c))) {
423 struct key_word *k;
424 int count;
425 long e;
427 count = p - r->keyword_first_chars;
428 k = r->keyword[count];
429 e = compare_word_to_right (edit, i, k->keyword, k->whole_word_chars_left, k->whole_word_chars_right, k->line_start);
430 if (e > 0) {
431 _rule.end = e;
432 _rule.keyword = count;
433 break;
437 return _rule;
440 static struct syntax_rule edit_get_rule (WEdit * edit, long byte_index)
442 long i;
444 if (byte_index > edit->last_get_rule) {
445 for (i = edit->last_get_rule + 1; i <= byte_index; i++) {
446 edit->rule = apply_rules_going_right (edit, i, edit->rule);
447 if (i > (edit->syntax_marker ? edit->syntax_marker->offset + SYNTAX_MARKER_DENSITY : SYNTAX_MARKER_DENSITY)) {
448 struct _syntax_marker *s;
450 s = edit->syntax_marker;
451 edit->syntax_marker = g_malloc0 (sizeof (struct _syntax_marker));
452 edit->syntax_marker->next = s;
453 edit->syntax_marker->offset = i;
454 edit->syntax_marker->rule = edit->rule;
457 } else if (byte_index < edit->last_get_rule) {
458 struct _syntax_marker *s;
460 for (;;) {
461 if (!edit->syntax_marker) {
462 memset (&edit->rule, 0, sizeof (edit->rule));
463 for (i = -1; i <= byte_index; i++)
464 edit->rule = apply_rules_going_right (edit, i, edit->rule);
465 break;
467 if (byte_index >= edit->syntax_marker->offset) {
468 edit->rule = edit->syntax_marker->rule;
469 for (i = edit->syntax_marker->offset + 1; i <= byte_index; i++)
470 edit->rule = apply_rules_going_right (edit, i, edit->rule);
471 break;
473 s = edit->syntax_marker->next;
474 MHL_PTR_FREE (edit->syntax_marker);
475 edit->syntax_marker = s;
478 edit->last_get_rule = byte_index;
479 return edit->rule;
482 static void translate_rule_to_color (WEdit * edit, struct syntax_rule rule, int *color)
484 struct key_word *k;
486 k = edit->rules[rule.context]->keyword[rule.keyword];
487 *color = k->color;
490 void edit_get_syntax_color (WEdit * edit, long byte_index, int *color)
492 if (edit->rules && byte_index < edit->last_byte &&
493 option_syntax_highlighting && use_colors) {
494 translate_rule_to_color (edit, edit_get_rule (edit, byte_index), color);
495 } else {
496 *color = use_colors ? EDITOR_NORMAL_COLOR_INDEX : 0;
502 Returns 0 on error/eof or a count of the number of bytes read
503 including the newline. Result must be free'd.
504 In case of an error, *line will not be modified.
506 static int read_one_line (char **line, FILE * f)
508 GString *p = g_string_new ("");
509 int c, r = 0;
511 for (;;) {
512 c = fgetc (f);
513 if (c == EOF) {
514 if (ferror (f)) {
515 if (errno == EINTR)
516 continue;
517 r = 0;
519 break;
521 r++;
522 /* handle all of \r\n, \r, \n correctly. */
523 if (c == '\r') {
524 if ( (c = fgetc (f)) == '\n')
525 r++;
526 else
527 ungetc (c, f);
528 break;
530 if (c == '\n')
531 break;
533 g_string_append_c (p, c);
535 if (r != 0) {
536 *line = p->str;
537 g_string_free (p, FALSE);
538 } else {
539 g_string_free (p, TRUE);
541 return r;
544 static char *convert (char *s)
546 char *r, *p;
548 p = r = s;
549 while (*s) {
550 switch (*s) {
551 case '\\':
552 s++;
553 switch (*s) {
554 case ' ':
555 *p = ' ';
556 s--;
557 break;
558 case 'n':
559 *p = '\n';
560 break;
561 case 'r':
562 *p = '\r';
563 break;
564 case 't':
565 *p = '\t';
566 break;
567 case 's':
568 *p = ' ';
569 break;
570 case '*':
571 *p = '*';
572 break;
573 case '\\':
574 *p = '\\';
575 break;
576 case '[':
577 case ']':
578 *p = SYNTAX_TOKEN_BRACKET;
579 break;
580 case '{':
581 case '}':
582 *p = SYNTAX_TOKEN_BRACE;
583 break;
584 case 0:
585 *p = *s;
586 return r;
587 default:
588 *p = *s;
589 break;
591 break;
592 case '*':
593 *p = SYNTAX_TOKEN_STAR;
594 break;
595 case '+':
596 *p = SYNTAX_TOKEN_PLUS;
597 break;
598 default:
599 *p = *s;
600 break;
602 s++;
603 p++;
605 *p = '\0';
606 return r;
609 #define whiteness(x) ((x) == '\t' || (x) == '\n' || (x) == ' ')
611 static int get_args (char *l, char **args, int args_size)
613 int argc = 0;
615 while (argc < args_size) {
616 char *p = l;
617 while (*p && whiteness (*p))
618 p++;
619 if (!*p)
620 break;
621 for (l = p + 1; *l && !whiteness (*l); l++);
622 if (*l)
623 *l++ = '\0';
624 args[argc++] = convert (p);
626 args[argc] = (char *) NULL;
627 return argc;
630 #define free_args(x)
631 #define break_a {result=line;break;}
632 #define check_a {if(!*a){result=line;break;}}
633 #define check_not_a {if(*a){result=line;break;}}
635 static int
636 this_try_alloc_color_pair (const char *fg, const char *bg)
638 char f[80], b[80], *p;
640 if (bg)
641 if (!*bg)
642 bg = 0;
643 if (fg)
644 if (!*fg)
645 fg = 0;
646 if (fg) {
647 g_strlcpy (f, fg, sizeof (f));
648 p = strchr (f, '/');
649 if (p)
650 *p = '\0';
651 fg = f;
653 if (bg) {
654 g_strlcpy (b, bg, sizeof (b));
655 p = strchr (b, '/');
656 if (p)
657 *p = '\0';
658 bg = b;
660 return try_alloc_color_pair (fg, bg);
663 static char *error_file_name = 0;
665 static FILE *open_include_file (const char *filename)
667 FILE *f;
669 MHL_PTR_FREE (error_file_name);
670 error_file_name = mhl_str_dup (filename);
671 if (*filename == PATH_SEP)
672 return fopen (filename, "r");
674 mhl_mem_free (error_file_name);
675 error_file_name = g_strconcat (home_dir, PATH_SEP_STR EDIT_DIR PATH_SEP_STR,
676 filename, (char *) NULL);
677 f = fopen (error_file_name, "r");
678 if (f)
679 return f;
681 mhl_mem_free (error_file_name);
682 error_file_name = g_strconcat (mc_home, PATH_SEP_STR "syntax" PATH_SEP_STR,
683 filename, (char *) NULL);
684 return fopen (error_file_name, "r");
687 /* returns line number on error */
688 static int
689 edit_read_syntax_rules (WEdit *edit, FILE *f, char **args, int args_size)
691 FILE *g = 0;
692 char *fg, *bg;
693 char last_fg[32] = "", last_bg[32] = "";
694 char whole_right[512];
695 char whole_left[512];
696 char *l = 0;
697 int save_line = 0, line = 0;
698 struct context_rule **r, *c = 0;
699 int num_words = -1, num_contexts = -1;
700 int result = 0;
701 int argc;
702 int i, j;
703 int alloc_contexts = MAX_CONTEXTS,
704 alloc_words_per_context = MAX_WORDS_PER_CONTEXT,
705 max_alloc_words_per_context = MAX_WORDS_PER_CONTEXT;
707 args[0] = 0;
709 strcpy (whole_left, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_01234567890");
710 strcpy (whole_right, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_01234567890");
712 r = edit->rules = g_malloc0 (alloc_contexts * sizeof (struct context_rule *));
714 if (!edit->defines)
715 edit->defines = g_tree_new ((GCompareFunc) strcmp);
717 for (;;) {
718 char **a;
720 line++;
721 l = 0;
722 if (!read_one_line (&l, f)) {
723 if (g) {
724 fclose (f);
725 f = g;
726 g = 0;
727 line = save_line + 1;
728 MHL_PTR_FREE (error_file_name);
729 MHL_PTR_FREE (l);
730 if (!read_one_line (&l, f))
731 break;
732 } else {
733 break;
736 argc = get_args (l, args, args_size);
737 a = args + 1;
738 if (!args[0]) {
739 /* do nothing */
740 } else if (!strcmp (args[0], "include")) {
741 if (g || argc != 2) {
742 result = line;
743 break;
745 g = f;
746 f = open_include_file (args[1]);
747 if (!f) {
748 MHL_PTR_FREE (error_file_name);
749 result = line;
750 break;
752 save_line = line;
753 line = 0;
754 } else if (!strcmp (args[0], "wholechars")) {
755 check_a;
756 if (!strcmp (*a, "left")) {
757 a++;
758 g_strlcpy (whole_left, *a, sizeof (whole_left));
759 } else if (!strcmp (*a, "right")) {
760 a++;
761 g_strlcpy (whole_right, *a, sizeof (whole_right));
762 } else {
763 g_strlcpy (whole_left, *a, sizeof (whole_left));
764 g_strlcpy (whole_right, *a, sizeof (whole_right));
766 a++;
767 check_not_a;
768 } else if (!strcmp (args[0], "context")) {
769 check_a;
770 if (num_contexts == -1) {
771 if (strcmp (*a, "default")) { /* first context is the default */
772 break_a;
774 a++;
775 c = r[0] = g_malloc0 (sizeof (struct context_rule));
776 c->left = mhl_str_dup (" ");
777 c->right = mhl_str_dup (" ");
778 num_contexts = 0;
779 } else {
780 /* Terminate previous context. */
781 r[num_contexts - 1]->keyword[num_words] = NULL;
782 c = r[num_contexts] = g_malloc0 (sizeof (struct context_rule));
783 if (!strcmp (*a, "exclusive")) {
784 a++;
785 c->between_delimiters = 1;
787 check_a;
788 if (!strcmp (*a, "whole")) {
789 a++;
790 c->whole_word_chars_left = mhl_str_dup (whole_left);
791 c->whole_word_chars_right = mhl_str_dup (whole_right);
792 } else if (!strcmp (*a, "wholeleft")) {
793 a++;
794 c->whole_word_chars_left = mhl_str_dup (whole_left);
795 } else if (!strcmp (*a, "wholeright")) {
796 a++;
797 c->whole_word_chars_right = mhl_str_dup (whole_right);
799 check_a;
800 if (!strcmp (*a, "linestart")) {
801 a++;
802 c->line_start_left = 1;
804 check_a;
805 c->left = mhl_str_dup (*a++);
806 check_a;
807 if (!strcmp (*a, "linestart")) {
808 a++;
809 c->line_start_right = 1;
811 check_a;
812 c->right = mhl_str_dup (*a++);
813 c->first_left = *c->left;
814 c->first_right = *c->right;
816 c->keyword = g_malloc (alloc_words_per_context * sizeof (struct key_word *));
817 num_words = 1;
818 c->keyword[0] = g_malloc0 (sizeof (struct key_word));
819 subst_defines (edit->defines, a, &args[1024]);
820 fg = *a;
821 if (*a)
822 a++;
823 bg = *a;
824 if (*a)
825 a++;
826 g_strlcpy (last_fg, fg ? fg : "", sizeof (last_fg));
827 g_strlcpy (last_bg, bg ? bg : "", sizeof (last_bg));
828 c->keyword[0]->color = this_try_alloc_color_pair (fg, bg);
829 c->keyword[0]->keyword = mhl_str_dup (" ");
830 check_not_a;
832 alloc_words_per_context = MAX_WORDS_PER_CONTEXT;
833 if (++num_contexts >= alloc_contexts) {
834 struct context_rule **tmp;
836 alloc_contexts += 128;
837 tmp = g_realloc (r, alloc_contexts * sizeof (struct context_rule *));
838 r = tmp;
840 } else if (!strcmp (args[0], "spellcheck")) {
841 if (!c) {
842 result = line;
843 break;
845 c->spelling = 1;
846 } else if (!strcmp (args[0], "keyword")) {
847 struct key_word *k;
849 if (num_words == -1)
850 break_a;
851 check_a;
852 k = r[num_contexts - 1]->keyword[num_words] = g_malloc0 (sizeof (struct key_word));
853 if (!strcmp (*a, "whole")) {
854 a++;
855 k->whole_word_chars_left = mhl_str_dup (whole_left);
856 k->whole_word_chars_right = mhl_str_dup (whole_right);
857 } else if (!strcmp (*a, "wholeleft")) {
858 a++;
859 k->whole_word_chars_left = mhl_str_dup (whole_left);
860 } else if (!strcmp (*a, "wholeright")) {
861 a++;
862 k->whole_word_chars_right = mhl_str_dup (whole_right);
864 check_a;
865 if (!strcmp (*a, "linestart")) {
866 a++;
867 k->line_start = 1;
869 check_a;
870 if (!strcmp (*a, "whole")) {
871 break_a;
873 k->keyword = mhl_str_dup (*a++);
874 k->first = *k->keyword;
875 subst_defines (edit->defines, a, &args[1024]);
876 fg = *a;
877 if (*a)
878 a++;
879 bg = *a;
880 if (*a)
881 a++;
882 if (!fg)
883 fg = last_fg;
884 if (!bg)
885 bg = last_bg;
886 k->color = this_try_alloc_color_pair (fg, bg);
887 check_not_a;
889 if (++num_words >= alloc_words_per_context) {
890 struct key_word **tmp;
892 alloc_words_per_context += 1024;
894 if (alloc_words_per_context > max_alloc_words_per_context)
895 max_alloc_words_per_context = alloc_words_per_context;
897 tmp = g_realloc (c->keyword, alloc_words_per_context * sizeof (struct key_word *));
898 c->keyword = tmp;
900 } else if (*(args[0]) == '#') {
901 /* do nothing for comment */
902 } else if (!strcmp (args[0], "file")) {
903 break;
904 } else if (!strcmp (args[0], "define")) {
905 char *key = *a++;
906 char **argv;
908 if (argc < 3)
909 break_a;
910 if ((argv = g_tree_lookup (edit->defines, key))) {
911 mc_defines_destroy (NULL, argv, NULL);
912 } else {
913 key = mhl_str_dup (key);
915 argv = g_new (char *, argc - 1);
916 g_tree_insert (edit->defines, key, argv);
917 while (*a) {
918 *argv++ = mhl_str_dup (*a++);
920 *argv = NULL;
921 } else { /* anything else is an error */
922 break_a;
924 free_args (args);
925 MHL_PTR_FREE (l);
927 free_args (args);
928 MHL_PTR_FREE (l);
930 /* Terminate context array. */
931 if (num_contexts > 0) {
932 r[num_contexts - 1]->keyword[num_words] = NULL;
933 r[num_contexts] = NULL;
936 if (!edit->rules[0])
937 MHL_PTR_FREE (edit->rules);
939 if (result)
940 return result;
942 if (num_contexts == -1) {
943 return line;
947 char *first_chars, *p;
949 first_chars = g_malloc (max_alloc_words_per_context + 2);
951 for (i = 0; edit->rules[i]; i++) {
952 c = edit->rules[i];
953 p = first_chars;
954 *p++ = (char) 1;
955 for (j = 1; c->keyword[j]; j++)
956 *p++ = c->keyword[j]->first;
957 *p = '\0';
958 c->keyword_first_chars = mhl_str_dup (first_chars);
961 mhl_mem_free (first_chars);
964 return result;
967 void edit_free_syntax_rules (WEdit * edit)
969 int i, j;
971 if (!edit)
972 return;
973 if (edit->defines)
974 destroy_defines (&edit->defines);
975 if (!edit->rules)
976 return;
978 edit_get_rule (edit, -1);
979 MHL_PTR_FREE (edit->syntax_type);
980 edit->syntax_type = 0;
982 for (i = 0; edit->rules[i]; i++) {
983 if (edit->rules[i]->keyword) {
984 for (j = 0; edit->rules[i]->keyword[j]; j++) {
985 MHL_PTR_FREE (edit->rules[i]->keyword[j]->keyword);
986 MHL_PTR_FREE (edit->rules[i]->keyword[j]->whole_word_chars_left);
987 MHL_PTR_FREE (edit->rules[i]->keyword[j]->whole_word_chars_right);
988 MHL_PTR_FREE (edit->rules[i]->keyword[j]);
991 MHL_PTR_FREE (edit->rules[i]->left);
992 MHL_PTR_FREE (edit->rules[i]->right);
993 MHL_PTR_FREE (edit->rules[i]->whole_word_chars_left);
994 MHL_PTR_FREE (edit->rules[i]->whole_word_chars_right);
995 MHL_PTR_FREE (edit->rules[i]->keyword);
996 MHL_PTR_FREE (edit->rules[i]->keyword_first_chars);
997 MHL_PTR_FREE (edit->rules[i]);
1000 while (edit->syntax_marker) {
1001 struct _syntax_marker *s = edit->syntax_marker->next;
1002 MHL_PTR_FREE (edit->syntax_marker);
1003 edit->syntax_marker = s;
1006 MHL_PTR_FREE (edit->rules);
1009 /* returns -1 on file error, line number on error in file syntax */
1010 static int
1011 edit_read_syntax_file (WEdit * edit, char ***pnames, const char *syntax_file,
1012 const char *editor_file, const char *first_line,
1013 const char *type)
1015 #define NENTRIES 30
1016 FILE *f, *g = NULL;
1017 regex_t r;
1018 regmatch_t pmatch[1];
1019 char *args[1024], *l = 0;
1020 int line = 0;
1021 int result = 0;
1022 int count = 0;
1023 char *lib_file;
1024 int found = 0;
1025 char **tmpnames = NULL;
1027 f = fopen (syntax_file, "r");
1028 if (!f){
1029 lib_file = mhl_str_dir_plus_file (mc_home, "syntax" PATH_SEP_STR "Syntax");
1030 f = fopen (lib_file, "r");
1031 mhl_mem_free (lib_file);
1032 if (!f)
1033 return -1;
1036 args[0] = 0;
1037 for (;;) {
1038 line++;
1039 MHL_PTR_FREE (l);
1040 if (!read_one_line (&l, f))
1041 break;
1042 (void)get_args (l, args, 1023); /* Final NULL */
1043 if (!args[0])
1044 continue;
1046 /* Looking for `include ...` lines before first `file ...` ones */
1047 if (!found && !strcmp (args[0], "include")) {
1048 if (g)
1049 continue;
1050 if (!args[1] || !(g = open_include_file (args[1]))) {
1051 result = line;
1052 break;
1054 goto found_type;
1057 /* looking for `file ...' lines only */
1058 if (strcmp (args[0], "file")) {
1059 continue;
1061 found = 1;
1063 /* must have two args or report error */
1064 if (!args[1] || !args[2]) {
1065 result = line;
1066 break;
1068 if (pnames && *pnames) {
1070 /* 1: just collecting a list of names of rule sets */
1071 /* Reallocate the list if required */
1072 if (count % NENTRIES == 0) {
1073 if ((tmpnames = (char**) g_realloc (*pnames, (count + NENTRIES
1074 + 1) * sizeof (char*))) != NULL)
1075 *pnames = tmpnames;
1076 else
1077 abort ();
1079 (*pnames)[count++] = mhl_str_dup (args[2]);
1080 (*pnames)[count] = NULL;
1081 } else if (type) {
1083 /* 2: rule set was explicitly specified by the caller */
1084 if (!strcmp (type, args[2]))
1085 goto found_type;
1086 } else if (editor_file && edit) {
1088 /* 3: auto-detect rule set from regular expressions */
1089 int q;
1090 if (regcomp (&r, args[1], REG_EXTENDED)) {
1091 result = line;
1092 break;
1095 /* does filename match arg 1 ? */
1096 q = !regexec (&r, editor_file, 1, pmatch, 0);
1097 regfree (&r);
1098 if (!q && args[3]) {
1099 if (regcomp (&r, args[3], REG_EXTENDED)) {
1100 result = line;
1101 break;
1104 /* does first line match arg 3 ? */
1105 q = !regexec (&r, first_line, 1, pmatch, 0);
1106 regfree (&r);
1108 if (q) {
1109 int line_error;
1110 char *syntax_type;
1111 found_type:
1112 syntax_type = args[2];
1113 line_error = edit_read_syntax_rules (edit, g ? g : f, args, 1023);
1114 if (line_error) {
1115 if (!error_file_name) /* an included file */
1116 result = line + line_error;
1117 else
1118 result = line_error;
1119 } else {
1120 MHL_PTR_FREE (edit->syntax_type);
1121 edit->syntax_type = mhl_str_dup (syntax_type);
1122 /* if there are no rules then turn off syntax highlighting for speed */
1123 if (!g && !edit->rules[1])
1124 if (!edit->rules[0]->keyword[1] && !edit->rules[0]->spelling) {
1125 edit_free_syntax_rules (edit);
1126 break;
1129 if (g) {
1130 fclose (g);
1131 g = NULL;
1132 } else {
1133 break;
1138 MHL_PTR_FREE (l);
1139 fclose (f);
1140 return result;
1143 static char *get_first_editor_line (WEdit * edit)
1145 int i;
1146 static char s[256];
1148 s[0] = '\0';
1149 if (!edit)
1150 return s;
1151 for (i = 0; i < 255; i++) {
1152 s[i] = edit_get_byte (edit, i);
1153 if (s[i] == '\n') {
1154 s[i] = '\0';
1155 break;
1158 s[255] = '\0';
1159 return s;
1163 * Load rules into edit struct. Either edit or *pnames must be NULL. If
1164 * edit is NULL, a list of types will be stored into names. If type is
1165 * NULL, then the type will be selected according to the filename.
1167 void
1168 edit_load_syntax (WEdit *edit, char ***pnames, const char *type)
1170 int r;
1171 char *f = NULL;
1173 if (option_auto_syntax)
1174 type = NULL;
1176 edit_free_syntax_rules (edit);
1178 if (!use_colors)
1179 return;
1181 if (!option_syntax_highlighting && (!pnames || !*pnames))
1182 return;
1184 if (edit) {
1185 if (!edit->filename)
1186 return;
1187 if (!*edit->filename && !type)
1188 return;
1190 f = mhl_str_dir_plus_file (home_dir, SYNTAX_FILE);
1191 r = edit_read_syntax_file (edit, pnames, f, edit ? edit->filename : 0,
1192 get_first_editor_line (edit), type);
1193 if (r == -1) {
1194 edit_free_syntax_rules (edit);
1195 message (D_ERROR, _(" Load syntax file "),
1196 _(" Cannot open file %s \n %s "), f,
1197 unix_error_string (errno));
1198 } else if (r) {
1199 edit_free_syntax_rules (edit);
1200 message (D_ERROR, _(" Load syntax file "),
1201 _(" Error in file %s on line %d "),
1202 error_file_name ? error_file_name : f, r);
1203 MHL_PTR_FREE (error_file_name);
1204 } else {
1205 /* succeeded */
1207 mhl_mem_free (f);