Ticket #2127: updated file extension for "sh"
[midnight-commander.git] / src / editor / syntax.c
blob618bc5c414240a457132b6aca7ca02c2ede6bb03
1 /* editor syntax highlighting.
3 Copyright (C) 1996, 1997, 1998, 2001, 2002, 2003, 2004, 2005, 2006,
4 2007 Free Software Foundation, Inc.
6 Authors: 1998 Paul Sheer
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 02110-1301, USA.
24 /** \file
25 * \brief Source: editor syntax highlighting
26 * \author Paul Sheer
27 * \date 1996, 1997
28 * \author Mikhail Pobolovets
29 * \date 2010
31 * Mispelled words are flushed from the syntax highlighting rules
32 * when they have been around longer than
33 * TRANSIENT_WORD_TIME_OUT seconds. At a cursor rate of 30
34 * chars per second and say 3 chars + a space per word, we can
35 * accumulate 450 words absolute max with a value of 60. This is
36 * below this limit of 1024 words in a context.
39 #include <config.h>
41 #include <stdio.h>
42 #include <stdarg.h>
43 #include <sys/types.h>
44 #include <unistd.h>
45 #include <string.h>
46 #include <ctype.h>
47 #include <errno.h>
48 #include <sys/stat.h>
49 #include <stdlib.h>
51 #include "lib/global.h"
52 #include "lib/search.h" /* search engine */
53 #include "lib/skin.h"
54 #include "lib/strutil.h" /* utf string functions */
56 #include "src/main.h" /* mc_home */
57 #include "src/wtools.h" /* message() */
59 #include "edit-impl.h"
60 #include "edit-widget.h"
63 /* bytes */
64 #define SYNTAX_MARKER_DENSITY 512
66 #define TRANSIENT_WORD_TIME_OUT 60
68 #define UNKNOWN_FORMAT "unknown"
70 #define MAX_WORDS_PER_CONTEXT 1024
71 #define MAX_CONTEXTS 128
73 #define RULE_ON_LEFT_BORDER 1
74 #define RULE_ON_RIGHT_BORDER 2
76 #define SYNTAX_TOKEN_STAR '\001'
77 #define SYNTAX_TOKEN_PLUS '\002'
78 #define SYNTAX_TOKEN_BRACKET '\003'
79 #define SYNTAX_TOKEN_BRACE '\004'
81 struct key_word
83 char *keyword;
84 unsigned char first;
85 char *whole_word_chars_left;
86 char *whole_word_chars_right;
87 int line_start;
88 int color;
91 struct context_rule
93 char *left;
94 unsigned char first_left;
95 char *right;
96 unsigned char first_right;
97 char line_start_left;
98 char line_start_right;
99 int between_delimiters;
100 char *whole_word_chars_left;
101 char *whole_word_chars_right;
102 char *keyword_first_chars;
103 int spelling;
104 /* first word is word[1] */
105 struct key_word **keyword;
108 struct _syntax_marker
110 long offset;
111 struct syntax_rule rule;
112 struct _syntax_marker *next;
115 int option_syntax_highlighting = 1;
116 int option_auto_syntax = 1;
118 static gint
119 mc_defines_destroy (gpointer key, gpointer value, gpointer data)
121 char **values = value;
123 (void) data;
125 g_free (key);
126 while (*values)
127 g_free (*values++);
128 g_free (value);
130 return FALSE;
133 /* Completely destroys the defines tree */
134 static void
135 destroy_defines (GTree ** defines)
137 g_tree_traverse (*defines, mc_defines_destroy, G_POST_ORDER, NULL);
138 g_tree_destroy (*defines);
139 *defines = NULL;
142 /* Wrapper for case insensitive mode */
143 inline static int
144 xx_tolower (WEdit * edit, int c)
146 return edit->is_case_insensitive ? tolower (c) : c;
149 static void
150 subst_defines (GTree * defines, char **argv, char **argv_end)
152 char **t, **p;
153 int argc;
155 while (*argv != NULL && argv < argv_end)
157 t = g_tree_lookup (defines, *argv);
158 if (t != NULL)
160 int count = 0;
162 /* Count argv array members */
163 argc = 0;
164 for (p = &argv[1]; *p != NULL; p++)
165 argc++;
167 /* Count members of definition array */
168 for (p = t; *p != NULL; p++)
169 count++;
170 p = &argv[count + argc];
172 /* Buffer overflow or infinitive loop in define */
173 if (p >= argv_end)
174 break;
176 /* Move rest of argv after definition members */
177 while (argc >= 0)
178 *p-- = argv[argc-- + 1];
180 /* Copy definition members to argv */
181 for (p = argv; *t != NULL; *p++ = *t++)
184 argv++;
188 static long
189 compare_word_to_right (WEdit * edit, long i, const char *text,
190 const char *whole_left, const char *whole_right, int line_start)
192 const unsigned char *p, *q;
193 int c, d, j;
195 if (*text == '\0')
196 return -1;
198 c = xx_tolower (edit, edit_get_byte (edit, i - 1));
199 if (line_start != 0 && c != '\n')
200 return -1;
201 if (whole_left != NULL && strchr (whole_left, c) != NULL)
202 return -1;
204 for (p = (unsigned char *) text, q = p + str_term_width1 ((char *) p); p < q; p++, i++)
206 switch (*p)
208 case SYNTAX_TOKEN_STAR:
209 if (++p > q)
210 return -1;
211 for (;;)
213 c = xx_tolower (edit, edit_get_byte (edit, i));
214 if (*p == '\0' && whole_right != NULL && strchr (whole_right, c) == NULL)
215 break;
216 if (c == *p)
217 break;
218 if (c == '\n')
219 return -1;
220 i++;
222 break;
223 case SYNTAX_TOKEN_PLUS:
224 if (++p > q)
225 return -1;
226 j = 0;
227 for (;;)
229 c = xx_tolower (edit, edit_get_byte (edit, i));
230 if (c == *p)
232 j = i;
233 if (*p == *text && p[1] == '\0') /* handle eg '+' and @+@ keywords properly */
234 break;
236 if (j && strchr ((char *) p + 1, c)) /* c exists further down, so it will get matched later */
237 break;
238 if (c == '\n' || c == '\t' || c == ' ')
240 if (!*p)
242 i--;
243 break;
245 if (j == 0)
246 return -1;
247 i = j;
248 break;
250 if (whole_right != NULL && (strchr (whole_right, c) == NULL))
252 if (*p == '\0')
254 i--;
255 break;
257 if (j == 0)
258 return -1;
259 i = j;
260 break;
262 i++;
264 break;
265 case SYNTAX_TOKEN_BRACKET:
266 if (++p > q)
267 return -1;
268 c = -1;
269 for (;; i++)
271 d = c;
272 c = xx_tolower (edit, edit_get_byte (edit, i));
273 for (j = 0; p[j] != SYNTAX_TOKEN_BRACKET && p[j]; j++)
274 if (c == p[j])
275 goto found_char2;
276 break;
277 found_char2:
278 ; /* dummy command */
280 i--;
281 while (*p != SYNTAX_TOKEN_BRACKET && p <= q)
282 p++;
283 if (p > q)
284 return -1;
285 if (p[1] == d)
286 i--;
287 break;
288 case SYNTAX_TOKEN_BRACE:
289 if (++p > q)
290 return -1;
291 c = xx_tolower (edit, edit_get_byte (edit, i));
292 for (; *p != SYNTAX_TOKEN_BRACE && *p; p++)
293 if (c == *p)
294 goto found_char3;
295 return -1;
296 found_char3:
297 while (*p != SYNTAX_TOKEN_BRACE && p < q)
298 p++;
299 break;
300 default:
301 if (*p != xx_tolower (edit, edit_get_byte (edit, i)))
302 return -1;
305 if (whole_right != NULL && strchr (whole_right, xx_tolower (edit, edit_get_byte (edit, i))) != NULL)
306 return -1;
307 return i;
310 static const char *
311 xx_strchr (WEdit * edit, const unsigned char *s, int char_byte)
313 while (*s >= '\005' && xx_tolower (edit, *s) != char_byte)
314 s++;
316 return (const char *) s;
319 static struct syntax_rule
320 apply_rules_going_right (WEdit * edit, long i, struct syntax_rule rule)
322 struct context_rule *r;
323 int c;
324 gboolean contextchanged = FALSE;
325 gboolean found_left = FALSE, found_right = FALSE;
326 gboolean keyword_foundleft = FALSE, keyword_foundright = FALSE;
327 gboolean is_end;
328 long end = 0;
329 struct syntax_rule _rule = rule;
331 c = xx_tolower (edit, edit_get_byte (edit, i));
332 if (c == 0)
333 return rule;
334 is_end = (rule.end == (unsigned char) i);
336 /* check to turn off a keyword */
337 if (_rule.keyword)
339 if (edit_get_byte (edit, i - 1) == '\n')
340 _rule.keyword = 0;
341 if (is_end)
343 _rule.keyword = 0;
344 keyword_foundleft = TRUE;
348 /* check to turn off a context */
349 if (_rule.context && !_rule.keyword)
351 long e;
353 r = edit->rules[_rule.context];
354 if (r->first_right == c && !(rule.border & RULE_ON_RIGHT_BORDER)
355 && (e =
356 compare_word_to_right (edit, i, r->right, r->whole_word_chars_left,
357 r->whole_word_chars_right, r->line_start_right)) > 0)
359 _rule.end = e;
360 found_right = TRUE;
361 _rule.border = RULE_ON_RIGHT_BORDER;
362 if (r->between_delimiters)
363 _rule.context = 0;
365 else if (is_end && rule.border & RULE_ON_RIGHT_BORDER)
367 /* always turn off a context at 4 */
368 found_left = TRUE;
369 _rule.border = 0;
370 if (!keyword_foundleft)
371 _rule.context = 0;
373 else if (is_end && rule.border & RULE_ON_LEFT_BORDER)
375 /* never turn off a context at 2 */
376 found_left = TRUE;
377 _rule.border = 0;
381 /* check to turn on a keyword */
382 if (!_rule.keyword)
384 const char *p;
386 r = edit->rules[_rule.context];
387 p = r->keyword_first_chars;
389 if (p != NULL)
390 while (*(p = xx_strchr (edit, (unsigned char *) p + 1, c)) != '\0')
392 struct key_word *k;
393 int count;
394 long e;
396 count = p - r->keyword_first_chars;
397 k = r->keyword[count];
398 e = compare_word_to_right (edit, i, k->keyword, k->whole_word_chars_left,
399 k->whole_word_chars_right, k->line_start);
400 if (e > 0)
402 end = e;
403 _rule.end = e;
404 _rule.keyword = count;
405 keyword_foundright = TRUE;
406 break;
411 /* check to turn on a context */
412 if (!_rule.context)
414 if (!found_left && is_end)
416 if (rule.border & RULE_ON_RIGHT_BORDER)
418 _rule.border = 0;
419 _rule.context = 0;
420 contextchanged = TRUE;
421 _rule.keyword = 0;
424 else if (rule.border & RULE_ON_LEFT_BORDER)
426 r = edit->rules[_rule._context];
427 _rule.border = 0;
428 if (r->between_delimiters)
430 _rule.context = _rule._context;
431 contextchanged = TRUE;
432 _rule.keyword = 0;
434 if (r->first_right == c)
436 long e;
438 e = compare_word_to_right (edit, i, r->right, r->whole_word_chars_left,
439 r->whole_word_chars_right,
440 r->line_start_right);
441 if (e >= end)
443 _rule.end = e;
444 found_right = TRUE;
445 _rule.border = RULE_ON_RIGHT_BORDER;
446 _rule.context = 0;
453 if (!found_right)
455 int count;
456 struct context_rule **rules = edit->rules;
458 for (count = 1; rules[count]; count++)
460 r = rules[count];
461 if (r->first_left == c)
463 long e;
465 e = compare_word_to_right (edit, i, r->left, r->whole_word_chars_left,
466 r->whole_word_chars_right, r->line_start_left);
467 if (e >= end && (!_rule.keyword || keyword_foundright))
469 _rule.end = e;
470 found_right = TRUE;
471 _rule.border = RULE_ON_LEFT_BORDER;
472 _rule._context = count;
473 if (!r->between_delimiters && !_rule.keyword)
475 _rule.context = count;
476 contextchanged = TRUE;
478 break;
485 /* check again to turn on a keyword if the context switched */
486 if (contextchanged && !_rule.keyword)
488 const char *p;
490 r = edit->rules[_rule.context];
491 p = r->keyword_first_chars;
493 while (*(p = xx_strchr (edit, (unsigned char *) p + 1, c)) != '\0')
495 struct key_word *k;
496 int count;
497 long e;
499 count = p - r->keyword_first_chars;
500 k = r->keyword[count];
501 e = compare_word_to_right (edit, i, k->keyword, k->whole_word_chars_left,
502 k->whole_word_chars_right, k->line_start);
503 if (e > 0)
505 _rule.end = e;
506 _rule.keyword = count;
507 break;
512 return _rule;
515 static struct syntax_rule
516 edit_get_rule (WEdit * edit, long byte_index)
518 long i;
520 if (byte_index > edit->last_get_rule)
522 for (i = edit->last_get_rule + 1; i <= byte_index; i++)
524 edit->rule = apply_rules_going_right (edit, i, edit->rule);
525 if (i >
526 (edit->syntax_marker ? edit->syntax_marker->offset +
527 SYNTAX_MARKER_DENSITY : SYNTAX_MARKER_DENSITY))
529 struct _syntax_marker *s;
531 s = edit->syntax_marker;
532 edit->syntax_marker = g_malloc (sizeof (struct _syntax_marker));
533 edit->syntax_marker->next = s;
534 edit->syntax_marker->offset = i;
535 edit->syntax_marker->rule = edit->rule;
539 else if (byte_index < edit->last_get_rule)
541 struct _syntax_marker *s;
543 for (;;)
545 if (!edit->syntax_marker)
547 memset (&edit->rule, 0, sizeof (edit->rule));
548 for (i = -1; i <= byte_index; i++)
549 edit->rule = apply_rules_going_right (edit, i, edit->rule);
550 break;
552 if (byte_index >= edit->syntax_marker->offset)
554 edit->rule = edit->syntax_marker->rule;
555 for (i = edit->syntax_marker->offset + 1; i <= byte_index; i++)
556 edit->rule = apply_rules_going_right (edit, i, edit->rule);
557 break;
559 s = edit->syntax_marker->next;
560 g_free (edit->syntax_marker);
561 edit->syntax_marker = s;
564 edit->last_get_rule = byte_index;
565 return edit->rule;
568 static inline void
569 translate_rule_to_color (WEdit * edit, struct syntax_rule rule, int *color)
571 *color = edit->rules[rule.context]->keyword[rule.keyword]->color;
574 void
575 edit_get_syntax_color (WEdit * edit, long byte_index, int *color)
577 if (!tty_use_colors ())
578 *color = 0;
579 else if (edit->rules && byte_index < edit->last_byte && option_syntax_highlighting)
580 translate_rule_to_color (edit, edit_get_rule (edit, byte_index), color);
581 else
582 *color = EDITOR_NORMAL_COLOR;
587 Returns 0 on error/eof or a count of the number of bytes read
588 including the newline. Result must be free'd.
589 In case of an error, *line will not be modified.
591 static size_t
592 read_one_line (char **line, FILE * f)
594 GString *p;
595 size_t r = 0;
597 /* not reallocate string too often */
598 p = g_string_sized_new (64);
600 for (;;)
602 int c;
604 c = fgetc (f);
605 if (c == EOF)
607 if (ferror (f))
609 if (errno == EINTR)
610 continue;
611 r = 0;
613 break;
615 r++;
617 /* handle all of \r\n, \r, \n correctly. */
618 if (c == '\n')
619 break;
620 if (c == '\r')
622 c = fgetc (f);
623 if (c == '\n')
624 r++;
625 else
626 ungetc (c, f);
627 break;
630 g_string_append_c (p, c);
632 if (r != 0)
633 *line = g_string_free (p, FALSE);
634 else
635 g_string_free (p, TRUE);
637 return r;
640 static char *
641 convert (char *s)
643 char *r, *p;
645 p = r = s;
646 while (*s)
648 switch (*s)
650 case '\\':
651 s++;
652 switch (*s)
654 case ' ':
655 *p = ' ';
656 s--;
657 break;
658 case 'n':
659 *p = '\n';
660 break;
661 case 'r':
662 *p = '\r';
663 break;
664 case 't':
665 *p = '\t';
666 break;
667 case 's':
668 *p = ' ';
669 break;
670 case '*':
671 *p = '*';
672 break;
673 case '\\':
674 *p = '\\';
675 break;
676 case '[':
677 case ']':
678 *p = SYNTAX_TOKEN_BRACKET;
679 break;
680 case '{':
681 case '}':
682 *p = SYNTAX_TOKEN_BRACE;
683 break;
684 case 0:
685 *p = *s;
686 return r;
687 default:
688 *p = *s;
689 break;
691 break;
692 case '*':
693 *p = SYNTAX_TOKEN_STAR;
694 break;
695 case '+':
696 *p = SYNTAX_TOKEN_PLUS;
697 break;
698 default:
699 *p = *s;
700 break;
702 s++;
703 p++;
705 *p = '\0';
706 return r;
709 #define whiteness(x) ((x) == '\t' || (x) == '\n' || (x) == ' ')
711 static int
712 get_args (char *l, char **args, int args_size)
714 int argc = 0;
716 while (argc < args_size)
718 char *p = l;
719 while (*p != '\0' && whiteness (*p))
720 p++;
721 if (*p == '\0')
722 break;
723 for (l = p + 1; *l != '\0' && !whiteness (*l); l++)
725 if (*l != '\0')
726 *l++ = '\0';
727 args[argc++] = convert (p);
729 args[argc] = (char *) NULL;
730 return argc;
733 #define free_args(x)
734 #define break_a {result=line;break;}
735 #define check_a {if(!*a){result=line;break;}}
736 #define check_not_a {if(*a){result=line;break;}}
738 static int
739 this_try_alloc_color_pair (const char *fg, const char *bg)
741 char f[80], b[80], *p;
743 if (bg != NULL && *bg == '\0')
744 bg = NULL;
745 if (fg != NULL && *fg == '\0')
746 fg = NULL;
747 if (fg != NULL)
749 g_strlcpy (f, fg, sizeof (f));
750 p = strchr (f, '/');
751 if (p != NULL)
752 *p = '\0';
753 fg = f;
755 if (bg != NULL)
757 g_strlcpy (b, bg, sizeof (b));
758 p = strchr (b, '/');
759 if (p != NULL)
760 *p = '\0';
761 bg = b;
763 return tty_try_alloc_color_pair (fg, bg);
766 static char *error_file_name = NULL;
768 static FILE *
769 open_include_file (const char *filename)
771 FILE *f;
773 MC_PTR_FREE (error_file_name);
774 error_file_name = g_strdup (filename);
775 if (g_path_is_absolute (filename))
776 return fopen (filename, "r");
778 g_free (error_file_name);
779 error_file_name = g_strconcat (home_dir, PATH_SEP_STR EDIT_DIR PATH_SEP_STR,
780 filename, (char *) NULL);
781 f = fopen (error_file_name, "r");
782 if (f != NULL)
783 return f;
785 g_free (error_file_name);
786 error_file_name = g_strconcat (mc_home, PATH_SEP_STR, "syntax", PATH_SEP_STR,
787 filename, (char *) NULL);
788 f = fopen (error_file_name, "r");
789 if (f != NULL)
790 return f;
792 g_free (error_file_name);
793 error_file_name = g_strconcat (mc_home_alt, PATH_SEP_STR "syntax" PATH_SEP_STR,
794 filename, (char *) NULL);
796 return fopen (error_file_name, "r");
799 inline static void
800 xx_lowerize_line (WEdit * edit, char *line, size_t len)
802 if (edit->is_case_insensitive)
804 size_t i;
805 for (i = 0; i < len; ++i)
806 line[i] = tolower (line[i]);
810 /* returns line number on error */
811 static int
812 edit_read_syntax_rules (WEdit * edit, FILE * f, char **args, int args_size)
814 FILE *g = NULL;
815 char *fg, *bg;
816 char last_fg[32] = "", last_bg[32] = "";
817 char whole_right[512];
818 char whole_left[512];
819 char *l = 0;
820 int save_line = 0, line = 0;
821 struct context_rule **r, *c = NULL;
822 int num_words = -1, num_contexts = -1;
823 int result = 0;
824 int argc;
825 int i, j;
826 int alloc_contexts = MAX_CONTEXTS,
827 alloc_words_per_context = MAX_WORDS_PER_CONTEXT,
828 max_alloc_words_per_context = MAX_WORDS_PER_CONTEXT;
830 args[0] = NULL;
831 edit->is_case_insensitive = FALSE;
833 strcpy (whole_left, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_01234567890");
834 strcpy (whole_right, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_01234567890");
836 r = edit->rules = g_malloc0 (alloc_contexts * sizeof (struct context_rule *));
838 if (!edit->defines)
839 edit->defines = g_tree_new ((GCompareFunc) strcmp);
841 for (;;)
843 char **a;
844 size_t len;
846 line++;
847 l = 0;
849 len = read_one_line (&l, f);
850 if (len == 0)
852 if (g)
854 fclose (f);
855 f = g;
856 g = 0;
857 line = save_line + 1;
858 MC_PTR_FREE (error_file_name);
859 MC_PTR_FREE (l);
860 len = read_one_line (&l, f);
861 if (len == 0)
862 break;
863 else
864 xx_lowerize_line (edit, l, len);
866 else
868 break;
871 else
873 xx_lowerize_line (edit, l, len);
875 argc = get_args (l, args, args_size);
876 a = args + 1;
877 if (!args[0])
879 /* do nothing */
881 else if (!strcmp (args[0], "include"))
883 if (g || argc != 2)
885 result = line;
886 break;
888 g = f;
889 f = open_include_file (args[1]);
890 if (!f)
892 MC_PTR_FREE (error_file_name);
893 result = line;
894 break;
896 save_line = line;
897 line = 0;
899 else if (!strcmp (args[0], "caseinsensitive"))
901 edit->is_case_insensitive = TRUE;
903 else if (!strcmp (args[0], "wholechars"))
905 check_a;
906 if (!strcmp (*a, "left"))
908 a++;
909 g_strlcpy (whole_left, *a, sizeof (whole_left));
911 else if (!strcmp (*a, "right"))
913 a++;
914 g_strlcpy (whole_right, *a, sizeof (whole_right));
916 else
918 g_strlcpy (whole_left, *a, sizeof (whole_left));
919 g_strlcpy (whole_right, *a, sizeof (whole_right));
921 a++;
922 check_not_a;
924 else if (!strcmp (args[0], "context"))
926 check_a;
927 if (num_contexts == -1)
929 if (strcmp (*a, "default"))
930 { /* first context is the default */
931 break_a;
933 a++;
934 c = r[0] = g_malloc0 (sizeof (struct context_rule));
935 c->left = g_strdup (" ");
936 c->right = g_strdup (" ");
937 num_contexts = 0;
939 else
941 /* Terminate previous context. */
942 r[num_contexts - 1]->keyword[num_words] = NULL;
943 c = r[num_contexts] = g_malloc0 (sizeof (struct context_rule));
944 if (!strcmp (*a, "exclusive"))
946 a++;
947 c->between_delimiters = 1;
949 check_a;
950 if (!strcmp (*a, "whole"))
952 a++;
953 c->whole_word_chars_left = g_strdup (whole_left);
954 c->whole_word_chars_right = g_strdup (whole_right);
956 else if (!strcmp (*a, "wholeleft"))
958 a++;
959 c->whole_word_chars_left = g_strdup (whole_left);
961 else if (!strcmp (*a, "wholeright"))
963 a++;
964 c->whole_word_chars_right = g_strdup (whole_right);
966 check_a;
967 if (!strcmp (*a, "linestart"))
969 a++;
970 c->line_start_left = 1;
972 check_a;
973 c->left = g_strdup (*a++);
974 check_a;
975 if (!strcmp (*a, "linestart"))
977 a++;
978 c->line_start_right = 1;
980 check_a;
981 c->right = g_strdup (*a++);
982 c->first_left = *c->left;
983 c->first_right = *c->right;
985 c->keyword = g_malloc (alloc_words_per_context * sizeof (struct key_word *));
986 num_words = 1;
987 c->keyword[0] = g_malloc0 (sizeof (struct key_word));
988 subst_defines (edit->defines, a, &args[1024]);
989 fg = *a;
990 if (*a)
991 a++;
992 bg = *a;
993 if (*a)
994 a++;
995 g_strlcpy (last_fg, fg ? fg : "", sizeof (last_fg));
996 g_strlcpy (last_bg, bg ? bg : "", sizeof (last_bg));
997 c->keyword[0]->color = this_try_alloc_color_pair (fg, bg);
998 c->keyword[0]->keyword = g_strdup (" ");
999 check_not_a;
1001 alloc_words_per_context = MAX_WORDS_PER_CONTEXT;
1002 if (++num_contexts >= alloc_contexts)
1004 struct context_rule **tmp;
1006 alloc_contexts += 128;
1007 tmp = g_realloc (r, alloc_contexts * sizeof (struct context_rule *));
1008 r = tmp;
1011 else if (!strcmp (args[0], "spellcheck"))
1013 if (!c)
1015 result = line;
1016 break;
1018 c->spelling = 1;
1020 else if (!strcmp (args[0], "keyword"))
1022 struct key_word *k;
1024 if (num_words == -1)
1025 break_a;
1026 check_a;
1027 k = r[num_contexts - 1]->keyword[num_words] = g_malloc0 (sizeof (struct key_word));
1028 if (!strcmp (*a, "whole"))
1030 a++;
1031 k->whole_word_chars_left = g_strdup (whole_left);
1032 k->whole_word_chars_right = g_strdup (whole_right);
1034 else if (!strcmp (*a, "wholeleft"))
1036 a++;
1037 k->whole_word_chars_left = g_strdup (whole_left);
1039 else if (!strcmp (*a, "wholeright"))
1041 a++;
1042 k->whole_word_chars_right = g_strdup (whole_right);
1044 check_a;
1045 if (!strcmp (*a, "linestart"))
1047 a++;
1048 k->line_start = 1;
1050 check_a;
1051 if (!strcmp (*a, "whole"))
1053 break_a;
1055 k->keyword = g_strdup (*a++);
1056 k->first = *k->keyword;
1057 subst_defines (edit->defines, a, &args[1024]);
1058 fg = *a;
1059 if (*a)
1060 a++;
1061 bg = *a;
1062 if (*a)
1063 a++;
1064 if (!fg)
1065 fg = last_fg;
1066 if (!bg)
1067 bg = last_bg;
1068 k->color = this_try_alloc_color_pair (fg, bg);
1069 check_not_a;
1071 if (++num_words >= alloc_words_per_context)
1073 struct key_word **tmp;
1075 alloc_words_per_context += 1024;
1077 if (alloc_words_per_context > max_alloc_words_per_context)
1078 max_alloc_words_per_context = alloc_words_per_context;
1080 tmp = g_realloc (c->keyword, alloc_words_per_context * sizeof (struct key_word *));
1081 c->keyword = tmp;
1084 else if (*(args[0]) == '#')
1086 /* do nothing for comment */
1088 else if (!strcmp (args[0], "file"))
1090 break;
1092 else if (!strcmp (args[0], "define"))
1094 char *key = *a++;
1095 char **argv;
1097 if (argc < 3)
1098 break_a;
1099 argv = g_tree_lookup (edit->defines, key);
1100 if (argv != NULL)
1101 mc_defines_destroy (NULL, argv, NULL);
1102 else
1103 key = g_strdup (key);
1105 argv = g_new (char *, argc - 1);
1106 g_tree_insert (edit->defines, key, argv);
1107 while (*a != NULL)
1109 *argv++ = g_strdup (*a++);
1111 *argv = NULL;
1113 else
1114 { /* anything else is an error */
1115 break_a;
1117 free_args (args);
1118 MC_PTR_FREE (l);
1120 free_args (args);
1121 MC_PTR_FREE (l);
1123 /* Terminate context array. */
1124 if (num_contexts > 0)
1126 r[num_contexts - 1]->keyword[num_words] = NULL;
1127 r[num_contexts] = NULL;
1130 if (!edit->rules[0])
1131 MC_PTR_FREE (edit->rules);
1133 if (result)
1134 return result;
1136 if (num_contexts == -1)
1138 return line;
1142 char *first_chars, *p;
1144 first_chars = g_malloc0 (max_alloc_words_per_context + 2);
1146 for (i = 0; edit->rules[i]; i++)
1148 c = edit->rules[i];
1149 p = first_chars;
1150 *p++ = (char) 1;
1151 for (j = 1; c->keyword[j]; j++)
1152 *p++ = c->keyword[j]->first;
1153 *p = '\0';
1154 c->keyword_first_chars = g_strdup (first_chars);
1157 g_free (first_chars);
1160 return result;
1163 void
1164 edit_free_syntax_rules (WEdit * edit)
1166 size_t i, j;
1168 if (!edit)
1169 return;
1170 if (edit->defines)
1171 destroy_defines (&edit->defines);
1172 if (!edit->rules)
1173 return;
1175 edit_get_rule (edit, -1);
1176 MC_PTR_FREE (edit->syntax_type);
1178 for (i = 0; edit->rules[i]; i++)
1180 if (edit->rules[i]->keyword)
1182 for (j = 0; edit->rules[i]->keyword[j]; j++)
1184 MC_PTR_FREE (edit->rules[i]->keyword[j]->keyword);
1185 MC_PTR_FREE (edit->rules[i]->keyword[j]->whole_word_chars_left);
1186 MC_PTR_FREE (edit->rules[i]->keyword[j]->whole_word_chars_right);
1187 MC_PTR_FREE (edit->rules[i]->keyword[j]);
1190 MC_PTR_FREE (edit->rules[i]->left);
1191 MC_PTR_FREE (edit->rules[i]->right);
1192 MC_PTR_FREE (edit->rules[i]->whole_word_chars_left);
1193 MC_PTR_FREE (edit->rules[i]->whole_word_chars_right);
1194 MC_PTR_FREE (edit->rules[i]->keyword);
1195 MC_PTR_FREE (edit->rules[i]->keyword_first_chars);
1196 MC_PTR_FREE (edit->rules[i]);
1199 while (edit->syntax_marker)
1201 struct _syntax_marker *s = edit->syntax_marker->next;
1202 g_free (edit->syntax_marker);
1203 edit->syntax_marker = s;
1206 MC_PTR_FREE (edit->rules);
1207 tty_color_free_all_tmp ();
1210 /* returns -1 on file error, line number on error in file syntax */
1211 static int
1212 edit_read_syntax_file (WEdit * edit, char ***pnames, const char *syntax_file,
1213 const char *editor_file, const char *first_line, const char *type)
1215 #define NENTRIES 30
1216 FILE *f, *g = NULL;
1217 char *args[1024], *l = NULL;
1218 int line = 0;
1219 int result = 0;
1220 int count = 0;
1221 char *lib_file;
1222 gboolean found = FALSE;
1223 char **tmpnames = NULL;
1225 f = fopen (syntax_file, "r");
1226 if (f == NULL)
1228 lib_file = concat_dir_and_file (mc_home, "Syntax");
1229 f = fopen (lib_file, "r");
1230 g_free (lib_file);
1231 if (f == NULL)
1232 return -1;
1235 args[0] = NULL;
1236 for (;;)
1238 line++;
1239 MC_PTR_FREE (l);
1240 if (read_one_line (&l, f) == 0)
1241 break;
1242 (void) get_args (l, args, 1023); /* Final NULL */
1243 if (args[0] == NULL)
1244 continue;
1246 /* Looking for `include ...` lines before first `file ...` ones */
1247 if (!found && strcmp (args[0], "include") == 0)
1249 if (g != NULL)
1250 continue;
1252 if (!args[1] || !(g = open_include_file (args[1])))
1254 result = line;
1255 break;
1257 goto found_type;
1260 /* looking for `file ...' lines only */
1261 if (strcmp (args[0], "file") != 0)
1262 continue;
1264 found = TRUE;
1266 /* must have two args or report error */
1267 if (!args[1] || !args[2])
1269 result = line;
1270 break;
1272 if (pnames && *pnames)
1274 /* 1: just collecting a list of names of rule sets */
1275 /* Reallocate the list if required */
1276 if (count % NENTRIES == 0)
1278 tmpnames =
1279 (char **) g_try_realloc (*pnames, (count + NENTRIES + 1) * sizeof (char *));
1280 if (tmpnames == NULL)
1281 break;
1282 *pnames = tmpnames;
1284 (*pnames)[count++] = g_strdup (args[2]);
1285 (*pnames)[count] = NULL;
1287 else if (type)
1289 /* 2: rule set was explicitly specified by the caller */
1290 if (strcmp (type, args[2]) == 0)
1291 goto found_type;
1293 else if (editor_file && edit)
1295 /* 3: auto-detect rule set from regular expressions */
1296 int q;
1298 q = mc_search (args[1], editor_file, MC_SEARCH_T_REGEX);
1299 /* does filename match arg 1 ? */
1300 if (!q && args[3])
1302 /* does first line match arg 3 ? */
1303 q = mc_search (args[3], first_line, MC_SEARCH_T_REGEX);
1305 if (q)
1307 int line_error;
1308 char *syntax_type;
1309 found_type:
1310 syntax_type = args[2];
1311 line_error = edit_read_syntax_rules (edit, g ? g : f, args, 1023);
1312 if (line_error)
1314 if (!error_file_name) /* an included file */
1315 result = line + line_error;
1316 else
1317 result = line_error;
1319 else
1321 MC_PTR_FREE (edit->syntax_type);
1322 edit->syntax_type = g_strdup (syntax_type);
1323 /* if there are no rules then turn off syntax highlighting for speed */
1324 if (!g && !edit->rules[1])
1325 if (!edit->rules[0]->keyword[1] && !edit->rules[0]->spelling)
1327 edit_free_syntax_rules (edit);
1328 break;
1332 if (g == NULL)
1333 break;
1335 fclose (g);
1336 g = NULL;
1340 MC_PTR_FREE (l);
1341 fclose (f);
1342 return result;
1345 static char *
1346 get_first_editor_line (WEdit * edit)
1348 size_t i;
1349 static char s[256];
1351 s[0] = '\0';
1352 if (edit == NULL)
1353 return s;
1355 for (i = 0; i < sizeof (s) - 1; i++)
1357 s[i] = edit_get_byte (edit, i);
1358 if (s[i] == '\n')
1360 s[i] = '\0';
1361 break;
1364 s[sizeof (s) - 1] = '\0';
1365 return s;
1369 * Load rules into edit struct. Either edit or *pnames must be NULL. If
1370 * edit is NULL, a list of types will be stored into names. If type is
1371 * NULL, then the type will be selected according to the filename.
1373 void
1374 edit_load_syntax (WEdit * edit, char ***pnames, const char *type)
1376 int r;
1377 char *f = NULL;
1379 if (option_auto_syntax)
1380 type = NULL;
1382 edit_free_syntax_rules (edit);
1384 if (!tty_use_colors ())
1385 return;
1387 if (!option_syntax_highlighting && (!pnames || !*pnames))
1388 return;
1390 if (edit)
1392 if (!edit->filename)
1393 return;
1394 if (!*edit->filename && !type)
1395 return;
1397 f = concat_dir_and_file (home_dir, EDIT_SYNTAX_FILE);
1398 r = edit_read_syntax_file (edit, pnames, f, edit ? edit->filename : 0,
1399 get_first_editor_line (edit), type);
1400 if (r == -1)
1402 edit_free_syntax_rules (edit);
1403 message (D_ERROR, _(" Load syntax file "),
1404 _(" Cannot open file %s \n %s "), f, unix_error_string (errno));
1406 else if (r != 0)
1408 edit_free_syntax_rules (edit);
1409 message (D_ERROR, _(" Load syntax file "),
1410 _(" Error in file %s on line %d "), error_file_name ? error_file_name : f, r);
1411 MC_PTR_FREE (error_file_name);
1414 g_free (f);
1417 const char *
1418 edit_get_syntax_type (const WEdit * edit)
1420 return edit->syntax_type;