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 "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[BUF_TINY / 2] = "";
 959     char last_bg[BUF_TINY / 2] = "";
 960     char last_attrs[BUF_TINY] = "";
 961     char whole_right[BUF_MEDIUM];
 962     char whole_left[BUF_MEDIUM];
 963     char *l = NULL;
 964     int save_line = 0, line = 0;
 965     context_rule_t *c = NULL;
 966     gboolean no_words = TRUE;
 967     int result = 0;
 968 
 969     args[0] = NULL;
 970     edit->is_case_insensitive = FALSE;
 971 
 972     strcpy (whole_left, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_01234567890");
 973     strcpy (whole_right, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_01234567890");
 974 
 975     edit->rules = g_ptr_array_new_with_free_func (context_rule_free);
 976 
 977     if (edit->defines == NULL)
 978         edit->defines = g_tree_new ((GCompareFunc) strcmp);
 979 
 980     while (TRUE)
 981     {
 982         char **a;
 983         size_t len;
 984         int argc;
 985 
 986         line++;
 987         l = NULL;
 988 
 989         len = read_one_line (&l, f);
 990         if (len == 0)
 991         {
 992             if (g == NULL)
 993                 break;
 994 
 995             fclose (f);
 996             f = g;
 997             g = NULL;
 998             line = save_line + 1;
 999             MC_PTR_FREE (error_file_name);
1000             MC_PTR_FREE (l);
1001             len = read_one_line (&l, f);
1002             if (len == 0)
1003                 break;
1004         }
1005 
1006         xx_lowerize_line (edit, l, len);
1007 
1008         argc = get_args (l, args, args_size);
1009         a = args + 1;
1010         if (args[0] == NULL)
1011         {
1012             // do nothing
1013         }
1014         else if (strcmp (args[0], "include") == 0)
1015         {
1016             if (g != NULL || argc != 2)
1017             {
1018                 result = line;
1019                 break;
1020             }
1021             g = f;
1022             f = open_include_file (args[1]);
1023             if (f == NULL)
1024             {
1025                 MC_PTR_FREE (error_file_name);
1026                 result = line;
1027                 break;
1028             }
1029             save_line = line;
1030             line = 0;
1031         }
1032         else if (strcmp (args[0], "caseinsensitive") == 0)
1033         {
1034             edit->is_case_insensitive = TRUE;
1035         }
1036         else if (strcmp (args[0], "wholechars") == 0)
1037         {
1038             check_a;
1039             if (strcmp (*a, "left") == 0)
1040             {
1041                 a++;
1042                 g_strlcpy (whole_left, *a, sizeof (whole_left));
1043             }
1044             else if (strcmp (*a, "right") == 0)
1045             {
1046                 a++;
1047                 g_strlcpy (whole_right, *a, sizeof (whole_right));
1048             }
1049             else
1050             {
1051                 g_strlcpy (whole_left, *a, sizeof (whole_left));
1052                 g_strlcpy (whole_right, *a, sizeof (whole_right));
1053             }
1054             a++;
1055             check_not_a;
1056         }
1057         else if (strcmp (args[0], "context") == 0)
1058         {
1059             syntax_keyword_t *k;
1060 
1061             check_a;
1062             if (edit->rules->len == 0)
1063             {
1064                 // first context is the default
1065                 if (strcmp (*a, "default") != 0)
1066                     break_a;
1067 
1068                 a++;
1069                 c = g_new0 (context_rule_t, 1);
1070                 g_ptr_array_add (edit->rules, c);
1071                 c->left = g_string_new (" ");
1072                 c->right = g_string_new (" ");
1073             }
1074             else
1075             {
1076                 // Start new context.
1077                 c = g_new0 (context_rule_t, 1);
1078                 g_ptr_array_add (edit->rules, c);
1079                 if (strcmp (*a, "exclusive") == 0)
1080                 {
1081                     a++;
1082                     c->between_delimiters = TRUE;
1083                 }
1084                 check_a;
1085                 if (strcmp (*a, "whole") == 0)
1086                 {
1087                     a++;
1088                     c->whole_word_chars_left = g_strdup (whole_left);
1089                     c->whole_word_chars_right = g_strdup (whole_right);
1090                 }
1091                 else if (strcmp (*a, "wholeleft") == 0)
1092                 {
1093                     a++;
1094                     c->whole_word_chars_left = g_strdup (whole_left);
1095                 }
1096                 else if (strcmp (*a, "wholeright") == 0)
1097                 {
1098                     a++;
1099                     c->whole_word_chars_right = g_strdup (whole_right);
1100                 }
1101                 check_a;
1102                 if (strcmp (*a, "linestart") == 0)
1103                 {
1104                     a++;
1105                     c->line_start_left = TRUE;
1106                 }
1107                 check_a;
1108                 c->left = g_string_new (*a++);
1109                 check_a;
1110                 if (strcmp (*a, "linestart") == 0)
1111                 {
1112                     a++;
1113                     c->line_start_right = TRUE;
1114                 }
1115                 check_a;
1116                 c->right = g_string_new (*a++);
1117                 c->first_left = c->left->str[0];
1118                 c->first_right = c->right->str[0];
1119             }
1120             c->keyword = g_ptr_array_new_with_free_func (syntax_keyword_free);
1121             k = g_new0 (syntax_keyword_t, 1);
1122             g_ptr_array_add (c->keyword, k);
1123             no_words = FALSE;
1124             subst_defines (edit->defines, a, &args[ARGS_LEN]);
1125             color.fg = *a;
1126             if (*a != NULL)
1127                 a++;
1128             color.bg = *a;
1129             if (*a != NULL)
1130                 a++;
1131             color.attrs = *a;
1132             if (*a != NULL)
1133                 a++;
1134             g_strlcpy (last_fg, color.fg != NULL ? color.fg : "", sizeof (last_fg));
1135             g_strlcpy (last_bg, color.bg != NULL ? color.bg : "", sizeof (last_bg));
1136             g_strlcpy (last_attrs, color.attrs != NULL ? color.attrs : "", sizeof (last_attrs));
1137             k->color = this_try_alloc_color_pair (&color);
1138             k->keyword = g_string_new (" ");
1139             check_not_a;
1140         }
1141         else if (strcmp (args[0], "spellcheck") == 0)
1142         {
1143             if (c == NULL)
1144             {
1145                 result = line;
1146                 break;
1147             }
1148             c->spelling = TRUE;
1149         }
1150         else if (strcmp (args[0], "keyword") == 0)
1151         {
1152             context_rule_t *last_rule;
1153             syntax_keyword_t *k;
1154 
1155             if (no_words)
1156                 break_a;
1157             check_a;
1158             last_rule = CONTEXT_RULE (g_ptr_array_index (edit->rules, edit->rules->len - 1));
1159             k = g_new0 (syntax_keyword_t, 1);
1160             g_ptr_array_add (last_rule->keyword, k);
1161             if (strcmp (*a, "whole") == 0)
1162             {
1163                 a++;
1164                 k->whole_word_chars_left = g_strdup (whole_left);
1165                 k->whole_word_chars_right = g_strdup (whole_right);
1166             }
1167             else if (strcmp (*a, "wholeleft") == 0)
1168             {
1169                 a++;
1170                 k->whole_word_chars_left = g_strdup (whole_left);
1171             }
1172             else if (strcmp (*a, "wholeright") == 0)
1173             {
1174                 a++;
1175                 k->whole_word_chars_right = g_strdup (whole_right);
1176             }
1177             check_a;
1178             if (strcmp (*a, "linestart") == 0)
1179             {
1180                 a++;
1181                 k->line_start = TRUE;
1182             }
1183             check_a;
1184             if (strcmp (*a, "whole") == 0)
1185                 break_a;
1186 
1187             k->keyword = g_string_new (*a++);
1188             subst_defines (edit->defines, a, &args[ARGS_LEN]);
1189             color.fg = *a;
1190             if (*a != NULL)
1191                 a++;
1192             color.bg = *a;
1193             if (*a != NULL)
1194                 a++;
1195             color.attrs = *a;
1196             if (*a != NULL)
1197                 a++;
1198             if (color.fg == NULL)
1199                 color.fg = last_fg;
1200             if (color.bg == NULL)
1201                 color.bg = last_bg;
1202             if (color.attrs == NULL)
1203                 color.attrs = last_attrs;
1204             k->color = this_try_alloc_color_pair (&color);
1205             check_not_a;
1206         }
1207         else if (*(args[0]) == '#')
1208         {
1209             // do nothing for comment
1210         }
1211         else if (strcmp (args[0], "file") == 0)
1212         {
1213             break;
1214         }
1215         else if (strcmp (args[0], "define") == 0)
1216         {
1217             char *key = *a++;
1218             char **argv;
1219 
1220             if (argc < 3)
1221                 break_a;
1222             argv = g_tree_lookup (edit->defines, key);
1223             if (argv != NULL)
1224                 mc_defines_destroy (NULL, argv, NULL);
1225             else
1226                 key = g_strdup (key);
1227 
1228             argv = g_new (char *, argc - 1);
1229             g_tree_insert (edit->defines, key, argv);
1230             while (*a != NULL)
1231                 *argv++ = g_strdup (*a++);
1232             *argv = NULL;
1233         }
1234         else
1235         {
1236             // anything else is an error
1237             break_a;
1238         }
1239         MC_PTR_FREE (l);
1240     }
1241     MC_PTR_FREE (l);
1242 
1243     if (edit->rules->len == 0)
1244     {
1245         g_ptr_array_free (edit->rules, TRUE);
1246         edit->rules = NULL;
1247     }
1248 
1249     if (result == 0)
1250     {
1251         size_t i;
1252         GString *first_chars;
1253 
1254         if (edit->rules == NULL)
1255             return line;
1256 
1257         first_chars = g_string_sized_new (32);
1258 
1259         // collect first character of keywords
1260         for (i = 0; i < edit->rules->len; i++)
1261         {
1262             size_t j;
1263 
1264             g_string_set_size (first_chars, 0);
1265             c = CONTEXT_RULE (g_ptr_array_index (edit->rules, i));
1266 
1267             g_string_append_c (first_chars, (char) 1);
1268             for (j = 1; j < c->keyword->len; j++)
1269             {
1270                 syntax_keyword_t *k;
1271 
1272                 k = SYNTAX_KEYWORD (g_ptr_array_index (c->keyword, j));
1273                 g_string_append_c (first_chars, k->keyword->str[0]);
1274             }
1275 
1276             c->keyword_first_chars = g_strndup (first_chars->str, first_chars->len);
1277         }
1278 
1279         g_string_free (first_chars, TRUE);
1280     }
1281 
1282     return result;
1283 }
1284 
1285 /* --------------------------------------------------------------------------------------------- */
1286 
1287 /* returns -1 on file error, line number on error in file syntax */
1288 static int
1289 edit_read_syntax_file (WEdit *edit, GPtrArray *pnames, const char *syntax_file,
     /* [previous][next][first][last][top][bottom][index][help]  */
1290                        const char *editor_file, const char *first_line, const char *type)
1291 {
1292     FILE *f, *g = NULL;
1293     char *args[ARGS_LEN], *l = NULL;
1294     long line = 0;
1295     int result = 0;
1296     gboolean found = FALSE;
1297 
1298     f = fopen (syntax_file, "r");
1299     if (f == NULL)
1300     {
1301         char *global_syntax_file;
1302 
1303         global_syntax_file =
1304             g_build_filename (mc_global.share_data_dir, EDIT_SYNTAX_FILE, (char *) NULL);
1305         f = fopen (global_syntax_file, "r");
1306         g_free (global_syntax_file);
1307         if (f == NULL)
1308             return -1;
1309     }
1310 
1311     args[0] = NULL;
1312     while (TRUE)
1313     {
1314         line++;
1315         MC_PTR_FREE (l);
1316         if (read_one_line (&l, f) == 0)
1317             break;
1318         (void) get_args (l, args, ARGS_LEN - 1);  // Final NULL
1319         if (args[0] == NULL)
1320             continue;
1321 
1322         // Looking for 'include ...' lines before first 'file ...' ones
1323         if (!found && strcmp (args[0], "include") == 0)
1324         {
1325             if (args[1] == NULL || (g = open_include_file (args[1])) == NULL)
1326             {
1327                 result = line;
1328                 break;
1329             }
1330             goto found_type;
1331         }
1332 
1333         // looking for 'file ...' lines only
1334         if (strcmp (args[0], "file") != 0)
1335             continue;
1336 
1337         found = TRUE;
1338 
1339         // must have two args or report error
1340         if (args[1] == NULL || args[2] == NULL)
1341         {
1342             result = line;
1343             break;
1344         }
1345 
1346         if (pnames != NULL)
1347         {
1348             // 1: just collecting a list of names of rule sets
1349             g_ptr_array_add (pnames, g_strdup (args[2]));
1350         }
1351         else if (type != NULL)
1352         {
1353             // 2: rule set was explicitly specified by the caller
1354             if (strcmp (type, args[2]) == 0)
1355                 goto found_type;
1356         }
1357         else if (editor_file != NULL && edit != NULL)
1358         {
1359             // 3: auto-detect rule set from regular expressions
1360             gboolean q;
1361 
1362             q = mc_search (args[1], NULL, editor_file, MC_SEARCH_T_REGEX);
1363             // does filename match arg 1 ?
1364             if (!q && args[3] != NULL)
1365             {
1366                 // does first line match arg 3 ?
1367                 q = mc_search (args[3], NULL, first_line, MC_SEARCH_T_REGEX);
1368             }
1369             if (q)
1370             {
1371                 int line_error;
1372                 char *syntax_type;
1373 
1374             found_type:
1375                 syntax_type = args[2];
1376                 line_error = edit_read_syntax_rules (edit, g ? g : f, args, ARGS_LEN - 1);
1377                 if (line_error != 0)
1378                 {
1379                     if (error_file_name == NULL)  // an included file
1380                         result = line + line_error;
1381                     else
1382                         result = line_error;
1383                 }
1384                 else
1385                 {
1386                     g_free (edit->syntax_type);
1387                     edit->syntax_type = g_strdup (syntax_type);
1388                     // if there are no rules then turn off syntax highlighting for speed
1389                     if (g == NULL && edit->rules->len == 1)
1390                     {
1391                         context_rule_t *r0;
1392 
1393                         r0 = CONTEXT_RULE (g_ptr_array_index (edit->rules, 0));
1394                         if (r0->keyword->len == 1 && !r0->spelling)
1395                         {
1396                             edit_free_syntax_rules (edit);
1397                             break;
1398                         }
1399                     }
1400                 }
1401 
1402                 if (g == NULL)
1403                     break;
1404 
1405                 fclose (g);
1406                 g = NULL;
1407             }
1408         }
1409     }
1410     g_free (l);
1411     fclose (f);
1412     return result;
1413 }
1414 
1415 /* --------------------------------------------------------------------------------------------- */
1416 
1417 static const char *
1418 get_first_editor_line (WEdit *edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
1419 {
1420     static char s[256];
1421 
1422     s[0] = '\0';
1423 
1424     if (edit != NULL)
1425     {
1426         size_t i;
1427 
1428         for (i = 0; i < sizeof (s) - 1; i++)
1429         {
1430             s[i] = edit_buffer_get_byte (&edit->buffer, i);
1431             if (s[i] == '\n')
1432             {
1433                 s[i] = '\0';
1434                 break;
1435             }
1436         }
1437 
1438         s[sizeof (s) - 1] = '\0';
1439     }
1440 
1441     return s;
1442 }
1443 
1444 /* --------------------------------------------------------------------------------------------- */
1445 
1446 static int
1447 pstrcmp (const void *p1, const void *p2)
     /* [previous][next][first][last][top][bottom][index][help]  */
1448 {
1449     return strcmp (*(char *const *) p1, *(char *const *) p2);
1450 }
1451 
1452 /* --------------------------------------------------------------------------------------------- */
1453 
1454 static int
1455 exec_edit_syntax_dialog (const GPtrArray *names, const char *current_syntax)
     /* [previous][next][first][last][top][bottom][index][help]  */
1456 {
1457     size_t i;
1458     Listbox *syntaxlist;
1459 
1460     syntaxlist =
1461         listbox_window_new (LIST_LINES, MAX_ENTRY_LEN, _ ("Choose syntax highlighting"), NULL);
1462     LISTBOX_APPEND_TEXT (syntaxlist, 'A', _ ("< Auto >"), NULL, FALSE);
1463     LISTBOX_APPEND_TEXT (syntaxlist, 'R', _ ("< Reload Current Syntax >"), NULL, FALSE);
1464 
1465     for (i = 0; i < names->len; i++)
1466     {
1467         const char *name;
1468 
1469         name = g_ptr_array_index (names, i);
1470         LISTBOX_APPEND_TEXT (syntaxlist, 0, name, NULL, FALSE);
1471         if (current_syntax != NULL && strcmp (name, current_syntax) == 0)
1472             listbox_set_current (syntaxlist->list, i + N_DFLT_ENTRIES);
1473     }
1474 
1475     return listbox_run (syntaxlist);
1476 }
1477 
1478 /* --------------------------------------------------------------------------------------------- */
1479 /*** public functions ****************************************************************************/
1480 /* --------------------------------------------------------------------------------------------- */
1481 
1482 int
1483 edit_get_syntax_color (WEdit *edit, off_t byte_index)
     /* [previous][next][first][last][top][bottom][index][help]  */
1484 {
1485     if (!tty_use_colors ())
1486         return 0;
1487 
1488     if (edit_options.syntax_highlighting && edit->rules != NULL && byte_index < edit->buffer.size)
1489     {
1490         edit_get_rule (edit, byte_index);
1491         return translate_rule_to_color (edit, &edit->rule);
1492     }
1493 
1494     return EDITOR_NORMAL_COLOR;
1495 }
1496 
1497 /* --------------------------------------------------------------------------------------------- */
1498 
1499 void
1500 edit_free_syntax_rules (WEdit *edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
1501 {
1502     if (edit == NULL)
1503         return;
1504 
1505     if (edit->defines != NULL)
1506         destroy_defines (&edit->defines);
1507 
1508     if (edit->rules == NULL)
1509         return;
1510 
1511     edit_get_rule (edit, -1);
1512     MC_PTR_FREE (edit->syntax_type);
1513 
1514     g_ptr_array_free (edit->rules, TRUE);
1515     edit->rules = NULL;
1516     g_clear_slist (&edit->syntax_marker, g_free);
1517     tty_color_free_temp ();
1518 }
1519 
1520 /* --------------------------------------------------------------------------------------------- */
1521 /**
1522  * Load rules into edit struct.  Either edit or *pnames must be NULL.  If
1523  * edit is NULL, a list of types will be stored into names.  If type is
1524  * NULL, then the type will be selected according to the filename.
1525  * type must be edit->syntax_type or NULL
1526  */
1527 void
1528 edit_load_syntax (WEdit *edit, GPtrArray *pnames, const char *type)
     /* [previous][next][first][last][top][bottom][index][help]  */
1529 {
1530     int r;
1531     char *f = NULL;
1532 
1533     if (auto_syntax)
1534         type = NULL;
1535 
1536     if (edit != NULL)
1537     {
1538         char *saved_type;
1539 
1540         saved_type = g_strdup (type);  // save edit->syntax_type
1541         edit_free_syntax_rules (edit);
1542         edit->syntax_type = saved_type;  // restore edit->syntax_type
1543     }
1544 
1545     if (!tty_use_colors ())
1546         return;
1547 
1548     if (!edit_options.syntax_highlighting && (pnames == NULL || pnames->len == 0))
1549         return;
1550 
1551     if (edit != NULL && edit->filename_vpath == NULL)
1552         return;
1553 
1554     f = mc_config_get_full_path (EDIT_SYNTAX_FILE);
1555     if (edit != NULL)
1556         r = edit_read_syntax_file (edit, pnames, f, vfs_path_as_str (edit->filename_vpath),
1557                                    get_first_editor_line (edit),
1558                                    auto_syntax ? NULL : edit->syntax_type);
1559     else
1560         r = edit_read_syntax_file (NULL, pnames, f, NULL, "", NULL);
1561     if (r == -1)
1562     {
1563         edit_free_syntax_rules (edit);
1564         message (D_ERROR, _ ("Load syntax file"), _ ("Cannot open file %s\n%s"), f,
1565                  unix_error_string (errno));
1566     }
1567     else if (r != 0)
1568     {
1569         edit_free_syntax_rules (edit);
1570         message (D_ERROR, _ ("Load syntax file"), _ ("Error in file %s on line %d"),
1571                  error_file_name != NULL ? error_file_name : f, r);
1572         MC_PTR_FREE (error_file_name);
1573     }
1574 
1575     g_free (f);
1576 }
1577 
1578 /* --------------------------------------------------------------------------------------------- */
1579 
1580 const char *
1581 edit_get_syntax_type (const WEdit *edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
1582 {
1583     return edit->syntax_type;
1584 }
1585 
1586 /* --------------------------------------------------------------------------------------------- */
1587 
1588 void
1589 edit_syntax_dialog (WEdit *edit)
     /* [previous][next][first][last][top][bottom][index][help]  */
1590 {
1591     GPtrArray *names;
1592     int syntax;
1593 
1594     names = g_ptr_array_new_with_free_func (g_free);
1595 
1596     /* We fill the list of syntax files every time the editor is invoked.
1597        Instead we could save the list to a file and update it once the syntax
1598        file gets updated (either by testing or by explicit user command). */
1599     edit_load_syntax (NULL, names, NULL);
1600     g_ptr_array_sort (names, pstrcmp);
1601 
1602     syntax = exec_edit_syntax_dialog (names, edit->syntax_type);
1603     if (syntax >= 0)
1604     {
1605         gboolean force_reload = FALSE;
1606         char *current_syntax;
1607         gboolean old_auto_syntax;
1608 
1609         current_syntax = g_strdup (edit->syntax_type);
1610         old_auto_syntax = auto_syntax;
1611 
1612         switch (syntax)
1613         {
1614         case 0:  // auto syntax
1615             auto_syntax = TRUE;
1616             break;
1617         case 1:  // reload current syntax
1618             force_reload = TRUE;
1619             break;
1620         default:
1621             auto_syntax = FALSE;
1622             g_free (edit->syntax_type);
1623             edit->syntax_type = g_strdup (g_ptr_array_index (names, syntax - N_DFLT_ENTRIES));
1624         }
1625 
1626         // Load or unload syntax rules if the option has changed
1627         if (force_reload || (auto_syntax && !old_auto_syntax) || old_auto_syntax
1628             || (current_syntax != NULL && edit->syntax_type != NULL
1629                 && strcmp (current_syntax, edit->syntax_type) != 0))
1630             edit_load_syntax (edit, NULL, edit->syntax_type);
1631 
1632         g_free (current_syntax);
1633     }
1634 
1635     g_ptr_array_free (names, TRUE);
1636 }
1637 
1638 /* --------------------------------------------------------------------------------------------- */

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