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

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