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