X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Foutput%2Fhtml.c;h=3e85b3d968b69c76653775a7239f10c9e08869e3;hb=d1d0556e538bf04cb0b991b7acb205c144a2f826;hp=901c98a2a09b4ac61cd4b06bab2cb7ae604de056;hpb=dcf9b154cbcaa35c3d8459a201b77eec8bcb30bd;p=pspp diff --git a/src/output/html.c b/src/output/html.c index 901c98a2a0..3e85b3d968 100644 --- a/src/output/html.c +++ b/src/output/html.c @@ -1,652 +1,512 @@ -/* PSPP - computes sample statistics. - Copyright (C) 1997-9, 2000 Free Software Foundation, Inc. - Written by Ben Pfaff . +/* PSPP - a program for statistical analysis. + Copyright (C) 1997-9, 2000, 2009, 2010, 2011, 2012, 2013, 2014 Free Software Foundation, Inc. - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2 of the - License, or (at your option) any later version. + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - This program is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. */ - -/* This #if encloses the rest of the file. */ -#if !NO_HTML + along with this program. If not, see . */ #include -#include "chart.h" -#include "htmlP.h" -#include "message.h" + #include +#include #include #include #include - -#if HAVE_UNISTD_H #include -#endif -#include "alloc.h" -#include "message.h" -#include "filename.h" -#include "getline.h" -#include "getlogin_r.h" -#include "output.h" -#include "manager.h" -#include "table.h" -#include "version.h" -#include "make-file.h" +#include "data/file-name.h" +#include "libpspp/assertion.h" +#include "libpspp/cast.h" +#include "libpspp/compiler.h" +#include "libpspp/message.h" +#include "libpspp/version.h" +#include "output/cairo.h" +#include "output/chart-item.h" +#include "output/driver-provider.h" +#include "output/message-item.h" +#include "output/options.h" +#include "output/output-item-provider.h" +#include "output/table-provider.h" +#include "output/table-item.h" +#include "output/text-item.h" + +#include "xalloc.h" #include "gettext.h" #define _(msgid) gettext (msgid) -/* Prototypes. */ -static int postopen (struct file_ext *); -static int preclose (struct file_ext *); +struct html_driver + { + struct output_driver driver; +#ifdef HAVE_CAIRO + struct xr_color fg; + struct xr_color bg; +#endif + char *file_name; + char *chart_file_name; + + char *command_name; + FILE *file; + size_t chart_cnt; + + bool css; + bool borders; + }; -static int -html_open_global (struct outp_class *this UNUSED) -{ - return 1; -} +static const struct output_driver_class html_driver_class; -static int -html_close_global (struct outp_class *this UNUSED) -{ - return 1; -} +static void html_output_table (struct html_driver *, const struct table *, + const char *caption); +static void escape_string (FILE *file, + const char *text, size_t length, + const char *space, const char *newline); +static void print_title_tag (FILE *file, const char *name, + const char *content); -static int -html_preopen_driver (struct outp_driver *this) +static struct html_driver * +html_driver_cast (struct output_driver *driver) { - struct html_driver_ext *x; - - assert (this->driver_open == 0); - msg (VM (1), _("HTML driver initializing as `%s'..."), this->name); - - this->ext = x = xmalloc (sizeof *x); - this->res = 0; - this->horiz = this->vert = 0; - this->width = this->length = 0; - - this->cp_x = this->cp_y = 0; - - x->prologue_fn = NULL; - - x->file.filename = NULL; - x->file.mode = "w"; - x->file.file = NULL; - x->file.sequence_no = &x->sequence_no; - x->file.param = this; - x->file.postopen = postopen; - x->file.preclose = preclose; - - x->sequence_no = 0; - - return 1; + assert (driver->class == &html_driver_class); + return UP_CAST (driver, struct html_driver, driver); } -static int -html_postopen_driver (struct outp_driver *this) +static struct driver_option * +opt (struct output_driver *d, struct string_map *options, const char *key, + const char *default_value) { - struct html_driver_ext *x = this->ext; - - assert (this->driver_open == 0); - if (NULL == x->file.filename) - x->file.filename = xstrdup ("pspp.html"); - - if (x->prologue_fn == NULL) - x->prologue_fn = xstrdup ("html-prologue"); - - msg (VM (2), _("%s: Initialization complete."), this->name); - this->driver_open = 1; - - return 1; + return driver_option_get (d, options, key, default_value); } -static int -html_close_driver (struct outp_driver *this) +static struct output_driver * +html_create (const char *file_name, enum settings_output_devices device_type, + struct string_map *o) { - struct html_driver_ext *x = this->ext; - - assert (this->driver_open); - msg (VM (2), _("%s: Beginning closing..."), this->name); - fn_close_ext (&x->file); - free (x->prologue_fn); - free (x->file.filename); - free (x); - msg (VM (3), _("%s: Finished closing."), this->name); - this->driver_open = 0; - - return 1; -} + struct output_driver *d; + struct html_driver *html; + + html = xzalloc (sizeof *html); + d = &html->driver; + output_driver_init (&html->driver, &html_driver_class, file_name, + device_type); + html->css = parse_boolean (opt (d, o, "css", "true")); + html->borders = parse_boolean (opt (d, o, "borders", "true")); + + html->file_name = xstrdup (file_name); + html->chart_file_name = parse_chart_file_name (opt (d, o, "charts", + file_name)); + html->file = NULL; + html->chart_cnt = 1; +#ifdef HAVE_CAIRO + parse_color (d, o, "background-color", "#FFFFFFFFFFFF", &html->bg); + parse_color (d, o, "foreground-color", "#000000000000", &html->fg); +#endif + html->file = fn_open (html->file_name, "w"); + if (html->file == NULL) + { + msg_error (errno, _("error opening output file `%s'"), html->file_name); + goto error; + } + fputs ("\n", html->file); + fputs ("\n", html->file); + fputs ("\n", html->file); + print_title_tag (html->file, "TITLE", _("PSPP Output")); + fprintf (html->file, "\n", version); + fputs ("\n", html->file); -/* Link the image contained in FILENAME to the - HTML stream in file F. */ -static int -link_image (struct file_ext *f, char *filename) -{ - fprintf (f->file, - "", filename); + if ( html->css) + { + fputs ("\n", + html->file); + fputs ("\n", + html->file); + } + fputs ("\n", html->file); + fputs ("file); + fputs (" LINK=\"#1f00ff\" ALINK=\"#ff0000\" VLINK=\"#9900dd\">\n", html->file); - if (ferror (f->file)) - return 0; + return d; - return 1; + error: + output_driver_destroy (d); + return NULL; } - -/* Generic option types. */ -enum -{ - boolean_arg = -10, - string_arg, - nonneg_int_arg -}; - -/* All the options that the HTML driver supports. */ -static struct outp_option option_tab[] = +/* Emits CONTENT to the output, escaping CONTENT as + necessary for HTML. */ +static void +print_title_tag (FILE *file, const char *name, const char *content) { - /* *INDENT-OFF* */ - {"output-file", 1, 0}, - {"prologue-file", string_arg, 0}, - {"", 0, 0}, - /* *INDENT-ON* */ -}; -static struct outp_option_info option_info; + if (content != NULL) + { + fprintf (file, "<%s>", name); + escape_string (file, content, strlen (content), " ", " - "); + fprintf (file, "\n", name); + } +} static void -html_option (struct outp_driver *this, const char *key, const struct string *val) +html_destroy (struct output_driver *driver) { - struct html_driver_ext *x = this->ext; - int cat, subcat; + struct html_driver *html = html_driver_cast (driver); - cat = outp_match_keyword (key, option_tab, &option_info, &subcat); - switch (cat) + if (html->file != NULL) { - case 0: - msg (SE, _("Unknown configuration parameter `%s' for HTML device " - "driver."), key); - break; - case 1: - free (x->file.filename); - x->file.filename = xstrdup (ds_c_str (val)); - break; - case string_arg: - { - char **dest; - switch (subcat) - { - case 0: - dest = &x->prologue_fn; - break; - default: - assert (0); - abort (); - } - if (*dest) - free (*dest); - *dest = xstrdup (ds_c_str (val)); - } - break; - default: - assert (0); + fprintf (html->file, + "\n" + "\n" + "\n"); + fn_close (html->file_name, html->file); } + free (html->chart_file_name); + free (html->file_name); + free (html->command_name); + free (html); } -/* Variables for the prologue. */ -struct html_variable - { - const char *key; - const char *value; - }; - -static struct html_variable *html_var_tab; - -/* Searches html_var_tab for a html_variable with key KEY, and returns - the associated value. */ -static const char * -html_get_var (const char *key) +static void +html_submit (struct output_driver *driver, + const struct output_item *output_item) { - struct html_variable *v; + struct html_driver *html = html_driver_cast (driver); - for (v = html_var_tab; v->key; v++) - if (!strcmp (key, v->key)) - return v->value; - return NULL; -} + output_driver_track_current_command (output_item, &html->command_name); -/* Writes the HTML prologue to file F. */ -static int -postopen (struct file_ext *f) -{ - static struct html_variable dict[] = + if (is_table_item (output_item)) { - {"generator", 0}, - {"date", 0}, - {"user", 0}, - {"host", 0}, - {"title", 0}, - {"subtitle", 0}, - {0, 0}, - }; - char login[128], host[128]; - time_t curtime; - struct tm *loctime; - - struct outp_driver *this = f->param; - struct html_driver_ext *x = this->ext; - - char *prologue_fn = fn_search_path (x->prologue_fn, config_path, NULL); - FILE *prologue_file; - - char *buf = NULL; - size_t buf_size = 0; - - if (prologue_fn == NULL) - { - msg (IE, _("Cannot find HTML prologue. The use of `-vv' " - "on the command line is suggested as a debugging aid.")); - return 0; + struct table_item *table_item = to_table_item (output_item); + html_output_table (html, table_item_get_table (table_item), + table_item_get_caption (table_item)); } - - msg (VM (1), _("%s: %s: Opening HTML prologue..."), this->name, prologue_fn); - prologue_file = fopen (prologue_fn, "rb"); - if (prologue_file == NULL) +#ifdef HAVE_CAIRO + else if (is_chart_item (output_item) && html->chart_file_name != NULL) { - fclose (prologue_file); - free (prologue_fn); - msg (IE, "%s: %s", prologue_fn, strerror (errno)); - goto error; + struct chart_item *chart_item = to_chart_item (output_item); + char *file_name; + + file_name = xr_draw_png_chart (chart_item, html->chart_file_name, + html->chart_cnt++, + &html->fg, + &html->bg + ); + if (file_name != NULL) + { + const char *title = chart_item_get_title (chart_item); + fprintf (html->file, "\"Chart:", + file_name, title ? title : _("No description")); + free (file_name); + } } - - dict[0].value = version; - - curtime = time (NULL); - loctime = localtime (&curtime); - dict[1].value = asctime (loctime); - { - char *cp = strchr (dict[1].value, '\n'); - if (cp) - *cp = 0; - } - - if (getenv ("LOGNAME") != NULL) - str_copy_rpad (login, sizeof login, getenv ("LOGNAME")); - else if (getlogin_r (login, sizeof login)) - strcpy (login, _("nobody")); - dict[2].value = login; - -#ifdef HAVE_UNISTD_H - if (gethostname (host, 128) == -1) +#endif /* HAVE_CAIRO */ + else if (is_text_item (output_item)) { - if (errno == ENAMETOOLONG) - host[127] = 0; - else - strcpy (host, _("nowhere")); + struct text_item *text_item = to_text_item (output_item); + const char *s = text_item_get_text (text_item); + + switch (text_item_get_type (text_item)) + { + case TEXT_ITEM_TITLE: + print_title_tag (html->file, "H1", s); + break; + + case TEXT_ITEM_SUBTITLE: + print_title_tag (html->file, "H2", s); + break; + + case TEXT_ITEM_COMMAND_OPEN: + fprintf (html->file, "
file, s, strlen (s), "_", "
"); + fprintf (html->file, "\">"); + print_title_tag (html->file, "H3", s); + break; + + case TEXT_ITEM_COMMAND_CLOSE: + fprintf (html->file, "
\n"); + break; + + case TEXT_ITEM_SUBHEAD: + print_title_tag (html->file, "H4", s); + break; + + case TEXT_ITEM_SYNTAX: + fprintf (html->file, "
");
+          escape_string (html->file, s, strlen (s), " ", "
"); + fprintf (html->file, "
\n"); + break; + + case TEXT_ITEM_PARAGRAPH: + print_title_tag (html->file, "P", s); + break; + + case TEXT_ITEM_MONOSPACE: + print_title_tag (html->file, "PRE", s); /* should be

*/ + break; + + case TEXT_ITEM_BLANK_LINE: + fputs ("
", html->file); + break; + + case TEXT_ITEM_EJECT_PAGE: + /* Nothing to do. */ + break; + + case TEXT_ITEM_COMMENT: + case TEXT_ITEM_ECHO: + /* We print out syntax anyway, so nothing to do here either. */ + break; + } } -#else - strcpy (host, _("nowhere")); -#endif - dict[3].value = host; - - dict[4].value = outp_title ? outp_title : ""; - dict[5].value = outp_subtitle ? outp_subtitle : ""; - - html_var_tab = dict; - while (-1 != getline (&buf, &buf_size, prologue_file)) + else if (is_message_item (output_item)) { - char *buf2; - int len; - - if (strstr (buf, "!!!")) - continue; - - { - char *cp = strstr (buf, "!title"); - if (cp) - { - if (outp_title == NULL) - continue; - else - *cp = '\0'; - } - } - - { - char *cp = strstr (buf, "!subtitle"); - if (cp) - { - if (outp_subtitle == NULL) - continue; - else - *cp = '\0'; - } - } - - /* PORTME: Line terminator. */ - buf2 = fn_interp_vars (buf, html_get_var); - len = strlen (buf2); - fwrite (buf2, len, 1, f->file); - if (buf2[len - 1] != '\n') - putc ('\n', f->file); - free (buf2); + const struct message_item *message_item = to_message_item (output_item); + const struct msg *msg = message_item_get_msg (message_item); + char *s = msg_to_string (msg, html->command_name); + print_title_tag (html->file, "P", s); + free (s); } - if (ferror (f->file)) - msg (IE, _("Reading `%s': %s."), prologue_fn, strerror (errno)); - fclose (prologue_file); - - free (prologue_fn); - free (buf); - - if (ferror (f->file)) - goto error; - - msg (VM (2), _("%s: HTML prologue read successfully."), this->name); - return 1; - -error: - msg (VM (1), _("%s: Error reading HTML prologue."), this->name); - return 0; } -/* Writes the HTML epilogue to file F. */ -static int -preclose (struct file_ext *f) -{ - fprintf (f->file, - "\n" - "\n" - "\n"); - - if (ferror (f->file)) - return 0; - return 1; -} - -static int -html_open_page (struct outp_driver *this) +/* Write LENGTH characters in TEXT to file F, escaping characters as necessary + for HTML. Spaces are replaced by SPACE, which should be " " or " " + New-lines are replaced by NEWLINE, which might be "
" or "\n" or + something else appropriate. */ +static void +escape_string (FILE *file, + const char *text, size_t length, + const char *space, const char *newline) { - struct html_driver_ext *x = this->ext; - - assert (this->driver_open && this->page_open == 0); - x->sequence_no++; - if (!fn_open_ext (&x->file)) + while (length-- > 0) { - if (errno) - msg (ME, _("HTML output driver: %s: %s"), x->file.filename, - strerror (errno)); - return 0; + char c = *text++; + switch (c) + { + case '\n': + fputs (newline, file); + break; + case '&': + fputs ("&", file); + break; + case '<': + fputs ("<", file); + break; + case '>': + fputs (">", file); + break; + case ' ': + fputs (space, file); + break; + case '"': + fputs (""", file); + break; + default: + putc (c, file); + break; + } } - - if (!ferror (x->file.file)) - this->page_open = 1; - return !ferror (x->file.file); } -static int -html_close_page (struct outp_driver *this) -{ - struct html_driver_ext *x = this->ext; - - assert (this->driver_open && this->page_open); - this->page_open = 0; - return !ferror (x->file.file); -} - -static void output_tab_table (struct outp_driver *, struct tab_table *); - static void -html_submit (struct outp_driver *this, struct som_entity *s) +put_border (FILE *file, int n_borders, int style, const char *border_name) { - extern struct som_table_class tab_table_class; - struct html_driver_ext *x = this->ext; - - assert (this->driver_open && this->page_open); - if (x->sequence_no == 0 && !html_open_page (this)) - { - msg (ME, _("Cannot open first page on HTML device %s."), this->name); - return; - } - - assert ( s->class == &tab_table_class ) ; - - switch (s->type) - { - case SOM_TABLE: - output_tab_table ( this, (struct tab_table *) s->ext); - break; - case SOM_CHART: - link_image( &x->file, ((struct chart *)s->ext)->filename); - break; - default: - assert(0); - break; - } - + fprintf (file, "%sborder-%s: %s", + n_borders == 0 ? " STYLE=\"" : "; ", + border_name, + style == TAL_1 ? "thin solid" : "double"); } -/* Write string S of length LEN to file F, escaping characters as - necessary for HTML. */ static void -escape_string (FILE *f, char *s, int len) +html_output_table (struct html_driver *html, + const struct table *t, const char *caption) { - char *ep = &s[len]; - char *bp, *cp; + int x, y; - for (bp = cp = s; bp < ep; bp = cp) - { - while (cp < ep && *cp != '&' && *cp != '<' && *cp != '>' && *cp) - cp++; - if (cp > bp) - fwrite (bp, 1, cp - bp, f); - if (cp < ep) - switch (*cp++) - { - case '&': - fputs ("&", f); - break; - case '<': - fputs ("<", f); - break; - case '>': - fputs (">", f); - break; - case 0: - break; - default: - assert (0); - } - } -} - -/* Write table T to THIS output driver. */ -static void -output_tab_table (struct outp_driver *this, struct tab_table *t) -{ - struct html_driver_ext *x = this->ext; - - if (t->nr == 1 && t->nc == 1) + fputs ("\n", html->file); + + if (caption != NULL) { - fputs ("

", x->file.file); - if (!ls_empty_p (t->cc)) - escape_string (x->file.file, ls_c_str (t->cc), ls_length (t->cc)); - fputs ("

\n", x->file.file); - - return; + fputs (" \n", html->file); } - fputs ("
", html->file); + escape_string (html->file, caption, strlen (caption), " ", "
"); + fputs ("
\n", x->file.file); - - if (!ls_empty_p (&t->title)) + for (y = 0; y < table_nr (t); y++) { - fprintf (x->file.file, " \n \n \n", x->file.file); + fputs (" \n", html->file); + for (x = 0; x < table_nc (t); x++) + { + const struct cell_contents *c; + struct table_cell cell; + const char *tag; + bool is_header; + int alignment, colspan, rowspan; + int top, left, right, bottom, n_borders; + + table_get_cell (t, x, y, &cell); + if (x != cell.d[TABLE_HORZ][0] || y != cell.d[TABLE_VERT][0]) + continue; + + /* Output or . */ + fprintf (html->file, "\n", tag); + + table_cell_free (&cell); + } + fputs (" \n", html->file); } - - { - int r; - unsigned char *ct = t->ct; - - for (r = 0; r < t->nr; r++) - { - int c; - - fputs (" \n", x->file.file); - for (c = 0; c < t->nc; c++, ct++) - { - struct fixed_string *cc; - int tag; - char header[128]; - char *cp; - struct tab_joined_cell *j = NULL; - - cc = t->cc + c + r * t->nc; - if (*ct & TAB_JOIN) - { - j = (struct tab_joined_cell *) ls_c_str (cc); - cc = &j->contents; - if (j->x1 != c || j->y1 != r) - continue; - } - - if (r < t->t || r >= t->nr - t->b - || c < t->l || c >= t->nc - t->r) - tag = 'H'; - else - tag = 'D'; - cp = stpcpy (header, " x2 - j->x1 > 1) - cp = spprintf (cp, " COLSPAN=%d", j->x2 - j->x1); - if (j->y2 - j->y1 > 1) - cp = spprintf (cp, " ROWSPAN=%d", j->y2 - j->y1); - - cc = &j->contents; - } - - strcpy (cp, ">"); - fputs (header, x->file.file); - - if ( ! (*ct & TAB_EMPTY) ) - { - char *s = ls_c_str (cc); - size_t l = ls_length (cc); - - while (l && isspace ((unsigned char) *s)) - { - l--; - s++; - } - - escape_string (x->file.file, s, l); - } - - fprintf (x->file.file, "\n", tag); - } - fputs (" \n", x->file.file); - } - } - - fputs ("
", t->nc); - escape_string (x->file.file, ls_c_str (&t->title), - ls_length (&t->title)); - fputs ("
or tag. */ + is_header = (y < table_ht (t) + || y >= table_nr (t) - table_hb (t) + || x < table_hl (t) + || x >= table_nc (t) - table_hr (t)); + tag = is_header ? "TH" : "TD"; + fprintf (html->file, " <%s", tag); + + alignment = (cell.n_contents > 0 + ? cell.contents[0].options & TAB_ALIGNMENT + : TAB_LEFT); + if (alignment != TAB_LEFT) + fprintf (html->file, " ALIGN=\"%s\"", + alignment == TAB_RIGHT ? "RIGHT" : "CENTER"); + + colspan = table_cell_colspan (&cell); + if (colspan > 1) + fprintf (html->file, " COLSPAN=\"%d\"", colspan); + + rowspan = table_cell_rowspan (&cell); + if (rowspan > 1) + fprintf (html->file, " ROWSPAN=\"%d\"", rowspan); + + if (html->borders) + { + /* Cell borders. */ + n_borders = 0; + + top = table_get_rule (t, TABLE_VERT, x, y); + if (top > TAL_GAP) + put_border (html->file, n_borders++, top, "top"); + + if (y + rowspan == table_nr (t)) + { + bottom = table_get_rule (t, TABLE_VERT, x, y + rowspan); + if (bottom > TAL_GAP) + put_border (html->file, n_borders++, bottom, "bottom"); + } + + left = table_get_rule (t, TABLE_HORZ, x, y); + if (left > TAL_GAP) + put_border (html->file, n_borders++, left, "left"); + + if (x + colspan == table_nc (t)) + { + right = table_get_rule (t, TABLE_HORZ, x + colspan, y); + if (right > TAL_GAP) + put_border (html->file, n_borders++, right, "right"); + } + + if (n_borders > 0) + fputs ("\"", html->file); + } + + putc ('>', html->file); + + /* Output cell contents. */ + for (c = cell.contents; c < &cell.contents[cell.n_contents]; c++) + { + if (c->text) + { + const char *s = c->text; + + if (c->options & TAB_EMPH) + fputs ("", html->file); + if (c->options & TAB_FIX) + { + fputs ("", html->file); + escape_string (html->file, s, strlen (s), " ", "
"); + fputs ("
", html->file); + } + else + { + s += strspn (s, CC_SPACES); + escape_string (html->file, s, strlen (s), " ", "
"); + } + if (c->options & TAB_EMPH) + fputs ("
", html->file); + } + else + html_output_table (html, c->table, NULL); + } + + /* Output
\n\n", x->file.file); -} - -static void -html_initialise_chart(struct outp_driver *d UNUSED, struct chart *ch) -{ - - FILE *fp; - - make_unique_file_stream(&fp, &ch->filename); - -#ifdef NO_CHARTS - ch->lp = 0; -#else - ch->pl_params = pl_newplparams(); - ch->lp = pl_newpl_r ("png", 0, fp, stderr, ch->pl_params); -#endif + fputs ("\n\n", html->file); } -static void -html_finalise_chart(struct outp_driver *d UNUSED, struct chart *ch) -{ - free(ch->filename); -} - - - -/* HTML driver class. */ -struct outp_class html_class = -{ - "html", - 0xfaeb, - 1, - - html_open_global, - html_close_global, - NULL, - - html_preopen_driver, - html_option, - html_postopen_driver, - html_close_driver, - - html_open_page, - html_close_page, - - html_submit, - - NULL, - NULL, - NULL, - - NULL, - NULL, - NULL, - NULL, - - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - - html_initialise_chart, - html_finalise_chart - -}; - -#endif /* !NO_HTML */ +struct output_driver_factory html_driver_factory = + { "html", "pspp.html", html_create }; +static const struct output_driver_class html_driver_class = + { + "html", + html_destroy, + html_submit, + NULL, + };