lib/widget/input_complete.c: minor refactoring and optimization.
[midnight-commander.git] / src / editor / syntax.c
bloba54baa6f81eb7d9614d9380829c02c9a2611b44b
1 /*
2 Editor syntax highlighting.
4 Copyright (C) 1996, 1997, 1998, 2001, 2002, 2003, 2004, 2005, 2006,
5 2007, 2010, 2011
6 The Free Software Foundation, Inc.
8 Written by:
9 Paul Sheer, 1998
10 Egmont Koblinger <egmont@gmail.com>, 2010
12 This file is part of the Midnight Commander.
14 The Midnight Commander is free software: you can redistribute it
15 and/or modify it under the terms of the GNU General Public License as
16 published by the Free Software Foundation, either version 3 of the License,
17 or (at your option) any later version.
19 The Midnight Commander is distributed in the hope that it will be useful,
20 but WITHOUT ANY WARRANTY; without even the implied warranty of
21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 GNU General Public License for more details.
24 You should have received a copy of the GNU General Public License
25 along with this program. If not, see <http://www.gnu.org/licenses/>.
28 /** \file
29 * \brief Source: editor syntax highlighting
30 * \author Paul Sheer
31 * \date 1996, 1997
32 * \author Mikhail Pobolovets
33 * \date 2010
35 * Mispelled words are flushed from the syntax highlighting rules
36 * when they have been around longer than
37 * TRANSIENT_WORD_TIME_OUT seconds. At a cursor rate of 30
38 * chars per second and say 3 chars + a space per word, we can
39 * accumulate 450 words absolute max with a value of 60. This is
40 * below this limit of 1024 words in a context.
43 #include <config.h>
45 #include <stdio.h>
46 #include <stdarg.h>
47 #include <sys/types.h>
48 #include <unistd.h>
49 #include <string.h>
50 #include <ctype.h>
51 #include <errno.h>
52 #include <sys/stat.h>
53 #include <stdlib.h>
55 #include "lib/global.h"
56 #include "lib/search.h" /* search engine */
57 #include "lib/skin.h"
58 #include "lib/strutil.h" /* utf string functions */
59 #include "lib/util.h"
60 #include "lib/widget.h" /* message() */
62 #include "edit-impl.h"
63 #include "editwidget.h"
65 /*** global variables ****************************************************************************/
67 int option_syntax_highlighting = 1;
68 int option_auto_syntax = 1;
70 /*** file scope macro definitions ****************************************************************/
72 /* bytes */
73 #define SYNTAX_MARKER_DENSITY 512
75 #define TRANSIENT_WORD_TIME_OUT 60
77 #define UNKNOWN_FORMAT "unknown"
79 #define MAX_WORDS_PER_CONTEXT 1024
80 #define MAX_CONTEXTS 128
82 #define RULE_ON_LEFT_BORDER 1
83 #define RULE_ON_RIGHT_BORDER 2
85 #define SYNTAX_TOKEN_STAR '\001'
86 #define SYNTAX_TOKEN_PLUS '\002'
87 #define SYNTAX_TOKEN_BRACKET '\003'
88 #define SYNTAX_TOKEN_BRACE '\004'
90 #define whiteness(x) ((x) == '\t' || (x) == '\n' || (x) == ' ')
92 #define free_args(x)
93 #define break_a {result=line;break;}
94 #define check_a {if(!*a){result=line;break;}}
95 #define check_not_a {if(*a){result=line;break;}}
97 /*** file scope type declarations ****************************************************************/
99 struct key_word
101 char *keyword;
102 unsigned char first;
103 char *whole_word_chars_left;
104 char *whole_word_chars_right;
105 long line_start;
106 int color;
109 struct context_rule
111 char *left;
112 unsigned char first_left;
113 char *right;
114 unsigned char first_right;
115 char line_start_left;
116 char line_start_right;
117 int between_delimiters;
118 char *whole_word_chars_left;
119 char *whole_word_chars_right;
120 char *keyword_first_chars;
121 gboolean spelling;
122 /* first word is word[1] */
123 struct key_word **keyword;
126 typedef struct
128 off_t offset;
129 edit_syntax_rule_t rule;
130 } syntax_marker_t;
132 /*** file scope variables ************************************************************************/
134 static char *error_file_name = NULL;
136 /*** file scope functions ************************************************************************/
137 /* --------------------------------------------------------------------------------------------- */
139 static gint
140 mc_defines_destroy (gpointer key, gpointer value, gpointer data)
142 char **values = value;
144 (void) data;
146 g_free (key);
147 while (*values)
148 g_free (*values++);
149 g_free (value);
151 return FALSE;
154 /* --------------------------------------------------------------------------------------------- */
155 /** Completely destroys the defines tree */
157 static void
158 destroy_defines (GTree ** defines)
160 g_tree_foreach (*defines, mc_defines_destroy, NULL);
161 g_tree_destroy (*defines);
162 *defines = NULL;
165 /* --------------------------------------------------------------------------------------------- */
167 /** Wrapper for case insensitive mode */
168 inline static int
169 xx_tolower (const WEdit * edit, int c)
171 return edit->is_case_insensitive ? tolower (c) : c;
174 /* --------------------------------------------------------------------------------------------- */
176 static void
177 subst_defines (GTree * defines, char **argv, char **argv_end)
179 char **t, **p;
180 int argc;
182 while (*argv != NULL && argv < argv_end)
184 t = g_tree_lookup (defines, *argv);
185 if (t != NULL)
187 int count = 0;
189 /* Count argv array members */
190 argc = 0;
191 for (p = &argv[1]; *p != NULL; p++)
192 argc++;
194 /* Count members of definition array */
195 for (p = t; *p != NULL; p++)
196 count++;
197 p = &argv[count + argc];
199 /* Buffer overflow or infinitive loop in define */
200 if (p >= argv_end)
201 break;
203 /* Move rest of argv after definition members */
204 while (argc >= 0)
205 *p-- = argv[argc-- + 1];
207 /* Copy definition members to argv */
208 for (p = argv; *t != NULL; *p++ = *t++)
211 argv++;
215 /* --------------------------------------------------------------------------------------------- */
217 static off_t
218 compare_word_to_right (const WEdit * edit, off_t i, const char *text,
219 const char *whole_left, const char *whole_right, long line_start)
221 const unsigned char *p, *q;
222 int c, d, j;
224 if (*text == '\0')
225 return -1;
227 c = xx_tolower (edit, edit_get_byte (edit, i - 1));
228 if ((line_start != 0 && c != '\n') || (whole_left != NULL && strchr (whole_left, c) != NULL))
229 return -1;
231 for (p = (unsigned char *) text, q = p + str_term_width1 ((char *) p); p < q; p++, i++)
233 switch (*p)
235 case SYNTAX_TOKEN_STAR:
236 if (++p > q)
237 return -1;
238 while (TRUE)
240 c = xx_tolower (edit, edit_get_byte (edit, i));
241 if (*p == '\0' && whole_right != NULL && strchr (whole_right, c) == NULL)
242 break;
243 if (c == *p)
244 break;
245 if (c == '\n')
246 return -1;
247 i++;
249 break;
250 case SYNTAX_TOKEN_PLUS:
251 if (++p > q)
252 return -1;
253 j = 0;
254 while (TRUE)
256 c = xx_tolower (edit, edit_get_byte (edit, i));
257 if (c == *p)
259 j = i;
260 if (*p == *text && p[1] == '\0') /* handle eg '+' and @+@ keywords properly */
261 break;
263 if (j != 0 && strchr ((char *) p + 1, c) != NULL) /* c exists further down, so it will get matched later */
264 break;
265 if (c == '\n' || c == '\t' || c == ' ' ||
266 (whole_right != NULL && strchr (whole_right, c) == NULL))
268 if (*p == '\0')
270 i--;
271 break;
273 if (j == 0)
274 return -1;
275 i = j;
276 break;
278 i++;
280 break;
281 case SYNTAX_TOKEN_BRACKET:
282 if (++p > q)
283 return -1;
284 c = -1;
285 while (TRUE)
287 d = c;
288 c = xx_tolower (edit, edit_get_byte (edit, i));
289 for (j = 0; p[j] != SYNTAX_TOKEN_BRACKET && p[j]; j++)
290 if (c == p[j])
291 goto found_char2;
292 break;
293 found_char2:
294 i++;
296 i--;
297 while (*p != SYNTAX_TOKEN_BRACKET && p <= q)
298 p++;
299 if (p > q)
300 return -1;
301 if (p[1] == d)
302 i--;
303 break;
304 case SYNTAX_TOKEN_BRACE:
305 if (++p > q)
306 return -1;
307 c = xx_tolower (edit, edit_get_byte (edit, i));
308 for (; *p != SYNTAX_TOKEN_BRACE && *p; p++)
309 if (c == *p)
310 goto found_char3;
311 return -1;
312 found_char3:
313 while (*p != SYNTAX_TOKEN_BRACE && p < q)
314 p++;
315 break;
316 default:
317 if (*p != xx_tolower (edit, edit_get_byte (edit, i)))
318 return -1;
321 return (whole_right != NULL &&
322 strchr (whole_right, xx_tolower (edit, edit_get_byte (edit, i))) != NULL) ? -1 : i;
325 /* --------------------------------------------------------------------------------------------- */
327 static const char *
328 xx_strchr (const WEdit * edit, const unsigned char *s, int char_byte)
330 while (*s >= '\005' && xx_tolower (edit, *s) != char_byte)
331 s++;
333 return (const char *) s;
336 /* --------------------------------------------------------------------------------------------- */
338 static edit_syntax_rule_t
339 apply_rules_going_right (WEdit * edit, off_t i, edit_syntax_rule_t rule)
341 struct context_rule *r;
342 int c;
343 gboolean contextchanged = FALSE;
344 gboolean found_left = FALSE, found_right = FALSE;
345 gboolean keyword_foundleft = FALSE, keyword_foundright = FALSE;
346 gboolean is_end;
347 off_t end = 0;
348 edit_syntax_rule_t _rule = rule;
350 c = xx_tolower (edit, edit_get_byte (edit, i));
351 if (c == 0)
352 return rule;
353 is_end = (rule.end == i);
355 /* check to turn off a keyword */
356 if (_rule.keyword)
358 if (edit_get_byte (edit, i - 1) == '\n')
359 _rule.keyword = 0;
360 if (is_end)
362 _rule.keyword = 0;
363 keyword_foundleft = TRUE;
367 /* check to turn off a context */
368 if (_rule.context && !_rule.keyword)
370 off_t e;
372 r = edit->rules[_rule.context];
373 if (r->first_right == c && !(rule.border & RULE_ON_RIGHT_BORDER)
374 && (e =
375 compare_word_to_right (edit, i, r->right, r->whole_word_chars_left,
376 r->whole_word_chars_right, r->line_start_right)) > 0)
378 _rule.end = e;
379 found_right = TRUE;
380 _rule.border = RULE_ON_RIGHT_BORDER;
381 if (r->between_delimiters)
382 _rule.context = 0;
384 else if (is_end && rule.border & RULE_ON_RIGHT_BORDER)
386 /* always turn off a context at 4 */
387 found_left = TRUE;
388 _rule.border = 0;
389 if (!keyword_foundleft)
390 _rule.context = 0;
392 else if (is_end && rule.border & RULE_ON_LEFT_BORDER)
394 /* never turn off a context at 2 */
395 found_left = TRUE;
396 _rule.border = 0;
400 /* check to turn on a keyword */
401 if (!_rule.keyword)
403 const char *p;
405 r = edit->rules[_rule.context];
406 p = r->keyword_first_chars;
408 if (p != NULL)
409 while (*(p = xx_strchr (edit, (unsigned char *) p + 1, c)) != '\0')
411 struct key_word *k;
412 int count;
413 off_t e;
415 count = p - r->keyword_first_chars;
416 k = r->keyword[count];
417 e = compare_word_to_right (edit, i, k->keyword, k->whole_word_chars_left,
418 k->whole_word_chars_right, k->line_start);
419 if (e > 0)
421 end = e;
422 _rule.end = e;
423 _rule.keyword = count;
424 keyword_foundright = TRUE;
425 break;
430 /* check to turn on a context */
431 if (!_rule.context)
433 if (!found_left && is_end)
435 if (rule.border & RULE_ON_RIGHT_BORDER)
437 _rule.border = 0;
438 _rule.context = 0;
439 contextchanged = TRUE;
440 _rule.keyword = 0;
443 else if (rule.border & RULE_ON_LEFT_BORDER)
445 r = edit->rules[_rule._context];
446 _rule.border = 0;
447 if (r->between_delimiters)
449 _rule.context = _rule._context;
450 contextchanged = TRUE;
451 _rule.keyword = 0;
453 if (r->first_right == c)
455 off_t e;
457 e = compare_word_to_right (edit, i, r->right, r->whole_word_chars_left,
458 r->whole_word_chars_right, r->line_start_right);
459 if (e >= end)
461 _rule.end = e;
462 found_right = TRUE;
463 _rule.border = RULE_ON_RIGHT_BORDER;
464 _rule.context = 0;
471 if (!found_right)
473 int count;
474 struct context_rule **rules = edit->rules;
476 for (count = 1; rules[count]; count++)
478 r = rules[count];
479 if (r->first_left == c)
481 off_t e;
483 e = compare_word_to_right (edit, i, r->left, r->whole_word_chars_left,
484 r->whole_word_chars_right, r->line_start_left);
485 if (e >= end && (!_rule.keyword || keyword_foundright))
487 _rule.end = e;
488 found_right = TRUE;
489 _rule.border = RULE_ON_LEFT_BORDER;
490 _rule._context = count;
491 if (!r->between_delimiters && !_rule.keyword)
493 _rule.context = count;
494 contextchanged = TRUE;
496 break;
503 /* check again to turn on a keyword if the context switched */
504 if (contextchanged && !_rule.keyword)
506 const char *p;
508 r = edit->rules[_rule.context];
509 p = r->keyword_first_chars;
511 while (*(p = xx_strchr (edit, (unsigned char *) p + 1, c)) != '\0')
513 struct key_word *k;
514 int count;
515 off_t e;
517 count = p - r->keyword_first_chars;
518 k = r->keyword[count];
519 e = compare_word_to_right (edit, i, k->keyword, k->whole_word_chars_left,
520 k->whole_word_chars_right, k->line_start);
521 if (e > 0)
523 _rule.end = e;
524 _rule.keyword = count;
525 break;
530 return _rule;
533 /* --------------------------------------------------------------------------------------------- */
535 static edit_syntax_rule_t
536 edit_get_rule (WEdit * edit, off_t byte_index)
538 off_t i;
540 if (byte_index > edit->last_get_rule)
542 for (i = edit->last_get_rule + 1; i <= byte_index; i++)
544 off_t d = SYNTAX_MARKER_DENSITY;
546 edit->rule = apply_rules_going_right (edit, i, edit->rule);
548 if (edit->syntax_marker != NULL)
549 d += ((syntax_marker_t *) edit->syntax_marker->data)->offset;
551 if (i > d)
553 syntax_marker_t *s;
555 s = g_new (syntax_marker_t, 1);
556 s->offset = i;
557 s->rule = edit->rule;
558 edit->syntax_marker = g_slist_prepend (edit->syntax_marker, s);
562 else if (byte_index < edit->last_get_rule)
564 while (TRUE)
566 syntax_marker_t *s;
568 if (edit->syntax_marker == NULL)
570 memset (&edit->rule, 0, sizeof (edit->rule));
571 for (i = -1; i <= byte_index; i++)
572 edit->rule = apply_rules_going_right (edit, i, edit->rule);
573 break;
576 s = (syntax_marker_t *) edit->syntax_marker->data;
578 if (byte_index >= s->offset)
580 edit->rule = s->rule;
581 for (i = s->offset + 1; i <= byte_index; i++)
582 edit->rule = apply_rules_going_right (edit, i, edit->rule);
583 break;
586 g_free (s);
587 edit->syntax_marker = g_slist_delete_link (edit->syntax_marker, edit->syntax_marker);
590 edit->last_get_rule = byte_index;
591 return edit->rule;
594 /* --------------------------------------------------------------------------------------------- */
596 static inline int
597 translate_rule_to_color (const WEdit * edit, edit_syntax_rule_t rule)
599 return edit->rules[rule.context]->keyword[rule.keyword]->color;
602 /* --------------------------------------------------------------------------------------------- */
604 Returns 0 on error/eof or a count of the number of bytes read
605 including the newline. Result must be free'd.
606 In case of an error, *line will not be modified.
609 static size_t
610 read_one_line (char **line, FILE * f)
612 GString *p;
613 size_t r = 0;
615 /* not reallocate string too often */
616 p = g_string_sized_new (64);
618 while (TRUE)
620 int c;
622 c = fgetc (f);
623 if (c == EOF)
625 if (ferror (f))
627 if (errno == EINTR)
628 continue;
629 r = 0;
631 break;
633 r++;
635 /* handle all of \r\n, \r, \n correctly. */
636 if (c == '\n')
637 break;
638 if (c == '\r')
640 c = fgetc (f);
641 if (c == '\n')
642 r++;
643 else
644 ungetc (c, f);
645 break;
648 g_string_append_c (p, c);
650 if (r != 0)
651 *line = g_string_free (p, FALSE);
652 else
653 g_string_free (p, TRUE);
655 return r;
658 /* --------------------------------------------------------------------------------------------- */
660 static char *
661 convert (char *s)
663 char *r, *p;
665 p = r = s;
666 while (*s)
668 switch (*s)
670 case '\\':
671 s++;
672 switch (*s)
674 case ' ':
675 *p = ' ';
676 s--;
677 break;
678 case 'n':
679 *p = '\n';
680 break;
681 case 'r':
682 *p = '\r';
683 break;
684 case 't':
685 *p = '\t';
686 break;
687 case 's':
688 *p = ' ';
689 break;
690 case '*':
691 *p = '*';
692 break;
693 case '\\':
694 *p = '\\';
695 break;
696 case '[':
697 case ']':
698 *p = SYNTAX_TOKEN_BRACKET;
699 break;
700 case '{':
701 case '}':
702 *p = SYNTAX_TOKEN_BRACE;
703 break;
704 case 0:
705 *p = *s;
706 return r;
707 default:
708 *p = *s;
709 break;
711 break;
712 case '*':
713 *p = SYNTAX_TOKEN_STAR;
714 break;
715 case '+':
716 *p = SYNTAX_TOKEN_PLUS;
717 break;
718 default:
719 *p = *s;
720 break;
722 s++;
723 p++;
725 *p = '\0';
726 return r;
729 /* --------------------------------------------------------------------------------------------- */
731 static int
732 get_args (char *l, char **args, int args_size)
734 int argc = 0;
736 while (argc < args_size)
738 char *p = l;
739 while (*p != '\0' && whiteness (*p))
740 p++;
741 if (*p == '\0')
742 break;
743 for (l = p + 1; *l != '\0' && !whiteness (*l); l++)
745 if (*l != '\0')
746 *l++ = '\0';
747 args[argc++] = convert (p);
749 args[argc] = (char *) NULL;
750 return argc;
753 /* --------------------------------------------------------------------------------------------- */
755 static int
756 this_try_alloc_color_pair (const char *fg, const char *bg, const char *attrs)
758 char f[80], b[80], a[80], *p;
760 if (bg != NULL && *bg == '\0')
761 bg = NULL;
762 if (fg != NULL && *fg == '\0')
763 fg = NULL;
764 if (attrs != NULL && *attrs == '\0')
765 attrs = NULL;
767 if ((fg == NULL) && (bg == NULL))
768 return EDITOR_NORMAL_COLOR;
770 if (fg != NULL)
772 g_strlcpy (f, fg, sizeof (f));
773 p = strchr (f, '/');
774 if (p != NULL)
775 *p = '\0';
776 fg = f;
778 if (bg != NULL)
780 g_strlcpy (b, bg, sizeof (b));
781 p = strchr (b, '/');
782 if (p != NULL)
783 *p = '\0';
784 bg = b;
786 if ((fg == NULL) || (bg == NULL))
788 /* get colors from skin */
789 char *editnormal;
791 editnormal = mc_skin_get ("editor", "_default_", "default;default");
793 if (fg == NULL)
795 g_strlcpy (f, editnormal, sizeof (f));
796 p = strchr (f, ';');
797 if (p != NULL)
798 *p = '\0';
799 if (f[0] == '\0')
800 g_strlcpy (f, "default", sizeof (f));
801 fg = f;
803 if (bg == NULL)
805 p = strchr (editnormal, ';');
806 if ((p != NULL) && (*(++p) != '\0'))
807 g_strlcpy (b, p, sizeof (b));
808 else
809 g_strlcpy (b, "default", sizeof (b));
810 bg = b;
813 g_free (editnormal);
816 if (attrs != NULL)
818 g_strlcpy (a, attrs, sizeof (a));
819 p = strchr (a, '/');
820 if (p != NULL)
821 *p = '\0';
822 /* get_args() mangles the + signs, unmangle 'em */
823 p = a;
824 while ((p = strchr (p, SYNTAX_TOKEN_PLUS)) != NULL)
825 *p++ = '+';
826 attrs = a;
828 return tty_try_alloc_color_pair (fg, bg, attrs);
831 /* --------------------------------------------------------------------------------------------- */
833 static FILE *
834 open_include_file (const char *filename)
836 FILE *f;
838 MC_PTR_FREE (error_file_name);
839 error_file_name = g_strdup (filename);
840 if (g_path_is_absolute (filename))
841 return fopen (filename, "r");
843 g_free (error_file_name);
844 error_file_name =
845 g_build_filename (mc_config_get_data_path (), EDIT_DIR, filename, (char *) NULL);
846 f = fopen (error_file_name, "r");
847 if (f != NULL)
848 return f;
850 g_free (error_file_name);
851 error_file_name = g_build_filename (mc_global.sysconfig_dir, "syntax", filename, (char *) NULL);
852 f = fopen (error_file_name, "r");
853 if (f != NULL)
854 return f;
856 g_free (error_file_name);
857 error_file_name =
858 g_build_filename (mc_global.share_data_dir, "syntax", filename, (char *) NULL);
860 return fopen (error_file_name, "r");
863 /* --------------------------------------------------------------------------------------------- */
865 inline static void
866 xx_lowerize_line (WEdit * edit, char *line, size_t len)
868 if (edit->is_case_insensitive)
870 size_t i;
871 for (i = 0; i < len; ++i)
872 line[i] = tolower (line[i]);
876 /* --------------------------------------------------------------------------------------------- */
877 /** returns line number on error */
879 static int
880 edit_read_syntax_rules (WEdit * edit, FILE * f, char **args, int args_size)
882 FILE *g = NULL;
883 char *fg, *bg, *attrs;
884 char last_fg[32] = "", last_bg[32] = "", last_attrs[64] = "";
885 char whole_right[512];
886 char whole_left[512];
887 char *l = 0;
888 int save_line = 0, line = 0;
889 struct context_rule **r, *c = NULL;
890 int num_words = -1, num_contexts = -1;
891 int result = 0;
892 int argc;
893 int i, j;
894 int alloc_contexts = MAX_CONTEXTS,
895 alloc_words_per_context = MAX_WORDS_PER_CONTEXT,
896 max_alloc_words_per_context = MAX_WORDS_PER_CONTEXT;
898 args[0] = NULL;
899 edit->is_case_insensitive = FALSE;
901 strcpy (whole_left, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_01234567890");
902 strcpy (whole_right, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_01234567890");
904 r = edit->rules = g_malloc0 (alloc_contexts * sizeof (struct context_rule *));
906 if (!edit->defines)
907 edit->defines = g_tree_new ((GCompareFunc) strcmp);
909 while (TRUE)
911 char **a;
912 size_t len;
914 line++;
915 l = 0;
917 len = read_one_line (&l, f);
918 if (len != 0)
919 xx_lowerize_line (edit, l, len);
920 else
922 if (g == NULL)
923 break;
925 fclose (f);
926 f = g;
927 g = NULL;
928 line = save_line + 1;
929 MC_PTR_FREE (error_file_name);
930 MC_PTR_FREE (l);
931 len = read_one_line (&l, f);
932 if (len == 0)
933 break;
934 xx_lowerize_line (edit, l, len);
937 argc = get_args (l, args, args_size);
938 a = args + 1;
939 if (args[0] == NULL)
941 /* do nothing */
943 else if (strcmp (args[0], "include") == 0)
945 if (g != NULL || argc != 2)
947 result = line;
948 break;
950 g = f;
951 f = open_include_file (args[1]);
952 if (f == NULL)
954 MC_PTR_FREE (error_file_name);
955 result = line;
956 break;
958 save_line = line;
959 line = 0;
961 else if (strcmp (args[0], "caseinsensitive") == 0)
963 edit->is_case_insensitive = TRUE;
965 else if (strcmp (args[0], "wholechars") == 0)
967 check_a;
968 if (strcmp (*a, "left") == 0)
970 a++;
971 g_strlcpy (whole_left, *a, sizeof (whole_left));
973 else if (strcmp (*a, "right") == 0)
975 a++;
976 g_strlcpy (whole_right, *a, sizeof (whole_right));
978 else
980 g_strlcpy (whole_left, *a, sizeof (whole_left));
981 g_strlcpy (whole_right, *a, sizeof (whole_right));
983 a++;
984 check_not_a;
986 else if (strcmp (args[0], "context") == 0)
988 check_a;
989 if (num_contexts == -1)
991 if (strcmp (*a, "default") != 0)
992 { /* first context is the default */
993 break_a;
995 a++;
996 c = r[0] = g_malloc0 (sizeof (struct context_rule));
997 c->left = g_strdup (" ");
998 c->right = g_strdup (" ");
999 num_contexts = 0;
1001 else
1003 /* Terminate previous context. */
1004 r[num_contexts - 1]->keyword[num_words] = NULL;
1005 c = r[num_contexts] = g_malloc0 (sizeof (struct context_rule));
1006 if (strcmp (*a, "exclusive") == 0)
1008 a++;
1009 c->between_delimiters = 1;
1011 check_a;
1012 if (strcmp (*a, "whole") == 0)
1014 a++;
1015 c->whole_word_chars_left = g_strdup (whole_left);
1016 c->whole_word_chars_right = g_strdup (whole_right);
1018 else if (strcmp (*a, "wholeleft") == 0)
1020 a++;
1021 c->whole_word_chars_left = g_strdup (whole_left);
1023 else if (strcmp (*a, "wholeright") == 0)
1025 a++;
1026 c->whole_word_chars_right = g_strdup (whole_right);
1028 check_a;
1029 if (strcmp (*a, "linestart") == 0)
1031 a++;
1032 c->line_start_left = 1;
1034 check_a;
1035 c->left = g_strdup (*a++);
1036 check_a;
1037 if (strcmp (*a, "linestart") == 0)
1039 a++;
1040 c->line_start_right = 1;
1042 check_a;
1043 c->right = g_strdup (*a++);
1044 c->first_left = *c->left;
1045 c->first_right = *c->right;
1047 c->keyword = g_malloc (alloc_words_per_context * sizeof (struct key_word *));
1048 num_words = 1;
1049 c->keyword[0] = g_malloc0 (sizeof (struct key_word));
1050 subst_defines (edit->defines, a, &args[1024]);
1051 fg = *a;
1052 if (*a != '\0')
1053 a++;
1054 bg = *a;
1055 if (*a != '\0')
1056 a++;
1057 attrs = *a;
1058 if (*a != '\0')
1059 a++;
1060 g_strlcpy (last_fg, fg != NULL ? fg : "", sizeof (last_fg));
1061 g_strlcpy (last_bg, bg != NULL ? bg : "", sizeof (last_bg));
1062 g_strlcpy (last_attrs, attrs != NULL ? attrs : "", sizeof (last_attrs));
1063 c->keyword[0]->color = this_try_alloc_color_pair (fg, bg, attrs);
1064 c->keyword[0]->keyword = g_strdup (" ");
1065 check_not_a;
1067 alloc_words_per_context = MAX_WORDS_PER_CONTEXT;
1068 if (++num_contexts >= alloc_contexts)
1070 struct context_rule **tmp;
1072 alloc_contexts += 128;
1073 tmp = g_realloc (r, alloc_contexts * sizeof (struct context_rule *));
1074 r = tmp;
1077 else if (strcmp (args[0], "spellcheck") == 0)
1079 if (c == NULL)
1081 result = line;
1082 break;
1084 c->spelling = TRUE;
1086 else if (strcmp (args[0], "keyword") == 0)
1088 struct key_word *k;
1090 if (num_words == -1)
1091 break_a;
1092 check_a;
1093 k = r[num_contexts - 1]->keyword[num_words] = g_malloc0 (sizeof (struct key_word));
1094 if (strcmp (*a, "whole") == 0)
1096 a++;
1097 k->whole_word_chars_left = g_strdup (whole_left);
1098 k->whole_word_chars_right = g_strdup (whole_right);
1100 else if (strcmp (*a, "wholeleft") == 0)
1102 a++;
1103 k->whole_word_chars_left = g_strdup (whole_left);
1105 else if (strcmp (*a, "wholeright") == 0)
1107 a++;
1108 k->whole_word_chars_right = g_strdup (whole_right);
1110 check_a;
1111 if (strcmp (*a, "linestart") == 0)
1113 a++;
1114 k->line_start = 1;
1116 check_a;
1117 if (strcmp (*a, "whole") == 0)
1119 break_a;
1121 k->keyword = g_strdup (*a++);
1122 k->first = *k->keyword;
1123 subst_defines (edit->defines, a, &args[1024]);
1124 fg = *a;
1125 if (*a != '\0')
1126 a++;
1127 bg = *a;
1128 if (*a != '\0')
1129 a++;
1130 attrs = *a;
1131 if (*a != '\0')
1132 a++;
1133 if (fg == NULL)
1134 fg = last_fg;
1135 if (bg == NULL)
1136 bg = last_bg;
1137 if (attrs == NULL)
1138 attrs = last_attrs;
1139 k->color = this_try_alloc_color_pair (fg, bg, attrs);
1140 check_not_a;
1142 if (++num_words >= alloc_words_per_context)
1144 struct key_word **tmp;
1146 alloc_words_per_context += 1024;
1148 if (alloc_words_per_context > max_alloc_words_per_context)
1149 max_alloc_words_per_context = alloc_words_per_context;
1151 tmp = g_realloc (c->keyword, alloc_words_per_context * sizeof (struct key_word *));
1152 c->keyword = tmp;
1155 else if (*(args[0]) == '#')
1157 /* do nothing for comment */
1159 else if (strcmp (args[0], "file") == 0)
1161 break;
1163 else if (strcmp (args[0], "define") == 0)
1165 char *key = *a++;
1166 char **argv;
1168 if (argc < 3)
1169 break_a;
1170 argv = g_tree_lookup (edit->defines, key);
1171 if (argv != NULL)
1172 mc_defines_destroy (NULL, argv, NULL);
1173 else
1174 key = g_strdup (key);
1176 argv = g_new (char *, argc - 1);
1177 g_tree_insert (edit->defines, key, argv);
1178 while (*a != NULL)
1179 *argv++ = g_strdup (*a++);
1180 *argv = NULL;
1182 else
1183 { /* anything else is an error */
1184 break_a;
1186 free_args (args);
1187 MC_PTR_FREE (l);
1189 free_args (args);
1190 MC_PTR_FREE (l);
1192 /* Terminate context array. */
1193 if (num_contexts > 0)
1195 r[num_contexts - 1]->keyword[num_words] = NULL;
1196 r[num_contexts] = NULL;
1199 if (edit->rules[0] == NULL)
1200 MC_PTR_FREE (edit->rules);
1202 if (result == 0)
1204 char *first_chars, *p;
1206 if (num_contexts == -1)
1207 return line;
1209 first_chars = g_malloc0 (max_alloc_words_per_context + 2);
1211 for (i = 0; edit->rules[i] != NULL; i++)
1213 c = edit->rules[i];
1214 p = first_chars;
1215 *p++ = (char) 1;
1216 for (j = 1; c->keyword[j] != NULL; j++)
1217 *p++ = c->keyword[j]->first;
1218 *p = '\0';
1219 c->keyword_first_chars = g_strdup (first_chars);
1222 g_free (first_chars);
1225 return result;
1228 /* --------------------------------------------------------------------------------------------- */
1230 /* returns -1 on file error, line number on error in file syntax */
1231 static int
1232 edit_read_syntax_file (WEdit * edit, char ***pnames, const char *syntax_file,
1233 const char *editor_file, const char *first_line, const char *type)
1235 #define NENTRIES 30
1236 FILE *f, *g = NULL;
1237 char *args[1024], *l = NULL;
1238 long line = 0;
1239 int result = 0;
1240 int count = 0;
1241 char *lib_file;
1242 gboolean found = FALSE;
1243 char **tmpnames = NULL;
1245 f = fopen (syntax_file, "r");
1246 if (f == NULL)
1248 lib_file = g_build_filename (mc_global.share_data_dir, "syntax", "Syntax", (char *) NULL);
1249 f = fopen (lib_file, "r");
1250 g_free (lib_file);
1251 if (f == NULL)
1252 return -1;
1255 args[0] = NULL;
1256 while (TRUE)
1258 line++;
1259 MC_PTR_FREE (l);
1260 if (read_one_line (&l, f) == 0)
1261 break;
1262 (void) get_args (l, args, 1023); /* Final NULL */
1263 if (args[0] == NULL)
1264 continue;
1266 /* Looking for `include ...` lines before first `file ...` ones */
1267 if (!found && strcmp (args[0], "include") == 0)
1269 if (g != NULL)
1270 continue;
1272 if (!args[1] || !(g = open_include_file (args[1])))
1274 result = line;
1275 break;
1277 goto found_type;
1280 /* looking for `file ...' lines only */
1281 if (strcmp (args[0], "file") != 0)
1282 continue;
1284 found = TRUE;
1286 /* must have two args or report error */
1287 if (!args[1] || !args[2])
1289 result = line;
1290 break;
1292 if (pnames && *pnames)
1294 /* 1: just collecting a list of names of rule sets */
1295 /* Reallocate the list if required */
1296 if (count % NENTRIES == 0)
1298 tmpnames =
1299 (char **) g_try_realloc (*pnames, (count + NENTRIES + 1) * sizeof (char *));
1300 if (tmpnames == NULL)
1301 break;
1302 *pnames = tmpnames;
1304 (*pnames)[count++] = g_strdup (args[2]);
1305 (*pnames)[count] = NULL;
1307 else if (type)
1309 /* 2: rule set was explicitly specified by the caller */
1310 if (strcmp (type, args[2]) == 0)
1311 goto found_type;
1313 else if (editor_file && edit)
1315 /* 3: auto-detect rule set from regular expressions */
1316 int q;
1318 q = mc_search (args[1], editor_file, MC_SEARCH_T_REGEX);
1319 /* does filename match arg 1 ? */
1320 if (!q && args[3])
1322 /* does first line match arg 3 ? */
1323 q = mc_search (args[3], first_line, MC_SEARCH_T_REGEX);
1325 if (q)
1327 int line_error;
1328 char *syntax_type;
1329 found_type:
1330 syntax_type = args[2];
1331 line_error = edit_read_syntax_rules (edit, g ? g : f, args, 1023);
1332 if (line_error)
1334 if (!error_file_name) /* an included file */
1335 result = line + line_error;
1336 else
1337 result = line_error;
1339 else
1341 g_free (edit->syntax_type);
1342 edit->syntax_type = g_strdup (syntax_type);
1343 /* if there are no rules then turn off syntax highlighting for speed */
1344 if (!g && !edit->rules[1])
1345 if (!edit->rules[0]->keyword[1] && !edit->rules[0]->spelling)
1347 edit_free_syntax_rules (edit);
1348 break;
1352 if (g == NULL)
1353 break;
1355 fclose (g);
1356 g = NULL;
1360 g_free (l);
1361 fclose (f);
1362 return result;
1365 /* --------------------------------------------------------------------------------------------- */
1367 static const char *
1368 get_first_editor_line (WEdit * edit)
1370 static char s[256];
1372 s[0] = '\0';
1374 if (edit != NULL)
1376 size_t i;
1378 for (i = 0; i < sizeof (s) - 1; i++)
1380 s[i] = edit_get_byte (edit, i);
1381 if (s[i] == '\n')
1383 s[i] = '\0';
1384 break;
1388 s[sizeof (s) - 1] = '\0';
1391 return s;
1394 /* --------------------------------------------------------------------------------------------- */
1395 /*** public functions ****************************************************************************/
1396 /* --------------------------------------------------------------------------------------------- */
1399 edit_get_syntax_color (WEdit * edit, off_t byte_index)
1401 if (!tty_use_colors ())
1402 return 0;
1404 if (edit->rules != NULL && byte_index < edit->last_byte && option_syntax_highlighting)
1405 return translate_rule_to_color (edit, edit_get_rule (edit, byte_index));
1407 return EDITOR_NORMAL_COLOR;
1410 /* --------------------------------------------------------------------------------------------- */
1412 void
1413 edit_free_syntax_rules (WEdit * edit)
1415 size_t i, j;
1417 if (!edit)
1418 return;
1419 if (edit->defines)
1420 destroy_defines (&edit->defines);
1421 if (!edit->rules)
1422 return;
1424 edit_get_rule (edit, -1);
1425 MC_PTR_FREE (edit->syntax_type);
1427 for (i = 0; edit->rules[i]; i++)
1429 if (edit->rules[i]->keyword)
1431 for (j = 0; edit->rules[i]->keyword[j]; j++)
1433 MC_PTR_FREE (edit->rules[i]->keyword[j]->keyword);
1434 MC_PTR_FREE (edit->rules[i]->keyword[j]->whole_word_chars_left);
1435 MC_PTR_FREE (edit->rules[i]->keyword[j]->whole_word_chars_right);
1436 MC_PTR_FREE (edit->rules[i]->keyword[j]);
1439 MC_PTR_FREE (edit->rules[i]->left);
1440 MC_PTR_FREE (edit->rules[i]->right);
1441 MC_PTR_FREE (edit->rules[i]->whole_word_chars_left);
1442 MC_PTR_FREE (edit->rules[i]->whole_word_chars_right);
1443 MC_PTR_FREE (edit->rules[i]->keyword);
1444 MC_PTR_FREE (edit->rules[i]->keyword_first_chars);
1445 MC_PTR_FREE (edit->rules[i]);
1448 g_slist_foreach (edit->syntax_marker, (GFunc) g_free, NULL);
1449 g_slist_free (edit->syntax_marker);
1450 edit->syntax_marker = NULL;
1451 MC_PTR_FREE (edit->rules);
1452 tty_color_free_all_tmp ();
1455 /* --------------------------------------------------------------------------------------------- */
1457 * Load rules into edit struct. Either edit or *pnames must be NULL. If
1458 * edit is NULL, a list of types will be stored into names. If type is
1459 * NULL, then the type will be selected according to the filename.
1460 * type must be edit->syntax_type or NULL
1462 void
1463 edit_load_syntax (WEdit * edit, char ***pnames, const char *type)
1465 int r;
1466 char *f = NULL;
1468 if (option_auto_syntax)
1469 type = NULL;
1471 if (edit != NULL)
1473 char *saved_type;
1475 saved_type = g_strdup (type); /* save edit->syntax_type */
1476 edit_free_syntax_rules (edit);
1477 edit->syntax_type = saved_type; /* restore edit->syntax_type */
1480 if (!tty_use_colors ())
1481 return;
1483 if (!option_syntax_highlighting && (!pnames || !*pnames))
1484 return;
1486 if (edit != NULL && edit->filename_vpath == NULL)
1487 return;
1489 f = mc_config_get_full_path (EDIT_SYNTAX_FILE);
1490 if (edit != NULL)
1492 char *tmp_f;
1494 tmp_f = vfs_path_to_str (edit->filename_vpath);
1495 r = edit_read_syntax_file (edit, pnames, f, tmp_f,
1496 get_first_editor_line (edit),
1497 option_auto_syntax ? NULL : edit->syntax_type);
1498 g_free (tmp_f);
1500 else
1501 r = edit_read_syntax_file (NULL, pnames, f, NULL, "", NULL);
1502 if (r == -1)
1504 edit_free_syntax_rules (edit);
1505 message (D_ERROR, _("Load syntax file"),
1506 _("Cannot open file %s\n%s"), f, unix_error_string (errno));
1508 else if (r != 0)
1510 edit_free_syntax_rules (edit);
1511 message (D_ERROR, _("Load syntax file"),
1512 _("Error in file %s on line %d"), error_file_name ? error_file_name : f, r);
1513 MC_PTR_FREE (error_file_name);
1516 g_free (f);
1519 /* --------------------------------------------------------------------------------------------- */
1521 const char *
1522 edit_get_syntax_type (const WEdit * edit)
1524 return edit->syntax_type;
1527 /* --------------------------------------------------------------------------------------------- */