ascii-chat
Binary v0.15.83-53-gb711d8720
Library lib/v0.3.2-701-gb711d8720
Video chat in your terminal
Loading...
Searching...
No Matches
manpage/content/options.c
Go to the documentation of this file.
1
6
7/* Storage capacities in elements; text buffers include the terminating NUL. */
8#define OPTIONS_MANPAGE_CONTENT_OPTIONS_DEFAULT_BUFFER_SIZE 256
9
11#include <ascii-chat/log/log.h>
12#include <ascii-chat/common.h>
15#include <stdio.h>
16#include <stdlib.h>
17#include <string.h>
18#include <limits.h>
19
21 if (!config) {
22 char *buffer = SAFE_MALLOC(1, char *);
23 buffer[0] = '\0';
24 return buffer;
25 }
26
27 if (config->num_descriptors == 0) {
28 // Generate fallback content when no options are defined
29 size_t buffer_capacity = 512;
30 char *buffer = SAFE_MALLOC(buffer_capacity, char *);
31 size_t offset = 0;
32 offset += safe_snprintf(buffer + offset, buffer_capacity - offset,
33 ".PP\nNo command-line options are available for this mode.\n");
34 log_debug("Generated OPTIONS section (fallback - no options found)");
35 return buffer;
36 }
37
38 // Use a growing buffer for dynamic content
39 size_t buffer_capacity = 8192;
40 char *buffer = SAFE_MALLOC(buffer_capacity, char *);
41 size_t offset = 0;
42
43 // Add introductory paragraph explaining mode flags, defaults, and env vars
44 offset +=
45 safe_snprintf(buffer + offset, buffer_capacity - offset, "Each option shows which modes it applies to with\n");
46 offset += safe_snprintf(buffer + offset, buffer_capacity - offset, ".B (modes: ...)\n");
47 offset += safe_snprintf(buffer + offset, buffer_capacity - offset, "notation. Default values are shown with\n");
48 offset += safe_snprintf(buffer + offset, buffer_capacity - offset, ".B (default: ...).\n");
49 offset += safe_snprintf(buffer + offset, buffer_capacity - offset, "See the\n");
50 offset += safe_snprintf(buffer + offset, buffer_capacity - offset, ".B ENVIRONMENT\n");
51 offset += safe_snprintf(buffer + offset, buffer_capacity - offset, "section for environment variable equivalents.\n");
52 offset += safe_snprintf(buffer + offset, buffer_capacity - offset, ".PP\n");
53 offset += safe_snprintf(buffer + offset, buffer_capacity - offset, "Configuration precedence (lowest to highest):\n");
54 offset += safe_snprintf(buffer + offset, buffer_capacity - offset, ".B config.toml\n");
55 offset += safe_snprintf(buffer + offset, buffer_capacity - offset, "(see\n");
56 offset += safe_snprintf(buffer + offset, buffer_capacity - offset, ".BR ascii-chat (5))\n");
57 offset += safe_snprintf(buffer + offset, buffer_capacity - offset, "< environment variables <\n");
58 offset += safe_snprintf(buffer + offset, buffer_capacity - offset, ".B command-line flags.\n");
59 offset += safe_snprintf(buffer + offset, buffer_capacity - offset, "Later values override earlier ones.\n");
60 offset += safe_snprintf(buffer + offset, buffer_capacity - offset, ".PP\n");
61 offset += safe_snprintf(buffer + offset, buffer_capacity - offset, "Options marked\n");
62 offset += safe_snprintf(buffer + offset, buffer_capacity - offset, ".B global\n");
63 offset += safe_snprintf(buffer + offset, buffer_capacity - offset,
64 "must be passed before any mode and apply to all modes.\n");
65 offset += safe_snprintf(buffer + offset, buffer_capacity - offset,
66 "Mode-specific options are passed after the mode name.\n");
67 offset += safe_snprintf(buffer + offset, buffer_capacity - offset, ".PP\n");
68 offset += safe_snprintf(buffer + offset, buffer_capacity - offset, "The default mode (running\n");
69 offset += safe_snprintf(buffer + offset, buffer_capacity - offset, ".B ascii-chat\n");
70 offset += safe_snprintf(buffer + offset, buffer_capacity - offset,
71 "with no mode) accepts both global and default mode options with no mode specified.\n");
72 offset += safe_snprintf(buffer + offset, buffer_capacity - offset, ".PP\n");
73
74 // Build list of unique groups in order of first appearance
75 const char **unique_groups = SAFE_MALLOC(config->num_descriptors * sizeof(const char *), const char **);
76 size_t num_unique_groups = 0;
77
78 for (size_t i = 0; i < config->num_descriptors; i++) {
79 const option_descriptor_t *desc = &config->descriptors[i];
80
81 // Skip hidden options
82 if (desc->hide_from_mode_help || desc->hide_from_binary_help || !desc->group) {
83 continue;
84 }
85
86 // Check if this group is already in the list
87 bool group_exists = false;
88 for (size_t j = 0; j < num_unique_groups; j++) {
89 if (unique_groups[j] && strcmp(unique_groups[j], desc->group) == 0) {
90 group_exists = true;
91 break;
92 }
93 }
94
95 // Add new group to list
96 if (!group_exists && num_unique_groups < config->num_descriptors) {
97 unique_groups[num_unique_groups++] = desc->group;
98 }
99 }
100
101 // Print options grouped by category
102 for (size_t g = 0; g < num_unique_groups; g++) {
103 const char *current_group = unique_groups[g];
104
105 // Add section heading for each group
106 offset += safe_snprintf(buffer + offset, buffer_capacity - offset, ".SS %s\n", current_group);
107
108 // Print all options in this group
109 for (size_t i = 0; i < config->num_descriptors; i++) {
110 const option_descriptor_t *desc = &config->descriptors[i];
111
112 // Skip if not in current group or if hidden
113 bool is_binary_option = (desc->mode_bitmask & OPTION_MODE_BINARY) != 0;
114 bool applies_to_mode = false;
115 if (is_binary_option) {
116 applies_to_mode = !desc->hide_from_binary_help;
117 } else {
118 applies_to_mode = !desc->hide_from_mode_help;
119 }
120 if (!applies_to_mode || !desc->group || strcmp(desc->group, current_group) != 0) {
121 continue;
122 }
123
124 // Ensure buffer is large enough
125 if (offset + 512 >= buffer_capacity) {
126 buffer_capacity *= 2;
127 buffer = SAFE_REALLOC(buffer, buffer_capacity, char *);
128 }
129
130 // Start option item
131 offset += safe_snprintf(buffer + offset, buffer_capacity - offset, ".TP\n");
132
133 // Write option flags
134 if (desc->short_name && desc->short_name != '\0') {
135 offset += safe_snprintf(buffer + offset, buffer_capacity - offset, ".B \\-%c, \\-\\-%s", desc->short_name,
136 desc->long_name);
137 } else {
138 offset += safe_snprintf(buffer + offset, buffer_capacity - offset, ".B \\-\\-%s", desc->long_name);
139 }
140
141 // Add argument placeholder for value-taking options
142 if (desc->type != OPTION_TYPE_ACTION) {
143 // Check for custom placeholder first, fall back to type-based placeholder
144 const char *placeholder =
146 if (placeholder && *placeholder) {
147 offset += safe_snprintf(buffer + offset, buffer_capacity - offset, " \\fI%s\\fR", placeholder);
148 }
149 }
150
151 offset += safe_snprintf(buffer + offset, buffer_capacity - offset, "\n");
152
153 // Write help text
154 if (desc->help_text) {
155 offset += safe_snprintf(buffer + offset, buffer_capacity - offset, "%s", escape_groff_special(desc->help_text));
156 if (!desc->default_value) {
157 offset += safe_snprintf(buffer + offset, buffer_capacity - offset, "\n");
158 } else {
159 offset += safe_snprintf(buffer + offset, buffer_capacity - offset, " ");
160 }
161 } else if (desc->default_value) {
162 offset += safe_snprintf(buffer + offset, buffer_capacity - offset, " ");
163 }
164
165 // Add default value if present
166 if (desc->default_value) {
168 memset(default_buf, 0, sizeof(default_buf)); // Initialize buffer to prevent garbage
169 int n = options_format_default_value(desc->type, desc->default_value, default_buf, sizeof(default_buf));
170 // Skip empty strings (like --grep's default of "")
171 if (n > 0 && default_buf[0] != '\0') {
172 offset += safe_snprintf(buffer + offset, buffer_capacity - offset, "(default: ");
173 if (desc->type == OPTION_TYPE_STRING) {
174 offset += safe_snprintf(buffer + offset, buffer_capacity - offset, "%s", escape_groff_special(default_buf));
175 } else {
176 offset += safe_snprintf(buffer + offset, buffer_capacity - offset, "%s", default_buf);
177 }
178 offset += safe_snprintf(buffer + offset, buffer_capacity - offset, ")\n");
179 }
180 }
181
182 // Add mode information if applicable
183 const char *mode_str = format_mode_names(desc->mode_bitmask);
184 if (mode_str && strcmp(mode_str, "all modes") != 0) {
185 if (strcmp(mode_str, "global") == 0) {
186 offset += safe_snprintf(buffer + offset, buffer_capacity - offset, "(mode: %s)\n", mode_str);
187 } else {
188 offset += safe_snprintf(buffer + offset, buffer_capacity - offset, "(modes: %s)\n", mode_str);
189 }
190 }
191
192 // Add REQUIRED note if applicable
193 if (desc->required) {
194 offset += safe_snprintf(buffer + offset, buffer_capacity - offset, "[REQUIRED]\n");
195 }
196 }
197 }
198
199 SAFE_FREE(unique_groups);
200 offset += safe_snprintf(buffer + offset, buffer_capacity - offset, "\n");
201
202 log_debug("Generated OPTIONS section (%zu bytes)", offset);
203 return buffer;
204}
205
206void manpage_content_free_options(char *content) {
207 if (content) {
208 SAFE_FREE(content);
209 }
210}
size_t buffer_capacity(const void *data)
Definition buffer.c:280
Options builder API for flexible command-line option configuration.
@ OPTION_TYPE_STRING
String value (–name foo).
Definition builder.h:169
@ OPTION_TYPE_ACTION
Action that executes and may exit (–list-webcams, etc.).
Definition builder.h:172
⚙️ Common definitions, error codes, macros, and types shared throughout the application
#define SAFE_REALLOC(ptr, size, cast)
Definition common.h:285
#define SAFE_FREE(ptr)
Definition common.h:377
#define SAFE_MALLOC(size, cast)
Definition common.h:265
#define log_debug(...)
Log a DEBUG message.
Definition log/log.h:595
const char * escape_groff_special(const char *str)
Escape special groff/troff characters in text.
Definition manpage.c:46
const char * format_mode_names(option_mode_bitmask_t mode_bitmask)
Format option mode names as human-readable string.
Definition manpage.c:50
@ OPTION_MODE_BINARY
Binary-level options (parsed before mode detection).
Definition options.h:945
int safe_snprintf(char *buffer, size_t buffer_size, const char *format,...)
Platform-safe snprintf wrapper.
📝 Logging API with multiple log levels and terminal output control
#define OPTIONS_MANPAGE_CONTENT_OPTIONS_DEFAULT_BUFFER_SIZE
char * manpage_content_generate_options(const options_config_t *config)
Generate OPTIONS section content.
void manpage_content_free_options(char *content)
Free generated options content.
OPTIONS section generator for man pages.
Man page template generation from options builder.
int options_format_default_value(option_type_t type, const void *default_value, char *buf, size_t bufsize)
Format option default value to string.
const char * options_get_type_placeholder(option_type_t type)
Get placeholder string for option type.
🔤 String Manipulation and Shell Escaping Utilities
The complete registry entry for one command-line/configuration option.
Definition builder.h:249
option_mode_bitmask_t mode_bitmask
Which modes this option applies to.
Definition builder.h:286
const char * help_text
Description for –help.
Definition builder.h:259
bool hide_from_mode_help
If true, don't show in mode-specific help (binary-level only).
Definition builder.h:262
bool hide_from_binary_help
If true, don't show in binary-level help (e.g., in release builds).
Definition builder.h:263
const char * arg_placeholder
Custom argument placeholder (e.g., "SHELL [FILE]" instead of "STR").
Definition builder.h:261
const char * group
Group name for help sections (e.g., "NETWORK OPTIONS").
Definition builder.h:260
const void * default_value
Pointer to default value (or NULL if required).
Definition builder.h:266
bool required
If true, option must be provided.
Definition builder.h:267
const char * long_name
Long option name (e.g., "port").
Definition builder.h:251
char short_name
Short option char (e.g., 'p', or '\0' if none).
Definition builder.h:252
option_type_t type
Value type.
Definition builder.h:255
Immutable parsing tables produced from an options builder.
Definition builder.h:414
option_descriptor_t * descriptors
Array of option descriptors.
Definition builder.h:415
size_t num_descriptors
Number of descriptors.
Definition builder.h:416