From 40adb027dd2a2ef055e0aa9fc0a3bf40eed14ebb Mon Sep 17 00:00:00 2001
From: Michael R Sweet
Mini-XML supports custom data types via global load and save +callbacks. Only a single set of callbacks can be active at any +time, however your callbacks can store additional information in +order to support multiple custom data types as needed. The +MXML_CUSTOM node type identifies custom data nodes.
+ +The load callback receives a pointer to the current data node +and a string of opaque character data from the XML source with +character entities converted to the corresponding UTF-8 +characters. For example, if we wanted to support a custom +date/time type whose value is encoded as "yyyy-mm-ddThh:mm:ssZ" +(ISO format), the load callback would look like the +following:
+ ++ typedef struct + { + unsigned year, /* Year */ + month, /* Month */ + day, /* Day */ + hour, /* Hour */ + minute, /* Minute */ + second; /* Second */ + time_t unix; /* UNIX time value */ + } iso_date_time_t; + + int /* I - 0 on success, -1 on error */ + load_custom(mxml_node_t *node, /* I - Node */ + const char *data) /* I - Value */ + { + iso_date_time_t *dt; /* Date/time value */ + struct tm tmdata; /* UNIX time data */ + + + /* + * Allocate data structure... + */ + + dt = calloc(1, sizeof(iso_date_time_t)); + + /* + * Try reading 6 unsigned integers from the data string... + */ + + if (sscanf(data, "%u-%u-%uT%u:%u:%uZ", + &(dt->year), &(dt->month), &(dt->day), + &(dt->hour), &(dt->minute), &(dt->second)) != 6) + { + /* + * Unable to read numbers, free the data structure and return an + * error... + */ + + free(dt); + + return (-1); + } + + /* + * Range check values... + */ + + if (dt->month < 1 || dt->month > 12 || + dt->day < 1 || dt->day > 31 || + dt->hour < 0 || dt->hour > 23 || + dt->minute < 0 || dt->minute > 59 || + dt->second < 0 || dt->second > 59) + { + /* + * Date information is out of range... + */ + + free(dt); + + return (-1); + } + + /* + * Convert ISO time to UNIX time in seconds... + */ + + tmdata.tm_year = dt->year - 1900; + tmdata.tm_mon = dt->month - 1; + tmdata.tm_day = dt->day; + tmdata.tm_hour = dt->hour; + tmdata.tm_min = dt->minute; + tmdata.tm_sec = dt->second; + + dt->unix = gmtime(&tmdata); + + /* + * Assign custom node data and destroy function pointers... + */ + + node->value.custom.data = dt; + node->value.custom.destroy = free; + + /* + * Return with no errors... + */ + + return (0); + } ++ +
The function itself can return 0 on success or -1 if it is +unable to decode the custom data or the data contains an error. +Custom data nodes contain a void pointer to the +allocated custom data for the node and a pointer to a destructor +function which will free the custom data when the node is +deleted.
+ +The save callback receives the node pointer and returns an +allocated string containing the custom data value. The following +save callback could be used for our ISO date/time type:
+ ++ char * /* I - Allocated string */ + save_custom(mxml_node_t *node) /* I - Node */ + { + char data[255]; /* Data string */ + iso_date_time_t *dt; /* ISO date/time pointer */ + + + dt = (iso_date_time_t *)node->custom.data; + + snprintf(data, sizeof(data), "%04u-%02u-%02uT%02u:%02u:%02uZ", + dt->year, dt->month, dt->day, dt->hour, + dt->minute, dt->second); + + return (strdup(data)); + } ++ +
You register the callback functions using the mxmlSetCustomHandlers() +function:
+ ++ mxmlSetCustomHandlers(load_custom, save_custom); ++ +
All of the examples so far have concentrated on creating and diff --git a/doc/intro.html b/doc/intro.html index 43572d1..2e5b3cc 100644 --- a/doc/intro.html +++ b/doc/intro.html @@ -1,6 +1,6 @@
-This programmers manual describes Mini-XML version 2.0, a +
This programmers manual describes Mini-XML version 2.1, a small XML parsing library that you can use to read and write XML and XML-like data files in your application without requiring large non-standard libraries. Mini-XML only requires an ANSI C diff --git a/doc/mxml.html b/doc/mxml.html index dc58c82..1e47204 100644 --- a/doc/mxml.html +++ b/doc/mxml.html @@ -1,7 +1,7 @@
-This programmers manual describes Mini-XML version 2.0, a small XML +
This programmers manual describes Mini-XML version 2.1, a small XML parsing library that you can use to read and write XML and XML-like data files in your application without requiring large non-standard libraries. Mini-XML only requires an ANSI C compatible compiler (GCC @@ -808,7 +816,139 @@ MXML_WS_BEFORE_CLOSE, or MXML_WS_AFTER_CLOSE. The callback mxmlSaveFile(tree, fp, whitespace_cb); fclose(fp); -
Mini-XML supports custom data types via global load and save + callbacks. Only a single set of callbacks can be active at any time, + however your callbacks can store additional information in order to + support multiple custom data types as needed. The MXML_CUSTOM + node type identifies custom data nodes.
+The load callback receives a pointer to the current data node and a + string of opaque character data from the XML source with character + entities converted to the corresponding UTF-8 characters. For example, + if we wanted to support a custom date/time type whose value is encoded + as "yyyy-mm-ddThh:mm:ssZ" (ISO format), the load callback would look + like the following:
++ typedef struct + { + unsigned year, /* Year */ + month, /* Month */ + day, /* Day */ + hour, /* Hour */ + minute, /* Minute */ + second; /* Second */ + time_t unix; /* UNIX time value */ + } iso_date_time_t; + + int /* I - 0 on success, -1 on error */ + load_custom(mxml_node_t *node, /* I - Node */ + const char *data) /* I - Value */ + { + iso_date_time_t *dt; /* Date/time value */ + struct tm tmdata; /* UNIX time data */ + + + /* + * Allocate data structure... + */ + + dt = calloc(1, sizeof(iso_date_time_t)); + + /* + * Try reading 6 unsigned integers from the data string... + */ + + if (sscanf(data, "%u-%u-%uT%u:%u:%uZ", + &(dt->year), &(dt->month), &(dt->day), + &(dt->hour), &(dt->minute), &(dt->second)) != 6) + { + /* + * Unable to read numbers, free the data structure and return an + * error... + */ + + free(dt); + + return (-1); + } + + /* + * Range check values... + */ + + if (dt->month <1 || dt->month > 12 || + dt->day <1 || dt->day > 31 || + dt->hour <0 || dt->hour > 23 || + dt->minute <0 || dt->minute > 59 || + dt->second <0 || dt->second > 59) + { + /* + * Date information is out of range... + */ + + free(dt); + + return (-1); + } + + /* + * Convert ISO time to UNIX time in seconds... + */ + + tmdata.tm_year = dt->year - 1900; + tmdata.tm_mon = dt->month - 1; + tmdata.tm_day = dt->day; + tmdata.tm_hour = dt->hour; + tmdata.tm_min = dt->minute; + tmdata.tm_sec = dt->second; + + dt->unix = gmtime(&tmdata); + + /* + * Assign custom node data and destroy function pointers... + */ + + node->value.custom.data = dt; + node->value.custom.destroy = free; + + /* + * Return with no errors... + */ + + return (0); + } ++
The function itself can return 0 on success or -1 if it is unable to + decode the custom data or the data contains an error. Custom data nodes + contain a void pointer to the allocated custom data for the + node and a pointer to a destructor function which will free the custom + data when the node is deleted.
+The save callback receives the node pointer and returns an allocated + string containing the custom data value. The following save callback + could be used for our ISO date/time type:
++ char * /* I - Allocated string */ + save_custom(mxml_node_t *node) /* I - Node */ + { + char data[255]; /* Data string */ + iso_date_time_t *dt; /* ISO date/time pointer */ + + + dt = (iso_date_time_t *)node->custom.data; + + snprintf(data, sizeof(data), "%04u-%02u-%02uT%02u:%02u:%02uZ", + dt->year, dt->month, dt->day, dt->hour, + dt->minute, dt->second); + + return (strdup(data)); + } ++
You register the callback functions using the +mxmlSetCustomHandlers() function:
++ mxmlSetCustomHandlers(load_custom, save_custom); ++
All of the examples so far have concentrated on creating and loading new XML data nodes. Many applications, however, need to manipulate or change the nodes during their operation, so Mini-XML provides functions @@ -825,7 +965,7 @@ mxmlSetInteger(), mxmlSetOpaque() mxmlSetText(node, 1, "new"); -
The mxmlNewTextf() and mxmlSetTextf() functions create and change text nodes, respectively, using printf-style format strings and arguments. @@ -836,7 +976,7 @@ mxmlSetInteger(), mxmlSetOpaque() node = mxmlNewTextf(node, 1, "%s/%s", path, filename); -
Mini-XML provides functions for managing indices of nodes. The current implementation provides the same functionality as the mxmlFindElement(). The advantage of using an index is that @@ -1647,6 +1787,12 @@ mxmldoc to generate correct documentation for the code. Single line
First node or NULL if the string has errors.
+ +Create a new custom data node. The new custom node is added to the + end of the specified parent's child list. The constant MXML_NO_PARENT + can be used to specify that the new element node has no parent. NULL + can be passed when the data in the node is not dynamically allocated or + is separately managed.
++mxml_node_t * +mxmlNewCustom( + mxml_node_t * parent, + void * data, + void (*destroy)(void *)); ++
Name | Description |
---|---|
parent | Parent node or MXML_NO_PARENT |
data | Pointer to data |
(*destroy)(void *) | Function to destroy data | +
New node
+Size of string
+ +Set the data and destructor of a custom data node. The node is not + changed if it is not a custom node.
++int +mxmlSetCustom( + mxml_node_t * node, + void * data, + void (*destroy)(void *)); ++
Name | Description |
---|---|
node | Node to set |
data | New data pointer |
(*destroy)(void *) | New destructor function |
0 on success, -1 on failure
+ + +Set the handling functions for custom data. The load function accepts + a node pointer and a data string and must return 0 on success and + non-zero on error. The save function accepts a node pointer and must + return a malloc'd string on success and NULL on error.
++void +mxmlSetCustomHandlers( + mxml_custom_load_cb_t load, + mxml_custom_save_cb_t save); ++
Name | Description |
---|---|
load | Load function |
save | Save function |
Nothing.
+An XML custom value.
++struct mxml_custom_s +{ + void * data; +}; ++
Name | Description |
---|---|
data | Pointer to (allocated) custom data |
An XML custom value.
++typedef struct mxml_custom_s mxml_custom_t; ++
union mxml_value_u { + mxml_custom_t custom; mxml_element_t element; int integer; char * opaque; @@ -3203,6 +3473,7 @@ union mxml_value_u+ ++ Name Description custom Custom data element Element integer Integer number @@ -3213,9 +3484,29 @@ union mxml_value_u opaque Opaque string Variables
+static mxml_custom_load_cb_t mxml_custom_load_cb = NULL; ++ + +
Local functions...
++static mxml_custom_save_cb_t mxml_custom_save_cb = NULL; ++