From a041291c80179094deff8c9cf25b17a28ecaa841 Mon Sep 17 00:00:00 2001 From: Bruno Haible Date: Sat, 5 Apr 2025 14:18:27 +0200 Subject: [PATCH] Remove all occurrences of reinterpret_cast<>. * src/keyword-list.h (Keyword_List): Add template parameter list . (KeywordExt_List): Define as a typedef, not as a subclass. (copy_list, delete_list, mergesort_list): Templatize accordingly. * src/keyword-list.icc (Keyword_List): Add template parameter list . (KeywordExt_List): Remove method definitions. * src/keyword-list.cc (Keyword_List): Add template parameter list . (copy_list, mergesort_list): Remove definitions on subclass. Add explicit template instantiation. * src/keyword.h (Keyword_Factory): Add template parameter list . * src/keyword.cc (Keyword_Factory): Likewise. Add explicit template instantiation. * src/input.h (Input): Add template parameter list . * src/input.cc (Input): Likewise. Add explicit template instantiation. * src/main.cc (KeywordExt_Factory): Define as a typedef, not as a subclass. (main): Update. --- ChangeLog | 20 + src/input.cc | 1549 +++++++++++++++++++++--------------------- src/input.h | 75 +- src/keyword-list.cc | 255 +++---- src/keyword-list.h | 60 +- src/keyword-list.icc | 42 +- src/keyword.cc | 32 +- src/keyword.h | 27 +- src/main.cc | 19 +- 9 files changed, 1060 insertions(+), 1019 deletions(-) diff --git a/ChangeLog b/ChangeLog index 537c27d..79ed848 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,23 @@ +2025-04-05 Bruno Haible + + Remove all occurrences of reinterpret_cast<>. + * src/keyword-list.h (Keyword_List): Add template parameter list . + (KeywordExt_List): Define as a typedef, not as a subclass. + (copy_list, delete_list, mergesort_list): Templatize accordingly. + * src/keyword-list.icc (Keyword_List): Add template parameter list . + (KeywordExt_List): Remove method definitions. + * src/keyword-list.cc (Keyword_List): Add template parameter list . + (copy_list, mergesort_list): Remove definitions on subclass. + Add explicit template instantiation. + * src/keyword.h (Keyword_Factory): Add template parameter list . + * src/keyword.cc (Keyword_Factory): Likewise. + Add explicit template instantiation. + * src/input.h (Input): Add template parameter list . + * src/input.cc (Input): Likewise. Add explicit template instantiation. + * src/main.cc (KeywordExt_Factory): Define as a typedef, not as a + subclass. + (main): Update. + 2025-04-05 Bruno Haible Make output on Windows identical to output on Unix. diff --git a/src/input.cc b/src/input.cc index 10aa3b6..19b0a34 100644 --- a/src/input.cc +++ b/src/input.cc @@ -1,5 +1,5 @@ /* Input routines. - Copyright (C) 1989-1998, 2002-2004, 2011, 2017-2018 Free Software Foundation, Inc. + Copyright (C) 1989-1998, 2002-2004, 2011, 2017-2018, 2025 Free Software Foundation, Inc. Written by Douglas C. Schmidt and Bruno Haible . @@ -28,10 +28,11 @@ #include "options.h" #include "getline.h" -Input::Input (FILE *stream, Keyword_Factory *keyword_factory) - : _stream (stream), _factory (keyword_factory) -{ -} +template + Input::Input (FILE *stream, Keyword_Factory *keyword_factory) + : _stream (stream), _factory (keyword_factory) + { + } /* Returns a pretty representation of the input file name, for error and warning messages. */ @@ -226,806 +227,824 @@ is_define_declaration (const char *line, const char *line_end, } /* Reads the entire input file. */ -void -Input::read_input () -{ - /* The input file has the following structure: - DECLARATIONS - %% - KEYWORDS - %% - ADDITIONAL_CODE - Since the DECLARATIONS and the ADDITIONAL_CODE sections are optional, - we have to read the entire file in the case there is only one %% - separator line, in order to determine whether the structure is - DECLARATIONS - %% - KEYWORDS - or - KEYWORDS - %% - ADDITIONAL_CODE - When the option -t is given or when the first section contains - declaration lines starting with %, we go for the first interpretation, - otherwise for the second interpretation. */ - - char *input = NULL; - size_t input_size = 0; - int input_length = get_delim (&input, &input_size, EOF, _stream); - if (input_length < 0) - { - if (ferror (_stream)) - fprintf (stderr, "%s: error while reading input file\n", - pretty_input_file_name ()); - else - fprintf (stderr, "%s: The input file is empty!\n", - pretty_input_file_name ()); - exit (1); - } - - /* Convert CR/LF line terminators (Windows) to LF line terminators (Unix). - GCC 3.3 and newer support CR/LF line terminators in C sources on Unix, - so we do the same. - The so-called "text mode" in stdio on Windows translates CR/LF to \n - automatically, but here we also need this conversion on Unix. As a side - effect, on Windows we also parse CR/CR/LF into a single \n, but this - is not a problem. */ +template + void + Input::read_input () { - char *p = input; - char *p_end = input + input_length; - /* Converting the initial segment without CRs is a no-op. */ - while (p < p_end && *p != '\r') - p++; - /* Then start the conversion for real. */ - char *q = p; - while (p < p_end) + /* The input file has the following structure: + DECLARATIONS + %% + KEYWORDS + %% + ADDITIONAL_CODE + Since the DECLARATIONS and the ADDITIONAL_CODE sections are optional, + we have to read the entire file in the case there is only one %% + separator line, in order to determine whether the structure is + DECLARATIONS + %% + KEYWORDS + or + KEYWORDS + %% + ADDITIONAL_CODE + When the option -t is given or when the first section contains + declaration lines starting with %, we go for the first interpretation, + otherwise for the second interpretation. */ + + char *input = NULL; + size_t input_size = 0; + int input_length = get_delim (&input, &input_size, EOF, _stream); + if (input_length < 0) { - if (p[0] == '\r' && p + 1 < p_end && p[1] == '\n') - p++; - *q++ = *p++; - } - input_length = q - input; - } - - /* We use input_end as a limit, in order to cope with NUL bytes in the - input. But note that one trailing NUL byte has been added after - input_end, for convenience. */ - char *input_end = input + input_length; - - const char *declarations; - const char *declarations_end; - const char *keywords; - const char *keywords_end; - unsigned int keywords_lineno; - - /* Break up the input into the three sections. */ - { - const char *separator[2] = { NULL, NULL }; - unsigned int separator_lineno[2] = { 0, 0 }; - int separators = 0; - { - unsigned int lineno = 1; - for (const char *p = input; p < input_end; ) - { - if (p[0] == '%' && p[1] == '%') - { - separator[separators] = p; - separator_lineno[separators] = lineno; - if (++separators == 2) - break; - } - lineno++; - p = (const char *) memchr (p, '\n', input_end - p); - if (p != NULL) - p++; - else - p = input_end; - } - } - - bool has_declarations; - if (separators == 1) - { - if (option[TYPE]) - has_declarations = true; + if (ferror (_stream)) + fprintf (stderr, "%s: error while reading input file\n", + pretty_input_file_name ()); else - { - has_declarations = false; - for (const char *p = input; p < separator[0]; ) - { - if (p[0] == '%') - { - has_declarations = true; - break; - } - p = (const char *) memchr (p, '\n', separator[0] - p); - if (p != NULL) - p++; - else - p = separator[0]; - } - } - } - else - has_declarations = (separators > 0); - - if (has_declarations) - { - declarations = input; - declarations_end = separator[0]; - /* Give a warning if the separator line is nonempty. */ - bool nonempty_line = false; - const char *p; - for (p = declarations_end + 2; p < input_end; ) - { - if (*p == '\n') - { - p++; - break; - } - if (!(*p == ' ' || *p == '\t')) - nonempty_line = true; - p++; - } - if (nonempty_line) - fprintf (stderr, "%s:%u: warning: junk after %%%% is ignored\n", - pretty_input_file_name (), separator_lineno[0]); - keywords = p; - keywords_lineno = separator_lineno[0] + 1; - } - else - { - declarations = NULL; - declarations_end = NULL; - keywords = input; - keywords_lineno = 1; - } - - if (separators > (has_declarations ? 1 : 0)) - { - keywords_end = separator[separators-1]; - _verbatim_code = separator[separators-1] + 2; - _verbatim_code_end = input_end; - _verbatim_code_lineno = separator_lineno[separators-1]; - } - else - { - keywords_end = input_end; - _verbatim_code = NULL; - _verbatim_code_end = NULL; - _verbatim_code_lineno = 0; - } - } - - /* Parse the declarations section. */ - - _verbatim_declarations = NULL; - _verbatim_declarations_end = NULL; - _verbatim_declarations_lineno = 0; - _struct_decl = NULL; - _struct_decl_lineno = 0; - _return_type = NULL; - _struct_tag = NULL; - { - unsigned int lineno = 1; - char *struct_decl = NULL; - unsigned int *struct_decl_linenos = NULL; - unsigned int struct_decl_linecount = 0; - for (const char *line = declarations; line < declarations_end; ) - { - const char *line_end; - line_end = (const char *) memchr (line, '\n', declarations_end - line); - if (line_end != NULL) - line_end++; - else - line_end = declarations_end; - - if (*line == '%') - { - if (line[1] == '{') - { - /* Handle %{. */ - if (_verbatim_declarations != NULL) - { - fprintf (stderr, "%s:%u:\n%s:%u:" - " only one %%{...%%} section is allowed\n", - pretty_input_file_name (), - _verbatim_declarations_lineno, - pretty_input_file_name (), lineno); - exit (1); - } - _verbatim_declarations = line + 2; - _verbatim_declarations_lineno = lineno; - } - else if (line[1] == '}') - { - /* Handle %}. */ - if (_verbatim_declarations == NULL) - { - fprintf (stderr, "%s:%u:" - " %%} outside of %%{...%%} section\n", - pretty_input_file_name (), lineno); - exit (1); - } - if (_verbatim_declarations_end != NULL) - { - fprintf (stderr, "%s:%u:" - " %%{...%%} section already closed\n", - pretty_input_file_name (), lineno); - exit (1); - } - _verbatim_declarations_end = line; - /* Give a warning if the rest of the line is nonempty. */ - bool nonempty_line = false; - const char *q; - for (q = line + 2; q < line_end; q++) - { - if (*q == '\n') - { - q++; - break; - } - if (!(*q == ' ' || *q == '\t')) - nonempty_line = true; - } - if (nonempty_line) - fprintf (stderr, "%s:%u:" - " warning: junk after %%} is ignored\n", - pretty_input_file_name (), lineno); - } - else if (_verbatim_declarations != NULL - && _verbatim_declarations_end == NULL) - { - fprintf (stderr, "%s:%u:" - " warning: %% directives are ignored" - " inside the %%{...%%} section\n", - pretty_input_file_name (), lineno); - } - else - { - char *arg; - - if (is_declaration_with_arg (line, line_end, lineno, - "delimiters", &arg)) - option.set_delimiters (arg); - else - - if (is_declaration (line, line_end, lineno, "struct-type")) - option.set (TYPE); - else - - if (is_declaration (line, line_end, lineno, "ignore-case")) - option.set (UPPERLOWER); - else - - if (is_declaration_with_arg (line, line_end, lineno, - "language", &arg)) - option.set_language (arg); - else - - if (is_define_declaration (line, line_end, lineno, - "slot-name", &arg)) - option.set_slot_name (arg); - else - - if (is_define_declaration (line, line_end, lineno, - "initializer-suffix", &arg)) - option.set_initializer_suffix (arg); - else - - if (is_define_declaration (line, line_end, lineno, - "hash-function-name", &arg)) - option.set_hash_name (arg); - else - - if (is_define_declaration (line, line_end, lineno, - "lookup-function-name", &arg)) - option.set_function_name (arg); - else - - if (is_define_declaration (line, line_end, lineno, - "class-name", &arg)) - option.set_class_name (arg); - else - - if (is_declaration (line, line_end, lineno, "7bit")) - option.set (SEVENBIT); - else - - if (is_declaration (line, line_end, lineno, "compare-lengths")) - option.set (LENTABLE); - else - - if (is_declaration (line, line_end, lineno, "compare-strncmp")) - option.set (COMP); - else - - if (is_declaration (line, line_end, lineno, "readonly-tables")) - option.set (CONST); - else - - if (is_declaration (line, line_end, lineno, "enum")) - option.set (ENUM); - else - - if (is_declaration (line, line_end, lineno, "includes")) - option.set (INCLUDE); - else - - if (is_declaration (line, line_end, lineno, "global-table")) - option.set (GLOBAL); - else - - if (is_declaration (line, line_end, lineno, "pic")) - option.set (SHAREDLIB); - else - - if (is_define_declaration (line, line_end, lineno, - "string-pool-name", &arg)) - option.set_stringpool_name (arg); - else - - if (is_declaration (line, line_end, lineno, "null-strings")) - option.set (NULLSTRINGS); - else - - if (is_define_declaration (line, line_end, lineno, - "constants-prefix", &arg)) - option.set_constants_prefix (arg); - else - - if (is_define_declaration (line, line_end, lineno, - "word-array-name", &arg)) - option.set_wordlist_name (arg); - else - - if (is_define_declaration (line, line_end, lineno, - "length-table-name", &arg)) - option.set_lengthtable_name (arg); - else - - if (is_declaration_with_arg (line, line_end, lineno, - "switch", &arg)) - { - option.set_total_switches (atoi (arg)); - if (option.get_total_switches () <= 0) - { - fprintf (stderr, "%s:%u: number of switches %s" - " must be a positive number\n", - pretty_input_file_name (), lineno, arg); - exit (1); - } - } - else - - if (is_declaration (line, line_end, lineno, "omit-struct-type")) - option.set (NOTYPE); - else - - { - fprintf (stderr, "%s:%u: unrecognized %% directive\n", - pretty_input_file_name (), lineno); - exit (1); - } - } - } - else if (!(_verbatim_declarations != NULL - && _verbatim_declarations_end == NULL)) - { - /* Append the line to struct_decl. */ - size_t old_len = (struct_decl ? strlen (struct_decl) : 0); - size_t line_len = line_end - line; - size_t new_len = old_len + line_len + 1; - char *new_struct_decl = new char[new_len]; - if (old_len > 0) - memcpy (new_struct_decl, struct_decl, old_len); - memcpy (new_struct_decl + old_len, line, line_len); - new_struct_decl[old_len + line_len] = '\0'; - if (struct_decl) - delete[] struct_decl; - struct_decl = new_struct_decl; - /* Append the lineno to struct_decl_linenos. */ - unsigned int *new_struct_decl_linenos = - new unsigned int[struct_decl_linecount + 1]; - if (struct_decl_linecount > 0) - memcpy (new_struct_decl_linenos, struct_decl_linenos, - struct_decl_linecount * sizeof (unsigned int)); - new_struct_decl_linenos[struct_decl_linecount] = lineno; - if (struct_decl_linenos) - delete[] struct_decl_linenos; - struct_decl_linenos = new_struct_decl_linenos; - /* Increment struct_decl_linecount. */ - struct_decl_linecount++; - } - lineno++; - line = line_end; - } - if (_verbatim_declarations != NULL && _verbatim_declarations_end == NULL) - { - fprintf (stderr, "%s:%u: unterminated %%{ section\n", - pretty_input_file_name (), _verbatim_declarations_lineno); + fprintf (stderr, "%s: The input file is empty!\n", + pretty_input_file_name ()); exit (1); } - /* Determine _struct_decl, _return_type, _struct_tag. */ - if (option[TYPE]) + /* Convert CR/LF line terminators (Windows) to LF line terminators (Unix). + GCC 3.3 and newer support CR/LF line terminators in C sources on Unix, + so we do the same. + The so-called "text mode" in stdio on Windows translates CR/LF to \n + automatically, but here we also need this conversion on Unix. As a side + effect, on Windows we also parse CR/CR/LF into a single \n, but this + is not a problem. */ + { + char *p = input; + char *p_end = input + input_length; + /* Converting the initial segment without CRs is a no-op. */ + while (p < p_end && *p != '\r') + p++; + /* Then start the conversion for real. */ + char *q = p; + while (p < p_end) + { + if (p[0] == '\r' && p + 1 < p_end && p[1] == '\n') + p++; + *q++ = *p++; + } + input_length = q - input; + } + + /* We use input_end as a limit, in order to cope with NUL bytes in the + input. But note that one trailing NUL byte has been added after + input_end, for convenience. */ + char *input_end = input + input_length; + + const char *declarations; + const char *declarations_end; + const char *keywords; + const char *keywords_end; + unsigned int keywords_lineno; + + /* Break up the input into the three sections. */ + { + const char *separator[2] = { NULL, NULL }; + unsigned int separator_lineno[2] = { 0, 0 }; + int separators = 0; { - if (struct_decl) + unsigned int lineno = 1; + for (const char *p = input; p < input_end; ) { - /* Drop leading whitespace and comments. */ + if (p[0] == '%' && p[1] == '%') + { + separator[separators] = p; + separator_lineno[separators] = lineno; + if (++separators == 2) + break; + } + lineno++; + p = (const char *) memchr (p, '\n', input_end - p); + if (p != NULL) + p++; + else + p = input_end; + } + } + + bool has_declarations; + if (separators == 1) + { + if (option[TYPE]) + has_declarations = true; + else { - char *p = struct_decl; - unsigned int *l = struct_decl_linenos; - for (;;) + has_declarations = false; + for (const char *p = input; p < separator[0]; ) { - if (p[0] == ' ' || p[0] == '\t') + if (p[0] == '%') { - p++; - continue; - } - if (p[0] == '\n') - { - l++; - p++; - continue; - } - if (p[0] == '/') - { - if (p[1] == '*') - { - /* Skip over ANSI C style comment. */ - p += 2; - while (p[0] != '\0') - { - if (p[0] == '*' && p[1] == '/') - { - p += 2; - break; - } - if (p[0] == '\n') - l++; - p++; - } - continue; - } - if (p[1] == '/') - { - /* Skip over ISO C99 or C++ style comment. */ - p += 2; - while (p[0] != '\0' && p[0] != '\n') - p++; - if (p[0] == '\n') - { - l++; - p++; - } - continue; - } + has_declarations = true; + break; } + p = (const char *) memchr (p, '\n', separator[0] - p); + if (p != NULL) + p++; + else + p = separator[0]; + } + } + } + else + has_declarations = (separators > 0); + + if (has_declarations) + { + declarations = input; + declarations_end = separator[0]; + /* Give a warning if the separator line is nonempty. */ + bool nonempty_line = false; + const char *p; + for (p = declarations_end + 2; p < input_end; ) + { + if (*p == '\n') + { + p++; break; } - if (p != struct_decl) - { - size_t len = strlen (p); - char *new_struct_decl = new char[len + 1]; - memcpy (new_struct_decl, p, len + 1); - delete[] struct_decl; - struct_decl = new_struct_decl; - } - _struct_decl_lineno = *l; - } - /* Drop trailing whitespace. */ - for (char *p = struct_decl + strlen (struct_decl); p > struct_decl;) - if (p[-1] == '\n' || p[-1] == ' ' || p[-1] == '\t') - *--p = '\0'; - else - break; - } - if (struct_decl == NULL || struct_decl[0] == '\0') - { - fprintf (stderr, "%s: missing struct declaration" - " for option --struct-type\n", - pretty_input_file_name ()); - exit (1); - } - { - /* Ensure trailing semicolon. */ - size_t old_len = strlen (struct_decl); - if (struct_decl[old_len - 1] != ';') - { - char *new_struct_decl = new char[old_len + 2]; - memcpy (new_struct_decl, struct_decl, old_len); - new_struct_decl[old_len] = ';'; - new_struct_decl[old_len + 1] = '\0'; - delete[] struct_decl; - struct_decl = new_struct_decl; + if (!(*p == ' ' || *p == '\t')) + nonempty_line = true; + p++; } + if (nonempty_line) + fprintf (stderr, "%s:%u: warning: junk after %%%% is ignored\n", + pretty_input_file_name (), separator_lineno[0]); + keywords = p; + keywords_lineno = separator_lineno[0] + 1; } - /* Set _struct_decl to the entire declaration. */ - _struct_decl = struct_decl; - /* Set _struct_tag to the naked "struct something". */ - const char *p; - for (p = struct_decl; *p && *p != '{' && *p != ';' && *p != '\n'; p++) - ; - for (; p > struct_decl;) - if (p[-1] == '\n' || p[-1] == ' ' || p[-1] == '\t') - --p; + else + { + declarations = NULL; + declarations_end = NULL; + keywords = input; + keywords_lineno = 1; + } + + if (separators > (has_declarations ? 1 : 0)) + { + keywords_end = separator[separators-1]; + _verbatim_code = separator[separators-1] + 2; + _verbatim_code_end = input_end; + _verbatim_code_lineno = separator_lineno[separators-1]; + } + else + { + keywords_end = input_end; + _verbatim_code = NULL; + _verbatim_code_end = NULL; + _verbatim_code_lineno = 0; + } + } + + /* Parse the declarations section. */ + + _verbatim_declarations = NULL; + _verbatim_declarations_end = NULL; + _verbatim_declarations_lineno = 0; + _struct_decl = NULL; + _struct_decl_lineno = 0; + _return_type = NULL; + _struct_tag = NULL; + { + unsigned int lineno = 1; + char *struct_decl = NULL; + unsigned int *struct_decl_linenos = NULL; + unsigned int struct_decl_linecount = 0; + for (const char *line = declarations; line < declarations_end; ) + { + const char *line_end; + line_end = (const char *) memchr (line, '\n', declarations_end - line); + if (line_end != NULL) + line_end++; else - break; - size_t struct_tag_length = p - struct_decl; - char *struct_tag = new char[struct_tag_length + 1]; - memcpy (struct_tag, struct_decl, struct_tag_length); - struct_tag[struct_tag_length] = '\0'; - _struct_tag = struct_tag; - /* The return type of the lookup function is "struct something *". - No "const" here, because if !option[CONST], some user code might - want to modify the structure. */ - char *return_type = new char[struct_tag_length + 3]; - memcpy (return_type, struct_decl, struct_tag_length); - return_type[struct_tag_length] = ' '; - return_type[struct_tag_length + 1] = '*'; - return_type[struct_tag_length + 2] = '\0'; - _return_type = return_type; - } + line_end = declarations_end; - if (struct_decl_linenos) - delete[] struct_decl_linenos; - } + if (*line == '%') + { + if (line[1] == '{') + { + /* Handle %{. */ + if (_verbatim_declarations != NULL) + { + fprintf (stderr, "%s:%u:\n%s:%u:" + " only one %%{...%%} section is allowed\n", + pretty_input_file_name (), + _verbatim_declarations_lineno, + pretty_input_file_name (), lineno); + exit (1); + } + _verbatim_declarations = line + 2; + _verbatim_declarations_lineno = lineno; + } + else if (line[1] == '}') + { + /* Handle %}. */ + if (_verbatim_declarations == NULL) + { + fprintf (stderr, "%s:%u:" + " %%} outside of %%{...%%} section\n", + pretty_input_file_name (), lineno); + exit (1); + } + if (_verbatim_declarations_end != NULL) + { + fprintf (stderr, "%s:%u:" + " %%{...%%} section already closed\n", + pretty_input_file_name (), lineno); + exit (1); + } + _verbatim_declarations_end = line; + /* Give a warning if the rest of the line is nonempty. */ + bool nonempty_line = false; + const char *q; + for (q = line + 2; q < line_end; q++) + { + if (*q == '\n') + { + q++; + break; + } + if (!(*q == ' ' || *q == '\t')) + nonempty_line = true; + } + if (nonempty_line) + fprintf (stderr, "%s:%u:" + " warning: junk after %%} is ignored\n", + pretty_input_file_name (), lineno); + } + else if (_verbatim_declarations != NULL + && _verbatim_declarations_end == NULL) + { + fprintf (stderr, "%s:%u:" + " warning: %% directives are ignored" + " inside the %%{...%%} section\n", + pretty_input_file_name (), lineno); + } + else + { + char *arg; - /* Parse the keywords section. */ - { - Keyword_List **list_tail = &_head; - const char *delimiters = option.get_delimiters (); - unsigned int lineno = keywords_lineno; - bool charset_dependent = false; - for (const char *line = keywords; line < keywords_end; ) - { - const char *line_end; - line_end = (const char *) memchr (line, '\n', keywords_end - line); - if (line_end != NULL) - line_end++; - else - line_end = keywords_end; + if (is_declaration_with_arg (line, line_end, lineno, + "delimiters", &arg)) + option.set_delimiters (arg); + else - if (line[0] == '#') - ; /* Comment line. */ - else if (line[0] == '%') - { - fprintf (stderr, "%s:%u:" - " declarations are not allowed in the keywords section.\n" - "To declare a keyword starting with %%, enclose it in" - " double-quotes.\n", - pretty_input_file_name (), lineno); - exit (1); - } - else - { - /* An input line carrying a keyword. */ - const char *keyword; - size_t keyword_length; - const char *rest; + if (is_declaration (line, line_end, lineno, "struct-type")) + option.set (TYPE); + else - if (line[0] == '"') + if (is_declaration (line, line_end, lineno, "ignore-case")) + option.set (UPPERLOWER); + else + + if (is_declaration_with_arg (line, line_end, lineno, + "language", &arg)) + option.set_language (arg); + else + + if (is_define_declaration (line, line_end, lineno, + "slot-name", &arg)) + option.set_slot_name (arg); + else + + if (is_define_declaration (line, line_end, lineno, + "initializer-suffix", &arg)) + option.set_initializer_suffix (arg); + else + + if (is_define_declaration (line, line_end, lineno, + "hash-function-name", &arg)) + option.set_hash_name (arg); + else + + if (is_define_declaration (line, line_end, lineno, + "lookup-function-name", &arg)) + option.set_function_name (arg); + else + + if (is_define_declaration (line, line_end, lineno, + "class-name", &arg)) + option.set_class_name (arg); + else + + if (is_declaration (line, line_end, lineno, "7bit")) + option.set (SEVENBIT); + else + + if (is_declaration (line, line_end, lineno, "compare-lengths")) + option.set (LENTABLE); + else + + if (is_declaration (line, line_end, lineno, "compare-strncmp")) + option.set (COMP); + else + + if (is_declaration (line, line_end, lineno, "readonly-tables")) + option.set (CONST); + else + + if (is_declaration (line, line_end, lineno, "enum")) + option.set (ENUM); + else + + if (is_declaration (line, line_end, lineno, "includes")) + option.set (INCLUDE); + else + + if (is_declaration (line, line_end, lineno, "global-table")) + option.set (GLOBAL); + else + + if (is_declaration (line, line_end, lineno, "pic")) + option.set (SHAREDLIB); + else + + if (is_define_declaration (line, line_end, lineno, + "string-pool-name", &arg)) + option.set_stringpool_name (arg); + else + + if (is_declaration (line, line_end, lineno, "null-strings")) + option.set (NULLSTRINGS); + else + + if (is_define_declaration (line, line_end, lineno, + "constants-prefix", &arg)) + option.set_constants_prefix (arg); + else + + if (is_define_declaration (line, line_end, lineno, + "word-array-name", &arg)) + option.set_wordlist_name (arg); + else + + if (is_define_declaration (line, line_end, lineno, + "length-table-name", &arg)) + option.set_lengthtable_name (arg); + else + + if (is_declaration_with_arg (line, line_end, lineno, + "switch", &arg)) + { + option.set_total_switches (atoi (arg)); + if (option.get_total_switches () <= 0) + { + fprintf (stderr, "%s:%u: number of switches %s" + " must be a positive number\n", + pretty_input_file_name (), lineno, arg); + exit (1); + } + } + else + + if (is_declaration (line, line_end, lineno, "omit-struct-type")) + option.set (NOTYPE); + else + + { + fprintf (stderr, "%s:%u: unrecognized %% directive\n", + pretty_input_file_name (), lineno); + exit (1); + } + } + } + else if (!(_verbatim_declarations != NULL + && _verbatim_declarations_end == NULL)) + { + /* Append the line to struct_decl. */ + size_t old_len = (struct_decl ? strlen (struct_decl) : 0); + size_t line_len = line_end - line; + size_t new_len = old_len + line_len + 1; + char *new_struct_decl = new char[new_len]; + if (old_len > 0) + memcpy (new_struct_decl, struct_decl, old_len); + memcpy (new_struct_decl + old_len, line, line_len); + new_struct_decl[old_len + line_len] = '\0'; + if (struct_decl) + delete[] struct_decl; + struct_decl = new_struct_decl; + /* Append the lineno to struct_decl_linenos. */ + unsigned int *new_struct_decl_linenos = + new unsigned int[struct_decl_linecount + 1]; + if (struct_decl_linecount > 0) + memcpy (new_struct_decl_linenos, struct_decl_linenos, + struct_decl_linecount * sizeof (unsigned int)); + new_struct_decl_linenos[struct_decl_linecount] = lineno; + if (struct_decl_linenos) + delete[] struct_decl_linenos; + struct_decl_linenos = new_struct_decl_linenos; + /* Increment struct_decl_linecount. */ + struct_decl_linecount++; + } + lineno++; + line = line_end; + } + if (_verbatim_declarations != NULL && _verbatim_declarations_end == NULL) + { + fprintf (stderr, "%s:%u: unterminated %%{ section\n", + pretty_input_file_name (), _verbatim_declarations_lineno); + exit (1); + } + + /* Determine _struct_decl, _return_type, _struct_tag. */ + if (option[TYPE]) + { + if (struct_decl) + { + /* Drop leading whitespace and comments. */ { - /* Parse a string in ANSI C syntax. */ - char *kp = new char[line_end-line]; - keyword = kp; - const char *lp = line + 1; - + char *p = struct_decl; + unsigned int *l = struct_decl_linenos; for (;;) { - if (lp == line_end) + if (p[0] == ' ' || p[0] == '\t') { - fprintf (stderr, "%s:%u: unterminated string\n", - pretty_input_file_name (), lineno); - exit (1); + p++; + continue; } - - char c = *lp; - if (c == '\\') + if (p[0] == '\n') { - c = *++lp; - switch (c) + l++; + p++; + continue; + } + if (p[0] == '/') + { + if (p[1] == '*') { - case '0': case '1': case '2': case '3': - case '4': case '5': case '6': case '7': - { - int code = 0; - int count = 0; - while (count < 3 && *lp >= '0' && *lp <= '7') - { - code = (code << 3) + (*lp - '0'); - lp++; - count++; - } - if (code > UCHAR_MAX) - fprintf (stderr, - "%s:%u: octal escape out of range\n", - pretty_input_file_name (), lineno); - *kp = static_cast(code); - break; - } - case 'x': - { - int code = 0; - int count = 0; - lp++; - while ((*lp >= '0' && *lp <= '9') - || (*lp >= 'A' && *lp <= 'F') - || (*lp >= 'a' && *lp <= 'f')) - { - code = (code << 4) - + (*lp >= 'A' && *lp <= 'F' - ? *lp - 'A' + 10 : - *lp >= 'a' && *lp <= 'f' - ? *lp - 'a' + 10 : - *lp - '0'); - lp++; - count++; - } - if (count == 0) - fprintf (stderr, "%s:%u: hexadecimal escape" - " without any hex digits\n", - pretty_input_file_name (), lineno); - if (code > UCHAR_MAX) - fprintf (stderr, "%s:%u: hexadecimal escape" - " out of range\n", - pretty_input_file_name (), lineno); - *kp = static_cast(code); - break; - } - case '\\': case '\'': case '"': - *kp = c; - lp++; - charset_dependent = true; - break; - case 'n': - *kp = '\n'; - lp++; - charset_dependent = true; - break; - case 't': - *kp = '\t'; - lp++; - charset_dependent = true; - break; - case 'r': - *kp = '\r'; - lp++; - charset_dependent = true; - break; - case 'f': - *kp = '\f'; - lp++; - charset_dependent = true; - break; - case 'b': - *kp = '\b'; - lp++; - charset_dependent = true; - break; - case 'a': - *kp = '\a'; - lp++; - charset_dependent = true; - break; - case 'v': - *kp = '\v'; - lp++; - charset_dependent = true; - break; - default: - fprintf (stderr, "%s:%u: invalid escape sequence" - " in string\n", - pretty_input_file_name (), lineno); - exit (1); + /* Skip over ANSI C style comment. */ + p += 2; + while (p[0] != '\0') + { + if (p[0] == '*' && p[1] == '/') + { + p += 2; + break; + } + if (p[0] == '\n') + l++; + p++; + } + continue; + } + if (p[1] == '/') + { + /* Skip over ISO C99 or C++ style comment. */ + p += 2; + while (p[0] != '\0' && p[0] != '\n') + p++; + if (p[0] == '\n') + { + l++; + p++; + } + continue; } } - else if (c == '"') - break; - else - { - *kp = c; - lp++; - charset_dependent = true; - } - kp++; + break; } - lp++; - if (lp < line_end && *lp != '\n') + if (p != struct_decl) { - if (strchr (delimiters, *lp) == NULL) - { - fprintf (stderr, "%s:%u: string not followed" - " by delimiter\n", - pretty_input_file_name (), lineno); - exit (1); - } - lp++; - } - keyword_length = kp - keyword; - if (option[TYPE]) - { - char *line_rest = new char[line_end - lp + 1]; - memcpy (line_rest, lp, line_end - lp); - line_rest[line_end - lp - - (line_end > lp && line_end[-1] == '\n' ? 1 : 0)] - = '\0'; - rest = line_rest; + size_t len = strlen (p); + char *new_struct_decl = new char[len + 1]; + memcpy (new_struct_decl, p, len + 1); + delete[] struct_decl; + struct_decl = new_struct_decl; } + _struct_decl_lineno = *l; + } + /* Drop trailing whitespace. */ + for (char *p = struct_decl + strlen (struct_decl); p > struct_decl;) + if (p[-1] == '\n' || p[-1] == ' ' || p[-1] == '\t') + *--p = '\0'; else - rest = empty_string; - } - else + break; + } + if (struct_decl == NULL || struct_decl[0] == '\0') + { + fprintf (stderr, "%s: missing struct declaration" + " for option --struct-type\n", + pretty_input_file_name ()); + exit (1); + } + { + /* Ensure trailing semicolon. */ + size_t old_len = strlen (struct_decl); + if (struct_decl[old_len - 1] != ';') { - /* Not a string. Look for the delimiter. */ - const char *lp = line; - for (;;) - { - if (!(lp < line_end && *lp != '\n')) - { - keyword = line; - keyword_length = lp - line; - rest = empty_string; - break; - } - if (strchr (delimiters, *lp) != NULL) - { - keyword = line; - keyword_length = lp - line; - lp++; - if (option[TYPE]) - { - char *line_rest = new char[line_end - lp + 1]; - memcpy (line_rest, lp, line_end - lp); - line_rest[line_end - lp - - (line_end > lp && line_end[-1] == '\n' - ? 1 : 0)] - = '\0'; - rest = line_rest; - } - else - rest = empty_string; - break; - } - lp++; - } - if (keyword_length > 0) - charset_dependent = true; + char *new_struct_decl = new char[old_len + 2]; + memcpy (new_struct_decl, struct_decl, old_len); + new_struct_decl[old_len] = ';'; + new_struct_decl[old_len + 1] = '\0'; + delete[] struct_decl; + struct_decl = new_struct_decl; } - - /* Allocate Keyword and add it to the list. */ - Keyword *new_kw = _factory->create_keyword (keyword, keyword_length, - rest, lineno); - *list_tail = new Keyword_List (new_kw); - list_tail = &(*list_tail)->rest(); } + /* Set _struct_decl to the entire declaration. */ + _struct_decl = struct_decl; + /* Set _struct_tag to the naked "struct something". */ + const char *p; + for (p = struct_decl; *p && *p != '{' && *p != ';' && *p != '\n'; p++) + ; + for (; p > struct_decl;) + if (p[-1] == '\n' || p[-1] == ' ' || p[-1] == '\t') + --p; + else + break; + size_t struct_tag_length = p - struct_decl; + char *struct_tag = new char[struct_tag_length + 1]; + memcpy (struct_tag, struct_decl, struct_tag_length); + struct_tag[struct_tag_length] = '\0'; + _struct_tag = struct_tag; + /* The return type of the lookup function is "struct something *". + No "const" here, because if !option[CONST], some user code might + want to modify the structure. */ + char *return_type = new char[struct_tag_length + 3]; + memcpy (return_type, struct_decl, struct_tag_length); + return_type[struct_tag_length] = ' '; + return_type[struct_tag_length + 1] = '*'; + return_type[struct_tag_length + 2] = '\0'; + _return_type = return_type; + } - lineno++; - line = line_end; - } - *list_tail = NULL; + if (struct_decl_linenos) + delete[] struct_decl_linenos; + } - if (_head == NULL) - { - fprintf (stderr, "%s: No keywords in input file!\n", - pretty_input_file_name ()); - exit (1); - } + /* Parse the keywords section. */ + { + Keyword_List **list_tail = &_head; + const char *delimiters = option.get_delimiters (); + unsigned int lineno = keywords_lineno; + bool charset_dependent = false; + for (const char *line = keywords; line < keywords_end; ) + { + const char *line_end; + line_end = (const char *) memchr (line, '\n', keywords_end - line); + if (line_end != NULL) + line_end++; + else + line_end = keywords_end; - _charset_dependent = charset_dependent; + if (line[0] == '#') + ; /* Comment line. */ + else if (line[0] == '%') + { + fprintf (stderr, "%s:%u:" + " declarations are not allowed in the keywords section.\n" + "To declare a keyword starting with %%, enclose it in" + " double-quotes.\n", + pretty_input_file_name (), lineno); + exit (1); + } + else + { + /* An input line carrying a keyword. */ + const char *keyword; + size_t keyword_length; + const char *rest; + + if (line[0] == '"') + { + /* Parse a string in ANSI C syntax. */ + char *kp = new char[line_end-line]; + keyword = kp; + const char *lp = line + 1; + + for (;;) + { + if (lp == line_end) + { + fprintf (stderr, "%s:%u: unterminated string\n", + pretty_input_file_name (), lineno); + exit (1); + } + + char c = *lp; + if (c == '\\') + { + c = *++lp; + switch (c) + { + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + { + int code = 0; + int count = 0; + while (count < 3 && *lp >= '0' && *lp <= '7') + { + code = (code << 3) + (*lp - '0'); + lp++; + count++; + } + if (code > UCHAR_MAX) + fprintf (stderr, + "%s:%u: octal escape out of range\n", + pretty_input_file_name (), lineno); + *kp = static_cast(code); + break; + } + case 'x': + { + int code = 0; + int count = 0; + lp++; + while ((*lp >= '0' && *lp <= '9') + || (*lp >= 'A' && *lp <= 'F') + || (*lp >= 'a' && *lp <= 'f')) + { + code = (code << 4) + + (*lp >= 'A' && *lp <= 'F' + ? *lp - 'A' + 10 : + *lp >= 'a' && *lp <= 'f' + ? *lp - 'a' + 10 : + *lp - '0'); + lp++; + count++; + } + if (count == 0) + fprintf (stderr, "%s:%u: hexadecimal escape" + " without any hex digits\n", + pretty_input_file_name (), lineno); + if (code > UCHAR_MAX) + fprintf (stderr, "%s:%u: hexadecimal escape" + " out of range\n", + pretty_input_file_name (), lineno); + *kp = static_cast(code); + break; + } + case '\\': case '\'': case '"': + *kp = c; + lp++; + charset_dependent = true; + break; + case 'n': + *kp = '\n'; + lp++; + charset_dependent = true; + break; + case 't': + *kp = '\t'; + lp++; + charset_dependent = true; + break; + case 'r': + *kp = '\r'; + lp++; + charset_dependent = true; + break; + case 'f': + *kp = '\f'; + lp++; + charset_dependent = true; + break; + case 'b': + *kp = '\b'; + lp++; + charset_dependent = true; + break; + case 'a': + *kp = '\a'; + lp++; + charset_dependent = true; + break; + case 'v': + *kp = '\v'; + lp++; + charset_dependent = true; + break; + default: + fprintf (stderr, "%s:%u: invalid escape sequence" + " in string\n", + pretty_input_file_name (), lineno); + exit (1); + } + } + else if (c == '"') + break; + else + { + *kp = c; + lp++; + charset_dependent = true; + } + kp++; + } + lp++; + if (lp < line_end && *lp != '\n') + { + if (strchr (delimiters, *lp) == NULL) + { + fprintf (stderr, "%s:%u: string not followed" + " by delimiter\n", + pretty_input_file_name (), lineno); + exit (1); + } + lp++; + } + keyword_length = kp - keyword; + if (option[TYPE]) + { + char *line_rest = new char[line_end - lp + 1]; + memcpy (line_rest, lp, line_end - lp); + line_rest[line_end - lp - + (line_end > lp && line_end[-1] == '\n' ? 1 : 0)] + = '\0'; + rest = line_rest; + } + else + rest = empty_string; + } + else + { + /* Not a string. Look for the delimiter. */ + const char *lp = line; + for (;;) + { + if (!(lp < line_end && *lp != '\n')) + { + keyword = line; + keyword_length = lp - line; + rest = empty_string; + break; + } + if (strchr (delimiters, *lp) != NULL) + { + keyword = line; + keyword_length = lp - line; + lp++; + if (option[TYPE]) + { + char *line_rest = new char[line_end - lp + 1]; + memcpy (line_rest, lp, line_end - lp); + line_rest[line_end - lp - + (line_end > lp && line_end[-1] == '\n' + ? 1 : 0)] + = '\0'; + rest = line_rest; + } + else + rest = empty_string; + break; + } + lp++; + } + if (keyword_length > 0) + charset_dependent = true; + } + + /* Allocate Keyword and add it to the list. */ + KT *new_kw = _factory->create_keyword (keyword, keyword_length, + rest, lineno); + *list_tail = new Keyword_List (new_kw); + list_tail = &(*list_tail)->rest(); + } + + lineno++; + line = line_end; + } + *list_tail = NULL; + + if (_head == NULL) + { + fprintf (stderr, "%s: No keywords in input file!\n", + pretty_input_file_name ()); + exit (1); + } + + _charset_dependent = charset_dependent; + } + + /* To be freed in the destructor. */ + _input = input; + _input_end = input_end; } - /* To be freed in the destructor. */ - _input = input; - _input_end = input_end; -} +template + Input::~Input () + { + /* Free allocated memory. */ + delete[] const_cast(_return_type); + delete[] const_cast(_struct_tag); + delete[] const_cast(_struct_decl); + delete[] _input; + } -Input::~Input () -{ - /* Free allocated memory. */ - delete[] const_cast(_return_type); - delete[] const_cast(_struct_tag); - delete[] const_cast(_struct_decl); - delete[] _input; -} +/* ------------------------------------------------------------------------- */ + +/* Explicit template instantiations. Needed to avoid link-time errors. + + C++ is just misdesigned: The most important aspect in building large + software packages is information hiding. (That's the point of having the + implementation of a .h file in a .cc file, isn't it? And of having + classes with private fields and methods, isn't it?) The fact that we + need the instantiation of the Input class only for KT = KeywordExt + comes from the code in main.cc. It is ugly that implementation details + of main.cc have an influence into this file here. */ + +template class Input; + +/* ------------------------------------------------------------------------- */ diff --git a/src/input.h b/src/input.h index 90ff01a..bf5cac4 100644 --- a/src/input.h +++ b/src/input.h @@ -2,7 +2,7 @@ /* Input routines. - Copyright (C) 1989-1998, 2002-2003 Free Software Foundation, Inc. + Copyright (C) 1989-1998, 2002-2003, 2025 Free Software Foundation, Inc. Written by Douglas C. Schmidt and Bruno Haible . @@ -27,41 +27,42 @@ #include #include "keyword-list.h" -class Input -{ -public: - Input (FILE *stream, Keyword_Factory *keyword_factory); - ~Input (); - void read_input (); -private: - /* Input stream. */ - FILE * _stream; - /* Creates the keywords. */ - Keyword_Factory * const _factory; -public: - /* Memory block containing the entire input. */ - char * _input; - char * _input_end; - /* The C code from the declarations section. */ - const char * _verbatim_declarations; - const char * _verbatim_declarations_end; - unsigned int _verbatim_declarations_lineno; - /* The C code from the end of the file. */ - const char * _verbatim_code; - const char * _verbatim_code_end; - unsigned int _verbatim_code_lineno; - /* Declaration of struct type for a keyword and its attributes. */ - const char * _struct_decl; - unsigned int _struct_decl_lineno; - /* Return type of the lookup function. */ - const char * _return_type; - /* Shorthand for user-defined struct tag type. */ - const char * _struct_tag; - /* List of all keywords. */ - Keyword_List * _head; - /* Whether the keyword chars would have different values in a different - character set. */ - bool _charset_dependent; -}; +template + class Input + { + public: + Input (FILE *stream, Keyword_Factory *keyword_factory); + ~Input (); + void read_input (); + private: + /* Input stream. */ + FILE * _stream; + /* Creates the keywords. */ + Keyword_Factory * const _factory; + public: + /* Memory block containing the entire input. */ + char * _input; + char * _input_end; + /* The C code from the declarations section. */ + const char * _verbatim_declarations; + const char * _verbatim_declarations_end; + unsigned int _verbatim_declarations_lineno; + /* The C code from the end of the file. */ + const char * _verbatim_code; + const char * _verbatim_code_end; + unsigned int _verbatim_code_lineno; + /* Declaration of struct type for a keyword and its attributes. */ + const char * _struct_decl; + unsigned int _struct_decl_lineno; + /* Return type of the lookup function. */ + const char * _return_type; + /* Shorthand for user-defined struct tag type. */ + const char * _struct_tag; + /* List of all keywords. */ + Keyword_List * _head; + /* Whether the keyword chars would have different values in a different + character set. */ + bool _charset_dependent; + }; #endif diff --git a/src/keyword-list.cc b/src/keyword-list.cc index 3798c3e..5ab8ba4 100644 --- a/src/keyword-list.cc +++ b/src/keyword-list.cc @@ -1,6 +1,6 @@ /* Keyword list. - Copyright (C) 2002-2003 Free Software Foundation, Inc. + Copyright (C) 2002-2003, 2025 Free Software Foundation, Inc. Written by Bruno Haible . This file is part of GNU GPERF. @@ -23,145 +23,130 @@ #include -/* -------------------------- Keyword_List class --------------------------- */ +/* ------------------------ Keyword_List class ------------------------- */ /* Constructor. */ -Keyword_List::Keyword_List (Keyword *car) - : _cdr (NULL), _car (car) -{ -} +template + Keyword_List::Keyword_List (KT *car) + : _cdr (NULL), _car (car) + { + } -/* ------------------------- KeywordExt_List class ------------------------- */ - -/* Constructor. */ -KeywordExt_List::KeywordExt_List (KeywordExt *car) - : Keyword_List (car) -{ -} - -/* ------------------------ Keyword_List functions ------------------------- */ +/* ---------------------- Keyword_List functions ----------------------- */ /* Copies a linear list, sharing the list elements. */ -Keyword_List * -copy_list (Keyword_List *list) -{ - Keyword_List *result; - Keyword_List **lastp = &result; - while (list != NULL) - { - Keyword_List *new_cons = new Keyword_List (list->first()); - *lastp = new_cons; - lastp = &new_cons->rest(); - list = list->rest(); - } - *lastp = NULL; - return result; -} - -/* Copies a linear list, sharing the list elements. */ -KeywordExt_List * -copy_list (KeywordExt_List *list) -{ - return static_cast (copy_list (static_cast (list))); -} +template + Keyword_List * + copy_list (Keyword_List *list) + { + Keyword_List *result; + Keyword_List **lastp = &result; + while (list != NULL) + { + Keyword_List *new_cons = new Keyword_List (list->first()); + *lastp = new_cons; + lastp = &new_cons->rest(); + list = list->rest(); + } + *lastp = NULL; + return result; + } /* Deletes a linear list, keeping the list elements in memory. */ -void -delete_list (Keyword_List *list) -{ - while (list != NULL) - { - Keyword_List *rest = list->rest(); - delete list; - list = rest; - } -} +template + void + delete_list (Keyword_List *list) + { + while (list != NULL) + { + Keyword_List *rest = list->rest(); + delete list; + list = rest; + } + } /* Type of a comparison function. */ -typedef bool (*Keyword_Comparison) (Keyword *keyword1, Keyword *keyword2); +#if 0 /* wishful thinking */ +template + typedef bool (*Keyword_Comparison) (KT *keyword1, KT *keyword2); +#endif /* Merges two sorted lists together to form one sorted list. */ -static Keyword_List * -merge (Keyword_List *list1, Keyword_List *list2, Keyword_Comparison less) -{ - Keyword_List *result; - Keyword_List **resultp = &result; - for (;;) - { - if (!list1) - { - *resultp = list2; - break; - } - if (!list2) - { - *resultp = list1; - break; - } - if (less (list2->first(), list1->first())) - { - *resultp = list2; - resultp = &list2->rest(); - /* We would have a stable sorting if the next line would read: - list2 = *resultp; */ - list2 = list1; list1 = *resultp; - } - else - { - *resultp = list1; - resultp = &list1->rest(); - list1 = *resultp; - } - } - return result; -} +template + static Keyword_List * + merge (Keyword_List *list1, Keyword_List *list2, + bool (*less) (KT *keyword1, KT *keyword2)) + { + Keyword_List *result; + Keyword_List **resultp = &result; + for (;;) + { + if (!list1) + { + *resultp = list2; + break; + } + if (!list2) + { + *resultp = list1; + break; + } + if (less (list2->first(), list1->first())) + { + *resultp = list2; + resultp = &list2->rest(); + /* We would have a stable sorting if the next line would read: + list2 = *resultp; */ + list2 = list1; list1 = *resultp; + } + else + { + *resultp = list1; + resultp = &list1->rest(); + list1 = *resultp; + } + } + return result; + } /* Sorts a linear list, given a comparison function. Note: This uses a variant of mergesort that is *not* a stable sorting algorithm. */ -Keyword_List * -mergesort_list (Keyword_List *list, Keyword_Comparison less) -{ - if (list == NULL || list->rest() == NULL) - /* List of length 0 or 1. Nothing to do. */ - return list; - else - { - /* Determine a list node in the middle. */ - Keyword_List *middle = list; - for (Keyword_List *temp = list->rest();;) - { - temp = temp->rest(); - if (temp == NULL) - break; - temp = temp->rest(); - middle = middle->rest(); - if (temp == NULL) - break; - } +template + Keyword_List * + mergesort_list (Keyword_List *list, + bool (*less) (KT *keyword1, KT *keyword2)) + { + if (list == NULL || list->rest() == NULL) + /* List of length 0 or 1. Nothing to do. */ + return list; + else + { + /* Determine a list node in the middle. */ + Keyword_List *middle = list; + for (Keyword_List *temp = list->rest();;) + { + temp = temp->rest(); + if (temp == NULL) + break; + temp = temp->rest(); + middle = middle->rest(); + if (temp == NULL) + break; + } - /* Cut the list into two halves. - If the list has n elements, the left half has ceiling(n/2) elements - and the right half has floor(n/2) elements. */ - Keyword_List *right_half = middle->rest(); - middle->rest() = NULL; + /* Cut the list into two halves. + If the list has n elements, the left half has ceiling(n/2) elements + and the right half has floor(n/2) elements. */ + Keyword_List *right_half = middle->rest(); + middle->rest() = NULL; - /* Sort the two halves, then merge them. */ - return merge (mergesort_list (list, less), - mergesort_list (right_half, less), - less); - } -} - -KeywordExt_List * -mergesort_list (KeywordExt_List *list, - bool (*less) (KeywordExt *keyword1, KeywordExt *keyword2)) -{ - return - static_cast - (mergesort_list (static_cast (list), - reinterpret_cast (less))); -} + /* Sort the two halves, then merge them. */ + return merge (mergesort_list (list, less), + mergesort_list (right_half, less), + less); + } + } #ifndef __OPTIMIZE__ @@ -171,3 +156,27 @@ mergesort_list (KeywordExt_List *list, #undef INLINE #endif /* not defined __OPTIMIZE__ */ + +/* ------------------------------------------------------------------------- */ + +/* Explicit template instantiations. Needed to avoid link-time errors. + + C++ is just misdesigned: The most important aspect in building large + software packages is information hiding. (That's the point of having the + implementation of a .h file in a .cc file, isn't it? And of having + classes with private fields and methods, isn't it?) The fact that we + need the instantiation of the Keyword_List class and associate functions + only for KT = KeywordExt comes from the code in main.cc. It is ugly that + implementation details of main.cc have an influence into this file here. */ + +template class Keyword_List; +template Keyword_List * + copy_list (Keyword_List *list); +template void + delete_list (Keyword_List *list); +template Keyword_List * + mergesort_list (Keyword_List *list, + bool (*less) (KeywordExt *keyword1, + KeywordExt *keyword2)); + +/* ------------------------------------------------------------------------- */ diff --git a/src/keyword-list.h b/src/keyword-list.h index e8abeaa..4e9aea9 100644 --- a/src/keyword-list.h +++ b/src/keyword-list.h @@ -2,7 +2,7 @@ /* Keyword list. - Copyright (C) 2002-2003 Free Software Foundation, Inc. + Copyright (C) 2002-2003, 2025 Free Software Foundation, Inc. Written by Bruno Haible . This file is part of GNU GPERF. @@ -25,52 +25,42 @@ #include "keyword.h" -/* List node of a linear list of Keyword. */ -class Keyword_List -{ -public: - /* Constructor. */ - Keyword_List (Keyword *car); +/* List node of a linear list of KT, where KT is a subclass of Keyword. */ +template + class Keyword_List + { + public: + /* Constructor. */ + Keyword_List (KT *car); - /* Access to first element of list. */ - Keyword * first () const; - /* Access to next element of list. */ - Keyword_List *& rest (); + /* Access to first element of list. */ + KT * first () const; + /* Access to next element of list. */ + Keyword_List *& rest (); -protected: - Keyword_List * _cdr; - Keyword * const _car; -}; + protected: + Keyword_List * _cdr; + KT * const _car; + }; /* List node of a linear list of KeywordExt. */ -class KeywordExt_List : public Keyword_List -{ -public: - /* Constructor. */ - KeywordExt_List (KeywordExt *car); - - /* Access to first element of list. */ - KeywordExt * first () const; - /* Access to next element of list. */ - KeywordExt_List *& rest (); -}; +typedef Keyword_List KeywordExt_List; /* Copies a linear list, sharing the list elements. */ -extern Keyword_List * copy_list (Keyword_List *list); -extern KeywordExt_List * copy_list (KeywordExt_List *list); +template + extern Keyword_List * copy_list (Keyword_List *list); /* Deletes a linear list, keeping the list elements in memory. */ -extern void delete_list (Keyword_List *list); +template + extern void delete_list (Keyword_List *list); /* Sorts a linear list, given a comparison function. Note: This uses a variant of mergesort that is *not* a stable sorting algorithm. */ -extern Keyword_List * mergesort_list (Keyword_List *list, - bool (*less) (Keyword *keyword1, - Keyword *keyword2)); -extern KeywordExt_List * mergesort_list (KeywordExt_List *list, - bool (*less) (KeywordExt *keyword1, - KeywordExt *keyword2)); +template + extern Keyword_List * mergesort_list (Keyword_List *list, + bool (*less) (KT *keyword1, + KT *keyword2)); #ifdef __OPTIMIZE__ diff --git a/src/keyword-list.icc b/src/keyword-list.icc index bebe2eb..f2dc023 100644 --- a/src/keyword-list.icc +++ b/src/keyword-list.icc @@ -1,6 +1,6 @@ /* Inline Functions for keyword-list.{h,cc}. - Copyright (C) 2002-2003 Free Software Foundation, Inc. + Copyright (C) 2002-2003, 2025 Free Software Foundation, Inc. Written by Bruno Haible . This file is part of GNU GPERF. @@ -18,34 +18,20 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ -/* -------------------------- Keyword_List class --------------------------- */ +/* ------------------------ Keyword_List class ------------------------- */ /* Access to first element of list. */ -INLINE Keyword * -Keyword_List::first () const -{ - return _car; -} +template + INLINE KT * + Keyword_List::first () const + { + return _car; + } /* Access to next element of list. */ -INLINE Keyword_List *& -Keyword_List::rest () -{ - return _cdr; -} - -/* ------------------------- KeywordExt_List class ------------------------- */ - -/* Access to first element of list. */ -INLINE KeywordExt * -KeywordExt_List::first () const -{ - return static_cast(_car); -} - -/* Access to next element of list. */ -INLINE KeywordExt_List *& -KeywordExt_List::rest () -{ - return *reinterpret_cast(&_cdr); -} +template + INLINE Keyword_List *& + Keyword_List::rest () + { + return _cdr; + } diff --git a/src/keyword.cc b/src/keyword.cc index b81537d..6d188c6 100644 --- a/src/keyword.cc +++ b/src/keyword.cc @@ -1,5 +1,5 @@ /* Keyword data. - Copyright (C) 1989-1998, 2000, 2002-2003 Free Software Foundation, Inc. + Copyright (C) 1989-1998, 2000, 2002-2003, 2025 Free Software Foundation, Inc. Written by Douglas C. Schmidt and Bruno Haible . @@ -136,13 +136,15 @@ KeywordExt::delete_selchars () /* ------------------------- Keyword_Factory class ------------------------- */ -Keyword_Factory::Keyword_Factory () -{ -} +template + Keyword_Factory::Keyword_Factory () + { + } -Keyword_Factory::~Keyword_Factory () -{ -} +template + Keyword_Factory::~Keyword_Factory () + { + } /* ------------------------------------------------------------------------- */ @@ -157,3 +159,19 @@ char empty_string[1] = ""; #undef INLINE #endif /* not defined __OPTIMIZE__ */ + +/* ------------------------------------------------------------------------- */ + +/* Explicit template instantiations. Needed to avoid link-time errors. + + C++ is just misdesigned: The most important aspect in building large + software packages is information hiding. (That's the point of having the + implementation of a .h file in a .cc file, isn't it? And of having + classes with private fields and methods, isn't it?) The fact that we + need the instantiation of the Keyword_Factory class only for + KT = KeywordExt comes from the code in main.cc. It is ugly that + implementation details of main.cc have an influence into this file here. */ + +template class Keyword_Factory; + +/* ------------------------------------------------------------------------- */ diff --git a/src/keyword.h b/src/keyword.h index 79b9808..363784e 100644 --- a/src/keyword.h +++ b/src/keyword.h @@ -2,7 +2,7 @@ /* Keyword data. - Copyright (C) 1989-1998, 2000, 2002-2003, 2017 Free Software Foundation, Inc. + Copyright (C) 1989-1998, 2000, 2002-2003, 2017, 2025 Free Software Foundation, Inc. Written by Douglas C. Schmidt and Bruno Haible . @@ -86,19 +86,20 @@ private: This factory is used to make the Input class independent of the concrete class KeywordExt. */ -class Keyword_Factory -{ -public: - /* Constructor. */ - Keyword_Factory (); - /* Destructor. */ - virtual ~Keyword_Factory (); +template + class Keyword_Factory + { + public: + /* Constructor. */ + Keyword_Factory (); + /* Destructor. */ + virtual ~Keyword_Factory (); - /* Creates a new Keyword. */ - virtual /*abstract*/ Keyword * - create_keyword (const char *allchars, int allchars_length, - const char *rest, unsigned int lineno) = 0; -}; + /* Creates a new Keyword. */ + virtual /*abstract*/ KT * + create_keyword (const char *allchars, int allchars_length, + const char *rest, unsigned int lineno); + }; /* A statically allocated empty string. */ extern char empty_string[1]; diff --git a/src/main.cc b/src/main.cc index b7c6071..c02b147 100644 --- a/src/main.cc +++ b/src/main.cc @@ -1,5 +1,5 @@ /* Driver program for the hash function generator - Copyright (C) 1989-1998, 2000, 2002-2003, 2009, 2017 Free Software Foundation, Inc. + Copyright (C) 1989-1998, 2000, 2002-2003, 2009, 2017, 2025 Free Software Foundation, Inc. Written by Douglas C. Schmidt and Bruno Haible . @@ -31,15 +31,12 @@ /* This Keyword factory produces KeywordExt instances. */ -class KeywordExt_Factory : public Keyword_Factory -{ -virtual Keyword * create_keyword (const char *allchars, int allchars_length, - const char *rest, unsigned int lineno); -}; +typedef Keyword_Factory KeywordExt_Factory; -Keyword * -KeywordExt_Factory::create_keyword (const char *allchars, int allchars_length, - const char *rest, unsigned int lineno) +template <> +KeywordExt * +Keyword_Factory::create_keyword (const char *allchars, int allchars_length, + const char *rest, unsigned int lineno) { return new KeywordExt (allchars, allchars_length, rest, lineno); } @@ -66,11 +63,11 @@ main (int argc, char *argv[]) { /* Initialize the keyword list. */ KeywordExt_Factory factory; - Input inputter (stdin, &factory); + Input inputter (stdin, &factory); inputter.read_input (); /* We can cast the keyword list to KeywordExt_List* because its list elements were created by KeywordExt_Factory. */ - KeywordExt_List* list = static_cast(inputter._head); + KeywordExt_List* list = inputter._head; { /* Search for a good hash function. */