summaryrefslogtreecommitdiff
path: root/jcnf/jcnf.c
diff options
context:
space:
mode:
Diffstat (limited to 'jcnf/jcnf.c')
-rw-r--r--jcnf/jcnf.c1248
1 files changed, 1248 insertions, 0 deletions
diff --git a/jcnf/jcnf.c b/jcnf/jcnf.c
new file mode 100644
index 0000000..c2fd4cc
--- /dev/null
+++ b/jcnf/jcnf.c
@@ -0,0 +1,1248 @@
+
+/*
+ * JSON based configuration format class.
+ */
+
+/*************************************************************************
+ Copyright 2008 Graeme W. Gill
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+
+ *************************************************************************/
+
+#define LOCK_RETRIES 5
+
+/* We convert JSON arrays to numerical sub paths on reading, */
+/* but they get converted back to maps on writing. */
+/* Paths are grouped under common paths on writing, */
+/* but aren't sorted. */
+
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#ifndef NT
+#include <sys/file.h>
+#endif
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <unistd.h>
+#include <time.h>
+
+#include "yajl/yajl_common.h"
+#include "yajl/yajl_gen.h"
+#include "yajl/yajl_parse.h"
+
+#include "jcnf.h"
+
+#define BUF_SIZE 2048
+
+/* - - - - - - - - - - - - - - - - */
+/* Utilities */
+
+/* Construct a string that represents the current key path */
+/* Return NULL on error (malloc fail) */
+/* Free the returned string when done */
+static char *cur_keypath(jcnf *p) {
+ char *ckey = NULL;
+ int i, len;
+
+ if (p->nrecd <= 0) {
+ return strdup("");
+ }
+ for (len = i = 0; i < p->nrecd; i++) {
+ if (p->recds[i].aix > -2) {
+ len += 3 + (int)log10((double)p->recds[p->nrecd-1].aix);
+ } else {
+ len += strlen(p->recds[i].key) + 1;
+ }
+ }
+
+ if ((ckey = malloc(len)) == NULL)
+ return NULL;
+
+ for (len = i = 0; i < p->nrecd; i++) {
+ int sl;
+
+ if (p->recds[i].aix > -2) {
+ char num[13];
+ sprintf(num, "%d",p->recds[i].aix);
+ sl = strlen(num);
+ memmove(ckey + len, num, sl);
+ } else {
+ sl = strlen(p->recds[i].key);
+ memmove(ckey + len, p->recds[i].key, sl);
+ }
+ len += sl;
+
+ if ((i+1) >= p->nrecd)
+ ckey[len] = '\000';
+ else
+ ckey[len] = '/';
+ len++;
+ }
+ return ckey;
+}
+
+/* Diagnostic - Print the value of a key */
+static jc_error jcnf_print_key(jcnf *p, int ix, FILE *fp) {
+ jc_key *kp;
+
+ if (ix < 0 || ix >= p->nkeys)
+ return jc_ix_oorange;
+
+ kp = p->keys[ix];
+
+ fprintf(fp,"Key '%s' has value",kp->key);
+
+ switch(kp->type) {
+ case jc_null: {
+ fprintf(fp," null");
+ break;
+ }
+ case jc_boolean: {
+ fprintf(fp," %s",*((int *)kp->data) ? "true" : "false");
+ break;
+ }
+ case jc_real: {
+ fprintf(fp," %lf",*((double *)kp->data));
+ break;
+ }
+ case jc_integer: {
+ fprintf(fp," %ld",*((long *)kp->data));
+ break;
+ }
+ case jc_string: {
+ fprintf(fp," '%s'",kp->data);
+ break;
+ }
+ default: {
+ fprintf(fp," unknown type %d",kp->type);
+ break;
+ }
+ }
+ if (kp->c_comment != NULL) {
+ fprintf(fp," C comment = '%s'",kp->c_comment);
+ }
+ if (kp->cpp_comment != NULL) {
+ fprintf(fp," C++ comment = '%s'",kp->cpp_comment);
+ }
+ fprintf(fp,"\n");
+ return jc_ok;
+}
+
+/* - - - - - - - - - - - - - - - - */
+/* Free keys contents */
+static void free_key_contents(jc_key *p) {
+ if (p->key != NULL) {
+ free(p->key);
+ p->key = NULL;
+ }
+ if (p->c_comment != NULL) {
+ free(p->c_comment);
+ p->c_comment = NULL;
+ }
+ if (p->cpp_comment != NULL) {
+ free(p->cpp_comment);
+ p->cpp_comment = NULL;
+ }
+ if (p->data != NULL) {
+ free(p->data);
+ p->data = NULL;
+ }
+ p->type = -1;
+}
+
+/* Free key and its contents */
+static void free_key(jc_key *p) {
+ free_key_contents(p);
+ free(p);
+}
+
+/* Set a keyvalues (internal) */
+/* All parameters are copied */
+/* return NZ on error */
+static jc_error jcnf_set_key_internal(
+ jcnf *p,
+ jc_key *kp, /* Pointer to key */
+ char *key, /* Key path name */
+ jc_type type,
+ void *data,
+ size_t dataSize, /* Data size (including string nul) */
+ char *comment /* C++ style comment */
+) {
+ free_key_contents(kp);
+
+ if ((kp->key = strdup(key)) == NULL)
+ return jc_malloc;
+ kp->type = type;
+
+ if (type != jc_null) {
+ if (type == jc_string && ((char *)data)[dataSize-1] != '\000')
+ return jc_string_not_terminated;
+ if ((kp->data = malloc(dataSize)) == NULL)
+ return jc_malloc;
+ kp->dataSize = dataSize;
+ memmove(kp->data, data, dataSize);
+ }
+
+ if (comment != NULL) {
+ if ((kp->cpp_comment = strdup(comment)) == NULL)
+ return jc_malloc;
+ }
+ return jc_ok;
+}
+
+/* Add a key value to the jcnf (internal) */
+/* All parameters are copied */
+/* return NZ on error */
+static jc_error jcnf_add_key_internal(
+ jcnf *p,
+ char *key, /* Key path name */
+ jc_type type,
+ void *data,
+ size_t dataSize, /* Data size (including string nul) */
+ char *comment
+) {
+ jc_error ev;
+ jc_key *kp;
+
+ if (key == NULL || (type != jc_null && (data == NULL || dataSize == 0)))
+ return jc_bad_addkey_params;
+
+ if (p->nkeys >= p->akeys) { /* Need more pointer space */
+ p->akeys = p->akeys ? 2 * p->akeys : 10;
+ if ((p->keys = realloc(p->keys, p->akeys * sizeof(jc_key*))) == NULL) {
+ return jc_malloc;
+ }
+ }
+ if ((kp = p->keys[p->nkeys] = calloc(1, sizeof(jc_key))) == NULL) {
+ return jc_malloc;
+ }
+ p->nkeys++;
+
+ if ((ev = jcnf_set_key_internal(p, kp, key, type, data, dataSize, comment)) != jc_ok)
+ return ev;
+ p->lk = kp;
+ return jc_ok;
+}
+
+/* Locate the index of the next key matching the key name, starting */
+/* at the given index. Update the index to the matching key. */
+/* Look for an exact match if exact != 0, or leading match if exact = 0 */
+/* Search backwards if bwd != 0 or forwards if bwd = 0 */
+/* Set *ix = -1 to begin search from the start/end for fwd/bwd. */
+/* Return jc_ix_oorange if no more match. */
+static jc_error jcnf_locate_key(
+ jcnf *p,
+ int *pix,
+ char *key,
+ int exact,
+ int bwd
+) {
+ int ix = *pix;
+ int sl;
+
+ if (ix == -1) {
+ if (bwd)
+ ix = p->nkeys-1;
+ else
+ ix = 0;
+ }
+
+ if (ix < 0 || ix >= p->nkeys)
+ return jc_ix_oorange;
+
+ if (key == NULL)
+ return jc_no_keyname;
+
+ sl = strlen(key);
+
+ /* We're doing a simple linear search */
+ for (; ix >= 0 && ix < p->nkeys; ix += bwd ? -1 : 1) {
+ if (exact) {
+ if (strcmp(key, p->keys[ix]->key) == 0)
+ break;
+ } else {
+ if (strncmp(key, p->keys[ix]->key, sl) == 0)
+ break;
+ }
+ }
+
+ if (ix < 0 || ix >= p->nkeys)
+ return jc_ix_oorange;
+
+ *pix = ix;
+ return jc_ok;
+}
+
+/* Retrieve a keys information. Return pointers may be NULL. */
+/* If ix >= 0, return the key of the given index. */
+/* If ix == -1, return the first from the beginning exactly matching key name. */
+/* jc_ix_oorange is returned when past end. */
+/* (Do not modify anything returned) */
+static jc_error jcnf_get_key(
+ jcnf *p,
+ int ix,
+ char **key,
+ jc_type *type,
+ unsigned char **data,
+ size_t *dataSize, /* Data size (including string nul) */
+ char **comment
+) {
+ jc_error ev;
+
+ if (ix == -1) {
+ if (key == NULL || *key == NULL)
+ return jc_no_keyname;
+ if ((ev = jcnf_locate_key(p, &ix, *key, 1, 0)) != jc_ok)
+ return ev;
+ } else if (ix < 0 || ix >= p->nkeys)
+ return jc_ix_oorange;
+
+ if (key != NULL)
+ *key = p->keys[ix]->key;
+ if (type != NULL)
+ *type = p->keys[ix]->type;
+ if (data != NULL)
+ *data = p->keys[ix]->data;
+ if (dataSize != NULL)
+ *dataSize = p->keys[ix]->dataSize;
+ if (comment != NULL)
+ *comment = p->keys[ix]->cpp_comment;
+
+ return jc_ok;
+}
+
+/* Set a keys information. */
+/* If ix >= 0, set the key of the given index. */
+/* jc_ix_oorange is returned when past end. */
+/* If ix == -1, overwrite an existing key with the same name, */
+/* or add a new key with that name at the end if there is no existing key. */
+static jc_error jcnf_set_key(
+ jcnf *p,
+ int ix,
+ char *key,
+ jc_type type,
+ unsigned char *data,
+ size_t dataSize, /* Data size (including string nul) */
+ char *comment
+) {
+ jc_error ev;
+
+ if (ix == -1) {
+ if (key == NULL)
+ return jc_no_keyname;
+ if ((ev = jcnf_locate_key(p, &ix, key, 1, 0)) != jc_ok) {
+ if (ev != jc_ix_oorange)
+ return ev;
+ if (p->modify == 0)
+ return jc_update_nomod;
+ p->modified = 1;
+ return jcnf_add_key_internal(p, key, type, data, dataSize, comment);
+ }
+ } else if (ix < 0 || ix >= p->nkeys)
+ return jc_ix_oorange;
+
+ if (p->modify == 0)
+ return jc_update_nomod;
+ p->modified = 1;
+ return jcnf_set_key_internal(p, p->keys[ix], key, type, data, dataSize, comment);
+}
+
+/* Add a key value to the jcnf at the end, irrespective of whether there is */
+/* an existing key with that name. */
+static jc_error jcnf_add_key(
+ jcnf *p,
+ char *key,
+ jc_type type,
+ unsigned char *data,
+ size_t dataSize, /* Data size (including string nul) */
+ char *comment
+) {
+ jc_error ev;
+ if (p->modify == 0)
+ return jc_update_nomod;
+ if ((ev = jcnf_add_key_internal(p,key,type,data,dataSize,comment)) == jc_ok) {
+ p->modified = 1;
+ }
+ return ev;
+}
+
+/* Delete a key. */
+/* If ix >= 0, delete the key of the given index. */
+/* jc_ix_oorange is returned when past end. */
+/* If ix == -1, delete the key with the given name. */
+static jc_error jcnf_delete_key(
+ jcnf *p,
+ int ix,
+ char *key
+) {
+ jc_error ev;
+
+ if (ix == -1) {
+ if (key == NULL)
+ return jc_no_keyname;
+ if ((ev = jcnf_locate_key(p, &ix, key, 1, 0)) != jc_ok)
+ return ev;
+ } else if (ix < 0 || ix >= p->nkeys)
+ return jc_ix_oorange;
+
+ if (p->modify == 0)
+ return jc_update_nomod;
+ free_key_contents(p->keys[ix]);
+
+ if ((ix+1) < p->nkeys) {
+ memmove(p->keys+ix, p->keys+ix+1,sizeof(jc_key *) * p->nkeys-ix-1);
+ }
+ p->nkeys--;
+ p->modified = 1;
+
+ return jc_ok;
+}
+
+/* - - - - - - - - - - - - - - - - */
+/* yajl parser callbacks */
+
+static int jcnf_yajl_null(void *ctx) {
+ jcnf *p = (jcnf *)ctx;
+ char *t1;
+
+// printf("null\n");
+
+ /* If current level is an array, bump the index */
+ if (p->nrecd > 0 && p->recds[p->nrecd-1].aix > -2)
+ p->recds[p->nrecd-1].aix++;
+ if ((t1 = cur_keypath(p)) == NULL)
+ return 0;
+
+ if (jcnf_add_key_internal(p, t1, jc_null, NULL, 0, NULL) != jc_ok) {
+ free(t1);
+ return 0;
+ }
+ free(t1);
+
+ return 1;
+}
+
+static int jcnf_yajl_boolean(void * ctx, int boolVal) {
+ jcnf *p = (jcnf *)ctx;
+ char *t1;
+
+// printf("bool: %s\n", boolVal ? "true" : "false");
+
+ /* If current level is an array, bump the index */
+ if (p->nrecd > 0 && p->recds[p->nrecd-1].aix > -2)
+ p->recds[p->nrecd-1].aix++;
+ if ((t1 = cur_keypath(p)) == NULL)
+ return 0;
+
+ if (jcnf_add_key_internal(p, t1, jc_boolean, (void *)&boolVal, sizeof(int), NULL) != jc_ok) {
+ free(t1);
+ return 0;
+ }
+ free(t1);
+
+ return 1;
+}
+
+static int jcnf_yajl_integer(void *ctx, long integerVal) {
+ jcnf *p = (jcnf *)ctx;
+ char *t1;
+
+// printf("integer: %lld\n", integerVal);
+
+ /* If current level is an array, bump the index */
+ if (p->nrecd > 0 && p->recds[p->nrecd-1].aix > -2)
+ p->recds[p->nrecd-1].aix++;
+ if ((t1 = cur_keypath(p)) == NULL)
+ return 0;
+
+ if (jcnf_add_key_internal(p, t1, jc_integer, (void *)&integerVal, sizeof(long), NULL) != jc_ok) {
+ free(t1);
+ return 0;
+ }
+ free(t1);
+
+ return 1;
+}
+
+static int jcnf_yajl_double(void *ctx, double doubleVal) {
+ jcnf *p = (jcnf *)ctx;
+ char *t1;
+
+// printf("double: %lf\n", doubleVal);
+
+ /* If current level is an array, bump the index */
+ if (p->nrecd > 0 && p->recds[p->nrecd-1].aix > -2)
+ p->recds[p->nrecd-1].aix++;
+ if ((t1 = cur_keypath(p)) == NULL)
+ return 0;
+
+ if (jcnf_add_key_internal(p, t1, jc_real, (void *)&doubleVal, sizeof(double), NULL) != jc_ok) {
+ free(t1);
+ return 0;
+ }
+ free(t1);
+
+ return 1;
+}
+
+static int jcnf_yajl_string(void *ctx, const unsigned char *stringVal,
+ unsigned int stringLen) {
+ jcnf *p = (jcnf *)ctx;
+ char *t1, *t2;
+
+// printf("string: '");
+// fwrite(stringVal, 1, stringLen, stdout);
+// printf("'\n");
+
+ /* If current level is an array, bump the index */
+ if (p->nrecd > 0 && p->recds[p->nrecd-1].aix > -2)
+ p->recds[p->nrecd-1].aix++;
+ if ((t1 = cur_keypath(p)) == NULL)
+ return 0;
+
+ if ((t2 = malloc(stringLen + 1)) == NULL) /* Room to add implicit nul */
+ return 0;
+ memmove(t2, stringVal, stringLen);
+ t2[stringLen] = 0;
+
+ if (jcnf_add_key_internal(p, t1, jc_string, (void *)t2, stringLen+1, NULL) != jc_ok) {
+ free(t2);
+ free(t1);
+ return 0;
+ }
+ free(t2);
+ free(t1);
+
+ return 1;
+}
+
+static int jcnf_yajl_c_comment(void *ctx, const unsigned char * stringVal,
+ unsigned int stringLen) {
+ jcnf *p = (jcnf *)ctx;
+
+// printf("c_comment: '");
+// fwrite(stringVal, 1, stringLen, stdout);
+// printf("'\n");
+
+ if (p->lk != NULL && p->lk->c_comment == NULL) {
+ char *t1;
+ if ((t1 = malloc(stringLen + 1)) == NULL)
+ return 0;
+ memmove(t1, stringVal, stringLen);
+ t1[stringLen] = 0;
+
+ p->lk->c_comment = t1;
+ }
+
+ return 1;
+}
+
+static int jcnf_yajl_cpp_comment(void *ctx, const unsigned char * stringVal,
+ unsigned int stringLen) {
+ jcnf *p = (jcnf *)ctx;
+
+// printf("cpp_comment: '");
+// fwrite(stringVal, 1, stringLen, stdout);
+// printf("'\n");
+
+ if (p->lk != NULL && p->lk->cpp_comment == NULL) {
+ char *t1;
+ if ((t1 = malloc(stringLen + 1)) == NULL)
+ return 0;
+ memmove(t1, stringVal, stringLen);
+ t1[stringLen] = 0;
+
+ p->lk->cpp_comment = t1;
+ }
+
+ return 1;
+}
+
+static int jcnf_yajl_start_map(void *ctx) {
+ jcnf *p = (jcnf *)ctx;
+
+ /* Start another recursion level */
+ if (p->nrecd >= p->arecd) {
+ p->arecd *= 2;
+ if ((p->recds = (jc_recd *)realloc(p->recds, p->arecd * sizeof(jc_recd))) == NULL) {
+ /* realloc failed */
+ return 0;
+ }
+ }
+
+ /* If current level is an array, bump the index */
+ if (p->nrecd > 0 && p->recds[p->nrecd-1].aix > -2)
+ p->recds[p->nrecd-1].aix++;
+
+ /* Move to new level */
+ p->recds[p->nrecd].key = NULL;
+ p->recds[p->nrecd].aix = -2;
+ p->nrecd++;
+
+#ifdef NEVER
+ printf("map open '{'\n");
+#endif
+
+ return 1;
+}
+
+static int jcnf_yajl_map_key(void *ctx, const unsigned char * stringVal,
+ unsigned int stringLen) {
+ jcnf *p = (jcnf *)ctx;
+ int i;
+
+ if (stringLen == 0) {
+ /* Zero length key */
+ return 0;
+ }
+
+ if (p->recds[p->nrecd-1].key != NULL) {
+ free(p->recds[p->nrecd-1].key);
+ p->recds[p->nrecd-1].key = NULL;
+ p->recds[p->nrecd-1].aix = -2;
+ }
+ if ((p->recds[p->nrecd-1].key = malloc(stringLen + 1)) == NULL)
+ return 0;
+
+ p->recds[p->nrecd-1].key[stringLen] = 0;
+ memmove(p->recds[p->nrecd-1].key, stringVal, stringLen);
+
+#ifdef NEVER
+ {
+ char *tt;
+
+ printf("Added path = '%s'\n", p->recds[p->nrecd-1].key);
+ if ((tt = cur_keypath(p)) != NULL) {
+ printf("Current path = '%s'\n",tt);
+ free(tt);
+ }
+ }
+#endif
+
+ return 1;
+}
+
+static int jcnf_yajl_end_map(void *ctx) {
+ jcnf *p = (jcnf *)ctx;
+
+ if (p->nrecd == 0) {
+ /* End of map without start of map */
+ return 0;
+ }
+ p->nrecd--;
+
+#ifdef NEVER
+ {
+ char *tt;
+ printf("map close '}'\n");
+ if ((tt = cur_keypath(p)) != NULL)
+ printf("Current path = '%s'\n",tt);
+ free(tt);
+ }
+#endif
+
+ return 1;
+}
+
+static int jcnf_yajl_start_array(void *ctx) {
+ jcnf *p = (jcnf *)ctx;
+
+#ifdef NEVER
+ printf("array open '['\n");
+#endif
+
+ /* Start another recursion level */
+ if (p->nrecd >= p->arecd) {
+ p->arecd *= 2;
+ if ((p->recds = (jc_recd *)realloc(p->recds, p->arecd * sizeof(jc_recd))) == NULL) {
+ /* realloc failed */
+ return 0;
+ }
+ }
+
+ /* If current level is an array, bump the index */
+ if (p->nrecd > 0 && p->recds[p->nrecd-1].aix > -2)
+ p->recds[p->nrecd-1].aix++;
+
+ /* Move to new level */
+ p->recds[p->nrecd].key = NULL;
+ p->recds[p->nrecd].aix = -1;
+ p->nrecd++;
+
+ return 1;
+}
+
+static int jcnf_yajl_end_array(void *ctx) {
+ jcnf *p = (jcnf *)ctx;
+
+#ifdef NEVER
+ printf("array close ']'\n");
+#endif
+ if (p->nrecd == 0) {
+ /* End of map without start of map */
+ return 0;
+ }
+ p->nrecd--;
+
+ return 1;
+}
+
+static yajl_callbacks callbacks = {
+ jcnf_yajl_null,
+ jcnf_yajl_boolean,
+ jcnf_yajl_integer,
+ jcnf_yajl_double,
+ NULL, /* number */
+ jcnf_yajl_string,
+ jcnf_yajl_c_comment,
+ jcnf_yajl_cpp_comment,
+ jcnf_yajl_start_map,
+ jcnf_yajl_map_key,
+ jcnf_yajl_end_map,
+ jcnf_yajl_start_array,
+ jcnf_yajl_end_array
+};
+
+/* - - - - - - - - - - - - - - - - */
+/* Lock the open fp */
+static jc_error jcnf_lock_file(jcnf *p) {
+#ifndef NT
+ int i, fh;
+ int lop;
+
+ fh = fileno(p->fp);
+
+ if (p->modify) {
+ lop = LOCK_EX | LOCK_NB;
+ } else {
+ lop = LOCK_SH | LOCK_NB;
+ }
+
+ for (i = 0; i < LOCK_RETRIES; i++) {
+ if (flock(fh, lop) == 0)
+ break;
+ sleep(1);
+ }
+ if (i >= LOCK_RETRIES)
+ return jc_locked;
+#endif
+ p->locked = 1;
+
+ return jc_ok;
+}
+
+/* Read a file into the object. */
+/* (Doesn't do locking) */
+/* Return NZ on error */
+static jc_error jcnf_read(
+ jcnf *p
+) {
+ jc_error ev;
+ yajl_handle hand;
+ yajl_parser_config cfg = { 0, 1 }; /* Validate UTF8 strings ? */
+ unsigned char buf[BUF_SIZE];
+ struct stat sbuf;
+
+ cfg.allowComments = 1;
+
+ if ((p->fp = fopen(p->fname, p->modify ? "r+" : "r")) == NULL) {
+ if (!p->modify)
+ return jc_noexisting;
+
+ if ((p->fp = fopen(p->fname, "w")) == NULL)
+ return jc_write_open;
+ if ((ev = jcnf_lock_file(p)) != jc_ok)
+ return ev;
+ return jc_ok;
+ }
+
+ if ((ev = jcnf_lock_file(p)) != jc_ok)
+ return ev;
+
+ hand = yajl_alloc(&callbacks, &cfg, NULL, (void *)p);
+
+ /* Parse the file */
+ for(;;) {
+ size_t rd;
+
+ rd = fread(buf, 1, BUF_SIZE, p->fp);
+
+ if (rd == 0) {
+ if (!feof(p->fp)) {
+ fprintf(stderr, "error reading from '%s'\n", p->fname);
+ return jc_read_fail;
+ }
+ break;
+ } else {
+ yajl_status stat;
+
+ /* read file data, pass to parser */
+ stat = yajl_parse(hand, buf, rd);
+ if (stat != yajl_status_insufficient_data &&
+ stat != yajl_status_ok)
+ {
+ unsigned char * str = yajl_get_error(hand, 1, buf, rd);
+ fflush(stdout);
+ fprintf(stderr, "%s", (char *) str);
+ yajl_free_error(hand, str);
+ return jc_parse_fail;
+ }
+ }
+ }
+
+ /* Record some things about the file so that we can */
+ /* recognized if it's been modified */
+ if (stat(p->fname, &sbuf) != 0) {
+ return jc_stat;
+ }
+ p->rsize = sbuf.st_size;
+ p->rtime = time(NULL);
+
+ yajl_free(hand);
+
+ /* We're not modifying it, so close it */
+ if (!p->modify) {
+ fclose(p->fp);
+ p->fp = NULL;
+ p->locked = 0;
+ }
+
+ return jc_ok;
+}
+
+/* Write an object into a file */
+/* Unlock it & close it afterwards. */
+/* Return NZ on error */
+static jc_error jcnf_write(
+ jcnf *p
+) {
+ FILE *fp; /* For temporary file */
+ char *tname = NULL;
+ yajl_gen_config conf = { 1, " " };
+ yajl_gen g;
+ yajl_status stat;
+ const unsigned char * buf;
+ unsigned int len;
+ int clevel = 0; /* Current level */
+ char *pkey = ""; /* Previous key */
+ char *ckey; /* Current key */
+ int i;
+
+ if (!p->locked && p->fp != NULL) {
+ return jc_update_nomod;
+ }
+
+#ifndef NT
+ {
+ int fh;
+
+ if ((tname = malloc(strlen(p->fname) + 8)) == NULL)
+ return jc_malloc;
+
+ /* Create temporary file, open it and lock it LOCK_EX */
+ strcpy(tname, p->fname);
+ strcat(tname,"-XXXXXX");
+ if ((fh = mkstemp(tname)) == -1) {
+ free(tname);
+ return jc_write_open;
+ }
+ if (fchmod(fh, 0644) != 0) {
+ free(tname);
+ return jc_write_open;
+ }
+ if ((fp = fdopen(fh, "w")) == NULL) {
+ free(tname);
+ return jc_write_open;
+ }
+ }
+#else
+ /* Open a temporary file in the same directory to write to */
+ if ((tname = malloc(strlen(p->fname) + 8)) == NULL)
+ return jc_malloc;
+
+ if (tmpnam(tname) == NULL) {
+ free(tname);
+ return jc_write_open;
+ }
+ if ((fp = fopen(tname, "w")) == NULL) {
+ free(tname);
+ return jc_write_open;
+ }
+#endif
+
+ g = yajl_gen_alloc(&conf, NULL);
+
+ /* Generate the file */
+ for (i = 0; i < p->nkeys; i++, pkey = ckey) {
+ char *pc, *cc, *dc;
+ int nplev; /* Number of previous different levels */
+ int ndlev; /* Number of new different levels */
+ int same;
+
+ ckey = p->keys[i]->key;
+
+ /* See how many keys are not in common */
+ nplev = ndlev = 0;
+ same = 1;
+ for(pc = pkey, dc = cc = ckey; *pc != '\000' || *cc != '\000';) {
+
+//printf("~1 pc = '%c', cc = '%c', same = %d\n",*pc,*cc,same);
+ if (same == 0 || *pc != *cc) {
+ same = 0;
+ if (*cc == '/') {
+ ndlev++;
+//printf("~1 ndlev now %d\n",ndlev);
+ }
+ if (*pc == '/') {
+ nplev++;
+//printf("~1 ndlev now %d\n",ndlev);
+ }
+
+ } else {
+ if (*cc == '/') {
+ dc = cc+1;
+ }
+ }
+ if (*pc != '\000') {
+ pc++;
+ if (same == 0 && *pc == '\000') {
+ nplev++;
+//printf("~1 nplev now %d\n",nplev);
+ }
+ }
+ if (*cc != '\000') {
+ cc++;
+ if (same == 0 && *cc == '\000') {
+ ndlev++;
+//printf("~1 ndlev now %d\n",ndlev);
+ }
+ }
+ }
+//printf("~1 Prev = '%s'\n",pkey);
+//printf("~1 Curr = '%s'\n",ckey);
+//printf("~1 New = '%s'\n",dc);
+//printf("~1 Old different = %d, new different = %d\n\n",nplev,ndlev);
+
+ while(nplev > 0) {
+ if (nplev > 1)
+ yajl_gen_map_close(g);
+ nplev--;
+ clevel--;
+ }
+ while(ndlev > 0) {
+ yajl_gen_map_open(g);
+ for (cc = dc; *cc != '\000' && *cc != '/'; cc++)
+ ;
+ yajl_gen_string(g, dc, cc-dc);
+ if (*cc != '\000')
+ dc = cc + 1;
+ ndlev--;
+ clevel++;
+ }
+
+ switch(p->keys[i]->type) {
+//printf("~1 key %d = type %d\n",i,p->keys[i]->type);
+ case jc_null:
+ yajl_gen_null(g);
+ break;
+
+ case jc_boolean:
+ yajl_gen_bool(g, *((int *)p->keys[i]->data));
+ break;
+
+ case jc_real:
+ yajl_gen_double(g, *((double *)p->keys[i]->data));
+ break;
+
+ case jc_integer:
+ yajl_gen_integer(g, *((long *)p->keys[i]->data));
+ break;
+
+ case jc_string:
+ yajl_gen_string(g, (char *)p->keys[i]->data, p->keys[i]->dataSize-1);
+ break;
+
+ default: {
+ free(tname);
+ return jc_unknown_key_type;
+ }
+ }
+
+ if (p->keys[i]->cpp_comment != NULL) {
+ yajl_gen_cpp_comment(g, p->keys[i]->cpp_comment, strlen(p->keys[i]->cpp_comment));
+ }
+ if (p->keys[i]->c_comment != NULL) {
+ yajl_gen_c_comment(g, p->keys[i]->c_comment, strlen(p->keys[i]->c_comment), 1);
+ }
+
+#ifdef NEVER
+ yajl_gen_map_open(g);
+ yajl_gen_string(g, "test", strlen("test"));
+ yajl_gen_string(g, "test value", strlen("test value"));
+ yajl_gen_c_comment(g, " A comment ", strlen(" A comment "));
+ yajl_gen_map_close(g);
+#endif
+
+ /* Do some writing */
+ yajl_gen_get_buf(g, &buf, &len);
+ if (len >= BUF_SIZE) {
+ if (fwrite(buf, 1, len, fp) != len)
+ return jc_write_fail;
+ yajl_gen_clear(g);
+ }
+ }
+
+ while(clevel > 0) {
+ yajl_gen_map_close(g);
+ clevel--;
+ }
+
+ yajl_gen_get_buf(g, &buf, &len);
+ if (len > 0) {
+ if (fwrite(buf, 1, len, fp) != len)
+ return jc_write_fail;
+ yajl_gen_clear(g);
+ }
+ yajl_gen_free(g);
+ if (fflush(fp) != 0) {
+ free(tname);
+ return jc_write_close;
+ }
+
+#ifdef NT
+ /* MSWindows rename won't replace existing or open files. */
+ /* Lucky this is just for testing, as this leaves a window */
+ if (fp != NULL) {
+ fclose(fp);
+ fp = NULL;
+ }
+ if (p->fp != NULL) {
+ fclose(p->fp);
+ p->fp = NULL;
+ }
+ unlink(p->fname);
+#endif
+
+//printf("~1 about to rename '%s' to '%s'\n",tname,p->fname);
+ /* Now atomicaly rename the file to replace the file we read */
+ if (rename(tname, p->fname) != 0) {
+ free(tname);
+ return jc_write_close;
+ }
+
+//printf("~1 closing files\n");
+ /* Close our files and release the locks */
+ if (fp != NULL && fclose(fp) != 0) {
+ free(tname);
+ return jc_write_close;
+ }
+ if (p->fp != NULL && fclose(p->fp) != 0) {
+ free(tname);
+ return jc_write_close;
+ }
+ p->fp = NULL;
+ p->locked = 0;
+ p->modify = 0;
+
+ free(tname);
+ return jc_ok;
+}
+
+/* Switch from read only to update of the config. */
+static jc_error jcnf_enable_modify(jcnf *p) {
+ jc_error ev;
+ struct stat sbuf;
+
+ if (p->modify)
+ return jc_ok; /* Nothing to do */
+
+ /* We need to re-open the file and lock it for modification */
+ if ((p->fp = fopen(p->fname, "r+")) == NULL) {
+ return jc_changed;
+ }
+ p->modify = 1;
+
+ if ((ev = jcnf_lock_file(p)) != jc_ok) {
+ p->modify = 0;
+ fclose(p->fp);
+ p->fp = NULL;
+ return ev;
+ }
+
+ /* Check that it hasn't been modified since it was first read */
+ if (stat(p->fname, &sbuf) != 0) {
+ p->modify = 0;
+ fclose(p->fp);
+ p->fp = NULL;
+ return jc_stat;
+ }
+
+ if (sbuf.st_size != p->rsize
+ || sbuf.st_mtime > p->rtime) {
+ p->modify = 0;
+ fclose(p->fp);
+ p->fp = NULL;
+ return jc_changed;
+ }
+
+ return jc_ok;
+}
+
+
+/* Update the file that was opened for modification, and unlock it. */
+static jc_error jcnf_update(jcnf *p) {
+ jc_error ev;
+ struct stat sbuf;
+
+ if (p->modified) {
+ if (p->modify == 0 || p->locked == 0) {
+
+ return jc_update_nomod; /* File wasn't opened for modification */
+ }
+ if ((ev = jcnf_write(p)) != jc_ok) {
+ return ev;
+ }
+ p->modified = 0;
+ }
+ p->modify = 0;
+
+ return jc_ok;
+}
+
+/* free the object */
+static void jcnf_del(jcnf *p) {
+
+ if (p->fp)
+ fclose(p->fp);
+
+ if (p->keys != NULL) {
+ int i;
+ for (i = 0; i < p->nkeys; i++) {
+ free_key(p->keys[i]);
+ }
+ free(p->keys);
+ }
+
+ if (p->recds != NULL) {
+ int i;
+ for (i = 0; i < p->nrecd; i++) {
+ free(p->recds[i].key);
+ }
+ free(p->recds);
+ }
+
+ if (p->fname)
+ free(p->fname);
+
+ free(p);
+}
+
+/* Create a new jconf. */
+/* Return NULL on error */
+jcnf *new_jcnf(
+ jc_error *pev, /* return error code on error */
+ char *fname, /* Corresponding filename */
+ jc_mod modify, /* Flag, nz to open for modification */
+ jc_crte create /* Flag, nz to create if it doesn't exist (modify must be set) */
+) {
+ jcnf *p;
+ jc_error ev;
+
+ if ((p = (jcnf *) calloc(1, sizeof(jcnf))) == NULL) {
+ if (pev != NULL) *pev = jc_malloc;
+ return NULL;
+ }
+
+ p->arecd = 10;
+ if ((p->recds = (jc_recd *) calloc(p->arecd, sizeof(jc_recd))) == NULL) {
+ if (pev != NULL) *pev = jc_malloc;
+ p->del(p);
+ return NULL;
+ }
+
+ if ((p->fname = strdup(fname)) == NULL) {
+ if (pev != NULL) *pev = jc_malloc;
+ p->del(p);
+ return NULL;
+ }
+
+ p->modify = modify == jc_modify ? 1 : 0;
+ p->create = create == jc_create ? 1 : 0;
+
+ p->locate_key = jcnf_locate_key;
+ p->get_key = jcnf_get_key;
+ p->set_key = jcnf_set_key;
+ p->add_key = jcnf_add_key;
+ p->delete_key = jcnf_delete_key;
+ p->print_key = jcnf_print_key;
+
+ p->enable_modify = jcnf_enable_modify;
+ p->update = jcnf_update;
+ p->del = jcnf_del;
+
+ if ((ev = jcnf_read(p)) != jc_ok) {
+ if (ev != jc_noexisting) {
+ if (pev != NULL) *pev = ev;
+ p->del(p);
+ return NULL;
+ }
+ }
+
+ if (pev != NULL) *pev = jc_ok;
+
+ return p;
+}
+
+/* ------------------------------- */
+/* Return a pointer to the nth element of the key name. */
+/* Return null if it is out of range or malloc failed. */
+/* Free the returned value when done. */
+char *jc_get_nth_elem(char *path, int n) {
+ int i;
+ char *p1, *p2, *rv;
+
+ if (path == NULL)
+ return NULL;
+
+ p1 = path;
+ if (*p1 == '/')
+ p1++;
+
+ for (i = 0; *p1 != '\000'; p1 = p2 + 1, i++) {
+ if ((p2 = strchr(p1, '/')) == NULL)
+ p2 = p1 + strlen(p1);
+ if (i >= n) {
+ if ((rv = malloc(p2 - p1 + 1)) == NULL)
+ return NULL;
+ strncpy(rv, p1, p2 - p1);
+ rv[p2-p1] = '\000';
+ return rv;
+ }
+ if (*p2 == '\000')
+ break;
+ }
+ return NULL;
+}
+
+