ascii-chat 0.8.38
Real-time terminal-based video chat with ASCII art conversion
Loading...
Searching...
No Matches
env.c
Go to the documentation of this file.
1
6#include <ascii-chat/util/env.h>
7#include <ascii-chat/common.h>
8#include <ascii-chat/log/logging.h>
9#include <stdio.h>
10#include <stdlib.h>
11#include <string.h>
12
21static const char *find_unescaped_semicolon(const char *str) {
22 if (!str) {
23 return NULL;
24 }
25
26 const char *p = str;
27 while (*p) {
28 if (*p == '\\' && *(p + 1)) {
29 // Skip escaped character (\\ or \;)
30 p += 2;
31 continue;
32 }
33 if (*p == ';') {
34 return p;
35 }
36 p++;
37 }
38 return NULL;
39}
40
48static void unescape_response(char *str) {
49 if (!str) {
50 return;
51 }
52
53 char *read = str;
54 char *write = str;
55
56 while (*read) {
57 if (*read == '\\' && *(read + 1)) {
58 // Escaped character: \; → ; or \\ → \.
59 read++; // Skip backslash
60 *write++ = *read++;
61 } else {
62 *write++ = *read++;
63 }
64 }
65 *write = '\0';
66}
67
93bool env_pop_prompt_response(char *response_out, size_t response_size) {
94 if (!response_out || response_size < 2) {
95 return false;
96 }
97
98 const char *env_value = SAFE_GETENV("ASCII_CHAT_QUESTION_PROMPT_RESPONSE");
99 if (!env_value || env_value[0] == '\0') {
100 return false;
101 }
102
103 // Validate: no leading semicolon (unless escaped)
104 if (env_value[0] == ';') {
105 log_warn("Invalid ASCII_CHAT_QUESTION_PROMPT_RESPONSE format: leading semicolon");
106 return false;
107 }
108
109 // Find the first unescaped semicolon
110 const char *semicolon = find_unescaped_semicolon(env_value);
111
112 if (semicolon == NULL) {
113 // No semicolon - this is the last (or only) response
114 size_t len = strlen(env_value);
115 if (len == 0) {
116 return false;
117 }
118
119 // Validate: ensure it's not empty
120 if (len >= response_size) {
121 log_warn("Response too long: %zu bytes (max: %zu)", len, response_size - 1);
122 return false;
123 }
124
125 // Copy the response
126 SAFE_STRNCPY(response_out, env_value, response_size);
127
128 // Unescape the response (\; → ; and \\ → \‍)
129 unescape_response(response_out);
130
131 // Clear the environment variable
132#ifdef _WIN32
133 _putenv("ASCII_CHAT_QUESTION_PROMPT_RESPONSE=");
134#else
135 unsetenv("ASCII_CHAT_QUESTION_PROMPT_RESPONSE");
136#endif
137
138 log_dev("Popped last response from stack: '%s' (cleared env)", response_out);
139 return true;
140 }
141
142 // Calculate response length
143 size_t response_len = (size_t)(semicolon - env_value);
144
145 // Validate: no empty segment
146 if (response_len == 0) {
147 log_warn("Invalid ASCII_CHAT_QUESTION_PROMPT_RESPONSE format: empty segment");
148 return false;
149 }
150
151 if (response_len >= response_size) {
152 log_warn("Response too long: %zu bytes (max: %zu)", response_len, response_size - 1);
153 return false;
154 }
155
156 // Copy the first response
157 memcpy(response_out, env_value, response_len);
158 response_out[response_len] = '\0';
159
160 // Unescape the response (\; → ; and \\ → \‍)
161 unescape_response(response_out);
162
163 // Calculate remaining stack (skip the semicolon)
164 const char *remaining = semicolon + 1;
165
166 // Validate: if remaining is empty or only semicolons, clear the env
167 bool remaining_valid = false;
168 for (const char *p = remaining; *p; p++) {
169 if (*p != ';') {
170 remaining_valid = true;
171 break;
172 }
173 }
174
175 // Update environment variable with remaining responses
176 if (remaining_valid && remaining[0] != '\0') {
177 char new_env[1024];
178 int written = snprintf(new_env, sizeof(new_env), "ASCII_CHAT_QUESTION_PROMPT_RESPONSE=%s", remaining);
179 if (written < 0 || (size_t)written >= sizeof(new_env)) {
180 log_warn("Failed to format new environment variable");
181 return false;
182 }
183
184#ifdef _WIN32
185 _putenv(new_env);
186#else
187 // For POSIX, we need to use setenv
188 if (setenv("ASCII_CHAT_QUESTION_PROMPT_RESPONSE", remaining, 1) != 0) {
189 log_warn("Failed to update ASCII_CHAT_QUESTION_PROMPT_RESPONSE");
190 return false;
191 }
192#endif
193
194 log_debug("Popped response from stack: '%s' (remaining: '%s')", response_out, remaining);
195 } else {
196 // No valid remaining responses - clear the env
197#ifdef _WIN32
198 _putenv("ASCII_CHAT_QUESTION_PROMPT_RESPONSE=");
199#else
200 unsetenv("ASCII_CHAT_QUESTION_PROMPT_RESPONSE");
201#endif
202
203 log_debug("Popped response from stack: '%s' (cleared env)", response_out);
204 }
205
206 return true;
207}
208
215 const char *env_value = SAFE_GETENV("ASCII_CHAT_QUESTION_PROMPT_RESPONSE");
216 if (!env_value || env_value[0] == '\0' || env_value[0] == ';') {
217 return false;
218 }
219
220 // Check if it's all semicolons
221 for (const char *p = env_value; *p; p++) {
222 if (*p != ';') {
223 return true;
224 }
225 }
226
227 return false;
228}
229
251 const char *env_value = SAFE_GETENV("ASCII_CHAT_QUESTION_PROMPT_RESPONSE");
252 if (!env_value) {
253 return true; // Not set is valid (no automated responses)
254 }
255
256 if (env_value[0] == '\0') {
257 return true; // Empty is valid (no automated responses)
258 }
259
260 // Invalid: leading semicolon (unless escaped)
261 if (env_value[0] == ';') {
262 return false;
263 }
264
265 // Check for consecutive unescaped semicolons or empty segments
266 const char *p = env_value;
267 bool last_was_semicolon = false;
268
269 while (*p) {
270 if (*p == '\\' && *(p + 1)) {
271 // Skip escaped character
272 last_was_semicolon = false;
273 p += 2;
274 continue;
275 }
276 if (*p == ';') {
277 if (last_was_semicolon) {
278 return false; // Consecutive unescaped semicolons (empty segment)
279 }
280 last_was_semicolon = true;
281 } else {
282 last_was_semicolon = false;
283 }
284 p++;
285 }
286
287 // Trailing semicolon is allowed
288 return true;
289}
bool env_validate_prompt_response_format(void)
Validate ASCII_CHAT_QUESTION_PROMPT_RESPONSE format.
Definition env.c:250
bool env_has_prompt_response(void)
Check if ASCII_CHAT_QUESTION_PROMPT_RESPONSE has any responses available.
Definition env.c:214
bool env_pop_prompt_response(char *response_out, size_t response_size)
Pop the first response from ASCII_CHAT_QUESTION_PROMPT_RESPONSE stack.
Definition env.c:93