Merge branch '3753_extfs_tester_access_configure_parameters'
[midnight-commander.git] / src / editor / syntax.c
blob96f9ca440964d6cf160abeb4894efb7b6c27a75f
1 /*
2 Editor syntax highlighting.
4 Copyright (C) 1996-2016
5 Free Software Foundation, Inc.
7 Written by:
8 Paul Sheer, 1998
9 Egmont Koblinger <egmont@gmail.com>, 2010
10 Slava Zanko <slavazanko@gmail.com>, 2013
11 Andrew Borodin <aborodin@vmail.ru>, 2013, 2014
13 This file is part of the Midnight Commander.
15 The Midnight Commander is free software: you can redistribute it
16 and/or modify it under the terms of the GNU General Public License as
17 published by the Free Software Foundation, either version 3 of the License,
18 or (at your option) any later version.
20 The Midnight Commander is distributed in the hope that it will be useful,
21 but WITHOUT ANY WARRANTY; without even the implied warranty of
22 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 GNU General Public License for more details.
25 You should have received a copy of the GNU General Public License
26 along with this program. If not, see <http://www.gnu.org/licenses/>.
29 /** \file
30 * \brief Source: editor syntax highlighting
31 * \author Paul Sheer
32 * \date 1996, 1997
33 * \author Mikhail Pobolovets
34 * \date 2010
36 * Mispelled words are flushed from the syntax highlighting rules
37 * when they have been around longer than
38 * TRANSIENT_WORD_TIME_OUT seconds. At a cursor rate of 30
39 * chars per second and say 3 chars + a space per word, we can
40 * accumulate 450 words absolute max with a value of 60. This is
41 * below this limit of 1024 words in a context.
44 #include <config.h>
46 #include <stdio.h>
47 #include <stdarg.h>
48 #include <sys/types.h>
49 #include <unistd.h>
50 #include <string.h>
51 #include <ctype.h>
52 #include <errno.h>
53 #include <sys/stat.h>
54 #include <stdlib.h>
56 #include "lib/global.h"
57 #include "lib/search.h" /* search engine */
58 #include "lib/skin.h"
59 #include "lib/fileloc.h" /* EDIT_DIR, EDIT_SYNTAX_FILE */
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 RULE_ON_LEFT_BORDER 1
78 #define RULE_ON_RIGHT_BORDER 2
80 #define SYNTAX_TOKEN_STAR '\001'
81 #define SYNTAX_TOKEN_PLUS '\002'
82 #define SYNTAX_TOKEN_BRACKET '\003'
83 #define SYNTAX_TOKEN_BRACE '\004'
85 #define whiteness(x) ((x) == '\t' || (x) == '\n' || (x) == ' ')
87 #define free_args(x)
88 #define break_a {result=line;break;}
89 #define check_a {if(!*a){result=line;break;}}
90 #define check_not_a {if(*a){result=line;break;}}
92 #define SYNTAX_KEYWORD(x) ((syntax_keyword_t *) (x))
93 #define CONTEXT_RULE(x) ((context_rule_t *) (x))
95 /*** file scope type declarations ****************************************************************/
97 typedef struct
99 char *keyword;
100 char *whole_word_chars_left;
101 char *whole_word_chars_right;
102 long line_start;
103 int color;
104 } syntax_keyword_t;
106 typedef struct
108 char *left;
109 unsigned char first_left;
110 char *right;
111 unsigned char first_right;
112 char line_start_left;
113 char line_start_right;
114 int between_delimiters;
115 char *whole_word_chars_left;
116 char *whole_word_chars_right;
117 char *keyword_first_chars;
118 gboolean spelling;
119 /* first word is word[1] */
120 GPtrArray *keyword;
121 } context_rule_t;
123 typedef struct
125 off_t offset;
126 edit_syntax_rule_t rule;
127 } syntax_marker_t;
129 /*** file scope variables ************************************************************************/
131 static char *error_file_name = NULL;
133 /* --------------------------------------------------------------------------------------------- */
134 /*** file scope functions ************************************************************************/
135 /* --------------------------------------------------------------------------------------------- */
137 static void
138 syntax_keyword_free (gpointer keyword)
140 syntax_keyword_t *k = SYNTAX_KEYWORD (keyword);
142 g_free (k->keyword);
143 g_free (k->whole_word_chars_left);
144 g_free (k->whole_word_chars_right);
145 g_free (k);
148 /* --------------------------------------------------------------------------------------------- */
150 static void
151 context_rule_free (gpointer rule)
153 context_rule_t *r = CONTEXT_RULE (rule);
155 g_free (r->left);
156 g_free (r->right);
157 g_free (r->whole_word_chars_left);
158 g_free (r->whole_word_chars_right);
159 g_free (r->keyword_first_chars);
161 if (r->keyword != NULL)
163 g_ptr_array_foreach (r->keyword, (GFunc) syntax_keyword_free, NULL);
164 g_ptr_array_free (r->keyword, TRUE);
167 g_free (r);
170 /* --------------------------------------------------------------------------------------------- */
172 static gint
173 mc_defines_destroy (gpointer key, gpointer value, gpointer data)
175 (void) data;
177 g_free (key);
178 g_strfreev ((char **) value);
180 return FALSE;
183 /* --------------------------------------------------------------------------------------------- */
184 /** Completely destroys the defines tree */
186 static void
187 destroy_defines (GTree ** defines)
189 g_tree_foreach (*defines, mc_defines_destroy, NULL);
190 g_tree_destroy (*defines);
191 *defines = NULL;
194 /* --------------------------------------------------------------------------------------------- */
196 /** Wrapper for case insensitive mode */
197 inline static int
198 xx_tolower (const WEdit * edit, int c)
200 return edit->is_case_insensitive ? tolower (c) : c;
203 /* --------------------------------------------------------------------------------------------- */
205 static void
206 subst_defines (GTree * defines, char **argv, char **argv_end)
208 char **t, **p;
209 int argc;
211 while (*argv != NULL && argv < argv_end)
213 t = g_tree_lookup (defines, *argv);
214 if (t != NULL)
216 int count = 0;
218 /* Count argv array members */
219 argc = 0;
220 for (p = &argv[1]; *p != NULL; p++)
221 argc++;
223 /* Count members of definition array */
224 for (p = t; *p != NULL; p++)
225 count++;
226 p = &argv[count + argc];
228 /* Buffer overflow or infinitive loop in define */
229 if (p >= argv_end)
230 break;
232 /* Move rest of argv after definition members */
233 while (argc >= 0)
234 *p-- = argv[argc-- + 1];
236 /* Copy definition members to argv */
237 for (p = argv; *t != NULL; *p++ = *t++)
240 argv++;
244 /* --------------------------------------------------------------------------------------------- */
246 static off_t
247 compare_word_to_right (const WEdit * edit, off_t i, const char *text,
248 const char *whole_left, const char *whole_right, long line_start)
250 const unsigned char *p, *q;
251 int c, d, j;
253 if (*text == '\0')
254 return -1;
256 c = xx_tolower (edit, edit_buffer_get_byte (&edit->buffer, i - 1));
257 if ((line_start != 0 && c != '\n') || (whole_left != NULL && strchr (whole_left, c) != NULL))
258 return -1;
260 for (p = (const unsigned char *) text, q = p + strlen ((const char *) p); p < q; p++, i++)
262 switch (*p)
264 case SYNTAX_TOKEN_STAR:
265 if (++p > q)
266 return -1;
267 while (TRUE)
269 c = xx_tolower (edit, edit_buffer_get_byte (&edit->buffer, i));
270 if (*p == '\0' && whole_right != NULL && strchr (whole_right, c) == NULL)
271 break;
272 if (c == *p)
273 break;
274 if (c == '\n')
275 return -1;
276 i++;
278 break;
279 case SYNTAX_TOKEN_PLUS:
280 if (++p > q)
281 return -1;
282 j = 0;
283 while (TRUE)
285 c = xx_tolower (edit, edit_buffer_get_byte (&edit->buffer, i));
286 if (c == *p)
288 j = i;
289 if (*p == *text && p[1] == '\0') /* handle eg '+' and @+@ keywords properly */
290 break;
292 if (j != 0 && strchr ((const char *) p + 1, c) != NULL) /* c exists further down, so it will get matched later */
293 break;
294 if (c == '\n' || c == '\t' || c == ' ' ||
295 (whole_right != NULL && strchr (whole_right, c) == NULL))
297 if (*p == '\0')
299 i--;
300 break;
302 if (j == 0)
303 return -1;
304 i = j;
305 break;
307 i++;
309 break;
310 case SYNTAX_TOKEN_BRACKET:
311 if (++p > q)
312 return -1;
313 c = -1;
314 while (TRUE)
316 d = c;
317 c = xx_tolower (edit, edit_buffer_get_byte (&edit->buffer, i));
318 for (j = 0; p[j] != SYNTAX_TOKEN_BRACKET && p[j]; j++)
319 if (c == p[j])
320 goto found_char2;
321 break;
322 found_char2:
323 i++;
325 i--;
326 while (*p != SYNTAX_TOKEN_BRACKET && p <= q)
327 p++;
328 if (p > q)
329 return -1;
330 if (p[1] == d)
331 i--;
332 break;
333 case SYNTAX_TOKEN_BRACE:
334 if (++p > q)
335 return -1;
336 c = xx_tolower (edit, edit_buffer_get_byte (&edit->buffer, i));
337 for (; *p != SYNTAX_TOKEN_BRACE && *p; p++)
338 if (c == *p)
339 goto found_char3;
340 return -1;
341 found_char3:
342 while (*p != SYNTAX_TOKEN_BRACE && p < q)
343 p++;
344 break;
345 default:
346 if (*p != xx_tolower (edit, edit_buffer_get_byte (&edit->buffer, i)))
347 return -1;
350 return (whole_right != NULL &&
351 strchr (whole_right,
352 xx_tolower (edit, edit_buffer_get_byte (&edit->buffer, i))) != NULL) ? -1 : i;
355 /* --------------------------------------------------------------------------------------------- */
357 static const char *
358 xx_strchr (const WEdit * edit, const unsigned char *s, int char_byte)
360 while (*s >= '\005' && xx_tolower (edit, *s) != char_byte)
361 s++;
363 return (const char *) s;
366 /* --------------------------------------------------------------------------------------------- */
368 static void
369 apply_rules_going_right (WEdit * edit, off_t i)
371 context_rule_t *r;
372 int c;
373 gboolean contextchanged = FALSE;
374 gboolean found_left = FALSE, found_right = FALSE;
375 gboolean keyword_foundleft = FALSE, keyword_foundright = FALSE;
376 gboolean is_end;
377 off_t end = 0;
378 edit_syntax_rule_t _rule = edit->rule;
380 c = xx_tolower (edit, edit_buffer_get_byte (&edit->buffer, i));
381 if (c == 0)
382 return;
384 is_end = (edit->rule.end == i);
386 /* check to turn off a keyword */
387 if (_rule.keyword != 0)
389 if (edit_buffer_get_byte (&edit->buffer, i - 1) == '\n')
390 _rule.keyword = 0;
391 if (is_end)
393 _rule.keyword = 0;
394 keyword_foundleft = TRUE;
398 /* check to turn off a context */
399 if (_rule.context != 0 && _rule.keyword == 0)
401 off_t e;
403 r = CONTEXT_RULE (g_ptr_array_index (edit->rules, _rule.context));
404 if (r->first_right == c && (edit->rule.border & RULE_ON_RIGHT_BORDER) == 0
405 && (e =
406 compare_word_to_right (edit, i, r->right, r->whole_word_chars_left,
407 r->whole_word_chars_right, r->line_start_right)) > 0)
409 _rule.end = e;
410 found_right = TRUE;
411 _rule.border = RULE_ON_RIGHT_BORDER;
412 if (r->between_delimiters)
413 _rule.context = 0;
415 else if (is_end && (edit->rule.border & RULE_ON_RIGHT_BORDER) != 0)
417 /* always turn off a context at 4 */
418 found_left = TRUE;
419 _rule.border = 0;
420 if (!keyword_foundleft)
421 _rule.context = 0;
423 else if (is_end && (edit->rule.border & RULE_ON_LEFT_BORDER) != 0)
425 /* never turn off a context at 2 */
426 found_left = TRUE;
427 _rule.border = 0;
431 /* check to turn on a keyword */
432 if (_rule.keyword == 0)
434 const char *p;
436 r = CONTEXT_RULE (g_ptr_array_index (edit->rules, _rule.context));
437 p = r->keyword_first_chars;
439 if (p != NULL)
440 while (*(p = xx_strchr (edit, (const unsigned char *) p + 1, c)) != '\0')
442 syntax_keyword_t *k;
443 int count;
444 off_t e;
446 count = p - r->keyword_first_chars;
447 k = SYNTAX_KEYWORD (g_ptr_array_index (r->keyword, count));
448 e = compare_word_to_right (edit, i, k->keyword, k->whole_word_chars_left,
449 k->whole_word_chars_right, k->line_start);
450 if (e > 0)
452 end = e;
453 _rule.end = e;
454 _rule.keyword = count;
455 keyword_foundright = TRUE;
456 break;
461 /* check to turn on a context */
462 if (_rule.context == 0)
464 if (!found_left && is_end)
466 if ((edit->rule.border & RULE_ON_RIGHT_BORDER) != 0)
468 _rule.border = 0;
469 _rule.context = 0;
470 contextchanged = TRUE;
471 _rule.keyword = 0;
474 else if ((edit->rule.border & RULE_ON_LEFT_BORDER) != 0)
476 r = CONTEXT_RULE (g_ptr_array_index (edit->rules, _rule._context));
477 _rule.border = 0;
478 if (r->between_delimiters)
480 _rule.context = _rule._context;
481 contextchanged = TRUE;
482 _rule.keyword = 0;
484 if (r->first_right == c)
486 off_t e;
488 e = compare_word_to_right (edit, i, r->right, r->whole_word_chars_left,
489 r->whole_word_chars_right, r->line_start_right);
490 if (e >= end)
492 _rule.end = e;
493 found_right = TRUE;
494 _rule.border = RULE_ON_RIGHT_BORDER;
495 _rule.context = 0;
502 if (!found_right)
504 size_t count;
506 for (count = 1; count < edit->rules->len; count++)
508 r = CONTEXT_RULE (g_ptr_array_index (edit->rules, count));
509 if (r->first_left == c)
511 off_t e;
513 e = compare_word_to_right (edit, i, r->left, r->whole_word_chars_left,
514 r->whole_word_chars_right, r->line_start_left);
515 if (e >= end && (_rule.keyword == 0 || keyword_foundright))
517 _rule.end = e;
518 found_right = TRUE;
519 _rule.border = RULE_ON_LEFT_BORDER;
520 _rule._context = count;
521 if (!r->between_delimiters && _rule.keyword == 0)
523 _rule.context = count;
524 contextchanged = TRUE;
526 break;
533 /* check again to turn on a keyword if the context switched */
534 if (contextchanged && _rule.keyword == 0)
536 const char *p;
538 r = CONTEXT_RULE (g_ptr_array_index (edit->rules, _rule.context));
539 p = r->keyword_first_chars;
541 while (*(p = xx_strchr (edit, (const unsigned char *) p + 1, c)) != '\0')
543 syntax_keyword_t *k;
544 int count;
545 off_t e;
547 count = p - r->keyword_first_chars;
548 k = SYNTAX_KEYWORD (g_ptr_array_index (r->keyword, count));
549 e = compare_word_to_right (edit, i, k->keyword, k->whole_word_chars_left,
550 k->whole_word_chars_right, k->line_start);
551 if (e > 0)
553 _rule.end = e;
554 _rule.keyword = count;
555 break;
560 edit->rule = _rule;
563 /* --------------------------------------------------------------------------------------------- */
565 static void
566 edit_get_rule (WEdit * edit, off_t byte_index)
568 off_t i;
570 if (byte_index > edit->last_get_rule)
572 for (i = edit->last_get_rule + 1; i <= byte_index; i++)
574 off_t d = SYNTAX_MARKER_DENSITY;
576 apply_rules_going_right (edit, i);
578 if (edit->syntax_marker != NULL)
579 d += ((syntax_marker_t *) edit->syntax_marker->data)->offset;
581 if (i > d)
583 syntax_marker_t *s;
585 s = g_new (syntax_marker_t, 1);
586 s->offset = i;
587 s->rule = edit->rule;
588 edit->syntax_marker = g_slist_prepend (edit->syntax_marker, s);
592 else if (byte_index < edit->last_get_rule)
594 while (TRUE)
596 syntax_marker_t *s;
598 if (edit->syntax_marker == NULL)
600 memset (&edit->rule, 0, sizeof (edit->rule));
601 for (i = -1; i <= byte_index; i++)
602 apply_rules_going_right (edit, i);
603 break;
606 s = (syntax_marker_t *) edit->syntax_marker->data;
608 if (byte_index >= s->offset)
610 edit->rule = s->rule;
611 for (i = s->offset + 1; i <= byte_index; i++)
612 apply_rules_going_right (edit, i);
613 break;
616 g_free (s);
617 edit->syntax_marker = g_slist_delete_link (edit->syntax_marker, edit->syntax_marker);
620 edit->last_get_rule = byte_index;
623 /* --------------------------------------------------------------------------------------------- */
625 static int
626 translate_rule_to_color (const WEdit * edit, const edit_syntax_rule_t * rule)
628 syntax_keyword_t *k;
629 context_rule_t *r;
631 r = CONTEXT_RULE (g_ptr_array_index (edit->rules, rule->context));
632 k = SYNTAX_KEYWORD (g_ptr_array_index (r->keyword, rule->keyword));
634 return k->color;
637 /* --------------------------------------------------------------------------------------------- */
639 Returns 0 on error/eof or a count of the number of bytes read
640 including the newline. Result must be free'd.
641 In case of an error, *line will not be modified.
644 static size_t
645 read_one_line (char **line, FILE * f)
647 GString *p;
648 size_t r = 0;
650 /* not reallocate string too often */
651 p = g_string_sized_new (64);
653 while (TRUE)
655 int c;
657 c = fgetc (f);
658 if (c == EOF)
660 if (ferror (f))
662 if (errno == EINTR)
663 continue;
664 r = 0;
666 break;
668 r++;
670 /* handle all of \r\n, \r, \n correctly. */
671 if (c == '\n')
672 break;
673 if (c == '\r')
675 c = fgetc (f);
676 if (c == '\n')
677 r++;
678 else
679 ungetc (c, f);
680 break;
683 g_string_append_c (p, c);
685 if (r != 0)
686 *line = g_string_free (p, FALSE);
687 else
688 g_string_free (p, TRUE);
690 return r;
693 /* --------------------------------------------------------------------------------------------- */
695 static char *
696 convert (char *s)
698 char *r, *p;
700 p = r = s;
701 while (*s)
703 switch (*s)
705 case '\\':
706 s++;
707 switch (*s)
709 case ' ':
710 *p = ' ';
711 s--;
712 break;
713 case 'n':
714 *p = '\n';
715 break;
716 case 'r':
717 *p = '\r';
718 break;
719 case 't':
720 *p = '\t';
721 break;
722 case 's':
723 *p = ' ';
724 break;
725 case '*':
726 *p = '*';
727 break;
728 case '\\':
729 *p = '\\';
730 break;
731 case '[':
732 case ']':
733 *p = SYNTAX_TOKEN_BRACKET;
734 break;
735 case '{':
736 case '}':
737 *p = SYNTAX_TOKEN_BRACE;
738 break;
739 case 0:
740 *p = *s;
741 return r;
742 default:
743 *p = *s;
744 break;
746 break;
747 case '*':
748 *p = SYNTAX_TOKEN_STAR;
749 break;
750 case '+':
751 *p = SYNTAX_TOKEN_PLUS;
752 break;
753 default:
754 *p = *s;
755 break;
757 s++;
758 p++;
760 *p = '\0';
761 return r;
764 /* --------------------------------------------------------------------------------------------- */
766 static int
767 get_args (char *l, char **args, int args_size)
769 int argc = 0;
771 while (argc < args_size)
773 char *p = l;
774 while (*p != '\0' && whiteness (*p))
775 p++;
776 if (*p == '\0')
777 break;
778 for (l = p + 1; *l != '\0' && !whiteness (*l); l++)
780 if (*l != '\0')
781 *l++ = '\0';
782 args[argc++] = convert (p);
784 args[argc] = (char *) NULL;
785 return argc;
788 /* --------------------------------------------------------------------------------------------- */
790 static int
791 this_try_alloc_color_pair (const char *fg, const char *bg, const char *attrs)
793 char f[80], b[80], a[80], *p;
795 if (bg != NULL && *bg == '\0')
796 bg = NULL;
797 if (fg != NULL && *fg == '\0')
798 fg = NULL;
799 if (attrs != NULL && *attrs == '\0')
800 attrs = NULL;
802 if ((fg == NULL) && (bg == NULL))
803 return EDITOR_NORMAL_COLOR;
805 if (fg != NULL)
807 g_strlcpy (f, fg, sizeof (f));
808 p = strchr (f, '/');
809 if (p != NULL)
810 *p = '\0';
811 fg = f;
813 if (bg != NULL)
815 g_strlcpy (b, bg, sizeof (b));
816 p = strchr (b, '/');
817 if (p != NULL)
818 *p = '\0';
819 bg = b;
821 if ((fg == NULL) || (bg == NULL))
823 /* get colors from skin */
824 char *editnormal;
826 editnormal = mc_skin_get ("editor", "_default_", "default;default");
828 if (fg == NULL)
830 g_strlcpy (f, editnormal, sizeof (f));
831 p = strchr (f, ';');
832 if (p != NULL)
833 *p = '\0';
834 if (f[0] == '\0')
835 g_strlcpy (f, "default", sizeof (f));
836 fg = f;
838 if (bg == NULL)
840 p = strchr (editnormal, ';');
841 if ((p != NULL) && (*(++p) != '\0'))
842 g_strlcpy (b, p, sizeof (b));
843 else
844 g_strlcpy (b, "default", sizeof (b));
845 bg = b;
848 g_free (editnormal);
851 if (attrs != NULL)
853 g_strlcpy (a, attrs, sizeof (a));
854 p = strchr (a, '/');
855 if (p != NULL)
856 *p = '\0';
857 /* get_args() mangles the + signs, unmangle 'em */
858 p = a;
859 while ((p = strchr (p, SYNTAX_TOKEN_PLUS)) != NULL)
860 *p++ = '+';
861 attrs = a;
863 return tty_try_alloc_color_pair (fg, bg, attrs);
866 /* --------------------------------------------------------------------------------------------- */
868 static FILE *
869 open_include_file (const char *filename)
871 FILE *f;
873 MC_PTR_FREE (error_file_name);
874 error_file_name = g_strdup (filename);
875 if (g_path_is_absolute (filename))
876 return fopen (filename, "r");
878 g_free (error_file_name);
879 error_file_name =
880 g_build_filename (mc_config_get_data_path (), EDIT_DIR, filename, (char *) NULL);
881 f = fopen (error_file_name, "r");
882 if (f != NULL)
883 return f;
885 g_free (error_file_name);
886 error_file_name = g_build_filename (mc_global.sysconfig_dir, "syntax", filename, (char *) NULL);
887 f = fopen (error_file_name, "r");
888 if (f != NULL)
889 return f;
891 g_free (error_file_name);
892 error_file_name =
893 g_build_filename (mc_global.share_data_dir, "syntax", filename, (char *) NULL);
895 return fopen (error_file_name, "r");
898 /* --------------------------------------------------------------------------------------------- */
900 inline static void
901 xx_lowerize_line (WEdit * edit, char *line, size_t len)
903 if (edit->is_case_insensitive)
905 size_t i;
906 for (i = 0; i < len; ++i)
907 line[i] = tolower (line[i]);
911 /* --------------------------------------------------------------------------------------------- */
912 /** returns line number on error */
914 static int
915 edit_read_syntax_rules (WEdit * edit, FILE * f, char **args, int args_size)
917 FILE *g = NULL;
918 char *fg, *bg, *attrs;
919 char last_fg[32] = "", last_bg[32] = "", last_attrs[64] = "";
920 char whole_right[512];
921 char whole_left[512];
922 char *l = 0;
923 int save_line = 0, line = 0;
924 context_rule_t *c = NULL;
925 gboolean no_words = TRUE;
926 int result = 0;
928 args[0] = NULL;
929 edit->is_case_insensitive = FALSE;
931 strcpy (whole_left, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_01234567890");
932 strcpy (whole_right, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_01234567890");
934 edit->rules = g_ptr_array_new ();
936 if (!edit->defines)
937 edit->defines = g_tree_new ((GCompareFunc) strcmp);
939 while (TRUE)
941 char **a;
942 size_t len;
943 int argc;
945 line++;
946 l = 0;
948 len = read_one_line (&l, f);
949 if (len != 0)
950 xx_lowerize_line (edit, l, len);
951 else
953 if (g == NULL)
954 break;
956 fclose (f);
957 f = g;
958 g = NULL;
959 line = save_line + 1;
960 MC_PTR_FREE (error_file_name);
961 MC_PTR_FREE (l);
962 len = read_one_line (&l, f);
963 if (len == 0)
964 break;
965 xx_lowerize_line (edit, l, len);
968 argc = get_args (l, args, args_size);
969 a = args + 1;
970 if (args[0] == NULL)
972 /* do nothing */
974 else if (strcmp (args[0], "include") == 0)
976 if (g != NULL || argc != 2)
978 result = line;
979 break;
981 g = f;
982 f = open_include_file (args[1]);
983 if (f == NULL)
985 MC_PTR_FREE (error_file_name);
986 result = line;
987 break;
989 save_line = line;
990 line = 0;
992 else if (strcmp (args[0], "caseinsensitive") == 0)
994 edit->is_case_insensitive = TRUE;
996 else if (strcmp (args[0], "wholechars") == 0)
998 check_a;
999 if (strcmp (*a, "left") == 0)
1001 a++;
1002 g_strlcpy (whole_left, *a, sizeof (whole_left));
1004 else if (strcmp (*a, "right") == 0)
1006 a++;
1007 g_strlcpy (whole_right, *a, sizeof (whole_right));
1009 else
1011 g_strlcpy (whole_left, *a, sizeof (whole_left));
1012 g_strlcpy (whole_right, *a, sizeof (whole_right));
1014 a++;
1015 check_not_a;
1017 else if (strcmp (args[0], "context") == 0)
1019 syntax_keyword_t *k;
1021 check_a;
1022 if (edit->rules->len == 0)
1024 /* first context is the default */
1025 if (strcmp (*a, "default") != 0)
1026 break_a;
1028 a++;
1029 c = g_new0 (context_rule_t, 1);
1030 g_ptr_array_add (edit->rules, c);
1031 c->left = g_strdup (" ");
1032 c->right = g_strdup (" ");
1034 else
1036 /* Start new context. */
1037 c = g_new0 (context_rule_t, 1);
1038 g_ptr_array_add (edit->rules, c);
1039 if (strcmp (*a, "exclusive") == 0)
1041 a++;
1042 c->between_delimiters = 1;
1044 check_a;
1045 if (strcmp (*a, "whole") == 0)
1047 a++;
1048 c->whole_word_chars_left = g_strdup (whole_left);
1049 c->whole_word_chars_right = g_strdup (whole_right);
1051 else if (strcmp (*a, "wholeleft") == 0)
1053 a++;
1054 c->whole_word_chars_left = g_strdup (whole_left);
1056 else if (strcmp (*a, "wholeright") == 0)
1058 a++;
1059 c->whole_word_chars_right = g_strdup (whole_right);
1061 check_a;
1062 if (strcmp (*a, "linestart") == 0)
1064 a++;
1065 c->line_start_left = 1;
1067 check_a;
1068 c->left = g_strdup (*a++);
1069 check_a;
1070 if (strcmp (*a, "linestart") == 0)
1072 a++;
1073 c->line_start_right = 1;
1075 check_a;
1076 c->right = g_strdup (*a++);
1077 c->first_left = *c->left;
1078 c->first_right = *c->right;
1080 c->keyword = g_ptr_array_new ();
1081 k = g_new0 (syntax_keyword_t, 1);
1082 g_ptr_array_add (c->keyword, k);
1083 no_words = FALSE;
1084 subst_defines (edit->defines, a, &args[1024]);
1085 fg = *a;
1086 if (*a != NULL)
1087 a++;
1088 bg = *a;
1089 if (*a != NULL)
1090 a++;
1091 attrs = *a;
1092 if (*a != NULL)
1093 a++;
1094 g_strlcpy (last_fg, fg != NULL ? fg : "", sizeof (last_fg));
1095 g_strlcpy (last_bg, bg != NULL ? bg : "", sizeof (last_bg));
1096 g_strlcpy (last_attrs, attrs != NULL ? attrs : "", sizeof (last_attrs));
1097 k->color = this_try_alloc_color_pair (fg, bg, attrs);
1098 k->keyword = g_strdup (" ");
1099 check_not_a;
1101 else if (strcmp (args[0], "spellcheck") == 0)
1103 if (c == NULL)
1105 result = line;
1106 break;
1108 c->spelling = TRUE;
1110 else if (strcmp (args[0], "keyword") == 0)
1112 context_rule_t *last_rule;
1113 syntax_keyword_t *k;
1115 if (no_words)
1116 break_a;
1117 check_a;
1118 last_rule = CONTEXT_RULE (g_ptr_array_index (edit->rules, edit->rules->len - 1));
1119 k = g_new0 (syntax_keyword_t, 1);
1120 g_ptr_array_add (last_rule->keyword, k);
1121 if (strcmp (*a, "whole") == 0)
1123 a++;
1124 k->whole_word_chars_left = g_strdup (whole_left);
1125 k->whole_word_chars_right = g_strdup (whole_right);
1127 else if (strcmp (*a, "wholeleft") == 0)
1129 a++;
1130 k->whole_word_chars_left = g_strdup (whole_left);
1132 else if (strcmp (*a, "wholeright") == 0)
1134 a++;
1135 k->whole_word_chars_right = g_strdup (whole_right);
1137 check_a;
1138 if (strcmp (*a, "linestart") == 0)
1140 a++;
1141 k->line_start = 1;
1143 check_a;
1144 if (strcmp (*a, "whole") == 0)
1146 break_a;
1148 k->keyword = g_strdup (*a++);
1149 subst_defines (edit->defines, a, &args[1024]);
1150 fg = *a;
1151 if (*a != NULL)
1152 a++;
1153 bg = *a;
1154 if (*a != NULL)
1155 a++;
1156 attrs = *a;
1157 if (*a != NULL)
1158 a++;
1159 if (fg == NULL)
1160 fg = last_fg;
1161 if (bg == NULL)
1162 bg = last_bg;
1163 if (attrs == NULL)
1164 attrs = last_attrs;
1165 k->color = this_try_alloc_color_pair (fg, bg, attrs);
1166 check_not_a;
1168 else if (*(args[0]) == '#')
1170 /* do nothing for comment */
1172 else if (strcmp (args[0], "file") == 0)
1174 break;
1176 else if (strcmp (args[0], "define") == 0)
1178 char *key = *a++;
1179 char **argv;
1181 if (argc < 3)
1182 break_a;
1183 argv = g_tree_lookup (edit->defines, key);
1184 if (argv != NULL)
1185 mc_defines_destroy (NULL, argv, NULL);
1186 else
1187 key = g_strdup (key);
1189 argv = g_new (char *, argc - 1);
1190 g_tree_insert (edit->defines, key, argv);
1191 while (*a != NULL)
1192 *argv++ = g_strdup (*a++);
1193 *argv = NULL;
1195 else
1196 { /* anything else is an error */
1197 break_a;
1199 free_args (args);
1200 MC_PTR_FREE (l);
1202 free_args (args);
1203 MC_PTR_FREE (l);
1205 if (edit->rules->len == 0)
1207 g_ptr_array_free (edit->rules, TRUE);
1208 edit->rules = NULL;
1211 if (result == 0)
1213 size_t i;
1214 GString *first_chars;
1216 if (edit->rules == NULL)
1217 return line;
1219 first_chars = g_string_sized_new (32);
1221 /* collect first character of keywords */
1222 for (i = 0; i < edit->rules->len; i++)
1224 size_t j;
1226 g_string_set_size (first_chars, 0);
1227 c = CONTEXT_RULE (g_ptr_array_index (edit->rules, i));
1229 g_string_append_c (first_chars, (char) 1);
1230 for (j = 1; j < c->keyword->len; j++)
1232 syntax_keyword_t *k;
1234 k = SYNTAX_KEYWORD (g_ptr_array_index (c->keyword, j));
1235 g_string_append_c (first_chars, k->keyword[0]);
1238 c->keyword_first_chars = g_strndup (first_chars->str, first_chars->len);
1241 g_string_free (first_chars, TRUE);
1244 return result;
1247 /* --------------------------------------------------------------------------------------------- */
1249 /* returns -1 on file error, line number on error in file syntax */
1250 static int
1251 edit_read_syntax_file (WEdit * edit, GPtrArray * pnames, const char *syntax_file,
1252 const char *editor_file, const char *first_line, const char *type)
1254 FILE *f, *g = NULL;
1255 char *args[1024], *l = NULL;
1256 long line = 0;
1257 int result = 0;
1258 char *lib_file;
1259 gboolean found = FALSE;
1261 f = fopen (syntax_file, "r");
1262 if (f == NULL)
1264 lib_file = g_build_filename (mc_global.share_data_dir, "syntax", "Syntax", (char *) NULL);
1265 f = fopen (lib_file, "r");
1266 g_free (lib_file);
1267 if (f == NULL)
1268 return -1;
1271 args[0] = NULL;
1272 while (TRUE)
1274 line++;
1275 MC_PTR_FREE (l);
1276 if (read_one_line (&l, f) == 0)
1277 break;
1278 (void) get_args (l, args, 1023); /* Final NULL */
1279 if (args[0] == NULL)
1280 continue;
1282 /* Looking for 'include ...' lines before first 'file ...' ones */
1283 if (!found && strcmp (args[0], "include") == 0)
1285 if (g != NULL)
1286 continue;
1288 if (!args[1] || !(g = open_include_file (args[1])))
1290 result = line;
1291 break;
1293 goto found_type;
1296 /* looking for 'file ...' lines only */
1297 if (strcmp (args[0], "file") != 0)
1298 continue;
1300 found = TRUE;
1302 /* must have two args or report error */
1303 if (!args[1] || !args[2])
1305 result = line;
1306 break;
1309 if (pnames != NULL)
1311 /* 1: just collecting a list of names of rule sets */
1312 g_ptr_array_add (pnames, g_strdup (args[2]));
1314 else if (type)
1316 /* 2: rule set was explicitly specified by the caller */
1317 if (strcmp (type, args[2]) == 0)
1318 goto found_type;
1320 else if (editor_file && edit)
1322 /* 3: auto-detect rule set from regular expressions */
1323 int q;
1325 q = mc_search (args[1], DEFAULT_CHARSET, editor_file, MC_SEARCH_T_REGEX);
1326 /* does filename match arg 1 ? */
1327 if (!q && args[3])
1329 /* does first line match arg 3 ? */
1330 q = mc_search (args[3], DEFAULT_CHARSET, first_line, MC_SEARCH_T_REGEX);
1332 if (q)
1334 int line_error;
1335 char *syntax_type;
1336 found_type:
1337 syntax_type = args[2];
1338 line_error = edit_read_syntax_rules (edit, g ? g : f, args, 1023);
1339 if (line_error)
1341 if (!error_file_name) /* an included file */
1342 result = line + line_error;
1343 else
1344 result = line_error;
1346 else
1348 g_free (edit->syntax_type);
1349 edit->syntax_type = g_strdup (syntax_type);
1350 /* if there are no rules then turn off syntax highlighting for speed */
1351 if (g == NULL && edit->rules->len == 1)
1353 context_rule_t *r0;
1355 r0 = CONTEXT_RULE (g_ptr_array_index (edit->rules, 0));
1356 if (r0->keyword->len == 1 && !r0->spelling)
1358 edit_free_syntax_rules (edit);
1359 break;
1364 if (g == NULL)
1365 break;
1367 fclose (g);
1368 g = NULL;
1372 g_free (l);
1373 fclose (f);
1374 return result;
1377 /* --------------------------------------------------------------------------------------------- */
1379 static const char *
1380 get_first_editor_line (WEdit * edit)
1382 static char s[256];
1384 s[0] = '\0';
1386 if (edit != NULL)
1388 size_t i;
1390 for (i = 0; i < sizeof (s) - 1; i++)
1392 s[i] = edit_buffer_get_byte (&edit->buffer, i);
1393 if (s[i] == '\n')
1395 s[i] = '\0';
1396 break;
1400 s[sizeof (s) - 1] = '\0';
1403 return s;
1406 /* --------------------------------------------------------------------------------------------- */
1407 /*** public functions ****************************************************************************/
1408 /* --------------------------------------------------------------------------------------------- */
1411 edit_get_syntax_color (WEdit * edit, off_t byte_index)
1413 if (!tty_use_colors ())
1414 return 0;
1416 if (edit->rules != NULL && byte_index < edit->buffer.size && option_syntax_highlighting)
1418 edit_get_rule (edit, byte_index);
1419 return translate_rule_to_color (edit, &edit->rule);
1422 return EDITOR_NORMAL_COLOR;
1425 /* --------------------------------------------------------------------------------------------- */
1427 void
1428 edit_free_syntax_rules (WEdit * edit)
1430 if (edit == NULL)
1431 return;
1433 if (edit->defines != NULL)
1434 destroy_defines (&edit->defines);
1436 if (edit->rules == NULL)
1437 return;
1439 edit_get_rule (edit, -1);
1440 MC_PTR_FREE (edit->syntax_type);
1442 g_ptr_array_foreach (edit->rules, (GFunc) context_rule_free, NULL);
1443 g_ptr_array_free (edit->rules, TRUE);
1444 edit->rules = NULL;
1445 g_slist_free_full (edit->syntax_marker, g_free);
1446 edit->syntax_marker = NULL;
1447 tty_color_free_all_tmp ();
1450 /* --------------------------------------------------------------------------------------------- */
1452 * Load rules into edit struct. Either edit or *pnames must be NULL. If
1453 * edit is NULL, a list of types will be stored into names. If type is
1454 * NULL, then the type will be selected according to the filename.
1455 * type must be edit->syntax_type or NULL
1457 void
1458 edit_load_syntax (WEdit * edit, GPtrArray * pnames, const char *type)
1460 int r;
1461 char *f = NULL;
1463 if (option_auto_syntax)
1464 type = NULL;
1466 if (edit != NULL)
1468 char *saved_type;
1470 saved_type = g_strdup (type); /* save edit->syntax_type */
1471 edit_free_syntax_rules (edit);
1472 edit->syntax_type = saved_type; /* restore edit->syntax_type */
1475 if (!tty_use_colors ())
1476 return;
1478 if (!option_syntax_highlighting && (pnames == NULL || pnames->len == 0))
1479 return;
1481 if (edit != NULL && edit->filename_vpath == NULL)
1482 return;
1484 f = mc_config_get_full_path (EDIT_SYNTAX_FILE);
1485 if (edit != NULL)
1486 r = edit_read_syntax_file (edit, pnames, f, vfs_path_as_str (edit->filename_vpath),
1487 get_first_editor_line (edit),
1488 option_auto_syntax ? NULL : edit->syntax_type);
1489 else
1490 r = edit_read_syntax_file (NULL, pnames, f, NULL, "", NULL);
1491 if (r == -1)
1493 edit_free_syntax_rules (edit);
1494 message (D_ERROR, _("Load syntax file"),
1495 _("Cannot open file %s\n%s"), f, unix_error_string (errno));
1497 else if (r != 0)
1499 edit_free_syntax_rules (edit);
1500 message (D_ERROR, _("Load syntax file"),
1501 _("Error in file %s on line %d"), error_file_name ? error_file_name : f, r);
1502 MC_PTR_FREE (error_file_name);
1505 g_free (f);
1508 /* --------------------------------------------------------------------------------------------- */
1510 const char *
1511 edit_get_syntax_type (const WEdit * edit)
1513 return edit->syntax_type;
1516 /* --------------------------------------------------------------------------------------------- */