/[sudobot]/branches/8.x/lib/common/env.c
ViewVC logotype

Annotation of /branches/8.x/lib/common/env.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 577 - (hide annotations)
Mon Jul 29 18:52:37 2024 UTC (8 months ago) by rakinar2
File MIME type: text/x-c
File size: 7735 byte(s)
chore: add old version archive branches (2.x to 9.x-dev)
1 rakinar2 577 #define _GNU_SOURCE
2    
3     #include <string.h>
4     #include <stdio.h>
5     #include <stdlib.h>
6     #include <unistd.h>
7     #include <errno.h>
8     #include <ctype.h>
9     #include <concord/chash.h>
10     #include "io/log.h"
11     #include "io/io.h"
12     #include "env.h"
13     #include "utils/xmalloc.h"
14     #include "utils/utils.h"
15     #include "utils/strutils.h"
16     #include "flags.h"
17     #include "sudobot.h"
18    
19     #define ENVTABLE_BUCKET struct envtable_bucket
20     #define ENVTABLE_HEAP 1
21     #define ENVTABLE_FREE_KEY(key) free(key)
22     #define ENVTABLE_HASH(key, hash) chash_string_hash(key, hash)
23     #define ENVTABLE_FREE_VALUE(value) free(value)
24     #define ENVTABLE_COMPARE(cmp_a, cmp_b) chash_string_compare(cmp_a, cmp_b)
25     #define ENVTABLE_INIT(bucket, _key, _value) chash_default_init(bucket, _key, _value)
26    
27     struct envtable_bucket
28     {
29     char *key;
30     char *value;
31     int state;
32     };
33    
34     struct envtable
35     {
36     int length;
37     int capacity;
38     struct envtable_bucket *buckets;
39     };
40    
41     static char *env_find_file_path();
42    
43     env_t *env_init()
44     {
45     env_t *env = xcalloc(1, sizeof (env_t));
46     struct envtable *table = chash_init(table, ENVTABLE);
47     env->table = table;
48     env->filepath = env_find_file_path();
49     env->error = NULL;
50     env->contents = NULL;
51     env->index = 0;
52     env->length = 0;
53     env->current_line = 1;
54     return env;
55     }
56    
57     static char *get_file_contents(FILE *file, size_t *sizeptr)
58     {
59     size_t size = 0;
60     char *buffer = NULL;
61    
62     fseek(file, 0, SEEK_END);
63     size = ftell(file);
64     fseek(file, 0, SEEK_SET);
65    
66     buffer = xmalloc(size + 1);
67    
68     if (fread(buffer, size, 1, file) <= 0)
69     return NULL;
70    
71     buffer[size] = 0;
72    
73     if (sizeptr != NULL)
74     *sizeptr = size;
75    
76     return buffer;
77     }
78    
79     static char *env_find_file_path()
80     {
81     char *path = opt_env_file_path;
82    
83     if (path == NULL)
84     {
85     char *cwd = NULL;
86     size_t len = 0;
87    
88     cwd = getcwd(NULL, 0);
89    
90     if (cwd == NULL)
91     sudobot_fatal_error("Failed to get the current working directory path");
92    
93     len = strlen(cwd);
94    
95     cwd = xrealloc(cwd, len + 6);
96     strncat(cwd, "/.env", 6);
97     path = cwd;
98     }
99     else
100     {
101     path = strdup(path);
102     }
103    
104     return path;
105     }
106    
107     static void env_file_read_contents(env_t *env)
108     {
109     char *path = env->filepath;
110    
111     log_info("Loading environment variables from: %s", path);
112    
113     FILE *file = fopen(path, "r");
114    
115     if (file == NULL)
116     {
117     if (errno == ENOENT && opt_env_file_path == NULL)
118     log_warn("No `.env` file was found in the current directory");
119     else
120     log_warn("Failed to open file: %s: %s", path, get_last_error());
121    
122     return;
123     }
124    
125     env->contents = get_file_contents(file, &env->length);
126     fclose(file);
127     }
128    
129     static bool is_newline(const char c)
130     {
131     return c == '\r' || c == '\n';
132     }
133    
134     static char *env_parse_var_name(env_t *env)
135     {
136     char *name = NULL;
137     size_t namelen = 0;
138    
139     while (env->index < env->length &&
140     isblank(env->contents[env->index]))
141     env->index++;
142    
143     while (env->index < env->length &&
144     (isalnum(env->contents[env->index]) || env->contents[env->index] == '_') &&
145     env->contents[env->index] != '=')
146     {
147     name = xrealloc(name, ++namelen);
148     name[namelen - 1] = env->contents[env->index];
149     env->index++;
150     }
151    
152     bool success = false;
153    
154     if (name == NULL)
155     env->error = strdup("Variable name must not be empty");
156     else if (env->contents[env->index] == '=')
157     success = true;
158     else if (is_newline(env->contents[env->index]))
159     env->error = strdup("Unexpected EOL");
160     else if (isspace(env->contents[env->index]))
161     env->error = strdup("Variable name must not contain spaces");
162     else if (!(isalnum(env->contents[env->index]) || env->contents[env->index] == '_'))
163     {
164     env->error = strdup("Invalid variable name: variable names must not contain anything except numbers, letters and underscores");
165     log_debug("C: %c", env->contents[env->index]);
166     }
167     else
168     success = true;
169    
170     if (!success || name == NULL)
171     {
172     free(name);
173     return NULL;
174     }
175    
176     name = xrealloc(name, ++namelen);
177     name[namelen - 1] = 0;
178     return name;
179     }
180    
181     static char *env_parse_var_value(env_t *env)
182     {
183     char *value = NULL;
184     size_t value_len = 0;
185     bool include_spaces = false;
186     char quote = '0';
187    
188     while (env->index < env->length &&
189     isblank(env->contents[env->index]))
190     env->index++;
191    
192     if (env->contents[env->index] == '"' || env->contents[env->index] == '\'')
193     {
194     quote = env->contents[env->index];
195     include_spaces = true;
196     env->index++;
197     }
198    
199     while (env->index < env->length &&
200     ((include_spaces && env->contents[env->index] != quote) ||
201     (!include_spaces && !isspace(env->contents[env->index]))))
202     {
203     if (include_spaces && is_newline(env->contents[env->index]))
204     {
205     free(value);
206     env->error = strdup("A variable value must not exceed one line");
207     return NULL;
208     }
209    
210     value = xrealloc(value, ++value_len);
211     value[value_len - 1] = env->contents[env->index];
212     env->index++;
213     }
214    
215     if (env->index >= env->length && (include_spaces || value_len == 0))
216     {
217     free(value);
218     env->error = strdup(include_spaces ? "Unexpected EOF: Unterminated string" : "Unexpected EOF");
219     return NULL;
220     }
221    
222     if (include_spaces && env->contents[env->index] != quote)
223     {
224     free(value);
225     env->error = strdup("Unterminated string");
226     return NULL;
227     }
228     else if (include_spaces)
229     env->index++;
230    
231     if (isspace(env->contents[env->index]))
232     env->index++;
233    
234     if (value == NULL)
235     return NULL;
236    
237     value = xrealloc(value, ++value_len);
238     value[value_len - 1] = 0;
239     return value;
240     }
241    
242     static bool env_parse_load(env_t *env)
243     {
244     while (env->index < env->length)
245     {
246     if (env->contents[env->index] == '#')
247     {
248     while (env->index < env->length && !is_newline(env->contents[env->index]))
249     env->index++;
250    
251     if (is_newline(env->contents[env->index]))
252     env->current_line++;
253    
254     continue;
255     }
256     else if (isspace(env->contents[env->index]))
257     {
258     if (is_newline(env->contents[env->index]))
259     env->current_line++;
260    
261     env->index++;
262     continue;
263     }
264     else
265     {
266     char *varname = env_parse_var_name(env);
267    
268     if (varname == NULL || env->error != NULL)
269     return false;
270    
271     env->index++;
272    
273     char *varval = env_parse_var_value(env);
274    
275     if (env->error != NULL)
276     return false;
277    
278     chash_assign(env->table, varname, varval, ENVTABLE);
279     continue;
280     }
281    
282     if (env->error != NULL)
283     return false;
284    
285     env->index++;
286     }
287    
288     return true;
289     }
290    
291     bool env_load(env_t *env)
292     {
293     env_file_read_contents(env);
294    
295     if (env->contents == NULL)
296     return false;
297    
298     return env_parse_load(env);
299     }
300    
301     void env_free(env_t *env)
302     {
303     chash_free(env->table, ENVTABLE);
304     free(env->error);
305     free(env->contents);
306     free(env->filepath);
307     free(env);
308     }
309    
310     const char *env_get_local(env_t *env, const char *restrict name)
311     {
312     int contains = chash_contains(env->table, "TOKEN", contains, ENVTABLE);;
313     char *value;
314    
315     if (contains == 0)
316     return NULL;
317    
318     value = chash_lookup(env->table, name, value, ENVTABLE);
319     return value;
320     }
321    
322     const char *env_get(env_t *env, const char *restrict name)
323     {
324     const char *value = env_get_local(env, name);
325    
326     if (value == NULL)
327     return getenv(name);
328    
329     return value;
330     }

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26