diff options
author | Jörg Frings-Fürst <debian@jff-webhosting.net> | 2014-09-01 13:56:46 +0200 |
---|---|---|
committer | Jörg Frings-Fürst <debian@jff-webhosting.net> | 2014-09-01 13:56:46 +0200 |
commit | 22f703cab05b7cd368f4de9e03991b7664dc5022 (patch) | |
tree | 6f4d50beaa42328e24b1c6b56b6ec059e4ef21a5 /jcnf/jcnf.c |
Initial import of argyll version 1.5.1-8debian/1.5.1-8
Diffstat (limited to 'jcnf/jcnf.c')
-rw-r--r-- | jcnf/jcnf.c | 1248 |
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; +} + + |