mc/gtkedit/syntax.c
Paul Sheer f7bc648b90 mcedit updates to reflect cooledit-3.11.6
a large number of minor changes have been made
	those really interested can check the cooledit
	changelogs.
	in particular, on the fly spell checking has been
	added. this is not yet enabled for use, but the code
	is there.
	syntax highlighting optimisations have been made
	and various syntax rules added for some new
	languages.
	comprehensive bookmarking support has been added, but
	this requires keybindings still.
1999-07-21 20:03:23 +00:00

1523 lines
39 KiB
C
Raw Blame History

/* editor syntax highlighting.
Copyright (C) 1996, 1997, 1998 the Free Software Foundation
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., 59 Temple Place, Suite 330, Boston, MA
02111-1307, USA.
*/
#include <config.h>
#if defined(MIDNIGHT) || defined(GTK)
#include "edit.h"
#else
#include "coolwidget.h"
#endif
#if defined (HAVE_MAD) && ! defined (MIDNIGHT) && ! defined (GTK)
#include "mad.h"
#endif
/* 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"
#if !defined(MIDNIGHT) || defined(HAVE_SYNTAXH)
int option_syntax_highlighting = 1;
int option_auto_spellcheck = 1;
/* these three functions are called from the outside */
void edit_load_syntax (WEdit * edit, char **names, char *type);
void edit_free_syntax_rules (WEdit * edit);
void edit_get_syntax_color (WEdit * edit, long byte_index, int *fg, int *bg);
static void *syntax_malloc (size_t x)
{
void *p;
p = malloc (x);
memset (p, 0, x);
return p;
}
#define syntax_free(x) {if(x){free(x);(x)=0;}}
static long compare_word_to_right (WEdit * edit, long i, char *text, char *whole_left, char *whole_right, int line_start)
{
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); (unsigned long) p < (unsigned long) q; p++, i++) {
switch (*p) {
case '\001':
p++;
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 '\002':
p++;
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 '\003':
p++;
c = -1;
for (;; i++) {
d = c;
c = edit_get_byte (edit, i);
for (j = 0; p[j] != '\003'; j++)
if (c == p[j])
goto found_char2;
break;
found_char2:
j = c; /* dummy command */
}
i--;
while (*p != '\003')
p++;
if (p[1] == d)
i--;
break;
case '\004':
p++;
c = edit_get_byte (edit, i);
for (; *p != '\004'; p++)
if (c == *p)
goto found_char3;
return -1;
found_char3:
for (; *p != '\004'; 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;
}
#define XXX \
if (*s < '\005' || *s == (unsigned char) c) \
goto done; \
s++;
static inline char *xx_strchr (const unsigned char *s, int c)
{
repeat:
XXX XXX XXX XXX XXX XXX XXX XXX;
XXX XXX XXX XXX XXX XXX XXX XXX;
goto repeat;
done:
return (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) {
struct key_word *k;
k = edit->rules[_rule.context]->keyword[_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) {
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 = (unsigned long) p - (unsigned long) 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;
break;
}
}
}
}
}
/* check again to turn on a keyword if the context switched */
if (contextchanged && !_rule.keyword) {
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 = (unsigned long) p - (unsigned long) 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 = malloc (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_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 *fg, int *bg)
{
struct key_word *k;
k = edit->rules[rule.context]->keyword[rule.keyword];
*bg = k->bg;
*fg = k->fg;
}
void edit_get_syntax_color (WEdit * edit, long byte_index, int *fg, int *bg)
{
if (edit->rules && byte_index < edit->last_byte && option_syntax_highlighting) {
translate_rule_to_color (edit, edit_get_rule (edit, byte_index), fg, bg);
} else {
#ifdef MIDNIGHT
*fg = EDITOR_NORMAL_COLOR;
#else
*fg = NO_COLOR;
*bg = NO_COLOR;
#endif
}
}
/*
Returns 0 on error/eof or a count of the number of bytes read
including the newline. Result must be free'd.
*/
static int read_one_line (char **line, FILE * f)
{
char *p;
int len = 256, c, r = 0, i = 0;
p = syntax_malloc (len);
for (;;) {
c = fgetc (f);
if (c == -1) {
if (errno == EINTR)
continue;
r = 0;
break;
} else if (c == '\n') {
r = i + 1; /* extra 1 for the newline just read */
break;
} else {
if (i >= len - 1) {
char *q;
q = syntax_malloc (len * 2);
memcpy (q, p, len);
syntax_free (p);
p = q;
len *= 2;
}
p[i++] = c;
}
}
p[i] = 0;
*line = p;
return r;
}
static char *strdup_convert (char *s)
{
char *r, *p;
p = r = (char *) strdup (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 = '\003';
break;
case '{':
case '}':
*p = '\004';
break;
default:
*p = *s;
break;
}
break;
case '*':
*p = '\001';
break;
case '+':
*p = '\002';
break;
default:
*p = *s;
break;
}
s++;
p++;
}
*p = '\0';
return r;
}
#define whiteness(x) ((x) == '\t' || (x) == '\n' || (x) == ' ')
static void get_args (char *l, char **args, int *argc)
{
*argc = 0;
l--;
for (;;) {
char *p;
for (p = l + 1; *p && whiteness (*p); p++);
if (!*p)
break;
for (l = p + 1; *l && !whiteness (*l); l++);
*l = '\0';
*args = strdup_convert (p);
(*argc)++;
args++;
}
*args = 0;
}
static void free_args (char **args)
{
while (*args) {
syntax_free (*args);
*args = 0;
args++;
}
}
#define check_a {if(!*a){result=line;break;}}
#define check_not_a {if(*a){result=line;break;}}
#ifdef MIDNIGHT
int try_alloc_color_pair (char *fg, char *bg);
int this_try_alloc_color_pair (char *fg, char *bg)
{
char f[80], b[80], *p;
if (bg)
if (!*bg)
bg = 0;
if (fg)
if (!*fg)
fg = 0;
if (fg) {
strcpy (f, fg);
p = strchr (f, '/');
if (p)
*p = '\0';
fg = f;
}
if (bg) {
strcpy (b, bg);
p = strchr (b, '/');
if (p)
*p = '\0';
bg = b;
}
return try_alloc_color_pair (fg, bg);
}
#else
#ifdef GTK
int allocate_color (WEdit *edit, gchar *color);
int this_allocate_color (WEdit *edit, char *fg)
{
char *p;
if (fg)
if (!*fg)
fg = 0;
if (!fg)
return allocate_color (edit, 0);
p = strchr (fg, '/');
if (!p)
return allocate_color (edit, fg);
return allocate_color (edit, p + 1);
}
#else
int this_allocate_color (WEdit *edit, char *fg)
{
char *p;
if (fg)
if (!*fg)
fg = 0;
if (!fg)
return allocate_color (0);
p = strchr (fg, '/');
if (!p)
return allocate_color (fg);
return allocate_color (p + 1);
}
#endif
#endif
static char *error_file_name = 0;
static FILE *open_include_file (char *filename)
{
FILE *f;
char p[MAX_PATH_LEN];
syntax_free (error_file_name);
error_file_name = (char *) strdup (filename);
if (*filename == '/')
return fopen (filename, "r");
strcpy (p, home_dir);
strcat (p, EDIT_DIR "/");
strcat (p, filename);
syntax_free (error_file_name);
error_file_name = (char *) strdup (p);
f = fopen (p, "r");
if (f)
return f;
strcpy (p, LIBDIR "/syntax/");
strcat (p, filename);
syntax_free (error_file_name);
error_file_name = (char *) strdup (p);
return fopen (p, "r");
}
/* returns line number on error */
static int edit_read_syntax_rules (WEdit * edit, FILE * f)
{
FILE *g = 0;
char *fg, *bg;
char last_fg[32] = "", last_bg[32] = "";
char whole_right[512];
char whole_left[512];
char *args[1024], *l = 0;
int save_line = 0, line = 0;
struct context_rule **r, *c = 0;
int num_words = -1, num_contexts = -1;
int argc, result = 0;
int i, j;
args[0] = 0;
strcpy (whole_left, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_01234567890");
strcpy (whole_right, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_01234567890");
r = edit->rules = syntax_malloc (MAX_CONTEXTS * sizeof (struct context_rule *));
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_free (error_file_name);
if (l)
free (l);
if (!read_one_line (&l, f))
break;
} else {
break;
}
}
get_args (l, args, &argc);
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_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++;
strcpy (whole_left, *a);
} else if (!strcmp (*a, "right")) {
a++;
strcpy (whole_right, *a);
} else {
strcpy (whole_left, *a);
strcpy (whole_right, *a);
}
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 */
*a = 0;
check_a;
}
a++;
c = r[0] = syntax_malloc (sizeof (struct context_rule));
c->left = (char *) strdup (" ");
c->right = (char *) strdup (" ");
num_contexts = 0;
} else {
c = r[num_contexts] = syntax_malloc (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 = (char *) strdup (whole_left);
c->whole_word_chars_right = (char *) strdup (whole_right);
} else if (!strcmp (*a, "wholeleft")) {
a++;
c->whole_word_chars_left = (char *) strdup (whole_left);
} else if (!strcmp (*a, "wholeright")) {
a++;
c->whole_word_chars_right = (char *) strdup (whole_right);
}
check_a;
if (!strcmp (*a, "linestart")) {
a++;
c->line_start_left = 1;
}
check_a;
c->left = (char *) strdup (*a++);
check_a;
if (!strcmp (*a, "linestart")) {
a++;
c->line_start_right = 1;
}
check_a;
c->right = (char *) strdup (*a++);
c->first_left = *c->left;
c->first_right = *c->right;
c->single_char = (strlen (c->right) == 1);
}
c->keyword = syntax_malloc (MAX_WORDS_PER_CONTEXT * sizeof (struct key_word *));
#if 0
c->max_words = MAX_WORDS_PER_CONTEXT;
#endif
num_words = 1;
c->keyword[0] = syntax_malloc (sizeof (struct key_word));
fg = *a;
if (*a)
a++;
bg = *a;
if (*a)
a++;
strcpy (last_fg, fg ? fg : "");
strcpy (last_bg, bg ? bg : "");
#ifdef MIDNIGHT
c->keyword[0]->fg = this_try_alloc_color_pair (fg, bg);
#else
c->keyword[0]->fg = this_allocate_color (edit, fg);
c->keyword[0]->bg = this_allocate_color (edit, bg);
#endif
c->keyword[0]->keyword = (char *) strdup (" ");
check_not_a;
num_contexts++;
} 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)
*a = 0;
check_a;
k = r[num_contexts - 1]->keyword[num_words] = syntax_malloc (sizeof (struct key_word));
if (!strcmp (*a, "whole")) {
a++;
k->whole_word_chars_left = (char *) strdup (whole_left);
k->whole_word_chars_right = (char *) strdup (whole_right);
} else if (!strcmp (*a, "wholeleft")) {
a++;
k->whole_word_chars_left = (char *) strdup (whole_left);
} else if (!strcmp (*a, "wholeright")) {
a++;
k->whole_word_chars_right = (char *) strdup (whole_right);
}
check_a;
if (!strcmp (*a, "linestart")) {
a++;
k->line_start = 1;
}
check_a;
if (!strcmp (*a, "whole")) {
*a = 0;
check_a;
}
k->keyword = (char *) strdup (*a++);
k->first = *k->keyword;
fg = *a;
if (*a)
a++;
bg = *a;
if (*a)
a++;
if (!fg)
fg = last_fg;
if (!bg)
bg = last_bg;
#ifdef MIDNIGHT
k->fg = this_try_alloc_color_pair (fg, bg);
#else
k->fg = this_allocate_color (edit, fg);
k->bg = this_allocate_color (edit, bg);
#endif
check_not_a;
num_words++;
} else if (!strncmp (args[0], "#", 1)) {
/* do nothing for comment */
} else if (!strcmp (args[0], "file")) {
break;
} else { /* anything else is an error */
*a = 0;
check_a;
}
free_args (args);
syntax_free (l);
}
free_args (args);
syntax_free (l);
if (!edit->rules[0])
syntax_free (edit->rules);
if (result)
return result;
if (num_contexts == -1) {
result = line;
return result;
}
{
char first_chars[MAX_WORDS_PER_CONTEXT + 2], *p;
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 = malloc (strlen (first_chars) + 2);
strcpy (c->keyword_first_chars, first_chars);
}
}
return result;
}
#if !defined (GTK) && !defined (MIDNIGHT)
/* strdup and append c */
static char *strdupc (char *s, int c)
{
char *t;
int l;
strcpy (t = malloc ((l = strlen (s)) + 3), s);
t[l] = c;
t[l + 1] = '\0';
return t;
}
static void edit_syntax_clear_keyword (WEdit * edit, int context, int j)
{
struct context_rule *c;
struct _syntax_marker *s;
c = edit->rules[context];
/* first we clear any instances of this keyword in our cache chain (we used to just clear the cache chain, but this slows things down) */
for (s = edit->syntax_marker; s; s = s->next)
if (s->rule.keyword == j)
s->rule.keyword = 0;
else if (s->rule.keyword > j)
s->rule.keyword--;
free (c->keyword[j]->keyword);
free (c->keyword[j]->whole_word_chars_left);
free (c->keyword[j]->whole_word_chars_right);
free (c->keyword[j]);
memcpy (&c->keyword[j], &c->keyword[j + 1], (MAX_WORDS_PER_CONTEXT - j) * sizeof (struct keyword *));
strcpy (&c->keyword_first_chars[j], &c->keyword_first_chars[j + 1]);
}
FILE *spelling_pipe_in = 0;
FILE *spelling_pipe_out = 0;
pid_t ispell_pid = 0;
/* adds a keyword for underlining into the keyword list for this context, returns 1 if too many words */
static int edit_syntax_add_keyword (WEdit * edit, char *keyword, int context, time_t t)
{
int j;
char *s;
struct context_rule *c;
c = edit->rules[context];
for (j = 1; c->keyword[j]; j++) {
/* if a keyword has been around for more than TRANSIENT_WORD_TIME_OUT
seconds, then remove it - we don't want to run out of space or makes syntax highlighting to slow */
if (c->keyword[j]->time) {
if (c->keyword[j]->time + TRANSIENT_WORD_TIME_OUT < t) {
edit->force |= REDRAW_PAGE;
edit_syntax_clear_keyword (edit, context, j);
j--;
}
}
}
/* are we out of space? */
if (j >= MAX_WORDS_PER_CONTEXT - 1)
return 1;
/* add the new keyword and date it */
c->keyword[j + 1] = 0;
c->keyword[j] = syntax_malloc (sizeof (struct key_word));
#ifdef MIDNIGHT
c->keyword[j]->fg = SPELLING_ERROR;
#else
c->keyword[j]->fg = c->keyword[0]->fg;
c->keyword[j]->bg = SPELLING_ERROR;
#endif
c->keyword[j]->keyword = (char *) strdup (keyword);
c->keyword[j]->first = *c->keyword[j]->keyword;
c->keyword[j]->whole_word_chars_left = (char *) strdup ("-'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ<59><5A><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>");
c->keyword[j]->whole_word_chars_right = (char *) strdup ("-'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ<59><5A><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>");
c->keyword[j]->time = t;
s = strdupc (c->keyword_first_chars, c->keyword[j]->first);
free (c->keyword_first_chars);
c->keyword_first_chars = s;
return 0;
}
/* checks spelling of the word at offset */
static int edit_check_spelling_at (WEdit * edit, long byte_index)
{
int context;
long p1, p2;
unsigned char *p, *q;
int r, c1, c2, j;
int ch;
time_t t;
struct context_rule *c;
/* sanity check */
if (!edit->rules || byte_index > edit->last_byte)
return 0;
/* in what context are we */
context = edit_get_rule (edit, byte_index).context;
c = edit->rules[context];
/* does this context have `spellcheck' */
if (!edit->rules[context]->spelling)
return 0;
/* find word start */
for (p1 = byte_index - 1;; p1--) {
ch = edit_get_byte (edit, p1);
if (isalpha (ch) || ch == '-' || ch == '\'')
continue;
break;
}
p1++;
/* find word end */
for (p2 = byte_index;; p2++) {
ch = edit_get_byte (edit, p2);
if (isalpha (ch) || ch == '-' || ch == '\'')
continue;
break;
}
if (p2 <= p1)
return 0;
/* create string */
q = p = malloc (p2 - p1 + 2);
for (; p1 < p2; p1++)
*p++ = edit_get_byte (edit, p1);
*p = '\0';
if (q[0] == '-' || strlen ((char *) q) > 40) { /* if you are using words over 40 characters, you are on your own */
free (q);
return 0;
}
time (&t);
for (j = 1; c->keyword[j]; j++) {
/* if the keyword is present, then update its time only. if it is a fixed keyword from the rules file, then just return */
if (!strcmp (c->keyword[j]->keyword, (char *) q)) {
if (c->keyword[j]->time)
c->keyword[j]->time = t;
free (q);
return 0;
}
}
/* feed it to ispell */
fprintf (spelling_pipe_out, "%s\n", (char *) q);
fflush (spelling_pipe_out);
/* what does ispell say? */
do {
r = fgetc (spelling_pipe_in);
} while (r == -1 && errno == EINTR);
if (r == -1) {
free (q);
return 1;
}
if (r == '\n') { /* ispell sometimes returns just blank line if it is given bad characters */
free (q);
return 0;
}
/* now read ispell output untill we get two blanks lines - we are not intersted in this part */
do {
c1 = fgetc (spelling_pipe_in);
} while (c1 == -1 && errno == EINTR);
for (;;) {
if (c1 == -1) {
free (q);
return 1;
}
do {
c2 = fgetc (spelling_pipe_in);
} while (c2 == -1 && errno == EINTR);
if (c1 == '\n' && c2 == '\n')
break;
c1 = c2;
}
/* spelled ok */
if (r == '*' || r == '+' || r == '-') {
free (q);
return 0;
}
/* not spelled ok - so add a syntax keyword for this word */
edit_syntax_add_keyword (edit, (char *) q, context, t);
free (q);
return 0;
}
char *option_alternate_dictionary = "";
int edit_check_spelling (WEdit * edit)
{
if (!option_auto_spellcheck)
return 0;
/* magic arg to close up shop */
if (!edit) {
option_auto_spellcheck = 0;
goto close_spelling;
}
/* do we at least have a syntax rule struct to put new wrongly spelled keyword in for highlighting? */
if (!edit->rules && !edit->explicit_syntax)
edit_load_syntax (edit, 0, UNKNOWN_FORMAT);
if (!edit->rules) {
option_auto_spellcheck = 0;
return 0;
}
/* is ispell running? */
if (!spelling_pipe_in) {
int in, out, a = 0;
char *arg[10];
arg[a++] = "ispell";
arg[a++] = "-a";
if (option_alternate_dictionary)
if (*option_alternate_dictionary) {
arg[a++] = "-d";
arg[a++] = option_alternate_dictionary;
}
arg[a++] = "-a";
arg[a++] = 0;
/* start ispell process */
ispell_pid = triple_pipe_open (&in, &out, 0, 1, arg[0], arg);
if (ispell_pid < 1) {
option_auto_spellcheck = 0;
#if 0
CErrorDialog (0, 0, 0, _ (" Spelling Message "), "%s", _ (" Fail trying to open ispell program. \n Check that it is in your path and works with the -a option. \n Alternatively, disable spell checking from the Options menu. "));
#endif
return 1;
}
/* prepare pipes */
spelling_pipe_in = (FILE *) fdopen (out, "r");
spelling_pipe_out = (FILE *) fdopen (in, "w");
if (!spelling_pipe_in || !spelling_pipe_out) {
option_auto_spellcheck = 0;
CErrorDialog (0, 0, 0, _ (" Spelling Message "), "%s", _ (" Fail trying to open ispell pipes. \n Check that it is in your path and works with the -a option. \n Alternatively, disable spell checking from the Options menu. "));
return 1;
}
/* read the banner message */
for (;;) {
int c1;
c1 = fgetc (spelling_pipe_in);
if (c1 == -1 && errno != EINTR) {
option_auto_spellcheck = 0;
CErrorDialog (0, 0, 0, _ (" Spelling Message "), "%s", _ (" Fail trying to read ispell pipes. \n Check that it is in your path and works with the -a option. \n Alternatively, disable spell checking from the Options menu. "));
return 1;
}
if (c1 == '\n')
break;
}
}
/* spellcheck the word under the cursor */
if (edit_check_spelling_at (edit, edit->curs1)) {
CMessageDialog (0, 0, 0, 0, _ (" Spelling Message "), "%s", _ (" Error reading from ispell. \n Ispell is being restarted. "));
close_spelling:
fclose (spelling_pipe_in);
spelling_pipe_in = 0;
fclose (spelling_pipe_out);
spelling_pipe_out = 0;
kill (ispell_pid, SIGKILL);
}
return 0;
}
#else /* ! GTK && ! MIDNIGHT*/
int edit_check_spelling (WEdit * edit)
{
return 0;
}
#endif
void (*syntax_change_callback) (CWidget *) = 0;
void edit_set_syntax_change_callback (void (*callback) (CWidget *))
{
syntax_change_callback = callback;
}
void edit_free_syntax_rules (WEdit * edit)
{
int i, j;
if (!edit)
return;
if (!edit->rules)
return;
edit_get_rule (edit, -1);
syntax_free (edit->syntax_type);
edit->syntax_type = 0;
if (syntax_change_callback)
#ifdef MIDNIGHT
(*syntax_change_callback) (&edit->widget);
#else
(*syntax_change_callback) (edit->widget);
#endif
for (i = 0; edit->rules[i]; i++) {
if (edit->rules[i]->keyword) {
for (j = 0; edit->rules[i]->keyword[j]; j++) {
syntax_free (edit->rules[i]->keyword[j]->keyword);
syntax_free (edit->rules[i]->keyword[j]->whole_word_chars_left);
syntax_free (edit->rules[i]->keyword[j]->whole_word_chars_right);
syntax_free (edit->rules[i]->keyword[j]);
}
}
syntax_free (edit->rules[i]->left);
syntax_free (edit->rules[i]->right);
syntax_free (edit->rules[i]->whole_word_chars_left);
syntax_free (edit->rules[i]->whole_word_chars_right);
syntax_free (edit->rules[i]->keyword);
syntax_free (edit->rules[i]->keyword_first_chars);
syntax_free (edit->rules[i]);
}
for (;;) {
struct _syntax_marker *s;
if (!edit->syntax_marker)
break;
s = edit->syntax_marker->next;
syntax_free (edit->syntax_marker);
edit->syntax_marker = s;
}
syntax_free (edit->rules);
}
#define CURRENT_SYNTAX_RULES_VERSION "61"
char *syntax_text[] = {
"# syntax rules version " CURRENT_SYNTAX_RULES_VERSION,
"# (after the slash is a Cooledit color, 0-26 or any of the X colors in rgb.txt)",
"# black",
"# red",
"# green",
"# brown",
"# blue",
"# magenta",
"# cyan",
"# lightgray",
"# gray",
"# brightred",
"# brightgreen",
"# yellow",
"# brightblue",
"# brightmagenta",
"# brightcyan",
"# white",
"",
"file gobledy_gook #\\sHelp\\ssupport\\sother\\sfile\\stypes",
"context default",
"file gobledy_gook #\\sby\\scoding\\srules\\sin\\s~/.cedit/syntax.",
"context default",
"file gobledy_gook #\\sSee\\sman/syntax\\sin\\sthe\\ssource\\sdistribution",
"context default",
"file gobledy_gook #\\sand\\sconsult\\sthe\\sman\\spage.",
"context default",
"",
"",
"file ..\\*\\\\.diff$ Unified\\sDiff\\sOutput ^diff.\\*(-u|--unified)",
"include diff.syntax",
"",
"file ..\\*\\\\.diff$ Context\\sDiff\\sOutput ^diff.\\*(-c|--context)",
"include diffc.syntax",
"",
"file ..\\*\\\\.lsm$ LSM\\sFile",
"include lsm.syntax",
"",
"file ..\\*\\\\.sh$ Shell\\sScript ^#!\\s\\*/.\\*/(ksh|bash|sh|pdkzsh)",
"include sh.syntax",
"",
"file ..\\*\\\\.(pl|PL])$ Perl\\sProgram ^#!\\s\\*/.\\*/perl",
"include perl.syntax",
"",
"file ..\\*\\\\.(py|PY])$ Python\\sProgram ^#!\\s\\*/.\\*/python",
"include python.syntax",
"",
"file ..\\*\\\\.(man|[0-9n]|[0-9]x)$ NROFF\\sSource",
"include nroff.syntax",
"",
"file ..\\*\\\\.(htm|html|HTM|HTML)$ HTML\\sFile",
"include html.syntax",
"",
"file ..\\*\\\\.(pp|PP|pas|PAS)$ Pascal\\sProgram",
"include pascal.syntax",
"",
"file ..\\*\\\\.(ada|adb|ADA|ADB)$ Ada\\sProgram",
"include ada95.syntax",
"",
"file ..\\*\\\\.tex$ LaTeX\\s2.09\\sDocument",
"include latex.syntax",
"",
"file ..\\*\\.(texi|texinfo|TEXI|TEXINFO)$ Texinfo\\sDocument",
"include texinfo.syntax",
"",
"file ..\\*\\\\.([chC]|CC|cxx|cc|cpp|CPP|CXX)$ C/C\\+\\+\\sProgram",
"include c.syntax",
"",
"file ..\\*\\\\.i$ SWIG\\sSource",
"include swig.syntax",
"",
"file ..\\*\\\\.(java|JAVA|Java|jav)$ Java\\sProgram",
"include java.syntax",
"",
"file ..\\*\\\\.(st)$ SmallTalk\\sProgram",
"include smalltalk.syntax",
"",
"file ..\\*\\\\.(ml|mli|mly|mll|mlp)$ ML\\sProgram",
"include ml.syntax",
"",
"file .\\*ChangeLog$ GNU\\sDistribution\\sChangeLog\\sFile",
"include changelog.syntax",
"",
"file .\\*Makefile[\\\\\\.a-z]\\*$ Makefile",
"include makefile.syntax",
"",
"file Don_t_match_me Mail\\sfolder ^From\\s",
"include mail.syntax",
"",
"file .\\*syntax$ Syntax\\sHighlighting\\sdefinitions",
"",
"context default",
" keyword whole spellch\\eck yellow/24",
" keyword whole keyw\\ord yellow/24",
" keyword whole whole\\[\\t\\s\\]l\\inestart brightcyan/17",
" keyword whole whole\\[\\t\\s\\]l\\inestart brightcyan/17",
" keyword whole wh\\oleleft\\[\\t\\s\\]l\\inestart brightcyan/17",
" keyword whole wh\\oleright\\[\\t\\s\\]l\\inestart brightcyan/17",
" keyword whole l\\inestart\\[\\t\\s\\]wh\\ole",
" keyword whole l\\inestart\\[\\t\\s\\]wh\\ole",
" keyword whole l\\inestart\\[\\t\\s\\]wh\\oleleft",
" keyword whole l\\inestart\\[\\t\\s\\]wh\\oleright",
" keyword wholeleft whole\\s brightcyan/17",
" keyword wholeleft whole\\t brightcyan/17",
" keyword whole wh\\oleleft brightcyan/17",
" keyword whole wh\\oleright brightcyan/17",
" keyword whole lin\\[e\\]start brightcyan/17",
" keyword whole c\\ontext\\[\\t\\s\\]exclusive brightred/18",
" keyword whole c\\ontext\\[\\t\\s\\]default brightred/18",
" keyword whole c\\ontext brightred/18",
" keyword whole wh\\olechars\\[\\t\\s\\]left brightcyan/17",
" keyword whole wh\\olechars\\[\\t\\s\\]right brightcyan/17",
" keyword whole wh\\olechars brightcyan/17",
" keyword whole f\\ile brightgreen/6",
" keyword whole in\\clude brightred/18",
"",
" keyword whole 0 lightgray/0 blue/26",
" keyword whole 1 lightgray/1 blue/26",
" keyword whole 2 lightgray/2 blue/26",
" keyword whole 3 lightgray/3 blue/26",
" keyword whole 4 lightgray/4 blue/26",
" keyword whole 5 lightgray/5 blue/26",
" keyword whole 6 lightgray/6",
" keyword whole 7 lightgray/7",
" keyword whole 8 lightgray/8",
" keyword whole 9 lightgray/9",
" keyword whole 10 lightgray/10",
" keyword whole 11 lightgray/11",
" keyword whole 12 lightgray/12",
" keyword whole 13 lightgray/13",
" keyword whole 14 lightgray/14",
" keyword whole 15 lightgray/15",
" keyword whole 16 lightgray/16",
" keyword whole 17 lightgray/17",
" keyword whole 18 lightgray/18",
" keyword whole 19 lightgray/19",
" keyword whole 20 lightgray/20",
" keyword whole 21 lightgray/21",
" keyword whole 22 lightgray/22",
" keyword whole 23 lightgray/23",
" keyword whole 24 lightgray/24",
" keyword whole 25 lightgray/25",
" keyword whole 26 lightgray/26",
"",
" keyword wholeleft black\\/ black/0",
" keyword wholeleft red\\/ red/DarkRed",
" keyword wholeleft green\\/ green/green3",
" keyword wholeleft brown\\/ brown/saddlebrown",
" keyword wholeleft blue\\/ blue/blue3",
" keyword wholeleft magenta\\/ magenta/magenta3",
" keyword wholeleft cyan\\/ cyan/cyan3",
" keyword wholeleft lightgray\\/ lightgray/lightgray",
" keyword wholeleft gray\\/ gray/gray",
" keyword wholeleft brightred\\/ brightred/red",
" keyword wholeleft brightgreen\\/ brightgreen/green1",
" keyword wholeleft yellow\\/ yellow/yellow",
" keyword wholeleft brightblue\\/ brightblue/blue1",
" keyword wholeleft brightmagenta\\/ brightmagenta/magenta",
" keyword wholeleft brightcyan\\/ brightcyan/cyan1",
" keyword wholeleft white\\/ white/26",
"",
"context linestart # \\n brown/22",
"",
"file .\\* " UNKNOWN_FORMAT,
"include unknown.syntax",
"",
0};
FILE *upgrade_syntax_file (char *syntax_file)
{
FILE *f;
char *p;
char line[80];
f = fopen (syntax_file, "r");
if (!f) {
char **syntax_line;
f = fopen (syntax_file, "w");
if (!f)
return 0;
for (syntax_line = syntax_text; *syntax_line; syntax_line++)
fprintf (f, "%s\n", *syntax_line);
fclose (f);
return fopen (syntax_file, "r");
}
memset (line, 0, 79);
fread (line, 80, 1, f);
if (!strstr (line, "syntax rules version"))
goto rename_rule_file;
p = strstr (line, "version") + strlen ("version") + 1;
if (atoi (p) < atoi (CURRENT_SYNTAX_RULES_VERSION)) {
char s[1024];
rename_rule_file:
strcpy (s, syntax_file);
strcat (s, ".OLD");
unlink (s);
rename (syntax_file, s);
unlink (syntax_file); /* might rename() fail ? */
#if defined(MIDNIGHT) || defined(GTK)
edit_message_dialog (" Load Syntax Rules ", " Your syntax rule file is outdated \n A new rule file is being installed. \n Your old rule file has been saved with a .OLD extension. ");
#else
CMessageDialog (0, 20, 20, 0, " Load Syntax Rules ", " Your syntax rule file is outdated \n A new rule file is being installed. \n Your old rule file has been saved with a .OLD extension. ");
#endif
return upgrade_syntax_file (syntax_file);
}
rewind (f);
return f;
}
/* returns -1 on file error, line number on error in file syntax */
static int edit_read_syntax_file (WEdit * edit, char **names, char *syntax_file, char *editor_file, char *first_line, char *type)
{
FILE *f;
regex_t r, r2;
regmatch_t pmatch[1];
char *args[1024], *l;
int line = 0;
int argc;
int result = 0;
int count = 0;
f = upgrade_syntax_file (syntax_file);
if (!f)
return -1;
args[0] = 0;
for (;;) {
line++;
if (!read_one_line (&l, f))
break;
get_args (l, args, &argc);
if (!args[0]) {
} else if (!strcmp (args[0], "file")) {
if (!args[1] || !args[2]) {
result = line;
break;
}
if (regcomp (&r, args[1], REG_EXTENDED)) {
result = line;
break;
}
if (regcomp (&r2, args[3] ? args[3] : "^nEvEr MaTcH aNyThInG$", REG_EXTENDED)) {
result = line;
break;
}
if (names) {
names[count++] = (char *) strdup (args[2]);
names[count] = 0;
} else if (type) {
if (!strcmp (type, args[2]))
goto found_type;
} else if (editor_file && edit) {
if (!regexec (&r, editor_file, 1, pmatch, 0) || !regexec (&r2, first_line, 1, pmatch, 0)) {
int line_error;
found_type:
line_error = edit_read_syntax_rules (edit, f);
if (line_error) {
if (!error_file_name) /* an included file */
result = line + line_error;
else
result = line_error;
} else {
syntax_free (edit->syntax_type);
edit->syntax_type = (char *) strdup (args[2]);
/* if there are no rules then turn off syntax highlighting for speed */
if (!edit->rules[1])
if (!edit->rules[0]->keyword[1] && !edit->rules[0]->spelling) {
edit_free_syntax_rules (edit);
break;
}
if (syntax_change_callback)
#ifdef MIDNIGHT
(*syntax_change_callback) (&edit->widget);
#else
(*syntax_change_callback) (edit->widget);
#endif
}
break;
}
}
}
free_args (args);
syntax_free (l);
}
free_args (args);
syntax_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;
}
/* loads rules into edit struct. one of edit or names must be zero. if
edit is zero, a list of types will be stored into name. type may be zero
in which case the type will be selected according to the filename. */
void edit_load_syntax (WEdit * edit, char **names, char *type)
{
int r;
char *f;
edit_free_syntax_rules (edit);
#ifdef MIDNIGHT
if (!SLtt_Use_Ansi_Colors || !use_colors)
return;
#endif
if (edit) {
if (!edit->filename)
return;
if (!*edit->filename && !type)
return;
}
f = catstrs (home_dir, SYNTAX_FILE, 0);
r = edit_read_syntax_file (edit, names, f, edit ? edit->filename : 0, get_first_editor_line (edit), type);
if (r == -1) {
edit_free_syntax_rules (edit);
edit_error_dialog (_ (" Load syntax file "), _ (" File access error "));
return;
}
if (r) {
char s[80];
edit_free_syntax_rules (edit);
sprintf (s, _ (" Error in file %s on line %d "), error_file_name ? error_file_name : f, r);
edit_error_dialog (_ (" Load syntax file "), s);
syntax_free (error_file_name);
return;
}
}
#else
int option_syntax_highlighting = 0;
void edit_load_syntax (WEdit * edit, char **names, char *type)
{
return;
}
void edit_free_syntax_rules (WEdit * edit)
{
return;
}
void edit_get_syntax_color (WEdit * edit, long byte_index, int *fg, int *bg)
{
*fg = NORMAL_COLOR;
}
#endif /* !defined(MIDNIGHT) || defined(HAVE_SYNTAXH) */