ascii-chat 0.8.38
Real-time terminal-based video chat with ASCII art conversion
Loading...
Searching...
No Matches
lib/options/manpage/content/options.c
Go to the documentation of this file.
1
7#include <ascii-chat/options/manpage/content/options.h>
8#include <ascii-chat/log/logging.h>
9#include <ascii-chat/common.h>
10#include <ascii-chat/options/manpage.h>
11#include <ascii-chat/options/builder.h>
12#include <stdio.h>
13#include <stdlib.h>
14#include <string.h>
15#include <limits.h>
16
17char *manpage_content_generate_options(const options_config_t *config) {
18 if (!config || config->num_descriptors == 0) {
19 char *buffer = SAFE_MALLOC(1, char *);
20 buffer[0] = '\0';
21 return buffer;
22 }
23
24 // Use a growing buffer for dynamic content
25 size_t buffer_capacity = 8192;
26 char *buffer = SAFE_MALLOC(buffer_capacity, char *);
27 size_t offset = 0;
28
29 // Add introductory paragraph explaining mode flags, defaults, and env vars
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 // Build list of unique groups in order of first appearance
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 // Skip hidden options
68 if (desc->hide_from_mode_help || desc->hide_from_binary_help || !desc->group) {
69 continue;
70 }
71
72 // Check if this group is already in the list
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 // Add new group to list
82 if (!group_exists && num_unique_groups < config->num_descriptors) {
83 unique_groups[num_unique_groups++] = desc->group;
84 }
85 }
86
87 // Print options grouped by category
88 for (size_t g = 0; g < num_unique_groups; g++) {
89 const char *current_group = unique_groups[g];
90
91 // Add section heading for each group
92 offset += safe_snprintf(buffer + offset, buffer_capacity - offset, ".SS %s\n", current_group);
93
94 // Print all options in this group
95 for (size_t i = 0; i < config->num_descriptors; i++) {
96 const option_descriptor_t *desc = &config->descriptors[i];
97
98 // Skip if not in current group or if hidden
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 // Ensure buffer is large enough
111 if (offset + 512 >= buffer_capacity) {
112 buffer_capacity *= 2;
113 buffer = SAFE_REALLOC(buffer, buffer_capacity, char *);
114 }
115
116 // Start option item
117 offset += safe_snprintf(buffer + offset, buffer_capacity - offset, ".TP\n");
118
119 // Write option flags
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 // Add argument placeholder for value-taking options
128 if (desc->type != OPTION_TYPE_ACTION) {
129 // Check for custom placeholder first, fall back to type-based placeholder
130 const char *placeholder =
131 desc->arg_placeholder ? desc->arg_placeholder : options_get_type_placeholder(desc->type);
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 // Write help text
140 if (desc->help_text) {
141 offset += safe_snprintf(buffer + offset, buffer_capacity - offset, "%s", escape_groff_special(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 // Add default value if present
152 if (desc->default_value) {
153 char default_buf[256];
154 memset(default_buf, 0, sizeof(default_buf)); // Initialize buffer to prevent garbage
155 int n = options_format_default_value(desc->type, desc->default_value, default_buf, sizeof(default_buf));
156 // Skip empty strings (like --grep's default of "")
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) {
160 offset += safe_snprintf(buffer + offset, buffer_capacity - offset, "%s", escape_groff_special(default_buf));
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 // Add mode information if applicable
169 const char *mode_str = format_mode_names(desc->mode_bitmask);
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 // Add REQUIRED note if applicable
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}
191
192void manpage_content_free_options(char *content) {
193 if (content) {
194 SAFE_FREE(content);
195 }
196}
char * manpage_content_generate_options(const options_config_t *config)
void manpage_content_free_options(char *content)
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.
Definition system.c:456