Manual pages: mcmcdiffmceditmcview

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

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