keymap files: unification of Fxx keys: move to lower case.
[midnight-commander.git] / src / editor / syntax.c
blobb00827ed7a45f7e14745ee266829dc26431ad86a
1 /* editor syntax highlighting.
3 Copyright (C) 1996, 1997, 1998, 2001, 2002, 2003, 2004, 2005, 2006,
4 2007, 2010 Free Software Foundation, Inc.
6 Authors:
7 1998 Paul Sheer
8 Egmont Koblinger <egmont@gmail.com>, 2010
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23 02110-1301, USA.
26 /** \file
27 * \brief Source: editor syntax highlighting
28 * \author Paul Sheer
29 * \date 1996, 1997
30 * \author Mikhail Pobolovets
31 * \date 2010
33 * Mispelled words are flushed from the syntax highlighting rules
34 * when they have been around longer than
35 * TRANSIENT_WORD_TIME_OUT seconds. At a cursor rate of 30
36 * chars per second and say 3 chars + a space per word, we can
37 * accumulate 450 words absolute max with a value of 60. This is
38 * below this limit of 1024 words in a context.
41 #include <config.h>
43 #include <stdio.h>
44 #include <stdarg.h>
45 #include <sys/types.h>
46 #include <unistd.h>
47 #include <string.h>
48 #include <ctype.h>
49 #include <errno.h>
50 #include <sys/stat.h>
51 #include <stdlib.h>
53 #include "lib/global.h"
54 #include "lib/search.h" /* search engine */
55 #include "lib/skin.h"
56 #include "lib/strutil.h" /* utf string functions */
57 #include "lib/util.h"
58 #include "lib/widget.h" /* message() */
60 #include "edit-impl.h"
61 #include "edit-widget.h"
63 /*** global variables ****************************************************************************/
65 int option_syntax_highlighting = 1;
66 int option_auto_syntax = 1;
68 /*** file scope macro definitions ****************************************************************/
70 /* bytes */
71 #define SYNTAX_MARKER_DENSITY 512
73 #define TRANSIENT_WORD_TIME_OUT 60
75 #define UNKNOWN_FORMAT "unknown"
77 #define MAX_WORDS_PER_CONTEXT 1024
78 #define MAX_CONTEXTS 128
80 #define RULE_ON_LEFT_BORDER 1
81 #define RULE_ON_RIGHT_BORDER 2
83 #define SYNTAX_TOKEN_STAR '\001'
84 #define SYNTAX_TOKEN_PLUS '\002'
85 #define SYNTAX_TOKEN_BRACKET '\003'
86 #define SYNTAX_TOKEN_BRACE '\004'
88 #define whiteness(x) ((x) == '\t' || (x) == '\n' || (x) == ' ')
90 #define free_args(x)
91 #define break_a {result=line;break;}
92 #define check_a {if(!*a){result=line;break;}}
93 #define check_not_a {if(*a){result=line;break;}}
95 /*** file scope type declarations ****************************************************************/
97 struct key_word
99 char *keyword;
100 unsigned char first;
101 char *whole_word_chars_left;
102 char *whole_word_chars_right;
103 int line_start;
104 int color;
107 struct context_rule
109 char *left;
110 unsigned char first_left;
111 char *right;
112 unsigned char first_right;
113 char line_start_left;
114 char line_start_right;
115 int between_delimiters;
116 char *whole_word_chars_left;
117 char *whole_word_chars_right;
118 char *keyword_first_chars;
119 int spelling;
120 /* first word is word[1] */
121 struct key_word **keyword;
124 struct _syntax_marker
126 long offset;
127 struct syntax_rule rule;
128 struct _syntax_marker *next;
131 /*** file scope variables ************************************************************************/
133 static char *error_file_name = NULL;
135 /*** file scope functions ************************************************************************/
136 /* --------------------------------------------------------------------------------------------- */
138 static gint
139 mc_defines_destroy (gpointer key, gpointer value, gpointer data)
141 char **values = value;
143 (void) data;
145 g_free (key);
146 while (*values)
147 g_free (*values++);
148 g_free (value);
150 return FALSE;
153 /* --------------------------------------------------------------------------------------------- */
154 /** Completely destroys the defines tree */
156 static void
157 destroy_defines (GTree ** defines)
159 g_tree_foreach (*defines, mc_defines_destroy, NULL);
160 g_tree_destroy (*defines);
161 *defines = NULL;
164 /* --------------------------------------------------------------------------------------------- */
166 /** Wrapper for case insensitive mode */
167 inline static int
168 xx_tolower (WEdit * edit, int c)
170 return edit->is_case_insensitive ? tolower (c) : c;
173 /* --------------------------------------------------------------------------------------------- */
175 static void
176 subst_defines (GTree * defines, char **argv, char **argv_end)
178 char **t, **p;
179 int argc;
181 while (*argv != NULL && argv < argv_end)
183 t = g_tree_lookup (defines, *argv);
184 if (t != NULL)
186 int count = 0;
188 /* Count argv array members */
189 argc = 0;
190 for (p = &argv[1]; *p != NULL; p++)
191 argc++;
193 /* Count members of definition array */
194 for (p = t; *p != NULL; p++)
195 count++;
196 p = &argv[count + argc];
198 /* Buffer overflow or infinitive loop in define */
199 if (p >= argv_end)
200 break;
202 /* Move rest of argv after definition members */
203 while (argc >= 0)
204 *p-- = argv[argc-- + 1];
206 /* Copy definition members to argv */
207 for (p = argv; *t != NULL; *p++ = *t++)
210 argv++;
214 /* --------------------------------------------------------------------------------------------- */
216 static long
217 compare_word_to_right (WEdit * edit, long i, const char *text,
218 const char *whole_left, const char *whole_right, int line_start)
220 const unsigned char *p, *q;
221 int c, d, j;
223 if (*text == '\0')
224 return -1;
226 c = xx_tolower (edit, edit_get_byte (edit, i - 1));
227 if (line_start != 0 && c != '\n')
228 return -1;
229 if (whole_left != NULL && strchr (whole_left, c) != NULL)
230 return -1;
232 for (p = (unsigned char *) text, q = p + str_term_width1 ((char *) p); p < q; p++, i++)
234 switch (*p)
236 case SYNTAX_TOKEN_STAR:
237 if (++p > q)
238 return -1;
239 for (;;)
241 c = xx_tolower (edit, edit_get_byte (edit, i));
242 if (*p == '\0' && whole_right != NULL && strchr (whole_right, c) == NULL)
243 break;
244 if (c == *p)
245 break;
246 if (c == '\n')
247 return -1;
248 i++;
250 break;
251 case SYNTAX_TOKEN_PLUS:
252 if (++p > q)
253 return -1;
254 j = 0;
255 for (;;)
257 c = xx_tolower (edit, edit_get_byte (edit, i));
258 if (c == *p)
260 j = i;
261 if (*p == *text && p[1] == '\0') /* handle eg '+' and @+@ keywords properly */
262 break;
264 if (j && strchr ((char *) p + 1, c)) /* c exists further down, so it will get matched later */
265 break;
266 if (c == '\n' || c == '\t' || c == ' ')
268 if (!*p)
270 i--;
271 break;
273 if (j == 0)
274 return -1;
275 i = j;
276 break;
278 if (whole_right != NULL && (strchr (whole_right, c) == NULL))
280 if (*p == '\0')
282 i--;
283 break;
285 if (j == 0)
286 return -1;
287 i = j;
288 break;
290 i++;
292 break;
293 case SYNTAX_TOKEN_BRACKET:
294 if (++p > q)
295 return -1;
296 c = -1;
297 for (;; i++)
299 d = c;
300 c = xx_tolower (edit, edit_get_byte (edit, i));
301 for (j = 0; p[j] != SYNTAX_TOKEN_BRACKET && p[j]; j++)
302 if (c == p[j])
303 goto found_char2;
304 break;
305 found_char2:
306 ; /* dummy command */
308 i--;
309 while (*p != SYNTAX_TOKEN_BRACKET && p <= q)
310 p++;
311 if (p > q)
312 return -1;
313 if (p[1] == d)
314 i--;
315 break;
316 case SYNTAX_TOKEN_BRACE:
317 if (++p > q)
318 return -1;
319 c = xx_tolower (edit, edit_get_byte (edit, i));
320 for (; *p != SYNTAX_TOKEN_BRACE && *p; p++)
321 if (c == *p)
322 goto found_char3;
323 return -1;
324 found_char3:
325 while (*p != SYNTAX_TOKEN_BRACE && p < q)
326 p++;
327 break;
328 default:
329 if (*p != xx_tolower (edit, edit_get_byte (edit, i)))
330 return -1;
333 if (whole_right != NULL
334 && strchr (whole_right, xx_tolower (edit, edit_get_byte (edit, i))) != NULL)
335 return -1;
336 return i;
339 /* --------------------------------------------------------------------------------------------- */
341 static const char *
342 xx_strchr (WEdit * edit, const unsigned char *s, int char_byte)
344 while (*s >= '\005' && xx_tolower (edit, *s) != char_byte)
345 s++;
347 return (const char *) s;
350 /* --------------------------------------------------------------------------------------------- */
352 static struct syntax_rule
353 apply_rules_going_right (WEdit * edit, long i, struct syntax_rule rule)
355 struct context_rule *r;
356 int c;
357 gboolean contextchanged = FALSE;
358 gboolean found_left = FALSE, found_right = FALSE;
359 gboolean keyword_foundleft = FALSE, keyword_foundright = FALSE;
360 gboolean is_end;
361 long end = 0;
362 struct syntax_rule _rule = rule;
364 c = xx_tolower (edit, edit_get_byte (edit, i));
365 if (c == 0)
366 return rule;
367 is_end = (rule.end == (unsigned char) i);
369 /* check to turn off a keyword */
370 if (_rule.keyword)
372 if (edit_get_byte (edit, i - 1) == '\n')
373 _rule.keyword = 0;
374 if (is_end)
376 _rule.keyword = 0;
377 keyword_foundleft = TRUE;
381 /* check to turn off a context */
382 if (_rule.context && !_rule.keyword)
384 long e;
386 r = edit->rules[_rule.context];
387 if (r->first_right == c && !(rule.border & RULE_ON_RIGHT_BORDER)
388 && (e =
389 compare_word_to_right (edit, i, r->right, r->whole_word_chars_left,
390 r->whole_word_chars_right, r->line_start_right)) > 0)
392 _rule.end = e;
393 found_right = TRUE;
394 _rule.border = RULE_ON_RIGHT_BORDER;
395 if (r->between_delimiters)
396 _rule.context = 0;
398 else if (is_end && rule.border & RULE_ON_RIGHT_BORDER)
400 /* always turn off a context at 4 */
401 found_left = TRUE;
402 _rule.border = 0;
403 if (!keyword_foundleft)
404 _rule.context = 0;
406 else if (is_end && rule.border & RULE_ON_LEFT_BORDER)
408 /* never turn off a context at 2 */
409 found_left = TRUE;
410 _rule.border = 0;
414 /* check to turn on a keyword */
415 if (!_rule.keyword)
417 const char *p;
419 r = edit->rules[_rule.context];
420 p = r->keyword_first_chars;
422 if (p != NULL)
423 while (*(p = xx_strchr (edit, (unsigned char *) p + 1, c)) != '\0')
425 struct key_word *k;
426 int count;
427 long e;
429 count = p - r->keyword_first_chars;
430 k = r->keyword[count];
431 e = compare_word_to_right (edit, i, k->keyword, k->whole_word_chars_left,
432 k->whole_word_chars_right, k->line_start);
433 if (e > 0)
435 end = e;
436 _rule.end = e;
437 _rule.keyword = count;
438 keyword_foundright = TRUE;
439 break;
444 /* check to turn on a context */
445 if (!_rule.context)
447 if (!found_left && is_end)
449 if (rule.border & RULE_ON_RIGHT_BORDER)
451 _rule.border = 0;
452 _rule.context = 0;
453 contextchanged = TRUE;
454 _rule.keyword = 0;
457 else if (rule.border & RULE_ON_LEFT_BORDER)
459 r = edit->rules[_rule._context];
460 _rule.border = 0;
461 if (r->between_delimiters)
463 _rule.context = _rule._context;
464 contextchanged = TRUE;
465 _rule.keyword = 0;
467 if (r->first_right == c)
469 long e;
471 e = compare_word_to_right (edit, i, r->right, r->whole_word_chars_left,
472 r->whole_word_chars_right, r->line_start_right);
473 if (e >= end)
475 _rule.end = e;
476 found_right = TRUE;
477 _rule.border = RULE_ON_RIGHT_BORDER;
478 _rule.context = 0;
485 if (!found_right)
487 int count;
488 struct context_rule **rules = edit->rules;
490 for (count = 1; rules[count]; count++)
492 r = rules[count];
493 if (r->first_left == c)
495 long e;
497 e = compare_word_to_right (edit, i, r->left, r->whole_word_chars_left,
498 r->whole_word_chars_right, r->line_start_left);
499 if (e >= end && (!_rule.keyword || keyword_foundright))
501 _rule.end = e;
502 found_right = TRUE;
503 _rule.border = RULE_ON_LEFT_BORDER;
504 _rule._context = count;
505 if (!r->between_delimiters && !_rule.keyword)
507 _rule.context = count;
508 contextchanged = TRUE;
510 break;
517 /* check again to turn on a keyword if the context switched */
518 if (contextchanged && !_rule.keyword)
520 const char *p;
522 r = edit->rules[_rule.context];
523 p = r->keyword_first_chars;
525 while (*(p = xx_strchr (edit, (unsigned char *) p + 1, c)) != '\0')
527 struct key_word *k;
528 int count;
529 long e;
531 count = p - r->keyword_first_chars;
532 k = r->keyword[count];
533 e = compare_word_to_right (edit, i, k->keyword, k->whole_word_chars_left,
534 k->whole_word_chars_right, k->line_start);
535 if (e > 0)
537 _rule.end = e;
538 _rule.keyword = count;
539 break;
544 return _rule;
547 /* --------------------------------------------------------------------------------------------- */
549 static struct syntax_rule
550 edit_get_rule (WEdit * edit, long byte_index)
552 long i;
554 if (byte_index > edit->last_get_rule)
556 for (i = edit->last_get_rule + 1; i <= byte_index; i++)
558 edit->rule = apply_rules_going_right (edit, i, edit->rule);
559 if (i >
560 (edit->syntax_marker ? edit->syntax_marker->offset +
561 SYNTAX_MARKER_DENSITY : SYNTAX_MARKER_DENSITY))
563 struct _syntax_marker *s;
565 s = edit->syntax_marker;
566 edit->syntax_marker = g_malloc (sizeof (struct _syntax_marker));
567 edit->syntax_marker->next = s;
568 edit->syntax_marker->offset = i;
569 edit->syntax_marker->rule = edit->rule;
573 else if (byte_index < edit->last_get_rule)
575 struct _syntax_marker *s;
577 for (;;)
579 if (!edit->syntax_marker)
581 memset (&edit->rule, 0, sizeof (edit->rule));
582 for (i = -1; i <= byte_index; i++)
583 edit->rule = apply_rules_going_right (edit, i, edit->rule);
584 break;
586 if (byte_index >= edit->syntax_marker->offset)
588 edit->rule = edit->syntax_marker->rule;
589 for (i = edit->syntax_marker->offset + 1; i <= byte_index; i++)
590 edit->rule = apply_rules_going_right (edit, i, edit->rule);
591 break;
593 s = edit->syntax_marker->next;
594 g_free (edit->syntax_marker);
595 edit->syntax_marker = s;
598 edit->last_get_rule = byte_index;
599 return edit->rule;
602 /* --------------------------------------------------------------------------------------------- */
604 static inline void
605 translate_rule_to_color (WEdit * edit, struct syntax_rule rule, int *color)
607 *color = edit->rules[rule.context]->keyword[rule.keyword]->color;
611 /* --------------------------------------------------------------------------------------------- */
613 Returns 0 on error/eof or a count of the number of bytes read
614 including the newline. Result must be free'd.
615 In case of an error, *line will not be modified.
618 static size_t
619 read_one_line (char **line, FILE * f)
621 GString *p;
622 size_t r = 0;
624 /* not reallocate string too often */
625 p = g_string_sized_new (64);
627 for (;;)
629 int c;
631 c = fgetc (f);
632 if (c == EOF)
634 if (ferror (f))
636 if (errno == EINTR)
637 continue;
638 r = 0;
640 break;
642 r++;
644 /* handle all of \r\n, \r, \n correctly. */
645 if (c == '\n')
646 break;
647 if (c == '\r')
649 c = fgetc (f);
650 if (c == '\n')
651 r++;
652 else
653 ungetc (c, f);
654 break;
657 g_string_append_c (p, c);
659 if (r != 0)
660 *line = g_string_free (p, FALSE);
661 else
662 g_string_free (p, TRUE);
664 return r;
667 /* --------------------------------------------------------------------------------------------- */
669 static char *
670 convert (char *s)
672 char *r, *p;
674 p = r = s;
675 while (*s)
677 switch (*s)
679 case '\\':
680 s++;
681 switch (*s)
683 case ' ':
684 *p = ' ';
685 s--;
686 break;
687 case 'n':
688 *p = '\n';
689 break;
690 case 'r':
691 *p = '\r';
692 break;
693 case 't':
694 *p = '\t';
695 break;
696 case 's':
697 *p = ' ';
698 break;
699 case '*':
700 *p = '*';
701 break;
702 case '\\':
703 *p = '\\';
704 break;
705 case '[':
706 case ']':
707 *p = SYNTAX_TOKEN_BRACKET;
708 break;
709 case '{':
710 case '}':
711 *p = SYNTAX_TOKEN_BRACE;
712 break;
713 case 0:
714 *p = *s;
715 return r;
716 default:
717 *p = *s;
718 break;
720 break;
721 case '*':
722 *p = SYNTAX_TOKEN_STAR;
723 break;
724 case '+':
725 *p = SYNTAX_TOKEN_PLUS;
726 break;
727 default:
728 *p = *s;
729 break;
731 s++;
732 p++;
734 *p = '\0';
735 return r;
738 /* --------------------------------------------------------------------------------------------- */
740 static int
741 get_args (char *l, char **args, int args_size)
743 int argc = 0;
745 while (argc < args_size)
747 char *p = l;
748 while (*p != '\0' && whiteness (*p))
749 p++;
750 if (*p == '\0')
751 break;
752 for (l = p + 1; *l != '\0' && !whiteness (*l); l++)
754 if (*l != '\0')
755 *l++ = '\0';
756 args[argc++] = convert (p);
758 args[argc] = (char *) NULL;
759 return argc;
762 /* --------------------------------------------------------------------------------------------- */
764 static int
765 this_try_alloc_color_pair (const char *fg, const char *bg, const char *attrs)
767 char f[80], b[80], a[80], *p;
769 if (bg != NULL && *bg == '\0')
770 bg = NULL;
771 if (fg != NULL && *fg == '\0')
772 fg = NULL;
773 if (attrs != NULL && *attrs == '\0')
774 attrs = NULL;
776 if ((fg == NULL) && (bg == NULL))
777 return EDITOR_NORMAL_COLOR;
779 if (fg != NULL)
781 g_strlcpy (f, fg, sizeof (f));
782 p = strchr (f, '/');
783 if (p != NULL)
784 *p = '\0';
785 fg = f;
787 if (bg != NULL)
789 g_strlcpy (b, bg, sizeof (b));
790 p = strchr (b, '/');
791 if (p != NULL)
792 *p = '\0';
793 bg = b;
795 if ((fg == NULL) || (bg == NULL))
797 /* get colors from skin */
798 char *editnormal;
800 editnormal = mc_skin_get ("editor", "_default_", "default;default");
802 if (fg == NULL)
804 g_strlcpy (f, editnormal, sizeof (f));
805 p = strchr (f, ';');
806 if (p != NULL)
807 *p = '\0';
808 if (f[0] == '\0')
809 g_strlcpy (f, "default", sizeof (f));
810 fg = f;
812 if (bg == NULL)
814 p = strchr (editnormal, ';');
815 if ((p != NULL) && (*(++p) != '\0'))
816 g_strlcpy (b, p, sizeof (b));
817 else
818 g_strlcpy (b, "default", sizeof (b));
819 bg = b;
822 g_free (editnormal);
825 if (attrs != NULL)
827 g_strlcpy (a, attrs, sizeof (a));
828 p = strchr (a, '/');
829 if (p != NULL)
830 *p = '\0';
831 /* get_args() mangles the + signs, unmangle 'em */
832 p = a;
833 while ((p = strchr (p, SYNTAX_TOKEN_PLUS)) != NULL)
834 *p++ = '+';
835 attrs = a;
837 return tty_try_alloc_color_pair (fg, bg, attrs);
840 /* --------------------------------------------------------------------------------------------- */
842 static FILE *
843 open_include_file (const char *filename)
845 FILE *f;
847 MC_PTR_FREE (error_file_name);
848 error_file_name = g_strdup (filename);
849 if (g_path_is_absolute (filename))
850 return fopen (filename, "r");
852 g_free (error_file_name);
853 error_file_name =
854 g_build_filename (mc_config_get_data_path (), EDIT_DIR, filename, (char *) NULL);
855 f = fopen (error_file_name, "r");
856 if (f != NULL)
857 return f;
859 g_free (error_file_name);
860 error_file_name = g_build_filename (mc_global.sysconfig_dir, "syntax", filename, (char *) NULL);
861 f = fopen (error_file_name, "r");
862 if (f != NULL)
863 return f;
865 g_free (error_file_name);
866 error_file_name = g_build_filename (mc_global.share_data_dir, "syntax", filename, (char *) NULL);
868 return fopen (error_file_name, "r");
871 /* --------------------------------------------------------------------------------------------- */
873 inline static void
874 xx_lowerize_line (WEdit * edit, char *line, size_t len)
876 if (edit->is_case_insensitive)
878 size_t i;
879 for (i = 0; i < len; ++i)
880 line[i] = tolower (line[i]);
884 /* --------------------------------------------------------------------------------------------- */
885 /** returns line number on error */
887 static int
888 edit_read_syntax_rules (WEdit * edit, FILE * f, char **args, int args_size)
890 FILE *g = NULL;
891 char *fg, *bg, *attrs;
892 char last_fg[32] = "", last_bg[32] = "", last_attrs[64] = "";
893 char whole_right[512];
894 char whole_left[512];
895 char *l = 0;
896 int save_line = 0, line = 0;
897 struct context_rule **r, *c = NULL;
898 int num_words = -1, num_contexts = -1;
899 int result = 0;
900 int argc;
901 int i, j;
902 int alloc_contexts = MAX_CONTEXTS,
903 alloc_words_per_context = MAX_WORDS_PER_CONTEXT,
904 max_alloc_words_per_context = MAX_WORDS_PER_CONTEXT;
906 args[0] = NULL;
907 edit->is_case_insensitive = FALSE;
909 strcpy (whole_left, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_01234567890");
910 strcpy (whole_right, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_01234567890");
912 r = edit->rules = g_malloc0 (alloc_contexts * sizeof (struct context_rule *));
914 if (!edit->defines)
915 edit->defines = g_tree_new ((GCompareFunc) strcmp);
917 for (;;)
919 char **a;
920 size_t len;
922 line++;
923 l = 0;
925 len = read_one_line (&l, f);
926 if (len == 0)
928 if (g)
930 fclose (f);
931 f = g;
932 g = 0;
933 line = save_line + 1;
934 MC_PTR_FREE (error_file_name);
935 MC_PTR_FREE (l);
936 len = read_one_line (&l, f);
937 if (len == 0)
938 break;
939 else
940 xx_lowerize_line (edit, l, len);
942 else
944 break;
947 else
949 xx_lowerize_line (edit, l, len);
951 argc = get_args (l, args, args_size);
952 a = args + 1;
953 if (!args[0])
955 /* do nothing */
957 else if (!strcmp (args[0], "include"))
959 if (g || argc != 2)
961 result = line;
962 break;
964 g = f;
965 f = open_include_file (args[1]);
966 if (!f)
968 MC_PTR_FREE (error_file_name);
969 result = line;
970 break;
972 save_line = line;
973 line = 0;
975 else if (!strcmp (args[0], "caseinsensitive"))
977 edit->is_case_insensitive = TRUE;
979 else if (!strcmp (args[0], "wholechars"))
981 check_a;
982 if (!strcmp (*a, "left"))
984 a++;
985 g_strlcpy (whole_left, *a, sizeof (whole_left));
987 else if (!strcmp (*a, "right"))
989 a++;
990 g_strlcpy (whole_right, *a, sizeof (whole_right));
992 else
994 g_strlcpy (whole_left, *a, sizeof (whole_left));
995 g_strlcpy (whole_right, *a, sizeof (whole_right));
997 a++;
998 check_not_a;
1000 else if (!strcmp (args[0], "context"))
1002 check_a;
1003 if (num_contexts == -1)
1005 if (strcmp (*a, "default"))
1006 { /* first context is the default */
1007 break_a;
1009 a++;
1010 c = r[0] = g_malloc0 (sizeof (struct context_rule));
1011 c->left = g_strdup (" ");
1012 c->right = g_strdup (" ");
1013 num_contexts = 0;
1015 else
1017 /* Terminate previous context. */
1018 r[num_contexts - 1]->keyword[num_words] = NULL;
1019 c = r[num_contexts] = g_malloc0 (sizeof (struct context_rule));
1020 if (!strcmp (*a, "exclusive"))
1022 a++;
1023 c->between_delimiters = 1;
1025 check_a;
1026 if (!strcmp (*a, "whole"))
1028 a++;
1029 c->whole_word_chars_left = g_strdup (whole_left);
1030 c->whole_word_chars_right = g_strdup (whole_right);
1032 else if (!strcmp (*a, "wholeleft"))
1034 a++;
1035 c->whole_word_chars_left = g_strdup (whole_left);
1037 else if (!strcmp (*a, "wholeright"))
1039 a++;
1040 c->whole_word_chars_right = g_strdup (whole_right);
1042 check_a;
1043 if (!strcmp (*a, "linestart"))
1045 a++;
1046 c->line_start_left = 1;
1048 check_a;
1049 c->left = g_strdup (*a++);
1050 check_a;
1051 if (!strcmp (*a, "linestart"))
1053 a++;
1054 c->line_start_right = 1;
1056 check_a;
1057 c->right = g_strdup (*a++);
1058 c->first_left = *c->left;
1059 c->first_right = *c->right;
1061 c->keyword = g_malloc (alloc_words_per_context * sizeof (struct key_word *));
1062 num_words = 1;
1063 c->keyword[0] = g_malloc0 (sizeof (struct key_word));
1064 subst_defines (edit->defines, a, &args[1024]);
1065 fg = *a;
1066 if (*a)
1067 a++;
1068 bg = *a;
1069 if (*a)
1070 a++;
1071 attrs = *a;
1072 if (*a)
1073 a++;
1074 g_strlcpy (last_fg, fg ? fg : "", sizeof (last_fg));
1075 g_strlcpy (last_bg, bg ? bg : "", sizeof (last_bg));
1076 g_strlcpy (last_attrs, attrs ? attrs : "", sizeof (last_attrs));
1077 c->keyword[0]->color = this_try_alloc_color_pair (fg, bg, attrs);
1078 c->keyword[0]->keyword = g_strdup (" ");
1079 check_not_a;
1081 alloc_words_per_context = MAX_WORDS_PER_CONTEXT;
1082 if (++num_contexts >= alloc_contexts)
1084 struct context_rule **tmp;
1086 alloc_contexts += 128;
1087 tmp = g_realloc (r, alloc_contexts * sizeof (struct context_rule *));
1088 r = tmp;
1091 else if (!strcmp (args[0], "spellcheck"))
1093 if (!c)
1095 result = line;
1096 break;
1098 c->spelling = 1;
1100 else if (!strcmp (args[0], "keyword"))
1102 struct key_word *k;
1104 if (num_words == -1)
1105 break_a;
1106 check_a;
1107 k = r[num_contexts - 1]->keyword[num_words] = g_malloc0 (sizeof (struct key_word));
1108 if (!strcmp (*a, "whole"))
1110 a++;
1111 k->whole_word_chars_left = g_strdup (whole_left);
1112 k->whole_word_chars_right = g_strdup (whole_right);
1114 else if (!strcmp (*a, "wholeleft"))
1116 a++;
1117 k->whole_word_chars_left = g_strdup (whole_left);
1119 else if (!strcmp (*a, "wholeright"))
1121 a++;
1122 k->whole_word_chars_right = g_strdup (whole_right);
1124 check_a;
1125 if (!strcmp (*a, "linestart"))
1127 a++;
1128 k->line_start = 1;
1130 check_a;
1131 if (!strcmp (*a, "whole"))
1133 break_a;
1135 k->keyword = g_strdup (*a++);
1136 k->first = *k->keyword;
1137 subst_defines (edit->defines, a, &args[1024]);
1138 fg = *a;
1139 if (*a)
1140 a++;
1141 bg = *a;
1142 if (*a)
1143 a++;
1144 attrs = *a;
1145 if (*a)
1146 a++;
1147 if (!fg)
1148 fg = last_fg;
1149 if (!bg)
1150 bg = last_bg;
1151 if (!attrs)
1152 attrs = last_attrs;
1153 k->color = this_try_alloc_color_pair (fg, bg, attrs);
1154 check_not_a;
1156 if (++num_words >= alloc_words_per_context)
1158 struct key_word **tmp;
1160 alloc_words_per_context += 1024;
1162 if (alloc_words_per_context > max_alloc_words_per_context)
1163 max_alloc_words_per_context = alloc_words_per_context;
1165 tmp = g_realloc (c->keyword, alloc_words_per_context * sizeof (struct key_word *));
1166 c->keyword = tmp;
1169 else if (*(args[0]) == '#')
1171 /* do nothing for comment */
1173 else if (!strcmp (args[0], "file"))
1175 break;
1177 else if (!strcmp (args[0], "define"))
1179 char *key = *a++;
1180 char **argv;
1182 if (argc < 3)
1183 break_a;
1184 argv = g_tree_lookup (edit->defines, key);
1185 if (argv != NULL)
1186 mc_defines_destroy (NULL, argv, NULL);
1187 else
1188 key = g_strdup (key);
1190 argv = g_new (char *, argc - 1);
1191 g_tree_insert (edit->defines, key, argv);
1192 while (*a != NULL)
1194 *argv++ = g_strdup (*a++);
1196 *argv = NULL;
1198 else
1199 { /* anything else is an error */
1200 break_a;
1202 free_args (args);
1203 MC_PTR_FREE (l);
1205 free_args (args);
1206 MC_PTR_FREE (l);
1208 /* Terminate context array. */
1209 if (num_contexts > 0)
1211 r[num_contexts - 1]->keyword[num_words] = NULL;
1212 r[num_contexts] = NULL;
1215 if (!edit->rules[0])
1216 MC_PTR_FREE (edit->rules);
1218 if (result)
1219 return result;
1221 if (num_contexts == -1)
1223 return line;
1227 char *first_chars, *p;
1229 first_chars = g_malloc0 (max_alloc_words_per_context + 2);
1231 for (i = 0; edit->rules[i]; i++)
1233 c = edit->rules[i];
1234 p = first_chars;
1235 *p++ = (char) 1;
1236 for (j = 1; c->keyword[j]; j++)
1237 *p++ = c->keyword[j]->first;
1238 *p = '\0';
1239 c->keyword_first_chars = g_strdup (first_chars);
1242 g_free (first_chars);
1245 return result;
1248 /* --------------------------------------------------------------------------------------------- */
1250 /* returns -1 on file error, line number on error in file syntax */
1251 static int
1252 edit_read_syntax_file (WEdit * edit, char ***pnames, const char *syntax_file,
1253 const char *editor_file, const char *first_line, const char *type)
1255 #define NENTRIES 30
1256 FILE *f, *g = NULL;
1257 char *args[1024], *l = NULL;
1258 int line = 0;
1259 int result = 0;
1260 int count = 0;
1261 char *lib_file;
1262 gboolean found = FALSE;
1263 char **tmpnames = NULL;
1265 f = fopen (syntax_file, "r");
1266 if (f == NULL)
1268 lib_file = g_build_filename (mc_global.share_data_dir, "syntax", "Syntax", (char *) NULL);
1269 f = fopen (lib_file, "r");
1270 g_free (lib_file);
1271 if (f == NULL)
1272 return -1;
1275 args[0] = NULL;
1276 for (;;)
1278 line++;
1279 MC_PTR_FREE (l);
1280 if (read_one_line (&l, f) == 0)
1281 break;
1282 (void) get_args (l, args, 1023); /* Final NULL */
1283 if (args[0] == NULL)
1284 continue;
1286 /* Looking for `include ...` lines before first `file ...` ones */
1287 if (!found && strcmp (args[0], "include") == 0)
1289 if (g != NULL)
1290 continue;
1292 if (!args[1] || !(g = open_include_file (args[1])))
1294 result = line;
1295 break;
1297 goto found_type;
1300 /* looking for `file ...' lines only */
1301 if (strcmp (args[0], "file") != 0)
1302 continue;
1304 found = TRUE;
1306 /* must have two args or report error */
1307 if (!args[1] || !args[2])
1309 result = line;
1310 break;
1312 if (pnames && *pnames)
1314 /* 1: just collecting a list of names of rule sets */
1315 /* Reallocate the list if required */
1316 if (count % NENTRIES == 0)
1318 tmpnames =
1319 (char **) g_try_realloc (*pnames, (count + NENTRIES + 1) * sizeof (char *));
1320 if (tmpnames == NULL)
1321 break;
1322 *pnames = tmpnames;
1324 (*pnames)[count++] = g_strdup (args[2]);
1325 (*pnames)[count] = NULL;
1327 else if (type)
1329 /* 2: rule set was explicitly specified by the caller */
1330 if (strcmp (type, args[2]) == 0)
1331 goto found_type;
1333 else if (editor_file && edit)
1335 /* 3: auto-detect rule set from regular expressions */
1336 int q;
1338 q = mc_search (args[1], editor_file, MC_SEARCH_T_REGEX);
1339 /* does filename match arg 1 ? */
1340 if (!q && args[3])
1342 /* does first line match arg 3 ? */
1343 q = mc_search (args[3], first_line, MC_SEARCH_T_REGEX);
1345 if (q)
1347 int line_error;
1348 char *syntax_type;
1349 found_type:
1350 syntax_type = args[2];
1351 line_error = edit_read_syntax_rules (edit, g ? g : f, args, 1023);
1352 if (line_error)
1354 if (!error_file_name) /* an included file */
1355 result = line + line_error;
1356 else
1357 result = line_error;
1359 else
1361 g_free (edit->syntax_type);
1362 edit->syntax_type = g_strdup (syntax_type);
1363 /* if there are no rules then turn off syntax highlighting for speed */
1364 if (!g && !edit->rules[1])
1365 if (!edit->rules[0]->keyword[1] && !edit->rules[0]->spelling)
1367 edit_free_syntax_rules (edit);
1368 break;
1372 if (g == NULL)
1373 break;
1375 fclose (g);
1376 g = NULL;
1380 g_free (l);
1381 fclose (f);
1382 return result;
1385 /* --------------------------------------------------------------------------------------------- */
1387 static char *
1388 get_first_editor_line (WEdit * edit)
1390 size_t i;
1391 static char s[256];
1393 s[0] = '\0';
1394 if (edit == NULL)
1395 return s;
1397 for (i = 0; i < sizeof (s) - 1; i++)
1399 s[i] = edit_get_byte (edit, i);
1400 if (s[i] == '\n')
1402 s[i] = '\0';
1403 break;
1406 s[sizeof (s) - 1] = '\0';
1407 return s;
1410 /* --------------------------------------------------------------------------------------------- */
1411 /*** public functions ****************************************************************************/
1412 /* --------------------------------------------------------------------------------------------- */
1414 void
1415 edit_get_syntax_color (WEdit * edit, long byte_index, int *color)
1417 if (!tty_use_colors ())
1418 *color = 0;
1419 else if (edit->rules && byte_index < edit->last_byte && option_syntax_highlighting)
1420 translate_rule_to_color (edit, edit_get_rule (edit, byte_index), color);
1421 else
1422 *color = EDITOR_NORMAL_COLOR;
1425 /* --------------------------------------------------------------------------------------------- */
1427 void
1428 edit_free_syntax_rules (WEdit * edit)
1430 size_t i, j;
1432 if (!edit)
1433 return;
1434 if (edit->defines)
1435 destroy_defines (&edit->defines);
1436 if (!edit->rules)
1437 return;
1439 edit_get_rule (edit, -1);
1440 MC_PTR_FREE (edit->syntax_type);
1442 for (i = 0; edit->rules[i]; i++)
1444 if (edit->rules[i]->keyword)
1446 for (j = 0; edit->rules[i]->keyword[j]; j++)
1448 MC_PTR_FREE (edit->rules[i]->keyword[j]->keyword);
1449 MC_PTR_FREE (edit->rules[i]->keyword[j]->whole_word_chars_left);
1450 MC_PTR_FREE (edit->rules[i]->keyword[j]->whole_word_chars_right);
1451 MC_PTR_FREE (edit->rules[i]->keyword[j]);
1454 MC_PTR_FREE (edit->rules[i]->left);
1455 MC_PTR_FREE (edit->rules[i]->right);
1456 MC_PTR_FREE (edit->rules[i]->whole_word_chars_left);
1457 MC_PTR_FREE (edit->rules[i]->whole_word_chars_right);
1458 MC_PTR_FREE (edit->rules[i]->keyword);
1459 MC_PTR_FREE (edit->rules[i]->keyword_first_chars);
1460 MC_PTR_FREE (edit->rules[i]);
1463 while (edit->syntax_marker)
1465 struct _syntax_marker *s = edit->syntax_marker->next;
1466 g_free (edit->syntax_marker);
1467 edit->syntax_marker = s;
1470 MC_PTR_FREE (edit->rules);
1471 tty_color_free_all_tmp ();
1474 /* --------------------------------------------------------------------------------------------- */
1476 * Load rules into edit struct. Either edit or *pnames must be NULL. If
1477 * edit is NULL, a list of types will be stored into names. If type is
1478 * NULL, then the type will be selected according to the filename.
1479 * type must be edit->syntax_type or NULL
1481 void
1482 edit_load_syntax (WEdit * edit, char ***pnames, const char *type)
1484 int r;
1485 char *f = NULL;
1487 if (option_auto_syntax)
1488 type = NULL;
1490 if (edit != NULL)
1492 char *saved_type;
1494 saved_type = g_strdup (type); /* save edit->syntax_type */
1495 edit_free_syntax_rules (edit);
1496 edit->syntax_type = saved_type; /* restore edit->syntax_type */
1499 if (!tty_use_colors ())
1500 return;
1502 if (!option_syntax_highlighting && (!pnames || !*pnames))
1503 return;
1505 if (edit != NULL)
1507 if (!edit->filename)
1508 return;
1509 if (!*edit->filename && !type)
1510 return;
1512 f = g_build_filename (mc_config_get_data_path (), EDIT_SYNTAX_FILE, (char *) NULL);
1513 if (edit != NULL)
1514 r = edit_read_syntax_file (edit, pnames, f, edit->filename,
1515 get_first_editor_line (edit),
1516 option_auto_syntax ? NULL : edit->syntax_type);
1517 else
1518 r = edit_read_syntax_file (NULL, pnames, f, NULL, "", NULL);
1519 if (r == -1)
1521 edit_free_syntax_rules (edit);
1522 message (D_ERROR, _("Load syntax file"),
1523 _("Cannot open file %s\n%s"), f, unix_error_string (errno));
1525 else if (r != 0)
1527 edit_free_syntax_rules (edit);
1528 message (D_ERROR, _("Load syntax file"),
1529 _("Error in file %s on line %d"), error_file_name ? error_file_name : f, r);
1530 MC_PTR_FREE (error_file_name);
1533 g_free (f);
1536 /* --------------------------------------------------------------------------------------------- */
1538 const char *
1539 edit_get_syntax_type (const WEdit * edit)
1541 return edit->syntax_type;
1544 /* --------------------------------------------------------------------------------------------- */