Ticket #2846: code cleanup before 4.8.1.4 release.
[midnight-commander.git] / src / editor / syntax.c
blobdc7c99c20d08091a0f05a6288f27b425847e22eb
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 int 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 int spelling;
122 /* first word is word[1] */
123 struct key_word **keyword;
126 struct _syntax_marker
128 long offset;
129 struct syntax_rule rule;
130 struct _syntax_marker *next;
133 /*** file scope variables ************************************************************************/
135 static char *error_file_name = NULL;
137 /*** file scope functions ************************************************************************/
138 /* --------------------------------------------------------------------------------------------- */
140 static gint
141 mc_defines_destroy (gpointer key, gpointer value, gpointer data)
143 char **values = value;
145 (void) data;
147 g_free (key);
148 while (*values)
149 g_free (*values++);
150 g_free (value);
152 return FALSE;
155 /* --------------------------------------------------------------------------------------------- */
156 /** Completely destroys the defines tree */
158 static void
159 destroy_defines (GTree ** defines)
161 g_tree_foreach (*defines, mc_defines_destroy, NULL);
162 g_tree_destroy (*defines);
163 *defines = NULL;
166 /* --------------------------------------------------------------------------------------------- */
168 /** Wrapper for case insensitive mode */
169 inline static int
170 xx_tolower (WEdit * edit, int c)
172 return edit->is_case_insensitive ? tolower (c) : c;
175 /* --------------------------------------------------------------------------------------------- */
177 static void
178 subst_defines (GTree * defines, char **argv, char **argv_end)
180 char **t, **p;
181 int argc;
183 while (*argv != NULL && argv < argv_end)
185 t = g_tree_lookup (defines, *argv);
186 if (t != NULL)
188 int count = 0;
190 /* Count argv array members */
191 argc = 0;
192 for (p = &argv[1]; *p != NULL; p++)
193 argc++;
195 /* Count members of definition array */
196 for (p = t; *p != NULL; p++)
197 count++;
198 p = &argv[count + argc];
200 /* Buffer overflow or infinitive loop in define */
201 if (p >= argv_end)
202 break;
204 /* Move rest of argv after definition members */
205 while (argc >= 0)
206 *p-- = argv[argc-- + 1];
208 /* Copy definition members to argv */
209 for (p = argv; *t != NULL; *p++ = *t++)
212 argv++;
216 /* --------------------------------------------------------------------------------------------- */
218 static long
219 compare_word_to_right (WEdit * edit, long i, const char *text,
220 const char *whole_left, const char *whole_right, int line_start)
222 const unsigned char *p, *q;
223 int c, d, j;
225 if (*text == '\0')
226 return -1;
228 c = xx_tolower (edit, edit_get_byte (edit, i - 1));
229 if (line_start != 0 && c != '\n')
230 return -1;
231 if (whole_left != NULL && strchr (whole_left, c) != NULL)
232 return -1;
234 for (p = (unsigned char *) text, q = p + str_term_width1 ((char *) p); p < q; p++, i++)
236 switch (*p)
238 case SYNTAX_TOKEN_STAR:
239 if (++p > q)
240 return -1;
241 for (;;)
243 c = xx_tolower (edit, edit_get_byte (edit, i));
244 if (*p == '\0' && whole_right != NULL && strchr (whole_right, c) == NULL)
245 break;
246 if (c == *p)
247 break;
248 if (c == '\n')
249 return -1;
250 i++;
252 break;
253 case SYNTAX_TOKEN_PLUS:
254 if (++p > q)
255 return -1;
256 j = 0;
257 for (;;)
259 c = xx_tolower (edit, edit_get_byte (edit, i));
260 if (c == *p)
262 j = i;
263 if (*p == *text && p[1] == '\0') /* handle eg '+' and @+@ keywords properly */
264 break;
266 if (j && strchr ((char *) p + 1, c)) /* c exists further down, so it will get matched later */
267 break;
268 if (c == '\n' || c == '\t' || c == ' ')
270 if (!*p)
272 i--;
273 break;
275 if (j == 0)
276 return -1;
277 i = j;
278 break;
280 if (whole_right != NULL && (strchr (whole_right, c) == NULL))
282 if (*p == '\0')
284 i--;
285 break;
287 if (j == 0)
288 return -1;
289 i = j;
290 break;
292 i++;
294 break;
295 case SYNTAX_TOKEN_BRACKET:
296 if (++p > q)
297 return -1;
298 c = -1;
299 for (;; i++)
301 d = c;
302 c = xx_tolower (edit, edit_get_byte (edit, i));
303 for (j = 0; p[j] != SYNTAX_TOKEN_BRACKET && p[j]; j++)
304 if (c == p[j])
305 goto found_char2;
306 break;
307 found_char2:
308 ; /* dummy command */
310 i--;
311 while (*p != SYNTAX_TOKEN_BRACKET && p <= q)
312 p++;
313 if (p > q)
314 return -1;
315 if (p[1] == d)
316 i--;
317 break;
318 case SYNTAX_TOKEN_BRACE:
319 if (++p > q)
320 return -1;
321 c = xx_tolower (edit, edit_get_byte (edit, i));
322 for (; *p != SYNTAX_TOKEN_BRACE && *p; p++)
323 if (c == *p)
324 goto found_char3;
325 return -1;
326 found_char3:
327 while (*p != SYNTAX_TOKEN_BRACE && p < q)
328 p++;
329 break;
330 default:
331 if (*p != xx_tolower (edit, edit_get_byte (edit, i)))
332 return -1;
335 if (whole_right != NULL
336 && strchr (whole_right, xx_tolower (edit, edit_get_byte (edit, i))) != NULL)
337 return -1;
338 return i;
341 /* --------------------------------------------------------------------------------------------- */
343 static const char *
344 xx_strchr (WEdit * edit, const unsigned char *s, int char_byte)
346 while (*s >= '\005' && xx_tolower (edit, *s) != char_byte)
347 s++;
349 return (const char *) s;
352 /* --------------------------------------------------------------------------------------------- */
354 static struct syntax_rule
355 apply_rules_going_right (WEdit * edit, long i, struct syntax_rule rule)
357 struct context_rule *r;
358 int c;
359 gboolean contextchanged = FALSE;
360 gboolean found_left = FALSE, found_right = FALSE;
361 gboolean keyword_foundleft = FALSE, keyword_foundright = FALSE;
362 gboolean is_end;
363 long end = 0;
364 struct syntax_rule _rule = rule;
366 c = xx_tolower (edit, edit_get_byte (edit, i));
367 if (c == 0)
368 return rule;
369 is_end = (rule.end == (unsigned char) i);
371 /* check to turn off a keyword */
372 if (_rule.keyword)
374 if (edit_get_byte (edit, i - 1) == '\n')
375 _rule.keyword = 0;
376 if (is_end)
378 _rule.keyword = 0;
379 keyword_foundleft = TRUE;
383 /* check to turn off a context */
384 if (_rule.context && !_rule.keyword)
386 long e;
388 r = edit->rules[_rule.context];
389 if (r->first_right == c && !(rule.border & RULE_ON_RIGHT_BORDER)
390 && (e =
391 compare_word_to_right (edit, i, r->right, r->whole_word_chars_left,
392 r->whole_word_chars_right, r->line_start_right)) > 0)
394 _rule.end = e;
395 found_right = TRUE;
396 _rule.border = RULE_ON_RIGHT_BORDER;
397 if (r->between_delimiters)
398 _rule.context = 0;
400 else if (is_end && rule.border & RULE_ON_RIGHT_BORDER)
402 /* always turn off a context at 4 */
403 found_left = TRUE;
404 _rule.border = 0;
405 if (!keyword_foundleft)
406 _rule.context = 0;
408 else if (is_end && rule.border & RULE_ON_LEFT_BORDER)
410 /* never turn off a context at 2 */
411 found_left = TRUE;
412 _rule.border = 0;
416 /* check to turn on a keyword */
417 if (!_rule.keyword)
419 const char *p;
421 r = edit->rules[_rule.context];
422 p = r->keyword_first_chars;
424 if (p != NULL)
425 while (*(p = xx_strchr (edit, (unsigned char *) p + 1, c)) != '\0')
427 struct key_word *k;
428 int count;
429 long e;
431 count = p - r->keyword_first_chars;
432 k = r->keyword[count];
433 e = compare_word_to_right (edit, i, k->keyword, k->whole_word_chars_left,
434 k->whole_word_chars_right, k->line_start);
435 if (e > 0)
437 end = e;
438 _rule.end = e;
439 _rule.keyword = count;
440 keyword_foundright = TRUE;
441 break;
446 /* check to turn on a context */
447 if (!_rule.context)
449 if (!found_left && is_end)
451 if (rule.border & RULE_ON_RIGHT_BORDER)
453 _rule.border = 0;
454 _rule.context = 0;
455 contextchanged = TRUE;
456 _rule.keyword = 0;
459 else if (rule.border & RULE_ON_LEFT_BORDER)
461 r = edit->rules[_rule._context];
462 _rule.border = 0;
463 if (r->between_delimiters)
465 _rule.context = _rule._context;
466 contextchanged = TRUE;
467 _rule.keyword = 0;
469 if (r->first_right == c)
471 long e;
473 e = compare_word_to_right (edit, i, r->right, r->whole_word_chars_left,
474 r->whole_word_chars_right, r->line_start_right);
475 if (e >= end)
477 _rule.end = e;
478 found_right = TRUE;
479 _rule.border = RULE_ON_RIGHT_BORDER;
480 _rule.context = 0;
487 if (!found_right)
489 int count;
490 struct context_rule **rules = edit->rules;
492 for (count = 1; rules[count]; count++)
494 r = rules[count];
495 if (r->first_left == c)
497 long e;
499 e = compare_word_to_right (edit, i, r->left, r->whole_word_chars_left,
500 r->whole_word_chars_right, r->line_start_left);
501 if (e >= end && (!_rule.keyword || keyword_foundright))
503 _rule.end = e;
504 found_right = TRUE;
505 _rule.border = RULE_ON_LEFT_BORDER;
506 _rule._context = count;
507 if (!r->between_delimiters && !_rule.keyword)
509 _rule.context = count;
510 contextchanged = TRUE;
512 break;
519 /* check again to turn on a keyword if the context switched */
520 if (contextchanged && !_rule.keyword)
522 const char *p;
524 r = edit->rules[_rule.context];
525 p = r->keyword_first_chars;
527 while (*(p = xx_strchr (edit, (unsigned char *) p + 1, c)) != '\0')
529 struct key_word *k;
530 int count;
531 long e;
533 count = p - r->keyword_first_chars;
534 k = r->keyword[count];
535 e = compare_word_to_right (edit, i, k->keyword, k->whole_word_chars_left,
536 k->whole_word_chars_right, k->line_start);
537 if (e > 0)
539 _rule.end = e;
540 _rule.keyword = count;
541 break;
546 return _rule;
549 /* --------------------------------------------------------------------------------------------- */
551 static struct syntax_rule
552 edit_get_rule (WEdit * edit, long byte_index)
554 long i;
556 if (byte_index > edit->last_get_rule)
558 for (i = edit->last_get_rule + 1; i <= byte_index; i++)
560 edit->rule = apply_rules_going_right (edit, i, edit->rule);
561 if (i >
562 (edit->syntax_marker ? edit->syntax_marker->offset +
563 SYNTAX_MARKER_DENSITY : SYNTAX_MARKER_DENSITY))
565 struct _syntax_marker *s;
567 s = edit->syntax_marker;
568 edit->syntax_marker = g_malloc (sizeof (struct _syntax_marker));
569 edit->syntax_marker->next = s;
570 edit->syntax_marker->offset = i;
571 edit->syntax_marker->rule = edit->rule;
575 else if (byte_index < edit->last_get_rule)
577 struct _syntax_marker *s;
579 for (;;)
581 if (!edit->syntax_marker)
583 memset (&edit->rule, 0, sizeof (edit->rule));
584 for (i = -1; i <= byte_index; i++)
585 edit->rule = apply_rules_going_right (edit, i, edit->rule);
586 break;
588 if (byte_index >= edit->syntax_marker->offset)
590 edit->rule = edit->syntax_marker->rule;
591 for (i = edit->syntax_marker->offset + 1; i <= byte_index; i++)
592 edit->rule = apply_rules_going_right (edit, i, edit->rule);
593 break;
595 s = edit->syntax_marker->next;
596 g_free (edit->syntax_marker);
597 edit->syntax_marker = s;
600 edit->last_get_rule = byte_index;
601 return edit->rule;
604 /* --------------------------------------------------------------------------------------------- */
606 static inline void
607 translate_rule_to_color (WEdit * edit, struct syntax_rule rule, int *color)
609 *color = edit->rules[rule.context]->keyword[rule.keyword]->color;
613 /* --------------------------------------------------------------------------------------------- */
615 Returns 0 on error/eof or a count of the number of bytes read
616 including the newline. Result must be free'd.
617 In case of an error, *line will not be modified.
620 static size_t
621 read_one_line (char **line, FILE * f)
623 GString *p;
624 size_t r = 0;
626 /* not reallocate string too often */
627 p = g_string_sized_new (64);
629 for (;;)
631 int c;
633 c = fgetc (f);
634 if (c == EOF)
636 if (ferror (f))
638 if (errno == EINTR)
639 continue;
640 r = 0;
642 break;
644 r++;
646 /* handle all of \r\n, \r, \n correctly. */
647 if (c == '\n')
648 break;
649 if (c == '\r')
651 c = fgetc (f);
652 if (c == '\n')
653 r++;
654 else
655 ungetc (c, f);
656 break;
659 g_string_append_c (p, c);
661 if (r != 0)
662 *line = g_string_free (p, FALSE);
663 else
664 g_string_free (p, TRUE);
666 return r;
669 /* --------------------------------------------------------------------------------------------- */
671 static char *
672 convert (char *s)
674 char *r, *p;
676 p = r = s;
677 while (*s)
679 switch (*s)
681 case '\\':
682 s++;
683 switch (*s)
685 case ' ':
686 *p = ' ';
687 s--;
688 break;
689 case 'n':
690 *p = '\n';
691 break;
692 case 'r':
693 *p = '\r';
694 break;
695 case 't':
696 *p = '\t';
697 break;
698 case 's':
699 *p = ' ';
700 break;
701 case '*':
702 *p = '*';
703 break;
704 case '\\':
705 *p = '\\';
706 break;
707 case '[':
708 case ']':
709 *p = SYNTAX_TOKEN_BRACKET;
710 break;
711 case '{':
712 case '}':
713 *p = SYNTAX_TOKEN_BRACE;
714 break;
715 case 0:
716 *p = *s;
717 return r;
718 default:
719 *p = *s;
720 break;
722 break;
723 case '*':
724 *p = SYNTAX_TOKEN_STAR;
725 break;
726 case '+':
727 *p = SYNTAX_TOKEN_PLUS;
728 break;
729 default:
730 *p = *s;
731 break;
733 s++;
734 p++;
736 *p = '\0';
737 return r;
740 /* --------------------------------------------------------------------------------------------- */
742 static int
743 get_args (char *l, char **args, int args_size)
745 int argc = 0;
747 while (argc < args_size)
749 char *p = l;
750 while (*p != '\0' && whiteness (*p))
751 p++;
752 if (*p == '\0')
753 break;
754 for (l = p + 1; *l != '\0' && !whiteness (*l); l++)
756 if (*l != '\0')
757 *l++ = '\0';
758 args[argc++] = convert (p);
760 args[argc] = (char *) NULL;
761 return argc;
764 /* --------------------------------------------------------------------------------------------- */
766 static int
767 this_try_alloc_color_pair (const char *fg, const char *bg, const char *attrs)
769 char f[80], b[80], a[80], *p;
771 if (bg != NULL && *bg == '\0')
772 bg = NULL;
773 if (fg != NULL && *fg == '\0')
774 fg = NULL;
775 if (attrs != NULL && *attrs == '\0')
776 attrs = NULL;
778 if ((fg == NULL) && (bg == NULL))
779 return EDITOR_NORMAL_COLOR;
781 if (fg != NULL)
783 g_strlcpy (f, fg, sizeof (f));
784 p = strchr (f, '/');
785 if (p != NULL)
786 *p = '\0';
787 fg = f;
789 if (bg != NULL)
791 g_strlcpy (b, bg, sizeof (b));
792 p = strchr (b, '/');
793 if (p != NULL)
794 *p = '\0';
795 bg = b;
797 if ((fg == NULL) || (bg == NULL))
799 /* get colors from skin */
800 char *editnormal;
802 editnormal = mc_skin_get ("editor", "_default_", "default;default");
804 if (fg == NULL)
806 g_strlcpy (f, editnormal, sizeof (f));
807 p = strchr (f, ';');
808 if (p != NULL)
809 *p = '\0';
810 if (f[0] == '\0')
811 g_strlcpy (f, "default", sizeof (f));
812 fg = f;
814 if (bg == NULL)
816 p = strchr (editnormal, ';');
817 if ((p != NULL) && (*(++p) != '\0'))
818 g_strlcpy (b, p, sizeof (b));
819 else
820 g_strlcpy (b, "default", sizeof (b));
821 bg = b;
824 g_free (editnormal);
827 if (attrs != NULL)
829 g_strlcpy (a, attrs, sizeof (a));
830 p = strchr (a, '/');
831 if (p != NULL)
832 *p = '\0';
833 /* get_args() mangles the + signs, unmangle 'em */
834 p = a;
835 while ((p = strchr (p, SYNTAX_TOKEN_PLUS)) != NULL)
836 *p++ = '+';
837 attrs = a;
839 return tty_try_alloc_color_pair (fg, bg, attrs);
842 /* --------------------------------------------------------------------------------------------- */
844 static FILE *
845 open_include_file (const char *filename)
847 FILE *f;
849 MC_PTR_FREE (error_file_name);
850 error_file_name = g_strdup (filename);
851 if (g_path_is_absolute (filename))
852 return fopen (filename, "r");
854 g_free (error_file_name);
855 error_file_name =
856 g_build_filename (mc_config_get_data_path (), EDIT_DIR, filename, (char *) NULL);
857 f = fopen (error_file_name, "r");
858 if (f != NULL)
859 return f;
861 g_free (error_file_name);
862 error_file_name = g_build_filename (mc_global.sysconfig_dir, "syntax", filename, (char *) NULL);
863 f = fopen (error_file_name, "r");
864 if (f != NULL)
865 return f;
867 g_free (error_file_name);
868 error_file_name =
869 g_build_filename (mc_global.share_data_dir, "syntax", filename, (char *) NULL);
871 return fopen (error_file_name, "r");
874 /* --------------------------------------------------------------------------------------------- */
876 inline static void
877 xx_lowerize_line (WEdit * edit, char *line, size_t len)
879 if (edit->is_case_insensitive)
881 size_t i;
882 for (i = 0; i < len; ++i)
883 line[i] = tolower (line[i]);
887 /* --------------------------------------------------------------------------------------------- */
888 /** returns line number on error */
890 static int
891 edit_read_syntax_rules (WEdit * edit, FILE * f, char **args, int args_size)
893 FILE *g = NULL;
894 char *fg, *bg, *attrs;
895 char last_fg[32] = "", last_bg[32] = "", last_attrs[64] = "";
896 char whole_right[512];
897 char whole_left[512];
898 char *l = 0;
899 int save_line = 0, line = 0;
900 struct context_rule **r, *c = NULL;
901 int num_words = -1, num_contexts = -1;
902 int result = 0;
903 int argc;
904 int i, j;
905 int alloc_contexts = MAX_CONTEXTS,
906 alloc_words_per_context = MAX_WORDS_PER_CONTEXT,
907 max_alloc_words_per_context = MAX_WORDS_PER_CONTEXT;
909 args[0] = NULL;
910 edit->is_case_insensitive = FALSE;
912 strcpy (whole_left, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_01234567890");
913 strcpy (whole_right, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_01234567890");
915 r = edit->rules = g_malloc0 (alloc_contexts * sizeof (struct context_rule *));
917 if (!edit->defines)
918 edit->defines = g_tree_new ((GCompareFunc) strcmp);
920 for (;;)
922 char **a;
923 size_t len;
925 line++;
926 l = 0;
928 len = read_one_line (&l, f);
929 if (len == 0)
931 if (g)
933 fclose (f);
934 f = g;
935 g = 0;
936 line = save_line + 1;
937 MC_PTR_FREE (error_file_name);
938 MC_PTR_FREE (l);
939 len = read_one_line (&l, f);
940 if (len == 0)
941 break;
942 else
943 xx_lowerize_line (edit, l, len);
945 else
947 break;
950 else
952 xx_lowerize_line (edit, l, len);
954 argc = get_args (l, args, args_size);
955 a = args + 1;
956 if (!args[0])
958 /* do nothing */
960 else if (!strcmp (args[0], "include"))
962 if (g || argc != 2)
964 result = line;
965 break;
967 g = f;
968 f = open_include_file (args[1]);
969 if (!f)
971 MC_PTR_FREE (error_file_name);
972 result = line;
973 break;
975 save_line = line;
976 line = 0;
978 else if (!strcmp (args[0], "caseinsensitive"))
980 edit->is_case_insensitive = TRUE;
982 else if (!strcmp (args[0], "wholechars"))
984 check_a;
985 if (!strcmp (*a, "left"))
987 a++;
988 g_strlcpy (whole_left, *a, sizeof (whole_left));
990 else if (!strcmp (*a, "right"))
992 a++;
993 g_strlcpy (whole_right, *a, sizeof (whole_right));
995 else
997 g_strlcpy (whole_left, *a, sizeof (whole_left));
998 g_strlcpy (whole_right, *a, sizeof (whole_right));
1000 a++;
1001 check_not_a;
1003 else if (!strcmp (args[0], "context"))
1005 check_a;
1006 if (num_contexts == -1)
1008 if (strcmp (*a, "default"))
1009 { /* first context is the default */
1010 break_a;
1012 a++;
1013 c = r[0] = g_malloc0 (sizeof (struct context_rule));
1014 c->left = g_strdup (" ");
1015 c->right = g_strdup (" ");
1016 num_contexts = 0;
1018 else
1020 /* Terminate previous context. */
1021 r[num_contexts - 1]->keyword[num_words] = NULL;
1022 c = r[num_contexts] = g_malloc0 (sizeof (struct context_rule));
1023 if (!strcmp (*a, "exclusive"))
1025 a++;
1026 c->between_delimiters = 1;
1028 check_a;
1029 if (!strcmp (*a, "whole"))
1031 a++;
1032 c->whole_word_chars_left = g_strdup (whole_left);
1033 c->whole_word_chars_right = g_strdup (whole_right);
1035 else if (!strcmp (*a, "wholeleft"))
1037 a++;
1038 c->whole_word_chars_left = g_strdup (whole_left);
1040 else if (!strcmp (*a, "wholeright"))
1042 a++;
1043 c->whole_word_chars_right = g_strdup (whole_right);
1045 check_a;
1046 if (!strcmp (*a, "linestart"))
1048 a++;
1049 c->line_start_left = 1;
1051 check_a;
1052 c->left = g_strdup (*a++);
1053 check_a;
1054 if (!strcmp (*a, "linestart"))
1056 a++;
1057 c->line_start_right = 1;
1059 check_a;
1060 c->right = g_strdup (*a++);
1061 c->first_left = *c->left;
1062 c->first_right = *c->right;
1064 c->keyword = g_malloc (alloc_words_per_context * sizeof (struct key_word *));
1065 num_words = 1;
1066 c->keyword[0] = g_malloc0 (sizeof (struct key_word));
1067 subst_defines (edit->defines, a, &args[1024]);
1068 fg = *a;
1069 if (*a)
1070 a++;
1071 bg = *a;
1072 if (*a)
1073 a++;
1074 attrs = *a;
1075 if (*a)
1076 a++;
1077 g_strlcpy (last_fg, fg ? fg : "", sizeof (last_fg));
1078 g_strlcpy (last_bg, bg ? bg : "", sizeof (last_bg));
1079 g_strlcpy (last_attrs, attrs ? attrs : "", sizeof (last_attrs));
1080 c->keyword[0]->color = this_try_alloc_color_pair (fg, bg, attrs);
1081 c->keyword[0]->keyword = g_strdup (" ");
1082 check_not_a;
1084 alloc_words_per_context = MAX_WORDS_PER_CONTEXT;
1085 if (++num_contexts >= alloc_contexts)
1087 struct context_rule **tmp;
1089 alloc_contexts += 128;
1090 tmp = g_realloc (r, alloc_contexts * sizeof (struct context_rule *));
1091 r = tmp;
1094 else if (!strcmp (args[0], "spellcheck"))
1096 if (!c)
1098 result = line;
1099 break;
1101 c->spelling = 1;
1103 else if (!strcmp (args[0], "keyword"))
1105 struct key_word *k;
1107 if (num_words == -1)
1108 break_a;
1109 check_a;
1110 k = r[num_contexts - 1]->keyword[num_words] = g_malloc0 (sizeof (struct key_word));
1111 if (!strcmp (*a, "whole"))
1113 a++;
1114 k->whole_word_chars_left = g_strdup (whole_left);
1115 k->whole_word_chars_right = g_strdup (whole_right);
1117 else if (!strcmp (*a, "wholeleft"))
1119 a++;
1120 k->whole_word_chars_left = g_strdup (whole_left);
1122 else if (!strcmp (*a, "wholeright"))
1124 a++;
1125 k->whole_word_chars_right = g_strdup (whole_right);
1127 check_a;
1128 if (!strcmp (*a, "linestart"))
1130 a++;
1131 k->line_start = 1;
1133 check_a;
1134 if (!strcmp (*a, "whole"))
1136 break_a;
1138 k->keyword = g_strdup (*a++);
1139 k->first = *k->keyword;
1140 subst_defines (edit->defines, a, &args[1024]);
1141 fg = *a;
1142 if (*a)
1143 a++;
1144 bg = *a;
1145 if (*a)
1146 a++;
1147 attrs = *a;
1148 if (*a)
1149 a++;
1150 if (!fg)
1151 fg = last_fg;
1152 if (!bg)
1153 bg = last_bg;
1154 if (!attrs)
1155 attrs = last_attrs;
1156 k->color = this_try_alloc_color_pair (fg, bg, attrs);
1157 check_not_a;
1159 if (++num_words >= alloc_words_per_context)
1161 struct key_word **tmp;
1163 alloc_words_per_context += 1024;
1165 if (alloc_words_per_context > max_alloc_words_per_context)
1166 max_alloc_words_per_context = alloc_words_per_context;
1168 tmp = g_realloc (c->keyword, alloc_words_per_context * sizeof (struct key_word *));
1169 c->keyword = tmp;
1172 else if (*(args[0]) == '#')
1174 /* do nothing for comment */
1176 else if (!strcmp (args[0], "file"))
1178 break;
1180 else if (!strcmp (args[0], "define"))
1182 char *key = *a++;
1183 char **argv;
1185 if (argc < 3)
1186 break_a;
1187 argv = g_tree_lookup (edit->defines, key);
1188 if (argv != NULL)
1189 mc_defines_destroy (NULL, argv, NULL);
1190 else
1191 key = g_strdup (key);
1193 argv = g_new (char *, argc - 1);
1194 g_tree_insert (edit->defines, key, argv);
1195 while (*a != NULL)
1197 *argv++ = g_strdup (*a++);
1199 *argv = NULL;
1201 else
1202 { /* anything else is an error */
1203 break_a;
1205 free_args (args);
1206 MC_PTR_FREE (l);
1208 free_args (args);
1209 MC_PTR_FREE (l);
1211 /* Terminate context array. */
1212 if (num_contexts > 0)
1214 r[num_contexts - 1]->keyword[num_words] = NULL;
1215 r[num_contexts] = NULL;
1218 if (!edit->rules[0])
1219 MC_PTR_FREE (edit->rules);
1221 if (result)
1222 return result;
1224 if (num_contexts == -1)
1226 return line;
1230 char *first_chars, *p;
1232 first_chars = g_malloc0 (max_alloc_words_per_context + 2);
1234 for (i = 0; edit->rules[i]; i++)
1236 c = edit->rules[i];
1237 p = first_chars;
1238 *p++ = (char) 1;
1239 for (j = 1; c->keyword[j]; j++)
1240 *p++ = c->keyword[j]->first;
1241 *p = '\0';
1242 c->keyword_first_chars = g_strdup (first_chars);
1245 g_free (first_chars);
1248 return result;
1251 /* --------------------------------------------------------------------------------------------- */
1253 /* returns -1 on file error, line number on error in file syntax */
1254 static int
1255 edit_read_syntax_file (WEdit * edit, char ***pnames, const char *syntax_file,
1256 const char *editor_file, const char *first_line, const char *type)
1258 #define NENTRIES 30
1259 FILE *f, *g = NULL;
1260 char *args[1024], *l = NULL;
1261 int line = 0;
1262 int result = 0;
1263 int count = 0;
1264 char *lib_file;
1265 gboolean found = FALSE;
1266 char **tmpnames = NULL;
1268 f = fopen (syntax_file, "r");
1269 if (f == NULL)
1271 lib_file = g_build_filename (mc_global.share_data_dir, "syntax", "Syntax", (char *) NULL);
1272 f = fopen (lib_file, "r");
1273 g_free (lib_file);
1274 if (f == NULL)
1275 return -1;
1278 args[0] = NULL;
1279 for (;;)
1281 line++;
1282 MC_PTR_FREE (l);
1283 if (read_one_line (&l, f) == 0)
1284 break;
1285 (void) get_args (l, args, 1023); /* Final NULL */
1286 if (args[0] == NULL)
1287 continue;
1289 /* Looking for `include ...` lines before first `file ...` ones */
1290 if (!found && strcmp (args[0], "include") == 0)
1292 if (g != NULL)
1293 continue;
1295 if (!args[1] || !(g = open_include_file (args[1])))
1297 result = line;
1298 break;
1300 goto found_type;
1303 /* looking for `file ...' lines only */
1304 if (strcmp (args[0], "file") != 0)
1305 continue;
1307 found = TRUE;
1309 /* must have two args or report error */
1310 if (!args[1] || !args[2])
1312 result = line;
1313 break;
1315 if (pnames && *pnames)
1317 /* 1: just collecting a list of names of rule sets */
1318 /* Reallocate the list if required */
1319 if (count % NENTRIES == 0)
1321 tmpnames =
1322 (char **) g_try_realloc (*pnames, (count + NENTRIES + 1) * sizeof (char *));
1323 if (tmpnames == NULL)
1324 break;
1325 *pnames = tmpnames;
1327 (*pnames)[count++] = g_strdup (args[2]);
1328 (*pnames)[count] = NULL;
1330 else if (type)
1332 /* 2: rule set was explicitly specified by the caller */
1333 if (strcmp (type, args[2]) == 0)
1334 goto found_type;
1336 else if (editor_file && edit)
1338 /* 3: auto-detect rule set from regular expressions */
1339 int q;
1341 q = mc_search (args[1], editor_file, MC_SEARCH_T_REGEX);
1342 /* does filename match arg 1 ? */
1343 if (!q && args[3])
1345 /* does first line match arg 3 ? */
1346 q = mc_search (args[3], first_line, MC_SEARCH_T_REGEX);
1348 if (q)
1350 int line_error;
1351 char *syntax_type;
1352 found_type:
1353 syntax_type = args[2];
1354 line_error = edit_read_syntax_rules (edit, g ? g : f, args, 1023);
1355 if (line_error)
1357 if (!error_file_name) /* an included file */
1358 result = line + line_error;
1359 else
1360 result = line_error;
1362 else
1364 g_free (edit->syntax_type);
1365 edit->syntax_type = g_strdup (syntax_type);
1366 /* if there are no rules then turn off syntax highlighting for speed */
1367 if (!g && !edit->rules[1])
1368 if (!edit->rules[0]->keyword[1] && !edit->rules[0]->spelling)
1370 edit_free_syntax_rules (edit);
1371 break;
1375 if (g == NULL)
1376 break;
1378 fclose (g);
1379 g = NULL;
1383 g_free (l);
1384 fclose (f);
1385 return result;
1388 /* --------------------------------------------------------------------------------------------- */
1390 static char *
1391 get_first_editor_line (WEdit * edit)
1393 size_t i;
1394 static char s[256];
1396 s[0] = '\0';
1397 if (edit == NULL)
1398 return s;
1400 for (i = 0; i < sizeof (s) - 1; i++)
1402 s[i] = edit_get_byte (edit, i);
1403 if (s[i] == '\n')
1405 s[i] = '\0';
1406 break;
1409 s[sizeof (s) - 1] = '\0';
1410 return s;
1413 /* --------------------------------------------------------------------------------------------- */
1414 /*** public functions ****************************************************************************/
1415 /* --------------------------------------------------------------------------------------------- */
1417 void
1418 edit_get_syntax_color (WEdit * edit, long byte_index, int *color)
1420 if (!tty_use_colors ())
1421 *color = 0;
1422 else if (edit->rules && byte_index < edit->last_byte && option_syntax_highlighting)
1423 translate_rule_to_color (edit, edit_get_rule (edit, byte_index), color);
1424 else
1425 *color = EDITOR_NORMAL_COLOR;
1428 /* --------------------------------------------------------------------------------------------- */
1430 void
1431 edit_free_syntax_rules (WEdit * edit)
1433 size_t i, j;
1435 if (!edit)
1436 return;
1437 if (edit->defines)
1438 destroy_defines (&edit->defines);
1439 if (!edit->rules)
1440 return;
1442 edit_get_rule (edit, -1);
1443 MC_PTR_FREE (edit->syntax_type);
1445 for (i = 0; edit->rules[i]; i++)
1447 if (edit->rules[i]->keyword)
1449 for (j = 0; edit->rules[i]->keyword[j]; j++)
1451 MC_PTR_FREE (edit->rules[i]->keyword[j]->keyword);
1452 MC_PTR_FREE (edit->rules[i]->keyword[j]->whole_word_chars_left);
1453 MC_PTR_FREE (edit->rules[i]->keyword[j]->whole_word_chars_right);
1454 MC_PTR_FREE (edit->rules[i]->keyword[j]);
1457 MC_PTR_FREE (edit->rules[i]->left);
1458 MC_PTR_FREE (edit->rules[i]->right);
1459 MC_PTR_FREE (edit->rules[i]->whole_word_chars_left);
1460 MC_PTR_FREE (edit->rules[i]->whole_word_chars_right);
1461 MC_PTR_FREE (edit->rules[i]->keyword);
1462 MC_PTR_FREE (edit->rules[i]->keyword_first_chars);
1463 MC_PTR_FREE (edit->rules[i]);
1466 while (edit->syntax_marker)
1468 struct _syntax_marker *s = edit->syntax_marker->next;
1469 g_free (edit->syntax_marker);
1470 edit->syntax_marker = s;
1473 MC_PTR_FREE (edit->rules);
1474 tty_color_free_all_tmp ();
1477 /* --------------------------------------------------------------------------------------------- */
1479 * Load rules into edit struct. Either edit or *pnames must be NULL. If
1480 * edit is NULL, a list of types will be stored into names. If type is
1481 * NULL, then the type will be selected according to the filename.
1482 * type must be edit->syntax_type or NULL
1484 void
1485 edit_load_syntax (WEdit * edit, char ***pnames, const char *type)
1487 int r;
1488 char *f = NULL;
1490 if (option_auto_syntax)
1491 type = NULL;
1493 if (edit != NULL)
1495 char *saved_type;
1497 saved_type = g_strdup (type); /* save edit->syntax_type */
1498 edit_free_syntax_rules (edit);
1499 edit->syntax_type = saved_type; /* restore edit->syntax_type */
1502 if (!tty_use_colors ())
1503 return;
1505 if (!option_syntax_highlighting && (!pnames || !*pnames))
1506 return;
1508 if (edit != NULL && edit->filename_vpath == NULL)
1509 return;
1511 f = mc_config_get_full_path (EDIT_SYNTAX_FILE);
1512 if (edit != NULL)
1514 char *tmp_f;
1516 tmp_f = vfs_path_to_str (edit->filename_vpath);
1517 r = edit_read_syntax_file (edit, pnames, f, tmp_f,
1518 get_first_editor_line (edit),
1519 option_auto_syntax ? NULL : edit->syntax_type);
1520 g_free (tmp_f);
1522 else
1523 r = edit_read_syntax_file (NULL, pnames, f, NULL, "", NULL);
1524 if (r == -1)
1526 edit_free_syntax_rules (edit);
1527 message (D_ERROR, _("Load syntax file"),
1528 _("Cannot open file %s\n%s"), f, unix_error_string (errno));
1530 else if (r != 0)
1532 edit_free_syntax_rules (edit);
1533 message (D_ERROR, _("Load syntax file"),
1534 _("Error in file %s on line %d"), error_file_name ? error_file_name : f, r);
1535 MC_PTR_FREE (error_file_name);
1538 g_free (f);
1541 /* --------------------------------------------------------------------------------------------- */
1543 const char *
1544 edit_get_syntax_type (const WEdit * edit)
1546 return edit->syntax_type;
1549 /* --------------------------------------------------------------------------------------------- */