X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Foutput%2Fhtml.c;h=4fd23184ec37e21def2d34c793a9aa21655c37fe;hb=b7994155882996ee11fb1b6b044c429b0d2f8a44;hp=901c98a2a09b4ac61cd4b06bab2cb7ae604de056;hpb=dcf9b154cbcaa35c3d8459a201b77eec8bcb30bd;p=pspp diff --git a/src/output/html.c b/src/output/html.c index 901c98a2a0..4fd23184ec 100644 --- a/src/output/html.c +++ b/src/output/html.c @@ -1,652 +1,499 @@ -/* 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 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 "error.h" +#include "xalloc.h" #include "gettext.h" #define _(msgid) gettext (msgid) -/* Prototypes. */ -static int postopen (struct file_ext *); -static int preclose (struct file_ext *); - -static int -html_open_global (struct outp_class *this UNUSED) -{ - return 1; -} - -static int -html_close_global (struct outp_class *this UNUSED) -{ - return 1; -} - -static int -html_preopen_driver (struct outp_driver *this) -{ - 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; +struct html_driver + { + struct output_driver driver; - x->sequence_no = 0; + char *file_name; + char *chart_file_name; - return 1; -} + char *command_name; + FILE *file; + size_t chart_cnt; -static int -html_postopen_driver (struct outp_driver *this) -{ - 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"); + bool in_syntax; + }; - msg (VM (2), _("%s: Initialization complete."), this->name); - this->driver_open = 1; +static const struct output_driver_class html_driver_class; - return 1; -} +static void html_output_table (struct html_driver *, struct table_item *); +static void escape_string (FILE *file, + const char *text, size_t length, + const char *space); +static void print_title_tag (FILE *file, const char *name, + const char *content); -static int -html_close_driver (struct outp_driver *this) +static struct html_driver * +html_driver_cast (struct output_driver *driver) { - 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; + assert (driver->class == &html_driver_class); + return UP_CAST (driver, struct html_driver, driver); } - -/* Link the image contained in FILENAME to the - HTML stream in file F. */ -static int -link_image (struct file_ext *f, char *filename) +static struct driver_option * +opt (struct output_driver *d, struct string_map *options, const char *key, + const char *default_value) { - fprintf (f->file, - "", filename); - - if (ferror (f->file)) - return 0; - - return 1; + return driver_option_get (d, options, key, default_value); } - -/* 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[] = +static struct output_driver * +html_create (const char *file_name, enum settings_output_devices device_type, + struct string_map *o) { - /* *INDENT-OFF* */ - {"output-file", 1, 0}, - {"prologue-file", string_arg, 0}, - {"", 0, 0}, - /* *INDENT-ON* */ -}; -static struct outp_option_info option_info; - -static void -html_option (struct outp_driver *this, const char *key, const struct string *val) -{ - struct html_driver_ext *x = this->ext; - int cat, subcat; - - cat = outp_match_keyword (key, option_tab, &option_info, &subcat); - switch (cat) + 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->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; + + html->file = fn_open (html->file_name, "w"); + 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); + error (0, errno, _("error opening output file \"%s\""), html->file_name); + goto error; } -} - -/* 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) -{ - struct html_variable *v; - for (v = html_var_tab; v->key; v++) - if (!strcmp (key, v->key)) - return v->value; + 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); + fputs ("\n", html->file); + fputs ("\n", + html->file); + fputs ("\n", html->file); + fputs ("file); + fputs (" LINK=\"#1f00ff\" ALINK=\"#ff0000\" VLINK=\"#9900dd\">\n", html->file); + + return d; + + error: + output_driver_destroy (d); return NULL; } -/* Writes the HTML prologue to file F. */ -static int -postopen (struct file_ext *f) +/* Emits CONTENT to the output, escaping CONTENT as + necessary for HTML. */ +static void +print_title_tag (FILE *file, const char *name, const char *content) { - static struct html_variable dict[] = - { - {"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; - } - - msg (VM (1), _("%s: %s: Opening HTML prologue..."), this->name, prologue_fn); - prologue_file = fopen (prologue_fn, "rb"); - if (prologue_file == NULL) - { - fclose (prologue_file); - free (prologue_fn); - msg (IE, "%s: %s", prologue_fn, strerror (errno)); - goto error; - } - - 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) + if (content != NULL) { - if (errno == ENAMETOOLONG) - host[127] = 0; - else - strcpy (host, _("nowhere")); + fprintf (file, "<%s>", name); + escape_string (file, content, strlen (content), " "); + fprintf (file, "\n", name); } -#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)) - { - 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); - } - 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) +static void +html_destroy (struct output_driver *driver) { - struct html_driver_ext *x = this->ext; + struct html_driver *html = html_driver_cast (driver); - assert (this->driver_open && this->page_open == 0); - x->sequence_no++; - if (!fn_open_ext (&x->file)) + if (html->file != NULL) { - if (errno) - msg (ME, _("HTML output driver: %s: %s"), x->file.filename, - strerror (errno)); - return 0; + if (html->in_syntax) + { + fprintf (html->file, "\n"); + html->in_syntax = false; + } + fprintf (html->file, + "\n" + "\n" + "\n"); + fn_close (html->file_name, html->file); } - - if (!ferror (x->file.file)) - this->page_open = 1; - return !ferror (x->file.file); + free (html->chart_file_name); + free (html->file_name); + free (html->command_name); + free (html); } -static int -html_close_page (struct outp_driver *this) +static bool +is_syntax_item (const struct output_item *item) { - struct html_driver_ext *x = this->ext; - - assert (this->driver_open && this->page_open); - this->page_open = 0; - return !ferror (x->file.file); + return (is_text_item (item) + && text_item_get_type (to_text_item (item)) == TEXT_ITEM_SYNTAX); } -static void output_tab_table (struct outp_driver *, struct tab_table *); - static void -html_submit (struct outp_driver *this, struct som_entity *s) +html_submit (struct output_driver *driver, + const struct output_item *output_item) { - 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; - } + struct html_driver *html = html_driver_cast (driver); - assert ( s->class == &tab_table_class ) ; + output_driver_track_current_command (output_item, &html->command_name); - switch (s->type) + if (html->in_syntax && !is_syntax_item (output_item)) { - 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 (html->file, "\n"); + html->in_syntax = false; } -} - -/* 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) -{ - char *ep = &s[len]; - char *bp, *cp; - - for (bp = cp = s; bp < ep; bp = cp) + if (is_table_item (output_item)) { - 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); - } + struct table_item *table_item = to_table_item (output_item); + html_output_table (html, table_item); } -} - -/* 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) +#ifdef HAVE_CAIRO + else if (is_chart_item (output_item) && html->chart_file_name != 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; + 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++); + if (file_name != NULL) + { + fprintf (html->file, "", file_name); + free (file_name); + } } - - fputs ("\n", x->file.file); - - if (!ls_empty_p (&t->title)) +#endif /* HAVE_CAIRO */ + else if (is_text_item (output_item)) { - fprintf (x->file.file, " \n \n \n", x->file.file); + 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: + if (!html->in_syntax) + { + fprintf (html->file, "
");
+              html->in_syntax = true;
+            }
+          else
+            putc ('\n', html->file);
+          escape_string (html->file, s, strlen (s), " ");
+          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 if (is_message_item (output_item)) + { + 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); } - - { - 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 ("
\n\n", x->file.file); } +/* Write LENGTH characters in TEXT to file F, escaping characters + as necessary for HTML. Spaces are replaced by SPACE, which + should be " " or " ". */ static void -html_initialise_chart(struct outp_driver *d UNUSED, struct chart *ch) +escape_string (FILE *file, + const char *text, size_t length, + const char *space) { - - 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 - + while (length-- > 0) + { + char c = *text++; + switch (c) + { + 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; + } + } } -static void -html_finalise_chart(struct outp_driver *d UNUSED, struct chart *ch) +static void +put_border (FILE *file, int n_borders, int style, const char *border_name) { - free(ch->filename); + fprintf (file, "%sborder-%s: %s", + n_borders == 0 ? " STYLE=\"" : "; ", + border_name, + style == TAL_1 ? "thin solid" : "double"); } - - -/* HTML driver class. */ -struct outp_class html_class = +static void +html_output_table (struct html_driver *html, struct table_item *item) { - "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, + const struct table *t = table_item_get_table (item); + const char *caption; + int x, y; - html_submit, + fputs ("\n", html->file); - NULL, - NULL, - NULL, - - NULL, - NULL, - NULL, - NULL, - - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, + caption = table_item_get_caption (item); + if (caption != NULL) + { + fputs (" \n", html->file); + } - html_initialise_chart, - html_finalise_chart + for (y = 0; y < table_nr (t); y++) + { + fputs (" \n", html->file); + for (x = 0; x < table_nc (t); x++) + { + struct table_cell cell; + const char *tag; + bool is_header; + int alignment, colspan, rowspan; + int top, left, right, bottom, n_borders; + const char *s; + + 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); + } -}; + fputs ("
", html->file); + escape_string (html->file, caption, strlen (caption), " "); + 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.options & TAB_ALIGNMENT; + 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); + + /* 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 == table_nr (t) - 1) + { + bottom = table_get_rule (t, TABLE_VERT, x, y + 1); + 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 == table_nc (t) - 1) + { + right = table_get_rule (t, TABLE_HORZ, x + 1, 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. */ + s = cell.contents; + if (cell.options & TAB_EMPH) + fputs ("", html->file); + if (cell.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 (cell.options & TAB_EMPH) + fputs ("", html->file); + + /* Output
\n\n", html->file); +} -#endif /* !NO_HTML */ +struct output_driver_factory html_driver_factory = { "html", html_create }; +static const struct output_driver_class html_driver_class = + { + "html", + html_destroy, + html_submit, + NULL, + };