#include <stdio.h> #include <string.h> #include "xmlobj.h" |
<link id="person"> <rec id="John"> <field id="key">John</field> <field id="name">Smith</field> </rec> <rec id="Sandy"> <field id="key">Sandy</field> <field id="name">Jones</field> </rec> </link>or it can be stored flat in the record, like this:
<person> <field id="key">John</field> <field id="name">Smith</field> </person> <person> <field id="key">Sandy</field> <field id="name">Jones</field> </person>Only the first allows keyed addressing, but in both instances, the field name "person(1).name" will return a value of "Jones". See? The way we do this is by getting fancy with xmlobj_field and xmlobj_is_field. Dotted names are easy -- we just call ourselves recursively to find the parent, and then use that as a context for a local name. Finding list elements is another question -- since they can either be on the top level or one down in subrecords, we have to be rather careful. Take a look. One complexity due to my having attacked this sort of thing more than once: a link field (i.e. a list of subrecords) can be a field element with an id, or a like-named element, or a "link" element with an id attribute. Thus if we're looking for addr(1), we have to look for "addr" elements, "link" elements with id=addr, and "field" elements with id=addr. Similarly, subrecords within link lists can either be "link-to" elements or "rec" elements. Sorry it's so messy, but otherwise I'd have to clean up some other projects and would surely screw something up. It's easier (for me) to make you work harder.
static int _field_match (XML * field, char * fieldname, char * key) { if (xml_is (field, fieldname) || (xml_is (field, "field") || xml_is (field, "link")) && !strcmp (fieldname, xml_attrval (field, "id"))) { /* Provisional match. */ return 1; } return 0; } static int _subrec_match (XML * field, char * fieldname, char * key) { if (xml_is (field, "rec") || xml_is (xml_parent (field), "link")) { /* Provisional match. */ if (!key || !strcmp (key, xml_attrval (field, "id"))) { return 1; } } return 0; } XMLAPI XML * xmlobj_is_field (XML * obj, XML * class, const char * fieldname) { XML * field; XML * subrec; int offset = 0; int indexing = 0; int subrec_hit; char * key = NULL; char * copy = NULL; char * localfield; char * mark; XML * context; if (!obj) return (NULL); if (strcspn (fieldname, ".") < strlen (fieldname)) { /* If we have dotted addressing, split on the last dot. */ copy = xml_strdup (fieldname); localfield = strrchr (copy, '.'); if (localfield) { *localfield = '\0'; localfield++; context = xmlobj_is_field (obj, class, copy); } if (!context) { free (copy); return NULL; } } else { context = obj; localfield = (char *) fieldname; } /* The local field may contain indexing. If so, split on the indexing character. */ if (mark = strchr (localfield, '(')) { if (!copy) { copy = xml_strdup (localfield); localfield = copy; mark = strchr (localfield, '('); } *mark = '\0'; mark++; offset = atoi (mark); indexing = 1; if (offset < 0) { /* Negative index is the signal to create a new subrecord. */ if (copy) free (copy); return NULL; } } else if (mark = strchr (localfield, '[')) { if (!copy) { copy = xml_strdup (localfield); localfield = copy; mark = strchr (localfield, '['); } *mark = '\0'; key = mark + 1; mark = strchr (key, ']'); if (mark) *mark = '\0'; indexing = 1; } for (field = xml_firstelem (context); field; field = xml_nextelem (field)) { if (_field_match (field, localfield, key)) { /* On match, check subrecs with the same logic, to one level. */ subrec_hit = 0; if (indexing) { for (subrec = xml_firstelem (field); subrec; subrec = xml_nextelem (subrec)) { if (_subrec_match (subrec, localfield, key)) { if (!offset) { if (copy) free (copy); return (subrec); } offset --; subrec_hit = 1; } } } /* No subrecs qualified, so if the main record *does*, do the right thing. */ if (!subrec_hit && !xml_is (field, "link")) { if (!key || !strcmp (key, xml_attrval (field, "id"))) { if (!offset) { if (copy) free (copy); return (field); } offset --; } } } } if (copy) free (copy); return NULL; } XMLAPI void xmlobj_unset (XML * obj, XML * class, const char * fieldname) { XML * field; field = xmlobj_is_field (obj, class, fieldname); if (!field) return; xml_delete_pretty (field); } XMLAPI XML * xmlobj_field (XML * obj, XML * class, const char * fieldname) { XML * field; XML * fdef = NULL; char * copy = NULL; char * localfield; XML * context; XML * subrec = NULL; int offset = 0; int indexing = 0; int subrec_hit; char * key = NULL; char * mark; if (!obj) return (NULL); if (strcspn (fieldname, ".") < strlen (fieldname)) { /* If we have dotted addressing, split on the last dot. */ copy = xml_strdup (fieldname); localfield = strrchr (copy, '.'); if (localfield) { *localfield = '\0'; localfield++; context = xmlobj_field (obj, class, copy); } if (!context) { free (copy); return NULL; } } else { context = obj; localfield = (char *) fieldname; } if (!(field = xmlobj_is_field (context, class, localfield))) { /* The local field may contain indexing. If so, split on the indexing character. */ if (mark = strchr (localfield, '(')) { if (!copy) { copy = xml_strdup (localfield); localfield = copy; mark = strchr (localfield, '('); } *mark = '\0'; mark++; offset = atoi (mark); indexing = 1; } else if (mark = strchr (localfield, '[')) { if (!copy) { copy = xml_strdup (localfield); localfield = copy; mark = strchr (localfield, '['); } *mark = '\0'; key = mark + 1; mark = strchr (key, ']'); if (mark) *mark = '\0'; indexing = 1; } if (class) fdef = xml_locf (class, ".field[%s]", fieldname); /* TODO: need to find defn for complex values. */ if (*xml_attrval (fdef, "element")) { field = xml_create (localfield); if (key) xml_set (field, "id", key); } else { if (indexing) { field = xml_locf (context, ".link[%s]", localfield); if (!field) { field = xml_create ("link"); xml_set (field, "id", localfield); xml_append_pretty (context, field); } subrec = xml_create ("rec"); xml_append_pretty (field, subrec); if (key) xml_set (subrec, "id", key); } else { field = xml_create ("field"); xml_set (field, "id", localfield); } } if (*xml_attrval (fdef, "ver-keep")) xml_set (subrec ? subrec : field, "ver", "0"); if (!subrec) xml_append_pretty (context, field); } return subrec ? subrec : field; } |
XMLAPI char * xmlobj_get (XML * obj, XML * class, const char * field) { XML * mark; char * val; char * copy; char * localfield; if (!obj) return (NULL); mark = xmlobj_is_field (obj, class, field); if (mark) return xmlobj_get_direct (mark); if (strrchr (field, '.')) { copy = xml_strdup (field); localfield = strrchr (copy, '.'); *localfield = '\0'; localfield++; mark = xmlobj_is_field (obj, class, copy); if (mark) { val = (char *) xml_attrval (mark, localfield); free (copy); if (*val) return (xml_strdup (val)); } else { free (copy); } } else { val = (char *) xml_attrval (obj, field); if (*val) return (xml_strdup (val)); } mark = xml_search (class, "field", "id", field); if (mark) { val = (char *) xml_attrval (mark, "default"); if (*val) return (xml_strdup (val)); } if (!strcmp (field, "key")) { /* Special handling for the pseudofield "key" */ return xmlobj_getkey (obj, class); } return (NULL); } XMLAPI char * xmlobj_get_direct (XML * field) { char * val; const char * ver; XML * v; if (!field) return NULL; ver = xml_attrval (field, "ver"); if (*ver) { v = xmlobj_ver_direct (field, ver); if (v) field = v; } val = (char *) xml_attrval (field, "value"); if (*val) return (xml_strdup (val)); return xml_stringcontenthtml (field); } XMLAPI int xmlobj_getnum (XML * obj, XML * class, const char * field) { int retval; char * val = xmlobj_get (obj, class, field); if (!val) return 0; retval = atoi (val); free (val); return (retval); } |
XMLAPI int xmlobj_set (XML * obj, XML * class, const char * field, const char * value) { XML * mark; int newver = 0; if (!obj) return 0; mark = xmlobj_field (obj, class, field); if (*xml_attrval (mark, "ver")) newver = xml_attrvalnum (mark, "ver") + 1; if (newver) { xml_setnum (mark, "ver", newver); mark = xmlobj_ver_direct (mark, xml_attrval (mark, "ver")); } xmlobj_set_direct (mark, value); /* TODO: if (newver) then get and apply retention rule. */ } XMLAPI int xmlobj_set_direct (XML * field, const char * value) { xml_unset (field, "value"); xml_replacecontent (field, xml_createtext (value ? value : "")); return 1; } XMLAPI int xmlobj_set_nodup (XML * obj, XML * class, const char * field, char * value) { XML * mark; int newver = 0; if (!obj) return 0; mark = xmlobj_field (obj, class, field); if (*xml_attrval (mark, "ver")) newver = xml_attrvalnum (mark, "ver") + 1; if (newver) { xml_setnum (mark, "ver", newver); mark = xmlobj_ver_direct (mark, xml_attrval (mark, "ver")); } xmlobj_set_direct_nodup (mark, value); /* TODO: if (newver) then get and apply retention rule. */ return 1; } XMLAPI int xmlobj_set_direct_nodup (XML * field, char * value) { xml_unset (field, "value"); xml_replacecontent (field, xml_createtext_nodup (value ? value : xml_strdup (""))); return 1; } XMLAPI int xmlobj_set_elem (XML * obj, XML * class, const char * field, const char * value) /* TODO: this doesn't do complex values */ { XML * mark; if (!obj) return 0; mark = xmlobj_is_field (obj, class, field); if (!mark) { mark = xml_create (field); xml_append_pretty (obj, mark); } xml_replacecontent (mark, xml_createtext (value ? value : "")); return 1; } XMLAPI int xmlobj_set_elem_nodup (XML * obj, XML * class, const char * field, char * value) { XML * mark; if (!obj) return 0; mark = xmlobj_is_field (obj, class, field); if (!mark) { mark = xml_create (field); xml_append_pretty (obj, mark); } xml_replacecontent (mark, xml_createtext_nodup (value ? value : xml_strdup(""))); return 1; } XMLAPI int xmlobj_setnum (XML * obj, XML * class, const char * field, int value) { XML * mark; char buf[sizeof(int)*3]; if (!obj) return 0; mark = xml_search (obj, "field", "id", field); if (!mark) { mark = xml_create ("field"); xml_set (mark, "id", field); xml_append_pretty (obj, mark); } sprintf (buf, "%d", value); xml_replacecontent (mark, xml_createtext (buf)); return 1; } |
XMLAPI char * xmlobj_format (XML * obj, XML * class, const char * format) { char * mark; int len; char * val; XML * s = xml_create ("s"); xml_set (s, "s", ""); mark = strchr (format, '['); while (mark) { xml_attrncat (s, "s", format, mark - format); format = mark + 1; xml_set (s, "name", ""); mark = strchr (format, ']'); if (mark) len = mark - format; else len = strlen (format); xml_attrncat (s, "name", format, len); val = xmlobj_get (obj, class, xml_attrval (s, "name")); if (val) { xml_attrcat (s, "s", val); free (val); } format += len; if (*format) format++; mark = strchr (format, '['); } xml_attrcat (s, "s", format); mark = xml_strdup (xml_attrval (s, "s")); xml_free (s); return (mark); } |
XMLAPI char * xmlobj_formatlong(XML * obj, XML * class, const char * format) { return NULL; } XMLAPI XML * xmlobj_template (XML * obj, XML * class, XML * template) { return NULL; } |
XMLAPI void xmlobj_fixkey (char * key) { char *scan; if (!key) return; do { if (!*key) return; if (*key >= 'A' && *key <= 'Z') *key += 'a' - 'A'; if (*key == ' ') *key = '_'; if (*key < '0' || *key > 'z' || (*key > '9' && *key < 'a' && *key != '_')) { if (*key != '~') { /* List of good punctuation. There may be more. Who knows? */ scan = key; while (*scan) { *scan = scan[1]; scan++; } key --; } } } while (*(++key)); } XMLAPI char * xmlobj_getkey (XML * obj, XML * class) { const char * key = NULL; XML * mark; char * ret; if (class) key = xml_attrval (class, "key"); if (key && *key) { if (strchr (key, '[')) { ret = xmlobj_format (obj, class, key); xmlobj_fixkey (ret); return ret; } else { /*ret = xmlobj_get (obj, class, key); -- if key="key" this is a loop.... 2003-05-28 */ mark = xmlobj_is_field (obj, class, key); ret = xmlobj_get_direct (mark); xmlobj_fixkey (ret); return ret; } } mark = xml_search (obj, "field", NULL, NULL); if (!mark) { return (xml_strdup ("none")); } else { key = xml_attrval (mark, "value"); if (*key) { ret = xml_strdup (key); xmlobj_fixkey (ret); xml_set (obj, "key", ret); return (ret); } else { ret = xml_stringcontenthtml (mark); xmlobj_fixkey (ret); return (ret); } } } |
XMLAPI char * xmlobj_getconf (XML * obj, const char * field, const char * deflt) { XML * mark; char * val; if (!obj) return (xml_strdup (deflt)); mark = xmlobj_is_field (obj, NULL, field); if (mark) return xmlobj_get_direct (mark); return (xml_strdup (deflt)); } XMLAPI int xmlobj_getconfnum (XML * obj, const char * field, int deflt) { XML * mark; int retval; char * val; if (!obj) return (deflt); mark = xmlobj_is_field (obj, NULL, field); if (mark) { val = xmlobj_get_direct (mark); retval = atoi(val); free(val); return retval; } return deflt; } XMLAPI void xmlobj_setconf (XML * obj, const char * field, const char * value) { FILE * f; XML * o; xmlobj_set (obj, NULL, field, value); if (*xml_attrval (obj, "filename")) { f = fopen (xml_attrval (obj, "filename"), "r"); if (f) { o = xml_read (f); fclose (f); if (o) { xmlobj_set (o, NULL, field, value); f = fopen (xml_attrval (obj, "filename"), "w"); if (f) { xml_write (f, o); fclose (f); } xml_free (o); } } } } XMLAPI void xmlobj_setconfnum (XML * obj, const char * field, int value) { FILE * f; XML * o; xmlobj_setnum (obj, NULL, field, value); if (*xml_attrval (obj, "filename")) { f = fopen (xml_attrval (obj, "filename"), "r"); if (f) { o = xml_read (f); fclose (f); if (o) { xmlobj_setnum (o, NULL, field, value); f = fopen (xml_attrval (obj, "filename"), "w"); if (f) { xml_write (f, o); fclose (f); } xml_free (o); } } } } |
XMLAPI XML * xmlobj_diff (XML * obj, XML * class, XML * changed) { XML * diff = xml_create ("diff"); XML * ofield; XML * cfield; XML * dfield; char * linkfield = NULL; XML_ATTR * attr = xml_attrfirst (changed); char * ovalue = NULL; char * cvalue = NULL; XML * olinkrec; XML * clinkrec; XML * dlinkrec; XML * olinkfld; XML * clinkfld; const char * key; while (attr) { if (strcmp (xml_attrval (obj, xml_attrname (attr)), xml_attrvalue (attr))) { xml_set (diff, xml_attrname (attr), xml_attrvalue (attr)); } attr = xml_attrnext (attr); } cfield = xml_firstelem (changed); while (cfield) { if (xml_is (cfield, "field")) { ofield = xml_locf (obj, ".field[%s]", xml_attrval (cfield, "id")); if (!ofield) { xml_append_pretty (diff, xml_copy (cfield)); } else { dfield = NULL; attr = xml_attrfirst (cfield); while (attr) { if (strcmp (xml_attrval (ofield, xml_attrname (attr)), xml_attrvalue (attr))) { if (!dfield) { dfield = xml_create ("field"); xml_set (dfield, "id", xml_attrval (cfield, "id")); } xml_set (dfield, xml_attrname (attr), xml_attrvalue (attr)); } attr = xml_attrnext (attr); } ovalue = xmlobj_get (obj, class, xml_attrval (cfield, "id")); cvalue = xmlobj_get (changed, class, xml_attrval (cfield, "id")); if (!ovalue) { if (cvalue) { if (!dfield) { dfield = xml_create ("field"); xml_set (dfield, "id", xml_attrval (cfield, "id")); } xml_append (dfield, xml_createtext_nodup (cvalue)); cvalue = NULL; } else { if (dfield) xml_set (dfield, "diff:content", "unchanged"); } } else if (cvalue) { if (!strcmp (ovalue, cvalue)) { if (dfield) xml_set (dfield, "diff:content", "unchanged"); } else { if (!dfield) { dfield = xml_create ("field"); xml_set (dfield, "id", xml_attrval (cfield, "id")); } xml_append (dfield, xml_createtext_nodup (cvalue)); cvalue = NULL; } } if (ovalue) free (ovalue); if (cvalue) free (cvalue); if (dfield) xml_append_pretty (diff, dfield); } } else if (xml_is (cfield, "link")) { /* I need to document how links work, but I'm using them to represent many-to-many linking tables. */ ofield = xml_locf (obj, ".link[%s]", xml_attrval (cfield, "id")); if (!ofield) { ofield = xml_copy (cfield); olinkrec = xml_firstelem (ofield); while (olinkrec) { xml_set (olinkrec, "diff:action", "add"); olinkrec = xml_nextelem (olinkrec); } xml_append_pretty (diff, ofield); } else { dfield = NULL; /* Find key of link; if a link is named e.g. id="My Table:local_id" then "local_id" is the key field of the link, which corresponds to the key of the current record. If this field is included in the link record, it should actually be completely ignored. */ key = xml_attrval (cfield, "id"); key = strchr (key, ':'); if (key) key++; /* Scan original for deleted links. */ olinkrec = xml_firstelem (ofield); while (olinkrec) { clinkrec = xml_firstelem (cfield); while (clinkrec) { clinkfld = xml_firstelem (clinkrec); while (clinkfld) { if (key && !strcmp (key, xml_attrval (clinkfld, "id"))) { clinkfld = xml_nextelem (clinkfld); continue; } cvalue = xmlobj_get (clinkrec, NULL, xml_attrval (clinkfld, "id")); ovalue = xmlobj_get (olinkrec, NULL, xml_attrval (clinkfld, "id")); if (!cvalue && !ovalue) { clinkfld = xml_nextelem (clinkfld); continue; } if (!cvalue) { free (ovalue); break; } if (!ovalue) { free (cvalue); break; } if (strcmp (cvalue, ovalue)) { free (ovalue); free (cvalue); break; } free (ovalue); free (cvalue); clinkfld = xml_nextelem (clinkfld); } if (!clinkfld) { /* All fields matched, so the records match. */ break; } /* Otherwise, keep looking for a matching field. */ clinkrec = xml_nextelem (clinkrec); } if (!clinkrec) { /* No link records matched, so this olinkrec was deleted. */ if (!dfield) { dfield = xml_create ("link"); xml_copyattrs (dfield, cfield); } dlinkrec = xml_create (xml_name (olinkrec)); xml_append_pretty (dfield, dlinkrec); xml_set (dlinkrec, "diff:action", "del"); olinkfld = xml_firstelem (olinkrec); while (olinkfld) { xml_append_pretty (dlinkrec, xml_copy (olinkfld)); olinkfld = xml_nextelem (olinkfld); } } /* Now check next link record. */ olinkrec = xml_nextelem (olinkrec); } /* Scan edited for added links. */ clinkrec = xml_firstelem (cfield); while (clinkrec) { olinkrec = xml_firstelem (ofield); while (olinkrec) { clinkfld = xml_firstelem (clinkrec); while (clinkfld) { if (key && !strcmp (key, xml_attrval (clinkfld, "id"))) { clinkfld = xml_nextelem (clinkfld); continue; } cvalue = xmlobj_get (clinkrec, NULL, xml_attrval (clinkfld, "id")); ovalue = xmlobj_get (olinkrec, NULL, xml_attrval (clinkfld, "id")); if (!cvalue && !ovalue) { clinkfld = xml_nextelem (clinkfld); continue; } if (!cvalue) { free (ovalue); break; } if (!ovalue) { free (cvalue); break; } if (strcmp (cvalue, ovalue)) { free (ovalue); free (cvalue); break; } free (ovalue); free (cvalue); clinkfld = xml_nextelem (clinkfld); } if (!clinkfld) { /* All fields matched, so the records match. */ break; } /* Otherwise, keep looking for a matching field. */ olinkrec = xml_nextelem (olinkrec); } if (!olinkrec) { /* No link records matched, so this clinkrec was added. */ if (!dfield) { dfield = xml_create ("link"); xml_copyattrs (dfield, cfield); } dlinkrec = xml_create (xml_name (clinkrec)); xml_append_pretty (dfield, dlinkrec); xml_set (dlinkrec, "diff:action", "add"); clinkfld = xml_firstelem (clinkrec); while (clinkfld) { xml_append_pretty (dlinkrec, xml_copy (clinkfld)); clinkfld = xml_nextelem (clinkfld); } } /* Now check next link record. */ clinkrec = xml_nextelem (clinkrec); } /* Scan original for matches that are possibly changed -- TODO (requires reference to record class) */ /* Finally, if we've accumulated any changes, write them to the diff. */ if (dfield) xml_append_pretty (diff, dfield); } } else { /* For arbitrary content, I'm not entirely sure what to do. One thing is obvious, though; arbitrary content which does not yet exist in the original object should be copied (that keeps things consistent with the above. ) */ ofield = xml_locf (obj, ".%s[%s]", xml_name (cfield), xml_attrval (cfield, "id")); if (!ofield) { xml_append (diff, xml_copy (cfield)); } else { /* ??? TODO: figure out if there's anything rational we can do here. */ } } cfield = xml_nextelem (cfield); } return diff; } |
static XML * _xmlobj_patch (XML * orig, XML * diff, int change_orig); XMLAPI XML * xmlobj_patch (XML * obj, XML * class, XML * diff) { return _xmlobj_patch (obj, diff, 1); } XMLAPI XML * xmlobj_undiff (XML * obj, XML * class, XML * diff) { return _xmlobj_patch (obj, diff, 0); } |
XML * _xmlobj_patch (XML * orig, XML * diff, int change_orig) { XML * undiff = xml_create ("diff"); char * value; XML * scan; XML * mark; XML_ATTR * attr = xml_attrfirst (diff); XML * olist; XML * ulist; XML * olinkrec; XML * dlinkfld; XML * olinkfld; char * dvalue; char * ovalue; XML * undifflink; while (attr) { xml_set (undiff, xml_attrname (attr), xml_attrval (orig, xml_attrname (attr))); if (change_orig) { xml_set (orig, xml_attrname (attr), xml_attrval (diff, xml_attrname (attr))); } attr = xml_attrnext (attr); } scan = xml_firstelem (diff); while (scan) { if (xml_is (scan, "field")) { /* Patching fields is easy. */ value = xmlobj_get (orig, NULL, xml_attrval (scan, "id")); if (value) { xmlobj_set (undiff, NULL, xml_attrval (scan, "id"), value); free ((void *) value); } else { xmlobj_set (undiff, NULL, xml_attrval (scan, "id"), ""); } if (change_orig) { value = xmlobj_get (diff, NULL, xml_attrval (scan, "id")); xmlobj_set (orig, NULL, xml_attrval (scan, "id"), value); free ((void *) value); } } else if (xml_is (scan, "link")) { /* To patch lists, we first make sure we've got the list in the undiff and orig. */ olist = xml_locf (orig, ".link[%s]", xml_attrval (scan, "id")); if (change_orig && !olist) { olist = xml_create ("link"); xml_copyattrs (olist, scan); xml_append_pretty (orig, olist); } ulist = xml_create ("link"); xml_copyattrs (ulist, scan); xml_append_pretty (undiff, ulist); /* Now we scan the list *change* spec, and undiff/patch as required. */ mark = xml_firstelem (scan); while (mark) { if (xml_is (mark, "link-to")) { if (!strcmp (xml_attrval (mark, "diff:action"), "add")) { olinkrec = NULL; if (olist) { /* Scan olist for link to be sure it's not there. */ olinkrec = xml_firstelem (olist); while (olinkrec) { if (xml_is (olinkrec, "link-to")) { dlinkfld = xml_firstelem (mark); while (dlinkfld) { dvalue = xmlobj_get (mark, NULL, xml_attrval (dlinkfld, "id")); ovalue = xmlobj_get (olinkrec, NULL, xml_attrval (dlinkfld, "id")); if (!dvalue && !ovalue) { } else if (!dvalue) { free ((void *) ovalue); break; } else if (!ovalue) { free ((void *) dvalue); break; } else { if (strcmp (dvalue, ovalue)) { free ((void *) dvalue); free ((void *) ovalue); break; } free ((void *) dvalue); free ((void *) ovalue); } dlinkfld = xml_nextelem (dlinkfld); } if (!dlinkfld) break; } olinkrec = xml_nextelem (olinkrec); } } if (!olinkrec) { /* We make an add only if there is no pre-existing identical link. */ undifflink = xml_copy (mark); xml_set (undifflink, "diff:action", "del"); xml_append_pretty (ulist, undifflink); if (change_orig) { undifflink = xml_copy (mark); xml_unset (undifflink, "diff:action"); xml_append_pretty (olist, undifflink); } } } else if (!strcmp (xml_attrval (mark, "diff:action"), "del")) { if (olist) { /* Scan olist for link to be sure it *is* there. */ olinkrec = xml_firstelem (olist); while (olinkrec) { if (xml_is (olinkrec, "link-to")) { dlinkfld = xml_firstelem (mark); while (dlinkfld) { dvalue = xmlobj_get (mark, NULL, xml_attrval (dlinkfld, "id")); ovalue = xmlobj_get (olinkrec, NULL, xml_attrval (dlinkfld, "id")); if (!dvalue && !ovalue) { } else if (!dvalue) { free ((void *) ovalue); break; } else if (!ovalue) { free ((void *) dvalue); break; } else { if (strcmp (dvalue, ovalue)) { free ((void *) dvalue); free ((void *) ovalue); break; } free ((void *) dvalue); free ((void *) ovalue); } dlinkfld = xml_nextelem (dlinkfld); } if (!dlinkfld) break; } olinkrec = xml_nextelem (olinkrec); } if (olinkrec) { undifflink = xml_copy (olinkrec); xml_set (undifflink, "diff:action", "add"); xml_append_pretty (ulist, undifflink); if (change_orig) { xml_delete (olinkrec); } } } } } mark = xml_nextelem (mark); } } else { /* For patching arbitrary content, we're fine -- but we don't have any way of removing it, so the undiff simply won't show it. TODO: think about whether this could make more sense. */ if (change_orig) { mark = xml_search (orig, xml_name (scan), "id", xml_attrval (scan, "id")); if (!mark) { xml_append_pretty (orig, xml_copy (scan)); } } } scan = xml_nextelem (scan); } return undiff; } |
XMLAPI XML * xmlobj_ver (XML * obj, XML * class, const char * field, const char * ver) { XML * fld = xmlobj_field (obj, class, field); return (xmlobj_ver_direct (fld, ver)); } XMLAPI XML * xmlobj_ver_direct (XML * field, const char * ver) { XML * v = xml_firstelem (field); while (v) { if (xml_is (v, "ver")) { if (!strcmp (ver, xml_attrval (v, "id"))) return v; } v = xml_nextelem (v); } v = xml_create ("ver"); xml_append_pretty (field, v); xml_set (v, "id", ver); return v; } XMLAPI char * xmlobj_getver (XML * obj, XML * class, const char * field, const char * ver) { XML * v = xmlobj_ver (obj, class, field, ver); return (xmlobj_get_direct (v)); } XMLAPI const char * xmlobj_curver (XML * obj, XML * class, const char * field) { XML * fld = xmlobj_field (obj, class, field); return (xml_attrval (fld, "ver")); } XMLAPI XML * xmlobj_setver (XML * obj, XML * class, const char * field, const char * ver) { XML * fld = xmlobj_field (obj, class, field); xml_set (fld, "ver", ver); return (xmlobj_ver_direct (fld, ver)); } XMLAPI XML * xmlobj_newver (XML * obj, XML * class, const char * field) { XML * fld = xmlobj_field (obj, class, field); return (xmlobj_newver_direct (fld)); } XMLAPI XML * xmlobj_newver_direct (XML * field) { xml_setnum (field, "ver", xml_attrvalnum (field, "ver") + 1); return (xmlobj_ver_direct (field, xml_attrval (field, "ver"))); } |
struct _xmlobj_sort_hdl { XML * sort; XML * class; XML * elem; }; int _xmlobj_sort_comparison (const void * a, const void * b); XMLAPI XML * xmlobj_list_sort (XML * list, XML * class, const char * order) { int i; XML * child; XML * sort = xml_create ("sort"); XML * sortelem; XML * field; char * mark; const char * mark2; struct _xmlobj_sort_hdl * array; ELEMENTLIST * elist; xml_set (list, "error-state", ""); /* Parse the order specifier. */ mark = strchr (order, ','); while (mark) { sortelem = xml_create ("field"); xml_set (sortelem, "id", ""); xml_attrncat (sortelem, "id", order, mark - order); xml_append (sort, sortelem); order = mark + 1; while (*order == ' ') order++; mark = strchr (order, ','); } sortelem = xml_create ("field"); xml_set (sortelem, "id", order); xml_append (sort, sortelem); /* Scan the order elements to fix up asc/desc and retrieve types. */ sortelem = xml_firstelem (sort); while (sortelem) { xml_set (sortelem, "dir", "asc"); mark = strchr (xml_attrval (sortelem, "id"), ' '); if (mark) { xml_set (sortelem, "dir", mark + 1); while (*xml_attrval (sortelem, "dir") == ' ') { mark2 = xml_attrval (sortelem, "dir"); xml_set (sortelem, "dir", mark2 + 1); } xml_set (sortelem, "newid", ""); xml_attrncat (sortelem, "newid", xml_attrval (sortelem, "id"), mark - xml_attrval (sortelem, "id")); xml_set (sortelem, "id", xml_attrval (sortelem, "newid")); xml_unset (sortelem, "newid"); if (strcmp (xml_attrval (sortelem, "dir"), "asc") && strcmp (xml_attrval (sortelem, "dir"), "desc")) { xml_setf (list, "error-state", "Unknown sort direction '%s' specified", xml_attrval (sortelem, "dir")); xml_free (sort); return (NULL); } } if (class && *xml_attrval (sortelem, "id") != '?' && *xml_attrval (sortelem, "id") != '!') { field = xml_search (class, "field", "id", xml_attrval (sortelem, "id")); if (!field) { xml_setf (list, "error-state", "Unknown field '%s' specified", xml_attrval (sortelem, "id")); xml_free (sort); return (NULL); } xml_set (sortelem, "type", xml_attrval (field, "type")); } if (*xml_attrval (sortelem, "id") == '?') { mark2 = xml_attrval (sortelem, "id"); xml_set (sortelem, "id", mark2 + 1); } sortelem = xml_nextelem (sortelem); } /* Count the children. */ i=0; child = xml_firstelem (list); while (child) { i++; child = xml_nextelem (child); } if (i < 2) { xml_free (sort); return list; } /* Build the array. */ array = (struct _xmlobj_sort_hdl *) malloc (i * sizeof (struct _xmlobj_sort_hdl)); i=0; child = xml_firstelem (list); while (child) { array[i].sort = sort; array[i].class = class; array[i].elem = child; i++; child = xml_nextelem (child); } /* Sort the array. */ qsort (array, i, sizeof (struct _xmlobj_sort_hdl), _xmlobj_sort_comparison); /* Rearrange the children, being very slick about it. */ i = 0; elist = list->children; while (elist) { if (elist->element->name) { elist->element = array[i].elem; i++; } elist = elist->next; } free ((void *) array); xml_free (sort); return list; } |
int _xmlobj_sort_comparison (const void * a, const void * b) { XML * sort; int res; int ia, ib; char * ca, * cb; struct _xmlobj_sort_hdl * _a = (struct _xmlobj_sort_hdl *) a; struct _xmlobj_sort_hdl * _b = (struct _xmlobj_sort_hdl *) b; if (a == b) return 0; sort = xml_firstelem (_a->sort); while (sort) { if (!strcmp (xml_attrval (sort, "type"), "numeric")) { ia = xmlobj_getnum (_a->elem, _a->class, xml_attrval (sort, "id")); ib = xmlobj_getnum (_b->elem, _b->class, xml_attrval (sort, "id")); res = 0; if (ia < ib) res = -1; if (ia > ib) res = 1; } else { ca = xmlobj_get (_a->elem, _a->class, xml_attrval (sort, "id")); cb = xmlobj_get (_b->elem, _b->class, xml_attrval (sort, "id")); if (!ca) ca = strdup (""); if (!cb) cb = strdup (""); res = strcmp (ca, cb); free (ca); free (cb); } if (!strcmp (xml_attrval (sort, "dir"), "desc")) res = - res; if (res) return res; sort = xml_nextelem (sort); } return 0; } |
This code and documentation are released under the terms of the GNU license. They are copyright (c) 2003, Vivtek. All rights reserved except those explicitly granted under the terms of the GNU license. |