mirror of
https://github.com/yuzu-emu/unicorn
synced 2024-11-25 13:19:22 +00:00
779 lines
18 KiB
C
779 lines
18 KiB
C
/*
|
|
glib_compat.c replacement functionality for glib code used in qemu
|
|
Copyright (C) 2016 Chris Eagle cseagle at gmail dot com
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License
|
|
as published by the Free Software Foundation; either version 2
|
|
of the License, or (at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
#ifndef _GNU_SOURCE
|
|
#define _GNU_SOURCE
|
|
#endif
|
|
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
|
|
#include "glib_compat.h"
|
|
|
|
#undef __HAVE_64_BIT_PTRS
|
|
|
|
#ifdef _WIN64
|
|
#define __HAVE_64_BIT_PTRS
|
|
#endif
|
|
|
|
#ifdef __GNUC__
|
|
#if defined(__x86_64__) || defined(__ppc64__) || defined(__aarch64__)
|
|
#define __HAVE_64_BIT_PTRS
|
|
#endif
|
|
#endif
|
|
|
|
/* All functions below added to eliminate GLIB dependency */
|
|
|
|
/* hashing and equality functions */
|
|
|
|
/*
|
|
Too many pointers are multiples of 8/16 so I rotate the low bits out
|
|
otherwise we get too many collisions at multiples of 8/16
|
|
This may be marginally better than what glib does in their direct_hash
|
|
but someone with some chops in this space should fix if it needs improving
|
|
*/
|
|
guint g_direct_hash(gconstpointer v)
|
|
{
|
|
#ifdef __HAVE_64_BIT_PTRS
|
|
uint64_t hash = (uint64_t)v;
|
|
hash = (hash >> 4) | (hash << 60);
|
|
hash = hash ^ (hash >> 32);
|
|
return (guint)hash;
|
|
#else
|
|
guint hash = (guint)v;
|
|
hash = (hash >> 3) | (hash << 29);
|
|
return hash;
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
djb2+ string hashing
|
|
see: http://www.cse.yorku.ca/~oz/hash.html
|
|
*/
|
|
guint g_str_hash(gconstpointer v)
|
|
{
|
|
const char *s = (const char*)v;
|
|
guint hash = 5381;
|
|
while (*s) {
|
|
hash = ((hash << 5) + hash) ^ (int)*s;
|
|
s++;
|
|
}
|
|
return hash;
|
|
}
|
|
|
|
gboolean g_str_equal(gconstpointer v1, gconstpointer v2)
|
|
{
|
|
return strcmp((const char*)v1, (const char*)v2) == 0;
|
|
}
|
|
|
|
/*
|
|
Bob Jenkins integer hash algorithm
|
|
see: http://burtleburtle.net/bob/hash/integer.html
|
|
*/
|
|
guint g_int_hash(gconstpointer v)
|
|
{
|
|
guint hash = *(const guint*)v;
|
|
hash = (hash + 0x7ed55d16) + (hash << 12);
|
|
hash = (hash ^ 0xc761c23c) ^ (hash >> 19);
|
|
hash = (hash + 0x165667b1) + (hash << 5);
|
|
hash = (hash + 0xd3a2646c) ^ (hash << 9);
|
|
hash = (hash + 0xfd7046c5) + (hash << 3);
|
|
hash = (hash ^ 0xb55a4f09) ^ (hash >> 16);
|
|
return hash;
|
|
}
|
|
|
|
gboolean g_int_equal(gconstpointer v1, gconstpointer v2)
|
|
{
|
|
return *(const int*)v1 == *(const int*)v2;
|
|
}
|
|
|
|
/* Doubly-linked list */
|
|
|
|
GList *g_list_first(GList *list)
|
|
{
|
|
if (list == NULL) return NULL;
|
|
while (list->prev) list = list->prev;
|
|
return list;
|
|
}
|
|
|
|
void g_list_foreach(GList *list, GFunc func, gpointer user_data)
|
|
{
|
|
GList *lp;
|
|
for (lp = list; lp; lp = lp->next) {
|
|
(*func)(lp->data, user_data);
|
|
}
|
|
}
|
|
|
|
void g_list_free(GList *list)
|
|
{
|
|
GList *lp, *next, *prev = NULL;
|
|
if (list) prev = list->prev;
|
|
for (lp = list; lp; lp = next) {
|
|
next = lp->next;
|
|
free(lp);
|
|
}
|
|
for (lp = prev; lp; lp = prev) {
|
|
prev = lp->prev;
|
|
free(lp);
|
|
}
|
|
}
|
|
|
|
GList *g_list_insert_sorted(GList *list, gpointer data, GCompareFunc compare)
|
|
{
|
|
GList *i;
|
|
GList *n = (GList*)g_malloc(sizeof(GList));
|
|
n->data = data;
|
|
if (list == NULL) {
|
|
n->next = n->prev = NULL;
|
|
return n;
|
|
}
|
|
for (i = list; i; i = i->next) {
|
|
n->prev = i->prev;
|
|
if ((*compare)(data, i->data) <= 0) {
|
|
n->next = i;
|
|
i->prev = n;
|
|
if (i == list) return n;
|
|
else return list;
|
|
}
|
|
}
|
|
n->prev = n->prev->next;
|
|
n->next = NULL;
|
|
n->prev->next = n;
|
|
return list;
|
|
}
|
|
|
|
GList *g_list_prepend(GList *list, gpointer data)
|
|
{
|
|
GList *n = (GList*)g_malloc(sizeof(GList));
|
|
n->next = list;
|
|
n->prev = NULL;
|
|
n->data = data;
|
|
return n;
|
|
}
|
|
|
|
GList *g_list_remove_link(GList *list, GList *llink)
|
|
{
|
|
if (llink) {
|
|
if (llink == list) list = list->next;
|
|
if (llink->prev) llink->prev->next = llink->next;
|
|
if (llink->next) llink->next->prev = llink->prev;
|
|
}
|
|
return list;
|
|
}
|
|
|
|
// code copied from glib/glist.c, version 2.28.0
|
|
static GList *g_list_sort_merge(GList *l1,
|
|
GList *l2,
|
|
GFunc compare_func,
|
|
gpointer user_data)
|
|
{
|
|
GList list, *l, *lprev;
|
|
gint cmp;
|
|
|
|
l = &list;
|
|
lprev = NULL;
|
|
|
|
while (l1 && l2)
|
|
{
|
|
cmp = ((GCompareDataFunc) compare_func) (l1->data, l2->data, user_data);
|
|
|
|
if (cmp <= 0)
|
|
{
|
|
l->next = l1;
|
|
l1 = l1->next;
|
|
}
|
|
else
|
|
{
|
|
l->next = l2;
|
|
l2 = l2->next;
|
|
}
|
|
l = l->next;
|
|
l->prev = lprev;
|
|
lprev = l;
|
|
}
|
|
l->next = l1 ? l1 : l2;
|
|
l->next->prev = l;
|
|
|
|
return list.next;
|
|
}
|
|
|
|
static GList *g_list_sort_real(GList *list,
|
|
GFunc compare_func,
|
|
gpointer user_data)
|
|
{
|
|
GList *l1, *l2;
|
|
|
|
if (!list)
|
|
return NULL;
|
|
if (!list->next)
|
|
return list;
|
|
|
|
l1 = list;
|
|
l2 = list->next;
|
|
|
|
while ((l2 = l2->next) != NULL)
|
|
{
|
|
if ((l2 = l2->next) == NULL)
|
|
break;
|
|
l1 = l1->next;
|
|
}
|
|
l2 = l1->next;
|
|
l1->next = NULL;
|
|
|
|
return g_list_sort_merge (g_list_sort_real (list, compare_func, user_data),
|
|
g_list_sort_real (l2, compare_func, user_data),
|
|
compare_func,
|
|
user_data);
|
|
}
|
|
|
|
/**
|
|
* g_list_sort:
|
|
* @list: a #GList
|
|
* @compare_func: the comparison function used to sort the #GList.
|
|
* This function is passed the data from 2 elements of the #GList
|
|
* and should return 0 if they are equal, a negative value if the
|
|
* first element comes before the second, or a positive value if
|
|
* the first element comes after the second.
|
|
*
|
|
* Sorts a #GList using the given comparison function.
|
|
*
|
|
* Returns: the start of the sorted #GList
|
|
*/
|
|
/**
|
|
* GCompareFunc:
|
|
* @a: a value.
|
|
* @b: a value to compare with.
|
|
* @Returns: negative value if @a < @b; zero if @a = @b; positive
|
|
* value if @a > @b.
|
|
*
|
|
* Specifies the type of a comparison function used to compare two
|
|
* values. The function should return a negative integer if the first
|
|
* value comes before the second, 0 if they are equal, or a positive
|
|
* integer if the first value comes after the second.
|
|
**/
|
|
GList *g_list_sort (GList *list, GCompareFunc compare_func)
|
|
{
|
|
return g_list_sort_real (list, (GFunc) compare_func, NULL);
|
|
}
|
|
|
|
/* END of g_list related functions */
|
|
|
|
/* Singly-linked list */
|
|
|
|
GSList *g_slist_append(GSList *list, gpointer data)
|
|
{
|
|
GSList *head = list;
|
|
if (list) {
|
|
while (list->next) list = list->next;
|
|
list->next = (GSList*)g_malloc(sizeof(GSList));
|
|
list = list->next;
|
|
} else {
|
|
head = list = (GSList*)g_malloc(sizeof(GSList));
|
|
}
|
|
list->data = data;
|
|
list->next = NULL;
|
|
return head;
|
|
}
|
|
|
|
void g_slist_foreach(GSList *list, GFunc func, gpointer user_data)
|
|
{
|
|
GSList *lp;
|
|
for (lp = list; lp; lp = lp->next) {
|
|
(*func)(lp->data, user_data);
|
|
}
|
|
}
|
|
|
|
void g_slist_free(GSList *list)
|
|
{
|
|
GSList *lp, *next;
|
|
for (lp = list; lp; lp = next) {
|
|
next = lp->next;
|
|
free(lp);
|
|
}
|
|
}
|
|
|
|
GSList *g_slist_prepend(GSList *list, gpointer data)
|
|
{
|
|
GSList *head = (GSList*)g_malloc(sizeof(GSList));
|
|
head->next = list;
|
|
head->data = data;
|
|
return head;
|
|
}
|
|
|
|
static GSList *g_slist_sort_merge (GSList *l1,
|
|
GSList *l2,
|
|
GFunc compare_func,
|
|
gpointer user_data)
|
|
{
|
|
GSList list, *l;
|
|
gint cmp;
|
|
|
|
l=&list;
|
|
|
|
while (l1 && l2)
|
|
{
|
|
cmp = ((GCompareDataFunc) compare_func) (l1->data, l2->data, user_data);
|
|
|
|
if (cmp <= 0)
|
|
{
|
|
l=l->next=l1;
|
|
l1=l1->next;
|
|
}
|
|
else
|
|
{
|
|
l=l->next=l2;
|
|
l2=l2->next;
|
|
}
|
|
}
|
|
l->next= l1 ? l1 : l2;
|
|
|
|
return list.next;
|
|
}
|
|
|
|
static GSList *g_slist_sort_real (GSList *list,
|
|
GFunc compare_func,
|
|
gpointer user_data)
|
|
{
|
|
GSList *l1, *l2;
|
|
|
|
if (!list)
|
|
return NULL;
|
|
if (!list->next)
|
|
return list;
|
|
|
|
l1 = list;
|
|
l2 = list->next;
|
|
|
|
while ((l2 = l2->next) != NULL)
|
|
{
|
|
if ((l2 = l2->next) == NULL)
|
|
break;
|
|
l1=l1->next;
|
|
}
|
|
l2 = l1->next;
|
|
l1->next = NULL;
|
|
|
|
return g_slist_sort_merge (g_slist_sort_real (list, compare_func, user_data),
|
|
g_slist_sort_real (l2, compare_func, user_data),
|
|
compare_func,
|
|
user_data);
|
|
}
|
|
|
|
/**
|
|
* g_slist_sort:
|
|
* @list: a #GSList
|
|
* @compare_func: the comparison function used to sort the #GSList.
|
|
* This function is passed the data from 2 elements of the #GSList
|
|
* and should return 0 if they are equal, a negative value if the
|
|
* first element comes before the second, or a positive value if
|
|
* the first element comes after the second.
|
|
*
|
|
* Sorts a #GSList using the given comparison function.
|
|
*
|
|
* Returns: the start of the sorted #GSList
|
|
*/
|
|
GSList *g_slist_sort (GSList *list,
|
|
GCompareFunc compare_func)
|
|
{
|
|
return g_slist_sort_real (list, (GFunc) compare_func, NULL);
|
|
}
|
|
|
|
/* END of g_slist related functions */
|
|
|
|
|
|
/* Hash table */
|
|
|
|
typedef struct _KeyValue {
|
|
gpointer key;
|
|
gpointer value;
|
|
} KeyValue;
|
|
|
|
struct _GHashTable {
|
|
GHashFunc hash_func;
|
|
GEqualFunc key_equal_func;
|
|
GDestroyNotify key_destroy_func;
|
|
GDestroyNotify value_destroy_func;
|
|
volatile gint refcount;
|
|
gint size;
|
|
guint num_entries;
|
|
GSList **buckets;
|
|
};
|
|
|
|
void g_hash_table_destroy(GHashTable *hash_table)
|
|
{
|
|
if (hash_table == NULL) return;
|
|
g_hash_table_remove_all(hash_table);
|
|
g_hash_table_unref(hash_table);
|
|
}
|
|
|
|
gpointer g_hash_table_find(GHashTable *hash_table, GHRFunc predicate, gpointer user_data)
|
|
{
|
|
if (hash_table == NULL) return NULL;
|
|
guint i;
|
|
for (i = 0; i < hash_table->size; i++) {
|
|
GSList *lp;
|
|
for (lp = hash_table->buckets[i]; lp; lp = lp->next) {
|
|
KeyValue *kv = (KeyValue*)(lp->data);
|
|
if ((*predicate)(kv->key, kv->value, user_data)) return kv->value;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
void g_hash_table_foreach(GHashTable *hash_table, GHFunc func, gpointer user_data)
|
|
{
|
|
if (hash_table == NULL) return;
|
|
guint i;
|
|
for (i = 0; i < hash_table->size; i++) {
|
|
GSList *lp;
|
|
for (lp = hash_table->buckets[i]; lp; lp = lp->next) {
|
|
KeyValue *kv = (KeyValue*)(lp->data);
|
|
(*func)(kv->key, kv->value, user_data);
|
|
}
|
|
}
|
|
}
|
|
|
|
gboolean g_hash_table_insert(GHashTable *hash_table, gpointer key, gpointer value)
|
|
{
|
|
if (hash_table == NULL) return TRUE;
|
|
GSList *lp;
|
|
guint hash = (*hash_table->hash_func)(key);
|
|
guint bnum = hash % hash_table->size;
|
|
for (lp = hash_table->buckets[bnum]; lp; lp = lp->next) {
|
|
KeyValue *kv = (KeyValue*)(lp->data);
|
|
int match = hash_table->key_equal_func ? (*hash_table->key_equal_func)(kv->key, key) : (kv->key == key);
|
|
if (match) {
|
|
/* replace */
|
|
kv->value = value;
|
|
return FALSE;
|
|
}
|
|
}
|
|
/* new key */
|
|
KeyValue *pair = (KeyValue*)g_malloc(sizeof(KeyValue));
|
|
pair->key = key;
|
|
pair->value = value;
|
|
hash_table->buckets[bnum] = g_slist_prepend(hash_table->buckets[bnum], pair);
|
|
hash_table->num_entries++;
|
|
/* grow and rehash at num_entries / size == ??? */
|
|
return TRUE;
|
|
}
|
|
|
|
gpointer g_hash_table_lookup(GHashTable *hash_table, gconstpointer key)
|
|
{
|
|
if (hash_table == NULL) return NULL;
|
|
GSList *lp;
|
|
guint hash = (*hash_table->hash_func)(key);
|
|
guint bnum = hash % hash_table->size;
|
|
for (lp = hash_table->buckets[bnum]; lp; lp = lp->next) {
|
|
KeyValue *kv = (KeyValue*)(lp->data);
|
|
int match = hash_table->key_equal_func ? (*hash_table->key_equal_func)(kv->key, key) : (kv->key == key);
|
|
if (match) {
|
|
return kv->value;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
GHashTable *g_hash_table_new(GHashFunc hash_func, GEqualFunc key_equal_func)
|
|
{
|
|
return g_hash_table_new_full(hash_func, key_equal_func, NULL, NULL);
|
|
}
|
|
|
|
GHashTable *g_hash_table_new_full(GHashFunc hash_func, GEqualFunc key_equal_func,
|
|
GDestroyNotify key_destroy_func, GDestroyNotify value_destroy_func)
|
|
{
|
|
GHashTable *ht = (GHashTable*)g_malloc(sizeof(GHashTable));
|
|
ht->hash_func = hash_func ? hash_func : g_direct_hash;
|
|
ht->key_equal_func = key_equal_func;
|
|
ht->key_destroy_func = key_destroy_func;
|
|
ht->value_destroy_func = value_destroy_func;
|
|
g_hash_table_ref(ht);
|
|
ht->size = 512;
|
|
ht->num_entries = 0;
|
|
ht->buckets = (GSList **)g_new0_(sizeof(GSList*), ht->size);
|
|
return ht;
|
|
}
|
|
|
|
void g_hash_table_remove_all(GHashTable *hash_table)
|
|
{
|
|
if (hash_table == NULL) return;
|
|
guint i;
|
|
for (i = 0; i < hash_table->size; i++) {
|
|
GSList *lp;
|
|
for (lp = hash_table->buckets[i]; lp; lp = lp->next) {
|
|
KeyValue *kv = (KeyValue*)lp->data;
|
|
if (hash_table->key_destroy_func) (*hash_table->key_destroy_func)(kv->key);
|
|
if (hash_table->value_destroy_func) (*hash_table->value_destroy_func)(kv->value);
|
|
free(lp->data);
|
|
}
|
|
g_slist_free(hash_table->buckets[i]);
|
|
hash_table->buckets[i] = NULL;
|
|
}
|
|
hash_table->num_entries = 0;
|
|
}
|
|
|
|
gboolean g_hash_table_remove(GHashTable *hash_table, gconstpointer key)
|
|
{
|
|
GSList *lp, *prev = NULL;
|
|
if (hash_table == NULL) return FALSE;
|
|
guint hash = (*hash_table->hash_func)(key);
|
|
guint bnum = hash % hash_table->size;
|
|
for (lp = hash_table->buckets[bnum]; lp; lp = lp->next) {
|
|
KeyValue *kv = (KeyValue*)(lp->data);
|
|
int match = hash_table->key_equal_func ? (*hash_table->key_equal_func)(kv->key, key) : (kv->key == key);
|
|
if (match) {
|
|
if (hash_table->key_destroy_func) (*hash_table->key_destroy_func)(kv->key);
|
|
if (hash_table->value_destroy_func) (*hash_table->value_destroy_func)(kv->value);
|
|
free(kv);
|
|
if (prev == NULL) {
|
|
hash_table->buckets[bnum] = lp->next;
|
|
} else {
|
|
prev->next = lp->next;
|
|
}
|
|
free(lp);
|
|
return TRUE;
|
|
}
|
|
prev = lp;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
void g_hash_table_unref(GHashTable *hash_table)
|
|
{
|
|
if (hash_table == NULL) return;
|
|
if (hash_table->refcount == 0) return;
|
|
hash_table->refcount--;
|
|
if (hash_table->refcount == 0) {
|
|
free(hash_table->buckets);
|
|
free(hash_table);
|
|
}
|
|
}
|
|
|
|
GHashTable *g_hash_table_ref(GHashTable *hash_table)
|
|
{
|
|
if (hash_table == NULL) return NULL;
|
|
hash_table->refcount++;
|
|
return hash_table;
|
|
}
|
|
|
|
guint g_hash_table_size(GHashTable *hash_table)
|
|
{
|
|
return hash_table ? hash_table->num_entries : 0;
|
|
}
|
|
|
|
/* END of g_hash_table related functions */
|
|
|
|
/* general g_XXX substitutes */
|
|
|
|
void g_free(gpointer ptr)
|
|
{
|
|
free(ptr);
|
|
}
|
|
|
|
gpointer g_malloc(size_t size)
|
|
{
|
|
if (size == 0) return NULL;
|
|
void *res = malloc(size);
|
|
if (res == NULL) exit(1);
|
|
return res;
|
|
}
|
|
|
|
gpointer g_malloc0(size_t size)
|
|
{
|
|
if (size == 0) return NULL;
|
|
void *res = calloc(size, 1);
|
|
if (res == NULL) exit(1);
|
|
return res;
|
|
}
|
|
|
|
gpointer g_try_malloc0(size_t size)
|
|
{
|
|
if (size == 0) return NULL;
|
|
return calloc(size, 1);
|
|
}
|
|
|
|
gpointer g_realloc(gpointer ptr, size_t size)
|
|
{
|
|
if (size == 0) {
|
|
free(ptr);
|
|
return NULL;
|
|
}
|
|
void *res = realloc(ptr, size);
|
|
if (res == NULL) exit(1);
|
|
return res;
|
|
}
|
|
|
|
char *g_strdup(const char *str)
|
|
{
|
|
return str ? strdup(str) : NULL;
|
|
}
|
|
|
|
char *g_strdup_printf(const char *format, ...)
|
|
{
|
|
va_list ap;
|
|
char *res;
|
|
va_start(ap, format);
|
|
res = g_strdup_vprintf(format, ap);
|
|
va_end(ap);
|
|
return res;
|
|
}
|
|
|
|
char *g_strdup_vprintf(const char *format, va_list ap)
|
|
{
|
|
char *str_res = NULL;
|
|
vasprintf(&str_res, format, ap);
|
|
return str_res;
|
|
}
|
|
|
|
char *g_strndup(const char *str, size_t n)
|
|
{
|
|
/* try to mimic glib's g_strndup */
|
|
char *res = calloc(n + 1, 1);
|
|
strncpy(res, str, n);
|
|
return res;
|
|
}
|
|
|
|
void g_strfreev(char **str_array)
|
|
{
|
|
char **p = str_array;
|
|
if (p) {
|
|
while (*p) {
|
|
free(*p++);
|
|
}
|
|
}
|
|
free(str_array);
|
|
}
|
|
|
|
gpointer g_memdup(gconstpointer mem, size_t byte_size)
|
|
{
|
|
if (mem) {
|
|
void *res = g_malloc(byte_size);
|
|
memcpy(res, mem, byte_size);
|
|
return res;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
gpointer g_new_(size_t sz, size_t n_structs)
|
|
{
|
|
size_t need = sz * n_structs;
|
|
if ((need / sz) != n_structs) return NULL;
|
|
return g_malloc(need);
|
|
}
|
|
|
|
gpointer g_new0_(size_t sz, size_t n_structs)
|
|
{
|
|
size_t need = sz * n_structs;
|
|
if ((need / sz) != n_structs) return NULL;
|
|
return g_malloc0(need);
|
|
}
|
|
|
|
gpointer g_renew_(size_t sz, gpointer mem, size_t n_structs)
|
|
{
|
|
size_t need = sz * n_structs;
|
|
if ((need / sz) != n_structs) return NULL;
|
|
return g_realloc(mem, need);
|
|
}
|
|
|
|
char *g_strconcat (const char *string1, ...)
|
|
{
|
|
va_list ap;
|
|
char *res;
|
|
size_t sz = strlen(string1);
|
|
va_start(ap, string1);
|
|
while (1) {
|
|
char *arg = va_arg(ap, char*);
|
|
if (arg == NULL) break;
|
|
sz += strlen(arg);
|
|
}
|
|
va_end(ap);
|
|
res = g_malloc(sz + 1);
|
|
strcpy(res, string1);
|
|
va_start(ap, string1);
|
|
while (1) {
|
|
char *arg = va_arg(ap, char*);
|
|
if (arg == NULL) break;
|
|
strcat(res, arg);
|
|
}
|
|
va_end(ap);
|
|
return res;
|
|
}
|
|
|
|
char **g_strsplit(const char *string, const char *delimiter, int max_tokens)
|
|
{
|
|
char **res;
|
|
if (string == NULL || *string == 0) {
|
|
res = (char**)g_malloc(sizeof(char*));
|
|
*res = NULL;
|
|
} else {
|
|
uint32_t ntokens, i, max = (uint32_t) max_tokens;
|
|
if (max == 0) max--;
|
|
int dlen = strlen(delimiter);
|
|
const char *p = string, *b;
|
|
for (ntokens = 1; ntokens < max; ntokens++) {
|
|
p = strstr(p, delimiter);
|
|
if (p == NULL) break;
|
|
p += dlen;
|
|
}
|
|
res = (char**)g_new_(sizeof(char*), ntokens + 1);
|
|
p = string;
|
|
for (b = p, i = 0; i < ntokens; b = p, i++) {
|
|
int len;
|
|
if (i == (ntokens - 1)) {
|
|
/* last piece special handling */
|
|
res[i] = strdup(b);
|
|
} else {
|
|
p = strstr(b, delimiter);
|
|
len = p - b;
|
|
res[i] = (char*)g_malloc(len + 1);
|
|
memcpy(res[i], b, len);
|
|
res[i][len] = 0;
|
|
p += dlen;
|
|
}
|
|
}
|
|
res[ntokens] = NULL;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
#ifdef _WIN32
|
|
|
|
#include <windows.h>
|
|
|
|
char *g_win32_error_message(int error)
|
|
{
|
|
char *msg;
|
|
char *winMsg = NULL;
|
|
if (error == 0) {
|
|
return (char*)g_malloc0(1);
|
|
}
|
|
|
|
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
|
NULL, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&msg, 0, NULL);
|
|
|
|
/* give the caller something they can just free */
|
|
msg = strdup(winMsg);
|
|
/* Free the allocated message. */
|
|
HeapFree(GetProcessHeap(), 0, winMsg);
|
|
|
|
return msg;
|
|
}
|
|
|
|
#endif
|