scald

Overview

Scald can assist lazy developers. Scald is a tool to generate program code from an HTML-like XML description. For example, scald will process an XML description into C code that prints an HTML form and saves user input data into a configuration file.

Details

There are many XSLT processors available to developers. The following examples use xmllint from libxml2 and xsltproc from libxslt. Here is example.xml, an example XML document that follows the scald schema:

<?xml version="1.0"?>

<!DOCTYPE webapp PUBLIC "-//Flyn Computing//DTD Scald XML Unstable//EN"
                        "http://www.flyn.org/xml/dtd/unstable/webapp.dtd">

<webapp name="/cgi-bin/example.cgi" short-name="example" description="Example">

        <form name="example" save-data-as="config" save-to-dir="/tmp/">
                <input name="example-input" type="text" label="Example Input"/>
                <submit value="Save"/>
        </form>

</webapp>

The file example.xml may be validated with the command xmllint –noout example.xml and processed with xsltproc http://www.flyn.org/xml/xslt/unstable/webapp-c.xslt example.xml. The result will be several functions, written in C:

void print_form(char *sidebar)
Creates and prints an HTML form, selecting default values from a configuration file, if available.
void save_data(s_cgi *cgi)
Reads a form and saves its values to a configuration file or spool.
char *get_post_mode(s_cgi *cgi)
Returns the CGI mode of execution.

After you process an XML file to generate C code, you should write a main function that calls these three functions. One useful technique is to write a system configuration daemon. After running save_data, the main function may notify this daemon, which will then reconfigure a system service based on the updated configuration file (it may be a bad idea to allow your webserver the rights required to update system configurations directly).

The scald schema also allows for dependencies between inputs. For example, the fragment below describes an input, password, that is only enabled if another input, authenticate, is selected. This dependency is enforced by Javascript code that scald automatically generates.

<input name="authenticate" type="checkbox" label="Use Authentication"/>
<input name="password" type="password" label="Password"
       enabled-by="document.formname.authenticate"/>

The call-print- entites allow one to generate custom HTML at run runtime. The following fragment indicates that options should be generated at runtime by a custom function:

<form name="initialize" save-data-as="config" save-to-dir="/tmp/">
        <select name="device">
                <call-print-options-fn/>
        </select>
</form>

Scald will generate code that expects the function print_document_initialize_device_options() to be defined in a separate file and linked against the C output that scald generates. Here is a simple implementation of print_document_initialize_device_options():

void print_document_initialize_device_options(char *obj)
{
        printf("<option value=\"foo\">Foo</option>");
        printf("<option value=\"bar\">Bar</option>");
}

A more general-purpose entity is call-print-input-fn. This may be used to generate any HTML input tags:

<form name="initialize" save-data-as="spool" save-to-dir="/tmp/">
        <call-print-input-fn name="example"/>
</form>

This requires one to define both a print and save function:

void save_to_spool_document_initialize_example_input(s_cgi *cgi,
                                                     char *dir, char *obj)
{
        /* Code to read data from s_cgi *cgi and write it to spool file
         * in char *dir
         */
}

void print_document_initialize_example_input(char *obj)
{
        /*  Code to print HTML form */
}

The onclick option is available to the input tag. This may be set to a Javascript function that will execute when the input is clicked. The Javascript function may be either provided by the XML document or dynamically generated using a call-print- entity as in the following example:

<form name="initialize" save-data-as="config" save-to-dir="/tmp/">
        <input label="Host" name="destination" value="host" type="radio" onclick="populate_with_hosts"/><br/>
        <input label="Disk" name="destination" value="disk" type="radio" onclick="populate_with_disks"/><br/>
        <select label="Destination" name="dest">
                <call-print-options-fn/>
        </select>
</form>

The following is the implementation that will be called by the code generated by the call-print-options-fn entity. It prints the two Javascript functions that are referenced by the onclick options above. It also calls the populate_with_hosts function that it defines in order to initially populate the options list. Clicking between the radio buttons defined above will switch the options available.

void print_document_initialize_dest_options(char *obj)
{
        printf ("<script type=\"text/javascript\">");
        printf ("function populate_with_hosts() {");
        printf ("document.backup.dest.length = 0;");
        /* C Code to print Javascript that assigns hosts, e.g.: */
        for (i = 0; i < num_opts; i++) {
                printf ("%s[%d] = new Option(\"%s\", \"%s\", false);\n", obj, 
                        i, "example.com", "example.com");
        }
        if (0 == i) {
                printf("for (i = %s.options.length; i >= 0; i--) {                      %s[i] = null;                   }\n", obj, obj);
        }
        printf ("}");
        printf ("function populate_with_disks() {");
        printf ("document.backup.dest.length = 0;");
        /* Code to print disks */
        printf ("}");
        printf ("populate_with_hosts(%s);", obj);
        printf ("</script>");
}