#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <inttypes.h>
#include <ctype.h>
#include <math.h>
#include <limits.h>
#include <node.h>
#include "plist.h"
#include "strbuf.h"
#include "time64.h"
#include "hashtable.h"
#include "common.h"
#ifdef DEBUG
static int plist_ostep_debug = 0;
#define PLIST_OSTEP_ERR(...) if (plist_ostep_debug) { fprintf(stderr, "libplist[ostepparser] ERROR: " __VA_ARGS__); }
#define PLIST_OSTEP_WRITE_ERR(...) if (plist_ostep_debug) { fprintf(stderr, "libplist[ostepwriter] ERROR: " __VA_ARGS__); }
#else
#define PLIST_OSTEP_ERR(...)
#define PLIST_OSTEP_WRITE_ERR(...)
#endif
void plist_ostep_init(void)
{
#ifdef DEBUG
char *env_debug = getenv("PLIST_OSTEP_DEBUG");
if (env_debug && !strcmp(env_debug, "1")) {
plist_ostep_debug = 1;
}
#endif
}
void plist_ostep_deinit(void)
{
}
void plist_ostep_set_debug(int debug)
{
#if DEBUG
plist_ostep_debug = debug;
#endif
}
#ifndef HAVE_STRNDUP
#ifndef _MSC_VER
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wshadow"
#endif
static char* strndup(const char* str, size_t len)
{
char *newstr = (char *)malloc(len+1);
if (newstr) {
strncpy(newstr, str, len);
newstr[len]= '\0';
}
return newstr;
}
#ifndef _MSC_VER
#pragma GCC diagnostic pop
#endif
#endif
static const char allowed_unquoted_chars[256] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
};
static int str_needs_quotes(const char* str, size_t len)
{
size_t i;
for (i = 0; i < len; i++) {
if (!allowed_unquoted_chars[(unsigned char)str[i]]) {
return 1;
}
}
return 0;
}
static plist_err_t node_to_openstep(node_t node, bytearray_t **outbuf, uint32_t depth, int prettify, int coerce)
{
plist_data_t node_data = NULL;
char *val = NULL;
size_t val_len = 0;
uint32_t i = 0;
if (!node)
return PLIST_ERR_INVALID_ARG;
node_data = plist_get_data(node);
switch (node_data->type)
{
case PLIST_INT:
val = (char*)malloc(64);
if (node_data->length == 16) {
val_len = snprintf(val, 64, "%" PRIu64, node_data->intval);
} else {
val_len = snprintf(val, 64, "%" PRIi64, node_data->intval);
}
str_buf_append(*outbuf, val, val_len);
free(val);
break;
case PLIST_REAL:
val = (char*)malloc(64);
val_len = dtostr(val, 64, node_data->realval);
str_buf_append(*outbuf, val, val_len);
free(val);
break;
case PLIST_STRING:
case PLIST_KEY: {
const char *charmap[32] = {
"\\U0000", "\\U0001", "\\U0002", "\\U0003", "\\U0004", "\\U0005", "\\U0006", "\\U0007",
"\\b", "\\t", "\\n", "\\U000b", "\\f", "\\r", "\\U000e", "\\U000f",
"\\U0010", "\\U0011", "\\U0012", "\\U0013", "\\U0014", "\\U0015", "\\U0016", "\\U0017",
"\\U0018", "\\U0019", "\\U001a", "\\U001b", "\\U001c", "\\U001d", "\\U001e", "\\U001f",
};
size_t j = 0;
size_t len = 0;
off_t start = 0;
off_t cur = 0;
int needs_quotes;
len = node_data->length;
needs_quotes = str_needs_quotes(node_data->strval, len);
if (needs_quotes) {
str_buf_append(*outbuf, "\"", 1);
}
for (j = 0; j < len; j++) {
unsigned char ch = (unsigned char)node_data->strval[j];
if (ch < 0x20) {
str_buf_append(*outbuf, node_data->strval + start, cur - start);
str_buf_append(*outbuf, charmap[ch], (charmap[ch][1] == 'u') ? 6 : 2);
start = cur+1;
} else if (ch == '"') {
str_buf_append(*outbuf, node_data->strval + start, cur - start);
str_buf_append(*outbuf, "\\\"", 2);
start = cur+1;
}
cur++;
}
str_buf_append(*outbuf, node_data->strval + start, cur - start);
if (needs_quotes) {
str_buf_append(*outbuf, "\"", 1);
}
} break;
case PLIST_ARRAY: {
str_buf_append(*outbuf, "(", 1);
node_t ch;
uint32_t cnt = 0;
for (ch = node_first_child(node); ch; ch = node_next_sibling(ch)) {
if (cnt > 0) {
str_buf_append(*outbuf, ",", 1);
}
if (prettify) {
str_buf_append(*outbuf, "\n", 1);
for (i = 0; i <= depth; i++) {
str_buf_append(*outbuf, " ", 2);
}
}
plist_err_t res = node_to_openstep(ch, outbuf, depth+1, prettify, coerce);
if (res < 0) {
return res;
}
cnt++;
}
if (cnt > 0 && prettify) {
str_buf_append(*outbuf, "\n", 1);
for (i = 0; i < depth; i++) {
str_buf_append(*outbuf, " ", 2);
}
}
str_buf_append(*outbuf, ")", 1);
} break;
case PLIST_DICT: {
str_buf_append(*outbuf, "{", 1);
node_t ch;
uint32_t cnt = 0;
for (ch = node_first_child(node); ch; ch = node_next_sibling(ch)) {
if (cnt > 0 && cnt % 2 == 0) {
str_buf_append(*outbuf, ";", 1);
}
if (cnt % 2 == 0 && prettify) {
str_buf_append(*outbuf, "\n", 1);
for (i = 0; i <= depth; i++) {
str_buf_append(*outbuf, " ", 2);
}
}
plist_err_t res = node_to_openstep(ch, outbuf, depth+1, prettify, coerce);
if (res < 0) {
return res;
}
if (cnt % 2 == 0) {
if (prettify) {
str_buf_append(*outbuf, " = ", 3);
} else {
str_buf_append(*outbuf, "=", 1);
}
}
cnt++;
}
if (cnt > 0) {
str_buf_append(*outbuf, ";", 1);
}
if (cnt > 0 && prettify) {
str_buf_append(*outbuf, "\n", 1);
for (i = 0; i < depth; i++) {
str_buf_append(*outbuf, " ", 2);
}
}
str_buf_append(*outbuf, "}", 1);
} break;
case PLIST_DATA: {
size_t j = 0;
size_t len = 0;
str_buf_append(*outbuf, "<", 1);
len = node_data->length;
for (j = 0; j < len; j++) {
char charb[4];
if (prettify && j > 0 && (j % 4 == 0))
str_buf_append(*outbuf, " ", 1);
sprintf(charb, "%02x", (unsigned char)node_data->buff[j]);
str_buf_append(*outbuf, charb, 2);
}
str_buf_append(*outbuf, ">", 1);
} break;
case PLIST_BOOLEAN:
if (coerce) {
if (node_data->boolval) {
str_buf_append(*outbuf, "1", 1);
} else {
str_buf_append(*outbuf, "0", 1);
}
} else {
PLIST_OSTEP_WRITE_ERR("PLIST_BOOLEAN type is not valid for OpenStep format\n");
return PLIST_ERR_FORMAT;
}
break;
case PLIST_NULL:
if (coerce) {
str_buf_append(*outbuf, "NULL", 4);
} else {
PLIST_OSTEP_WRITE_ERR("PLIST_NULL type is not valid for OpenStep format\n");
return PLIST_ERR_FORMAT;
}
break;
case PLIST_DATE:
if (coerce) {
Time64_T timev;
if (plist_real_to_time64(node_data->realval, &timev) < 0) {
PLIST_OSTEP_WRITE_ERR("Encountered invalid date value %f\n", node_data->realval);
return PLIST_ERR_INVALID_ARG;
}
struct TM _btime;
struct TM *btime = gmtime64_r(&timev, &_btime);
char datebuf[32];
size_t datelen = 0;
if (btime) {
struct tm _tmcopy;
copy_TM64_to_tm(btime, &_tmcopy);
datelen = strftime(datebuf, sizeof(datebuf), "%Y-%m-%dT%H:%M:%SZ", &_tmcopy);
}
if (datelen <= 0) {
datelen = snprintf(datebuf, sizeof(datebuf), "1970-01-01T00:00:00Z");
}
str_buf_append(*outbuf, "\"", 1);
str_buf_append(*outbuf, datebuf, datelen);
str_buf_append(*outbuf, "\"", 1);
} else {
PLIST_OSTEP_WRITE_ERR("PLIST_DATE type is not valid for OpenStep format\n");
return PLIST_ERR_FORMAT;
}
break;
case PLIST_UID:
if (coerce) {
val = (char*)malloc(64);
if (node_data->length == 16) {
val_len = snprintf(val, 64, "%" PRIu64, node_data->intval);
} else {
val_len = snprintf(val, 64, "%" PRIi64, node_data->intval);
}
str_buf_append(*outbuf, val, val_len);
free(val);
} else {
PLIST_OSTEP_WRITE_ERR("PLIST_UID type is not valid for OpenStep format\n");
return PLIST_ERR_FORMAT;
}
break;
default:
return PLIST_ERR_UNKNOWN;
}
return PLIST_ERR_SUCCESS;
}
static plist_err_t _node_estimate_size(node_t node, uint64_t *size, uint32_t depth, int prettify, int coerce, hashtable_t *visited)
{
plist_data_t data;
if (!node) {
return PLIST_ERR_INVALID_ARG;
}
if (depth > PLIST_MAX_NESTING_DEPTH) {
PLIST_OSTEP_WRITE_ERR("node tree is nested too deeply\n");
return PLIST_ERR_MAX_NESTING;
}
if (hash_table_lookup(visited, node)) {
PLIST_OSTEP_WRITE_ERR("circular reference detected\n");
return PLIST_ERR_CIRCULAR_REF;
}
hash_table_insert(visited, node, (void*)1);
data = plist_get_data(node);
if (node->children) {
node_t ch;
unsigned int n_children = node_n_children(node);
for (ch = node_first_child(node); ch; ch = node_next_sibling(ch)) {
plist_err_t res = _node_estimate_size(ch, size, depth + 1, prettify, coerce, visited);
if (res < 0) {
return res;
}
}
switch (data->type) {
case PLIST_DICT:
*size += 2; *size += n_children; if (prettify) {
*size += n_children*2; *size += (uint64_t)n_children * (depth+1); *size += 1; }
break;
case PLIST_ARRAY:
*size += 2; *size += n_children-1; if (prettify) {
*size += n_children; *size += (uint64_t)n_children * ((depth+1)<<1); *size += 1; }
break;
default:
break;
}
if (prettify)
*size += (depth << 1); } else {
switch (data->type) {
case PLIST_STRING:
case PLIST_KEY:
*size += data->length;
*size += 2;
break;
case PLIST_INT:
if (data->length == 16) {
*size += num_digits_u(data->intval);
} else {
*size += num_digits_i((int64_t)data->intval);
}
break;
case PLIST_REAL:
*size += dtostr(NULL, 0, data->realval);
break;
case PLIST_DICT:
case PLIST_ARRAY:
*size += 2;
break;
case PLIST_DATA:
*size += 2; *size += data->length*2;
if (prettify)
*size += data->length/4;
break;
case PLIST_BOOLEAN:
if (coerce) {
*size += 1;
} else {
PLIST_OSTEP_WRITE_ERR("PLIST_BOOLEAN type is not valid for OpenStep format\n");
return PLIST_ERR_FORMAT;
}
break;
case PLIST_DATE:
if (coerce) {
*size += 24;
} else {
PLIST_OSTEP_WRITE_ERR("PLIST_DATE type is not valid for OpenStep format\n");
return PLIST_ERR_FORMAT;
}
break;
case PLIST_UID:
if (coerce) {
if (data->length == 16) {
*size += num_digits_u(data->intval);
} else {
*size += num_digits_i((int64_t)data->intval);
}
} else {
PLIST_OSTEP_WRITE_ERR("PLIST_UID type is not valid for OpenStep format\n");
return PLIST_ERR_FORMAT;
}
break;
case PLIST_NULL:
if (coerce) {
*size += 4;
} else {
PLIST_OSTEP_WRITE_ERR("PLIST_NULL type is not valid for OpenStep format\n");
return PLIST_ERR_FORMAT;
}
break;
default:
PLIST_OSTEP_WRITE_ERR("invalid node type encountered\n");
return PLIST_ERR_UNKNOWN;
}
}
return PLIST_ERR_SUCCESS;
}
static plist_err_t node_estimate_size(node_t node, uint64_t *size, uint32_t depth, int prettify, int coerce)
{
hashtable_t *visited = hash_table_new(plist_node_ptr_hash, plist_node_ptr_compare, NULL);
if (!visited) return PLIST_ERR_NO_MEM;
plist_err_t err = _node_estimate_size(node, size, depth, prettify, coerce, visited);
hash_table_destroy(visited);
return err;
}
plist_err_t plist_to_openstep(plist_t plist, char **openstep, uint32_t* length, int prettify)
{
plist_write_options_t opts = prettify ? PLIST_OPT_NONE : PLIST_OPT_COMPACT;
return plist_to_openstep_with_options(plist, openstep, length, opts);
}
plist_err_t plist_to_openstep_with_options(plist_t plist, char **openstep, uint32_t* length, plist_write_options_t options)
{
uint64_t size = 0;
plist_err_t res;
if (!plist || !openstep || !length) {
return PLIST_ERR_INVALID_ARG;
}
int prettify = !(options & PLIST_OPT_COMPACT);
int coerce = options & PLIST_OPT_COERCE;
res = node_estimate_size((node_t)plist, &size, 0, prettify, coerce);
if (res < 0) {
return res;
}
strbuf_t *outbuf = str_buf_new(size);
if (!outbuf) {
PLIST_OSTEP_WRITE_ERR("Could not allocate output buffer");
return PLIST_ERR_NO_MEM;
}
res = node_to_openstep((node_t)plist, &outbuf, 0, prettify, coerce);
if (res < 0) {
str_buf_free(outbuf);
*openstep = NULL;
*length = 0;
return res;
}
if (prettify) {
str_buf_append(outbuf, "\n", 1);
}
str_buf_append(outbuf, "\0", 1);
*openstep = (char*)outbuf->data;
*length = outbuf->len - 1;
outbuf->data = NULL;
str_buf_free(outbuf);
return PLIST_ERR_SUCCESS;
}
struct _parse_ctx {
const char *start;
const char *pos;
const char *end;
plist_err_t err;
uint32_t depth;
};
typedef struct _parse_ctx* parse_ctx;
static void parse_skip_ws(parse_ctx ctx)
{
while (ctx->pos < ctx->end) {
if (*ctx->pos == '/' && (ctx->end - ctx->pos > 1)) {
if (*(ctx->pos+1) == '/') {
ctx->pos++;
while (ctx->pos < ctx->end) {
if ((*ctx->pos == '\n') || (*ctx->pos == '\r')) {
break;
}
ctx->pos++;
}
} else if (*(ctx->pos+1) == '*') {
ctx->pos++;
while (ctx->pos < ctx->end) {
if (*ctx->pos == '*' && (ctx->end - ctx->pos > 1)) {
if (*(ctx->pos+1) == '/') {
ctx->pos+=2;
break;
}
}
ctx->pos++;
}
}
if (ctx->pos >= ctx->end) {
break;
}
}
if (!(((*(ctx->pos) == ' ') || (*(ctx->pos) == '\t') || (*(ctx->pos) == '\r') || (*(ctx->pos) == '\n')))) {
break;
}
ctx->pos++;
}
}
#define HEX_DIGIT(x) ((x <= '9') ? (x - '0') : ((x <= 'F') ? (x - 'A' + 10) : (x - 'a' + 10)))
static plist_err_t node_from_openstep(parse_ctx ctx, plist_t *plist);
static void parse_dict_data(parse_ctx ctx, plist_t dict)
{
plist_t key = NULL;
plist_t val = NULL;
while (ctx->pos < ctx->end && !ctx->err) {
parse_skip_ws(ctx);
if (ctx->pos >= ctx->end || *ctx->pos == '}') {
break;
}
key = NULL;
ctx->err = node_from_openstep(ctx, &key);
if (ctx->err != PLIST_ERR_SUCCESS) {
break;
}
if (!PLIST_IS_STRING(key)) {
PLIST_OSTEP_ERR("Invalid type for dictionary key at offset %ld\n", (long int)(ctx->pos - ctx->start));
ctx->err = PLIST_ERR_PARSE;
break;
}
parse_skip_ws(ctx);
if (ctx->pos >= ctx->end) {
PLIST_OSTEP_ERR("EOF while parsing dictionary '=' delimiter at offset %ld\n", (long int)(ctx->pos - ctx->start));
ctx->err = PLIST_ERR_PARSE;
break;
}
if (*ctx->pos != '=') {
PLIST_OSTEP_ERR("Missing '=' while parsing dictionary item at offset %ld\n", (long int)(ctx->pos - ctx->start));
ctx->err = PLIST_ERR_PARSE;
break;
}
ctx->pos++;
if (ctx->pos >= ctx->end) {
PLIST_OSTEP_ERR("EOF while parsing dictionary item at offset %ld\n", (long int)(ctx->pos - ctx->start));
ctx->err = PLIST_ERR_PARSE;
break;
}
val = NULL;
ctx->err = node_from_openstep(ctx, &val);
if (ctx->err != PLIST_ERR_SUCCESS) {
break;
}
if (!val) {
PLIST_OSTEP_ERR("Missing value for dictionary item at offset %ld\n", (long int)(ctx->pos - ctx->start));
ctx->err = PLIST_ERR_PARSE;
break;
}
parse_skip_ws(ctx);
if (ctx->pos >= ctx->end) {
PLIST_OSTEP_ERR("EOF while parsing dictionary item terminator ';' at offset %ld\n", (long int)(ctx->pos - ctx->start));
ctx->err = PLIST_ERR_PARSE;
break;
}
if (*ctx->pos != ';') {
PLIST_OSTEP_ERR("Missing terminating ';' while parsing dictionary item at offset %ld\n", (long int)(ctx->pos - ctx->start));
ctx->err = PLIST_ERR_PARSE;
break;
}
plist_dict_set_item(dict, plist_get_string_ptr(key, NULL), val);
plist_free(key);
key = NULL;
val = NULL;
ctx->pos++;
}
plist_free(key);
plist_free(val);
}
static plist_err_t node_from_openstep(parse_ctx ctx, plist_t *plist)
{
plist_t subnode = NULL;
const char *p = NULL;
ctx->depth++;
if (ctx->depth > PLIST_MAX_NESTING_DEPTH) {
PLIST_OSTEP_ERR("Too many levels of recursion (%u) at offset %ld\n", ctx->depth, (long int)(ctx->pos - ctx->start));
ctx->err = PLIST_ERR_MAX_NESTING;
return ctx->err;
}
while (ctx->pos < ctx->end && !ctx->err) {
parse_skip_ws(ctx);
if (ctx->pos >= ctx->end) {
break;
}
plist_data_t data = plist_new_plist_data();
if (*ctx->pos == '{') {
data->type = PLIST_DICT;
subnode = plist_new_node(data);
ctx->pos++;
parse_dict_data(ctx, subnode);
if (ctx->err) {
goto err_out;
}
if (ctx->pos >= ctx->end) {
PLIST_OSTEP_ERR("EOF while parsing dictionary terminator '}' at offset %ld\n", (long int)(ctx->pos - ctx->start));
ctx->err = PLIST_ERR_PARSE;
break;
}
if (*ctx->pos != '}') {
PLIST_OSTEP_ERR("Missing terminating '}' at offset %ld\n", (long int)(ctx->pos - ctx->start));
ctx->err = PLIST_ERR_PARSE;
goto err_out;
}
ctx->pos++;
*plist = subnode;
parse_skip_ws(ctx);
break;
} else if (*ctx->pos == '(') {
data->type = PLIST_ARRAY;
subnode = plist_new_node(data);
ctx->pos++;
plist_t tmp = NULL;
while (ctx->pos < ctx->end && !ctx->err) {
parse_skip_ws(ctx);
if (ctx->pos >= ctx->end || *ctx->pos == ')') {
break;
}
ctx->err = node_from_openstep(ctx, &tmp);
if (ctx->err != PLIST_ERR_SUCCESS) {
break;
}
if (!tmp) {
ctx->err = PLIST_ERR_PARSE;
break;
}
plist_array_append_item(subnode, tmp);
tmp = NULL;
parse_skip_ws(ctx);
if (ctx->pos >= ctx->end) {
PLIST_OSTEP_ERR("EOF while parsing array item delimiter ',' at offset %ld\n", (long int)(ctx->pos - ctx->start));
ctx->err = PLIST_ERR_PARSE;
break;
}
if (*ctx->pos != ',') {
break;
}
ctx->pos++;
}
plist_free(tmp);
tmp = NULL;
if (ctx->err != PLIST_ERR_SUCCESS) {
goto err_out;
}
if (ctx->pos >= ctx->end) {
PLIST_OSTEP_ERR("EOF while parsing array terminator ')' at offset %ld\n", (long int)(ctx->pos - ctx->start));
ctx->err = PLIST_ERR_PARSE;
break;
}
if (*ctx->pos != ')') {
PLIST_OSTEP_ERR("Missing terminating ')' at offset %ld\n", (long int)(ctx->pos - ctx->start));
ctx->err = PLIST_ERR_PARSE;
goto err_out;
}
ctx->pos++;
*plist = subnode;
parse_skip_ws(ctx);
break;
} else if (*ctx->pos == '<') {
data->type = PLIST_DATA;
ctx->pos++;
bytearray_t *bytes = byte_array_new(256);
while (ctx->pos < ctx->end && !ctx->err) {
parse_skip_ws(ctx);
if (ctx->pos >= ctx->end) {
PLIST_OSTEP_ERR("EOF while parsing data terminator '>' at offset %ld\n", (long int)(ctx->pos - ctx->start));
ctx->err = PLIST_ERR_PARSE;
break;
}
if (*ctx->pos == '>') {
break;
}
if (!isxdigit(*ctx->pos)) {
PLIST_OSTEP_ERR("Invalid byte group in data at offset %ld\n", (long int)(ctx->pos - ctx->start));
ctx->err = PLIST_ERR_PARSE;
break;
}
uint8_t b = HEX_DIGIT(*ctx->pos);
ctx->pos++;
if (ctx->pos >= ctx->end) {
PLIST_OSTEP_ERR("Unexpected end of data at offset %ld\n", (long int)(ctx->pos - ctx->start));
ctx->err = PLIST_ERR_PARSE;
break;
}
if (!isxdigit(*ctx->pos)) {
PLIST_OSTEP_ERR("Invalid byte group in data at offset %ld\n", (long int)(ctx->pos - ctx->start));
ctx->err = PLIST_ERR_PARSE;
break;
}
b = (b << 4) + HEX_DIGIT(*ctx->pos);
byte_array_append(bytes, &b, 1);
ctx->pos++;
}
if (ctx->err) {
byte_array_free(bytes);
plist_free_data(data);
goto err_out;
}
if (ctx->pos >= ctx->end) {
byte_array_free(bytes);
plist_free_data(data);
PLIST_OSTEP_ERR("EOF while parsing data terminator '>' at offset %ld\n", (long int)(ctx->pos - ctx->start));
ctx->err = PLIST_ERR_PARSE;
goto err_out;
}
if (*ctx->pos != '>') {
byte_array_free(bytes);
plist_free_data(data);
PLIST_OSTEP_ERR("Missing terminating '>' at offset %ld\n", (long int)(ctx->pos - ctx->start));
ctx->err = PLIST_ERR_PARSE;
goto err_out;
}
ctx->pos++;
data->buff = (uint8_t*)bytes->data;
data->length = bytes->len;
bytes->data = NULL;
byte_array_free(bytes);
*plist = plist_new_node(data);
parse_skip_ws(ctx);
break;
} else if (*ctx->pos == '"' || *ctx->pos == '\'') {
char c = *ctx->pos;
ctx->pos++;
p = ctx->pos;
size_t num_escapes = 0;
while (ctx->pos < ctx->end) {
if (*ctx->pos == '\\') {
num_escapes++;
}
if ((*ctx->pos == c) && (*(ctx->pos-1) != '\\')) {
break;
}
ctx->pos++;
}
if (ctx->pos >= ctx->end) {
plist_free_data(data);
PLIST_OSTEP_ERR("EOF while parsing quoted string at offset %ld\n", (long int)(ctx->pos - ctx->start));
ctx->err = PLIST_ERR_PARSE;
goto err_out;
}
if (*ctx->pos != c) {
plist_free_data(data);
PLIST_OSTEP_ERR("Missing closing quote (%c) at offset %ld\n", c, (long int)(ctx->pos - ctx->start));
ctx->err = PLIST_ERR_PARSE;
goto err_out;
}
size_t slen = ctx->pos - p;
ctx->pos++; char* strbuf = (char*)malloc(slen+1);
if (num_escapes > 0) {
size_t i = 0;
size_t o = 0;
while (i < slen) {
if (p[i] == '\\') {
i++;
switch (p[i]) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7': {
unsigned char chr = 0;
int maxd = 3;
while ((i < slen) && (p[i] >= '0' && p[i] <= '7') && --maxd) {
chr = (chr << 3) + p[i] - '0';
i++;
}
strbuf[o++] = (char)chr;
} break;
case 'U': {
i++;
uint16_t wchr = 0;
int maxd = 4;
while ((i < slen) && isxdigit(p[i]) && maxd--) {
wchr = (wchr << 4) + ((p[i] <= '9') ? (p[i] - '0') : ((p[i] <= 'F') ? (p[i] - 'A' + 10) : (p[i] - 'a' + 10)));
i++;
}
if (wchr >= 0x800) {
strbuf[o++] = (char)(0xE0 + ((wchr >> 12) & 0xF));
strbuf[o++] = (char)(0x80 + ((wchr >> 6) & 0x3F));
strbuf[o++] = (char)(0x80 + (wchr & 0x3F));
} else if (wchr >= 0x80) {
strbuf[o++] = (char)(0xC0 + ((wchr >> 6) & 0x1F));
strbuf[o++] = (char)(0x80 + (wchr & 0x3F));
} else {
strbuf[o++] = (char)(wchr & 0x7F);
}
} break;
case 'a': strbuf[o++] = '\a'; i++; break;
case 'b': strbuf[o++] = '\b'; i++; break;
case 'f': strbuf[o++] = '\f'; i++; break;
case 'n': strbuf[o++] = '\n'; i++; break;
case 'r': strbuf[o++] = '\r'; i++; break;
case 't': strbuf[o++] = '\t'; i++; break;
case 'v': strbuf[o++] = '\v'; i++; break;
case '"': strbuf[o++] = '"'; i++; break;
case '\'': strbuf[o++] = '\''; i++; break;
default:
break;
}
} else {
strbuf[o++] = p[i++];
}
}
strbuf[o] = '\0';
slen = o;
} else {
strncpy(strbuf, p, slen);
strbuf[slen] = '\0';
}
data->type = PLIST_STRING;
data->strval = strbuf;
data->length = slen;
*plist = plist_new_node(data);
parse_skip_ws(ctx);
break;
} else {
size_t slen = 0;
parse_skip_ws(ctx);
p = ctx->pos;
while (ctx->pos < ctx->end) {
if (!allowed_unquoted_chars[(uint8_t)*ctx->pos]) {
break;
}
ctx->pos++;
}
slen = ctx->pos-p;
if (slen > 0) {
data->type = PLIST_STRING;
data->strval = strndup(p, slen);
data->length = slen;
*plist = plist_new_node(data);
parse_skip_ws(ctx);
break;
} else {
plist_free_data(data);
PLIST_OSTEP_ERR("Unexpected character when parsing unquoted string at offset %ld\n", (long int)(ctx->pos - ctx->start));
ctx->err = PLIST_ERR_PARSE;
break;
}
}
ctx->pos++;
}
ctx->depth--;
err_out:
if (ctx->err != PLIST_ERR_SUCCESS) {
plist_free(subnode);
plist_free(*plist);
*plist = NULL;
return ctx->err;
}
return PLIST_ERR_SUCCESS;
}
plist_err_t plist_from_openstep(const char *plist_ostep, uint32_t length, plist_t * plist)
{
if (!plist) {
return PLIST_ERR_INVALID_ARG;
}
*plist = NULL;
if (!plist_ostep || (length == 0)) {
return PLIST_ERR_INVALID_ARG;
}
struct _parse_ctx ctx = { plist_ostep, plist_ostep, plist_ostep + length, 0 , 0 };
plist_err_t err = node_from_openstep(&ctx, plist);
if (err == 0) {
if (!*plist) {
*plist = plist_new_dict();
} else if (ctx.pos < ctx.end && *ctx.pos == '=') {
plist_free(*plist);
*plist = NULL;
plist_t pl = plist_new_dict();
ctx.pos = plist_ostep;
parse_dict_data(&ctx, pl);
if (ctx.err > 0) {
plist_free(pl);
PLIST_OSTEP_ERR("Failed to parse strings data\n");
err = PLIST_ERR_PARSE;
} else {
*plist = pl;
}
}
}
return err;
}