From e58c58977b070db560f226335f850022793167ab Mon Sep 17 00:00:00 2001 From: Jeff Squyres Date: Sun, 6 Apr 2025 15:54:35 -0400 Subject: [PATCH] show_help: static string improvements Fix warnings from older C compilers that do not like literal strings that are over 4095 characters line. Admittedly do this by hueristic: make each line in a show_help INI help file be its own string (rather than joining all the lines in a given [section] together). I.e., assume that each individual line is less than 4095 characters. Also make a few other improvements: * Remove an unused string in show_help.c * Fixed a problem where leading spaces were trimmed from the help text files in the resulting C strings. * Fixed a problem where blank lines were snipped from the help text files in the resulting C strings. * Simplify the Python generation script by statically initialzing one large struct named help_files (vs. initializing a struct per help file, and then initializing a 2nd struct to map the filenames to all the individual per-file structs). * Also use C99-style name-specified initializers, making the resulting code mode human-readable. * Also add comments at the end of each INI section and filename to make the resulting code more human readable. * Use C99-style "{0}" to mark empty / end-of-array sentinel values. * At run time, the individual lines of a given show_help section are opal_argv_join()'ed together and emitted. The alloc'ed string is cached (just in case it is used again -- which it likely won't be) and freed during opal_finalize(). The resulting generated static initialization code looks like this: ```c static file_entry help_files[] = { { .filename = "help-opal-wrapper.txt", .entries = (ini_entry[]) { { .section = "no-language-support", .content = (const char*[]) { "Unfortunately, this installation of Open MPI was not compiled with", "%s support. As such, the %s compiler is non-functional.", NULL }, }, // End of section no-language-support {0} }, // End of file help-opal-wrapper.txt }, {0} }; ``` Signed-off-by: Jeff Squyres --- opal/util/convert-help-files-to-c-code.py | 93 +++++++++++++++-------- opal/util/show_help.c | 4 +- opal/util/show_help.h | 8 ++ 3 files changed, 73 insertions(+), 32 deletions(-) diff --git a/opal/util/convert-help-files-to-c-code.py b/opal/util/convert-help-files-to-c-code.py index 38489481371..d8814d9fb1b 100755 --- a/opal/util/convert-help-files-to-c-code.py +++ b/opal/util/convert-help-files-to-c-code.py @@ -41,8 +41,8 @@ def parse_ini_files(file_paths, verbose=False): current_section = None with open(file_path) as file: for line in file: - line = line.strip() - if line.startswith('#') or not line: + line = line.rstrip() + if line.startswith('#'): continue if line.startswith('[') and line.endswith(']'): current_section = line[1:-1] @@ -60,18 +60,24 @@ def parse_ini_files(file_paths, verbose=False): def generate_c_code(parsed_data): # Generate C code with an array of filenames and their # corresponding INI sections. - c_code = f"""// THIS FILE IS GENERATED AUTOMATICALLY! EDITS WILL BE LOST! + c_code = [] + c_code.append(f"""// THIS FILE IS GENERATED AUTOMATICALLY! EDITS WILL BE LOST! // This file generated by {sys.argv[0]} -""" +""") # Rather than escaping the C code {} in f strings, make this a # separate (non-f-string) addition to c_code. - c_code += """#include + c_code.append("""#include #include +#include + +#include "opal/util/argv.h" +#include "opal/util/show_help.h" typedef struct { const char *section; - const char *content; + const char **content; + char *joined; } ini_entry; typedef struct { @@ -79,34 +85,44 @@ def generate_c_code(parsed_data): ini_entry *entries; } file_entry; -""" +static file_entry help_files[] = {""") ini_arrays = [] file_entries = [] - for idx, (filename, sections) in enumerate(parsed_data.items()): - var_name = filename.replace('-', '_').replace('.', '_') - - ini_entries = [] - for section, content_list in sections.items(): - content = '\n'.join(content_list) - c_content = content.replace('"','\\"').replace("\n", '\\n"\n"') - ini_entries.append(f' {{ "{section}", "{c_content}" }}') - ini_entries.append(f' {{ NULL, NULL }}') - - ini_array_name = f"ini_entries_{idx}" - ini_arrays.append(f"static ini_entry {ini_array_name}[] = {{\n" + ",\n".join(ini_entries) + "\n};\n") - file_entries.append(f' {{ "{filename}", {ini_array_name} }}') - file_entries.append(f' {{ NULL, NULL }}') - - c_code += "\n".join(ini_arrays) + "\n" - c_code += "static file_entry help_files[] = {\n" + ",\n".join(file_entries) + "\n};\n" - - c_code += """ - + sp4 = f'{" " * 4}' + sp8 = f'{" " * 8}' + sp12 = f'{" " * 12}' + sp16 = f'{" " * 16}' + sp20 = f'{" " * 20}' + + for filename, sections in parsed_data.items(): + c_code.append(f'{sp4}{{') + c_code.append(f'{sp8}.filename = "{filename}",') + c_code.append(f'{sp8}.entries = (ini_entry[]) {{') + + for section_name, lines in sections.items(): + c_code.append(f'{sp12}{{') + c_code.append(f'{sp16}.section = "{section_name}",') + c_code.append(f'{sp16}.content = (const char*[]) {{') + for line in lines: + c_string = line.replace('"','\\"').replace("\n", '\\n"\n"') + c_code.append(f'{sp20}"{c_string}",') + c_code.append(f'{sp20}NULL') + c_code.append(f'{sp16}}},') + c_code.append(f'{sp12}}}, // End of section {section_name}') + + c_code.append(f'{sp12}{{0}}') + c_code.append(f'{sp8}}}, // End of file {filename}') + c_code.append(f'{sp4}}},') + + c_code.append(f'{sp4}{{0}}') + c_code.append(f'}};') + + c_code.append(""" const char *opal_show_help_get_content(const char *filename, const char* topic) { - file_entry *fe; + const file_entry *fe; ini_entry *ie; for (int i = 0; help_files[i].filename != NULL; ++i) { @@ -115,7 +131,10 @@ def generate_c_code(parsed_data): for (int j = 0; fe->entries[j].section != NULL; ++j) { ie = &(fe->entries[j]); if (strcmp(ie->section, topic) == 0) { - return ie->content; + if (NULL == ie->joined) { + ie->joined = opal_argv_join((char**)ie->content, '\\n'); + } + return ie->joined; } } } @@ -123,9 +142,21 @@ def generate_c_code(parsed_data): return NULL; } -""" - return c_code +void opal_show_help_content_free(void) +{ + for (int i = 0; help_files[i].filename != NULL; ++i) { + for (int j = 0; help_files[i].entries[j].section != NULL; j++) { + if (NULL != help_files[i].entries[j].joined) { + free(help_files[i].entries[j].joined); + help_files[i].entries[j].joined = NULL; + } + } + } +} +""") + + return '\n'.join(c_code) #------------------------------- diff --git a/opal/util/show_help.c b/opal/util/show_help.c index 33b912a19fb..a4ab63ca614 100644 --- a/opal/util/show_help.c +++ b/opal/util/show_help.c @@ -43,7 +43,6 @@ /* * Private variables */ -static const char *default_filename = "help-messages"; static const char *dash_line = "--------------------------------------------------------------------------\n"; static int output_stream = -1; @@ -95,6 +94,9 @@ static void opal_show_help_finalize(void) opal_argv_free(search_dirs); search_dirs = NULL; } + + /* free the rendered help strings */ + opal_show_help_content_free(); } static void opal_show_help_output(const char *msg) { diff --git a/opal/util/show_help.h b/opal/util/show_help.h index c497dd235cf..1a24b42c79c 100644 --- a/opal/util/show_help.h +++ b/opal/util/show_help.h @@ -179,6 +179,14 @@ OPAL_DECLSPEC int opal_show_help_add_dir(const char *directory); */ const char *opal_show_help_get_content(const char *filename, const char* topic); +/** + * \internal + * + * Free up any strings that may have been allocated in when rendering + * show_help strings. + */ +void opal_show_help_content_free(void); + END_C_DECLS #endif