Coverage Report

Created: 2025-12-14 08:40

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