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

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