cppcheck: reduce variable scope.
[midnight-commander.git] / src / editor / syntax.c
blob84f832942293439a63d57d734f3d05c5baf28be2
1 /*
2 Editor syntax highlighting.
4 Copyright (C) 1996, 1997, 1998, 2001, 2002, 2003, 2004, 2005, 2006,
5 2007, 2010, 2011, 2013
6 The Free Software Foundation, Inc.
8 Written by:
9 Paul Sheer, 1998
10 Egmont Koblinger <egmont@gmail.com>, 2010
11 Slava Zanko <slavazanko@gmail.com>, 2013
12 Andrew Borodin <aborodin@vmail.ru>, 2013
14 This file is part of the Midnight Commander.
16 The Midnight Commander is free software: you can redistribute it
17 and/or modify it under the terms of the GNU General Public License as
18 published by the Free Software Foundation, either version 3 of the License,
19 or (at your option) any later version.
21 The Midnight Commander is distributed in the hope that it will be useful,
22 but WITHOUT ANY WARRANTY; without even the implied warranty of
23 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 GNU General Public License for more details.
26 You should have received a copy of the GNU General Public License
27 along with this program. If not, see <http://www.gnu.org/licenses/>.
30 /** \file
31 * \brief Source: editor syntax highlighting
32 * \author Paul Sheer
33 * \date 1996, 1997
34 * \author Mikhail Pobolovets
35 * \date 2010
37 * Mispelled words are flushed from the syntax highlighting rules
38 * when they have been around longer than
39 * TRANSIENT_WORD_TIME_OUT seconds. At a cursor rate of 30
40 * chars per second and say 3 chars + a space per word, we can
41 * accumulate 450 words absolute max with a value of 60. This is
42 * below this limit of 1024 words in a context.
45 #include <config.h>
47 #include <stdio.h>
48 #include <stdarg.h>
49 #include <sys/types.h>
50 #include <unistd.h>
51 #include <string.h>
52 #include <ctype.h>
53 #include <errno.h>
54 #include <sys/stat.h>
55 #include <stdlib.h>
57 #include "lib/global.h"
58 #include "lib/search.h" /* search engine */
59 #include "lib/skin.h"
60 #include "lib/strutil.h" /* utf string functions */
61 #include "lib/util.h"
62 #include "lib/widget.h" /* message() */
64 #include "edit-impl.h"
65 #include "editwidget.h"
67 /*** global variables ****************************************************************************/
69 int option_syntax_highlighting = 1;
70 int option_auto_syntax = 1;
72 /*** file scope macro definitions ****************************************************************/
74 /* bytes */
75 #define SYNTAX_MARKER_DENSITY 512
77 #define TRANSIENT_WORD_TIME_OUT 60
79 #define UNKNOWN_FORMAT "unknown"
81 #define MAX_WORDS_PER_CONTEXT 1024
82 #define MAX_CONTEXTS 128
84 #define RULE_ON_LEFT_BORDER 1
85 #define RULE_ON_RIGHT_BORDER 2
87 #define SYNTAX_TOKEN_STAR '\001'
88 #define SYNTAX_TOKEN_PLUS '\002'
89 #define SYNTAX_TOKEN_BRACKET '\003'
90 #define SYNTAX_TOKEN_BRACE '\004'
92 #define whiteness(x) ((x) == '\t' || (x) == '\n' || (x) == ' ')
94 #define free_args(x)
95 #define break_a {result=line;break;}
96 #define check_a {if(!*a){result=line;break;}}
97 #define check_not_a {if(*a){result=line;break;}}
99 /*** file scope type declarations ****************************************************************/
101 struct key_word
103 char *keyword;
104 unsigned char first;
105 char *whole_word_chars_left;
106 char *whole_word_chars_right;
107 long line_start;
108 int color;
111 struct context_rule
113 char *left;
114 unsigned char first_left;
115 char *right;
116 unsigned char first_right;
117 char line_start_left;
118 char line_start_right;
119 int between_delimiters;
120 char *whole_word_chars_left;
121 char *whole_word_chars_right;
122 char *keyword_first_chars;
123 gboolean spelling;
124 /* first word is word[1] */
125 struct key_word **keyword;
128 typedef struct
130 off_t offset;
131 edit_syntax_rule_t rule;
132 } syntax_marker_t;
134 /*** file scope variables ************************************************************************/
136 static char *error_file_name = NULL;
138 /*** file scope functions ************************************************************************/
139 /* --------------------------------------------------------------------------------------------- */
141 static gint
142 mc_defines_destroy (gpointer key, gpointer value, gpointer data)
144 (void) data;
146 g_free (key);
147 g_strfreev ((char **) value);
149 return FALSE;
152 /* --------------------------------------------------------------------------------------------- */
153 /** Completely destroys the defines tree */
155 static void
156 destroy_defines (GTree ** defines)
158 g_tree_foreach (*defines, mc_defines_destroy, NULL);
159 g_tree_destroy (*defines);
160 *defines = NULL;
163 /* --------------------------------------------------------------------------------------------- */
165 /** Wrapper for case insensitive mode */
166 inline static int
167 xx_tolower (const WEdit * edit, int c)
169 return edit->is_case_insensitive ? tolower (c) : c;
172 /* --------------------------------------------------------------------------------------------- */
174 static void
175 subst_defines (GTree * defines, char **argv, char **argv_end)
177 char **t, **p;
178 int argc;
180 while (*argv != NULL && argv < argv_end)
182 t = g_tree_lookup (defines, *argv);
183 if (t != NULL)
185 int count = 0;
187 /* Count argv array members */
188 argc = 0;
189 for (p = &argv[1]; *p != NULL; p++)
190 argc++;
192 /* Count members of definition array */
193 for (p = t; *p != NULL; p++)
194 count++;
195 p = &argv[count + argc];
197 /* Buffer overflow or infinitive loop in define */
198 if (p >= argv_end)
199 break;
201 /* Move rest of argv after definition members */
202 while (argc >= 0)
203 *p-- = argv[argc-- + 1];
205 /* Copy definition members to argv */
206 for (p = argv; *t != NULL; *p++ = *t++)
209 argv++;
213 /* --------------------------------------------------------------------------------------------- */
215 static off_t
216 compare_word_to_right (const WEdit * edit, off_t i, const char *text,
217 const char *whole_left, const char *whole_right, long line_start)
219 const unsigned char *p, *q;
220 int c, d, j;
222 if (*text == '\0')
223 return -1;
225 c = xx_tolower (edit, edit_buffer_get_byte (&edit->buffer, i - 1));
226 if ((line_start != 0 && c != '\n') || (whole_left != NULL && strchr (whole_left, c) != NULL))
227 return -1;
229 for (p = (unsigned char *) text, q = p + strlen ((char *) p); p < q; p++, i++)
231 switch (*p)
233 case SYNTAX_TOKEN_STAR:
234 if (++p > q)
235 return -1;
236 while (TRUE)
238 c = xx_tolower (edit, edit_buffer_get_byte (&edit->buffer, i));
239 if (*p == '\0' && whole_right != NULL && strchr (whole_right, c) == NULL)
240 break;
241 if (c == *p)
242 break;
243 if (c == '\n')
244 return -1;
245 i++;
247 break;
248 case SYNTAX_TOKEN_PLUS:
249 if (++p > q)
250 return -1;
251 j = 0;
252 while (TRUE)
254 c = xx_tolower (edit, edit_buffer_get_byte (&edit->buffer, i));
255 if (c == *p)
257 j = i;
258 if (*p == *text && p[1] == '\0') /* handle eg '+' and @+@ keywords properly */
259 break;
261 if (j != 0 && strchr ((char *) p + 1, c) != NULL) /* c exists further down, so it will get matched later */
262 break;
263 if (c == '\n' || c == '\t' || c == ' ' ||
264 (whole_right != NULL && strchr (whole_right, c) == NULL))
266 if (*p == '\0')
268 i--;
269 break;
271 if (j == 0)
272 return -1;
273 i = j;
274 break;
276 i++;
278 break;
279 case SYNTAX_TOKEN_BRACKET:
280 if (++p > q)
281 return -1;
282 c = -1;
283 while (TRUE)
285 d = c;
286 c = xx_tolower (edit, edit_buffer_get_byte (&edit->buffer, i));
287 for (j = 0; p[j] != SYNTAX_TOKEN_BRACKET && p[j]; j++)
288 if (c == p[j])
289 goto found_char2;
290 break;
291 found_char2:
292 i++;
294 i--;
295 while (*p != SYNTAX_TOKEN_BRACKET && p <= q)
296 p++;
297 if (p > q)
298 return -1;
299 if (p[1] == d)
300 i--;
301 break;
302 case SYNTAX_TOKEN_BRACE:
303 if (++p > q)
304 return -1;
305 c = xx_tolower (edit, edit_buffer_get_byte (&edit->buffer, i));
306 for (; *p != SYNTAX_TOKEN_BRACE && *p; p++)
307 if (c == *p)
308 goto found_char3;
309 return -1;
310 found_char3:
311 while (*p != SYNTAX_TOKEN_BRACE && p < q)
312 p++;
313 break;
314 default:
315 if (*p != xx_tolower (edit, edit_buffer_get_byte (&edit->buffer, i)))
316 return -1;
319 return (whole_right != NULL &&
320 strchr (whole_right,
321 xx_tolower (edit, edit_buffer_get_byte (&edit->buffer, i))) != NULL) ? -1 : i;
324 /* --------------------------------------------------------------------------------------------- */
326 static const char *
327 xx_strchr (const WEdit * edit, const unsigned char *s, int char_byte)
329 while (*s >= '\005' && xx_tolower (edit, *s) != char_byte)
330 s++;
332 return (const char *) s;
335 /* --------------------------------------------------------------------------------------------- */
337 static void
338 apply_rules_going_right (WEdit * edit, off_t i)
340 struct context_rule *r;
341 int c;
342 gboolean contextchanged = FALSE;
343 gboolean found_left = FALSE, found_right = FALSE;
344 gboolean keyword_foundleft = FALSE, keyword_foundright = FALSE;
345 gboolean is_end;
346 off_t end = 0;
347 edit_syntax_rule_t _rule = edit->rule;
349 c = xx_tolower (edit, edit_buffer_get_byte (&edit->buffer, i));
350 if (c == 0)
351 return;
353 is_end = (edit->rule.end == i);
355 /* check to turn off a keyword */
356 if (_rule.keyword != 0)
358 if (edit_buffer_get_byte (&edit->buffer, 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 != 0 && _rule.keyword == 0)
370 off_t e;
372 r = edit->rules[_rule.context];
373 if (r->first_right == c && (edit->rule.border & RULE_ON_RIGHT_BORDER) == 0
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 && (edit->rule.border & RULE_ON_RIGHT_BORDER) != 0)
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 && (edit->rule.border & RULE_ON_LEFT_BORDER) != 0)
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 == 0)
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 == 0)
433 if (!found_left && is_end)
435 if ((edit->rule.border & RULE_ON_RIGHT_BORDER) != 0)
437 _rule.border = 0;
438 _rule.context = 0;
439 contextchanged = TRUE;
440 _rule.keyword = 0;
443 else if ((edit->rule.border & RULE_ON_LEFT_BORDER) != 0)
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] != NULL; 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 == 0 || 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 == 0)
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 == 0)
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 edit->rule = _rule;
533 /* --------------------------------------------------------------------------------------------- */
535 static void
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 apply_rules_going_right (edit, i);
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 apply_rules_going_right (edit, i);
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 apply_rules_going_right (edit, i);
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;
593 /* --------------------------------------------------------------------------------------------- */
595 static inline int
596 translate_rule_to_color (const WEdit * edit, const edit_syntax_rule_t * rule)
598 return edit->rules[rule->context]->keyword[rule->keyword]->color;
601 /* --------------------------------------------------------------------------------------------- */
603 Returns 0 on error/eof or a count of the number of bytes read
604 including the newline. Result must be free'd.
605 In case of an error, *line will not be modified.
608 static size_t
609 read_one_line (char **line, FILE * f)
611 GString *p;
612 size_t r = 0;
614 /* not reallocate string too often */
615 p = g_string_sized_new (64);
617 while (TRUE)
619 int c;
621 c = fgetc (f);
622 if (c == EOF)
624 if (ferror (f))
626 if (errno == EINTR)
627 continue;
628 r = 0;
630 break;
632 r++;
634 /* handle all of \r\n, \r, \n correctly. */
635 if (c == '\n')
636 break;
637 if (c == '\r')
639 c = fgetc (f);
640 if (c == '\n')
641 r++;
642 else
643 ungetc (c, f);
644 break;
647 g_string_append_c (p, c);
649 if (r != 0)
650 *line = g_string_free (p, FALSE);
651 else
652 g_string_free (p, TRUE);
654 return r;
657 /* --------------------------------------------------------------------------------------------- */
659 static char *
660 convert (char *s)
662 char *r, *p;
664 p = r = s;
665 while (*s)
667 switch (*s)
669 case '\\':
670 s++;
671 switch (*s)
673 case ' ':
674 *p = ' ';
675 s--;
676 break;
677 case 'n':
678 *p = '\n';
679 break;
680 case 'r':
681 *p = '\r';
682 break;
683 case 't':
684 *p = '\t';
685 break;
686 case 's':
687 *p = ' ';
688 break;
689 case '*':
690 *p = '*';
691 break;
692 case '\\':
693 *p = '\\';
694 break;
695 case '[':
696 case ']':
697 *p = SYNTAX_TOKEN_BRACKET;
698 break;
699 case '{':
700 case '}':
701 *p = SYNTAX_TOKEN_BRACE;
702 break;
703 case 0:
704 *p = *s;
705 return r;
706 default:
707 *p = *s;
708 break;
710 break;
711 case '*':
712 *p = SYNTAX_TOKEN_STAR;
713 break;
714 case '+':
715 *p = SYNTAX_TOKEN_PLUS;
716 break;
717 default:
718 *p = *s;
719 break;
721 s++;
722 p++;
724 *p = '\0';
725 return r;
728 /* --------------------------------------------------------------------------------------------- */
730 static int
731 get_args (char *l, char **args, int args_size)
733 int argc = 0;
735 while (argc < args_size)
737 char *p = l;
738 while (*p != '\0' && whiteness (*p))
739 p++;
740 if (*p == '\0')
741 break;
742 for (l = p + 1; *l != '\0' && !whiteness (*l); l++)
744 if (*l != '\0')
745 *l++ = '\0';
746 args[argc++] = convert (p);
748 args[argc] = (char *) NULL;
749 return argc;
752 /* --------------------------------------------------------------------------------------------- */
754 static int
755 this_try_alloc_color_pair (const char *fg, const char *bg, const char *attrs)
757 char f[80], b[80], a[80], *p;
759 if (bg != NULL && *bg == '\0')
760 bg = NULL;
761 if (fg != NULL && *fg == '\0')
762 fg = NULL;
763 if (attrs != NULL && *attrs == '\0')
764 attrs = NULL;
766 if ((fg == NULL) && (bg == NULL))
767 return EDITOR_NORMAL_COLOR;
769 if (fg != NULL)
771 g_strlcpy (f, fg, sizeof (f));
772 p = strchr (f, '/');
773 if (p != NULL)
774 *p = '\0';
775 fg = f;
777 if (bg != NULL)
779 g_strlcpy (b, bg, sizeof (b));
780 p = strchr (b, '/');
781 if (p != NULL)
782 *p = '\0';
783 bg = b;
785 if ((fg == NULL) || (bg == NULL))
787 /* get colors from skin */
788 char *editnormal;
790 editnormal = mc_skin_get ("editor", "_default_", "default;default");
792 if (fg == NULL)
794 g_strlcpy (f, editnormal, sizeof (f));
795 p = strchr (f, ';');
796 if (p != NULL)
797 *p = '\0';
798 if (f[0] == '\0')
799 g_strlcpy (f, "default", sizeof (f));
800 fg = f;
802 if (bg == NULL)
804 p = strchr (editnormal, ';');
805 if ((p != NULL) && (*(++p) != '\0'))
806 g_strlcpy (b, p, sizeof (b));
807 else
808 g_strlcpy (b, "default", sizeof (b));
809 bg = b;
812 g_free (editnormal);
815 if (attrs != NULL)
817 g_strlcpy (a, attrs, sizeof (a));
818 p = strchr (a, '/');
819 if (p != NULL)
820 *p = '\0';
821 /* get_args() mangles the + signs, unmangle 'em */
822 p = a;
823 while ((p = strchr (p, SYNTAX_TOKEN_PLUS)) != NULL)
824 *p++ = '+';
825 attrs = a;
827 return tty_try_alloc_color_pair (fg, bg, attrs);
830 /* --------------------------------------------------------------------------------------------- */
832 static FILE *
833 open_include_file (const char *filename)
835 FILE *f;
837 MC_PTR_FREE (error_file_name);
838 error_file_name = g_strdup (filename);
839 if (g_path_is_absolute (filename))
840 return fopen (filename, "r");
842 g_free (error_file_name);
843 error_file_name =
844 g_build_filename (mc_config_get_data_path (), EDIT_DIR, filename, (char *) NULL);
845 f = fopen (error_file_name, "r");
846 if (f != NULL)
847 return f;
849 g_free (error_file_name);
850 error_file_name = g_build_filename (mc_global.sysconfig_dir, "syntax", filename, (char *) NULL);
851 f = fopen (error_file_name, "r");
852 if (f != NULL)
853 return f;
855 g_free (error_file_name);
856 error_file_name =
857 g_build_filename (mc_global.share_data_dir, "syntax", filename, (char *) NULL);
859 return fopen (error_file_name, "r");
862 /* --------------------------------------------------------------------------------------------- */
864 inline static void
865 xx_lowerize_line (WEdit * edit, char *line, size_t len)
867 if (edit->is_case_insensitive)
869 size_t i;
870 for (i = 0; i < len; ++i)
871 line[i] = tolower (line[i]);
875 /* --------------------------------------------------------------------------------------------- */
876 /** returns line number on error */
878 static int
879 edit_read_syntax_rules (WEdit * edit, FILE * f, char **args, int args_size)
881 FILE *g = NULL;
882 char *fg, *bg, *attrs;
883 char last_fg[32] = "", last_bg[32] = "", last_attrs[64] = "";
884 char whole_right[512];
885 char whole_left[512];
886 char *l = 0;
887 int save_line = 0, line = 0;
888 struct context_rule **r, *c = NULL;
889 int num_words = -1, num_contexts = -1;
890 int result = 0;
891 int alloc_contexts = MAX_CONTEXTS,
892 alloc_words_per_context = MAX_WORDS_PER_CONTEXT,
893 max_alloc_words_per_context = MAX_WORDS_PER_CONTEXT;
895 args[0] = NULL;
896 edit->is_case_insensitive = FALSE;
898 strcpy (whole_left, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_01234567890");
899 strcpy (whole_right, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_01234567890");
901 r = edit->rules = g_malloc0 (alloc_contexts * sizeof (struct context_rule *));
903 if (!edit->defines)
904 edit->defines = g_tree_new ((GCompareFunc) strcmp);
906 while (TRUE)
908 char **a;
909 size_t len;
910 int argc;
912 line++;
913 l = 0;
915 len = read_one_line (&l, f);
916 if (len != 0)
917 xx_lowerize_line (edit, l, len);
918 else
920 if (g == NULL)
921 break;
923 fclose (f);
924 f = g;
925 g = NULL;
926 line = save_line + 1;
927 MC_PTR_FREE (error_file_name);
928 MC_PTR_FREE (l);
929 len = read_one_line (&l, f);
930 if (len == 0)
931 break;
932 xx_lowerize_line (edit, l, len);
935 argc = get_args (l, args, args_size);
936 a = args + 1;
937 if (args[0] == NULL)
939 /* do nothing */
941 else if (strcmp (args[0], "include") == 0)
943 if (g != NULL || argc != 2)
945 result = line;
946 break;
948 g = f;
949 f = open_include_file (args[1]);
950 if (f == NULL)
952 MC_PTR_FREE (error_file_name);
953 result = line;
954 break;
956 save_line = line;
957 line = 0;
959 else if (strcmp (args[0], "caseinsensitive") == 0)
961 edit->is_case_insensitive = TRUE;
963 else if (strcmp (args[0], "wholechars") == 0)
965 check_a;
966 if (strcmp (*a, "left") == 0)
968 a++;
969 g_strlcpy (whole_left, *a, sizeof (whole_left));
971 else if (strcmp (*a, "right") == 0)
973 a++;
974 g_strlcpy (whole_right, *a, sizeof (whole_right));
976 else
978 g_strlcpy (whole_left, *a, sizeof (whole_left));
979 g_strlcpy (whole_right, *a, sizeof (whole_right));
981 a++;
982 check_not_a;
984 else if (strcmp (args[0], "context") == 0)
986 check_a;
987 if (num_contexts == -1)
989 if (strcmp (*a, "default") != 0)
990 { /* first context is the default */
991 break_a;
993 a++;
994 c = r[0] = g_malloc0 (sizeof (struct context_rule));
995 c->left = g_strdup (" ");
996 c->right = g_strdup (" ");
997 num_contexts = 0;
999 else
1001 /* Terminate previous context. */
1002 r[num_contexts - 1]->keyword[num_words] = NULL;
1003 c = r[num_contexts] = g_malloc0 (sizeof (struct context_rule));
1004 if (strcmp (*a, "exclusive") == 0)
1006 a++;
1007 c->between_delimiters = 1;
1009 check_a;
1010 if (strcmp (*a, "whole") == 0)
1012 a++;
1013 c->whole_word_chars_left = g_strdup (whole_left);
1014 c->whole_word_chars_right = g_strdup (whole_right);
1016 else if (strcmp (*a, "wholeleft") == 0)
1018 a++;
1019 c->whole_word_chars_left = g_strdup (whole_left);
1021 else if (strcmp (*a, "wholeright") == 0)
1023 a++;
1024 c->whole_word_chars_right = g_strdup (whole_right);
1026 check_a;
1027 if (strcmp (*a, "linestart") == 0)
1029 a++;
1030 c->line_start_left = 1;
1032 check_a;
1033 c->left = g_strdup (*a++);
1034 check_a;
1035 if (strcmp (*a, "linestart") == 0)
1037 a++;
1038 c->line_start_right = 1;
1040 check_a;
1041 c->right = g_strdup (*a++);
1042 c->first_left = *c->left;
1043 c->first_right = *c->right;
1045 c->keyword = g_malloc (alloc_words_per_context * sizeof (struct key_word *));
1046 num_words = 1;
1047 c->keyword[0] = g_malloc0 (sizeof (struct key_word));
1048 subst_defines (edit->defines, a, &args[1024]);
1049 fg = *a;
1050 if (*a != '\0')
1051 a++;
1052 bg = *a;
1053 if (*a != '\0')
1054 a++;
1055 attrs = *a;
1056 if (*a != '\0')
1057 a++;
1058 g_strlcpy (last_fg, fg != NULL ? fg : "", sizeof (last_fg));
1059 g_strlcpy (last_bg, bg != NULL ? bg : "", sizeof (last_bg));
1060 g_strlcpy (last_attrs, attrs != NULL ? attrs : "", sizeof (last_attrs));
1061 c->keyword[0]->color = this_try_alloc_color_pair (fg, bg, attrs);
1062 c->keyword[0]->keyword = g_strdup (" ");
1063 check_not_a;
1065 alloc_words_per_context = MAX_WORDS_PER_CONTEXT;
1066 if (++num_contexts >= alloc_contexts)
1068 struct context_rule **tmp;
1070 alloc_contexts += 128;
1071 tmp = g_realloc (r, alloc_contexts * sizeof (struct context_rule *));
1072 r = tmp;
1075 else if (strcmp (args[0], "spellcheck") == 0)
1077 if (c == NULL)
1079 result = line;
1080 break;
1082 c->spelling = TRUE;
1084 else if (strcmp (args[0], "keyword") == 0)
1086 struct key_word *k;
1088 if (num_words == -1)
1089 break_a;
1090 check_a;
1091 k = r[num_contexts - 1]->keyword[num_words] = g_malloc0 (sizeof (struct key_word));
1092 if (strcmp (*a, "whole") == 0)
1094 a++;
1095 k->whole_word_chars_left = g_strdup (whole_left);
1096 k->whole_word_chars_right = g_strdup (whole_right);
1098 else if (strcmp (*a, "wholeleft") == 0)
1100 a++;
1101 k->whole_word_chars_left = g_strdup (whole_left);
1103 else if (strcmp (*a, "wholeright") == 0)
1105 a++;
1106 k->whole_word_chars_right = g_strdup (whole_right);
1108 check_a;
1109 if (strcmp (*a, "linestart") == 0)
1111 a++;
1112 k->line_start = 1;
1114 check_a;
1115 if (strcmp (*a, "whole") == 0)
1117 break_a;
1119 k->keyword = g_strdup (*a++);
1120 k->first = *k->keyword;
1121 subst_defines (edit->defines, a, &args[1024]);
1122 fg = *a;
1123 if (*a != '\0')
1124 a++;
1125 bg = *a;
1126 if (*a != '\0')
1127 a++;
1128 attrs = *a;
1129 if (*a != '\0')
1130 a++;
1131 if (fg == NULL)
1132 fg = last_fg;
1133 if (bg == NULL)
1134 bg = last_bg;
1135 if (attrs == NULL)
1136 attrs = last_attrs;
1137 k->color = this_try_alloc_color_pair (fg, bg, attrs);
1138 check_not_a;
1140 if (++num_words >= alloc_words_per_context)
1142 struct key_word **tmp;
1144 alloc_words_per_context += 1024;
1146 if (alloc_words_per_context > max_alloc_words_per_context)
1147 max_alloc_words_per_context = alloc_words_per_context;
1149 tmp = g_realloc (c->keyword, alloc_words_per_context * sizeof (struct key_word *));
1150 c->keyword = tmp;
1153 else if (*(args[0]) == '#')
1155 /* do nothing for comment */
1157 else if (strcmp (args[0], "file") == 0)
1159 break;
1161 else if (strcmp (args[0], "define") == 0)
1163 char *key = *a++;
1164 char **argv;
1166 if (argc < 3)
1167 break_a;
1168 argv = g_tree_lookup (edit->defines, key);
1169 if (argv != NULL)
1170 mc_defines_destroy (NULL, argv, NULL);
1171 else
1172 key = g_strdup (key);
1174 argv = g_new (char *, argc - 1);
1175 g_tree_insert (edit->defines, key, argv);
1176 while (*a != NULL)
1177 *argv++ = g_strdup (*a++);
1178 *argv = NULL;
1180 else
1181 { /* anything else is an error */
1182 break_a;
1184 free_args (args);
1185 MC_PTR_FREE (l);
1187 free_args (args);
1188 MC_PTR_FREE (l);
1190 /* Terminate context array. */
1191 if (num_contexts > 0)
1193 r[num_contexts - 1]->keyword[num_words] = NULL;
1194 r[num_contexts] = NULL;
1197 if (edit->rules[0] == NULL)
1198 MC_PTR_FREE (edit->rules);
1200 if (result == 0)
1202 int i;
1203 char *first_chars;
1205 if (num_contexts == -1)
1206 return line;
1208 first_chars = g_malloc0 (max_alloc_words_per_context + 2);
1210 for (i = 0; edit->rules[i] != NULL; i++)
1212 char *p;
1213 int j;
1215 c = edit->rules[i];
1216 p = first_chars;
1217 *p++ = (char) 1;
1218 for (j = 1; c->keyword[j] != NULL; j++)
1219 *p++ = c->keyword[j]->first;
1220 *p = '\0';
1221 c->keyword_first_chars = g_strdup (first_chars);
1224 g_free (first_chars);
1227 return result;
1230 /* --------------------------------------------------------------------------------------------- */
1232 /* returns -1 on file error, line number on error in file syntax */
1233 static int
1234 edit_read_syntax_file (WEdit * edit, char ***pnames, const char *syntax_file,
1235 const char *editor_file, const char *first_line, const char *type)
1237 #define NENTRIES 30
1238 FILE *f, *g = NULL;
1239 char *args[1024], *l = NULL;
1240 long line = 0;
1241 int result = 0;
1242 int count = 0;
1243 char *lib_file;
1244 gboolean found = FALSE;
1245 char **tmpnames = NULL;
1247 f = fopen (syntax_file, "r");
1248 if (f == NULL)
1250 lib_file = g_build_filename (mc_global.share_data_dir, "syntax", "Syntax", (char *) NULL);
1251 f = fopen (lib_file, "r");
1252 g_free (lib_file);
1253 if (f == NULL)
1254 return -1;
1257 args[0] = NULL;
1258 while (TRUE)
1260 line++;
1261 MC_PTR_FREE (l);
1262 if (read_one_line (&l, f) == 0)
1263 break;
1264 (void) get_args (l, args, 1023); /* Final NULL */
1265 if (args[0] == NULL)
1266 continue;
1268 /* Looking for 'include ...' lines before first 'file ...' ones */
1269 if (!found && strcmp (args[0], "include") == 0)
1271 if (g != NULL)
1272 continue;
1274 if (!args[1] || !(g = open_include_file (args[1])))
1276 result = line;
1277 break;
1279 goto found_type;
1282 /* looking for 'file ...' lines only */
1283 if (strcmp (args[0], "file") != 0)
1284 continue;
1286 found = TRUE;
1288 /* must have two args or report error */
1289 if (!args[1] || !args[2])
1291 result = line;
1292 break;
1294 if (pnames && *pnames)
1296 /* 1: just collecting a list of names of rule sets */
1297 /* Reallocate the list if required */
1298 if (count % NENTRIES == 0)
1300 tmpnames =
1301 (char **) g_try_realloc (*pnames, (count + NENTRIES + 1) * sizeof (char *));
1302 if (tmpnames == NULL)
1303 break;
1304 *pnames = tmpnames;
1306 (*pnames)[count++] = g_strdup (args[2]);
1307 (*pnames)[count] = NULL;
1309 else if (type)
1311 /* 2: rule set was explicitly specified by the caller */
1312 if (strcmp (type, args[2]) == 0)
1313 goto found_type;
1315 else if (editor_file && edit)
1317 /* 3: auto-detect rule set from regular expressions */
1318 int q;
1320 q = mc_search (args[1], DEFAULT_CHARSET, editor_file, MC_SEARCH_T_REGEX);
1321 /* does filename match arg 1 ? */
1322 if (!q && args[3])
1324 /* does first line match arg 3 ? */
1325 q = mc_search (args[3], DEFAULT_CHARSET, first_line, MC_SEARCH_T_REGEX);
1327 if (q)
1329 int line_error;
1330 char *syntax_type;
1331 found_type:
1332 syntax_type = args[2];
1333 line_error = edit_read_syntax_rules (edit, g ? g : f, args, 1023);
1334 if (line_error)
1336 if (!error_file_name) /* an included file */
1337 result = line + line_error;
1338 else
1339 result = line_error;
1341 else
1343 g_free (edit->syntax_type);
1344 edit->syntax_type = g_strdup (syntax_type);
1345 /* if there are no rules then turn off syntax highlighting for speed */
1346 if (!g && !edit->rules[1])
1347 if (!edit->rules[0]->keyword[1] && !edit->rules[0]->spelling)
1349 edit_free_syntax_rules (edit);
1350 break;
1354 if (g == NULL)
1355 break;
1357 fclose (g);
1358 g = NULL;
1362 g_free (l);
1363 fclose (f);
1364 return result;
1367 /* --------------------------------------------------------------------------------------------- */
1369 static const char *
1370 get_first_editor_line (WEdit * edit)
1372 static char s[256];
1374 s[0] = '\0';
1376 if (edit != NULL)
1378 size_t i;
1380 for (i = 0; i < sizeof (s) - 1; i++)
1382 s[i] = edit_buffer_get_byte (&edit->buffer, i);
1383 if (s[i] == '\n')
1385 s[i] = '\0';
1386 break;
1390 s[sizeof (s) - 1] = '\0';
1393 return s;
1396 /* --------------------------------------------------------------------------------------------- */
1397 /*** public functions ****************************************************************************/
1398 /* --------------------------------------------------------------------------------------------- */
1401 edit_get_syntax_color (WEdit * edit, off_t byte_index)
1403 if (!tty_use_colors ())
1404 return 0;
1406 if (edit->rules != NULL && byte_index < edit->buffer.size && option_syntax_highlighting)
1408 edit_get_rule (edit, byte_index);
1409 return translate_rule_to_color (edit, &edit->rule);
1412 return EDITOR_NORMAL_COLOR;
1415 /* --------------------------------------------------------------------------------------------- */
1417 void
1418 edit_free_syntax_rules (WEdit * edit)
1420 size_t i, j;
1422 if (!edit)
1423 return;
1424 if (edit->defines)
1425 destroy_defines (&edit->defines);
1426 if (!edit->rules)
1427 return;
1429 edit_get_rule (edit, -1);
1430 MC_PTR_FREE (edit->syntax_type);
1432 for (i = 0; edit->rules[i]; i++)
1434 if (edit->rules[i]->keyword)
1436 for (j = 0; edit->rules[i]->keyword[j]; j++)
1438 MC_PTR_FREE (edit->rules[i]->keyword[j]->keyword);
1439 MC_PTR_FREE (edit->rules[i]->keyword[j]->whole_word_chars_left);
1440 MC_PTR_FREE (edit->rules[i]->keyword[j]->whole_word_chars_right);
1441 MC_PTR_FREE (edit->rules[i]->keyword[j]);
1444 MC_PTR_FREE (edit->rules[i]->left);
1445 MC_PTR_FREE (edit->rules[i]->right);
1446 MC_PTR_FREE (edit->rules[i]->whole_word_chars_left);
1447 MC_PTR_FREE (edit->rules[i]->whole_word_chars_right);
1448 MC_PTR_FREE (edit->rules[i]->keyword);
1449 MC_PTR_FREE (edit->rules[i]->keyword_first_chars);
1450 MC_PTR_FREE (edit->rules[i]);
1453 g_slist_foreach (edit->syntax_marker, (GFunc) g_free, NULL);
1454 g_slist_free (edit->syntax_marker);
1455 edit->syntax_marker = NULL;
1456 MC_PTR_FREE (edit->rules);
1457 tty_color_free_all_tmp ();
1460 /* --------------------------------------------------------------------------------------------- */
1462 * Load rules into edit struct. Either edit or *pnames must be NULL. If
1463 * edit is NULL, a list of types will be stored into names. If type is
1464 * NULL, then the type will be selected according to the filename.
1465 * type must be edit->syntax_type or NULL
1467 void
1468 edit_load_syntax (WEdit * edit, char ***pnames, const char *type)
1470 int r;
1471 char *f = NULL;
1473 if (option_auto_syntax)
1474 type = NULL;
1476 if (edit != NULL)
1478 char *saved_type;
1480 saved_type = g_strdup (type); /* save edit->syntax_type */
1481 edit_free_syntax_rules (edit);
1482 edit->syntax_type = saved_type; /* restore edit->syntax_type */
1485 if (!tty_use_colors ())
1486 return;
1488 if (!option_syntax_highlighting && (!pnames || !*pnames))
1489 return;
1491 if (edit != NULL && edit->filename_vpath == NULL)
1492 return;
1494 f = mc_config_get_full_path (EDIT_SYNTAX_FILE);
1495 if (edit != NULL)
1496 r = edit_read_syntax_file (edit, pnames, f, vfs_path_as_str (edit->filename_vpath),
1497 get_first_editor_line (edit),
1498 option_auto_syntax ? NULL : edit->syntax_type);
1499 else
1500 r = edit_read_syntax_file (NULL, pnames, f, NULL, "", NULL);
1501 if (r == -1)
1503 edit_free_syntax_rules (edit);
1504 message (D_ERROR, _("Load syntax file"),
1505 _("Cannot open file %s\n%s"), f, unix_error_string (errno));
1507 else if (r != 0)
1509 edit_free_syntax_rules (edit);
1510 message (D_ERROR, _("Load syntax file"),
1511 _("Error in file %s on line %d"), error_file_name ? error_file_name : f, r);
1512 MC_PTR_FREE (error_file_name);
1515 g_free (f);
1518 /* --------------------------------------------------------------------------------------------- */
1520 const char *
1521 edit_get_syntax_type (const WEdit * edit)
1523 return edit->syntax_type;
1526 /* --------------------------------------------------------------------------------------------- */