ascii-chat 0.11.33
Video chat in your terminal
Loading...
Searching...
No Matches
help.c File Reference

Help text generation and formatting for options. More...

Go to the source code of this file.

Functions

int options_config_calculate_max_col_width (const options_config_t *config)
 Calculate global max column width across all help sections.
 
void options_config_print_usage (const options_config_t *config, FILE *stream)
 Print usage/help text.
 
void options_config_print_usage_section (const options_config_t *config, FILE *stream)
 Print only the USAGE section.
 
void options_config_print_options_sections_with_width (const options_config_t *config, FILE *stream, int max_col_width, asciichat_mode_t mode)
 Print everything except the USAGE section.
 
void options_config_print_options_sections (const options_config_t *config, FILE *stream, asciichat_mode_t mode)
 Print everything except the USAGE section (backward compatibility wrapper)
 
void options_print_help_for_mode (const options_config_t *config, asciichat_mode_t mode, const char *program_name, const char *description, FILE *desc)
 Print help for a specific mode or binary level.
 
void options_struct_destroy (const options_config_t *config, void *options_struct)
 Clean up memory owned by options struct.
 

Detailed Description

Help text generation and formatting for options.

Implements all help/usage text generation functions including programmatic section printers and unified help output.

Author
Zachary Fogg me@zf.nosp@m.o.gg
Date
January 2026

Definition in file help.c.

Function Documentation

◆ options_config_calculate_max_col_width()

int options_config_calculate_max_col_width ( const options_config_t *  config)

Calculate global max column width across all help sections.

Calculate global max column width for help output alignment.

Calculates the maximum width needed for proper alignment across USAGE, EXAMPLES, OPTIONS, and MODES sections.

Definition at line 77 of file help.c.

77 {
78 if (!config)
79 return 0;
80
81 const char *binary_name = PLATFORM_BINARY_NAME;
82
83 int max_col_width = 0;
84 char temp_buf[BUFFER_SIZE_MEDIUM];
85
86 // Check USAGE entries (capped at 45 chars for max first column)
87 for (size_t i = 0; i < config->num_usage_lines; i++) {
88 const usage_descriptor_t *usage = &config->usage_lines[i];
89 int len = 0;
90
91 len += safe_snprintf(temp_buf + len, sizeof(temp_buf) - len, "%s", binary_name);
92
93 if (usage->mode) {
94 const char *colored_mode = colored_string(LOG_COLOR_FATAL, usage->mode);
95 len += safe_snprintf(temp_buf + len, sizeof(temp_buf) - len, " %s", colored_mode);
96 }
97
98 if (usage->positional) {
99 const char *colored_pos = colored_string(LOG_COLOR_INFO, usage->positional);
100 len += safe_snprintf(temp_buf + len, sizeof(temp_buf) - len, " %s", colored_pos);
101 }
102
103 if (usage->show_options) {
104 const char *options_text =
105 (usage->mode && strcmp(usage->mode, "<mode>") == 0) ? "[mode-options...]" : "[options...]";
106 const char *colored_opts = colored_string(LOG_COLOR_WARN, options_text);
107 len += safe_snprintf(temp_buf + len, sizeof(temp_buf) - len, " %s", colored_opts);
108 }
109
110 int w = utf8_display_width(temp_buf);
111 if (w > LAYOUT_COLUMN_WIDTH) {
113 }
114 if (w > max_col_width)
115 max_col_width = w;
116 }
117
118 // Check EXAMPLES entries
119 for (size_t i = 0; i < config->num_examples; i++) {
120 const example_descriptor_t *example = &config->examples[i];
121 int len = 0;
122
123 // Only prepend program name if this is not a utility command
124 if (!example->is_utility_command) {
125 len += safe_snprintf(temp_buf + len, sizeof(temp_buf) - len, "%s", binary_name);
126
127 // Programmatically add mode name based on mode_bitmask
128 const char *mode_name = get_mode_name_from_bitmask(example->mode_bitmask);
129 if (mode_name) {
130 len += safe_snprintf(temp_buf + len, sizeof(temp_buf) - len, " %s", mode_name);
131 }
132 }
133
134 if (example->args) {
135 const char *colored_args = colored_string(LOG_COLOR_INFO, example->args);
136 len += safe_snprintf(temp_buf + len, sizeof(temp_buf) - len, " %s", colored_args);
137 }
138
139 int w = utf8_display_width(temp_buf);
140 if (w > LAYOUT_COLUMN_WIDTH) {
142 }
143 if (w > max_col_width)
144 max_col_width = w;
145 }
146
147 // Check MODES entries (capped at 45 chars)
148 for (size_t i = 0; i < config->num_modes; i++) {
149 const char *colored_name = colored_string(LOG_COLOR_FATAL, config->modes[i].name);
150 int w = utf8_display_width(colored_name);
151 if (w > LAYOUT_COLUMN_WIDTH) {
153 }
154 if (w > max_col_width)
155 max_col_width = w;
156 }
157
158 // Check OPTIONS entries (from descriptors)
159 for (size_t i = 0; i < config->num_descriptors; i++) {
160 const option_descriptor_t *desc = &config->descriptors[i];
161 if (desc->hide_from_mode_help || desc->hide_from_binary_help || !desc->group)
162 continue;
163
164 // Build option display string with separate coloring for short and long flags
165 char opts_buf[BUFFER_SIZE_SMALL];
166 if (desc->short_name && desc->short_name != '\0') {
167 char short_flag[16];
168 safe_snprintf(short_flag, sizeof(short_flag), "-%c", desc->short_name);
169 char long_flag[BUFFER_SIZE_SMALL];
170 safe_snprintf(long_flag, sizeof(long_flag), "--%s", desc->long_name);
171 // Color short flag, add comma, color long flag
172 safe_snprintf(opts_buf, sizeof(opts_buf), "%s, %s", colored_string(LOG_COLOR_WARN, short_flag),
173 colored_string(LOG_COLOR_WARN, long_flag));
174 } else {
175 char long_flag[BUFFER_SIZE_SMALL];
176 safe_snprintf(long_flag, sizeof(long_flag), "--%s", desc->long_name);
177 safe_snprintf(opts_buf, sizeof(opts_buf), "%s", colored_string(LOG_COLOR_WARN, long_flag));
178 }
179 const char *colored_opts = opts_buf;
180
181 int w = utf8_display_width(colored_opts);
182 if (w > LAYOUT_COLUMN_WIDTH) {
184 }
185 if (w > max_col_width)
186 max_col_width = w;
187 }
188
189 // Enforce maximum column width of 45 characters
190 if (max_col_width > 45) {
191 max_col_width = 45;
192 }
193
194 return max_col_width;
195}
#define BUFFER_SIZE_SMALL
Small buffer size (256 bytes)
#define BUFFER_SIZE_MEDIUM
Medium buffer size (512 bytes)
@ LOG_COLOR_FATAL
Definition log/log.h:136
@ LOG_COLOR_INFO
Definition log/log.h:133
@ LOG_COLOR_WARN
Definition log/log.h:134
void usage(FILE *desc, asciichat_mode_t mode)
Print usage information for a specific mode.
#define PLATFORM_BINARY_NAME
Suppress unused parameter warnings.
int safe_snprintf(char *buffer, size_t buffer_size, const char *format,...)
Safe formatted string printing to buffer.
Definition system.c:148
const char * colored_string(log_color_t color, const char *text)
Build a colored string for terminal output.
int utf8_display_width(const char *str)
Calculate terminal display width of a UTF-8 string.
Definition utf8.c:46
#define LAYOUT_COLUMN_WIDTH
Definition layout.h:19
Example descriptor for programmatic EXAMPLES generation.
Definition builder.h:310
uint32_t mode_bitmask
Bitmask of modes (OPTION_MODE_SERVER, OPTION_MODE_CLIENT, etc.)
Definition builder.h:311
const char * args
Command-line arguments part of the example.
Definition builder.h:312
bool is_utility_command
True if this is a utility command (don't prepend program name)
Definition builder.h:315
const char * name
Mode name (e.g., "server", "client")
Definition builder.h:322
Option descriptor.
Definition builder.h:241
bool hide_from_mode_help
If true, don't show in mode-specific help (binary-level only)
Definition builder.h:254
bool hide_from_binary_help
If true, don't show in binary-level help (e.g., in release builds)
Definition builder.h:255
const char * group
Group name for help sections (e.g., "NETWORK OPTIONS")
Definition builder.h:252
const char * long_name
Long option name (e.g., "port")
Definition builder.h:243
char short_name
Short option char (e.g., 'p', or '\0' if none)
Definition builder.h:244
size_t num_usage_lines
Number of usage lines.
Definition builder.h:417
option_descriptor_t * descriptors
Array of option descriptors.
Definition builder.h:402
size_t num_modes
Number of modes.
Definition builder.h:423
size_t num_descriptors
Number of descriptors.
Definition builder.h:403
size_t num_examples
Number of examples.
Definition builder.h:420
help_mode_descriptor_t * modes
Array of mode descriptors.
Definition builder.h:422
usage_descriptor_t * usage_lines
Array of usage line descriptors.
Definition builder.h:416
example_descriptor_t * examples
Array of example descriptors.
Definition builder.h:419
Usage line descriptor for programmatic USAGE generation.
Definition builder.h:296

References example_descriptor_t::args, BUFFER_SIZE_MEDIUM, BUFFER_SIZE_SMALL, colored_string(), options_config_t::descriptors, options_config_t::examples, option_descriptor_t::group, option_descriptor_t::hide_from_binary_help, option_descriptor_t::hide_from_mode_help, example_descriptor_t::is_utility_command, LAYOUT_COLUMN_WIDTH, LOG_COLOR_FATAL, LOG_COLOR_INFO, LOG_COLOR_WARN, option_descriptor_t::long_name, example_descriptor_t::mode_bitmask, options_config_t::modes, help_mode_descriptor_t::name, options_config_t::num_descriptors, options_config_t::num_examples, options_config_t::num_modes, options_config_t::num_usage_lines, PLATFORM_BINARY_NAME, safe_snprintf(), option_descriptor_t::short_name, usage(), options_config_t::usage_lines, and utf8_display_width().

Referenced by options_config_print_options_sections_with_width(), and options_config_print_usage_section().

◆ options_config_print_options_sections()

void options_config_print_options_sections ( const options_config_t *  config,
FILE *  stream,
asciichat_mode_t  mode 
)

Print everything except the USAGE section (backward compatibility wrapper)

Print everything except the USAGE section (backward compatibility)

Calls options_config_print_options_sections_with_width with auto-calculation.

Definition at line 1085 of file help.c.

1085 {
1086 options_config_print_options_sections_with_width(config, stream, 0, mode);
1087}
void options_config_print_options_sections_with_width(const options_config_t *config, FILE *stream, int max_col_width, asciichat_mode_t mode)
Print everything except the USAGE section.
Definition help.c:890

References options_config_print_options_sections_with_width().

◆ options_config_print_options_sections_with_width()

void options_config_print_options_sections_with_width ( const options_config_t *  config,
FILE *  stream,
int  max_col_width,
asciichat_mode_t  mode 
)

Print everything except the USAGE section.

Prints MODES, MODE-OPTIONS, EXAMPLES, and OPTIONS sections. Used with options_config_print_usage_section to allow custom content in between.

Definition at line 890 of file help.c.

891 {
892 if (!config || !stream) {
893 SET_ERRNO(ERROR_INVALID_PARAM, "Config or stream is NULL");
894 return;
895 }
896
897 // Detect terminal width - try actual terminal size first, fallback to COLUMNS env var
898 int term_width = 80;
899 terminal_size_t term_size;
900 if (terminal_get_size(&term_size) == ASCIICHAT_OK && term_size.cols > 40) {
901 term_width = term_size.cols;
902 } else {
903 const char *cols_env = SAFE_GETENV("COLUMNS");
904 if (cols_env) {
905 char *endptr;
906 errno = 0;
907 long cols = strtol(cols_env, &endptr, 10);
908 if (*endptr == '\0' && errno == 0 && cols > 40 && cols <= INT_MAX)
909 term_width = (int)cols;
910 else if (*endptr != '\0' || errno != 0 || cols <= 40)
911 log_error("options_config_print_*: Invalid COLUMNS value: %s", cols_env);
912 }
913 }
914
915 // Calculate column width if not provided
916 if (max_col_width <= 0) {
917 max_col_width = options_config_calculate_max_col_width(config);
918 }
919
920 // CAP max_col_width: 86 if terminal wide, otherwise 45 for narrow first column
921 int max_col_cap = (term_width > 170) ? 86 : 45;
922 if (max_col_width > max_col_cap) {
923 max_col_width = max_col_cap;
924 }
925
926 // Determine if this is binary-level help
927 // Discovery mode IS the binary-level help (shows binary options + discovery options)
928 // Other modes show only mode-specific options
929 bool for_binary_help = (mode == MODE_DISCOVERY);
930 // Build list of unique groups in order of first appearance
931 const char **unique_groups = SAFE_MALLOC(config->num_descriptors * sizeof(const char *), const char **);
932 size_t num_unique_groups = 0;
933
934 // For binary-level help, we now use a two-pass system to order groups,
935 // ensuring binary-level option groups appear before discovery-mode groups.
936 if (for_binary_help) {
937 // Pass 1: Add GENERAL first, then LOGGING if present
938 bool general_added = false;
939 bool logging_added = false;
940 for (size_t i = 0; i < config->num_descriptors; i++) {
941 const option_descriptor_t *desc = &config->descriptors[i];
942 if (desc->group) {
943 if (!general_added && strcmp(desc->group, "GENERAL") == 0) {
944 unique_groups[num_unique_groups++] = "GENERAL";
945 general_added = true;
946 }
947 if (!logging_added && strcmp(desc->group, "LOGGING") == 0) {
948 unique_groups[num_unique_groups++] = "LOGGING";
949 logging_added = true;
950 }
951 if (general_added && logging_added) {
952 break;
953 }
954 }
955 }
956
957 // Pass 2: Collect all other groups that apply for binary help (all modes), if not already added.
958 for (size_t i = 0; i < config->num_descriptors; i++) {
959 const option_descriptor_t *desc = &config->descriptors[i];
960 // An option applies if option_applies_to_mode says so for binary help (which checks OPTION_MODE_ALL)
961 if (option_applies_to_mode(desc, mode, for_binary_help) && desc->group) {
962 // Skip GENERAL and LOGGING groups if we already added them (for binary help)
963 if (strcmp(desc->group, "GENERAL") == 0 || strcmp(desc->group, "LOGGING") == 0) {
964 continue;
965 }
966
967 bool group_exists = false;
968 for (size_t j = 0; j < num_unique_groups; j++) {
969 if (strcmp(unique_groups[j], desc->group) == 0) {
970 group_exists = true;
971 break;
972 }
973 }
974 if (!group_exists) {
975 unique_groups[num_unique_groups++] = desc->group;
976 }
977 } else if (desc->group) {
978 }
979 }
980 } else {
981 // Original logic for other modes
982 for (size_t i = 0; i < config->num_descriptors; i++) {
983 const option_descriptor_t *desc = &config->descriptors[i];
984 if (option_applies_to_mode(desc, mode, for_binary_help) && desc->group) {
985 bool group_exists = false;
986 for (size_t j = 0; j < num_unique_groups; j++) {
987 if (strcmp(unique_groups[j], desc->group) == 0) {
988 group_exists = true;
989 break;
990 }
991 }
992 if (!group_exists) {
993 unique_groups[num_unique_groups++] = desc->group;
994 }
995 }
996 }
997 }
998
999 // Print options grouped by category
1000 for (size_t gi = 0; gi < num_unique_groups; gi++) {
1001 const char *current_group = unique_groups[gi];
1002 // Add newline before each group except the first
1003 if (gi > 0) {
1004 fprintf(stream, "\n");
1005 }
1006 fprintf(stream, "%s\n", colored_string(LOG_COLOR_DEBUG, current_group));
1007
1008 for (size_t i = 0; i < config->num_descriptors; i++) {
1009 const option_descriptor_t *desc = &config->descriptors[i];
1010 if (!option_applies_to_mode(desc, mode, for_binary_help) || !desc->group ||
1011 strcmp(desc->group, current_group) != 0) {
1012 continue;
1013 }
1014
1015 // Build option string (flag part) with separate coloring for short and long flags
1016 char colored_option_str[BUFFER_SIZE_MEDIUM] = "";
1017 int colored_len = 0;
1018
1019 // Short name and long name with separate coloring
1020 if (desc->short_name) {
1021 char short_flag[16];
1022 safe_snprintf(short_flag, sizeof(short_flag), "-%c", desc->short_name);
1023 char long_flag[BUFFER_SIZE_SMALL];
1024 safe_snprintf(long_flag, sizeof(long_flag), "--%s", desc->long_name);
1025 // Color short flag, plain comma-space, color long flag
1026 colored_len +=
1027 safe_snprintf(colored_option_str + colored_len, sizeof(colored_option_str) - colored_len, "%s, %s",
1029 } else {
1030 char long_flag[BUFFER_SIZE_SMALL];
1031 safe_snprintf(long_flag, sizeof(long_flag), "--%s", desc->long_name);
1032 colored_len += safe_snprintf(colored_option_str + colored_len, sizeof(colored_option_str) - colored_len, "%s",
1033 colored_string(LOG_COLOR_WARN, long_flag));
1034 }
1035
1036 // Value placeholder (colored green)
1037 if (desc->type != OPTION_TYPE_BOOL && desc->type != OPTION_TYPE_ACTION) {
1038 colored_len += safe_snprintf(colored_option_str + colored_len, sizeof(colored_option_str) - colored_len, " ");
1039 const char *placeholder = get_option_help_placeholder_str(desc);
1040 if (placeholder[0] != '\0') {
1041 colored_len += safe_snprintf(colored_option_str + colored_len, sizeof(colored_option_str) - colored_len, "%s",
1042 colored_string(LOG_COLOR_INFO, placeholder));
1043 }
1044 }
1045
1046 // Build description with defaults and env vars
1047 char desc_str[1024] = "";
1048 int desc_len = 0;
1049
1050 if (desc->help_text) {
1051 desc_len += safe_snprintf(desc_str + desc_len, sizeof(desc_str) - desc_len, "%s", desc->help_text);
1052 }
1053
1054 // Skip adding default if the description already mentions it
1055 bool description_has_default =
1056 desc->help_text && (strstr(desc->help_text, "(default:") || strstr(desc->help_text, "=default)"));
1057
1058 if (desc->default_value && !description_has_default) {
1059 char default_buf[256];
1060 int default_len = format_option_default_value_str(desc, default_buf, sizeof(default_buf));
1061 if (default_len > 0) {
1062 desc_len +=
1063 safe_snprintf(desc_str + desc_len, sizeof(desc_str) - desc_len, " (%s %s)",
1064 colored_string(LOG_COLOR_FATAL, "default:"), colored_string(LOG_COLOR_FATAL, default_buf));
1065 }
1066 }
1067
1068 if (desc->required) {
1069 desc_len += safe_snprintf(desc_str + desc_len, sizeof(desc_str) - desc_len, " [REQUIRED]");
1070 }
1071
1072 layout_print_two_column_row(stream, colored_option_str, desc_str, max_col_width, term_width, 2);
1073 }
1074 }
1075
1076 // Cleanup
1077 SAFE_FREE(unique_groups);
1078}
const char * get_option_help_placeholder_str(const option_descriptor_t *desc)
Get help placeholder string for an option type.
Definition builder.c:43
int format_option_default_value_str(const option_descriptor_t *desc, char *buf, size_t bufsize)
Format option default value as a string.
Definition builder.c:83
bool option_applies_to_mode(const option_descriptor_t *desc, asciichat_mode_t mode, bool for_binary_help)
Check if an option applies to a specific mode using bitmask.
Definition builder.c:177
@ OPTION_TYPE_ACTION
Action that executes and may exit (–list-webcams, etc.)
Definition builder.h:167
@ OPTION_TYPE_BOOL
Boolean flag (–flag, no value)
Definition builder.h:162
#define SAFE_FREE(ptr)
Definition common.h:376
#define SAFE_MALLOC(size, cast)
Definition common.h:264
#define SAFE_GETENV(name)
Definition common.h:434
#define SET_ERRNO(code, context_msg,...)
Set error code with custom context message and log it, returning the error code.
@ ASCIICHAT_OK
Definition error_codes.h:51
@ ERROR_INVALID_PARAM
#define log_error(...)
Log an ERROR message.
Definition log/log.h:587
@ LOG_COLOR_DEBUG
Definition log/log.h:132
@ MODE_DISCOVERY
Discovery mode - participant that can dynamically become host.
asciichat_error_t terminal_get_size(terminal_size_t *size)
Get terminal size.
int errno
int options_config_calculate_max_col_width(const options_config_t *config)
Calculate global max column width across all help sections.
Definition help.c:77
void layout_print_two_column_row(FILE *stream, const char *first_column, const char *second_column, int first_col_len, int term_width, int continuation_indent_extra)
Print two-column row with automatic wrapping.
Definition layout.c:220
const char * help_text
Description for –help.
Definition builder.h:251
const void * default_value
Pointer to default value (or NULL if required)
Definition builder.h:258
bool required
If true, option must be provided.
Definition builder.h:259
option_type_t type
Value type.
Definition builder.h:247
Terminal size structure.
Definition terminal.h:163
int cols
Number of columns (width) in terminal.
Definition terminal.h:165

References ASCIICHAT_OK, BUFFER_SIZE_MEDIUM, BUFFER_SIZE_SMALL, colored_string(), terminal_size_t::cols, option_descriptor_t::default_value, options_config_t::descriptors, errno, ERROR_INVALID_PARAM, format_option_default_value_str(), get_option_help_placeholder_str(), option_descriptor_t::group, option_descriptor_t::help_text, layout_print_two_column_row(), LOG_COLOR_DEBUG, LOG_COLOR_FATAL, LOG_COLOR_INFO, LOG_COLOR_WARN, log_error, option_descriptor_t::long_name, MODE_DISCOVERY, options_config_t::num_descriptors, option_applies_to_mode(), OPTION_TYPE_ACTION, OPTION_TYPE_BOOL, options_config_calculate_max_col_width(), option_descriptor_t::required, SAFE_FREE, SAFE_GETENV, SAFE_MALLOC, safe_snprintf(), SET_ERRNO, option_descriptor_t::short_name, terminal_get_size(), and option_descriptor_t::type.

Referenced by options_config_print_options_sections(), and options_print_help_for_mode().

◆ options_config_print_usage()

void options_config_print_usage ( const options_config_t *  config,
FILE *  stream 
)

Print usage/help text.

Generates formatted help with grouped options.

Parameters
configOptions configuration
streamOutput stream (stdout or stderr)

Definition at line 706 of file help.c.

706 {
707 if (!config || !stream)
708 return;
709
710 // Detect terminal width from COLUMNS env var or use default
711 int term_width = 80;
712 const char *cols_env = SAFE_GETENV("COLUMNS");
713 if (cols_env) {
714 char *endptr;
715 errno = 0;
716 long cols = strtol(cols_env, &endptr, 10);
717 if (*endptr == '\0' && errno == 0 && cols > 40 && cols <= INT_MAX)
718 term_width = (int)cols;
719 else if (*endptr != '\0' || errno != 0 || cols <= 40)
720 log_error("options_config_print_binary_help: Invalid COLUMNS value: %s", cols_env);
721 }
722
723 // Binary-level help uses MODE_DISCOVERY internally
725 bool for_binary_help = true;
726
727 // Calculate per-section column widths (each section is independent, capped at 75)
728 int usage_max_col_width = calculate_section_max_col_width(config, "usage", mode, for_binary_help);
729 int modes_max_col_width = calculate_section_max_col_width(config, "modes", mode, for_binary_help);
730 int examples_max_col_width = calculate_section_max_col_width(config, "examples", mode, for_binary_help);
731 int options_max_col_width = calculate_section_max_col_width(config, "options", mode, for_binary_help);
732
733 // Print programmatically generated sections (USAGE, MODES, MODE-OPTIONS, EXAMPLES)
734 print_usage_section(config, stream, term_width, usage_max_col_width);
735 print_modes_section(config, stream, term_width, modes_max_col_width);
736 print_mode_options_section(stream, term_width, 40); // Keep reasonable width for mode-options
737 print_examples_section(config, stream, term_width, examples_max_col_width, MODE_SERVER, true);
738
739 // Build list of unique groups in order of first appearance
740
741 const char **unique_groups = SAFE_MALLOC(config->num_descriptors * sizeof(const char *), const char **);
742 size_t num_unique_groups = 0;
743
744 for (size_t i = 0; i < config->num_descriptors; i++) {
745 const option_descriptor_t *desc = &config->descriptors[i];
746 // Filter by mode_bitmask - for binary help, show binary options
747 if (!option_applies_to_mode(desc, MODE_SERVER, for_binary_help) || !desc->group) {
748 continue;
749 }
750
751 // Check if this group is already in the list
752 bool group_exists = false;
753 for (size_t j = 0; j < num_unique_groups; j++) {
754 if (unique_groups[j] && strcmp(unique_groups[j], desc->group) == 0) {
755 group_exists = true;
756 break;
757 }
758 }
759
760 // Add new group to list
761 if (!group_exists && num_unique_groups < config->num_descriptors) {
762 unique_groups[num_unique_groups++] = desc->group;
763 }
764 }
765
766 // Print options grouped by group name
767 for (size_t g = 0; g < num_unique_groups; g++) {
768 const char *current_group = unique_groups[g];
769 // Only add leading newline for groups after the first one
770 if (g > 0) {
771 fprintf(stream, "\n");
772 }
773 fprintf(stream, "%s\n", colored_string(LOG_COLOR_DEBUG, current_group));
774
775 // Print all options in this group
776 for (size_t i = 0; i < config->num_descriptors; i++) {
777 const option_descriptor_t *desc = &config->descriptors[i];
778
779 // Skip if not in current group or if doesn't apply to mode
780 if (!option_applies_to_mode(desc, MODE_SERVER, for_binary_help) || !desc->group ||
781 strcmp(desc->group, current_group) != 0) {
782 continue;
783 }
784
785 // Build option string with separate coloring for short and long flags
786 char option_str[BUFFER_SIZE_MEDIUM] = "";
787 int option_len = 0;
788
789 // Short name and long name with separate coloring
790 if (desc->short_name) {
791 char short_flag[16];
792 safe_snprintf(short_flag, sizeof(short_flag), "-%c", desc->short_name);
793 char long_flag[BUFFER_SIZE_SMALL];
794 safe_snprintf(long_flag, sizeof(long_flag), "--%s", desc->long_name);
795 // Color short flag, plain comma-space, color long flag
796 option_len +=
797 safe_snprintf(option_str + option_len, sizeof(option_str) - option_len, "%s, %s",
799 } else {
800 char long_flag[BUFFER_SIZE_SMALL];
801 safe_snprintf(long_flag, sizeof(long_flag), "--%s", desc->long_name);
802 option_len += safe_snprintf(option_str + option_len, sizeof(option_str) - option_len, "%s",
803 colored_string(LOG_COLOR_WARN, long_flag));
804 }
805
806 // Value placeholder (colored green)
807 if (desc->type != OPTION_TYPE_BOOL && desc->type != OPTION_TYPE_ACTION) {
808 option_len += safe_snprintf(option_str + option_len, sizeof(option_str) - option_len, " ");
809 const char *placeholder = get_option_help_placeholder_str(desc);
810 if (placeholder[0] != '\0') {
811 option_len += safe_snprintf(option_str + option_len, sizeof(option_str) - option_len, "%s",
812 colored_string(LOG_COLOR_INFO, placeholder));
813 }
814 }
815
816 // Build description string (plain text, colors applied when printing)
817 char desc_str[BUFFER_SIZE_MEDIUM] = "";
818 int desc_len = 0;
819
820 if (desc->help_text) {
821 desc_len += safe_snprintf(desc_str + desc_len, sizeof(desc_str) - desc_len, "%s", desc->help_text);
822 }
823
824 // Skip adding default if the description already mentions it
825 bool description_has_default =
826 desc->help_text && (strstr(desc->help_text, "(default:") || strstr(desc->help_text, "=default)"));
827
828 if (desc->default_value && !description_has_default) {
829 char default_buf[256];
830 int default_len = format_option_default_value_str(desc, default_buf, sizeof(default_buf));
831 if (default_len > 0) {
832 desc_len +=
833 safe_snprintf(desc_str + desc_len, sizeof(desc_str) - desc_len, " (%s %s)",
835 }
836 }
837
838 if (desc->required) {
839 desc_len += safe_snprintf(desc_str + desc_len, sizeof(desc_str) - desc_len, " [REQUIRED]");
840 }
841
842 // Use layout function with section-specific column width for consistent alignment
843 // option_str already contains colored_string() results, so pass it directly
844 layout_print_two_column_row(stream, option_str, desc_str, options_max_col_width, term_width, 2);
845 }
846 }
847
848 // Cleanup
849 SAFE_FREE(unique_groups);
850
851 fprintf(stream, "\n");
852}
asciichat_mode_t
Mode type for options parsing.
@ MODE_SERVER
Server mode - network server options.

References BUFFER_SIZE_MEDIUM, BUFFER_SIZE_SMALL, colored_string(), option_descriptor_t::default_value, options_config_t::descriptors, errno, format_option_default_value_str(), get_option_help_placeholder_str(), option_descriptor_t::group, option_descriptor_t::help_text, layout_print_two_column_row(), LOG_COLOR_DEBUG, LOG_COLOR_FATAL, LOG_COLOR_INFO, LOG_COLOR_WARN, log_error, option_descriptor_t::long_name, MODE_DISCOVERY, MODE_SERVER, options_config_t::num_descriptors, option_applies_to_mode(), OPTION_TYPE_ACTION, OPTION_TYPE_BOOL, option_descriptor_t::required, SAFE_FREE, SAFE_GETENV, SAFE_MALLOC, safe_snprintf(), option_descriptor_t::short_name, and option_descriptor_t::type.

◆ options_config_print_usage_section()

void options_config_print_usage_section ( const options_config_t *  config,
FILE *  stream 
)

Print only the USAGE section.

Splits usage printing from other sections to allow custom content (like positional argument examples) to be inserted between USAGE and other sections.

Definition at line 860 of file help.c.

860 {
861 if (!config || !stream)
862 return;
863
864 // Detect terminal width from COLUMNS env var or use default
865 int term_width = 80;
866 const char *cols_env = SAFE_GETENV("COLUMNS");
867 if (cols_env) {
868 char *endptr;
869 errno = 0;
870 long cols = strtol(cols_env, &endptr, 10);
871 if (*endptr == '\0' && errno == 0 && cols > 40 && cols <= INT_MAX)
872 term_width = (int)cols;
873 else if (*endptr != '\0' || errno != 0 || cols <= 40)
874 log_error("options_config_print_binary_help: Invalid COLUMNS value: %s", cols_env);
875 }
876
877 // Calculate global max column width across all sections for consistent alignment
878 int max_col_width = options_config_calculate_max_col_width(config);
879
880 // Print only USAGE section
881 print_usage_section(config, stream, term_width, max_col_width);
882}

References errno, log_error, options_config_calculate_max_col_width(), and SAFE_GETENV.

◆ options_print_help_for_mode()

void options_print_help_for_mode ( const options_config_t *  config,
asciichat_mode_t  mode,
const char *  program_name,
const char *  description,
FILE *  desc 
)

Print help for a specific mode or binary level.

Print help for a specific mode or binary level (unified function)

This is the single unified function for all help output (binary level and all modes). It handles common layout logic, terminal detection, and section printing.

Parameters
configOptions config with all options (binary + all modes)
modeMode to show help for (use -1 for binary-level help)
program_nameFull program name with mode (e.g., "ascii-chat server")
descriptionBrief description of the mode/binary
descOutput file stream (usually stdout)

Definition at line 1105 of file help.c.

1106 {
1107 if (!config || !desc) {
1108 if (desc) {
1109 fprintf(desc, "Error: Failed to create options config\n");
1110 } else {
1111 SET_ERRNO(ERROR_INVALID_PARAM, "Config or desc is NULL");
1112 }
1113 return;
1114 }
1115
1116 // Detect terminal width early so we can decide whether to show ASCII art
1117 int term_width = 80;
1118 terminal_size_t term_size;
1119 if (terminal_get_size(&term_size) == ASCIICHAT_OK && term_size.cols > 40) {
1120 term_width = term_size.cols;
1121 } else {
1122 const char *cols_env = SAFE_GETENV("COLUMNS");
1123 if (cols_env) {
1124 char *endptr;
1125 errno = 0;
1126 long cols = strtol(cols_env, &endptr, 10);
1127 if (*endptr == '\0' && errno == 0 && cols > 40 && cols <= INT_MAX)
1128 term_width = (int)cols;
1129 else if (*endptr != '\0' || errno != 0 || cols <= 40)
1130 log_error("options_config_print_*: Invalid COLUMNS value: %s", cols_env);
1131 }
1132 }
1133
1134 // Print ASCII art logo only if terminal is wide enough (ASCII art is ~52 chars wide)
1135 if (term_width >= 60) {
1136 (void)fprintf(desc, " __ _ ___ ___(_|_) ___| |__ __ _| |_ \n");
1137 (void)fprintf(desc, " / _` / __|/ __| | |_____ / __| '_ \\ / _` | __|\n");
1138 (void)fprintf(desc, "| (_| \\__ \\ (__| | |_____| (__| | | | (_| | |_ \n");
1139 (void)fprintf(desc, " \\__,_|___/\\___|_|_| \\___|_| |_|\\__,_|\\__|\n");
1140 (void)fprintf(desc, "\n");
1141 }
1142
1143 // Print program name and description (color mode name magenta if it's a mode-specific help)
1144 if (program_name) {
1145 const char *space = strchr(program_name, ' ');
1146 if (space && mode >= 0) {
1147 // Mode-specific help: color the mode name
1148 int binary_len = space - program_name;
1149 (void)fprintf(desc, "%.*s %s - %s\n\n", binary_len, program_name, colored_string(LOG_COLOR_FATAL, space + 1),
1150 description);
1151 } else {
1152 // Binary-level help: use colored_string for the program name too
1153 (void)fprintf(desc, "%s - %s\n\n", colored_string(LOG_COLOR_FATAL, program_name), description);
1154 }
1155 }
1156
1157 // Print project links
1158 print_project_links(desc);
1159 (void)fprintf(desc, "\n");
1160
1161 // Determine if this is binary-level help (called for 'ascii-chat --help')
1162 // Binary help uses MODE_DISCOVERY as the mode value
1163 bool for_binary_help = (mode == MODE_DISCOVERY);
1164
1165 // Calculate column widths for USAGE upfront (before printing) for alignment
1166 int usage_max_col_width = calculate_section_max_col_width(config, "usage", mode, for_binary_help);
1167 // Use USAGE width for both USAGE and EXAMPLES sections for consistent alignment
1168 // (don't let long examples push column too far right)
1169 int usage_examples_col_width = usage_max_col_width;
1170
1171 // Print USAGE section (with section-specific column width and mode filtering)
1172 fprintf(desc, "%s\n", colored_string(LOG_COLOR_DEBUG, "USAGE"));
1173 if (config->num_usage_lines > 0) {
1174 // Get mode name for filtering usage lines
1175 const char *mode_name = NULL;
1176 switch (mode) {
1177 case MODE_SERVER:
1178 mode_name = "server";
1179 break;
1180 case MODE_CLIENT:
1181 mode_name = "client";
1182 break;
1183 case MODE_MIRROR:
1184 mode_name = "mirror";
1185 break;
1187 mode_name = "discovery-service";
1188 break;
1189 case MODE_DISCOVERY:
1190 default:
1191 mode_name = NULL; // Binary help shows all usage lines
1192 break;
1193 }
1194
1195 for (size_t i = 0; i < config->num_usage_lines; i++) {
1196 const usage_descriptor_t *usage = &config->usage_lines[i];
1197
1198 // Filter usage lines by mode
1199 if (!for_binary_help) {
1200 // For mode-specific help, show the current mode's usage line and the generic [mode] --help line
1201 bool is_current_mode = (usage->mode && mode_name && strcmp(usage->mode, mode_name) == 0);
1202 bool is_generic_help_line =
1203 (!usage->mode && usage->positional && strcmp(usage->positional, "[mode] --help") == 0);
1204
1205 if (!is_current_mode && !is_generic_help_line) {
1206 continue;
1207 }
1208 }
1209
1210 char usage_buf[BUFFER_SIZE_MEDIUM];
1211 int len = 0;
1212
1213 len += safe_snprintf(usage_buf + len, sizeof(usage_buf) - len, "ascii-chat");
1214
1215 // For mode-specific help, use [mode] placeholder when showing --help usage
1216 if (usage->mode) {
1217 if (!for_binary_help && usage->positional && strcmp(usage->positional, "--help") == 0) {
1218 // Mode-specific help: show [mode] --help as generic placeholder
1219 len +=
1220 safe_snprintf(usage_buf + len, sizeof(usage_buf) - len, " %s", colored_string(LOG_COLOR_FATAL, "[mode]"));
1221 } else {
1222 // Binary help or non-help usage: show actual mode name
1223 len += safe_snprintf(usage_buf + len, sizeof(usage_buf) - len, " %s",
1225 }
1226 }
1227
1228 if (usage->positional) {
1229 len += safe_snprintf(usage_buf + len, sizeof(usage_buf) - len, " %s",
1230 colored_string(LOG_COLOR_INFO, usage->positional));
1231 }
1232
1233 if (usage->show_options) {
1234 const char *options_text =
1235 (usage->mode && strcmp(usage->mode, "<mode>") == 0) ? "[mode-options...]" : "[options...]";
1236 len += safe_snprintf(usage_buf + len, sizeof(usage_buf) - len, " %s",
1237 colored_string(LOG_COLOR_WARN, options_text));
1238 }
1239
1240 layout_print_two_column_row(desc, usage_buf, usage->description, usage_examples_col_width, term_width, 0);
1241 }
1242 }
1243 fprintf(desc, "\n");
1244
1245 // Print MODES section (only for binary-level help)
1246 if (for_binary_help && config->num_modes > 0) {
1247 int modes_max_col_width = calculate_section_max_col_width(config, "modes", mode, for_binary_help);
1248 print_modes_section(config, desc, term_width, modes_max_col_width);
1249 }
1250
1251 // Print positional argument examples (with mode filtering and section-specific column width)
1252 if (config->num_positional_args > 0) {
1253 // First, check if any positional args apply to this mode
1254 option_mode_bitmask_t current_mode_bitmask = 1U << mode;
1255 bool has_applicable_positional_args = false;
1256
1257 for (size_t pa_idx = 0; pa_idx < config->num_positional_args; pa_idx++) {
1258 const positional_arg_descriptor_t *pos_arg = &config->positional_args[pa_idx];
1259
1260 // Filter by mode_bitmask (matching the parsing code logic)
1261 if (pos_arg->mode_bitmask != 0 && !(pos_arg->mode_bitmask & current_mode_bitmask)) {
1262 continue;
1263 }
1264
1265 if (pos_arg->section_heading && pos_arg->examples && pos_arg->num_examples > 0) {
1266 has_applicable_positional_args = true;
1267 break;
1268 }
1269 }
1270
1271 // Only print the section if there are applicable positional args
1272 if (has_applicable_positional_args) {
1273 int positional_max_col_width = calculate_section_max_col_width(config, "positional", mode, false);
1274
1275 for (size_t pa_idx = 0; pa_idx < config->num_positional_args; pa_idx++) {
1276 const positional_arg_descriptor_t *pos_arg = &config->positional_args[pa_idx];
1277
1278 // Filter by mode_bitmask (matching the parsing code logic)
1279 if (pos_arg->mode_bitmask != 0 && !(pos_arg->mode_bitmask & current_mode_bitmask)) {
1280 continue;
1281 }
1282
1283 if (pos_arg->section_heading && pos_arg->examples && pos_arg->num_examples > 0) {
1284 (void)fprintf(desc, "%s\n", colored_string(LOG_COLOR_DEBUG, pos_arg->section_heading));
1285
1286 for (size_t i = 0; i < pos_arg->num_examples; i++) {
1287 const char *example = pos_arg->examples[i];
1288 const char *p = example;
1289 const char *desc_start = NULL;
1290
1291 while (*p == ' ')
1292 p++;
1293 const char *first_part = p;
1294
1295 while (*p && !(*p == ' ' && *(p + 1) == ' '))
1296 p++;
1297 int first_len_bytes = (int)(p - first_part);
1298
1299 while (*p == ' ')
1300 p++;
1301 if (*p) {
1302 desc_start = p;
1303 }
1304
1305 char colored_first_part[256];
1306 safe_snprintf(colored_first_part, sizeof(colored_first_part), "%.*s", first_len_bytes, first_part);
1307 char colored_result[512];
1308 safe_snprintf(colored_result, sizeof(colored_result), "%s",
1309 colored_string(LOG_COLOR_INFO, colored_first_part));
1310
1311 layout_print_two_column_row(desc, colored_result, desc_start ? desc_start : "", positional_max_col_width,
1312 term_width, 0);
1313 }
1314 (void)fprintf(desc, "\n");
1315 }
1316 }
1317 }
1318 }
1319
1320 // Print EXAMPLES section (using USAGE column width for alignment)
1321 print_examples_section(config, desc, term_width, usage_examples_col_width, mode, for_binary_help);
1322
1323 // Print custom sections (after EXAMPLES, before OPTIONS)
1324 if (config->num_custom_sections > 0) {
1325 option_mode_bitmask_t current_mode_bitmask = 1U << mode;
1326 for (size_t i = 0; i < config->num_custom_sections; i++) {
1327 const custom_section_descriptor_t *section = &config->custom_sections[i];
1328
1329 // Filter by mode_bitmask
1330 if (section->mode_bitmask != 0 && !(section->mode_bitmask & current_mode_bitmask)) {
1331 continue;
1332 }
1333
1334 if (section->heading) {
1335 fprintf(desc, "%s\n", colored_string(LOG_COLOR_DEBUG, section->heading));
1336 }
1337
1338 if (section->content) {
1339 // Special handling for KEYBINDINGS section: colorize keybindings and wrap to 70 chars
1340 if (section->heading && strcmp(section->heading, "KEYBINDINGS") == 0) {
1341 // Build colored version with proper keybinding colorization
1342 char colored_output[2048] = "";
1343 const char *src = section->content;
1344 char *dst = colored_output;
1345 size_t remaining = sizeof(colored_output) - 1;
1346
1347 while (*src && remaining > 0) {
1348 // Colorize ? that don't end sentences
1349 if (*src == '?' && *(src + 1) != '\n' && *(src + 1) != '\0') {
1350 const char *colored = colored_string(LOG_COLOR_FATAL, "?");
1351 size_t len = strlen(colored);
1352 if (len <= remaining) {
1353 memcpy(dst, colored, len);
1354 dst += len;
1355 remaining -= len;
1356 src += 1;
1357 } else {
1358 break;
1359 }
1360 } else if (strncmp(src, "Space", 5) == 0 &&
1361 (src > section->content && (*(src - 1) == ',' || *(src - 1) == ' '))) {
1362 const char *colored = colored_string(LOG_COLOR_FATAL, "Space");
1363 size_t len = strlen(colored);
1364 if (len <= remaining) {
1365 memcpy(dst, colored, len);
1366 dst += len;
1367 remaining -= len;
1368 src += 5;
1369 } else {
1370 break;
1371 }
1372 } else if (strncmp(src, "arrows", 6) == 0 &&
1373 (src > section->content && (*(src - 1) == ',' || *(src - 1) == ' '))) {
1374 const char *colored = colored_string(LOG_COLOR_FATAL, "arrows");
1375 size_t len = strlen(colored);
1376 if (len <= remaining) {
1377 memcpy(dst, colored, len);
1378 dst += len;
1379 remaining -= len;
1380 src += 6;
1381 } else {
1382 break;
1383 }
1384 } else if (*src == 'm' && (src > section->content && (*(src - 1) == ',' || *(src - 1) == ' ')) &&
1385 (*(src + 1) == ',' || *(src + 1) == ')')) {
1386 const char *colored = colored_string(LOG_COLOR_FATAL, "m");
1387 size_t len = strlen(colored);
1388 if (len <= remaining) {
1389 memcpy(dst, colored, len);
1390 dst += len;
1391 remaining -= len;
1392 src += 1;
1393 } else {
1394 break;
1395 }
1396 } else if (*src == 'c' && (src > section->content && (*(src - 1) == ',' || *(src - 1) == ' ')) &&
1397 (*(src + 1) == ',' || *(src + 1) == ')')) {
1398 const char *colored = colored_string(LOG_COLOR_FATAL, "c");
1399 size_t len = strlen(colored);
1400 if (len <= remaining) {
1401 memcpy(dst, colored, len);
1402 dst += len;
1403 remaining -= len;
1404 src += 1;
1405 } else {
1406 break;
1407 }
1408 } else if (*src == 'f' && (src > section->content && (*(src - 1) == ',' || *(src - 1) == ' ')) &&
1409 (*(src + 1) == ',' || *(src + 1) == ')')) {
1410 const char *colored = colored_string(LOG_COLOR_FATAL, "f");
1411 size_t len = strlen(colored);
1412 if (len <= remaining) {
1413 memcpy(dst, colored, len);
1414 dst += len;
1415 remaining -= len;
1416 src += 1;
1417 } else {
1418 break;
1419 }
1420 } else if (*src == 'r' && (src > section->content && (*(src - 1) == ',' || *(src - 1) == ' ')) &&
1421 (*(src + 1) == ')')) {
1422 const char *colored = colored_string(LOG_COLOR_FATAL, "r");
1423 size_t len = strlen(colored);
1424 if (len <= remaining) {
1425 memcpy(dst, colored, len);
1426 dst += len;
1427 remaining -= len;
1428 src += 1;
1429 } else {
1430 break;
1431 }
1432 } else {
1433 *dst++ = *src++;
1434 remaining--;
1435 }
1436 }
1437 *dst = '\0';
1438 // Print with 2-space indent, wrapping at min(terminal width, 90)
1439 fprintf(desc, " ");
1440 int keybindings_wrap_width = term_width < 90 ? term_width : 90;
1441 layout_print_wrapped_description(desc, colored_output, 2, keybindings_wrap_width, 0);
1442 fprintf(desc, "\n");
1443 } else {
1444 fprintf(desc, "%s\n", section->content);
1445 }
1446 }
1447
1448 fprintf(desc, "\n");
1449 }
1450 }
1451
1452 // Print options sections (with section-specific column width for options)
1453 int options_max_col_width = calculate_section_max_col_width(config, "options", mode, for_binary_help);
1454 options_config_print_options_sections_with_width(config, desc, options_max_col_width, mode);
1455}
option_mode_bitmask_t
Option mode bitmask.
@ MODE_CLIENT
Client mode - network client options.
@ MODE_DISCOVERY_SERVICE
Discovery server mode - session management and WebRTC signaling.
@ MODE_MIRROR
Mirror mode - local webcam viewing (no network)
void layout_print_wrapped_description(FILE *stream, const char *text, int indent_width, int term_width, int continuation_indent_extra)
Print text with wrapping and proper indentation.
Definition layout.c:98
void print_project_links(FILE *desc)
Print project links with link emoji and colored styling.
Custom help section descriptor.
Definition builder.h:389
option_mode_bitmask_t mode_bitmask
Which modes show this section.
Definition builder.h:392
const char * content
Section content.
Definition builder.h:391
const char * heading
Section heading (e.g., "INTERACTIVE CONTROLS")
Definition builder.h:390
positional_arg_descriptor_t * positional_args
Array of positional argument descriptors.
Definition builder.h:408
size_t num_custom_sections
Number of custom sections.
Definition builder.h:426
custom_section_descriptor_t * custom_sections
Array of custom help sections.
Definition builder.h:425
size_t num_positional_args
Number of positional arguments.
Definition builder.h:409
Positional argument descriptor.
Definition builder.h:357
const char * section_heading
Section heading for examples (e.g., "ADDRESS FORMATS")
Definition builder.h:363
option_mode_bitmask_t mode_bitmask
Which modes this positional arg applies to.
Definition builder.h:380
const char ** examples
Array of example strings with descriptions.
Definition builder.h:364
size_t num_examples
Number of examples.
Definition builder.h:365

References ASCIICHAT_OK, BUFFER_SIZE_MEDIUM, colored_string(), terminal_size_t::cols, custom_section_descriptor_t::content, options_config_t::custom_sections, errno, ERROR_INVALID_PARAM, positional_arg_descriptor_t::examples, custom_section_descriptor_t::heading, layout_print_two_column_row(), layout_print_wrapped_description(), LOG_COLOR_DEBUG, LOG_COLOR_FATAL, LOG_COLOR_INFO, LOG_COLOR_WARN, log_error, positional_arg_descriptor_t::mode_bitmask, custom_section_descriptor_t::mode_bitmask, MODE_CLIENT, MODE_DISCOVERY, MODE_DISCOVERY_SERVICE, MODE_MIRROR, MODE_SERVER, options_config_t::num_custom_sections, positional_arg_descriptor_t::num_examples, options_config_t::num_modes, options_config_t::num_positional_args, options_config_t::num_usage_lines, options_config_print_options_sections_with_width(), options_config_t::positional_args, print_project_links(), SAFE_GETENV, safe_snprintf(), positional_arg_descriptor_t::section_heading, SET_ERRNO, terminal_get_size(), usage(), and options_config_t::usage_lines.

Referenced by usage().

◆ options_struct_destroy()

void options_struct_destroy ( const options_config_t *  config,
void *  options_struct 
)

Clean up memory owned by options struct.

Frees all auto-duplicated strings and sets pointers to NULL. Call this before freeing the options struct.

Parameters
configOptions configuration
options_structOptions struct to clean up

Definition at line 1457 of file help.c.

1457 {
1458 if (!config || !options_struct)
1459 return;
1460
1461 // Free all owned strings
1462 for (size_t i = 0; i < config->num_owned_strings; i++) {
1463 free(config->owned_strings[i]);
1464 }
1465
1466 // Reset owned strings tracking
1467 ((options_config_t *)config)->num_owned_strings = 0;
1468
1469 // NULL out string fields
1470 char *base = (char *)options_struct;
1471 for (size_t i = 0; i < config->num_descriptors; i++) {
1472 const option_descriptor_t *desc = &config->descriptors[i];
1473 if (desc->type == OPTION_TYPE_STRING && desc->owns_memory) {
1474 char **field = (char **)(base + desc->offset);
1475 *field = NULL;
1476 }
1477 }
1478}
@ OPTION_TYPE_STRING
String value (–name foo)
Definition builder.h:164
size_t offset
offsetof(struct, field) - where to store value
Definition builder.h:248
bool owns_memory
If true, strings are strdup'd and freed on cleanup.
Definition builder.h:272
Options configuration.
Definition builder.h:401
char ** owned_strings
Strdup'd strings to free on cleanup.
Definition builder.h:429
size_t num_owned_strings
Number of owned strings.
Definition builder.h:430

References options_config_t::descriptors, options_config_t::num_descriptors, options_config_t::num_owned_strings, option_descriptor_t::offset, OPTION_TYPE_STRING, options_config_t::owned_strings, option_descriptor_t::owns_memory, and option_descriptor_t::type.