root/src/editor/syntax.c

/* [previous][next][first][last][top][bottom][index][help]  */

DEFINITIONS

This source file includes following definitions.
  1. syntax_keyword_free
  2. context_rule_free
  3. mc_defines_destroy
  4. destroy_defines
  5. xx_tolower
  6. subst_defines
  7. compare_word_to_right
  8. xx_strchr
  9. apply_rules_going_right
  10. edit_get_rule
  11. translate_rule_to_color
  12. read_one_line
  13. convert
  14. get_args
  15. this_try_alloc_color_pair
  16. open_include_file
  17. xx_lowerize_line
  18. edit_read_syntax_rules
  19. edit_read_syntax_file
  20. get_first_editor_line
  21. pstrcmp
  22. exec_edit_syntax_dialog
  23. edit_get_syntax_color
  24. edit_free_syntax_rules
  25. edit_load_syntax
  26. edit_get_syntax_type
  27. edit_syntax_dialog

   1 /*
   2    Editor syntax highlighting.
   3 
   4    Copyright (C) 1996-2021
   5    Free Software Foundation, Inc.
   6 
   7    Written by:
   8    Paul Sheer, 1998
   9    Leonard den Ottolander <leonard den ottolander nl>, 2005, 2006
  10    Egmont Koblinger <egmont@gmail.com>, 2010
  11    Slava Zanko <slavazanko@gmail.com>, 2013
  12    Andrew Borodin <aborodin@vmail.ru>, 2013, 2014, 2021
  13 
  14    This file is part of the Midnight Commander.
  15 
  16    The Midnight Commander is free software: you can redistribute it
  17    and/or modify it under the terms of the GNU General Public License as
  18    published by the Free Software Foundation, either version 3 of the License,
  19    or (at your option) any later version.
  20 
  21    The Midnight Commander is distributed in the hope that it will be useful,
  22    but WITHOUT ANY WARRANTY; without even the implied warranty of
  23    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  24    GNU General Public License for more details.
  25 
  26    You should have received a copy of the GNU General Public License
  27    along with this program.  If not, see <http://www.gnu.org/licenses/>.
  28  */
  29 
  30 /** \file
  31  *  \brief Source: editor syntax highlighting
  32  *  \author Paul Sheer
  33  *  \date 1996, 1997
  34  *  \author Mikhail Pobolovets
  35  *  \date 2010
  36  *
  37  *  Mispelled words are flushed from the syntax highlighting rules
  38  *  when they have been around longer than
  39  *  TRANSIENT_WORD_TIME_OUT seconds. At a cursor rate of 30
  40  *  chars per second and say 3 chars + a space per word, we can
  41  *  accumulate 450 words absolute max with a value of 60. This is
  42  *  below this limit of 1024 words in a context.
  43  */
  44 
  45 #include <config.h>
  46 
  47 #include <stdio.h>
  48 #include <stdarg.h>
  49 #include <sys/types.h>
  50 #include <unistd.h>
  51 #include <string.h>
  52 #include <ctype.h>
  53 #include <errno.h>
  54 #include <sys/stat.h>
  55 #include <stdlib.h>
  56 
  57 #include "lib/global.h"
  58 #include "lib/search.h"         /* search engine */
  59 #include "lib/skin.h"
  60 #include "lib/fileloc.h"        /* EDIT_HOME_DIR, EDIT_HOME_SYNTAX_FILE */
  61 #include "lib/strutil.h"        /* utf string functions */
  62 #include "lib/util.h"
  63 #include "lib/widget.h"         /* Listbox, message() */
  64 
  65 #include "edit-impl.h"
  66 #include "editwidget.h"
  67 
  68 /*** global variables ****************************************************************************/
  69 
  70 gboolean option_syntax_highlighting = TRUE;
  71 gboolean option_auto_syntax = TRUE;
  72 
  73 /*** file scope macro definitions ****************************************************************/
  74 
  75 /* bytes */
  76 #define SYNTAX_MARKER_DENSITY 512
  77 
  78 #define RULE_ON_LEFT_BORDER 1
  79 #define RULE_ON_RIGHT_BORDER 2
  80 
  81 #define SYNTAX_TOKEN_STAR       '\001'
  82 #define SYNTAX_TOKEN_PLUS       '\002'
  83 #define SYNTAX_TOKEN_BRACKET    '\003'
  84 #define SYNTAX_TOKEN_BRACE      '\004'
  85 
  86 #define break_a { result = line; break; }
  87 #define check_a { if (*a == NULL) { result = line; break; } }
  88 #define check_not_a { if (*a != NULL) { result = line ;break; } }
  89 
  90 #define SYNTAX_KEYWORD(x) ((syntax_keyword_t *) (x))
  91 #define CONTEXT_RULE(x) ((context_rule_t *) (x))
  92 
  93 #define ARGS_LEN 1024
  94 
  95 #define MAX_ENTRY_LEN 40
  96 #define LIST_LINES 14
  97 #define N_DFLT_ENTRIES 2
  98 
  99 /*** file scope type declarations ****************************************************************/
 100 
 101 typedef struct
 102 {
 103     char *keyword;
 104     char *whole_word_chars_left;
 105     char *whole_word_chars_right;
 106     gboolean line_start;
 107     int color;
 108 } syntax_keyword_t;
 109 
 110 typedef struct
 111 {
 112     char *left;
 113     unsigned char first_left;
 114     char *right;
 115     unsigned char first_right;
 116     gboolean line_start_left;
 117     gboolean line_start_right;
 118     gboolean between_delimiters;
 119     char *whole_word_chars_left;
 120     char *whole_word_chars_right;
 121     char *keyword_first_chars;
 122     gboolean spelling;
 123     /* first word is word[1] */
 124     GPtrArray *keyword;
 125 } context_rule_t;
 126 
 127 typedef struct
 128 {
 129     off_t offset;
 130     edit_syntax_rule_t rule;
 131 } syntax_marker_t;
 132 
 133 /*** file scope variables ************************************************************************/
 134 
 135 static char *error_file_name = NULL;
 136 
 137 /* --------------------------------------------------------------------------------------------- */
 138 /*** file scope functions ************************************************************************/
 139 /* --------------------------------------------------------------------------------------------- */
 140 
 141 static void
 142 syntax_keyword_free (gpointer keyword)
     /* [previous][next][first][last][top][bottom][index][help]  */
 143 {
 144     syntax_keyword_t *k = SYNTAX_KEYWORD (keyword);
 145 
 146     g_free (k->keyword);
 147     g_free (k->whole_word_chars_left);
 148     g_free (k->whole_word_chars_right);
 149     g_free (k);
 150 }
 151 
 152 /* --------------------------------------------------------------------------------------------- */
 153 
 154 static void
 155 context_rule_free (gpointer rule)
     /* [previous][next][first][last][top][bottom][index][help]  */
 156 {
 157     context_rule_t *r = CONTEXT_RULE (rule);
 158 
 159     g_free (r->left);
 160     g_free (r->right);
 161     g_free (r->whole_word_chars_left);
 162     g_free (r->whole_word_chars_right);
 163     g_free (r->keyword_first_chars);
 164 
 165     if (r->keyword != NULL)
 166     {
 167         g_ptr_array_foreach (r->keyword, (GFunc) syntax_keyword_free, NULL);
 168         g_ptr_array_free (r->keyword, TRUE);
 169     }
 170 
 171     g_free (r);
 172 }
 173 
 174 /* --------------------------------------------------------------------------------------------- */
 175 
 176 static gint
 177 mc_defines_destroy (gpointer key, gpointer value, gpointer data)
     /* [previous][next][first][last][top][bottom][index][help]  */
 178 {
 179     (void) data;
 180 
 181     g_free (key);
 182     g_strfreev ((char **) value);
 183 
 184     return FALSE;
 185 }
 186 
 187 /* --------------------------------------------------------------------------------------------- */
 188 /** Completely destroys the defines tree */
 189 
 190 static void
 191 destroy_defines (GTree ** defines)
     /* [previous][next][first][last][top][bottom][index][help]  */
 192 {
 193     g_tree_foreach (*defines, mc_defines_destroy, NULL);
 194     g_tree_destroy (*defines);
 195     *defines = NULL;
 196 }
 197 
 198 /* --------------------------------------------------------------------------------------------- */
 199 
 200 /** Wrapper for case insensitive mode */
 201 inline static int
 202 xx_tolower (const WEdit * edit, int c)
     /* [previous][next][first][last][top][bottom][index][help]  */
 203 {
 204     return edit->is_case_insensitive ? tolower (c) : c;
 205 }
 206 
 207 /* --------------------------------------------------------------------------------------------- */
 208 
 209 static void
 210 subst_defines (GTree * defines, char **argv, char **argv_end)
     /* [previous][next][first][last][top][bottom][index][help]  */
 211 {
 212     for (; *argv != NULL && argv < argv_end; argv++)
 213     {
 214         char **t;
 215 
 216         t = g_tree_lookup (defines, *argv);
 217         if (t != NULL)
 218         {
 219             int argc, count;
 220             char **p;
 221 
 222             /* Count argv array members */
 223             argc = g_strv_length (argv + 1);
 224 
 225             /* Count members of definition array */
 226             count = g_strv_length (t);
 227 
 228             p = argv + count + argc;
 229             /* Buffer overflow or infinitive loop in define */
 230             if (p >= argv_end)
 231                 break;
 232 
 233             /* Move rest of argv after definition members */
 234             while (argc >= 0)
 235                 *p-- = argv[argc-- + 1];
 236 
 237             /* Copy definition members to argv */
 238             for (p = argv; *t != NULL; *p++ = *t++)
 239                 ;
 240         }
 241     }
 242 }
 243 
 244 /* --------------------------------------------------------------------------------------------- */
 245 
 246 static off_t
 247 compare_word_to_right (const WEdit * edit, off_t i, const char *text,
     /* [previous][next][first][last][top][bottom][index][help]  */
 248                        const char *whole_left, const char *whole_right, gboolean line_start)
 249 {
 250     const unsigned char *p, *q;
 251     int c, d, j;
 252 
 253     if (*text == '\0')
 254         return -1;
 255 
 256     c = xx_tolower (edit, edit_buffer_get_byte (&edit->buffer, i - 1));
 257     if ((line_start && c != '\n') || (whole_left != NULL && strchr (whole_left, c) != NULL))
 258         return -1;
 259 
 260     for (p = (const unsigned char *) text, q = p + strlen ((const char *) p); p < q; p++, i++)
 261     {
 262         switch (*p)
 263         {
 264         case SYNTAX_TOKEN_STAR:
 265             if (++p > q)
 266                 return -1;
 267             while (TRUE)
 268             {
 269                 c = xx_tolower (edit, edit_buffer_get_byte (&edit->buffer, i));
 270                 if (*p == '\0' && whole_right != NULL && strchr (whole_right, c) == NULL)
 271                     break;
 272                 if (c == *p)
 273                     break;
 274                 if (c == '\n')
 275                     return -1;
 276                 i++;
 277             }
 278             break;
 279         case SYNTAX_TOKEN_PLUS:
 280             if (++p > q)
 281                 return -1;
 282             j = 0;
 283             while (TRUE)
 284             {
 285                 c = xx_tolower (edit, edit_buffer_get_byte (&edit->buffer, i));
 286                 if (c == *p)
 287                 {
 288                     j = i;
 289                     if (p[0] == text[0] && p[1] == '\0')        /* handle eg '+' and @+@ keywords properly */
 290                         break;
 291                 }
 292                 if (j != 0 && strchr ((const char *) p + 1, c) != NULL) /* c exists further down, so it will get matched later */
 293                     break;
 294                 if (whiteness (c) || (whole_right != NULL && strchr (whole_right, c) == NULL))
 295                 {
 296                     if (*p == '\0')
 297                     {
 298                         i--;
 299                         break;
 300                     }
 301                     if (j == 0)
 302                         return -1;
 303                     i = j;
 304                     break;
 305                 }
 306                 i++;
 307             }
 308             break;
 309         case SYNTAX_TOKEN_BRACKET:
 310             if (++p > q)
 311                 return -1;
 312             c = -1;
 313             while (TRUE)
 314             {
 315                 d = c;
 316                 c = xx_tolower (edit, edit_buffer_get_byte (&edit->buffer, i));
 317                 for (j = 0; p[j] != SYNTAX_TOKEN_BRACKET && p[j] != '\0'; j++)
 318                     if (c == p[j])
 319                         goto found_char2;
 320                 break;
 321               found_char2:
 322                 i++;
 323             }
 324             i--;
 325             while (*p != SYNTAX_TOKEN_BRACKET && p <= q)
 326                 p++;
 327             if (p > q)
 328                 return -1;
 329             if (p[1] == d)
 330                 i--;
 331             break;
 332         case SYNTAX_TOKEN_BRACE:
 333             if (++p > q)
 334                 return -1;
 335             c = xx_tolower (edit, edit_buffer_get_byte (&edit->buffer, i));
 336             for (; *p != SYNTAX_TOKEN_BRACE && *p != '\0'; p++)
 337                 if (c == *p)
 338                     goto found_char3;
 339             return -1;
 340           found_char3:
 341             while (*p != SYNTAX_TOKEN_BRACE && p < q)
 342                 p++;
 343             break;
 344         default:
 345             if (*p != xx_tolower (edit, edit_buffer_get_byte (&edit->buffer, i)))
 346                 return -1;
 347         }
 348     }
 349     return (whole_right != NULL &&
 350             strchr (whole_right,
 351                     xx_tolower (edit, edit_buffer_get_byte (&edit->buffer, i))) != NULL) ? -1 : i;
 352 }
 353 
 354 /* --------------------------------------------------------------------------------------------- */
 355 
 356 static const char *
 357 xx_strchr (const WEdit * edit, const unsigned char *s, int char_byte)
     /* [previous][next][first][last][top][bottom][index][help]  */
 358 {
 359     while (*s >= '\005' && xx_tolower (edit, *s) != char_byte)
 360         s++;
 361 
 362     return (const char *) s;
 363 }
 364 
 365 /* --------------------------------------------------------------------------------------------- */
 366 
 367 static void
 368 apply_rules_going_right (WEdit * edit, off_t i)
     /* [previous][next][first][last][top][bottom][index][help]  */
 369 {
 370     context_rule_t *r;
 371     int c;
 372     gboolean contextchanged = FALSE;
 373     gboolean found_left = FALSE, found_right = FALSE;
 374     gboolean keyword_foundleft = FALSE, keyword_foundright = FALSE;
 375     gboolean is_end;
 376     off_t end = 0;
 377     edit_syntax_rule_t _rule = edit->rule;
 378 
 379     c = xx_tolower (edit, edit_buffer_get_byte (&edit->buffer, i));
 380     if (c == 0)
 381         return;
 382 
 383     is_end = (edit->rule.end == i);
 384 
 385     /* check to turn off a keyword */
 386     if (_rule.keyword != 0)
 387     {
 388         if (edit_buffer_get_byte (&edit->buffer, i - 1) == '\n')
 389             _rule.keyword = 0;
 390         if (is_end)
 391         {
 392             _rule.keyword = 0;
 393             keyword_foundleft = TRUE;
 394         }
 395     }
 396 
 397     /* check to turn off a context */
 398     if (_rule.context != 0 && _rule.keyword == 0)
 399     {
 400         off_t e;
 401 
 402         r = CONTEXT_RULE (g_ptr_array_index (edit->rules, _rule.context));
 403         if (r->first_right == c && (edit->rule.border & RULE_ON_RIGHT_BORDER) == 0
 404             && (e =
 405                 compare_word_to_right (edit, i, r->right, r->whole_word_chars_left,
 406                                        r->whole_word_chars_right, r->line_start_right)) > 0)
 407         {
 408             _rule.end = e;
 409             found_right = TRUE;
 410             _rule.border = RULE_ON_RIGHT_BORDER;
 411             if (r->between_delimiters)
 412                 _rule.context = 0;
 413         }
 414         else if (is_end && (edit->rule.border & RULE_ON_RIGHT_BORDER) != 0)
 415         {
 416             /* always turn off a context at 4 */
 417             found_left = TRUE;
 418             _rule.border = 0;
 419             if (!keyword_foundleft)
 420                 _rule.context = 0;
 421         }
 422         else if (is_end && (edit->rule.border & RULE_ON_LEFT_BORDER) != 0)
 423         {
 424             /* never turn off a context at 2 */
 425             found_left = TRUE;
 426             _rule.border = 0;
 427         }
 428     }
 429 
 430     /* check to turn on a keyword */
 431     if (_rule.keyword == 0)
 432     {
 433         const char *p;
 434 
 435         r = CONTEXT_RULE (g_ptr_array_index (edit->rules, _rule.context));
 436         p = r->keyword_first_chars;
 437 
 438         if (p != NULL)
 439             while (*(p = xx_strchr (edit, (const unsigned char *) p + 1, c)) != '\0')
 440             {
 441                 syntax_keyword_t *k;
 442                 int count;
 443                 off_t e;
 444 
 445                 count = p - r->keyword_first_chars;
 446                 k = SYNTAX_KEYWORD (g_ptr_array_index (r->keyword, count));
 447                 e = compare_word_to_right (edit, i, k->keyword, k->whole_word_chars_left,
 448                                            k->whole_word_chars_right, k->line_start);
 449                 if (e > 0)
 450                 {
 451                     /* when both context and keyword terminate with a newline,
 452                        the context overflows to the next line and colorizes it incorrectly */
 453                     if (e > i + 1 && _rule._context != 0
 454                         && k->keyword[strlen (k->keyword) - 1] == '\n')
 455                     {
 456                         r = CONTEXT_RULE (g_ptr_array_index (edit->rules, _rule._context));
 457                         if (r->right != NULL && r->right[0] != '\0'
 458                             && r->right[strlen (r->right) - 1] == '\n')
 459                             e--;
 460                     }
 461 
 462                     end = e;
 463                     _rule.end = e;
 464                     _rule.keyword = count;
 465                     keyword_foundright = TRUE;
 466                     break;
 467                 }
 468             }
 469     }
 470 
 471     /* check to turn on a context */
 472     if (_rule.context == 0)
 473     {
 474         if (!found_left && is_end)
 475         {
 476             if ((edit->rule.border & RULE_ON_RIGHT_BORDER) != 0)
 477             {
 478                 _rule.border = 0;
 479                 _rule.context = 0;
 480                 contextchanged = TRUE;
 481                 _rule.keyword = 0;
 482 
 483             }
 484             else if ((edit->rule.border & RULE_ON_LEFT_BORDER) != 0)
 485             {
 486                 r = CONTEXT_RULE (g_ptr_array_index (edit->rules, _rule._context));
 487                 _rule.border = 0;
 488                 if (r->between_delimiters)
 489                 {
 490                     _rule.context = _rule._context;
 491                     contextchanged = TRUE;
 492                     _rule.keyword = 0;
 493 
 494                     if (r->first_right == c)
 495                     {
 496                         off_t e;
 497 
 498                         e = compare_word_to_right (edit, i, r->right, r->whole_word_chars_left,
 499                                                    r->whole_word_chars_right, r->line_start_right);
 500                         if (e >= end)
 501                         {
 502                             _rule.end = e;
 503                             found_right = TRUE;
 504                             _rule.border = RULE_ON_RIGHT_BORDER;
 505                             _rule.context = 0;
 506                         }
 507                     }
 508                 }
 509             }
 510         }
 511 
 512         if (!found_right)
 513         {
 514             size_t count;
 515 
 516             for (count = 1; count < edit->rules->len; count++)
 517             {
 518                 r = CONTEXT_RULE (g_ptr_array_index (edit->rules, count));
 519                 if (r->first_left == c)
 520                 {
 521                     off_t e;
 522 
 523                     e = compare_word_to_right (edit, i, r->left, r->whole_word_chars_left,
 524                                                r->whole_word_chars_right, r->line_start_left);
 525                     if (e >= end && (_rule.keyword == 0 || keyword_foundright))
 526                     {
 527                         _rule.end = e;
 528                         _rule.border = RULE_ON_LEFT_BORDER;
 529                         _rule._context = count;
 530                         if (!r->between_delimiters && _rule.keyword == 0)
 531                         {
 532                             _rule.context = count;
 533                             contextchanged = TRUE;
 534                         }
 535                         break;
 536                     }
 537                 }
 538             }
 539         }
 540     }
 541 
 542     /* check again to turn on a keyword if the context switched */
 543     if (contextchanged && _rule.keyword == 0)
 544     {
 545         const char *p;
 546 
 547         r = CONTEXT_RULE (g_ptr_array_index (edit->rules, _rule.context));
 548         p = r->keyword_first_chars;
 549 
 550         while (*(p = xx_strchr (edit, (const unsigned char *) p + 1, c)) != '\0')
 551         {
 552             syntax_keyword_t *k;
 553             int count;
 554             off_t e;
 555 
 556             count = p - r->keyword_first_chars;
 557             k = SYNTAX_KEYWORD (g_ptr_array_index (r->keyword, count));
 558             e = compare_word_to_right (edit, i, k->keyword, k->whole_word_chars_left,
 559                                        k->whole_word_chars_right, k->line_start);
 560             if (e > 0)
 561             {
 562                 _rule.end = e;
 563                 _rule.keyword = count;
 564                 break;
 565             }
 566         }
 567     }
 568 
 569     edit->rule = _rule;
 570 }
 571 
 572 /* --------------------------------------------------------------------------------------------- */
 573 
 574 static void
 575 edit_get_rule (WEdit * edit, off_t byte_index)
     /* [previous][next][first][last][top][bottom][index][help]  */
 576 {
 577     off_t i;
 578 
 579     if (byte_index > edit->last_get_rule)
 580     {
 581         for (i = edit->last_get_rule + 1; i <= byte_index; i++)
 582         {
 583             off_t d = SYNTAX_MARKER_DENSITY;
 584 
 585             apply_rules_going_right (edit, i);
 586 
 587             if (edit->syntax_marker != NULL)
 588                 d += ((syntax_marker_t *) edit->syntax_marker->data)->offset;
 589 
 590             if (i > d)
 591             {
 592                 syntax_marker_t *s;
 593 
 594                 s = g_new (syntax_marker_t, 1);
 595                 s->offset = i;
 596                 s->rule = edit->rule;
 597                 edit->syntax_marker = g_slist_prepend (edit->syntax_marker, s);
 598             }
 599         }
 600     }
 601     else if (byte_index < edit->last_get_rule)
 602     {
 603         while (TRUE)
 604         {
 605             syntax_marker_t *s;
 606 
 607             if (edit->syntax_marker == NULL)
 608             {
 609                 memset (&edit->rule, 0, sizeof (edit->rule));
 610                 for (i = -1; i <= byte_index; i++)
 611                     apply_rules_going_right (edit, i);
 612                 break;
 613             }
 614 
 615             s = (syntax_marker_t *) edit->syntax_marker->data;
 616 
 617             if (byte_index >= s->offset)
 618             {
 619                 edit->rule = s->rule;
 620                 for (i = s->offset + 1; i <= byte_index; i++)
 621                     apply_rules_going_right (edit, i);
 622                 break;
 623             }
 624 
 625             g_free (s);
 626             edit->syntax_marker = g_slist_delete_link (edit->syntax_marker, edit->syntax_marker);
 627         }
 628     }
 629     edit->last_get_rule = byte_index;
 630 }
 631 
 632 /* --------------------------------------------------------------------------------------------- */
 633 
 634 static int
 635 translate_rule_to_color (const WEdit * edit, const edit_syntax_rule_t * rule)
     /* [previous][next][first][last][top][bottom][index][help]  */
 636 {
 637     syntax_keyword_t *k;
 638     context_rule_t *r;
 639 
 640     r = CONTEXT_RULE (g_ptr_array_index (edit->rules, rule->context));
 641     k = SYNTAX_KEYWORD (g_ptr_array_index (r->keyword, rule->keyword));
 642 
 643     return k->color;
 644 }
 645 
 646 /* --------------------------------------------------------------------------------------------- */
 647 /**
 648    Returns 0 on error/eof or a count of the number of bytes read
 649    including the newline. Result must be free'd.
 650    In case of an error, *line will not be modified.
 651  */
 652 
 653 static size_t
 654 read_one_line (char **line, FILE * f)
     /* [previous][next][first][last][top][bottom][index][help]  */
 655 {
 656     GString *p;
 657     size_t r = 0;
 658 
 659     /* not reallocate string too often */
 660     p = g_string_sized_new (64);
 661 
 662     while (TRUE)
 663     {
 664         int c;
 665 
 666         c = fgetc (f);
 667         if (c == EOF)
 668         {
 669             if (ferror (f))
 670             {
 671                 if (errno == EINTR)
 672                     continue;
 673                 r = 0;
 674             }
 675             break;
 676         }
 677         r++;
 678 
 679         /* handle all of \r\n, \r, \n correctly. */
 680         if (c == '\n')
 681             break;
 682         if (c == '\r')
 683         {
 684             c = fgetc (f);
 685             if (c == '\n')
 686                 r++;
 687             else
 688                 ungetc (c, f);
 689             break;
 690         }
 691 
 692         g_string_append_c (p, c);
 693     }
 694     if (r != 0)
 695         *line = g_string_free (p, FALSE);
 696     else
 697         g_string_free (p, TRUE);
 698 
 699     return r;
 700 }
 701 
 702 /* --------------------------------------------------------------------------------------------- */
 703 
 704 static char *
 705 convert (char *s)
     /* [previous][next][first][last][top][bottom][index][help]  */
 706 {
 707     char *r, *p;
 708 
 709     p = r = s;
 710     while (*s)
 711     {
 712         switch (*s)
 713         {
 714         case '\\':
 715             s++;
 716             switch (*s)
 717             {
 718             case ' ':
 719                 *p = ' ';
 720                 s--;
 721                 break;
 722             case 'n':
 723                 *p = '\n';
 724                 break;
 725             case 'r':
 726                 *p = '\r';
 727                 break;
 728             case 't':
 729                 *p = '\t';
 730                 break;
 731             case 's':
 732                 *p = ' ';
 733                 break;
 734             case '*':
 735                 *p = '*';
 736                 break;
 737             case '\\':
 738                 *p = '\\';
 739                 break;
 740             case '[':
 741             case ']':
 742                 *p = SYNTAX_TOKEN_BRACKET;
 743                 break;
 744             case '{':
 745             case '}':
 746                 *p = SYNTAX_TOKEN_BRACE;
 747                 break;
 748             case 0:
 749                 *p = *s;
 750                 return r;
 751             default:
 752                 *p = *s;
 753                 break;
 754             }
 755             break;
 756         case '*':
 757             *p = SYNTAX_TOKEN_STAR;
 758             break;
 759         case '+':
 760             *p = SYNTAX_TOKEN_PLUS;
 761             break;
 762         default:
 763             *p = *s;
 764             break;
 765         }
 766         s++;
 767         p++;
 768     }
 769     *p = '\0';
 770     return r;
 771 }
 772 
 773 /* --------------------------------------------------------------------------------------------- */
 774 
 775 static int
 776 get_args (char *l, char **args, int args_size)
     /* [previous][next][first][last][top][bottom][index][help]  */
 777 {
 778     int argc = 0;
 779 
 780     while (argc < args_size)
 781     {
 782         char *p = l;
 783 
 784         while (*p != '\0' && whiteness (*p))
 785             p++;
 786         if (*p == '\0')
 787             break;
 788         for (l = p + 1; *l != '\0' && !whiteness (*l); l++)
 789             ;
 790         if (*l != '\0')
 791             *l++ = '\0';
 792         args[argc++] = convert (p);
 793     }
 794     args[argc] = (char *) NULL;
 795     return argc;
 796 }
 797 
 798 /* --------------------------------------------------------------------------------------------- */
 799 
 800 static int
 801 this_try_alloc_color_pair (const char *fg, const char *bg, const char *attrs)
     /* [previous][next][first][last][top][bottom][index][help]  */
 802 {
 803     char f[80], b[80], a[80], *p;
 804 
 805     if (bg != NULL && *bg == '\0')
 806         bg = NULL;
 807     if (fg != NULL && *fg == '\0')
 808         fg = NULL;
 809     if (attrs != NULL && *attrs == '\0')
 810         attrs = NULL;
 811 
 812     if ((fg == NULL) && (bg == NULL))
 813         return EDITOR_NORMAL_COLOR;
 814 
 815     if (fg != NULL)
 816     {
 817         g_strlcpy (f, fg, sizeof (f));
 818         p = strchr (f, '/');
 819         if (p != NULL)
 820             *p = '\0';
 821         fg = f;
 822     }
 823     if (bg != NULL)
 824     {
 825         g_strlcpy (b, bg, sizeof (b));
 826         p = strchr (b, '/');
 827         if (p != NULL)
 828             *p = '\0';
 829         bg = b;
 830     }
 831     if ((fg == NULL) || (bg == NULL))
 832     {
 833         /* get colors from skin */
 834         char *editnormal;
 835 
 836         editnormal = mc_skin_get ("editor", "_default_", "default;default");
 837 
 838         if (fg == NULL)
 839         {
 840             g_strlcpy (f, editnormal, sizeof (f));
 841             p = strchr (f, ';');
 842             if (p != NULL)
 843                 *p = '\0';
 844             if (f[0] == '\0')
 845                 g_strlcpy (f, "default", sizeof (f));
 846             fg = f;
 847         }
 848         if (bg == NULL)
 849         {
 850             p = strchr (editnormal, ';');
 851             if ((p != NULL) && (*(++p) != '\0'))
 852                 g_strlcpy (b, p, sizeof (b));
 853             else
 854                 g_strlcpy (b, "default", sizeof (b));
 855             bg = b;
 856         }
 857 
 858         g_free (editnormal);
 859     }
 860 
 861     if (attrs != NULL)
 862     {
 863         g_strlcpy (a, attrs, sizeof (a));
 864         p = strchr (a, '/');
 865         if (p != NULL)
 866             *p = '\0';
 867         /* get_args() mangles the + signs, unmangle 'em */
 868         p = a;
 869         while ((p = strchr (p, SYNTAX_TOKEN_PLUS)) != NULL)
 870             *p++ = '+';
 871         attrs = a;
 872     }
 873     return tty_try_alloc_color_pair (fg, bg, attrs);
 874 }
 875 
 876 /* --------------------------------------------------------------------------------------------- */
 877 
 878 static FILE *
 879 open_include_file (const char *filename)
     /* [previous][next][first][last][top][bottom][index][help]  */
 880 {
 881     FILE *f;
 882 
 883     g_free (error_file_name);
 884     error_file_name = g_strdup (filename);
 885     if (g_path_is_absolute (filename))
 886         return fopen (filename, "r");
 887 
 888     g_free (error_file_name);
 889     error_file_name =
 890         g_build_filename (mc_config_get_data_path (), EDIT_HOME_DIR, filename, (char *) NULL);
 891     f = fopen (error_file_name, "r");
 892     if (f != NULL)
 893         return f;
 894 
 895     g_free (error_file_name);
 896     error_file_name = g_build_filename (mc_global.sysconfig_dir, "syntax", filename, (char *) NULL);
 897     f = fopen (error_file_name, "r");
 898     if (f != NULL)
 899         return f;
 900 
 901     g_free (error_file_name);
 902     error_file_name =
 903         g_build_filename (mc_global.share_data_dir, "syntax", filename, (char *) NULL);
 904 
 905     return fopen (error_file_name, "r");
 906 }
 907 
 908 /* --------------------------------------------------------------------------------------------- */
 909 
 910 inline static void
 911 xx_lowerize_line (WEdit * edit, char *line, size_t len)
     /* [previous][next][first][last][top][bottom][index][help]  */
 912 {
 913     if (edit->is_case_insensitive)
 914     {
 915         size_t i;
 916 
 917         for (i = 0; i < len; ++i)
 918             line[i] = tolower (line[i]);
 919     }
 920 }
 921 
 922 /* --------------------------------------------------------------------------------------------- */
 923 /** returns line number on error */
 924 
 925 static int
 926 edit_read_syntax_rules (WEdit * edit, FILE * f, char **args, int args_size)
     /* [previous][next][first][last][top][bottom][index][help]  */
 927 {
 928     FILE *g = NULL;
 929     char *fg, *bg, *attrs;
 930     char last_fg[32] = "", last_bg[32] = "", last_attrs[64] = "";
 931     char whole_right[512];
 932     char whole_left[512];
 933     char *l = NULL;
 934     int save_line = 0, line = 0;
 935     context_rule_t *c = NULL;
 936     gboolean no_words = TRUE;
 937     int result = 0;
 938 
 939     args[0] = NULL;
 940     edit->is_case_insensitive = FALSE;
 941 
 942     strcpy (whole_left, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_01234567890");
 943     strcpy (whole_right, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_01234567890");
 944 
 945     edit->rules = g_ptr_array_new ();
 946 
 947     if (edit->defines == NULL)
 948         edit->defines = g_tree_new ((GCompareFunc) strcmp);
 949 
 950     while (TRUE)
 951     {
 952         char **a;
 953         size_t len;
 954         int argc;
 955 
 956         line++;
 957         l = NULL;
 958 
 959         len = read_one_line (&l, f);
 960         if (len != 0)
 961             xx_lowerize_line (edit, l, len);
 962         else
 963         {
 964             if (g == NULL)
 965                 break;
 966 
 967             fclose (f);
 968             f = g;
 969             g = NULL;
 970             line = save_line + 1;
 971             MC_PTR_FREE (error_file_name);
 972             MC_PTR_FREE (l);
 973             len = read_one_line (&l, f);
 974             if (len == 0)
 975                 break;
 976             xx_lowerize_line (edit, l, len);
 977         }
 978 
 979         argc = get_args (l, args, args_size);
 980         a = args + 1;
 981         if (args[0] == NULL)
 982         {
 983             /* do nothing */
 984         }
 985         else if (strcmp (args[0], "include") == 0)
 986         {
 987             if (g != NULL || argc != 2)
 988             {
 989                 result = line;
 990                 break;
 991             }
 992             g = f;
 993             f = open_include_file (args[1]);
 994             if (f == NULL)
 995             {
 996                 MC_PTR_FREE (error_file_name);
 997                 result = line;
 998                 break;
 999             }
1000             save_line = line;
1001             line = 0;
1002         }
1003         else if (strcmp (args[0], "caseinsensitive") == 0)
1004         {
1005             edit->is_case_insensitive = TRUE;
1006         }
1007         else if (strcmp (args[0], "wholechars") == 0)
1008         {
1009             check_a;
1010             if (strcmp (*a, "left") == 0)
1011             {
1012                 a++;
1013                 g_strlcpy (whole_left, *a, sizeof (whole_left));
1014             }
1015             else if (strcmp (*a, "right") == 0)
1016             {
1017                 a++;
1018                 g_strlcpy (whole_right, *a, sizeof (whole_right));
1019             }
1020             else
1021             {
1022                 g_strlcpy (whole_left, *a, sizeof (whole_left));
1023                 g_strlcpy (whole_right, *a, sizeof (whole_right));
1024             }
1025             a++;
1026             check_not_a;
1027         }
1028         else if (strcmp (args[0], "context") == 0)
1029         {
1030             syntax_keyword_t *k;
1031 
1032             check_a;
1033             if (edit->rules->len == 0)
1034             {
1035                 /* first context is the default */
1036                 if (strcmp (*a, "default") != 0)
1037                     break_a;
1038 
1039                 a++;
1040                 c = g_new0 (context_rule_t, 1);
1041                 g_ptr_array_add (edit->rules, c);
1042                 c->left = g_strdup (" ");
1043                 c->right = g_strdup (" ");
1044             }
1045             else
1046             {
1047                 /* Start new context.  */
1048                 c = g_new0 (context_rule_t, 1);
1049                 g_ptr_array_add (edit->rules, c);
1050                 if (strcmp (*a, "exclusive") == 0)
1051                 {
1052                     a++;
1053                     c->between_delimiters = TRUE;
1054                 }
1055                 check_a;
1056                 if (strcmp (*a, "whole") == 0)
1057                 {
1058                     a++;
1059                     c->whole_word_chars_left = g_strdup (whole_left);
1060                     c->whole_word_chars_right = g_strdup (whole_right);
1061                 }
1062                 else if (strcmp (*a, "wholeleft") == 0)
1063                 {
1064                     a++;
1065                     c->whole_word_chars_left = g_strdup (whole_left);
1066                 }
1067                 else if (strcmp (*a, "wholeright") == 0)
1068                 {
1069                     a++;
1070                     c->whole_word_chars_right = g_strdup (whole_right);
1071                 }
1072                 check_a;
1073                 if (strcmp (*a, "linestart") == 0)
1074                 {
1075                     a++;
1076                     c->line_start_left = TRUE;
1077                 }
1078                 check_a;
1079                 c->left = g_strdup (*a++);
1080                 check_a;
1081                 if (strcmp (*a, "linestart") == 0)
1082                 {
1083                     a++;
1084                     c->line_start_right = TRUE;
1085                 }
1086                 check_a;
1087                 c->right = g_strdup (*a++);
1088                 c->first_left = *c->left;
1089                 c->first_right = *c->right;
1090             }
1091             c->keyword = g_ptr_array_new ();
1092             k = g_new0 (syntax_keyword_t, 1);
1093             g_ptr_array_add (c->keyword, k);
1094             no_words = FALSE;
1095             subst_defines (edit->defines, a, &args[ARGS_LEN]);
1096             fg = *a;
1097             if (*a != NULL)
1098                 a++;
1099             bg = *a;
1100             if (*a != NULL)
1101                 a++;
1102             attrs = *a;
1103             if (*a != NULL)
1104                 a++;
1105             g_strlcpy (last_fg, fg != NULL ? fg : "", sizeof (last_fg));
1106             g_strlcpy (last_bg, bg != NULL ? bg : "", sizeof (last_bg));
1107             g_strlcpy (last_attrs, attrs != NULL ? attrs : "", sizeof (last_attrs));
1108             k->color = this_try_alloc_color_pair (fg, bg, attrs);
1109             k->keyword = g_strdup (" ");
1110             check_not_a;
1111         }
1112         else if (strcmp (args[0], "spellcheck") == 0)
1113         {
1114             if (c == NULL)
1115             {
1116                 result = line;
1117                 break;
1118             }
1119             c->spelling = TRUE;
1120         }
1121         else if (strcmp (args[0], "keyword") == 0)
1122         {
1123             context_rule_t *last_rule;
1124             syntax_keyword_t *k;
1125 
1126             if (no_words)
1127                 break_a;
1128             check_a;
1129             last_rule = CONTEXT_RULE (g_ptr_array_index (edit->rules, edit->rules->len - 1));
1130             k = g_new0 (syntax_keyword_t, 1);
1131             g_ptr_array_add (last_rule->keyword, k);
1132             if (strcmp (*a, "whole") == 0)
1133             {
1134                 a++;
1135                 k->whole_word_chars_left = g_strdup (whole_left);
1136                 k->whole_word_chars_right = g_strdup (whole_right);
1137             }
1138             else if (strcmp (*a, "wholeleft") == 0)
1139             {
1140                 a++;
1141                 k->whole_word_chars_left = g_strdup (whole_left);
1142             }
1143             else if (strcmp (*a, "wholeright") == 0)
1144             {
1145                 a++;
1146                 k->whole_word_chars_right = g_strdup (whole_right);
1147             }
1148             check_a;
1149             if (strcmp (*a, "linestart") == 0)
1150             {
1151                 a++;
1152                 k->line_start = TRUE;
1153             }
1154             check_a;
1155             if (strcmp (*a, "whole") == 0)
1156                 break_a;
1157 
1158             k->keyword = g_strdup (*a++);
1159             subst_defines (edit->defines, a, &args[ARGS_LEN]);
1160             fg = *a;
1161             if (*a != NULL)
1162                 a++;
1163             bg = *a;
1164             if (*a != NULL)
1165                 a++;
1166             attrs = *a;
1167             if (*a != NULL)
1168                 a++;
1169             if (fg == NULL)
1170                 fg = last_fg;
1171             if (bg == NULL)
1172                 bg = last_bg;
1173             if (attrs == NULL)
1174                 attrs = last_attrs;
1175             k->color = this_try_alloc_color_pair (fg, bg, attrs);
1176             check_not_a;
1177         }
1178         else if (*(args[0]) == '#')
1179         {
1180             /* do nothing for comment */
1181         }
1182         else if (strcmp (args[0], "file") == 0)
1183         {
1184             break;
1185         }
1186         else if (strcmp (args[0], "define") == 0)
1187         {
1188             char *key = *a++;
1189             char **argv;
1190 
1191             if (argc < 3)
1192                 break_a;
1193             argv = g_tree_lookup (edit->defines, key);
1194             if (argv != NULL)
1195                 mc_defines_destroy (NULL, argv, NULL);
1196             else
1197                 key = g_strdup (key);
1198 
1199             argv = g_new (char *, argc - 1);
1200             g_tree_insert (edit->defines, key, argv);
1201             while (*a != NULL)
1202                 *argv++ = g_strdup (*a++);
1203             *argv = NULL;
1204         }
1205         else
1206         {
1207             /* anything else is an error */
1208             break_a;
1209         }
1210         MC_PTR_FREE (l);
1211     }
1212     MC_PTR_FREE (l);
1213 
1214     if (edit->rules->len == 0)
1215     {
1216         g_ptr_array_free (edit->rules, TRUE);
1217         edit->rules = NULL;
1218     }
1219 
1220     if (result == 0)
1221     {
1222         size_t i;
1223         GString *first_chars;
1224 
1225         if (edit->rules == NULL)
1226             return line;
1227 
1228         first_chars = g_string_sized_new (32);
1229 
1230         /* collect first character of keywords */
1231         for (i = 0; i < edit->rules->len; i++)
1232         {
1233             size_t j;
1234 
1235             g_string_set_size (first_chars, 0);
1236             c = CONTEXT_RULE (g_ptr_array_index (edit->rules, i));
1237 
1238             g_string_append_c (first_chars, (char) 1);
1239             for (j = 1; j < c->keyword->len; j++)
1240             {
1241                 syntax_keyword_t *k;
1242 
1243                 k = SYNTAX_KEYWORD (g_ptr_array_index (c->keyword, j));
1244                 g_string_append_c (first_chars, k->keyword[0]);
1245             }
1246 
1247             c->keyword_first_chars = g_strndup (first_chars->str, first_chars->len);
1248         }
1249 
1250         g_string_free (first_chars, TRUE);
1251     }
1252 
1253     return result;
1254 }
1255 
1256 /* --------------------------------------------------------------------------------------------- */
1257 
1258 /* returns -1 on file error, line number on error in file syntax */
1259 static int
1260 edit_read_syntax_file (WEdit * edit, GPtrArray * pnames, const char *syntax_file,
     /* [previous][next][first][last][top][bottom][index][help]  */
1261                        const char *editor_file, const char *first_line, const char *type)
1262 {
1263     FILE *f, *g = NULL;
1264     char *args[ARGS_LEN], *l = NULL;
1265     long line = 0;
1266     int result = 0;
1267     char *lib_file;
1268     gboolean found = FALSE;
1269 
1270     f = fopen (syntax_file, "r");
1271     if (f == NULL)
1272     {
1273         lib_file = g_build_filename (mc_global.share_data_dir, "syntax", "Syntax", (char *) NULL);
1274         f = fopen (lib_file, "r");
1275         g_free (lib_file);
1276         if (f == NULL)
1277             return -1;
1278     }
1279 
1280     args[0] = NULL;
1281     while (TRUE)
1282     {
1283         line++;
1284         MC_PTR_FREE (l);
1285         if (read_one_line (&l, f) == 0)
1286             break;
1287         (void) get_args (l, args, ARGS_LEN - 1);        /* Final NULL */
1288         if (args[0] == NULL)
1289             continue;
1290 
1291         /* Looking for 'include ...' lines before first 'file ...' ones */
1292         if (!found && strcmp (args[0], "include") == 0)
1293         {
1294             if (args[1] == NULL || (g = open_include_file (args[1])) == NULL)
1295             {
1296                 result = line;
1297                 break;
1298             }
1299             goto found_type;
1300         }
1301 
1302         /* looking for 'file ...' lines only */
1303         if (strcmp (args[0], "file") != 0)
1304             continue;
1305 
1306         found = TRUE;
1307 
1308         /* must have two args or report error */
1309         if (args[1] == NULL || args[2] == NULL)
1310         {
1311             result = line;
1312             break;
1313         }
1314 
1315         if (pnames != NULL)
1316         {
1317             /* 1: just collecting a list of names of rule sets */
1318             g_ptr_array_add (pnames, g_strdup (args[2]));
1319         }
1320         else if (type != NULL)
1321         {
1322             /* 2: rule set was explicitly specified by the caller */
1323             if (strcmp (type, args[2]) == 0)
1324                 goto found_type;
1325         }
1326         else if (editor_file != NULL && edit != NULL)
1327         {
1328             /* 3: auto-detect rule set from regular expressions */
1329             gboolean q;
1330 
1331             q = mc_search (args[1], DEFAULT_CHARSET, editor_file, MC_SEARCH_T_REGEX);
1332             /* does filename match arg 1 ? */
1333             if (!q && args[3] != NULL)
1334             {
1335                 /* does first line match arg 3 ? */
1336                 q = mc_search (args[3], DEFAULT_CHARSET, first_line, MC_SEARCH_T_REGEX);
1337             }
1338             if (q)
1339             {
1340                 int line_error;
1341                 char *syntax_type;
1342 
1343               found_type:
1344                 syntax_type = args[2];
1345                 line_error = edit_read_syntax_rules (edit, g ? g : f, args, ARGS_LEN - 1);
1346                 if (line_error != 0)
1347                 {
1348                     if (error_file_name == NULL)        /* an included file */
1349                         result = line + line_error;
1350                     else
1351                         result = line_error;
1352                 }
1353                 else
1354                 {
1355                     g_free (edit->syntax_type);
1356                     edit->syntax_type = g_strdup (syntax_type);
1357                     /* if there are no rules then turn off syntax highlighting for speed */
1358                     if (g == NULL && edit->rules->len == 1)
1359                     {
1360                         context_rule_t *r0;
1361 
1362                         r0 = CONTEXT_RULE (g_ptr_array_index (edit->rules, 0));
1363                         if (r0->keyword->len == 1 && !r0->spelling)
1364                         {
1365                             edit_free_syntax_rules (edit);
1366                             break;
1367                         }
1368                     }
1369                 }
1370 
1371                 if (g == NULL)
1372                     break;
1373 
1374                 fclose (g);
1375                 g = NULL;
1376             }
1377         }
1378     }
1379     g_free (l);
1380     fclose (f);
1381     return result;
1382 }
1383 
1384 /* --------------------------------------------------------------------------------------------- */
1385 
1386 static const char *
1387 get_first_editor_line (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
1388 {
1389     static char s[256];
1390 
1391     s[0] = '\0';
1392 
1393     if (edit != NULL)
1394     {
1395         size_t i;
1396 
1397         for (i = 0; i < sizeof (s) - 1; i++)
1398         {
1399             s[i] = edit_buffer_get_byte (&edit->buffer, i);
1400             if (s[i] == '\n')
1401             {
1402                 s[i] = '\0';
1403                 break;
1404             }
1405         }
1406 
1407         s[sizeof (s) - 1] = '\0';
1408     }
1409 
1410     return s;
1411 }
1412 
1413 /* --------------------------------------------------------------------------------------------- */
1414 
1415 static int
1416 pstrcmp (const void *p1, const void *p2)
     /* [previous][next][first][last][top][bottom][index][help]  */
1417 {
1418     return strcmp (*(char *const *) p1, *(char *const *) p2);
1419 }
1420 
1421 /* --------------------------------------------------------------------------------------------- */
1422 
1423 static int
1424 exec_edit_syntax_dialog (const GPtrArray * names, const char *current_syntax)
     /* [previous][next][first][last][top][bottom][index][help]  */
1425 {
1426     size_t i;
1427     Listbox *syntaxlist;
1428 
1429     syntaxlist = create_listbox_window (LIST_LINES, MAX_ENTRY_LEN,
1430                                         _("Choose syntax highlighting"), NULL);
1431     LISTBOX_APPEND_TEXT (syntaxlist, 'A', _("< Auto >"), NULL, FALSE);
1432     LISTBOX_APPEND_TEXT (syntaxlist, 'R', _("< Reload Current Syntax >"), NULL, FALSE);
1433 
1434     for (i = 0; i < names->len; i++)
1435     {
1436         const char *name;
1437 
1438         name = g_ptr_array_index (names, i);
1439         LISTBOX_APPEND_TEXT (syntaxlist, 0, name, NULL, FALSE);
1440         if (current_syntax != NULL && strcmp (name, current_syntax) == 0)
1441             listbox_select_entry (syntaxlist->list, i + N_DFLT_ENTRIES);
1442     }
1443 
1444     return run_listbox (syntaxlist);
1445 }
1446 
1447 /* --------------------------------------------------------------------------------------------- */
1448 /*** public functions ****************************************************************************/
1449 /* --------------------------------------------------------------------------------------------- */
1450 
1451 int
1452 edit_get_syntax_color (WEdit * edit, off_t byte_index)
     /* [previous][next][first][last][top][bottom][index][help]  */
1453 {
1454     if (!tty_use_colors ())
1455         return 0;
1456 
1457     if (edit->rules != NULL && byte_index < edit->buffer.size && option_syntax_highlighting)
1458     {
1459         edit_get_rule (edit, byte_index);
1460         return translate_rule_to_color (edit, &edit->rule);
1461     }
1462 
1463     return EDITOR_NORMAL_COLOR;
1464 }
1465 
1466 /* --------------------------------------------------------------------------------------------- */
1467 
1468 void
1469 edit_free_syntax_rules (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
1470 {
1471     if (edit == NULL)
1472         return;
1473 
1474     if (edit->defines != NULL)
1475         destroy_defines (&edit->defines);
1476 
1477     if (edit->rules == NULL)
1478         return;
1479 
1480     edit_get_rule (edit, -1);
1481     MC_PTR_FREE (edit->syntax_type);
1482 
1483     g_ptr_array_foreach (edit->rules, (GFunc) context_rule_free, NULL);
1484     g_ptr_array_free (edit->rules, TRUE);
1485     edit->rules = NULL;
1486     g_clear_slist (&edit->syntax_marker, g_free);
1487     tty_color_free_all_tmp ();
1488 }
1489 
1490 /* --------------------------------------------------------------------------------------------- */
1491 /**
1492  * Load rules into edit struct.  Either edit or *pnames must be NULL.  If
1493  * edit is NULL, a list of types will be stored into names.  If type is
1494  * NULL, then the type will be selected according to the filename.
1495  * type must be edit->syntax_type or NULL
1496  */
1497 void
1498 edit_load_syntax (WEdit * edit, GPtrArray * pnames, const char *type)
     /* [previous][next][first][last][top][bottom][index][help]  */
1499 {
1500     int r;
1501     char *f = NULL;
1502 
1503     if (option_auto_syntax)
1504         type = NULL;
1505 
1506     if (edit != NULL)
1507     {
1508         char *saved_type;
1509 
1510         saved_type = g_strdup (type);   /* save edit->syntax_type */
1511         edit_free_syntax_rules (edit);
1512         edit->syntax_type = saved_type; /* restore edit->syntax_type */
1513     }
1514 
1515     if (!tty_use_colors ())
1516         return;
1517 
1518     if (!option_syntax_highlighting && (pnames == NULL || pnames->len == 0))
1519         return;
1520 
1521     if (edit != NULL && edit->filename_vpath == NULL)
1522         return;
1523 
1524     f = mc_config_get_full_path (EDIT_HOME_SYNTAX_FILE);
1525     if (edit != NULL)
1526         r = edit_read_syntax_file (edit, pnames, f, vfs_path_as_str (edit->filename_vpath),
1527                                    get_first_editor_line (edit),
1528                                    option_auto_syntax ? NULL : edit->syntax_type);
1529     else
1530         r = edit_read_syntax_file (NULL, pnames, f, NULL, "", NULL);
1531     if (r == -1)
1532     {
1533         edit_free_syntax_rules (edit);
1534         message (D_ERROR, _("Load syntax file"),
1535                  _("Cannot open file %s\n%s"), f, unix_error_string (errno));
1536     }
1537     else if (r != 0)
1538     {
1539         edit_free_syntax_rules (edit);
1540         message (D_ERROR, _("Load syntax file"),
1541                  _("Error in file %s on line %d"), error_file_name != NULL ? error_file_name : f,
1542                  r);
1543         MC_PTR_FREE (error_file_name);
1544     }
1545 
1546     g_free (f);
1547 }
1548 
1549 /* --------------------------------------------------------------------------------------------- */
1550 
1551 const char *
1552 edit_get_syntax_type (const WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
1553 {
1554     return edit->syntax_type;
1555 }
1556 
1557 /* --------------------------------------------------------------------------------------------- */
1558 
1559 void
1560 edit_syntax_dialog (WEdit * edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
1561 {
1562     GPtrArray *names;
1563     int syntax;
1564 
1565     names = g_ptr_array_new ();
1566 
1567     /* We fill the list of syntax files every time the editor is invoked.
1568        Instead we could save the list to a file and update it once the syntax
1569        file gets updated (either by testing or by explicit user command). */
1570     edit_load_syntax (NULL, names, NULL);
1571     g_ptr_array_sort (names, pstrcmp);
1572 
1573     syntax = exec_edit_syntax_dialog (names, edit->syntax_type);
1574     if (syntax >= 0)
1575     {
1576         gboolean force_reload = FALSE;
1577         char *current_syntax;
1578         gboolean old_auto_syntax;
1579 
1580         current_syntax = g_strdup (edit->syntax_type);
1581         old_auto_syntax = option_auto_syntax;
1582 
1583         switch (syntax)
1584         {
1585         case 0:                /* auto syntax */
1586             option_auto_syntax = TRUE;
1587             break;
1588         case 1:                /* reload current syntax */
1589             force_reload = TRUE;
1590             break;
1591         default:
1592             option_auto_syntax = FALSE;
1593             g_free (edit->syntax_type);
1594             edit->syntax_type = g_strdup (g_ptr_array_index (names, syntax - N_DFLT_ENTRIES));
1595         }
1596 
1597         /* Load or unload syntax rules if the option has changed */
1598         if (force_reload || (option_auto_syntax && !old_auto_syntax) || old_auto_syntax ||
1599             (current_syntax != NULL && edit->syntax_type != NULL &&
1600              strcmp (current_syntax, edit->syntax_type) != 0))
1601             edit_load_syntax (edit, NULL, edit->syntax_type);
1602 
1603         g_free (current_syntax);
1604     }
1605 
1606     g_ptr_array_foreach (names, (GFunc) g_free, NULL);
1607     g_ptr_array_free (names, TRUE);
1608 }
1609 
1610 /* --------------------------------------------------------------------------------------------- */

/* [previous][next][first][last][top][bottom][index][help]  */