17 {
18 if (!config || config->num_descriptors == 0) {
19 char *buffer = SAFE_MALLOC(1, char *);
20 buffer[0] = '\0';
21 return buffer;
22 }
23
24
25 size_t buffer_capacity = 8192;
26 char *buffer = SAFE_MALLOC(buffer_capacity, char *);
27 size_t offset = 0;
28
29
30 offset +=
31 safe_snprintf(buffer + offset, buffer_capacity - offset,
"Each option shows which modes it applies to with\n");
32 offset +=
safe_snprintf(buffer + offset, buffer_capacity - offset,
".B (modes: ...)\n");
33 offset +=
safe_snprintf(buffer + offset, buffer_capacity - offset,
"notation. Default values are shown with\n");
34 offset +=
safe_snprintf(buffer + offset, buffer_capacity - offset,
".B (default: ...).\n");
35 offset +=
safe_snprintf(buffer + offset, buffer_capacity - offset,
"See the\n");
36 offset +=
safe_snprintf(buffer + offset, buffer_capacity - offset,
".B ENVIRONMENT\n");
37 offset +=
safe_snprintf(buffer + offset, buffer_capacity - offset,
"section for environment variable equivalents.\n");
38 offset +=
safe_snprintf(buffer + offset, buffer_capacity - offset,
".PP\n");
39 offset +=
safe_snprintf(buffer + offset, buffer_capacity - offset,
"Configuration precedence (lowest to highest):\n");
40 offset +=
safe_snprintf(buffer + offset, buffer_capacity - offset,
".B config.toml\n");
41 offset +=
safe_snprintf(buffer + offset, buffer_capacity - offset,
"(see\n");
42 offset +=
safe_snprintf(buffer + offset, buffer_capacity - offset,
".BR ascii-chat (5))\n");
43 offset +=
safe_snprintf(buffer + offset, buffer_capacity - offset,
"< environment variables <\n");
44 offset +=
safe_snprintf(buffer + offset, buffer_capacity - offset,
".B command-line flags.\n");
45 offset +=
safe_snprintf(buffer + offset, buffer_capacity - offset,
"Later values override earlier ones.\n");
46 offset +=
safe_snprintf(buffer + offset, buffer_capacity - offset,
".PP\n");
47 offset +=
safe_snprintf(buffer + offset, buffer_capacity - offset,
"Options marked\n");
48 offset +=
safe_snprintf(buffer + offset, buffer_capacity - offset,
".B global\n");
49 offset +=
safe_snprintf(buffer + offset, buffer_capacity - offset,
50 "must be passed before any mode and apply to all modes.\n");
51 offset +=
safe_snprintf(buffer + offset, buffer_capacity - offset,
52 "Mode-specific options are passed after the mode name.\n");
53 offset +=
safe_snprintf(buffer + offset, buffer_capacity - offset,
".PP\n");
54 offset +=
safe_snprintf(buffer + offset, buffer_capacity - offset,
"The default mode (running\n");
55 offset +=
safe_snprintf(buffer + offset, buffer_capacity - offset,
".B ascii-chat\n");
56 offset +=
safe_snprintf(buffer + offset, buffer_capacity - offset,
57 "with no mode) accepts both global and default mode options with no mode specified.\n");
58 offset +=
safe_snprintf(buffer + offset, buffer_capacity - offset,
".PP\n");
59
60
61 const char **unique_groups = SAFE_MALLOC(config->num_descriptors * sizeof(const char *), const char **);
62 size_t num_unique_groups = 0;
63
64 for (size_t i = 0; i < config->num_descriptors; i++) {
65 const option_descriptor_t *desc = &config->descriptors[i];
66
67
68 if (desc->hide_from_mode_help || desc->hide_from_binary_help || !desc->group) {
69 continue;
70 }
71
72
73 bool group_exists = false;
74 for (size_t j = 0; j < num_unique_groups; j++) {
75 if (unique_groups[j] && strcmp(unique_groups[j], desc->group) == 0) {
76 group_exists = true;
77 break;
78 }
79 }
80
81
82 if (!group_exists && num_unique_groups < config->num_descriptors) {
83 unique_groups[num_unique_groups++] = desc->group;
84 }
85 }
86
87
88 for (size_t g = 0; g < num_unique_groups; g++) {
89 const char *current_group = unique_groups[g];
90
91
92 offset +=
safe_snprintf(buffer + offset, buffer_capacity - offset,
".SS %s\n", current_group);
93
94
95 for (size_t i = 0; i < config->num_descriptors; i++) {
96 const option_descriptor_t *desc = &config->descriptors[i];
97
98
99 bool is_binary_option = (desc->mode_bitmask & OPTION_MODE_BINARY) != 0;
100 bool applies_to_mode = false;
101 if (is_binary_option) {
102 applies_to_mode = !desc->hide_from_binary_help;
103 } else {
104 applies_to_mode = !desc->hide_from_mode_help;
105 }
106 if (!applies_to_mode || !desc->group || strcmp(desc->group, current_group) != 0) {
107 continue;
108 }
109
110
111 if (offset + 512 >= buffer_capacity) {
112 buffer_capacity *= 2;
113 buffer = SAFE_REALLOC(buffer, buffer_capacity, char *);
114 }
115
116
117 offset +=
safe_snprintf(buffer + offset, buffer_capacity - offset,
".TP\n");
118
119
120 if (desc->short_name && desc->short_name != '\0') {
121 offset +=
safe_snprintf(buffer + offset, buffer_capacity - offset,
".B \\-%c, \\-\\-%s", desc->short_name,
122 desc->long_name);
123 } else {
124 offset +=
safe_snprintf(buffer + offset, buffer_capacity - offset,
".B \\-\\-%s", desc->long_name);
125 }
126
127
128 if (desc->type != OPTION_TYPE_ACTION) {
129
130 const char *placeholder =
132 if (placeholder && *placeholder) {
133 offset +=
safe_snprintf(buffer + offset, buffer_capacity - offset,
" \\fI%s\\fR", placeholder);
134 }
135 }
136
137 offset +=
safe_snprintf(buffer + offset, buffer_capacity - offset,
"\n");
138
139
140 if (desc->help_text) {
142 if (!desc->default_value) {
143 offset +=
safe_snprintf(buffer + offset, buffer_capacity - offset,
"\n");
144 } else {
145 offset +=
safe_snprintf(buffer + offset, buffer_capacity - offset,
" ");
146 }
147 } else if (desc->default_value) {
148 offset +=
safe_snprintf(buffer + offset, buffer_capacity - offset,
" ");
149 }
150
151
152 if (desc->default_value) {
153 char default_buf[256];
154 memset(default_buf, 0, sizeof(default_buf));
156
157 if (n > 0 && default_buf[0] != '\0') {
158 offset +=
safe_snprintf(buffer + offset, buffer_capacity - offset,
"(default: ");
159 if (desc->type == OPTION_TYPE_STRING) {
161 } else {
162 offset +=
safe_snprintf(buffer + offset, buffer_capacity - offset,
"%s", default_buf);
163 }
164 offset +=
safe_snprintf(buffer + offset, buffer_capacity - offset,
")\n");
165 }
166 }
167
168
170 if (mode_str && strcmp(mode_str, "all modes") != 0) {
171 if (strcmp(mode_str, "global") == 0) {
172 offset +=
safe_snprintf(buffer + offset, buffer_capacity - offset,
"(mode: %s)\n", mode_str);
173 } else {
174 offset +=
safe_snprintf(buffer + offset, buffer_capacity - offset,
"(modes: %s)\n", mode_str);
175 }
176 }
177
178
179 if (desc->required) {
180 offset +=
safe_snprintf(buffer + offset, buffer_capacity - offset,
"[REQUIRED]\n");
181 }
182 }
183 }
184
185 SAFE_FREE(unique_groups);
186 offset +=
safe_snprintf(buffer + offset, buffer_capacity - offset,
"\n");
187
188 log_debug("Generated OPTIONS section (%zu bytes)", offset);
189 return buffer;
190}
int options_format_default_value(option_type_t type, const void *default_value, char *buf, size_t bufsize)
const char * options_get_type_placeholder(option_type_t type)
const char * format_mode_names(option_mode_bitmask_t mode_bitmask)
const char * escape_groff_special(const char *str)
int safe_snprintf(char *buffer, size_t buffer_size, const char *format,...)
Safe formatted string printing to buffer.