ascii-chat 0.11.33
Video chat in your terminal
Loading...
Searching...
No Matches
env.c
Go to the documentation of this file.
1
7#include <ascii-chat/common.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 platform_unsetenv("ASCII_CHAT_QUESTION_PROMPT_RESPONSE");
133
134 log_dev("Popped last response from stack: '%s' (cleared env)", response_out);
135 return true;
136 }
137
138 // Calculate response length
139 size_t response_len = (size_t)(semicolon - env_value);
140
141 // Validate: no empty segment
142 if (response_len == 0) {
143 log_warn("Invalid ASCII_CHAT_QUESTION_PROMPT_RESPONSE format: empty segment");
144 return false;
145 }
146
147 if (response_len >= response_size) {
148 log_warn("Response too long: %zu bytes (max: %zu)", response_len, response_size - 1);
149 return false;
150 }
151
152 // Copy the first response
153 memcpy(response_out, env_value, response_len);
154 response_out[response_len] = '\0';
155
156 // Unescape the response (\; → ; and \\ → \‍)
157 unescape_response(response_out);
158
159 // Calculate remaining stack (skip the semicolon)
160 const char *remaining = semicolon + 1;
161
162 // Validate: if remaining is empty or only semicolons, clear the env
163 bool remaining_valid = false;
164 for (const char *p = remaining; *p; p++) {
165 if (*p != ';') {
166 remaining_valid = true;
167 break;
168 }
169 }
170
171 // Update environment variable with remaining responses
172 if (remaining_valid && remaining[0] != '\0') {
173 char new_env[1024];
174 int written = snprintf(new_env, sizeof(new_env), "ASCII_CHAT_QUESTION_PROMPT_RESPONSE=%s", remaining);
175 if (written < 0 || (size_t)written >= sizeof(new_env)) {
176 log_warn("Failed to format new environment variable");
177 return false;
178 }
179
180 if (platform_setenv("ASCII_CHAT_QUESTION_PROMPT_RESPONSE", remaining) != 0) {
181 log_warn("Failed to update ASCII_CHAT_QUESTION_PROMPT_RESPONSE");
182 return false;
183 }
184
185 log_debug("Popped response from stack: '%s' (remaining: '%s')", response_out, remaining);
186 } else {
187 // No valid remaining responses - clear the env
188 platform_unsetenv("ASCII_CHAT_QUESTION_PROMPT_RESPONSE");
189
190 log_debug("Popped response from stack: '%s' (cleared env)", response_out);
191 }
192
193 return true;
194}
195
202 const char *env_value = SAFE_GETENV("ASCII_CHAT_QUESTION_PROMPT_RESPONSE");
203 if (!env_value || env_value[0] == '\0' || env_value[0] == ';') {
204 return false;
205 }
206
207 // Check if it's all semicolons
208 for (const char *p = env_value; *p; p++) {
209 if (*p != ';') {
210 return true;
211 }
212 }
213
214 return false;
215}
216
238 const char *env_value = SAFE_GETENV("ASCII_CHAT_QUESTION_PROMPT_RESPONSE");
239 if (!env_value) {
240 return true; // Not set is valid (no automated responses)
241 }
242
243 if (env_value[0] == '\0') {
244 return true; // Empty is valid (no automated responses)
245 }
246
247 // Invalid: leading semicolon (unless escaped)
248 if (env_value[0] == ';') {
249 return false;
250 }
251
252 // Check for consecutive unescaped semicolons or empty segments
253 const char *p = env_value;
254 bool last_was_semicolon = false;
255
256 while (*p) {
257 if (*p == '\\' && *(p + 1)) {
258 // Skip escaped character
259 last_was_semicolon = false;
260 p += 2;
261 continue;
262 }
263 if (*p == ';') {
264 if (last_was_semicolon) {
265 return false; // Consecutive unescaped semicolons (empty segment)
266 }
267 last_was_semicolon = true;
268 } else {
269 last_was_semicolon = false;
270 }
271 p++;
272 }
273
274 // Trailing semicolon is allowed
275 return true;
276}
⚙️ Common definitions, error codes, macros, and types shared throughout the application
bool env_validate_prompt_response_format(void)
Validate ASCII_CHAT_QUESTION_PROMPT_RESPONSE format.
Definition env.c:237
bool env_has_prompt_response(void)
Check if ASCII_CHAT_QUESTION_PROMPT_RESPONSE has any responses available.
Definition env.c:201
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
Environment variable utilities with stack-based prompt responses.
#define SAFE_STRNCPY(dst, src, size)
Definition common.h:414
#define SAFE_GETENV(name)
Definition common.h:434
#define log_warn(...)
Log a WARN message.
Definition log/log.h:574
#define log_dev(...)
Log a DEV message (most verbose, development only)
Definition log/log.h:534
#define log_debug(...)
Log a DEBUG message.
Definition log/log.h:548
int platform_setenv(const char *name, const char *value)
Set an environment variable.
Definition wasm/system.c:50
int platform_unsetenv(const char *name)
Remove an environment variable.
Definition wasm/system.c:56
📝 Logging API with multiple log levels and terminal output control