123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209 |
- /* editor syntax highlighting.
- Copyright (C) 1996, 1997, 1998, 2001, 2002, 2003, 2004, 2005, 2006,
- 2007 Free Software Foundation, Inc.
- Authors: 1998 Paul Sheer
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- 02110-1301, USA.
- */
- #include <config.h>
- #include <stdio.h>
- #include <stdarg.h>
- #include <sys/types.h>
- #include <unistd.h>
- #include <string.h>
- #include <ctype.h>
- #include <errno.h>
- #include <sys/stat.h>
- #include <stdlib.h>
- #include "../src/global.h"
- #include "edit.h"
- #include "edit-widget.h"
- #include "../src/color.h" /* use_colors */
- #include "../src/main.h" /* mc_home */
- #include "../src/wtools.h" /* message() */
- /* bytes */
- #define SYNTAX_MARKER_DENSITY 512
- /*
- Mispelled words are flushed from the syntax highlighting rules
- when they have been around longer than
- TRANSIENT_WORD_TIME_OUT seconds. At a cursor rate of 30
- chars per second and say 3 chars + a space per word, we can
- accumulate 450 words absolute max with a value of 60. This is
- below this limit of 1024 words in a context.
- */
- #define TRANSIENT_WORD_TIME_OUT 60
- #define UNKNOWN_FORMAT "unknown"
- #define MAX_WORDS_PER_CONTEXT 1024
- #define MAX_CONTEXTS 128
- #define RULE_ON_LEFT_BORDER 1
- #define RULE_ON_RIGHT_BORDER 2
- #define SYNTAX_TOKEN_STAR '\001'
- #define SYNTAX_TOKEN_PLUS '\002'
- #define SYNTAX_TOKEN_BRACKET '\003'
- #define SYNTAX_TOKEN_BRACE '\004'
- struct key_word {
- char *keyword;
- unsigned char first;
- char *whole_word_chars_left;
- char *whole_word_chars_right;
- int line_start;
- int color;
- };
- struct context_rule {
- char *left;
- unsigned char first_left;
- char *right;
- unsigned char first_right;
- char line_start_left;
- char line_start_right;
- int between_delimiters;
- char *whole_word_chars_left;
- char *whole_word_chars_right;
- char *keyword_first_chars;
- int spelling;
- /* first word is word[1] */
- struct key_word **keyword;
- };
- struct _syntax_marker {
- long offset;
- struct syntax_rule rule;
- struct _syntax_marker *next;
- };
- int option_syntax_highlighting = 1;
- int option_auto_syntax = 1;
- char *option_syntax_type = NULL;
- #define syntax_g_free(x) do {g_free(x); (x)=0;} while (0)
- static gint
- mc_defines_destroy (gpointer key, gpointer value, gpointer data)
- {
- char **values = value;
- (void) data;
- g_free (key);
- while (*values)
- g_free (*values++);
- g_free (value);
- return FALSE;
- }
- /* Completely destroys the defines tree */
- static inline void
- destroy_defines (GTree **defines)
- {
- g_tree_traverse (*defines, mc_defines_destroy, G_POST_ORDER, NULL);
- g_tree_destroy (*defines);
- *defines = 0;
- }
- static void
- subst_defines (GTree *defines, char **argv, char **argv_end)
- {
- char **t, **p;
- int argc;
- while (*argv && argv < argv_end) {
- if ((t = g_tree_lookup (defines, *argv))) {
- int count = 0;
- /* Count argv array members */
- argc = 0;
- for (p = &argv[1]; *p; p++)
- argc++;
- /* Count members of definition array */
- for (p = t; *p; p++)
- count++;
- p = &argv[count + argc];
- /* Buffer overflow or infinitive loop in define */
- if (p >= argv_end)
- break;
- /* Move rest of argv after definition members */
- while (argc >= 0)
- *p-- = argv[argc-- + 1];
- /* Copy definition members to argv */
- for (p = argv; *t; *p++ = *t++);
- }
- argv++;
- }
- }
- static long
- compare_word_to_right (WEdit *edit, long i, const char *text,
- const char *whole_left, const char *whole_right,
- int line_start)
- {
- const unsigned char *p, *q;
- int c, d, j;
- if (!*text)
- return -1;
- c = edit_get_byte (edit, i - 1);
- if (line_start)
- if (c != '\n')
- return -1;
- if (whole_left)
- if (strchr (whole_left, c))
- return -1;
- for (p = (unsigned char *) text, q = p + strlen ((char *) p); p < q; p++, i++) {
- switch (*p) {
- case SYNTAX_TOKEN_STAR:
- if (++p > q)
- return -1;
- for (;;) {
- c = edit_get_byte (edit, i);
- if (!*p)
- if (whole_right)
- if (!strchr (whole_right, c))
- break;
- if (c == *p)
- break;
- if (c == '\n')
- return -1;
- i++;
- }
- break;
- case SYNTAX_TOKEN_PLUS:
- if (++p > q)
- return -1;
- j = 0;
- for (;;) {
- c = edit_get_byte (edit, i);
- if (c == *p) {
- j = i;
- if (*p == *text && !p[1]) /* handle eg '+' and @+@ keywords properly */
- break;
- }
- if (j && strchr ((char *) p + 1, c)) /* c exists further down, so it will get matched later */
- break;
- if (c == '\n' || c == '\t' || c == ' ') {
- if (!*p) {
- i--;
- break;
- }
- if (!j)
- return -1;
- i = j;
- break;
- }
- if (whole_right)
- if (!strchr (whole_right, c)) {
- if (!*p) {
- i--;
- break;
- }
- if (!j)
- return -1;
- i = j;
- break;
- }
- i++;
- }
- break;
- case SYNTAX_TOKEN_BRACKET:
- if (++p > q)
- return -1;
- c = -1;
- for (;; i++) {
- d = c;
- c = edit_get_byte (edit, i);
- for (j = 0; p[j] != SYNTAX_TOKEN_BRACKET && p[j]; j++)
- if (c == p[j])
- goto found_char2;
- break;
- found_char2:
- ; /* dummy command */
- }
- i--;
- while (*p != SYNTAX_TOKEN_BRACKET && p <= q)
- p++;
- if (p > q)
- return -1;
- if (p[1] == d)
- i--;
- break;
- case SYNTAX_TOKEN_BRACE:
- if (++p > q)
- return -1;
- c = edit_get_byte (edit, i);
- for (; *p != SYNTAX_TOKEN_BRACE && *p; p++)
- if (c == *p)
- goto found_char3;
- return -1;
- found_char3:
- while (*p != SYNTAX_TOKEN_BRACE && p < q)
- p++;
- break;
- default:
- if (*p != edit_get_byte (edit, i))
- return -1;
- }
- }
- if (whole_right)
- if (strchr (whole_right, edit_get_byte (edit, i)))
- return -1;
- return i;
- }
- static inline const char *xx_strchr (const unsigned char *s, int c)
- {
- while (*s >= '\005' && *s != (unsigned char) c) {
- s++;
- }
- return (const char *) s;
- }
- static inline struct syntax_rule apply_rules_going_right (WEdit * edit, long i, struct syntax_rule rule)
- {
- struct context_rule *r;
- int contextchanged = 0, c;
- int found_right = 0, found_left = 0, keyword_foundleft = 0, keyword_foundright = 0;
- int is_end;
- long end = 0;
- struct syntax_rule _rule = rule;
- if (!(c = edit_get_byte (edit, i)))
- return rule;
- is_end = (rule.end == (unsigned char) i);
- /* check to turn off a keyword */
- if (_rule.keyword) {
- if (edit_get_byte (edit, i - 1) == '\n')
- _rule.keyword = 0;
- if (is_end) {
- _rule.keyword = 0;
- keyword_foundleft = 1;
- }
- }
- /* check to turn off a context */
- if (_rule.context && !_rule.keyword) {
- long e;
- r = edit->rules[_rule.context];
- 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) {
- _rule.end = e;
- found_right = 1;
- _rule.border = RULE_ON_RIGHT_BORDER;
- if (r->between_delimiters)
- _rule.context = 0;
- } else if (is_end && rule.border & RULE_ON_RIGHT_BORDER) {
- /* always turn off a context at 4 */
- found_left = 1;
- _rule.border = 0;
- if (!keyword_foundleft)
- _rule.context = 0;
- } else if (is_end && rule.border & RULE_ON_LEFT_BORDER) {
- /* never turn off a context at 2 */
- found_left = 1;
- _rule.border = 0;
- }
- }
- /* check to turn on a keyword */
- if (!_rule.keyword) {
- const char *p;
- p = (r = edit->rules[_rule.context])->keyword_first_chars;
- if (p)
- while (*(p = xx_strchr ((unsigned char *) p + 1, c))) {
- struct key_word *k;
- int count;
- long e;
- count = p - r->keyword_first_chars;
- k = r->keyword[count];
- e = compare_word_to_right (edit, i, k->keyword, k->whole_word_chars_left, k->whole_word_chars_right, k->line_start);
- if (e > 0) {
- end = e;
- _rule.end = e;
- _rule.keyword = count;
- keyword_foundright = 1;
- break;
- }
- }
- }
- /* check to turn on a context */
- if (!_rule.context) {
- if (!found_left && is_end) {
- if (rule.border & RULE_ON_RIGHT_BORDER) {
- _rule.border = 0;
- _rule.context = 0;
- contextchanged = 1;
- _rule.keyword = 0;
- } else if (rule.border & RULE_ON_LEFT_BORDER) {
- r = edit->rules[_rule._context];
- _rule.border = 0;
- if (r->between_delimiters) {
- long e;
- _rule.context = _rule._context;
- contextchanged = 1;
- _rule.keyword = 0;
- 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) {
- _rule.end = e;
- found_right = 1;
- _rule.border = RULE_ON_RIGHT_BORDER;
- _rule.context = 0;
- }
- }
- }
- }
- if (!found_right) {
- int count;
- struct context_rule **rules = edit->rules;
- for (count = 1; rules[count]; count++) {
- r = rules[count];
- if (r->first_left == c) {
- long e;
- e = compare_word_to_right (edit, i, r->left, r->whole_word_chars_left, r->whole_word_chars_right, r->line_start_left);
- if (e >= end && (!_rule.keyword || keyword_foundright)) {
- _rule.end = e;
- found_right = 1;
- _rule.border = RULE_ON_LEFT_BORDER;
- _rule._context = count;
- if (!r->between_delimiters)
- if (!_rule.keyword) {
- _rule.context = count;
- contextchanged = 1;
- }
- break;
- }
- }
- }
- }
- }
- /* check again to turn on a keyword if the context switched */
- if (contextchanged && !_rule.keyword) {
- const char *p;
- p = (r = edit->rules[_rule.context])->keyword_first_chars;
- while (*(p = xx_strchr ((unsigned char *) p + 1, c))) {
- struct key_word *k;
- int count;
- long e;
- count = p - r->keyword_first_chars;
- k = r->keyword[count];
- e = compare_word_to_right (edit, i, k->keyword, k->whole_word_chars_left, k->whole_word_chars_right, k->line_start);
- if (e > 0) {
- _rule.end = e;
- _rule.keyword = count;
- break;
- }
- }
- }
- return _rule;
- }
- static struct syntax_rule edit_get_rule (WEdit * edit, long byte_index)
- {
- long i;
- if (byte_index > edit->last_get_rule) {
- for (i = edit->last_get_rule + 1; i <= byte_index; i++) {
- edit->rule = apply_rules_going_right (edit, i, edit->rule);
- if (i > (edit->syntax_marker ? edit->syntax_marker->offset + SYNTAX_MARKER_DENSITY : SYNTAX_MARKER_DENSITY)) {
- struct _syntax_marker *s;
- s = edit->syntax_marker;
- edit->syntax_marker = g_malloc0 (sizeof (struct _syntax_marker));
- edit->syntax_marker->next = s;
- edit->syntax_marker->offset = i;
- edit->syntax_marker->rule = edit->rule;
- }
- }
- } else if (byte_index < edit->last_get_rule) {
- struct _syntax_marker *s;
- for (;;) {
- if (!edit->syntax_marker) {
- memset (&edit->rule, 0, sizeof (edit->rule));
- for (i = -1; i <= byte_index; i++)
- edit->rule = apply_rules_going_right (edit, i, edit->rule);
- break;
- }
- if (byte_index >= edit->syntax_marker->offset) {
- edit->rule = edit->syntax_marker->rule;
- for (i = edit->syntax_marker->offset + 1; i <= byte_index; i++)
- edit->rule = apply_rules_going_right (edit, i, edit->rule);
- break;
- }
- s = edit->syntax_marker->next;
- syntax_g_free (edit->syntax_marker);
- edit->syntax_marker = s;
- }
- }
- edit->last_get_rule = byte_index;
- return edit->rule;
- }
- static void translate_rule_to_color (WEdit * edit, struct syntax_rule rule, int *color)
- {
- struct key_word *k;
- k = edit->rules[rule.context]->keyword[rule.keyword];
- *color = k->color;
- }
- void edit_get_syntax_color (WEdit * edit, long byte_index, int *color)
- {
- if (edit->rules && byte_index < edit->last_byte &&
- option_syntax_highlighting && use_colors) {
- translate_rule_to_color (edit, edit_get_rule (edit, byte_index), color);
- } else {
- *color = use_colors ? EDITOR_NORMAL_COLOR_INDEX : 0;
- }
- }
- /*
- Returns 0 on error/eof or a count of the number of bytes read
- including the newline. Result must be free'd.
- In case of an error, *line will not be modified.
- */
- static int read_one_line (char **line, FILE * f)
- {
- GString *p = g_string_new ("");
- int c, r = 0;
- for (;;) {
- c = fgetc (f);
- if (c == EOF) {
- if (ferror (f)) {
- if (errno == EINTR)
- continue;
- r = 0;
- }
- break;
- }
- r++;
- /* handle all of \r\n, \r, \n correctly. */
- if (c == '\r') {
- if ( (c = fgetc (f)) == '\n')
- r++;
- else
- ungetc (c, f);
- break;
- }
- if (c == '\n')
- break;
- g_string_append_c (p, c);
- }
- if (r != 0) {
- *line = p->str;
- g_string_free (p, FALSE);
- } else {
- g_string_free (p, TRUE);
- }
- return r;
- }
- static char *convert (char *s)
- {
- char *r, *p;
- p = r = s;
- while (*s) {
- switch (*s) {
- case '\\':
- s++;
- switch (*s) {
- case ' ':
- *p = ' ';
- s--;
- break;
- case 'n':
- *p = '\n';
- break;
- case 'r':
- *p = '\r';
- break;
- case 't':
- *p = '\t';
- break;
- case 's':
- *p = ' ';
- break;
- case '*':
- *p = '*';
- break;
- case '\\':
- *p = '\\';
- break;
- case '[':
- case ']':
- *p = SYNTAX_TOKEN_BRACKET;
- break;
- case '{':
- case '}':
- *p = SYNTAX_TOKEN_BRACE;
- break;
- case 0:
- *p = *s;
- return r;
- default:
- *p = *s;
- break;
- }
- break;
- case '*':
- *p = SYNTAX_TOKEN_STAR;
- break;
- case '+':
- *p = SYNTAX_TOKEN_PLUS;
- break;
- default:
- *p = *s;
- break;
- }
- s++;
- p++;
- }
- *p = '\0';
- return r;
- }
- #define whiteness(x) ((x) == '\t' || (x) == '\n' || (x) == ' ')
- static int get_args (char *l, char **args, int args_size)
- {
- int argc = 0;
- while (argc < args_size) {
- char *p = l;
- while (*p && whiteness (*p))
- p++;
- if (!*p)
- break;
- for (l = p + 1; *l && !whiteness (*l); l++);
- if (*l)
- *l++ = '\0';
- args[argc++] = convert (p);
- }
- args[argc] = (char *) NULL;
- return argc;
- }
- #define free_args(x)
- #define break_a {result=line;break;}
- #define check_a {if(!*a){result=line;break;}}
- #define check_not_a {if(*a){result=line;break;}}
- static int
- this_try_alloc_color_pair (const char *fg, const char *bg)
- {
- char f[80], b[80], *p;
- if (bg)
- if (!*bg)
- bg = 0;
- if (fg)
- if (!*fg)
- fg = 0;
- if (fg) {
- g_strlcpy (f, fg, sizeof (f));
- p = strchr (f, '/');
- if (p)
- *p = '\0';
- fg = f;
- }
- if (bg) {
- g_strlcpy (b, bg, sizeof (b));
- p = strchr (b, '/');
- if (p)
- *p = '\0';
- bg = b;
- }
- return try_alloc_color_pair (fg, bg);
- }
- static char *error_file_name = 0;
- static FILE *open_include_file (const char *filename)
- {
- FILE *f;
- syntax_g_free (error_file_name);
- error_file_name = g_strdup (filename);
- if (*filename == PATH_SEP)
- return fopen (filename, "r");
- g_free (error_file_name);
- error_file_name = g_strconcat (home_dir, PATH_SEP_STR EDIT_DIR PATH_SEP_STR,
- filename, (char *) NULL);
- f = fopen (error_file_name, "r");
- if (f)
- return f;
- g_free (error_file_name);
- error_file_name = g_strconcat (mc_home, PATH_SEP_STR "syntax" PATH_SEP_STR,
- filename, (char *) NULL);
- return fopen (error_file_name, "r");
- }
- /* returns line number on error */
- static int
- edit_read_syntax_rules (WEdit *edit, FILE *f, char **args, int args_size)
- {
- FILE *g = 0;
- char *fg, *bg;
- char last_fg[32] = "", last_bg[32] = "";
- char whole_right[512];
- char whole_left[512];
- char *l = 0;
- int save_line = 0, line = 0;
- struct context_rule **r, *c = 0;
- int num_words = -1, num_contexts = -1;
- int result = 0;
- int argc;
- int i, j;
- int alloc_contexts = MAX_CONTEXTS,
- alloc_words_per_context = MAX_WORDS_PER_CONTEXT,
- max_alloc_words_per_context = MAX_WORDS_PER_CONTEXT;
- args[0] = 0;
- strcpy (whole_left, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_01234567890");
- strcpy (whole_right, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_01234567890");
- r = edit->rules = g_malloc0 (alloc_contexts * sizeof (struct context_rule *));
- if (!edit->defines)
- edit->defines = g_tree_new ((GCompareFunc) strcmp);
- for (;;) {
- char **a;
- line++;
- l = 0;
- if (!read_one_line (&l, f)) {
- if (g) {
- fclose (f);
- f = g;
- g = 0;
- line = save_line + 1;
- syntax_g_free (error_file_name);
- syntax_g_free (l);
- if (!read_one_line (&l, f))
- break;
- } else {
- break;
- }
- }
- argc = get_args (l, args, args_size);
- a = args + 1;
- if (!args[0]) {
- /* do nothing */
- } else if (!strcmp (args[0], "include")) {
- if (g || argc != 2) {
- result = line;
- break;
- }
- g = f;
- f = open_include_file (args[1]);
- if (!f) {
- syntax_g_free (error_file_name);
- result = line;
- break;
- }
- save_line = line;
- line = 0;
- } else if (!strcmp (args[0], "wholechars")) {
- check_a;
- if (!strcmp (*a, "left")) {
- a++;
- g_strlcpy (whole_left, *a, sizeof (whole_left));
- } else if (!strcmp (*a, "right")) {
- a++;
- g_strlcpy (whole_right, *a, sizeof (whole_right));
- } else {
- g_strlcpy (whole_left, *a, sizeof (whole_left));
- g_strlcpy (whole_right, *a, sizeof (whole_right));
- }
- a++;
- check_not_a;
- } else if (!strcmp (args[0], "context")) {
- check_a;
- if (num_contexts == -1) {
- if (strcmp (*a, "default")) { /* first context is the default */
- break_a;
- }
- a++;
- c = r[0] = g_malloc0 (sizeof (struct context_rule));
- c->left = g_strdup (" ");
- c->right = g_strdup (" ");
- num_contexts = 0;
- } else {
- /* Terminate previous context. */
- r[num_contexts - 1]->keyword[num_words] = NULL;
- c = r[num_contexts] = g_malloc0 (sizeof (struct context_rule));
- if (!strcmp (*a, "exclusive")) {
- a++;
- c->between_delimiters = 1;
- }
- check_a;
- if (!strcmp (*a, "whole")) {
- a++;
- c->whole_word_chars_left = g_strdup (whole_left);
- c->whole_word_chars_right = g_strdup (whole_right);
- } else if (!strcmp (*a, "wholeleft")) {
- a++;
- c->whole_word_chars_left = g_strdup (whole_left);
- } else if (!strcmp (*a, "wholeright")) {
- a++;
- c->whole_word_chars_right = g_strdup (whole_right);
- }
- check_a;
- if (!strcmp (*a, "linestart")) {
- a++;
- c->line_start_left = 1;
- }
- check_a;
- c->left = g_strdup (*a++);
- check_a;
- if (!strcmp (*a, "linestart")) {
- a++;
- c->line_start_right = 1;
- }
- check_a;
- c->right = g_strdup (*a++);
- c->first_left = *c->left;
- c->first_right = *c->right;
- }
- c->keyword = g_malloc (alloc_words_per_context * sizeof (struct key_word *));
- num_words = 1;
- c->keyword[0] = g_malloc0 (sizeof (struct key_word));
- subst_defines (edit->defines, a, &args[1024]);
- fg = *a;
- if (*a)
- a++;
- bg = *a;
- if (*a)
- a++;
- g_strlcpy (last_fg, fg ? fg : "", sizeof (last_fg));
- g_strlcpy (last_bg, bg ? bg : "", sizeof (last_bg));
- c->keyword[0]->color = this_try_alloc_color_pair (fg, bg);
- c->keyword[0]->keyword = g_strdup (" ");
- check_not_a;
- alloc_words_per_context = MAX_WORDS_PER_CONTEXT;
- if (++num_contexts >= alloc_contexts) {
- struct context_rule **tmp;
- alloc_contexts += 128;
- tmp = g_realloc (r, alloc_contexts * sizeof (struct context_rule *));
- r = tmp;
- }
- } else if (!strcmp (args[0], "spellcheck")) {
- if (!c) {
- result = line;
- break;
- }
- c->spelling = 1;
- } else if (!strcmp (args[0], "keyword")) {
- struct key_word *k;
- if (num_words == -1)
- break_a;
- check_a;
- k = r[num_contexts - 1]->keyword[num_words] = g_malloc0 (sizeof (struct key_word));
- if (!strcmp (*a, "whole")) {
- a++;
- k->whole_word_chars_left = g_strdup (whole_left);
- k->whole_word_chars_right = g_strdup (whole_right);
- } else if (!strcmp (*a, "wholeleft")) {
- a++;
- k->whole_word_chars_left = g_strdup (whole_left);
- } else if (!strcmp (*a, "wholeright")) {
- a++;
- k->whole_word_chars_right = g_strdup (whole_right);
- }
- check_a;
- if (!strcmp (*a, "linestart")) {
- a++;
- k->line_start = 1;
- }
- check_a;
- if (!strcmp (*a, "whole")) {
- break_a;
- }
- k->keyword = g_strdup (*a++);
- k->first = *k->keyword;
- subst_defines (edit->defines, a, &args[1024]);
- fg = *a;
- if (*a)
- a++;
- bg = *a;
- if (*a)
- a++;
- if (!fg)
- fg = last_fg;
- if (!bg)
- bg = last_bg;
- k->color = this_try_alloc_color_pair (fg, bg);
- check_not_a;
- if (++num_words >= alloc_words_per_context) {
- struct key_word **tmp;
- alloc_words_per_context += 1024;
- if (alloc_words_per_context > max_alloc_words_per_context)
- max_alloc_words_per_context = alloc_words_per_context;
- tmp = g_realloc (c->keyword, alloc_words_per_context * sizeof (struct key_word *));
- c->keyword = tmp;
- }
- } else if (*(args[0]) == '#') {
- /* do nothing for comment */
- } else if (!strcmp (args[0], "file")) {
- break;
- } else if (!strcmp (args[0], "define")) {
- char *key = *a++;
- char **argv;
- if (argc < 3)
- break_a;
- if ((argv = g_tree_lookup (edit->defines, key))) {
- mc_defines_destroy (NULL, argv, NULL);
- } else {
- key = g_strdup (key);
- }
- argv = g_new (char *, argc - 1);
- g_tree_insert (edit->defines, key, argv);
- while (*a) {
- *argv++ = g_strdup (*a++);
- };
- *argv = NULL;
- } else { /* anything else is an error */
- break_a;
- }
- free_args (args);
- syntax_g_free (l);
- }
- free_args (args);
- syntax_g_free (l);
- /* Terminate context array. */
- if (num_contexts > 0) {
- r[num_contexts - 1]->keyword[num_words] = NULL;
- r[num_contexts] = NULL;
- }
- if (!edit->rules[0])
- syntax_g_free (edit->rules);
- if (result)
- return result;
- if (num_contexts == -1) {
- return line;
- }
- {
- char *first_chars, *p;
- first_chars = g_malloc (max_alloc_words_per_context + 2);
- for (i = 0; edit->rules[i]; i++) {
- c = edit->rules[i];
- p = first_chars;
- *p++ = (char) 1;
- for (j = 1; c->keyword[j]; j++)
- *p++ = c->keyword[j]->first;
- *p = '\0';
- c->keyword_first_chars = g_strdup (first_chars);
- }
- g_free (first_chars);
- }
- return result;
- }
- void edit_free_syntax_rules (WEdit * edit)
- {
- int i, j;
- if (!edit)
- return;
- if (edit->defines)
- destroy_defines (&edit->defines);
- if (!edit->rules)
- return;
- edit_get_rule (edit, -1);
- syntax_g_free (edit->syntax_type);
- edit->syntax_type = 0;
- for (i = 0; edit->rules[i]; i++) {
- if (edit->rules[i]->keyword) {
- for (j = 0; edit->rules[i]->keyword[j]; j++) {
- syntax_g_free (edit->rules[i]->keyword[j]->keyword);
- syntax_g_free (edit->rules[i]->keyword[j]->whole_word_chars_left);
- syntax_g_free (edit->rules[i]->keyword[j]->whole_word_chars_right);
- syntax_g_free (edit->rules[i]->keyword[j]);
- }
- }
- syntax_g_free (edit->rules[i]->left);
- syntax_g_free (edit->rules[i]->right);
- syntax_g_free (edit->rules[i]->whole_word_chars_left);
- syntax_g_free (edit->rules[i]->whole_word_chars_right);
- syntax_g_free (edit->rules[i]->keyword);
- syntax_g_free (edit->rules[i]->keyword_first_chars);
- syntax_g_free (edit->rules[i]);
- }
- while (edit->syntax_marker) {
- struct _syntax_marker *s = edit->syntax_marker->next;
- syntax_g_free (edit->syntax_marker);
- edit->syntax_marker = s;
- }
- syntax_g_free (edit->rules);
- }
- /* returns -1 on file error, line number on error in file syntax */
- static int
- edit_read_syntax_file (WEdit * edit, char ***pnames, const char *syntax_file,
- const char *editor_file, const char *first_line,
- const char *type)
- {
- #define NENTRIES 30
- FILE *f, *g = NULL;
- regex_t r;
- regmatch_t pmatch[1];
- char *args[1024], *l = 0;
- int line = 0;
- int result = 0;
- int count = 0;
- char *lib_file;
- int found = 0;
- char **tmpnames = NULL;
- f = fopen (syntax_file, "r");
- if (!f){
- lib_file = concat_dir_and_file (mc_home, "syntax" PATH_SEP_STR "Syntax");
- f = fopen (lib_file, "r");
- g_free (lib_file);
- if (!f)
- return -1;
- }
- args[0] = 0;
- for (;;) {
- line++;
- syntax_g_free (l);
- if (!read_one_line (&l, f))
- break;
- (void)get_args (l, args, 1023); /* Final NULL */
- if (!args[0])
- continue;
- /* Looking for `include ...` lines before first `file ...` ones */
- if (!found && !strcmp (args[0], "include")) {
- if (g)
- continue;
- if (!args[1] || !(g = open_include_file (args[1]))) {
- result = line;
- break;
- }
- goto found_type;
- }
- /* looking for `file ...' lines only */
- if (strcmp (args[0], "file")) {
- continue;
- }
- found = 1;
- /* must have two args or report error */
- if (!args[1] || !args[2]) {
- result = line;
- break;
- }
- if (pnames && *pnames) {
- /* 1: just collecting a list of names of rule sets */
- /* Reallocate the list if required */
- if (count % NENTRIES == 0) {
- if ((tmpnames = (char**) g_realloc (*pnames, (count + NENTRIES
- + 1) * sizeof (char*))) != NULL)
- *pnames = tmpnames;
- else
- abort ();
- }
- (*pnames)[count++] = g_strdup (args[2]);
- (*pnames)[count] = NULL;
- } else if (type) {
- /* 2: rule set was explicitly specified by the caller */
- if (!strcmp (type, args[2]))
- goto found_type;
- } else if (editor_file && edit) {
- /* 3: auto-detect rule set from regular expressions */
- int q;
- if (regcomp (&r, args[1], REG_EXTENDED)) {
- result = line;
- break;
- }
- /* does filename match arg 1 ? */
- q = !regexec (&r, editor_file, 1, pmatch, 0);
- regfree (&r);
- if (!q && args[3]) {
- if (regcomp (&r, args[3], REG_EXTENDED)) {
- result = line;
- break;
- }
- /* does first line match arg 3 ? */
- q = !regexec (&r, first_line, 1, pmatch, 0);
- regfree (&r);
- }
- if (q) {
- int line_error;
- char *syntax_type;
- found_type:
- syntax_type = args[2];
- line_error = edit_read_syntax_rules (edit, g ? g : f, args, 1023);
- if (line_error) {
- if (!error_file_name) /* an included file */
- result = line + line_error;
- else
- result = line_error;
- } else {
- syntax_g_free (edit->syntax_type);
- edit->syntax_type = g_strdup (syntax_type);
- /* if there are no rules then turn off syntax highlighting for speed */
- if (!g && !edit->rules[1])
- if (!edit->rules[0]->keyword[1] && !edit->rules[0]->spelling) {
- edit_free_syntax_rules (edit);
- break;
- }
- }
- if (g) {
- fclose (g);
- g = NULL;
- } else {
- break;
- }
- }
- }
- }
- syntax_g_free (l);
- fclose (f);
- return result;
- }
- static char *get_first_editor_line (WEdit * edit)
- {
- int i;
- static char s[256];
- s[0] = '\0';
- if (!edit)
- return s;
- for (i = 0; i < 255; i++) {
- s[i] = edit_get_byte (edit, i);
- if (s[i] == '\n') {
- s[i] = '\0';
- break;
- }
- }
- s[255] = '\0';
- return s;
- }
- /*
- * Load rules into edit struct. Either edit or *pnames must be NULL. If
- * edit is NULL, a list of types will be stored into names. If type is
- * NULL, then the type will be selected according to the filename.
- */
- void
- edit_load_syntax (WEdit *edit, char ***pnames, const char *type)
- {
- int r;
- char *f = NULL;
- if (option_auto_syntax)
- type = NULL;
- edit_free_syntax_rules (edit);
- if (!use_colors)
- return;
- if (!option_syntax_highlighting && (!pnames || !*pnames))
- return;
- if (edit) {
- if (!edit->filename)
- return;
- if (!*edit->filename && !type)
- return;
- }
- f = concat_dir_and_file (home_dir, SYNTAX_FILE);
- r = edit_read_syntax_file (edit, pnames, f, edit ? edit->filename : 0,
- get_first_editor_line (edit), type);
- if (r == -1) {
- edit_free_syntax_rules (edit);
- message (D_ERROR, _(" Load syntax file "),
- _(" Cannot open file %s \n %s "), f,
- unix_error_string (errno));
- } else if (r) {
- edit_free_syntax_rules (edit);
- message (D_ERROR, _(" Load syntax file "),
- _(" Error in file %s on line %d "),
- error_file_name ? error_file_name : f, r);
- syntax_g_free (error_file_name);
- } else {
- /* succeeded */
- }
- g_free (f);
- }
|