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