Removed unused defines.
[midnight-commander.git] / src / editor / syntax.c
blob50ff25ad9a7bcc60e38508e7bd1c0086cb7d37db
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 /** \file
25 * \brief Source: editor syntax highlighting
26 * \author Paul Sheer
27 * \date 1996, 1997
29 * Mispelled words are flushed from the syntax highlighting rules
30 * when they have been around longer than
31 * TRANSIENT_WORD_TIME_OUT seconds. At a cursor rate of 30
32 * chars per second and say 3 chars + a space per word, we can
33 * accumulate 450 words absolute max with a value of 60. This is
34 * below this limit of 1024 words in a context.
37 #include <config.h>
39 #include <stdio.h>
40 #include <stdarg.h>
41 #include <sys/types.h>
42 #include <unistd.h>
43 #include <string.h>
44 #include <ctype.h>
45 #include <errno.h>
46 #include <sys/stat.h>
47 #include <stdlib.h>
49 #include "lib/global.h"
50 #include "lib/search.h" /* search engine */
51 #include "lib/skin.h"
52 #include "lib/strutil.h" /* utf string functions */
54 #include "src/main.h" /* mc_home */
55 #include "src/wtools.h" /* message() */
57 #include "edit-impl.h"
58 #include "edit-widget.h"
60 /* bytes */
61 #define SYNTAX_MARKER_DENSITY 512
63 #define TRANSIENT_WORD_TIME_OUT 60
65 #define UNKNOWN_FORMAT "unknown"
67 #define MAX_WORDS_PER_CONTEXT 1024
68 #define MAX_CONTEXTS 128
70 #define RULE_ON_LEFT_BORDER 1
71 #define RULE_ON_RIGHT_BORDER 2
73 #define SYNTAX_TOKEN_STAR '\001'
74 #define SYNTAX_TOKEN_PLUS '\002'
75 #define SYNTAX_TOKEN_BRACKET '\003'
76 #define SYNTAX_TOKEN_BRACE '\004'
78 struct key_word {
79 char *keyword;
80 unsigned char first;
81 char *whole_word_chars_left;
82 char *whole_word_chars_right;
83 int line_start;
84 int color;
87 struct context_rule {
88 char *left;
89 unsigned char first_left;
90 char *right;
91 unsigned char first_right;
92 char line_start_left;
93 char line_start_right;
94 int between_delimiters;
95 char *whole_word_chars_left;
96 char *whole_word_chars_right;
97 char *keyword_first_chars;
98 int spelling;
99 /* first word is word[1] */
100 struct key_word **keyword;
103 struct _syntax_marker {
104 long offset;
105 struct syntax_rule rule;
106 struct _syntax_marker *next;
109 int option_syntax_highlighting = 1;
110 int option_auto_syntax = 1;
111 char *option_syntax_type = NULL;
113 static gint
114 mc_defines_destroy (gpointer key, gpointer value, gpointer data)
116 char **values = value;
118 (void) data;
120 g_free (key);
121 while (*values)
122 g_free (*values++);
123 g_free (value);
125 return FALSE;
128 /* Completely destroys the defines tree */
129 static void
130 destroy_defines (GTree **defines)
132 g_tree_traverse (*defines, mc_defines_destroy, G_POST_ORDER, NULL);
133 g_tree_destroy (*defines);
134 *defines = NULL;
137 static void
138 subst_defines (GTree *defines, char **argv, char **argv_end)
140 char **t, **p;
141 int argc;
143 while (*argv && argv < argv_end) {
144 if ((t = g_tree_lookup (defines, *argv))) {
145 int count = 0;
147 /* Count argv array members */
148 argc = 0;
149 for (p = &argv[1]; *p; p++)
150 argc++;
152 /* Count members of definition array */
153 for (p = t; *p; p++)
154 count++;
155 p = &argv[count + argc];
157 /* Buffer overflow or infinitive loop in define */
158 if (p >= argv_end)
159 break;
161 /* Move rest of argv after definition members */
162 while (argc >= 0)
163 *p-- = argv[argc-- + 1];
165 /* Copy definition members to argv */
166 for (p = argv; *t; *p++ = *t++);
168 argv++;
172 static long
173 compare_word_to_right (WEdit *edit, long i, const char *text,
174 const char *whole_left, const char *whole_right,
175 int line_start)
177 const unsigned char *p, *q;
178 int c, d, j;
180 if (!*text)
181 return -1;
182 c = edit_get_byte (edit, i - 1);
183 if (line_start)
184 if (c != '\n')
185 return -1;
186 if (whole_left)
187 if (strchr (whole_left, c))
188 return -1;
190 for (p = (unsigned char *) text, q = p + str_term_width1 ((char *) p); p < q; p++, i++) {
191 switch (*p) {
192 case SYNTAX_TOKEN_STAR:
193 if (++p > q)
194 return -1;
195 for (;;) {
196 c = edit_get_byte (edit, i);
197 if (!*p)
198 if (whole_right)
199 if (!strchr (whole_right, c))
200 break;
201 if (c == *p)
202 break;
203 if (c == '\n')
204 return -1;
205 i++;
207 break;
208 case SYNTAX_TOKEN_PLUS:
209 if (++p > q)
210 return -1;
211 j = 0;
212 for (;;) {
213 c = edit_get_byte (edit, i);
214 if (c == *p) {
215 j = i;
216 if (*p == *text && !p[1]) /* handle eg '+' and @+@ keywords properly */
217 break;
219 if (j && strchr ((char *) p + 1, c)) /* c exists further down, so it will get matched later */
220 break;
221 if (c == '\n' || c == '\t' || c == ' ') {
222 if (!*p) {
223 i--;
224 break;
226 if (!j)
227 return -1;
228 i = j;
229 break;
231 if (whole_right)
232 if (!strchr (whole_right, c)) {
233 if (!*p) {
234 i--;
235 break;
237 if (!j)
238 return -1;
239 i = j;
240 break;
242 i++;
244 break;
245 case SYNTAX_TOKEN_BRACKET:
246 if (++p > q)
247 return -1;
248 c = -1;
249 for (;; i++) {
250 d = c;
251 c = edit_get_byte (edit, i);
252 for (j = 0; p[j] != SYNTAX_TOKEN_BRACKET && p[j]; j++)
253 if (c == p[j])
254 goto found_char2;
255 break;
256 found_char2:
257 ; /* dummy command */
259 i--;
260 while (*p != SYNTAX_TOKEN_BRACKET && p <= q)
261 p++;
262 if (p > q)
263 return -1;
264 if (p[1] == d)
265 i--;
266 break;
267 case SYNTAX_TOKEN_BRACE:
268 if (++p > q)
269 return -1;
270 c = edit_get_byte (edit, i);
271 for (; *p != SYNTAX_TOKEN_BRACE && *p; p++)
272 if (c == *p)
273 goto found_char3;
274 return -1;
275 found_char3:
276 while (*p != SYNTAX_TOKEN_BRACE && p < q)
277 p++;
278 break;
279 default:
280 if (*p != edit_get_byte (edit, i))
281 return -1;
284 if (whole_right)
285 if (strchr (whole_right, edit_get_byte (edit, i)))
286 return -1;
287 return i;
290 static const char *xx_strchr (const unsigned char *s, int c)
292 while (*s >= '\005' && *s != (unsigned char) c) {
293 s++;
295 return (const char *) s;
298 static struct syntax_rule apply_rules_going_right (WEdit * edit, long i, struct syntax_rule rule)
300 struct context_rule *r;
301 int contextchanged = 0, c;
302 int found_right = 0, found_left = 0, keyword_foundleft = 0, keyword_foundright = 0;
303 int is_end;
304 long end = 0;
305 struct syntax_rule _rule = rule;
307 if (!(c = edit_get_byte (edit, i)))
308 return rule;
309 is_end = (rule.end == (unsigned char) i);
311 /* check to turn off a keyword */
312 if (_rule.keyword) {
313 if (edit_get_byte (edit, i - 1) == '\n')
314 _rule.keyword = 0;
315 if (is_end) {
316 _rule.keyword = 0;
317 keyword_foundleft = 1;
321 /* check to turn off a context */
322 if (_rule.context && !_rule.keyword) {
323 long e;
324 r = edit->rules[_rule.context];
325 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) {
326 _rule.end = e;
327 found_right = 1;
328 _rule.border = RULE_ON_RIGHT_BORDER;
329 if (r->between_delimiters)
330 _rule.context = 0;
331 } else if (is_end && rule.border & RULE_ON_RIGHT_BORDER) {
333 /* always turn off a context at 4 */
334 found_left = 1;
335 _rule.border = 0;
336 if (!keyword_foundleft)
337 _rule.context = 0;
338 } else if (is_end && rule.border & RULE_ON_LEFT_BORDER) {
340 /* never turn off a context at 2 */
341 found_left = 1;
342 _rule.border = 0;
346 /* check to turn on a keyword */
347 if (!_rule.keyword) {
348 const char *p;
350 p = (r = edit->rules[_rule.context])->keyword_first_chars;
351 if (p)
352 while (*(p = xx_strchr ((unsigned char *) p + 1, c))) {
353 struct key_word *k;
354 int count;
355 long e;
357 count = p - r->keyword_first_chars;
358 k = r->keyword[count];
359 e = compare_word_to_right (edit, i, k->keyword, k->whole_word_chars_left, k->whole_word_chars_right, k->line_start);
360 if (e > 0) {
361 end = e;
362 _rule.end = e;
363 _rule.keyword = count;
364 keyword_foundright = 1;
365 break;
369 /* check to turn on a context */
370 if (!_rule.context) {
371 if (!found_left && is_end) {
372 if (rule.border & RULE_ON_RIGHT_BORDER) {
373 _rule.border = 0;
374 _rule.context = 0;
375 contextchanged = 1;
376 _rule.keyword = 0;
378 } else if (rule.border & RULE_ON_LEFT_BORDER) {
379 r = edit->rules[_rule._context];
380 _rule.border = 0;
381 if (r->between_delimiters) {
382 long e;
383 _rule.context = _rule._context;
384 contextchanged = 1;
385 _rule.keyword = 0;
386 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) {
387 _rule.end = e;
388 found_right = 1;
389 _rule.border = RULE_ON_RIGHT_BORDER;
390 _rule.context = 0;
396 if (!found_right) {
397 int count;
398 struct context_rule **rules = edit->rules;
400 for (count = 1; rules[count]; count++) {
401 r = rules[count];
402 if (r->first_left == c) {
403 long e;
405 e = compare_word_to_right (edit, i, r->left, r->whole_word_chars_left, r->whole_word_chars_right, r->line_start_left);
406 if (e >= end && (!_rule.keyword || keyword_foundright)) {
407 _rule.end = e;
408 found_right = 1;
409 _rule.border = RULE_ON_LEFT_BORDER;
410 _rule._context = count;
411 if (!r->between_delimiters)
412 if (!_rule.keyword) {
413 _rule.context = count;
414 contextchanged = 1;
416 break;
423 /* check again to turn on a keyword if the context switched */
424 if (contextchanged && !_rule.keyword) {
425 const char *p;
427 p = (r = edit->rules[_rule.context])->keyword_first_chars;
428 while (*(p = xx_strchr ((unsigned char *) p + 1, c))) {
429 struct key_word *k;
430 int count;
431 long e;
433 count = p - r->keyword_first_chars;
434 k = r->keyword[count];
435 e = compare_word_to_right (edit, i, k->keyword, k->whole_word_chars_left, k->whole_word_chars_right, k->line_start);
436 if (e > 0) {
437 _rule.end = e;
438 _rule.keyword = count;
439 break;
443 return _rule;
446 static struct syntax_rule edit_get_rule (WEdit * edit, long byte_index)
448 long i;
450 if (byte_index > edit->last_get_rule) {
451 for (i = edit->last_get_rule + 1; i <= byte_index; i++) {
452 edit->rule = apply_rules_going_right (edit, i, edit->rule);
453 if (i > (edit->syntax_marker ? edit->syntax_marker->offset + SYNTAX_MARKER_DENSITY : SYNTAX_MARKER_DENSITY)) {
454 struct _syntax_marker *s;
456 s = edit->syntax_marker;
457 edit->syntax_marker = g_malloc (sizeof (struct _syntax_marker));
458 edit->syntax_marker->next = s;
459 edit->syntax_marker->offset = i;
460 edit->syntax_marker->rule = edit->rule;
463 } else if (byte_index < edit->last_get_rule) {
464 struct _syntax_marker *s;
466 for (;;) {
467 if (!edit->syntax_marker) {
468 memset (&edit->rule, 0, sizeof (edit->rule));
469 for (i = -1; i <= byte_index; i++)
470 edit->rule = apply_rules_going_right (edit, i, edit->rule);
471 break;
473 if (byte_index >= edit->syntax_marker->offset) {
474 edit->rule = edit->syntax_marker->rule;
475 for (i = edit->syntax_marker->offset + 1; i <= byte_index; i++)
476 edit->rule = apply_rules_going_right (edit, i, edit->rule);
477 break;
479 s = edit->syntax_marker->next;
480 g_free (edit->syntax_marker);
481 edit->syntax_marker = s;
484 edit->last_get_rule = byte_index;
485 return edit->rule;
488 static inline void
489 translate_rule_to_color (WEdit * edit, struct syntax_rule rule, int *color)
491 *color = edit->rules[rule.context]->keyword[rule.keyword]->color;
494 void
495 edit_get_syntax_color (WEdit * edit, long byte_index, int *color)
497 if (!tty_use_colors ())
498 *color = 0;
499 else if (edit->rules && byte_index < edit->last_byte &&
500 option_syntax_highlighting)
501 translate_rule_to_color (edit, edit_get_rule (edit, byte_index), color);
502 else
503 *color = EDITOR_NORMAL_COLOR;
508 Returns 0 on error/eof or a count of the number of bytes read
509 including the newline. Result must be free'd.
510 In case of an error, *line will not be modified.
512 static size_t
513 read_one_line (char **line, FILE *f)
515 GString *p;
516 size_t r = 0;
518 /* not reallocate string too often */
519 p = g_string_sized_new (64);
521 for (;;) {
522 int c;
524 c = fgetc (f);
525 if (c == EOF) {
526 if (ferror (f)) {
527 if (errno == EINTR)
528 continue;
529 r = 0;
531 break;
533 r++;
535 /* handle all of \r\n, \r, \n correctly. */
536 if (c == '\n')
537 break;
538 if (c == '\r') {
539 c = fgetc (f);
540 if (c == '\n')
541 r++;
542 else
543 ungetc (c, f);
544 break;
547 g_string_append_c (p, c);
549 if (r != 0)
550 *line = g_string_free (p, FALSE);
551 else
552 g_string_free (p, TRUE);
554 return r;
557 static char *convert (char *s)
559 char *r, *p;
561 p = r = s;
562 while (*s) {
563 switch (*s) {
564 case '\\':
565 s++;
566 switch (*s) {
567 case ' ':
568 *p = ' ';
569 s--;
570 break;
571 case 'n':
572 *p = '\n';
573 break;
574 case 'r':
575 *p = '\r';
576 break;
577 case 't':
578 *p = '\t';
579 break;
580 case 's':
581 *p = ' ';
582 break;
583 case '*':
584 *p = '*';
585 break;
586 case '\\':
587 *p = '\\';
588 break;
589 case '[':
590 case ']':
591 *p = SYNTAX_TOKEN_BRACKET;
592 break;
593 case '{':
594 case '}':
595 *p = SYNTAX_TOKEN_BRACE;
596 break;
597 case 0:
598 *p = *s;
599 return r;
600 default:
601 *p = *s;
602 break;
604 break;
605 case '*':
606 *p = SYNTAX_TOKEN_STAR;
607 break;
608 case '+':
609 *p = SYNTAX_TOKEN_PLUS;
610 break;
611 default:
612 *p = *s;
613 break;
615 s++;
616 p++;
618 *p = '\0';
619 return r;
622 #define whiteness(x) ((x) == '\t' || (x) == '\n' || (x) == ' ')
624 static int get_args (char *l, char **args, int args_size)
626 int argc = 0;
628 while (argc < args_size) {
629 char *p = l;
630 while (*p && whiteness (*p))
631 p++;
632 if (!*p)
633 break;
634 for (l = p + 1; *l && !whiteness (*l); l++);
635 if (*l)
636 *l++ = '\0';
637 args[argc++] = convert (p);
639 args[argc] = (char *) NULL;
640 return argc;
643 #define free_args(x)
644 #define break_a {result=line;break;}
645 #define check_a {if(!*a){result=line;break;}}
646 #define check_not_a {if(*a){result=line;break;}}
648 static int
649 this_try_alloc_color_pair (const char *fg, const char *bg)
651 char f[80], b[80], *p;
653 if (bg)
654 if (!*bg)
655 bg = 0;
656 if (fg)
657 if (!*fg)
658 fg = 0;
659 if (fg) {
660 g_strlcpy (f, fg, sizeof (f));
661 p = strchr (f, '/');
662 if (p)
663 *p = '\0';
664 fg = f;
666 if (bg) {
667 g_strlcpy (b, bg, sizeof (b));
668 p = strchr (b, '/');
669 if (p)
670 *p = '\0';
671 bg = b;
673 return tty_try_alloc_color_pair (fg, bg);
676 static char *error_file_name = 0;
678 static FILE *open_include_file (const char *filename)
680 FILE *f;
682 MC_PTR_FREE (error_file_name);
683 error_file_name = g_strdup (filename);
684 if (*filename == PATH_SEP)
685 return fopen (filename, "r");
687 g_free (error_file_name);
688 error_file_name = g_strconcat (home_dir, PATH_SEP_STR EDIT_DIR PATH_SEP_STR,
689 filename, (char *) NULL);
690 f = fopen (error_file_name, "r");
691 if (f)
692 return f;
694 g_free (error_file_name);
695 error_file_name = g_strconcat (mc_home, PATH_SEP_STR, "syntax", PATH_SEP_STR,
696 filename, (char *) NULL);
697 f = fopen (error_file_name, "r");
698 if (f)
699 return f;
701 g_free (error_file_name);
702 error_file_name = g_strconcat (mc_home_alt, PATH_SEP_STR "syntax" PATH_SEP_STR,
703 filename, (char *) NULL);
705 return fopen (error_file_name, "r");
708 /* returns line number on error */
709 static int
710 edit_read_syntax_rules (WEdit *edit, FILE *f, char **args, int args_size)
712 FILE *g = NULL;
713 char *fg, *bg;
714 char last_fg[32] = "", last_bg[32] = "";
715 char whole_right[512];
716 char whole_left[512];
717 char *l = 0;
718 int save_line = 0, line = 0;
719 struct context_rule **r, *c = NULL;
720 int num_words = -1, num_contexts = -1;
721 int result = 0;
722 int argc;
723 int i, j;
724 int alloc_contexts = MAX_CONTEXTS,
725 alloc_words_per_context = MAX_WORDS_PER_CONTEXT,
726 max_alloc_words_per_context = MAX_WORDS_PER_CONTEXT;
728 args[0] = 0;
730 strcpy (whole_left, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_01234567890");
731 strcpy (whole_right, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_01234567890");
733 r = edit->rules = g_malloc0 (alloc_contexts * sizeof (struct context_rule *));
735 if (!edit->defines)
736 edit->defines = g_tree_new ((GCompareFunc) strcmp);
738 for (;;) {
739 char **a;
741 line++;
742 l = 0;
743 if (read_one_line (&l, f) == 0) {
744 if (g) {
745 fclose (f);
746 f = g;
747 g = 0;
748 line = save_line + 1;
749 MC_PTR_FREE (error_file_name);
750 MC_PTR_FREE (l);
751 if (read_one_line (&l, f) == 0)
752 break;
753 } else {
754 break;
757 argc = get_args (l, args, args_size);
758 a = args + 1;
759 if (!args[0]) {
760 /* do nothing */
761 } else if (!strcmp (args[0], "include")) {
762 if (g || argc != 2) {
763 result = line;
764 break;
766 g = f;
767 f = open_include_file (args[1]);
768 if (!f) {
769 MC_PTR_FREE (error_file_name);
770 result = line;
771 break;
773 save_line = line;
774 line = 0;
775 } else if (!strcmp (args[0], "wholechars")) {
776 check_a;
777 if (!strcmp (*a, "left")) {
778 a++;
779 g_strlcpy (whole_left, *a, sizeof (whole_left));
780 } else if (!strcmp (*a, "right")) {
781 a++;
782 g_strlcpy (whole_right, *a, sizeof (whole_right));
783 } else {
784 g_strlcpy (whole_left, *a, sizeof (whole_left));
785 g_strlcpy (whole_right, *a, sizeof (whole_right));
787 a++;
788 check_not_a;
789 } else if (!strcmp (args[0], "context")) {
790 check_a;
791 if (num_contexts == -1) {
792 if (strcmp (*a, "default")) { /* first context is the default */
793 break_a;
795 a++;
796 c = r[0] = g_malloc0 (sizeof (struct context_rule));
797 c->left = g_strdup (" ");
798 c->right = g_strdup (" ");
799 num_contexts = 0;
800 } else {
801 /* Terminate previous context. */
802 r[num_contexts - 1]->keyword[num_words] = NULL;
803 c = r[num_contexts] = g_malloc0 (sizeof (struct context_rule));
804 if (!strcmp (*a, "exclusive")) {
805 a++;
806 c->between_delimiters = 1;
808 check_a;
809 if (!strcmp (*a, "whole")) {
810 a++;
811 c->whole_word_chars_left = g_strdup (whole_left);
812 c->whole_word_chars_right = g_strdup (whole_right);
813 } else if (!strcmp (*a, "wholeleft")) {
814 a++;
815 c->whole_word_chars_left = g_strdup (whole_left);
816 } else if (!strcmp (*a, "wholeright")) {
817 a++;
818 c->whole_word_chars_right = g_strdup (whole_right);
820 check_a;
821 if (!strcmp (*a, "linestart")) {
822 a++;
823 c->line_start_left = 1;
825 check_a;
826 c->left = g_strdup (*a++);
827 check_a;
828 if (!strcmp (*a, "linestart")) {
829 a++;
830 c->line_start_right = 1;
832 check_a;
833 c->right = g_strdup (*a++);
834 c->first_left = *c->left;
835 c->first_right = *c->right;
837 c->keyword = g_malloc (alloc_words_per_context * sizeof (struct key_word *));
838 num_words = 1;
839 c->keyword[0] = g_malloc0 (sizeof (struct key_word));
840 subst_defines (edit->defines, a, &args[1024]);
841 fg = *a;
842 if (*a)
843 a++;
844 bg = *a;
845 if (*a)
846 a++;
847 g_strlcpy (last_fg, fg ? fg : "", sizeof (last_fg));
848 g_strlcpy (last_bg, bg ? bg : "", sizeof (last_bg));
849 c->keyword[0]->color = this_try_alloc_color_pair (fg, bg);
850 c->keyword[0]->keyword = g_strdup (" ");
851 check_not_a;
853 alloc_words_per_context = MAX_WORDS_PER_CONTEXT;
854 if (++num_contexts >= alloc_contexts) {
855 struct context_rule **tmp;
857 alloc_contexts += 128;
858 tmp = g_realloc (r, alloc_contexts * sizeof (struct context_rule *));
859 r = tmp;
861 } else if (!strcmp (args[0], "spellcheck")) {
862 if (!c) {
863 result = line;
864 break;
866 c->spelling = 1;
867 } else if (!strcmp (args[0], "keyword")) {
868 struct key_word *k;
870 if (num_words == -1)
871 break_a;
872 check_a;
873 k = r[num_contexts - 1]->keyword[num_words] = g_malloc0 (sizeof (struct key_word));
874 if (!strcmp (*a, "whole")) {
875 a++;
876 k->whole_word_chars_left = g_strdup (whole_left);
877 k->whole_word_chars_right = g_strdup (whole_right);
878 } else if (!strcmp (*a, "wholeleft")) {
879 a++;
880 k->whole_word_chars_left = g_strdup (whole_left);
881 } else if (!strcmp (*a, "wholeright")) {
882 a++;
883 k->whole_word_chars_right = g_strdup (whole_right);
885 check_a;
886 if (!strcmp (*a, "linestart")) {
887 a++;
888 k->line_start = 1;
890 check_a;
891 if (!strcmp (*a, "whole")) {
892 break_a;
894 k->keyword = g_strdup (*a++);
895 k->first = *k->keyword;
896 subst_defines (edit->defines, a, &args[1024]);
897 fg = *a;
898 if (*a)
899 a++;
900 bg = *a;
901 if (*a)
902 a++;
903 if (!fg)
904 fg = last_fg;
905 if (!bg)
906 bg = last_bg;
907 k->color = this_try_alloc_color_pair (fg, bg);
908 check_not_a;
910 if (++num_words >= alloc_words_per_context) {
911 struct key_word **tmp;
913 alloc_words_per_context += 1024;
915 if (alloc_words_per_context > max_alloc_words_per_context)
916 max_alloc_words_per_context = alloc_words_per_context;
918 tmp = g_realloc (c->keyword, alloc_words_per_context * sizeof (struct key_word *));
919 c->keyword = tmp;
921 } else if (*(args[0]) == '#') {
922 /* do nothing for comment */
923 } else if (!strcmp (args[0], "file")) {
924 break;
925 } else if (!strcmp (args[0], "define")) {
926 char *key = *a++;
927 char **argv;
929 if (argc < 3)
930 break_a;
931 if ((argv = g_tree_lookup (edit->defines, key))) {
932 mc_defines_destroy (NULL, argv, NULL);
933 } else {
934 key = g_strdup (key);
936 argv = g_new (char *, argc - 1);
937 g_tree_insert (edit->defines, key, argv);
938 while (*a) {
939 *argv++ = g_strdup (*a++);
941 *argv = NULL;
942 } else { /* anything else is an error */
943 break_a;
945 free_args (args);
946 MC_PTR_FREE (l);
948 free_args (args);
949 MC_PTR_FREE (l);
951 /* Terminate context array. */
952 if (num_contexts > 0) {
953 r[num_contexts - 1]->keyword[num_words] = NULL;
954 r[num_contexts] = NULL;
957 if (!edit->rules[0])
958 MC_PTR_FREE (edit->rules);
960 if (result)
961 return result;
963 if (num_contexts == -1) {
964 return line;
968 char *first_chars, *p;
970 first_chars = g_malloc0 (max_alloc_words_per_context + 2);
972 for (i = 0; edit->rules[i]; i++) {
973 c = edit->rules[i];
974 p = first_chars;
975 *p++ = (char) 1;
976 for (j = 1; c->keyword[j]; j++)
977 *p++ = c->keyword[j]->first;
978 *p = '\0';
979 c->keyword_first_chars = g_strdup (first_chars);
982 g_free (first_chars);
985 return result;
988 void
989 edit_free_syntax_rules (WEdit * edit)
991 size_t i, j;
993 if (!edit)
994 return;
995 if (edit->defines)
996 destroy_defines (&edit->defines);
997 if (!edit->rules)
998 return;
1000 edit_get_rule (edit, -1);
1001 MC_PTR_FREE (edit->syntax_type);
1003 for (i = 0; edit->rules[i]; i++) {
1004 if (edit->rules[i]->keyword) {
1005 for (j = 0; edit->rules[i]->keyword[j]; j++) {
1006 MC_PTR_FREE (edit->rules[i]->keyword[j]->keyword);
1007 MC_PTR_FREE (edit->rules[i]->keyword[j]->whole_word_chars_left);
1008 MC_PTR_FREE (edit->rules[i]->keyword[j]->whole_word_chars_right);
1009 MC_PTR_FREE (edit->rules[i]->keyword[j]);
1012 MC_PTR_FREE (edit->rules[i]->left);
1013 MC_PTR_FREE (edit->rules[i]->right);
1014 MC_PTR_FREE (edit->rules[i]->whole_word_chars_left);
1015 MC_PTR_FREE (edit->rules[i]->whole_word_chars_right);
1016 MC_PTR_FREE (edit->rules[i]->keyword);
1017 MC_PTR_FREE (edit->rules[i]->keyword_first_chars);
1018 MC_PTR_FREE (edit->rules[i]);
1021 while (edit->syntax_marker) {
1022 struct _syntax_marker *s = edit->syntax_marker->next;
1023 g_free (edit->syntax_marker);
1024 edit->syntax_marker = s;
1027 MC_PTR_FREE (edit->rules);
1028 tty_color_free_all_tmp();
1031 /* returns -1 on file error, line number on error in file syntax */
1032 static int
1033 edit_read_syntax_file (WEdit * edit, char ***pnames, const char *syntax_file,
1034 const char *editor_file, const char *first_line,
1035 const char *type)
1037 #define NENTRIES 30
1038 FILE *f, *g = NULL;
1039 char *args[1024], *l = NULL;
1040 int line = 0;
1041 int result = 0;
1042 int count = 0;
1043 char *lib_file;
1044 int found = 0;
1045 char **tmpnames = NULL;
1047 f = fopen (syntax_file, "r");
1048 if (!f){
1049 lib_file = concat_dir_and_file (mc_home, "Syntax");
1050 f = fopen (lib_file, "r");
1051 g_free (lib_file);
1052 if (!f)
1053 return -1;
1056 args[0] = 0;
1057 for (;;) {
1058 line++;
1059 MC_PTR_FREE (l);
1060 if (read_one_line (&l, f) == 0)
1061 break;
1062 (void)get_args (l, args, 1023); /* Final NULL */
1063 if (!args[0])
1064 continue;
1066 /* Looking for `include ...` lines before first `file ...` ones */
1067 if (!found && !strcmp (args[0], "include")) {
1068 if (g)
1069 continue;
1070 if (!args[1] || !(g = open_include_file (args[1]))) {
1071 result = line;
1072 break;
1074 goto found_type;
1077 /* looking for `file ...' lines only */
1078 if (strcmp (args[0], "file")) {
1079 continue;
1081 found = 1;
1083 /* must have two args or report error */
1084 if (!args[1] || !args[2]) {
1085 result = line;
1086 break;
1088 if (pnames && *pnames) {
1090 /* 1: just collecting a list of names of rule sets */
1091 /* Reallocate the list if required */
1092 if (count % NENTRIES == 0) {
1093 tmpnames = (char**) g_try_realloc (*pnames, (count + NENTRIES + 1) * sizeof (char*));
1094 if (tmpnames == NULL)
1095 break;
1096 *pnames = tmpnames;
1098 (*pnames)[count++] = g_strdup (args[2]);
1099 (*pnames)[count] = NULL;
1100 } else if (type) {
1102 /* 2: rule set was explicitly specified by the caller */
1103 if (!strcmp (type, args[2]))
1104 goto found_type;
1105 } else if (editor_file && edit) {
1107 /* 3: auto-detect rule set from regular expressions */
1108 int q;
1109 q = mc_search(args[1], editor_file, MC_SEARCH_T_REGEX);
1110 /* does filename match arg 1 ? */
1111 if (!q && args[3]) {
1112 /* does first line match arg 3 ? */
1113 q = mc_search(args[3], first_line, MC_SEARCH_T_REGEX);
1115 if (q) {
1116 int line_error;
1117 char *syntax_type;
1118 found_type:
1119 syntax_type = args[2];
1120 line_error = edit_read_syntax_rules (edit, g ? g : f, args, 1023);
1121 if (line_error) {
1122 if (!error_file_name) /* an included file */
1123 result = line + line_error;
1124 else
1125 result = line_error;
1126 } else {
1127 MC_PTR_FREE (edit->syntax_type);
1128 edit->syntax_type = g_strdup (syntax_type);
1129 /* if there are no rules then turn off syntax highlighting for speed */
1130 if (!g && !edit->rules[1])
1131 if (!edit->rules[0]->keyword[1] && !edit->rules[0]->spelling) {
1132 edit_free_syntax_rules (edit);
1133 break;
1136 if (g) {
1137 fclose (g);
1138 g = NULL;
1139 } else {
1140 break;
1145 MC_PTR_FREE (l);
1146 fclose (f);
1147 return result;
1150 static char *get_first_editor_line (WEdit * edit)
1152 size_t i;
1153 static char s[256];
1155 s[0] = '\0';
1156 if (edit == NULL)
1157 return s;
1159 for (i = 0; i < sizeof (s) - 1; i++) {
1160 s[i] = edit_get_byte (edit, i);
1161 if (s[i] == '\n') {
1162 s[i] = '\0';
1163 break;
1166 s[sizeof(s) - 1] = '\0';
1167 return s;
1171 * Load rules into edit struct. Either edit or *pnames must be NULL. If
1172 * edit is NULL, a list of types will be stored into names. If type is
1173 * NULL, then the type will be selected according to the filename.
1175 void
1176 edit_load_syntax (WEdit *edit, char ***pnames, const char *type)
1178 int r;
1179 char *f = NULL;
1181 if (option_auto_syntax)
1182 type = NULL;
1184 edit_free_syntax_rules (edit);
1186 if (!tty_use_colors ())
1187 return;
1189 if (!option_syntax_highlighting && (!pnames || !*pnames))
1190 return;
1192 if (edit) {
1193 if (!edit->filename)
1194 return;
1195 if (!*edit->filename && !type)
1196 return;
1198 f = concat_dir_and_file (home_dir, EDIT_SYNTAX_FILE);
1199 r = edit_read_syntax_file (edit, pnames, f, edit ? edit->filename : 0,
1200 get_first_editor_line (edit), type);
1201 if (r == -1) {
1202 edit_free_syntax_rules (edit);
1203 message (D_ERROR, _(" Load syntax file "),
1204 _(" Cannot open file %s \n %s "), f,
1205 unix_error_string (errno));
1206 } else if (r) {
1207 edit_free_syntax_rules (edit);
1208 message (D_ERROR, _(" Load syntax file "),
1209 _(" Error in file %s on line %d "),
1210 error_file_name ? error_file_name : f, r);
1211 MC_PTR_FREE (error_file_name);
1212 } else {
1213 /* succeeded */
1215 g_free (f);
1218 const char *
1219 edit_get_syntax_type (const WEdit *edit)
1221 return edit->syntax_type;