Coverage Report

Created: 2023-09-25 06:55

/src/mdbtools/src/libmdb/fakeglib.c
Line
Count
Source (jump to first uncovered line)
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
726
void *g_memdup(const void *src, size_t len) {
41
726
    void *dst = malloc(len);
42
726
    memcpy(dst, src, len);
43
726
    return dst;
44
726
}
45
46
19
int g_str_equal(const void *str1, const void *str2) {
47
19
    return strcmp(str1, str2) == 0;
48
19
}
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
654
char *g_strdup(const char *input) {
128
654
    size_t len = strlen(input);
129
654
    return g_memdup(input, len+1);
130
654
}
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
854
char *g_strdup_printf(const char *format, ...) {
145
854
    char *ret = NULL;
146
854
    va_list argp;
147
148
854
    va_start(argp, format);
149
#ifdef HAVE_VASNPRINTF
150
    size_t len = 0;
151
    ret = vasnprintf(ret, &len, format, argp);
152
#else
153
854
    int gcc_is_dumb = vasprintf(&ret, format, argp);
154
854
    (void)gcc_is_dumb;
155
854
#endif
156
854
    va_end(argp);
157
158
854
    return ret;
159
854
}
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
    for (size_t i=0; i<len; i++) {
259
        // u >= 0x10000 requires surrogate pairs, ignore
260
0
        dst += g_unichar_to_utf8(utf16[i], dst);
261
0
    }
262
0
    *dst++ = '\0';
263
0
    free(utf16);
264
0
    return utf8;
265
0
}
266
267
0
gchar *g_utf8_casefold(const gchar *str, gssize len) {
268
0
    return g_utf8_strdown(str, len);
269
0
}
270
271
0
gchar *g_utf8_strdown(const gchar *str, gssize len) {
272
0
    gssize i = 0;
273
0
    if (len == -1)
274
0
        len = strlen(str);
275
0
    gchar *lower = malloc(len+1);
276
0
    while (i<len) {
277
0
        wchar_t u = 0;
278
0
        uint8_t c = str[i];
279
0
        if ((c & 0xF0) == 0xE0) {
280
0
            u = (c & 0x0F) << 12;
281
0
            u += (str[i+1] & 0x3F) << 6;
282
0
            u += (str[i+2] & 0x3F);
283
0
        } else if ((c & 0xE0) == 0xC0) {
284
0
            u = (c & 0x1F) << 6;
285
0
            u += (str[i+1] & 0x3F);
286
0
        } else {
287
0
            u = (c & 0x7F);
288
0
        }
289
0
        i += g_unichar_to_utf8(towlower(u), &lower[i]);
290
0
    }
291
0
    lower[len] = '\0';
292
0
    return lower;
293
0
}
294
295
/* GHashTable */
296
297
typedef struct MyNode {
298
    char *key;
299
    void *value;
300
} MyNode;
301
302
19
void *g_hash_table_lookup(GHashTable *table, const void *key) {
303
19
    guint i;
304
19
    for (i=0; i<table->array->len; i++) {
305
19
        MyNode *node = g_ptr_array_index(table->array, i);
306
19
        if (table->compare(key, node->key))
307
19
            return node->value;
308
19
    }
309
0
    return NULL;
310
19
}
311
312
gboolean g_hash_table_lookup_extended (GHashTable *table, const void *lookup_key,
313
0
        void **orig_key, void **value) {
314
0
    guint i;
315
0
    for (i=0; i<table->array->len; i++) {
316
0
        MyNode *node = g_ptr_array_index(table->array, i);
317
0
        if (table->compare(lookup_key, node->key)) {
318
0
            *orig_key = node->key;
319
0
            *value = node->value;
320
0
            return TRUE;
321
0
        }
322
0
    }
323
0
    return FALSE;
324
0
}
325
326
114
void g_hash_table_insert(GHashTable *table, void *key, void *value) {
327
114
    MyNode *node = calloc(1, sizeof(MyNode));
328
114
    node->value = value;
329
114
    node->key = key;
330
114
    g_ptr_array_add(table->array, node);
331
114
}
332
333
0
gboolean g_hash_table_remove(GHashTable *table, gconstpointer key) {
334
0
    int found = 0;
335
0
    for (guint i=0; i<table->array->len; i++) {
336
0
        MyNode *node = g_ptr_array_index(table->array, i);
337
0
        if (found) {
338
0
            table->array->pdata[i-1] = table->array->pdata[i];
339
0
        } else if (!found && table->compare(key, node->key)) {
340
0
            found = 1;
341
0
        }
342
0
    }
343
0
    if (found) {
344
0
        table->array->len--;
345
0
    }
346
0
    return found;
347
0
}
348
349
19
GHashTable *g_hash_table_new(GHashFunc hashes, GEqualFunc equals) {
350
19
    GHashTable *table = calloc(1, sizeof(GHashTable));
351
19
    table->array = g_ptr_array_new();
352
19
    table->compare = equals;
353
19
    return table;
354
19
}
355
356
19
void g_hash_table_foreach(GHashTable *table, GHFunc function, void *data) {
357
19
    guint i;
358
133
    for (i=0; i<table->array->len; i++) {
359
114
        MyNode *node = g_ptr_array_index(table->array, i);
360
114
        function(node->key, node->value, data);
361
114
    }
362
19
}
363
364
19
void g_hash_table_destroy(GHashTable *table) {
365
19
    guint i;
366
133
    for (i=0; i<table->array->len; i++) {
367
114
        MyNode *node = g_ptr_array_index(table->array, i);
368
114
        free(node);
369
114
    }
370
19
    g_ptr_array_free(table->array, TRUE);
371
19
    free(table);
372
19
}
373
374
/* GPtrArray */
375
376
29
void g_ptr_array_sort(GPtrArray *array, GCompareFunc func) {
377
29
    qsort(array->pdata, array->len, sizeof(void *), func);
378
29
}
379
380
0
void g_ptr_array_foreach(GPtrArray *array, GFunc function, gpointer user_data) {
381
0
    guint i;
382
0
    for (i=0; i<array->len; i++) {
383
0
        function(g_ptr_array_index(array, i), user_data);
384
0
    }
385
0
}
386
387
72
GPtrArray *g_ptr_array_new() {
388
72
    GPtrArray *array = malloc(sizeof(GPtrArray));
389
72
    array->len = 0;
390
72
    array->pdata = NULL;
391
72
    return array;
392
72
}
393
394
2.18k
void g_ptr_array_add(GPtrArray *array, void *entry) {
395
2.18k
    array->pdata = realloc(array->pdata, (array->len+1) * sizeof(void *));
396
2.18k
    array->pdata[array->len++] = entry;
397
2.18k
}
398
399
0
gboolean g_ptr_array_remove(GPtrArray *array, gpointer data) {
400
0
    int found = 0;
401
0
    for (guint i=0; i<array->len; i++) {
402
0
        if (found) {
403
0
            array->pdata[i-1] = array->pdata[i];
404
0
        } else if (!found && array->pdata[i] == data) {
405
0
            found = 1;
406
0
        }
407
0
    }
408
0
    if (found) {
409
0
        array->len--;
410
0
    }
411
0
    return found;
412
0
}
413
414
72
void g_ptr_array_free(GPtrArray *array, gboolean something) {
415
72
    free(array->pdata);
416
72
    free(array);
417
72
}
418
419
/* GList */
420
421
0
GList *g_list_append(GList *list, void *data) {
422
0
    GList *new_list = calloc(1, sizeof(GList));
423
0
    new_list->data = data;
424
0
    new_list->next = list;
425
0
    if (list)
426
0
        list->prev = new_list;
427
0
    return new_list;
428
0
}
429
430
0
GList *g_list_last(GList *list) {
431
0
    while (list && list->next) {
432
0
        list = list->next;
433
0
    }
434
0
    return list;
435
0
}
436
437
0
GList *g_list_remove(GList *list, void *data) {
438
0
    GList *link = list;
439
0
    while (link) {
440
0
        if (link->data == data) {
441
0
            GList *return_list = list;
442
0
            if (link->prev)
443
0
                link->prev->next = link->next;
444
0
            if (link->next)
445
0
                link->next->prev = link->prev;
446
0
            if (link == list)
447
0
                return_list = link->next;
448
0
            free(link);
449
0
            return return_list;
450
0
        }
451
0
        link = link->next;
452
0
    }
453
0
    return list;
454
0
}
455
456
0
void g_list_free(GList *list) {
457
0
    GList *next = NULL;
458
0
    while (list) {
459
0
        next = list->next;
460
0
        free(list);
461
0
        list = next;
462
0
    }
463
0
}
464
465
/* GOption */
466
467
void g_option_context_add_main_entries (GOptionContext *context,
468
        const GOptionEntry *entries,
469
0
        const gchar *translation_domain) {
470
0
    context->entries = entries;
471
0
}
472
473
gchar *g_option_context_get_help (GOptionContext *context,
474
0
        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
0
    const char * appname = program_invocation_short_name;
479
#else
480
    const char * appname = "mdb-util";
481
#endif
482
483
0
    char *help = malloc(4096);
484
0
    char *end = help + 4096;
485
0
    char *p = help;
486
0
    p += snprintf(p, end - p,
487
0
            "Usage:\n  %s [OPTION\xE2\x80\xA6] %s\n\n", appname, context->desc);
488
0
    p += snprintf(p, end - p,
489
0
            "Help Options:\n  -h, --%-20s%s\n\n", "help", "Show help options");
490
0
    p += snprintf(p, end - p,
491
0
            "Application Options:\n");
492
0
    int i=0;
493
0
    for (i=0; context->entries[i].long_name; i++) {
494
0
        p += snprintf(p, end - p, "  ");
495
0
        if (context->entries[i].short_name) {
496
0
            p += snprintf(p, end - p, "-%c, ", context->entries[i].short_name);
497
0
        }
498
0
        p += snprintf(p, end - p, "--");
499
0
        if (context->entries[i].arg_description) {
500
0
            char *long_name = g_strconcat(
501
0
                    context->entries[i].long_name, "=",
502
0
                    context->entries[i].arg_description, NULL);
503
0
            p += snprintf(p, end - p, "%-20s", long_name);
504
0
            free(long_name);
505
0
        } else {
506
0
            p += snprintf(p, end - p, "%-20s", context->entries[i].long_name);
507
0
        }
508
0
        if (!context->entries[i].short_name) {
509
0
            p += snprintf(p, end - p, "    ");
510
0
        }
511
0
        p += snprintf(p, end - p, "%s\n", context->entries[i].description);
512
0
    }
513
0
    p += snprintf(p, end - p, "\n");
514
0
    return help;
515
0
}
516
517
0
GOptionContext *g_option_context_new(const char *description) {
518
0
    GOptionContext *ctx = calloc(1, sizeof(GOptionContext));
519
0
    ctx->desc = description;
520
0
    return ctx;
521
0
}
522
523
gboolean g_option_context_parse(GOptionContext *context,
524
0
        gint *argc, gchar ***argv, GError **error) {
525
0
    int i;
526
0
    int count = 0;
527
0
    int len = 0;
528
0
    if (*argc == 2 &&
529
0
            (strcmp((*argv)[1], "-h") == 0 || strcmp((*argv)[1], "--help") == 0)) {
530
0
        fprintf(stderr, "%s", g_option_context_get_help(context, TRUE, NULL));
531
0
        exit(0);
532
0
    }
533
0
    for (i=0; context->entries[i].long_name; i++) {
534
0
        GOptionArg arg = context->entries[i].arg;
535
0
        count++;
536
0
        len++;
537
0
        if (arg != G_OPTION_ARG_NONE)
538
0
            len++;
539
0
    }
540
0
    struct option *long_opts = calloc(count+1, sizeof(struct option));
541
0
    char *short_opts = calloc(1, len+1);
542
0
    int j=0;
543
0
    for (i=0; i<count; i++) {
544
0
        const GOptionEntry *entry = &context->entries[i];
545
0
        GOptionArg arg = entry->arg;
546
0
        short_opts[j++] = entry->short_name;
547
0
        if (arg != G_OPTION_ARG_NONE)
548
0
            short_opts[j++] = ':';
549
0
        long_opts[i].name = entry->long_name;
550
0
        long_opts[i].has_arg = entry->arg == G_OPTION_ARG_NONE ? no_argument : required_argument;
551
0
    }
552
0
    int c;
553
0
    int longindex = 0;
554
0
    opterr = 0;
555
0
    while ((c = getopt_long(*argc, *argv, short_opts, long_opts, &longindex)) != -1) {
556
0
        if (c == '?') {
557
0
            *error = malloc(sizeof(GError));
558
0
            if (optopt) {
559
0
                (*error)->message = g_strdup_printf("Unrecognized option: -%c", optopt);
560
0
            } else {
561
0
                (*error)->message = g_strdup_printf("Unrecognized option: %s", (*argv)[optind-1]);
562
0
            }
563
0
            free(short_opts);
564
0
            free(long_opts);
565
0
            return FALSE;
566
0
        }
567
0
        const GOptionEntry *entry = NULL;
568
0
        if (c == 0) {
569
0
            entry = &context->entries[longindex];
570
0
        } else {
571
0
            for (i=0; i<count; i++) {
572
0
                if (context->entries[i].short_name == c) {
573
0
                    entry = &context->entries[i];
574
0
                    break;
575
0
                }
576
0
            }
577
0
        }
578
0
        if (entry->arg == G_OPTION_ARG_NONE) {
579
0
            *(int *)entry->arg_data = !(entry->flags & G_OPTION_FLAG_REVERSE);
580
0
        } else if (entry->arg == G_OPTION_ARG_INT) {
581
0
            char *endptr = NULL;
582
0
            *(int *)entry->arg_data = strtol(optarg, &endptr, 10);
583
0
            if (*endptr) {
584
0
                *error = malloc(sizeof(GError));
585
0
                (*error)->message = malloc(100);
586
0
                snprintf((*error)->message, 100, "Argument to --%s must be an integer", entry->long_name);
587
0
                free(short_opts);
588
0
                free(long_opts);
589
0
                return FALSE;
590
0
            }
591
0
        } else if (entry->arg == G_OPTION_ARG_FILENAME) {
592
0
            *(char **)entry->arg_data = strdup(optarg);
593
0
        } else if (entry->arg == G_OPTION_ARG_STRING) {
594
0
            char *result = g_locale_to_utf8(optarg, -1, NULL, NULL, error);
595
0
            if (result == NULL) {
596
0
                free(short_opts);
597
0
                free(long_opts);
598
0
                return FALSE;
599
0
            }
600
0
            *(char **)entry->arg_data = result;
601
0
        }
602
0
    }
603
0
    *argc -= (optind - 1);
604
0
    *argv += (optind - 1);
605
0
    free(short_opts);
606
0
    free(long_opts);
607
608
0
    return TRUE;
609
0
}
610
611
0
void g_option_context_free(GOptionContext *context) {
612
0
    free(context);
613
0
}