Manual pages: mc • mcdiff • mcedit • mcview

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

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