Sync with gnulib 62bb7a8bf95807d6339e1e17fc0d21c319b280a2.
[midnight-commander.git] / src / editor / syntax.c
blob7be2da25f313e9012dcb4c106c554e2a97868262
1 /*
2 Editor syntax highlighting.
4 Copyright (C) 1996, 1997, 1998, 2001, 2002, 2003, 2004, 2005, 2006,
5 2007, 2010, 2011
6 The Free Software Foundation, Inc.
8 Written by:
9 Paul Sheer, 1998
10 Egmont Koblinger <egmont@gmail.com>, 2010
12 This file is part of the Midnight Commander.
14 The Midnight Commander is free software: you can redistribute it
15 and/or modify it under the terms of the GNU General Public License as
16 published by the Free Software Foundation, either version 3 of the License,
17 or (at your option) any later version.
19 The Midnight Commander is distributed in the hope that it will be useful,
20 but WITHOUT ANY WARRANTY; without even the implied warranty of
21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 GNU General Public License for more details.
24 You should have received a copy of the GNU General Public License
25 along with this program. If not, see <http://www.gnu.org/licenses/>.
28 /** \file
29 * \brief Source: editor syntax highlighting
30 * \author Paul Sheer
31 * \date 1996, 1997
32 * \author Mikhail Pobolovets
33 * \date 2010
35 * Mispelled words are flushed from the syntax highlighting rules
36 * when they have been around longer than
37 * TRANSIENT_WORD_TIME_OUT seconds. At a cursor rate of 30
38 * chars per second and say 3 chars + a space per word, we can
39 * accumulate 450 words absolute max with a value of 60. This is
40 * below this limit of 1024 words in a context.
43 #include <config.h>
45 #include <stdio.h>
46 #include <stdarg.h>
47 #include <sys/types.h>
48 #include <unistd.h>
49 #include <string.h>
50 #include <ctype.h>
51 #include <errno.h>
52 #include <sys/stat.h>
53 #include <stdlib.h>
55 #include "lib/global.h"
56 #include "lib/search.h" /* search engine */
57 #include "lib/skin.h"
58 #include "lib/strutil.h" /* utf string functions */
59 #include "lib/util.h"
60 #include "lib/widget.h" /* message() */
62 #include "edit-impl.h"
63 #include "editwidget.h"
65 /*** global variables ****************************************************************************/
67 int option_syntax_highlighting = 1;
68 int option_auto_syntax = 1;
70 /*** file scope macro definitions ****************************************************************/
72 /* bytes */
73 #define SYNTAX_MARKER_DENSITY 512
75 #define TRANSIENT_WORD_TIME_OUT 60
77 #define UNKNOWN_FORMAT "unknown"
79 #define MAX_WORDS_PER_CONTEXT 1024
80 #define MAX_CONTEXTS 128
82 #define RULE_ON_LEFT_BORDER 1
83 #define RULE_ON_RIGHT_BORDER 2
85 #define SYNTAX_TOKEN_STAR '\001'
86 #define SYNTAX_TOKEN_PLUS '\002'
87 #define SYNTAX_TOKEN_BRACKET '\003'
88 #define SYNTAX_TOKEN_BRACE '\004'
90 #define whiteness(x) ((x) == '\t' || (x) == '\n' || (x) == ' ')
92 #define free_args(x)
93 #define break_a {result=line;break;}
94 #define check_a {if(!*a){result=line;break;}}
95 #define check_not_a {if(*a){result=line;break;}}
97 /*** file scope type declarations ****************************************************************/
99 struct key_word
101 char *keyword;
102 unsigned char first;
103 char *whole_word_chars_left;
104 char *whole_word_chars_right;
105 long line_start;
106 int color;
109 struct context_rule
111 char *left;
112 unsigned char first_left;
113 char *right;
114 unsigned char first_right;
115 char line_start_left;
116 char line_start_right;
117 int between_delimiters;
118 char *whole_word_chars_left;
119 char *whole_word_chars_right;
120 char *keyword_first_chars;
121 gboolean spelling;
122 /* first word is word[1] */
123 struct key_word **keyword;
126 typedef struct
128 off_t offset;
129 edit_syntax_rule_t rule;
130 } syntax_marker_t;
132 /*** file scope variables ************************************************************************/
134 static char *error_file_name = NULL;
136 /*** file scope functions ************************************************************************/
137 /* --------------------------------------------------------------------------------------------- */
139 static gint
140 mc_defines_destroy (gpointer key, gpointer value, gpointer data)
142 (void) data;
144 g_free (key);
145 g_strfreev ((char **) value);
147 return FALSE;
150 /* --------------------------------------------------------------------------------------------- */
151 /** Completely destroys the defines tree */
153 static void
154 destroy_defines (GTree ** defines)
156 g_tree_foreach (*defines, mc_defines_destroy, NULL);
157 g_tree_destroy (*defines);
158 *defines = NULL;
161 /* --------------------------------------------------------------------------------------------- */
163 /** Wrapper for case insensitive mode */
164 inline static int
165 xx_tolower (const WEdit * edit, int c)
167 return edit->is_case_insensitive ? tolower (c) : c;
170 /* --------------------------------------------------------------------------------------------- */
172 static void
173 subst_defines (GTree * defines, char **argv, char **argv_end)
175 char **t, **p;
176 int argc;
178 while (*argv != NULL && argv < argv_end)
180 t = g_tree_lookup (defines, *argv);
181 if (t != NULL)
183 int count = 0;
185 /* Count argv array members */
186 argc = 0;
187 for (p = &argv[1]; *p != NULL; p++)
188 argc++;
190 /* Count members of definition array */
191 for (p = t; *p != NULL; p++)
192 count++;
193 p = &argv[count + argc];
195 /* Buffer overflow or infinitive loop in define */
196 if (p >= argv_end)
197 break;
199 /* Move rest of argv after definition members */
200 while (argc >= 0)
201 *p-- = argv[argc-- + 1];
203 /* Copy definition members to argv */
204 for (p = argv; *t != NULL; *p++ = *t++)
207 argv++;
211 /* --------------------------------------------------------------------------------------------- */
213 static off_t
214 compare_word_to_right (const WEdit * edit, off_t i, const char *text,
215 const char *whole_left, const char *whole_right, long line_start)
217 const unsigned char *p, *q;
218 int c, d, j;
220 if (*text == '\0')
221 return -1;
223 c = xx_tolower (edit, edit_get_byte (edit, i - 1));
224 if ((line_start != 0 && c != '\n') || (whole_left != NULL && strchr (whole_left, c) != NULL))
225 return -1;
227 for (p = (unsigned char *) text, q = p + str_term_width1 ((char *) p); p < q; p++, i++)
229 switch (*p)
231 case SYNTAX_TOKEN_STAR:
232 if (++p > q)
233 return -1;
234 while (TRUE)
236 c = xx_tolower (edit, edit_get_byte (edit, i));
237 if (*p == '\0' && whole_right != NULL && strchr (whole_right, c) == NULL)
238 break;
239 if (c == *p)
240 break;
241 if (c == '\n')
242 return -1;
243 i++;
245 break;
246 case SYNTAX_TOKEN_PLUS:
247 if (++p > q)
248 return -1;
249 j = 0;
250 while (TRUE)
252 c = xx_tolower (edit, edit_get_byte (edit, i));
253 if (c == *p)
255 j = i;
256 if (*p == *text && p[1] == '\0') /* handle eg '+' and @+@ keywords properly */
257 break;
259 if (j != 0 && strchr ((char *) p + 1, c) != NULL) /* c exists further down, so it will get matched later */
260 break;
261 if (c == '\n' || c == '\t' || c == ' ' ||
262 (whole_right != NULL && strchr (whole_right, c) == NULL))
264 if (*p == '\0')
266 i--;
267 break;
269 if (j == 0)
270 return -1;
271 i = j;
272 break;
274 i++;
276 break;
277 case SYNTAX_TOKEN_BRACKET:
278 if (++p > q)
279 return -1;
280 c = -1;
281 while (TRUE)
283 d = c;
284 c = xx_tolower (edit, edit_get_byte (edit, i));
285 for (j = 0; p[j] != SYNTAX_TOKEN_BRACKET && p[j]; j++)
286 if (c == p[j])
287 goto found_char2;
288 break;
289 found_char2:
290 i++;
292 i--;
293 while (*p != SYNTAX_TOKEN_BRACKET && p <= q)
294 p++;
295 if (p > q)
296 return -1;
297 if (p[1] == d)
298 i--;
299 break;
300 case SYNTAX_TOKEN_BRACE:
301 if (++p > q)
302 return -1;
303 c = xx_tolower (edit, edit_get_byte (edit, i));
304 for (; *p != SYNTAX_TOKEN_BRACE && *p; p++)
305 if (c == *p)
306 goto found_char3;
307 return -1;
308 found_char3:
309 while (*p != SYNTAX_TOKEN_BRACE && p < q)
310 p++;
311 break;
312 default:
313 if (*p != xx_tolower (edit, edit_get_byte (edit, i)))
314 return -1;
317 return (whole_right != NULL &&
318 strchr (whole_right, xx_tolower (edit, edit_get_byte (edit, i))) != NULL) ? -1 : i;
321 /* --------------------------------------------------------------------------------------------- */
323 static const char *
324 xx_strchr (const WEdit * edit, const unsigned char *s, int char_byte)
326 while (*s >= '\005' && xx_tolower (edit, *s) != char_byte)
327 s++;
329 return (const char *) s;
332 /* --------------------------------------------------------------------------------------------- */
334 static edit_syntax_rule_t
335 apply_rules_going_right (WEdit * edit, off_t i, edit_syntax_rule_t rule)
337 struct context_rule *r;
338 int c;
339 gboolean contextchanged = FALSE;
340 gboolean found_left = FALSE, found_right = FALSE;
341 gboolean keyword_foundleft = FALSE, keyword_foundright = FALSE;
342 gboolean is_end;
343 off_t end = 0;
344 edit_syntax_rule_t _rule = rule;
346 c = xx_tolower (edit, edit_get_byte (edit, i));
347 if (c == 0)
348 return rule;
349 is_end = (rule.end == i);
351 /* check to turn off a keyword */
352 if (_rule.keyword)
354 if (edit_get_byte (edit, i - 1) == '\n')
355 _rule.keyword = 0;
356 if (is_end)
358 _rule.keyword = 0;
359 keyword_foundleft = TRUE;
363 /* check to turn off a context */
364 if (_rule.context && !_rule.keyword)
366 off_t e;
368 r = edit->rules[_rule.context];
369 if (r->first_right == c && !(rule.border & RULE_ON_RIGHT_BORDER)
370 && (e =
371 compare_word_to_right (edit, i, r->right, r->whole_word_chars_left,
372 r->whole_word_chars_right, r->line_start_right)) > 0)
374 _rule.end = e;
375 found_right = TRUE;
376 _rule.border = RULE_ON_RIGHT_BORDER;
377 if (r->between_delimiters)
378 _rule.context = 0;
380 else if (is_end && rule.border & RULE_ON_RIGHT_BORDER)
382 /* always turn off a context at 4 */
383 found_left = TRUE;
384 _rule.border = 0;
385 if (!keyword_foundleft)
386 _rule.context = 0;
388 else if (is_end && rule.border & RULE_ON_LEFT_BORDER)
390 /* never turn off a context at 2 */
391 found_left = TRUE;
392 _rule.border = 0;
396 /* check to turn on a keyword */
397 if (!_rule.keyword)
399 const char *p;
401 r = edit->rules[_rule.context];
402 p = r->keyword_first_chars;
404 if (p != NULL)
405 while (*(p = xx_strchr (edit, (unsigned char *) p + 1, c)) != '\0')
407 struct key_word *k;
408 int count;
409 off_t e;
411 count = p - r->keyword_first_chars;
412 k = r->keyword[count];
413 e = compare_word_to_right (edit, i, k->keyword, k->whole_word_chars_left,
414 k->whole_word_chars_right, k->line_start);
415 if (e > 0)
417 end = e;
418 _rule.end = e;
419 _rule.keyword = count;
420 keyword_foundright = TRUE;
421 break;
426 /* check to turn on a context */
427 if (!_rule.context)
429 if (!found_left && is_end)
431 if (rule.border & RULE_ON_RIGHT_BORDER)
433 _rule.border = 0;
434 _rule.context = 0;
435 contextchanged = TRUE;
436 _rule.keyword = 0;
439 else if (rule.border & RULE_ON_LEFT_BORDER)
441 r = edit->rules[_rule._context];
442 _rule.border = 0;
443 if (r->between_delimiters)
445 _rule.context = _rule._context;
446 contextchanged = TRUE;
447 _rule.keyword = 0;
449 if (r->first_right == c)
451 off_t e;
453 e = compare_word_to_right (edit, i, r->right, r->whole_word_chars_left,
454 r->whole_word_chars_right, r->line_start_right);
455 if (e >= end)
457 _rule.end = e;
458 found_right = TRUE;
459 _rule.border = RULE_ON_RIGHT_BORDER;
460 _rule.context = 0;
467 if (!found_right)
469 int count;
470 struct context_rule **rules = edit->rules;
472 for (count = 1; rules[count]; count++)
474 r = rules[count];
475 if (r->first_left == c)
477 off_t e;
479 e = compare_word_to_right (edit, i, r->left, r->whole_word_chars_left,
480 r->whole_word_chars_right, r->line_start_left);
481 if (e >= end && (!_rule.keyword || keyword_foundright))
483 _rule.end = e;
484 found_right = TRUE;
485 _rule.border = RULE_ON_LEFT_BORDER;
486 _rule._context = count;
487 if (!r->between_delimiters && !_rule.keyword)
489 _rule.context = count;
490 contextchanged = TRUE;
492 break;
499 /* check again to turn on a keyword if the context switched */
500 if (contextchanged && !_rule.keyword)
502 const char *p;
504 r = edit->rules[_rule.context];
505 p = r->keyword_first_chars;
507 while (*(p = xx_strchr (edit, (unsigned char *) p + 1, c)) != '\0')
509 struct key_word *k;
510 int count;
511 off_t e;
513 count = p - r->keyword_first_chars;
514 k = r->keyword[count];
515 e = compare_word_to_right (edit, i, k->keyword, k->whole_word_chars_left,
516 k->whole_word_chars_right, k->line_start);
517 if (e > 0)
519 _rule.end = e;
520 _rule.keyword = count;
521 break;
526 return _rule;
529 /* --------------------------------------------------------------------------------------------- */
531 static edit_syntax_rule_t
532 edit_get_rule (WEdit * edit, off_t byte_index)
534 off_t i;
536 if (byte_index > edit->last_get_rule)
538 for (i = edit->last_get_rule + 1; i <= byte_index; i++)
540 off_t d = SYNTAX_MARKER_DENSITY;
542 edit->rule = apply_rules_going_right (edit, i, edit->rule);
544 if (edit->syntax_marker != NULL)
545 d += ((syntax_marker_t *) edit->syntax_marker->data)->offset;
547 if (i > d)
549 syntax_marker_t *s;
551 s = g_new (syntax_marker_t, 1);
552 s->offset = i;
553 s->rule = edit->rule;
554 edit->syntax_marker = g_slist_prepend (edit->syntax_marker, s);
558 else if (byte_index < edit->last_get_rule)
560 while (TRUE)
562 syntax_marker_t *s;
564 if (edit->syntax_marker == NULL)
566 memset (&edit->rule, 0, sizeof (edit->rule));
567 for (i = -1; i <= byte_index; i++)
568 edit->rule = apply_rules_going_right (edit, i, edit->rule);
569 break;
572 s = (syntax_marker_t *) edit->syntax_marker->data;
574 if (byte_index >= s->offset)
576 edit->rule = s->rule;
577 for (i = s->offset + 1; i <= byte_index; i++)
578 edit->rule = apply_rules_going_right (edit, i, edit->rule);
579 break;
582 g_free (s);
583 edit->syntax_marker = g_slist_delete_link (edit->syntax_marker, edit->syntax_marker);
586 edit->last_get_rule = byte_index;
587 return edit->rule;
590 /* --------------------------------------------------------------------------------------------- */
592 static inline int
593 translate_rule_to_color (const WEdit * edit, edit_syntax_rule_t rule)
595 return edit->rules[rule.context]->keyword[rule.keyword]->color;
598 /* --------------------------------------------------------------------------------------------- */
600 Returns 0 on error/eof or a count of the number of bytes read
601 including the newline. Result must be free'd.
602 In case of an error, *line will not be modified.
605 static size_t
606 read_one_line (char **line, FILE * f)
608 GString *p;
609 size_t r = 0;
611 /* not reallocate string too often */
612 p = g_string_sized_new (64);
614 while (TRUE)
616 int c;
618 c = fgetc (f);
619 if (c == EOF)
621 if (ferror (f))
623 if (errno == EINTR)
624 continue;
625 r = 0;
627 break;
629 r++;
631 /* handle all of \r\n, \r, \n correctly. */
632 if (c == '\n')
633 break;
634 if (c == '\r')
636 c = fgetc (f);
637 if (c == '\n')
638 r++;
639 else
640 ungetc (c, f);
641 break;
644 g_string_append_c (p, c);
646 if (r != 0)
647 *line = g_string_free (p, FALSE);
648 else
649 g_string_free (p, TRUE);
651 return r;
654 /* --------------------------------------------------------------------------------------------- */
656 static char *
657 convert (char *s)
659 char *r, *p;
661 p = r = s;
662 while (*s)
664 switch (*s)
666 case '\\':
667 s++;
668 switch (*s)
670 case ' ':
671 *p = ' ';
672 s--;
673 break;
674 case 'n':
675 *p = '\n';
676 break;
677 case 'r':
678 *p = '\r';
679 break;
680 case 't':
681 *p = '\t';
682 break;
683 case 's':
684 *p = ' ';
685 break;
686 case '*':
687 *p = '*';
688 break;
689 case '\\':
690 *p = '\\';
691 break;
692 case '[':
693 case ']':
694 *p = SYNTAX_TOKEN_BRACKET;
695 break;
696 case '{':
697 case '}':
698 *p = SYNTAX_TOKEN_BRACE;
699 break;
700 case 0:
701 *p = *s;
702 return r;
703 default:
704 *p = *s;
705 break;
707 break;
708 case '*':
709 *p = SYNTAX_TOKEN_STAR;
710 break;
711 case '+':
712 *p = SYNTAX_TOKEN_PLUS;
713 break;
714 default:
715 *p = *s;
716 break;
718 s++;
719 p++;
721 *p = '\0';
722 return r;
725 /* --------------------------------------------------------------------------------------------- */
727 static int
728 get_args (char *l, char **args, int args_size)
730 int argc = 0;
732 while (argc < args_size)
734 char *p = l;
735 while (*p != '\0' && whiteness (*p))
736 p++;
737 if (*p == '\0')
738 break;
739 for (l = p + 1; *l != '\0' && !whiteness (*l); l++)
741 if (*l != '\0')
742 *l++ = '\0';
743 args[argc++] = convert (p);
745 args[argc] = (char *) NULL;
746 return argc;
749 /* --------------------------------------------------------------------------------------------- */
751 static int
752 this_try_alloc_color_pair (const char *fg, const char *bg, const char *attrs)
754 char f[80], b[80], a[80], *p;
756 if (bg != NULL && *bg == '\0')
757 bg = NULL;
758 if (fg != NULL && *fg == '\0')
759 fg = NULL;
760 if (attrs != NULL && *attrs == '\0')
761 attrs = NULL;
763 if ((fg == NULL) && (bg == NULL))
764 return EDITOR_NORMAL_COLOR;
766 if (fg != NULL)
768 g_strlcpy (f, fg, sizeof (f));
769 p = strchr (f, '/');
770 if (p != NULL)
771 *p = '\0';
772 fg = f;
774 if (bg != NULL)
776 g_strlcpy (b, bg, sizeof (b));
777 p = strchr (b, '/');
778 if (p != NULL)
779 *p = '\0';
780 bg = b;
782 if ((fg == NULL) || (bg == NULL))
784 /* get colors from skin */
785 char *editnormal;
787 editnormal = mc_skin_get ("editor", "_default_", "default;default");
789 if (fg == NULL)
791 g_strlcpy (f, editnormal, sizeof (f));
792 p = strchr (f, ';');
793 if (p != NULL)
794 *p = '\0';
795 if (f[0] == '\0')
796 g_strlcpy (f, "default", sizeof (f));
797 fg = f;
799 if (bg == NULL)
801 p = strchr (editnormal, ';');
802 if ((p != NULL) && (*(++p) != '\0'))
803 g_strlcpy (b, p, sizeof (b));
804 else
805 g_strlcpy (b, "default", sizeof (b));
806 bg = b;
809 g_free (editnormal);
812 if (attrs != NULL)
814 g_strlcpy (a, attrs, sizeof (a));
815 p = strchr (a, '/');
816 if (p != NULL)
817 *p = '\0';
818 /* get_args() mangles the + signs, unmangle 'em */
819 p = a;
820 while ((p = strchr (p, SYNTAX_TOKEN_PLUS)) != NULL)
821 *p++ = '+';
822 attrs = a;
824 return tty_try_alloc_color_pair (fg, bg, attrs);
827 /* --------------------------------------------------------------------------------------------- */
829 static FILE *
830 open_include_file (const char *filename)
832 FILE *f;
834 MC_PTR_FREE (error_file_name);
835 error_file_name = g_strdup (filename);
836 if (g_path_is_absolute (filename))
837 return fopen (filename, "r");
839 g_free (error_file_name);
840 error_file_name =
841 g_build_filename (mc_config_get_data_path (), EDIT_DIR, filename, (char *) NULL);
842 f = fopen (error_file_name, "r");
843 if (f != NULL)
844 return f;
846 g_free (error_file_name);
847 error_file_name = g_build_filename (mc_global.sysconfig_dir, "syntax", filename, (char *) NULL);
848 f = fopen (error_file_name, "r");
849 if (f != NULL)
850 return f;
852 g_free (error_file_name);
853 error_file_name =
854 g_build_filename (mc_global.share_data_dir, "syntax", filename, (char *) NULL);
856 return fopen (error_file_name, "r");
859 /* --------------------------------------------------------------------------------------------- */
861 inline static void
862 xx_lowerize_line (WEdit * edit, char *line, size_t len)
864 if (edit->is_case_insensitive)
866 size_t i;
867 for (i = 0; i < len; ++i)
868 line[i] = tolower (line[i]);
872 /* --------------------------------------------------------------------------------------------- */
873 /** returns line number on error */
875 static int
876 edit_read_syntax_rules (WEdit * edit, FILE * f, char **args, int args_size)
878 FILE *g = NULL;
879 char *fg, *bg, *attrs;
880 char last_fg[32] = "", last_bg[32] = "", last_attrs[64] = "";
881 char whole_right[512];
882 char whole_left[512];
883 char *l = 0;
884 int save_line = 0, line = 0;
885 struct context_rule **r, *c = NULL;
886 int num_words = -1, num_contexts = -1;
887 int result = 0;
888 int argc;
889 int i, j;
890 int alloc_contexts = MAX_CONTEXTS,
891 alloc_words_per_context = MAX_WORDS_PER_CONTEXT,
892 max_alloc_words_per_context = MAX_WORDS_PER_CONTEXT;
894 args[0] = NULL;
895 edit->is_case_insensitive = FALSE;
897 strcpy (whole_left, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_01234567890");
898 strcpy (whole_right, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_01234567890");
900 r = edit->rules = g_malloc0 (alloc_contexts * sizeof (struct context_rule *));
902 if (!edit->defines)
903 edit->defines = g_tree_new ((GCompareFunc) strcmp);
905 while (TRUE)
907 char **a;
908 size_t len;
910 line++;
911 l = 0;
913 len = read_one_line (&l, f);
914 if (len != 0)
915 xx_lowerize_line (edit, l, len);
916 else
918 if (g == NULL)
919 break;
921 fclose (f);
922 f = g;
923 g = NULL;
924 line = save_line + 1;
925 MC_PTR_FREE (error_file_name);
926 MC_PTR_FREE (l);
927 len = read_one_line (&l, f);
928 if (len == 0)
929 break;
930 xx_lowerize_line (edit, l, len);
933 argc = get_args (l, args, args_size);
934 a = args + 1;
935 if (args[0] == NULL)
937 /* do nothing */
939 else if (strcmp (args[0], "include") == 0)
941 if (g != NULL || argc != 2)
943 result = line;
944 break;
946 g = f;
947 f = open_include_file (args[1]);
948 if (f == NULL)
950 MC_PTR_FREE (error_file_name);
951 result = line;
952 break;
954 save_line = line;
955 line = 0;
957 else if (strcmp (args[0], "caseinsensitive") == 0)
959 edit->is_case_insensitive = TRUE;
961 else if (strcmp (args[0], "wholechars") == 0)
963 check_a;
964 if (strcmp (*a, "left") == 0)
966 a++;
967 g_strlcpy (whole_left, *a, sizeof (whole_left));
969 else if (strcmp (*a, "right") == 0)
971 a++;
972 g_strlcpy (whole_right, *a, sizeof (whole_right));
974 else
976 g_strlcpy (whole_left, *a, sizeof (whole_left));
977 g_strlcpy (whole_right, *a, sizeof (whole_right));
979 a++;
980 check_not_a;
982 else if (strcmp (args[0], "context") == 0)
984 check_a;
985 if (num_contexts == -1)
987 if (strcmp (*a, "default") != 0)
988 { /* first context is the default */
989 break_a;
991 a++;
992 c = r[0] = g_malloc0 (sizeof (struct context_rule));
993 c->left = g_strdup (" ");
994 c->right = g_strdup (" ");
995 num_contexts = 0;
997 else
999 /* Terminate previous context. */
1000 r[num_contexts - 1]->keyword[num_words] = NULL;
1001 c = r[num_contexts] = g_malloc0 (sizeof (struct context_rule));
1002 if (strcmp (*a, "exclusive") == 0)
1004 a++;
1005 c->between_delimiters = 1;
1007 check_a;
1008 if (strcmp (*a, "whole") == 0)
1010 a++;
1011 c->whole_word_chars_left = g_strdup (whole_left);
1012 c->whole_word_chars_right = g_strdup (whole_right);
1014 else if (strcmp (*a, "wholeleft") == 0)
1016 a++;
1017 c->whole_word_chars_left = g_strdup (whole_left);
1019 else if (strcmp (*a, "wholeright") == 0)
1021 a++;
1022 c->whole_word_chars_right = g_strdup (whole_right);
1024 check_a;
1025 if (strcmp (*a, "linestart") == 0)
1027 a++;
1028 c->line_start_left = 1;
1030 check_a;
1031 c->left = g_strdup (*a++);
1032 check_a;
1033 if (strcmp (*a, "linestart") == 0)
1035 a++;
1036 c->line_start_right = 1;
1038 check_a;
1039 c->right = g_strdup (*a++);
1040 c->first_left = *c->left;
1041 c->first_right = *c->right;
1043 c->keyword = g_malloc (alloc_words_per_context * sizeof (struct key_word *));
1044 num_words = 1;
1045 c->keyword[0] = g_malloc0 (sizeof (struct key_word));
1046 subst_defines (edit->defines, a, &args[1024]);
1047 fg = *a;
1048 if (*a != '\0')
1049 a++;
1050 bg = *a;
1051 if (*a != '\0')
1052 a++;
1053 attrs = *a;
1054 if (*a != '\0')
1055 a++;
1056 g_strlcpy (last_fg, fg != NULL ? fg : "", sizeof (last_fg));
1057 g_strlcpy (last_bg, bg != NULL ? bg : "", sizeof (last_bg));
1058 g_strlcpy (last_attrs, attrs != NULL ? attrs : "", sizeof (last_attrs));
1059 c->keyword[0]->color = this_try_alloc_color_pair (fg, bg, attrs);
1060 c->keyword[0]->keyword = g_strdup (" ");
1061 check_not_a;
1063 alloc_words_per_context = MAX_WORDS_PER_CONTEXT;
1064 if (++num_contexts >= alloc_contexts)
1066 struct context_rule **tmp;
1068 alloc_contexts += 128;
1069 tmp = g_realloc (r, alloc_contexts * sizeof (struct context_rule *));
1070 r = tmp;
1073 else if (strcmp (args[0], "spellcheck") == 0)
1075 if (c == NULL)
1077 result = line;
1078 break;
1080 c->spelling = TRUE;
1082 else if (strcmp (args[0], "keyword") == 0)
1084 struct key_word *k;
1086 if (num_words == -1)
1087 break_a;
1088 check_a;
1089 k = r[num_contexts - 1]->keyword[num_words] = g_malloc0 (sizeof (struct key_word));
1090 if (strcmp (*a, "whole") == 0)
1092 a++;
1093 k->whole_word_chars_left = g_strdup (whole_left);
1094 k->whole_word_chars_right = g_strdup (whole_right);
1096 else if (strcmp (*a, "wholeleft") == 0)
1098 a++;
1099 k->whole_word_chars_left = g_strdup (whole_left);
1101 else if (strcmp (*a, "wholeright") == 0)
1103 a++;
1104 k->whole_word_chars_right = g_strdup (whole_right);
1106 check_a;
1107 if (strcmp (*a, "linestart") == 0)
1109 a++;
1110 k->line_start = 1;
1112 check_a;
1113 if (strcmp (*a, "whole") == 0)
1115 break_a;
1117 k->keyword = g_strdup (*a++);
1118 k->first = *k->keyword;
1119 subst_defines (edit->defines, a, &args[1024]);
1120 fg = *a;
1121 if (*a != '\0')
1122 a++;
1123 bg = *a;
1124 if (*a != '\0')
1125 a++;
1126 attrs = *a;
1127 if (*a != '\0')
1128 a++;
1129 if (fg == NULL)
1130 fg = last_fg;
1131 if (bg == NULL)
1132 bg = last_bg;
1133 if (attrs == NULL)
1134 attrs = last_attrs;
1135 k->color = this_try_alloc_color_pair (fg, bg, attrs);
1136 check_not_a;
1138 if (++num_words >= alloc_words_per_context)
1140 struct key_word **tmp;
1142 alloc_words_per_context += 1024;
1144 if (alloc_words_per_context > max_alloc_words_per_context)
1145 max_alloc_words_per_context = alloc_words_per_context;
1147 tmp = g_realloc (c->keyword, alloc_words_per_context * sizeof (struct key_word *));
1148 c->keyword = tmp;
1151 else if (*(args[0]) == '#')
1153 /* do nothing for comment */
1155 else if (strcmp (args[0], "file") == 0)
1157 break;
1159 else if (strcmp (args[0], "define") == 0)
1161 char *key = *a++;
1162 char **argv;
1164 if (argc < 3)
1165 break_a;
1166 argv = g_tree_lookup (edit->defines, key);
1167 if (argv != NULL)
1168 mc_defines_destroy (NULL, argv, NULL);
1169 else
1170 key = g_strdup (key);
1172 argv = g_new (char *, argc - 1);
1173 g_tree_insert (edit->defines, key, argv);
1174 while (*a != NULL)
1175 *argv++ = g_strdup (*a++);
1176 *argv = NULL;
1178 else
1179 { /* anything else is an error */
1180 break_a;
1182 free_args (args);
1183 MC_PTR_FREE (l);
1185 free_args (args);
1186 MC_PTR_FREE (l);
1188 /* Terminate context array. */
1189 if (num_contexts > 0)
1191 r[num_contexts - 1]->keyword[num_words] = NULL;
1192 r[num_contexts] = NULL;
1195 if (edit->rules[0] == NULL)
1196 MC_PTR_FREE (edit->rules);
1198 if (result == 0)
1200 char *first_chars, *p;
1202 if (num_contexts == -1)
1203 return line;
1205 first_chars = g_malloc0 (max_alloc_words_per_context + 2);
1207 for (i = 0; edit->rules[i] != NULL; i++)
1209 c = edit->rules[i];
1210 p = first_chars;
1211 *p++ = (char) 1;
1212 for (j = 1; c->keyword[j] != NULL; j++)
1213 *p++ = c->keyword[j]->first;
1214 *p = '\0';
1215 c->keyword_first_chars = g_strdup (first_chars);
1218 g_free (first_chars);
1221 return result;
1224 /* --------------------------------------------------------------------------------------------- */
1226 /* returns -1 on file error, line number on error in file syntax */
1227 static int
1228 edit_read_syntax_file (WEdit * edit, char ***pnames, const char *syntax_file,
1229 const char *editor_file, const char *first_line, const char *type)
1231 #define NENTRIES 30
1232 FILE *f, *g = NULL;
1233 char *args[1024], *l = NULL;
1234 long line = 0;
1235 int result = 0;
1236 int count = 0;
1237 char *lib_file;
1238 gboolean found = FALSE;
1239 char **tmpnames = NULL;
1241 f = fopen (syntax_file, "r");
1242 if (f == NULL)
1244 lib_file = g_build_filename (mc_global.share_data_dir, "syntax", "Syntax", (char *) NULL);
1245 f = fopen (lib_file, "r");
1246 g_free (lib_file);
1247 if (f == NULL)
1248 return -1;
1251 args[0] = NULL;
1252 while (TRUE)
1254 line++;
1255 MC_PTR_FREE (l);
1256 if (read_one_line (&l, f) == 0)
1257 break;
1258 (void) get_args (l, args, 1023); /* Final NULL */
1259 if (args[0] == NULL)
1260 continue;
1262 /* Looking for `include ...` lines before first `file ...` ones */
1263 if (!found && strcmp (args[0], "include") == 0)
1265 if (g != NULL)
1266 continue;
1268 if (!args[1] || !(g = open_include_file (args[1])))
1270 result = line;
1271 break;
1273 goto found_type;
1276 /* looking for `file ...' lines only */
1277 if (strcmp (args[0], "file") != 0)
1278 continue;
1280 found = TRUE;
1282 /* must have two args or report error */
1283 if (!args[1] || !args[2])
1285 result = line;
1286 break;
1288 if (pnames && *pnames)
1290 /* 1: just collecting a list of names of rule sets */
1291 /* Reallocate the list if required */
1292 if (count % NENTRIES == 0)
1294 tmpnames =
1295 (char **) g_try_realloc (*pnames, (count + NENTRIES + 1) * sizeof (char *));
1296 if (tmpnames == NULL)
1297 break;
1298 *pnames = tmpnames;
1300 (*pnames)[count++] = g_strdup (args[2]);
1301 (*pnames)[count] = NULL;
1303 else if (type)
1305 /* 2: rule set was explicitly specified by the caller */
1306 if (strcmp (type, args[2]) == 0)
1307 goto found_type;
1309 else if (editor_file && edit)
1311 /* 3: auto-detect rule set from regular expressions */
1312 int q;
1314 q = mc_search (args[1], editor_file, MC_SEARCH_T_REGEX);
1315 /* does filename match arg 1 ? */
1316 if (!q && args[3])
1318 /* does first line match arg 3 ? */
1319 q = mc_search (args[3], first_line, MC_SEARCH_T_REGEX);
1321 if (q)
1323 int line_error;
1324 char *syntax_type;
1325 found_type:
1326 syntax_type = args[2];
1327 line_error = edit_read_syntax_rules (edit, g ? g : f, args, 1023);
1328 if (line_error)
1330 if (!error_file_name) /* an included file */
1331 result = line + line_error;
1332 else
1333 result = line_error;
1335 else
1337 g_free (edit->syntax_type);
1338 edit->syntax_type = g_strdup (syntax_type);
1339 /* if there are no rules then turn off syntax highlighting for speed */
1340 if (!g && !edit->rules[1])
1341 if (!edit->rules[0]->keyword[1] && !edit->rules[0]->spelling)
1343 edit_free_syntax_rules (edit);
1344 break;
1348 if (g == NULL)
1349 break;
1351 fclose (g);
1352 g = NULL;
1356 g_free (l);
1357 fclose (f);
1358 return result;
1361 /* --------------------------------------------------------------------------------------------- */
1363 static const char *
1364 get_first_editor_line (WEdit * edit)
1366 static char s[256];
1368 s[0] = '\0';
1370 if (edit != NULL)
1372 size_t i;
1374 for (i = 0; i < sizeof (s) - 1; i++)
1376 s[i] = edit_get_byte (edit, i);
1377 if (s[i] == '\n')
1379 s[i] = '\0';
1380 break;
1384 s[sizeof (s) - 1] = '\0';
1387 return s;
1390 /* --------------------------------------------------------------------------------------------- */
1391 /*** public functions ****************************************************************************/
1392 /* --------------------------------------------------------------------------------------------- */
1395 edit_get_syntax_color (WEdit * edit, off_t byte_index)
1397 if (!tty_use_colors ())
1398 return 0;
1400 if (edit->rules != NULL && byte_index < edit->last_byte && option_syntax_highlighting)
1401 return translate_rule_to_color (edit, edit_get_rule (edit, byte_index));
1403 return EDITOR_NORMAL_COLOR;
1406 /* --------------------------------------------------------------------------------------------- */
1408 void
1409 edit_free_syntax_rules (WEdit * edit)
1411 size_t i, j;
1413 if (!edit)
1414 return;
1415 if (edit->defines)
1416 destroy_defines (&edit->defines);
1417 if (!edit->rules)
1418 return;
1420 edit_get_rule (edit, -1);
1421 MC_PTR_FREE (edit->syntax_type);
1423 for (i = 0; edit->rules[i]; i++)
1425 if (edit->rules[i]->keyword)
1427 for (j = 0; edit->rules[i]->keyword[j]; j++)
1429 MC_PTR_FREE (edit->rules[i]->keyword[j]->keyword);
1430 MC_PTR_FREE (edit->rules[i]->keyword[j]->whole_word_chars_left);
1431 MC_PTR_FREE (edit->rules[i]->keyword[j]->whole_word_chars_right);
1432 MC_PTR_FREE (edit->rules[i]->keyword[j]);
1435 MC_PTR_FREE (edit->rules[i]->left);
1436 MC_PTR_FREE (edit->rules[i]->right);
1437 MC_PTR_FREE (edit->rules[i]->whole_word_chars_left);
1438 MC_PTR_FREE (edit->rules[i]->whole_word_chars_right);
1439 MC_PTR_FREE (edit->rules[i]->keyword);
1440 MC_PTR_FREE (edit->rules[i]->keyword_first_chars);
1441 MC_PTR_FREE (edit->rules[i]);
1444 g_slist_foreach (edit->syntax_marker, (GFunc) g_free, NULL);
1445 g_slist_free (edit->syntax_marker);
1446 edit->syntax_marker = NULL;
1447 MC_PTR_FREE (edit->rules);
1448 tty_color_free_all_tmp ();
1451 /* --------------------------------------------------------------------------------------------- */
1453 * Load rules into edit struct. Either edit or *pnames must be NULL. If
1454 * edit is NULL, a list of types will be stored into names. If type is
1455 * NULL, then the type will be selected according to the filename.
1456 * type must be edit->syntax_type or NULL
1458 void
1459 edit_load_syntax (WEdit * edit, char ***pnames, const char *type)
1461 int r;
1462 char *f = NULL;
1464 if (option_auto_syntax)
1465 type = NULL;
1467 if (edit != NULL)
1469 char *saved_type;
1471 saved_type = g_strdup (type); /* save edit->syntax_type */
1472 edit_free_syntax_rules (edit);
1473 edit->syntax_type = saved_type; /* restore edit->syntax_type */
1476 if (!tty_use_colors ())
1477 return;
1479 if (!option_syntax_highlighting && (!pnames || !*pnames))
1480 return;
1482 if (edit != NULL && edit->filename_vpath == NULL)
1483 return;
1485 f = mc_config_get_full_path (EDIT_SYNTAX_FILE);
1486 if (edit != NULL)
1488 char *tmp_f;
1490 tmp_f = vfs_path_to_str (edit->filename_vpath);
1491 r = edit_read_syntax_file (edit, pnames, f, tmp_f,
1492 get_first_editor_line (edit),
1493 option_auto_syntax ? NULL : edit->syntax_type);
1494 g_free (tmp_f);
1496 else
1497 r = edit_read_syntax_file (NULL, pnames, f, NULL, "", NULL);
1498 if (r == -1)
1500 edit_free_syntax_rules (edit);
1501 message (D_ERROR, _("Load syntax file"),
1502 _("Cannot open file %s\n%s"), f, unix_error_string (errno));
1504 else if (r != 0)
1506 edit_free_syntax_rules (edit);
1507 message (D_ERROR, _("Load syntax file"),
1508 _("Error in file %s on line %d"), error_file_name ? error_file_name : f, r);
1509 MC_PTR_FREE (error_file_name);
1512 g_free (f);
1515 /* --------------------------------------------------------------------------------------------- */
1517 const char *
1518 edit_get_syntax_type (const WEdit * edit)
1520 return edit->syntax_type;
1523 /* --------------------------------------------------------------------------------------------- */