mirror of
https://github.com/reactos/reactos.git
synced 2024-09-06 10:52:52 +00:00
257 lines
7.4 KiB
C
257 lines
7.4 KiB
C
![]() |
/* Written by Krzysztof Kowalczyk (http://blog.kowalczyk.info)
|
||
|
The author disclaims copyright to this source code. */
|
||
|
#include "base_util.h"
|
||
|
#include "tstr_util.h"
|
||
|
#include "prefs_util.h"
|
||
|
#include "netstr.h"
|
||
|
|
||
|
/* length of PT_*_PREFIX string in characters. All should have the same length */
|
||
|
#define TYPE_PREFIX_CCH_LEN 2
|
||
|
|
||
|
/* when we serialize names of variables, we prepend name with the following
|
||
|
type indentifiers */
|
||
|
#define PT_INT_PREFIX _T("i ")
|
||
|
#define PT_STRING_PREFIX _T("s ")
|
||
|
|
||
|
static int pref_type_valid(pref_type type)
|
||
|
{
|
||
|
if (PT_INT == type)
|
||
|
return TRUE;
|
||
|
if (PT_STRING == type)
|
||
|
return TRUE;
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
/* Given a string value 'txt' and it's type 'type', return a string that
|
||
|
encodes type within a string */
|
||
|
TCHAR *pref_tstr_with_type(const TCHAR *txt, pref_type type)
|
||
|
{
|
||
|
if (PT_INT == type)
|
||
|
return tstr_cat(PT_INT_PREFIX, txt);
|
||
|
else if (PT_STRING == type)
|
||
|
return tstr_cat(PT_STRING_PREFIX, txt);
|
||
|
else
|
||
|
assert(0);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
/* Serialize 'pref' to a buffer 'buf_ptr' of size 'buf_len_cb_ptr'.
|
||
|
Return TRUE if ok, FALSE if failed (e.g. buffer is not large enough).
|
||
|
If 'buf_ptr' is NULL, returns desired size in 'buf_len_cb_ptr'.
|
||
|
*/
|
||
|
static int prefs_serialize_pref(prefs_data *pref, TCHAR **buf_ptr, size_t *buf_len_cb_ptr)
|
||
|
{
|
||
|
size_t len_cb;
|
||
|
TCHAR * name_with_type;
|
||
|
int f_ok;
|
||
|
|
||
|
assert(pref);
|
||
|
assert(pref->name);
|
||
|
assert(pref_type_valid(pref->type));
|
||
|
assert(buf_len_cb_ptr);
|
||
|
|
||
|
if (!buf_ptr) {
|
||
|
len_cb = netstr_tstrn_serialized_len_cb(TYPE_PREFIX_CCH_LEN + tstr_len(pref->name));
|
||
|
if (PT_INT == pref->type)
|
||
|
len_cb += netstr_int_serialized_len_cb(*pref->data.data_int);
|
||
|
else if (PT_STRING == pref->type)
|
||
|
len_cb += netstr_tstr_serialized_len_cb(*pref->data.data_str);
|
||
|
else
|
||
|
assert(0);
|
||
|
*buf_len_cb_ptr = len_cb;
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
name_with_type = pref_tstr_with_type(pref->name, pref->type);
|
||
|
if (!name_with_type) return FALSE;
|
||
|
f_ok = netstr_tstr_serialize(name_with_type, buf_ptr, buf_len_cb_ptr);
|
||
|
free((void*)name_with_type);
|
||
|
if (!f_ok)
|
||
|
return FALSE;
|
||
|
|
||
|
if (PT_INT == pref->type)
|
||
|
f_ok = netstr_int_serialize(*pref->data.data_int, buf_ptr, buf_len_cb_ptr);
|
||
|
else if (PT_STRING == pref->type)
|
||
|
f_ok = netstr_tstr_serialize(*pref->data.data_str, buf_ptr, buf_len_cb_ptr);
|
||
|
else
|
||
|
assert(0);
|
||
|
|
||
|
if (!f_ok)
|
||
|
return FALSE;
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
/* Return the size of memory required to serialize 'pref' data */
|
||
|
static size_t prefs_serialized_pref_cb_len(prefs_data *pref)
|
||
|
{
|
||
|
int f_ok;
|
||
|
size_t len;
|
||
|
|
||
|
f_ok = prefs_serialize_pref(pref, NULL, &len);
|
||
|
if (!f_ok)
|
||
|
return 0;
|
||
|
return len;
|
||
|
}
|
||
|
|
||
|
/* Serialize 'prefs' as string. Returns newly allocated string and
|
||
|
length, in bytes, of string in '*tstr_len_cb_ptr' (not including
|
||
|
terminating zero). 'tstr_len_cb_ptr' can be NULL.
|
||
|
Returns NULL on error.
|
||
|
Caller needs to free() the result */
|
||
|
TCHAR *prefs_to_tstr(prefs_data *prefs, size_t *tstr_len_cb_ptr)
|
||
|
{
|
||
|
int i = 0;
|
||
|
size_t total_serialized_len_cb = 0;
|
||
|
size_t len_cb;
|
||
|
int f_ok;
|
||
|
TCHAR * serialized = NULL;
|
||
|
TCHAR * tmp;
|
||
|
size_t tmp_len_cb;
|
||
|
|
||
|
/* calculate the size of buffer required to serialize 'prefs' */
|
||
|
while (prefs[i].name) {
|
||
|
len_cb = prefs_serialized_pref_cb_len(&(prefs[i]));
|
||
|
assert(len_cb > 0);
|
||
|
total_serialized_len_cb += len_cb;
|
||
|
++i;
|
||
|
}
|
||
|
|
||
|
if (0 == total_serialized_len_cb)
|
||
|
return NULL;
|
||
|
|
||
|
/* allocate the buffer and serialize to it */
|
||
|
serialized = (TCHAR*)malloc(total_serialized_len_cb+sizeof(TCHAR));
|
||
|
if (!serialized) return NULL;
|
||
|
tmp = serialized;
|
||
|
tmp_len_cb = total_serialized_len_cb;
|
||
|
i = 0;
|
||
|
while (prefs[i].name) {
|
||
|
f_ok = prefs_serialize_pref(&(prefs[i]), &tmp, &tmp_len_cb);
|
||
|
assert(f_ok);
|
||
|
assert(tmp_len_cb >= 0);
|
||
|
++i;
|
||
|
}
|
||
|
assert(0 == tmp_len_cb);
|
||
|
*tmp = 0;
|
||
|
if (tstr_len_cb_ptr)
|
||
|
*tstr_len_cb_ptr = total_serialized_len_cb;
|
||
|
return serialized;
|
||
|
}
|
||
|
|
||
|
/* Find a variable with a given 'name' and 'type' in 'prefs' array */
|
||
|
prefs_data *prefs_find_by_name_type(prefs_data *prefs, const TCHAR *name, pref_type type)
|
||
|
{
|
||
|
int i = 0;
|
||
|
while (prefs[i].name) {
|
||
|
if ((prefs[i].type == type) && (tstr_ieq(name, prefs[i].name))) {
|
||
|
return &(prefs[i]);
|
||
|
}
|
||
|
++i;
|
||
|
}
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
/* Incrementally parse one serialized variable in a string '*str_ptr' of
|
||
|
remaining size '*str_len_cb_ptr'.
|
||
|
It reads name, type and value of the variable from the string and
|
||
|
updates 'prefs' slot with this name/type with this value. If slot
|
||
|
with a given name/type doesn't exist, nothing happens.
|
||
|
It updates the '*str_ptr' and '*str_len_cb_ptr' to reflect consuming
|
||
|
the part that contained one variable. The idea is to call it in a
|
||
|
loop until '*str_len_cb_ptr' reaches 0.
|
||
|
Returns FALSE on error. */
|
||
|
static int prefs_parse_item(prefs_data *prefs, const TCHAR **str_ptr, size_t *str_len_cb_ptr)
|
||
|
{
|
||
|
const TCHAR * name_with_type = NULL;
|
||
|
const TCHAR * name;
|
||
|
size_t str_len_cch;
|
||
|
const TCHAR * value_str = NULL;
|
||
|
int value_int;
|
||
|
int f_ok;
|
||
|
pref_type type;
|
||
|
prefs_data * pref;
|
||
|
|
||
|
assert(str_ptr);
|
||
|
if (!str_ptr) return FALSE;
|
||
|
assert(str_len_cb_ptr);
|
||
|
if (!str_len_cb_ptr) return FALSE;
|
||
|
|
||
|
f_ok = netstr_parse_str(str_ptr, str_len_cb_ptr, &name_with_type, &str_len_cch);
|
||
|
if (!f_ok)
|
||
|
goto Error;
|
||
|
|
||
|
if (tstr_startswithi(name_with_type, PT_INT_PREFIX))
|
||
|
type = PT_INT;
|
||
|
else if (tstr_startswithi(name_with_type, PT_STRING_PREFIX))
|
||
|
type = PT_STRING;
|
||
|
else {
|
||
|
assert(0);
|
||
|
goto Error;
|
||
|
}
|
||
|
/* skip the type prefix */
|
||
|
name = name_with_type + TYPE_PREFIX_CCH_LEN;
|
||
|
|
||
|
pref = prefs_find_by_name_type(prefs, name, type);
|
||
|
|
||
|
if (PT_STRING == type)
|
||
|
f_ok = netstr_parse_str(str_ptr, str_len_cb_ptr, &value_str, &str_len_cch);
|
||
|
else if (PT_INT == type)
|
||
|
f_ok = netstr_parse_int(str_ptr, str_len_cb_ptr, &value_int);
|
||
|
else {
|
||
|
assert(0);
|
||
|
goto Error;
|
||
|
}
|
||
|
if (!f_ok)
|
||
|
goto Error;
|
||
|
|
||
|
if (!pref) {
|
||
|
/* it's ok to not have a given preference e.g. when changing version some of the
|
||
|
preferences might go away. But we still want to be notified about that during
|
||
|
developement, since it's unlikely thing to happen */
|
||
|
assert(0);
|
||
|
goto Exit;
|
||
|
}
|
||
|
|
||
|
if (PT_INT == type)
|
||
|
*pref->data.data_int = value_int;
|
||
|
else if (PT_STRING == type) {
|
||
|
/* taking memory ownership */
|
||
|
*pref->data.data_str = (TCHAR*)value_str;
|
||
|
value_str = NULL;
|
||
|
} else {
|
||
|
assert(0);
|
||
|
goto Error;
|
||
|
}
|
||
|
|
||
|
Exit:
|
||
|
free((void*)name_with_type);
|
||
|
free((void*)value_str);
|
||
|
return TRUE;
|
||
|
Error:
|
||
|
free((void*)name_with_type);
|
||
|
free((void*)value_str);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
int prefs_from_tstr(prefs_data *prefs, const TCHAR *str, size_t str_len_cch)
|
||
|
{
|
||
|
int f_ok;
|
||
|
size_t str_len_cb;
|
||
|
|
||
|
assert(str);
|
||
|
if (!str) return FALSE;
|
||
|
|
||
|
if (-1 == str_len_cch)
|
||
|
str_len_cch = tstr_len(str);
|
||
|
|
||
|
str_len_cb = str_len_cch * sizeof(TCHAR);
|
||
|
while (0 != str_len_cb) {
|
||
|
f_ok = prefs_parse_item(prefs, &str, &str_len_cb);
|
||
|
if (!f_ok)
|
||
|
return FALSE;
|
||
|
}
|
||
|
assert(0 == str_len_cb);
|
||
|
return TRUE;
|
||
|
}
|