| /* |
| * Copyright 2001-2004 Brandon Long |
| * All Rights Reserved. |
| * |
| * ClearSilver Templating System |
| * |
| * This code is made available under the terms of the ClearSilver License. |
| * http://www.clearsilver.net/license.hdf |
| * |
| */ |
| |
| #include "cs_config.h" |
| |
| #include <stdlib.h> |
| #include <unistd.h> |
| #include <string.h> |
| #include <stdio.h> |
| #include <ctype.h> |
| #include <errno.h> |
| #include <limits.h> |
| #include <stdarg.h> |
| #include <sys/stat.h> |
| #include "neo_misc.h" |
| #include "neo_err.h" |
| #include "neo_rand.h" |
| #include "neo_hdf.h" |
| #include "neo_str.h" |
| #include "neo_files.h" |
| #include "ulist.h" |
| |
| /* Ok, in order to use the hash, we have to support n-len strings |
| * instead of null terminated strings (since in set_value and walk_hdf |
| * we are merely using part of the HDF name for lookup, and that might |
| * be a const, and we don't want the overhead of allocating/copying |
| * that data out...) |
| * |
| * Since HASH doesn't maintain any data placed in it, merely pointers to |
| * it, we use the HDF node itself as the key, and have specific |
| * comp/hash functions which just use the name/name_len as the key. |
| */ |
| |
| static int hash_hdf_comp(const void *a, const void *b) |
| { |
| HDF *ha = (HDF *)a; |
| HDF *hb = (HDF *)b; |
| |
| return (ha->name_len == hb->name_len) && !strncmp(ha->name, hb->name, ha->name_len); |
| } |
| |
| static UINT32 hash_hdf_hash(const void *a) |
| { |
| HDF *ha = (HDF *)a; |
| return ne_crc((UINT8 *)(ha->name), ha->name_len); |
| } |
| |
| static NEOERR *_alloc_hdf (HDF **hdf, const char *name, size_t nlen, |
| const char *value, int dup, int wf, HDF *top) |
| { |
| *hdf = calloc (1, sizeof (HDF)); |
| if (*hdf == NULL) |
| { |
| return nerr_raise (NERR_NOMEM, "Unable to allocate memory for hdf element"); |
| } |
| |
| (*hdf)->top = top; |
| |
| if (name != NULL) |
| { |
| (*hdf)->name_len = nlen; |
| (*hdf)->name = (char *) malloc (nlen + 1); |
| if ((*hdf)->name == NULL) |
| { |
| free((*hdf)); |
| (*hdf) = NULL; |
| return nerr_raise (NERR_NOMEM, |
| "Unable to allocate memory for hdf element: %s", name); |
| } |
| strncpy((*hdf)->name, name, nlen); |
| (*hdf)->name[nlen] = '\0'; |
| } |
| if (value != NULL) |
| { |
| if (dup) |
| { |
| (*hdf)->alloc_value = 1; |
| (*hdf)->value = strdup(value); |
| if ((*hdf)->value == NULL) |
| { |
| free((*hdf)->name); |
| free((*hdf)); |
| (*hdf) = NULL; |
| return nerr_raise (NERR_NOMEM, |
| "Unable to allocate memory for hdf element %s", name); |
| } |
| } |
| else |
| { |
| (*hdf)->alloc_value = wf; |
| /* We're overriding the const of value here for the set_buf case |
| * where we overrode the char * to const char * earlier, since |
| * alloc_value actually keeps track of the const-ness for us */ |
| (*hdf)->value = (char *)value; |
| } |
| } |
| return STATUS_OK; |
| } |
| |
| static void _dealloc_hdf_attr(HDF_ATTR **attr) |
| { |
| HDF_ATTR *next; |
| |
| while ((*attr) != NULL) |
| { |
| next = (*attr)->next; |
| if ((*attr)->key) free((*attr)->key); |
| if ((*attr)->value) free((*attr)->value); |
| free(*attr); |
| *attr = next; |
| } |
| *attr = NULL; |
| } |
| |
| static void _dealloc_hdf (HDF **hdf) |
| { |
| HDF *myhdf = *hdf; |
| HDF *next = NULL; |
| |
| if (myhdf == NULL) return; |
| if (myhdf->child != NULL) |
| _dealloc_hdf(&(myhdf->child)); |
| |
| /* This was easier recursively, but dangerous on long lists, so we |
| * walk it ourselves */ |
| next = myhdf->next; |
| while (next != NULL) |
| { |
| myhdf->next = next->next; |
| next->next = NULL; |
| _dealloc_hdf(&next); |
| next = myhdf->next; |
| } |
| if (myhdf->name != NULL) |
| { |
| free (myhdf->name); |
| myhdf->name = NULL; |
| } |
| if (myhdf->value != NULL) |
| { |
| if (myhdf->alloc_value) |
| free (myhdf->value); |
| myhdf->value = NULL; |
| } |
| if (myhdf->attr != NULL) |
| { |
| _dealloc_hdf_attr(&(myhdf->attr)); |
| } |
| if (myhdf->hash != NULL) |
| { |
| ne_hash_destroy(&myhdf->hash); |
| } |
| free(myhdf); |
| *hdf = NULL; |
| } |
| |
| NEOERR* hdf_init (HDF **hdf) |
| { |
| NEOERR *err; |
| HDF *my_hdf; |
| |
| *hdf = NULL; |
| |
| err = nerr_init(); |
| if (err != STATUS_OK) |
| return nerr_pass (err); |
| |
| err = _alloc_hdf (&my_hdf, NULL, 0, NULL, 0, 0, NULL); |
| if (err != STATUS_OK) |
| return nerr_pass (err); |
| |
| my_hdf->top = my_hdf; |
| |
| *hdf = my_hdf; |
| |
| return STATUS_OK; |
| } |
| |
| void hdf_destroy (HDF **hdf) |
| { |
| if (*hdf == NULL) return; |
| if ((*hdf)->top == (*hdf)) |
| { |
| _dealloc_hdf(hdf); |
| } |
| } |
| |
| static int _walk_hdf (HDF *hdf, const char *name, HDF **node) |
| { |
| HDF *parent = NULL; |
| HDF *hp = hdf; |
| HDF hash_key; |
| int x = 0; |
| const char *s, *n; |
| int r; |
| |
| *node = NULL; |
| |
| if (hdf == NULL) return -1; |
| if (name == NULL || name[0] == '\0') |
| { |
| *node = hdf; |
| return 0; |
| } |
| |
| if (hdf->link) |
| { |
| r = _walk_hdf (hdf->top, hdf->value, &hp); |
| if (r) return r; |
| if (hp) |
| { |
| parent = hp; |
| hp = hp->child; |
| } |
| } |
| else |
| { |
| parent = hdf; |
| hp = hdf->child; |
| } |
| if (hp == NULL) |
| { |
| return -1; |
| } |
| |
| n = name; |
| s = strchr (n, '.'); |
| x = (s == NULL) ? strlen(n) : s - n; |
| |
| while (1) |
| { |
| if (parent && parent->hash) |
| { |
| hash_key.name = (char *)n; |
| hash_key.name_len = x; |
| hp = ne_hash_lookup(parent->hash, &hash_key); |
| } |
| else |
| { |
| while (hp != NULL) |
| { |
| if (hp->name && (x == hp->name_len) && !strncmp(hp->name, n, x)) |
| { |
| break; |
| } |
| else |
| { |
| hp = hp->next; |
| } |
| } |
| } |
| if (hp == NULL) |
| { |
| return -1; |
| } |
| if (s == NULL) break; |
| |
| if (hp->link) |
| { |
| r = _walk_hdf (hp->top, hp->value, &hp); |
| if (r) { |
| return r; |
| } |
| parent = hp; |
| hp = hp->child; |
| } |
| else |
| { |
| parent = hp; |
| hp = hp->child; |
| } |
| n = s + 1; |
| s = strchr (n, '.'); |
| x = (s == NULL) ? strlen(n) : s - n; |
| } |
| if (hp->link) |
| { |
| return _walk_hdf (hp->top, hp->value, node); |
| } |
| |
| *node = hp; |
| return 0; |
| } |
| |
| int hdf_get_int_value (HDF *hdf, const char *name, int defval) |
| { |
| HDF *node; |
| int v; |
| char *n; |
| |
| if ((_walk_hdf(hdf, name, &node) == 0) && (node->value != NULL)) |
| { |
| v = strtol (node->value, &n, 10); |
| if (node->value == n) v = defval; |
| return v; |
| } |
| return defval; |
| } |
| |
| /* This should return a const char *, but changing this would have big |
| * repurcussions for any C code using this function, so no change for now */ |
| char* hdf_get_value (HDF *hdf, const char *name, const char *defval) |
| { |
| HDF *node; |
| |
| if ((_walk_hdf(hdf, name, &node) == 0) && (node->value != NULL)) |
| { |
| return node->value; |
| } |
| return (char *)defval; |
| } |
| |
| char* hdf_get_valuevf (HDF *hdf, const char *namefmt, va_list ap) |
| { |
| HDF *node; |
| char *name; |
| |
| name = vsprintf_alloc(namefmt, ap); |
| if (name == NULL) return NULL; |
| if ((_walk_hdf(hdf, name, &node) == 0) && (node->value != NULL)) |
| { |
| free(name); |
| return node->value; |
| } |
| free(name); |
| return NULL; |
| } |
| |
| char* hdf_get_valuef (HDF *hdf, const char *namefmt, ...) |
| { |
| char *val; |
| va_list ap; |
| |
| va_start(ap, namefmt); |
| val = hdf_get_valuevf(hdf, namefmt, ap); |
| va_end(ap); |
| return val; |
| } |
| |
| NEOERR* hdf_get_copy (HDF *hdf, const char *name, char **value, |
| const char *defval) |
| { |
| HDF *node; |
| |
| if ((_walk_hdf(hdf, name, &node) == 0) && (node->value != NULL)) |
| { |
| *value = strdup(node->value); |
| if (*value == NULL) |
| { |
| return nerr_raise (NERR_NOMEM, "Unable to allocate copy of %s", name); |
| } |
| } |
| else |
| { |
| if (defval == NULL) |
| *value = NULL; |
| else |
| { |
| *value = strdup(defval); |
| if (*value == NULL) |
| { |
| return nerr_raise (NERR_NOMEM, "Unable to allocate copy of %s", name); |
| } |
| } |
| } |
| return STATUS_OK; |
| } |
| |
| HDF* hdf_get_obj (HDF *hdf, const char *name) |
| { |
| HDF *obj; |
| |
| _walk_hdf(hdf, name, &obj); |
| return obj; |
| } |
| |
| HDF* hdf_get_child (HDF *hdf, const char *name) |
| { |
| HDF *obj; |
| _walk_hdf(hdf, name, &obj); |
| if (obj != NULL) return obj->child; |
| return obj; |
| } |
| |
| HDF_ATTR* hdf_get_attr (HDF *hdf, const char *name) |
| { |
| HDF *obj; |
| _walk_hdf(hdf, name, &obj); |
| if (obj != NULL) return obj->attr; |
| return NULL; |
| } |
| |
| NEOERR* hdf_set_attr (HDF *hdf, const char *name, const char *key, |
| const char *value) |
| { |
| HDF *obj; |
| HDF_ATTR *attr, *last; |
| |
| _walk_hdf(hdf, name, &obj); |
| if (obj == NULL) |
| return nerr_raise(NERR_ASSERT, "Unable to set attribute on none existant node"); |
| |
| if (obj->attr != NULL) |
| { |
| attr = obj->attr; |
| last = attr; |
| while (attr != NULL) |
| { |
| if (!strcmp(attr->key, key)) |
| { |
| if (attr->value) free(attr->value); |
| /* a set of NULL deletes the attr */ |
| if (value == NULL) |
| { |
| if (attr == obj->attr) |
| obj->attr = attr->next; |
| else |
| last->next = attr->next; |
| free(attr->key); |
| free(attr); |
| return STATUS_OK; |
| } |
| attr->value = strdup(value); |
| if (attr->value == NULL) |
| return nerr_raise(NERR_NOMEM, "Unable to set attr %s to %s", key, value); |
| return STATUS_OK; |
| } |
| last = attr; |
| attr = attr->next; |
| } |
| last->next = (HDF_ATTR *) calloc(1, sizeof(HDF_ATTR)); |
| if (last->next == NULL) |
| return nerr_raise(NERR_NOMEM, "Unable to set attr %s to %s", key, value); |
| attr = last->next; |
| } |
| else |
| { |
| if (value == NULL) return STATUS_OK; |
| obj->attr = (HDF_ATTR *) calloc(1, sizeof(HDF_ATTR)); |
| if (obj->attr == NULL) |
| return nerr_raise(NERR_NOMEM, "Unable to set attr %s to %s", key, value); |
| attr = obj->attr; |
| } |
| attr->key = strdup(key); |
| attr->value = strdup(value); |
| if (attr->key == NULL || attr->value == NULL) |
| return nerr_raise(NERR_NOMEM, "Unable to set attr %s to %s", key, value); |
| |
| return STATUS_OK; |
| } |
| |
| HDF* hdf_obj_child (HDF *hdf) |
| { |
| HDF *obj; |
| if (hdf == NULL) return NULL; |
| if (hdf->link) |
| { |
| if (_walk_hdf(hdf->top, hdf->value, &obj)) |
| return NULL; |
| return obj->child; |
| } |
| return hdf->child; |
| } |
| |
| HDF* hdf_obj_next (HDF *hdf) |
| { |
| if (hdf == NULL) return NULL; |
| return hdf->next; |
| } |
| |
| HDF* hdf_obj_top (HDF *hdf) |
| { |
| if (hdf == NULL) return NULL; |
| return hdf->top; |
| } |
| |
| HDF_ATTR* hdf_obj_attr (HDF *hdf) |
| { |
| if (hdf == NULL) return NULL; |
| return hdf->attr; |
| } |
| |
| char* hdf_obj_name (HDF *hdf) |
| { |
| if (hdf == NULL) return NULL; |
| return hdf->name; |
| } |
| |
| char* hdf_obj_value (HDF *hdf) |
| { |
| int count = 0; |
| |
| if (hdf == NULL) return NULL; |
| while (hdf->link && count < 100) |
| { |
| if (_walk_hdf (hdf->top, hdf->value, &hdf)) |
| return NULL; |
| count++; |
| } |
| return hdf->value; |
| } |
| |
| void _merge_attr (HDF_ATTR *dest, HDF_ATTR *src) |
| { |
| HDF_ATTR *da, *ld; |
| HDF_ATTR *sa, *ls; |
| BOOL found; |
| |
| sa = src; |
| ls = src; |
| while (sa != NULL) |
| { |
| da = dest; |
| ld = da; |
| found = 0; |
| while (da != NULL) |
| { |
| if (!strcmp(da->key, sa->key)) |
| { |
| if (da->value) free(da->value); |
| da->value = sa->value; |
| sa->value = NULL; |
| found = 1; |
| break; |
| } |
| ld = da; |
| da = da->next; |
| } |
| if (!found) |
| { |
| ld->next = sa; |
| ls->next = sa->next; |
| if (src == sa) src = sa->next; |
| ld->next->next = NULL; |
| sa = ls->next; |
| } |
| else |
| { |
| ls = sa; |
| sa = sa->next; |
| } |
| } |
| _dealloc_hdf_attr(&src); |
| } |
| |
| NEOERR* _hdf_hash_level(HDF *hdf) |
| { |
| NEOERR *err; |
| HDF *child; |
| |
| err = ne_hash_init(&(hdf->hash), hash_hdf_hash, hash_hdf_comp); |
| if (err) return nerr_pass(err); |
| |
| child = hdf->child; |
| while (child) |
| { |
| err = ne_hash_insert(hdf->hash, child, child); |
| if (err) return nerr_pass(err); |
| child = child->next; |
| } |
| return STATUS_OK; |
| } |
| |
| static NEOERR* _set_value (HDF *hdf, const char *name, const char *value, |
| int dup, int wf, int link, HDF_ATTR *attr, |
| HDF **set_node) |
| { |
| NEOERR *err; |
| HDF *hn, *hp, *hs; |
| HDF hash_key; |
| int x = 0; |
| const char *s = name; |
| const char *n = name; |
| int count = 0; |
| |
| if (set_node != NULL) *set_node = NULL; |
| if (hdf == NULL) |
| { |
| return nerr_raise(NERR_ASSERT, "Unable to set %s on NULL hdf", name); |
| } |
| |
| /* HACK: allow setting of this node by passing an empty name */ |
| if (name == NULL || name[0] == '\0') |
| { |
| /* handle setting attr first */ |
| if (hdf->attr == NULL) |
| { |
| hdf->attr = attr; |
| } |
| else |
| { |
| _merge_attr(hdf->attr, attr); |
| } |
| /* if we're setting ourselves to ourselves... */ |
| if (hdf->value == value) |
| { |
| if (set_node != NULL) *set_node = hdf; |
| return STATUS_OK; |
| } |
| if (hdf->alloc_value) |
| { |
| free(hdf->value); |
| hdf->value = NULL; |
| } |
| if (value == NULL) |
| { |
| hdf->alloc_value = 0; |
| hdf->value = NULL; |
| } |
| else if (dup) |
| { |
| hdf->alloc_value = 1; |
| hdf->value = strdup(value); |
| if (hdf->value == NULL) |
| return nerr_raise (NERR_NOMEM, "Unable to duplicate value %s for %s", |
| value, name); |
| } |
| else |
| { |
| hdf->alloc_value = wf; |
| hdf->value = (char *)value; |
| } |
| if (set_node != NULL) *set_node = hdf; |
| return STATUS_OK; |
| } |
| |
| n = name; |
| s = strchr (n, '.'); |
| x = (s != NULL) ? s - n : strlen(n); |
| if (x == 0) |
| { |
| return nerr_raise(NERR_ASSERT, "Unable to set Empty component %s", name); |
| } |
| |
| if (hdf->link) |
| { |
| char *new_name = (char *) malloc(strlen(hdf->value) + 1 + strlen(name) + 1); |
| if (new_name == NULL) |
| { |
| return nerr_raise(NERR_NOMEM, "Unable to allocate memory"); |
| } |
| strcpy(new_name, hdf->value); |
| strcat(new_name, "."); |
| strcat(new_name, name); |
| err = _set_value (hdf->top, new_name, value, dup, wf, link, attr, set_node); |
| free(new_name); |
| return nerr_pass(err); |
| } |
| else |
| { |
| hn = hdf; |
| } |
| |
| while (1) |
| { |
| /* examine cache to see if we have a match */ |
| count = 0; |
| hp = hn->last_hp; |
| hs = hn->last_hs; |
| |
| if ((hs == NULL && hp == hn->child) || (hs && hs->next == hp)) |
| { |
| if (hp && hp->name && (x == hp->name_len) && !strncmp (hp->name, n, x)) |
| { |
| goto skip_search; |
| } |
| } |
| |
| hp = hn->child; |
| hs = NULL; |
| |
| /* Look for a matching node at this level */ |
| if (hn->hash != NULL) |
| { |
| hash_key.name = (char *)n; |
| hash_key.name_len = x; |
| hp = ne_hash_lookup(hn->hash, &hash_key); |
| hs = hn->last_child; |
| } |
| else |
| { |
| while (hp != NULL) |
| { |
| if (hp->name && (x == hp->name_len) && !strncmp(hp->name, n, x)) |
| { |
| break; |
| } |
| hs = hp; |
| hp = hp->next; |
| count++; |
| } |
| } |
| |
| /* save in cache any value we found */ |
| if (hp) { |
| hn->last_hp = hp; |
| hn->last_hs = hs; |
| } |
| |
| skip_search: |
| |
| if (hp == NULL) |
| { |
| /* If there was no matching node at this level, we need to |
| * allocate an intersitial node (or the actual node if we're |
| * at the last part of the HDF name) */ |
| if (s != NULL) |
| { |
| /* intersitial */ |
| err = _alloc_hdf (&hp, n, x, NULL, 0, 0, hdf->top); |
| } |
| else |
| { |
| err = _alloc_hdf (&hp, n, x, value, dup, wf, hdf->top); |
| if (link) hp->link = 1; |
| else hp->link = 0; |
| hp->attr = attr; |
| } |
| if (err != STATUS_OK) |
| return nerr_pass (err); |
| if (hn->child == NULL) |
| hn->child = hp; |
| else |
| hs->next = hp; |
| hn->last_child = hp; |
| |
| /* This is the point at which we convert to a hash table |
| * at this level, if we're over the count */ |
| if (count > FORCE_HASH_AT && hn->hash == NULL) |
| { |
| err = _hdf_hash_level(hn); |
| if (err) return nerr_pass(err); |
| } |
| else if (hn->hash != NULL) |
| { |
| err = ne_hash_insert(hn->hash, hp, hp); |
| if (err) return nerr_pass(err); |
| } |
| } |
| else if (s == NULL) |
| { |
| /* If there is a matching node and we're at the end of the HDF |
| * name, then we update the value of the node */ |
| /* handle setting attr first */ |
| if (hp->attr == NULL) |
| { |
| hp->attr = attr; |
| } |
| else |
| { |
| _merge_attr(hp->attr, attr); |
| } |
| if (hp->value != value) |
| { |
| if (hp->alloc_value) |
| { |
| free(hp->value); |
| hp->value = NULL; |
| } |
| if (value == NULL) |
| { |
| hp->alloc_value = 0; |
| hp->value = NULL; |
| } |
| else if (dup) |
| { |
| hp->alloc_value = 1; |
| hp->value = strdup(value); |
| if (hp->value == NULL) |
| return nerr_raise (NERR_NOMEM, "Unable to duplicate value %s for %s", |
| value, name); |
| } |
| else |
| { |
| hp->alloc_value = wf; |
| hp->value = (char *)value; |
| } |
| } |
| if (link) hp->link = 1; |
| else hp->link = 0; |
| } |
| else if (hp->link) |
| { |
| char *new_name = (char *) malloc(strlen(hp->value) + strlen(s) + 1); |
| if (new_name == NULL) |
| { |
| return nerr_raise(NERR_NOMEM, "Unable to allocate memory"); |
| } |
| strcpy(new_name, hp->value); |
| strcat(new_name, s); |
| err = _set_value (hdf->top, new_name, value, dup, wf, link, attr, set_node); |
| free(new_name); |
| return nerr_pass(err); |
| } |
| /* At this point, we're done if there is not more HDF name space to |
| * traverse */ |
| if (s == NULL) |
| break; |
| /* Otherwise, we need to find the next part of the namespace */ |
| n = s + 1; |
| s = strchr (n, '.'); |
| x = (s != NULL) ? s - n : strlen(n); |
| if (x == 0) |
| { |
| return nerr_raise(NERR_ASSERT, "Unable to set Empty component %s", name); |
| } |
| hn = hp; |
| } |
| if (set_node != NULL) *set_node = hp; |
| return STATUS_OK; |
| } |
| |
| NEOERR* hdf_set_value (HDF *hdf, const char *name, const char *value) |
| { |
| return nerr_pass(_set_value (hdf, name, value, 1, 1, 0, NULL, NULL)); |
| } |
| |
| NEOERR* hdf_set_value_attr (HDF *hdf, const char *name, const char *value, |
| HDF_ATTR *attr) |
| { |
| return nerr_pass(_set_value (hdf, name, value, 1, 1, 0, attr, NULL)); |
| } |
| |
| NEOERR* hdf_set_symlink (HDF *hdf, const char *src, const char *dest) |
| { |
| return nerr_pass(_set_value (hdf, src, dest, 1, 1, 1, NULL, NULL)); |
| } |
| |
| NEOERR* hdf_set_int_value (HDF *hdf, const char *name, int value) |
| { |
| char buf[256]; |
| |
| snprintf (buf, sizeof(buf), "%d", value); |
| return nerr_pass(_set_value (hdf, name, buf, 1, 1, 0, NULL, NULL)); |
| } |
| |
| NEOERR* hdf_set_buf (HDF *hdf, const char *name, char *value) |
| { |
| return nerr_pass(_set_value (hdf, name, value, 0, 1, 0, NULL, NULL)); |
| } |
| |
| NEOERR* hdf_set_copy (HDF *hdf, const char *dest, const char *src) |
| { |
| HDF *node; |
| if ((_walk_hdf(hdf, src, &node) == 0) && (node->value != NULL)) |
| { |
| return nerr_pass(_set_value (hdf, dest, node->value, 0, 0, 0, NULL, NULL)); |
| } |
| return nerr_raise (NERR_NOT_FOUND, "Unable to find %s", src); |
| } |
| |
| NEOERR* hdf_set_valuevf (HDF *hdf, const char *fmt, va_list ap) |
| { |
| NEOERR *err; |
| char *k; |
| char *v; |
| |
| k = vsprintf_alloc(fmt, ap); |
| if (k == NULL) |
| { |
| return nerr_raise(NERR_NOMEM, "Unable to allocate memory for format string"); |
| } |
| v = strchr(k, '='); |
| if (v == NULL) |
| { |
| err = nerr_raise(NERR_ASSERT, "No equals found: %s", k); |
| free(k); |
| return err; |
| } |
| *v++ = '\0'; |
| err = hdf_set_value(hdf, k, v); |
| free(k); |
| return nerr_pass(err); |
| } |
| |
| NEOERR* hdf_set_valuef (HDF *hdf, const char *fmt, ...) |
| { |
| NEOERR *err; |
| va_list ap; |
| |
| va_start(ap, fmt); |
| err = hdf_set_valuevf(hdf, fmt, ap); |
| va_end(ap); |
| return nerr_pass(err); |
| } |
| |
| NEOERR* hdf_get_node (HDF *hdf, const char *name, HDF **ret) |
| { |
| _walk_hdf(hdf, name, ret); |
| if (*ret == NULL) |
| { |
| return nerr_pass(_set_value (hdf, name, NULL, 0, 1, 0, NULL, ret)); |
| } |
| return STATUS_OK; |
| } |
| |
| /* Ok, this version avoids the bubble sort by walking the level once to |
| * load them all into a ULIST, qsort'ing the list, and then dumping them |
| * back out... */ |
| NEOERR *hdf_sort_obj (HDF *h, int (*compareFunc)(const void *, const void *)) |
| { |
| NEOERR *err = STATUS_OK; |
| ULIST *level = NULL; |
| HDF *p, *c; |
| int x; |
| |
| if (h == NULL) return STATUS_OK; |
| c = h->child; |
| if (c == NULL) return STATUS_OK; |
| |
| do { |
| err = uListInit(&level, 40, 0); |
| if (err) return nerr_pass(err); |
| for (p = c; p; p = p->next) { |
| err = uListAppend(level, p); |
| if (err) break; |
| } |
| err = uListSort(level, compareFunc); |
| if (err) break; |
| uListGet(level, 0, (void *)&c); |
| h->child = c; |
| for (x = 1; x < uListLength(level); x++) |
| { |
| uListGet(level, x, (void *)&p); |
| c->next = p; |
| p->next = NULL; |
| c = p; |
| } |
| h->last_child = c; |
| } while (0); |
| uListDestroy(&level, 0); |
| return nerr_pass(err); |
| } |
| |
| NEOERR* hdf_remove_tree (HDF *hdf, const char *name) |
| { |
| HDF *hp = hdf; |
| HDF *lp = NULL, *ln = NULL; /* last parent, last node */ |
| int x = 0; |
| const char *s = name; |
| const char *n = name; |
| |
| if (hdf == NULL) return STATUS_OK; |
| |
| hp = hdf->child; |
| if (hp == NULL) |
| { |
| return STATUS_OK; |
| } |
| |
| lp = hdf; |
| ln = NULL; |
| |
| n = name; |
| s = strchr (n, '.'); |
| x = (s == NULL) ? strlen(n) : s - n; |
| |
| while (1) |
| { |
| while (hp != NULL) |
| { |
| if (hp->name && (x == hp->name_len) && !strncmp(hp->name, n, x)) |
| { |
| break; |
| } |
| else |
| { |
| ln = hp; |
| hp = hp->next; |
| } |
| } |
| if (hp == NULL) |
| { |
| return STATUS_OK; |
| } |
| if (s == NULL) break; |
| |
| lp = hp; |
| ln = NULL; |
| hp = hp->child; |
| n = s + 1; |
| s = strchr (n, '.'); |
| x = (s == NULL) ? strlen(n) : s - n; |
| } |
| |
| if (lp->hash != NULL) |
| { |
| ne_hash_remove(lp->hash, hp); |
| } |
| if (ln) |
| { |
| ln->next = hp->next; |
| /* check to see if we are the last parent's last_child, if so |
| * repoint so hash table inserts will go to the right place */ |
| if (hp == lp->last_child) |
| lp->last_child = ln; |
| hp->next = NULL; |
| } |
| else |
| { |
| lp->child = hp->next; |
| hp->next = NULL; |
| } |
| _dealloc_hdf (&hp); |
| |
| return STATUS_OK; |
| } |
| |
| static NEOERR * _copy_attr (HDF_ATTR **dest, HDF_ATTR *src) |
| { |
| HDF_ATTR *copy, *last = NULL; |
| |
| *dest = NULL; |
| while (src != NULL) |
| { |
| copy = (HDF_ATTR *)malloc(sizeof(HDF_ATTR)); |
| if (copy == NULL) |
| { |
| _dealloc_hdf_attr(dest); |
| return nerr_raise(NERR_NOMEM, "Unable to allocate copy of HDF_ATTR"); |
| } |
| copy->key = strdup(src->key); |
| copy->value = strdup(src->value); |
| copy->next = NULL; |
| if ((copy->key == NULL) || (copy->value == NULL)) |
| { |
| _dealloc_hdf_attr(dest); |
| return nerr_raise(NERR_NOMEM, "Unable to allocate copy of HDF_ATTR"); |
| } |
| if (last) { |
| last->next = copy; |
| } |
| else |
| { |
| *dest = copy; |
| } |
| last = copy; |
| src = src->next; |
| } |
| return STATUS_OK; |
| } |
| |
| static NEOERR * _copy_nodes (HDF *dest, HDF *src) |
| { |
| NEOERR *err = STATUS_OK; |
| HDF *dt, *st; |
| HDF_ATTR *attr_copy; |
| |
| st = src->child; |
| while (st != NULL) |
| { |
| err = _copy_attr(&attr_copy, st->attr); |
| if (err) return nerr_pass(err); |
| err = _set_value(dest, st->name, st->value, 1, 1, 0, attr_copy, &dt); |
| if (err) { |
| _dealloc_hdf_attr(&attr_copy); |
| return nerr_pass(err); |
| } |
| if (src->child) |
| { |
| err = _copy_nodes (dt, st); |
| if (err) return nerr_pass(err); |
| } |
| st = st->next; |
| } |
| return STATUS_OK; |
| } |
| |
| NEOERR* hdf_copy (HDF *dest, const char *name, HDF *src) |
| { |
| NEOERR *err; |
| HDF *node; |
| |
| if (_walk_hdf(dest, name, &node) == -1) |
| { |
| err = _set_value (dest, name, NULL, 0, 0, 0, NULL, &node); |
| if (err) return nerr_pass (err); |
| } |
| return nerr_pass (_copy_nodes (node, src)); |
| } |
| |
| /* BUG: currently, this only prints something if there is a value... |
| * but we now allow attributes on nodes with no value... */ |
| |
| static void gen_ml_break(char *ml, size_t len) |
| { |
| int nlen; |
| int x = 0; |
| |
| ml[x++] = '\n'; |
| nlen = 2 + neo_rand(len-5); |
| if (nlen == 0) |
| { |
| nlen = len / 2; |
| } |
| while (nlen) |
| { |
| ml[x++] = ('A' + neo_rand(26)); |
| nlen--; |
| } |
| ml[x++] = '\n'; |
| ml[x] = '\0'; |
| } |
| |
| typedef NEOERR *(*DUMPF_CB)(void *rock, const char *fmt, ...); |
| |
| static NEOERR *_fp_dump_cb (void *rock, const char *fmt, ...) |
| { |
| FILE *fp = (FILE *)rock; |
| va_list ap; |
| |
| va_start (ap, fmt); |
| vfprintf(fp, fmt, ap); |
| va_end(ap); |
| return STATUS_OK; |
| } |
| |
| static NEOERR *_string_dump_cb (void *rock, const char *fmt, ...) |
| { |
| NEOERR *err; |
| STRING *str = (STRING *)rock; |
| va_list ap; |
| |
| va_start (ap, fmt); |
| err = string_appendvf(str, fmt, ap); |
| va_end(ap); |
| return nerr_pass(err); |
| } |
| |
| #define DUMP_TYPE_DOTTED 0 |
| #define DUMP_TYPE_COMPACT 1 |
| #define DUMP_TYPE_PRETTY 2 |
| |
| static NEOERR* hdf_dump_cb(HDF *hdf, const char *prefix, int dtype, int lvl, |
| void *rock, DUMPF_CB dump_cbf) |
| { |
| NEOERR *err; |
| char *p, op; |
| char ml[10] = "\nEOM\n"; |
| int ml_len = strlen(ml); |
| char whsp[256] = ""; |
| |
| if (dtype == DUMP_TYPE_PRETTY) |
| { |
| memset(whsp, ' ', 256); |
| if (lvl > 127) |
| lvl = 127; |
| whsp[lvl*2] = '\0'; |
| } |
| |
| if (hdf != NULL) hdf = hdf->child; |
| |
| while (hdf != NULL) |
| { |
| op = '='; |
| if (hdf->value) |
| { |
| if (hdf->link) op = ':'; |
| if (prefix && (dtype == DUMP_TYPE_DOTTED)) |
| { |
| err = dump_cbf(rock, "%s.%s", prefix, hdf->name); |
| } |
| else |
| { |
| err = dump_cbf(rock, "%s%s", whsp, hdf->name); |
| } |
| if (err) return nerr_pass (err); |
| if (hdf->attr) |
| { |
| HDF_ATTR *attr = hdf->attr; |
| char *v = NULL; |
| |
| err = dump_cbf(rock, " ["); |
| if (err) return nerr_pass(err); |
| while (attr != NULL) |
| { |
| if (attr->value == NULL || !strcmp(attr->value, "1")) |
| err = dump_cbf(rock, "%s", attr->key); |
| else |
| { |
| v = repr_string_alloc(attr->value); |
| |
| if (v == NULL) |
| return nerr_raise(NERR_NOMEM, "Unable to repr attr %s value %s", attr->key, attr->value); |
| err = dump_cbf(rock, "%s=%s", attr->key, v); |
| free(v); |
| } |
| if (err) return nerr_pass(err); |
| if (attr->next) |
| { |
| err = dump_cbf(rock, ", "); |
| if (err) return nerr_pass(err); |
| } |
| attr = attr->next; |
| } |
| err = dump_cbf(rock, "] "); |
| if (err) return nerr_pass(err); |
| } |
| if (strchr (hdf->value, '\n')) |
| { |
| int vlen = strlen(hdf->value); |
| |
| while (strstr(hdf->value, ml) || ((vlen > ml_len) && !strncmp(hdf->value + vlen - ml_len + 1, ml, strlen(ml) - 1))) |
| { |
| gen_ml_break(ml, sizeof(ml)); |
| ml_len = strlen(ml); |
| } |
| if (hdf->value[strlen(hdf->value)-1] != '\n') |
| err = dump_cbf(rock, " << %s%s%s", ml+1, hdf->value, ml); |
| else |
| err = dump_cbf(rock, " << %s%s%s", ml+1, hdf->value, ml+1); |
| } |
| else |
| { |
| err = dump_cbf(rock, " %c %s\n", op, hdf->value); |
| } |
| if (err) return nerr_pass (err); |
| } |
| if (hdf->child) |
| { |
| if (prefix && (dtype == DUMP_TYPE_DOTTED)) |
| { |
| p = (char *) malloc (strlen(hdf->name) + strlen(prefix) + 2); |
| sprintf (p, "%s.%s", prefix, hdf->name); |
| err = hdf_dump_cb (hdf, p, dtype, lvl+1, rock, dump_cbf); |
| free(p); |
| } |
| else |
| { |
| if (hdf->name && (dtype != DUMP_TYPE_DOTTED)) |
| { |
| err = dump_cbf(rock, "%s%s {\n", whsp, hdf->name); |
| if (err) return nerr_pass (err); |
| err = hdf_dump_cb (hdf, hdf->name, dtype, lvl+1, rock, dump_cbf); |
| if (err) return nerr_pass (err); |
| err = dump_cbf(rock, "%s}\n", whsp); |
| } |
| else |
| { |
| err = hdf_dump_cb (hdf, hdf->name, dtype, lvl+1, rock, dump_cbf); |
| } |
| } |
| if (err) return nerr_pass (err); |
| } |
| hdf = hdf->next; |
| } |
| return STATUS_OK; |
| } |
| |
| NEOERR* hdf_dump_str (HDF *hdf, const char *prefix, int dtype, STRING *str) |
| { |
| return nerr_pass(hdf_dump_cb(hdf, prefix, dtype, 0, str, _string_dump_cb)); |
| } |
| |
| NEOERR* hdf_dump(HDF *hdf, const char *prefix) |
| { |
| return nerr_pass(hdf_dump_cb(hdf, prefix, DUMP_TYPE_DOTTED, 0, stdout, _fp_dump_cb)); |
| } |
| |
| NEOERR* hdf_dump_format (HDF *hdf, int lvl, FILE *fp) |
| { |
| return nerr_pass(hdf_dump_cb(hdf, "", DUMP_TYPE_PRETTY, 0, fp, _fp_dump_cb)); |
| } |
| |
| NEOERR *hdf_write_file (HDF *hdf, const char *path) |
| { |
| NEOERR *err; |
| FILE *fp; |
| |
| fp = fopen(path, "w"); |
| if (fp == NULL) |
| return nerr_raise_errno (NERR_IO, "Unable to open %s for writing", path); |
| |
| err = hdf_dump_format (hdf, 0, fp); |
| |
| fclose (fp); |
| if (err) |
| { |
| unlink(path); |
| } |
| return nerr_pass(err); |
| } |
| |
| NEOERR *hdf_write_file_atomic (HDF *hdf, const char *path) |
| { |
| NEOERR *err; |
| FILE *fp; |
| char tpath[_POSIX_PATH_MAX]; |
| static int count = 0; |
| |
| snprintf(tpath, sizeof(tpath), "%s.%5.5f.%d", path, ne_timef(), count++); |
| |
| fp = fopen(tpath, "w"); |
| if (fp == NULL) |
| return nerr_raise_errno (NERR_IO, "Unable to open %s for writing", tpath); |
| |
| err = hdf_dump_format (hdf, 0, fp); |
| |
| fclose (fp); |
| |
| if (err) |
| { |
| unlink(tpath); |
| return nerr_pass(err); |
| } |
| if (rename(tpath, path) == -1) |
| { |
| unlink (tpath); |
| return nerr_raise_errno (NERR_IO, "Unable to rename file %s to %s", |
| tpath, path); |
| } |
| |
| return STATUS_OK; |
| } |
| |
| NEOERR *hdf_write_string (HDF *hdf, char **s) |
| { |
| STRING str; |
| NEOERR *err; |
| |
| *s = NULL; |
| |
| string_init (&str); |
| |
| err = hdf_dump_str (hdf, NULL, 1, &str); |
| if (err) |
| { |
| string_clear (&str); |
| return nerr_pass(err); |
| } |
| if (str.buf == NULL) |
| { |
| *s = strdup(""); |
| if (*s == NULL) return nerr_raise(NERR_NOMEM, "Unable to allocate empty string"); |
| } |
| else |
| { |
| *s = str.buf; |
| } |
| |
| return STATUS_OK; |
| } |
| |
| |
| #define SKIPWS(s) while (*s && isspace(*s)) s++; |
| |
| static int _copy_line (const char **s, char *buf, size_t buf_len) |
| { |
| int x = 0; |
| const char *st = *s; |
| |
| while (*st && x < buf_len-1) |
| { |
| buf[x++] = *st; |
| if (*st++ == '\n') break; |
| } |
| buf[x] = '\0'; |
| *s = st; |
| |
| return x; |
| } |
| |
| /* Copy the characters in the file (up to the next newline) into line |
| * and advance s to the next line */ |
| static NEOERR *_copy_line_advance(const char **s, STRING *line) |
| { |
| NEOERR *err; |
| int x = 0; |
| const char *st = *s; |
| const char *nl; |
| |
| nl = strchr(st, '\n'); |
| if (nl == NULL) |
| { |
| x = strlen(st); |
| err = string_appendn(line, st, x); |
| if (err) return nerr_pass(err); |
| *s = st + x; |
| } |
| else |
| { |
| x = nl - st; |
| err = string_appendn(line, st, x); |
| if (err) return nerr_pass(err); |
| *s = nl + 1; |
| } |
| |
| return STATUS_OK; |
| } |
| |
| char *_strndup(const char *s, int len) { |
| int x; |
| char *dup; |
| if (s == NULL) return NULL; |
| dup = (char *) malloc(len+1); |
| if (dup == NULL) return NULL; |
| for (x = 0; x < len && s[x]; x++) |
| { |
| dup[x] = s[x]; |
| } |
| dup[x] = '\0'; |
| dup[len] = '\0'; |
| return dup; |
| } |
| |
| /* attributes are of the form [key1, key2, key3=value, key4="repr"] */ |
| static NEOERR* parse_attr(char **str, HDF_ATTR **attr) |
| { |
| NEOERR *err = STATUS_OK; |
| char *s = *str; |
| char *k, *v; |
| int k_l, v_l; |
| STRING buf; |
| char c; |
| HDF_ATTR *ha, *hal = NULL; |
| |
| *attr = NULL; |
| |
| string_init(&buf); |
| while (*s && *s != ']') |
| { |
| k = s; |
| k_l = 0; |
| v = NULL; |
| v_l = 0; |
| while (*s && isalnum(*s)) s++; |
| k_l = s-k; |
| if (*s == '\0' || k_l == 0) |
| { |
| _dealloc_hdf_attr(attr); |
| return nerr_raise(NERR_PARSE, "Misformed attribute specification: %s", *str); |
| } |
| SKIPWS(s); |
| if (*s == '=') |
| { |
| s++; |
| SKIPWS(s); |
| if (*s == '"') |
| { |
| s++; |
| while (*s && *s != '"') |
| { |
| if (*s == '\\') |
| { |
| if (isdigit(*(s+1))) |
| { |
| s++; |
| c = *s - '0'; |
| if (isdigit(*(s+1))) |
| { |
| s++; |
| c = (c * 8) + (*s - '0'); |
| if (isdigit(*(s+1))) |
| { |
| s++; |
| c = (c * 8) + (*s - '0'); |
| } |
| } |
| } |
| else |
| { |
| s++; |
| if (*s == 'n') c = '\n'; |
| else if (*s == 't') c = '\t'; |
| else if (*s == 'r') c = '\r'; |
| else c = *s; |
| } |
| err = string_append_char(&buf, c); |
| } |
| else |
| { |
| err = string_append_char(&buf, *s); |
| } |
| if (err) |
| { |
| string_clear(&buf); |
| _dealloc_hdf_attr(attr); |
| return nerr_pass(err); |
| } |
| s++; |
| } |
| if (*s == '\0') |
| { |
| _dealloc_hdf_attr(attr); |
| string_clear(&buf); |
| return nerr_raise(NERR_PARSE, "Misformed attribute specification: %s", *str); |
| } |
| s++; |
| v = buf.buf; |
| v_l = buf.len; |
| } |
| else |
| { |
| v = s; |
| while (*s && *s != ' ' && *s != ',' && *s != ']') s++; |
| if (*s == '\0') |
| { |
| _dealloc_hdf_attr(attr); |
| return nerr_raise(NERR_PARSE, "Misformed attribute specification: %s", *str); |
| } |
| v_l = s-v; |
| } |
| } |
| else |
| { |
| v = "1"; |
| } |
| ha = (HDF_ATTR*) calloc (1, sizeof(HDF_ATTR)); |
| if (ha == NULL) |
| { |
| _dealloc_hdf_attr(attr); |
| string_clear(&buf); |
| return nerr_raise(NERR_NOMEM, "Unable to load attributes: %s", s); |
| } |
| if (*attr == NULL) *attr = ha; |
| ha->key = _strndup(k, k_l); |
| if (v) |
| ha->value = _strndup(v, v_l); |
| else |
| ha->value = strdup(""); |
| if (ha->key == NULL || ha->value == NULL) |
| { |
| _dealloc_hdf_attr(attr); |
| string_clear(&buf); |
| return nerr_raise(NERR_NOMEM, "Unable to load attributes: %s", s); |
| } |
| if (hal != NULL) hal->next = ha; |
| hal = ha; |
| string_clear(&buf); |
| SKIPWS(s); |
| if (*s == ',') |
| { |
| s++; |
| SKIPWS(s); |
| } |
| } |
| if (*s == '\0') |
| { |
| _dealloc_hdf_attr(attr); |
| return nerr_raise(NERR_PARSE, "Misformed attribute specification: %s", *str); |
| } |
| *str = s+1; |
| return STATUS_OK; |
| } |
| |
| #define INCLUDE_ERROR 0 |
| #define INCLUDE_IGNORE 1 |
| #define INCLUDE_FILE 2 |
| |
| static NEOERR* _hdf_read_string (HDF *hdf, const char **str, STRING *line, |
| const char *path, int *lineno, int include_handle) |
| { |
| NEOERR *err; |
| HDF *lower; |
| char *s; |
| char *name, *value; |
| HDF_ATTR *attr = NULL; |
| |
| while (**str != '\0') |
| { |
| /* Reset string length, but don't free the reserved buffer */ |
| line->len = 0; |
| err = _copy_line_advance(str, line); |
| if (err) return nerr_pass(err); |
| attr = NULL; |
| (*lineno)++; |
| s = line->buf; |
| SKIPWS(s); |
| if (!strncmp(s, "#include ", 9)) |
| { |
| if (include_handle == INCLUDE_ERROR) |
| { |
| return nerr_raise (NERR_PARSE, |
| "[%d]: #include not supported in string parse", |
| *lineno); |
| } |
| else if (include_handle == INCLUDE_FILE) |
| { |
| int l; |
| s += 9; |
| name = neos_strip(s); |
| l = strlen(name); |
| if (name[0] == '"' && name[l-1] == '"') |
| { |
| name[l-1] = '\0'; |
| name++; |
| } |
| err = hdf_read_file(hdf, name); |
| if (err != STATUS_OK) |
| { |
| return nerr_pass_ctx(err, "In file %s:%d", path, *lineno); |
| } |
| } |
| } |
| else if (s[0] == '#') |
| { |
| /* comment: pass */ |
| } |
| else if (s[0] == '}') /* up */ |
| { |
| s = neos_strip(s); |
| if (strcmp(s, "}")) |
| { |
| err = nerr_raise(NERR_PARSE, |
| "[%s:%d] Trailing garbage on line following }: %s", path, *lineno, |
| line->buf); |
| return err; |
| } |
| return STATUS_OK; |
| } |
| else if (s[0]) |
| { |
| /* Valid hdf name is [0-9a-zA-Z_.]+ */ |
| name = s; |
| while (*s && (isalnum(*s) || *s == '_' || *s == '.')) s++; |
| SKIPWS(s); |
| |
| if (s[0] == '[') /* attributes */ |
| { |
| *s = '\0'; |
| name = neos_strip(name); |
| s++; |
| err = parse_attr(&s, &attr); |
| if (err) |
| { |
| return nerr_pass_ctx(err, "In file %s:%d", path, *lineno); |
| } |
| SKIPWS(s); |
| } |
| if (s[0] == '=') /* assignment */ |
| { |
| *s = '\0'; |
| name = neos_strip(name); |
| s++; |
| value = neos_strip(s); |
| err = _set_value (hdf, name, value, 1, 1, 0, attr, NULL); |
| if (err != STATUS_OK) |
| { |
| return nerr_pass_ctx(err, "In file %s:%d", path, *lineno); |
| } |
| } |
| else if (s[0] == ':' && s[1] == '=') /* copy */ |
| { |
| *s = '\0'; |
| name = neos_strip(name); |
| s+=2; |
| value = neos_strip(s); |
| value = hdf_get_value(hdf->top, value, ""); |
| err = _set_value (hdf, name, value, 1, 1, 0, attr, NULL); |
| if (err != STATUS_OK) |
| { |
| return nerr_pass_ctx(err, "In file %s:%d", path, *lineno); |
| } |
| } |
| else if (s[0] == ':') /* link */ |
| { |
| *s = '\0'; |
| name = neos_strip(name); |
| s++; |
| value = neos_strip(s); |
| err = _set_value (hdf, name, value, 1, 1, 1, attr, NULL); |
| if (err != STATUS_OK) |
| { |
| return nerr_pass_ctx(err, "In file %s:%d", path, *lineno); |
| } |
| } |
| else if (s[0] == '{') /* deeper */ |
| { |
| *s = '\0'; |
| name = neos_strip(name); |
| lower = hdf_get_obj (hdf, name); |
| if (lower == NULL) |
| { |
| err = _set_value (hdf, name, NULL, 1, 1, 0, attr, &lower); |
| } |
| else |
| { |
| err = _set_value (lower, NULL, lower->value, 1, 1, 0, attr, NULL); |
| } |
| if (err != STATUS_OK) |
| { |
| return nerr_pass_ctx(err, "In file %s:%d", path, *lineno); |
| } |
| err = _hdf_read_string (lower, str, line, path, lineno, include_handle); |
| if (err != STATUS_OK) |
| { |
| return nerr_pass_ctx(err, "In file %s:%d", path, *lineno); |
| } |
| } |
| else if (s[0] == '<' && s[1] == '<') /* multi-line assignment */ |
| { |
| char *m; |
| int msize = 0; |
| int mmax = 128; |
| int l; |
| |
| *s = '\0'; |
| name = neos_strip(name); |
| s+=2; |
| value = neos_strip(s); |
| l = strlen(value); |
| if (l == 0) |
| { |
| err = nerr_raise(NERR_PARSE, |
| "[%s:%d] No multi-assignment terminator given: %s", path, *lineno, |
| line->buf); |
| return err; |
| } |
| m = (char *) malloc (mmax * sizeof(char)); |
| if (m == NULL) |
| { |
| return nerr_raise(NERR_NOMEM, |
| "[%s:%d] Unable to allocate memory for multi-line assignment to %s", |
| path, *lineno, name); |
| } |
| while (_copy_line (str, m+msize, mmax-msize) != 0) |
| { |
| (*lineno)++; |
| if (!strncmp(value, m+msize, l) && isspace(m[msize+l])) |
| { |
| m[msize] = '\0'; |
| break; |
| } |
| msize += strlen(m+msize); |
| if (msize + l + 10 > mmax) |
| { |
| mmax += 128; |
| m = (char *) realloc (m, mmax * sizeof(char)); |
| if (m == NULL) |
| { |
| return nerr_raise(NERR_NOMEM, |
| "[%s:%d] Unable to allocate memory for multi-line assignment to %s: size=%d", |
| path, *lineno, name, mmax); |
| } |
| } |
| } |
| err = _set_value (hdf, name, m, 0, 1, 0, attr, NULL); |
| if (err != STATUS_OK) |
| { |
| free (m); |
| return nerr_pass_ctx(err, "In file %s:%d", path, *lineno); |
| } |
| |
| } |
| else |
| { |
| err = nerr_raise(NERR_PARSE, "[%s:%d] Unable to parse line %s", path, |
| *lineno, line->buf); |
| return err; |
| } |
| } |
| } |
| return STATUS_OK; |
| } |
| |
| NEOERR * hdf_read_string (HDF *hdf, const char *str) |
| { |
| NEOERR *err; |
| int lineno = 0; |
| STRING line; |
| string_init(&line); |
| err = _hdf_read_string(hdf, &str, &line, "<string>", &lineno, INCLUDE_ERROR); |
| string_clear(&line); |
| return nerr_pass(err); |
| } |
| |
| NEOERR * hdf_read_string_ignore (HDF *hdf, const char *str, int ignore) |
| { |
| NEOERR *err; |
| int lineno = 0; |
| STRING line; |
| string_init(&line); |
| err = _hdf_read_string(hdf, &str, &line, "<string>", &lineno, |
| (ignore ? INCLUDE_IGNORE : INCLUDE_ERROR)); |
| string_clear(&line); |
| return nerr_pass(err); |
| } |
| |
| /* The search path is part of the HDF by convention */ |
| NEOERR* hdf_search_path (HDF *hdf, const char *path, char *full) |
| { |
| HDF *paths; |
| struct stat s; |
| |
| for (paths = hdf_get_child (hdf, "hdf.loadpaths"); |
| paths; |
| paths = hdf_obj_next (paths)) |
| { |
| snprintf (full, _POSIX_PATH_MAX, "%s/%s", hdf_obj_value(paths), path); |
| errno = 0; |
| if (stat (full, &s) == -1) |
| { |
| if (errno != ENOENT) |
| return nerr_raise_errno (NERR_SYSTEM, "Stat of %s failed", full); |
| } |
| else |
| { |
| return STATUS_OK; |
| } |
| } |
| |
| strncpy (full, path, _POSIX_PATH_MAX); |
| if (stat (full, &s) == -1) |
| { |
| if (errno != ENOENT) |
| return nerr_raise_errno (NERR_SYSTEM, "Stat of %s failed", full); |
| } |
| else return STATUS_OK; |
| |
| return nerr_raise (NERR_NOT_FOUND, "Path %s not found", path); |
| } |
| |
| NEOERR* hdf_read_file (HDF *hdf, const char *path) |
| { |
| NEOERR *err; |
| int lineno = 0; |
| char fpath[_POSIX_PATH_MAX]; |
| char *ibuf = NULL; |
| const char *ptr = NULL; |
| HDF *top = hdf->top; |
| STRING line; |
| |
| string_init(&line); |
| |
| if (path == NULL) |
| return nerr_raise(NERR_ASSERT, "Can't read NULL file"); |
| |
| if (top->fileload) |
| { |
| err = top->fileload(top->fileload_ctx, hdf, path, &ibuf); |
| } |
| else |
| { |
| if (path[0] != '/') |
| { |
| err = hdf_search_path (hdf, path, fpath); |
| if (err != STATUS_OK) return nerr_pass(err); |
| path = fpath; |
| } |
| |
| err = ne_load_file (path, &ibuf); |
| } |
| if (err) return nerr_pass(err); |
| |
| ptr = ibuf; |
| err = _hdf_read_string(hdf, &ptr, &line, path, &lineno, INCLUDE_FILE); |
| free(ibuf); |
| string_clear(&line); |
| return nerr_pass(err); |
| } |
| |
| void hdf_register_fileload(HDF *hdf, void *ctx, HDFFILELOAD fileload) |
| { |
| if (hdf == NULL) return; |
| if (hdf->top != NULL) hdf = hdf->top; |
| hdf->fileload_ctx = ctx; |
| hdf->fileload = fileload; |
| } |
| |