/*
* 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
*
*/
#ifndef __NEO_HDF_H_
#define __NEO_HDF_H_ 1
__BEGIN_DECLS
#include <stdio.h>
#include "util/neo_err.h"
#include "util/neo_hash.h"
#define FORCE_HASH_AT 10
typedef struct _hdf HDF;
/* HDFFILELOAD is a callback function to intercept file load requests and
* provide templates via another mechanism. This way you can load templates
* that you compiled-into your binary, from in-memory caches, or from a
* zip file, etc. The HDF is provided so you can choose to use the
* hdf_search_path function to find the file. contents should return
* a full malloc copy of the contents of the file, which the parser will
* own and free. Use hdf_register_fileload to set this function for
* your top level HDF node.
* NOTE: Technically, we shouldn't need a separate copy for each parse, but
* using the separate copy makes this equivalent to the CSFILELOAD function. We
* can change this if we really want to save that copy at the expense of
* slightly more complicated code. */
typedef NEOERR* (*HDFFILELOAD)(void *ctx, HDF *hdf, const char *filename,
char **contents);
typedef struct _attr
{
char *key;
char *value;
struct _attr *next;
} HDF_ATTR;
struct _hdf
{
int link;
int alloc_value;
char *name;
int name_len;
char *value;
struct _attr *attr;
struct _hdf *top;
struct _hdf *next;
struct _hdf *child;
/* the following fields are used to implement a cache */
struct _hdf *last_hp;
struct _hdf *last_hs;
/* the following HASH is used when we reach more than FORCE_HASH_AT
* elements */
NE_HASH *hash;
/* When using the HASH, we need to know where to append new children */
struct _hdf *last_child;
/* Should only be set on the head node, used to override the default file
* load method */
void *fileload_ctx;
HDFFILELOAD fileload;
};
/*
* Function: hdf_init - Initialize an HDF data set
* Description: hdf_init initializes an HDF data set and returns the
* pointer to the top node in the data set.
* Input: hdf - pointer to an HDF pointer
* Output: hdf - allocated hdf node
* Returns: NERR_NOMEM - unable to allocate memory for dataset
*/
NEOERR* hdf_init (HDF **hdf);
/*
* Function: hdf_destroy - deallocate an HDF data set
* Description: hdf_destroy is used to deallocate all memory associated
* with an hdf data set. Although you can pass an HDF node
* as an argument to this function, you are likely to cause
* a segfault if you continue to access the data set. In
* the future, we may restrict hdf_destroy so it only works
* on the top level node.
* Input: hdf - pointer to an HDF data set allocated with hdf_init
* Output: None
* Returns: None
*/
void hdf_destroy (HDF **hdf);
/*
* Function: hdf_get_int_value - Return the integer value of a point in
* the data set
* Description: hdf_get_int_value walks the HDF data set pointed to by
* hdf to name, and returns the value of that node
* converted to an integer. If that node does not exist,
* or it does not contain a number, the defval is returned.
* Input: hdf -> a node in an HDF data set
* name -> the name of a node to walk to in the data set
* defval -> value to return in case of error or if the node
* doesn't exist
* Output: None
* Returns: The integer value of the node, or the defval
*/
int hdf_get_int_value (HDF *hdf, const char *name, int defval);
/*
* Function: hdf_get_value - Return the value of a node in the data set
* Description: hdf_get_value walks the data set pointed to by hdf via
* name and returns the string value located there, or
* defval if the node doesn't exist
* Input: hdf -> the dataset node to start from
* name -> the name to walk the data set to
* defval -> the default value to return if the node doesn't
* exist
* Output: None
* Returns: A pointer to the string stored in the data set, or defval.
* The data set maintains ownership of the string, if you want
* a copy you either have to call strdup yourself, or use
* hdf_get_copy
*/
char *hdf_get_value (HDF *hdf, const char *name, const char *defval);
/*
* Function: hdf_get_valuevf - Return the value of a node in the data set
* Description: hdf_get_valuevf walks the data set pointed to by hdf via
* namefmt printf expanded with varargs ap, and returns the
* string value located there, or NULL if it doesn't exist.
* This differs from hdf_get_value in that there is no
* default value possible.
* Input: hdf -> the dataset node to start from
* namefmt -> the format string
* ap -> va_list of varargs
* Output: None
* Returns: A pointer to the string stored in the data set, or NULL.
* The data set maintains ownership of the string, if you want
* a copy you either have to call strdup yourself.
*/
char* hdf_get_valuevf (HDF *hdf, const char *namefmt, va_list ap);
/*
* Function: hdf_get_valuef - Return the value of a node in the data set
* Description: hdf_get_valuef walks the data set pointed to by hdf via
* namefmt printf expanded with varargs, and returns the
* string value located there, or NULL if it doesn't exist.
* This differs from hdf_get_value in that there is no
* default value possible.
* Input: hdf -> the dataset node to start from
* namefmt -> the printf-style format string
* ... -> arguments to fill out namefmt
* Output: None
* Returns: A pointer to the string stored in the data set, or NULL.
* The data set maintains ownership of the string, if you want
* a copy you either have to call strdup yourself.
*/
char* hdf_get_valuef (HDF *hdf, const char *namefmt, ...)
ATTRIBUTE_PRINTF(2,3);
/*
* Function: hdf_get_copy - Returns a copy of a string in the HDF data set
* Description: hdf_get_copy is similar to hdf_get_value, except that it
* returns an malloc'd copy of the string.
* Input: hdf -> the dataset node to start from
* name -> the name to walk the data set to
* defval -> the default value to return if the node doesn't
* exist
* Output: value -> the allocated string (if defval = NULL, then value
* will be NULL if defval is used)
* Returns: NERR_NOMEM if unable to allocate the new copy
*/
NEOERR* hdf_get_copy (HDF *hdf, const char *name, char **value,
const char *defval);
/*
* Function: hdf_get_obj - return the HDF data set node at a named location
* Description: hdf_get_obj walks the dataset given by hdf to the node
* named name, and then returns the pointer to that node
* Input: hdf -> the dataset node to start from
* name -> the name to walk to
* Output: None
* Returns: the pointer to the named node, or NULL if it doesn't exist
*/
HDF* hdf_get_obj (HDF *hdf, const char *name);
/*
* Function: hdf_get_node - Similar to hdf_get_obj except all the nodes
* are created if the don't exist.
* Description: hdf_get_node is similar to hdf_get_obj, except instead
* of stopping if it can't find a node in the tree, it will
* create all of the nodes necessary to hand you back the
* node you ask for. Nodes are created with no value.
* Input: hdf -> the dataset node to start from
* name -> the name to walk to
* Output: ret -> the dataset node you asked for
* Returns: NERR_NOMEM - unable to allocate new nodes
*/
NEOERR * hdf_get_node (HDF *hdf, const char *name, HDF **ret);
/*
* Function: hdf_get_child - return the first child of the named node
* Description: hdf_get_child will walk the dataset starting at hdf to
* name, and return the first child of that node
* Input: hdf -> the dataset node to start from
* name -> the name to walk to
* Output: None
* Returns: The first child of the named dataset node or NULL if the
* node is not found (or it has no children)
*/
HDF* hdf_get_child (HDF *hdf, const char *name);
/*
* Function: hdf_get_attr -
* Description:
* Input:
* Output:
* Returns:
*/
HDF_ATTR* hdf_get_attr (HDF *hdf, const char *name);
/*
* Function: hdf_set_attr -
* Description:
* Input:
* Output:
* Returns:
*/
NEOERR* hdf_set_attr (HDF *hdf, const char *name, const char *key,
const char *value);
/*
* Function: hdf_obj_child - Return the first child of a dataset node
* Description: hdf_obj_child and the other hdf_obj_ functions are
* accessors to the HDF dataset. Although we do not
* currently "hide" the HDF struct implementation, we
* recommend you use the accessor functions instead of
* accessing the values directly.
* Input: hdf -> the hdf dataset node
* Output: None
* Returns: The pointer to the first child, or NULL if there is none
*/
HDF* hdf_obj_child (HDF *hdf);
/*
* Function: hdf_obj_next - Return the next node of a dataset level
* Description: hdf_obj_next is an accessor function for the HDF struct
* Input: hdf -> the hdf dataset node
* Output: None
* Returns: The pointer to the next node, or NULL if there is none
*/
HDF* hdf_obj_next (HDF *hdf);
/*
* Function: hdf_obj_top - Return the pointer to the top dataset node
* Description: hdf_obj_top is an accessor function which returns a
* pointer to the top of the dataset, the node which was
* returned by hdf_init. This is most useful for
* implementations of language wrappers where individual
* nodes are tied garbage colletion wise to the top node of
* the data set
* Input: hdf -> the hdf dataset node
* Output: None
* Returns: The pointer to the top node
*/
HDF* hdf_obj_top (HDF *hdf);
/*
* Function: hdf_obj_attr - Return the HDF Attributes for a node
* Description:
* Input:
* Output:
* Returns:
*/
HDF_ATTR* hdf_obj_attr (HDF *hdf);
/*
* Function: hdf_obj_name - Return the name of a node
* Description: hdf_obj_name is an accessor function for a datset node
* which returns the name of the node. This is just the
* local name, and not the full path.
* Input: hdf -> the hdf dataset node
* Output: None
* Returns: The name of the node. If this is the top node, the name is
* NULL.
*/
char* hdf_obj_name (HDF *hdf);
/*
* Function: hdf_obj_value - Return the value of a node
* Description: hdf_obj_value is an accessor function for a dataset node
* which returns the value of the node, or NULL if the node
* has no value. This is not a copy of the value, so the
* node retains ownership of the value
* Input: hdf -> the hdf dataset node
* Output: None
* Returns: The value of the node, or NULL if it has no value
*/
char* hdf_obj_value (HDF *hdf);
/*
* Function: hdf_set_value - Set the value of a named node
* Description: hdf_set_value will set the value of a named node. All
* of the interstitial nodes which don't exist will be
* created with a value of NULL. Existing nodes are not
* modified. New nodes are created at the end of the list.
* If a list of nodes exceeds FORCE_HASH_AT, then a HASH
* will be created at that level and all of the nodes will
* be added to the hash for faster lookup times.
* The copy of the value will be made which the dataset
* will own.
* Input: hdf -> the pointer to the hdf dataset
* name -> the named node to walk to
* value -> the value to set the node to
* Output: None
* Returns: NERR_NOMEM
*/
NEOERR* hdf_set_value (HDF *hdf, const char *name, const char *value);
/*
* Function: hdf_set_valuef - Set the value of a named node
* Description: hdf_set_valuef is a convenience function that wraps
* hdf_set_value. Due to limitations of C, the fmt is in
* the format "name=value", where we will first format the
* entire string, and then break it at the first (from the
* left) equal sign (=) and use the left portion as the
* name and the right portion as the value. This function
* is somewhat inefficient in that it first allocates the
* full name=value, and then the call to hdf_set_value
* duplicates the value portion, and then we free the
* name=value.
* Currently, we don't strip whitespace from the key or
* value. In the future, this function might work more
* like reading a single line of an HDF string or file,
* allowing for attributes and symlinks to be specified...
* maybe.
* Input: hdf -> the pointer to the hdf dataset
* fmt -> the name=value printf(3) format string
* Output: None
* Returns: NERR_NOMEM
*/
NEOERR* hdf_set_valuef (HDF *hdf, const char *fmt, ...)
ATTRIBUTE_PRINTF(2,3);
NEOERR* hdf_set_valuevf (HDF *hdf, const char *fmt, va_list ap);
/*
* Function: hdf_set_int_value - Set the value of a named node to a number
* Description: hdf_set_int_value is a helper function that maps an
* integer to a string, and then calls hdf_set_value with
* that string
* Input: hdf -> the pointer to the hdf dataset
* name -> the named node to walk to
* value -> the value to set the node to
* Output: None
* Returns: NERR_NOMEM
*/
NEOERR* hdf_set_int_value (HDF *hdf, const char *name, int value);
/*
* Function: hdf_set_copy -> Copy a value from one location in the
* dataset to another
* Description: hdf_set_copy first walks the hdf dataset to the named src
* node, and then copies that value to the named dest node.
* If the src node is not found, an error is raised.
* Input: hdf -> the pointer to the dataset node
* dest -> the name of the destination node
* src -> the name of the source node
* Output: None
* Returns: NERR_NOMEM, NERR_NOT_FOUND
*/
NEOERR* hdf_set_copy (HDF *hdf, const char *dest, const char *src);
/*
* Function: hdf_set_buf - Set the value of a node without duplicating
* the value
* Description: hdf_set_buf is similar to hdf_set_value, except the
* dataset takes ownership of the value instead of making a
* copy of it. The dataset assumes that value was
* malloc'd, since it will attempt to free it when
* hdf_destroy is called
* Input: hdf -> the hdf dataset node
* name -> the name to walk to
* value -> the malloc'd value
* Output: None
* Returns: NERR_NOMEM - unable to allocate a node
*/
NEOERR* hdf_set_buf (HDF *hdf, const char *name, char *value);
/*
* Function: hdf_set_symlink - Set part of the tree to link to another
* Description: hdf_set_symlink creates a link between two sections of
* an HDF dataset. The link is "by name" hence the term
* "symlink". This means that the destination node does
* not need to exist. Any attempt to access the source
* node will cause the function to walk to the dest node,
* and then continue walking from there. Using symlinks
* can "hide" values in the dataset since you won't be able
* to access any children of the linked node directly,
* though dumps and other things which access the data
* structure directly will bypass the symlink. Use this
* feature sparingly as its likely to surprise you.
* Input: hdf -> the dataset node
* src -> the source node name
* dest -> the destination node name (from the top of the
* dataset, not relative names)
* Output: None
* Returns: NERR_NOMEM
*/
NEOERR *hdf_set_symlink (HDF *hdf, const char *src, const char *dest);
/*
* Function: hdf_sort_obj - sort the children of an HDF node
* Description: hdf_sort_obj will sort the children of an HDF node,
* based on the given comparison function.
* This function works by creating an array of the pointers
* for each child object of h, using qsort to sort that
* array, and then re-ordering the linked list of children
* to the new order. The qsort compare function uses a
* pointer to the value in the array, which in our case is
* a pointer to an HDF struct, so your comparison function
* should work on HDF ** pointers.
* Input: h - HDF node
* compareFunc - function which returns 1,0,-1 depending on some
* criteria. The arguments to this sort function
* are pointers to pointers to HDF elements. For
* example:
* int sortByName(const void *a, const void *b) {
* HDF **ha = (HDF **)a;
* HDF **hb = (HDF **)b;
*
* return strcasecmp(hdf_obj_name(*ha), hdf_obj_name(*hb));
* }
*
* Output: None (h children will be sorted)
* Return: NERR_NOMEM
*/
NEOERR *hdf_sort_obj(HDF *h, int (*compareFunc)(const void *, const void *));
/*
* Function: hdf_read_file - read an HDF data file
* Description:
* Input:
* Output:
* Returns: NERR_IO, NERR_NOMEM, NERR_PARSE
*/
NEOERR* hdf_read_file (HDF *hdf, const char *path);
/*
* Function: hdf_write_file - write an HDF data file
* Description:
* Input:
* Output:
* Returns: NERR_IO
*/
NEOERR* hdf_write_file (HDF *hdf, const char *path);
/*
* Function: hdf_write_file_atomic - write an HDF data file atomically
* Description: hdf_write_file_atomic is similar to hdf_write_file,
* except the new file is created with a unique name and
* then rename(2) is used to atomically replace the old
* file with the new file
* Input:
* Output:
* Returns: NERR_IO
*/
NEOERR* hdf_write_file_atomic (HDF *hdf, const char *path);
/*
* Function: hdf_read_string - read an HDF string
* Description:
* Input:
* Output:
* Returns: NERR_NOMEM, NERR_PARSE
*/
NEOERR* hdf_read_string (HDF *hdf, const char *s);
/*
* Function: hdf_read_string_ignore - Read an HDF string and ignore errors
* Description:
* Input:
* Output:
* Returns: NERR_NOMEM
*/
NEOERR* hdf_read_string_ignore (HDF *hdf, const char *s, int ignore);
/*
* Function: hdf_write_string - serialize an HDF dataset to a string
* Description:
* Input:
* Output:
* Returns: NERR_NOMEM
*/
NEOERR* hdf_write_string (HDF *hdf, char **s);
/*
* Function: hdf_dump - dump an HDF dataset to stdout
* Description:
* Input:
* Output:
* Returns:
*/
NEOERR* hdf_dump (HDF *hdf, const char *prefix);
/*
* Function: hdf_dump_format - dump an HDF dataset to FILE *fp
* Description:
* Input:
* Output:
* Returns:
*/
NEOERR* hdf_dump_format (HDF *hdf, int lvl, FILE *fp);
/*
* Function: hdf_dump_str - dump an HDF dataset to STRING
* Description:
* Input:
* Output:
* Returns:
*/
NEOERR* hdf_dump_str(HDF *hdf, const char *prefix, int compact, STRING *str);
/*
* Function: hdf_remove_tree - delete a subtree of an HDF dataset
* Description:
* Input:
* Output:
* Returns:
*/
NEOERR* hdf_remove_tree (HDF *hdf, const char *name);
/*
* Function: hdf_copy - copy part of an HDF dataset to another
* Description: hdf_copy is a deep copy of an HDF tree pointed to by
* src to the named node of dest. dest and src need not be
* part of the same data set
* Input: dest_hdf -> the destination dataset
* name -> the name of the destination node
* src -> the hdf dataset to copy to the destination
* Output: None
* Returns: NERR_NOMEM, NERR_NOT_FOUND
*/
NEOERR* hdf_copy (HDF *dest_hdf, const char *name, HDF *src);
/*
* Function: hdf_search_path - Find a file given a search path in HDF
* Description: hdf_search_path is a convenience/utility function that
* searches for relative filenames in a search path. The
* search path is the list given by the children of
* hdf.loadpaths.
* Input: hdf -> the hdf dataset to use
* path -> the relative path
* full -> a pointer to a _POSIX_PATH_MAX buffer
* Output: full -> the full path of the file
* Returns: NERR_NOT_FOUND if the file wasn't found in the search path
*/
NEOERR* hdf_search_path (HDF *hdf, const char *path, char *full);
/*
* Function: hdf_register_fileload - register a fileload function
* Description: hdf_register_fileload registers a fileload function that
* overrides the built-in function. The built-in function
* uses hdf_search_path and ne_file_load (based on stat/open/read)
* to find and load the file on every hdf_read_file (including
* #include). You can override this function if you wish to provide
* other file search functions, or load the hdf file
* from an in-memory cache, etc.
* Input: hdf - pointer to a head HDF node
* ctx - pointer that is passed to the HDFFILELOAD function when called
* fileload - a HDFFILELOAD function
* Output: None
* Return: None
*
*/
void hdf_register_fileload(HDF *hdf, void *ctx, HDFFILELOAD fileload);
__END_DECLS
#endif /* __NEO_HDF_H_ */