1 /* fakeglib.c - A shim for applications that require GLib
2 * without the whole kit and kaboodle.
3 *
4 * Copyright (C) 2020 Evan Miller
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
15 *
16 * You should have received a copy of the GNU Library General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21 #include "mdbfakeglib.h"
22
23 #include <stddef.h>
24 #include <stdlib.h>
25 #include <stdarg.h>
26 #include <stdio.h>
27 #include <string.h>
28 #include <getopt.h>
29 #include <errno.h>
30 #include <wctype.h>
31 #ifdef HAVE_ICONV
32 #include <iconv.h>
33 #endif
34
35 /* Linked from libmdb */
36 const char *mdb_iconv_name_from_code_page(int code_page);
37
38 /* string functions */
39
g_memdup(const void * src,size_t len)40 void *g_memdup(const void *src, size_t len) {
41 void *dst = malloc(len);
42 memcpy(dst, src, len);
43 return dst;
44 }
45
g_str_equal(const void * str1,const void * str2)46 int g_str_equal(const void *str1, const void *str2) {
47 return strcmp(str1, str2) == 0;
48 }
49
50 // max_tokens not yet implemented
g_strsplit(const char * haystack,const char * needle,int max_tokens)51 char **g_strsplit(const char *haystack, const char *needle, int max_tokens) {
52 char **ret = NULL;
53 char *found = NULL;
54 size_t components = 2; // last component + terminating NULL
55
56 while ((found = strstr(haystack, needle))) {
57 components++;
58 haystack = found + strlen(needle);
59 }
60
61 ret = calloc(components, sizeof(char *));
62
63 int i = 0;
64 while ((found = strstr(haystack, needle))) {
65 ret[i++] = g_strndup(haystack, found - haystack);
66 haystack = found + strlen(needle);
67 }
68 ret[i] = strdup(haystack);
69
70 return ret;
71 }
72
g_strfreev(char ** dir)73 void g_strfreev(char **dir) {
74 int i=0;
75 while (dir[i]) {
76 free(dir[i]);
77 i++;
78 }
79 free(dir);
80 }
81
g_strconcat(const char * first,...)82 char *g_strconcat(const char *first, ...) {
83 char *ret = NULL;
84 size_t len = strlen(first);
85 char *arg = NULL;
86 va_list argp;
87
88 va_start(argp, first);
89 while ((arg = va_arg(argp, char *))) {
90 len += strlen(arg);
91 }
92 va_end(argp);
93
94 ret = malloc(len+1);
95
96 char *pos = strcpy(ret, first) + strlen(first);
97
98 va_start(argp, first);
99 while ((arg = va_arg(argp, char *))) {
100 pos = strcpy(pos, arg) + strlen(arg);
101 }
102 va_end(argp);
103
104 ret[len] = '\0';
105
106 return ret;
107 }
108
109 #if defined _WIN32 && !defined(HAVE_VASPRINTF) && !defined(HAVE_VASNPRINTF)
vasprintf(char ** ret,const char * format,va_list ap)110 int vasprintf(char **ret, const char *format, va_list ap) {
111 int len;
112 int retval;
113 char *result;
114 if ((len = _vscprintf(format, ap)) < 0)
115 return -1;
116 if ((result = malloc(len+1)) == NULL)
117 return -1;
118 if ((retval = vsprintf_s(result, len+1, format, ap)) == -1) {
119 free(result);
120 return -1;
121 }
122 *ret = result;
123 return retval;
124 }
125 #endif
126
g_strdup(const char * input)127 char *g_strdup(const char *input) {
128 size_t len = strlen(input);
129 return g_memdup(input, len+1);
130 }
131
g_strndup(const char * src,size_t len)132 char *g_strndup(const char *src, size_t len) {
133 if (!src)
134 return NULL;
135 char *result = malloc(len+1);
136 size_t i=0;
137 while (*src && i<len) {
138 result[i++] = *src++;
139 }
140 result[i] = '\0';
141 return result;
142 }
143
g_strdup_printf(const char * format,...)144 char *g_strdup_printf(const char *format, ...) {
145 char *ret = NULL;
146 va_list argp;
147
148 va_start(argp, format);
149 #ifdef HAVE_VASNPRINTF
150 size_t len = 0;
151 ret = vasnprintf(ret, &len, format, argp);
152 #else
153 int gcc_is_dumb = vasprintf(&ret, format, argp);
154 (void)gcc_is_dumb;
155 #endif
156 va_end(argp);
157
158 return ret;
159 }
160
g_strdelimit(gchar * string,const gchar * delimiters,gchar new_delimiter)161 gchar *g_strdelimit(gchar *string, const gchar *delimiters, gchar new_delimiter) {
162 char *orig = string;
163 if (delimiters == NULL)
164 delimiters = G_STR_DELIMITERS;
165 size_t n = strlen(delimiters);
166 while (*string) {
167 size_t i;
168 for (i=0; i<n; i++) {
169 if (*string == delimiters[i]) {
170 *string = new_delimiter;
171 break;
172 }
173 }
174 string++;
175 }
176
177 return orig;
178 }
179
g_printerr(const gchar * format,...)180 void g_printerr(const gchar *format, ...) {
181 va_list argp;
182 va_start(argp, format);
183 vfprintf(stderr, format, argp);
184 va_end(argp);
185 }
186
187 /* GString */
188
g_string_new(const gchar * init)189 GString *g_string_new (const gchar *init) {
190 GString *str = calloc(1, sizeof(GString));
191 str->str = strdup(init ? init : "");
192 str->len = strlen(str->str);
193 str->allocated_len = str->len+1;
194 return str;
195 }
196
g_string_assign(GString * string,const gchar * rval)197 GString *g_string_assign(GString *string, const gchar *rval) {
198 size_t len = strlen(rval);
199 string->str = realloc(string->str, len+1);
200 strncpy(string->str, rval, len+1);
201 string->len = len;
202 string->allocated_len = len+1;
203 return string;
204 }
205
g_string_append(GString * string,const gchar * val)206 GString * g_string_append (GString *string, const gchar *val) {
207 size_t len = strlen(val);
208 string->str = realloc(string->str, string->len + len + 1);
209 strncpy(&string->str[string->len], val, len+1);
210 string->len += len;
211 string->allocated_len = string->len + len + 1;
212 return string;
213 }
214
g_string_free(GString * string,gboolean free_segment)215 gchar *g_string_free (GString *string, gboolean free_segment) {
216 char *data = string->str;
217 free(string);
218 if (free_segment) {
219 free(data);
220 return NULL;
221 }
222 return data;
223 }
224
225 /* conversion */
g_unichar_to_utf8(gunichar u,gchar * dst)226 gint g_unichar_to_utf8(gunichar u, gchar *dst) {
227 if (u >= 0x0800) {
228 *dst++ = 0xE0 | ((u & 0xF000) >> 12);
229 *dst++ = 0x80 | ((u & 0x0FC0) >> 6);
230 *dst++ = 0x80 | ((u & 0x003F) >> 0);
231 return 3;
232 }
233 if (u >= 0x0080) {
234 *dst++ = 0xC0 | ((u & 0x07C0) >> 6);
235 *dst++ = 0x80 | ((u & 0x003F) >> 0);
236 return 2;
237 }
238 *dst++ = (u & 0x7F);
239 return 1;
240 }
241
g_locale_to_utf8(const gchar * opsysstring,size_t len,size_t * bytes_read,size_t * bytes_written,GError ** error)242 gchar *g_locale_to_utf8(const gchar *opsysstring, size_t len,
243 size_t *bytes_read, size_t *bytes_written, GError **error) {
244 if (len == (size_t)-1)
245 len = strlen(opsysstring);
246 size_t wlen = mbstowcs(NULL, opsysstring, 0);
247 if (wlen == (size_t)-1) {
248 if (error) {
249 *error = malloc(sizeof(GError));
250 (*error)->message = g_strdup_printf("Invalid multibyte string: %s\n", opsysstring);
251 }
252 return NULL;
253 }
254 wchar_t *utf16 = malloc(sizeof(wchar_t)*(wlen+1));
255 mbstowcs(utf16, opsysstring, wlen+1);
256 gchar *utf8 = malloc(3*len+1);
257 gchar *dst = utf8;
258 for (size_t i=0; i<len; i++) {
259 // u >= 0x10000 requires surrogate pairs, ignore
260 dst += g_unichar_to_utf8(utf16[i], dst);
261 }
262 *dst++ = '\0';
263 free(utf16);
264 return utf8;
265 }
266
g_utf8_casefold(const gchar * str,gssize len)267 gchar *g_utf8_casefold(const gchar *str, gssize len) {
268 return g_utf8_strdown(str, len);
269 }
270
g_utf8_strdown(const gchar * str,gssize len)271 gchar *g_utf8_strdown(const gchar *str, gssize len) {
272 gssize i = 0;
273 if (len == -1)
274 len = strlen(str);
275 gchar *lower = malloc(len+1);
276 while (i<len) {
277 wchar_t u = 0;
278 uint8_t c = str[i];
279 if ((c & 0xF0) == 0xE0) {
280 u = (c & 0x0F) << 12;
281 u += (str[i+1] & 0x3F) << 6;
282 u += (str[i+2] & 0x3F);
283 } else if ((c & 0xE0) == 0xC0) {
284 u = (c & 0x1F) << 6;
285 u += (str[i+1] & 0x3F);
286 } else {
287 u = (c & 0x7F);
288 }
289 i += g_unichar_to_utf8(towlower(u), &lower[i]);
290 }
291 lower[len] = '\0';
292 return lower;
293 }
294
295 /* GHashTable */
296
297 typedef struct MyNode {
298 char *key;
299 void *value;
300 } MyNode;
301
g_hash_table_lookup(GHashTable * table,const void * key)302 void *g_hash_table_lookup(GHashTable *table, const void *key) {
303 guint i;
304 for (i=0; i<table->array->len; i++) {
305 MyNode *node = g_ptr_array_index(table->array, i);
306 if (table->compare(key, node->key))
307 return node->value;
308 }
309 return NULL;
310 }
311
g_hash_table_lookup_extended(GHashTable * table,const void * lookup_key,void ** orig_key,void ** value)312 gboolean g_hash_table_lookup_extended (GHashTable *table, const void *lookup_key,
313 void **orig_key, void **value) {
314 guint i;
315 for (i=0; i<table->array->len; i++) {
316 MyNode *node = g_ptr_array_index(table->array, i);
317 if (table->compare(lookup_key, node->key)) {
318 *orig_key = node->key;
319 *value = node->value;
320 return TRUE;
321 }
322 }
323 return FALSE;
324 }
325
g_hash_table_insert(GHashTable * table,void * key,void * value)326 void g_hash_table_insert(GHashTable *table, void *key, void *value) {
327 MyNode *node = calloc(1, sizeof(MyNode));
328 node->value = value;
329 node->key = key;
330 g_ptr_array_add(table->array, node);
331 }
332
g_hash_table_remove(GHashTable * table,gconstpointer key)333 gboolean g_hash_table_remove(GHashTable *table, gconstpointer key) {
334 int found = 0;
335 for (guint i=0; i<table->array->len; i++) {
336 MyNode *node = g_ptr_array_index(table->array, i);
337 if (found) {
338 table->array->pdata[i-1] = table->array->pdata[i];
339 } else if (!found && table->compare(key, node->key)) {
340 found = 1;
341 }
342 }
343 if (found) {
344 table->array->len--;
345 }
346 return found;
347 }
348
g_hash_table_new(GHashFunc hashes,GEqualFunc equals)349 GHashTable *g_hash_table_new(GHashFunc hashes, GEqualFunc equals) {
350 GHashTable *table = calloc(1, sizeof(GHashTable));
351 table->array = g_ptr_array_new();
352 table->compare = equals;
353 return table;
354 }
355
g_hash_table_foreach(GHashTable * table,GHFunc function,void * data)356 void g_hash_table_foreach(GHashTable *table, GHFunc function, void *data) {
357 guint i;
358 for (i=0; i<table->array->len; i++) {
359 MyNode *node = g_ptr_array_index(table->array, i);
360 function(node->key, node->value, data);
361 }
362 }
363
g_hash_table_destroy(GHashTable * table)364 void g_hash_table_destroy(GHashTable *table) {
365 guint i;
366 for (i=0; i<table->array->len; i++) {
367 MyNode *node = g_ptr_array_index(table->array, i);
368 free(node);
369 }
370 g_ptr_array_free(table->array, TRUE);
371 free(table);
372 }
373
374 /* GPtrArray */
375
g_ptr_array_sort(GPtrArray * array,GCompareFunc func)376 void g_ptr_array_sort(GPtrArray *array, GCompareFunc func) {
377 qsort(array->pdata, array->len, sizeof(void *), func);
378 }
379
g_ptr_array_foreach(GPtrArray * array,GFunc function,gpointer user_data)380 void g_ptr_array_foreach(GPtrArray *array, GFunc function, gpointer user_data) {
381 guint i;
382 for (i=0; i<array->len; i++) {
383 function(g_ptr_array_index(array, i), user_data);
384 }
385 }
386
g_ptr_array_new()387 GPtrArray *g_ptr_array_new() {
388 GPtrArray *array = malloc(sizeof(GPtrArray));
389 array->len = 0;
390 array->pdata = NULL;
391 return array;
392 }
393
g_ptr_array_add(GPtrArray * array,void * entry)394 void g_ptr_array_add(GPtrArray *array, void *entry) {
395 array->pdata = realloc(array->pdata, (array->len+1) * sizeof(void *));
396 array->pdata[array->len++] = entry;
397 }
398
g_ptr_array_remove(GPtrArray * array,gpointer data)399 gboolean g_ptr_array_remove(GPtrArray *array, gpointer data) {
400 int found = 0;
401 for (guint i=0; i<array->len; i++) {
402 if (found) {
403 array->pdata[i-1] = array->pdata[i];
404 } else if (!found && array->pdata[i] == data) {
405 found = 1;
406 }
407 }
408 if (found) {
409 array->len--;
410 }
411 return found;
412 }
413
g_ptr_array_free(GPtrArray * array,gboolean something)414 void g_ptr_array_free(GPtrArray *array, gboolean something) {
415 free(array->pdata);
416 free(array);
417 }
418
419 /* GList */
420
g_list_append(GList * list,void * data)421 GList *g_list_append(GList *list, void *data) {
422 GList *new_list = calloc(1, sizeof(GList));
423 new_list->data = data;
424 new_list->next = list;
425 if (list)
426 list->prev = new_list;
427 return new_list;
428 }
429
g_list_last(GList * list)430 GList *g_list_last(GList *list) {
431 while (list && list->next) {
432 list = list->next;
433 }
434 return list;
435 }
436
g_list_remove(GList * list,void * data)437 GList *g_list_remove(GList *list, void *data) {
438 GList *link = list;
439 while (link) {
440 if (link->data == data) {
441 GList *return_list = list;
442 if (link->prev)
443 link->prev->next = link->next;
444 if (link->next)
445 link->next->prev = link->prev;
446 if (link == list)
447 return_list = link->next;
448 free(link);
449 return return_list;
450 }
451 link = link->next;
452 }
453 return list;
454 }
455
g_list_free(GList * list)456 void g_list_free(GList *list) {
457 GList *next = NULL;
458 while (list) {
459 next = list->next;
460 free(list);
461 list = next;
462 }
463 }
464
465 /* GOption */
466
g_option_context_add_main_entries(GOptionContext * context,const GOptionEntry * entries,const gchar * translation_domain)467 void g_option_context_add_main_entries (GOptionContext *context,
468 const GOptionEntry *entries,
469 const gchar *translation_domain) {
470 context->entries = entries;
471 }
472
g_option_context_get_help(GOptionContext * context,gboolean main_help,void * group)473 gchar *g_option_context_get_help (GOptionContext *context,
474 gboolean main_help, void *group) {
475 #if defined(__APPLE__) || defined(__FreeBSD__)
476 const char * appname = getprogname();
477 #elif HAVE_DECL_PROGRAM_INVOCATION_SHORT_NAME
478 const char * appname = program_invocation_short_name;
479 #else
480 const char * appname = "mdb-util";
481 #endif
482
483 char *help = malloc(4096);
484 char *end = help + 4096;
485 char *p = help;
486 p += snprintf(p, end - p,
487 "Usage:\n %s [OPTION\xE2\x80\xA6] %s\n\n", appname, context->desc);
488 p += snprintf(p, end - p,
489 "Help Options:\n -h, --%-20s%s\n\n", "help", "Show help options");
490 p += snprintf(p, end - p,
491 "Application Options:\n");
492 int i=0;
493 for (i=0; context->entries[i].long_name; i++) {
494 p += snprintf(p, end - p, " ");
495 if (context->entries[i].short_name) {
496 p += snprintf(p, end - p, "-%c, ", context->entries[i].short_name);
497 }
498 p += snprintf(p, end - p, "--");
499 if (context->entries[i].arg_description) {
500 char *long_name = g_strconcat(
501 context->entries[i].long_name, "=",
502 context->entries[i].arg_description, NULL);
503 p += snprintf(p, end - p, "%-20s", long_name);
504 free(long_name);
505 } else {
506 p += snprintf(p, end - p, "%-20s", context->entries[i].long_name);
507 }
508 if (!context->entries[i].short_name) {
509 p += snprintf(p, end - p, " ");
510 }
511 p += snprintf(p, end - p, "%s\n", context->entries[i].description);
512 }
513 p += snprintf(p, end - p, "\n");
514 return help;
515 }
516
g_option_context_new(const char * description)517 GOptionContext *g_option_context_new(const char *description) {
518 GOptionContext *ctx = calloc(1, sizeof(GOptionContext));
519 ctx->desc = description;
520 return ctx;
521 }
522
g_option_context_parse(GOptionContext * context,gint * argc,gchar *** argv,GError ** error)523 gboolean g_option_context_parse(GOptionContext *context,
524 gint *argc, gchar ***argv, GError **error) {
525 int i;
526 int count = 0;
527 int len = 0;
528 if (*argc == 2 &&
529 (strcmp((*argv)[1], "-h") == 0 || strcmp((*argv)[1], "--help") == 0)) {
530 fprintf(stderr, "%s", g_option_context_get_help(context, TRUE, NULL));
531 exit(0);
532 }
533 for (i=0; context->entries[i].long_name; i++) {
534 GOptionArg arg = context->entries[i].arg;
535 count++;
536 len++;
537 if (arg != G_OPTION_ARG_NONE)
538 len++;
539 }
540 struct option *long_opts = calloc(count+1, sizeof(struct option));
541 char *short_opts = calloc(1, len+1);
542 int j=0;
543 for (i=0; i<count; i++) {
544 const GOptionEntry *entry = &context->entries[i];
545 GOptionArg arg = entry->arg;
546 short_opts[j++] = entry->short_name;
547 if (arg != G_OPTION_ARG_NONE)
548 short_opts[j++] = ':';
549 long_opts[i].name = entry->long_name;
550 long_opts[i].has_arg = entry->arg == G_OPTION_ARG_NONE ? no_argument : required_argument;
551 }
552 int c;
553 int longindex = 0;
554 opterr = 0;
555 while ((c = getopt_long(*argc, *argv, short_opts, long_opts, &longindex)) != -1) {
556 if (c == '?') {
557 *error = malloc(sizeof(GError));
558 if (optopt) {
559 (*error)->message = g_strdup_printf("Unrecognized option: -%c", optopt);
560 } else {
561 (*error)->message = g_strdup_printf("Unrecognized option: %s", (*argv)[optind-1]);
562 }
563 free(short_opts);
564 free(long_opts);
565 return FALSE;
566 }
567 const GOptionEntry *entry = NULL;
568 if (c == 0) {
569 entry = &context->entries[longindex];
570 } else {
571 for (i=0; i<count; i++) {
572 if (context->entries[i].short_name == c) {
573 entry = &context->entries[i];
574 break;
575 }
576 }
577 }
578 if (entry->arg == G_OPTION_ARG_NONE) {
579 *(int *)entry->arg_data = !(entry->flags & G_OPTION_FLAG_REVERSE);
580 } else if (entry->arg == G_OPTION_ARG_INT) {
581 char *endptr = NULL;
582 *(int *)entry->arg_data = strtol(optarg, &endptr, 10);
583 if (*endptr) {
584 *error = malloc(sizeof(GError));
585 (*error)->message = malloc(100);
586 snprintf((*error)->message, 100, "Argument to --%s must be an integer", entry->long_name);
587 free(short_opts);
588 free(long_opts);
589 return FALSE;
590 }
591 } else if (entry->arg == G_OPTION_ARG_FILENAME) {
592 *(char **)entry->arg_data = strdup(optarg);
593 } else if (entry->arg == G_OPTION_ARG_STRING) {
594 char *result = g_locale_to_utf8(optarg, -1, NULL, NULL, error);
595 if (result == NULL) {
596 free(short_opts);
597 free(long_opts);
598 return FALSE;
599 }
600 *(char **)entry->arg_data = result;
601 }
602 }
603 *argc -= (optind - 1);
604 *argv += (optind - 1);
605 free(short_opts);
606 free(long_opts);
607
608 return TRUE;
609 }
610
g_option_context_free(GOptionContext * context)611 void g_option_context_free(GOptionContext *context) {
612 free(context);
613 }
614