git.m455.casa

utils.h

clone url: git://git.m455.casa/utils.h


utils.c

1 #include <stdlib.h>
2 #include <string.h>
3 #include <stdio.h>
4
5 struct kv {
6 char *key;
7 char *value;
8 };
9
10 /* static void print_size_t(char* prefix, size_t size) { */
11 /* printf("%s: %lu\n", prefix, (unsigned long)size); */
12 /* } */
13
14 /* thank you k&r pg. 143 lol */
15 static char *str_dup(char *str) {
16 char *str_copy;
17
18 str_copy = malloc(strlen(str) + 1);
19 if (str_copy == NULL) {
20 perror("str_dup() malloc() returned NULL");
21 return NULL;
22 }
23
24 strcpy(str_copy, str);
25
26 return str_copy;
27 }
28
29 static const char *char_html_escape(char c) {
30 switch(c) {
31 case '&':
32 return "&amp;";
33 case '<':
34 return "&lt;";
35 case '>':
36 return "&gt;";
37 case '"':
38 return "&quot;";
39 case '\'':
40 return "&#39;";
41 default:
42 return NULL;
43 }
44 }
45
46 static char *str_html_escape(char *str) {
47 char *buf;
48 const char *entity;
49 size_t str_i;
50 size_t buf_size;
51
52 buf_size = 0;
53 for(str_i = 0; str_i < strlen(str); str_i++) {
54 entity = char_html_escape(str[str_i]);
55 if (entity == NULL) {
56 buf_size++;
57 }
58 else {
59 buf_size = buf_size + strlen(entity);
60 }
61 }
62
63 buf = malloc(buf_size + 1);
64 if (buf == NULL) {
65 perror("str_html_escape() malloc() returned NULL");
66 return NULL;
67 }
68 /* for strcat */
69 buf[0] = '\0';
70
71 /* we don't need to allocate space for the initial '\0' here, because it's
72 * overwritten by the first character of the string being appended by
73 * strncat.
74
75 * strncat also automatically appends a nul character at the end of each
76 * string concatination
77
78 * the '+ 1's after the strlen()s in each strncat call are so the nul
79 * character after the source string is included in the concatination.
80 */
81 for(str_i = 0; str_i < strlen(str); str_i++) {
82 entity = char_html_escape(str[str_i]);
83 if (entity == NULL) {
84 /* the '&' is here because strncat requires the second argument
85 * to be a pointer, so providing just a str[str_i] would mean we
86 * are providing just a value of type 'char'. the '&' operator
87 * turns values into pointers
88 */
89 strncat(buf, &str[str_i], 1);
90 }
91 else {
92 strcat(buf, entity);
93 }
94 }
95 return buf;
96 }
97
98 static char *str_slice(char *str, size_t start, size_t end) {
99 char *buf;
100
101 if (end < start) {
102 perror("error: str_slice() end index is less than start index");
103 return NULL;
104 }
105
106 buf = malloc((end - start) + 1);
107 if (buf == NULL) {
108 perror("str_slice() malloc() returned NULL");
109 return NULL;
110 }
111
112 strncpy(buf, str + start, end - start);
113
114 return buf;
115 }
116
117 static char *str_rep(char *str, char *from_str, char *to_str) {
118 char *buf;
119 char *found_from_str;
120 size_t occurences;
121
122 /* if any of these conditions are true, return the original string (str):
123 * - from_str and to_str are the same
124 * - no from_str was found in str
125 * - from_str is ""
126 */
127 if (strcmp(from_str, to_str) == 0
128 || strstr(str, from_str) == NULL
129 || (strlen(from_str) == 0 && strlen(to_str) > 0)) {
130 buf = str_dup(str);
131 return buf;
132 }
133
134 occurences = 0;
135 found_from_str = strstr(str, from_str);
136 while ((found_from_str = strstr(found_from_str, from_str)) != NULL) {
137 occurences++;
138 found_from_str = found_from_str + strlen(from_str);
139 }
140 /* first, subtrack all occurences of from_str from str */
141 buf = malloc((strlen(str) - (strlen(from_str) * occurences)) +
142 /* next, add all occurences of to_str to str */
143 (strlen(to_str) * occurences) +
144 /* +1 for nul terminator */
145 1);
146 if (buf == NULL) {
147 perror("str_rep() malloc() returned NULL");
148 return NULL;
149 }
150 /* for strcat */
151 buf[0] = '\0';
152
153 while(occurences != 0) {
154 found_from_str = strstr(str, from_str);
155 if (found_from_str == NULL) {
156 free(buf);
157 perror("str_rep() while() strstr() returned NULL");
158 return NULL;
159 }
160
161 /* add text before from_str:
162 * 'found_from_str - str' returns the index of first character of
163 * from_str. this index is used as a maximum amount of bytes to copy by
164 * strncat, so it won't actually include the first character.
165
166 * for example:
167
168 * if "you" is the from_str in the string "hello you", we have the
169 * following indices:
170
171 * |h|e|l|l|o| |y|o|u
172 * |0|1|2|3|4|5|6|7|8
173
174 * the first character of "you" ('y'), starts off at index 6.
175
176 * we convert this index (6) from int to size_t because strncat's
177 * 'count' parameter requires a type of size_t. strncat then uses the
178 * converted value as its limit of how many bytes to concatinate,
179 * resulting in us copying up to, and including, 6 bytes into buf like
180 * this:
181
182 * |h|e|l|l|o| |y|o|u
183 * |1|2|3|4|5|6|7|8|9
184 */
185 strncat(buf, str, (size_t)(found_from_str - str));
186
187 strcat(buf, to_str);
188
189 /* create a new str to operate on, which is everything after the
190 * from_str in str
191 */
192 str = found_from_str + strlen(from_str);
193 occurences--;
194 }
195
196 /* add remaining text that didn't contain the from_str to buffer: str is
197 * actually mutated in the while loop above, so 'str' is actually the range
198 * of text after the last character of the last instance of from_str in str.
199 */
200 strcat(buf, str);
201
202 return buf;
203 }
204
205 static char *str_format(char *str, char *from_str, char *to_str) {
206 char *mustached_from_str;
207 char *formatted_str;
208
209 mustached_from_str = malloc(strlen("{{") + strlen(from_str) + strlen("}}") + 1);
210 if (mustached_from_str == NULL) {
211 perror("str_format() malloc() returned NULL");
212 return NULL;
213 }
214
215 sprintf(mustached_from_str, "{{%s}}", from_str);
216
217 formatted_str = str_rep(str, mustached_from_str, to_str);
218 if (formatted_str == NULL) {
219 free(mustached_from_str);
220 perror("str_format() str_rep() returned NULL");
221 return NULL;
222 }
223
224 free(mustached_from_str);
225
226 return formatted_str;
227 }
228
229 static size_t pairs_in_file(char *file) {
230 FILE *file_stream;
231 char line[256];
232 char *token;
233 int token_count;
234 size_t pair_count;
235
236 file_stream = fopen(file, "r");
237 if (file_stream == NULL) {
238 perror("file_to_kvs() fopen() failed");
239 fclose(file_stream);
240 return 0;
241 }
242
243 pair_count = 0;
244 while (fgets(line, sizeof(line), file_stream) != NULL) {
245 token_count = 0;
246 token = strtok(line, "=\n");
247 if (token != NULL) {
248 token_count++;
249 token = strtok(NULL, "=\n");
250 if (token != NULL) {
251 token_count++;
252 }
253 }
254 if (token_count != 2) {
255 perror("pairs_in_file() while loop failed: incorrect key-value formatting in config file");
256 fclose(file_stream);
257 return 0;
258 }
259 pair_count++;
260 }
261 fclose(file_stream);
262
263 return pair_count;
264 }
265
266 /* this will eventuallyyyyy populate a template, but for now i just want to */
267 /* properly return a struct and figure out how to free its memory lol */
268 static struct kv *file_to_kvs(char *file) {
269 FILE *file_stream;
270 char line[256];
271 char *token;
272 size_t pair_count;
273 size_t i;
274 size_t j;
275 struct kv *config;
276
277 /* do this before fopen below, so we aren't trying to read the file while it's already open */
278 pair_count = pairs_in_file(file);
279 if (pair_count == 0) {
280 perror("file_to_kvs() pair_count is 0");
281 return NULL;
282 }
283
284 file_stream = fopen(file, "r");
285 if (file_stream == NULL) {
286 perror("file_to_kvs() fopen() failed");
287 fclose(file_stream);
288 return NULL;
289 }
290
291 config = calloc(pair_count, sizeof(struct kv));
292 if (config == NULL) {
293 perror("file_to_kvs() malloc() returned NULL");
294 fclose(file_stream);
295 return NULL;
296 }
297
298 i = 0;
299 while (fgets(line, sizeof(line), file_stream) != NULL) {
300 token = strtok(line, "=\n");
301 if (token != NULL) {
302 config[i].key = str_dup(token);
303 if (config[i].key == NULL) {
304 perror("file_to_kvs() config[i].key value is NULL");
305 for(j = 0; j < i; j++) {
306 free(config[j].key);
307 free(config[j].value);
308 };
309 free(config);
310 return NULL;
311 }
312
313 token = strtok(NULL, "=\n");
314 if (token != NULL) {
315 config[i].value = str_dup(token);
316 if (config[i].value == NULL) {
317 perror("file_to_kvs() config[i].value value is NULL");
318 for(j = 0; j < i; j++) {
319 free(config[j].key);
320 free(config[j].value);
321 };
322 free(config);
323 return NULL;
324 }
325 }
326 }
327 i++;
328 }
329
330 fclose(file_stream);
331
332 return config;
333 }
334
335 int main(void) {
336 char *str_rep_result;
337 char *str_slice_result;
338 char *str_html_escape_result;
339 char *str_format_result;
340 struct kv *config;
341 size_t pair_count;
342 size_t i;
343
344 /* str_rep() test ========================================= */
345 str_rep_result = str_rep("hey i'm a dog here. are you a dog too? okay cool, you're a dog too lol.", "dog", "person");
346 if (str_rep_result == NULL) {
347 perror("main() str_rep() returned NULL");
348 return 1;
349 }
350 /* printf("%s\n", str_rep_result); */
351 free(str_rep_result);
352
353 /* str_slice() test ========================================= */
354 str_slice_result = str_slice("hello", 0, strlen("hello") - 1);
355 if (str_slice_result == NULL) {
356 perror("main() str_slice() returned NULL");
357 return 1;
358 }
359 /* printf("%s\n", str_slice_result); */
360 free(str_slice_result);
361
362 /* str_html_escape() test ========================================= */
363 str_html_escape_result = str_html_escape("hello 小李, i'm bob, & you?");
364 if (str_html_escape_result == NULL) {
365 perror("main() str_html_escape() returned NULL");
366 return 1;
367 }
368 /* printf("%s\n", str_html_escape_result); */
369
370 /* double checking that the returned buffer size is correct */
371 /* print_size_t("returned size", strlen(str_html_escape_result) + 1); */
372 /* print_size_t("converted size", strlen("hello 小李, i&#39;m bob, &amp; you?") + 1); */
373 free(str_html_escape_result);
374
375 /* str_format() test ============================================== */
376 str_format_result = str_format("hello {{name}}, what's up?", "name", "sherry");
377 if (str_format_result == NULL) {
378 perror("main() str_format() returned NULL");
379 return 1;
380 }
381 /* printf("%s\n", str_format_result); */
382 free(str_format_result);
383
384 /* file_to_kvs stuff =================================================== */
385 config = file_to_kvs("config.txt");
386 if (config == NULL) {
387 perror("main() file_to_kvs() returned NULL");
388 return 1;
389 }
390
391 pair_count = pairs_in_file("config.txt");
392 if (pair_count == 0) {
393 perror("file_to_kvs() pair_count is 0");
394 return 1;
395 }
396 for(i = 0; i < pair_count; i++) {
397 printf("'%s': '%s'\n", config[i].key, config[i].value);
398 }
399
400 for(i = 0; i < pair_count; i++) {
401 free(config[i].key);
402 free(config[i].value);
403 };
404
405 free(config);
406
407 return(0);
408 }