Ticket #1906: edit: crash on file open whoen some Syntax files are absent (reported...
[midnight-commander.git] / edit / syntax.c
blob7479483cd46730880aadb0bc1776c21592d76975
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
29 * Mispelled words are flushed from the syntax highlighting rules
30 * when they have been around longer than
31 * TRANSIENT_WORD_TIME_OUT seconds. At a cursor rate of 30
32 * chars per second and say 3 chars + a space per word, we can
33 * accumulate 450 words absolute max with a value of 60. This is
34 * below this limit of 1024 words in a context.
37 #include <config.h>
39 #include <stdio.h>
40 #include <stdarg.h>
41 #include <sys/types.h>
42 #include <unistd.h>
43 #include <string.h>
44 #include <ctype.h>
45 #include <errno.h>
46 #include <sys/stat.h>
47 #include <stdlib.h>
49 #include "../src/global.h"
51 #include "edit-impl.h"
52 #include "edit-widget.h"
54 #include "../src/search/search.h" /* search engine */
56 #include "../src/skin/skin.h"
58 #include "edit-impl.h"
59 #include "edit-widget.h"
61 #include "../src/main.h" /* mc_home */
62 #include "../src/wtools.h" /* message() */
63 #include "../src/strutil.h" /* utf string functions */
65 /* bytes */
66 #define SYNTAX_MARKER_DENSITY 512
68 #define TRANSIENT_WORD_TIME_OUT 60
70 #define UNKNOWN_FORMAT "unknown"
72 #define MAX_WORDS_PER_CONTEXT 1024
73 #define MAX_CONTEXTS 128
75 #define RULE_ON_LEFT_BORDER 1
76 #define RULE_ON_RIGHT_BORDER 2
78 #define SYNTAX_TOKEN_STAR '\001'
79 #define SYNTAX_TOKEN_PLUS '\002'
80 #define SYNTAX_TOKEN_BRACKET '\003'
81 #define SYNTAX_TOKEN_BRACE '\004'
83 struct key_word {
84 char *keyword;
85 unsigned char first;
86 char *whole_word_chars_left;
87 char *whole_word_chars_right;
88 int line_start;
89 int color;
92 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 {
109 long offset;
110 struct syntax_rule rule;
111 struct _syntax_marker *next;
114 int option_syntax_highlighting = 1;
115 int option_auto_syntax = 1;
116 char *option_syntax_type = NULL;
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 = 0;
142 static void
143 subst_defines (GTree *defines, char **argv, char **argv_end)
145 char **t, **p;
146 int argc;
148 while (*argv && argv < argv_end) {
149 if ((t = g_tree_lookup (defines, *argv))) {
150 int count = 0;
152 /* Count argv array members */
153 argc = 0;
154 for (p = &argv[1]; *p; p++)
155 argc++;
157 /* Count members of definition array */
158 for (p = t; *p; p++)
159 count++;
160 p = &argv[count + argc];
162 /* Buffer overflow or infinitive loop in define */
163 if (p >= argv_end)
164 break;
166 /* Move rest of argv after definition members */
167 while (argc >= 0)
168 *p-- = argv[argc-- + 1];
170 /* Copy definition members to argv */
171 for (p = argv; *t; *p++ = *t++);
173 argv++;
177 static long
178 compare_word_to_right (WEdit *edit, long i, const char *text,
179 const char *whole_left, const char *whole_right,
180 int line_start)
182 const unsigned char *p, *q;
183 int c, d, j;
185 if (!*text)
186 return -1;
187 c = edit_get_byte (edit, i - 1);
188 if (line_start)
189 if (c != '\n')
190 return -1;
191 if (whole_left)
192 if (strchr (whole_left, c))
193 return -1;
195 for (p = (unsigned char *) text, q = p + str_term_width1 ((char *) p); p < q; p++, i++) {
196 switch (*p) {
197 case SYNTAX_TOKEN_STAR:
198 if (++p > q)
199 return -1;
200 for (;;) {
201 c = edit_get_byte (edit, i);
202 if (!*p)
203 if (whole_right)
204 if (!strchr (whole_right, c))
205 break;
206 if (c == *p)
207 break;
208 if (c == '\n')
209 return -1;
210 i++;
212 break;
213 case SYNTAX_TOKEN_PLUS:
214 if (++p > q)
215 return -1;
216 j = 0;
217 for (;;) {
218 c = edit_get_byte (edit, i);
219 if (c == *p) {
220 j = i;
221 if (*p == *text && !p[1]) /* handle eg '+' and @+@ keywords properly */
222 break;
224 if (j && strchr ((char *) p + 1, c)) /* c exists further down, so it will get matched later */
225 break;
226 if (c == '\n' || c == '\t' || c == ' ') {
227 if (!*p) {
228 i--;
229 break;
231 if (!j)
232 return -1;
233 i = j;
234 break;
236 if (whole_right)
237 if (!strchr (whole_right, c)) {
238 if (!*p) {
239 i--;
240 break;
242 if (!j)
243 return -1;
244 i = j;
245 break;
247 i++;
249 break;
250 case SYNTAX_TOKEN_BRACKET:
251 if (++p > q)
252 return -1;
253 c = -1;
254 for (;; i++) {
255 d = c;
256 c = edit_get_byte (edit, i);
257 for (j = 0; p[j] != SYNTAX_TOKEN_BRACKET && p[j]; j++)
258 if (c == p[j])
259 goto found_char2;
260 break;
261 found_char2:
262 ; /* dummy command */
264 i--;
265 while (*p != SYNTAX_TOKEN_BRACKET && p <= q)
266 p++;
267 if (p > q)
268 return -1;
269 if (p[1] == d)
270 i--;
271 break;
272 case SYNTAX_TOKEN_BRACE:
273 if (++p > q)
274 return -1;
275 c = edit_get_byte (edit, i);
276 for (; *p != SYNTAX_TOKEN_BRACE && *p; p++)
277 if (c == *p)
278 goto found_char3;
279 return -1;
280 found_char3:
281 while (*p != SYNTAX_TOKEN_BRACE && p < q)
282 p++;
283 break;
284 default:
285 if (*p != edit_get_byte (edit, i))
286 return -1;
289 if (whole_right)
290 if (strchr (whole_right, edit_get_byte (edit, i)))
291 return -1;
292 return i;
295 static const char *xx_strchr (const unsigned char *s, int c)
297 while (*s >= '\005' && *s != (unsigned char) c) {
298 s++;
300 return (const char *) s;
303 static struct syntax_rule apply_rules_going_right (WEdit * edit, long i, struct syntax_rule rule)
305 struct context_rule *r;
306 int contextchanged = 0, c;
307 int found_right = 0, found_left = 0, keyword_foundleft = 0, keyword_foundright = 0;
308 int is_end;
309 long end = 0;
310 struct syntax_rule _rule = rule;
312 if (!(c = edit_get_byte (edit, i)))
313 return rule;
314 is_end = (rule.end == (unsigned char) i);
316 /* check to turn off a keyword */
317 if (_rule.keyword) {
318 if (edit_get_byte (edit, i - 1) == '\n')
319 _rule.keyword = 0;
320 if (is_end) {
321 _rule.keyword = 0;
322 keyword_foundleft = 1;
326 /* check to turn off a context */
327 if (_rule.context && !_rule.keyword) {
328 long e;
329 r = edit->rules[_rule.context];
330 if (r->first_right == c && !(rule.border & RULE_ON_RIGHT_BORDER) && (e = compare_word_to_right (edit, i, r->right, r->whole_word_chars_left, r->whole_word_chars_right, r->line_start_right)) > 0) {
331 _rule.end = e;
332 found_right = 1;
333 _rule.border = RULE_ON_RIGHT_BORDER;
334 if (r->between_delimiters)
335 _rule.context = 0;
336 } else if (is_end && rule.border & RULE_ON_RIGHT_BORDER) {
338 /* always turn off a context at 4 */
339 found_left = 1;
340 _rule.border = 0;
341 if (!keyword_foundleft)
342 _rule.context = 0;
343 } else if (is_end && rule.border & RULE_ON_LEFT_BORDER) {
345 /* never turn off a context at 2 */
346 found_left = 1;
347 _rule.border = 0;
351 /* check to turn on a keyword */
352 if (!_rule.keyword) {
353 const char *p;
355 p = (r = edit->rules[_rule.context])->keyword_first_chars;
356 if (p)
357 while (*(p = xx_strchr ((unsigned char *) p + 1, c))) {
358 struct key_word *k;
359 int count;
360 long e;
362 count = p - r->keyword_first_chars;
363 k = r->keyword[count];
364 e = compare_word_to_right (edit, i, k->keyword, k->whole_word_chars_left, k->whole_word_chars_right, k->line_start);
365 if (e > 0) {
366 end = e;
367 _rule.end = e;
368 _rule.keyword = count;
369 keyword_foundright = 1;
370 break;
374 /* check to turn on a context */
375 if (!_rule.context) {
376 if (!found_left && is_end) {
377 if (rule.border & RULE_ON_RIGHT_BORDER) {
378 _rule.border = 0;
379 _rule.context = 0;
380 contextchanged = 1;
381 _rule.keyword = 0;
383 } else if (rule.border & RULE_ON_LEFT_BORDER) {
384 r = edit->rules[_rule._context];
385 _rule.border = 0;
386 if (r->between_delimiters) {
387 long e;
388 _rule.context = _rule._context;
389 contextchanged = 1;
390 _rule.keyword = 0;
391 if (r->first_right == c && (e = compare_word_to_right (edit, i, r->right, r->whole_word_chars_left, r->whole_word_chars_right, r->line_start_right)) >= end) {
392 _rule.end = e;
393 found_right = 1;
394 _rule.border = RULE_ON_RIGHT_BORDER;
395 _rule.context = 0;
401 if (!found_right) {
402 int count;
403 struct context_rule **rules = edit->rules;
405 for (count = 1; rules[count]; count++) {
406 r = rules[count];
407 if (r->first_left == c) {
408 long e;
410 e = compare_word_to_right (edit, i, r->left, r->whole_word_chars_left, r->whole_word_chars_right, r->line_start_left);
411 if (e >= end && (!_rule.keyword || keyword_foundright)) {
412 _rule.end = e;
413 found_right = 1;
414 _rule.border = RULE_ON_LEFT_BORDER;
415 _rule._context = count;
416 if (!r->between_delimiters)
417 if (!_rule.keyword) {
418 _rule.context = count;
419 contextchanged = 1;
421 break;
428 /* check again to turn on a keyword if the context switched */
429 if (contextchanged && !_rule.keyword) {
430 const char *p;
432 p = (r = edit->rules[_rule.context])->keyword_first_chars;
433 while (*(p = xx_strchr ((unsigned char *) p + 1, c))) {
434 struct key_word *k;
435 int count;
436 long e;
438 count = p - r->keyword_first_chars;
439 k = r->keyword[count];
440 e = compare_word_to_right (edit, i, k->keyword, k->whole_word_chars_left, k->whole_word_chars_right, k->line_start);
441 if (e > 0) {
442 _rule.end = e;
443 _rule.keyword = count;
444 break;
448 return _rule;
451 static struct syntax_rule edit_get_rule (WEdit * edit, long byte_index)
453 long i;
455 if (byte_index > edit->last_get_rule) {
456 for (i = edit->last_get_rule + 1; i <= byte_index; i++) {
457 edit->rule = apply_rules_going_right (edit, i, edit->rule);
458 if (i > (edit->syntax_marker ? edit->syntax_marker->offset + SYNTAX_MARKER_DENSITY : SYNTAX_MARKER_DENSITY)) {
459 struct _syntax_marker *s;
461 s = edit->syntax_marker;
462 edit->syntax_marker = g_malloc0 (sizeof (struct _syntax_marker));
463 edit->syntax_marker->next = s;
464 edit->syntax_marker->offset = i;
465 edit->syntax_marker->rule = edit->rule;
468 } else if (byte_index < edit->last_get_rule) {
469 struct _syntax_marker *s;
471 for (;;) {
472 if (!edit->syntax_marker) {
473 memset (&edit->rule, 0, sizeof (edit->rule));
474 for (i = -1; i <= byte_index; i++)
475 edit->rule = apply_rules_going_right (edit, i, edit->rule);
476 break;
478 if (byte_index >= edit->syntax_marker->offset) {
479 edit->rule = edit->syntax_marker->rule;
480 for (i = edit->syntax_marker->offset + 1; i <= byte_index; i++)
481 edit->rule = apply_rules_going_right (edit, i, edit->rule);
482 break;
484 s = edit->syntax_marker->next;
485 MC_PTR_FREE (edit->syntax_marker);
486 edit->syntax_marker = s;
489 edit->last_get_rule = byte_index;
490 return edit->rule;
493 static inline void
494 translate_rule_to_color (WEdit * edit, struct syntax_rule rule, int *color)
496 struct key_word *k;
498 k = edit->rules[rule.context]->keyword[rule.keyword];
499 *color = k->color;
502 void edit_get_syntax_color (WEdit * edit, long byte_index, int *color)
504 if (edit->rules && byte_index < edit->last_byte &&
505 option_syntax_highlighting && tty_use_colors ()) {
506 translate_rule_to_color (edit, edit_get_rule (edit, byte_index), color);
507 } else {
508 *color = tty_use_colors () ? mc_skin_color_get("editor", "_default_") : 0;
514 Returns 0 on error/eof or a count of the number of bytes read
515 including the newline. Result must be free'd.
516 In case of an error, *line will not be modified.
518 static int read_one_line (char **line, FILE * f)
520 GString *p = g_string_new ("");
521 int c, r = 0;
523 for (;;) {
524 c = fgetc (f);
525 if (c == EOF) {
526 if (ferror (f)) {
527 if (errno == EINTR)
528 continue;
529 r = 0;
531 break;
533 r++;
534 /* handle all of \r\n, \r, \n correctly. */
535 if (c == '\r') {
536 if ( (c = fgetc (f)) == '\n')
537 r++;
538 else
539 ungetc (c, f);
540 break;
542 if (c == '\n')
543 break;
545 g_string_append_c (p, c);
547 if (r != 0) {
548 *line = p->str;
549 g_string_free (p, FALSE);
550 } else {
551 g_string_free (p, TRUE);
553 return r;
556 static char *convert (char *s)
558 char *r, *p;
560 p = r = s;
561 while (*s) {
562 switch (*s) {
563 case '\\':
564 s++;
565 switch (*s) {
566 case ' ':
567 *p = ' ';
568 s--;
569 break;
570 case 'n':
571 *p = '\n';
572 break;
573 case 'r':
574 *p = '\r';
575 break;
576 case 't':
577 *p = '\t';
578 break;
579 case 's':
580 *p = ' ';
581 break;
582 case '*':
583 *p = '*';
584 break;
585 case '\\':
586 *p = '\\';
587 break;
588 case '[':
589 case ']':
590 *p = SYNTAX_TOKEN_BRACKET;
591 break;
592 case '{':
593 case '}':
594 *p = SYNTAX_TOKEN_BRACE;
595 break;
596 case 0:
597 *p = *s;
598 return r;
599 default:
600 *p = *s;
601 break;
603 break;
604 case '*':
605 *p = SYNTAX_TOKEN_STAR;
606 break;
607 case '+':
608 *p = SYNTAX_TOKEN_PLUS;
609 break;
610 default:
611 *p = *s;
612 break;
614 s++;
615 p++;
617 *p = '\0';
618 return r;
621 #define whiteness(x) ((x) == '\t' || (x) == '\n' || (x) == ' ')
623 static int get_args (char *l, char **args, int args_size)
625 int argc = 0;
627 while (argc < args_size) {
628 char *p = l;
629 while (*p && whiteness (*p))
630 p++;
631 if (!*p)
632 break;
633 for (l = p + 1; *l && !whiteness (*l); l++);
634 if (*l)
635 *l++ = '\0';
636 args[argc++] = convert (p);
638 args[argc] = (char *) NULL;
639 return argc;
642 #define free_args(x)
643 #define break_a {result=line;break;}
644 #define check_a {if(!*a){result=line;break;}}
645 #define check_not_a {if(*a){result=line;break;}}
647 static int
648 this_try_alloc_color_pair (const char *fg, const char *bg)
650 char f[80], b[80], *p;
652 if (bg)
653 if (!*bg)
654 bg = 0;
655 if (fg)
656 if (!*fg)
657 fg = 0;
658 if (fg) {
659 g_strlcpy (f, fg, sizeof (f));
660 p = strchr (f, '/');
661 if (p)
662 *p = '\0';
663 fg = f;
665 if (bg) {
666 g_strlcpy (b, bg, sizeof (b));
667 p = strchr (b, '/');
668 if (p)
669 *p = '\0';
670 bg = b;
672 return tty_try_alloc_color_pair (fg, bg);
675 static char *error_file_name = 0;
677 static FILE *open_include_file (const char *filename)
679 FILE *f;
681 MC_PTR_FREE (error_file_name);
682 error_file_name = g_strdup (filename);
683 if (*filename == PATH_SEP)
684 return fopen (filename, "r");
686 g_free (error_file_name);
687 error_file_name = g_strconcat (home_dir, PATH_SEP_STR EDIT_DIR PATH_SEP_STR,
688 filename, (char *) NULL);
689 f = fopen (error_file_name, "r");
690 if (f)
691 return f;
693 g_free (error_file_name);
694 error_file_name = g_strconcat (mc_home, PATH_SEP_STR, "syntax", PATH_SEP_STR,
695 filename, (char *) NULL);
696 f = fopen (error_file_name, "r");
697 if (f)
698 return f;
700 g_free (error_file_name);
701 error_file_name = g_strconcat (mc_home_alt, PATH_SEP_STR "syntax" PATH_SEP_STR,
702 filename, (char *) NULL);
704 return fopen (error_file_name, "r");
707 /* returns line number on error */
708 static int
709 edit_read_syntax_rules (WEdit *edit, FILE *f, char **args, int args_size)
711 FILE *g = 0;
712 char *fg, *bg;
713 char last_fg[32] = "", last_bg[32] = "";
714 char whole_right[512];
715 char whole_left[512];
716 char *l = 0;
717 int save_line = 0, line = 0;
718 struct context_rule **r, *c = 0;
719 int num_words = -1, num_contexts = -1;
720 int result = 0;
721 int argc;
722 int i, j;
723 int alloc_contexts = MAX_CONTEXTS,
724 alloc_words_per_context = MAX_WORDS_PER_CONTEXT,
725 max_alloc_words_per_context = MAX_WORDS_PER_CONTEXT;
727 args[0] = 0;
729 strcpy (whole_left, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_01234567890");
730 strcpy (whole_right, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_01234567890");
732 r = edit->rules = g_malloc0 (alloc_contexts * sizeof (struct context_rule *));
734 if (!edit->defines)
735 edit->defines = g_tree_new ((GCompareFunc) strcmp);
737 for (;;) {
738 char **a;
740 line++;
741 l = 0;
742 if (!read_one_line (&l, f)) {
743 if (g) {
744 fclose (f);
745 f = g;
746 g = 0;
747 line = save_line + 1;
748 MC_PTR_FREE (error_file_name);
749 MC_PTR_FREE (l);
750 if (!read_one_line (&l, f))
751 break;
752 } else {
753 break;
756 argc = get_args (l, args, args_size);
757 a = args + 1;
758 if (!args[0]) {
759 /* do nothing */
760 } else if (!strcmp (args[0], "include")) {
761 if (g || argc != 2) {
762 result = line;
763 break;
765 g = f;
766 f = open_include_file (args[1]);
767 if (!f) {
768 MC_PTR_FREE (error_file_name);
769 result = line;
770 break;
772 save_line = line;
773 line = 0;
774 } else if (!strcmp (args[0], "wholechars")) {
775 check_a;
776 if (!strcmp (*a, "left")) {
777 a++;
778 g_strlcpy (whole_left, *a, sizeof (whole_left));
779 } else if (!strcmp (*a, "right")) {
780 a++;
781 g_strlcpy (whole_right, *a, sizeof (whole_right));
782 } else {
783 g_strlcpy (whole_left, *a, sizeof (whole_left));
784 g_strlcpy (whole_right, *a, sizeof (whole_right));
786 a++;
787 check_not_a;
788 } else if (!strcmp (args[0], "context")) {
789 check_a;
790 if (num_contexts == -1) {
791 if (strcmp (*a, "default")) { /* first context is the default */
792 break_a;
794 a++;
795 c = r[0] = g_malloc0 (sizeof (struct context_rule));
796 c->left = g_strdup (" ");
797 c->right = g_strdup (" ");
798 num_contexts = 0;
799 } else {
800 /* Terminate previous context. */
801 r[num_contexts - 1]->keyword[num_words] = NULL;
802 c = r[num_contexts] = g_malloc0 (sizeof (struct context_rule));
803 if (!strcmp (*a, "exclusive")) {
804 a++;
805 c->between_delimiters = 1;
807 check_a;
808 if (!strcmp (*a, "whole")) {
809 a++;
810 c->whole_word_chars_left = g_strdup (whole_left);
811 c->whole_word_chars_right = g_strdup (whole_right);
812 } else if (!strcmp (*a, "wholeleft")) {
813 a++;
814 c->whole_word_chars_left = g_strdup (whole_left);
815 } else if (!strcmp (*a, "wholeright")) {
816 a++;
817 c->whole_word_chars_right = g_strdup (whole_right);
819 check_a;
820 if (!strcmp (*a, "linestart")) {
821 a++;
822 c->line_start_left = 1;
824 check_a;
825 c->left = g_strdup (*a++);
826 check_a;
827 if (!strcmp (*a, "linestart")) {
828 a++;
829 c->line_start_right = 1;
831 check_a;
832 c->right = g_strdup (*a++);
833 c->first_left = *c->left;
834 c->first_right = *c->right;
836 c->keyword = g_malloc (alloc_words_per_context * sizeof (struct key_word *));
837 num_words = 1;
838 c->keyword[0] = g_malloc0 (sizeof (struct key_word));
839 subst_defines (edit->defines, a, &args[1024]);
840 fg = *a;
841 if (*a)
842 a++;
843 bg = *a;
844 if (*a)
845 a++;
846 g_strlcpy (last_fg, fg ? fg : "", sizeof (last_fg));
847 g_strlcpy (last_bg, bg ? bg : "", sizeof (last_bg));
848 c->keyword[0]->color = this_try_alloc_color_pair (fg, bg);
849 c->keyword[0]->keyword = g_strdup (" ");
850 check_not_a;
852 alloc_words_per_context = MAX_WORDS_PER_CONTEXT;
853 if (++num_contexts >= alloc_contexts) {
854 struct context_rule **tmp;
856 alloc_contexts += 128;
857 tmp = g_realloc (r, alloc_contexts * sizeof (struct context_rule *));
858 r = tmp;
860 } else if (!strcmp (args[0], "spellcheck")) {
861 if (!c) {
862 result = line;
863 break;
865 c->spelling = 1;
866 } else if (!strcmp (args[0], "keyword")) {
867 struct key_word *k;
869 if (num_words == -1)
870 break_a;
871 check_a;
872 k = r[num_contexts - 1]->keyword[num_words] = g_malloc0 (sizeof (struct key_word));
873 if (!strcmp (*a, "whole")) {
874 a++;
875 k->whole_word_chars_left = g_strdup (whole_left);
876 k->whole_word_chars_right = g_strdup (whole_right);
877 } else if (!strcmp (*a, "wholeleft")) {
878 a++;
879 k->whole_word_chars_left = g_strdup (whole_left);
880 } else if (!strcmp (*a, "wholeright")) {
881 a++;
882 k->whole_word_chars_right = g_strdup (whole_right);
884 check_a;
885 if (!strcmp (*a, "linestart")) {
886 a++;
887 k->line_start = 1;
889 check_a;
890 if (!strcmp (*a, "whole")) {
891 break_a;
893 k->keyword = g_strdup (*a++);
894 k->first = *k->keyword;
895 subst_defines (edit->defines, a, &args[1024]);
896 fg = *a;
897 if (*a)
898 a++;
899 bg = *a;
900 if (*a)
901 a++;
902 if (!fg)
903 fg = last_fg;
904 if (!bg)
905 bg = last_bg;
906 k->color = this_try_alloc_color_pair (fg, bg);
907 check_not_a;
909 if (++num_words >= alloc_words_per_context) {
910 struct key_word **tmp;
912 alloc_words_per_context += 1024;
914 if (alloc_words_per_context > max_alloc_words_per_context)
915 max_alloc_words_per_context = alloc_words_per_context;
917 tmp = g_realloc (c->keyword, alloc_words_per_context * sizeof (struct key_word *));
918 c->keyword = tmp;
920 } else if (*(args[0]) == '#') {
921 /* do nothing for comment */
922 } else if (!strcmp (args[0], "file")) {
923 break;
924 } else if (!strcmp (args[0], "define")) {
925 char *key = *a++;
926 char **argv;
928 if (argc < 3)
929 break_a;
930 if ((argv = g_tree_lookup (edit->defines, key))) {
931 mc_defines_destroy (NULL, argv, NULL);
932 } else {
933 key = g_strdup (key);
935 argv = g_new (char *, argc - 1);
936 g_tree_insert (edit->defines, key, argv);
937 while (*a) {
938 *argv++ = g_strdup (*a++);
940 *argv = NULL;
941 } else { /* anything else is an error */
942 break_a;
944 free_args (args);
945 MC_PTR_FREE (l);
947 free_args (args);
948 MC_PTR_FREE (l);
950 /* Terminate context array. */
951 if (num_contexts > 0) {
952 r[num_contexts - 1]->keyword[num_words] = NULL;
953 r[num_contexts] = NULL;
956 if (!edit->rules[0])
957 MC_PTR_FREE (edit->rules);
959 if (result)
960 return result;
962 if (num_contexts == -1) {
963 return line;
967 char *first_chars, *p;
969 first_chars = g_malloc (max_alloc_words_per_context + 2);
971 for (i = 0; edit->rules[i]; i++) {
972 c = edit->rules[i];
973 p = first_chars;
974 *p++ = (char) 1;
975 for (j = 1; c->keyword[j]; j++)
976 *p++ = c->keyword[j]->first;
977 *p = '\0';
978 c->keyword_first_chars = g_strdup (first_chars);
981 g_free (first_chars);
984 return result;
987 void edit_free_syntax_rules (WEdit * edit)
989 int i, j;
991 if (!edit)
992 return;
993 if (edit->defines)
994 destroy_defines (&edit->defines);
995 if (!edit->rules)
996 return;
998 edit_get_rule (edit, -1);
999 MC_PTR_FREE (edit->syntax_type);
1000 edit->syntax_type = 0;
1002 for (i = 0; edit->rules[i]; i++) {
1003 if (edit->rules[i]->keyword) {
1004 for (j = 0; edit->rules[i]->keyword[j]; j++) {
1005 MC_PTR_FREE (edit->rules[i]->keyword[j]->keyword);
1006 MC_PTR_FREE (edit->rules[i]->keyword[j]->whole_word_chars_left);
1007 MC_PTR_FREE (edit->rules[i]->keyword[j]->whole_word_chars_right);
1008 MC_PTR_FREE (edit->rules[i]->keyword[j]);
1011 MC_PTR_FREE (edit->rules[i]->left);
1012 MC_PTR_FREE (edit->rules[i]->right);
1013 MC_PTR_FREE (edit->rules[i]->whole_word_chars_left);
1014 MC_PTR_FREE (edit->rules[i]->whole_word_chars_right);
1015 MC_PTR_FREE (edit->rules[i]->keyword);
1016 MC_PTR_FREE (edit->rules[i]->keyword_first_chars);
1017 MC_PTR_FREE (edit->rules[i]);
1020 while (edit->syntax_marker) {
1021 struct _syntax_marker *s = edit->syntax_marker->next;
1022 MC_PTR_FREE (edit->syntax_marker);
1023 edit->syntax_marker = s;
1026 MC_PTR_FREE (edit->rules);
1027 tty_color_free_all_tmp();
1030 /* returns -1 on file error, line number on error in file syntax */
1031 static int
1032 edit_read_syntax_file (WEdit * edit, char ***pnames, const char *syntax_file,
1033 const char *editor_file, const char *first_line,
1034 const char *type)
1036 #define NENTRIES 30
1037 FILE *f, *g = NULL;
1038 char *args[1024], *l = 0;
1039 int line = 0;
1040 int result = 0;
1041 int count = 0;
1042 char *lib_file;
1043 int found = 0;
1044 char **tmpnames = NULL;
1046 f = fopen (syntax_file, "r");
1047 if (!f){
1048 lib_file = concat_dir_and_file (mc_home, "Syntax");
1049 f = fopen (lib_file, "r");
1050 g_free (lib_file);
1051 if (!f)
1052 return -1;
1055 args[0] = 0;
1056 for (;;) {
1057 line++;
1058 MC_PTR_FREE (l);
1059 if (!read_one_line (&l, f))
1060 break;
1061 (void)get_args (l, args, 1023); /* Final NULL */
1062 if (!args[0])
1063 continue;
1065 /* Looking for `include ...` lines before first `file ...` ones */
1066 if (!found && !strcmp (args[0], "include")) {
1067 if (g)
1068 continue;
1069 if (!args[1] || !(g = open_include_file (args[1]))) {
1070 result = line;
1071 break;
1073 goto found_type;
1076 /* looking for `file ...' lines only */
1077 if (strcmp (args[0], "file")) {
1078 continue;
1080 found = 1;
1082 /* must have two args or report error */
1083 if (!args[1] || !args[2]) {
1084 result = line;
1085 break;
1087 if (pnames && *pnames) {
1089 /* 1: just collecting a list of names of rule sets */
1090 /* Reallocate the list if required */
1091 if (count % NENTRIES == 0) {
1092 tmpnames = (char**) g_try_realloc (*pnames, (count + NENTRIES + 1) * sizeof (char*));
1093 if (tmpnames == NULL)
1094 break;
1095 *pnames = tmpnames;
1097 (*pnames)[count++] = g_strdup (args[2]);
1098 (*pnames)[count] = NULL;
1099 } else if (type) {
1101 /* 2: rule set was explicitly specified by the caller */
1102 if (!strcmp (type, args[2]))
1103 goto found_type;
1104 } else if (editor_file && edit) {
1106 /* 3: auto-detect rule set from regular expressions */
1107 int q;
1108 q = mc_search(args[1], editor_file, MC_SEARCH_T_REGEX);
1109 /* does filename match arg 1 ? */
1110 if (!q && args[3]) {
1111 /* does first line match arg 3 ? */
1112 q = mc_search(args[3], first_line, MC_SEARCH_T_REGEX);
1114 if (q) {
1115 int line_error;
1116 char *syntax_type;
1117 found_type:
1118 syntax_type = args[2];
1119 line_error = edit_read_syntax_rules (edit, g ? g : f, args, 1023);
1120 if (line_error) {
1121 if (!error_file_name) /* an included file */
1122 result = line + line_error;
1123 else
1124 result = line_error;
1125 } else {
1126 MC_PTR_FREE (edit->syntax_type);
1127 edit->syntax_type = g_strdup (syntax_type);
1128 /* if there are no rules then turn off syntax highlighting for speed */
1129 if (!g && !edit->rules[1])
1130 if (!edit->rules[0]->keyword[1] && !edit->rules[0]->spelling) {
1131 edit_free_syntax_rules (edit);
1132 break;
1135 if (g) {
1136 fclose (g);
1137 g = NULL;
1138 } else {
1139 break;
1144 MC_PTR_FREE (l);
1145 fclose (f);
1146 return result;
1149 static char *get_first_editor_line (WEdit * edit)
1151 size_t i;
1152 static char s[256];
1154 s[0] = '\0';
1155 if (edit == NULL)
1156 return s;
1158 for (i = 0; i < sizeof (s) - 1; i++) {
1159 s[i] = edit_get_byte (edit, i);
1160 if (s[i] == '\n') {
1161 s[i] = '\0';
1162 break;
1165 s[sizeof(s) - 1] = '\0';
1166 return s;
1170 * Load rules into edit struct. Either edit or *pnames must be NULL. If
1171 * edit is NULL, a list of types will be stored into names. If type is
1172 * NULL, then the type will be selected according to the filename.
1174 void
1175 edit_load_syntax (WEdit *edit, char ***pnames, const char *type)
1177 int r;
1178 char *f = NULL;
1180 if (option_auto_syntax)
1181 type = NULL;
1183 edit_free_syntax_rules (edit);
1185 if (!tty_use_colors ())
1186 return;
1188 if (!option_syntax_highlighting && (!pnames || !*pnames))
1189 return;
1191 if (edit) {
1192 if (!edit->filename)
1193 return;
1194 if (!*edit->filename && !type)
1195 return;
1197 f = concat_dir_and_file (home_dir, EDIT_SYNTAX_FILE);
1198 r = edit_read_syntax_file (edit, pnames, f, edit ? edit->filename : 0,
1199 get_first_editor_line (edit), type);
1200 if (r == -1) {
1201 edit_free_syntax_rules (edit);
1202 message (D_ERROR, _(" Load syntax file "),
1203 _(" Cannot open file %s \n %s "), f,
1204 unix_error_string (errno));
1205 } else if (r) {
1206 edit_free_syntax_rules (edit);
1207 message (D_ERROR, _(" Load syntax file "),
1208 _(" Error in file %s on line %d "),
1209 error_file_name ? error_file_name : f, r);
1210 MC_PTR_FREE (error_file_name);
1211 } else {
1212 /* succeeded */
1214 g_free (f);
1217 const char *
1218 edit_get_syntax_type (const WEdit *edit)
1220 return edit->syntax_type;