ascii-chat 0.8.38
Real-time terminal-based video chat with ASCII art conversion
Loading...
Searching...
No Matches
string.c File Reference

🔤 String manipulation utilities: ASCII escaping, trimming, case conversion, and formatting More...

Go to the source code of this file.

Macros

#define COLORED_BUFFERS   4
 
#define COLORED_BUFFER_SIZE   256
 

Functions

void escape_ascii (const char *str, const char *escape_char, char *out_buffer, size_t out_buffer_size)
 
bool validate_shell_safe (const char *str, const char *allowed_chars)
 
bool escape_shell_single_quotes (const char *str, char *out_buffer, size_t out_buffer_size)
 
bool escape_shell_double_quotes (const char *str, char *out_buffer, size_t out_buffer_size)
 
bool string_needs_shell_quoting (const char *str)
 
bool escape_path_for_shell (const char *path, char *out_buffer, size_t out_buffer_size)
 
const char * colored_string (log_color_t color, const char *text)
 

Detailed Description

🔤 String manipulation utilities: ASCII escaping, trimming, case conversion, and formatting

Definition in file util/string.c.

Macro Definition Documentation

◆ COLORED_BUFFER_SIZE

#define COLORED_BUFFER_SIZE   256

◆ COLORED_BUFFERS

#define COLORED_BUFFERS   4

Function Documentation

◆ colored_string()

const char * colored_string ( log_color_t  color,
const char *  text 
)

Definition at line 271 of file util/string.c.

271 {
272#define COLORED_BUFFERS 4
273#define COLORED_BUFFER_SIZE 256
274 static char buffers[COLORED_BUFFERS][COLORED_BUFFER_SIZE];
275 static int buffer_idx = 0;
276
277 if (!text) {
278 return "";
279 }
280
281 // Check if we should use colors
282 // Use global flag that persists even after options cleanup
283 extern bool g_color_flag_passed;
284 extern bool g_color_flag_value;
285
286 bool use_colors = false;
287
288 // Priority 1: If --color was explicitly passed, force colors
290 use_colors = true;
291 }
292 // Priority 2: If --color NOT explicitly passed, use terminal detection
293 else if (!g_color_flag_passed) {
294 use_colors = terminal_should_color_output(STDOUT_FILENO);
295 }
296 // Priority 3: If --color=false was explicitly passed, disable colors
297 // (use_colors stays false)
298
299 if (!use_colors) {
300 // No colors, just return the text directly
301 return text;
302 }
303
304 // Use rotating buffer to handle multiple calls in same fprintf
305 char *current_buf = buffers[buffer_idx];
306 buffer_idx = (buffer_idx + 1) % COLORED_BUFFERS;
307
308 const char *color_code = log_level_color(color);
309 const char *reset_code = log_level_color(LOG_COLOR_RESET);
310
311 // Ensure we never pass NULL to snprintf (handle uninitialized color system)
312 if (!color_code)
313 color_code = "";
314 if (!reset_code)
315 reset_code = "";
316
317 // Format into rotating static buffer: color_code + text + reset_code
318 safe_snprintf(current_buf, COLORED_BUFFER_SIZE, "%s%s%s", color_code, text, reset_code);
319 return current_buf;
320}
ASCIICHAT_API bool g_color_flag_value
Definition common.c:50
ASCIICHAT_API bool g_color_flag_passed
Definition common.c:49
const char * log_level_color(log_color_t color)
bool terminal_should_color_output(int fd)
Determine if color output should be used.
int safe_snprintf(char *buffer, size_t buffer_size, const char *format,...)
Safe formatted string printing to buffer.
Definition system.c:456
#define COLORED_BUFFERS
#define COLORED_BUFFER_SIZE

References COLORED_BUFFER_SIZE, COLORED_BUFFERS, g_color_flag_passed, g_color_flag_value, log_level_color(), safe_snprintf(), and terminal_should_color_output().

Referenced by action_show_capabilities_immediate(), action_show_version(), asciichat_print_error_context(), colorize_log_message(), log_labeled(), log_template_apply(), options_config_calculate_max_col_width(), options_config_print_options_sections_with_width(), options_config_print_usage(), options_print_help_for_mode(), platform_is_binary_in_path(), platform_print_backtrace_symbols(), print_project_links(), session_display_render_help(), and splash_display_status().

◆ escape_ascii()

void escape_ascii ( const char *  str,
const char *  escape_char,
char *  out_buffer,
size_t  out_buffer_size 
)

Definition at line 17 of file util/string.c.

17 {
18 if (escape_char == NULL || str == NULL || !out_buffer || out_buffer_size == 0) {
19 SET_ERRNO(ERROR_INVALID_PARAM, "Invalid string or escape character or output buffer");
20 return;
21 }
22
23 // Count how many characters need escaping
24 size_t escape_count = 0;
25 for (size_t i = 0; i < strlen(str); i++) {
26 for (size_t j = 0; j < strlen(escape_char); j++) {
27 if (str[i] == escape_char[j]) {
28 escape_count++;
29 break;
30 }
31 }
32 }
33
34 size_t total_len = strlen(str) + escape_count;
35 if (out_buffer_size < total_len) {
36 SET_ERRNO(ERROR_INVALID_PARAM, "Output buffer is too small");
37 return;
38 }
39
40 size_t out_pos = 0;
41 for (size_t i = 0; i < strlen(str); i++) {
42 bool needs_escape = false;
43 for (size_t j = 0; j < strlen(escape_char); j++) {
44 if (str[i] == escape_char[j]) {
45 needs_escape = true;
46 break;
47 }
48 }
49
50 if (needs_escape) {
51 out_buffer[out_pos++] = '\\';
52 }
53 out_buffer[out_pos++] = str[i];
54 }
55
56 out_buffer[out_pos] = '\0';
57}

Referenced by display_mitm_warning().

◆ escape_path_for_shell()

bool escape_path_for_shell ( const char *  path,
char *  out_buffer,
size_t  out_buffer_size 
)

Definition at line 241 of file util/string.c.

241 {
242 if (!path || !out_buffer || out_buffer_size == 0) {
243 SET_ERRNO(ERROR_INVALID_PARAM, "Invalid path or output buffer");
244 return false;
245 }
246
247 // Check if quoting is needed
248 if (!string_needs_shell_quoting(path)) {
249 // No quoting needed, just copy the path
250 size_t path_len = strlen(path);
251 if (out_buffer_size <= path_len) {
252 SET_ERRNO(ERROR_BUFFER_OVERFLOW, "Output buffer too small for path");
253 return false;
254 }
255 SAFE_STRNCPY(out_buffer, path, out_buffer_size);
256 return true;
257 }
258
259 // Quoting is needed, use platform-specific escaping
260#ifdef _WIN32
261 return escape_shell_double_quotes(path, out_buffer, out_buffer_size);
262#else
263 return escape_shell_single_quotes(path, out_buffer, out_buffer_size);
264#endif
265}
bool escape_shell_double_quotes(const char *str, char *out_buffer, size_t out_buffer_size)
bool escape_shell_single_quotes(const char *str, char *out_buffer, size_t out_buffer_size)
bool string_needs_shell_quoting(const char *str)

References escape_shell_double_quotes(), escape_shell_single_quotes(), and string_needs_shell_quoting().

Referenced by gpg_sign_with_key().

◆ escape_shell_double_quotes()

bool escape_shell_double_quotes ( const char *  str,
char *  out_buffer,
size_t  out_buffer_size 
)

Definition at line 171 of file util/string.c.

171 {
172 if (!str || !out_buffer || out_buffer_size == 0) {
173 return false;
174 }
175
176 // Calculate required size: each " becomes \" (2 chars) instead of 1
177 // Each \ also needs escaping, so \\ becomes \\\\
178 // Worst case: all chars are quotes/backslashes, so we need strlen(str) * 2 + 2 (for surrounding quotes)
179 size_t required_size = 2; // Start and end quotes
180 for (size_t i = 0; str[i] != '\0'; i++) {
181 if (str[i] == '"' || str[i] == '\\') {
182 required_size += 2; // \" or '\\'
183 } else {
184 required_size += 1;
185 }
186 }
187
188 if (out_buffer_size < required_size) {
189 SET_ERRNO(ERROR_BUFFER_OVERFLOW, "Output buffer too small for shell escaping");
190 return false;
191 }
192
193 size_t out_pos = 0;
194 out_buffer[out_pos++] = '"'; // Opening quote
195
196 for (size_t i = 0; str[i] != '\0'; i++) {
197 if (str[i] == '"') {
198 // Escape double quote: " becomes \"
199 out_buffer[out_pos++] = '\\';
200 out_buffer[out_pos++] = '"';
201 } else if (str[i] == '\\') {
202 // Escape backslash: \ becomes '\\'
203 out_buffer[out_pos++] = '\\';
204 out_buffer[out_pos++] = '\\';
205 } else {
206 out_buffer[out_pos++] = str[i];
207 }
208 }
209
210 out_buffer[out_pos++] = '"'; // Closing quote
211 out_buffer[out_pos] = '\0';
212
213 return true;
214}

Referenced by escape_path_for_shell().

◆ escape_shell_single_quotes()

bool escape_shell_single_quotes ( const char *  str,
char *  out_buffer,
size_t  out_buffer_size 
)

Definition at line 129 of file util/string.c.

129 {
130 if (!str || !out_buffer || out_buffer_size == 0) {
131 return false;
132 }
133
134 // Calculate required size: each ' becomes '\'' (4 chars) instead of 1
135 // Worst case: all chars are quotes, so we need strlen(str) * 4 + 2 (for surrounding quotes)
136 size_t required_size = 2; // Start and end quotes
137 for (size_t i = 0; str[i] != '\0'; i++) {
138 if (str[i] == '\'') {
139 required_size += 4; // '\''
140 } else {
141 required_size += 1;
142 }
143 }
144
145 if (out_buffer_size < required_size) {
146 SET_ERRNO(ERROR_BUFFER_OVERFLOW, "Output buffer too small for shell escaping");
147 return false;
148 }
149
150 size_t out_pos = 0;
151 out_buffer[out_pos++] = '\''; // Opening quote
152
153 for (size_t i = 0; str[i] != '\0'; i++) {
154 if (str[i] == '\'') {
155 // Replace ' with '\'' (close quote, escaped quote, open quote)
156 out_buffer[out_pos++] = '\'';
157 out_buffer[out_pos++] = '\\';
158 out_buffer[out_pos++] = '\'';
159 out_buffer[out_pos++] = '\'';
160 } else {
161 out_buffer[out_pos++] = str[i];
162 }
163 }
164
165 out_buffer[out_pos++] = '\''; // Closing quote
166 out_buffer[out_pos] = '\0';
167
168 return true;
169}

Referenced by escape_path_for_shell(), and gpg_get_public_key().

◆ string_needs_shell_quoting()

bool string_needs_shell_quoting ( const char *  str)

Definition at line 216 of file util/string.c.

216 {
217 if (!str || str[0] == '\0') {
218 return false; // Empty strings don't need quoting
219 }
220
221 // Check for characters that need shell quoting
222 for (size_t i = 0; str[i] != '\0'; i++) {
223 char c = str[i];
224
225 // Whitespace characters always need quoting
226 if (c == ' ' || c == '\t' || c == '\n' || c == '\r') {
227 return true;
228 }
229
230 // Shell special characters
231 if (c == '"' || c == '\'' || c == '$' || c == '`' || c == '\\' || c == '<' || c == '>' || c == '&' || c == ';' ||
232 c == '|' || c == '(' || c == ')' || c == '[' || c == ']' || c == '{' || c == '}' || c == '*' || c == '?' ||
233 c == '!' || c == '~' || c == '#' || c == '@') {
234 return true;
235 }
236 }
237
238 return false;
239}

Referenced by escape_path_for_shell().

◆ validate_shell_safe()

bool validate_shell_safe ( const char *  str,
const char *  allowed_chars 
)

Definition at line 59 of file util/string.c.

59 {
60 if (!str) {
61 return false;
62 }
63
64 // Shell metacharacters that are dangerous
65 // ; & | $ ` \ " ' < > ( ) [ ] { } * ? ! ~ # @ space tab newline
66 const char *dangerous = ";|$`\\\"'<>()[]{}*?!~#@ \t\n\r";
67
68 // First pass: validate UTF-8 encoding
69 if (!utf8_is_valid(str)) {
70 return false;
71 }
72
73 // Second pass: validate each character for shell safety
74 // Convert to codepoints and check only ASCII range for dangerous characters
75 // (non-ASCII characters are allowed by default)
76 const uint8_t *p = (const uint8_t *)str;
77 while (*p) {
78 uint32_t codepoint;
79 int decode_len = utf8_decode(p, &codepoint);
80 if (decode_len < 0) {
81 return false; // Should not happen after utf8_is_valid check
82 }
83
84 // Only validate ASCII range where shell metacharacters exist
85 // Non-ASCII characters (codepoint > 127) are allowed
86 if (codepoint <= 127) {
87 unsigned char c = (unsigned char)codepoint;
88
89 // Allow ASCII alphanumeric characters
90 if (isalnum(c)) {
91 p += decode_len;
92 continue;
93 }
94
95 // Check if character is in allowed_chars list
96 if (allowed_chars) {
97 bool is_allowed = false;
98 for (size_t j = 0; allowed_chars[j] != '\0'; j++) {
99 if (c == (unsigned char)allowed_chars[j]) {
100 is_allowed = true;
101 break;
102 }
103 }
104 if (is_allowed) {
105 p += decode_len;
106 continue;
107 }
108 }
109
110 // Check if character is dangerous
111 bool is_dangerous = false;
112 for (size_t j = 0; dangerous[j] != '\0'; j++) {
113 if (c == (unsigned char)dangerous[j]) {
114 is_dangerous = true;
115 break;
116 }
117 }
118 if (is_dangerous) {
119 return false;
120 }
121 }
122
123 p += decode_len;
124 }
125
126 return true;
127}
bool utf8_is_valid(const char *str)
Definition utf8.c:158
int utf8_decode(const uint8_t *s, uint32_t *codepoint)
Definition utf8.c:18

References utf8_decode(), and utf8_is_valid().

Referenced by gpg_get_public_key().