/* * Copyright (c) 2001-2002 Packet Design, LLC. * All rights reserved. * * Subject to the following obligations and disclaimer of warranty, * use and redistribution of this software, in source or object code * forms, with or without modifications are expressly permitted by * Packet Design; provided, however, that: * * (i) Any and all reproductions of the source or object code * must include the copyright notice above and the following * disclaimer of warranties; and * (ii) No rights are granted, in any manner or form, to use * Packet Design trademarks, including the mark "PACKET DESIGN" * on advertising, endorsements, or otherwise except as such * appears in the above copyright notice or in the software. * * THIS SOFTWARE IS BEING PROVIDED BY PACKET DESIGN "AS IS", AND * TO THE MAXIMUM EXTENT PERMITTED BY LAW, PACKET DESIGN MAKES NO * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING * THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, * OR NON-INFRINGEMENT. PACKET DESIGN DOES NOT WARRANT, GUARANTEE, * OR MAKE ANY REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS * OF THE USE OF THIS SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, * RELIABILITY OR OTHERWISE. IN NO EVENT SHALL PACKET DESIGN BE * LIABLE FOR ANY DAMAGES RESULTING FROM OR ARISING OUT OF ANY USE * OF THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, PUNITIVE, OR CONSEQUENTIAL * DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, LOSS OF * USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF * THE USE OF THIS SOFTWARE, EVEN IF PACKET DESIGN IS ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. * * Author: Archie Cobbs */ #ifndef _PDEL_STRUCTS_STRUCTS_H_ #define _PDEL_STRUCTS_STRUCTS_H_ /********************************************************************* STRUCTS TYPES ------------- A "structs type" is a descriptor for a C data structure that allows access to the data structure in an automated fashion. For example, once a data structure is described by a associated structs type, the following operations can be performed automatically on any instance of the data structure: o Initialization to default value o Comparison of two instances for equality o "Deep" copying (aka. "cloning") o Access to arbitrary sub-fields by name (aka. "introspection") o Conversion to/from ASCII (primitive types) o Import/export from/to XML, with precise input validation, automating conversion between internal and external formats o Conversion into XML-RPC request or reply o Import/export from/to a machine independent binary encoding o Inserting, deleting, and extending elements in arrays In addition, any required dynamic memory allocation and free'ing is handled automatically. Supported data structures include the normal primitive C types, plus arrays, structures, unions, and pointers to supported types, as well as some other useful types such as opaque binary data, IP address, regular expression, etc. User-defined types may be added as well. Put simply, a structs type describes how to interpret a region of memory so that the operations listed above may be peformed in an automated fashion. Arrays must be defined using the "structs_array" structure. Unions must be defined using the "structs_union" structure. Nested items in a data structure can be identified by names, where each name component is either the name of a structure or union field, or an array index. Name components are separated by dots. For example, "foo.list.12" (see below). EXAMPLE ------- Consider the C data structure defined by "struct janfu": struct fubar { struct structs_array list; // array of strings }; struct janfu { u_int16_t value; // 16 bit hex value struct fubar *foo; // fubar structure }; A memory region containing a 'struct janfu' would be described in C code like so: // Descriptor for field 'list' in 'struct fubar': array of strings static const struct structs_type list_array_type = STRUCTS_ARRAY_TYPE(&structs_type_string, "list_elem", "list_elem"); // Descriptor for 'struct fubar' static const struct structs_field fubar_fields[] = { STRUCTS_STRUCT_FIELD(fubar, list, &list_array_type), STRUCTS_STRUCT_FIELD_END }; static const struct structs_type fubar_type = STRUCTS_STRUCT_TYPE(fubar, &fubar_fields); // Descriptor for a variable of type 'struct fubar *' static const struct structs_type foo_ptr_type = STRUCTS_POINTER_TYPE(&fubar_type, "struct fubar"); // Descriptor for 'struct janfu' static const struct structs_field janfu_fields[] = { STRUCTS_STRUCT_FIELD(janfu, value, &structs_type_uint16), STRUCTS_STRUCT_FIELD(janfu, foo, &foo_ptr_type), STRUCTS_STRUCT_FIELD_END }; static const struct structs_type janfu_type = STRUCTS_STRUCT_TYPE(janfu, &janfu_fields); The type "janfu_type" describes a 'struct janfu' data structure. Then "foo.list.12" refers to the 12th item in the "list" array (assuming it actually exists), counting from zero. It's value could be accessed like this: void print_list_12(const struct janfu *j) { char *s; s = structs_get_string(&janfu_type, "foo.list.12", j, NULL); if (s == NULL) { fprintf(stderr, "structs_get_string: %s: %s", "foo.list.12", strerror(errno)); exit(1); } printf("foo.list.12 = %s\n", s); free(s); } An instance of a "struct janfu", if output as XML, might look like this: 0x1234 first list element second list element third list element A function call that would produce the above output is: void dump_janfu(struct janfu *j) { structs_xml_output(&janfu_type, "janfu", NULL, j, stdout, NULL, 0); } *********************************************************************/ /* Name component separator */ #define STRUCTS_SEPARATOR '.' struct structs_type; /* For representing arbitrary binary data */ struct structs_data { u_int length; /* number of bytes */ u_char *data; /* bytes */ }; /********************************************************************* STRUCTS TYPE METHODS *********************************************************************/ /* * The structs type "init" method. * * The region of memory pointed to by "data" must be at least * type->size bytes and should be uninitialized (i.e., it should * not contain a valid instance of the type). * * This method should initialize the memory with a valid instance * of the type, setting this new instance to be equal to the default * value for the type, and allocating any resources required for the * instance in the process. * * Returns 0 if successful, or -1 (and sets errno) if there was an error. */ typedef int structs_init_t(const struct structs_type *type, void *data); /* * The structs type "copy" method. * * The region of memory pointed to by "from" contains a valid instance * of the type described by "type". * * The region of memory pointed to by "to" is uninitialized. * * This method should initialize "to" with a "deep" copy of the * data structure pointed to by "from". The copy must be equal * (according to the "equal" method) to the original. * * Returns 0 if successful, or -1 (and sets errno) if there was an error. */ typedef int structs_copy_t(const struct structs_type *type, const void *from, void *to); /* * The structs type "equal" method. * * The regions of memory pointed to by "data1" and "data2" contain valid * instances of the type described by "type". * * Returns 1 if "data1" and "data2" are equal (by a "deep" comparison), or * 0 if not, or -1 (and sets errno) if there was an error. */ typedef int structs_equal_t(const struct structs_type *type, const void *data1, const void *data2); /* * The structs type "ascify" method. * * The region of memory pointed to by "data" contains a valid instance * of the type described by "type". * * The method should convert this instance into a NUL-terminated * ASCII string stored in a dynamically allocated buffer that is * allocated with the memory type described by "mtype". * * The ASCII string must be unique, in that it can be used as input * to the "binify" method to create an instance equal (according to the * "equal" method) to the original. * * Returns the ASCII string buffer if successful, or NULL * (and sets errno) if error. */ typedef char *structs_ascify_t(const struct structs_type *type, const char *mtype, const void *data); /* * The structs type "binify" method. * * The region of memory pointed to by "data" is uninitialized. * * The variable "ascii" points to a NUL-terminated string. This method * should use the contents of the string to create a new instace of * the type in the memory pointed to by "data". * * Returns 0 if successful, or -1 if there was an error. In the latter * case, either errno should be set, or a more informative error string * may be printed (including NUL) into "ebuf", which has total size "emax" * (see snprintf(3)). * * This method must be robust in the face of bogus data in "ascii"; * it must return an error if "ascii" is invalid. */ typedef int structs_binify_t(const struct structs_type *type, const char *ascii, void *data, char *ebuf, size_t emax); /* * The structs type "encode" method. * * The region of memory pointed to by "data" contains a valid instance * of the type described by "type". * * This method should encode the data structure into a host order * independent, self-delimiting sequence of bytes. The sequence must be * stored in a dynamically allocated buffer allocated with the memory type * described by "mtype", and "code" must be filled in with the buffer's * pointer and length. * * The binary sequence must be unique, in that it can be used as input * to the "decode" method to create an instance equal (according to the * "equal" method) to the original. * * Returns 0 if successful, or -1 (and sets errno) if there was an error. */ typedef int structs_encode_t(const struct structs_type *type, const char *mtype, struct structs_data *code, const void *data); /* * The structs type "decode" method. * * The region of memory pointed to by "data" is uninitialized. * * The "code" parameter points to a sequence of "cmax" binary data bytes. * This method should decode the binary data and use it to reconstruct * an instance of "type" in the memory region pointed to by "data". * * Returns the number of bytes consumed in the decoding process if * successful, or -1 (and sets errno) if there was an error. In the * latter case, either errno should be set, or a more informative error * string may be printed (including NUL) into "ebuf", which has total * size "emax" (see snprintf(3)). */ typedef int structs_decode_t(const struct structs_type *type, const u_char *code, size_t cmax, void *data, char *ebuf, size_t emax); /* * The structs type "uninit" method. * * The region of memory pointed to by "data" contains a valid instance * of the type described by "type". * * This method should free any resources associated with the instance * pointed to by "data". Upon return, the memory pointed to by "data" * may be no longer used or referenced. */ typedef void structs_uninit_t(const struct structs_type *type, void *data); /********************************************************************* STRUCTS TYPE DEFINITION *********************************************************************/ /* * This structure defines a structs type. */ struct structs_type { size_t size; /* size of an instance */ const char *name; /* human informative name */ int tclass; /* see classes listed below */ structs_init_t *init; /* type "init" method */ structs_copy_t *copy; /* type "copy" method */ structs_equal_t *equal; /* type "equal" method */ structs_ascify_t *ascify; /* type "ascify" method */ structs_binify_t *binify; /* type "binify" method */ structs_encode_t *encode; /* type "encode" method */ structs_decode_t *decode; /* type "decode" method */ structs_uninit_t *uninit; /* type "uninit" method */ union { /* type specific arguments */ const void *v; const char *s; int i; } args[3]; }; /* Classes of types */ #define STRUCTS_TYPE_PRIMITIVE 0 #define STRUCTS_TYPE_POINTER 1 #define STRUCTS_TYPE_ARRAY 2 #define STRUCTS_TYPE_FIXEDARRAY 3 #define STRUCTS_TYPE_STRUCTURE 4 #define STRUCTS_TYPE_UNION 5 /********************************************************************* GENERIC TYPE METHODS *********************************************************************/ __BEGIN_DECLS /* These operate on simple block regions of memory */ extern structs_init_t structs_region_init; extern structs_copy_t structs_region_copy; extern structs_equal_t structs_region_equal; extern structs_encode_t structs_region_encode; extern structs_decode_t structs_region_decode; extern structs_encode_t structs_region_encode_netorder; extern structs_decode_t structs_region_decode_netorder; /* These always return an error with errno set to EOPNOTSUPP */ extern structs_init_t structs_notsupp_init; extern structs_copy_t structs_notsupp_copy; extern structs_equal_t structs_notsupp_equal; extern structs_ascify_t structs_notsupp_ascify; extern structs_binify_t structs_notsupp_binify; extern structs_encode_t structs_notsupp_encode; extern structs_decode_t structs_notsupp_decode; /* This copies any type that is ascifyable by converting to ASCII and back */ extern structs_copy_t structs_ascii_copy; /* This does nothing */ extern structs_uninit_t structs_nothing_free; __END_DECLS /********************************************************************* BASIC STRUCTS FUNCTIONS *********************************************************************/ __BEGIN_DECLS /* * Initialize an item in a data structure to its default value. * This assumes the item is not already initialized. * * If "name" is NULL or empty string, the entire structure is initialied. * * Returns 0 if successful, otherwise -1 and sets errno. */ extern int structs_init(const struct structs_type *type, const char *name, void *data); /* * Reset an item in a data structure to its default value. * This assumes the item is already initialized. * * If "name" is NULL or empty string, the entire structure is reset. * * Returns 0 if successful, otherwise -1 and sets errno. */ extern int structs_reset(const struct structs_type *type, const char *name, void *data); /* * Free an item in a data structure, returning it to its uninitialized state. * This function can be thought of as the opposite of structs_init(). * * If "name" is NULL or empty string, the entire structure is free'd. * * Returns 0 if successful, otherwise -1 and sets errno. */ extern int structs_free(const struct structs_type *type, const char *name, void *data); /* * Compare two data structures of the same type for equality. * * If "name" is NULL or empty string, the entire structures are compared. * Otherwise, the "name" sub-elements of both structures are compared. * * Returns zero or one (for not equal or equal, respectively), * or -1 (and sets errno) if there was an error. */ extern int structs_equal(const struct structs_type *type, const char *name, const void *data1, const void *data2); /* * Find an item in a data structure structure. * * If "name" is NULL or empty string, "type" is returned and "datap" * is unmodified. * * If "set_union" is true, then any unions in the path will be reset * so that their fields agree with "name" in the case that "name" contains * a different field name than is currently set for that union. However, * these changes will only occur if "name" is actually found. * * Returns the type of the item and points *datap at it, or NULL * (and sets errno) if there was an error. */ extern const struct structs_type *structs_find( const struct structs_type *type, const char *name, void **datap, int set_union); /* * Get a copy of an item from a data structure. * * If "name" is NULL or empty string, the entire structure is copied. * * It is assumed that "to" points to a region big enough to hold * the copy of the item. "to" will not be free'd first, so it should * not already be initialized. * * Note: "name" is only applied to "from". The structs types of * "from." and "to" must be the same. * * Returns 0 if successful, otherwise -1 and sets errno. */ extern int structs_get(const struct structs_type *type, const char *name, const void *from, void *to); /* * Set an item in a data structure. * * If "name" is NULL or empty string, the entire structure is copied. * * It is assumed that "to" is already initialized. * * Note: "name" is only applied to "to". The structs types of * "from" and "to." must be the same. * * Returns 0 if successful, otherwise -1 and sets errno. */ extern int structs_set(const struct structs_type *type, const void *from, const char *name, void *to); /* * Get the ASCII form of an item, in a string allocated with the * memory type described by "mtype". * * The caller is reponsible for freeing the returned string. * * Returns the ASCII string if successful, otherwise NULL and sets errno. */ extern char *structs_get_string(const struct structs_type *type, const char *name, const void *data, const char *mtype); /* * Set the value of an item in a data structure from an ASCII string. * * The referred to item must be of a type that supports ASCII encoding, * and is assumed to be already initialized. * * Returns 0 if successful, otherwise -1 and sets errno. */ extern int structs_set_string(const struct structs_type *type, const char *name, const char *ascii, void *data, char *ebuf, size_t emax); /* * Get the binary encoded form of an item, put into a buffer * allocated with memory type "mtype" and described by "code". * * The caller is reponsible for freeing the allocated bytes. * * Returns 0 if successful, otherwise -1 and sets errno. */ extern int structs_get_binary(const struct structs_type *type, const char *name, const void *data, const char *mtype, struct structs_data *code); /* * Set an item's value from its binary encoded value. * * The referred to item is assumed to be already initialized. * * Returns 0 if successful, otherwise -1 and sets errno. */ extern int structs_set_binary(const struct structs_type *type, const char *name, const struct structs_data *code, void *data, char *ebuf, size_t emax); /* * Traverse a structure, returning the names of all primitive fields * (i.e., the "leaves" of the data structure). * * The number of items is returned, or -1 and errno if there was an error. * The caller must free the list by freeing each string in the array, * and then the array itself; all memory is allocated using memory type * "mtype". * * If "type" is a primitive type, a list of length one is returned * containing an empty string. */ extern int structs_traverse(const struct structs_type *type, const void *data, char ***listp, const char *mtype); __END_DECLS #endif /* _PDEL_STRUCTS_STRUCTS_H_ */