#include <stdio.h> #include <string.h> #include <time.h> #include <math.h> #include "xmlapi.h" #include "xmlobj.h" |
This template will create a little HTML snippet containing the value "myvalue" gotten from the record passed in. Thus it expresses that record. Value lookup is configurable; configuration is done using the config XML passed in. This XML is of course pretty arbitrary (like all the rest of my XML) so it can be used for other things, notably the overall configuration of a managed site. At any rate, values can be gotten from the record's attributes, from files in the file system, or from the record's children (in which case they should have "id" attributes to provide names). This order isn't entirely random; it's the order I use for weaving a site from managed pieces and it makes sense right now. The order, however, should at some point be considered configurable. Lookups will later be much more interesting; they'll have the power to create composite values using multiple fields. But right now I'm under some time pressure, so I'm going to put that off. I've done versions of this under Perl, though, so I know how to use them and I've got a model to work from. It'll happen pretty soon. Conditionally expressed pieces are done using <template:if> and <template:else>. After all the trouble I went through to redefine conditionals for the wftk, you'd think I'd avoid simple "if" and "else" but they were easy to code and they're natural to understand, so there ya go. To build an element, you often need to set its attributes. There's no way to do that with the template:value statement alone, so I've defined <template:elem> and <template:attr> to take care of it. (October 20, 2001) Sometimes we want to embed template pieces into other XML structures (such as, say, nav bars) -- in this case, template_apply has to be able to cope with the situation that it's being called directly on a template component (template:value, template:if, or template:elem). In this case, it passes control straight to the responsible handler instead of trying to step along its children. Anyway, on with the show. First we define our main callable routines:<html> <table> <tr><td>Value:</td><td><template:value name="myvalue"/></td></tr> </table> </html>
static XML * xml_template_apply_value (XML * context, XML * template, XML * record, char * valuecallback()); static void xml_template_apply_if (XML * context, XML * into, XML * template, XML * record, char * valuecallback()); static XML * xml_template_apply_elem (XML * context, XML * template, XML * record, char * valuecallback()); XMLAPI XML * xml_template_apply (XML * context, XML * template, XML * record, char * valuecallback()) { XML * ret; XML * piece; if (!xml_is_element (template)) return (xml_copy (template)); if (xml_is (template, "template:value")) { return xml_template_apply_value (context, template, record, valuecallback); } ret = xml_create (xml_name (template)); if (xml_is (template, "template:elem")) { xml_append (ret, xml_template_apply_elem (context, template, record, valuecallback)); return ret; } if (xml_is (template, "template:if")) { xml_template_apply_if (context, ret, template, record, valuecallback); return ret; } xml_copyattrs (ret, template); piece = xml_first (template); while (piece) { if (xml_is (piece, "template:value")) { xml_append (ret, xml_template_apply_value (context, piece, record, valuecallback)); } else if (xml_is (piece, "template:if")) { xml_template_apply_if (context, ret, piece, record, valuecallback); } else if (xml_is (piece, "template:elem")) { xml_append (ret, xml_template_apply_elem (context, piece, record, valuecallback)); } else { xml_append (ret, xml_template_apply (context, piece, record, valuecallback)); } piece = xml_next (piece); } return (ret); } XMLAPI XML * xml_template_apply_list (XML * context, XML * template, XML * list, XML * filter, char * valuecallback()) { XML * cur = NULL; XML * ret; XML * piece; XML * subpiece; XML * between; XML * entry; int skip; if (!xml_is_element (template)) return (xml_copy (template)); if (*xml_attrval (list, "cur")) { cur = xml_loc (list, xml_attrval (list, "cur")); } if (!cur) cur = xml_firstelem (list); ret = xml_create (xml_name (template)); xml_copyattrs (ret, template); piece = xml_first (template); while (piece) { if (xml_is (piece, "template:list")) { entry = xml_firstelem (list); between = NULL; while (entry) { skip = 0; if (*xml_attrval (list, "record") && !xml_is (entry, xml_attrval (list, "record"))) skip = 1; if (!skip) { if (between) { subpiece = xml_first (between); while (subpiece) { xml_append (ret, xml_template_apply (context, subpiece, entry, valuecallback)); subpiece = xml_next (subpiece); } } subpiece = xml_first (piece); while (subpiece) { if (xml_is (subpiece, "template:between")) { between = subpiece; } else { xml_append (ret, xml_template_apply (context, subpiece, entry, valuecallback)); } subpiece = xml_next (subpiece); } } entry = xml_nextelem (entry); } } else if (xml_is (piece, "template:value")) { xml_append (ret, xml_template_apply_value (context, piece, cur, valuecallback)); } else if (xml_is (piece, "template:if")) { xml_template_apply_if (context, ret, piece, cur, valuecallback); } else if (xml_is (piece, "template:elem")) { xml_append (ret, xml_template_apply_elem (context, piece, cur, valuecallback)); } else { xml_append (ret, xml_template_apply_list (context, piece, list, filter, valuecallback)); } piece = xml_next (piece); } return (ret); } |
static void xml_template_apply_lookup (XML * context, XML * template, const char * name, const char * value, XML * record, char * valuecallback()); void xml_template_apply_if (XML * context, XML * into, XML * template, XML * record, char * valuecallback()) { XML * piece; XML * subpiece; xml_template_apply_lookup (context, template, xml_attrval (template, "name"), "value", record, valuecallback); if (*xml_attrval (template, "value")) { piece = xml_first (template); while (piece) { if (xml_is (piece, "template:else")) { /* Skip */ } else if (xml_is (piece, "template:if")) { xml_template_apply_if (context, into, piece, record, valuecallback); } else if (xml_is (piece, "template:value")) { xml_append (into, xml_template_apply_value (context, piece, record, valuecallback)); } else if (xml_is (piece, "template:elem")) { xml_append (into, xml_template_apply_elem (context, piece, record, valuecallback)); } else { xml_append (into, xml_template_apply (context, piece, record, valuecallback)); } piece = xml_next (piece); } } else { piece = xml_firstelem (template); while (piece) { if (xml_is (piece, "template:else")) { piece = xml_first (piece); while (piece) { if (xml_is (piece, "template:if")) { xml_template_apply_if (context, into, piece, record, valuecallback); } else if (xml_is (piece, "template:value")) { xml_append (into, xml_template_apply_value (context, piece, record, valuecallback)); } else if (xml_is (piece, "template:elem")) { xml_append (into, xml_template_apply_elem (context, piece, record, valuecallback)); } else { xml_append (into, xml_template_apply (context, piece, record, valuecallback)); } piece = xml_next (piece); } break; } piece = xml_nextelem (piece); } } } |
XML * xml_template_apply_value (XML * context, XML * template, XML * record, char * valuecallback()) { const char * format; XML * scratch = xml_create ("s"); const char * mark; const char * mark2; time_t t; struct tm * tm; int span; int d, y, m, z, f, jd; char buf[100]; if (*xml_attrval (template, "name")) { xml_template_apply_lookup (context, template, xml_attrval (template, "name"), "value", record, valuecallback); } else { xml_set_nodup (template, "value", xml_stringcontenthtml (template)); } format = xml_attrval (template, "format"); while (format && *format) { if (strchr ("; \t\r\n", *format)) { format++; continue; } if (!strncmp (format, "notnull", 7)) { if (!*xml_attrval (template, "value")) { xml_free (scratch); return (xml_createtext ("")); } } else if (!strncmp (format, "nonbreaking", 11)) { xml_set (scratch, "s", ""); mark = xml_attrval (template, "value"); while (mark2 = strchr (mark, ' ')) { xml_attrncat (scratch, "s", mark, mark2 - mark); xml_attrcat (scratch, "s", " "); mark = mark2; while (*mark == ' ') mark++; } xml_attrcat (scratch, "s", mark); xml_set (template, "value", xml_attrval (scratch, "s")); } else if (!strncmp (format, "html-quoted", 11)) { xml_set (scratch, "s", ""); mark = xml_attrval (template, "value"); while (strlen (mark) && (span = strcspn (mark, "<>&")) < strlen(mark)) { if (span) xml_attrncat (scratch, "s", mark, span); mark += span; switch (*mark) { case '<': xml_attrcat (scratch, "s", "<"); break; case '>': xml_attrcat (scratch, "s", ">"); break; case '&': xml_attrcat (scratch, "s", "&"); break; } mark++; } if (strlen (mark)) xml_attrcat (scratch, "s", mark); xml_set (template, "value", xml_attrval (scratch, "s")); } else if (!strncmp (format, "link", 4)) { format += 4; if (*format == ':') { format++; mark = strchr (format, ';'); if (mark) { xml_attrncat (scratch, "f", format, mark - format); } xml_set_nodup (scratch, "f", xmlobj_format (record, NULL, mark ? xml_attrval (scratch, "f") : format)); xml_setf (template, "value", "<a href=\"%s\">%s</a>", xml_attrval (scratch, "f"), xml_attrval (template, "value")); } } else if (!strncmp (format, "timestamp", 9)) { format += 9; if (*format == ':') { format++; mark = format; } else { mark = "%c"; } t = (time_t) atol (xml_attrval (template, "value")); tm = localtime (&t); strftime (buf, sizeof(buf), mark, tm); xml_set (template, "value", buf); } else if (!strncmp (format, "date", 4)) { format += 4; if (*format == ':') { format++; mark = format; } else { mark = "%m/%c/%Y"; } strcpy (buf, xml_attrval (template, "value")); /* The date may be in numeric yyyymmdd format or in ISO format yyyy-mm-dd*hh:mm:ss */ span = strspn (buf, "0123456789"); if (span > 4) { /* Numeric-only */ d = atoi (buf + 6); buf[6] = '\0'; m = atoi (buf + 4); buf[4] = '\0'; y = atoi (buf); } else { /* Something like ISO format. */ y = atoi (buf); mark = buf + span + 1; m = atoi (mark); mark = buf + strspn (mark, "0123456789"); d = atoi (mark); } while (m > 12) { m-=12; y++; } while (m < 1) { m+=12; y--; } xml_set (scratch, "s", ""); while (mark2 = strchr (mark, '%')) { xml_attrncat (scratch, "s", mark, mark2 - mark); mark = mark2 + 1; switch (*mark) { case '%': xml_attrcat (scratch, "s", "%"); break; case 'b': switch (m) { case 1: xml_attrcat (scratch, "s", "Jan"); break; case 2: xml_attrcat (scratch, "s", "Feb"); break; case 3: xml_attrcat (scratch, "s", "Mar"); break; case 4: xml_attrcat (scratch, "s", "Apr"); break; case 5: xml_attrcat (scratch, "s", "May"); break; case 6: xml_attrcat (scratch, "s", "Jun"); break; case 7: xml_attrcat (scratch, "s", "Jul"); break; case 8: xml_attrcat (scratch, "s", "Aug"); break; case 9: xml_attrcat (scratch, "s", "Sep"); break; case 10: xml_attrcat (scratch, "s", "Oct"); break; case 11: xml_attrcat (scratch, "s", "Nov"); break; case 12: xml_attrcat (scratch, "s", "Dec"); break; } break; case 'B': switch (m) { case 1: xml_attrcat (scratch, "s", "January"); break; case 2: xml_attrcat (scratch, "s", "February"); break; case 3: xml_attrcat (scratch, "s", "March"); break; case 4: xml_attrcat (scratch, "s", "April"); break; case 5: xml_attrcat (scratch, "s", "May"); break; case 6: xml_attrcat (scratch, "s", "June"); break; case 7: xml_attrcat (scratch, "s", "July"); break; case 8: xml_attrcat (scratch, "s", "August"); break; case 9: xml_attrcat (scratch, "s", "September"); break; case 10: xml_attrcat (scratch, "s", "October"); break; case 11: xml_attrcat (scratch, "s", "November"); break; case 12: xml_attrcat (scratch, "s", "December"); break; } break; case 'd': sprintf (buf, "%d", d); xml_attrcat (scratch, "s", buf); break; case 'm': sprintf (buf, "%d", m); xml_attrcat (scratch, "s", buf); break; case 'y': case 'Y': sprintf (buf, "%d", y); xml_attrcat (scratch, "s", buf); break; } mark++; } xml_attrcat (scratch, "s", mark); xml_set (template, "value", xml_attrval (scratch, "s")); } format = strchr (format, ';'); } xml_free (scratch); return (xml_createtext (xml_attrval (template, "value"))); } |
XML * xml_template_apply_elem (XML * context, XML * template, XML * record, char * valuecallback()) { XML * ret; XML * attrval; XML * piece; int whitespace; ret = xml_create (*xml_attrval (template, "elem") ? xml_attrval (template, "elem") : "elem"); whitespace = strcmp (xml_attrval (template, "mode"), "nowhitespace"); piece = whitespace ? xml_first (template) : xml_firstelem (template); while (piece) { if (xml_is (piece, "template:attr")) { attrval = xml_template_apply (context, piece, record, valuecallback); if (attrval) { if (*xml_attrval (attrval, "name")) { xml_template_apply_lookup (context, attrval, xml_attrval (attrval, "name"), "value", record, valuecallback); } else { xml_set_nodup (attrval, "value", xml_stringcontenthtml (attrval)); } xml_set (ret, *xml_attrval (attrval, "attr") ? xml_attrval (attrval, "attr") : "id", xml_attrval (attrval, "value")); xml_free (attrval); } } else if (xml_is (piece, "template:value")) { xml_append (ret, xml_template_apply_value (context, piece, record, valuecallback)); } else if (xml_is (piece, "template:if")) { xml_template_apply_if (context, ret, piece, record, valuecallback); } else if (xml_is (piece, "template:elem")) { xml_append (ret, xml_template_apply_elem (context, piece, record, valuecallback)); } else { xml_append (ret, xml_template_apply (context, piece, record, valuecallback)); } piece = whitespace ? xml_next (piece) : xml_nextelem (piece); } return (ret); } |
void xml_template_apply_lookup (XML * context, XML * template, const char * name, const char * value, XML * record, char * valuecallback()) { XML * elem; char namebuf[256]; int len; char filename[256]; char * mark; char t[64]; time_t tm; FILE * file; if (!template) return; xml_set (template, value, ""); while (strchr (name, '|')) { len = strchr (name, '|') - name; len = len > sizeof (namebuf) - 1 ? sizeof (namebuf) - 1 : len; memset (namebuf, '\0', sizeof (namebuf)); strncpy (namebuf, name, len); xml_template_apply_lookup (context, template, namebuf, value, record, valuecallback); if (*xml_attrval (template, value)) return; name = strchr (name, '|') + 1; } if (*name == '!') { /* Special values. */ if (!strcmp (name + 1, "content")) { xml_set_nodup (template, value, xml_stringcontenthtml (record)); } else if (!strcmp (name + 1, "record")) { xml_set_nodup (template, value, xml_stringhtml (record)); } else if (!strcmp (name + 1, "now")) { /* Julian second dating is herewith standard. It can be formatted with the "timestamp" format. */ sprintf (t, "%ld", time(&tm)); xml_set (template, value, t); } } else { /* Check record attribute. */ if (*xml_attrval (record, name)) { xml_set (template, value, xml_attrval (record, name)); return; } /* And here we call our value callback. */ if (valuecallback) { xml_set_nodup (template, value, (valuecallback) (context, record, name)); } else { if (strchr (name, '[')) { xml_set_nodup (template, value, xmlobj_format (record, NULL, name)); } else { xml_set_nodup (template, value, xmlobj_get (record, NULL, name)); } } } } |
This code and documentation are released under the terms of the GNU license. They are additionally copyright (c) 2002, Vivtek. All rights reserved except those explicitly granted under the terms of the GNU license. |