Rewrite PSPP output engine. fc11-i386-build74 fc11-x64-build71 sid-i386-build141
authorBen Pfaff <blp@gnu.org>
Sun, 17 Jan 2010 02:07:31 +0000 (18:07 -0800)
committerBen Pfaff <blp@gnu.org>
Sun, 17 Jan 2010 02:07:31 +0000 (18:07 -0800)
This rewrite makes the PSPP output engine much more object-oriented and
extensible than previously.  It should make it much easier to add new
output features, such as cell footnotes and nested tables.  It also makes
minimal changes to code that currently uses the output engine.  The largest
changes are to the implementation of the LIST procedure, which are
necessary because this procedure had a too-intimate relationship with the
details of the output drivers.

The cairo and ascii drivers are now much better at breaking large cells and
large tables across pages and at choosing appropriate sizes for rows and
columns.

This commit adds a new output driver called "csv", which presents output
in comma-separated value format.  It also updates all of the test cases
that produce output to use this output format.

This commit enhances the HTML output significantly and the ODF output
slightly.

This commit adds support for charts to the GUI output.  However, it
temporarily removes the tree-view output summary pane from GUI output.  It
should not be difficult to restore this later.

235 files changed:
.gitignore
Makefile.am
NEWS
README
configure.ac
doc/introduction.texi
perl-module/automake.mk
perl-module/t/Pspp.t
src/data/make-file.c
src/language/command.c
src/language/data-io/data-parser.c
src/language/data-io/list.q
src/language/data-io/print-space.c
src/language/data-io/print.c
src/language/dictionary/split-file.c
src/language/dictionary/sys-file-info.c
src/language/lexer/lexer.c
src/language/prompt.c
src/language/stats/binomial.c
src/language/stats/chisquare.c
src/language/stats/correlations.c
src/language/stats/crosstabs.q
src/language/stats/descriptives.c
src/language/stats/examine.q
src/language/stats/factor.c
src/language/stats/frequencies.q
src/language/stats/glm.q
src/language/stats/npar-summary.c
src/language/stats/oneway.q
src/language/stats/rank.q
src/language/stats/regression.q
src/language/stats/reliability.q
src/language/stats/roc.c
src/language/stats/sign.c
src/language/stats/t-test.q
src/language/stats/wilcoxon.c
src/language/syntax-file.c
src/language/tests/paper-size.c
src/language/utilities/echo.c
src/language/utilities/include.c
src/language/utilities/set.q
src/language/utilities/title.c
src/output/ascii.c
src/output/automake.mk
src/output/cairo-chart.c [new file with mode: 0644]
src/output/cairo-chart.h [new file with mode: 0644]
src/output/cairo.c
src/output/cairo.h
src/output/chart-item-provider.h [new file with mode: 0644]
src/output/chart-item.c [new file with mode: 0644]
src/output/chart-item.h [new file with mode: 0644]
src/output/chart-provider.h [deleted file]
src/output/chart.c [deleted file]
src/output/chart.h [deleted file]
src/output/charts/boxplot-cairo.c [new file with mode: 0644]
src/output/charts/boxplot.c
src/output/charts/boxplot.h
src/output/charts/cartesian.c [deleted file]
src/output/charts/cartesian.h [deleted file]
src/output/charts/np-plot-cairo.c [new file with mode: 0644]
src/output/charts/np-plot.c
src/output/charts/np-plot.h
src/output/charts/piechart-cairo.c [new file with mode: 0644]
src/output/charts/piechart.c
src/output/charts/piechart.h
src/output/charts/plot-chart.c [deleted file]
src/output/charts/plot-chart.h [deleted file]
src/output/charts/plot-hist-cairo.c [new file with mode: 0644]
src/output/charts/plot-hist.c
src/output/charts/plot-hist.h
src/output/charts/roc-chart-cairo.c [new file with mode: 0644]
src/output/charts/roc-chart.c
src/output/charts/roc-chart.h
src/output/charts/scree-cairo.c [new file with mode: 0644]
src/output/charts/scree.c
src/output/charts/scree.h
src/output/csv.c [new file with mode: 0644]
src/output/driver-provider.h [new file with mode: 0644]
src/output/driver.c [new file with mode: 0644]
src/output/driver.h [new file with mode: 0644]
src/output/html.c
src/output/htmlP.h [deleted file]
src/output/manager.c [deleted file]
src/output/manager.h [deleted file]
src/output/measure.c [new file with mode: 0644]
src/output/measure.h [new file with mode: 0644]
src/output/mk-class-boilerplate [new file with mode: 0755]
src/output/odt.c
src/output/options.c [new file with mode: 0644]
src/output/options.h [new file with mode: 0644]
src/output/output-item-provider.h [new file with mode: 0644]
src/output/output-item.c [new file with mode: 0644]
src/output/output-item.h [new file with mode: 0644]
src/output/output.c [deleted file]
src/output/output.h [deleted file]
src/output/render.c [new file with mode: 0644]
src/output/render.h [new file with mode: 0644]
src/output/tab.c [new file with mode: 0644]
src/output/tab.h [new file with mode: 0644]
src/output/table-casereader.c [new file with mode: 0644]
src/output/table-item.c [new file with mode: 0644]
src/output/table-item.h [new file with mode: 0644]
src/output/table-paste.c [new file with mode: 0644]
src/output/table-provider.h [new file with mode: 0644]
src/output/table-select.c [new file with mode: 0644]
src/output/table-transpose.c [new file with mode: 0644]
src/output/table.c
src/output/table.h
src/output/text-item.c [new file with mode: 0644]
src/output/text-item.h [new file with mode: 0644]
src/ui/gui/executor.c
src/ui/gui/psppire-output-window.c
src/ui/gui/psppire-output-window.h
src/ui/gui/psppire.c
src/ui/source-init-opts.c
src/ui/terminal/main.c
src/ui/terminal/msg-ui.c
src/ui/terminal/read-line.c
src/ui/terminal/terminal-opts.c
tests/.gitignore
tests/atlocal.in [new file with mode: 0644]
tests/automake.mk
tests/bugs/agg-crash-2.sh
tests/bugs/compute-lv.sh
tests/bugs/computebug.out [deleted file]
tests/bugs/computebug.sh
tests/bugs/computebug.stat [deleted file]
tests/bugs/crosstabs-crash.sh
tests/bugs/crosstabs-crash2.sh
tests/bugs/crosstabs2.sh
tests/bugs/examine-missing2.sh
tests/bugs/get.sh
tests/bugs/keep-all.sh
tests/bugs/match-files-scratch.sh
tests/bugs/multipass.sh
tests/bugs/overwrite-input-file.sh
tests/bugs/overwrite-special-file.sh
tests/bugs/random.sh
tests/bugs/recode-copy-bug-1.out [deleted file]
tests/bugs/recode-copy-bug-1.stat [deleted file]
tests/bugs/recode-copy-bug-2.out [deleted file]
tests/bugs/recode-copy-bug-2.stat [deleted file]
tests/bugs/recode-copy-bug.sh
tests/bugs/shbang.sh
tests/bugs/t-test-alpha.sh
tests/bugs/t-test-alpha3.sh
tests/bugs/t-test-paired.sh
tests/bugs/t-test-with-temp.sh
tests/bugs/temp-freq.sh
tests/bugs/temporary.sh
tests/command/add-files.sh
tests/command/aggregate.sh
tests/command/attributes.sh
tests/command/autorecod.sh
tests/command/beg-data.sh
tests/command/correlation.sh
tests/command/count.sh
tests/command/data-list.sh
tests/command/do-if.sh
tests/command/do-repeat.sh
tests/command/examine-extremes.sh
tests/command/examine-percentiles.sh
tests/command/examine.sh
tests/command/file-handle.sh
tests/command/file-label.sh
tests/command/filter.sh
tests/command/flip.sh
tests/command/get-data-gnm.sh
tests/command/get-data-psql.sh
tests/command/get-data-txt-examples.sh
tests/command/get-data-txt-importcases.sh
tests/command/get-data-txt.sh
tests/command/import-export.sh
tests/command/input-program.sh
tests/command/insert.sh
tests/command/lag.sh
tests/command/line-ends.sh
tests/command/list.sh
tests/command/longvars.sh
tests/command/loop.sh
tests/command/match-files.sh
tests/command/n_of_cases.sh
tests/command/no_case_size.sh
tests/command/npar-binomial.sh
tests/command/npar-chisquare.sh
tests/command/npar-sign.sh
tests/command/npar-wilcoxon.sh
tests/command/oneway-missing.sh
tests/command/oneway-with-splits.sh
tests/command/oneway.sh
tests/command/print.sh
tests/command/rank.sh
tests/command/regression-qr.sh
tests/command/regression.sh
tests/command/reliability.sh
tests/command/rename.sh
tests/command/roc.sh
tests/command/roc2.sh
tests/command/sample.sh
tests/command/split-file.sh
tests/command/sysfile-info.sh
tests/command/sysfiles-old.sh
tests/command/sysfiles.sh
tests/command/t-test-1-indep-val.sh
tests/command/t-test-1-sample-missing-anal.sh
tests/command/t-test-1-sample-missing-list.sh
tests/command/t-test-1s.sh
tests/command/t-test-groups.sh
tests/command/t-test-indep-missing-anal.sh
tests/command/t-test-indep-missing-list.sh
tests/command/t-test-paired-missing-anal.sh
tests/command/t-test-paired-missing-list.sh
tests/command/t-test-pairs.sh
tests/command/tabs.sh
tests/command/trimmed-mean.sh
tests/command/update.sh
tests/command/use.sh
tests/command/variable-display.sh
tests/command/vector.sh
tests/command/very-long-strings.sh
tests/command/weight.sh
tests/expressions/valuelabel.sh
tests/expressions/variables.sh
tests/expressions/vectors.sh
tests/formats/360.sh
tests/output/render-test.c [new file with mode: 0644]
tests/output/render.at [new file with mode: 0644]
tests/stats/descript-basic.sh
tests/stats/descript-mean-bug.sh
tests/stats/descript-missing.sh
tests/stats/ntiles.sh
tests/stats/percentiles-compatible.sh
tests/stats/percentiles-enhanced.sh
tests/testsuite.at [new file with mode: 0644]
tests/xforms/recode.sh

index ad89bf36e4884d85df1c47754057f8b2e1635d28..56d869b2b4951552e97c3d0a5afdf2794216bce1 100644 (file)
@@ -39,3 +39,4 @@ gitlog-to-changelog
 *.deps
 *.la
 *.libs
+/package.m4
index 02ae7d5b2129e882d72e65559bf81c9d7d3ad5fd..0022e1d70c4f785268868e2bc72455c8302e452b 100644 (file)
@@ -34,6 +34,7 @@ EXTRA_DIST = OChangeLog ONEWS config.rpath pspp-mode.el
 CLEANFILES = 
 CLEAN_LOCAL =
 ALL_LOCAL =
+CHECK_LOCAL =
 ACLOCAL_AMFLAGS = -I m4 -I gl/m4
 noinst_LIBRARIES=
 noinst_LTLIBRARIES=
@@ -86,6 +87,7 @@ uninstall-hook: $(UNINSTALL_DATA_HOOKS)
 
 clean-local: $(CLEAN_LOCAL)
 all-local: $(ALL_LOCAL)
+check-local: $(CHECK_LOCAL)
 
 # A convenience target to build all the binaries
 programs: $(PROGRAMS)
diff --git a/NEWS b/NEWS
index ab21afde986538b1ac362b74cedc3191f243112f..f1a0ee4e6348acb01b2ef4ab54b5ebb8e8c580a6 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,5 +1,5 @@
 PSPP NEWS -- history of user-visible changes.
-Time-stamp: <2009-12-05 20:39:07 blp>
+Time-stamp: <2009-12-05 20:44:30 blp>
 Copyright (C) 1996-9, 2000, 2008, 2009 Free Software Foundation, Inc.
 See the end for copying conditions.
 
@@ -12,7 +12,10 @@ Changes from 0.7.2 to 0.7.3:
    not have Cairo and Pango installed, you must run `configure' with
    --without-cairo.
 
- * The PostScript driver has been removed.
+ * The new "cairo" output driver supports output in PostScript, PDF,
+   and SVG formats.  Its functionality is a superset of that of the
+   "postscript" driver, which has been removed.  You must have Cairo
+   and Pango installed to build the "cairo" driver.
 
 Changes from 0.7.1 to 0.7.2:
 
diff --git a/README b/README
index 02e81d77e9525093cc3252d38903717b6252b9eb..e54877a10fdb90c31c1c766985084c91953b4d50 100644 (file)
--- a/README
+++ b/README
@@ -4,7 +4,7 @@ is a free replacement for the proprietary program SPSS.
 PSPP development is ongoing. It already supports a large subset of
 SPSS's syntax.  Its statistical procedure support is currently
 limited, but growing.  At your option, PSPP will produce statistical
-reports in ASCII, PostScript, or HTML formats. 
+reports in ASCII, PostScript, PDF, HTML, or SVG formats. 
 
 Instructions for PSPP installation are in INSTALL, including a list of
 prerequisite packages and other PSPP-specific information.  Full
@@ -52,8 +52,8 @@ following support to users:
 
        * Attractive output, including graphs, in a variety of human-
           and machine-readable formats.  PSPP currently produces
-          output in ASCII, PostScript, and HTML formats.  We will
-          enhance PSPP's output formatting in the future.
+          output in ASCII, PostScript, PDF, HTML, and SVG formats.  We
+          will enhance PSPP's output formatting in the future.
 
        * Good documentation.  Currently the PSPP manual describes its
           language completely, but we would like to add information on
index fd60137ca233b4684c1893f2c99458508b5e1610..ea9f69fd4b3fbc95efc10facfa2f69ea81b865cb 100644 (file)
@@ -1,9 +1,10 @@
 dnl Process this file with autoconf to produce a configure script.
 
 dnl Initialize.
-AC_PREREQ(2.60)
+AC_PREREQ(2.63)
 AC_INIT([pspp],[0.7.3],[bug-gnu-pspp@gnu.org])
 AC_CONFIG_HEADERS([config.h])
+AC_CONFIG_TESTDIR([tests])
 AM_INIT_AUTOMAKE
 
 dnl Checks for programs.
@@ -228,7 +229,7 @@ RELOCATABLE_STRIP=:
 
 PSPP_CHECK_PREREQS
 
-AC_CONFIG_FILES([Makefile gl/Makefile])
+AC_CONFIG_FILES([Makefile gl/Makefile tests/atlocal])
 
 AC_OUTPUT
 echo "PSPP configured successfully."
index 735a54c5a20720859c62c4103f3b4ff1afb5b253..f96a2329e3747afa6b6cafc7e5142626a4208ead 100644 (file)
@@ -15,15 +15,15 @@ later in this manual.
 @cindex files, PSPP
 @cindex output, PSPP
 @cindex PostScript
+@cindex PDF
+@cindex HTML
+@cindex SVG
 @cindex graphics
 @cindex Ghostscript
 @cindex Free Software Foundation
-PSPP produces output in two forms: tables and charts.  Both of these can
-be written in several formats; currently, ASCII, PostScript, and HTML
-are supported.  In the future, more drivers, such as PCL and X Window
-System drivers, may be developed.  For now, Ghostscript, available from
-the Free Software Foundation, may be used to convert PostScript chart
-output to other formats.
+PSPP produces tables and charts as output, which it can produce in
+several formats; currently, ASCII, PostScript, PDF, HTML, and SVG are
+supported.
 
 The current version of PSPP, @value{VERSION}, is woefully incomplete in
 terms of its statistical procedure support.  PSPP is a work in progress.
index ef9acc3790c8bc93e13b44dc89c3fcecc46cf3b3..8eeb0a341ddd0c96a7a70bf6438c6167d391f09e 100644 (file)
@@ -34,6 +34,7 @@ PHONY += module-make
 module-make: perl-module/Makefile src/libpspp-core.la
        cd perl-module && $(MAKE) $(AM_MAKEFLAGS)
 
+ALL_LOCAL += perl_module_tarball
 perl_module_tarball:
        if test x"$(top_builddir)" != x"$(top_srcdir)" ; then \
         for f in $(module_sources); do \
@@ -47,22 +48,19 @@ perl_module_tarball:
        fi
        $(MAKE) $(AM_MAKEFLAGS) module-make perl-module/PSPP-Perl-$(VERSION_FOR_PERL).tar.gz
 
-ALL_LOCAL += perl_module_tarball
-
-check-local:
+CHECK_LOCAL += perl_module_check
+perl_module_check:
        loc=`pwd` ; cd $(top_builddir)/src/.libs ; llp=`pwd` ; cd $$loc ;  \
        LANG=C LD_LIBRARY_PATH=$$llp sh -c "cd perl-module && $(MAKE) $(AM_MAKEFLAGS) test"
 
-
-perl_module_CLEAN:
+CLEAN_LOCAL += perl_module_clean
+perl_module_clean:
        cd perl-module && $(MAKE) $(AM_MAKEFLAGS) clean || true
        if test x"$(top_builddir)" != x"$(top_srcdir)" ; then \
          rm -f $(module_sources) ; \
        fi
        rm -f perl-module/Makefile.old
 
-CLEAN_LOCAL += perl_module_CLEAN
-
 CLEANFILES += \
         perl-module/PSPP-Perl-$(VERSION_FOR_PERL).tar.gz \
        perl-module/pspp-module-config \
index 92180aba9cde097c4409b2d71f6d78c66974504c..4f65a4b5b2838e1322b6f12dea65bd1a85d60a6d 100644 (file)
@@ -40,7 +40,7 @@ sub run_pspp_syntax
     print FH "$syntax";
     close (FH);
 
-    system ("cd $tempdir; $pspp_cmd -o raw-ascii $syntaxfile");
+    system ("cd $tempdir; $pspp_cmd --testing-mode $syntaxfile");
 }
 
 sub run_pspp_syntax_cmp
@@ -52,7 +52,7 @@ sub run_pspp_syntax_cmp
 
     run_pspp_syntax ($tempdir, $syntax);
 
-    my $diff =  diff ("$tempdir/pspp.list", \$result);
+    my $diff =  diff ("$tempdir/pspp.csv", \$result);
 
     if ( ! ($diff eq ""))
     {
@@ -175,32 +175,27 @@ sub run_pspp_syntax_cmp
        DISPLAY DOCUMENTS.
        LIST.
 SYNTAX
-1.1 DISPLAY.  
-+--------+-------------------------------------------+--------+
-|Variable|Description                                |Position|
-#========#===========================================#========#
-|id      |Format: F2.0                               |       1|
-|        |Measure: Scale                             |        |
-|        |Display Alignment: Right                   |        |
-|        |Display Width: 8                           |        |
-+--------+-------------------------------------------+--------+
-|name    |Format: A20                                |       2|
-|        |Measure: Nominal                           |        |
-|        |Display Alignment: Left                    |        |
-|        |Display Width: 20                          |        |
-+--------+-------------------------------------------+--------+
+Variable,Description,,Position
+id,Format: F2.0,,1
+,Measure: Scale,,
+,Display Alignment: Right,,
+,Display Width: 8,,
+name,Format: A20,,2
+,Measure: Nominal,,
+,Display Alignment: Left,,
+,Display Width: 20,,
 
 File label:
+
 This is the file label
 
 Documents in the active file:
 
 This is a document line
 
-id                 name
--- --------------------
-21 wheelbarrow          
-
+Table: Data List
+id,name
+21,wheelbarrow         
 RESULT
 
 
@@ -256,40 +251,30 @@ RESULT
 GET FILE='$tempfile'.
 DISPLAY DICTIONARY.
 SYNTAX
-1.1 DISPLAY.  
-+----------+---------------------------------------------+--------+
-|Variable  |Description                                  |Position|
-#==========#=============================================#========#
-|integer   |My Integer                                   |       1|
-|          |Format: F8.0                                 |        |
-|          |Measure: Scale                               |        |
-|          |Display Alignment: Right                     |        |
-|          |Display Width: 8                             |        |
-|          |Missing Values: 9; 99                        |        |
-|          +---------+-----------------------------------+        |
-|          |        0|Zero                               |        |
-|          |        1|Unity                              |        |
-|          |        2|Duality                            |        |
-+----------+---------+-----------------------------------+--------+
-|string    |My String                                    |       2|
-|          |Format: A8                                   |        |
-|          |Measure: Nominal                             |        |
-|          |Display Alignment: Left                      |        |
-|          |Display Width: 8                             |        |
-|          |Missing Values: "this    "; "that    "       |        |
-|          +---------+-----------------------------------+        |
-|          | xx      |foo                                |        |
-|          | yy      |bar                                |        |
-+----------+---------+-----------------------------------+--------+
-|longstring|My Long String                               |       3|
-|          |Format: A9                                   |        |
-|          |Measure: Nominal                             |        |
-|          |Display Alignment: Left                      |        |
-|          |Display Width: 9                             |        |
-|          +---------+-----------------------------------+        |
-|          |xxx      |xfoo                               |        |
-+----------+---------+-----------------------------------+--------+
-
+Variable,Description,,Position
+integer,My Integer,,1
+,Format: F8.0,,
+,Measure: Scale,,
+,Display Alignment: Right,,
+,Display Width: 8,,
+,Missing Values: 9; 99,,
+,0,Zero,
+,1,Unity,
+,2,Duality,
+string,My String,,2
+,Format: A8,,
+,Measure: Nominal,,
+,Display Alignment: Left,,
+,Display Width: 8,,
+,"Missing Values: ""this    ""; ""that    """,,
+,xx      ,foo,
+,yy      ,bar,
+longstring,My Long String,,3
+,Format: A9,,
+,Measure: Nominal,,
+,Display Alignment: Left,,
+,Display Width: 9,,
+,xxx      ,xfoo,
 RESULT
 
   }
@@ -493,7 +478,7 @@ EOF
 
 SYNTAX
 
- system ("cp $tempdir/pspp.list $tempdir/in.txt");
+ system ("cp $tempdir/pspp.csv $tempdir/in.txt");
 
  run_pspp_syntax ($tempdir, <<SYNTAX);
  get file='$tempdir/out.sav'.
@@ -502,7 +487,7 @@ SYNTAX
 
 SYNTAX
  
- ok (! diff ("$tempdir/pspp.list", "$tempdir/in.txt"), "Streaming of files");
+ ok (! diff ("$tempdir/pspp.csv", "$tempdir/in.txt"), "Streaming of files");
 }
 
 
index e4c9234044a687a078d198c2ec85f5385cd4f7dd..bcb62906d4154c39ca2c6749a2b3e330680767ee 100644 (file)
@@ -22,6 +22,7 @@
 #include <errno.h>
 #include <stdio.h>
 #include <sys/stat.h>
+#include <unistd.h>
 
 #include <data/file-name.h>
 #include <data/make-file.h>
index 4c3612fb290977674247b7bd2d66801afa56f818..803b5398888dec3c739c748afa8c13059d272ec3 100644 (file)
 #include <ctype.h>
 #include <errno.h>
 #include <unistd.h>
+#if HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
+#if HAVE_READLINE
+#include <readline/readline.h>
+#endif
 
 #include <data/casereader.h>
 #include <data/dictionary.h>
 #include <libpspp/message.h>
 #include <libpspp/message.h>
 #include <libpspp/str.h>
-#include <output/manager.h>
 #include <libpspp/getl.h>
-
-#if HAVE_SYS_WAIT_H
-#include <sys/wait.h>
-#endif
-
-#if HAVE_READLINE
-#include <readline/readline.h>
-#endif
+#include <output/text-item.h>
 
 #include "xalloc.h"
 #include "xmalloca.h"
@@ -142,11 +140,7 @@ cmd_parse_in_state (struct lexer *lexer, struct dataset *ds,
 {
   int result;
 
-  som_new_series ();
-
   result = do_parse_command (lexer, ds, state);
-  if (cmd_result_is_failure (result))
-    lex_discard_rest_of_command (lexer);
 
   assert (!proc_is_open (ds));
   unset_cmd_algorithm ();
@@ -174,8 +168,9 @@ static enum cmd_result
 do_parse_command (struct lexer *lexer,
                  struct dataset *ds, enum cmd_state state)
 {
-  const struct command *command;
+  const struct command *command = NULL;
   enum cmd_result result;
+  bool opened = false;
 
   /* Read the command's first token. */
   prompt_set_style (PROMPT_FIRST);
@@ -202,53 +197,56 @@ do_parse_command (struct lexer *lexer,
       result = CMD_FAILURE;
       goto finish;
     }
-  else if (command->function == NULL)
+  text_item_submit (text_item_create (TEXT_ITEM_COMMAND_OPEN, command->name));
+  opened = true;
+
+  if (command->function == NULL)
     {
       msg (SE, _("%s is not yet implemented."), command->name);
       result = CMD_NOT_IMPLEMENTED;
-      goto finish;
     }
   else if ((command->flags & F_TESTING) && !settings_get_testing_mode ())
     {
       msg (SE, _("%s may be used only in testing mode."), command->name);
       result = CMD_FAILURE;
-      goto finish;
     }
   else if ((command->flags & F_ENHANCED) && settings_get_syntax () != ENHANCED)
     {
       msg (SE, _("%s may be used only in enhanced syntax mode."),
            command->name);
       result = CMD_FAILURE;
-      goto finish;
     }
   else if (!in_correct_state (command, state))
     {
       report_state_mismatch (command, state);
       result = CMD_FAILURE;
-      goto finish;
     }
-
-  /* Execute command. */
-  msg_set_command_name (command->name);
-  som_set_command_name (command->name);
-  result = command->function (lexer, ds);
-  som_set_command_name (NULL);
-  msg_set_command_name (NULL);
+  else
+    {
+      /* Execute command. */
+      msg_set_command_name (command->name);
+      result = command->function (lexer, ds);
+      msg_set_command_name (NULL);
+    }
 
   assert (cmd_result_is_valid (result));
 
  finish:
-  if ( cmd_result_is_failure (result))
+  if (cmd_result_is_failure (result))
     {
-      const struct source_stream *cs = lex_get_source_stream (lexer);
-
-      if ( source_stream_current_error_mode (cs) == ERRMODE_STOP )
+      lex_discard_rest_of_command (lexer);
+      if (source_stream_current_error_mode (
+            lex_get_source_stream (lexer)) == ERRMODE_STOP )
        {
          msg (MW, _("Error encountered while ERROR=STOP is effective."));
          result = CMD_CASCADING_FAILURE;
        }
     }
 
+  if (opened)
+    text_item_submit (text_item_create (TEXT_ITEM_COMMAND_CLOSE,
+                                        command->name));
+
   return result;
 }
 
index fe78aeb36ed272f309481870d79ea6669d48d735..eb578e270d3e9fee03e5b9c50e3fb688abad7b2f 100644 (file)
@@ -31,7 +31,7 @@
 #include <language/data-io/data-reader.h>
 #include <libpspp/message.h>
 #include <libpspp/str.h>
-#include <output/table.h>
+#include <output/tab.h>
 
 #include "xalloc.h"
 
@@ -645,7 +645,6 @@ dump_fixed_table (const struct data_parser *parser,
   size_t i;
 
   t = tab_create (4, parser->field_cnt + 1);
-  tab_columns (t, TAB_COL_DOWN);
   tab_headers (t, 0, 0, 1, 0);
   tab_text (t, 0, 0, TAB_CENTER | TAT_TITLE, _("Variable"));
   tab_text (t, 1, 0, TAB_CENTER | TAT_TITLE, _("Record"));
@@ -653,7 +652,6 @@ dump_fixed_table (const struct data_parser *parser,
   tab_text (t, 3, 0, TAB_CENTER | TAT_TITLE, _("Format"));
   tab_box (t, TAL_1, TAL_1, TAL_0, TAL_1, 0, 0, 3, parser->field_cnt);
   tab_hline (t, TAL_2, 0, 3, 1);
-  tab_dim (t, tab_natural_dimensions, NULL, NULL);
 
   for (i = 0; i < parser->field_cnt; i++)
     {
@@ -686,13 +684,11 @@ dump_delimited_table (const struct data_parser *parser,
   size_t i;
 
   t = tab_create (2, parser->field_cnt + 1);
-  tab_columns (t, TAB_COL_DOWN);
   tab_headers (t, 0, 0, 1, 0);
   tab_text (t, 0, 0, TAB_CENTER | TAT_TITLE, _("Variable"));
   tab_text (t, 1, 0, TAB_CENTER | TAT_TITLE, _("Format"));
   tab_box (t, TAL_1, TAL_1, TAL_0, TAL_1, 0, 0, 1, parser->field_cnt);
   tab_hline (t, TAL_2, 0, 1, 1);
-  tab_dim (t, tab_natural_dimensions, NULL, NULL);
 
   for (i = 0; i < parser->field_cnt; i++)
     {
index 871fa69ef4db6369fcda469e77eb81c1c9adae90..60f51b523b0c0250abbe4ae43753880be464ff4b 100644 (file)
@@ -29,7 +29,7 @@
 #include <data/data-out.h>
 #include <data/format.h>
 #include <data/procedure.h>
-#include <data/short-names.h>
+#include <data/subcase.h>
 #include <data/variable.h>
 #include <language/command.h>
 #include <language/dictionary/split-file.h>
 #include <libpspp/ll.h>
 #include <libpspp/message.h>
 #include <libpspp/misc.h>
-#include <output/htmlP.h>
-#include <output/manager.h>
-#include <output/output.h>
-#include <output/table.h>
+#include <output/tab.h>
+#include <output/table-item.h>
 
 #include "minmax.h"
 #include "xalloc.h"
 /* (declarations) */
 /* (functions) */
 
-/* Layout for one output driver. */
-struct list_target
-  {
-    struct ll ll;
-    struct outp_driver *driver;
-    int type;          /* 0=Values and labels fit across the page. */
-    size_t n_vertical; /* Number of labels to list vertically. */
-    size_t header_rows;        /* Number of header rows. */
-    char **header;     /* The header itself. */
-  };
-
 /* Parsed command. */
 static struct cmd_list cmd;
 
-/* Line buffer. */
-static struct string line_buffer;
-
-/* TTY-style output functions. */
-static unsigned n_lines_remaining (struct outp_driver *d);
-static unsigned n_chars_width (struct outp_driver *d);
-static void write_line (struct outp_driver *d, const char *s);
-
-/* Other functions. */
-static void list_case (const struct ccase *, casenumber case_idx,
-                       const struct dataset *, struct ll_list *targets);
-static void determine_layout (struct ll_list *targets);
-static void clean_up (struct ll_list *targets);
-static void write_header (struct list_target *);
-static void write_all_headers (struct casereader *, const struct dataset *,
-                               struct ll_list *targets);
-
-/* Returns the number of text lines that can fit on the remainder of
-   the page. */
-static inline unsigned
-n_lines_remaining (struct outp_driver *d)
-{
-  int diff;
-
-  diff = d->length - d->cp_y;
-  return (diff > 0) ? (diff / d->font_height) : 0;
-}
-
-/* Returns the number of fixed-width character that can fit across the
-   page. */
-static inline unsigned
-n_chars_width (struct outp_driver *d)
-{
-  return d->width / d->fixed_width;
-}
-
-/* Writes the line S at the current position and advances to the next
-   line.  */
-static void
-write_line (struct outp_driver *d, const char *s)
-{
-  struct outp_text text;
-
-  assert (d->cp_y + d->font_height <= d->length);
-  text.font = OUTP_FIXED;
-  text.justification = OUTP_LEFT;
-  text.string = ss_cstr (s);
-  text.x = d->cp_x;
-  text.y = d->cp_y;
-  text.h = text.v = INT_MAX;
-  d->class->text_draw (d, &text);
-  d->cp_x = 0;
-  d->cp_y += d->font_height;
-}
-
 /* Parses and executes the LIST procedure. */
 int
 cmd_list (struct lexer *lexer, struct dataset *ds)
 {
   struct dictionary *dict = dataset_dict (ds);
-  struct variable *casenum_var = NULL;
   struct casegrouper *grouper;
   struct casereader *group;
-  struct ll_list targets;
-  casenumber case_idx;
+  struct subcase sc;
+  size_t i;
   bool ok;
 
   if (!parse_list (lexer, ds, &cmd, NULL))
@@ -190,570 +121,67 @@ cmd_list (struct lexer *lexer, struct dataset *ds)
       cmd.step = 1;
     }
 
-  /* Case number. */
-  if (cmd.numbering == LST_NUMBERED)
-    {
-      /* Initialize the case-number variable. */
-      int width = cmd.last == LONG_MAX ? 5 : intlog10 (cmd.last);
-      struct fmt_spec format = fmt_for_output (FMT_F, width, 0);
-      casenum_var = var_create ("Case#", 0);
-      var_set_both_formats (casenum_var, &format);
-
-      /* Add the case-number variable at the beginning of the variable list. */
-      cmd.n_variables++;
-      cmd.v_variables = xnrealloc (cmd.v_variables,
-                                   cmd.n_variables, sizeof *cmd.v_variables);
-      memmove (&cmd.v_variables[1], &cmd.v_variables[0],
-              (cmd.n_variables - 1) * sizeof *cmd.v_variables);
-      cmd.v_variables[0] = casenum_var;
-    }
-
-  determine_layout (&targets);
+  subcase_init_empty (&sc);
+  for (i = 0; i < cmd.n_variables; i++)
+    subcase_add_var (&sc, cmd.v_variables[i], SC_ASCEND);
 
-  case_idx = 0;
-  for (grouper = casegrouper_create_splits (proc_open (ds), dict);
-       casegrouper_get_next_group (grouper, &group);
-       casereader_destroy (group))
+  grouper = casegrouper_create_splits (proc_open (ds), dict);
+  while (casegrouper_get_next_group (grouper, &group))
     {
-      struct ccase *c;
-
-      write_all_headers (group, ds, &targets);
-      for (; (c = casereader_read (group)) != NULL; case_unref (c))
-        {
-          case_idx++;
-          if (case_idx >= cmd.first && case_idx <= cmd.last
-              && (case_idx - cmd.first) % cmd.step == 0)
-            list_case (c, case_idx, ds, &targets);
-        }
-    }
-  ok = casegrouper_destroy (grouper);
-  ok = proc_commit (ds) && ok;
-
-  ds_destroy(&line_buffer);
-
-  clean_up (&targets);
-
-  var_destroy (casenum_var);
-
-  return ok ? CMD_SUCCESS : CMD_CASCADING_FAILURE;
-}
-
-/* Writes headers to all devices.  This is done at the beginning of
-   each SPLIT FILE group. */
-static void
-write_all_headers (struct casereader *input, const struct dataset *ds,
-                   struct ll_list *targets)
-{
-  struct list_target *target;
-  struct ccase *c;
-
-  c = casereader_peek (input, 0);
-  if (c == NULL)
-    return;
-  output_split_file_values (ds, c);
-  case_unref (c);
-
-  ll_for_each (target, struct list_target, ll, targets)
-    {
-      struct outp_driver *d = target->driver;
-      if (!d->class->special)
-       {
-         d->cp_y += d->font_height;            /* Blank line. */
-         write_header (target);
-       }
-      else if (d->class == &html_class)
-       {
-         struct html_driver_ext *x = d->ext;
-
-         fputs ("<TABLE BORDER=1>\n  <TR>\n", x->file);
+      struct ccase *ccase;
+      struct table *t;
 
-         {
-           size_t i;
+      group = casereader_project (group, &sc);
+      if (cmd.numbering == LST_NUMBERED)
+        group = casereader_create_arithmetic_sequence (group, 1, 1);
+      group = casereader_select (group, cmd.first - 1,
+                                 (cmd.last != LONG_MAX ? cmd.last
+                                  : CASENUMBER_MAX), cmd.step);
 
-           for (i = 0; i < cmd.n_variables; i++)
-             fprintf (x->file, "    <TH><EM>%s</EM></TH>\n",
-                      var_get_name (cmd.v_variables[i]));
-         }
-
-         fputs ("  </TR>\n", x->file);
-       }
-      else
-       NOT_REACHED ();
-    }
-}
-
-/* Writes the headers.  Some of them might be vertical; most are
-   probably horizontal. */
-static void
-write_header (struct list_target *target)
-{
-  struct outp_driver *d = target->driver;
-
-  if (d->class->special || !target->header_rows)
-    return;
-
-  if (n_lines_remaining (d) < target->header_rows + 1)
-    {
-      outp_eject_page (d);
-      assert (n_lines_remaining (d) >= target->header_rows + 1);
-    }
-
-  /* Design the header. */
-  if (!target->header)
-    {
-      size_t i;
-      size_t x;
-
-      /* Allocate, initialize header. */
-      target->header = xnmalloc (target->header_rows, sizeof *target->header);
-      {
-       int w = n_chars_width (d);
-       for (i = 0; i < target->header_rows; i++)
-         {
-           target->header[i] = xmalloc (w + 1);
-           memset (target->header[i], ' ', w);
-         }
-      }
-
-      /* Put in vertical names. */
-      for (i = x = 0; i < target->n_vertical; i++)
-       {
-         const struct variable *v = cmd.v_variables[i];
-          const char *name = var_get_name (v);
-          size_t name_len = strlen (name);
-          const struct fmt_spec *print = var_get_print_format (v);
-         size_t j;
-
-         memset (&target->header[target->header_rows - 1][x], '-', print->w);
-         x += print->w - 1;
-         for (j = 0; j < name_len; j++)
-           target->header[name_len - j - 1][x] = name[j];
-         x += 2;
-       }
-
-      /* Put in horizontal names. */
-      for (; i < cmd.n_variables; i++)
-       {
-         const struct variable *v = cmd.v_variables[i];
-          const char *name = var_get_name (v);
-          size_t name_len = strlen (name);
-          const struct fmt_spec *print = var_get_print_format (v);
-
-         memset (&target->header[target->header_rows - 1][x], '-',
-                 MAX (print->w, (int) name_len));
-         if ((int) name_len < print->w)
-           x += print->w - name_len;
-         memcpy (&target->header[0][x], name, name_len);
-         x += name_len + 1;
-       }
-
-      /* Add null bytes. */
-      for (i = 0; i < target->header_rows; i++)
-       {
-         for (x = n_chars_width (d); x >= 1; x--)
-           if (target->header[i][x - 1] != ' ')
-             {
-               target->header[i][x] = 0;
-               break;
-             }
-         assert (x);
-       }
-    }
-
-  /* Write out the header, in back-to-front order except for the last line. */
-  if (target->header_rows >= 2)
-    {
-      size_t i;
-
-      for (i = target->header_rows - 1; i-- != 0; )
-        write_line (d, target->header[i]);
-    }
-  write_line (d, target->header[target->header_rows - 1]);
-}
-
-
-/* Frees up all the memory we've allocated. */
-static void
-clean_up (struct ll_list *targets)
-{
-  struct list_target *target, *next;
-
-  ll_for_each_safe (target, next, struct list_target, ll, targets)
-    {
-      struct outp_driver *d = target->driver;
-      if (d->class->special == 0)
+      ccase = casereader_peek (group, 0);
+      if (ccase != NULL)
         {
-          if (target->header)
-            {
-              size_t i;
-              for (i = 0; i < target->header_rows; i++)
-                free (target->header[i]);
-              free (target->header);
-            }
-        }
-      else if (d->class == &html_class)
-        {
-          if (d->page_open)
-            {
-              struct html_driver_ext *x = d->ext;
-
-              fputs ("</TABLE>\n", x->file);
-            }
+          output_split_file_values (ds, ccase);
+          case_unref (ccase);
         }
-      else
-        NOT_REACHED ();
-
-      ll_remove (&target->ll);
-      free (target);
-    }
-  
-  free (cmd.v_variables);
-}
 
-/* Writes string STRING at the current position.  If the text would
-   fall off the side of the page, then advance to the next line,
-   indenting by amount INDENT. */
-static void
-write_varname (struct outp_driver *d, char *string, int indent)
-{
-  struct outp_text text;
-  int width;
-
-  if (d->cp_x + outp_string_width (d, string, OUTP_FIXED) > d->width)
-    {
-      d->cp_y += d->font_height;
-      if (d->cp_y + d->font_height > d->length)
-       outp_eject_page (d);
-      d->cp_x = indent;
-    }
-
-  text.font = OUTP_FIXED;
-  text.justification = OUTP_LEFT;
-  text.string = ss_cstr (string);
-  text.x = d->cp_x;
-  text.y = d->cp_y;
-  text.h = text.v = INT_MAX;
-  d->class->text_draw (d, &text);
-  d->class->text_metrics (d, &text, &width, NULL);
-  d->cp_x += width;
-}
-
-/* When we can't fit all the values across the page, we write out all
-   the variable names just once.  This is where we do it. */
-static void
-write_fallback_headers (struct outp_driver *d)
-{
-  const int max_width = n_chars_width(d) - 10;
-
-  int index = 0;
-  int width = 0;
-  int line_number = 0;
-
-  const char *Line = _("Line");
-  char *leader = xmalloca (strlen (Line)
-                           + INT_STRLEN_BOUND (line_number) + 1 + 1);
-
-  while (index < cmd.n_variables)
-    {
-      struct outp_text text;
-      int leader_width;
-
-      /* Ensure that there is enough room for a line of text. */
-      if (d->cp_y + d->font_height > d->length)
-       outp_eject_page (d);
-
-      /* The leader is a string like `Line 1: '.  Write the leader. */
-      sprintf (leader, "%s %d:", Line, ++line_number);
-      text.font = OUTP_FIXED;
-      text.justification = OUTP_LEFT;
-      text.string = ss_cstr (leader);
-      text.x = 0;
-      text.y = d->cp_y;
-      text.h = text.v = INT_MAX;
-      d->class->text_draw (d, &text);
-      d->class->text_metrics (d, &text, &leader_width, NULL);
-      d->cp_x = leader_width;
-
-      goto entry;
-      do
-       {
-         width++;
-
-       entry:
-         {
-           int var_width = var_get_print_format (cmd.v_variables[index])->w;
-           if (width + var_width > max_width && width != 0)
-             {
-               width = 0;
-               d->cp_x = 0;
-               d->cp_y += d->font_height;
-               break;
-             }
-           width += var_width;
-         }
-
-         {
-           char varname[VAR_NAME_LEN + 2];
-           snprintf (varname, sizeof varname,
-                      " %s", var_get_name (cmd.v_variables[index]));
-           write_varname (d, varname, leader_width);
-         }
-       }
-      while (++index < cmd.n_variables);
-
-    }
-  d->cp_x = 0;
-  d->cp_y += d->font_height;
-
-  freea (leader);
-}
-
-/* There are three possible layouts for the LIST procedure:
-
-   1. If the values and their variables' name fit across the page,
-   then they are listed across the page in that way.
-
-   2. If the values can fit across the page, but not the variable
-   names, then as many variable names as necessary are printed
-   vertically to compensate.
-
-   3. If not even the values can fit across the page, the variable
-   names are listed just once, at the beginning, in a compact format,
-   and the values are listed with a variable name label at the
-   beginning of each line for easier reference.
-
-   This is complicated by the fact that we have to do all this for
-   every output driver, not just once.  */
-static void
-determine_layout (struct ll_list *targets)
-{
-  struct outp_driver *d;
-
-  /* This is the largest page width of any driver, so we can tell what
-     size buffer to allocate. */
-  int largest_page_width = 0;
-
-  ll_init (targets);
-  for (d = outp_drivers (NULL); d; d = outp_drivers (d))
-    {
-      size_t column;   /* Current column. */
-      int width;       /* Accumulated width. */
-      int height;       /* Height of vertical names. */
-      int max_width;   /* Page width. */
-
-      struct list_target *target;
-
-      target = xmalloc (sizeof *target);
-      ll_push_tail (targets, &target->ll);
-      target->driver = d;
-      target->type = 0;
-      target->n_vertical = 0;
-      target->header = NULL;
-
-      if (d->class == &html_class)
-       continue;
-      assert (d->class->special == 0);
-
-      outp_open_page (d);
-
-      max_width = n_chars_width (d);
-      largest_page_width = MAX (largest_page_width, max_width);
-
-      /* Try layout #1. */
-      for (width = cmd.n_variables - 1, column = 0; column < cmd.n_variables; column++)
-       {
-         const struct variable *v = cmd.v_variables[column];
-          int fmt_width = var_get_print_format (v)->w;
-          int name_len = strlen (var_get_name (v));
-         width += MAX (fmt_width, name_len);
-       }
-      if (width <= max_width)
-       {
-         target->header_rows = 2;
-         continue;
-       }
-
-      /* Try layout #2. */
-      for (width = cmd.n_variables - 1, height = 0, column = 0;
-          column < cmd.n_variables && width <= max_width;
-          column++)
+      if (cmd.numbering == LST_NUMBERED)
         {
-          const struct variable *v = cmd.v_variables[column];
-          int fmt_width = var_get_print_format (v)->w;
-          size_t name_len = strlen (var_get_name (v));
-          width += fmt_width;
-          if (name_len > height)
-            height = name_len;
-        }
-
-      /* If it fit then we need to determine how many labels can be
-         written horizontally. */
-      if (width <= max_width && height <= SHORT_NAME_LEN)
-       {
-#ifndef NDEBUG
-         target->n_vertical = SIZE_MAX;
-#endif
-         for (column = cmd.n_variables; column-- != 0; )
-           {
-             const struct variable *v = cmd.v_variables[column];
-              int name_len = strlen (var_get_name (v));
-              int fmt_width = var_get_print_format (v)->w;
-             int trial_width = width - fmt_width + MAX (fmt_width, name_len);
-             if (trial_width > max_width)
-               {
-                 target->n_vertical = column + 1;
-                 break;
-               }
-             width = trial_width;
-           }
-         assert (target->n_vertical != SIZE_MAX);
-
-         target->n_vertical = cmd.n_variables;
-         /* Finally determine the length of the headers. */
-         for (target->header_rows = 0, column = 0;
-              column < target->n_vertical;
-              column++)
-            {
-              const struct variable *var = cmd.v_variables[column];
-              size_t name_len = strlen (var_get_name (var));
-              target->header_rows = MAX (target->header_rows, name_len);
-            }
-         target->header_rows++;
-         continue;
-       }
-
-      /* Otherwise use the ugly fallback listing format. */
-      target->type = 1;
-      target->header_rows = 0;
-
-      d->cp_y += d->font_height;
-      write_fallback_headers (d);
-      d->cp_y += d->font_height;
-    }
-
-  ds_init_empty (&line_buffer);
-}
+          struct fmt_spec fmt;
+          size_t col;
+          int width;
 
-/* Writes case C to output. */
-static void
-list_case (const struct ccase *c, casenumber case_idx,
-           const struct dataset *ds, struct ll_list *targets)
-{
-  struct dictionary *dict = dataset_dict (ds);
-  const char *encoding = dict_get_encoding (dict);
-  struct list_target *target;
+          width = cmd.last == LONG_MAX ? 5 : intlog10 (cmd.last);
+          fmt = fmt_for_output (FMT_F, width, 0);
+          col = caseproto_get_n_widths (casereader_get_proto (group)) - 1;
 
-  ll_for_each (target, struct list_target, ll, targets)
-    {
-      struct outp_driver *d = target->driver;
+          t = table_from_casereader (group, col, _("Case Number"), &fmt);
+        }
+      else
+        t = NULL;
 
-      if (d->class->special == 0)
+      for (i = 0; i < cmd.n_variables; i++)
         {
-          const int max_width = n_chars_width (d);
-          int column;
-
-          if (!target->header_rows)
-            {
-              ds_put_format(&line_buffer, "%8s: ",
-                            var_get_name (cmd.v_variables[0]));
-            }
-
-
-          for (column = 0; column < cmd.n_variables; column++)
-            {
-              const struct variable *v = cmd.v_variables[column];
-              const struct fmt_spec *print = var_get_print_format (v);
-              int width;
-              char *s;
-
-              if (target->type == 0 && column >= target->n_vertical)
-                {
-                  int name_len = strlen (var_get_name (v));
-                  width = MAX (name_len, print->w);
-                }
-              else
-                width = print->w;
+          const struct variable *var = cmd.v_variables[i];
+          struct table *c;
 
-              if (width + ds_length(&line_buffer) > max_width &&
-                  ds_length(&line_buffer) != 0)
-                {
-                  if (!n_lines_remaining (d))
-                    {
-                      outp_eject_page (d);
-                      write_header (target);
-                    }
-
-                  write_line (d, ds_cstr (&line_buffer));
-                  ds_clear(&line_buffer);
-
-                  if (!target->header_rows)
-                    ds_put_format (&line_buffer, "%8s: ", var_get_name (v));
-                }
-
-              if (width > print->w)
-                ds_put_char_multiple(&line_buffer, ' ', width - print->w);
-
-              if (fmt_is_string (print->type) || dict_contains_var (dict, v))
-                s = data_out (case_data (c, v), encoding, print);
-              else
-                {
-                  union value case_idx_value;
-                  case_idx_value.f = case_idx;
-                  s = data_out (&case_idx_value, encoding, print);
-                }
-
-              ds_put_cstr (&line_buffer, s);
-              free (s);
-              ds_put_char(&line_buffer, ' ');
-            }
-
-          if (!n_lines_remaining (d))
-            {
-              outp_eject_page (d);
-              write_header (target);
-            }
-
-          write_line (d, ds_cstr (&line_buffer));
-          ds_clear(&line_buffer);
+          c = table_from_casereader (group, i, var_get_name (var),
+                                     var_get_print_format (var));
+          t = table_hpaste (t, c);
         }
-      else if (d->class == &html_class)
-        {
-          struct html_driver_ext *x = d->ext;
-          int column;
-
-          fputs ("  <TR>\n", x->file);
 
-          for (column = 0; column < cmd.n_variables; column++)
-            {
-              const struct variable *v = cmd.v_variables[column];
-              const struct fmt_spec *print = var_get_print_format (v);
-              char *s;
+      casereader_destroy (group);
 
-              if (fmt_is_string (print->type)
-                  || dict_contains_var (dict, v))
-                s = data_out (case_data (c, v), encoding, print);
-              else
-                {
-                  union value case_idx_value;
-                  case_idx_value.f = case_idx;
-                  s = data_out (&case_idx_value, encoding, print);
-                }
-
-              fputs ("    <TD>", x->file);
-              html_put_cell_contents (d, TAB_FIX, ss_cstr (s));
-              fputs ("</TD>\n", x->file);
+      table_item_submit (table_item_create (t, "Data List"));
+    }
+  ok = casegrouper_destroy (grouper);
+  ok = proc_commit (ds) && ok;
 
-              free (s);
-            }
+  subcase_destroy (&sc);
 
-          fputs ("  </TR>\n", x->file);
-        }
-      else
-        NOT_REACHED ();
-    }
+  return ok ? CMD_SUCCESS : CMD_CASCADING_FAILURE;
 }
 
-
 /*
    Local Variables:
    mode: c
index 3f73ee25a8de16fb1f1883608e2c6fa8cf25fdba..25bcc750516964264f5bf99e9fd15fed251ed405 100644 (file)
@@ -26,8 +26,8 @@
 #include <language/data-io/file-handle.h>
 #include <language/expressions/public.h>
 #include <language/lexer/lexer.h>
-#include <output/manager.h>
 #include <libpspp/message.h>
+#include <output/text-item.h>
 
 #include "xalloc.h"
 
@@ -123,7 +123,7 @@ print_space_trns_proc (void *t_, struct ccase **c,
 
   while (n--)
     if (trns->writer == NULL)
-      som_blank_line ();
+      text_item_submit (text_item_create (TEXT_ITEM_BLANK_LINE, ""));
     else
       dfm_put_record (trns->writer, " ", 1);
 
index 9c8f56e84289196182fc10bb8599e26a35c8f3b3..fd98eaedcc319fe5b44faf0294aa7dce6265a020 100644 (file)
@@ -38,8 +38,8 @@
 #include <libpspp/message.h>
 #include <libpspp/misc.h>
 #include <libpspp/pool.h>
-#include <output/manager.h>
-#include <output/table.h>
+#include <output/text-item.h>
+#include <output/tab.h>
 
 #include "xalloc.h"
 
@@ -397,7 +397,6 @@ dump_table (struct print_trns *trns, const struct file_handle *fh)
 
   spec_cnt = ll_count (&trns->specs);
   t = tab_create (4, spec_cnt + 1);
-  tab_columns (t, TAB_COL_DOWN);
   tab_box (t, TAL_1, TAL_1, TAL_0, TAL_1, 0, 0, 3, spec_cnt);
   tab_hline (t, TAL_2, 0, 3, 1);
   tab_headers (t, 0, 0, 1, 0);
@@ -405,7 +404,6 @@ dump_table (struct print_trns *trns, const struct file_handle *fh)
   tab_text (t, 1, 0, TAB_CENTER | TAT_TITLE, _("Record"));
   tab_text (t, 2, 0, TAB_CENTER | TAT_TITLE, _("Columns"));
   tab_text (t, 3, 0, TAB_CENTER | TAT_TITLE, _("Format"));
-  tab_dim (t, tab_natural_dimensions, NULL, NULL);
   row = 1;
   ll_for_each (spec, struct prt_out_spec, ll, &trns->specs)
     {
@@ -516,14 +514,14 @@ flush_records (struct print_trns *trns, int target_record,
         {
           *eject = false;
           if (trns->writer == NULL)
-            som_eject_page ();
+            text_item_submit (text_item_create (TEXT_ITEM_EJECT_PAGE, ""));
           else
             leader = '1';
         }
       line[0] = legacy_from_native (trns->encoding, leader);
 
       if (trns->writer == NULL)
-        tab_output_text (TAB_FIX | TAT_NOWRAP, &line[1]);
+        tab_output_text (TAB_FIX, &line[1]);
       else
         {
           if (!trns->include_prefix)
index d2f59ccc813fe8661435bb03bee0cbf1a01caa20..d27ab3a65b3ab1b34c6c74b9a8cb1b011287a7a2 100644 (file)
@@ -31,8 +31,7 @@
 #include <language/lexer/variable-parser.h>
 #include <libpspp/message.h>
 #include <libpspp/str.h>
-#include <output/manager.h>
-#include <output/table.h>
+#include <output/tab.h>
 
 #include "xalloc.h"
 
@@ -78,7 +77,6 @@ output_split_file_values (const struct dataset *ds, const struct ccase *c)
     return;
 
   t = tab_create (3, split_cnt + 1);
-  tab_dim (t, tab_natural_dimensions, NULL, NULL);
   tab_vline (t, TAL_GAP, 1, 0, split_cnt);
   tab_vline (t, TAL_GAP, 2, 0, split_cnt);
   tab_text (t, 0, 0, TAB_NONE, _("Variable"));
@@ -104,6 +102,5 @@ output_split_file_values (const struct dataset *ds, const struct ccase *c)
       if (val_lab)
        tab_text (t, 2, i + 1, TAB_LEFT, val_lab);
     }
-  tab_flags (t, SOMF_NO_TITLE);
   tab_submit (t);
 }
index 6c44a0c3baaefefbda7129b081442468106d7c9c..1f47e1af104ba5281f1634eda67f81b9f49c3412 100644 (file)
@@ -39,9 +39,7 @@
 #include <libpspp/message.h>
 #include <libpspp/message.h>
 #include <libpspp/misc.h>
-#include <output/manager.h>
-#include <output/output.h>
-#include <output/table.h>
+#include <output/tab.h>
 
 #include "minmax.h"
 #include "xalloc.h"
@@ -66,23 +64,6 @@ enum
 static int describe_variable (const struct variable *v, struct tab_table *t,
                               int r, int pc, int flags);
 
-/* Sets the widths of all the columns and heights of all the rows in
-   table T for driver D. */
-static void
-sysfile_info_dim (struct tab_rendering *r, void *aux UNUSED)
-{
-  const struct tab_table *t = r->table;
-  static const int max[] = {20, 5, 35, 3, 0};
-  const int *p;
-  int i;
-
-  for (p = max; *p; p++)
-    r->w[p - max] = MIN (tab_natural_width (r, p - max),
-                         *p * r->driver->prop_em_width);
-  for (i = 0; i < tab_nr (t); i++)
-    r->h[i] = tab_natural_height (r, i);
-}
-
 /* SYSFILE INFO utility. */
 int
 cmd_sysfile_info (struct lexer *lexer, struct dataset *ds UNUSED)
@@ -161,11 +142,9 @@ cmd_sysfile_info (struct lexer *lexer, struct dataset *ds UNUSED)
                    dict_get_encoding(d) ? dict_get_encoding(d) : _("Unknown"));
 
 
-  tab_dim (t, tab_natural_dimensions, NULL, NULL);
   tab_submit (t);
 
   t = tab_create (4, 1 + 2 * dict_get_var_cnt (d));
-  tab_dim (t, sysfile_info_dim, NULL, NULL);
   tab_headers (t, 0, 0, 1, 0);
   tab_text (t, 0, 0, TAB_LEFT | TAT_TITLE, _("Variable"));
   tab_joint_text (t, 1, 0, 2, 0, TAB_LEFT | TAT_TITLE, _("Description"));
@@ -180,7 +159,6 @@ cmd_sysfile_info (struct lexer *lexer, struct dataset *ds UNUSED)
   tab_vline (t, TAL_1, 3, 0, r);
 
   tab_resize (t, -1, r);
-  tab_flags (t, SOMF_NO_TITLE);
   tab_submit (t);
 
   dict_destroy (d);
@@ -213,7 +191,6 @@ cmd_display (struct lexer *lexer, struct dataset *ds)
     display_documents (dataset_dict (ds));
   else if (lex_match_id (lexer, "FILE"))
     {
-      som_blank_line ();
       if (!lex_force_match_id (lexer, "LABEL"))
        return CMD_FAILURE;
       if (dict_get_label (dataset_dict (ds)) == NULL)
@@ -310,7 +287,6 @@ cmd_display (struct lexer *lexer, struct dataset *ds)
 static void
 display_macros (void)
 {
-  som_blank_line ();
   tab_output_text (TAB_LEFT, _("Macros not supported."));
 }
 
@@ -319,7 +295,6 @@ display_documents (const struct dictionary *dict)
 {
   const char *documents = dict_get_documents (dict);
 
-  som_blank_line ();
   if (documents == NULL)
     tab_output_text (TAB_LEFT, _("The active file dictionary does not "
                                  "contain any documents."));
@@ -330,50 +305,19 @@ display_documents (const struct dictionary *dict)
 
       tab_output_text (TAB_LEFT | TAT_TITLE,
                       _("Documents in the active file:"));
-      som_blank_line ();
       for (i = 0; i < dict_get_document_line_cnt (dict); i++)
         {
           dict_get_document_line (dict, i, &line);
-          tab_output_text (TAB_LEFT | TAB_FIX | TAT_NOWRAP, ds_cstr (&line));
+          tab_output_text (TAB_LEFT | TAB_FIX, ds_cstr (&line));
         }
       ds_destroy (&line);
     }
 }
 
-struct variables_dim_aux
-  {
-    int flags;
-  };
-
-/* Sets the widths of all the columns and heights of all the rows in
-   table T for driver D. */
-static void
-variables_dim (struct tab_rendering *r, void *aux_)
-{
-  const struct outp_driver *d = r->driver;
-  struct variables_dim_aux *aux = aux_;
-
-  tab_natural_dimensions (r, NULL);
-  if (aux->flags & (DF_VALUE_LABELS | DF_VARIABLE_LABELS | DF_MISSING_VALUES
-                    | DF_AT_ATTRIBUTES | DF_ATTRIBUTES))
-    {
-      r->w[1] = MAX (r->w[1], d->prop_em_width * 5);
-      r->w[2] = MAX (r->w[2], d->prop_em_width * 35);
-    }
-}
-
-static void
-variables_dim_free (void *aux_)
-{
-  struct variables_dim_aux *aux = aux_;
-  free (aux);
-}
-
 static void
 display_variables (const struct variable **vl, size_t n, int flags)
 {
   struct tab_table *t;
-  struct variables_dim_aux *aux;
   int nc;                      /* Number of columns. */
   int pc;                      /* `Position column' */
   int r;                       /* Current row. */
@@ -400,10 +344,6 @@ display_variables (const struct variable **vl, size_t n, int flags)
   if (flags & DF_DICT_INDEX)
     tab_text (t, pc, 0, TAB_LEFT | TAT_TITLE, _("Position"));
 
-  aux = xmalloc (sizeof *aux);
-  aux->flags = flags;
-  tab_dim (t, variables_dim, variables_dim_free, aux);
-
   r = 1;
   for (i = 0; i < n; i++)
     r = describe_variable (vl[i], t, r, pc, flags);
@@ -413,12 +353,9 @@ display_variables (const struct variable **vl, size_t n, int flags)
       tab_box (t, TAL_1, TAL_1, -1, -1, 0, 0, nc - 1, r - 1);
       tab_vline (t, TAL_1, 1, 0, r - 1);
     }
-  else
-    tab_flags (t, SOMF_NO_TITLE);
   if (flags & ~DF_DICT_INDEX)
     tab_vline (t, TAL_1, nc - 1, 0, r - 1);
   tab_resize (t, -1, r);
-  tab_columns (t, TAB_COL_DOWN);
   tab_submit (t);
 }
 \f
@@ -490,8 +427,6 @@ display_data_file_attributes (struct attrset *set, int flags)
   tab_text (t, 0, 0, TAB_LEFT | TAT_TITLE, _("Attribute"));
   tab_text (t, 1, 0, TAB_LEFT | TAT_TITLE, _("Value"));
   display_attributes (t, set, flags, 0, 1);
-  tab_columns (t, TAB_COL_DOWN);
-  tab_dim (t, tab_natural_dimensions, NULL, NULL);
   tab_title (t, "Custom data file attributes.");
   tab_submit (t);
 }
@@ -720,8 +655,6 @@ display_vectors (const struct dictionary *dict, int sorted)
 
   t = tab_create (4, nrow + 1);
   tab_headers (t, 0, 0, 1, 0);
-  tab_columns (t, TAB_COL_DOWN);
-  tab_dim (t, tab_natural_dimensions, NULL, NULL);
   tab_box (t, TAL_1, TAL_1, -1, -1, 0, 0, 3, nrow);
   tab_box (t, -1, -1, -1, TAL_1, 0, 0, 3, nrow);
   tab_hline (t, TAL_2, 0, 3, 1);
@@ -729,7 +662,6 @@ display_vectors (const struct dictionary *dict, int sorted)
   tab_text (t, 1, 0, TAT_TITLE | TAB_LEFT, _("Position"));
   tab_text (t, 2, 0, TAT_TITLE | TAB_LEFT, _("Variable"));
   tab_text (t, 3, 0, TAT_TITLE | TAB_LEFT, _("Print Format"));
-  tab_flags (t, SOMF_NO_TITLE);
 
   row = 1;
   for (i = 0; i < nvec; i++)
index 8b3f2a48d00474811a09c090f8a82d1b75ed7546..5ff0b8bcd38d83c2e8c17446c97535e76f7f5ad1 100644 (file)
@@ -32,6 +32,7 @@
 #include <libpspp/getl.h>
 #include <libpspp/str.h>
 #include <output/journal.h>
+#include <output/text-item.h>
 
 #include "xalloc.h"
 
@@ -873,16 +874,17 @@ lex_preprocess_line (struct string *line,
     }
 }
 
-/* Reads a line, without performing any preprocessing.
-   Sets *SYNTAX, if SYNTAX is non-null, to the line's syntax
-   mode. */
+/* Reads a line, without performing any preprocessing. */
 bool
 lex_get_line_raw (struct lexer *lexer)
 {
   bool ok = getl_read_line (lexer->ss, &lexer->line_buffer);
-  enum syntax_mode mode = lex_current_syntax_mode (lexer);
-  journal_write (mode == GETL_BATCH, ds_cstr (&lexer->line_buffer));
-
+  if (ok)
+    {
+      const char *line = ds_cstr (&lexer->line_buffer);
+      journal_write (lex_current_syntax_mode (lexer) == GETL_BATCH, line);
+      text_item_submit (text_item_create (TEXT_ITEM_SYNTAX, line));
+    }
   return ok;
 }
 
index 5ecd42fb34fa6b91a2322aecbecc3f400af43f7d..34690d503d960d491c427e1e44f01b565651966b 100644 (file)
@@ -33,7 +33,7 @@
 #include <libpspp/str.h>
 #include <libpspp/verbose-msg.h>
 #include <libpspp/version.h>
-#include <output/table.h>
+#include <output/tab.h>
 
 #include "xalloc.h"
 
index bc87a94da3316b0c0cf0b99c0035092a7d93a8cb..3c8925803bfd5741623120e21c412ab8caa35d2e 100644 (file)
@@ -16,7 +16,7 @@
 
 #include <config.h>
 #include <libpspp/compiler.h>
-#include <output/table.h>
+#include <output/tab.h>
 
 #include <data/format.h>
 #include <data/case.h>
@@ -189,8 +189,6 @@ binomial_execute (const struct dataset *ds,
 
       struct tab_table *table = tab_create (7, ost->n_vars * 3 + 1);
 
-      tab_dim (table, tab_natural_dimensions, NULL, NULL);
-
       tab_title (table, _("Binomial Test"));
 
       tab_headers (table, 2, 0, 1, 0);
index bc1b6474ff16c167d06ea94ca55de828475d7a9e..1f2df8d29a2417395836e4f9b81a9f84e722a959 100644 (file)
@@ -36,7 +36,7 @@
 #include <libpspp/hash.h>
 #include <libpspp/message.h>
 #include <libpspp/taint.h>
-#include <output/table.h>
+#include <output/tab.h>
 
 #include <gsl/gsl_cdf.h>
 
@@ -181,7 +181,6 @@ create_variable_frequency_table (const struct dictionary *dict,
     }
 
   table = tab_create(4, n_cells + 2);
-  tab_dim (table, tab_natural_dimensions, NULL, NULL);
 
   tab_title (table, var_to_string(var));
   tab_text (table, 1, 0, TAB_LEFT, _("Observed N"));
@@ -217,7 +216,6 @@ create_combo_frequency_table (const struct chisquare_test *test)
   int n_cells = test->hi - test->lo + 1;
 
   table = tab_create(1 + ost->n_vars * 4, n_cells + 3);
-  tab_dim (table, tab_natural_dimensions, NULL, NULL);
 
   tab_title (table, _("Frequencies"));
   for ( i = 0 ; i < ost->n_vars ; ++i )
@@ -273,7 +271,6 @@ create_stats_table (const struct chisquare_test *test)
 
   struct tab_table *table;
   table = tab_create (1 + ost->n_vars, 4);
-  tab_dim (table, tab_natural_dimensions, NULL, NULL);
   tab_title (table, _("Test Statistics"));
   tab_headers (table, 1, 0, 1, 0);
 
index c575205ccf2e8b8858e06383854e13f340baaf98..7fcca77cc7a883acbd05c8cdac604eb7bcc66544 100644 (file)
@@ -30,8 +30,7 @@
 #include <language/dictionary/split-file.h>
 #include <language/lexer/lexer.h>
 #include <language/lexer/variable-parser.h>
-#include <output/manager.h>
-#include <output/table.h>
+#include <output/tab.h>
 #include <libpspp/message.h>
 #include <data/format.h>
 #include <math/moments.h>
@@ -96,7 +95,6 @@ output_descriptives (const struct corr *corr, const gsl_matrix *means,
 
   struct tab_table *t = tab_create (nc, nr);
   tab_title (t, _("Descriptive Statistics"));
-  tab_dim (t, tab_natural_dimensions, NULL, NULL);
 
   tab_headers (t, heading_columns, 0, heading_rows, 0);
 
@@ -191,7 +189,6 @@ output_correlation (const struct corr *corr, const struct corr_opts *opts,
 
   t = tab_create (nc, nr);
   tab_title (t, _("Correlations"));
-  tab_dim (t, tab_natural_dimensions, NULL, NULL);
 
   tab_headers (t, heading_columns, 0, heading_rows, 0);
 
index 5695ed6478b64490095ea0c8abde83873fb38030..bb4acafa62c6cd279bb7622dc4ff18206bd415e3 100644 (file)
@@ -56,8 +56,7 @@
 #include <libpspp/misc.h>
 #include <libpspp/pool.h>
 #include <libpspp/str.h>
-#include <output/output.h>
-#include <output/table.h>
+#include <output/tab.h>
 
 #include "minmax.h"
 #include "xalloc.h"
@@ -201,12 +200,6 @@ struct crosstabs_proc
     unsigned int statistics;    /* Bit k is 1 if statistic k is requested. */
   };
 
-/* Auxiliary data structure for tab_dim. */
-struct crosstabs_dim_aux
-  {
-    enum mv_class exclude;
-  };
-
 static void
 init_proc (struct crosstabs_proc *proc, struct dataset *ds)
 {
@@ -249,8 +242,7 @@ static void tabulate_general_case (struct pivot_table *, const struct ccase *,
 static void tabulate_integer_case (struct pivot_table *, const struct ccase *,
                                    double weight);
 static void postcalc (struct crosstabs_proc *);
-static void submit (struct crosstabs_proc *, struct pivot_table *,
-                    struct tab_table *);
+static void submit (struct pivot_table *, struct tab_table *);
 
 /* Parse and execute CROSSTABS, then clean up. */
 int
@@ -897,7 +889,7 @@ make_summary_table (struct crosstabs_proc *proc)
     }
   ds_destroy (&name);
 
-  submit (proc, NULL, summary);
+  submit (NULL, summary);
 }
 \f
 /* Output. */
@@ -920,8 +912,6 @@ static void display_symmetric (struct crosstabs_proc *, struct pivot_table *,
 static void display_risk (struct pivot_table *, struct tab_table *);
 static void display_directional (struct crosstabs_proc *, struct pivot_table *,
                                  struct tab_table *);
-static void crosstabs_dim (struct tab_rendering *, void *aux);
-static void crosstabs_dim_free (void *aux);
 static void table_value_missing (struct crosstabs_proc *proc,
                                  struct tab_table *table, int c, int r,
                                 unsigned char opt, const union value *v,
@@ -1029,18 +1019,18 @@ output_pivot_table (struct crosstabs_proc *proc, struct pivot_table *pt)
       free (x.col_tot);
     }
 
-  submit (proc, NULL, table);
+  submit (NULL, table);
 
   if (chisq)
     {
       if (!showed_fisher)
        tab_resize (chisq, 4 + (pt->n_vars - 2), -1);
-      submit (proc, pt, chisq);
+      submit (pt, chisq);
     }
 
-  submit (proc, pt, sym);
-  submit (proc, pt, risk);
-  submit (proc, pt, direct);
+  submit (pt, sym);
+  submit (pt, risk);
+  submit (pt, direct);
 
   free (pt->cols);
 }
@@ -1348,10 +1338,8 @@ delete_missing (struct pivot_table *pt)
 
 /* Prepare table T for submission, and submit it. */
 static void
-submit (struct crosstabs_proc *proc, struct pivot_table *pt,
-        struct tab_table *t)
+submit (struct pivot_table *pt, struct tab_table *t)
 {
-  struct crosstabs_dim_aux *aux;
   int i;
 
   if (t == NULL)
@@ -1360,7 +1348,7 @@ submit (struct crosstabs_proc *proc, struct pivot_table *pt,
   tab_resize (t, -1, 0);
   if (tab_nr (t) == tab_t (t))
     {
-      tab_destroy (t);
+      table_unref (&t->table);
       return;
     }
   tab_offset (t, 0, 0);
@@ -1375,63 +1363,9 @@ submit (struct crosstabs_proc *proc, struct pivot_table *pt,
           tab_nr (t) - 1);
   tab_vline (t, TAL_2, tab_l (t), 0, tab_nr (t) - 1);
 
-  aux = xmalloc (sizeof *aux);
-  aux->exclude = proc->exclude;
-  tab_dim (t, crosstabs_dim, crosstabs_dim_free, aux);
-
   tab_submit (t);
 }
 
-/* Sets the widths of all the columns and heights of all the rows in
-   table T for driver D. */
-static void
-crosstabs_dim (struct tab_rendering *r, void *aux_)
-{
-  const struct tab_table *t = r->table;
-  struct outp_driver *d = r->driver;
-  struct crosstabs_dim_aux *aux = aux_;
-  int i;
-
-  /* Width of a numerical column. */
-  int c = outp_string_width (d, "0.000000", OUTP_PROPORTIONAL);
-  if (aux->exclude == MV_NEVER)
-    c += outp_string_width (d, "M", OUTP_PROPORTIONAL);
-
-  /* Set width for header columns. */
-  if (tab_l (t) != 0)
-    {
-      size_t i;
-      int w;
-
-      w = d->width - c * (tab_nc (t) - tab_l (t));
-      for (i = 0; i <= tab_nc (t); i++)
-        w -= r->wrv[i];
-      w /= tab_l (t);
-
-      if (w < d->prop_em_width * 8)
-       w = d->prop_em_width * 8;
-
-      if (w > d->prop_em_width * 15)
-       w = d->prop_em_width * 15;
-
-      for (i = 0; i < tab_l (t); i++)
-       r->w[i] = w;
-    }
-
-  for (i = tab_l (t); i < tab_nc (t); i++)
-    r->w[i] = c;
-
-  for (i = 0; i < tab_nr (t); i++)
-    r->h[i] = tab_natural_height (r, i);
-}
-
-static void
-crosstabs_dim_free (void *aux_)
-{
-  struct crosstabs_dim_aux *aux = aux_;
-  free (aux);
-}
-
 static bool
 find_crosstab (struct pivot_table *pt, size_t *row0p, size_t *row1p)
 {
index e78d771e147a95ea5630360d8e9c120984f96a14..eb04bfa663889649855c771a944f98057e7381d8 100644 (file)
@@ -35,8 +35,7 @@
 #include <libpspp/message.h>
 #include <libpspp/assertion.h>
 #include <math/moments.h>
-#include <output/manager.h>
-#include <output/table.h>
+#include <output/tab.h>
 
 #include "xalloc.h"
 
@@ -554,13 +553,11 @@ dump_z_table (struct dsc_proc *dsc)
 
   t = tab_create (2, cnt + 1);
   tab_title (t, _("Mapping of variables to corresponding Z-scores."));
-  tab_columns (t, SOM_COL_DOWN);
   tab_headers (t, 0, 0, 1, 0);
   tab_box (t, TAL_1, TAL_1, TAL_0, TAL_1, 0, 0, 1, cnt);
   tab_hline (t, TAL_2, 0, 1, 1);
   tab_text (t, 0, 0, TAB_CENTER | TAT_TITLE, _("Source"));
   tab_text (t, 1, 0, TAB_CENTER | TAT_TITLE, _("Target"));
-  tab_dim (t, tab_natural_dimensions, NULL, NULL);
 
   {
     size_t i, y;
@@ -879,7 +876,6 @@ display (struct dsc_proc *dsc)
   tab_box (t, -1, -1, -1, TAL_1, 1, 0, nc - 1, dsc->var_cnt);
   tab_hline (t, TAL_2, 0, nc - 1, 1);
   tab_vline (t, TAL_2, 1, 0, dsc->var_cnt);
-  tab_dim (t, tab_natural_dimensions, NULL, NULL);
 
   nc = 0;
   tab_text (t, nc++, 0, TAB_LEFT | TAT_TITLE, _("Variable"));
index 4ca3af966505080338b2ff85679885ed1bd7a963..d06f98ef9d79692cc6ad98cd1372dcfa79d571b4 100644 (file)
 #include <libpspp/misc.h>
 #include <libpspp/str.h>
 #include <math/moments.h>
+#include <output/chart-item.h>
 #include <output/charts/boxplot.h>
 #include <output/charts/np-plot.h>
-#include <output/manager.h>
-#include <output/table.h>
+#include <output/tab.h>
 
 #include "minmax.h"
 #include "xalloc.h"
@@ -61,9 +61,7 @@
 #define N_(msgid) msgid
 
 /* (headers) */
-#include <output/chart.h>
 #include <output/charts/plot-hist.h>
-#include <output/charts/plot-chart.h>
 #include <math/histogram.h>
 
 /* (specification)
@@ -339,7 +337,7 @@ show_npplot (const struct variable **dependent_var,
          struct string label;
          const struct factor_result *result =
            ll_data (ll, struct factor_result, ll);
-          struct chart *npp, *dnpp;
+          struct chart_item *npp, *dnpp;
           struct casereader *reader;
           struct np *np;
 
@@ -350,20 +348,20 @@ show_npplot (const struct variable **dependent_var,
           np = result->metrics[v].np;
           reader = casewriter_make_reader (np->writer);
           npp = np_plot_create (np, reader, ds_cstr (&label));
-          dnpp = dnp_plot_create (np, reader, ds_cstr (&label));
+          dnpp = np_plot_create (np, reader, ds_cstr (&label));
 
          ds_destroy (&label);
 
           if (npp == NULL || dnpp == NULL)
             {
               msg (MW, _("Not creating NP plot because data set is empty."));
-              chart_unref (npp);
-              chart_unref (dnpp);
+              chart_item_unref (npp);
+              chart_item_unref (dnpp);
             }
           else
             {
-              chart_submit (npp);
-              chart_submit (dnpp);
+              chart_item_submit (npp);
+              chart_item_submit (dnpp);
             }
 
          statistic_destroy (&np->parent.parent);
@@ -406,8 +404,9 @@ show_histogram (const struct variable **dependent_var,
 
           moments1_calculate (result->metrics[v].moments,
                               &n, &mean, &var, NULL,  NULL);
-          chart_submit (histogram_chart_create (histogram, ds_cstr (&str),
-                                                n, mean, sqrt (var), false));
+          chart_item_submit (histogram_chart_create (histogram->gsl_hist,
+                                                     ds_cstr (&str), n, mean,
+                                                     sqrt (var), false));
 
          ds_destroy (&str);
        }
@@ -471,7 +470,7 @@ show_boxplot_groups (const struct variable **dependent_var,
          ds_destroy (&str);
        }
 
-      chart_submit (boxplot_get_chart (boxplot));
+      boxplot_submit (boxplot);
     }
 }
 
@@ -519,7 +518,7 @@ show_boxplot_variables (const struct variable **dependent_var,
           metrics->box_whisker = NULL;
        }
 
-      chart_submit (boxplot_get_chart (boxplot));
+      boxplot_submit (boxplot);
     }
 }
 
@@ -1135,8 +1134,6 @@ show_summary (const struct variable **dependent_var, int n_dep_var,
   tbl = tab_create (n_cols, n_rows);
   tab_headers (tbl, heading_columns, 0, heading_rows, 0);
 
-  tab_dim (tbl, tab_natural_dimensions, NULL, NULL);
-
   /* Outline the box */
   tab_box (tbl,
           TAL_2, TAL_2,
@@ -1372,8 +1369,6 @@ show_descriptives (const struct variable **dependent_var,
   tbl = tab_create (n_cols, n_rows);
   tab_headers (tbl, heading_columns, 0, heading_rows, 0);
 
-  tab_dim (tbl, tab_natural_dimensions, NULL, NULL);
-
   /* Outline the box */
   tab_box (tbl,
           TAL_2, TAL_2,
@@ -1684,8 +1679,6 @@ show_extremes (const struct variable **dependent_var,
   tbl = tab_create (n_cols, n_rows);
   tab_headers (tbl, heading_columns, 0, heading_rows, 0);
 
-  tab_dim (tbl, tab_natural_dimensions, NULL, NULL);
-
   /* Outline the box */
   tab_box (tbl,
           TAL_2, TAL_2,
@@ -1888,8 +1881,6 @@ show_percentiles (const struct variable **dependent_var,
   tbl = tab_create (n_cols, n_rows);
   tab_headers (tbl, heading_columns, 0, heading_rows, 0);
 
-  tab_dim (tbl, tab_natural_dimensions, NULL, NULL);
-
   /* Outline the box */
   tab_box (tbl,
           TAL_2, TAL_2,
index 2dbf3b69143f88ad53a1515670000f42f09e0dc3..29e30c16b4b022b3eb26858507562dedc90f6d0e 100644 (file)
 #include <libpspp/misc.h>
 #include <libpspp/message.h>
 
-#include <output/table.h>
+#include <output/tab.h>
 
 #include <output/charts/scree.h>
-#include <output/chart.h>
+#include <output/chart-item.h>
 
 #include "gettext.h"
 #define _(msgid) gettext (msgid)
@@ -935,7 +935,7 @@ show_scree (const struct cmd_factor *f, struct idata *idata)
 
   s = scree_create (idata->eval, label);
 
-  chart_submit (scree_get_chart (s));
+  scree_submit (s);
 }
 
 static void
@@ -964,8 +964,6 @@ show_communalities (const struct cmd_factor * factor,
 
   tab_title (t, _("Communalities"));
 
-  tab_dim (t, tab_natural_dimensions, NULL, NULL);
-
   tab_headers (t, heading_columns, 0, heading_rows, 0);
 
   c = 1;
@@ -1027,8 +1025,6 @@ show_factor_matrix (const struct cmd_factor *factor, struct idata *idata, const
   else 
     tab_title (t, _("Factor Matrix"));
 
-  tab_dim (t, tab_natural_dimensions, NULL, NULL);
-
   tab_headers (t, heading_columns, 0, heading_rows, 0);
 
   if ( factor->extraction == EXTRACTION_PC )
@@ -1136,8 +1132,6 @@ show_explained_variance (const struct cmd_factor * factor, struct idata *idata,
 
   tab_title (t, _("Total Variance Explained"));
 
-  tab_dim (t, tab_natural_dimensions, NULL, NULL);
-
   tab_headers (t, heading_columns, 0, heading_rows, 0);
 
   /* Outline the box */
@@ -1288,8 +1282,6 @@ show_correlation_matrix (const struct cmd_factor *factor, const struct idata *id
 
   tab_title (t, _("Correlation Matrix"));
 
-  tab_dim (t, tab_natural_dimensions, NULL, NULL);
-
   tab_hline (t, TAL_1, 0, nc - 1, heading_rows);
 
   if (nr > heading_rows)
@@ -1431,7 +1423,6 @@ do_factor (const struct cmd_factor *factor, struct casereader *r)
 
       struct tab_table *t = tab_create (nc, nr);
       tab_title (t, _("Descriptive Statistics"));
-      tab_dim (t, tab_natural_dimensions, NULL, NULL);
 
       tab_headers (t, heading_columns, 0, heading_rows, 0);
 
index e1d6fb0e3dfcaa95518dc2f60b346da92199887b..e52aaf0f9ae3f50d7b40880050e18b6d06170c34 100644 (file)
 #include <libpspp/str.h>
 #include <math/histogram.h>
 #include <math/moments.h>
-#include <output/chart.h>
+#include <output/chart-item.h>
 #include <output/charts/piechart.h>
 #include <output/charts/plot-hist.h>
-#include <output/manager.h>
-#include <output/output.h>
-#include <output/table.h>
+#include <output/tab.h>
 
 #include "freq.h"
 
@@ -612,12 +610,12 @@ postcalc (const struct dataset *ds)
 
          hist = freq_tab_to_hist (ft,v);
 
-          chart_submit (histogram_chart_create (
-                          hist, var_to_string(v),
-                         vf->tab.valid_cases,
-                         d[frq_mean],
-                         d[frq_stddev],
-                         normal));
+          chart_item_submit (histogram_chart_create (
+                               hist->gsl_hist, var_to_string(v),
+                               vf->tab.valid_cases,
+                               d[frq_mean],
+                               d[frq_stddev],
+                               normal));
 
          statistic_destroy (&hist->parent);
        }
@@ -1001,41 +999,6 @@ compare_freq_alpha_d (const void *a_, const void *b_, const void *v_)
 \f
 /* Frequency table display. */
 
-struct full_dim_aux
-  {
-    bool show_labels;
-  };
-
-/* Sets the widths of all the columns and heights of all the rows in
-   table T for driver D. */
-static void
-full_dim (struct tab_rendering *r, void *aux_)
-{
-  const struct outp_driver *d = r->driver;
-  const struct tab_table *t = r->table;
-  const struct full_dim_aux *aux = aux_;
-  int i;
-
-  for (i = 0; i < tab_nc (t); i++)
-    {
-      r->w[i] = tab_natural_width (r, i);
-      if (aux->show_labels && i == 0)
-        r->w[i] = MIN (r->w[i], d->prop_em_width * 15);
-      else
-        r->w[i] = MAX (r->w[i], d->prop_em_width * 8);
-    }
-
-  for (i = 0; i < tab_nr (t); i++)
-    r->h[i] = d->font_height;
-}
-
-static void
-full_dim_free (void *aux_)
-{
-  struct full_dim_aux *aux = aux_;
-  free (aux);
-}
-
 /* Displays a full frequency table for variable V. */
 static void
 dump_full (const struct variable *v, const struct variable *wv)
@@ -1060,18 +1023,12 @@ dump_full (const struct variable *v, const struct variable *wv)
 
   const bool lab = (cmd.labels == FRQ_LABELS);
 
-  struct full_dim_aux *aux;
-
   vf = get_var_freqs (v);
   ft = &vf->tab;
   n_categories = ft->n_valid + ft->n_missing;
   t = tab_create (5 + lab, n_categories + 2);
   tab_headers (t, 0, 0, 1, 0);
 
-  aux = xmalloc (sizeof *aux);
-  aux->show_labels = lab;
-  tab_dim (t, full_dim, full_dim_free, aux);
-
   if (lab)
     tab_text (t, 0, 0, TAB_CENTER | TAT_TITLE, _("Value Label"));
 
@@ -1137,31 +1094,6 @@ dump_full (const struct variable *v, const struct variable *wv)
   tab_submit (t);
 }
 
-/* Sets the widths of all the columns and heights of all the rows in
-   table T for driver D. */
-static void
-condensed_dim (struct tab_rendering *r, void *aux UNUSED)
-{
-  struct outp_driver *d = r->driver;
-  const struct tab_table *t = r->table;
-
-  int cum_width = outp_string_width (d, _("Cum"), OUTP_PROPORTIONAL);
-  int zeros_width = outp_string_width (d, "000", OUTP_PROPORTIONAL);
-  int max_width = MAX (cum_width, zeros_width);
-
-  int i;
-
-  for (i = 0; i < 2; i++)
-    {
-      r->w[i] = tab_natural_width (r, i);
-      r->w[i] = MAX (r->w[i], d->prop_em_width * 8);
-    }
-  for (i = 2; i < 4; i++)
-    r->w[i] = max_width;
-  for (i = 0; i < tab_nr (t); i++)
-    r->h[i] = d->font_height;
-}
-
 /* Display condensed frequency table for variable V. */
 static void
 dump_condensed (const struct variable *v, const struct variable *wv)
@@ -1186,7 +1118,6 @@ dump_condensed (const struct variable *v, const struct variable *wv)
   tab_text (t, 2, 1, TAB_CENTER | TAT_TITLE, _("Pct"));
   tab_text (t, 3, 0, TAB_CENTER | TAT_TITLE, _("Cum"));
   tab_text (t, 3, 1, TAB_CENTER | TAT_TITLE, _("Pct"));
-  tab_dim (t, condensed_dim, NULL, NULL);
 
   r = 2;
   for (f = ft->valid; f < ft->missing; f++)
@@ -1216,7 +1147,6 @@ dump_condensed (const struct variable *v, const struct variable *wv)
           0, 0, 3, r - 1);
   tab_hline (t, TAL_2, 0, 3, 2);
   tab_title (t, "%s", var_to_string (v));
-  tab_columns (t, SOM_COL_DOWN);
   tab_submit (t);
 }
 \f
@@ -1385,7 +1315,6 @@ dump_statistics (const struct variable *v, bool show_varname,
   calc_stats (v, stat_value);
 
   t = tab_create (3, n_stats + n_percentiles + 2);
-  tab_dim (t, tab_natural_dimensions, NULL, NULL);
 
   tab_box (t, TAL_1, TAL_1, -1, -1 , 0 , 0 , 2, tab_nr(t) - 1) ;
 
@@ -1426,11 +1355,8 @@ dump_statistics (const struct variable *v, bool show_varname,
                  var_get_print_format (v));
     }
 
-  tab_columns (t, SOM_COL_DOWN);
   if (show_varname)
     tab_title (t, "%s", var_to_string (v));
-  else
-    tab_flags (t, SOMF_NO_TITLE);
 
 
   tab_submit (t);
@@ -1519,7 +1445,7 @@ do_piechart(const struct variable *var, const struct freq_tab *frq_tab)
 
   slices = freq_tab_to_slice_array(frq_tab, var, &n_slices);
 
-  chart_submit (piechart_create (var_to_string(var), slices, n_slices));
+  chart_item_submit (piechart_create (var_to_string(var), slices, n_slices));
 
   for (i = 0 ; i < n_slices ; ++i )
     ds_destroy (&slices[i].label);
index fd36041636e5ad25d619ceffee2ea42824b7aa8f..0804945fabaa1504eff3779906b77dce6d3b9bda 100644 (file)
@@ -43,7 +43,7 @@
 #include <math/coefficient.h>
 #include <math/linreg.h>
 #include <math/moments.h>
-#include <output/table.h>
+#include <output/tab.h>
 
 #include "xalloc.h"
 #include "gettext.h"
index 8e6f11a8efa3aeb20591c7bf3476b8096c00e292..05fa1d02527951bf7ecbf26a724ee76cfe2e8ffe 100644 (file)
@@ -17,7 +17,7 @@
 #include <config.h>
 
 #include <data/format.h>
-#include <output/table.h>
+#include <output/tab.h>
 #include <data/casereader.h>
 #include <libpspp/hash.h>
 #include <data/variable.h>
@@ -104,7 +104,6 @@ do_summary_box (const struct descriptives *desc,
 
   table = tab_create (columns, 2 + n_vars);
 
-  tab_dim (table, tab_natural_dimensions, NULL, NULL);
 
   tab_title (table, _("Descriptive Statistics"));
 
@@ -159,13 +158,13 @@ do_summary_box (const struct descriptives *desc,
       const struct variable *var = vv[v];
       const struct fmt_spec *fmt = var_get_print_format (var);
 
-      tab_text (table, 0, 2 + v, TAT_NONE, var_to_string (var));
+      tab_text (table, 0, 2 + v, 0, var_to_string (var));
 
-      tab_double (table, 1, 2 + v, TAT_NONE, desc[v].n, fmt);
-      tab_double (table, 2, 2 + v, TAT_NONE, desc[v].mean, fmt);
-      tab_double (table, 3, 2 + v, TAT_NONE, desc[v].std_dev, fmt);
-      tab_double (table, 4, 2 + v, TAT_NONE, desc[v].min, fmt);
-      tab_double (table, 5, 2 + v, TAT_NONE, desc[v].max, fmt);
+      tab_double (table, 1, 2 + v, 0, desc[v].n, fmt);
+      tab_double (table, 2, 2 + v, 0, desc[v].mean, fmt);
+      tab_double (table, 3, 2 + v, 0, desc[v].std_dev, fmt);
+      tab_double (table, 4, 2 + v, 0, desc[v].min, fmt);
+      tab_double (table, 5, 2 + v, 0, desc[v].max, fmt);
     }
 
 
index ddb84d896fa1ac6701f8f4634071525a363ad3f9..2d55edff73fb286597a7be6771b467a9c4b6422c 100644 (file)
@@ -40,8 +40,7 @@
 #include <math/group-proc.h>
 #include <math/group.h>
 #include <math/levene.h>
-#include <output/manager.h>
-#include <output/table.h>
+#include <output/tab.h>
 #include "sort-criteria.h"
 #include <data/format.h>
 
@@ -261,8 +260,6 @@ show_anova_table (void)
 
   t = tab_create (n_cols, n_rows);
   tab_headers (t, 2, 0, 1, 0);
-  tab_dim (t, tab_natural_dimensions, NULL, NULL);
-
 
   tab_box (t,
           TAL_2, TAL_2,
@@ -372,7 +369,6 @@ show_descriptives (const struct dictionary *dict)
 
   t = tab_create (n_cols, n_rows);
   tab_headers (t, 2, 0, 2, 0);
-  tab_dim (t, tab_natural_dimensions, NULL, NULL);
 
 
   /* Put a frame around the entire box, and vertical lines inside */
@@ -519,7 +515,7 @@ show_homogeneity (void)
 
   t = tab_create (n_cols, n_rows);
   tab_headers (t, 1, 0, 1, 0);
-  tab_dim (t, tab_natural_dimensions, NULL, NULL);
+
 
   /* Put a frame around the entire box, and vertical lines inside */
   tab_box (t,
@@ -579,7 +575,6 @@ show_contrast_coeffs (short *bad_contrast)
 
   t = tab_create (n_cols, n_rows);
   tab_headers (t, 2, 0, 2, 0);
-  tab_dim (t, tab_natural_dimensions, NULL, NULL);
 
   /* Put a frame around the entire box, and vertical lines inside */
   tab_box (t,
@@ -663,7 +658,6 @@ show_contrast_tests (short *bad_contrast)
 
   t = tab_create (n_cols, n_rows);
   tab_headers (t, 3, 0, 1, 0);
-  tab_dim (t, tab_natural_dimensions, NULL, NULL);
 
   /* Put a frame around the entire box, and vertical lines inside */
   tab_box (t,
index c225370e459269cc572430f87e533f7a591662c4..ec3052c0acee616def219464aa7bf19edfec4550 100644 (file)
@@ -35,8 +35,7 @@
 #include <libpspp/compiler.h>
 #include <libpspp/taint.h>
 #include <math/sort.h>
-#include <output/manager.h>
-#include <output/table.h>
+#include <output/tab.h>
 
 #include <gsl/gsl_cdf.h>
 
@@ -692,7 +691,7 @@ cmd_rank (struct lexer *lexer, struct dataset *ds)
       int v;
 
       tab_output_text (0, _("Variables Created By RANK"));
-      tab_output_text (0, "\n");
+      tab_output_text (0, "");
 
       for (i = 0 ; i <  n_rank_specs ; ++i )
        {
index b61ae586b488292d23fda2b8ee9b96daab4d37f2..2487b9021d08bb14d86a14ed84e3b99dd6f5bad6 100644 (file)
@@ -43,7 +43,7 @@
 #include <math/coefficient.h>
 #include <math/linreg.h>
 #include <math/moments.h>
-#include <output/table.h>
+#include <output/tab.h>
 
 #include "xalloc.h"
 
@@ -150,7 +150,6 @@ reg_stats_r (pspp_linreg_cache * c)
   adjrsq = 1.0 - (1.0 - rsq) * (c->n_obs - 1.0) / (c->n_obs - c->n_indeps);
   std_error = sqrt (pspp_linreg_mse (c));
   t = tab_create (n_cols, n_rows);
-  tab_dim (t, tab_natural_dimensions, NULL, NULL);
   tab_box (t, TAL_2, TAL_2, -1, TAL_1, 0, 0, n_cols - 1, n_rows - 1);
   tab_hline (t, TAL_2, 0, n_cols - 1, 1);
   tab_vline (t, TAL_2, 2, 0, n_rows - 1);
@@ -193,7 +192,6 @@ reg_stats_coeff (pspp_linreg_cache * c)
 
   t = tab_create (n_cols, n_rows);
   tab_headers (t, 2, 0, 1, 0);
-  tab_dim (t, tab_natural_dimensions, NULL, NULL);
   tab_box (t, TAL_2, TAL_2, -1, TAL_1, 0, 0, n_cols - 1, n_rows - 1);
   tab_hline (t, TAL_2, 0, n_cols - 1, 1);
   tab_vline (t, TAL_2, 2, 0, n_rows - 1);
@@ -290,7 +288,6 @@ reg_stats_anova (pspp_linreg_cache * c)
   assert (c != NULL);
   t = tab_create (n_cols, n_rows);
   tab_headers (t, 2, 0, 1, 0);
-  tab_dim (t, tab_natural_dimensions, NULL, NULL);
 
   tab_box (t, TAL_2, TAL_2, -1, TAL_1, 0, 0, n_cols - 1, n_rows - 1);
 
@@ -381,7 +378,6 @@ reg_stats_bcov (pspp_linreg_cache * c)
   n_rows = 2 * (c->n_indeps + 1);
   t = tab_create (n_cols, n_rows);
   tab_headers (t, 2, 0, 1, 0);
-  tab_dim (t, tab_natural_dimensions, NULL, NULL);
   tab_box (t, TAL_2, TAL_2, -1, TAL_1, 0, 0, n_cols - 1, n_rows - 1);
   tab_hline (t, TAL_2, 0, n_cols - 1, 1);
   tab_vline (t, TAL_2, 2, 0, n_rows - 1);
index dfb81367912a3bcd96a25aa00bb964145a98f0ab..478f5c5bc1baa06c967f980e6d2704587fe131bc 100644 (file)
@@ -27,8 +27,8 @@
 #include <language/command.h>
 #include <libpspp/misc.h>
 #include <math/moments.h>
-#include <output/manager.h>
-#include <output/table.h>
+#include <output/tab.h>
+#include <output/text-item.h>
 
 #include "xalloc.h"
 #include "xmalloca.h"
@@ -379,18 +379,8 @@ run_reliability (struct casereader *input, struct dataset *ds,
        alpha (s->n_items, s->sum_of_variances, s->variance_of_sums);
     }
 
-
-  {
-    struct tab_table *tab = tab_create(1, 1);
-
-    tab_dim (tab, tab_natural_dimensions, NULL, NULL);
-    tab_flags (tab, SOMF_NO_TITLE );
-
-    tab_text_format (tab, 0, 0, 0, "Scale: %s", ds_cstr (&rel->scale_name));
-
-    tab_submit(tab);
-  }
-
+  text_item_submit (text_item_create_format (TEXT_ITEM_PARAGRAPH, "Scale: %s",
+                                             ds_cstr (&rel->scale_name)));
 
   case_processing_summary (n_valid, n_missing, dataset_dict (ds));
 }
@@ -428,8 +418,6 @@ reliability_statistics (const struct reliability *rel)
   struct tab_table *tbl = tab_create (n_cols, n_rows);
   tab_headers (tbl, heading_columns, 0, heading_rows, 0);
 
-  tab_dim (tbl, tab_natural_dimensions, NULL, NULL);
-
   tab_title (tbl, _("Reliability Statistics"));
 
   /* Vertical lines for the data only */
@@ -471,8 +459,6 @@ reliability_summary_total (const struct reliability *rel)
   struct tab_table *tbl = tab_create (n_cols, n_rows);
   tab_headers (tbl, heading_columns, 0, heading_rows, 0);
 
-  tab_dim (tbl, tab_natural_dimensions, NULL, NULL);
-
   tab_title (tbl, _("Item-Total Statistics"));
 
   /* Vertical lines for the data only */
@@ -681,8 +667,6 @@ case_processing_summary (casenumber n_valid, casenumber n_missing,
   tbl = tab_create (n_cols, n_rows);
   tab_headers (tbl, heading_columns, 0, heading_rows, 0);
 
-  tab_dim (tbl, tab_natural_dimensions, NULL, NULL);
-
   tab_title (tbl, _("Case Processing Summary"));
 
   /* Vertical lines for the data only */
index 1f2691d8919e01eac157688d053f218c426581af..724bc523a3beef42df39ca5b801da5918f88d398 100644 (file)
 
 #include <language/stats/roc.h>
 
-#include <data/procedure.h>
-#include <language/lexer/variable-parser.h>
-#include <language/lexer/value-parser.h>
-#include <language/command.h>
-#include <language/lexer/lexer.h>
-
 #include <data/casegrouper.h>
 #include <data/casereader.h>
 #include <data/casewriter.h>
 #include <data/dictionary.h>
 #include <data/format.h>
-#include <math/sort.h>
+#include <data/procedure.h>
 #include <data/subcase.h>
-
-
+#include <language/command.h>
+#include <language/lexer/lexer.h>
+#include <language/lexer/value-parser.h>
+#include <language/lexer/variable-parser.h>
 #include <libpspp/misc.h>
+#include <math/sort.h>
+#include <output/chart-item.h>
+#include <output/charts/roc-chart.h>
+#include <output/tab.h>
 
 #include <gsl/gsl_cdf.h>
-#include <output/table.h>
-
-#include <output/chart.h>
-#include <output/charts/roc-chart.h>
 
 #include "gettext.h"
 #define _(msgid) gettext (msgid)
@@ -939,7 +935,6 @@ show_auc  (struct roc_state *rs, const struct cmd_roc *roc)
 
   tab_headers (tbl, n_cols - n_fields, 0, 1, 0);
 
-  tab_dim (tbl, tab_natural_dimensions, NULL, NULL);
 
   tab_text (tbl, n_cols - n_fields, 1, TAT_TITLE, _("Area"));
 
@@ -1031,8 +1026,6 @@ show_summary (const struct cmd_roc *roc)
 
   tab_headers (tbl, 1, 0, 2, 0);
 
-  tab_dim (tbl, tab_natural_dimensions, NULL, NULL);
-
   tab_box (tbl,
           TAL_2, TAL_2,
           -1, -1,
@@ -1093,8 +1086,6 @@ show_coords (struct roc_state *rs, const struct cmd_roc *roc)
 
   tab_headers (tbl, 1, 0, 1, 0);
 
-  tab_dim (tbl, tab_natural_dimensions, NULL, NULL);
-
   tab_hline (tbl, TAL_2, 0, n_cols - 1, 1);
 
   if ( roc->n_vars > 1)
@@ -1171,7 +1162,7 @@ output_roc (struct roc_state *rs, const struct cmd_roc *roc)
       for (i = 0; i < roc->n_vars; i++)
         roc_chart_add_var (rc, var_get_name (roc->vars[i]),
                            rs[i].cutpoint_rdr);
-      chart_submit (roc_chart_get_chart (rc));
+      roc_chart_submit (rc);
     }
 
   show_auc (rs, roc);
index d5970c6ff0a61297803374c92755762cdca1f6d0..754b0d7dfcd6b47c08d62f6f4db05ab8a6e90812 100644 (file)
@@ -19,7 +19,7 @@
 
 #include <data/variable.h>
 #include <libpspp/str.h>
-#include <output/table.h>
+#include <output/tab.h>
 #include <gsl/gsl_cdf.h>
 #include <gsl/gsl_randist.h>
 #include "npar.h"
@@ -57,8 +57,6 @@ output_frequency_table (const struct two_sample_test *t2s,
   const struct variable *wv = dict_get_weight (dict);
   const struct fmt_spec *wfmt = wv ? var_get_print_format (wv) : & F_8_0;
 
-  tab_dim (table, tab_natural_dimensions, NULL, NULL);
-
   tab_title (table, _("Frequencies"));
 
   tab_headers (table, 2, 0, 1, 0);
@@ -110,8 +108,6 @@ output_statistics_table (const struct two_sample_test *t2s,
   int i;
   struct tab_table *table = tab_create (1 + t2s->n_pairs, 4);
 
-  tab_dim (table, tab_natural_dimensions, NULL, NULL);
-
   tab_title (table, _("Test Statistics"));
 
   tab_headers (table, 0, 1,  0, 1);
index a483bd9e35f2dea56afd12eb16ab6905725120aa..8aee3b157992827fa9021c876b39822809ab43b2 100644 (file)
@@ -44,8 +44,7 @@
 #include <math/group-proc.h>
 #include <math/levene.h>
 #include <math/correlation.h>
-#include <output/manager.h>
-#include <output/table.h>
+#include <output/tab.h>
 #include <data/format.h>
 
 #include "minmax.h"
@@ -479,11 +478,9 @@ ssbox_base_init (struct ssbox *this, int cols, int rows)
   this->finalize = ssbox_base_finalize;
   this->t = tab_create (cols, rows);
 
-  tab_columns (this->t, SOM_COL_DOWN);
   tab_headers (this->t, 0, 0, 1, 0);
   tab_box (this->t, TAL_2, TAL_2, TAL_0, TAL_1, 0, 0, cols - 1, rows - 1);
   tab_hline (this->t, TAL_2, 0, cols- 1, 1);
-  tab_dim (this->t, tab_natural_dimensions, NULL, NULL);
 }
 \f
 /* ssbox implementations. */
@@ -1073,7 +1070,6 @@ trbox_base_init (struct trbox *self, size_t data_rows, int cols)
   tab_headers (self->t, 0, 0, 3, 0);
   tab_box (self->t, TAL_2, TAL_2, TAL_0, TAL_0, 0, 0, cols - 1, rows - 1);
   tab_hline (self->t, TAL_2, 0, cols- 1, 3);
-  tab_dim (self->t, tab_natural_dimensions, NULL, NULL);
 }
 
 /* Base finalizer for the trbox */
@@ -1095,12 +1091,10 @@ pscbox (struct t_test_proc *proc)
 
   table = tab_create (cols, rows);
 
-  tab_columns (table, SOM_COL_DOWN);
   tab_headers (table, 0, 0, 1, 0);
   tab_box (table, TAL_2, TAL_2, TAL_0, TAL_1, 0, 0, cols - 1, rows - 1);
   tab_hline (table, TAL_2, 0, cols - 1, 1);
   tab_vline (table, TAL_2, 2, 0, rows - 1);
-  tab_dim (table, tab_natural_dimensions, NULL, NULL);
   tab_title (table, _("Paired Samples Correlations"));
 
   /* column headings */
index c7e302779e63a8937c64a707eeb74126823e948c..e1dab91c9ff661cbf3d64618e9f5edb7a7b703d8 100644 (file)
@@ -38,7 +38,7 @@
 #include <libpspp/misc.h>
 #include <math/sort.h>
 #include <math/wilcoxon-sig.h>
-#include <output/table.h>
+#include <output/tab.h>
 #include <signal.h>
 #include <unistd.h>
 
@@ -227,8 +227,6 @@ show_ranks_box (const struct wilcoxon_state *ws,
 
   struct tab_table *table = tab_create (5, 1 + 4 * t2s->n_pairs);
 
-  tab_dim (table, tab_natural_dimensions, NULL, NULL);
-
   tab_title (table, _("Ranks"));
 
   tab_headers (table, 2, 0, 1, 0);
@@ -308,8 +306,6 @@ show_tests_box (const struct wilcoxon_state *ws,
   size_t i;
   struct tab_table *table = tab_create (1 + t2s->n_pairs, exact ? 5 : 3);
 
-  tab_dim (table, tab_natural_dimensions, NULL, NULL);
-
   tab_title (table, _("Test Statistics"));
 
   tab_headers (table, 1, 0, 1, 0);
index 9b5fd0430b56879fd92e1996c732cbe16e0c2068..9470e5083ebd2fcf3e668393029d3eaa1ff6df36 100644 (file)
@@ -35,7 +35,7 @@
 #include <libpspp/str.h>
 #include <libpspp/verbose-msg.h>
 #include <libpspp/version.h>
-#include <output/table.h>
+#include <output/tab.h>
 
 #include <libpspp/ll.h>
 
index d8c49a595c0250a30155080a5372c629a2da598f..5aea2c334f34f5157a7dbcd77d4fa0f4a478f6b7 100644 (file)
 #include <language/command.h>
 #include <language/lexer/lexer.h>
 #include <libpspp/assertion.h>
-#include <output/output.h>
+#include <libpspp/string-map.h>
+#include <output/measure.h>
 
 /* Executes the DEBUG PAPER SIZE command. */
 int
 cmd_debug_paper_size (struct lexer *lexer, struct dataset *ds UNUSED)
 {
+  const char *paper_size;
   int h, v;
 
   if (!lex_force_string (lexer))
     return CMD_FAILURE;
+  paper_size = ds_cstr (lex_tokstr (lexer));
 
-  printf ("\"%s\" => ", ds_cstr (lex_tokstr (lexer)));
-  if (outp_get_paper_size (ds_cstr (lex_tokstr (lexer)), &h, &v))
+  printf ("\"%s\" => ", paper_size);
+  if (measure_paper (paper_size, &h, &v))
     printf ("%.1f x %.1f in, %.0f x %.0f mm\n",
             h / 72000., v / 72000.,
             h / (72000 / 25.4), v / (72000 / 25.4));
index 3d3c2c40e73ebde043d0be174f37205e41b3d5f8..ebd6a990fc88e1c1c02a48a7562fd91d4fccdc32 100644 (file)
@@ -19,8 +19,7 @@
 #include <libpspp/str.h>
 #include <language/lexer/lexer.h>
 #include <language/command.h>
-#include <output/table.h>
-#include <output/manager.h>
+#include <output/tab.h>
 
 #include "xalloc.h"
 
@@ -35,9 +34,6 @@ cmd_echo (struct lexer *lexer, struct dataset *ds UNUSED)
 
   tab = tab_create(1, 1);
 
-  tab_dim (tab, tab_natural_dimensions, NULL, NULL);
-  tab_flags (tab, SOMF_NO_TITLE );
-
   tab_text(tab, 0, 0, 0, ds_cstr (lex_tokstr (lexer)));
 
   tab_submit(tab);
index fe98aeb87f3347b82b61853adc70360cee87f132..ceb78771d748fb6f18bb8cb79274a23421720eab 100644 (file)
    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
 
 #include <config.h>
+
 #include <ctype.h>
 #include <errno.h>
 #include <stdlib.h>
 #include <string.h>
+#include <unistd.h>
+
+#include <data/file-name.h>
 #include <language/command.h>
-#include <libpspp/message.h>
-#include <libpspp/getl.h>
-#include <language/syntax-file.h>
 #include <language/lexer/lexer.h>
+#include <language/syntax-file.h>
+#include <libpspp/getl.h>
+#include <libpspp/message.h>
 #include <libpspp/str.h>
-#include <data/file-name.h>
 
 #include "dirname.h"
 #include "xalloc.h"
index 5c2b571ec57a7df2a7f9203abe2d23d244e083c9..46d5cd8dd145a4ab74800eb2e76f29142165b553 100644 (file)
@@ -40,8 +40,8 @@
 #include <libpspp/message.h>
 #include <libpspp/i18n.h>
 #include <math/random.h>
+#include <output/driver.h>
 #include <output/journal.h>
-#include <output/output.h>
 
 #if HAVE_LIBTERMCAP
 #if HAVE_TERMCAP_H
@@ -504,7 +504,7 @@ stc_custom_listing (struct lexer *lexer, struct dataset *ds UNUSED, struct cmd_s
       /* FIXME */
       return 0;
     }
-  outp_enable_device (listing, OUTP_DEV_LISTING);
+  output_set_type_enabled (listing, OUTPUT_DEVICE_LISTING);
 
   return 1;
 }
index 1bdb0e9a0db6487467d51aa958452cf91ef13637..fe826db155dadb8ad1b5cd97d2df7709399171ba 100644 (file)
 #include <libpspp/message.h>
 #include <libpspp/start-date.h>
 #include <libpspp/version.h>
-#include <output/output.h>
+#include <output/text-item.h>
 
 #include "xalloc.h"
 
 #include "gettext.h"
 #define _(msgid) gettext (msgid)
 
-static int get_title (struct lexer *, const char *cmd, char **title);
+static int parse_title (struct lexer *, enum text_item_type);
+static void set_title (const char *title, enum text_item_type);
 
 int
 cmd_title (struct lexer *lexer, struct dataset *ds UNUSED)
 {
-  return get_title (lexer, "TITLE", &outp_title);
+  return parse_title (lexer, TEXT_ITEM_TITLE);
 }
 
 int
 cmd_subtitle (struct lexer *lexer, struct dataset *ds UNUSED)
 {
-  return get_title (lexer, "SUBTITLE", &outp_subtitle);
+  return parse_title (lexer, TEXT_ITEM_SUBTITLE);
 }
 
 static int
-get_title (struct lexer *lexer, const char *cmd, char **title)
+parse_title (struct lexer *lexer, enum text_item_type type)
 {
   int c;
 
@@ -59,30 +60,24 @@ get_title (struct lexer *lexer, const char *cmd, char **title)
       lex_get (lexer);
       if (!lex_force_string (lexer))
        return CMD_FAILURE;
-      if (*title)
-       free (*title);
-      *title = ds_xstrdup (lex_tokstr (lexer));
+      set_title (ds_cstr (lex_tokstr (lexer)), type);
       lex_get (lexer);
-      if (lex_token (lexer) != '.')
-       {
-         msg (SE, _("%s: `.' expected after string."), cmd);
-         return CMD_FAILURE;
-       }
+      return lex_end_of_command (lexer);
     }
   else
     {
-      char *cp;
-
-      if (*title)
-       free (*title);
-      *title = xstrdup (lex_rest_of_line (lexer));
+      set_title (lex_rest_of_line (lexer), type);
       lex_discard_line (lexer);
-      for (cp = *title; *cp; cp++)
-       *cp = toupper ((unsigned char) (*cp));
     }
   return CMD_SUCCESS;
 }
 
+static void
+set_title (const char *title, enum text_item_type type)
+{
+  text_item_submit (text_item_create (type, title));
+}
+
 /* Performs the FILE LABEL command. */
 int
 cmd_file_label (struct lexer *lexer, struct dataset *ds)
index b224b3ebea9e54bdfac5d6859a18793583b046f6..4978e125cc67829c1fd9356f838c3a39f4e36b52 100644 (file)
 #include <data/settings.h>
 #include <libpspp/assertion.h>
 #include <libpspp/compiler.h>
-#include <libpspp/pool.h>
 #include <libpspp/start-date.h>
+#include <libpspp/string-map.h>
 #include <libpspp/version.h>
-#include <output/chart-provider.h>
-#include <output/chart.h>
-#include <output/output.h>
+#include <output/cairo.h>
+#include <output/chart-item-provider.h>
+#include "output/options.h"
+#include <output/tab.h>
+#include <output/text-item.h>
+#include <output/driver-provider.h>
+#include <output/render.h>
+#include <output/table-item.h>
 
 #include "error.h"
 #include "minmax.h"
 #include "gettext.h"
 #define _(msgid) gettext (msgid)
 
-/* ASCII driver options: (defaults listed first)
-
-   output-file="pspp.list"
-   append=no|yes                If output-file exists, append to it?
-   chart-files="pspp-#.png"     Name used for charts.
-   chart-type=png|none
-
-   paginate=on|off              Formfeeds are desired?
-   tab-width=8                  Width of a tab; 0 to not use tabs.
-
-   headers=on|off               Put headers at top of page?
-   emphasis=bold|underline|none Style to use for emphasis.
-   length=66|auto
-   width=79|auto
-   squeeze=off|on               Squeeze multiple newlines into exactly one.
-
-   top-margin=2
-   bottom-margin=2
-
-   box[x]="strng"               Sets box character X (X in base 4: 0-3333).
-   init="string"                Set initialization string.
- */
-
-/* Disable messages by failed range checks. */
-/*#define SUPPRESS_WARNINGS 1 */
+/* This file uses TABLE_HORZ and TABLE_VERT enough to warrant abbreviating. */
+#define H TABLE_HORZ
+#define V TABLE_VERT
 
 /* Line styles bit shifts. */
 enum
@@ -77,16 +60,23 @@ enum
     LNS_COUNT = 256
   };
 
+static inline int
+make_box_index (int left, int right, int top, int bottom)
+{
+  return ((left << LNS_LEFT) | (right << LNS_RIGHT)
+          | (top << LNS_TOP) | (bottom << LNS_BOTTOM));
+}
+
 /* Character attributes. */
 #define ATTR_EMPHASIS   0x100   /* Bold-face. */
 #define ATTR_BOX        0x200   /* Line drawing character. */
 
 /* A line of text. */
-struct line
+struct ascii_line
   {
     unsigned short *chars;      /* Characters and attributes. */
-    int char_cnt;               /* Length. */
-    int char_cap;               /* Allocated bytes. */
+    int n_chars;                /* Length. */
+    int allocated_chars;        /* Allocated "chars" elements. */
   };
 
 /* How to emphasize text. */
@@ -97,10 +87,10 @@ enum emphasis_style
     EMPH_NONE                   /* No emphasis. */
   };
 
-/* ASCII output driver extension record. */
-struct ascii_driver_ext
+/* ASCII output driver. */
+struct ascii_driver
   {
-    struct pool *pool;
+    struct output_driver driver;
 
     /* User parameters. */
     bool append;                /* Append if output-file already exists? */
@@ -109,12 +99,13 @@ struct ascii_driver_ext
     bool squeeze_blank_lines;   /* Squeeze multiple blank lines into one? */
     enum emphasis_style emphasis; /* How to emphasize text. */
     int tab_width;             /* Width of a tab; 0 not to use tabs. */
-    bool enable_charts;         /* Enable charts? */
-    const char *chart_file_name; /* Name of files used for charts. */
+    char *chart_file_name;      /* Name of files used for charts. */
 
+    int width;                  /* Page width. */
+    int length;                 /* Page length minus margins and header. */
     bool auto_width;            /* Use viewwidth as page width? */
     bool auto_length;           /* Use viewlength as page width? */
-    int page_length;           /* Page length before subtracting margins. */
+
     int top_margin;            /* Top margin in lines. */
     int bottom_margin;         /* Bottom margin in lines. */
 
@@ -122,811 +113,806 @@ struct ascii_driver_ext
     char *init;                 /* Device initialization string. */
 
     /* Internal state. */
+    char *title;
+    char *subtitle;
     char *file_name;            /* Output file name. */
     FILE *file;                 /* Output file. */
     bool reported_error;        /* Reported file open error? */
     int page_number;           /* Current page number. */
-    struct line *lines;         /* Page content. */
-    int line_cap;               /* Number of lines allocated. */
+    struct ascii_line *lines;   /* Page content. */
+    int allocated_lines;        /* Number of lines allocated. */
     int chart_cnt;              /* Number of charts so far. */
+    int y;
   };
 
-static void ascii_flush (struct outp_driver *);
-static int get_default_box_char (size_t idx);
-static bool update_page_size (struct outp_driver *, bool issue_error);
-static bool handle_option (void *this, const char *key,
-                           const struct string *val);
+static int vertical_margins (const struct ascii_driver *);
 
-static bool
-ascii_open_driver (const char *name, int types, struct substring options)
+static const char *get_default_box (int right, int bottom, int left, int top);
+static bool update_page_size (struct ascii_driver *, bool issue_error);
+static int parse_page_size (struct driver_option *);
+
+static void ascii_close_page (struct ascii_driver *);
+static void ascii_open_page (struct ascii_driver *);
+
+static void ascii_draw_line (void *, int bb[TABLE_N_AXES][2],
+                             enum render_line_style styles[TABLE_N_AXES][2]);
+static void ascii_measure_cell_width (void *, const struct table_cell *,
+                                      int *min, int *max);
+static int ascii_measure_cell_height (void *, const struct table_cell *,
+                                      int width);
+static void ascii_draw_cell (void *, const struct table_cell *,
+                             int bb[TABLE_N_AXES][2],
+                             int clip[TABLE_N_AXES][2]);
+
+static struct ascii_driver *
+ascii_driver_cast (struct output_driver *driver)
 {
-  struct outp_driver *this;
-  struct ascii_driver_ext *x;
-  int i;
+  assert (driver->class == &ascii_class);
+  return UP_CAST (driver, struct ascii_driver, driver);
+}
 
-  this = outp_allocate_driver (&ascii_class, name, types);
-  this->width = 79;
-  this->font_height = 1;
-  this->prop_em_width = 1;
-  this->fixed_width = 1;
-  for (i = 0; i < OUTP_L_COUNT; i++)
-    this->horiz_line_width[i] = this->vert_line_width[i] = i != OUTP_L_NONE;
-
-  this->ext = x = pool_create_container (struct ascii_driver_ext, pool);
-  x->append = false;
-  x->headers = true;
-  x->paginate = true;
-  x->squeeze_blank_lines = false;
-  x->emphasis = EMPH_BOLD;
-  x->tab_width = 8;
-  x->chart_file_name = pool_strdup (x->pool, "pspp-#.png");
-  x->enable_charts = true;
-  x->auto_width = false;
-  x->auto_length = false;
-  x->page_length = 66;
-  x->top_margin = 2;
-  x->bottom_margin = 2;
-  for (i = 0; i < LNS_COUNT; i++)
-    x->box[i] = NULL;
-  x->init = NULL;
-  x->file_name = pool_strdup (x->pool, "pspp.list");
-  x->file = NULL;
-  x->reported_error = false;
-  x->page_number = 0;
-  x->lines = NULL;
-  x->line_cap = 0;
-  x->chart_cnt = 1;
-
-  if (!outp_parse_options (this->name, options, handle_option, this))
-    goto error;
+static struct driver_option *
+opt (struct output_driver *d, struct string_map *options, const char *key,
+     const char *default_value)
+{
+  return driver_option_get (d, options, key, default_value);
+}
 
-  if (!update_page_size (this, true))
-    goto error;
+static struct output_driver *
+ascii_create (const char *name, enum output_device_type device_type,
+              struct string_map *o)
+{
+  struct output_driver *d;
+  struct ascii_driver *a;
+  int paper_length;
+  int right, bottom, left, top;
+
+  a = xzalloc (sizeof *a);
+  d = &a->driver;
+  output_driver_init (&a->driver, &ascii_class, name, device_type);
+  a->append = parse_boolean (opt (d, o, "append", "false"));
+  a->headers = parse_boolean (opt (d, o, "headers", "true"));
+  a->paginate = parse_boolean (opt (d, o, "paginate", "true"));
+  a->squeeze_blank_lines = parse_boolean (opt (d, o, "squeeze", "false"));
+  a->emphasis = parse_enum (opt (d, o, "emphasis", "bold"),
+                            "bold", EMPH_BOLD,
+                            "underline", EMPH_UNDERLINE,
+                            "none", EMPH_NONE,
+                            (char *) NULL);
+  a->tab_width = parse_int (opt (d, o, "tab-width", "0"), 8, INT_MAX);
+
+  if (parse_enum (opt (d, o, "chart-type", "png"),
+                  "png", true,
+                  "none", false,
+                  (char *) NULL))
+    a->chart_file_name = parse_chart_file_name (opt (d, o, "chart-files",
+                                                     "pspp-#.png"));
+  else
+    a->chart_file_name = NULL;
 
-  for (i = 0; i < LNS_COUNT; i++)
-    if (x->box[i] == NULL)
-      {
-        char s[2];
-        s[0] = get_default_box_char (i);
-        s[1] = '\0';
-        x->box[i] = pool_strdup (x->pool, s);
-      }
+  a->top_margin = parse_int (opt (d, o, "top-margin", "2"), 0, INT_MAX);
+  a->bottom_margin = parse_int (opt (d, o, "bottom-margin", "2"), 0, INT_MAX);
 
-  outp_register_driver (this);
+  a->width = parse_page_size (opt (d, o, "width", "79"));
+  paper_length = parse_page_size (opt (d, o, "length", "66"));
+  a->auto_width = a->width < 0;
+  a->auto_length = paper_length < 0;
+  a->length = paper_length - vertical_margins (a);
 
-  return true;
+  for (right = 0; right < 4; right++)
+    for (bottom = 0; bottom < 4; bottom++)
+      for (left = 0; left < 4; left++)
+        for (top = 0; top < 4; top++)
+          {
+            int indx = make_box_index (left, right, top, bottom);
+            const char *default_value;
+            char name[16];
+
+            sprintf (name, "box[%d%d%d%d]", right, bottom, left, top);
+            default_value = get_default_box (right, bottom, left, top);
+            a->box[indx] = parse_string (opt (d, o, name, default_value));
+          }
+  a->init = parse_string (opt (d, o, "init", ""));
+
+  a->title = xstrdup ("");
+  a->subtitle = xstrdup ("");
+  a->file_name = parse_string (opt (d, o, "output-file", "pspp.list"));
+  a->file = NULL;
+  a->reported_error = false;
+  a->page_number = 0;
+  a->lines = NULL;
+  a->allocated_lines = 0;
+  a->chart_cnt = 1;
+
+  if (!update_page_size (a, true))
+    goto error;
 
- error:
-  pool_destroy (x->pool);
-  outp_free_driver (this);
-  return false;
+  return d;
+
+error:
+  output_driver_destroy (d);
+  return NULL;
 }
 
-static int
-get_default_box_char (size_t idx)
+static const char *
+get_default_box (int right, int bottom, int left, int top)
 {
-  /* Disassemble IDX into components. */
-  unsigned top = (idx >> LNS_TOP) & 3;
-  unsigned left = (idx >> LNS_LEFT) & 3;
-  unsigned bottom = (idx >> LNS_BOTTOM) & 3;
-  unsigned right = (idx >> LNS_RIGHT) & 3;
-
-  /* Reassemble components into nibbles in the order TLBR.
-     This makes it easy to read the case labels. */
-  unsigned value = (top << 12) | (left << 8) | (bottom << 4) | (right << 0);
-  switch (value)
+  switch ((top << 12) | (left << 8) | (bottom << 4) | (right << 0))
     {
     case 0x0000:
-      return ' ';
+      return " ";
 
     case 0x0100: case 0x0101: case 0x0001:
-      return '-';
+      return "-";
 
     case 0x1000: case 0x1010: case 0x0010:
-      return '|';
+      return "|";
 
     case 0x0300: case 0x0303: case 0x0003:
     case 0x0200: case 0x0202: case 0x0002:
-      return '=';
+      return "=";
 
     default:
-      return left > 1 || top > 1 || right > 1 || bottom > 1 ? '#' : '+';
+      return left > 1 || top > 1 || right > 1 || bottom > 1 ? "#" : "+";
+    }
+}
+
+static int
+parse_page_size (struct driver_option *option)
+{
+  int dim = atol (option->default_value);
+
+  if (option->value != NULL)
+    {
+      if (!strcmp (option->value, "auto"))
+        dim = -1;
+      else
+        {
+          int value;
+          char *tail;
+
+          errno = 0;
+          value = strtol (option->value, &tail, 0);
+          if (dim >= 1 && errno != ERANGE && *tail == '\0')
+            dim = value;
+          else
+            error (0, 0, _("%s: %s must be positive integer or `auto'"),
+                   option->driver_name, option->name);
+        }
     }
+
+  driver_option_destroy (option);
+
+  return dim;
+}
+
+static int
+vertical_margins (const struct ascii_driver *a)
+{
+  return a->top_margin + a->bottom_margin + (a->headers ? 3 : 0);
 }
 
 /* Re-calculates the page width and length based on settings,
    margins, and, if "auto" is set, the size of the user's
    terminal window or GUI output window. */
 static bool
-update_page_size (struct outp_driver *this, bool issue_error)
+update_page_size (struct ascii_driver *a, bool issue_error)
 {
-  struct ascii_driver_ext *x = this->ext;
-  int margins = x->top_margin + x->bottom_margin + 1 + (x->headers ? 3 : 0);
-
-  if (x->auto_width)
-    this->width = settings_get_viewwidth ();
-  if (x->auto_length)
-    x->page_length = settings_get_viewlength ();
+  enum { MIN_WIDTH = 6, MIN_LENGTH = 6 };
 
-  this->length = x->page_length - margins;
+  if (a->auto_width)
+    a->width = settings_get_viewwidth ();
+  if (a->auto_length)
+    a->length = settings_get_viewlength () - vertical_margins (a);
 
-  if (this->width < 59 || this->length < 15)
+  if (a->width < MIN_WIDTH || a->length < MIN_LENGTH)
     {
       if (issue_error)
         error (0, 0,
                _("ascii: page excluding margins and headers "
-                 "must be at least 59 characters wide by 15 lines long, but "
+                 "must be at least %d characters wide by %d lines long, but "
                  "as configured is only %d characters by %d lines"),
-             this->width, this->length);
-      if (this->width < 59)
-        this->width = 59;
-      if (this->length < 15)
-        {
-          this->length = 15;
-          x->page_length = this->length + margins;
-        }
+               MIN_WIDTH, MIN_LENGTH,
+               a->width, a->length);
+      if (a->width < MIN_WIDTH)
+        a->width = MIN_WIDTH;
+      if (a->length < MIN_LENGTH)
+        a->length = MIN_LENGTH;
       return false;
     }
 
   return true;
 }
 
-static bool
-ascii_close_driver (struct outp_driver *this)
+static void
+ascii_destroy (struct output_driver *driver)
 {
-  struct ascii_driver_ext *x = this->ext;
+  struct ascii_driver *a = ascii_driver_cast (driver);
+  int i;
 
-  ascii_flush (this);
-  pool_detach_file (x->pool, x->file);
-  pool_destroy (x->pool);
+  if (a->y > 0)
+    ascii_close_page (a);
 
-  return true;
+  free (a->title);
+  free (a->subtitle);
+  free (a->file_name);
+  free (a->chart_file_name);
+  for (i = 0; i < LNS_COUNT; i++)
+    free (a->box[i]);
+  free (a->init);
+  if (a->file != NULL)
+    fclose (a->file);
+  for (i = 0; i < a->allocated_lines; i++)
+    free (a->lines[i].chars);
+  free (a->lines);
+  free (a);
 }
 
-/* Generic option types. */
-enum
-  {
-    boolean_arg,
-    emphasis_arg,
-    nonneg_int_arg,
-    page_size_arg,
-    string_arg
-  };
-
-static const struct outp_option option_tab[] =
-  {
-    {"headers", boolean_arg, 0},
-    {"paginate", boolean_arg, 1},
-    {"squeeze", boolean_arg, 2},
-    {"append", boolean_arg, 3},
+static void
+ascii_flush (struct output_driver *driver)
+{
+  struct ascii_driver *a = ascii_driver_cast (driver);
+  if (a->file != NULL)
+    fflush (a->file);
+}
 
-    {"emphasis", emphasis_arg, 0},
+static void
+ascii_init_caption_cell (const char *caption, struct table_cell *cell)
+{
+  cell->contents = caption;
+  cell->options = TAB_LEFT;
+  cell->destructor = NULL;
+}
 
-    {"length", page_size_arg, 0},
-    {"width", page_size_arg, 1},
+static void
+ascii_submit (struct output_driver *driver,
+              const struct output_item *output_item)
+{
+  struct ascii_driver *a = ascii_driver_cast (driver);
+  if (is_table_item (output_item))
+    {
+      struct table_item *table_item = to_table_item (output_item);
+      const char *caption = table_item_get_caption (table_item);
+      struct render_params params;
+      struct render_page *page;
+      struct render_break x_break;
+      int caption_height;
+      int i;
 
-    {"top-margin", nonneg_int_arg, 0},
-    {"bottom-margin", nonneg_int_arg, 1},
-    {"tab-width", nonneg_int_arg, 2},
+      update_page_size (a, false);
 
-    {"output-file", string_arg, 0},
-    {"chart-files", string_arg, 1},
-    {"chart-type", string_arg, 2},
-    {"init", string_arg, 3},
+      if (caption != NULL)
+        {
+          /* XXX doesn't do well with very large captions */
+          struct table_cell cell;
+          ascii_init_caption_cell (caption, &cell);
+          caption_height = ascii_measure_cell_height (a, &cell, a->width);
+        }
+      else
+        caption_height = 0;
+
+      params.draw_line = ascii_draw_line;
+      params.measure_cell_width = ascii_measure_cell_width;
+      params.measure_cell_height = ascii_measure_cell_height;
+      params.draw_cell = ascii_draw_cell,
+      params.aux = a;
+      params.size[H] = a->width;
+      params.size[V] = a->length - caption_height;
+      params.font_size[H] = 1;
+      params.font_size[V] = 1;
+      for (i = 0; i < RENDER_N_LINES; i++)
+        {
+          int width = i == RENDER_LINE_NONE ? 0 : 1;
+          params.line_widths[H][i] = width;
+          params.line_widths[V][i] = width;
+        }
 
-    {NULL, 0, 0},
-  };
+      if (a->file == NULL)
+        {
+          ascii_open_page (a);
+          a->y = 0;
+        }
 
-static bool
-handle_option (void *this_, const char *key,
-               const struct string *val)
-{
-  struct outp_driver *this = this_;
-  struct ascii_driver_ext *x = this->ext;
-  int subcat;
-  const char *value;
+      page = render_page_create (&params, table_item_get_table (table_item));
+      for (render_break_init (&x_break, page, H);
+           render_break_has_next (&x_break); )
+        {
+          struct render_page *x_slice;
+          struct render_break y_break;
 
-  value = ds_cstr (val);
-  if (!strncmp (key, "box[", 4))
-    {
-      char *tail;
-      int indx = strtol (&key[4], &tail, 4);
-      if (*tail != ']' || indx < 0 || indx > LNS_COUNT)
-       {
-         error (0, 0, _("ascii: bad index value for `box' key: syntax "
-                         "is box[INDEX], 0 <= INDEX < %d decimal, with INDEX "
-                         "expressed in base 4"),
-                 LNS_COUNT);
-         return false;
-       }
-      if (x->box[indx] != NULL)
-       error (0, 0, _("ascii: multiple values for %s"), key);
-      x->box[indx] = pool_strdup (x->pool, value);
-      return true;
+          x_slice = render_break_next (&x_break, a->width);
+          for (render_break_init (&y_break, x_slice, V);
+               render_break_has_next (&y_break); )
+            {
+              struct render_page *y_slice;
+              int space;
+
+              if (a->y > 0)
+                a->y++;
+
+              space = a->length - a->y - caption_height;
+              if (render_break_next_size (&y_break) > space)
+                {
+                  assert (a->y > 0);
+                  ascii_close_page (a);
+                  a->y = 0;
+                  ascii_open_page (a);
+                  continue;
+                }
+
+              y_slice = render_break_next (&y_break, space);
+              if (caption_height)
+                {
+                  struct table_cell cell;
+                  int bb[TABLE_N_AXES][2];
+
+                  ascii_init_caption_cell (caption, &cell);
+                  bb[H][0] = 0;
+                  bb[H][1] = a->width;
+                  bb[V][0] = 0;
+                  bb[V][1] = caption_height;
+                  ascii_draw_cell (a, &cell, bb, bb);
+                  a->y += caption_height;
+                  caption_height = 0;
+                }
+              render_page_draw (y_slice);
+              a->y += render_page_get_size (y_slice, V);
+              render_page_unref (y_slice);
+            }
+          render_break_destroy (&y_break);
+        }
+      render_break_destroy (&x_break);
     }
+  else if (is_chart_item (output_item) && a->chart_file_name != NULL)
+    {
+      struct chart_item *chart_item = to_chart_item (output_item);
+      char *file_name;
 
-  switch (outp_match_keyword (key, option_tab, &subcat))
+      file_name = xr_draw_png_chart (chart_item, a->chart_file_name,
+                                     a->chart_cnt++);
+      if (file_name != NULL)
+        {
+          struct text_item *text_item;
+
+          text_item = text_item_create_format (
+            TEXT_ITEM_PARAGRAPH, _("See %s for a chart."), file_name);
+
+          ascii_submit (driver, &text_item->output_item);
+          text_item_unref (text_item);
+          free (file_name);
+        }
+    }
+  else if (is_text_item (output_item))
     {
-    case -1:
-      error (0, 0, _("ascii: unknown parameter `%s'"), key);
-      break;
-    case page_size_arg:
-      {
-       char *tail;
-       int arg;
+      const struct text_item *text_item = to_text_item (output_item);
+      enum text_item_type type = text_item_get_type (text_item);
+      const char *text = text_item_get_text (text_item);
 
-        if (ss_equals_case (ds_ss (val), ss_cstr ("auto")))
-          {
-            if (!(this->device & OUTP_DEV_SCREEN))
-              {
-                /* We only let `screen' devices have `auto'
-                   length or width because output to such devices
-                   is flushed before each new command.  Resizing
-                   a device in the middle of output seems like a
-                   bad idea. */
-                error (0, 0, _("ascii: only screen devices may have `auto' "
-                               "length or width"));
-              }
-            else if (subcat == 0)
-              x->auto_length = true;
-            else
-              x->auto_width = true;
-          }
-        else
-          {
-            errno = 0;
-            arg = strtol (value, &tail, 0);
-            if (arg < 1 || errno == ERANGE || *tail)
-              {
-                error (0, 0, _("ascii: positive integer required as "
-                               "`%s' value"),
-                       key);
-                break;
-              }
-            switch (subcat)
-              {
-              case 0:
-                x->page_length = arg;
-                break;
-              case 1:
-                this->width = arg;
-                break;
-              default:
-                NOT_REACHED ();
-              }
-          }
-      }
-      break;
-    case emphasis_arg:
-      if (!strcmp (value, "bold"))
-        x->emphasis = EMPH_BOLD;
-      else if (!strcmp (value, "underline"))
-        x->emphasis = EMPH_UNDERLINE;
-      else if (!strcmp (value, "none"))
-        x->emphasis = EMPH_NONE;
-      else
-        error (0, 0,
-               _("ascii: `emphasis' value must be `bold', "
-                 "`underline', or `none'"));
-      break;
-    case nonneg_int_arg:
-      {
-       char *tail;
-       int arg;
-
-       errno = 0;
-       arg = strtol (value, &tail, 0);
-       if (arg < 0 || errno == ERANGE || *tail)
-         {
-           error (0, 0,
-                   _("ascii: zero or positive integer required as `%s' value"),
-                   key);
-           break;
-         }
-       switch (subcat)
-         {
-         case 0:
-           x->top_margin = arg;
-           break;
-         case 1:
-           x->bottom_margin = arg;
-           break;
-         case 2:
-           x->tab_width = arg;
-           break;
-         default:
-           NOT_REACHED ();
-         }
-      }
-      break;
-    case boolean_arg:
-      {
-       bool setting;
-       if (!strcmp (value, "on") || !strcmp (value, "true")
-           || !strcmp (value, "yes") || atoi (value))
-         setting = true;
-       else if (!strcmp (value, "off") || !strcmp (value, "false")
-                || !strcmp (value, "no") || !strcmp (value, "0"))
-         setting = false;
-       else
-         {
-           error (0, 0, _("ascii: boolean value expected for `%s'"), key);
-           return false;
-         }
-       switch (subcat)
-         {
-         case 0:
-           x->headers = setting;
-           break;
-         case 1:
-           x->paginate = setting;
-           break;
-          case 2:
-            x->squeeze_blank_lines = setting;
-            break;
-          case 3:
-            x->append = setting;
-            break;
-         default:
-           NOT_REACHED ();
-         }
-      }
-      break;
-    case string_arg:
-      switch (subcat)
+      switch (type)
         {
-        case 0:
-          x->file_name = pool_strdup (x->pool, value);
+        case TEXT_ITEM_TITLE:
+          free (a->title);
+          a->title = xstrdup (text);
           break;
-        case 1:
-          if (ds_find_char (val, '#') != SIZE_MAX)
-            x->chart_file_name = pool_strdup (x->pool, value);
-          else
-            error (0, 0, _("`chart-files' value must contain `#'"));
+
+        case TEXT_ITEM_SUBTITLE:
+          free (a->subtitle);
+          a->subtitle = xstrdup (text);
           break;
-        case 2:
-          if (!strcmp (value, "png"))
-            x->enable_charts = true;
-          else if (!strcmp (value, "none"))
-            x->enable_charts = false;
-          else
-            {
-              error (0, 0,
-                     _("ascii: `png' or `none' expected for `chart-type'"));
-              return false;
-            }
+
+        case TEXT_ITEM_COMMAND_CLOSE:
+          break;
+
+        case TEXT_ITEM_BLANK_LINE:
+          if (a->y > 0)
+            a->y++;
           break;
-        case 3:
-          x->init = pool_strdup (x->pool, value);
+
+        case TEXT_ITEM_EJECT_PAGE:
+          if (a->y > 0)
+            ascii_close_page (a);
+          break;
+
+        default:
+          {
+            struct table_item *item;
+
+            item = table_item_create (table_from_string (0, text), NULL);
+            ascii_submit (&a->driver, &item->output_item);
+            table_item_unref (item);
+          }
           break;
         }
-      break;
-    default:
-      NOT_REACHED ();
     }
-
-  return true;
 }
 
+const struct output_driver_class ascii_class =
+  {
+    "ascii",
+    ascii_create,
+    ascii_destroy,
+    ascii_submit,
+    ascii_flush,
+  };
+\f
+enum wrap_mode
+  {
+    WRAP_WORD,
+    WRAP_CHAR,
+    WRAP_WORD_CHAR
+  };
+
+static void ascii_expand_line (struct ascii_driver *, int y, int length);
+static void ascii_layout_cell (struct ascii_driver *,
+                               const struct table_cell *,
+                               int bb[TABLE_N_AXES][2],
+                               int clip[TABLE_N_AXES][2], enum wrap_mode wrap,
+                               int *width, int *height);
+
 static void
-ascii_open_page (struct outp_driver *this)
+ascii_draw_line (void *a_, int bb[TABLE_N_AXES][2],
+                 enum render_line_style styles[TABLE_N_AXES][2])
 {
-  struct ascii_driver_ext *x = this->ext;
-  int i;
+  struct ascii_driver *a = a_;
+  unsigned short int value;
+  int x1, y1;
+  int x, y;
 
-  update_page_size (this, false);
+  /* Clip to the page. */
+  if (bb[H][0] >= a->width || bb[V][0] + a->y >= a->length)
+    return;
+  x1 = MIN (bb[H][1], a->width);
+  y1 = MIN (bb[V][1] + a->y, a->length);
 
-  if (x->file == NULL)
+  /* Draw. */
+  value = ATTR_BOX | make_box_index (styles[V][0], styles[V][1],
+                                     styles[H][0], styles[H][1]);
+  for (y = bb[V][0] + a->y; y < y1; y++)
     {
-      x->file = fn_open (x->file_name, x->append ? "a" : "w");
-      if (x->file != NULL)
-        {
-          pool_attach_file (x->pool, x->file);
-          if (x->init != NULL)
-            fputs (x->init, x->file);
-        }
-      else
-        {
-          /* Report the error to the user and complete
-             initialization.  If we do not finish initialization,
-             then calls to other driver functions will segfault
-             later.  It would be better to simply drop the driver
-             entirely, but we do not have a convenient mechanism
-             for this (yet). */
-          if (!x->reported_error)
-            error (0, errno, _("ascii: opening output file \"%s\""),
-                   x->file_name);
-          x->reported_error = true;
-        }
+      ascii_expand_line (a, y, x1);
+      for (x = bb[H][0]; x < x1; x++)
+        a->lines[y].chars[x] = value;
     }
+}
 
-  x->page_number++;
-
-  if (this->length > x->line_cap)
+static void
+ascii_measure_cell_width (void *a_, const struct table_cell *cell,
+                          int *min_width, int *max_width)
+{
+  struct ascii_driver *a = a_;
+  int bb[TABLE_N_AXES][2];
+  int clip[TABLE_N_AXES][2];
+  int h;
+
+  bb[H][0] = 0;
+  bb[H][1] = INT_MAX;
+  bb[V][0] = 0;
+  bb[V][1] = INT_MAX;
+  clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
+  ascii_layout_cell (a, cell, bb, clip, WRAP_WORD, max_width, &h);
+
+  if (strchr (cell->contents, ' '))
     {
-      x->lines = pool_nrealloc (x->pool,
-                                x->lines, this->length, sizeof *x->lines);
-      for (i = x->line_cap; i < this->length; i++)
-        {
-          struct line *line = &x->lines[i];
-          line->chars = NULL;
-          line->char_cap = 0;
-        }
-      x->line_cap = this->length;
+      bb[H][1] = 1;
+      ascii_layout_cell (a, cell, bb, clip, WRAP_WORD, min_width, &h);
     }
-
-  for (i = 0; i < this->length; i++)
-    x->lines[i].char_cnt = 0;
+  else
+    *min_width = *max_width;
 }
 
-/* Ensures that at least the first LENGTH characters of line Y in
-   THIS driver identified X have been cleared out. */
-static inline void
-expand_line (struct outp_driver *this, int y, int length)
+static int
+ascii_measure_cell_height (void *a_, const struct table_cell *cell, int width)
 {
-  struct ascii_driver_ext *ext = this->ext;
-  struct line *line = &ext->lines[y];
-  if (line->char_cnt < length)
-    {
-      int x;
-      if (line->char_cap < length)
-        {
-          line->char_cap = MIN (length * 2, this->width);
-          line->chars = pool_nrealloc (ext->pool,
-                                       line->chars,
-                                       line->char_cap, sizeof *line->chars);
-        }
-      for (x = line->char_cnt; x < length; x++)
-        line->chars[x] = ' ';
-      line->char_cnt = length;
-    }
+  struct ascii_driver *a = a_;
+  int bb[TABLE_N_AXES][2];
+  int clip[TABLE_N_AXES][2];
+  int w, h;
+
+  bb[H][0] = 0;
+  bb[H][1] = width;
+  bb[V][0] = 0;
+  bb[V][1] = INT_MAX;
+  clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
+  ascii_layout_cell (a, cell, bb, clip, WRAP_WORD, &w, &h);
+  return h;
 }
 
 static void
-ascii_line (struct outp_driver *this,
-            int x0, int y0, int x1, int y1,
-            enum outp_line_style top, enum outp_line_style left,
-            enum outp_line_style bottom, enum outp_line_style right)
+ascii_draw_cell (void *a_, const struct table_cell *cell,
+                 int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2])
 {
-  struct ascii_driver_ext *ext = this->ext;
-  int y;
-  unsigned short value;
+  struct ascii_driver *a = a_;
+  int w, h;
 
-  assert (this->page_open);
-#if DEBUGGING
-  if (x0 < 0 || x1 > this->width || y0 < 0 || y1 > this->length)
-    {
-#if !SUPPRESS_WARNINGS
-      printf (_("ascii: bad line (%d,%d)-(%d,%d) out of (%d,%d)\n"),
-             x0, y0, x1, y1, this->width, this->length);
-#endif
-      return;
-    }
-#endif
+  ascii_layout_cell (a, cell, bb, clip, WRAP_WORD, &w, &h);
+}
 
-  value = ((left << LNS_LEFT) | (right << LNS_RIGHT)
-           | (top << LNS_TOP) | (bottom << LNS_BOTTOM) | ATTR_BOX);
-  for (y = y0; y < y1; y++)
+/* Ensures that at least the first LENGTH characters of line Y in
+   ascii driver A have been cleared out. */
+static void
+ascii_expand_line (struct ascii_driver *a, int y, int length)
+{
+  struct ascii_line *line = &a->lines[y];
+  if (line->n_chars < length)
     {
       int x;
-
-      expand_line (this, y, x1);
-      for (x = x0; x < x1; x++)
-        ext->lines[y].chars[x] = value;
+      if (line->allocated_chars < length)
+        {
+          line->allocated_chars = MAX (length, MIN (length * 2, a->width));
+          line->chars = xnrealloc (line->chars, line->allocated_chars,
+                                   sizeof *line->chars);
+        }
+      for (x = line->n_chars; x < length; x++)
+        line->chars[x] = ' ';
+      line->n_chars = length;
     }
 }
 
 static void
-text_draw (struct outp_driver *this,
-           enum outp_font font,
-           int x, int y,
-           enum outp_justification justification, int width,
-           const char *string, size_t length)
+text_draw (struct ascii_driver *a, const struct table_cell *cell,
+           int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
+           int y, const char *string, int n)
 {
-  struct ascii_driver_ext *ext = this->ext;
-  unsigned short attr = font == OUTP_EMPHASIS ? ATTR_EMPHASIS : 0;
-
-  int line_len;
+  int x0 = MAX (0, clip[H][0]);
+  int y0 = MAX (0, clip[V][0] + a->y);
+  int x1 = clip[H][1];
+  int y1 = MIN (a->length, clip[V][1] + a->y);
+  int x;
+
+  y += a->y;
+  if (y < y0 || y >= y1)
+    return;
 
-  switch (justification)
+  switch (cell->options & TAB_ALIGNMENT)
     {
-    case OUTP_LEFT:
+    case TAB_LEFT:
+      x = bb[H][0];
       break;
-    case OUTP_CENTER:
-      x += (width - length + 1) / 2;
+    case TAB_CENTER:
+      x = (bb[H][0] + bb[H][1] - n + 1) / 2;
       break;
-    case OUTP_RIGHT:
-      x += width - length;
+    case TAB_RIGHT:
+      x = bb[H][1] - n;
       break;
     default:
       NOT_REACHED ();
     }
 
-  if (y >= this->length || x >= this->width)
-    return;
-
-  if (x + length > this->width)
-    length = this->width - x;
+  if (x0 > x)
+    {
+      n -= x0 - x;
+      if (n <= 0)
+        return;
+      string += x0 - x;
+      x = x0;
+    }
+  if (x + n >= x1)
+    n = x1 - x;
 
-  line_len = x + length;
+  if (n > 0)
+    {
+      int attr = cell->options & TAB_EMPH ? ATTR_EMPHASIS : 0;
+      size_t i;
 
-  expand_line (this, y, line_len);
-  while (length-- > 0)
-    ext->lines[y].chars[x++] = *string++ | attr;
+      ascii_expand_line (a, y, x + n);
+      for (i = 0; i < n; i++)
+        a->lines[y].chars[x + i] = string[i] | attr;
+    }
 }
 
-/* Divides the text T->S into lines of width T->H.  Sets *WIDTH
-   to the maximum width of a line and *HEIGHT to the number of
-   lines, if those arguments are non-null.  Actually draws the
-   text if DRAW is true. */
 static void
-delineate (struct outp_driver *this, const struct outp_text *text, bool draw,
-           int *width, int *height)
+ascii_layout_cell (struct ascii_driver *a, const struct table_cell *cell,
+                   int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
+                   enum wrap_mode wrap, int *width, int *height)
 {
-  int max_width;
-  int height_left;
-
-  const char *cp = ss_data (text->string);
-
-  max_width = 0;
-  height_left = text->v;
+  size_t length = strlen (cell->contents);
+  int y, pos;
 
-  while (height_left > 0)
+  *width = 0;
+  pos = 0;
+  for (y = bb[V][0]; y < bb[V][1] && pos < length; y++)
     {
-      size_t chars_left;
+      const char *line = &cell->contents[pos];
+      const char *new_line;
       size_t line_len;
-      const char *end;
 
-      /* Initially the line is up to text->h characters long. */
-      chars_left = ss_end (text->string) - cp;
-      if (chars_left == 0)
-        break;
-      line_len = MIN (chars_left, text->h);
+      /* Find line length without considering word wrap. */
+      line_len = MIN (bb[H][1] - bb[H][0], length - pos);
+      new_line = memchr (line, '\n', line_len);
+      if (new_line != NULL)
+        line_len = new_line - line;
 
-      /* A new-line terminates the line prematurely. */
-      end = memchr (cp, '\n', line_len);
-      if (end != NULL)
-        line_len = end - cp;
-
-      /* Don't cut off words if it can be avoided. */
-      if (cp + line_len < ss_end (text->string))
+      /* Word wrap. */
+      if (pos + line_len < length && wrap != WRAP_CHAR)
         {
           size_t space_len = line_len;
-          while (space_len > 0 && !isspace ((unsigned char) cp[space_len]))
+          while (space_len > 0 && !isspace ((unsigned char) line[space_len]))
             space_len--;
           if (space_len > 0)
             line_len = space_len;
+          else if (wrap == WRAP_WORD)
+            {
+              while (pos + line_len < length
+                     && !isspace ((unsigned char) line[line_len]))
+                line_len++;
+            }
         }
+      if (line_len > *width)
+        *width = line_len;
 
       /* Draw text. */
-      if (draw)
-        text_draw (this,
-                   text->font,
-                   text->x, text->y + (text->v - height_left),
-                   text->justification, text->h,
-                   cp, line_len);
-
-      /* Update. */
-      height_left--;
-      if (line_len > max_width)
-        max_width = line_len;
+      text_draw (a, cell, bb, clip, y, line, line_len);
 
       /* Next line. */
-      cp += line_len;
-      if (cp < ss_end (text->string) && isspace ((unsigned char) *cp))
-        cp++;
+      pos += line_len;
+      if (pos < length && isspace ((unsigned char) cell->contents[pos]))
+        pos++;
     }
-
-  if (width != NULL)
-    *width = max_width;
-  if (height != NULL)
-    *height = text->v - height_left;
+  *height = y - bb[V][0];
 }
+\f
+/* ascii_close_page () and support routines. */
 
 static void
-ascii_text_metrics (struct outp_driver *this, const struct outp_text *t,
-                    int *width, int *height)
+ascii_open_page (struct ascii_driver *a)
 {
-  delineate (this, t, false, width, height);
-}
+  int i;
 
-static void
-ascii_text_draw (struct outp_driver *this, const struct outp_text *t)
-{
-  assert (this->page_open);
-  delineate (this, t, true, NULL, NULL);
+  if (a->file == NULL)
+    {
+      a->file = fn_open (a->file_name, a->append ? "a" : "w");
+      if (a->file != NULL)
+        {
+          if (a->init != NULL)
+            fputs (a->init, a->file);
+        }
+      else
+        {
+          /* Report the error to the user and complete
+             initialization.  If we do not finish initialization,
+             then calls to other driver functions will segfault
+             later.  It would be better to simply drop the driver
+             entirely, but we do not have a convenient mechanism
+             for this (yet). */
+          if (!a->reported_error)
+            error (0, errno, _("ascii: opening output file \"%s\""),
+                   a->file_name);
+          a->reported_error = true;
+        }
+    }
+
+  a->page_number++;
+
+  if (a->length > a->allocated_lines)
+    {
+      a->lines = xnrealloc (a->lines, a->length, sizeof *a->lines);
+      for (i = a->allocated_lines; i < a->length; i++)
+        {
+          struct ascii_line *line = &a->lines[i];
+          line->chars = NULL;
+          line->allocated_chars = 0;
+        }
+      a->allocated_lines = a->length;
+    }
+
+  for (i = 0; i < a->length; i++)
+    a->lines[i].n_chars = 0;
 }
-\f
-/* ascii_close_page () and support routines. */
 
-/* Writes the LENGTH characters in S to OUT.  */
+/* Writes LINE to A's output file.  */
 static void
-output_line (struct outp_driver *this, const struct line *line,
-             struct string *out)
+output_line (struct ascii_driver *a, const struct ascii_line *line)
 {
-  struct ascii_driver_ext *ext = this->ext;
-  const unsigned short *s = line->chars;
   size_t length;
+  size_t i;
 
-  for (length = line->char_cnt; length-- > 0; s++)
-    if (*s & ATTR_BOX)
-      ds_put_cstr (out, ext->box[*s & 0xff]);
-    else
-      {
-        if (*s & ATTR_EMPHASIS)
-          {
-            if (ext->emphasis == EMPH_BOLD)
-              {
-                ds_put_char (out, *s);
-                ds_put_char (out, '\b');
-              }
-            else if (ext->emphasis == EMPH_UNDERLINE)
-              ds_put_cstr (out, "_\b");
-          }
-        ds_put_char (out, *s);
-      }
+  length = line->n_chars;
+  while (length > 0 && line->chars[length - 1] == ' ')
+    length--;
+
+  for (i = 0; i < length; i++)
+    {
+      int attribute = line->chars[i] & (ATTR_BOX | ATTR_EMPHASIS);
+      int ch = line->chars[i] & ~(ATTR_BOX | ATTR_EMPHASIS);
+
+      switch (attribute)
+        {
+        case ATTR_BOX:
+          fputs (a->box[ch], a->file);
+          break;
+
+        case ATTR_EMPHASIS:
+          if (a->emphasis == EMPH_BOLD)
+            fprintf (a->file, "%c\b%c", ch, ch);
+          else if (a->emphasis == EMPH_UNDERLINE)
+            fprintf (a->file, "_\b%c", ch);
+          else
+            putc (ch, a->file);
+          break;
+
+        default:
+          putc (ch, a->file);
+          break;
+        }
+    }
+
+  putc ('\n', a->file);
 }
 
 static void
-append_lr_justified (struct string *out, int width,
-                     const char *left, const char *right)
+output_title_line (FILE *out, int width, const char *left, const char *right)
 {
-  ds_put_char_multiple (out, ' ', width);
+  struct string s = DS_EMPTY_INITIALIZER;
+  ds_put_char_multiple (&s, ' ', width);
   if (left != NULL)
     {
       size_t length = MIN (strlen (left), width);
-      memcpy (ds_end (out) - width, left, length);
+      memcpy (ds_end (&s) - width, left, length);
     }
   if (right != NULL)
     {
       size_t length = MIN (strlen (right), width);
-      memcpy (ds_end (out) - length, right, length);
+      memcpy (ds_end (&s) - length, right, length);
     }
-  ds_put_char (out, '\n');
+  ds_put_char (&s, '\n');
+  fputs (ds_cstr (&s), out);
+  ds_destroy (&s);
 }
 
 static void
-dump_output (struct outp_driver *this, struct string *out)
+ascii_close_page (struct ascii_driver *a)
 {
-  struct ascii_driver_ext *x = this->ext;
-  fwrite (ds_data (out), ds_length (out), 1, x->file);
-  ds_clear (out);
-}
-
-static void
-ascii_close_page (struct outp_driver *this)
-{
-  struct ascii_driver_ext *x = this->ext;
-  struct string out;
-  int line_num;
+  bool any_blank;
+  int i, y;
 
-  if (x->file == NULL)
+  if (a->file == NULL)
     return;
 
-  ds_init_empty (&out);
+  if (!a->top_margin && !a->bottom_margin && a->squeeze_blank_lines
+      && !a->paginate && a->page_number > 1)
+    putc ('\n', a->file);
 
-  ds_put_char_multiple (&out, '\n', x->top_margin);
-  if (x->headers)
+  for (i = 0; i < a->top_margin; i++)
+    putc ('\n', a->file);
+  if (a->headers)
     {
       char *r1, *r2;
 
-      r1 = xasprintf (_("%s - Page %d"), get_start_date (), x->page_number);
+      r1 = xasprintf (_("%s - Page %d"), get_start_date (), a->page_number);
       r2 = xasprintf ("%s - %s" , version, host_system);
 
-      append_lr_justified (&out, this->width, outp_title, r1);
-      append_lr_justified (&out, this->width, outp_subtitle, r2);
-      ds_put_char (&out, '\n');
+      output_title_line (a->file, a->width, a->title, r1);
+      output_title_line (a->file, a->width, a->subtitle, r2);
+      putc ('\n', a->file);
 
       free (r1);
       free (r2);
     }
-  dump_output (this, &out);
 
-  for (line_num = 0; line_num < this->length; line_num++)
+  any_blank = false;
+  for (y = 0; y < a->allocated_lines; y++)
     {
+      struct ascii_line *line = &a->lines[y];
 
-      /* Squeeze multiple blank lines into a single blank line if
-         requested. */
-      if (x->squeeze_blank_lines)
+      if (a->squeeze_blank_lines && y > 0 && line->n_chars == 0)
+        any_blank = true;
+      else
         {
-          if (line_num >= x->line_cap)
-            break;
-          if (line_num > 0
-              && x->lines[line_num].char_cnt == 0
-              && x->lines[line_num - 1].char_cnt == 0)
-            continue;
-        }
-
-      if (line_num < x->line_cap)
-        output_line (this, &x->lines[line_num], &out);
-      ds_put_char (&out, '\n');
-      dump_output (this, &out);
-    }
-
-  ds_put_char_multiple (&out, '\n', x->bottom_margin);
-  if (x->paginate)
-    ds_put_char (&out, '\f');
-
-  dump_output (this, &out);
-  ds_destroy (&out);
-}
-
-/* Flushes all output to the user and lets the user deal with it.
-   This is applied only to output drivers that are designated as
-   "screen" drivers that the user is interacting with in real
-   time. */
-static void
-ascii_flush (struct outp_driver *this)
-{
-  struct ascii_driver_ext *x = this->ext;
-  if (x->file != NULL)
-    {
-      if (fn_close (x->file_name, x->file) != 0)
-        error (0, errno, _("ascii: closing output file \"%s\""),
-               x->file_name);
-      pool_detach_file (x->pool, x->file);
-      x->file = NULL;
-    }
-}
+          if (any_blank)
+            {
+              putc ('\n', a->file);
+              any_blank = false;
+            }
 
-static void
-ascii_output_chart (struct outp_driver *this, const struct chart *chart)
-{
-  struct ascii_driver_ext *x = this->ext;
-  struct outp_text t;
-  char *file_name;
-  char *text;
-
-  /* Draw chart into separate file */
-  file_name = chart_draw_png (chart, x->chart_file_name, x->chart_cnt++);
-
-  /* Mention chart in output.
-     First advance current position. */
-  if (!this->page_open)
-    outp_open_page (this);
-  else
-    {
-      this->cp_y++;
-      if (this->cp_y >= this->length)
-        {
-          outp_close_page (this);
-          outp_open_page (this);
+          output_line (a, line);
         }
     }
-
-  /* Then write the text. */
-  text = xasprintf ("See %s for a chart.", file_name);
-  t.font = OUTP_FIXED;
-  t.justification = OUTP_LEFT;
-  t.string = ss_cstr (text);
-  t.h = this->width;
-  t.v = 1;
-  t.x = 0;
-  t.y = this->cp_y;
-  ascii_text_draw (this, &t);
-  this->cp_y++;
-
-  free (file_name);
-  free (text);
+  if (!a->squeeze_blank_lines)
+    for (y = a->allocated_lines; y < a->length; y++)
+      putc ('\n', a->file);
+
+  for (i = 0; i < a->bottom_margin; i++)
+    putc ('\n', a->file);
+  if (a->paginate)
+    putc ('\f', a->file);
 }
-
-const struct outp_class ascii_class =
-{
-  "ascii",
-  0,
-
-  ascii_open_driver,
-  ascii_close_driver,
-
-  ascii_open_page,
-  ascii_close_page,
-  ascii_flush,
-
-  ascii_output_chart,
-
-  NULL,                         /* submit */
-
-  ascii_line,
-  ascii_text_metrics,
-  ascii_text_draw,
-};
index b89dc05cba6abbbdafa5bcbf083b0ee99c193fd0..1d9f65868bfdb651ffb68d35096926a325449dcf 100644 (file)
@@ -6,37 +6,63 @@ src_output_liboutput_la_CPPFLAGS = $(LIBXML2_CFLAGS) $(AM_CPPFLAGS)
 
 src_output_liboutput_la_SOURCES = \
        src/output/ascii.c \
-       src/output/chart.c \
-       src/output/chart.h \
+       src/output/chart-item-provider.h \
+       src/output/chart-item.c \
+       src/output/chart-item.h \
        src/output/charts/boxplot.c \
        src/output/charts/boxplot.h \
-       src/output/charts/cartesian.c \
-       src/output/charts/cartesian.h \
        src/output/charts/np-plot.c \
        src/output/charts/np-plot.h \
        src/output/charts/piechart.c \
        src/output/charts/piechart.h \
-       src/output/charts/plot-chart.c \
-       src/output/charts/plot-chart.h \
        src/output/charts/plot-hist.c \
        src/output/charts/plot-hist.h \
        src/output/charts/roc-chart.c \
        src/output/charts/roc-chart.h \
        src/output/charts/scree.c \
        src/output/charts/scree.h \
+       src/output/csv.c \
+       src/output/driver-provider.h \
+       src/output/driver.c \
+       src/output/driver.h \
        src/output/html.c \
-       src/output/htmlP.h \
        src/output/journal.c \
        src/output/journal.h \
-       src/output/manager.c \
-       src/output/manager.h \
+       src/output/measure.c \
+       src/output/measure.h \
        src/output/odt.c \
-       src/output/output.c \
-       src/output/output.h \
+       src/output/options.c \
+       src/output/options.h \
+       src/output/output-item-provider.h \
+       src/output/output-item.c \
+       src/output/output-item.h \
+       src/output/render.c \
+       src/output/render.h \
+       src/output/tab.c \
+       src/output/tab.h \
+       src/output/table-casereader.c \
+       src/output/table-item.c \
+       src/output/table-item.h \
+       src/output/table-paste.c \
+       src/output/table-provider.h \
+       src/output/table-select.c \
+       src/output/table-transpose.c \
        src/output/table.c \
-       src/output/table.h
+       src/output/table.h \
+       src/output/text-item.c \
+       src/output/text-item.h
 if HAVE_CAIRO
-src_output_liboutput_la_SOURCES += src/output/cairo.c
+src_output_liboutput_la_SOURCES += \
+       src/output/cairo-chart.c \
+       src/output/cairo-chart.h \
+       src/output/cairo.c \
+       src/output/cairo.h \
+       src/output/charts/boxplot-cairo.c \
+       src/output/charts/np-plot-cairo.c \
+       src/output/charts/piechart-cairo.c \
+       src/output/charts/plot-hist-cairo.c \
+       src/output/charts/roc-chart-cairo.c \
+       src/output/charts/scree-cairo.c
 endif
 
 EXTRA_DIST += src/output/OChangeLog
diff --git a/src/output/cairo-chart.c b/src/output/cairo-chart.c
new file mode 100644 (file)
index 0000000..b5e17e0
--- /dev/null
@@ -0,0 +1,499 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2004, 2009 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 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.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+
+#include "output/cairo-chart.h"
+
+#include <assert.h>
+#include <cairo/cairo.h>
+#include <pango/pango.h>
+#include <pango/pangocairo.h>
+#include <errno.h>
+#include <float.h>
+#include <math.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "libpspp/assertion.h"
+#include "math/chart-geometry.h"
+#include "output/cairo.h"
+#include "output/chart-item.h"
+
+#include "error.h"
+#include "xalloc.h"
+#include "xvasprintf.h"
+
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
+
+void
+xrchart_geometry_init (cairo_t *cr, struct xrchart_geometry *geom,
+                       double width, double length)
+{
+  /* Set default chartetry. */
+  geom->data_top = 0.900 * length;
+  geom->data_right = 0.800 * width;
+  geom->data_bottom = 0.120 * length;
+  geom->data_left = 0.150 * width;
+  geom->abscissa_top = 0.070 * length;
+  geom->ordinate_right = 0.120 * width;
+  geom->title_bottom = 0.920 * length;
+  geom->legend_left = 0.810 * width;
+  geom->legend_right = width;
+  geom->font_size = 15.0;
+  geom->in_path = false;
+  geom->dataset = NULL;
+  geom->n_datasets = 0;
+
+  geom->fill_colour.red = 255;
+  geom->fill_colour.green = 0;
+  geom->fill_colour.blue = 0;
+
+  cairo_set_line_width (cr, 1.0);
+
+  cairo_rectangle (cr, geom->data_left, geom->data_bottom,
+                   geom->data_right - geom->data_left,
+                   geom->data_top - geom->data_bottom);
+  cairo_stroke (cr);
+}
+
+void
+xrchart_geometry_free (cairo_t *cr UNUSED, struct xrchart_geometry *geom)
+{
+  int i;
+
+  for (i = 0 ; i < geom->n_datasets; ++i)
+    free (geom->dataset[i]);
+  free (geom->dataset);
+}
+
+#if ! PANGO_VERSION_CHECK (2, 22, 0)
+int pango_layout_get_baseline (PangoLayout    *layout);
+
+/* Shamelessly copied from the pango source */
+int
+pango_layout_get_baseline (PangoLayout    *layout)
+{
+  int baseline;
+
+  /* XXX this is so inefficient */
+  PangoLayoutIter *iter = pango_layout_get_iter (layout);
+  baseline = pango_layout_iter_get_baseline (iter);
+  pango_layout_iter_free (iter);
+
+  return baseline;
+}
+#endif
+
+
+
+const struct xrchart_colour data_colour[XRCHART_N_COLOURS] =
+  {
+    { 165, 42, 42 },            /* brown */
+    { 255, 0, 0 },              /* red */
+    { 255, 165, 0 },            /* orange */
+    { 255, 255, 0 },            /* yellow */
+    { 0, 255, 0 },              /* green */
+    { 0, 0, 255 },              /* blue */
+    { 238, 130, 238 },          /* violet */
+    { 190, 190, 190 },          /* grey */
+    { 255, 192, 203 },          /* pink */
+  };
+
+void
+xrchart_draw_marker (cairo_t *cr, double x, double y,
+                     enum xrmarker_type marker, double size)
+{
+  cairo_save (cr);
+  cairo_translate (cr, x, y);
+  cairo_scale (cr, size / 2.0, size / 2.0);
+  cairo_set_line_width (cr, cairo_get_line_width (cr) / (size / 2.0));
+  switch (marker)
+    {
+    case XRMARKER_CIRCLE:
+      cairo_arc (cr, 0, 0, 1.0, 0, 2 * M_PI);
+      cairo_stroke (cr);
+      break;
+
+    case XRMARKER_ASTERISK:
+      cairo_move_to (cr, 0, -1.0); /* | */
+      cairo_line_to (cr, 0, 1.0);
+      cairo_move_to (cr, -M_SQRT1_2, -M_SQRT1_2); /* / */
+      cairo_line_to (cr, M_SQRT1_2, M_SQRT1_2);
+      cairo_move_to (cr, -M_SQRT1_2, M_SQRT1_2); /* \ */
+      cairo_line_to (cr, M_SQRT1_2, -M_SQRT1_2);
+      cairo_stroke (cr);
+      break;
+
+    case XRMARKER_SQUARE:
+      cairo_rectangle (cr, -1.0, -1.0, 2.0, 2.0);
+      cairo_stroke (cr);
+      break;
+    }
+  cairo_restore (cr);
+}
+
+void
+xrchart_label (cairo_t *cr, int horz_justify, int vert_justify,
+               double font_size, const char *string)
+{
+  PangoFontDescription *desc;
+  PangoLayout *layout;
+  double x, y;
+
+  desc = pango_font_description_from_string ("sans serif");
+  if (desc == NULL)
+    {
+      cairo_new_path (cr);
+      return;
+    }
+  pango_font_description_set_absolute_size (desc, font_size * PANGO_SCALE);
+
+  cairo_save (cr);
+  cairo_get_current_point (cr, &x, &y);
+  cairo_translate (cr, x, y);
+  cairo_move_to (cr, 0, 0);
+  cairo_scale (cr, 1.0, -1.0);
+
+  layout = pango_cairo_create_layout (cr);
+  pango_layout_set_font_description (layout, desc);
+  pango_layout_set_text (layout, string, -1);
+  if (horz_justify != 'l')
+    {
+      int width_pango;
+      double width;
+
+      pango_layout_get_size (layout, &width_pango, NULL);
+      width = (double) width_pango / PANGO_SCALE;
+      if (horz_justify == 'r')
+        cairo_rel_move_to (cr, -width, 0);
+      else
+        cairo_rel_move_to (cr, -width / 2.0, 0);
+    }
+  if (vert_justify == 'x')
+    {
+      int baseline_pango = pango_layout_get_baseline (layout);
+      double baseline = (double) baseline_pango / PANGO_SCALE;
+      cairo_rel_move_to (cr, 0, -baseline);
+    }
+  else if (vert_justify != 't')
+    {
+      int height_pango;
+      double height;
+
+      pango_layout_get_size (layout, NULL, &height_pango);
+      height = (double) height_pango / PANGO_SCALE;
+      if (vert_justify == 'b')
+        cairo_rel_move_to (cr, 0, -height);
+      else if (vert_justify == 'c')
+        cairo_rel_move_to (cr, 0, -height / 2.0);
+    }
+  pango_cairo_show_layout (cr, layout);
+  g_object_unref (layout);
+
+  cairo_restore (cr);
+
+  cairo_new_path (cr);
+
+  pango_font_description_free (desc);
+}
+
+/* Draw a tick mark at position
+   If label is non zero, then print it at the tick mark
+*/
+void
+draw_tick (cairo_t *cr, const struct xrchart_geometry *geom,
+           enum tick_orientation orientation,
+           double position,
+           const char *label, ...)
+{
+  const int tickSize = 10;
+  double x, y;
+
+  cairo_move_to (cr, geom->data_left, geom->data_bottom);
+
+  if (orientation == TICK_ABSCISSA)
+    {
+      cairo_rel_move_to (cr, position, 0);
+      cairo_rel_line_to (cr, 0, -tickSize);
+    }
+  else if (orientation == TICK_ORDINATE)
+    {
+      cairo_rel_move_to (cr, 0, position);
+      cairo_rel_line_to (cr, -tickSize, 0);
+    }
+  else
+    NOT_REACHED ();
+  cairo_get_current_point (cr, &x, &y);
+
+  cairo_stroke (cr);
+
+  if (label != NULL)
+    {
+      va_list ap;
+      char *s;
+
+      cairo_move_to (cr, x, y);
+
+      va_start (ap, label);
+      s = xvasprintf (label, ap);
+      if (orientation == TICK_ABSCISSA)
+        xrchart_label (cr, 'c', 't', geom->font_size, s);
+      else if (orientation == TICK_ORDINATE)
+        {
+          if (fabs (position) < DBL_EPSILON)
+           cairo_rel_move_to (cr, 0, 10);
+          xrchart_label (cr, 'r', 'c', geom->font_size, s);
+        }
+      free (s);
+      va_end (ap);
+    }
+}
+
+
+/* Write the title on a chart*/
+void
+xrchart_write_title (cairo_t *cr, const struct xrchart_geometry *geom,
+                   const char *title, ...)
+{
+  va_list ap;
+  char *s;
+
+  cairo_save (cr);
+  cairo_move_to (cr, geom->data_left, geom->title_bottom);
+
+  va_start(ap, title);
+  s = xvasprintf (title, ap);
+  xrchart_label (cr, 'l', 'x', geom->font_size * 1.5, s);
+  free (s);
+  va_end (ap);
+
+  cairo_restore (cr);
+}
+
+
+/* Set the scale for the abscissa */
+void
+xrchart_write_xscale (cairo_t *cr, struct xrchart_geometry *geom,
+                    double min, double max, int ticks)
+{
+  double x;
+
+  const double tick_interval =
+    chart_rounded_tick ((max - min) / (double) ticks);
+
+  geom->x_max = ceil (max / tick_interval) * tick_interval;
+  geom->x_min = floor (min / tick_interval) * tick_interval;
+  geom->abscissa_scale = fabs(geom->data_right - geom->data_left) /
+    fabs(geom->x_max - geom->x_min);
+
+  for (x = geom->x_min; x <= geom->x_max; x += tick_interval)
+    draw_tick (cr, geom, TICK_ABSCISSA,
+               (x - geom->x_min) * geom->abscissa_scale, "%g", x);
+}
+
+
+/* Set the scale for the ordinate */
+void
+xrchart_write_yscale (cairo_t *cr, struct xrchart_geometry *geom,
+                    double smin, double smax, int ticks)
+{
+  double y;
+
+  const double tick_interval =
+    chart_rounded_tick ((smax - smin) / (double) ticks);
+
+  geom->y_max = ceil (smax / tick_interval) * tick_interval;
+  geom->y_min = floor (smin / tick_interval) * tick_interval;
+
+  geom->ordinate_scale =
+    (fabs (geom->data_top - geom->data_bottom)
+     / fabs (geom->y_max - geom->y_min));
+
+  for (y = geom->y_min; y <= geom->y_max; y += tick_interval)
+    draw_tick (cr, geom, TICK_ORDINATE,
+              (y - geom->y_min) * geom->ordinate_scale, "%g", y);
+}
+
+/* Write the abscissa label */
+void
+xrchart_write_xlabel (cairo_t *cr, const struct xrchart_geometry *geom,
+                    const char *label)
+{
+  cairo_move_to (cr, geom->data_left, geom->abscissa_top);
+  xrchart_label (cr, 'l', 't', geom->font_size, label);
+}
+
+/* Write the ordinate label */
+void
+xrchart_write_ylabel (cairo_t *cr, const struct xrchart_geometry *geom,
+                    const char *label)
+{
+  cairo_save (cr);
+  cairo_translate (cr, -geom->data_bottom, -geom->ordinate_right);
+  cairo_move_to (cr, 0, 0);
+  cairo_rotate (cr, M_PI / 2.0);
+  xrchart_label (cr, 'l', 'x', geom->font_size, label);
+  cairo_restore (cr);
+}
+
+
+void
+xrchart_write_legend (cairo_t *cr, const struct xrchart_geometry *geom)
+{
+  int i;
+  const int vstep = geom->font_size * 2;
+  const int xpad = 10;
+  const int ypad = 10;
+  const int swatch = 20;
+  const int legend_top = geom->data_top;
+  const int legend_bottom = legend_top -
+    (vstep * geom->n_datasets + 2 * ypad );
+
+  cairo_save (cr);
+
+  cairo_rectangle (cr, geom->legend_left, legend_top,
+                   geom->legend_right - xpad - geom->legend_left,
+                   legend_bottom - legend_top);
+  cairo_stroke (cr);
+
+  for (i = 0 ; i < geom->n_datasets ; ++i )
+    {
+      const int ypos = legend_top - vstep * (i + 1);
+      const int xpos = geom->legend_left + xpad;
+      const struct xrchart_colour *colour;
+
+      cairo_move_to (cr, xpos, ypos);
+
+      cairo_save (cr);
+      colour = &data_colour [ i % XRCHART_N_COLOURS];
+      cairo_set_source_rgb (cr,
+                            colour->red / 255.0,
+                            colour->green / 255.0,
+                            colour->blue / 255.0);
+      cairo_rectangle (cr, xpos, ypos, swatch, swatch);
+      cairo_fill_preserve (cr);
+      cairo_stroke (cr);
+      cairo_restore (cr);
+
+      cairo_move_to (cr, xpos + swatch * 1.5, ypos);
+      xrchart_label (cr, 'l', 'x', geom->font_size, geom->dataset[i]);
+    }
+
+  cairo_restore (cr);
+}
+
+/* Start a new vector called NAME */
+void
+xrchart_vector_start (cairo_t *cr, struct xrchart_geometry *geom, const char *name)
+{
+  const struct xrchart_colour *colour;
+
+  cairo_save (cr);
+
+  colour = &data_colour[geom->n_datasets % XRCHART_N_COLOURS];
+  cairo_set_source_rgb (cr,
+                        colour->red / 255.0,
+                        colour->green / 255.0,
+                        colour->blue / 255.0);
+
+  geom->n_datasets++;
+  geom->dataset = xrealloc (geom->dataset,
+                            geom->n_datasets * sizeof (*geom->dataset));
+
+  geom->dataset[geom->n_datasets - 1] = strdup (name);
+}
+
+/* Plot a data point */
+void
+xrchart_datum (cairo_t *cr, const struct xrchart_geometry *geom,
+             int dataset UNUSED, double x, double y)
+{
+  double x_pos = (x - geom->x_min) * geom->abscissa_scale + geom->data_left;
+  double y_pos = (y - geom->y_min) * geom->ordinate_scale + geom->data_bottom;
+
+  xrchart_draw_marker (cr, x_pos, y_pos, XRMARKER_SQUARE, 15);
+}
+
+void
+xrchart_vector_end (cairo_t *cr, struct xrchart_geometry *geom)
+{
+  cairo_stroke (cr);
+  cairo_restore (cr);
+  geom->in_path = false;
+}
+
+/* Plot a data point */
+void
+xrchart_vector (cairo_t *cr, struct xrchart_geometry *geom, double x, double y)
+{
+  const double x_pos =
+    (x - geom->x_min) * geom->abscissa_scale + geom->data_left ;
+
+  const double y_pos =
+    (y - geom->y_min) * geom->ordinate_scale + geom->data_bottom ;
+
+  if (geom->in_path)
+    cairo_line_to (cr, x_pos, y_pos);
+  else
+    {
+      cairo_move_to (cr, x_pos, y_pos);
+      geom->in_path = true;
+    }
+}
+
+
+
+/* Draw a line with slope SLOPE and intercept INTERCEPT.
+   between the points limit1 and limit2.
+   If lim_dim is XRCHART_DIM_Y then the limit{1,2} are on the
+   y axis otherwise the x axis
+*/
+void
+xrchart_line(cairo_t *cr, const struct xrchart_geometry *geom,
+           double slope, double intercept,
+          double limit1, double limit2, enum xrchart_dim lim_dim)
+{
+  double x1, y1;
+  double x2, y2;
+
+  if ( lim_dim == XRCHART_DIM_Y )
+    {
+      x1 = ( limit1 - intercept ) / slope;
+      x2 = ( limit2 - intercept ) / slope;
+      y1 = limit1;
+      y2 = limit2;
+    }
+  else
+    {
+      x1 = limit1;
+      x2 = limit2;
+      y1 = slope * x1 + intercept;
+      y2 = slope * x2 + intercept;
+    }
+
+  y1 = (y1 - geom->y_min) * geom->ordinate_scale + geom->data_bottom;
+  y2 = (y2 - geom->y_min) * geom->ordinate_scale + geom->data_bottom;
+  x1 = (x1 - geom->x_min) * geom->abscissa_scale + geom->data_left;
+  x2 = (x2 - geom->x_min) * geom->abscissa_scale + geom->data_left;
+
+  cairo_move_to (cr, x1, y1);
+  cairo_line_to (cr, x2, y2);
+  cairo_stroke (cr);
+}
diff --git a/src/output/cairo-chart.h b/src/output/cairo-chart.h
new file mode 100644 (file)
index 0000000..041a169
--- /dev/null
@@ -0,0 +1,165 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2009 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 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.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef OUTPUT_CAIRO_CHART_H
+#define OUTPUT_CAIRO_CHART_H 1
+
+#include <cairo/cairo.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include "libpspp/compiler.h"
+
+struct chart_item;
+
+struct xrchart_colour
+  {
+    uint8_t red;
+    uint8_t green;
+    uint8_t blue;
+  };
+
+/* The geometry of a chart. */
+struct xrchart_geometry
+  {
+    int data_top;
+    int data_right;
+    int data_bottom;
+    int data_left;
+
+    int abscissa_top;
+
+    int ordinate_right;
+
+    int title_bottom;
+
+    /* Legend. */
+    int legend_left;
+    int legend_right;
+    char **dataset;
+    int n_datasets;
+
+    /* Default font size for the plot. */
+    double font_size;
+
+    struct xrchart_colour fill_colour;
+
+    /* Stuff particular to cartesians and boxplots. */
+    double ordinate_scale;
+    double abscissa_scale;
+    double x_min;
+    double x_max;
+    double y_min;
+    double y_max;
+    bool in_path;
+  };
+
+void xrchart_geometry_init (cairo_t *, struct xrchart_geometry *,
+                            double width, double length);
+void xrchart_geometry_free (cairo_t *, struct xrchart_geometry *);
+
+#define XRCHART_N_COLOURS 9
+extern const struct xrchart_colour data_colour[];
+
+enum tick_orientation
+  {
+    TICK_ABSCISSA=0,
+    TICK_ORDINATE
+  };
+
+enum xrmarker_type
+  {
+    XRMARKER_CIRCLE,              /* Hollow circle. */
+    XRMARKER_ASTERISK,            /* Asterisk (*). */
+    XRMARKER_SQUARE               /* Hollow square. */
+  };
+
+void xrchart_draw_marker (cairo_t *, double x, double y, enum xrmarker_type,
+                          double size);
+
+void xrchart_label (cairo_t *, int horz_justify, int vert_justify,
+                    double font_size, const char *);
+
+/* Draw a tick mark at position
+   If label is non zero, then print it at the tick mark
+*/
+void draw_tick (cairo_t *, const struct xrchart_geometry *,
+                enum tick_orientation orientation, double position,
+                const char *label, ...)
+  PRINTF_FORMAT (5, 6);
+
+
+/* Write the title on a chart*/
+void xrchart_write_title (cairo_t *, const struct xrchart_geometry *,
+                          const char *title, ...)
+  PRINTF_FORMAT (3, 4);
+
+/* Set the scale for the abscissa */
+void xrchart_write_xscale (cairo_t *, struct xrchart_geometry *,
+                           double min, double max, int ticks);
+
+
+/* Set the scale for the ordinate */
+void xrchart_write_yscale (cairo_t *, struct xrchart_geometry *,
+                           double smin, double smax, int ticks);
+
+void xrchart_write_xlabel (cairo_t *, const struct xrchart_geometry *,
+                           const char *label) ;
+
+/* Write the ordinate label */
+void xrchart_write_ylabel (cairo_t *, const struct xrchart_geometry *,
+                           const char *label);
+
+void xrchart_write_legend (cairo_t *, const struct xrchart_geometry *);
+
+enum xrchart_dim
+  {
+    XRCHART_DIM_X,
+    XRCHART_DIM_Y
+  };
+
+void xrchart_vector_start (cairo_t *, struct xrchart_geometry *,
+                           const char *name);
+void xrchart_vector_end (cairo_t *, struct xrchart_geometry *);
+void xrchart_vector (cairo_t *, struct xrchart_geometry *, double x, double y);
+
+/* Plot a data point */
+void xrchart_datum (cairo_t *, const struct xrchart_geometry *,
+                    int dataset UNUSED, double x, double y);
+
+/* Draw a line with slope SLOPE and intercept INTERCEPT.
+   between the points limit1 and limit2.
+   If lim_dim is XRCHART_DIM_Y then the limit{1,2} are on the
+   y axis otherwise the x axis
+*/
+void xrchart_line (cairo_t *, const struct xrchart_geometry *,
+                   double slope, double intercept,
+                   double limit1, double limit2, enum xrchart_dim lim_dim);
+
+/* Drawing various kinds of charts. */
+void xrchart_draw_boxplot (const struct chart_item *, cairo_t *,
+                           struct xrchart_geometry *);
+void xrchart_draw_roc (const struct chart_item *, cairo_t *,
+                       struct xrchart_geometry *);
+void xrchart_draw_piechart (const struct chart_item *, cairo_t *,
+                            struct xrchart_geometry *);
+void xrchart_draw_histogram (const struct chart_item *, cairo_t *,
+                             struct xrchart_geometry *);
+void xrchart_draw_np_plot (const struct chart_item *, cairo_t *,
+                           struct xrchart_geometry *);
+void xrchart_draw_scree (const struct chart_item *, cairo_t *,
+                         struct xrchart_geometry *);
+
+#endif /* output/cairo-chart.h */
index 162009c421407d6c706c839789edcfed2503a34c..612878035eb98baf98e20020c1c33919fad19355 100644 (file)
 #include <output/cairo.h>
 
 #include <libpspp/assertion.h>
+#include <libpspp/cast.h>
 #include <libpspp/start-date.h>
+#include <libpspp/str.h>
+#include <libpspp/string-map.h>
 #include <libpspp/version.h>
-#include <output/chart-provider.h>
-#include <output/manager.h>
-#include <output/output.h>
+#include <output/cairo-chart.h>
+#include <output/chart-item-provider.h>
+#include <output/charts/boxplot.h>
+#include <output/charts/np-plot.h>
+#include <output/charts/piechart.h>
+#include <output/charts/plot-hist.h>
+#include <output/charts/roc-chart.h>
+#include <output/charts/scree.h>
+#include <output/driver-provider.h>
+#include <output/options.h>
+#include <output/render.h>
+#include <output/tab.h>
+#include <output/table-item.h>
+#include <output/table.h>
+#include <output/text-item.h>
 
 #include <cairo/cairo-pdf.h>
 #include <cairo/cairo-ps.h>
 #include "gettext.h"
 #define _(msgid) gettext (msgid)
 
-/* Cairo driver options: (defaults listed first)
-
-   output-file="pspp.pdf"
-   output-type=pdf|ps|png|svg
-   paper-size=letter (see "papersize" file)
-   orientation=portrait|landscape
-   headers=on|off
-
-   left-margin=0.5in
-   right-margin=0.5in
-   top-margin=0.5in
-   bottom-margin=0.5in
-
-   prop-font=serif
-   emph-font=serif italic
-   fixed-font=monospace
-   font-size=10000
-
-   line-gutter=1pt
-   line-spacing=1pt
-   line-width=0.5pt
- */
+/* This file uses TABLE_HORZ and TABLE_VERT enough to warrant abbreviating. */
+#define H TABLE_HORZ
+#define V TABLE_VERT
 
 /* Measurements as we present to the rest of PSPP. */
 #define XR_POINT PANGO_SCALE
@@ -77,12 +73,6 @@ xr_to_pt (int x)
   return x / (double) XR_POINT;
 }
 
-static int
-pt_to_xr (double x)
-{
-  return x * XR_POINT + 0.5;
-}
-
 /* Output types. */
 enum xr_output_type
   {
@@ -91,6 +81,15 @@ enum xr_output_type
     XR_SVG
   };
 
+/* Cairo fonts. */
+enum xr_font_type
+  {
+    XR_FONT_PROPORTIONAL,
+    XR_FONT_EMPHASIS,
+    XR_FONT_FIXED,
+    XR_N_FONTS
+  };
+
 /* A font for use with Cairo. */
 struct xr_font
   {
@@ -100,173 +99,185 @@ struct xr_font
     PangoFontMetrics *metrics;
   };
 
-/* Cairo output driver extension record. */
-struct xr_driver_ext
+/* Cairo output driver. */
+struct xr_driver
   {
-    cairo_t *cairo;
-    struct xr_font fonts[OUTP_FONT_CNT];
+    struct output_driver driver;
 
-    bool draw_headers;          /* Draw headers at top of page? */
-    int page_number;           /* Current page number. */
+    /* User parameters. */
+    bool headers;               /* Draw headers at top of page? */
+
+    struct xr_font fonts[XR_N_FONTS];
+    int font_height;            /* In XR units. */
+
+    int width;                  /* Page width minus margins. */
+    int length;                 /* Page length minus margins and header. */
+
+    int left_margin;            /* Left margin in XR units. */
+    int right_margin;           /* Right margin in XR units. */
+    int top_margin;             /* Top margin in XR units. */
+    int bottom_margin;          /* Bottom margin in XR units. */
 
     int line_gutter;           /* Space around lines. */
     int line_space;            /* Space between lines. */
     int line_width;            /* Width of lines. */
-  };
 
-struct xr_driver_options
-  {
-    struct outp_driver *driver;
-
-    char *file_name;            /* Output file name. */
     enum xr_output_type file_type; /* Type of output file. */
 
-
-    bool portrait;              /* Portrait mode? */
-
-    int paper_width;            /* Width of paper before dropping margins. */
-    int paper_length;           /* Length of paper before dropping margins. */
-    int left_margin;           /* Left margin in XR units. */
-    int right_margin;          /* Right margin in XR units. */
-    int top_margin;            /* Top margin in XR units. */
-    int bottom_margin;         /* Bottom margin in XR units. */
+    /* Internal state. */
+    struct render_params *params;
+    char *title;
+    char *subtitle;
+    cairo_t *cairo;
+    int page_number;           /* Current page number. */
+    int y;
   };
 
-static bool handle_option (void *options, const char *key,
-                           const struct string *val);
-static void draw_headers (struct outp_driver *this);
+static void xr_show_page (struct xr_driver *);
+static void draw_headers (struct xr_driver *);
 
-static bool load_font (struct outp_driver *this, struct xr_font *);
+static bool load_font (struct xr_driver *, struct xr_font *);
 static void free_font (struct xr_font *);
-static int text_width (struct outp_driver *, const char *, enum outp_font);
+
+static void xr_draw_line (void *, int bb[TABLE_N_AXES][2],
+                          enum render_line_style styles[TABLE_N_AXES][2]);
+static void xr_measure_cell_width (void *, const struct table_cell *,
+                                   int *min, int *max);
+static int xr_measure_cell_height (void *, const struct table_cell *,
+                                   int width);
+static void xr_draw_cell (void *, const struct table_cell *,
+                          int bb[TABLE_N_AXES][2],
+                          int clip[TABLE_N_AXES][2]);
 \f
 /* Driver initialization. */
 
-static struct outp_driver *
-xr_allocate (const char *name, int types)
+static struct xr_driver *
+xr_driver_cast (struct output_driver *driver)
 {
-  struct outp_driver *this;
-  struct xr_driver_ext *x;
-  size_t i;
+  assert (driver->class == &cairo_class);
+  return UP_CAST (driver, struct xr_driver, driver);
+}
 
-  this = outp_allocate_driver (&cairo_class, name, types);
-  this->width = this->length = 0;
-  this->font_height = XR_POINT * 10;
-  this->ext = x = xzalloc (sizeof *x);
-  x->cairo = NULL;
-  x->fonts[OUTP_FIXED].string = xstrdup ("monospace");
-  x->fonts[OUTP_PROPORTIONAL].string = xstrdup ("serif");
-  x->fonts[OUTP_EMPHASIS].string = xstrdup ("serif italic");
-  for (i = 0; i < OUTP_FONT_CNT; i++)
-    {
-      struct xr_font *font = &x->fonts[i];
-      font->desc = NULL;
-      font->metrics = NULL;
-      font->layout = NULL;
-    }
-  x->draw_headers = true;
-  x->page_number = 0;
-  x->line_gutter = XR_POINT;
-  x->line_space = XR_POINT;
-  x->line_width = XR_POINT / 2;
+static struct driver_option *
+opt (struct output_driver *d, struct string_map *options, const char *key,
+     const char *default_value)
+{
+  return driver_option_get (d, options, key, default_value);
+}
 
-  return this;
+static struct xr_driver *
+xr_allocate (const char *name, int device_type, struct string_map *o)
+{
+  struct output_driver *d;
+  struct xr_driver *xr;
+
+  xr = xzalloc (sizeof *xr);
+  d = &xr->driver;
+  output_driver_init (d, &cairo_class, name, device_type);
+  xr->headers = true;
+  xr->font_height = XR_POINT * 10;
+  xr->fonts[XR_FONT_FIXED].string
+    = parse_string (opt (d, o, "fixed-font", "monospace"));
+  xr->fonts[XR_FONT_PROPORTIONAL].string
+    = parse_string (opt (d, o, "prop-font", "serif"));
+  xr->fonts[XR_FONT_EMPHASIS].string
+    = parse_string (opt (d, o, "emph-font", "serif italic"));
+  xr->line_gutter = XR_POINT;
+  xr->line_space = XR_POINT;
+  xr->line_width = XR_POINT / 2;
+  xr->page_number = 1;
+
+  return xr;
 }
 
 static bool
-xr_set_cairo (struct outp_driver *this, cairo_t *cairo)
+xr_set_cairo (struct xr_driver *xr, cairo_t *cairo)
 {
-  struct xr_driver_ext *x = this->ext;
   int i;
 
-  x->cairo = cairo;
+  xr->cairo = cairo;
 
-  cairo_set_line_width (x->cairo, xr_to_pt (x->line_width));
+  cairo_set_line_width (xr->cairo, xr_to_pt (xr->line_width));
 
-  for (i = 0; i < OUTP_FONT_CNT; i++)
-    if (!load_font (this, &x->fonts[i]))
+  for (i = 0; i < XR_N_FONTS; i++)
+    if (!load_font (xr, &xr->fonts[i]))
       return false;
 
-  this->fixed_width = text_width (this, "0", OUTP_FIXED);
-  this->prop_em_width = text_width (this, "0", OUTP_PROPORTIONAL);
-
-  this->horiz_line_width[OUTP_L_NONE] = 0;
-  this->horiz_line_width[OUTP_L_SINGLE] = 2 * x->line_gutter + x->line_width;
-  this->horiz_line_width[OUTP_L_DOUBLE] = (2 * x->line_gutter + x->line_space
-                                           + 2 * x->line_width);
-  memcpy (this->vert_line_width, this->horiz_line_width,
-          sizeof this->vert_line_width);
-
-  return true;
-}
-
-struct outp_driver *
-xr_create_driver (cairo_t *cairo)
-{
-  struct outp_driver *this;
-
-  this = xr_allocate ("cairo", 0);
-  this->width = INT_MAX / 8;
-  this->length = INT_MAX / 8;
-  if (!xr_set_cairo (this, cairo))
+  if (xr->params == NULL)
     {
-      this->class->close_driver (this);
-      outp_free_driver (this);
-      return NULL;
+      int single_width, double_width;
+
+      xr->params = xmalloc (sizeof *xr->params);
+      xr->params->draw_line = xr_draw_line;
+      xr->params->measure_cell_width = xr_measure_cell_width;
+      xr->params->measure_cell_height = xr_measure_cell_height;
+      xr->params->draw_cell = xr_draw_cell;
+      xr->params->aux = xr;
+      xr->params->size[H] = xr->width;
+      xr->params->size[V] = xr->length;
+      xr->params->font_size[H] = xr->font_height / 2; /* XXX */
+      xr->params->font_size[V] = xr->font_height;
+
+      single_width = 2 * xr->line_gutter + xr->line_width;
+      double_width = 2 * xr->line_gutter + xr->line_space + 2 * xr->line_width;
+      for (i = 0; i < TABLE_N_AXES; i++)
+        {
+          xr->params->line_widths[i][RENDER_LINE_NONE] = 0;
+          xr->params->line_widths[i][RENDER_LINE_SINGLE] = single_width;
+          xr->params->line_widths[i][RENDER_LINE_DOUBLE] = double_width;
+        }
     }
-  return this;
+
+  return true;
 }
 
-static bool
-xr_open_driver (const char *name, int types, struct substring option_string)
+static struct output_driver *
+xr_create (const char *name, enum output_device_type device_type,
+           struct string_map *o)
 {
-  struct outp_driver *this;
-  struct xr_driver_ext *x;
-  struct xr_driver_options options;
+  enum { MIN_LENGTH = 3 };
+  struct output_driver *d;
+  struct xr_driver *xr;
   cairo_surface_t *surface;
   cairo_status_t status;
   double width_pt, length_pt;
-
-  this = xr_allocate (name, types);
-  x = this->ext;
-
-  options.driver = this;
-  options.file_name = xstrdup ("pspp.pdf");
-  options.file_type = XR_PDF;
-  options.portrait = true;
-  outp_get_paper_size ("", &options.paper_width, &options.paper_length);
-  options.left_margin = XR_INCH / 2;
-  options.right_margin = XR_INCH / 2;
-  options.top_margin = XR_INCH / 2;
-  options.bottom_margin = XR_INCH / 2;
-
-  outp_parse_options (this->name, option_string, handle_option, &options);
-
-  width_pt = options.paper_width / 1000.0;
-  length_pt = options.paper_length / 1000.0;
-  if (options.portrait)
-    {
-      this->width = pt_to_xr (width_pt);
-      this->length = pt_to_xr (length_pt);
-    }
-  else
-    {
-      this->width = pt_to_xr (width_pt);
-      this->length = pt_to_xr (length_pt);
-    }
-  if (x->draw_headers)
-    options.top_margin += 3 * this->font_height;
-  this->width -= options.left_margin + options.right_margin;
-  this->length -= options.top_margin + options.bottom_margin;
-
-  if (options.file_type == XR_PDF)
-    surface = cairo_pdf_surface_create (options.file_name,
-                                        width_pt, length_pt);
-  else if (options.file_type == XR_PS)
-    surface = cairo_ps_surface_create (options.file_name, width_pt, length_pt);
-  else if (options.file_type == XR_SVG)
-    surface = cairo_svg_surface_create (options.file_name,
-                                        width_pt, length_pt);
+  int paper_width, paper_length;
+  char *file_name;
+
+  xr = xr_allocate (name, device_type, o);
+  d = &xr->driver;
+
+  xr->headers = parse_boolean (opt (d, o, "headers", "true"));
+
+  xr->file_type = parse_enum (opt (d, o, "output-type", "pdf"),
+                              "pdf", XR_PDF,
+                              "ps", XR_PS,
+                              "svg", XR_SVG,
+                              (char *) NULL);
+  file_name = parse_string (opt (d, o, "output-file",
+                                 (xr->file_type == XR_PDF ? "pspp.pdf"
+                                  : xr->file_type == XR_PS ? "pspp.ps"
+                                  : "pspp.svg")));
+
+  parse_paper_size (opt (d, o, "paper-size", ""), &paper_width, &paper_length);
+  xr->left_margin = parse_dimension (opt (d, o, "left-margin", ".5in"));
+  xr->right_margin = parse_dimension (opt (d, o, "right-margin", ".5in"));
+  xr->top_margin = parse_dimension (opt (d, o, "top-margin", ".5in"));
+  xr->bottom_margin = parse_dimension (opt (d, o, "bottom-margin", ".5in"));
+
+  if (xr->headers)
+    xr->top_margin += 3 * xr->font_height;
+  xr->width = paper_width - xr->left_margin - xr->right_margin;
+  xr->length = paper_length - xr->top_margin - xr->bottom_margin;
+
+  width_pt = paper_width / 1000.0;
+  length_pt = paper_length / 1000.0;
+  if (xr->file_type == XR_PDF)
+    surface = cairo_pdf_surface_create (file_name, width_pt, length_pt);
+  else if (xr->file_type == XR_PS)
+    surface = cairo_ps_surface_create (file_name, width_pt, length_pt);
+  else if (xr->file_type == XR_SVG)
+    surface = cairo_svg_surface_create (file_name, width_pt, length_pt);
   else
     NOT_REACHED ();
 
@@ -274,264 +285,244 @@ xr_open_driver (const char *name, int types, struct substring option_string)
   if (status != CAIRO_STATUS_SUCCESS)
     {
       error (0, 0, _("opening output file \"%s\": %s"),
-             options.file_name, cairo_status_to_string (status));
+             file_name, cairo_status_to_string (status));
       cairo_surface_destroy (surface);
       goto error;
     }
 
-  x->cairo = cairo_create (surface);
+  xr->cairo = cairo_create (surface);
   cairo_surface_destroy (surface);
 
-  cairo_translate (x->cairo,
-                   xr_to_pt (options.left_margin),
-                   xr_to_pt (options.top_margin));
+  cairo_translate (xr->cairo,
+                   xr_to_pt (xr->left_margin),
+                   xr_to_pt (xr->top_margin));
 
-  if (this->length / this->font_height < 15)
+  if (!xr_set_cairo (xr, xr->cairo))
+    goto error;
+
+  if (xr->length / xr->font_height < MIN_LENGTH)
     {
       error (0, 0, _("The defined page is not long "
-                     "enough to hold margins and headers, plus least 15 "
+                     "enough to hold margins and headers, plus least %d "
                      "lines of the default fonts.  In fact, there's only "
                      "room for %d lines."),
-             this->length / this->font_height);
+             MIN_LENGTH,
+             xr->length / xr->font_height);
       goto error;
     }
 
-  if (!xr_set_cairo (this, x->cairo))
-    goto error;
-
-  outp_register_driver (this);
-  free (options.file_name);
-  return true;
+  free (file_name);
+  return &xr->driver;
 
  error:
-  this->class->close_driver (this);
-  outp_free_driver (this);
-  free (options.file_name);
-  return false;
+  output_driver_destroy (&xr->driver);
+  return NULL;
 }
 
-static bool
-xr_close_driver (struct outp_driver *this)
+static void
+xr_destroy (struct output_driver *driver)
 {
-  struct xr_driver_ext *x = this->ext;
-  bool ok = true;
+  struct xr_driver *xr = xr_driver_cast (driver);
   size_t i;
 
-  if (x->cairo != NULL)
+  if (xr->cairo != NULL)
     {
       cairo_status_t status;
 
-      cairo_surface_finish (cairo_get_target (x->cairo));
-      status = cairo_status (x->cairo);
+      if (xr->y > 0)
+        xr_show_page (xr);
+
+      cairo_surface_finish (cairo_get_target (xr->cairo));
+      status = cairo_status (xr->cairo);
       if (status != CAIRO_STATUS_SUCCESS)
-        error (0, 0, _("error writing output file for %s driver: %s"),
-               this->name, cairo_status_to_string (status));
-      cairo_destroy (x->cairo);
+        error (0, 0, _("error drawing output for %s driver: %s"),
+               output_driver_get_name (driver),
+               cairo_status_to_string (status));
+      cairo_destroy (xr->cairo);
     }
 
-  for (i = 0; i < OUTP_FONT_CNT; i++)
-    free_font (&x->fonts[i]);
-  free (x);
-
-  return ok;
+  for (i = 0; i < XR_N_FONTS; i++)
+    free_font (&xr->fonts[i]);
+  free (xr->params);
+  free (xr);
 }
 
-/* Generic option types. */
-enum
-{
-  output_file_arg,
-  output_type_arg,
-  paper_size_arg,
-  orientation_arg,
-  line_style_arg,
-  boolean_arg,
-  dimension_arg,
-  string_arg,
-  nonneg_int_arg
-};
+static void
+xr_flush (struct output_driver *driver)
+{
+  struct xr_driver *xr = xr_driver_cast (driver);
 
-/* All the options that the Cairo driver supports. */
-static const struct outp_option option_tab[] =
-{
-  {"output-file",              output_file_arg,0},
-  {"output-type",               output_type_arg,0},
-  {"paper-size",               paper_size_arg, 0},
-  {"orientation",              orientation_arg,0},
-
-  {"headers",                  boolean_arg,    1},
-
-  {"prop-font",                string_arg,     OUTP_PROPORTIONAL},
-  {"emph-font",                string_arg,     OUTP_EMPHASIS},
-  {"fixed-font",               string_arg,     OUTP_FIXED},
-
-  {"left-margin",              dimension_arg,  0},
-  {"right-margin",             dimension_arg,  1},
-  {"top-margin",               dimension_arg,  2},
-  {"bottom-margin",            dimension_arg,  3},
-  {"font-size",                        dimension_arg,  4},
-  {"line-width",               dimension_arg,  5},
-  {"line-gutter",              dimension_arg,  6},
-  {"line-width",               dimension_arg,  7},
-  {NULL, 0, 0},
-};
+  cairo_surface_flush (cairo_get_target (xr->cairo));
+}
 
-static bool
-handle_option (void *options_, const char *key, const struct string *val)
+static void
+xr_init_caption_cell (const char *caption, struct table_cell *cell)
 {
-  struct xr_driver_options *options = options_;
-  struct outp_driver *this = options->driver;
-  struct xr_driver_ext *x = this->ext;
-  int subcat;
-  char *value = ds_cstr (val);
+  cell->contents = caption;
+  cell->options = TAB_LEFT;
+  cell->destructor = NULL;
+}
 
-  switch (outp_match_keyword (key, option_tab, &subcat))
+static struct render_page *
+xr_render_table_item (struct xr_driver *xr, const struct table_item *item,
+                      int *caption_heightp)
+{
+  const char *caption = table_item_get_caption (item);
+
+  if (caption != NULL)
     {
-    case -1:
-      error (0, 0,
-             _("unknown configuration parameter `%s' for %s device "
-               "driver"), key, this->class->name);
-      break;
-    case output_file_arg:
-      free (options->file_name);
-      options->file_name = xstrdup (value);
-      break;
-    case output_type_arg:
-      if (!strcmp (value, "pdf"))
-        options->file_type = XR_PDF;
-      else if (!strcmp (value, "ps"))
-        options->file_type = XR_PS;
-      else if (!strcmp (value, "svg"))
-        options->file_type = XR_SVG;
-      else
+      /* XXX doesn't do well with very large captions */
+      struct table_cell cell;
+      xr_init_caption_cell (caption, &cell);
+      *caption_heightp = xr_measure_cell_height (xr, &cell, xr->width);
+    }
+  else
+    *caption_heightp = 0;
+
+  return render_page_create (xr->params, table_item_get_table (item));
+}
+
+static void
+xr_submit (struct output_driver *driver, const struct output_item *output_item)
+{
+  struct xr_driver *xr = xr_driver_cast (driver);
+  if (is_table_item (output_item))
+    {
+      struct table_item *table_item = to_table_item (output_item);
+      struct render_break x_break;
+      struct render_page *page;
+      int caption_height;
+
+      if (xr->y > 0)
+        xr->y += xr->font_height;
+
+      page = xr_render_table_item (xr, table_item, &caption_height);
+      xr->params->size[V] = xr->length - caption_height;
+      for (render_break_init (&x_break, page, H);
+           render_break_has_next (&x_break); )
         {
-          error (0, 0, _("unknown Cairo output type \"%s\""), value);
-          return false;
+          struct render_page *x_slice;
+          struct render_break y_break;
+
+          x_slice = render_break_next (&x_break, xr->width);
+          for (render_break_init (&y_break, x_slice, V);
+               render_break_has_next (&y_break); )
+            {
+              int space = xr->length - xr->y;
+              struct render_page *y_slice;
+
+              /* XXX doesn't allow for caption or space between segments */
+              if (render_break_next_size (&y_break) > space)
+                {
+                  assert (xr->y > 0);
+                  xr_show_page (xr);
+                  continue;
+                }
+
+              y_slice = render_break_next (&y_break, space);
+              if (caption_height)
+                {
+                  struct table_cell cell;
+                  int bb[TABLE_N_AXES][2];
+
+                  xr_init_caption_cell (table_item_get_caption (table_item),
+                                        &cell);
+                  bb[H][0] = 0;
+                  bb[H][1] = xr->width;
+                  bb[V][0] = 0;
+                  bb[V][1] = caption_height;
+                  xr_draw_cell (xr, &cell, bb, bb);
+                  xr->y += caption_height;
+                  caption_height = 0;
+                }
+
+              render_page_draw (y_slice);
+              xr->y += render_page_get_size (y_slice, V);
+              render_page_unref (y_slice);
+            }
+          render_break_destroy (&y_break);
         }
-      break;
-    case paper_size_arg:
-      outp_get_paper_size (value,
-                           &options->paper_width, &options->paper_length);
-      break;
-    case orientation_arg:
-      if (!strcmp (value, "portrait"))
-       options->portrait = true;
-      else if (!strcmp (value, "landscape"))
-       options->portrait = false;
-      else
-       error (0, 0, _("unknown orientation `%s' (valid orientations are "
-                       "`portrait' and `landscape')"), value);
-      break;
-    case boolean_arg:
-      if (!strcmp (value, "on") || !strcmp (value, "true")
-          || !strcmp (value, "yes") || atoi (value))
-        x->draw_headers = true;
-      else if (!strcmp (value, "off") || !strcmp (value, "false")
-               || !strcmp (value, "no") || !strcmp (value, "0"))
-        x->draw_headers = false;
-      else
+      render_break_destroy (&x_break);
+    }
+  else if (is_chart_item (output_item))
+    {
+      if (xr->y > 0)
+        xr_show_page (xr);
+      xr_draw_chart (to_chart_item (output_item), xr->cairo, 0.0, 0.0,
+                     xr_to_pt (xr->width), xr_to_pt (xr->length));
+      xr_show_page (xr);
+    }
+  else if (is_text_item (output_item))
+    {
+      const struct text_item *text_item = to_text_item (output_item);
+      enum text_item_type type = text_item_get_type (text_item);
+      const char *text = text_item_get_text (text_item);
+
+      switch (type)
         {
-          error (0, 0, _("boolean value expected for %s"), key);
-          return false;
-        }
-      break;
-    case dimension_arg:
-      {
-       int dimension = outp_evaluate_dimension (value);
+        case TEXT_ITEM_TITLE:
+          free (xr->title);
+          xr->title = xstrdup (text);
+          break;
 
-       if (dimension <= 0)
+        case TEXT_ITEM_SUBTITLE:
+          free (xr->subtitle);
+          xr->subtitle = xstrdup (text);
           break;
-       switch (subcat)
-         {
-         case 0:
-           options->left_margin = dimension;
-           break;
-         case 1:
-           options->right_margin = dimension;
-           break;
-         case 2:
-           options->top_margin = dimension;
-           break;
-         case 3:
-           options->bottom_margin = dimension;
-           break;
-         case 4:
-           this->font_height = dimension;
-           break;
-         case 5:
-           x->line_width = dimension;
-           break;
-         case 6:
-           x->line_gutter = dimension;
-           break;
-         case 7:
-           x->line_width = dimension;
-           break;
-         default:
-           NOT_REACHED ();
-         }
-      }
-      break;
-    case string_arg:
-      free (x->fonts[subcat].string);
-      x->fonts[subcat].string = ds_xstrdup (val);
-      break;
-    default:
-      NOT_REACHED ();
-    }
 
-  return true;
-}
-\f
-/* Basic file operations. */
+        case TEXT_ITEM_COMMAND_CLOSE:
+          break;
 
-static void
-xr_open_page (struct outp_driver *this)
-{
-  struct xr_driver_ext *x = this->ext;
+        case TEXT_ITEM_BLANK_LINE:
+          if (xr->y > 0)
+            xr->y += xr->font_height;
+          break;
 
-  x->page_number++;
+        case TEXT_ITEM_EJECT_PAGE:
+          if (xr->y > 0)
+            xr_show_page (xr);
+          break;
 
-  if (x->draw_headers)
-    draw_headers (this);
-}
+        default:
+          {
+            struct table_item *item;
 
-static void
-xr_close_page (struct outp_driver *this)
-{
-  struct xr_driver_ext *x = this->ext;
-  cairo_show_page (x->cairo);
+            item = table_item_create (table_from_string (0, text), NULL);
+            xr_submit (&xr->driver, &item->output_item);
+            table_item_unref (item);
+          }
+          break;
+        }
+
+    }
 }
 
 static void
-xr_output_chart (struct outp_driver *this, const struct chart *chart)
+xr_show_page (struct xr_driver *xr)
 {
-  struct xr_driver_ext *x = this->ext;
-  struct chart_geometry geom;
-
-  outp_eject_page (this);
-  outp_open_page (this);
-
-  cairo_save (x->cairo);
-  cairo_translate (x->cairo, 0.0, xr_to_pt (this->length));
-  cairo_scale (x->cairo, 1.0, -1.0);
-  chart_geometry_init (x->cairo, &geom,
-                       xr_to_pt (this->width), xr_to_pt (this->length));
-  chart_draw (chart, x->cairo, &geom);
-  chart_geometry_free (x->cairo, &geom);
-  cairo_restore (x->cairo);
+  if (xr->headers)
+    {
+      xr->y = 0;
+      draw_headers (xr);
+    }
+  cairo_show_page (xr->cairo);
 
-  outp_close_page (this);
+  xr->page_number++;
+  xr->y = 0;
 }
 \f
-/* Draws a line from (x0,y0) to (x1,y1). */
 static void
-dump_line (struct outp_driver *this, int x0, int y0, int x1, int y1)
+xr_layout_cell (struct xr_driver *, const struct table_cell *,
+                int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
+                PangoWrapMode, int *width, int *height);
+
+static void
+dump_line (struct xr_driver *xr, int x0, int y0, int x1, int y1)
 {
-  struct xr_driver_ext *x = this->ext;
-  cairo_new_path (x->cairo);
-  cairo_move_to (x->cairo, xr_to_pt (x0), xr_to_pt (y0));
-  cairo_line_to (x->cairo, xr_to_pt (x1), xr_to_pt (y1));
-  cairo_stroke (x->cairo);
+  cairo_new_path (xr->cairo);
+  cairo_move_to (xr->cairo, xr_to_pt (x0), xr_to_pt (y0 + xr->y));
+  cairo_line_to (xr->cairo, xr_to_pt (x1), xr_to_pt (y1 + xr->y));
+  cairo_stroke (xr->cairo);
 }
 
 /* Draws a horizontal line X0...X2 at Y if LEFT says so,
@@ -539,19 +530,18 @@ dump_line (struct outp_driver *this, int x0, int y0, int x1, int y1)
    Draws a horizontal line X1...X3 at Y if RIGHT says so,
    shortening it to X2...X3 if SHORTEN is true. */
 static void
-horz_line (struct outp_driver *this,
-           int x0, int x1, int x2, int x3, int y,
-           enum outp_line_style left, enum outp_line_style right,
+horz_line (struct xr_driver *xr, int x0, int x1, int x2, int x3, int y,
+           enum render_line_style left, enum render_line_style right,
            bool shorten)
 {
-  if (left != OUTP_L_NONE && right != OUTP_L_NONE && !shorten)
-    dump_line (this, x0, y, x3, y);
+  if (left != RENDER_LINE_NONE && right != RENDER_LINE_NONE && !shorten)
+    dump_line (xr, x0, y, x3, y);
   else
     {
-      if (left != OUTP_L_NONE)
-        dump_line (this, x0, y, shorten ? x1 : x2, y);
-      if (right != OUTP_L_NONE)
-        dump_line (this, shorten ? x2 : x1, y, x3, y);
+      if (left != RENDER_LINE_NONE)
+        dump_line (xr, x0, y, shorten ? x1 : x2, y);
+      if (right != RENDER_LINE_NONE)
+        dump_line (xr, shorten ? x2 : x1, y, x3, y);
     }
 }
 
@@ -560,33 +550,34 @@ horz_line (struct outp_driver *this,
    Draws a vertical line Y1...Y3 at X if BOTTOM says so,
    shortening it to Y2...Y3 if SHORTEN is true. */
 static void
-vert_line (struct outp_driver *this,
-           int y0, int y1, int y2, int y3, int x,
-           enum outp_line_style top, enum outp_line_style bottom,
+vert_line (struct xr_driver *xr, int y0, int y1, int y2, int y3, int x,
+           enum render_line_style top, enum render_line_style bottom,
            bool shorten)
 {
-  if (top != OUTP_L_NONE && bottom != OUTP_L_NONE && !shorten)
-    dump_line (this, x, y0, x, y3);
+  if (top != RENDER_LINE_NONE && bottom != RENDER_LINE_NONE && !shorten)
+    dump_line (xr, x, y0, x, y3);
   else
     {
-      if (top != OUTP_L_NONE)
-        dump_line (this, x, y0, x, shorten ? y1 : y2);
-      if (bottom != OUTP_L_NONE)
-        dump_line (this, x, shorten ? y2 : y1, x, y3);
+      if (top != RENDER_LINE_NONE)
+        dump_line (xr, x, y0, x, shorten ? y1 : y2);
+      if (bottom != RENDER_LINE_NONE)
+        dump_line (xr, x, shorten ? y2 : y1, x, y3);
     }
 }
 
-/* Draws a generalized intersection of lines in the rectangle
-   (X0,Y0)-(X3,Y3).  The line coming from the top to the center
-   is of style TOP, from left to center of style LEFT, from
-   bottom to center of style BOTTOM, and from right to center of
-   style RIGHT. */
 static void
-xr_line (struct outp_driver *this,
-         int x0, int y0, int x3, int y3,
-         enum outp_line_style top, enum outp_line_style left,
-         enum outp_line_style bottom, enum outp_line_style right)
+xr_draw_line (void *xr_, int bb[TABLE_N_AXES][2],
+              enum render_line_style styles[TABLE_N_AXES][2])
 {
+  const int x0 = bb[H][0];
+  const int y0 = bb[V][0];
+  const int x3 = bb[H][1];
+  const int y3 = bb[V][1];
+  const int top = styles[H][0];
+  const int left = styles[V][0];
+  const int bottom = styles[H][1];
+  const int right = styles[V][1];
+
   /* The algorithm here is somewhat subtle, to allow it to handle
      all the kinds of intersections that we need.
 
@@ -616,16 +607,16 @@ xr_line (struct outp_driver *this,
                   |        #     #       |
                y3 |________#_____#_______|
   */
-  struct xr_driver_ext *ext = this->ext;
+  struct xr_driver *xr = xr_;
 
   /* Offset from center of each line in a pair of double lines. */
-  int double_line_ofs = (ext->line_space + ext->line_width) / 2;
+  int double_line_ofs = (xr->line_space + xr->line_width) / 2;
 
   /* Are the lines along each axis single or double?
      (It doesn't make sense to have different kinds of line on the
      same axis, so we don't try to gracefully handle that case.) */
-  bool double_vert = top == OUTP_L_DOUBLE || bottom == OUTP_L_DOUBLE;
-  bool double_horz = left == OUTP_L_DOUBLE || right == OUTP_L_DOUBLE;
+  bool double_vert = top == RENDER_LINE_DOUBLE || bottom == RENDER_LINE_DOUBLE;
+  bool double_horz = left == RENDER_LINE_DOUBLE || right == RENDER_LINE_DOUBLE;
 
   /* When horizontal lines are doubled,
      the left-side line along y1 normally runs from x0 to x2,
@@ -652,16 +643,16 @@ xr_line (struct outp_driver *this,
      single.  We actually choose to cut off the line anyhow, as
      shown in the first diagram above.
   */
-  bool shorten_y1_lines = top == OUTP_L_DOUBLE;
-  bool shorten_y2_lines = bottom == OUTP_L_DOUBLE;
+  bool shorten_y1_lines = top == RENDER_LINE_DOUBLE;
+  bool shorten_y2_lines = bottom == RENDER_LINE_DOUBLE;
   bool shorten_yc_line = shorten_y1_lines && shorten_y2_lines;
   int horz_line_ofs = double_vert ? double_line_ofs : 0;
   int xc = (x0 + x3) / 2;
   int x1 = xc - horz_line_ofs;
   int x2 = xc + horz_line_ofs;
 
-  bool shorten_x1_lines = left == OUTP_L_DOUBLE;
-  bool shorten_x2_lines = right == OUTP_L_DOUBLE;
+  bool shorten_x1_lines = left == RENDER_LINE_DOUBLE;
+  bool shorten_x2_lines = right == RENDER_LINE_DOUBLE;
   bool shorten_xc_line = shorten_x1_lines && shorten_x2_lines;
   int vert_line_ofs = double_horz ? double_line_ofs : 0;
   int yc = (y0 + y3) / 2;
@@ -669,164 +660,187 @@ xr_line (struct outp_driver *this,
   int y2 = yc + vert_line_ofs;
 
   if (!double_horz)
-    horz_line (this, x0, x1, x2, x3, yc, left, right, shorten_yc_line);
+    horz_line (xr, x0, x1, x2, x3, yc, left, right, shorten_yc_line);
   else
     {
-      horz_line (this, x0, x1, x2, x3, y1, left, right, shorten_y1_lines);
-      horz_line (this, x0, x1, x2, x3, y2, left, right, shorten_y2_lines);
+      horz_line (xr, x0, x1, x2, x3, y1, left, right, shorten_y1_lines);
+      horz_line (xr, x0, x1, x2, x3, y2, left, right, shorten_y2_lines);
     }
 
   if (!double_vert)
-    vert_line (this, y0, y1, y2, y3, xc, top, bottom, shorten_xc_line);
+    vert_line (xr, y0, y1, y2, y3, xc, top, bottom, shorten_xc_line);
   else
     {
-      vert_line (this, y0, y1, y2, y3, x1, top, bottom, shorten_x1_lines);
-      vert_line (this, y0, y1, y2, y3, x2, top, bottom, shorten_x2_lines);
+      vert_line (xr, y0, y1, y2, y3, x1, top, bottom, shorten_x1_lines);
+      vert_line (xr, y0, y1, y2, y3, x2, top, bottom, shorten_x2_lines);
     }
 }
 
-/* Writes STRING at location (X,Y) trimmed to the given MAX_WIDTH
-   and with the given JUSTIFICATION for THIS driver. */
+static void
+xr_measure_cell_width (void *xr_, const struct table_cell *cell,
+                       int *min_width, int *max_width)
+{
+  struct xr_driver *xr = xr_;
+  int bb[TABLE_N_AXES][2];
+  int clip[TABLE_N_AXES][2];
+  int h;
+
+  bb[H][0] = 0;
+  bb[H][1] = INT_MAX;
+  bb[V][0] = 0;
+  bb[V][1] = INT_MAX;
+  clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
+  xr_layout_cell (xr, cell, bb, clip, PANGO_WRAP_WORD, max_width, &h);
+
+  bb[H][1] = 1;
+  xr_layout_cell (xr, cell, bb, clip, PANGO_WRAP_WORD, min_width, &h);
+}
+
 static int
-draw_text (struct outp_driver *this,
-           const char *string, int x, int y, int max_width,
-           enum outp_justification justification)
-{
-  struct outp_text text;
-  int width;
-
-  text.font = OUTP_PROPORTIONAL;
-  text.justification = justification;
-  text.string = ss_cstr (string);
-  text.h = max_width;
-  text.v = this->font_height;
-  text.x = x;
-  text.y = y;
-  this->class->text_metrics (this, &text, &width, NULL);
-  this->class->text_draw (this, &text);
-  return width;
+xr_measure_cell_height (void *xr_, const struct table_cell *cell, int width)
+{
+  struct xr_driver *xr = xr_;
+  int bb[TABLE_N_AXES][2];
+  int clip[TABLE_N_AXES][2];
+  int w, h;
+
+  bb[H][0] = 0;
+  bb[H][1] = width;
+  bb[V][0] = 0;
+  bb[V][1] = INT_MAX;
+  clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
+  xr_layout_cell (xr, cell, bb, clip, PANGO_WRAP_WORD, &w, &h);
+  return h;
 }
 
+static void
+xr_draw_cell (void *xr_, const struct table_cell *cell,
+              int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2])
+{
+  struct xr_driver *xr = xr_;
+  int w, h;
+
+  xr_layout_cell (xr, cell, bb, clip, PANGO_WRAP_WORD, &w, &h);
+}
+\f
 /* Writes STRING at location (X,Y) trimmed to the given MAX_WIDTH
-   and with the given JUSTIFICATION for THIS driver. */
+   and with the given cell OPTIONS for XR. */
 static int
-text_width (struct outp_driver *this, const char *string, enum outp_font font)
-{
-  struct outp_text text;
-  int width;
-
-  text.font = font;
-  text.justification = OUTP_LEFT;
-  text.string = ss_cstr (string);
-  text.h = INT_MAX;
-  text.v = this->font_height;
-  text.x = 0;
-  text.y = 0;
-  this->class->text_metrics (this, &text, &width, NULL);
-  return width;
+draw_text (struct xr_driver *xr, const char *string, int x, int y,
+           int max_width, unsigned int options)
+{
+  struct table_cell cell;
+  int bb[TABLE_N_AXES][2];
+  int w, h;
+
+  cell.contents = string;
+  cell.options = options;
+  bb[H][0] = x;
+  bb[V][0] = y;
+  bb[H][1] = x + max_width;
+  bb[V][1] = xr->font_height;
+  xr_layout_cell (xr, &cell, bb, bb, PANGO_WRAP_WORD_CHAR, &w, &h);
+  return w;
 }
 
 /* Writes LEFT left-justified and RIGHT right-justified within
    (X0...X1) at Y.  LEFT or RIGHT or both may be null. */
 static void
-draw_header_line (struct outp_driver *this,
-                  const char *left, const char *right,
+draw_header_line (struct xr_driver *xr, const char *left, const char *right,
                   int x0, int x1, int y)
 {
   int right_width = 0;
   if (right != NULL)
-    right_width = (draw_text (this, right, x0, y, x1 - x0, OUTP_RIGHT)
-                   + this->prop_em_width);
+    right_width = (draw_text (xr, right, x0, y, x1 - x0, TAB_RIGHT)
+                   + xr->font_height / 2);
   if (left != NULL)
-    draw_text (this, left, x0, y, x1 - x0 - right_width, OUTP_LEFT);
+    draw_text (xr, left, x0, y, x1 - x0 - right_width, TAB_LEFT);
 }
 
-/* Draw top of page headers for THIS driver. */
+/* Draw top of page headers for XR. */
 static void
-draw_headers (struct outp_driver *this)
+draw_headers (struct xr_driver *xr)
 {
-  struct xr_driver_ext *ext = this->ext;
   char *r1, *r2;
   int x0, x1;
   int y;
 
-  y = -3 * this->font_height;
-  x0 = this->prop_em_width;
-  x1 = this->width - this->prop_em_width;
+  y = -3 * xr->font_height;
+  x0 = xr->font_height / 2;
+  x1 = xr->width - xr->font_height / 2;
 
   /* Draw box. */
-  cairo_rectangle (ext->cairo, 0, xr_to_pt (y), xr_to_pt (this->width),
-                   xr_to_pt (2 * (this->font_height
-                                  + ext->line_width + ext->line_gutter)));
-  cairo_save (ext->cairo);
-  cairo_set_source_rgb (ext->cairo, 0.9, 0.9, 0.9);
-  cairo_fill_preserve (ext->cairo);
-  cairo_restore (ext->cairo);
-  cairo_stroke (ext->cairo);
-
-  y += ext->line_width + ext->line_gutter;
-
-  r1 = xasprintf (_("%s - Page %d"), get_start_date (), ext->page_number);
+  cairo_rectangle (xr->cairo, 0, xr_to_pt (y), xr_to_pt (xr->width),
+                   xr_to_pt (2 * (xr->font_height
+                                  + xr->line_width + xr->line_gutter)));
+  cairo_save (xr->cairo);
+  cairo_set_source_rgb (xr->cairo, 0.9, 0.9, 0.9);
+  cairo_fill_preserve (xr->cairo);
+  cairo_restore (xr->cairo);
+  cairo_stroke (xr->cairo);
+
+  y += xr->line_width + xr->line_gutter;
+
+  r1 = xasprintf (_("%s - Page %d"), get_start_date (), xr->page_number);
   r2 = xasprintf ("%s - %s", version, host_system);
 
-  draw_header_line (this, outp_title, r1, x0, x1, y);
-  y += this->font_height;
+  draw_header_line (xr, xr->title, r1, x0, x1, y);
+  y += xr->font_height;
 
-  draw_header_line (this, outp_subtitle, r2, x0, x1, y);
+  draw_header_line (xr, xr->subtitle, r2, x0, x1, y);
 
   free (r1);
   free (r2);
 }
 \f
-/* Format TEXT on THIS driver.
-   If DRAW is nonzero, draw the text.
-   The width of the widest line is stored into *WIDTH, if WIDTH
-   is nonnull.
-   The total height of the text written is stored into *HEIGHT,
-   if HEIGHT is nonnull. */
 static void
-text (struct outp_driver *this, const struct outp_text *text, bool draw,
-      int *width, int *height)
+xr_layout_cell (struct xr_driver *xr, const struct table_cell *cell,
+                int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
+                PangoWrapMode wrap, int *width, int *height)
 {
-  struct xr_driver_ext *ext = this->ext;
-  struct xr_font *font = &ext->fonts[text->font];
+  struct xr_font *font;
+
+  font = (cell->options & TAB_FIX ? &xr->fonts[XR_FONT_FIXED]
+          : cell->options & TAB_EMPH ? &xr->fonts[XR_FONT_EMPHASIS]
+          : &xr->fonts[XR_FONT_PROPORTIONAL]);
+
+  pango_layout_set_text (font->layout, cell->contents, -1);
 
-  pango_layout_set_text (font->layout,
-                         text->string.string, text->string.length);
   pango_layout_set_alignment (
     font->layout,
-    (text->justification == OUTP_RIGHT ? PANGO_ALIGN_RIGHT
-     : text->justification == OUTP_LEFT ? PANGO_ALIGN_LEFT
+    ((cell->options & TAB_ALIGNMENT) == TAB_RIGHT ? PANGO_ALIGN_RIGHT
+     : (cell->options & TAB_ALIGNMENT) == TAB_LEFT ? PANGO_ALIGN_LEFT
      : PANGO_ALIGN_CENTER));
-  pango_layout_set_width (font->layout, text->h == INT_MAX ? -1 : text->h);
-  pango_layout_set_wrap (font->layout, PANGO_WRAP_WORD_CHAR);
-  /* XXX need to limit number of lines to those that fit in text->v. */
+  pango_layout_set_width (font->layout,
+                          bb[H][1] == INT_MAX ? -1 : bb[H][1] - bb[H][0]);
+  pango_layout_set_wrap (font->layout, wrap);
 
-  if (draw)
+  if (clip[H][0] != clip[H][1])
     {
-      int x = text->x;
-      if (text->justification != OUTP_LEFT && text->h != INT_MAX)
+      cairo_save (xr->cairo);
+
+      if (clip[H][1] != INT_MAX || clip[V][1] != INT_MAX)
         {
-          int w, h, excess;
-          pango_layout_get_size (font->layout, &w, &h);
-          excess = text->h - w;
-          if (excess > 0)
-            {
-              if (text->justification == OUTP_CENTER)
-                x += excess / 2;
-              else
-                x += excess;
-            }
+          double x0 = xr_to_pt (clip[H][0]);
+          double y0 = xr_to_pt (clip[V][0] + xr->y);
+          double x1 = xr_to_pt (clip[H][1]);
+          double y1 = xr_to_pt (clip[V][1] + xr->y);
+
+          cairo_rectangle (xr->cairo, x0, y0, x1 - x0, y1 - y0);
+          cairo_clip (xr->cairo);
         }
-      cairo_save (ext->cairo);
-      cairo_translate (ext->cairo, xr_to_pt (text->x), xr_to_pt (text->y));
-      pango_cairo_show_layout (ext->cairo, font->layout);
-      cairo_restore (ext->cairo);
+
+      cairo_translate (xr->cairo,
+                       xr_to_pt (bb[H][0]),
+                       xr_to_pt (bb[V][0] + xr->y));
+      pango_cairo_show_layout (xr->cairo, font->layout);
+      cairo_restore (xr->cairo);
     }
 
   if (width != NULL || height != NULL)
     {
       int w, h;
+
       pango_layout_get_size (font->layout, &w, &h);
       if (width != NULL)
         *width = w;
@@ -834,27 +848,13 @@ text (struct outp_driver *this, const struct outp_text *text, bool draw,
         *height = h;
     }
 }
-
-static void
-xr_text_metrics (struct outp_driver *this, const struct outp_text *t,
-                 int *width, int *height)
-{
-  text (this, t, false, width, height);
-}
-
-static void
-xr_text_draw (struct outp_driver *this, const struct outp_text *t)
-{
-  text (this, t, true, NULL, NULL);
-}
 \f
 /* Attempts to load FONT, initializing its other members based on
-   its 'string' member and the information in THIS.  Returns true
+   its 'string' member and the information in DRIVER.  Returns true
    if successful, otherwise false. */
 static bool
-load_font (struct outp_driver *this, struct xr_font *font)
+load_font (struct xr_driver *xr, struct xr_font *font)
 {
-  struct xr_driver_ext *x = this->ext;
   PangoContext *context;
   PangoLanguage *language;
 
@@ -864,9 +864,9 @@ load_font (struct outp_driver *this, struct xr_font *font)
       error (0, 0, _("\"%s\": bad font specification"), font->string);
       return false;
     }
-  pango_font_description_set_absolute_size (font->desc, this->font_height);
+  pango_font_description_set_absolute_size (font->desc, xr->font_height);
 
-  font->layout = pango_cairo_create_layout (x->cairo);
+  font->layout = pango_cairo_create_layout (xr->cairo);
   pango_layout_set_font_description (font->layout, font->desc);
 
   language = pango_language_get_default ();
@@ -888,23 +888,183 @@ free_font (struct xr_font *font)
 }
 
 /* Cairo driver class. */
-const struct outp_class cairo_class =
+const struct output_driver_class cairo_class =
 {
   "cairo",
-  0,
+  xr_create,
+  xr_destroy,
+  xr_submit,
+  xr_flush,
+};
+\f
+/* GUI rendering helpers. */
 
-  xr_open_driver,
-  xr_close_driver,
+struct xr_rendering
+  {
+    /* Table items. */
+    struct render_page *page;
+    struct xr_driver *xr;
+    int title_height;
+
+    /* Chart items. */
+    struct chart_item *chart;
+  };
 
-  xr_open_page,
-  xr_close_page,
-  NULL,
+#define CHART_WIDTH 500
+#define CHART_HEIGHT 375
 
-  xr_output_chart,
+struct xr_driver *
+xr_create_driver (cairo_t *cairo)
+{
+  struct xr_driver *xr;
+  struct string_map map;
 
-  NULL,
+  string_map_init (&map);
+  xr = xr_allocate ("cairo", 0, &map);
+  string_map_destroy (&map);
 
-  xr_line,
-  xr_text_metrics,
-  xr_text_draw,
-};
+  xr->width = INT_MAX / 8;
+  xr->length = INT_MAX / 8;
+  if (!xr_set_cairo (xr, cairo))
+    {
+      output_driver_destroy (&xr->driver);
+      return NULL;
+    }
+  return xr;
+}
+
+struct xr_rendering *
+xr_rendering_create (struct xr_driver *xr, const struct output_item *item,
+                     cairo_t *cr)
+{
+  struct xr_rendering *r = NULL;
+
+  if (is_text_item (item))
+    {
+      const struct text_item *text_item = to_text_item (item);
+      const char *text = text_item_get_text (text_item);
+      struct table_item *table_item;
+
+      table_item = table_item_create (table_from_string (0, text), NULL);
+      r = xr_rendering_create (xr, &table_item->output_item, cr);
+      table_item_unref (table_item);
+    }
+  else if (is_table_item (item))
+    {
+      r = xzalloc (sizeof *r);
+      r->xr = xr;
+      xr_set_cairo (xr, cr);
+      r->page = xr_render_table_item (xr, to_table_item (item),
+                                      &r->title_height);
+    }
+  else if (is_chart_item (item))
+    {
+      r = xzalloc (sizeof *r);
+      r->chart = to_chart_item (output_item_ref (item));
+    }
+
+  return r;
+}
+
+void
+xr_rendering_measure (struct xr_rendering *r, int *w, int *h)
+{
+  if (r->chart == NULL)
+    {
+      *w = render_page_get_size (r->page, H) / 1024;
+      *h = (render_page_get_size (r->page, V) + r->title_height) / 1024;
+    }
+  else
+    {
+      *w = CHART_WIDTH;
+      *h = CHART_HEIGHT;
+    }
+}
+
+void
+xr_rendering_draw (struct xr_rendering *r, cairo_t *cr)
+{
+  if (r->chart == NULL)
+    {
+      struct xr_driver *xr = r->xr;
+
+      xr_set_cairo (xr, cr);
+      xr->y = 0;
+      render_page_draw (r->page);
+    }
+  else
+    xr_draw_chart (r->chart, cr, 0, 0, CHART_WIDTH, CHART_HEIGHT);
+}
+
+void
+xr_draw_chart (const struct chart_item *chart_item, cairo_t *cr,
+               double x, double y, double width, double height)
+{
+  struct xrchart_geometry geom;
+
+  cairo_save (cr);
+  cairo_translate (cr, x, y + height);
+  cairo_scale (cr, 1.0, -1.0);
+  xrchart_geometry_init (cr, &geom, width, height);
+  if (is_boxplot (chart_item))
+    xrchart_draw_boxplot (chart_item, cr, &geom);
+  else if (is_histogram_chart (chart_item))
+    xrchart_draw_histogram (chart_item, cr, &geom);
+  else if (is_np_plot_chart (chart_item))
+    xrchart_draw_np_plot (chart_item, cr, &geom);
+  else if (is_piechart (chart_item))
+    xrchart_draw_piechart (chart_item, cr, &geom);
+  else if (is_roc_chart (chart_item))
+    xrchart_draw_roc (chart_item, cr, &geom);
+  else if (is_scree (chart_item))
+    xrchart_draw_scree (chart_item, cr, &geom);
+  else
+    NOT_REACHED ();
+  xrchart_geometry_free (cr, &geom);
+
+  cairo_restore (cr);
+}
+
+char *
+xr_draw_png_chart (const struct chart_item *item,
+                   const char *file_name_template, int number)
+{
+  const int width = 640;
+  const int length = 480;
+
+  cairo_surface_t *surface;
+  cairo_status_t status;
+  const char *number_pos;
+  char *file_name;
+  cairo_t *cr;
+
+  number_pos = strchr (file_name_template, '#');
+  if (number_pos != NULL)
+    file_name = xasprintf ("%.*s%d%s", (int) (number_pos - file_name_template),
+                           file_name_template, number, number_pos + 1);
+  else
+    file_name = xstrdup (file_name_template);
+
+  surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, length);
+  cr = cairo_create (surface);
+
+  cairo_save (cr);
+  cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
+  cairo_rectangle (cr, 0, 0, width, length);
+  cairo_fill (cr);
+  cairo_restore (cr);
+
+  cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
+
+  xr_draw_chart (item, cr, 0.0, 0.0, width, length);
+
+  status = cairo_surface_write_to_png (surface, file_name);
+  if (status != CAIRO_STATUS_SUCCESS)
+    error (0, 0, _("writing output file \"%s\": %s"),
+           file_name, cairo_status_to_string (status));
+
+  cairo_destroy (cr);
+  cairo_surface_destroy (surface);
+
+  return file_name;
+}
index c4f8a81380171e228820dd178db0a993f525357d..80b2b87ee505deff0a743cd68f4b8436fcdae7e9 100644 (file)
 
 #include <cairo/cairo.h>
 
-struct outp_driver *xr_create_driver (cairo_t *);
+struct chart_item;
+struct output_item;
+
+/* Used by PSPPIRE to render in the GUI. */
+struct xr_driver *xr_create_driver (cairo_t *);
+struct xr_rendering *xr_rendering_create (struct xr_driver *,
+                                          const struct output_item *,
+                                          cairo_t *);
+void xr_rendering_measure (struct xr_rendering *, int *w, int *h);
+void xr_rendering_draw (struct xr_rendering *, cairo_t *);
+
+/* Render charts with Cairo. */
+void xr_draw_chart (const struct chart_item *, cairo_t *,
+                    double x, double y, double width, double height);
+char *xr_draw_png_chart (const struct chart_item *,
+                         const char *file_name_template, int number);
 
 #endif /* output/cairo.h */
diff --git a/src/output/chart-item-provider.h b/src/output/chart-item-provider.h
new file mode 100644 (file)
index 0000000..4f13fcf
--- /dev/null
@@ -0,0 +1,31 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2004, 2009 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 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.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef OUTPUT_CHART_ITEM_PROVIDER_H
+#define OUTPUT_CHART_ITEM_PROVIDER_H 1
+
+#include <output/chart-item.h>
+#include <output/output-item.h>
+
+struct chart_item_class
+  {
+    void (*destroy) (struct chart_item *);
+  };
+
+void chart_item_init (struct chart_item *, const struct chart_item_class *,
+                      const char *title);
+
+#endif /* output/chart-provider.h */
diff --git a/src/output/chart-item.c b/src/output/chart-item.c
new file mode 100644 (file)
index 0000000..227994d
--- /dev/null
@@ -0,0 +1,88 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2004, 2009 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 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.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+
+#include <output/chart-item.h>
+#include <output/chart-item-provider.h>
+
+#include <assert.h>
+#include <stdlib.h>
+
+#include <libpspp/cast.h>
+#include <libpspp/compiler.h>
+#include <output/driver.h>
+#include <output/output-item-provider.h>
+
+#include "gl/xalloc.h"
+
+/* Initializes ITEM as a chart item of the specified CLASS.  The new chart item
+   initially has the specified TITLE, which may be NULL if no title is yet
+   available.  The caller retains ownership of TITLE.
+
+   A chart item is an abstract class, that is, a plain chart_item is not useful
+   on its own.  Thus, this function is normally called from the initialization
+   function of some subclass of chart_item. */
+void
+chart_item_init (struct chart_item *item, const struct chart_item_class *class,
+                 const char *title)
+{
+  output_item_init (&item->output_item, &chart_item_class);
+  item->class = class;
+  item->title = title != NULL ? xstrdup (title) : NULL;
+}
+
+/* Returns ITEM's title, which is a null pointer if no title has been set. */
+const char *
+chart_item_get_title (const struct chart_item *item)
+{
+  return item->title;
+}
+
+/* Sets ITEM's title to TITLE, replacing any previous title.  Specify NULL for
+   TITLE to clear any title from ITEM.  The caller retains ownership of
+   TITLE.
+
+   This function may only be used on a chart_item that is unshared. */
+void
+chart_item_set_title (struct chart_item *item, const char *title)
+{
+  assert (!chart_item_is_shared (item));
+  free (item->title);
+  item->title = title != NULL ? xstrdup (title) : NULL;
+}
+
+/* Submits ITEM to the configured output drivers, and transfers ownership to
+   the output subsystem. */
+void
+chart_item_submit (struct chart_item *item)
+{
+  output_submit (&item->output_item);
+}
+\f
+static void
+chart_item_destroy (struct output_item *output_item)
+{
+  struct chart_item *item = to_chart_item (output_item);
+  char *title = item->title;
+  item->class->destroy (item);
+  free (title);
+}
+
+const struct output_item_class chart_item_class =
+  {
+    chart_item_destroy,
+  };
diff --git a/src/output/chart-item.h b/src/output/chart-item.h
new file mode 100644 (file)
index 0000000..c005a46
--- /dev/null
@@ -0,0 +1,100 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2009 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 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.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef OUTPUT_CHART_ITEM_H
+#define OUTPUT_CHART_ITEM_H 1
+
+/* Chart items.
+
+   A chart item is a subclass of an output item (see output/output-item.h).
+
+   A chart item is abstract.  Every actual chart is a subclass of
+   chart_item. */
+
+#include <stdbool.h>
+#include <output/output-item.h>
+
+/* A chart item.
+
+   The members of struct chart_item should not be accessed directly.  Use one
+   of the accessor functions defined below. */
+struct chart_item
+  {
+    struct output_item output_item; /* Superclass */
+    const struct chart_item_class *class; /* Subclass. */
+    char *title;                /* May be null if there is no title. */
+  };
+
+const char *chart_item_get_title (const struct chart_item *);
+void chart_item_set_title (struct chart_item *, const char *);
+\f
+/* This boilerplate for chart_item, a subclass of output_item, was
+   autogenerated by mk-class-boilerplate. */
+
+#include <assert.h>
+#include <libpspp/cast.h>
+
+extern const struct output_item_class chart_item_class;
+
+/* Returns true if SUPER is a chart_item, otherwise false. */
+static inline bool
+is_chart_item (const struct output_item *super)
+{
+  return super->class == &chart_item_class;
+}
+
+/* Returns SUPER converted to chart_item.  SUPER must be a chart_item, as
+   reported by is_chart_item. */
+static inline struct chart_item *
+to_chart_item (const struct output_item *super)
+{
+  assert (is_chart_item (super));
+  return UP_CAST (super, struct chart_item, output_item);
+}
+
+/* Returns INSTANCE converted to output_item. */
+static inline struct output_item *
+chart_item_super (const struct chart_item *instance)
+{
+  return CONST_CAST (struct output_item *, &instance->output_item);
+}
+
+/* Increments INSTANCE's reference count and returns INSTANCE. */
+static inline struct chart_item *
+chart_item_ref (const struct chart_item *instance)
+{
+  return to_chart_item (output_item_ref (&instance->output_item));
+}
+
+/* Decrements INSTANCE's reference count, then destroys INSTANCE if
+   the reference count is now zero. */
+static inline void
+chart_item_unref (struct chart_item *instance)
+{
+  output_item_unref (&instance->output_item);
+}
+
+/* Returns true if INSTANCE's reference count is greater than 1,
+   false otherwise. */
+static inline bool
+chart_item_is_shared (const struct chart_item *instance)
+{
+  return output_item_is_shared (&instance->output_item);
+}
+
+void chart_item_submit (struct chart_item *);
+\f
+#endif /* output/chart-item.h */
diff --git a/src/output/chart-provider.h b/src/output/chart-provider.h
deleted file mode 100644 (file)
index 9becb6f..0000000
+++ /dev/null
@@ -1,89 +0,0 @@
-/* PSPP - a program for statistical analysis.
-   Copyright (C) 2004, 2009 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 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.
-
-   You should have received a copy of the GNU General Public License
-   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
-
-#ifndef OUTPUT_CHART_PROVIDER_H
-#define OUTPUT_CHART_PROVIDER_H 1
-
-#include <cairo/cairo.h>
-#include <stdbool.h>
-#include <stdint.h>
-#include <output/chart.h>
-
-struct chart_colour
-  {
-    uint8_t red;
-    uint8_t green;
-    uint8_t blue;
-  };
-
-/* The geometry of a chart. */
-struct chart_geometry
-  {
-    int data_top   ;
-    int data_right ;
-    int data_bottom;
-    int data_left  ;
-
-    int abscissa_top;
-
-    int ordinate_right ;
-
-    int title_bottom ;
-
-    /* Legend. */
-    int legend_left ;
-    int legend_right ;
-    const char **dataset;
-    int n_datasets;
-
-    /* Default font size for the plot. */
-    double font_size;
-
-    struct chart_colour fill_colour;
-
-    /* Stuff Particular to Cartesians (and Boxplots ) */
-    double ordinate_scale;
-    double abscissa_scale;
-    double x_min;
-    double x_max;
-    double y_min;
-    double y_max;
-    bool in_path;
-  };
-
-struct chart_class
-  {
-    void (*draw) (const struct chart *, cairo_t *, struct chart_geometry *);
-    void (*destroy) (struct chart *);
-  };
-
-struct chart
-  {
-    const struct chart_class *class;
-    int ref_cnt;
-  };
-
-void chart_init (struct chart *, const struct chart_class *);
-
-void chart_geometry_init (cairo_t *, struct chart_geometry *,
-                          double width, double length);
-void chart_geometry_free (cairo_t *, struct chart_geometry *);
-
-void chart_draw (const struct chart *, cairo_t *, struct chart_geometry *);
-char *chart_draw_png (const struct chart *, const char *file_name_template,
-                      int number);
-
-#endif /* output/chart-provider.h */
diff --git a/src/output/chart.c b/src/output/chart.c
deleted file mode 100644 (file)
index e31422b..0000000
+++ /dev/null
@@ -1,181 +0,0 @@
-/* PSPP - a program for statistical analysis.
-   Copyright (C) 2004, 2009 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 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.
-
-   You should have received a copy of the GNU General Public License
-   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
-
-#include <config.h>
-
-#include <output/chart.h>
-#include <output/chart-provider.h>
-
-#include <assert.h>
-#include <cairo/cairo.h>
-#include <errno.h>
-#include <float.h>
-#include <math.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <libpspp/str.h>
-#include <output/manager.h>
-#include <output/output.h>
-
-#include "error.h"
-#include "xalloc.h"
-
-#include "gettext.h"
-#define _(msgid) gettext (msgid)
-
-extern struct som_table_class tab_table_class;
-
-void
-chart_init (struct chart *chart, const struct chart_class *class)
-{
-  chart->class = class;
-  chart->ref_cnt = 1;
-}
-
-void
-chart_geometry_init (cairo_t *cr, struct chart_geometry *geom,
-                     double width, double length)
-{
-  /* Set default chartetry. */
-  geom->data_top = 0.900 * length;
-  geom->data_right = 0.800 * width;
-  geom->data_bottom = 0.120 * length;
-  geom->data_left = 0.150 * width;
-  geom->abscissa_top = 0.070 * length;
-  geom->ordinate_right = 0.120 * width;
-  geom->title_bottom = 0.920 * length;
-  geom->legend_left = 0.810 * width;
-  geom->legend_right = width;
-  geom->font_size = 15.0;
-  geom->in_path = false;
-  geom->dataset = NULL;
-  geom->n_datasets = 0;
-
-  geom->fill_colour.red = 255;
-  geom->fill_colour.green = 0;
-  geom->fill_colour.blue = 0;
-
-  cairo_set_line_width (cr, 1.0);
-
-  cairo_rectangle (cr, geom->data_left, geom->data_bottom,
-                   geom->data_right - geom->data_left,
-                   geom->data_top - geom->data_bottom);
-  cairo_stroke (cr);
-}
-
-void
-chart_geometry_free (cairo_t *cr UNUSED, struct chart_geometry *geom)
-{
-  int i;
-
-  for (i = 0 ; i < geom->n_datasets; ++i)
-    free (geom->dataset[i]);
-  free (geom->dataset);
-}
-
-void
-chart_draw (const struct chart *chart, cairo_t *cr,
-            struct chart_geometry *geom)
-{
-  chart->class->draw (chart, cr, geom);
-}
-
-char *
-chart_draw_png (const struct chart *chart, const char *file_name_template,
-                int number)
-{
-  const int width = 640;
-  const int length = 480;
-
-  struct chart_geometry geom;
-  cairo_surface_t *surface;
-  cairo_status_t status;
-  const char *number_pos;
-  char *file_name;
-  cairo_t *cr;
-
-  number_pos = strchr (file_name_template, '#');
-  if (number_pos != NULL)
-    file_name = xasprintf ("%.*s%d%s", (int) (number_pos - file_name_template),
-                           file_name_template, number, number_pos + 1);
-  else
-    file_name = xstrdup (file_name_template);
-
-  surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, length);
-  cr = cairo_create (surface);
-
-  cairo_translate (cr, 0.0, length);
-  cairo_scale (cr, 1.0, -1.0);
-
-  cairo_save (cr);
-  cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
-  cairo_rectangle (cr, 0, 0, width, length);
-  cairo_fill (cr);
-  cairo_restore (cr);
-
-  cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
-
-  chart_geometry_init (cr, &geom, width, length);
-  chart_draw (chart, cr, &geom);
-  chart_geometry_free (cr, &geom);
-
-  status = cairo_surface_write_to_png (surface, file_name);
-  if (status != CAIRO_STATUS_SUCCESS)
-    error (0, 0, _("writing output file \"%s\": %s"),
-           file_name, cairo_status_to_string (status));
-
-  cairo_destroy (cr);
-  cairo_surface_destroy (surface);
-
-  return file_name;
-}
-
-
-struct chart *
-chart_ref (const struct chart *chart_)
-{
-  struct chart *chart = CONST_CAST (struct chart *, chart_);
-  chart->ref_cnt++;
-  return chart;
-}
-
-void
-chart_unref (struct chart *chart)
-{
-  if (chart != NULL)
-    {
-      assert (chart->ref_cnt > 0);
-      if (--chart->ref_cnt == 0)
-        chart->class->destroy (chart);
-    }
-}
-
-void
-chart_submit (struct chart *chart)
-{
-#ifdef HAVE_CAIRO
-  struct outp_driver *d;
-
-  for (d = outp_drivers (NULL); d; d = outp_drivers (d))
-    if (d->class->output_chart != NULL)
-      d->class->output_chart (d, chart);
-#endif
-
-  chart_unref (chart);
-}
diff --git a/src/output/chart.h b/src/output/chart.h
deleted file mode 100644 (file)
index d6b9b3d..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-/* PSPP - a program for statistical analysis.
-   Copyright (C) 2009 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 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.
-
-   You should have received a copy of the GNU General Public License
-   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
-
-#ifndef OUTPUT_CHART_H
-#define OUTPUT_CHART_H 1
-
-#include <cairo/cairo.h>
-
-struct chart;
-
-struct chart *chart_ref (const struct chart *);
-void chart_unref (struct chart *);
-
-void chart_submit (struct chart *);
-
-#endif /* output/chart.h */
diff --git a/src/output/charts/boxplot-cairo.c b/src/output/charts/boxplot-cairo.c
new file mode 100644 (file)
index 0000000..ddecbfc
--- /dev/null
@@ -0,0 +1,179 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2009 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 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.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+
+#include <output/cairo-chart.h>
+
+#include <math.h>
+
+#include <output/charts/boxplot.h>
+#include <math/box-whisker.h>
+#include <math/chart-geometry.h>
+
+/* Draw an OUTLIER on the plot CH
+ * at CENTRELINE
+ */
+static void
+draw_case (cairo_t *cr, const struct xrchart_geometry *geom, double centreline,
+          const struct outlier *outlier)
+{
+  double y = geom->data_bottom + (outlier->value - geom->y_min) * geom->ordinate_scale;
+  xrchart_draw_marker (cr, centreline, y,
+                     outlier->extreme ? XRMARKER_ASTERISK : XRMARKER_CIRCLE,
+                     20);
+
+  cairo_move_to (cr, centreline + 10, y);
+  xrchart_label (cr, 'l', 'c', geom->font_size, ds_cstr (&outlier->label));
+}
+
+static void
+boxplot_draw_box (cairo_t *cr, const struct xrchart_geometry *geom,
+                  double box_centre,
+                  double box_width,
+                  const struct box_whisker *bw,
+                  const char *name)
+{
+  double whisker[2];
+  double hinge[3];
+  struct ll *ll;
+
+  const struct ll_list *outliers;
+
+  const double box_left = box_centre - box_width / 2.0;
+
+  const double box_right = box_centre + box_width / 2.0;
+
+  double box_bottom ;
+  double box_top ;
+  double bottom_whisker ;
+  double top_whisker ;
+
+  box_whisker_whiskers (bw, whisker);
+  box_whisker_hinges (bw, hinge);
+
+  box_bottom = geom->data_bottom + (hinge[0] - geom->y_min ) * geom->ordinate_scale;
+
+  box_top = geom->data_bottom + (hinge[2] - geom->y_min ) * geom->ordinate_scale;
+
+  bottom_whisker = geom->data_bottom + (whisker[0] - geom->y_min) *
+    geom->ordinate_scale;
+
+  top_whisker = geom->data_bottom + (whisker[1] - geom->y_min) * geom->ordinate_scale;
+
+  /* Draw the box */
+  cairo_rectangle (cr,
+                   box_left,
+                   box_bottom,
+                   box_right - box_left,
+                   box_top - box_bottom);
+  cairo_save (cr);
+  cairo_set_source_rgb (cr,
+                        geom->fill_colour.red / 255.0,
+                        geom->fill_colour.green / 255.0,
+                        geom->fill_colour.blue / 255.0);
+  cairo_fill (cr);
+  cairo_restore (cr);
+  cairo_stroke (cr);
+
+  /* Draw the median */
+  cairo_save (cr);
+  cairo_set_line_width (cr, cairo_get_line_width (cr) * 5);
+  cairo_move_to (cr,
+                 box_left,
+                 geom->data_bottom + (hinge[1] - geom->y_min) * geom->ordinate_scale);
+  cairo_line_to (cr,
+                 box_right,
+                 geom->data_bottom + (hinge[1] - geom->y_min) * geom->ordinate_scale);
+  cairo_stroke (cr);
+  cairo_restore (cr);
+
+  /* Draw the bottom whisker */
+  cairo_move_to (cr, box_left, bottom_whisker);
+  cairo_line_to (cr, box_right, bottom_whisker);
+  cairo_stroke (cr);
+
+  /* Draw top whisker */
+  cairo_move_to (cr, box_left, top_whisker);
+  cairo_line_to (cr, box_right, top_whisker);
+  cairo_stroke (cr);
+
+  /* Draw centre line.
+     (bottom half) */
+  cairo_move_to (cr, box_centre, bottom_whisker);
+  cairo_line_to (cr, box_centre, box_bottom);
+  cairo_stroke (cr);
+
+  /* (top half) */
+  cairo_move_to (cr, box_centre, top_whisker);
+  cairo_line_to (cr, box_centre, box_top);
+  cairo_stroke (cr);
+
+  outliers = box_whisker_outliers (bw);
+  for (ll = ll_head (outliers);
+       ll != ll_null (outliers); ll = ll_next (ll))
+    {
+      const struct outlier *outlier = ll_data (ll, struct outlier, ll);
+      draw_case (cr, geom, box_centre, outlier);
+    }
+
+  /* Draw  tick  mark on x axis */
+  draw_tick(cr, geom, TICK_ABSCISSA, box_centre - geom->data_left, "%s", name);
+}
+
+static void
+boxplot_draw_yscale (cairo_t *cr, struct xrchart_geometry *geom,
+                     double y_max, double y_min)
+{
+  double y_tick;
+  double d;
+
+  geom->y_max = y_max;
+  geom->y_min = y_min;
+
+  y_tick = chart_rounded_tick (fabs (geom->y_max - geom->y_min) / 5.0);
+
+  geom->y_min = (ceil (geom->y_min / y_tick) - 1.0) * y_tick;
+
+  geom->y_max = (floor (geom->y_max / y_tick) + 1.0) * y_tick;
+
+  geom->ordinate_scale = (fabs (geom->data_top - geom->data_bottom)
+                          / fabs (geom->y_max - geom->y_min));
+
+  for (d = geom->y_min; d <= geom->y_max; d += y_tick)
+    draw_tick (cr, geom, TICK_ORDINATE,
+               (d - geom->y_min) * geom->ordinate_scale, "%g", d);
+}
+
+void
+xrchart_draw_boxplot (const struct chart_item *chart_item, cairo_t *cr,
+                      struct xrchart_geometry *geom)
+{
+  const struct boxplot *boxplot = to_boxplot (chart_item);
+  double box_width;
+  size_t i;
+
+  boxplot_draw_yscale (cr, geom, boxplot->y_max, boxplot->y_min);
+  xrchart_write_title (cr, geom, "%s", chart_item->title);
+
+  box_width = (geom->data_right - geom->data_left) / boxplot->n_boxes / 2.0;
+  for (i = 0; i < boxplot->n_boxes; i++)
+    {
+      const struct boxplot_box *box = &boxplot->boxes[i];
+      const double box_centre = (i * 2 + 1) * box_width + geom->data_left;
+      boxplot_draw_box (cr, geom, box_centre, box_width, box->bw, box->label);
+    }
+}
index 015385e74cd0ddbada68dfbf3fd2ea9f8b8df24d..7e9248004b3c2b2662f684241e55ab06b865b0c4 100644 (file)
 
 #include <output/charts/boxplot.h>
 
-#include <math.h>
-#include <assert.h>
-#include <cairo/cairo.h>
-
-#include <libpspp/cast.h>
-#include <libpspp/misc.h>
-#include <math/chart-geometry.h>
 #include <math/box-whisker.h>
-#include <output/chart.h>
-#include <output/chart-provider.h>
-#include <output/charts/plot-chart.h>
-
-/* Draw a box-and-whiskers plot
-*/
-
-struct box
-  {
-    struct box_whisker *bw;
-    char *label;
-  };
-
-struct boxplot
-  {
-    struct chart chart;
-    double y_min;
-    double y_max;
-    char *title;
-    struct box *boxes;
-    size_t n_boxes, boxes_allocated;
-  };
-
-static const struct chart_class boxplot_chart_class;
+#include <output/chart-item-provider.h>
 
 struct boxplot *
 boxplot_create (double y_min, double y_max, const char *title)
 {
   struct boxplot *boxplot = xmalloc (sizeof *boxplot);
-  chart_init (&boxplot->chart, &boxplot_chart_class);
+  chart_item_init (&boxplot->chart_item, &boxplot_class, title);
   boxplot->y_min = y_min;
   boxplot->y_max = y_max;
-  boxplot->title = xstrdup (title);
   boxplot->boxes = NULL;
   boxplot->n_boxes = boxplot->boxes_allocated = 0;
   return boxplot;
@@ -69,7 +38,7 @@ void
 boxplot_add_box (struct boxplot *boxplot,
                  struct box_whisker *bw, const char *label)
 {
-  struct box *box;
+  struct boxplot_box *box;
   if (boxplot->n_boxes >= boxplot->boxes_allocated)
     boxplot->boxes = x2nrealloc (boxplot->boxes, &boxplot->boxes_allocated,
                                  sizeof *boxplot->boxes);
@@ -78,176 +47,15 @@ boxplot_add_box (struct boxplot *boxplot,
   box->label = xstrdup (label);
 }
 
-struct chart *
-boxplot_get_chart (struct boxplot *boxplot)
-{
-  return &boxplot->chart;
-}
-
-/* Draw an OUTLIER on the plot CH
- * at CENTRELINE
- */
-static void
-draw_case (cairo_t *cr, const struct chart_geometry *geom, double centreline,
-          const struct outlier *outlier)
-{
-  double y = geom->data_bottom + (outlier->value - geom->y_min) * geom->ordinate_scale;
-  chart_draw_marker (cr, centreline, y,
-                     outlier->extreme ? MARKER_ASTERISK : MARKER_CIRCLE,
-                     20);
-
-  cairo_move_to (cr, centreline + 10, y);
-  chart_label (cr, 'l', 'c', geom->font_size, ds_cstr (&outlier->label));
-}
-
-static void
-boxplot_draw_box (cairo_t *cr, const struct chart_geometry *geom,
-                  double box_centre,
-                  double box_width,
-                  const struct box_whisker *bw,
-                  const char *name)
-{
-  double whisker[2];
-  double hinge[3];
-  struct ll *ll;
-
-  const struct ll_list *outliers;
-
-  const double box_left = box_centre - box_width / 2.0;
-
-  const double box_right = box_centre + box_width / 2.0;
-
-  double box_bottom ;
-  double box_top ;
-  double bottom_whisker ;
-  double top_whisker ;
-
-  box_whisker_whiskers (bw, whisker);
-  box_whisker_hinges (bw, hinge);
-
-  box_bottom = geom->data_bottom + (hinge[0] - geom->y_min ) * geom->ordinate_scale;
-
-  box_top = geom->data_bottom + (hinge[2] - geom->y_min ) * geom->ordinate_scale;
-
-  bottom_whisker = geom->data_bottom + (whisker[0] - geom->y_min) *
-    geom->ordinate_scale;
-
-  top_whisker = geom->data_bottom + (whisker[1] - geom->y_min) * geom->ordinate_scale;
-
-  /* Draw the box */
-  cairo_rectangle (cr,
-                   box_left,
-                   box_bottom,
-                   box_right - box_left,
-                   box_top - box_bottom);
-  cairo_save (cr);
-  cairo_set_source_rgb (cr,
-                        geom->fill_colour.red / 255.0,
-                        geom->fill_colour.green / 255.0,
-                        geom->fill_colour.blue / 255.0);
-  cairo_fill (cr);
-  cairo_restore (cr);
-  cairo_stroke (cr);
-
-  /* Draw the median */
-  cairo_save (cr);
-  cairo_set_line_width (cr, cairo_get_line_width (cr) * 5);
-  cairo_move_to (cr,
-                 box_left,
-                 geom->data_bottom + (hinge[1] - geom->y_min) * geom->ordinate_scale);
-  cairo_line_to (cr,
-                 box_right,
-                 geom->data_bottom + (hinge[1] - geom->y_min) * geom->ordinate_scale);
-  cairo_stroke (cr);
-  cairo_restore (cr);
-
-  /* Draw the bottom whisker */
-  cairo_move_to (cr, box_left, bottom_whisker);
-  cairo_line_to (cr, box_right, bottom_whisker);
-  cairo_stroke (cr);
-
-  /* Draw top whisker */
-  cairo_move_to (cr, box_left, top_whisker);
-  cairo_line_to (cr, box_right, top_whisker);
-  cairo_stroke (cr);
-
-  /* Draw centre line.
-     (bottom half) */
-  cairo_move_to (cr, box_centre, bottom_whisker);
-  cairo_line_to (cr, box_centre, box_bottom);
-  cairo_stroke (cr);
-
-  /* (top half) */
-  cairo_move_to (cr, box_centre, top_whisker);
-  cairo_line_to (cr, box_centre, box_top);
-  cairo_stroke (cr);
-
-  outliers = box_whisker_outliers (bw);
-  for (ll = ll_head (outliers);
-       ll != ll_null (outliers); ll = ll_next (ll))
-    {
-      const struct outlier *outlier = ll_data (ll, struct outlier, ll);
-      draw_case (cr, geom, box_centre, outlier);
-    }
-
-  /* Draw  tick  mark on x axis */
-  draw_tick(cr, geom, TICK_ABSCISSA, box_centre - geom->data_left, "%s", name);
-}
-
-static void
-boxplot_draw_yscale (cairo_t *cr, struct chart_geometry *geom,
-                     double y_max, double y_min)
-{
-  double y_tick;
-  double d;
-
-  geom->y_max = y_max;
-  geom->y_min = y_min;
-
-  y_tick = chart_rounded_tick (fabs (geom->y_max - geom->y_min) / 5.0);
-
-  geom->y_min = (ceil (geom->y_min / y_tick) - 1.0) * y_tick;
-
-  geom->y_max = (floor (geom->y_max / y_tick) + 1.0) * y_tick;
-
-  geom->ordinate_scale = (fabs (geom->data_top - geom->data_bottom)
-                          / fabs (geom->y_max - geom->y_min));
-
-  for (d = geom->y_min; d <= geom->y_max; d += y_tick)
-    draw_tick (cr, geom, TICK_ORDINATE,
-               (d - geom->y_min) * geom->ordinate_scale, "%g", d);
-}
-
-static void
-boxplot_chart_draw (const struct chart *chart, cairo_t *cr,
-                    struct chart_geometry *geom)
-{
-  const struct boxplot *boxplot = UP_CAST (chart, struct boxplot, chart);
-  double box_width;
-  size_t i;
-
-  boxplot_draw_yscale (cr, geom, boxplot->y_max, boxplot->y_min);
-  chart_write_title (cr, geom, "%s", boxplot->title);
-
-  box_width = (geom->data_right - geom->data_left) / boxplot->n_boxes / 2.0;
-  for (i = 0; i < boxplot->n_boxes; i++)
-    {
-      const struct box *box = &boxplot->boxes[i];
-      const double box_centre = (i * 2 + 1) * box_width + geom->data_left;
-      boxplot_draw_box (cr, geom, box_centre, box_width, box->bw, box->label);
-    }
-}
-
 static void
-boxplot_chart_destroy (struct chart *chart)
+boxplot_chart_destroy (struct chart_item *chart_item)
 {
-  struct boxplot *boxplot = UP_CAST (chart, struct boxplot, chart);
+  struct boxplot *boxplot = to_boxplot (chart_item);
   size_t i;
 
-  free (boxplot->title);
   for (i = 0; i < boxplot->n_boxes; i++)
     {
-      struct box *box = &boxplot->boxes[i];
+      struct boxplot_box *box = &boxplot->boxes[i];
       struct statistic *statistic = &box->bw->parent.parent;
       statistic->destroy (statistic);
       free (box->label);
@@ -256,8 +64,7 @@ boxplot_chart_destroy (struct chart *chart)
   free (boxplot);
 }
 
-static const struct chart_class boxplot_chart_class =
+const struct chart_item_class boxplot_class =
   {
-    boxplot_chart_draw,
     boxplot_chart_destroy
   };
index 1e9d00706f88da4613e7b65aa727e23b832b861e..bd5a7a32379b3b029babb89718b53908851f13eb 100644 (file)
 #ifndef OUTPUT_CHARTS_BOXPLOT_H
 #define OUTPUT_CHARTS_BOXPLOT_H 1
 
-struct box_whisker;
+#include <stddef.h>
+#include <output/chart-item.h>
+
+/* Box-whiskers plot. */
+struct boxplot
+  {
+    struct chart_item chart_item;
+
+    /* Data. */
+    struct boxplot_box *boxes;
+    size_t n_boxes, boxes_allocated;
+
+    /* Derived from data and convenient for plotting. */
+    double y_min;               /* Minimum Y coordinate of extremum. */
+    double y_max;               /* Maximum Y coordinate of extremum. */
+  };
+
+/* One box within a box-whiskers plot. */
+struct boxplot_box
+  {
+    struct box_whisker *bw;
+    char *label;                /* Variable name or factor label. */
+  };
 
 struct boxplot *boxplot_create (double y_min, double y_max, const char *title);
 void boxplot_add_box (struct boxplot *,
                       struct box_whisker *, const char *label);
-struct chart *boxplot_get_chart (struct boxplot *);
+\f
+/* This boilerplate for boxplot, a subclass of chart_item, was
+   autogenerated by mk-class-boilerplate. */
+
+#include <assert.h>
+#include <libpspp/cast.h>
+
+extern const struct chart_item_class boxplot_class;
+
+/* Returns true if SUPER is a boxplot, otherwise false. */
+static inline bool
+is_boxplot (const struct chart_item *super)
+{
+  return super->class == &boxplot_class;
+}
+
+/* Returns SUPER converted to boxplot.  SUPER must be a boxplot, as
+   reported by is_boxplot. */
+static inline struct boxplot *
+to_boxplot (const struct chart_item *super)
+{
+  assert (is_boxplot (super));
+  return UP_CAST (super, struct boxplot, chart_item);
+}
+
+/* Returns INSTANCE converted to chart_item. */
+static inline struct chart_item *
+boxplot_super (const struct boxplot *instance)
+{
+  return CONST_CAST (struct chart_item *, &instance->chart_item);
+}
+
+/* Increments INSTANCE's reference count and returns INSTANCE. */
+static inline struct boxplot *
+boxplot_ref (const struct boxplot *instance)
+{
+  return to_boxplot (chart_item_ref (&instance->chart_item));
+}
+
+/* Decrements INSTANCE's reference count, then destroys INSTANCE if
+   the reference count is now zero. */
+static inline void
+boxplot_unref (struct boxplot *instance)
+{
+  chart_item_unref (&instance->chart_item);
+}
+
+/* Returns true if INSTANCE's reference count is greater than 1,
+   false otherwise. */
+static inline bool
+boxplot_is_shared (const struct boxplot *instance)
+{
+  return chart_item_is_shared (&instance->chart_item);
+}
 
+static inline void
+boxplot_submit (struct boxplot *instance)
+{
+  chart_item_submit (&instance->chart_item);
+}
+\f
 #endif /* output/charts/boxplot.h */
diff --git a/src/output/charts/cartesian.c b/src/output/charts/cartesian.c
deleted file mode 100644 (file)
index eabcf51..0000000
+++ /dev/null
@@ -1,130 +0,0 @@
-/* PSPP - a program for statistical analysis.
-   Copyright (C) 2004, 2009 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 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.
-
-   You should have received a copy of the GNU General Public License
-   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
-
-
-#include <config.h>
-
-#include <output/charts/cartesian.h>
-
-#include <cairo/cairo.h>
-#include <math.h>
-#include <assert.h>
-
-#include <output/chart.h>
-#include <output/chart-provider.h>
-#include <output/charts/plot-chart.h>
-#include <libpspp/compiler.h>
-
-#include "xalloc.h"
-
-/* Start a new vector called NAME */
-void
-chart_vector_start (cairo_t *cr, struct chart_geometry *geom, const char *name)
-{
-  const struct chart_colour *colour;
-
-  cairo_save (cr);
-
-  colour = &data_colour[geom->n_datasets % N_CHART_COLOURS];
-  cairo_set_source_rgb (cr,
-                        colour->red / 255.0,
-                        colour->green / 255.0,
-                        colour->blue / 255.0);
-
-  geom->n_datasets++;
-  geom->dataset = xrealloc (geom->dataset,
-                            geom->n_datasets * sizeof (*geom->dataset));
-
-  geom->dataset[geom->n_datasets - 1] = strdup (name);
-}
-
-/* Plot a data point */
-void
-chart_datum (cairo_t *cr, const struct chart_geometry *geom,
-             int dataset UNUSED, double x, double y)
-{
-  double x_pos = (x - geom->x_min) * geom->abscissa_scale + geom->data_left;
-  double y_pos = (y - geom->y_min) * geom->ordinate_scale + geom->data_bottom;
-
-  chart_draw_marker (cr, x_pos, y_pos, MARKER_SQUARE, 15);
-}
-
-void
-chart_vector_end (cairo_t *cr, struct chart_geometry *geom)
-{
-  cairo_stroke (cr);
-  cairo_restore (cr);
-  geom->in_path = false;
-}
-
-/* Plot a data point */
-void
-chart_vector (cairo_t *cr, struct chart_geometry *geom, double x, double y)
-{
-  const double x_pos =
-    (x - geom->x_min) * geom->abscissa_scale + geom->data_left ;
-
-  const double y_pos =
-    (y - geom->y_min) * geom->ordinate_scale + geom->data_bottom ;
-
-  if (geom->in_path)
-    cairo_line_to (cr, x_pos, y_pos);
-  else
-    {
-      cairo_move_to (cr, x_pos, y_pos);
-      geom->in_path = true;
-    }
-}
-
-
-
-/* Draw a line with slope SLOPE and intercept INTERCEPT.
-   between the points limit1 and limit2.
-   If lim_dim is CHART_DIM_Y then the limit{1,2} are on the
-   y axis otherwise the x axis
-*/
-void
-chart_line(cairo_t *cr, const struct chart_geometry *geom,
-           double slope, double intercept,
-          double limit1, double limit2, enum CHART_DIM lim_dim)
-{
-  double x1, y1;
-  double x2, y2;
-
-  if ( lim_dim == CHART_DIM_Y )
-    {
-      x1 = ( limit1 - intercept ) / slope;
-      x2 = ( limit2 - intercept ) / slope;
-      y1 = limit1;
-      y2 = limit2;
-    }
-  else
-    {
-      x1 = limit1;
-      x2 = limit2;
-      y1 = slope * x1 + intercept;
-      y2 = slope * x2 + intercept;
-    }
-
-  y1 = (y1 - geom->y_min) * geom->ordinate_scale + geom->data_bottom;
-  y2 = (y2 - geom->y_min) * geom->ordinate_scale + geom->data_bottom;
-  x1 = (x1 - geom->x_min) * geom->abscissa_scale + geom->data_left;
-  x2 = (x2 - geom->x_min) * geom->abscissa_scale + geom->data_left;
-
-  cairo_move_to (cr, x1, y1);
-  cairo_line_to (cr, x2, y2);
-  cairo_stroke (cr);
-}
diff --git a/src/output/charts/cartesian.h b/src/output/charts/cartesian.h
deleted file mode 100644 (file)
index 3c21db6..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
-/* PSPP - a program for statistical analysis.
-   Copyright (C) 2004, 2009 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 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.
-
-   You should have received a copy of the GNU General Public License
-   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
-
-
-
-#ifndef CARTESIAN_H
-#define CARTESIAN_H
-
-#include <cairo/cairo.h>
-#include <libpspp/compiler.h>
-#include <output/chart.h>
-
-enum CHART_DIM
-  {
-    CHART_DIM_X,
-    CHART_DIM_Y
-  };
-
-struct chart_geometry;
-
-void  chart_vector_start (cairo_t *, struct chart_geometry *,
-                          const char *name);
-void chart_vector_end (cairo_t *, struct chart_geometry *);
-void chart_vector (cairo_t *, struct chart_geometry *, double x, double y);
-
-/* Plot a data point */
-void chart_datum(cairo_t *, const struct chart_geometry *,
-                 int dataset UNUSED, double x, double y);
-
-/* Draw a line with slope SLOPE and intercept INTERCEPT.
-   between the points limit1 and limit2.
-   If lim_dim is CHART_DIM_Y then the limit{1,2} are on the
-   y axis otherwise the x axis
-*/
-void chart_line(cairo_t *, const struct chart_geometry *,
-                double slope, double intercept,
-               double limit1, double limit2, enum CHART_DIM lim_dim);
-
-
-#endif
diff --git a/src/output/charts/np-plot-cairo.c b/src/output/charts/np-plot-cairo.c
new file mode 100644 (file)
index 0000000..a918652
--- /dev/null
@@ -0,0 +1,90 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2009 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 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.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+
+#include <output/charts/np-plot.h>
+
+#include <data/case.h>
+#include <data/casereader.h>
+#include <math/np.h>
+#include <output/cairo-chart.h>
+
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
+
+static void
+np_plot_chart_draw (const struct chart_item *chart_item, cairo_t *cr,
+                    struct xrchart_geometry *geom)
+{
+  const struct np_plot_chart *npp = to_np_plot_chart (chart_item);
+  struct casereader *data;
+  struct ccase *c;
+
+  xrchart_write_title (cr, geom, _("Normal Q-Q Plot of %s"), npp->label);
+  xrchart_write_xlabel (cr, geom, _("Observed Value"));
+  xrchart_write_ylabel (cr, geom, _("Expected Normal"));
+  xrchart_write_xscale (cr, geom,
+                      npp->x_lower - npp->slack,
+                      npp->x_upper + npp->slack, 5);
+  xrchart_write_yscale (cr, geom, npp->y_first, npp->y_last, 5);
+
+  data = casereader_clone (npp->data);
+  for (; (c = casereader_read (data)) != NULL; case_unref (c))
+    xrchart_datum (cr, geom, 0,
+                 case_data_idx (c, NP_IDX_Y)->f,
+                 case_data_idx (c, NP_IDX_NS)->f);
+  casereader_destroy (data);
+
+  xrchart_line (cr, geom, npp->slope, npp->intercept,
+              npp->y_first, npp->y_last, XRCHART_DIM_Y);
+}
+
+static void
+dnp_plot_chart_draw (const struct chart_item *chart_item, cairo_t *cr,
+                     struct xrchart_geometry *geom)
+{
+  const struct np_plot_chart *dnpp = to_np_plot_chart (chart_item);
+  struct casereader *data;
+  struct ccase *c;
+
+  xrchart_write_title (cr, geom, _("Detrended Normal Q-Q Plot of %s"),
+                       dnpp->label);
+  xrchart_write_xlabel (cr, geom, _("Observed Value"));
+  xrchart_write_ylabel (cr, geom, _("Dev from Normal"));
+  xrchart_write_xscale (cr, geom, dnpp->y_min, dnpp->y_max, 5);
+  xrchart_write_yscale (cr, geom, dnpp->dns_min, dnpp->dns_max, 5);
+
+  data = casereader_clone (dnpp->data);
+  for (; (c = casereader_read (data)) != NULL; case_unref (c))
+    xrchart_datum (cr, geom, 0, case_data_idx (c, NP_IDX_Y)->f,
+                   case_data_idx (c, NP_IDX_DNS)->f);
+  casereader_destroy (data);
+
+  xrchart_line (cr, geom, 0, 0, dnpp->y_min, dnpp->y_max, XRCHART_DIM_X);
+}
+
+void
+xrchart_draw_np_plot (const struct chart_item *chart_item, cairo_t *cr,
+                      struct xrchart_geometry *geom)
+{
+  const struct np_plot_chart *npp = to_np_plot_chart (chart_item);
+
+  if (npp->detrended)
+    dnp_plot_chart_draw (chart_item, cr, geom);
+  else
+    np_plot_chart_draw (chart_item, cr, geom);
+}
index c077b8723def9395f567944a6d2d9559f8cf7373..e912479a72bbd0bceca65079a1bfc3c098522c80 100644 (file)
 #include <gsl/gsl_cdf.h>
 
 #include <data/casereader.h>
-#include <data/casewriter.h>
 #include <libpspp/cast.h>
-#include <libpspp/message.h>
 #include <math/np.h>
-#include <output/chart-provider.h>
-#include <output/charts/cartesian.h>
-#include <output/charts/plot-chart.h>
+#include <output/chart-item-provider.h>
 
 #include "gl/minmax.h"
 
-#include "gettext.h"
-#define _(msgid) gettext (msgid)
-
-/* An NP or DNP plot. */
-struct np_plot_chart
-  {
-    struct chart chart;
-    char *label;
-    struct casereader *data;
-
-    /* Copied directly from struct np. */
-    double y_min, y_max;
-    double dns_min, dns_max;
-
-    /* Calculated. */
-    double slope, intercept;
-    double y_first, y_last;
-    double x_lower, x_upper;
-    double slack;
-  };
-
-static const struct chart_class np_plot_chart_class;
-static const struct chart_class dnp_plot_chart_class;
-
-static struct chart *
-make_np_plot (const struct chart_class *class,
-              const struct np *np, const struct casereader *reader,
-              const char *label)
+static struct chart_item *
+make_np_plot (const struct np *np, const struct casereader *reader,
+              const char *label, bool detrended)
 {
   struct np_plot_chart *npp;
 
@@ -66,13 +37,13 @@ make_np_plot (const struct chart_class *class,
     return NULL;
 
   npp = xmalloc (sizeof *npp);
-  chart_init (&npp->chart, class);
-  npp->label = xstrdup (label);
+  chart_item_init (&npp->chart_item, &np_plot_chart_class, label);
   npp->data = casereader_clone (reader);
   npp->y_min = np->y_min;
   npp->y_max = np->y_max;
   npp->dns_min = np->dns_min;
   npp->dns_max = np->dns_max;
+  npp->detrended = detrended;
 
   /* Slope and intercept of the ideal normal probability line. */
   npp->slope = 1.0 / np->stddev;
@@ -87,7 +58,7 @@ make_np_plot (const struct chart_class *class,
   npp->x_upper = MAX (np->y_max, (npp->y_last  - npp->intercept) / npp->slope);
   npp->slack = (npp->x_upper - npp->x_lower) * 0.05;
 
-  return &npp->chart;
+  return &npp->chart_item;
 }
 
 /* Creates and returns a normal probability plot corresponding to
@@ -98,11 +69,11 @@ make_np_plot (const struct chart_class *class,
    Returns a null pointer if the data set is empty.
 
    The caller retains ownership of NP and READER. */
-struct chart *
+struct chart_item *
 np_plot_create (const struct np *np, const struct casereader *reader,
                 const char *label)
 {
-  return make_np_plot (&np_plot_chart_class, np, reader, label);
+  return make_np_plot (np, reader, label, false);
 }
 
 /* Creates and returns a detrended normal probability plot
@@ -114,83 +85,23 @@ np_plot_create (const struct np *np, const struct casereader *reader,
    Returns a null pointer if the data set is empty.
 
    The caller retains ownership of NP and READER. */
-struct chart *
+struct chart_item *
 dnp_plot_create (const struct np *np, const struct casereader *reader,
                  const char *label)
 {
-  return make_np_plot (&dnp_plot_chart_class, np, reader, label);
-}
-
-static void
-np_plot_chart_draw (const struct chart *chart, cairo_t *cr,
-                    struct chart_geometry *geom)
-{
-  const struct np_plot_chart *npp = UP_CAST (chart, struct np_plot_chart,
-                                             chart);
-  struct casereader *data;
-  struct ccase *c;
-
-  chart_write_title (cr, geom, _("Normal Q-Q Plot of %s"), npp->label);
-  chart_write_xlabel (cr, geom, _("Observed Value"));
-  chart_write_ylabel (cr, geom, _("Expected Normal"));
-  chart_write_xscale (cr, geom,
-                      npp->x_lower - npp->slack,
-                      npp->x_upper + npp->slack, 5);
-  chart_write_yscale (cr, geom, npp->y_first, npp->y_last, 5);
-
-  data = casereader_clone (npp->data);
-  for (; (c = casereader_read (data)) != NULL; case_unref (c))
-    chart_datum (cr, geom, 0,
-                 case_data_idx (c, NP_IDX_Y)->f,
-                 case_data_idx (c, NP_IDX_NS)->f);
-  casereader_destroy (data);
-
-  chart_line (cr, geom, npp->slope, npp->intercept,
-              npp->y_first, npp->y_last, CHART_DIM_Y);
+  return make_np_plot (np, reader, label, true);
 }
 
 static void
-dnp_plot_chart_draw (const struct chart *chart, cairo_t *cr,
-                     struct chart_geometry *geom)
+np_plot_chart_destroy (struct chart_item *chart_item)
 {
-  const struct np_plot_chart *dnpp = UP_CAST (chart, struct np_plot_chart,
-                                              chart);
-  struct casereader *data;
-  struct ccase *c;
-
-  chart_write_title (cr, geom, _("Detrended Normal Q-Q Plot of %s"),
-                     dnpp->label);
-  chart_write_xlabel (cr, geom, _("Observed Value"));
-  chart_write_ylabel (cr, geom, _("Dev from Normal"));
-  chart_write_xscale (cr, geom, dnpp->y_min, dnpp->y_max, 5);
-  chart_write_yscale (cr, geom, dnpp->dns_min, dnpp->dns_max, 5);
-
-  data = casereader_clone (dnpp->data);
-  for (; (c = casereader_read (data)) != NULL; case_unref (c))
-    chart_datum (cr, geom, 0, case_data_idx (c, NP_IDX_Y)->f,
-                 case_data_idx (c, NP_IDX_DNS)->f);
-  casereader_destroy (data);
-
-  chart_line (cr, geom, 0, 0, dnpp->y_min, dnpp->y_max, CHART_DIM_X);
-}
-
-static void
-np_plot_chart_destroy (struct chart *chart)
-{
-  struct np_plot_chart *npp = UP_CAST (chart, struct np_plot_chart, chart);
+  struct np_plot_chart *npp = to_np_plot_chart (chart_item);
   casereader_destroy (npp->data);
   free (npp->label);
   free (npp);
 }
 
-static const struct chart_class np_plot_chart_class =
-  {
-    np_plot_chart_draw,
-    np_plot_chart_destroy
-  };
-
-static const struct chart_class dnp_plot_chart_class =
+const struct chart_item_class np_plot_chart_class =
   {
-    dnp_plot_chart_draw,
     np_plot_chart_destroy
   };
index c9742359e9091f1ae15900a1c98571f63c5f21c8..82194e2016bd984668b303267751bd8b99365c86 100644 (file)
 #ifndef OUTPUT_CHARTS_NP_PLOT_H
 #define OUTPUT_CHARTS_NP_PLOT_H 1
 
-struct casereader;
+#include <output/chart-item.h>
+
 struct np;
 
-struct chart *np_plot_create (const struct np *, const struct casereader *,
-                              const char *label);
-struct chart *dnp_plot_create (const struct np *, const struct casereader *,
-                               const char *label);
+/* An NP or DNP plot. */
+struct np_plot_chart
+  {
+    struct chart_item chart_item;
+    char *label;
+    struct casereader *data;
+    bool detrended;
+
+    /* Copied directly from struct np. */
+    double y_min, y_max;
+    double dns_min, dns_max;
+
+    /* Calculated. */
+    double slope, intercept;
+    double y_first, y_last;
+    double x_lower, x_upper;
+    double slack;
+  };
+
+struct chart_item *np_plot_create (const struct np *,
+                                   const struct casereader *,
+                                   const char *label);
+struct chart_item *dnp_plot_create (const struct np *,
+                                    const struct casereader *,
+                                    const char *label);
+\f
+/* This boilerplate for np_plot_chart, a subclass of chart_item, was
+   autogenerated by mk-class-boilerplate. */
+
+#include <assert.h>
+#include <libpspp/cast.h>
+
+extern const struct chart_item_class np_plot_chart_class;
+
+/* Returns true if SUPER is a np_plot_chart, otherwise false. */
+static inline bool
+is_np_plot_chart (const struct chart_item *super)
+{
+  return super->class == &np_plot_chart_class;
+}
+
+/* Returns SUPER converted to np_plot_chart.  SUPER must be a np_plot_chart, as
+   reported by is_np_plot_chart. */
+static inline struct np_plot_chart *
+to_np_plot_chart (const struct chart_item *super)
+{
+  assert (is_np_plot_chart (super));
+  return UP_CAST (super, struct np_plot_chart, chart_item);
+}
+
+/* Returns INSTANCE converted to chart_item. */
+static inline struct chart_item *
+np_plot_chart_super (const struct np_plot_chart *instance)
+{
+  return CONST_CAST (struct chart_item *, &instance->chart_item);
+}
+
+/* Increments INSTANCE's reference count and returns INSTANCE. */
+static inline struct np_plot_chart *
+np_plot_chart_ref (const struct np_plot_chart *instance)
+{
+  return to_np_plot_chart (chart_item_ref (&instance->chart_item));
+}
+
+/* Decrements INSTANCE's reference count, then destroys INSTANCE if
+   the reference count is now zero. */
+static inline void
+np_plot_chart_unref (struct np_plot_chart *instance)
+{
+  chart_item_unref (&instance->chart_item);
+}
+
+/* Returns true if INSTANCE's reference count is greater than 1,
+   false otherwise. */
+static inline bool
+np_plot_chart_is_shared (const struct np_plot_chart *instance)
+{
+  return chart_item_is_shared (&instance->chart_item);
+}
 
+static inline void
+np_plot_chart_submit (struct np_plot_chart *instance)
+{
+  chart_item_submit (&instance->chart_item);
+}
+\f
 #endif /* output/charts/np-plot.h */
diff --git a/src/output/charts/piechart-cairo.c b/src/output/charts/piechart-cairo.c
new file mode 100644 (file)
index 0000000..501c0bd
--- /dev/null
@@ -0,0 +1,123 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2009 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 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.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+
+#include <output/charts/piechart.h>
+
+#include <math.h>
+
+#include <output/cairo-chart.h>
+
+#include "gl/minmax.h"
+
+/* Draw a single slice of the pie */
+static void
+draw_segment(cairo_t *cr,
+            double x0, double y0,
+            double radius,
+            double start_angle, double segment_angle,
+            const struct xrchart_colour *colour)
+{
+  cairo_move_to (cr, x0, y0);
+  cairo_arc (cr, x0, y0, radius, start_angle, start_angle + segment_angle);
+  cairo_line_to (cr, x0, y0);
+  cairo_save (cr);
+  cairo_set_source_rgb (cr,
+                        colour->red / 255.0,
+                        colour->green / 255.0,
+                        colour->blue / 255.0);
+  cairo_fill_preserve (cr);
+  cairo_restore (cr);
+  cairo_stroke (cr);
+}
+
+void
+xrchart_draw_piechart (const struct chart_item *chart_item, cairo_t *cr,
+                       struct xrchart_geometry *geom)
+{
+  const struct piechart *pie = to_piechart (chart_item);
+  double total_magnitude;
+  double left_label, right_label;
+  double centre_x, centre_y;
+  double radius;
+  double angle;
+  int i;
+
+  centre_x = (geom->data_right + geom->data_left) / 2.0 ;
+  centre_y = (geom->data_top + geom->data_bottom) / 2.0 ;
+
+  left_label = geom->data_left + (geom->data_right - geom->data_left)/10.0;
+  right_label = geom->data_right - (geom->data_right - geom->data_left)/10.0;
+
+  radius = MIN (5.0 / 12.0 * (geom->data_top - geom->data_bottom),
+                1.0 / 4.0 * (geom->data_right - geom->data_left));
+
+  radius = MIN (5.0 / 12.0 * (geom->data_top - geom->data_bottom),
+                1.0 / 4.0 * (geom->data_right - geom->data_left));
+
+  xrchart_write_title (cr, geom, "%s", chart_item_get_title (chart_item));
+
+  total_magnitude = 0.0;
+  for (i = 0; i < pie->n_slices; i++)
+    total_magnitude += pie->slices[i].magnitude;
+
+  angle = 0.0;
+  for (i = 0; i < pie->n_slices ; ++i )
+    {
+      const double segment_angle =
+       pie->slices[i].magnitude / total_magnitude * 2 * M_PI ;
+
+      const double label_x = centre_x -
+       radius * sin(angle + segment_angle/2.0);
+
+      const double label_y = centre_y +
+       radius * cos(angle + segment_angle/2.0);
+
+      /* Fill the segment */
+      draw_segment (cr,
+                    centre_x, centre_y, radius,
+                    angle, segment_angle,
+                    &data_colour[i % XRCHART_N_COLOURS]);
+
+      /* Now add the labels */
+      if ( label_x < centre_x )
+       {
+          cairo_move_to (cr, label_x, label_y);
+          cairo_line_to (cr, left_label, label_y);
+          cairo_stroke (cr);
+         cairo_move_to (cr, left_label, label_y + 5);
+         xrchart_label (cr, 'l', 'x', geom->font_size,
+                         ds_cstr (&pie->slices[i].label));
+       }
+      else
+       {
+         cairo_move_to (cr, label_x, label_y);
+          cairo_line_to (cr, right_label, label_y);
+          cairo_stroke (cr);
+         cairo_move_to (cr, right_label, label_y + 5);
+         xrchart_label (cr, 'r', 'x', geom->font_size,
+                         ds_cstr (&pie->slices[i].label));
+       }
+
+      angle += segment_angle;
+    }
+
+  /* Draw an outline to the pie */
+  cairo_arc (cr, centre_x, centre_y, radius, 0, 2 * M_PI);
+  cairo_stroke (cr);
+}
+
index 935c6eb0fed9784024c54e571e53936e07c09c7b..0b9153d545b76d26340532a22b9e218bb29f15c6 100644 (file)
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
 
-
 #include <config.h>
 
 #include <output/charts/piechart.h>
 
-#include <assert.h>
-#include <float.h>
-#include <gsl/gsl_math.h>
-#include <math.h>
-#include <stdio.h>
+#include <stdlib.h>
 
-#include <data/value-labels.h>
 #include <libpspp/cast.h>
 #include <libpspp/str.h>
-#include <output/charts/plot-chart.h>
-#include <output/chart-provider.h>
-
-#include "minmax.h"
-
-struct piechart
-  {
-    struct chart chart;
-    char *title;
-    struct slice *slices;
-    int n_slices;
-  };
-
-static const struct chart_class piechart_class;
-
-/* Draw a single slice of the pie */
-static void
-draw_segment(cairo_t *,
-            double centre_x, double centre_y,
-            double radius,
-            double start_angle, double segment_angle,
-            const struct chart_colour *) ;
-
+#include <output/chart-item-provider.h>
 
+#include "gl/xalloc.h"
 
 /* Creates and returns a chart that will render a piechart with
    the given TITLE and the N_SLICES described in SLICES. */
-struct chart *
+struct chart_item *
 piechart_create (const char *title, const struct slice *slices, int n_slices)
 {
   struct piechart *pie;
   int i;
 
   pie = xmalloc (sizeof *pie);
-  chart_init (&pie->chart, &piechart_class);
-  pie->title = xstrdup (title);
+  chart_item_init (&pie->chart_item, &piechart_class, title);
   pie->slices = xnmalloc (n_slices, sizeof *pie->slices);
   for (i = 0; i < n_slices; i++)
     {
@@ -74,113 +46,15 @@ piechart_create (const char *title, const struct slice *slices, int n_slices)
       dst->magnitude = src->magnitude;
     }
   pie->n_slices = n_slices;
-  return &pie->chart;
-}
-
-static void
-piechart_draw (const struct chart *chart, cairo_t *cr,
-               struct chart_geometry *geom)
-{
-  const struct piechart *pie = UP_CAST (chart, struct piechart, chart);
-  double total_magnitude;
-  double left_label, right_label;
-  double centre_x, centre_y;
-  double radius;
-  double angle;
-  int i;
-
-  centre_x = (geom->data_right + geom->data_left) / 2.0 ;
-  centre_y = (geom->data_top + geom->data_bottom) / 2.0 ;
-
-  left_label = geom->data_left + (geom->data_right - geom->data_left)/10.0;
-  right_label = geom->data_right - (geom->data_right - geom->data_left)/10.0;
-
-  radius = MIN (5.0 / 12.0 * (geom->data_top - geom->data_bottom),
-                1.0 / 4.0 * (geom->data_right - geom->data_left));
-
-  radius = MIN (5.0 / 12.0 * (geom->data_top - geom->data_bottom),
-                1.0 / 4.0 * (geom->data_right - geom->data_left));
-
-  chart_write_title (cr, geom, "%s", pie->title);
-
-  total_magnitude = 0.0;
-  for (i = 0; i < pie->n_slices; i++)
-    total_magnitude += pie->slices[i].magnitude;
-
-  angle = 0.0;
-  for (i = 0; i < pie->n_slices ; ++i )
-    {
-      const double segment_angle =
-       pie->slices[i].magnitude / total_magnitude * 2 * M_PI ;
-
-      const double label_x = centre_x -
-       radius * sin(angle + segment_angle/2.0);
-
-      const double label_y = centre_y +
-       radius * cos(angle + segment_angle/2.0);
-
-      /* Fill the segment */
-      draw_segment (cr,
-                    centre_x, centre_y, radius,
-                    angle, segment_angle,
-                    &data_colour[i % N_CHART_COLOURS]);
-
-      /* Now add the labels */
-      if ( label_x < centre_x )
-       {
-          cairo_move_to (cr, label_x, label_y);
-          cairo_line_to (cr, left_label, label_y);
-          cairo_stroke (cr);
-         cairo_move_to (cr, left_label, label_y + 5);
-         chart_label (cr, 'l', 'x', geom->font_size,
-                       ds_cstr (&pie->slices[i].label));
-       }
-      else
-       {
-         cairo_move_to (cr, label_x, label_y);
-          cairo_line_to (cr, right_label, label_y);
-          cairo_stroke (cr);
-         cairo_move_to (cr, right_label, label_y + 5);
-         chart_label (cr, 'r', 'x', geom->font_size,
-                       ds_cstr (&pie->slices[i].label));
-       }
-
-      angle += segment_angle;
-    }
-
-  /* Draw an outline to the pie */
-  cairo_arc (cr, centre_x, centre_y, radius, 0, 2 * M_PI);
-  cairo_stroke (cr);
-}
-
-/* Draw a single slice of the pie */
-static void
-draw_segment(cairo_t *cr,
-            double x0, double y0,
-            double radius,
-            double start_angle, double segment_angle,
-            const struct chart_colour *colour)
-{
-  cairo_move_to (cr, x0, y0);
-  cairo_arc (cr, x0, y0, radius, start_angle, start_angle + segment_angle);
-  cairo_line_to (cr, x0, y0);
-  cairo_save (cr);
-  cairo_set_source_rgb (cr,
-                        colour->red / 255.0,
-                        colour->green / 255.0,
-                        colour->blue / 255.0);
-  cairo_fill_preserve (cr);
-  cairo_restore (cr);
-  cairo_stroke (cr);
+  return &pie->chart_item;
 }
 
 static void
-piechart_destroy (struct chart *chart)
+piechart_destroy (struct chart_item *chart_item)
 {
-  struct piechart *pie = UP_CAST (chart, struct piechart, chart);
+  struct piechart *pie = to_piechart (chart_item);
   int i;
 
-  free (pie->title);
   for (i = 0; i < pie->n_slices; i++)
     {
       struct slice *slice = &pie->slices[i];
@@ -190,8 +64,7 @@ piechart_destroy (struct chart *chart)
   free (pie);
 }
 
-static const struct chart_class piechart_class =
+const struct chart_item_class piechart_class =
   {
-    piechart_draw,
     piechart_destroy
   };
index 39a0c2d5d28e7c05766b93e62a4f0d91b6f4e292..312a45ee08ce759f84e9c02e2991ca9acd3163f0 100644 (file)
 #define PIECHART_H
 
 #include <libpspp/str.h>
+#include <output/chart-item.h>
 
-struct slice {
-  struct string label;
-  double magnitude;
-};
+struct piechart
+  {
+    struct chart_item chart_item;
+    struct slice *slices;
+    int n_slices;
+  };
 
-struct chart *piechart_create (const char *title,
-                               const struct slice *, int n_slices);
+struct slice
+  {
+    struct string label;
+    double magnitude;
+  };
 
-#endif
+struct chart_item *piechart_create (const char *title,
+                                    const struct slice *, int n_slices);
+\f
+/* This boilerplate for piechart, a subclass of chart_item, was
+   autogenerated by mk-class-boilerplate. */
 
+#include <assert.h>
+#include <libpspp/cast.h>
+
+extern const struct chart_item_class piechart_class;
+
+/* Returns true if SUPER is a piechart, otherwise false. */
+static inline bool
+is_piechart (const struct chart_item *super)
+{
+  return super->class == &piechart_class;
+}
+
+/* Returns SUPER converted to piechart.  SUPER must be a piechart, as
+   reported by is_piechart. */
+static inline struct piechart *
+to_piechart (const struct chart_item *super)
+{
+  assert (is_piechart (super));
+  return UP_CAST (super, struct piechart, chart_item);
+}
+
+/* Returns INSTANCE converted to chart_item. */
+static inline struct chart_item *
+piechart_super (const struct piechart *instance)
+{
+  return CONST_CAST (struct chart_item *, &instance->chart_item);
+}
+
+/* Increments INSTANCE's reference count and returns INSTANCE. */
+static inline struct piechart *
+piechart_ref (const struct piechart *instance)
+{
+  return to_piechart (chart_item_ref (&instance->chart_item));
+}
+
+/* Decrements INSTANCE's reference count, then destroys INSTANCE if
+   the reference count is now zero. */
+static inline void
+piechart_unref (struct piechart *instance)
+{
+  chart_item_unref (&instance->chart_item);
+}
+
+/* Returns true if INSTANCE's reference count is greater than 1,
+   false otherwise. */
+static inline bool
+piechart_is_shared (const struct piechart *instance)
+{
+  return chart_item_is_shared (&instance->chart_item);
+}
+
+static inline void
+piechart_submit (struct piechart *instance)
+{
+  chart_item_submit (&instance->chart_item);
+}
+\f
+#endif /* output/charts/piechart.h */
diff --git a/src/output/charts/plot-chart.c b/src/output/charts/plot-chart.c
deleted file mode 100644 (file)
index cda6d1e..0000000
+++ /dev/null
@@ -1,356 +0,0 @@
-/* PSPP - a program for statistical analysis.
-   Copyright (C) 2004, 2009 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 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.
-
-   You should have received a copy of the GNU General Public License
-   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
-
-#include <config.h>
-
-#include <output/charts/plot-chart.h>
-
-#include <assert.h>
-#include <float.h>
-#include <math.h>
-#include <pango/pango-font.h>
-#include <pango/pango-layout.h>
-#include <pango/pango.h>
-#include <pango/pangocairo.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdio.h>
-#include <string.h>
-
-#include <libpspp/assertion.h>
-#include <libpspp/str.h>
-#include <math/chart-geometry.h>
-#include <output/chart-provider.h>
-#include <output/manager.h>
-#include <output/output.h>
-
-#include "xalloc.h"
-
-#if ! PANGO_VERSION_CHECK (2, 22, 0)
-int pango_layout_get_baseline (PangoLayout    *layout);
-
-/* Shamelessly copied from the pango source */
-int
-pango_layout_get_baseline (PangoLayout    *layout)
-{
-  int baseline;
-
-  /* XXX this is so inefficient */
-  PangoLayoutIter *iter = pango_layout_get_iter (layout);
-  baseline = pango_layout_iter_get_baseline (iter);
-  pango_layout_iter_free (iter);
-
-  return baseline;
-}
-#endif
-
-
-
-const struct chart_colour data_colour[N_CHART_COLOURS] =
-  {
-    { 165, 42, 42 },            /* brown */
-    { 255, 0, 0 },              /* red */
-    { 255, 165, 0 },            /* orange */
-    { 255, 255, 0 },            /* yellow */
-    { 0, 255, 0 },              /* green */
-    { 0, 0, 255 },              /* blue */
-    { 238, 130, 238 },          /* violet */
-    { 190, 190, 190 },          /* grey */
-    { 255, 192, 203 },          /* pink */
-  };
-
-void
-chart_draw_marker (cairo_t *cr, double x, double y, enum marker_type marker,
-                   double size)
-{
-  cairo_save (cr);
-  cairo_translate (cr, x, y);
-  cairo_scale (cr, size / 2.0, size / 2.0);
-  cairo_set_line_width (cr, cairo_get_line_width (cr) / (size / 2.0));
-  switch (marker)
-    {
-    case MARKER_CIRCLE:
-      cairo_arc (cr, 0, 0, 1.0, 0, 2 * M_PI);
-      cairo_stroke (cr);
-      break;
-
-    case MARKER_ASTERISK:
-      cairo_move_to (cr, 0, -1.0); /* | */
-      cairo_line_to (cr, 0, 1.0);
-      cairo_move_to (cr, -M_SQRT1_2, -M_SQRT1_2); /* / */
-      cairo_line_to (cr, M_SQRT1_2, M_SQRT1_2);
-      cairo_move_to (cr, -M_SQRT1_2, M_SQRT1_2); /* \ */
-      cairo_line_to (cr, M_SQRT1_2, -M_SQRT1_2);
-      cairo_stroke (cr);
-      break;
-
-    case MARKER_SQUARE:
-      cairo_rectangle (cr, -1.0, -1.0, 2.0, 2.0);
-      cairo_stroke (cr);
-      break;
-    }
-  cairo_restore (cr);
-}
-
-void
-chart_label (cairo_t *cr, int horz_justify, int vert_justify, double font_size,
-             const char *string)
-{
-  PangoFontDescription *desc;
-  PangoLayout *layout;
-  double x, y;
-
-  desc = pango_font_description_from_string ("sans serif");
-  if (desc == NULL)
-    {
-      cairo_new_path (cr);
-      return;
-    }
-  pango_font_description_set_absolute_size (desc, font_size * PANGO_SCALE);
-
-  cairo_save (cr);
-  cairo_get_current_point (cr, &x, &y);
-  cairo_translate (cr, x, y);
-  cairo_move_to (cr, 0, 0);
-  cairo_scale (cr, 1.0, -1.0);
-
-  layout = pango_cairo_create_layout (cr);
-  pango_layout_set_font_description (layout, desc);
-  pango_layout_set_text (layout, string, -1);
-  if (horz_justify != 'l')
-    {
-      int width_pango;
-      double width;
-
-      pango_layout_get_size (layout, &width_pango, NULL);
-      width = (double) width_pango / PANGO_SCALE;
-      if (horz_justify == 'r')
-        cairo_rel_move_to (cr, -width, 0);
-      else
-        cairo_rel_move_to (cr, -width / 2.0, 0);
-    }
-  if (vert_justify == 'x')
-    {
-      int baseline_pango = pango_layout_get_baseline (layout);
-      double baseline = (double) baseline_pango / PANGO_SCALE;
-      cairo_rel_move_to (cr, 0, -baseline);
-    }
-  else if (vert_justify != 't')
-    {
-      int height_pango;
-      double height;
-
-      pango_layout_get_size (layout, NULL, &height_pango);
-      height = (double) height_pango / PANGO_SCALE;
-      if (vert_justify == 'b')
-        cairo_rel_move_to (cr, 0, -height);
-      else if (vert_justify == 'c')
-        cairo_rel_move_to (cr, 0, -height / 2.0);
-    }
-  pango_cairo_show_layout (cr, layout);
-  g_object_unref (layout);
-
-  cairo_restore (cr);
-
-  cairo_new_path (cr);
-
-  pango_font_description_free (desc);
-}
-
-/* Draw a tick mark at position
-   If label is non zero, then print it at the tick mark
-*/
-void
-draw_tick (cairo_t *cr, const struct chart_geometry *geom,
-           enum tick_orientation orientation,
-           double position,
-           const char *label, ...)
-{
-  const int tickSize = 10;
-  double x, y;
-
-  cairo_move_to (cr, geom->data_left, geom->data_bottom);
-
-  if (orientation == TICK_ABSCISSA)
-    {
-      cairo_rel_move_to (cr, position, 0);
-      cairo_rel_line_to (cr, 0, -tickSize);
-    }
-  else if (orientation == TICK_ORDINATE)
-    {
-      cairo_rel_move_to (cr, 0, position);
-      cairo_rel_line_to (cr, -tickSize, 0);
-    }
-  else
-    NOT_REACHED ();
-  cairo_get_current_point (cr, &x, &y);
-
-  cairo_stroke (cr);
-
-  if (label != NULL)
-    {
-      va_list ap;
-      char *s;
-
-      cairo_move_to (cr, x, y);
-
-      va_start (ap, label);
-      s = xvasprintf (label, ap);
-      if (orientation == TICK_ABSCISSA)
-        chart_label (cr, 'c', 't', geom->font_size, s);
-      else if (orientation == TICK_ORDINATE)
-        {
-          if (fabs (position) < DBL_EPSILON)
-           cairo_rel_move_to (cr, 0, 10);
-          chart_label (cr, 'r', 'c', geom->font_size, s);
-        }
-      free (s);
-      va_end (ap);
-    }
-}
-
-
-/* Write the title on a chart*/
-void
-chart_write_title (cairo_t *cr, const struct chart_geometry *geom,
-                   const char *title, ...)
-{
-  va_list ap;
-  char *s;
-
-  cairo_save (cr);
-  cairo_move_to (cr, geom->data_left, geom->title_bottom);
-
-  va_start(ap, title);
-  s = xvasprintf (title, ap);
-  chart_label (cr, 'l', 'x', geom->font_size * 1.5, s);
-  free (s);
-  va_end (ap);
-
-  cairo_restore (cr);
-}
-
-
-/* Set the scale for the abscissa */
-void
-chart_write_xscale (cairo_t *cr, struct chart_geometry *geom,
-                    double min, double max, int ticks)
-{
-  double x;
-
-  const double tick_interval =
-    chart_rounded_tick ((max - min) / (double) ticks);
-
-  geom->x_max = ceil (max / tick_interval) * tick_interval;
-  geom->x_min = floor (min / tick_interval) * tick_interval;
-  geom->abscissa_scale = fabs(geom->data_right - geom->data_left) /
-    fabs(geom->x_max - geom->x_min);
-
-  for (x = geom->x_min; x <= geom->x_max; x += tick_interval)
-    draw_tick (cr, geom, TICK_ABSCISSA,
-               (x - geom->x_min) * geom->abscissa_scale, "%g", x);
-}
-
-
-/* Set the scale for the ordinate */
-void
-chart_write_yscale (cairo_t *cr, struct chart_geometry *geom,
-                    double smin, double smax, int ticks)
-{
-  double y;
-
-  const double tick_interval =
-    chart_rounded_tick ((smax - smin) / (double) ticks);
-
-  geom->y_max = ceil (smax / tick_interval) * tick_interval;
-  geom->y_min = floor (smin / tick_interval) * tick_interval;
-
-  geom->ordinate_scale =
-    (fabs (geom->data_top - geom->data_bottom)
-     / fabs (geom->y_max - geom->y_min));
-
-  for (y = geom->y_min; y <= geom->y_max; y += tick_interval)
-    draw_tick (cr, geom, TICK_ORDINATE,
-              (y - geom->y_min) * geom->ordinate_scale, "%g", y);
-}
-
-/* Write the abscissa label */
-void
-chart_write_xlabel (cairo_t *cr, const struct chart_geometry *geom,
-                    const char *label)
-{
-  cairo_move_to (cr, geom->data_left, geom->abscissa_top);
-  chart_label (cr, 'l', 't', geom->font_size, label);
-}
-
-/* Write the ordinate label */
-void
-chart_write_ylabel (cairo_t *cr, const struct chart_geometry *geom,
-                    const char *label)
-{
-  cairo_save (cr);
-  cairo_translate (cr, -geom->data_bottom, -geom->ordinate_right);
-  cairo_move_to (cr, 0, 0);
-  cairo_rotate (cr, M_PI / 2.0);
-  chart_label (cr, 'l', 'x', geom->font_size, label);
-  cairo_restore (cr);
-}
-
-
-void
-chart_write_legend (cairo_t *cr, const struct chart_geometry *geom)
-{
-  int i;
-  const int vstep = geom->font_size * 2;
-  const int xpad = 10;
-  const int ypad = 10;
-  const int swatch = 20;
-  const int legend_top = geom->data_top;
-  const int legend_bottom = legend_top -
-    (vstep * geom->n_datasets + 2 * ypad );
-
-  cairo_save (cr);
-
-  cairo_rectangle (cr, geom->legend_left, legend_top,
-                   geom->legend_right - xpad - geom->legend_left,
-                   legend_bottom - legend_top);
-  cairo_stroke (cr);
-
-  for (i = 0 ; i < geom->n_datasets ; ++i )
-    {
-      const int ypos = legend_top - vstep * (i + 1);
-      const int xpos = geom->legend_left + xpad;
-      const struct chart_colour *colour;
-
-      cairo_move_to (cr, xpos, ypos);
-
-      cairo_save (cr);
-      colour = &data_colour [ i % N_CHART_COLOURS];
-      cairo_set_source_rgb (cr,
-                            colour->red / 255.0,
-                            colour->green / 255.0,
-                            colour->blue / 255.0);
-      cairo_rectangle (cr, xpos, ypos, swatch, swatch);
-      cairo_fill_preserve (cr);
-      cairo_stroke (cr);
-      cairo_restore (cr);
-
-      cairo_move_to (cr, xpos + swatch * 1.5, ypos);
-      chart_label (cr, 'l', 'x', geom->font_size, geom->dataset[i]);
-    }
-
-  cairo_restore (cr);
-}
diff --git a/src/output/charts/plot-chart.h b/src/output/charts/plot-chart.h
deleted file mode 100644 (file)
index 896b630..0000000
+++ /dev/null
@@ -1,98 +0,0 @@
-/* PSPP - a program for statistical analysis.
-   Copyright (C) 2004, 2009 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 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.
-
-   You should have received a copy of the GNU General Public License
-   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
-
-#ifndef PLOT_CHART_H
-#define PLOT_CHART_H
-
-#include <cairo/cairo.h>
-#include <stdio.h>
-#include <stdarg.h>
-#include <string.h>
-#include <stdio.h>
-#include <float.h>
-#include <assert.h>
-#include <math.h>
-
-
-#include <math/chart-geometry.h>
-#include <output/chart.h>
-#include <output/chart-provider.h>
-
-#include <libpspp/compiler.h>
-#include <libpspp/str.h>
-#include <output/manager.h>
-#include <output/output.h>
-
-#define N_CHART_COLOURS 9
-extern const struct chart_colour data_colour[];
-
-enum tick_orientation
-  {
-    TICK_ABSCISSA=0,
-    TICK_ORDINATE
-  };
-
-struct chart_geometry;
-
-
-enum marker_type
-  {
-    MARKER_CIRCLE,              /* Hollow circle. */
-    MARKER_ASTERISK,            /* Asterisk (*). */
-    MARKER_SQUARE               /* Hollow square. */
-  };
-
-void chart_draw_marker (cairo_t *, double x, double y, enum marker_type,
-                        double size);
-
-void chart_label (cairo_t *, int horz_justify, int vert_justify,
-                  double font_size, const char *);
-
-/* Draw a tick mark at position
-   If label is non zero, then print it at the tick mark
-*/
-void draw_tick(cairo_t *, const struct chart_geometry *,
-         enum tick_orientation orientation,
-         double position,
-              const char *label, ...)
-  PRINTF_FORMAT (5, 6);
-
-
-/* Write the title on a chart*/
-void   chart_write_title(cairo_t *, const struct chart_geometry *,
-                         const char *title, ...)
-  PRINTF_FORMAT (3, 4);
-
-
-/* Set the scale for the abscissa */
-void  chart_write_xscale(cairo_t *, struct chart_geometry *,
-                         double min, double max, int ticks);
-
-
-/* Set the scale for the ordinate */
-void  chart_write_yscale(cairo_t *, struct chart_geometry *,
-                         double smin, double smax, int ticks);
-
-void chart_write_xlabel(cairo_t *, const struct chart_geometry *,
-                        const char *label) ;
-
-/* Write the ordinate label */
-void  chart_write_ylabel(cairo_t *, const struct chart_geometry *,
-                         const char *label);
-
-void chart_write_legend (cairo_t *, const struct chart_geometry *);
-
-#endif
diff --git a/src/output/charts/plot-hist-cairo.c b/src/output/charts/plot-hist-cairo.c
new file mode 100644 (file)
index 0000000..a70fa17
--- /dev/null
@@ -0,0 +1,162 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2009 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 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.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+
+#include <output/charts/plot-hist.h>
+
+#include <gsl/gsl_randist.h>
+
+#include <data/val-type.h>
+#include <output/cairo-chart.h>
+
+#include "gl/xvasprintf.h"
+
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
+
+/* Write the legend of the chart */
+static void
+histogram_write_legend (cairo_t *cr, const struct xrchart_geometry *geom,
+                        double n, double mean, double stddev)
+{
+  double y = geom->data_bottom;
+  cairo_save (cr);
+
+  if (n != SYSMIS)
+    {
+      char *buf = xasprintf ("N = %.2f", n);
+      cairo_move_to (cr, geom->legend_left, y);
+      xrchart_label (cr, 'l', 'b', geom->font_size, buf);
+      y += geom->font_size * 1.5;
+      free (buf);
+    }
+
+  if (mean != SYSMIS)
+    {
+      char *buf = xasprintf ("Mean = %.1f", mean);
+      cairo_move_to (cr,geom->legend_left, y);
+      xrchart_label (cr, 'l', 'b', geom->font_size, buf);
+      y += geom->font_size * 1.5;
+      free (buf);
+    }
+
+  if (stddev != SYSMIS)
+    {
+      char *buf = xasprintf ("Std. Dev = %.2f", stddev);
+      cairo_move_to (cr, geom->legend_left, y);
+      xrchart_label (cr, 'l', 'b', geom->font_size, buf);
+      free (buf);
+    }
+
+  cairo_restore (cr);
+}
+
+static void
+hist_draw_bar (cairo_t *cr, const struct xrchart_geometry *geom,
+               const gsl_histogram *h, int bar)
+{
+  double upper;
+  double lower;
+  double height;
+
+  const size_t bins = gsl_histogram_bins (h);
+  const double x_pos = (geom->data_right - geom->data_left) * bar / (double) bins ;
+  const double width = (geom->data_right - geom->data_left) / (double) bins ;
+
+  assert ( 0 == gsl_histogram_get_range (h, bar, &lower, &upper));
+
+  assert ( upper >= lower);
+
+  height = gsl_histogram_get (h, bar) *
+    (geom->data_top - geom->data_bottom) / gsl_histogram_max_val (h);
+
+  cairo_rectangle (cr, geom->data_left + x_pos, geom->data_bottom,
+                   width, height);
+  cairo_save (cr);
+  cairo_set_source_rgb (cr,
+                        geom->fill_colour.red / 255.0,
+                        geom->fill_colour.green / 255.0,
+                        geom->fill_colour.blue / 255.0);
+  cairo_fill_preserve (cr);
+  cairo_restore (cr);
+  cairo_stroke (cr);
+
+  draw_tick (cr, geom, TICK_ABSCISSA,
+             x_pos + width / 2.0, "%g", (upper + lower) / 2.0);
+}
+
+void
+xrchart_draw_histogram (const struct chart_item *chart_item, cairo_t *cr,
+                        struct xrchart_geometry *geom)
+{
+  struct histogram_chart *h = to_histogram_chart (chart_item);
+  int i;
+  int bins;
+
+  xrchart_write_title (cr, geom, _("HISTOGRAM"));
+
+  xrchart_write_ylabel (cr, geom, _("Frequency"));
+  xrchart_write_xlabel (cr, geom, chart_item_get_title (chart_item));
+
+  if (h->gsl_hist == NULL)
+    {
+      /* Probably all values are SYSMIS. */
+      return;
+    }
+
+  bins = gsl_histogram_bins (h->gsl_hist);
+
+  xrchart_write_yscale (cr, geom, 0, gsl_histogram_max_val (h->gsl_hist), 5);
+
+  for (i = 0; i < bins; i++)
+    hist_draw_bar (cr, geom, h->gsl_hist, i);
+
+  histogram_write_legend (cr, geom, h->n, h->mean, h->stddev);
+
+  if (h->show_normal
+      && h->n != SYSMIS && h->mean != SYSMIS && h->stddev != SYSMIS)
+    {
+      /* Draw the normal curve */
+      double d;
+      double x_min, x_max, not_used;
+      double abscissa_scale;
+      double ordinate_scale;
+      double range;
+
+      gsl_histogram_get_range (h->gsl_hist, 0, &x_min, &not_used);
+      range = not_used - x_min;
+      gsl_histogram_get_range (h->gsl_hist, bins - 1, &not_used, &x_max);
+
+      abscissa_scale = (geom->data_right - geom->data_left) / (x_max - x_min);
+      ordinate_scale = (geom->data_top - geom->data_bottom) /
+       gsl_histogram_max_val (h->gsl_hist);
+
+      cairo_move_to (cr, geom->data_left, geom->data_bottom);
+      for (d = geom->data_left;
+          d <= geom->data_right;
+          d += (geom->data_right - geom->data_left) / 100.0)
+       {
+         const double x = (d - geom->data_left) / abscissa_scale + x_min;
+         const double y = h->n * range *
+           gsl_ran_gaussian_pdf (x - h->mean, h->stddev);
+
+          cairo_line_to (cr, d, geom->data_bottom  + y * ordinate_scale);
+
+       }
+      cairo_stroke (cr);
+    }
+}
index 334818b4cad9428975f8c2e1623ea567ff57ac5c..5925df6d1a35af5d0122f75312b497bc01e68eb8 100644 (file)
 
 #include <stdio.h>
 #include <math.h>
-#include <gsl/gsl_histogram.h>
 #include <gsl/gsl_randist.h>
 #include <assert.h>
 
 #include <output/charts/plot-hist.h>
-#include <output/charts/plot-chart.h>
-#include <output/chart-provider.h>
+#include <output/chart-item-provider.h>
 
-#include <data/variable.h>
 #include <libpspp/cast.h>
-#include <libpspp/hash.h>
-#include <output/chart.h>
 #include <math/histogram.h>
 #include <math/moments.h>
 
 #include "gettext.h"
 #define _(msgid) gettext (msgid)
 
-static const struct chart_class histogram_chart_class;
-
-/* Write the legend of the chart */
-static void
-histogram_write_legend (cairo_t *cr, const struct chart_geometry *geom,
-                        double n, double mean, double stddev)
-{
-  double y = geom->data_bottom;
-  cairo_save (cr);
-
-  if (n != SYSMIS)
-    {
-      char *buf = xasprintf ("N = %.2f", n);
-      cairo_move_to (cr, geom->legend_left, y);
-      chart_label (cr, 'l', 'b', geom->font_size, buf);
-      y += geom->font_size * 1.5;
-      free (buf);
-    }
-
-  if (mean != SYSMIS)
-    {
-      char *buf = xasprintf ("Mean = %.1f", mean);
-      cairo_move_to (cr,geom->legend_left, y);
-      chart_label (cr, 'l', 'b', geom->font_size, buf);
-      y += geom->font_size * 1.5;
-      free (buf);
-    }
-
-  if (stddev != SYSMIS)
-    {
-      char *buf = xasprintf ("Std. Dev = %.2f", stddev);
-      cairo_move_to (cr, geom->legend_left, y);
-      chart_label (cr, 'l', 'b', geom->font_size, buf);
-      free (buf);
-    }
-
-  cairo_restore (cr);
-}
-
-static void
-hist_draw_bar (cairo_t *cr, const struct chart_geometry *geom,
-               const gsl_histogram *h, int bar)
-{
-  double upper;
-  double lower;
-  double height;
-
-  const size_t bins = gsl_histogram_bins (h);
-  const double x_pos = (geom->data_right - geom->data_left) * bar / (double) bins ;
-  const double width = (geom->data_right - geom->data_left) / (double) bins ;
-
-  assert ( 0 == gsl_histogram_get_range (h, bar, &lower, &upper));
-
-  assert ( upper >= lower);
-
-  height = gsl_histogram_get (h, bar) *
-    (geom->data_top - geom->data_bottom) / gsl_histogram_max_val (h);
-
-  cairo_rectangle (cr, geom->data_left + x_pos, geom->data_bottom,
-                   width, height);
-  cairo_save (cr);
-  cairo_set_source_rgb (cr,
-                        geom->fill_colour.red / 255.0,
-                        geom->fill_colour.green / 255.0,
-                        geom->fill_colour.blue / 255.0);
-  cairo_fill_preserve (cr);
-  cairo_restore (cr);
-  cairo_stroke (cr);
-
-  draw_tick (cr, geom, TICK_ABSCISSA,
-             x_pos + width / 2.0, "%g", (upper + lower) / 2.0);
-}
-
-struct histogram_chart
-  {
-    struct chart chart;
-    gsl_histogram *gsl_hist;
-    char *label;
-    double n;
-    double mean;
-    double stddev;
-    bool show_normal;
-  };
-
 /* Plots a histogram of the data in HIST with the given LABEL.
    Labels the histogram with each of N, MEAN, and STDDEV that is
    not SYSMIS.  If all three are not SYSMIS and SHOW_NORMAL is
    true, also draws a normal curve on the histogram. */
-struct chart *
-histogram_chart_create (const struct histogram *hist, const char *label,
+struct chart_item *
+histogram_chart_create (const gsl_histogram *hist, const char *label,
                         double n, double mean, double stddev,
                         bool show_normal)
 {
   struct histogram_chart *h;
 
   h = xmalloc (sizeof *h);
-  chart_init (&h->chart, &histogram_chart_class);
-  h->gsl_hist = hist->gsl_hist ? gsl_histogram_clone (hist->gsl_hist) : NULL;
-  h->label = xstrdup (label);
+  chart_item_init (&h->chart_item, &histogram_chart_class, label);
+  h->gsl_hist = hist != NULL ? gsl_histogram_clone (hist) : NULL;
   h->n = n;
   h->mean = mean;
   h->stddev = stddev;
   h->show_normal = show_normal;
-  return &h->chart;
-}
-
-static void
-histogram_chart_draw (const struct chart *chart, cairo_t *cr,
-                      struct chart_geometry *geom)
-{
-  struct histogram_chart *h = UP_CAST (chart, struct histogram_chart, chart);
-  int i;
-  int bins;
-
-  chart_write_title (cr, geom, _("HISTOGRAM"));
-
-  chart_write_ylabel (cr, geom, _("Frequency"));
-  chart_write_xlabel (cr, geom, h->label);
-
-  if (h->gsl_hist == NULL)
-    {
-      /* Probably all values are SYSMIS. */
-      return;
-    }
-
-  bins = gsl_histogram_bins (h->gsl_hist);
-
-  chart_write_yscale (cr, geom, 0, gsl_histogram_max_val (h->gsl_hist), 5);
-
-  for (i = 0; i < bins; i++)
-    hist_draw_bar (cr, geom, h->gsl_hist, i);
-
-  histogram_write_legend (cr, geom, h->n, h->mean, h->stddev);
-
-  if (h->show_normal
-      && h->n != SYSMIS && h->mean != SYSMIS && h->stddev != SYSMIS)
-    {
-      /* Draw the normal curve */
-      double d;
-      double x_min, x_max, not_used;
-      double abscissa_scale;
-      double ordinate_scale;
-      double range;
-
-      gsl_histogram_get_range (h->gsl_hist, 0, &x_min, &not_used);
-      range = not_used - x_min;
-      gsl_histogram_get_range (h->gsl_hist, bins - 1, &not_used, &x_max);
-
-      abscissa_scale = (geom->data_right - geom->data_left) / (x_max - x_min);
-      ordinate_scale = (geom->data_top - geom->data_bottom) /
-       gsl_histogram_max_val (h->gsl_hist);
-
-      cairo_move_to (cr, geom->data_left, geom->data_bottom);
-      for (d = geom->data_left;
-          d <= geom->data_right;
-          d += (geom->data_right - geom->data_left) / 100.0)
-       {
-         const double x = (d - geom->data_left) / abscissa_scale + x_min;
-         const double y = h->n * range *
-           gsl_ran_gaussian_pdf (x - h->mean, h->stddev);
-
-          cairo_line_to (cr, d, geom->data_bottom  + y * ordinate_scale);
-
-       }
-      cairo_stroke (cr);
-    }
+  return &h->chart_item;
 }
 
-
 static void
-histogram_chart_destroy (struct chart *chart)
+histogram_chart_destroy (struct chart_item *chart_item)
 {
-  struct histogram_chart *h = UP_CAST (chart, struct histogram_chart, chart);
+  struct histogram_chart *h = UP_CAST (chart_item, struct histogram_chart,
+                                       chart_item);
   if (h->gsl_hist != NULL)
     gsl_histogram_free (h->gsl_hist);
-  free (h->label);
   free (h);
 }
 
-static const struct chart_class histogram_chart_class =
+const struct chart_item_class histogram_chart_class =
   {
-    histogram_chart_draw,
     histogram_chart_destroy
   };
index 1e5b59edb5d8458badf707b3f6c21a424afbc3c2..237b8b3c11ef7d2ac612675ef879b456a96cf714 100644 (file)
 #ifndef OUTPUT_PLOT_HIST_H
 #define OUTPUT_PLOT_HIST_H
 
+#include <gsl/gsl_histogram.h>
 #include <stdbool.h>
 
-struct chart;
-struct histogram;
+#include "output/chart-item.h"
+
+struct histogram_chart
+  {
+    struct chart_item chart_item;
+    gsl_histogram *gsl_hist;
+    double n;
+    double mean;
+    double stddev;
+    bool show_normal;
+  };
 
 /* Creates and returns a new chart that depicts a histogram of
    the data in HIST with the given LABEL.  Labels the histogram
    with each of N, MEAN, and STDDEV that is not SYSMIS.  If all
    three are not SYSMIS and SHOW_NORMAL is true, also draws a
    normal curve on the histogram. */
-struct chart *histogram_chart_create (const struct histogram *hist,
-                                      const char *label,
-                                      double n, double mean, double stddev,
-                                      bool show_normal);
+struct chart_item *histogram_chart_create (const gsl_histogram *,
+                                           const char *label, double n,
+                                           double mean, double stddev,
+                                           bool show_normal);
+\f
+/* This boilerplate for histogram_chart, a subclass of chart_item, was
+   autogenerated by mk-class-boilerplate. */
+
+#include <assert.h>
+#include <libpspp/cast.h>
+
+extern const struct chart_item_class histogram_chart_class;
+
+/* Returns true if SUPER is a histogram_chart, otherwise false. */
+static inline bool
+is_histogram_chart (const struct chart_item *super)
+{
+  return super->class == &histogram_chart_class;
+}
+
+/* Returns SUPER converted to histogram_chart.  SUPER must be a histogram_chart, as
+   reported by is_histogram_chart. */
+static inline struct histogram_chart *
+to_histogram_chart (const struct chart_item *super)
+{
+  assert (is_histogram_chart (super));
+  return UP_CAST (super, struct histogram_chart, chart_item);
+}
+
+/* Returns INSTANCE converted to chart_item. */
+static inline struct chart_item *
+histogram_chart_super (const struct histogram_chart *instance)
+{
+  return CONST_CAST (struct chart_item *, &instance->chart_item);
+}
+
+/* Increments INSTANCE's reference count and returns INSTANCE. */
+static inline struct histogram_chart *
+histogram_chart_ref (const struct histogram_chart *instance)
+{
+  return to_histogram_chart (chart_item_ref (&instance->chart_item));
+}
+
+/* Decrements INSTANCE's reference count, then destroys INSTANCE if
+   the reference count is now zero. */
+static inline void
+histogram_chart_unref (struct histogram_chart *instance)
+{
+  chart_item_unref (&instance->chart_item);
+}
+
+/* Returns true if INSTANCE's reference count is greater than 1,
+   false otherwise. */
+static inline bool
+histogram_chart_is_shared (const struct histogram_chart *instance)
+{
+  return chart_item_is_shared (&instance->chart_item);
+}
 
+static inline void
+histogram_chart_submit (struct histogram_chart *instance)
+{
+  chart_item_submit (&instance->chart_item);
+}
+\f
 #endif /* output/plot-hist.h */
diff --git a/src/output/charts/roc-chart-cairo.c b/src/output/charts/roc-chart-cairo.c
new file mode 100644 (file)
index 0000000..b5c22ac
--- /dev/null
@@ -0,0 +1,73 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2009 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 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.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+
+#include <output/charts/roc-chart.h>
+
+#include <data/case.h>
+#include <data/casereader.h>
+#include <language/stats/roc.h>
+#include <output/cairo-chart.h>
+
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
+
+void
+xrchart_draw_roc (const struct chart_item *chart_item, cairo_t *cr,
+                  struct xrchart_geometry *geom)
+{
+  const struct roc_chart *rc = to_roc_chart (chart_item);
+  size_t i;
+
+  xrchart_write_title (cr, geom, _("ROC Curve"));
+  xrchart_write_xlabel (cr, geom, _("1 - Specificity"));
+  xrchart_write_ylabel (cr, geom, _("Sensitivity"));
+
+  xrchart_write_xscale (cr, geom, 0, 1, 5);
+  xrchart_write_yscale (cr, geom, 0, 1, 5);
+
+  if ( rc->reference )
+    {
+      xrchart_line (cr, geom, 1.0, 0,
+                    0.0, 1.0,
+                    XRCHART_DIM_X);
+    }
+
+  for (i = 0; i < rc->n_vars; ++i)
+    {
+      const struct roc_var *rv = &rc->vars[i];
+      struct casereader *r = casereader_clone (rv->cutpoint_reader);
+      struct ccase *cc;
+
+      xrchart_vector_start (cr, geom, rv->name);
+      for (; (cc = casereader_read (r)) != NULL; case_unref (cc))
+       {
+         double se = case_data_idx (cc, ROC_TP)->f;
+         double sp = case_data_idx (cc, ROC_TN)->f;
+
+         se /= case_data_idx (cc, ROC_FN)->f + case_data_idx (cc, ROC_TP)->f ;
+         sp /= case_data_idx (cc, ROC_TN)->f + case_data_idx (cc, ROC_FP)->f ;
+
+         xrchart_vector (cr, geom, 1 - sp, se);
+       }
+      xrchart_vector_end (cr, geom);
+      casereader_destroy (r);
+    }
+
+  xrchart_write_legend (cr, geom);
+}
+
index 2094ede545c79edb0c264a2c63c0630cfef1339f..5af8f27ff35ee93eeb8ede9086ce738e71d24944 100644 (file)
@@ -18,9 +18,7 @@
 
 #include <output/charts/roc-chart.h>
 
-#include <output/chart-provider.h>
-#include <output/charts/cartesian.h>
-#include <output/charts/plot-chart.h>
+#include <output/chart-item-provider.h>
 #include <data/casereader.h>
 #include <language/stats/roc.h>
 
 #include "gettext.h"
 #define _(msgid) gettext (msgid)
 
-struct roc_var
-  {
-    char *name;
-    struct casereader *cutpoint_reader;
-  };
-
-struct roc_chart
-  {
-    struct chart chart;
-    bool reference;
-    struct roc_var *vars;
-    size_t n_vars;
-    size_t allocated_vars;
-  };
-
-static const struct chart_class roc_chart_class;
-
 struct roc_chart *
 roc_chart_create (bool reference)
 {
   struct roc_chart *rc = xmalloc (sizeof *rc);
-  chart_init (&rc->chart, &roc_chart_class);
+  chart_item_init (&rc->chart_item, &roc_chart_class, NULL);
   rc->reference = reference;
   rc->vars = NULL;
   rc->n_vars = 0;
@@ -72,61 +53,10 @@ roc_chart_add_var (struct roc_chart *rc, const char *var_name,
   rv->cutpoint_reader = casereader_clone (cutpoint_reader);
 }
 
-struct chart *
-roc_chart_get_chart (struct roc_chart *rc)
-{
-  return &rc->chart;
-}
-
 static void
-roc_chart_draw (const struct chart *chart, cairo_t *cr,
-                struct chart_geometry *geom)
+roc_chart_destroy (struct chart_item *chart_item)
 {
-  const struct roc_chart *rc = UP_CAST (chart, struct roc_chart, chart);
-  size_t i;
-
-  chart_write_title (cr, geom, _("ROC Curve"));
-  chart_write_xlabel (cr, geom, _("1 - Specificity"));
-  chart_write_ylabel (cr, geom, _("Sensitivity"));
-
-  chart_write_xscale (cr, geom, 0, 1, 5);
-  chart_write_yscale (cr, geom, 0, 1, 5);
-
-  if ( rc->reference )
-    {
-      chart_line (cr, geom, 1.0, 0,
-                 0.0, 1.0,
-                 CHART_DIM_X);
-    }
-
-  for (i = 0; i < rc->n_vars; ++i)
-    {
-      const struct roc_var *rv = &rc->vars[i];
-      struct casereader *r = casereader_clone (rv->cutpoint_reader);
-      struct ccase *cc;
-
-      chart_vector_start (cr, geom, rv->name);
-      for (; (cc = casereader_read (r)) != NULL; case_unref (cc))
-       {
-         double se = case_data_idx (cc, ROC_TP)->f;
-         double sp = case_data_idx (cc, ROC_TN)->f;
-
-         se /= case_data_idx (cc, ROC_FN)->f + case_data_idx (cc, ROC_TP)->f ;
-         sp /= case_data_idx (cc, ROC_TN)->f + case_data_idx (cc, ROC_FP)->f ;
-
-         chart_vector (cr, geom, 1 - sp, se);
-       }
-      chart_vector_end (cr, geom);
-      casereader_destroy (r);
-    }
-
-  chart_write_legend (cr, geom);
-}
-
-static void
-roc_chart_destroy (struct chart *chart)
-{
-  struct roc_chart *rc = UP_CAST (chart, struct roc_chart, chart);
+  struct roc_chart *rc = UP_CAST (chart_item, struct roc_chart, chart_item);
   size_t i;
 
   for (i = 0; i < rc->n_vars; i++)
@@ -139,10 +69,7 @@ roc_chart_destroy (struct chart *chart)
   free (rc);
 }
 
-static const struct chart_class roc_chart_class =
+const struct chart_item_class roc_chart_class =
   {
-    roc_chart_draw,
     roc_chart_destroy
   };
-
-
index dca842071a667f4fe05c379096977202d5755ebf..a995f9302d4f3f4d3d39feefb2045a01ba62a3fe 100644 (file)
 #define OUTPUT_CHARTS_ROC_CHART_H 1
 
 #include <stdbool.h>
+#include <stddef.h>
+#include <output/chart-item.h>
 
-struct casereader;
+struct roc_chart
+  {
+    struct chart_item chart_item;
+    bool reference;
+    struct roc_var *vars;
+    size_t n_vars;
+    size_t allocated_vars;
+  };
+
+struct roc_var
+  {
+    char *name;
+    struct casereader *cutpoint_reader;
+  };
 
 struct roc_chart *roc_chart_create (bool reference);
 void roc_chart_add_var (struct roc_chart *, const char *var_name,
                         const struct casereader *cutpoint_reader);
-struct chart *roc_chart_get_chart (struct roc_chart *);
+\f
+/* This boilerplate for roc_chart, a subclass of chart_item, was
+   autogenerated by mk-class-boilerplate. */
+
+#include <assert.h>
+#include <libpspp/cast.h>
+
+extern const struct chart_item_class roc_chart_class;
+
+/* Returns true if SUPER is a roc_chart, otherwise false. */
+static inline bool
+is_roc_chart (const struct chart_item *super)
+{
+  return super->class == &roc_chart_class;
+}
+
+/* Returns SUPER converted to roc_chart.  SUPER must be a roc_chart, as
+   reported by is_roc_chart. */
+static inline struct roc_chart *
+to_roc_chart (const struct chart_item *super)
+{
+  assert (is_roc_chart (super));
+  return UP_CAST (super, struct roc_chart, chart_item);
+}
+
+/* Returns INSTANCE converted to chart_item. */
+static inline struct chart_item *
+roc_chart_super (const struct roc_chart *instance)
+{
+  return CONST_CAST (struct chart_item *, &instance->chart_item);
+}
+
+/* Increments INSTANCE's reference count and returns INSTANCE. */
+static inline struct roc_chart *
+roc_chart_ref (const struct roc_chart *instance)
+{
+  return to_roc_chart (chart_item_ref (&instance->chart_item));
+}
+
+/* Decrements INSTANCE's reference count, then destroys INSTANCE if
+   the reference count is now zero. */
+static inline void
+roc_chart_unref (struct roc_chart *instance)
+{
+  chart_item_unref (&instance->chart_item);
+}
+
+/* Returns true if INSTANCE's reference count is greater than 1,
+   false otherwise. */
+static inline bool
+roc_chart_is_shared (const struct roc_chart *instance)
+{
+  return chart_item_is_shared (&instance->chart_item);
+}
 
+static inline void
+roc_chart_submit (struct roc_chart *instance)
+{
+  chart_item_submit (&instance->chart_item);
+}
+\f
 #endif /* output/charts/roc-chart.h */
diff --git a/src/output/charts/scree-cairo.c b/src/output/charts/scree-cairo.c
new file mode 100644 (file)
index 0000000..d48a364
--- /dev/null
@@ -0,0 +1,65 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2009 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 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.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+
+#include <output/charts/scree.h>
+
+#include <math.h>
+
+#include <output/cairo-chart.h>
+
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
+
+void
+xrchart_draw_scree (const struct chart_item *chart_item, cairo_t *cr,
+                    struct xrchart_geometry *geom)
+{
+  const struct scree *rc = to_scree (chart_item);
+  size_t i;
+  double min, max;
+
+  xrchart_write_title (cr, geom, _("Scree Plot"));
+  xrchart_write_xlabel (cr, geom, rc->xlabel);
+  xrchart_write_ylabel (cr, geom, _("Eigenvalue"));
+
+  gsl_vector_minmax (rc->eval, &min, &max);
+
+  if ( fabs (max) > fabs (min))
+    max = fabs (max);
+  else
+    max = fabs (min);
+
+  xrchart_write_yscale (cr, geom, 0, max, max);
+  xrchart_write_xscale (cr, geom, 0, rc->eval->size + 1, rc->eval->size + 1);
+
+  xrchart_vector_start (cr, geom, "");
+  for (i = 0 ; i < rc->eval->size; ++i)
+    {
+      const double x = 1 + i;
+      const double y = gsl_vector_get (rc->eval, i);
+      xrchart_vector (cr, geom, x, y);
+    }
+  xrchart_vector_end (cr, geom);
+
+  for (i = 0 ; i < rc->eval->size; ++i)
+    {
+      const double x = 1 + i;
+      const double y = gsl_vector_get (rc->eval, i);
+      xrchart_datum (cr, geom, 0, x, y);
+    }
+}
index 44f7c7a60107b86fed41d9209460090f147ec83d..ffa77568bd975a4c537beef1541d4169cb0cb701 100644 (file)
 
 #include <output/charts/scree.h>
 
-#include <output/chart-provider.h>
-#include <output/charts/cartesian.h>
-#include <output/charts/plot-chart.h>
+#include <output/chart-item-provider.h>
 
-#include <gsl/gsl_vector.h>
-
-#include "xalloc.h"
+#include "gl/xalloc.h"
 
 #include "gettext.h"
 #define _(msgid) gettext (msgid)
 
-struct scree
-  {
-    struct chart chart;
-    const gsl_vector *eval;
-    const char *xlabel;
-  };
-
-static const struct chart_class scree_class;
-
 struct scree *
 scree_create (const gsl_vector *eigenvalues, const char *xlabel)
 {
   struct scree *rc = xmalloc (sizeof *rc);
-  chart_init (&rc->chart, &scree_class);
-  rc->eval = eigenvalues;
-  rc->xlabel = xlabel;
-  return rc;
-}
-
-
-struct chart *
-scree_get_chart (struct scree *rc)
-{
-  return &rc->chart;
-}
-
-static void
-scree_draw (const struct chart *chart, cairo_t *cr,
-                struct chart_geometry *geom)
-{
-  const struct scree *rc = UP_CAST (chart, struct scree, chart);
-  size_t i;
-  double min, max;
-
-  chart_write_title (cr, geom, _("Scree Plot"));
-  chart_write_xlabel (cr, geom, rc->xlabel);
-  chart_write_ylabel (cr, geom, _("Eigenvalue"));
+  chart_item_init (&rc->chart_item, &scree_class, NULL);
 
-  gsl_vector_minmax (rc->eval, &min, &max);
+  rc->eval = gsl_vector_alloc (eigenvalues->size);
+  gsl_vector_memcpy (rc->eval, eigenvalues);
 
-  if ( fabs (max) > fabs (min))
-    max = fabs (max);
-  else
-    max = fabs (min);
+  rc->xlabel = xstrdup (xlabel);
 
-  chart_write_yscale (cr, geom, 0, max, max);
-  chart_write_xscale (cr, geom, 0, rc->eval->size + 1, rc->eval->size + 1);
-
-  chart_vector_start (cr, geom, "");
-  for (i = 0 ; i < rc->eval->size; ++i)
-    {
-      const double x = 1 + i;
-      const double y = gsl_vector_get (rc->eval, i);
-      chart_vector (cr, geom, x, y);
-    }
-  chart_vector_end (cr, geom);
-
-  for (i = 0 ; i < rc->eval->size; ++i)
-    {
-      const double x = 1 + i;
-      const double y = gsl_vector_get (rc->eval, i);
-      chart_datum (cr, geom, 0, x, y);
-    }
+  return rc;
 }
 
 static void
-scree_destroy (struct chart *chart)
+scree_destroy (struct chart_item *chart_item)
 {
-  struct scree *rc = UP_CAST (chart, struct scree, chart);
+  struct scree *rc = to_scree (chart_item);
 
+  gsl_vector_free (rc->eval);
+  free (rc->xlabel);
   free (rc);
 }
 
-static const struct chart_class scree_class =
+const struct chart_item_class scree_class =
   {
-    scree_draw,
     scree_destroy
   };
-
-
index 9324499886e48e7db5175b0166998cf6e6e4b294..43f5b822482c066bbf7b7364ce301f4fdd3e02a2 100644 (file)
 #define OUTPUT_CHARTS_SCREE_H 1
 
 #include <gsl/gsl_vector.h>
+#include <output/chart-item.h>
 
-struct scree;
-struct chart;
+/* A scree plot. */
+struct scree
+  {
+    struct chart_item chart_item;
+    gsl_vector *eval;
+    char *xlabel;
+  };
 
 /* Create a "Scree Plot" of EIGENVALUES with LABEL on the X Axis */
 struct scree *scree_create (const gsl_vector *eigenvalues, const char *label);
 
 /* Return the chart underlying SCREE */
 struct chart *scree_get_chart (struct scree *scree);
+\f
+/* This boilerplate for scree, a subclass of chart_item, was
+   autogenerated by mk-class-boilerplate. */
 
-#endif 
+#include <assert.h>
+#include <libpspp/cast.h>
+
+extern const struct chart_item_class scree_class;
+
+/* Returns true if SUPER is a scree, otherwise false. */
+static inline bool
+is_scree (const struct chart_item *super)
+{
+  return super->class == &scree_class;
+}
+
+/* Returns SUPER converted to scree.  SUPER must be a scree, as
+   reported by is_scree. */
+static inline struct scree *
+to_scree (const struct chart_item *super)
+{
+  assert (is_scree (super));
+  return UP_CAST (super, struct scree, chart_item);
+}
+
+/* Returns INSTANCE converted to chart_item. */
+static inline struct chart_item *
+scree_super (const struct scree *instance)
+{
+  return CONST_CAST (struct chart_item *, &instance->chart_item);
+}
+
+/* Increments INSTANCE's reference count and returns INSTANCE. */
+static inline struct scree *
+scree_ref (const struct scree *instance)
+{
+  return to_scree (chart_item_ref (&instance->chart_item));
+}
+
+/* Decrements INSTANCE's reference count, then destroys INSTANCE if
+   the reference count is now zero. */
+static inline void
+scree_unref (struct scree *instance)
+{
+  chart_item_unref (&instance->chart_item);
+}
+
+/* Returns true if INSTANCE's reference count is greater than 1,
+   false otherwise. */
+static inline bool
+scree_is_shared (const struct scree *instance)
+{
+  return chart_item_is_shared (&instance->chart_item);
+}
+
+static inline void
+scree_submit (struct scree *instance)
+{
+  chart_item_submit (&instance->chart_item);
+}
+\f
+#endif /* output/charts/scree.h */
diff --git a/src/output/csv.c b/src/output/csv.c
new file mode 100644 (file)
index 0000000..158c789
--- /dev/null
@@ -0,0 +1,245 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2009 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 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.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+
+#include <errno.h>
+#include <stdlib.h>
+
+#include <data/file-name.h>
+#include <libpspp/assertion.h>
+#include <libpspp/compiler.h>
+#include <libpspp/string-map.h>
+#include <output/text-item.h>
+#include <output/driver-provider.h>
+#include <output/options.h>
+#include <output/table-item.h>
+#include <output/table-provider.h>
+
+#include "gl/error.h"
+#include "gl/xalloc.h"
+#include "gl/xvasprintf.h"
+
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
+
+/* Comma-separated value output driver. */
+struct csv_driver
+  {
+    struct output_driver driver;
+
+    char *separator;            /* Comma or tab. */
+
+    char *file_name;            /* Output file name. */
+    FILE *file;                 /* Output file. */
+    bool reported_error;        /* Reported file open error? */
+  };
+
+static struct csv_driver *
+csv_driver_cast (struct output_driver *driver)
+{
+  assert (driver->class == &csv_class);
+  return UP_CAST (driver, struct csv_driver, driver);
+}
+
+static struct driver_option *
+opt (struct output_driver *d, struct string_map *options, const char *key,
+     const char *default_value)
+{
+  return driver_option_get (d, options, key, default_value);
+}
+
+static struct output_driver *
+csv_create (const char *name, enum output_device_type device_type,
+            struct string_map *o)
+{
+  struct output_driver *d;
+  struct csv_driver *csv;
+
+  csv = xzalloc (sizeof *csv);
+  d = &csv->driver;
+  output_driver_init (&csv->driver, &csv_class, name, device_type);
+
+  csv->separator = parse_string (opt (d, o, "separator", ","));
+  csv->file_name = parse_string (opt (d, o, "output-file", "pspp.csv"));
+  csv->file = NULL;
+  csv->reported_error = false;
+
+  return d;
+}
+
+static void
+csv_destroy (struct output_driver *driver)
+{
+  struct csv_driver *csv = csv_driver_cast (driver);
+
+  free (csv->separator);
+  free (csv->file_name);
+  if (csv->file != NULL)
+    fclose (csv->file);
+  free (csv);
+}
+
+static void
+csv_flush (struct output_driver *driver)
+{
+  struct csv_driver *csv = csv_driver_cast (driver);
+  if (csv->file != NULL)
+    fflush (csv->file);
+}
+
+static void
+csv_output_field (FILE *file, const char *field)
+{
+  while (*field == ' ')
+    field++;
+
+  if (field[strcspn (field, "\"\n\r,\t")])
+    {
+      const char *p;
+
+      putc ('"', file);
+      for (p = field; *p != '\0'; p++)
+        {
+          if (*p == '"')
+            putc ('"', file);
+          putc (*p, file);
+        }
+      putc ('"', file);
+    }
+  else
+    fputs (field, file);
+}
+
+static void
+csv_output_field_format (FILE *file, const char *format, ...)
+  PRINTF_FORMAT (2, 3);
+
+static void
+csv_output_field_format (FILE *file, const char *format, ...)
+{
+  va_list args;
+  char *s;
+
+  va_start (args, format);
+  s = xvasprintf (format, args);
+  va_end (args);
+
+  csv_output_field (file, s);
+  free (s);
+}
+
+static bool
+csv_open_file (struct csv_driver *csv)
+{
+  if (csv->file == NULL)
+    {
+      csv->file = fn_open (csv->file_name, "w");
+      if (csv->file == NULL)
+        {
+          if (!csv->reported_error)
+            error (0, errno, _("csv: opening output file \"%s\""),
+                   csv->file_name);
+          csv->reported_error = true;
+          return false;
+        }
+    }
+  else
+    putc ('\n', csv->file);
+
+  return true;
+}
+
+static void
+csv_submit (struct output_driver *driver,
+            const struct output_item *output_item)
+{
+  struct csv_driver *csv = csv_driver_cast (driver);
+
+  if (is_table_item (output_item))
+    {
+      struct table_item *table_item = to_table_item (output_item);
+      const char *caption = table_item_get_caption (table_item);
+      const struct table *t = table_item_get_table (table_item);
+      int x, y;
+
+      if (!csv_open_file (csv))
+        return;
+
+      if (caption != NULL)
+        {
+          csv_output_field_format (csv->file, "Table: %s", caption);
+          putc ('\n', csv->file);
+        }
+
+      for (y = 0; y < table_nr (t); y++)
+        {
+          for (x = 0; x < table_nc (t); x++)
+            {
+              struct table_cell cell;
+
+              table_get_cell (t, x, y, &cell);
+
+              if (x > 0)
+                fputs (csv->separator, csv->file);
+
+              if (x != cell.d[TABLE_HORZ][0] || y != cell.d[TABLE_VERT][0])
+                csv_output_field (csv->file, "");
+              else
+                csv_output_field (csv->file, cell.contents);
+
+              table_cell_free (&cell);
+            }
+          putc ('\n', csv->file);
+        }
+    }
+  else if (is_text_item (output_item))
+    {
+      const struct text_item *text_item = to_text_item (output_item);
+      enum text_item_type type = text_item_get_type (text_item);
+      const char *text = text_item_get_text (text_item);
+
+      if (type == TEXT_ITEM_COMMAND_OPEN || type == TEXT_ITEM_COMMAND_CLOSE
+          || type == TEXT_ITEM_SYNTAX)
+        return;
+
+      csv_open_file (csv);
+      switch (type)
+        {
+        case TEXT_ITEM_TITLE:
+          csv_output_field_format (csv->file, "Title: %s", text);
+          break;
+
+        case TEXT_ITEM_SUBTITLE:
+          csv_output_field_format (csv->file, "Subtitle: %s", text);
+          break;
+
+        default:
+          csv_output_field (csv->file, text);
+          break;
+        }
+      putc ('\n', csv->file);
+    }
+}
+
+const struct output_driver_class csv_class =
+  {
+    "csv",
+    csv_create,
+    csv_destroy,
+    csv_submit,
+    csv_flush,
+  };
diff --git a/src/output/driver-provider.h b/src/output/driver-provider.h
new file mode 100644 (file)
index 0000000..92929b5
--- /dev/null
@@ -0,0 +1,99 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 1997-9, 2000, 2007, 2009 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 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.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef OUTPUT_DRIVER_PROVIDER_H
+#define OUTPUT_DRIVER_PROVIDER_H 1
+
+#include <libpspp/compiler.h>
+#include <stdbool.h>
+#include <output/driver.h>
+
+struct output_item;
+struct string_map;
+
+/* A configured output driver. */
+struct output_driver
+  {
+    const struct output_driver_class *class; /* Driver class. */
+    char *name;                              /* Name of this driver. */
+    enum output_device_type device_type;     /* One of OUTPUT_DEVICE_*. */
+  };
+
+void output_driver_init (struct output_driver *,
+                         const struct output_driver_class *,
+                         const char *name, enum output_device_type);
+void output_driver_destroy (struct output_driver *);
+
+const char *output_driver_get_name (const struct output_driver *);
+
+/* One kind of output driver.
+
+   Output driver implementations must not call msg() to report errors.  This
+   can lead to reentrance in the output driver, because msg() may report error
+   messages using the output drivers.  Instead, this code should report errors
+   with error(), which will never call into the output drivers.  */
+struct output_driver_class
+  {
+    const char *name;          /* Name of this driver class. */
+
+    /* Creates a new output driver of this class.  NAME and TYPE should be
+       passed directly to output_driver_init.  Returns the new output driver if
+       successful, otherwise a null pointer.
+
+       It is up to the driver class to decide how to interpret OPTIONS.  The
+       functions in output/options.h can be useful.  OPTIONS may be modified
+       but the caller is responsible for destroying it.
+
+       The returned driver should not have been registered (with
+       output_driver_register).  The caller will register the driver (if this
+       is desirable). */
+    struct output_driver *(*create) (const char *name,
+                                     enum output_device_type type,
+                                     struct string_map *options);
+
+    /* Closes and frees DRIVER. */
+    void (*destroy) (struct output_driver *driver);
+
+    /* Passes ITEM to DRIVER to be written as output.  The caller retains
+       ownership of ITEM (but DRIVER may keep a copy of it by incrementing the
+       reference count by calling output_item_ref). */
+    void (*submit) (struct output_driver *driver,
+                    const struct output_item *item);
+
+    /* Ensures that any output items passed to the 'submit' function for DRIVER
+       have actually been displayed.
+
+       This is called from the text-based UI before showing the command prompt,
+       to ensure that the user has actually been shown any preceding output If
+       it doesn't make sense for DRIVER to be used this way, then this function
+       need not do anything. */
+    void (*flush) (struct output_driver *driver);
+  };
+
+void output_driver_register (struct output_driver *);
+void output_driver_unregister (struct output_driver *);
+bool output_driver_is_registered (const struct output_driver *);
+
+/* Common drivers. */
+extern const struct output_driver_class ascii_class;
+extern const struct output_driver_class html_class;
+extern const struct output_driver_class odt_class;
+extern const struct output_driver_class csv_class;
+#ifdef HAVE_CAIRO
+extern const struct output_driver_class cairo_class;
+#endif
+
+#endif /* output/driver-provider.h */
diff --git a/src/output/driver.c b/src/output/driver.c
new file mode 100644 (file)
index 0000000..6dd2517
--- /dev/null
@@ -0,0 +1,722 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 1997-9, 2000, 2007, 2009 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 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.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+
+#include <output/driver.h>
+#include <output/driver-provider.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <data/file-name.h>
+#include <data/settings.h>
+#include <libpspp/array.h>
+#include <libpspp/assertion.h>
+#include <libpspp/string-map.h>
+#include <libpspp/string-set.h>
+#include <libpspp/str.h>
+#include <output/output-item.h>
+#include <output/text-item.h>
+
+#include "error.h"
+#include "xalloc.h"
+#include "xmemdup0.h"
+
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
+
+static const struct output_driver_class *driver_classes[];
+
+static struct output_driver **drivers;
+static size_t n_drivers, allocated_drivers;
+
+static unsigned int enabled_device_types = ((1u << OUTPUT_DEVICE_UNKNOWN)
+                                            | (1u << OUTPUT_DEVICE_LISTING)
+                                            | (1u << OUTPUT_DEVICE_SCREEN)
+                                            | (1u << OUTPUT_DEVICE_PRINTER));
+
+static struct output_item *deferred_syntax;
+static bool in_command;
+
+void
+output_close (void)
+{
+  while (n_drivers > 0)
+    {
+      struct output_driver *d = drivers[--n_drivers];
+      output_driver_destroy (d);
+    }
+}
+
+static void
+expand_macro (const char *name, struct string *dst, void *macros_)
+{
+  const struct string_map *macros = macros_;
+
+  if (!strcmp (name, "viewwidth"))
+    ds_put_format (dst, "%d", settings_get_viewwidth ());
+  else if (!strcmp (name, "viewlength"))
+    ds_put_format (dst, "%d", settings_get_viewlength ());
+  else
+    {
+      const char *value = string_map_find (macros, name);
+      if (value != NULL)
+        ds_put_cstr (dst, value);
+    }
+}
+
+/* Defines one configuration macro based on the text in BP, which
+   should be of the form `KEY=VALUE'.  Returns true if
+   successful, false if S is not in the proper form. */
+bool
+output_define_macro (const char *s, struct string_map *macros)
+{
+  const char *key_start, *value;
+  size_t key_len;
+  char *key;
+
+  s += strspn (s, CC_SPACES);
+
+  key_start = s;
+  key_len = strcspn (s, "=" CC_SPACES);
+  if (key_len == 0)
+    return false;
+  s += key_len;
+
+  s += strspn (s, CC_SPACES);
+  if (*s == '=')
+    s++;
+
+  s += strspn (s, CC_SPACES);
+  value = s;
+
+  key = xmemdup0 (key_start, key_len);
+  if (!string_map_contains (macros, key))
+    {
+      struct string expanded_value = DS_EMPTY_INITIALIZER;
+
+      fn_interp_vars (ss_cstr (value), expand_macro, &macros, &expanded_value);
+      string_map_insert_nocopy (macros, key, ds_steal_cstr (&expanded_value));
+    }
+  else
+    free (key);
+
+  return true;
+}
+
+static void
+add_driver_names (char *to, struct string_set *names)
+{
+  char *save_ptr = NULL;
+  char *name;
+
+  for (name = strtok_r (to, CC_SPACES, &save_ptr); name != NULL;
+       name = strtok_r (NULL, CC_SPACES, &save_ptr))
+    string_set_insert (names, name);
+}
+
+static void
+init_default_drivers (void)
+{
+  error (0, 0, _("using default output driver configuration"));
+  output_configure_driver ("list:ascii:listing:"
+                           "length=66 width=79 output-file=\"pspp.list\"");
+}
+
+static void
+warn_unused_drivers (const struct string_set *unused_drivers,
+                     const struct string_set *requested_drivers)
+{
+  const struct string_set_node *node;
+  const char *name;
+
+  STRING_SET_FOR_EACH (name, node, unused_drivers)
+    if (string_set_contains (requested_drivers, name))
+      error (0, 0, _("unknown output driver `%s'"), name);
+    else
+      error (0, 0, _("output driver `%s' referenced but never defined"), name);
+}
+
+void
+output_read_configuration (const struct string_map *macros_,
+                           const struct string_set *driver_names_)
+{
+  struct string_map macros = STRING_MAP_INITIALIZER (macros);
+  struct string_set driver_names = STRING_SET_INITIALIZER (driver_names);
+  char *devices_file_name = NULL;
+  FILE *devices_file = NULL;
+  struct string line = DS_EMPTY_INITIALIZER;
+  int line_number;
+
+  ds_init_empty (&line);
+
+  devices_file_name = fn_search_path ("devices", config_path);
+  if (devices_file_name == NULL)
+    {
+      error (0, 0, _("cannot find output initialization file "
+                     "(use `-vv' to view search path)"));
+      goto exit;
+    }
+  devices_file = fopen (devices_file_name, "r");
+  if (devices_file == NULL)
+    {
+      error (0, errno, _("cannot open \"%s\""), devices_file_name);
+      goto exit;
+    }
+
+  string_map_replace_map (&macros, macros_);
+  string_set_union (&driver_names, driver_names_);
+  if (string_set_is_empty (&driver_names))
+    string_set_insert (&driver_names, "default");
+
+  line_number = 0;
+  for (;;)
+    {
+      char *cp, *delimiter, *name;
+
+      if (!ds_read_config_line (&line, &line_number, devices_file))
+       {
+         if (ferror (devices_file))
+           error (0, errno, _("reading \"%s\""), devices_file_name);
+         break;
+       }
+
+      cp = ds_cstr (&line);
+      cp += strspn (cp, CC_SPACES);
+
+      if (*cp == '\0')
+        continue;
+      else if (!strncmp ("define", cp, 6) && isspace ((unsigned char) cp[6]))
+        {
+          if (!output_define_macro (&cp[7], &macros))
+            error_at_line (0, 0, devices_file_name, line_number,
+                           _("\"%s\" is not a valid macro definition"),
+                           &cp[7]);
+          continue;
+        }
+
+      delimiter = cp + strcspn (cp, ":=");
+      name = xmemdup0 (cp, delimiter - cp);
+      if (*delimiter == '=')
+        {
+          if (string_set_delete (&driver_names, name))
+            add_driver_names (delimiter + 1, &driver_names);
+        }
+      else if (*delimiter == ':')
+        {
+          if (string_set_delete (&driver_names, name))
+            {
+              fn_interp_vars (ds_ss (&line), expand_macro, &macros, &line);
+              output_configure_driver (ds_cstr (&line));
+            }
+        }
+      else
+        error_at_line (0, 0, devices_file_name, line_number,
+                       _("syntax error"));
+      free (name);
+    }
+
+  warn_unused_drivers (&driver_names, driver_names_);
+
+exit:
+  if (devices_file != NULL)
+    fclose (devices_file);
+  free (devices_file_name);
+  ds_destroy (&line);
+  string_set_destroy (&driver_names);
+  string_map_destroy (&macros);
+
+  if (n_drivers == 0)
+    {
+      error (0, 0, _("no active output drivers"));
+      init_default_drivers ();
+    }
+}
+
+/* Obtains a token from S and advances its position.  Errors are
+   reported against the given DRIVER_NAME.
+   The token is stored in TOKEN.  Returns true if successful,
+   false on syntax error.
+
+   Caller is responsible for skipping leading spaces. */
+static bool
+get_option_token (char **s_, const char *driver_name,
+                  struct string *token)
+{
+  struct substring s = ss_cstr (*s_);
+  int c;
+
+  ds_clear (token);
+  c = ss_get_char (&s);
+  if (c == EOF)
+    {
+      error (0, 0, _("syntax error parsing options for \"%s\" driver"),
+             driver_name);
+      return false;
+    }
+  else if (c == '\'' || c == '"')
+    {
+      int quote = c;
+
+      for (;;)
+        {
+          c = ss_get_char (&s);
+          if (c == quote)
+            break;
+          else if (c == EOF)
+            {
+              error (0, 0,
+                     _("reached end of options inside quoted string "
+                       "parsing options for \"%s\" driver"),
+                     driver_name);
+              return false;
+            }
+          else if (c != '\\')
+            ds_put_char (token, c);
+          else
+            {
+              int out;
+
+              c = ss_get_char (&s);
+              switch (c)
+                {
+                case '\'':
+                  out = '\'';
+                  break;
+                case '"':
+                  out = '"';
+                  break;
+                case '\\':
+                  out = '\\';
+                  break;
+                case 'a':
+                  out = '\a';
+                  break;
+                case 'b':
+                  out = '\b';
+                  break;
+                case 'f':
+                  out = '\f';
+                  break;
+                case 'n':
+                  out = '\n';
+                  break;
+                case 'r':
+                  out = '\r';
+                  break;
+                case 't':
+                  out = '\t';
+                  break;
+                case 'v':
+                  out = '\v';
+                  break;
+                case '0':
+                case '1':
+                case '2':
+                case '3':
+                case '4':
+                case '5':
+                case '6':
+                case '7':
+                  out = c - '0';
+                  while (ss_first (s) >= '0' && ss_first (s) <= '7')
+                    out = out * 8 + (ss_get_char (&s) - '0');
+                  break;
+                case 'x':
+                case 'X':
+                  out = 0;
+                  while (isxdigit (ss_first (s)))
+                    {
+                      c = ss_get_char (&s);
+                      out *= 16;
+                      if (isdigit (c))
+                        out += c - '0';
+                      else
+                        out += tolower (c) - 'a' + 10;
+                    }
+                  break;
+                default:
+                  error (0, 0, _("syntax error in string constant "
+                                 "parsing options for \"%s\" driver"),
+                         driver_name);
+                  return false;
+                }
+              ds_put_char (token, out);
+            }
+        }
+    }
+  else
+    {
+      for (;;)
+        {
+          ds_put_char (token, c);
+
+          c = ss_first (s);
+          if (c == EOF || c == '=' || isspace (c))
+            break;
+          ss_advance (&s, 1);
+        }
+    }
+
+  *s_ = s.string;
+  return 1;
+}
+
+static void
+parse_options (const char *driver_name, char *options,
+               struct string_map *option_map)
+{
+  struct string key = DS_EMPTY_INITIALIZER;
+  struct string value = DS_EMPTY_INITIALIZER;
+
+  for (;;)
+    {
+      options += strspn (options, CC_SPACES);
+      if (*options == '\0')
+        break;
+
+      if (!get_option_token (&options, driver_name, &key))
+        break;
+
+      options += strspn (options, CC_SPACES);
+      if (*options != '=')
+       {
+         error (0, 0, _("syntax error expecting `=' "
+                         "parsing options for driver \"%s\""),
+                 driver_name);
+         break;
+       }
+      options++;
+
+      options += strspn (options, CC_SPACES);
+      if (!get_option_token (&options, driver_name, &value))
+        break;
+
+      if (string_map_contains (option_map, ds_cstr (&key)))
+        error (0, 0, _("driver \"%s\" defines option \"%s\" multiple times"),
+               driver_name, ds_cstr (&key));
+      else
+        string_map_insert (option_map, ds_cstr (&key), ds_cstr (&value));
+    }
+
+  ds_destroy (&key);
+  ds_destroy (&value);
+}
+
+static char *
+trim_token (char *token)
+{
+  if (token != NULL)
+    {
+      char *end;
+
+      /* Trim leading spaces. */
+      while (isspace ((unsigned char) *token))
+        token++;
+
+      /* Trim trailing spaces. */
+      end = strchr (token, '\0');
+      while (end > token && isspace ((unsigned char) end[-1]))
+        *--end = '\0';
+
+      /* An empty token is a null token. */
+      if (*token == '\0')
+        return NULL;
+    }
+  return token;
+}
+
+static const struct output_driver_class *
+find_output_class (const char *name)
+{
+  const struct output_driver_class **classp;
+
+  for (classp = driver_classes; *classp != NULL; classp++)
+    if (!strcmp ((*classp)->name, name))
+      break;
+
+  return *classp;
+}
+
+static struct output_driver *
+create_driver (const char *driver_name, const char *class_name,
+               const char *device_type, struct string_map *options)
+{
+  const struct output_driver_class *class;
+  enum output_device_type type;
+  struct output_driver *driver;
+
+  type = OUTPUT_DEVICE_UNKNOWN;
+  if (device_type != NULL && device_type[0] != '\0')
+    {
+      if (!strcmp (device_type, "listing"))
+        type = OUTPUT_DEVICE_LISTING;
+      else if (!strcmp (device_type, "screen"))
+        type = OUTPUT_DEVICE_SCREEN;
+      else if (!strcmp (device_type, "printer"))
+        type = OUTPUT_DEVICE_PRINTER;
+      else
+        error (0, 0, _("unknown device type `%s'"), device_type);
+    }
+
+  class = find_output_class (class_name);
+  if (class != NULL)
+    driver = class->create (driver_name, type, options);
+  else
+    {
+      error (0, 0, _("unknown output driver class `%s'"), class_name);
+      driver = NULL;
+    }
+
+  string_map_destroy (options);
+
+  return driver;
+}
+
+struct output_driver *
+output_driver_create (const char *class_name, struct string_map *options)
+{
+  return create_driver (class_name, class_name, NULL, options);
+}
+
+/* String LINE is in format:
+   DRIVERNAME:CLASSNAME:DEVICETYPE:OPTIONS
+*/
+void
+output_configure_driver (const char *line_)
+{
+  char *save_ptr = NULL;
+  char *line = xstrdup (line_);
+  char *driver_name = trim_token (strtok_r (line, ":", &save_ptr));
+  char *class_name = trim_token (strtok_r (NULL, ":", &save_ptr));
+  char *device_type = trim_token (strtok_r (NULL, ":", &save_ptr));
+  char *options = trim_token (strtok_r (NULL, "", &save_ptr));
+
+  if (driver_name && class_name)
+    {
+      struct string_map option_map;
+      struct output_driver *driver;
+
+      string_map_init (&option_map);
+      if (options != NULL)
+        parse_options (driver_name, options, &option_map);
+
+      driver = create_driver (driver_name, class_name,
+                              device_type, &option_map);
+      if (driver != NULL)
+        output_driver_register (driver);
+    }
+  else
+    error (0, 0,
+           _("driver definition line missing driver name or class name"));
+
+  free (line);
+}
+
+/* Display on stdout a list of all registered driver classes. */
+void
+output_list_classes (void)
+{
+  const struct output_driver_class **classp;
+
+  printf (_("Driver classes:"));
+  for (classp = driver_classes; *classp != NULL; classp++)
+    printf (" %s", (*classp)->name);
+  putc ('\n', stdout);
+}
+
+static bool
+driver_is_enabled (const struct output_driver *d)
+{
+  return (1u << d->device_type) & enabled_device_types;
+}
+
+static void
+output_submit__ (struct output_item *item)
+{
+  size_t i;
+
+  for (i = 0; i < n_drivers; i++)
+    {
+      struct output_driver *d = drivers[i];
+      if (driver_is_enabled (d))
+        d->class->submit (d, item);
+    }
+
+  output_item_unref (item);
+}
+
+/* Submits ITEM to the configured output drivers, and transfers ownership to
+   the output subsystem. */
+void
+output_submit (struct output_item *item)
+{
+  if (is_text_item (item))
+    {
+      struct text_item *text = to_text_item (item);
+      switch (text_item_get_type (text))
+        {
+        case TEXT_ITEM_SYNTAX:
+          if (!in_command)
+            {
+              if (deferred_syntax != NULL)
+                output_submit__ (deferred_syntax);
+              deferred_syntax = item;
+              return;
+            }
+          break;
+
+        case TEXT_ITEM_COMMAND_OPEN:
+          output_submit__ (item);
+          if (deferred_syntax != NULL)
+            {
+              output_submit__ (deferred_syntax);
+              deferred_syntax = NULL;
+            }
+          in_command = true;
+          return;
+
+        case TEXT_ITEM_COMMAND_CLOSE:
+          in_command = false;
+          break;
+
+        default:
+          break;
+        }
+    }
+
+  output_submit__ (item);
+}
+
+/* Flushes output to screen devices, so that the user can see
+   output that doesn't fill up an entire page. */
+void
+output_flush (void)
+{
+  size_t i;
+
+  for (i = 0; i < n_drivers; i++)
+    {
+      struct output_driver *d = drivers[i];
+      if (driver_is_enabled (d) && d->class->flush != NULL)
+        d->class->flush (d);
+    }
+}
+
+unsigned int
+output_get_enabled_types (void)
+{
+  return enabled_device_types;
+}
+
+void
+output_set_enabled_types (unsigned int types)
+{
+  enabled_device_types = types;
+}
+
+void
+output_set_type_enabled (bool enable, enum output_device_type type)
+{
+  unsigned int bit = 1u << type;
+  if (enable)
+    enabled_device_types |= bit;
+  else
+    enabled_device_types |= ~bit;
+}
+\f
+void
+output_driver_init (struct output_driver *driver,
+                    const struct output_driver_class *class,
+                    const char *name, enum output_device_type type)
+{
+  driver->class = class;
+  driver->name = xstrdup (name);
+  driver->device_type = type;
+}
+
+void
+output_driver_destroy (struct output_driver *driver)
+{
+  if (driver != NULL)
+    {
+      char *name = driver->name;
+      if (output_driver_is_registered (driver))
+        output_driver_unregister (driver);
+      if (driver->class->destroy)
+        driver->class->destroy (driver);
+      free (name);
+    }
+}
+
+const char *
+output_driver_get_name (const struct output_driver *driver)
+{
+  return driver->name;
+}
+\f
+void
+output_driver_register (struct output_driver *driver)
+{
+  assert (!output_driver_is_registered (driver));
+  if (n_drivers >= allocated_drivers)
+    drivers = x2nrealloc (drivers, &allocated_drivers, sizeof *drivers);
+  drivers[n_drivers++] = driver;
+}
+
+void
+output_driver_unregister (struct output_driver *driver)
+{
+  size_t i;
+
+  for (i = 0; i < n_drivers; i++)
+    if (drivers[i] == driver)
+      {
+        remove_element (drivers, n_drivers, sizeof *drivers, i);
+        return;
+      }
+  NOT_REACHED ();
+}
+
+bool
+output_driver_is_registered (const struct output_driver *driver)
+{
+  size_t i;
+
+  for (i = 0; i < n_drivers; i++)
+    if (drivers[i] == driver)
+      return true;
+  return false;
+}
+\f
+/* Known driver classes. */
+
+static const struct output_driver_class *driver_classes[] =
+  {
+    &ascii_class,
+#ifdef HAVE_CAIRO
+    &cairo_class,
+#endif
+    &html_class,
+    &odt_class,
+    &csv_class,
+    NULL,
+  };
+
diff --git a/src/output/driver.h b/src/output/driver.h
new file mode 100644 (file)
index 0000000..3fb4cab
--- /dev/null
@@ -0,0 +1,53 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 1997-9, 2000, 2007, 2009 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 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.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef OUTPUT_DRIVER_H
+#define OUTPUT_DRIVER_H 1
+
+#include <stdbool.h>
+
+struct output_item;
+struct string_map;
+struct string_set;
+
+void output_close (void);
+
+struct output_driver *output_driver_create (const char *class_name,
+                                            struct string_map *options);
+
+bool output_define_macro (const char *, struct string_map *macros);
+void output_read_configuration (const struct string_map *macros,
+                                const struct string_set *drivers);
+void output_configure_driver (const char *);
+
+void output_list_classes (void);
+
+void output_submit (struct output_item *);
+void output_flush (void);
+
+/* Device types. */
+enum output_device_type
+  {
+    OUTPUT_DEVICE_UNKNOWN,     /* Unknown type of device. */
+    OUTPUT_DEVICE_LISTING,     /* Listing device. */
+    OUTPUT_DEVICE_SCREEN,      /* Screen device. */
+    OUTPUT_DEVICE_PRINTER      /* Printer device. */
+  };
+unsigned int output_get_enabled_types (void);
+void output_set_enabled_types (unsigned int);
+void output_set_type_enabled (bool enable, enum output_device_type);
+
+#endif /* output/driver.h */
index dfa524a7cfd5f60cd6120023283d1106c635c35a..57d77a7667c94e7e8d68cd7b9739daa9ced2f2f7 100644 (file)
@@ -15,8 +15,7 @@
    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
 
 #include <config.h>
-#include "chart.h"
-#include "htmlP.h"
+
 #include <errno.h>
 #include <stdint.h>
 #include <stdlib.h>
 #include <time.h>
 #include <unistd.h>
 
+#include <data/file-name.h>
 #include <libpspp/assertion.h>
+#include <libpspp/cast.h>
 #include <libpspp/compiler.h>
-#include <data/file-name.h>
-#include <output/chart-provider.h>
-#include <output/output.h>
-#include <output/manager.h>
-#include <output/table.h>
 #include <libpspp/version.h>
+#include <output/cairo.h>
+#include <output/chart-item.h>
+#include <output/driver-provider.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)
 
-/* HTML driver options: (defaults listed first)
+struct html_driver
+  {
+    struct output_driver driver;
+
+    char *file_name;
+    char *chart_file_name;
+
+    FILE *file;
+    size_t chart_cnt;
+
+    bool in_syntax;
+  };
 
-   output-file="pspp.html"
-   chart-files="pspp-#.png"
-*/
+const struct output_driver_class html_class;
 
+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 bool handle_option (void *this,
-                           const char *key, const struct string *val);
 static void print_title_tag (FILE *file, const char *name,
                              const char *content);
 
-static bool
-html_open_driver (const char *name, int types, struct substring options)
+static struct html_driver *
+html_driver_cast (struct output_driver *driver)
 {
-  struct outp_driver *this;
-  struct html_driver_ext *x;
-
-  this = outp_allocate_driver (&html_class, name, types);
-  this->ext = x = xmalloc (sizeof *x);
-  x->file_name = xstrdup ("pspp.html");
-  x->chart_file_name = xstrdup ("pspp-#.png");
-  x->file = NULL;
-  x->chart_cnt = 1;
+  assert (driver->class == &html_class);
+  return UP_CAST (driver, struct html_driver, driver);
+}
 
-  outp_parse_options (name, options, handle_option, this);
+static struct driver_option *
+opt (struct output_driver *d, struct string_map *options, const char *key,
+     const char *default_value)
+{
+  return driver_option_get (d, options, key, default_value);
+}
 
-  x->file = fn_open (x->file_name, "w");
-  if (x->file == NULL)
+static struct output_driver *
+html_create (const char *name, enum output_device_type device_type,
+             struct string_map *o)
+{
+  struct output_driver *d;
+  struct html_driver *html;
+
+  html = xzalloc (sizeof *html);
+  d = &html->driver;
+  output_driver_init (&html->driver, &html_class, name, device_type);
+  html->file_name = parse_string (opt (d, o, "output-file", "pspp.html"));
+  html->chart_file_name = parse_chart_file_name (opt (d, o, "chart-files",
+                                                      "pspp-#.png"));
+  html->file = NULL;
+  html->chart_cnt = 1;
+
+  html->file = fn_open (html->file_name, "w");
+  if (html->file == NULL)
     {
-      error (0, errno, _("opening HTML output file: %s"), x->file_name);
+      error (0, errno, _("opening HTML output file: %s"), html->file_name);
       goto error;
     }
 
   fputs ("<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"\n"
-         "   \"http://www.w3.org/TR/html4/loose.dtd\">\n", x->file);
-  fputs ("<HTML>\n", x->file);
-  fputs ("<HEAD>\n", x->file);
-  /* The <TITLE> tag is required, so we use a default if the user
-     didn't provide one. */
-  print_title_tag (x->file,
-                   "TITLE", outp_title ? outp_title : _("PSPP Output"));
-  fprintf (x->file, "<META NAME=\"generator\" CONTENT=\"%s\">\n", version);
+         "   \"http://www.w3.org/TR/html4/loose.dtd\">\n", html->file);
+  fputs ("<HTML>\n", html->file);
+  fputs ("<HEAD>\n", html->file);
+  print_title_tag (html->file, "TITLE", _("PSPP Output"));
+  fprintf (html->file, "<META NAME=\"generator\" CONTENT=\"%s\">\n", version);
+  fputs ("<META http-equiv=\"Content-Style-Type\" content=\"text/css\">\n",
+         html->file);
   fputs ("<META HTTP-EQUIV=\"Content-Type\" "
-         "CONTENT=\"text/html; charset=ISO-8859-1\">\n", x->file);
-  fputs ("</HEAD>\n", x->file);
-  fputs ("<BODY BGCOLOR=\"#ffffff\" TEXT=\"#000000\"\n", x->file);
-  fputs (" LINK=\"#1f00ff\" ALINK=\"#ff0000\" VLINK=\"#9900dd\">\n", x->file);
-  print_title_tag (x->file, "H1", outp_title);
-  print_title_tag (x->file, "H2", outp_subtitle);
-
-  outp_register_driver (this);
-  return true;
+         "CONTENT=\"text/html; charset=ISO-8859-1\">\n", html->file);
+  fputs ("<STYLE>\n"
+         "<!--\n"
+         "body {\n"
+         "  background: white;\n"
+         "  color: black;\n"
+         "  padding: 0em 12em 0em 3em;\n"
+         "  margin: 0\n"
+         "}\n"
+         "body>p {\n"
+         "  margin: 0pt 0pt 0pt 0em\n"
+         "}\n"
+         "body>p + p {\n"
+         "  text-indent: 1.5em;\n"
+         "}\n"
+         "h1 {\n"
+         "  font-size: 150%;\n"
+         "  margin-left: -1.33em\n"
+         "}\n"
+         "h2 {\n"
+         "  font-size: 125%;\n"
+         "  font-weight: bold;\n"
+         "  margin-left: -.8em\n"
+         "}\n"
+         "h3 {\n"
+         "  font-size: 100%;\n"
+         "  font-weight: bold;\n"
+         "  margin-left: -.5em }\n"
+         "h4 {\n"
+         "  font-size: 100%;\n"
+         "  margin-left: 0em\n"
+         "}\n"
+         "h1, h2, h3, h4, h5, h6 {\n"
+         "  font-family: sans-serif;\n"
+         "  color: blue\n"
+         "}\n"
+         "html {\n"
+         "  margin: 0\n"
+         "}\n"
+         "code {\n"
+         "  font-family: sans-serif\n"
+         "}\n"
+         "table {\n"
+         "  border-collapse: collapse;\n"
+         "  margin-bottom: 1em\n"
+         "}\n"
+         "th { background: #dddddd; font-weight: normal; font-style: oblique }\n"
+         "caption {\n"
+         "  text-align: left\n"
+         "}\n"
+         "-->\n"
+         "</STYLE>\n",
+         html->file);
+  fputs ("</HEAD>\n", html->file);
+  fputs ("<BODY BGCOLOR=\"#ffffff\" TEXT=\"#000000\"\n", html->file);
+  fputs (" LINK=\"#1f00ff\" ALINK=\"#ff0000\" VLINK=\"#9900dd\">\n", html->file);
+
+  return d;
 
  error:
-  this->class->close_driver (this);
-  outp_free_driver (this);
-  return false;
+  output_driver_destroy (d);
+  return NULL;
 }
 
 /* Emits <NAME>CONTENT</NAME> to the output, escaping CONTENT as
@@ -114,113 +188,128 @@ print_title_tag (FILE *file, const char *name, const char *content)
     }
 }
 
-static bool
-html_close_driver (struct outp_driver *this)
+static void
+html_destroy (struct output_driver *driver)
 {
-  struct html_driver_ext *x = this->ext;
-  bool ok;
+  struct html_driver *html = html_driver_cast (driver);
 
-  if (x->file != NULL)
+  if (html->file != NULL)
     {
-      fprintf (x->file,
+      if (html->in_syntax)
+        {
+          fprintf (html->file, "</PRE>\n");
+          html->in_syntax = false;
+        }
+      fprintf (html->file,
                "</BODY>\n"
                "</HTML>\n"
                "<!-- end of file -->\n");
-      ok = fn_close (x->file_name, x->file) == 0;
-      x->file = NULL;
+      fn_close (html->file_name, html->file);
     }
-  else
-    ok = true;
-  free (x->chart_file_name);
-  free (x->file_name);
-  free (x);
+  free (html->chart_file_name);
+  free (html->file_name);
+  free (html);
+}
 
-  return ok;
+static bool
+is_syntax_item (const struct output_item *item)
+{
+  return (is_text_item (item)
+          && text_item_get_type (to_text_item (item)) == TEXT_ITEM_SYNTAX);
 }
 
-/* Generic option types. */
-enum
-  {
-    string_arg,
-    nonneg_int_arg
-  };
+static void
+html_submit (struct output_driver *driver,
+             const struct output_item *output_item)
+{
+  struct html_driver *html = html_driver_cast (driver);
 
-/* All the options that the HTML driver supports. */
-static const struct outp_option option_tab[] =
-  {
-    {"output-file",            string_arg,     0},
-    {"chart-files",            string_arg,     1},
-    {NULL, 0, 0},
-  };
+  if (html->in_syntax && !is_syntax_item (output_item))
+    {
+      fprintf (html->file, "</PRE>\n");
+      html->in_syntax = false;
+    }
 
-static bool
-handle_option (void *this_, const char *key, const struct string *val)
-{
-  struct outp_driver *this = this_;
-  struct html_driver_ext *x = this->ext;
-  int subcat;
+  if (is_table_item (output_item))
+    {
+      struct table_item *table_item = to_table_item (output_item);
+      html_output_table (html, table_item);
+    }
+  else if (is_chart_item (output_item) && html->chart_file_name != NULL)
+    {
+      struct chart_item *chart_item = to_chart_item (output_item);
+      char *file_name;
 
-  switch (outp_match_keyword (key, option_tab, &subcat))
+      file_name = xr_draw_png_chart (chart_item, html->chart_file_name,
+                                     html->chart_cnt++);
+      if (file_name != NULL)
+        {
+          fprintf (html->file, "<IMG SRC=\"%s\"/>", file_name);
+          free (file_name);
+        }
+    }
+  else if (is_text_item (output_item))
     {
-    case -1:
-      error (0, 0,
-             _("unknown configuration parameter `%s' for HTML device driver"),
-             key);
-      break;
-    case string_arg:
-      switch (subcat)
+      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 0:
-          free (x->file_name);
-          x->file_name = ds_xstrdup (val);
+        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 1:
-          if (ds_find_char (val, '#') != SIZE_MAX)
+
+        case TEXT_ITEM_COMMAND_OPEN:
+          fprintf (html->file, "<DIV class=\"");
+          escape_string (html->file, s, strlen (s), "_");
+          fprintf (html->file, "\">");
+          print_title_tag (html->file, "H3", s);
+          break;
+
+        case TEXT_ITEM_COMMAND_CLOSE:
+          fprintf (html->file, "</DIV>\n");
+          break;
+
+        case TEXT_ITEM_SUBHEAD:
+          print_title_tag (html->file, "H4", s);
+          break;
+
+        case TEXT_ITEM_SYNTAX:
+          if (!html->in_syntax)
             {
-              free (x->chart_file_name);
-              x->chart_file_name = ds_xstrdup (val);
+              fprintf (html->file, "<PRE class=\"syntax\">");
+              html->in_syntax = true;
             }
           else
-            error (0, 0, _("`chart-files' value must contain `#'"));
+            putc ('\n', html->file);
+          escape_string (html->file, s, strlen (s), " ");
           break;
-        default:
-          NOT_REACHED ();
-        }
-      break;
-    default:
-      NOT_REACHED ();
-    }
-
-  return true;
-}
-
-static void output_tab_table (struct outp_driver *, struct tab_table *);
 
-static void
-html_output_chart (struct outp_driver *this, const struct chart *chart)
-{
-  struct html_driver_ext *x = this->ext;
-  char *file_name;
+        case TEXT_ITEM_PARAGRAPH:
+          print_title_tag (html->file, "P", s);
+          break;
 
-  file_name = chart_draw_png (chart, x->chart_file_name, x->chart_cnt++);
-  fprintf (x->file, "<IMG SRC=\"%s\"/>", file_name);
-  free (file_name);
-}
+        case TEXT_ITEM_MONOSPACE:
+          print_title_tag (html->file, "PRE", s); /* should be <P><TT> */
+          break;
 
-static void
-html_submit (struct outp_driver *this, struct som_entity *s)
-{
-  extern struct som_table_class tab_table_class;
+        case TEXT_ITEM_BLANK_LINE:
+          fputs ("<BR>", html->file);
+          break;
 
-  assert (s->class == &tab_table_class ) ;
+        case TEXT_ITEM_EJECT_PAGE:
+          /* Nothing to do. */
+          break;
 
-  switch (s->type)
-    {
-    case SOM_TABLE:
-      output_tab_table ( this, (struct tab_table *) s->ext);
-      break;
-    default:
-      NOT_REACHED ();
+        case TEXT_ITEM_COMMENT:
+        case TEXT_ITEM_ECHO:
+          /* We print out syntax anyway, so nothing to do here either. */
+          break;
+        }
     }
 }
 
@@ -249,6 +338,9 @@ escape_string (FILE *file,
         case ' ':
           fputs (space, file);
           break;
+        case '"':
+          fputs ("&quot;", file);
+          break;
         default:
           putc (c, file);
           break;
@@ -256,135 +348,150 @@ escape_string (FILE *file,
     }
 }
 
-/* Outputs content for a cell with options OPTS and contents
-   TEXT. */
-void
-html_put_cell_contents (struct outp_driver *this,
-                        unsigned int opts, const struct substring text)
+static void
+put_border (FILE *file, int n_borders, int style, const char *border_name)
 {
-  struct html_driver_ext *x = this->ext;
-
-  if (!(opts & TAB_EMPTY))
-    {
-      if (opts & TAB_EMPH)
-        fputs ("<EM>", x->file);
-      if (opts & TAB_FIX)
-        {
-          fputs ("<TT>", x->file);
-          escape_string (x->file, ss_data (text), ss_length (text), "&nbsp;");
-          fputs ("</TT>", x->file);
-        }
-      else
-        {
-          size_t initial_spaces = ss_span (text, ss_cstr (CC_SPACES));
-          escape_string (x->file,
-                         ss_data (text) + initial_spaces,
-                         ss_length (text) - initial_spaces,
-                         " ");
-        }
-      if (opts & TAB_EMPH)
-        fputs ("</EM>", x->file);
-    }
+  fprintf (file, "%sborder-%s: %s",
+           n_borders == 0 ? " STYLE=\"" : "; ",
+           border_name,
+           style == TAL_1 ? "thin solid" : "double");
 }
 
-/* Write table T to THIS output driver. */
 static void
-output_tab_table (struct outp_driver *this, struct tab_table *t)
+html_output_table (struct html_driver *html, struct table_item *item)
 {
-  struct html_driver_ext *x = this->ext;
+  const struct table *t = table_item_get_table (item);
+  const char *caption;
+  int x, y;
 
-  if (tab_nr (t) == 1 && tab_nc (t) == 1)
-    {
-      fputs ("<P>", x->file);
-      html_put_cell_contents (this, t->ct[0], *t->cc);
-      fputs ("</P>\n", x->file);
+  fputs ("<TABLE>\n", html->file);
 
-      return;
+  caption = table_item_get_caption (item);
+  if (caption != NULL)
+    {
+      fputs ("  <CAPTION>", html->file);
+      escape_string (html->file, caption, strlen (caption), " ");
+      fputs ("</CAPTION>\n", html->file);
     }
 
-  fputs ("<TABLE BORDER=1>\n", x->file);
-
-  if (t->title != NULL)
+  for (y = 0; y < table_nr (t); y++)
     {
-      fprintf (x->file, "  <CAPTION>");
-      escape_string (x->file, t->title, strlen (t->title), " ");
-      fputs ("</CAPTION>\n", x->file);
-    }
+      fputs ("  <TR>\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 <TD> or <TH> 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");
+            }
 
-  {
-    int r;
-    unsigned char *ct = t->ct;
-
-    for (r = 0; r < tab_nr (t); r++)
-      {
-       int c;
-
-       fputs ("  <TR>\n", x->file);
-       for (c = 0; c < tab_nc (t); c++, ct++)
-         {
-            struct substring *cc;
-            const char *tag;
-            struct tab_joined_cell *j = NULL;
-
-            cc = t->cc + c + r * tab_nc (t);
-           if (*ct & TAB_JOIN)
-              {
-                j = (struct tab_joined_cell *) ss_data (*cc);
-                cc = &j->contents;
-                if (j->x1 != c || j->y1 != r)
-                  continue;
-              }
-
-            /* Output <TD> or <TH> tag. */
-            tag = (r < tab_t (t) || r >= tab_nr (t) - tab_b (t)
-                   || c < tab_l (t) || c >= tab_nc (t) - tab_r (t)) ? "TH" : "TD";
-            fprintf (x->file, "    <%s ALIGN=%s",
-                     tag,
-                     (*ct & TAB_ALIGN_MASK) == TAB_LEFT ? "LEFT"
-                     : (*ct & TAB_ALIGN_MASK) == TAB_RIGHT ? "RIGHT"
-                     : "CENTER");
-           if (*ct & TAB_JOIN)
-             {
-               if (j->x2 - j->x1 > 1)
-                 fprintf (x->file, " COLSPAN=%d", j->x2 - j->x1);
-               if (j->y2 - j->y1 > 1)
-                 fprintf (x->file, " ROWSPAN=%d", j->y2 - j->y1);
-             }
-           putc ('>', x->file);
-
-            /* Output cell contents. */
-            html_put_cell_contents (this, *ct, *cc);
-
-            /* Output </TH> or </TD>. */
-           fprintf (x->file, "</%s>\n", tag);
-         }
-       fputs ("  </TR>\n", x->file);
-      }
-  }
-
-  fputs ("</TABLE>\n\n", x->file);
-}
+          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);
 
-/* HTML driver class. */
-const struct outp_class html_class =
-  {
-    "html",
-    1,
+          if (top > TAL_GAP || bottom > TAL_GAP
+              || left > TAL_GAP || right > TAL_GAP)
+            {
+              fputs (" STYLE=\"", html->file);
+              if (top > TAL_GAP)
+                fprintf (html->file, "border-top: %s",
+                         top == TAL_1 ? "thin solid" : "double");
+
+              if (top > TAL_GAP && left > TAL_GAP)
+                fputs ("; ", html->file);
+
+              if (left > TAL_GAP)
+                fprintf (html->file, "border-left: %s",
+                         left == TAL_1 ? "thin solid" : "double");
+              fputs ("\"", html->file);
+            }
 
-    html_open_driver,
-    html_close_driver,
+          putc ('>', html->file);
 
-    NULL,
-    NULL,
-    NULL,
+          /* Output cell contents. */
+          s = cell.contents;
+          if (cell.options & TAB_EMPH)
+            fputs ("<EM>", html->file);
+          if (cell.options & TAB_FIX)
+            {
+              fputs ("<TT>", html->file);
+              escape_string (html->file, s, strlen (s), "&nbsp;");
+              fputs ("</TT>", html->file);
+            }
+          else
+            {
+              s += strspn (s, CC_SPACES);
+              escape_string (html->file, s, strlen (s), " ");
+            }
+          if (cell.options & TAB_EMPH)
+            fputs ("</EM>", html->file);
 
-    html_output_chart,
+          /* Output </TH> or </TD>. */
+          fprintf (html->file, "</%s>\n", tag);
 
-    html_submit,
+          table_cell_free (&cell);
+        }
+      fputs ("  </TR>\n", html->file);
+    }
 
-    NULL,
-    NULL,
+  fputs ("</TABLE>\n\n", html->file);
+}
+
+const struct output_driver_class html_class =
+  {
+    "html",
+    html_create,
+    html_destroy,
+    html_submit,
     NULL,
   };
diff --git a/src/output/htmlP.h b/src/output/htmlP.h
deleted file mode 100644 (file)
index 05340f4..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-/* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000 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 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.
-
-   You should have received a copy of the GNU General Public License
-   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
-
-#if !htmlP_h
-#define htmlP_h 1
-
-#include <data/file-name.h>
-
-/* HTML output driver extension record. */
-struct html_driver_ext
-  {
-    char *file_name;
-    char *chart_file_name;
-    FILE *file;
-
-    size_t chart_cnt;
-  };
-
-extern const struct outp_class html_class;
-
-struct outp_driver;
-void html_put_cell_contents (struct outp_driver *this,
-                             unsigned int opts, struct substring text);
-
-#endif /* !htmlP_h */
diff --git a/src/output/manager.c b/src/output/manager.c
deleted file mode 100644 (file)
index 9b90139..0000000
+++ /dev/null
@@ -1,390 +0,0 @@
-/* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000, 2007, 2009 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 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.
-
-   You should have received a copy of the GNU General Public License
-   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
-
-#include <config.h>
-
-#include <output/manager.h>
-
-#include <stdio.h>
-#include <stdlib.h>
-
-#include <libpspp/assertion.h>
-#include <output/output.h>
-
-#include "gl/xalloc.h"
-
-/* Table. */
-static int table_num = 1;
-static int subtable_num;
-
-/* Name of PSPP's current command, or NULL if outside a command. */
-static char *command_name;
-\f
-struct som_entity *
-som_entity_clone (struct som_entity *entity)
-{
-  struct som_entity *copy = xmemdup (entity, sizeof *entity);
-  copy->command_name = xstrdup (entity->command_name);
-  return copy;
-}
-
-void
-som_entity_destroy (struct som_entity *entity)
-{
-  if (entity != NULL)
-    {
-      free (entity->command_name);
-      free (entity);
-    }
-}
-
-/* Increments table_num so different procedures' output can be
-   distinguished. */
-void
-som_new_series (void)
-{
-  if (subtable_num != 0)
-    {
-      table_num++;
-      subtable_num = 0;
-    }
-}
-
-/* Sets COMMAND_NAME as the name of the current command,
-   for embedding in output. */
-void
-som_set_command_name (const char *command_name_)
-{
-  free (command_name);
-  command_name = command_name_ ? xstrdup (command_name_) : NULL;
-}
-
-/* Ejects the paper for all active devices. */
-void
-som_eject_page (void)
-{
-  struct outp_driver *d;
-
-  for (d = outp_drivers (NULL); d; d = outp_drivers (d))
-    outp_eject_page (d);
-}
-
-/* Flushes output on all active devices. */
-void
-som_flush (void)
-{
-  struct outp_driver *d;
-
-  for (d = outp_drivers (NULL); d; d = outp_drivers (d))
-    outp_flush (d);
-}
-
-/* Skip down a single line on all active devices. */
-void
-som_blank_line (void)
-{
-  struct outp_driver *d;
-
-  for (d = outp_drivers (NULL); d; d = outp_drivers (d))
-    if (d->page_open && d->cp_y != 0)
-      d->cp_y += d->font_height;
-}
-\f
-static void render_columns (void *r, struct outp_driver *, struct som_entity *,
-                            int tw, int th,
-                            int hl, int hr, int ht, int hb);
-static void render_simple (void *r, struct outp_driver *, struct som_entity *,
-                           int tw, int th,
-                           int hl, int hr, int ht, int hb);
-static void render_segments (void *r, struct outp_driver *,
-                             struct som_entity *,
-                             int tw, int th,
-                             int hl, int hr, int ht, int hb);
-
-static void output_entity (struct outp_driver *, struct som_entity *);
-
-/* Output table T to appropriate output devices. */
-void
-som_submit (struct som_entity *t)
-{
-  struct outp_driver *d;
-  unsigned int flags;
-
-#if DEBUGGING
-  static int entry;
-
-  assert (entry++ == 0);
-#endif
-
-  t->class->flags (t, &flags);
-  if (!(flags & SOMF_NO_TITLE))
-    subtable_num++;
-  t->table_num = table_num;
-  t->subtable_num = subtable_num;
-  t->command_name = command_name ? xstrdup (command_name) : NULL;
-
-  if (t->type == SOM_TABLE)
-    {
-      int hl, hr, ht, hb;
-      int nc, nr;
-
-      t->class->count (t, &nc, &nr);
-      t->class->headers (t, &hl, &hr, &ht, &hb);
-      if (hl + hr > nc || ht + hb > nr)
-       {
-         fprintf (stderr, "headers: (l,r)=(%d,%d), (t,b)=(%d,%d) "
-                   "in table size (%d,%d)\n",
-                   hl, hr, ht, hb, nc, nr);
-         NOT_REACHED ();
-       }
-      else if (hl + hr == nc)
-       fprintf (stderr, "warning: headers (l,r)=(%d,%d) in table width %d\n",
-                hl, hr, nc);
-      else if (ht + hb == nr)
-       fprintf (stderr, "warning: headers (t,b)=(%d,%d) in table height %d\n",
-                ht, hb, nr);
-    }
-
-  for (d = outp_drivers (NULL); d; d = outp_drivers (d))
-    output_entity (d, t);
-
-#if DEBUGGING
-  assert (--entry == 0);
-#endif
-}
-
-static bool
-check_fits_width (struct som_entity *t, const struct outp_driver *d, void *r)
-{
-  int hl, hr, ht, hb;
-  int nc, nr;
-  int i;
-
-  t->class->headers (t, &hl, &hr, &ht, &hb);
-  t->class->count (t, &nc, &nr);
-  for (i = hl; i < nc - hr; i++)
-    {
-      int end, actual;
-      t->class->cumulate (r, SOM_COLUMNS, i, &end, d->width, &actual);
-      if (end == i)
-        return false;
-    }
-
-  return true;
-}
-
-static bool
-check_fits_length (struct som_entity *t, const struct outp_driver *d, void *r)
-{
-  int hl, hr, ht, hb;
-  int nc, nr;
-  int i;
-
-  t->class->headers (t, &hl, &hr, &ht, &hb);
-  t->class->count (t, &nc, &nr);
-  for (i = ht; i < nr - hb; i++)
-    {
-      int end, actual;
-      t->class->cumulate (r, SOM_ROWS, i, &end, d->length, &actual);
-      if (end == i)
-        return false;
-    }
-
-  return true;
-}
-
-/* Output entity T to driver D. */
-static void
-output_entity (struct outp_driver *d, struct som_entity *t)
-{
-  bool fits_width, fits_length;
-  unsigned int flags;
-  int hl, hr, ht, hb;
-  int tw, th;
-  int nc, nr;
-  int cs;
-  void *r;
-
-  outp_open_page (d);
-  if (d->class->special)
-    {
-      d->class->submit (d, t);
-      return;
-    }
-
-  t->class->headers (t, &hl, &hr, &ht, &hb);
-  t->class->count (t, &nc, &nr);
-  t->class->columns (t, &cs);
-  t->class->flags (t, &flags);
-
-  r = t->class->render_init (t, d, hl, hr, ht, hb);
-
-  fits_width = check_fits_width (t, d, r);
-  fits_length = check_fits_length (t, d, r);
-  if (!fits_width || !fits_length)
-    {
-      t->class->render_free (r);
-
-      if (!fits_width)
-        hl = hr = 0;
-      if (!fits_length)
-        ht = hb = 0;
-
-      r = t->class->render_init (t, d, hl, hr, ht, hb);
-    }
-  t->class->area (r, &tw, &th);
-
-  if (!(flags & SOMF_NO_SPACING) && d->cp_y != 0)
-    d->cp_y += d->font_height;
-
-  if (cs != SOM_COL_NONE
-      && 2 * (tw + d->prop_em_width) <= d->width
-      && nr - (ht + hb) > 5)
-    render_columns (r, d, t, tw, th, hl, hr, ht, hb);
-  else if (tw < d->width && th + d->cp_y < d->length)
-    render_simple (r, d, t, tw, th, hl, hr, ht, hb);
-  else
-    render_segments (r, d, t, tw, th, hl, hr, ht, hb);
-
-  t->class->render_free (r);
-}
-
-/* Render the table into multiple columns. */
-static void
-render_columns (void *r, struct outp_driver *d, struct som_entity *t,
-                int tw, int th UNUSED,
-                int hl UNUSED, int hr UNUSED, int ht, int hb)
-{
-  int y0, y1;
-  int max_len = 0;
-  int index = 0;
-  int nc, nr;
-  int cs;
-
-  t->class->count (t, &nc, &nr);
-  t->class->columns (t, &cs);
-
-  assert (cs == SOM_COL_DOWN);
-  assert (d->cp_x == 0);
-
-  for (y0 = ht; y0 < nr - hb; y0 = y1)
-    {
-      int len;
-
-      t->class->cumulate (r, SOM_ROWS, y0, &y1, d->length - d->cp_y, &len);
-
-      if (y0 == y1)
-       {
-         assert (d->cp_y);
-         outp_eject_page (d);
-       }
-      else
-        {
-         if (len > max_len)
-           max_len = len;
-
-         t->class->title (r, index++, 0, t->table_num, t->subtable_num,
-                           t->command_name);
-         t->class->render (r, 0, y0, nc, y1);
-
-         d->cp_x += tw + 2 * d->prop_em_width;
-         if (d->cp_x + tw > d->width)
-           {
-             d->cp_x = 0;
-             d->cp_y += max_len;
-             max_len = 0;
-           }
-       }
-    }
-
-  if (d->cp_x > 0)
-    {
-      d->cp_x = 0;
-      d->cp_y += max_len;
-    }
-}
-
-/* Render the table by itself on the current page. */
-static void
-render_simple (void *r, struct outp_driver *d, struct som_entity *t,
-               int tw, int th,
-               int hl, int hr, int ht, int hb)
-{
-  int nc, nr;
-
-  t->class->count (t, &nc, &nr);
-
-  assert (d->cp_x == 0);
-  assert (tw < d->width && th + d->cp_y < d->length);
-
-  t->class->title (r, 0, 0, t->table_num, t->subtable_num, t->command_name);
-  t->class->render (r, hl, ht, nc - hr, nr - hb);
-  d->cp_y += th;
-}
-
-/* General table breaking routine. */
-static void
-render_segments (void *r, struct outp_driver *d, struct som_entity *t,
-                 int tw UNUSED, int th UNUSED,
-                 int hl, int hr, int ht, int hb)
-{
-  int count = 0;
-
-  int x_index;
-  int x0, x1;
-
-  int nc, nr;
-
-  assert (d->cp_x == 0);
-
-  t->class->count (t, &nc, &nr);
-  for (x_index = 0, x0 = hl; x0 < nc - hr; x0 = x1, x_index++)
-    {
-      int y_index;
-      int y0, y1;
-
-      t->class->cumulate (r, SOM_COLUMNS, x0, &x1, d->width, NULL);
-      if (x_index == 0 && x1 != nc - hr)
-       x_index++;
-
-      for (y_index = 0, y0 = ht; y0 < nr - hb; y0 = y1, y_index++)
-       {
-         int len;
-
-         if (count++ != 0 && d->cp_y != 0)
-           d->cp_y += d->font_height;
-
-         t->class->cumulate (r, SOM_ROWS, y0, &y1, d->length - d->cp_y, &len);
-         if (y_index == 0 && y1 != nr - hb)
-           y_index++;
-
-         if (y0 == y1)
-           {
-             assert (d->cp_y);
-             outp_eject_page (d);
-           }
-          else
-            {
-             t->class->title (r, x_index ? x_index : y_index,
-                              x_index ? y_index : 0,
-                               t->table_num, t->subtable_num, t->command_name);
-             t->class->render (r, x0, y0, x1, y1);
-
-             d->cp_y += len;
-           }
-       }
-    }
-}
diff --git a/src/output/manager.h b/src/output/manager.h
deleted file mode 100644 (file)
index e727698..0000000
+++ /dev/null
@@ -1,113 +0,0 @@
-/* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000, 2009 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 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.
-
-   You should have received a copy of the GNU General Public License
-   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
-
-#if !som_h
-#define som_h 1
-
-/* Structured Output Manager.
-
-   som considers the output stream to be a series of tables.  Each
-   table is made up of a rectangular grid of cells.  Cells can be
-   joined to form larger cells.  Rows and columns can be separated by
-   rules of various types.  Tables too large to fit on a single page
-   will be divided into sections.  Rows and columns can be designated
-   as headers, which causes them to be repeated in each section.
-
-   Every table is an instance of a particular table class.  A table
-   class is responsible for keeping track of cell data, for handling
-   requests from the som, and finally for rendering cell data to the
-   output drivers.  Tables may implement these operations in any way
-   desired, and in fact almost every operation performed by som may be
-   overridden in a table class.  */
-
-#include <stdbool.h>
-
-enum som_type
-  {
-    SOM_TABLE
-  } ;
-
-/* Entity (Table or Chart) . */
-struct som_entity
-  {
-    const struct som_table_class *class;       /* Table class. */
-    enum som_type type;                 /* Table or Chart */
-    void *ext;                         /* Owned by table or chart class. */
-    int table_num;                      /* Table number. */
-    int subtable_num;                   /* Sub-table number. */
-    char *command_name;                 /* Command that yielded this output. */
-  };
-
-struct som_entity *som_entity_clone (struct som_entity *);
-void som_entity_destroy (struct som_entity *);
-
-/* Group styles. */
-enum
-  {
-    SOM_COL_NONE,                      /* No columns. */
-    SOM_COL_DOWN                       /* Columns down first. */
-  };
-
-/* Cumulation types. */
-enum
-  {
-    SOM_ROWS,                   /* Rows. */
-    SOM_COLUMNS                 /* Columns. */
-  };
-
-/* Flags. */
-enum
-  {
-    SOMF_NONE = 0,
-    SOMF_NO_SPACING = 01,      /* No spacing before the table. */
-    SOMF_NO_TITLE = 02         /* No title. */
-  };
-
-/* Table class. */
-struct outp_driver;
-struct som_table_class
-  {
-    /* Operations on tables. */
-    void (*count) (struct som_entity *, int *n_columns, int *n_rows);
-    void (*columns) (struct som_entity *, int *style);
-    void (*headers) (struct som_entity *, int *l, int *r, int *t, int *b);
-    void (*flags) (struct som_entity *, unsigned *);
-
-    /* Creating and freeing driver-specific table rendering data. */
-    void *(*render_init) (struct som_entity *, struct outp_driver *,
-                          int l, int r, int t, int b);
-    void (*render_free) (void *);
-
-    /* Rendering operations. */
-    void (*area) (void *, int *horiz, int *vert);
-    void (*cumulate) (void *, int cumtype, int start, int *end,
-                      int max, int *actual);
-    void (*title) (void *, int x, int y, int table_num, int subtable_num,
-                   const char *command_name);
-    void (*render) (void *, int x1, int y1, int x2, int y2);
-  };
-
-/* Submission. */
-void som_new_series (void);
-void som_set_command_name (const char *);
-void som_submit (struct som_entity *t);
-
-/* Miscellaneous. */
-void som_eject_page (void);
-void som_blank_line (void);
-void som_flush (void);
-
-#endif /* som_h */
diff --git a/src/output/measure.c b/src/output/measure.c
new file mode 100644 (file)
index 0000000..8f6e666
--- /dev/null
@@ -0,0 +1,311 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 1997-9, 2000, 2007, 2009 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 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.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+
+#include <output/measure.h>
+
+#include <ctype.h>
+#include <errno.h>
+#if HAVE_LC_PAPER
+#include <langinfo.h>
+#endif
+#include <libpspp/str.h>
+#include <stdlib.h>
+
+#include <data/file-name.h>
+
+#include "gl/error.h"
+
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
+
+static double parse_unit (const char *);
+static bool parse_paper_size (const char *, int *h, int *v);
+static bool get_standard_paper_size (struct substring name, int *h, int *v);
+static bool read_paper_conf (const char *file_name, int *h, int *v);
+static bool get_default_paper_size (int *h, int *v);
+
+/* Determines the size of a dimensional measurement and returns
+   the size in units of 1/72000".  Units are assumed to be
+   millimeters unless otherwise specified.  Returns -1 on
+   error. */
+int
+measure_dimension (const char *dimen)
+{
+  double raw, factor;
+  char *tail;
+
+  /* Number. */
+  raw = strtod (dimen, &tail);
+  if (raw < 0.0)
+    goto syntax_error;
+
+  /* Unit. */
+  factor = parse_unit (tail);
+  if (factor == 0.0)
+    goto syntax_error;
+
+  return raw * factor;
+
+syntax_error:
+  error (0, 0, _("`%s' is not a valid length."), dimen);
+  return -1;
+}
+
+/* Stores the dimensions, in 1/72000" units, of paper identified
+   by SIZE into *H and *V.  SIZE can be the name of a kind of
+   paper ("a4", "letter", ...) or a pair of dimensions
+   ("210x297", "8.5x11in", ...).  Returns true on success, false
+   on failure.  On failure, *H and *V are set for A4 paper. */
+bool
+measure_paper (const char *size, int *h, int *v)
+{
+  struct substring s;
+  bool ok;
+
+  s = ss_cstr (size);
+  ss_trim (&s, ss_cstr (CC_SPACES));
+
+  if (ss_is_empty (s))
+    {
+      /* Treat empty string as default paper size. */
+      ok = get_default_paper_size (h, v);
+    }
+  else if (isdigit (ss_first (s)))
+    {
+      /* Treat string that starts with digit as explicit size. */
+      ok = parse_paper_size (size, h, v);
+      if (!ok)
+        error (0, 0, _("syntax error in paper size `%s'"), size);
+    }
+  else
+    {
+      /* Check against standard paper sizes. */
+      ok = get_standard_paper_size (s, h, v);
+    }
+
+  /* Default to A4 on error. */
+  if (!ok)
+    {
+      *h = 210 * (72000 / 25.4);
+      *v = 297 * (72000 / 25.4);
+    }
+  return ok;
+}
+\f
+/* Parses UNIT as a dimensional unit.  Returns the multiplicative
+   factor needed to change a quantity measured in that unit into
+   1/72000" units.  If UNIT is empty, it is treated as
+   millimeters.  If the unit is unrecognized, returns 0. */
+static double
+parse_unit (const char *unit)
+{
+  struct unit
+    {
+      char name[3];
+      double factor;
+    };
+
+  static const struct unit units[] =
+    {
+      {"pt", 72000 / 72},
+      {"pc", 72000 / 72 * 12.0},
+      {"in", 72000},
+      {"cm", 72000 / 2.54},
+      {"mm", 72000 / 25.4},
+      {"", 72000 / 25.4},
+    };
+
+  const struct unit *p;
+
+  unit += strspn (unit, CC_SPACES);
+  for (p = units; p < units + sizeof units / sizeof *units; p++)
+    if (!strcasecmp (unit, p->name))
+      return p->factor;
+  return 0.0;
+}
+
+/* Stores the dimensions in 1/72000" units of paper identified by
+   SIZE, which is of form `HORZ x VERT [UNIT]' where HORZ and
+   VERT are numbers and UNIT is an optional unit of measurement,
+   into *H and *V.  Return true on success. */
+static bool
+parse_paper_size (const char *size, int *h, int *v)
+{
+  double raw_h, raw_v, factor;
+  char *tail;
+
+  /* Width. */
+  raw_h = strtod (size, &tail);
+  if (raw_h <= 0.0)
+    return false;
+
+  /* Delimiter. */
+  tail += strspn (tail, CC_SPACES "x,");
+
+  /* Length. */
+  raw_v = strtod (tail, &tail);
+  if (raw_v <= 0.0)
+    return false;
+
+  /* Unit. */
+  factor = parse_unit (tail);
+  if (factor == 0.0)
+    return false;
+
+  *h = raw_h * factor + .5;
+  *v = raw_v * factor + .5;
+  return true;
+}
+
+static bool
+get_standard_paper_size (struct substring name, int *h, int *v)
+{
+  static const char *sizes[][2] =
+    {
+      {"a0", "841 x 1189 mm"},
+      {"a1", "594 x 841 mm"},
+      {"a2", "420 x 594 mm"},
+      {"a3", "297 x 420 mm"},
+      {"a4", "210 x 297 mm"},
+      {"a5", "148 x 210 mm"},
+      {"b5", "176 x 250 mm"},
+      {"a6", "105 x 148 mm"},
+      {"a7", "74 x 105 mm"},
+      {"a8", "52 x 74 mm"},
+      {"a9", "37 x 52 mm"},
+      {"a10", "26 x 37 mm"},
+      {"b0", "1000 x 1414 mm"},
+      {"b1", "707 x 1000 mm"},
+      {"b2", "500 x 707 mm"},
+      {"b3", "353 x 500 mm"},
+      {"b4", "250 x 353 mm"},
+      {"letter", "612 x 792 pt"},
+      {"legal", "612 x 1008 pt"},
+      {"executive", "522 x 756 pt"},
+      {"note", "612 x 792 pt"},
+      {"11x17", "792 x 1224 pt"},
+      {"tabloid", "792 x 1224 pt"},
+      {"statement", "396 x 612 pt"},
+      {"halfletter", "396 x 612 pt"},
+      {"halfexecutive", "378 x 522 pt"},
+      {"folio", "612 x 936 pt"},
+      {"quarto", "610 x 780 pt"},
+      {"ledger", "1224 x 792 pt"},
+      {"archA", "648 x 864 pt"},
+      {"archB", "864 x 1296 pt"},
+      {"archC", "1296 x 1728 pt"},
+      {"archD", "1728 x 2592 pt"},
+      {"archE", "2592 x 3456 pt"},
+      {"flsa", "612 x 936 pt"},
+      {"flse", "612 x 936 pt"},
+      {"csheet", "1224 x 1584 pt"},
+      {"dsheet", "1584 x 2448 pt"},
+      {"esheet", "2448 x 3168 pt"},
+    };
+
+  size_t i;
+
+  for (i = 0; i < sizeof sizes / sizeof *sizes; i++)
+    if (ss_equals_case (ss_cstr (sizes[i][0]), name))
+      {
+        bool ok = parse_paper_size (sizes[i][1], h, v);
+        assert (ok);
+        return ok;
+      }
+  error (0, 0, _("unknown paper type `%.*s'"),
+         (int) ss_length (name), ss_data (name));
+  return false;
+}
+
+/* Reads file FILE_NAME to find a paper size.  Stores the
+   dimensions, in 1/72000" units, into *H and *V.  Returns true
+   on success, false on failure. */
+static bool
+read_paper_conf (const char *file_name, int *h, int *v)
+{
+  struct string line = DS_EMPTY_INITIALIZER;
+  int line_number = 0;
+  FILE *file;
+
+  file = fopen (file_name, "r");
+  if (file == NULL)
+    {
+      error (0, errno, _("error opening \"%s\""), file_name);
+      return false;
+    }
+
+  for (;;)
+    {
+      struct substring name;
+
+      if (!ds_read_config_line (&line, &line_number, file))
+       {
+         if (ferror (file))
+           error (0, errno, _("error reading \"%s\""), file_name);
+         break;
+       }
+
+      name = ds_ss (&line);
+      ss_trim (&name, ss_cstr (CC_SPACES));
+      if (!ss_is_empty (name))
+        {
+          bool ok = get_standard_paper_size (name, h, v);
+          fclose (file);
+          ds_destroy (&line);
+          return ok;
+        }
+    }
+
+  fclose (file);
+  ds_destroy (&line);
+  error (0, 0, _("paper size file \"%s\" does not state a paper size"),
+         file_name);
+  return false;
+}
+
+/* The user didn't specify a paper size, so let's choose a
+   default based on his environment.  Stores the
+   dimensions, in 1/72000" units, into *H and *V.  Returns true
+   on success, false on failure. */
+static bool
+get_default_paper_size (int *h, int *v)
+{
+  /* libpaper in Debian (and other distributions?) allows the
+     paper size to be specified in $PAPERSIZE or in a file
+     specified in $PAPERCONF. */
+  if (getenv ("PAPERSIZE") != NULL)
+    return get_standard_paper_size (ss_cstr (getenv ("PAPERSIZE")), h, v);
+  if (getenv ("PAPERCONF") != NULL)
+    return read_paper_conf (getenv ("PAPERCONF"), h, v);
+
+#if HAVE_LC_PAPER
+  /* LC_PAPER is a non-standard glibc extension. */
+  *h = (int) nl_langinfo(_NL_PAPER_WIDTH) * (72000 / 25.4);
+  *v = (int) nl_langinfo(_NL_PAPER_HEIGHT) * (72000 / 25.4);
+  if (*h > 0 && *v > 0)
+     return true;
+#endif
+
+  /* libpaper defaults to /etc/papersize. */
+  if (fn_exists ("/etc/papersize"))
+    return read_paper_conf ("/etc/papersize", h, v);
+
+  /* Can't find a default. */
+  return false;
+}
+
diff --git a/src/output/measure.h b/src/output/measure.h
new file mode 100644 (file)
index 0000000..4b6fab3
--- /dev/null
@@ -0,0 +1,25 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 1997-9, 2000, 2007, 2009 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 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.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef OUTPUT_MEASURE_H
+#define OUTPUT_MEASURE_H 1
+
+#include <stdbool.h>
+
+int measure_dimension (const char *dimen);
+bool measure_paper (const char *size, int *h, int *v);
+
+#endif /* output/measure.h */
diff --git a/src/output/mk-class-boilerplate b/src/output/mk-class-boilerplate
new file mode 100755 (executable)
index 0000000..b2191aa
--- /dev/null
@@ -0,0 +1,77 @@
+#! /usr/bin/perl
+
+while (<>) {
+    if (my ($class, $super) = /boilerplate for ([a-zA-Z0-9_]+), a subclass of ([a-zA-Z0-9_]+)/) {
+       while (<>) {
+           last if /\f/;
+       }
+       print <<EOF;
+/* This boilerplate for ${class}, a subclass of ${super}, was
+   autogenerated by mk-class-boilerplate. */
+
+#include <assert.h>
+#include <libpspp/cast.h>
+
+extern const struct ${super}_class ${class}_class;
+
+/* Returns true if SUPER is a ${class}, otherwise false. */
+static inline bool
+is_${class} (const struct ${super} *super)
+{
+  return super->class == &${class}_class;
+}
+
+/* Returns SUPER converted to ${class}.  SUPER must be a ${class}, as
+   reported by is_${class}. */
+static inline struct ${class} *
+to_${class} (const struct ${super} *super)
+{
+  assert (is_${class} (super));
+  return UP_CAST (super, struct ${class}, ${super});
+}
+
+/* Returns INSTANCE converted to ${super}. */
+static inline struct ${super} *
+${class}_super (const struct ${class} *instance)
+{
+  return CONST_CAST (struct ${super} *, &instance->${super});
+}
+
+/* Increments INSTANCE's reference count and returns INSTANCE. */
+static inline struct ${class} *
+${class}_ref (const struct ${class} *instance)
+{
+  return to_${class} (${super}_ref (&instance->${super}));
+}
+
+/* Decrements INSTANCE's reference count, then destroys INSTANCE if
+   the reference count is now zero. */
+static inline void
+${class}_unref (struct ${class} *instance)
+{
+  ${super}_unref (&instance->${super});
+}
+
+/* Returns true if INSTANCE's reference count is greater than 1,
+   false otherwise. */
+static inline bool
+${class}_is_shared (const struct ${class} *instance)
+{
+  return ${super}_is_shared (&instance->${super});
+}
+
+EOF
+       if ($super ne 'output_item') {
+           print <<EOF;
+static inline void
+${class}_submit (struct ${class} *instance)
+{
+  ${super}_submit (&instance->${super});
+}
+EOF
+       } else {
+           print "void ${class}_submit (struct ${class} *);\n";
+       }
+    }
+    print;
+}
index 8471b6b3fb7eb46bb16a536b748161eac7337902..47b8c43ea27a38e49e6366c2d129459fe6ff54fe 100644 (file)
 
 #include <config.h>
 
-#include "gettext.h"
-#define _(msgid) gettext (msgid)
-
 /* A driver for creating OpenDocument Format text files from PSPP's output */
 
 #include <libpspp/assertion.h>
+#include <libpspp/cast.h>
+#include <libpspp/str.h>
 #include <libpspp/version.h>
+#include <output/driver-provider.h>
+#include <output/options.h>
+#include <output/tab.h>
+#include <output/table-item.h>
+#include <output/table-provider.h>
+#include <output/text-item.h>
 
-#include <output/manager.h>
-#include <output/output.h>
-#include <output/table.h>
-
-#include <time.h>
+#include <libgen.h>
+#include <libxml/xmlwriter.h>
 #include <pwd.h>
 #include <sys/stat.h>
 #include <sys/types.h>
-
-#include <libgen.h>
-
-#include <libxml/xmlwriter.h>
+#include <time.h>
+#include <unistd.h>
 
 #include "xalloc.h"
-
 #include "error.h"
 
-#define _xml(X) (const xmlChar *)(X)
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
 
+#define _xml(X) (const xmlChar *)(X)
 
-struct odf_driver_options
+struct odt_driver
 {
-  struct outp_driver *driver;
-  
+  struct output_driver driver;
+
   char *file_name;            /* Output file name. */
   bool debug;
-};
 
-
-struct odt_driver_ext 
-{
   /* The name of the temporary directory used to construct the ODF */
   char *dirname;
 
@@ -64,10 +61,16 @@ struct odt_driver_ext
   /* Writer fot the manifest.xml file */
   xmlTextWriterPtr manifest_wtr;
 
-  struct odf_driver_options opts;
+  /* Number of tables so far. */
+  int table_num;
 };
 
-
+static struct odt_driver *
+odt_driver_cast (struct output_driver *driver)
+{
+  assert (driver->class == &odt_class);
+  return UP_CAST (driver, struct odt_driver, driver);
+}
 
 /* Create the "mimetype" file needed by ODF */
 static void
@@ -87,7 +90,7 @@ create_mimetype (const char *dirname)
 
 /* Create a new XML file called FILENAME in the temp directory, and return a writer for it */
 static xmlTextWriterPtr
-create_writer (const struct odt_driver_ext *driver, const char *filename)
+create_writer (const struct odt_driver *driver, const char *filename)
 {
   char *copy = NULL;
   xmlTextWriterPtr w;
@@ -112,20 +115,20 @@ create_writer (const struct odt_driver_ext *driver, const char *filename)
 
 
 static void
-register_file (struct odt_driver_ext *x, const char *filename)
+register_file (struct odt_driver *odt, const char *filename)
 {
-  assert (x->manifest_wtr);
-  xmlTextWriterStartElement (x->manifest_wtr, _xml("manifest:file-entry"));
-  xmlTextWriterWriteAttribute (x->manifest_wtr, _xml("manifest:media-type"),  _xml("text/xml"));
-  xmlTextWriterWriteAttribute (x->manifest_wtr, _xml("manifest:full-path"),  _xml (filename));
-  xmlTextWriterEndElement (x->manifest_wtr);
+  assert (odt->manifest_wtr);
+  xmlTextWriterStartElement (odt->manifest_wtr, _xml("manifest:file-entry"));
+  xmlTextWriterWriteAttribute (odt->manifest_wtr, _xml("manifest:media-type"),  _xml("text/xml"));
+  xmlTextWriterWriteAttribute (odt->manifest_wtr, _xml("manifest:full-path"),  _xml (filename));
+  xmlTextWriterEndElement (odt->manifest_wtr);
 }
 
 static void
-write_style_data (struct odt_driver_ext *x)
+write_style_data (struct odt_driver *odt)
 {
-  xmlTextWriterPtr w = create_writer (x, "styles.xml");
-  register_file (x, "styles.xml");
+  xmlTextWriterPtr w = create_writer (odt, "styles.xml");
+  register_file (odt, "styles.xml");
 
   xmlTextWriterStartElement (w, _xml ("office:document-styles"));
   xmlTextWriterWriteAttribute (w, _xml ("xmlns:office"),
@@ -214,10 +217,10 @@ write_style_data (struct odt_driver_ext *x)
 }
 
 static void
-write_meta_data (struct odt_driver_ext *x)
+write_meta_data (struct odt_driver *odt)
 {
-  xmlTextWriterPtr w = create_writer (x, "meta.xml");
-  register_file (x, "meta.xml");
+  xmlTextWriterPtr w = create_writer (odt, "meta.xml");
+  register_file (odt, "meta.xml");
 
   xmlTextWriterStartElement (w, _xml ("office:document-meta"));
   xmlTextWriterWriteAttribute (w, _xml ("xmlns:office"), _xml ("urn:oasis:names:tc:opendocument:xmlns:office:1.0"));
@@ -272,312 +275,241 @@ enum
   boolean_arg,
 };
 
-static const struct outp_option option_tab[] =
-{
-  {"output-file",              output_file_arg,0},
-
-  {"debug",                    boolean_arg,    1},
-
-  {NULL, 0, 0},
-};
-
-static bool
-handle_option (void *options_, const char *key, const struct string *val)
+static struct driver_option *
+opt (struct output_driver *d, struct string_map *options, const char *key,
+     const char *default_value)
 {
-  struct odf_driver_options *options = options_;
-  struct outp_driver *this = options->driver;
-  int subcat;
-  char *value = ds_cstr (val);
-
-  switch (outp_match_keyword (key, option_tab, &subcat))
-    {
-    case -1:
-      error (0, 0,
-             _("unknown configuration parameter `%s' for %s device "
-               "driver"), key, this->class->name);
-      break;
-    case output_file_arg:
-      free (options->file_name);
-      options->file_name = xstrdup (value);
-      break;
-    case boolean_arg:
-      if (!strcmp (value, "on") || !strcmp (value, "true")
-          || !strcmp (value, "yes") || atoi (value))
-        options->debug = true;
-      else if (!strcmp (value, "off") || !strcmp (value, "false")
-               || !strcmp (value, "no") || !strcmp (value, "0"))
-        options->debug = false;
-      else
-        {
-          error (0, 0, _("boolean value expected for %s"), key);
-          return false;
-        }
-      break;
-
-    default:
-      NOT_REACHED ();
-    }
-
-  return true;
+  return driver_option_get (d, options, key, default_value);
 }
 
-
-static bool
-odt_open_driver (const char *name, int types, struct substring option_string)
+static struct output_driver *
+odt_create (const char *name, enum output_device_type device_type,
+            struct string_map *o)
 {
-  struct odt_driver_ext *x;
-  struct outp_driver *this = outp_allocate_driver (&odt_class, name, types);
+  struct output_driver *d;
+  struct odt_driver *odt;
 
-  this->ext = x = xmalloc (sizeof *x);
+  odt = xzalloc (sizeof *odt);
+  d = &odt->driver;
+  output_driver_init (d, &odt_class, name, device_type);
 
-  x->opts.driver = this;
-  x->opts.file_name = xstrdup ("pspp.pdt");
-  x->opts.debug = false;
+  odt->file_name = parse_string (opt (d, o, "output-file", "pspp.pdt"));
+  odt->debug = parse_boolean (opt (d, o, "debug", "false"));
 
-  outp_parse_options (this->name, option_string, handle_option, &x->opts);
+  odt->dirname = xstrdup ("odt-XXXXXX");
+  mkdtemp (odt->dirname);
 
-  outp_register_driver (this);
-
-  x->dirname = xstrdup ("odt-XXXXXX");
-  mkdtemp (x->dirname);
-
-  create_mimetype (x->dirname);
+  create_mimetype (odt->dirname);
 
   /* Create the manifest */
-  x->manifest_wtr = create_writer (x, "META-INF/manifest.xml");
+  odt->manifest_wtr = create_writer (odt, "META-INF/manifest.xml");
 
-  xmlTextWriterStartElement (x->manifest_wtr, _xml("manifest:manifest"));
-  xmlTextWriterWriteAttribute (x->manifest_wtr, _xml("xmlns:manifest"),
+  xmlTextWriterStartElement (odt->manifest_wtr, _xml("manifest:manifest"));
+  xmlTextWriterWriteAttribute (odt->manifest_wtr, _xml("xmlns:manifest"),
                               _xml("urn:oasis:names:tc:opendocument:xmlns:manifest:1.0"));
 
 
   /* Add a manifest entry for the document as a whole */
-  xmlTextWriterStartElement (x->manifest_wtr, _xml("manifest:file-entry"));
-  xmlTextWriterWriteAttribute (x->manifest_wtr, _xml("manifest:media-type"),  _xml("application/vnd.oasis.opendocument.text"));
-  xmlTextWriterWriteAttribute (x->manifest_wtr, _xml("manifest:full-path"),  _xml("/"));
-  xmlTextWriterEndElement (x->manifest_wtr);
+  xmlTextWriterStartElement (odt->manifest_wtr, _xml("manifest:file-entry"));
+  xmlTextWriterWriteAttribute (odt->manifest_wtr, _xml("manifest:media-type"),  _xml("application/vnd.oasis.opendocument.text"));
+  xmlTextWriterWriteAttribute (odt->manifest_wtr, _xml("manifest:full-path"),  _xml("/"));
+  xmlTextWriterEndElement (odt->manifest_wtr);
 
 
-  write_meta_data (x);
-  write_style_data (x);
+  write_meta_data (odt);
+  write_style_data (odt);
 
-  x->content_wtr = create_writer (x, "content.xml");
-  register_file (x, "content.xml");
+  odt->content_wtr = create_writer (odt, "content.xml");
+  register_file (odt, "content.xml");
 
 
   /* Some necessary junk at the start */
-  xmlTextWriterStartElement (x->content_wtr, _xml("office:document-content"));
-  xmlTextWriterWriteAttribute (x->content_wtr, _xml("xmlns:office"),
+  xmlTextWriterStartElement (odt->content_wtr, _xml("office:document-content"));
+  xmlTextWriterWriteAttribute (odt->content_wtr, _xml("xmlns:office"),
                               _xml("urn:oasis:names:tc:opendocument:xmlns:office:1.0"));
 
-  xmlTextWriterWriteAttribute (x->content_wtr, _xml("xmlns:text"),
+  xmlTextWriterWriteAttribute (odt->content_wtr, _xml("xmlns:text"),
                               _xml("urn:oasis:names:tc:opendocument:xmlns:text:1.0"));
 
-  xmlTextWriterWriteAttribute (x->content_wtr, _xml("xmlns:table"),
+  xmlTextWriterWriteAttribute (odt->content_wtr, _xml("xmlns:table"),
                               _xml("urn:oasis:names:tc:opendocument:xmlns:table:1.0"));
 
-  xmlTextWriterWriteAttribute (x->content_wtr, _xml("office:version"), _xml("1.1"));
+  xmlTextWriterWriteAttribute (odt->content_wtr, _xml("office:version"), _xml("1.1"));
 
-  xmlTextWriterStartElement (x->content_wtr, _xml("office:body"));
-  xmlTextWriterStartElement (x->content_wtr, _xml("office:text"));
+  xmlTextWriterStartElement (odt->content_wtr, _xml("office:body"));
+  xmlTextWriterStartElement (odt->content_wtr, _xml("office:text"));
 
 
 
   /* Close the manifest */
-  xmlTextWriterEndElement (x->manifest_wtr);
-  xmlTextWriterEndDocument (x->manifest_wtr);
-  xmlFreeTextWriter (x->manifest_wtr);
+  xmlTextWriterEndElement (odt->manifest_wtr);
+  xmlTextWriterEndDocument (odt->manifest_wtr);
+  xmlFreeTextWriter (odt->manifest_wtr);
 
-  return true;
+  return d;
 }
 
-static bool
-odt_close_driver (struct outp_driver *this)
+static void
+odt_destroy (struct output_driver *driver)
 {
+  struct odt_driver *odt = odt_driver_cast (driver);
+
   struct string zip_cmd;
   struct string rm_cmd;
-  struct odt_driver_ext *x = this->ext;
 
-  xmlTextWriterEndElement (x->content_wtr); /* office:text */
-  xmlTextWriterEndElement (x->content_wtr); /* office:body */
-  xmlTextWriterEndElement (x->content_wtr); /* office:document-content */
+  xmlTextWriterEndElement (odt->content_wtr); /* office:text */
+  xmlTextWriterEndElement (odt->content_wtr); /* office:body */
+  xmlTextWriterEndElement (odt->content_wtr); /* office:document-content */
 
-  xmlTextWriterEndDocument (x->content_wtr);
-  xmlFreeTextWriter (x->content_wtr);
+  xmlTextWriterEndDocument (odt->content_wtr);
+  xmlFreeTextWriter (odt->content_wtr);
 
   /* Zip up the directory */
   ds_init_empty (&zip_cmd);
   ds_put_format (&zip_cmd,
                 "cd %s ; rm -f ../%s; zip -q -X ../%s mimetype; zip -q -X -u -r ../pspp.odt .",
-                x->dirname, x->opts.file_name, x->opts.file_name);
+                odt->dirname, odt->file_name, odt->file_name);
   system (ds_cstr (&zip_cmd));
   ds_destroy (&zip_cmd);
 
 
-  if ( !x->opts.debug )
+  if ( !odt->debug )
     {
       /* Remove the temp dir */
       ds_init_empty (&rm_cmd);
-      ds_put_format (&rm_cmd, "rm -r %s", x->dirname);
+      ds_put_format (&rm_cmd, "rm -r %s", odt->dirname);
       system (ds_cstr (&rm_cmd));
       ds_destroy (&rm_cmd);
     }
   else
-    fprintf (stderr, "Not removing directory %s\n", x->dirname);
-
-  free (x->dirname);
-  free (x);
+    fprintf (stderr, "Not removing directory %s\n", odt->dirname);
 
-  return true;
+  free (odt->dirname);
+  free (odt);
 }
 
 static void
-odt_open_page (struct outp_driver *this UNUSED)
-{
-}
-
-static void
-odt_close_page (struct outp_driver *this UNUSED)
-{
-}
-
-static void
-odt_output_chart (struct outp_driver *this UNUSED, const struct chart *chart UNUSED)
-{
- printf ("%s\n", __FUNCTION__);
-}
-
-
-/* Submit a table to the ODT driver */
-static void
-odt_submit (struct outp_driver *this, struct som_entity *e)
+odt_submit_table (struct odt_driver *odt, struct table_item *item)
 {
+  const struct table *tab = table_item_get_table (item);
+  const char *caption = table_item_get_caption (item);
   int r, c;
-  
-  struct odt_driver_ext *x = this->ext;
-  struct tab_table *tab = e->ext;
-
 
   /* Write a heading for the table */
-  xmlTextWriterStartElement (x->content_wtr, _xml("text:h"));
-  xmlTextWriterWriteFormatAttribute (x->content_wtr, _xml("text:level"), "%d", e->subtable_num == 1 ? 2 : 3);
-  xmlTextWriterWriteString (x->content_wtr, _xml (tab->title) );
-  xmlTextWriterEndElement (x->content_wtr);
+  if (caption != NULL)
+    {
+      xmlTextWriterStartElement (odt->content_wtr, _xml("text:h"));
+      xmlTextWriterWriteFormatAttribute (odt->content_wtr, _xml("text:level"),
+                                         "%d", 2);
+      xmlTextWriterWriteString (odt->content_wtr,
+                                _xml (table_item_get_caption (item)) );
+      xmlTextWriterEndElement (odt->content_wtr);
+    }
 
   /* Start table */
-  xmlTextWriterStartElement (x->content_wtr, _xml("table:table"));
-  xmlTextWriterWriteFormatAttribute (x->content_wtr, _xml("table:name"), 
-                                    "TABLE-%d.%d", e->table_num, e->subtable_num);
+  xmlTextWriterStartElement (odt->content_wtr, _xml("table:table"));
+  xmlTextWriterWriteFormatAttribute (odt->content_wtr, _xml("table:name"), 
+                                    "TABLE-%d", odt->table_num++);
 
 
   /* Start column definitions */
-  xmlTextWriterStartElement (x->content_wtr, _xml("table:table-column"));
-  xmlTextWriterWriteFormatAttribute (x->content_wtr, _xml("table:number-columns-repeated"), "%d", tab_nc (tab));
-  xmlTextWriterEndElement (x->content_wtr);
+  xmlTextWriterStartElement (odt->content_wtr, _xml("table:table-column"));
+  xmlTextWriterWriteFormatAttribute (odt->content_wtr, _xml("table:number-columns-repeated"), "%d", table_nc (tab));
+  xmlTextWriterEndElement (odt->content_wtr);
 
 
   /* Deal with row headers */
-  if ( tab_t (tab) > 0)
-    xmlTextWriterStartElement (x->content_wtr, _xml("table:table-header-rows"));
+  if ( table_ht (tab) > 0)
+    xmlTextWriterStartElement (odt->content_wtr, _xml("table:table-header-rows"));
     
 
   /* Write all the rows */
-  for (r = 0 ; r < tab_nr (tab); ++r)
+  for (r = 0 ; r < table_nr (tab); ++r)
     {
-      int spanned_columns = 0;
       /* Start row definition */
-      xmlTextWriterStartElement (x->content_wtr, _xml("table:table-row"));
+      xmlTextWriterStartElement (odt->content_wtr, _xml("table:table-row"));
 
       /* Write all the columns */
-      for (c = 0 ; c < tab_nc (tab) ; ++c)
+      for (c = 0 ; c < table_nc (tab) ; ++c)
        {
-         char *s = NULL;
-         unsigned int opts = tab->ct[tab_nc (tab) * r + c];
-         struct substring ss = tab->cc[tab_nc (tab) * r + c];
+          struct table_cell cell;
 
-         if (opts & TAB_EMPTY)
-           {
-             xmlTextWriterStartElement (x->content_wtr, _xml("table:table-cell"));
-             xmlTextWriterEndElement (x->content_wtr);
-             continue;
-           }
+          table_get_cell (tab, c, r, &cell);
 
-         if ( opts & TAB_JOIN)
-           {
-             if ( spanned_columns == 0)
-               {
-                 struct tab_joined_cell *j = (struct tab_joined_cell*) ss_data (ss);
-                 s = ss_xstrdup (j->contents);
-               }
-           }
-         else
-           s = ss_xstrdup (ss);
+          if (c == cell.d[TABLE_HORZ][0] && r == cell.d[TABLE_VERT][0])
+            {
+              int colspan = table_cell_colspan (&cell);
+              int rowspan = table_cell_rowspan (&cell);
 
-         if ( spanned_columns == 0 )
-           {
-             xmlTextWriterStartElement (x->content_wtr, _xml("table:table-cell"));
-             xmlTextWriterWriteAttribute (x->content_wtr, _xml("office:value-type"), _xml("string"));
+              xmlTextWriterStartElement (odt->content_wtr, _xml("table:table-cell"));
+              xmlTextWriterWriteAttribute (odt->content_wtr, _xml("office:value-type"), _xml("string"));
 
-             if ( opts & TAB_JOIN )
-               {
-                 struct tab_joined_cell *j = (struct tab_joined_cell*) ss_data (ss);
-                 spanned_columns = j->x2 - j->x1;
+              if (colspan > 1)
+                xmlTextWriterWriteFormatAttribute (
+                  odt->content_wtr, _xml("table:number-columns-spanned"),
+                  "%d", colspan);
 
-                 xmlTextWriterWriteFormatAttribute (x->content_wtr,
-                                                    _xml("table:number-columns-spanned"),
-                                                    "%d", spanned_columns);
-               }
+              if (rowspan > 1)
+                xmlTextWriterWriteFormatAttribute (
+                  odt->content_wtr, _xml("table:number-rows-spanned"),
+                  "%d", rowspan);
 
-             xmlTextWriterStartElement (x->content_wtr, _xml("text:p"));
+             xmlTextWriterStartElement (odt->content_wtr, _xml("text:p"));
 
-             if ( r < tab_t (tab) || c < tab_l (tab) )
-               xmlTextWriterWriteAttribute (x->content_wtr, _xml("text:style-name"), _xml("Table_20_Heading"));
+             if ( r < table_ht (tab) || c < table_hl (tab) )
+               xmlTextWriterWriteAttribute (odt->content_wtr, _xml("text:style-name"), _xml("Table_20_Heading"));
              else
-               xmlTextWriterWriteAttribute (x->content_wtr, _xml("text:style-name"), _xml("Table_20_Contents"));
+               xmlTextWriterWriteAttribute (odt->content_wtr, _xml("text:style-name"), _xml("Table_20_Contents"));
+
+             xmlTextWriterWriteString (odt->content_wtr, _xml(cell.contents));
 
-             xmlTextWriterWriteString (x->content_wtr, _xml (s));
-         
-             xmlTextWriterEndElement (x->content_wtr); /* text:p */
-             xmlTextWriterEndElement (x->content_wtr); /* table:table-cell */
+             xmlTextWriterEndElement (odt->content_wtr); /* text:p */
+             xmlTextWriterEndElement (odt->content_wtr); /* table:table-cell */
            }
          else
            {
-             xmlTextWriterStartElement (x->content_wtr, _xml("table:covered-table-cell"));
-             xmlTextWriterEndElement (x->content_wtr);
+             xmlTextWriterStartElement (odt->content_wtr, _xml("table:covered-table-cell"));
+             xmlTextWriterEndElement (odt->content_wtr);
            }
-         if ( opts & TAB_JOIN )
-           spanned_columns --;
 
-         free (s);
+          table_cell_free (&cell);
        }
   
-      xmlTextWriterEndElement (x->content_wtr); /* row */
+      xmlTextWriterEndElement (odt->content_wtr); /* row */
 
-      if ( tab_t (tab) > 0 && r == tab_t (tab) - 1)
-       xmlTextWriterEndElement (x->content_wtr); /* table-header-rows */
+      if ( table_ht (tab) > 0 && r == table_ht (tab) - 1)
+       xmlTextWriterEndElement (odt->content_wtr); /* table-header-rows */
     }
 
-  xmlTextWriterEndElement (x->content_wtr); /* table */
+  xmlTextWriterEndElement (odt->content_wtr); /* table */
 }
 
+/* Submit a table to the ODT driver */
+static void
+odt_submit (struct output_driver *driver,
+            const struct output_item *output_item)
+{
+  struct odt_driver *odt = odt_driver_cast (driver);
+  if (is_table_item (output_item))
+    odt_submit_table (odt, to_table_item (output_item));
+  else if (is_text_item (output_item))
+    {
+      const struct text_item *text_item = to_text_item (output_item);
+      const char *text = text_item_get_text (text_item);
+
+      /* XXX apply different styles based on text_item's type.  */
+      xmlTextWriterStartElement (odt->content_wtr, _xml("text:p"));
+      xmlTextWriterWriteString (odt->content_wtr, _xml(text));
+      xmlTextWriterEndElement (odt->content_wtr);
+    }
+}
 
 /* ODT driver class. */
-const struct outp_class odt_class =
+const struct output_driver_class odt_class =
 {
   "odf",
-  1,
-
-  odt_open_driver,
-  odt_close_driver,
-
-  odt_open_page,
-  odt_close_page,
-  NULL,
-
-  odt_output_chart,
+  odt_create,
+  odt_destroy,
   odt_submit,
-
-  NULL,
-  NULL,
   NULL,
 };
diff --git a/src/output/options.c b/src/output/options.c
new file mode 100644 (file)
index 0000000..918f718
--- /dev/null
@@ -0,0 +1,311 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 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 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.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+
+#include "output/options.h"
+
+#include <errno.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "libpspp/str.h"
+#include "libpspp/string-map.h"
+#include "output/driver-provider.h"
+#include "output/measure.h"
+
+#include "gl/error.h"
+#include "gl/xalloc.h"
+
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
+
+/* Creates and returns a new struct driver_option that contains copies of
+   all of the supplied arguments.  All of the arguments must be nonnull,
+   except that VALUE may be NULL (if the user did not supply a value for this
+   option).
+
+   Refer to struct driver_option for the meaning of each argument. */
+struct driver_option *
+driver_option_create (const char *driver_name, const char *name,
+                      const char *value, const char *default_value)
+{
+  struct driver_option *o = xmalloc (sizeof *o);
+  o->driver_name = xstrdup (driver_name);
+  o->name = xstrdup (name);
+  o->value = value != NULL ? xstrdup (value) : NULL;
+  o->default_value = xstrdup (default_value);
+  return o;
+}
+
+/* Creates and returns a new struct driver_option for output driver DRIVER
+   (which is needed only to the extent that its name will be used in error
+   messages).  The option named NAME is extracted from OPTIONS.  DEFAULT_VALUE
+   is the default value of the option, used if the given option was not
+   supplied or was invalid. */
+struct driver_option *
+driver_option_get (struct output_driver *driver, struct string_map *options,
+                   const char *name, const char *default_value)
+{
+  struct driver_option *option;
+  char *value;
+
+  value = string_map_find_and_delete (options, name);
+  option = driver_option_create (output_driver_get_name (driver), name, value,
+                                 default_value);
+  free (value);
+  return option;
+}
+
+/* Frees driver option O. */
+void
+driver_option_destroy (struct driver_option *o)
+{
+  if (o != NULL)
+    {
+      free (o->driver_name);
+      free (o->name);
+      free (o->value);
+      free (o->default_value);
+      free (o);
+    }
+}
+
+/* Stores the paper size of the value of option O into *H and *V, in 1/72000"
+   units.  Any syntax accepted by measure_paper() may be used.
+
+   Destroys O. */
+void
+parse_paper_size (struct driver_option *o, int *h, int *v)
+{
+  if (o->value == NULL || !measure_paper (o->value, h, v))
+    measure_paper (o->default_value, h, v);
+  driver_option_destroy (o);
+}
+
+static int
+do_parse_boolean (const char *driver_name, const char *key,
+                  const char *value)
+{
+  if (!strcmp (value, "on") || !strcmp (value, "true")
+      || !strcmp (value, "yes") || !strcmp (value, "1"))
+    return true;
+  else if (!strcmp (value, "off") || !strcmp (value, "false")
+           || !strcmp (value, "no") || !strcmp (value, "0"))
+    return false;
+  else
+    {
+      error (0, 0, _("%s: \"%s\" is \"%s\" but a Boolean value is required"),
+             driver_name, value, key);
+      return -1;
+    }
+}
+
+/* Parses and return O's value as a Boolean value.  "true" and "false", "yes"
+   and "no", "on" and "off", and "1" and "0" are acceptable boolean strings.
+
+   Destroys O. */
+bool
+parse_boolean (struct driver_option *o)
+{
+  bool retval;
+
+  retval = do_parse_boolean (o->driver_name, o->name, o->default_value) > 0;
+  if (o->value != NULL)
+    {
+      int value = do_parse_boolean (o->driver_name, o->name, o->value);
+      if (value >= 0)
+        retval = value;
+    }
+
+  driver_option_destroy (o);
+
+  return retval;
+}
+
+/* Parses O's value as an enumeration constant.  The arguments to this function
+   consist of a series of string/int pairs, terminated by a null pointer value.
+   O's value is compared to each string in turn, and parse_enum() returns the
+   int associated with the first matching string.  If there is no match, or if
+   O has no user-specified value, then O's default value is treated the same
+   way.  If the default value still does not match, parse_enum() returns 0.
+
+   Example: parse_enum (o, "a", 1, "b", 2, (char *) NULL) returns 1 if O's
+   value if "a", 2 if O's value is "b".
+
+   Destroys O. */
+int
+parse_enum (struct driver_option *o, ...)
+{
+  va_list args;
+  int retval;
+
+  retval = 0;
+  va_start (args, o);
+  for (;;)
+    {
+      const char *s;
+      int value;
+
+      s = va_arg (args, const char *);
+      if (s == NULL)
+        {
+          if (o->value != NULL)
+            {
+              struct string choices;
+              int i;
+
+              ds_init_empty (&choices);
+              va_end (args);
+              va_start (args, o);
+              for (i = 0; ; i++)
+                {
+                  s = va_arg (args, const char *);
+                  if (s == NULL)
+                    break;
+                  value = va_arg (args, int);
+
+                  if (i > 0)
+                    ds_put_cstr (&choices, ", ");
+                  ds_put_format (&choices, "`%s'", s);
+                }
+
+              error (0, 0, _("%s: \"%s\" is \"%s\" but one of the following "
+                             "is required: %s"),
+                     o->driver_name, o->name, o->value, ds_cstr (&choices));
+              ds_destroy (&choices);
+            }
+          break;
+        }
+      value = va_arg (args, int);
+
+      if (o->value != NULL && !strcmp (s, o->value))
+        {
+          retval = value;
+          break;
+        }
+      else if (!strcmp (s, o->default_value))
+        retval = value;
+    }
+  va_end (args);
+  driver_option_destroy (o);
+  return retval;
+}
+
+/* Parses O's value as an integer in the range MIN_VALUE to MAX_VALUE
+   (inclusive) and returns the integer.
+
+   Destroys O. */
+int
+parse_int (struct driver_option *o, int min_value, int max_value)
+{
+  int retval = strtol (o->default_value, NULL, 0);
+
+  if (o->value != NULL)
+    {
+      int value;
+      char *tail;
+
+      errno = 0;
+      value = strtol (o->value, &tail, 0);
+      if (tail != o->value && *tail == '\0' && errno != ERANGE
+          && value >= min_value && value <= max_value)
+        retval = value;
+      else if (max_value == INT_MAX)
+        {
+          if (min_value == 0)
+            error (0, 0, _("%s: \"%s\" is \"%s\" but a nonnegative integer "
+                           "is required"),
+                   o->driver_name, o->name, o->value);
+          else if (min_value == 1)
+            error (0, 0, _("%s: \"%s\" is \"%s\" but a positive integer is "
+                           "required"), o->driver_name, o->name, o->value);
+          else if (min_value == INT_MIN)
+            error (0, 0, _("%s: \"%s\" is \"%s\" but an integer is required"),
+                   o->driver_name, o->name, o->value);
+          else
+            error (0, 0, _("%s: \"%s\" is \"%s\" but an integer greater "
+                           "than %d is required"),
+                   o->driver_name, o->name, o->value, min_value - 1);
+        }
+      else
+        error (0, 0, _("%s: \"%s\" is \"%s\"  but an integer between %d and "
+                       "%d is required"),
+               o->driver_name, o->name, o->value, min_value, max_value);
+    }
+
+  driver_option_destroy (o);
+  return retval;
+}
+
+/* Parses O's value as a dimension, as understood by measure_dimension(), and
+   returns its length in units of 1/72000".
+
+   Destroys O. */
+int
+parse_dimension (struct driver_option *o)
+{
+  int retval;
+
+  retval = o->value != NULL ? measure_dimension (o->value) : -1;
+  if (retval == -1)
+    retval = measure_dimension (o->default_value);
+
+  driver_option_destroy (o);
+  return retval;
+}
+
+/* Parses O's value as a string and returns it as a malloc'd string that the
+   caller is responsible for freeing.
+
+   Destroys O. */
+char *
+parse_string (struct driver_option *o)
+{
+  char *retval = xstrdup (o->value != NULL ? o->value : o->default_value);
+  driver_option_destroy (o);
+  return retval;
+}
+
+/* Parses O's value as a string and returns it as a malloc'd string that the
+   caller is responsible for freeing.
+
+   The string must contain at least one '#' character, which the client will
+   presumably replace by a number as part of writing charts to separate files.
+
+   Destroys O. */
+char *
+parse_chart_file_name (struct driver_option *o)
+{
+  char *value;
+
+  if (o->value != NULL && strchr (o->value, '#') != NULL)
+    value = xstrdup (o->value);
+  else
+    {
+      value = xstrdup (o->default_value);
+      if (o->value != NULL)
+        error (0, 0, _("%s: \"%s\" is \"%s\" but a file name that contains "
+                       "\"#\" is required."),
+               o->name, o->value, o->driver_name);
+    }
+
+  driver_option_destroy (o);
+
+  return value;
+}
diff --git a/src/output/options.h b/src/output/options.h
new file mode 100644 (file)
index 0000000..3f9e705
--- /dev/null
@@ -0,0 +1,55 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 1997-9, 2000, 2007, 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 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.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef OUTPUT_OPTIONS_H
+#define OUTPUT_OPTIONS_H 1
+
+/* Helper functions for driver option parsing. */
+
+#include <stdbool.h>
+#include "libpspp/compiler.h"
+
+struct output_driver;
+struct string_map;
+
+/* An option being parsed. */
+struct driver_option
+  {
+    char *driver_name;          /* Driver's name, for use in error messages. */
+    char *name;                 /* Option name, for use in error messages.  */
+    char *value;                /* Value supplied by user (NULL if none). */
+    char *default_value;        /* Default value supplied by driver. */
+  };
+
+struct driver_option *driver_option_create (const char *driver_name,
+                                            const char *name,
+                                            const char *value,
+                                            const char *default_value);
+struct driver_option *driver_option_get (struct output_driver *,
+                                         struct string_map *,
+                                         const char *name,
+                                         const char *default_value);
+void driver_option_destroy (struct driver_option *);
+
+void parse_paper_size (struct driver_option *, int *h, int *v);
+bool parse_boolean (struct driver_option *);
+int parse_enum (struct driver_option *, ...) SENTINEL(0);
+int parse_int (struct driver_option *, int min_value, int max_value);
+int parse_dimension (struct driver_option *);
+char *parse_string (struct driver_option *);
+char *parse_chart_file_name (struct driver_option *);
+
+#endif /* output/options.h */
diff --git a/src/output/output-item-provider.h b/src/output/output-item-provider.h
new file mode 100644 (file)
index 0000000..a810f97
--- /dev/null
@@ -0,0 +1,35 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2009 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 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.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef OUTPUT_ITEM_PROVIDER_H
+#define OUTPUT_ITEM_PROVIDER_H 1
+
+#include <output/output-item.h>
+
+/* Class structure for an output item.
+
+   This structure must be provided by an output_item subclass to initialize an
+   instance of output_item. */
+struct output_item_class
+  {
+    /* Destroys and frees ITEM.  Called when output_item_unref() drops ITEM's
+       reference count to 0. */
+    void (*destroy) (struct output_item *item);
+  };
+
+void output_item_init (struct output_item *, const struct output_item_class *);
+
+#endif /* output/output-item-provider.h */
diff --git a/src/output/output-item.c b/src/output/output-item.c
new file mode 100644 (file)
index 0000000..e1c889f
--- /dev/null
@@ -0,0 +1,73 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2009 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 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.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+
+#include <output/output-item-provider.h>
+
+#include <assert.h>
+#include <stdlib.h>
+
+#include <libpspp/assertion.h>
+#include <libpspp/cast.h>
+
+#include "xalloc.h"
+\f
+/* Increases ITEM's reference count, indicating that it has an additional
+   owner.  An output item that is shared among multiple owners must not be
+   modified. */
+struct output_item *
+output_item_ref (const struct output_item *item_)
+{
+  struct output_item *item = CONST_CAST (struct output_item *, item_);
+  item->ref_cnt++;
+  return item;
+}
+
+/* Decreases ITEM's reference count, indicating that it has one fewer owner.
+   If ITEM no longer has any owners, it is freed. */
+void
+output_item_unref (struct output_item *item)
+{
+  if (item != NULL)
+    {
+      assert (item->ref_cnt > 0);
+      if (--item->ref_cnt == 0)
+        item->class->destroy (item);
+    }
+}
+
+/* Returns true if ITEM has more than one owner.  An output item that is shared
+   among multiple owners must not be modified. */
+bool
+output_item_is_shared (const struct output_item *item)
+{
+  return item->ref_cnt > 1;
+}
+\f
+/* Initializes ITEM as an output item of the specified CLASS, initially with a
+   reference count of 1.
+
+   An output item is an abstract class, that is, a plain output_item is not
+   useful on its own.  Thus, this function is normally called from the
+   initialization function of some subclass of output_item. */
+void
+output_item_init (struct output_item *item,
+                  const struct output_item_class *class)
+{
+  item->class = class;
+  item->ref_cnt = 1;
+}
diff --git a/src/output/output-item.h b/src/output/output-item.h
new file mode 100644 (file)
index 0000000..57ced30
--- /dev/null
@@ -0,0 +1,50 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2009 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 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.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef OUTPUT_ITEM_H
+#define OUTPUT_ITEM_H 1
+
+/* Output items.
+
+   An output item is a self-contained chunk of output.  The
+   following kinds of output items currently exist:
+
+        - Tables (see output/table-item.h).
+
+        - Charts (see output/chart-item.h).
+
+        - Text strings (see output/text-item.h).
+*/
+
+#include <libpspp/cast.h>
+#include <stdbool.h>
+
+/* A single output item. */
+struct output_item
+  {
+    const struct output_item_class *class;
+
+    /* Reference count.  An output item may be shared between multiple owners,
+       indicated by a reference count greater than 1.  When this is the case,
+       the output item must not be modified. */
+    int ref_cnt;
+  };
+
+struct output_item *output_item_ref (const struct output_item *);
+void output_item_unref (struct output_item *);
+bool output_item_is_shared (const struct output_item *);
+
+#endif /* output/output-item.h */
diff --git a/src/output/output.c b/src/output/output.c
deleted file mode 100644 (file)
index 2f4788f..0000000
+++ /dev/null
@@ -1,1180 +0,0 @@
-/* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000, 2007, 2009 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 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.
-
-   You should have received a copy of the GNU General Public License
-   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
-
-#include <config.h>
-
-#include <ctype.h>
-#include <errno.h>
-#if HAVE_LC_PAPER
-#include <langinfo.h>
-#endif
-#include <locale.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-#include <data/file-name.h>
-#include <data/settings.h>
-#include <libpspp/misc.h>
-#include <libpspp/str.h>
-#include <output/htmlP.h>
-#include <output/output.h>
-
-#include "error.h"
-#include "intprops.h"
-#include "xalloc.h"
-
-#include "gettext.h"
-#define _(msgid) gettext (msgid)
-
-/* Where the output driver name came from. */
-enum
-  {
-    OUTP_S_COMMAND_LINE,       /* Specified by the user. */
-    OUTP_S_INIT_FILE           /* `default' or the init file. */
-  };
-
-/* Names the output drivers to be used. */
-struct outp_names
-  {
-    char *name;                        /* Name of the output driver. */
-    int source;                        /* OUTP_S_* */
-    struct outp_names *next, *prev;
-  };
-
-/* Defines an init file macro. */
-struct outp_defn
-  {
-    char *key;
-    struct string value;
-    struct outp_defn *next, *prev;
-  };
-
-static struct outp_defn *outp_macros;
-static struct outp_names *outp_configure_vec;
-
-/* A list of driver classes. */
-struct outp_driver_class_list
-  {
-    const struct outp_class *class;
-    struct outp_driver_class_list *next;
-  };
-
-static struct outp_driver_class_list *outp_class_list;
-static struct ll_list outp_driver_list = LL_INITIALIZER (outp_driver_list);
-
-char *outp_title;
-char *outp_subtitle;
-
-/* A set of OUTP_DEV_* bits indicating the devices that are
-   disabled. */
-static int disabled_devices;
-
-static void destroy_driver (struct outp_driver *);
-static void configure_driver (const struct substring, const struct substring,
-                              const struct substring, const struct substring);
-
-/* Add a class to the class list. */
-static void
-add_class (const struct outp_class *class)
-{
-  struct outp_driver_class_list *new_list = xmalloc (sizeof *new_list);
-
-  new_list->class = class;
-
-  if (!outp_class_list)
-    {
-      outp_class_list = new_list;
-      new_list->next = NULL;
-    }
-  else
-    {
-      new_list->next = outp_class_list;
-      outp_class_list = new_list;
-    }
-}
-
-/* Finds the outp_names in outp_configure_vec with name between BP and
-   EP exclusive. */
-static struct outp_names *
-search_names (char *bp, char *ep)
-{
-  struct outp_names *n;
-
-  for (n = outp_configure_vec; n; n = n->next)
-    if ((int) strlen (n->name) == ep - bp && !memcmp (n->name, bp, ep - bp))
-      return n;
-  return NULL;
-}
-
-/* Deletes outp_names NAME from outp_configure_vec. */
-static void
-delete_name (struct outp_names * n)
-{
-  free (n->name);
-  if (n->prev)
-    n->prev->next = n->next;
-  if (n->next)
-    n->next->prev = n->prev;
-  if (n == outp_configure_vec)
-    outp_configure_vec = n->next;
-  free (n);
-}
-
-/* Adds the name between BP and EP exclusive to list
-   outp_configure_vec with source SOURCE. */
-static void
-add_name (char *bp, char *ep, int source)
-{
-  struct outp_names *n = xmalloc (sizeof *n);
-  n->name = xmalloc (ep - bp + 1);
-  memcpy (n->name, bp, ep - bp);
-  n->name[ep - bp] = 0;
-  n->source = source;
-  n->next = outp_configure_vec;
-  n->prev = NULL;
-  if (outp_configure_vec)
-    outp_configure_vec->prev = n;
-  outp_configure_vec = n;
-}
-
-/* Checks that outp_configure_vec is empty, complains and clears
-   it if it isn't. */
-static void
-check_configure_vec (void)
-{
-  struct outp_names *n;
-
-  for (n = outp_configure_vec; n; n = n->next)
-    if (n->source == OUTP_S_COMMAND_LINE)
-      error (0, 0, _("unknown output driver `%s'"), n->name);
-    else
-      error (0, 0, _("output driver `%s' referenced but never defined"),
-             n->name);
-  outp_configure_clear ();
-}
-
-/* Searches outp_configure_vec for the name between BP and EP
-   exclusive.  If found, it is deleted, then replaced by the names
-   given in EP+1, if any. */
-static void
-expand_name (char *bp, char *ep)
-{
-  struct outp_names *n = search_names (bp, ep);
-  if (!n)
-    return;
-  delete_name (n);
-
-  bp = ep + 1;
-  for (;;)
-    {
-      while (isspace ((unsigned char) *bp))
-       bp++;
-      ep = bp;
-      while (*ep && !isspace ((unsigned char) *ep))
-       ep++;
-      if (bp == ep)
-       return;
-      if (!search_names (bp, ep))
-       add_name (bp, ep, OUTP_S_INIT_FILE);
-      bp = ep;
-    }
-}
-
-/* Looks for a macro with key KEY, and returns the corresponding value
-   if found, or NULL if not. */
-static const char *
-find_defn_value (const char *key)
-{
-  static char buf[INT_STRLEN_BOUND (int) + 1];
-  struct outp_defn *d;
-
-  for (d = outp_macros; d; d = d->next)
-    if (!strcmp (key, d->key))
-      return ds_cstr (&d->value);
-  if (!strcmp (key, "viewwidth"))
-    {
-      sprintf (buf, "%d", settings_get_viewwidth ());
-      return buf;
-    }
-  else if (!strcmp (key, "viewlength"))
-    {
-      sprintf (buf, "%d", settings_get_viewlength ());
-      return buf;
-    }
-  else
-    return getenv (key);
-}
-
-static void
-insert_defn_value (const char *var, struct string *dst, void *aux UNUSED)
-{
-  const char *value = find_defn_value (var);
-  if (value != NULL)
-    ds_put_cstr (dst, value);
-}
-
-/* Initializes global variables. */
-void
-outp_init (void)
-{
-  char def[] = "default";
-
-  add_class (&html_class);
-  add_class (&ascii_class);
-#ifdef HAVE_CAIRO
-  add_class (&cairo_class);
-#endif
-  add_class (&odt_class);
-
-  add_name (def, &def[strlen (def)], OUTP_S_INIT_FILE);
-}
-
-/* Deletes all the output macros. */
-static void
-delete_macros (void)
-{
-  struct outp_defn *d, *next;
-
-  for (d = outp_macros; d; d = next)
-    {
-      next = d->next;
-      free (d->key);
-      ds_destroy (&d->value);
-      free (d);
-    }
-}
-
-static void
-init_default_drivers (void)
-{
-  error (0, 0, _("using default output driver configuration"));
-  configure_driver (ss_cstr ("list"),
-                    ss_cstr ("ascii"),
-                    ss_cstr ("listing"),
-                    ss_cstr ("length=66 width=79 output-file=\"pspp.list\""));
-}
-
-/* Reads the initialization file; initializes
-   outp_driver_list. */
-void
-outp_read_devices (void)
-{
-  int result = 0;
-
-  char *init_fn;
-
-  FILE *f = NULL;
-  struct string line;
-  int line_number;
-
-  init_fn = fn_search_path (fn_getenv_default ("STAT_OUTPUT_INIT_FILE",
-                                              "devices"),
-                           fn_getenv_default ("STAT_OUTPUT_INIT_PATH",
-                                              config_path));
-
-  ds_init_empty (&line);
-
-  if (init_fn == NULL)
-    {
-      error (0, 0, _("cannot find output initialization file "
-                     "(use `-vv' to view search path)"));
-      goto exit;
-    }
-
-  f = fopen (init_fn, "r");
-  if (f == NULL)
-    {
-      error (0, errno, _("cannot open \"%s\""), init_fn);
-      goto exit;
-    }
-
-  line_number = 0;
-  for (;;)
-    {
-      char *cp;
-
-      if (!ds_read_config_line (&line, &line_number, f))
-       {
-         if (ferror (f))
-           error (0, errno, _("reading \"%s\""), init_fn);
-         break;
-       }
-      for (cp = ds_cstr (&line); isspace ((unsigned char) *cp); cp++);
-      if (!strncmp ("define", cp, 6) && isspace ((unsigned char) cp[6]))
-       outp_configure_macro (&cp[7]);
-      else if (*cp)
-       {
-         char *ep;
-         for (ep = cp; *ep && *ep != ':' && *ep != '='; ep++);
-         if (*ep == '=')
-           expand_name (cp, ep);
-         else if (*ep == ':')
-           {
-             struct outp_names *n = search_names (cp, ep);
-             if (n)
-               {
-                 outp_configure_driver_line (ds_ss (&line));
-                 delete_name (n);
-               }
-           }
-         else
-           error_at_line (0, 0, init_fn, line_number, _("syntax error"));
-       }
-    }
-  result = 1;
-
-  check_configure_vec ();
-
-exit:
-  if (f && -1 == fclose (f))
-    error (0, errno, _("error closing \"%s\""), init_fn);
-  free (init_fn);
-  ds_destroy (&line);
-  delete_macros ();
-
-  if (result)
-    {
-      if (ll_is_empty (&outp_driver_list))
-        error (0, 0, _("no active output drivers"));
-    }
-  else
-    error (0, 0, _("error reading device definition file"));
-
-  if (!result || ll_is_empty (&outp_driver_list))
-    init_default_drivers ();
-}
-
-/* Clear the list of drivers to configure. */
-void
-outp_configure_clear (void)
-{
-  struct outp_names *n, *next;
-
-  for (n = outp_configure_vec; n; n = next)
-    {
-      next = n->next;
-      free (n->name);
-      free (n);
-    }
-  outp_configure_vec = NULL;
-}
-
-/* Adds the name BP to the list of drivers to configure into
-   outp_driver_list. */
-void
-outp_configure_add (char *bp)
-{
-  char *ep = &bp[strlen (bp)];
-  if (!search_names (bp, ep))
-    add_name (bp, ep, OUTP_S_COMMAND_LINE);
-}
-
-/* Defines one configuration macro based on the text in BP, which
-   should be of the form `KEY=VALUE'. */
-void
-outp_configure_macro (char *bp)
-{
-  struct outp_defn *d;
-  char *ep;
-
-  while (isspace ((unsigned char) *bp))
-    bp++;
-  ep = bp;
-  while (*ep && !isspace ((unsigned char) *ep) && *ep != '=')
-    ep++;
-
-  d = xmalloc (sizeof *d);
-  d->key = xmalloc (ep - bp + 1);
-  memcpy (d->key, bp, ep - bp);
-  d->key[ep - bp] = 0;
-
-  /* Earlier definitions for a particular KEY override later ones. */
-  if (find_defn_value (d->key))
-    {
-      free (d->key);
-      free (d);
-      return;
-    }
-
-  if (*ep == '=')
-    ep++;
-  while (isspace ((unsigned char) *ep))
-    ep++;
-
-  ds_init_cstr (&d->value, ep);
-  fn_interp_vars (ds_ss (&d->value), insert_defn_value, NULL, &d->value);
-  d->next = outp_macros;
-  d->prev = NULL;
-  if (outp_macros)
-    outp_macros->prev = d;
-  outp_macros = d;
-}
-
-/* Closes all the output drivers. */
-void
-outp_done (void)
-{
-  struct outp_driver_class_list *n = outp_class_list ;
-  outp_configure_clear ();
-  while (!ll_is_empty (&outp_driver_list))
-    {
-      struct outp_driver *d = ll_data (ll_head (&outp_driver_list),
-                                       struct outp_driver, node);
-      destroy_driver (d);
-    }
-
-  while (n)
-    {
-      struct outp_driver_class_list *next = n->next;
-      free(n);
-      n = next;
-    }
-  outp_class_list = NULL;
-
-  free (outp_title);
-  outp_title = NULL;
-
-  free (outp_subtitle);
-  outp_subtitle = NULL;
-}
-
-/* Display on stdout a list of all registered driver classes. */
-void
-outp_list_classes (void)
-{
-  int width = settings_get_viewwidth ();
-  struct outp_driver_class_list *c;
-
-  printf (_("Driver classes:\n\t"));
-  width -= 8;
-  for (c = outp_class_list; c; c = c->next)
-    {
-      if ((int) strlen (c->class->name) + 1 > width)
-       {
-         printf ("\n\t");
-         width = settings_get_viewwidth () - 8;
-       }
-      else
-       putc (' ', stdout);
-      fputs (c->class->name, stdout);
-    }
-  putc('\n', stdout);
-}
-
-/* Obtains a token from S and advances its position.  Errors are
-   reported against the given DRIVER_NAME.
-   The token is stored in TOKEN.  Returns true if successful,
-   false on syntax error.
-
-   Caller is responsible for skipping leading spaces. */
-static bool
-get_option_token (struct substring *s, const char *driver_name,
-                  struct string *token)
-{
-  int c;
-
-  ds_clear (token);
-  c = ss_get_char (s);
-  if (c == EOF)
-    {
-      error (0, 0, _("syntax error parsing options for \"%s\" driver"),
-             driver_name);
-      return false;
-    }
-  else if (c == '\'' || c == '"')
-    {
-      int quote = c;
-
-      for (;;)
-        {
-          c = ss_get_char (s);
-          if (c == quote)
-            break;
-          else if (c == EOF)
-            {
-              error (0, 0,
-                     _("reached end of options inside quoted string "
-                       "parsing options for \"%s\" driver"),
-                     driver_name);
-              return false;
-            }
-          else if (c != '\\')
-            ds_put_char (token, c);
-          else
-            {
-              int out;
-
-              c = ss_get_char (s);
-              switch (c)
-                {
-                case '\'':
-                  out = '\'';
-                  break;
-                case '"':
-                  out = '"';
-                  break;
-                case '\\':
-                  out = '\\';
-                  break;
-                case 'a':
-                  out = '\a';
-                  break;
-                case 'b':
-                  out = '\b';
-                  break;
-                case 'f':
-                  out = '\f';
-                  break;
-                case 'n':
-                  out = '\n';
-                  break;
-                case 'r':
-                  out = '\r';
-                  break;
-                case 't':
-                  out = '\t';
-                  break;
-                case 'v':
-                  out = '\v';
-                  break;
-                case '0':
-                case '1':
-                case '2':
-                case '3':
-                case '4':
-                case '5':
-                case '6':
-                case '7':
-                  out = c - '0';
-                  while (ss_first (*s) >= '0' && ss_first (*s) <= '7')
-                    out = out * 8 + (ss_get_char (s) - '0');
-                  break;
-                case 'x':
-                case 'X':
-                  out = 0;
-                  while (isxdigit (ss_first (*s)))
-                    {
-                      c = ss_get_char (s);
-                      out *= 16;
-                      if (isdigit (c))
-                        out += c - '0';
-                      else
-                        out += tolower (c) - 'a' + 10;
-                    }
-                  break;
-                default:
-                  error (0, 0, _("syntax error in string constant "
-                                 "parsing options for \"%s\" driver"),
-                         driver_name);
-                  return false;
-                }
-              ds_put_char (token, out);
-            }
-        }
-    }
-  else
-    {
-      for (;;)
-        {
-          ds_put_char (token, c);
-
-          c = ss_first (*s);
-          if (c == EOF || c == '=' || isspace (c))
-            break;
-          ss_advance (s, 1);
-        }
-    }
-
-  return 1;
-}
-
-bool
-outp_parse_options (const char *driver_name, struct substring options,
-                    bool (*callback) (void *aux, const char *key,
-                                      const struct string *value), void *aux)
-{
-  struct string key = DS_EMPTY_INITIALIZER;
-  struct string value = DS_EMPTY_INITIALIZER;
-  struct substring left = options;
-  bool ok = true;
-
-  do
-    {
-      ss_ltrim (&left, ss_cstr (CC_SPACES));
-      if (ss_is_empty (left))
-        break;
-
-      if (!get_option_token (&left, driver_name, &key))
-        break;
-
-      ss_ltrim (&left, ss_cstr (CC_SPACES));
-      if (!ss_match_char (&left, '='))
-       {
-         error (0, 0, _("syntax error expecting `=' "
-                         "parsing options for driver \"%s\""),
-                 driver_name);
-         break;
-       }
-
-      ss_ltrim (&left, ss_cstr (CC_SPACES));
-      if (!get_option_token (&left, driver_name, &value))
-        break;
-
-      ok = callback (aux, ds_cstr (&key), &value);
-    }
-  while (ok);
-
-  ds_destroy (&key);
-  ds_destroy (&value);
-
-  return ok;
-}
-
-/* Find the driver in outp_driver_list with name NAME. */
-static struct outp_driver *
-find_driver (char *name)
-{
-  struct outp_driver *d;
-  ll_for_each (d, struct outp_driver, node, &outp_driver_list)
-    if (!strcmp (d->name, name))
-      return d;
-  return NULL;
-}
-
-/* Adds a driver to outp_driver_list pursuant to the
-   specification provided.  */
-static void
-configure_driver (struct substring driver_name, struct substring class_name,
-                  struct substring device_type, struct substring options)
-{
-  struct outp_driver_class_list *c;
-  struct substring token;
-  size_t save_idx = 0;
-  char *name;
-  int device;
-
-  /* Find class. */
-  for (c = outp_class_list; c; c = c->next)
-    if (!ss_compare (ss_cstr (c->class->name), class_name))
-      break;
-  if (c == NULL)
-    {
-      error (0, 0, _("unknown output driver class `%.*s'"),
-             (int) ss_length (class_name), ss_data (class_name));
-      return;
-    }
-
-  /* Parse device type. */
-  device = 0;
-  while (ss_tokenize (device_type, ss_cstr (CC_SPACES), &save_idx, &token))
-    if (!ss_compare (token, ss_cstr ("listing")))
-      device |= OUTP_DEV_LISTING;
-    else if (!ss_compare (token, ss_cstr ("screen")))
-      device |= OUTP_DEV_SCREEN;
-    else if (!ss_compare (token, ss_cstr ("printer")))
-      device |= OUTP_DEV_PRINTER;
-    else
-      error (0, 0, _("unknown device type `%.*s'"),
-             (int) ss_length (token), ss_data (token));
-
-  /* Open driver. */
-  name = ss_xstrdup (driver_name);
-  if (!c->class->open_driver (name, device, options))
-    error (0, 0, _("cannot initialize output driver `%s' of class `%s'"),
-           name, c->class->name);
-  free (name);
-}
-
-/* Allocates and returns a new outp_driver for a device with the
-   given NAME and CLASS and the OUTP_DEV_* type(s) in TYPES
-
-   This function is intended to be used by output drivers, not
-   by their clients. */
-struct outp_driver *
-outp_allocate_driver (const struct outp_class *class,
-                      const char *name, int types)
-{
-  struct outp_driver *d = xmalloc (sizeof *d);
-  d->class = class;
-  d->name = xstrdup (name);
-  d->page_open = false;
-  d->device = types;
-  d->cp_x = d->cp_y = 0;
-  d->ext = NULL;
-  return d;
-}
-
-/* Frees driver D and the data that it owns directly.  The
-   driver's class must already have unregistered D (if it was
-   registered) and freed data private to its class.
-
-   This function is intended to be used by output drivers, not
-   by their clients. */
-void
-outp_free_driver (struct outp_driver *d)
-{
-  free (d->name);
-  free (d);
-}
-
-/* Adds D to the list of drivers that will be used for output. */
-void
-outp_register_driver (struct outp_driver *d)
-{
-  struct outp_driver *victim;
-
-  /* Find like-named driver and delete. */
-  victim = find_driver (d->name);
-  if (victim != NULL)
-    destroy_driver (victim);
-
-  /* Add D to list. */
-  ll_push_tail (&outp_driver_list, &d->node);
-}
-
-/* Remove driver D from the list of drivers that will be used for
-   output. */
-void
-outp_unregister_driver (struct outp_driver *d)
-{
-  ll_remove (&d->node);
-}
-
-/* String LINE is in format:
-   DRIVERNAME:CLASSNAME:DEVICETYPE:OPTIONS
-   Adds a driver to outp_driver_list pursuant to the specification
-   provided.  */
-void
-outp_configure_driver_line (struct substring line_)
-{
-  struct string line = DS_EMPTY_INITIALIZER;
-  struct substring tokens[4];
-  size_t save_idx;
-  size_t i;
-
-  fn_interp_vars (line_, insert_defn_value, NULL, &line);
-
-  save_idx = 0;
-  for (i = 0; i < 4; i++)
-    {
-      struct substring *token = &tokens[i];
-      ds_separate (&line, ss_cstr (i < 3 ? ":" : ""), &save_idx, token);
-      ss_trim (token, ss_cstr (CC_SPACES));
-    }
-
-  if (!ss_is_empty (tokens[0]) && !ss_is_empty (tokens[1]))
-    configure_driver (tokens[0], tokens[1], tokens[2], tokens[3]);
-  else
-    error (0, 0,
-           _("driver definition line missing driver name or class name"));
-
-  ds_destroy (&line);
-}
-
-/* Destroys output driver D. */
-static void
-destroy_driver (struct outp_driver *d)
-{
-  outp_close_page (d);
-  if (d->class && d->class->close_driver)
-    d->class->close_driver (d);
-  outp_unregister_driver (d);
-  outp_free_driver (d);
-}
-
-/* Tries to match S as one of the keywords in TAB, with
-   corresponding information structure INFO.  Returns category
-   code and stores subcategory in *SUBCAT on success.  Returns -1
-   on failure. */
-int
-outp_match_keyword (const char *s, const struct outp_option *tab, int *subcat)
-{
-  for (; tab->keyword != NULL; tab++)
-    if (!strcmp (s, tab->keyword))
-      {
-        *subcat = tab->subcat;
-        return tab->cat;
-      }
-  return -1;
-}
-
-/* Parses UNIT as a dimensional unit.  Returns the multiplicative
-   factor needed to change a quantity measured in that unit into
-   1/72000" units.  If UNIT is empty, it is treated as
-   millimeters.  If the unit is unrecognized, returns 0. */
-static double
-parse_unit (const char *unit)
-{
-  struct unit
-    {
-      char name[3];
-      double factor;
-    };
-
-  static const struct unit units[] =
-    {
-      {"pt", 72000 / 72},
-      {"pc", 72000 / 72 * 12.0},
-      {"in", 72000},
-      {"cm", 72000 / 2.54},
-      {"mm", 72000 / 25.4},
-      {"", 72000 / 25.4},
-    };
-
-  const struct unit *p;
-
-  unit += strspn (unit, CC_SPACES);
-  for (p = units; p < units + sizeof units / sizeof *units; p++)
-    if (!strcasecmp (unit, p->name))
-      return p->factor;
-  return 0.0;
-}
-
-/* Determines the size of a dimensional measurement and returns
-   the size in units of 1/72000".  Units are assumed to be
-   millimeters unless otherwise specified.  Returns 0 on
-   error. */
-int
-outp_evaluate_dimension (const char *dimen)
-{
-  double raw, factor;
-  char *tail;
-
-  /* Number. */
-  raw = strtod (dimen, &tail);
-  if (raw <= 0.0)
-    goto syntax_error;
-
-  /* Unit. */
-  factor = parse_unit (tail);
-  if (factor == 0.0)
-    goto syntax_error;
-
-  return raw * factor;
-
-syntax_error:
-  error (0, 0, _("`%s' is not a valid length."), dimen);
-  return 0;
-}
-
-/* Stores the dimensions in 1/72000" units of paper identified by
-   SIZE, which is of form `HORZ x VERT [UNIT]' where HORZ and
-   VERT are numbers and UNIT is an optional unit of measurement,
-   into *H and *V.  Return true on success. */
-static bool
-parse_paper_size (const char *size, int *h, int *v)
-{
-  double raw_h, raw_v, factor;
-  char *tail;
-
-  /* Width. */
-  raw_h = strtod (size, &tail);
-  if (raw_h <= 0.0)
-    return false;
-
-  /* Delimiter. */
-  tail += strspn (tail, CC_SPACES "x,");
-
-  /* Length. */
-  raw_v = strtod (tail, &tail);
-  if (raw_v <= 0.0)
-    return false;
-
-  /* Unit. */
-  factor = parse_unit (tail);
-  if (factor == 0.0)
-    return false;
-
-  *h = raw_h * factor + .5;
-  *v = raw_v * factor + .5;
-  return true;
-}
-
-static bool
-get_standard_paper_size (struct substring name, int *h, int *v)
-{
-  static const char *sizes[][2] =
-    {
-      {"a0", "841 x 1189 mm"},
-      {"a1", "594 x 841 mm"},
-      {"a2", "420 x 594 mm"},
-      {"a3", "297 x 420 mm"},
-      {"a4", "210 x 297 mm"},
-      {"a5", "148 x 210 mm"},
-      {"b5", "176 x 250 mm"},
-      {"a6", "105 x 148 mm"},
-      {"a7", "74 x 105 mm"},
-      {"a8", "52 x 74 mm"},
-      {"a9", "37 x 52 mm"},
-      {"a10", "26 x 37 mm"},
-      {"b0", "1000 x 1414 mm"},
-      {"b1", "707 x 1000 mm"},
-      {"b2", "500 x 707 mm"},
-      {"b3", "353 x 500 mm"},
-      {"b4", "250 x 353 mm"},
-      {"letter", "612 x 792 pt"},
-      {"legal", "612 x 1008 pt"},
-      {"executive", "522 x 756 pt"},
-      {"note", "612 x 792 pt"},
-      {"11x17", "792 x 1224 pt"},
-      {"tabloid", "792 x 1224 pt"},
-      {"statement", "396 x 612 pt"},
-      {"halfletter", "396 x 612 pt"},
-      {"halfexecutive", "378 x 522 pt"},
-      {"folio", "612 x 936 pt"},
-      {"quarto", "610 x 780 pt"},
-      {"ledger", "1224 x 792 pt"},
-      {"archA", "648 x 864 pt"},
-      {"archB", "864 x 1296 pt"},
-      {"archC", "1296 x 1728 pt"},
-      {"archD", "1728 x 2592 pt"},
-      {"archE", "2592 x 3456 pt"},
-      {"flsa", "612 x 936 pt"},
-      {"flse", "612 x 936 pt"},
-      {"csheet", "1224 x 1584 pt"},
-      {"dsheet", "1584 x 2448 pt"},
-      {"esheet", "2448 x 3168 pt"},
-    };
-
-  size_t i;
-
-  for (i = 0; i < sizeof sizes / sizeof *sizes; i++)
-    if (ss_equals_case (ss_cstr (sizes[i][0]), name))
-      {
-        bool ok = parse_paper_size (sizes[i][1], h, v);
-        assert (ok);
-        return ok;
-      }
-  error (0, 0, _("unknown paper type `%.*s'"),
-         (int) ss_length (name), ss_data (name));
-  return false;
-}
-
-/* Reads file FILE_NAME to find a paper size.  Stores the
-   dimensions, in 1/72000" units, into *H and *V.  Returns true
-   on success, false on failure. */
-static bool
-read_paper_conf (const char *file_name, int *h, int *v)
-{
-  struct string line = DS_EMPTY_INITIALIZER;
-  int line_number = 0;
-  FILE *file;
-
-  file = fopen (file_name, "r");
-  if (file == NULL)
-    {
-      error (0, errno, _("error opening \"%s\""), file_name);
-      return false;
-    }
-
-  for (;;)
-    {
-      struct substring name;
-
-      if (!ds_read_config_line (&line, &line_number, file))
-       {
-         if (ferror (file))
-           error (0, errno, _("error reading \"%s\""), file_name);
-         break;
-       }
-
-      name = ds_ss (&line);
-      ss_trim (&name, ss_cstr (CC_SPACES));
-      if (!ss_is_empty (name))
-        {
-          bool ok = get_standard_paper_size (name, h, v);
-          fclose (file);
-          ds_destroy (&line);
-          return ok;
-        }
-    }
-
-  fclose (file);
-  ds_destroy (&line);
-  error (0, 0, _("paper size file \"%s\" does not state a paper size"),
-         file_name);
-  return false;
-}
-
-/* The user didn't specify a paper size, so let's choose a
-   default based on his environment.  Stores the
-   dimensions, in 1/72000" units, into *H and *V.  Returns true
-   on success, false on failure. */
-static bool
-get_default_paper_size (int *h, int *v)
-{
-  /* libpaper in Debian (and other distributions?) allows the
-     paper size to be specified in $PAPERSIZE or in a file
-     specified in $PAPERCONF. */
-  if (getenv ("PAPERSIZE") != NULL)
-    return get_standard_paper_size (ss_cstr (getenv ("PAPERSIZE")), h, v);
-  if (getenv ("PAPERCONF") != NULL)
-    return read_paper_conf (getenv ("PAPERCONF"), h, v);
-
-#if HAVE_LC_PAPER
-  /* LC_PAPER is a non-standard glibc extension. */
-  *h = (int) nl_langinfo(_NL_PAPER_WIDTH) * (72000 / 25.4);
-  *v = (int) nl_langinfo(_NL_PAPER_HEIGHT) * (72000 / 25.4);
-  if (*h > 0 && *v > 0)
-     return true;
-#endif
-
-  /* libpaper defaults to /etc/papersize. */
-  if (fn_exists ("/etc/papersize"))
-    return read_paper_conf ("/etc/papersize", h, v);
-
-  /* Can't find a default. */
-  return false;
-}
-
-/* Stores the dimensions, in 1/72000" units, of paper identified
-   by SIZE into *H and *V.  SIZE can be the name of a kind of
-   paper ("a4", "letter", ...) or a pair of dimensions
-   ("210x297", "8.5x11in", ...).  Returns true on success, false
-   on failure.  On failure, *H and *V are set for A4 paper. */
-bool
-outp_get_paper_size (const char *size, int *h, int *v)
-{
-  struct substring s;
-  bool ok;
-
-  s = ss_cstr (size);
-  ss_trim (&s, ss_cstr (CC_SPACES));
-
-  if (ss_is_empty (s))
-    {
-      /* Treat empty string as default paper size. */
-      ok = get_default_paper_size (h, v);
-    }
-  else if (isdigit (ss_first (s)))
-    {
-      /* Treat string that starts with digit as explicit size. */
-      ok = parse_paper_size (size, h, v);
-      if (!ok)
-        error (0, 0, _("syntax error in paper size `%s'"), size);
-    }
-  else
-    {
-      /* Check against standard paper sizes. */
-      ok = get_standard_paper_size (s, h, v);
-    }
-
-  /* Default to A4 on error. */
-  if (!ok)
-    {
-      *h = 210 * (72000 / 25.4);
-      *v = 297 * (72000 / 25.4);
-    }
-  return ok;
-}
-
-/* If D is NULL, returns the first enabled driver if any, NULL if
-   none.  Otherwise D must be the last driver returned by this
-   function, in which case the next enabled driver is returned or NULL
-   if that was the last. */
-struct outp_driver *
-outp_drivers (struct outp_driver *d)
-{
-  do
-    {
-      struct ll *next;
-
-      next = d == NULL ? ll_head (&outp_driver_list) : ll_next (&d->node);
-      if (next == ll_null (&outp_driver_list))
-        return NULL;
-
-      d = ll_data (next, struct outp_driver, node);
-    }
-  while (d->device != 0 && (d->device & disabled_devices) == d->device);
-
-  return d;
-}
-
-/* Enables (if ENABLE is true) or disables (if ENABLE is false) the
-   device(s) given in mask DEVICE. */
-void
-outp_enable_device (bool enable, int device)
-{
-  if (enable)
-    disabled_devices &= ~device;
-  else
-    disabled_devices |= device;
-}
-
-/* Opens a page on driver D (if one is not open). */
-void
-outp_open_page (struct outp_driver *d)
-{
-  if (!d->page_open)
-    {
-      d->cp_x = d->cp_y = 0;
-
-      d->page_open = true;
-      if (d->class->open_page != NULL)
-        d->class->open_page (d);
-    }
-}
-
-/* Closes the page on driver D (if one is open). */
-void
-outp_close_page (struct outp_driver *d)
-{
-  if (d->page_open)
-    {
-      if (d->class->close_page != NULL)
-        d->class->close_page (d);
-      d->page_open = false;
-    }
-}
-
-/* Ejects the page on device D, if a page is open and non-blank,
-   and opens a new page.  */
-void
-outp_eject_page (struct outp_driver *d)
-{
-  if (d->page_open && d->cp_y != 0)
-    outp_close_page (d);
-  outp_open_page (d);
-}
-
-/* Flushes output to screen devices, so that the user can see
-   output that doesn't fill up an entire page. */
-void
-outp_flush (struct outp_driver *d)
-{
-  if (d->device & OUTP_DEV_SCREEN && d->class->flush != NULL)
-    {
-      outp_close_page (d);
-      d->class->flush (d);
-    }
-}
-
-/* Returns the width of string S, in device units, when output on
-   device D. */
-int
-outp_string_width (struct outp_driver *d, const char *s, enum outp_font font)
-{
-  struct outp_text text;
-  int width;
-
-  text.font = font;
-  text.justification = OUTP_LEFT;
-  text.string = ss_cstr (s);
-  text.h = text.v = INT_MAX;
-  d->class->text_metrics (d, &text, &width, NULL);
-
-  return width;
-}
diff --git a/src/output/output.h b/src/output/output.h
deleted file mode 100644 (file)
index 398781f..0000000
+++ /dev/null
@@ -1,179 +0,0 @@
-/* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000, 2007, 2009 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 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.
-
-   You should have received a copy of the GNU General Public License
-   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
-
-#ifndef OUTPUT_OUTPUT_H
-#define OUTPUT_OUTPUT_H 1
-
-#include <libpspp/ll.h>
-#include <libpspp/str.h>
-
-/* Line styles.  */
-enum outp_line_style
-  {
-    OUTP_L_NONE,               /* No line. */
-    OUTP_L_SINGLE,             /* Single line. */
-    OUTP_L_DOUBLE,             /* Double line. */
-    OUTP_L_COUNT
-  };
-
-/* Text justification. */
-enum outp_justification
-  {
-    OUTP_RIGHT,                 /* Right justification. */
-    OUTP_LEFT,                  /* Left justification. */
-    OUTP_CENTER,                /* Center justification. */
-  };
-
-enum outp_font
-  {
-    OUTP_FIXED,                 /* Fixed-width font. */
-    OUTP_PROPORTIONAL,          /* Proportional font. */
-    OUTP_EMPHASIS,              /* Proportional font used for emphasis. */
-    OUTP_FONT_CNT               /* Number of fonts. */
-  };
-
-/* Describes text output. */
-struct outp_text
-  {
-    enum outp_font font;
-    enum outp_justification justification;
-    struct substring string;
-    int h, v;                  /* Horizontal, vertical size. */
-    int x, y;                  /* Position. */
-  };
-
-struct som_entity;
-struct outp_driver;
-struct chart;
-
-/* Defines a class of output driver. */
-struct outp_class
-  {
-    const char *name;          /* Name of this driver class. */
-    int special;               /* Boolean value. */
-
-    bool (*open_driver) (const char *name, int types,
-                         struct substring options);
-    bool (*close_driver) (struct outp_driver *);
-
-    void (*open_page) (struct outp_driver *);
-    void (*close_page) (struct outp_driver *);
-
-    void (*flush) (struct outp_driver *);
-
-    void (*output_chart) (struct outp_driver *, const struct chart *);
-
-    /* special != 0 only. */
-    void (*submit) (struct outp_driver *, struct som_entity *);
-
-    /* special == 0 only.  */
-    void (*line) (struct outp_driver *, int x0, int y0, int x1, int y1,
-                  enum outp_line_style top, enum outp_line_style left,
-                  enum outp_line_style bottom, enum outp_line_style right);
-    void (*text_metrics) (struct outp_driver *, const struct outp_text *,
-                          int *width, int *height);
-    void (*text_draw) (struct outp_driver *, const struct outp_text *);
-  };
-
-/* Device types. */
-enum
-  {
-    OUTP_DEV_NONE = 0,         /* None of the below. */
-    OUTP_DEV_LISTING = 001,    /* Listing device. */
-    OUTP_DEV_SCREEN = 002,     /* Screen device. */
-    OUTP_DEV_PRINTER = 004,    /* Printer device. */
-  };
-
-/* Defines the configuration of an output driver. */
-struct outp_driver
-  {
-    struct ll node;             /* Node in list of drivers. */
-    const struct outp_class *class;    /* Driver class. */
-    char *name;                        /* Name of this driver. */
-    bool page_open;            /* 1=page is open, 0=page is closed. */
-    int device;                        /* Zero or more of OUTP_DEV_*. */
-    int cp_x, cp_y;            /* Current position. */
-
-    int width, length;         /* Page size. */
-    int font_height;           /* Default font character height. */
-    int prop_em_width;         /* Proportional font em width. */
-    int fixed_width;           /* Fixed-pitch font character width. */
-    int horiz_line_width[OUTP_L_COUNT];        /* Width of horizontal lines. */
-    int vert_line_width[OUTP_L_COUNT]; /* Width of vertical lines. */
-
-    void *ext;                 /* Private extension record. */
-  };
-
-/* Option structure for the keyword recognizer. */
-struct outp_option
-  {
-    const char *keyword;       /* Keyword name. */
-    int cat;                   /* Category. */
-    int subcat;                        /* Subcategory. */
-  };
-
-
-/* Title, subtitle. */
-extern char *outp_title;
-extern char *outp_subtitle;
-
-void outp_init (void);
-void outp_done (void);
-void outp_read_devices (void);
-void outp_configure_driver_line (struct substring);
-
-struct outp_driver *outp_allocate_driver (const struct outp_class *class,
-                                          const char *name, int types);
-void outp_free_driver (struct outp_driver *);
-void outp_register_driver (struct outp_driver *);
-void outp_unregister_driver (struct outp_driver *);
-
-void outp_configure_clear (void);
-void outp_configure_add (char *);
-void outp_configure_macro (char *);
-
-void outp_list_classes (void);
-
-void outp_enable_device (bool enable, int device);
-struct outp_driver *outp_drivers (struct outp_driver *);
-
-bool outp_parse_options (const char *driver_name, struct substring options,
-                         bool (*callback) (void *aux, const char *key,
-                                           const struct string *value),
-                         void *aux);
-int outp_match_keyword (const char *, const struct outp_option *, int *);
-
-int outp_evaluate_dimension (const char *);
-bool outp_get_paper_size (const char *, int *h, int *v);
-
-void outp_open_page (struct outp_driver *);
-void outp_close_page (struct outp_driver *);
-void outp_eject_page (struct outp_driver *);
-void outp_flush (struct outp_driver *);
-
-int outp_string_width (struct outp_driver *, const char *, enum outp_font);
-
-/* Imported from som-frnt.c. */
-void som_destroy_driver (struct outp_driver *);
-
-/* Common drivers. */
-extern const struct outp_class ascii_class;
-#ifdef HAVE_CAIRO
-extern const struct outp_class cairo_class;
-#endif
-extern const struct outp_class odt_class;
-
-#endif /* output/output.h */
diff --git a/src/output/render.c b/src/output/render.c
new file mode 100644 (file)
index 0000000..05db149
--- /dev/null
@@ -0,0 +1,1302 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2009 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 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.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "libpspp/assertion.h"
+#include "libpspp/hash-functions.h"
+#include "libpspp/hmap.h"
+#include "output/render.h"
+#include "output/table.h"
+
+#include "gl/minmax.h"
+#include "gl/xalloc.h"
+
+/* This file uses TABLE_HORZ and TABLE_VERT enough to warrant abbreviating. */
+#define H TABLE_HORZ
+#define V TABLE_VERT
+\f
+/* A layout for rendering a specific table on a specific device.
+
+   May represent the layout of an entire table presented to
+   render_page_create(), or a rectangular subregion of a table broken out using
+   render_page_next() to allow a table to be broken across multiple pages. */
+struct render_page
+  {
+    const struct render_params *params; /* Parameters of the target device. */
+    struct table *table;                /* Table rendered. */
+    int ref_cnt;
+
+    /* Local copies of table->n and table->h, for convenience. */
+    int n[TABLE_N_AXES];
+    int h[TABLE_N_AXES][2];
+
+    /* cp[H] represents x positions within the table.
+       cp[H][0] = 0.
+       cp[H][1] = the width of the leftmost vertical rule.
+       cp[H][2] = cp[H][1] + the width of the leftmost column.
+       cp[H][3] = cp[H][2] + the width of the second-from-left vertical rule.
+       and so on:
+       cp[H][2 * nc] = x position of the rightmost vertical rule.
+       cp[H][2 * nc + 1] = total table width including all rules.
+
+       Similarly, cp[V] represents y positions within the table.
+       cp[V][0] = 0.
+       cp[V][1] = the height of the topmost horizontal rule.
+       cp[V][2] = cp[V][1] + the height of the topmost column.
+       cp[V][3] = cp[V][2] + the height of the second-from-top horizontal rule.
+       and so on:
+       cp[V][2 * nr] = y position of the bottommost horizontal rule.
+       cp[V][2 * nr + 1] = total table height including all rules.
+
+       Rules and columns can have width or height 0, in which case consecutive
+       values in this array are equal. */
+    int *cp[TABLE_N_AXES];
+
+    /* render_break_next() can break a table such that some cells are not fully
+       contained within a render_page.  This will happen if a cell is too wide
+       or two tall to fit on a single page, or if a cell spans multiple rows or
+       columns and the page only includes some of those rows or columns.
+
+       This hash table contains "struct render_overflow"s that represents each
+       such cell that doesn't completely fit on this page.
+
+       Each overflow cell borders at least one header edge of the table and may
+       border more.  (A single table cell that is so large that it fills the
+       entire page can overflow on all four sides!) */
+    struct hmap overflows;
+
+    /* If a single column (or row) is too wide (or tall) to fit on a page
+       reasonably, then render_break_next() will split a single row or column
+       across multiple render_pages.  This member indicates when this has
+       happened:
+
+       is_edge_cutoff[H][0] is true if pixels have been cut off the left side
+       of the leftmost column in this page, and false otherwise.
+
+       is_edge_cutoff[H][1] is true if pixels have been cut off the right side
+       of the rightmost column in this page, and false otherwise.
+
+       is_edge_cutoff[V][0] and is_edge_cutoff[V][1] are similar for the top
+       and bottom of the table.
+
+       The effect of is_edge_cutoff is to prevent rules along the edge in
+       question from being rendered.
+
+       When is_edge_cutoff is true for a given edge, the 'overflows' hmap will
+       contain a node for each cell along that edge. */
+    bool is_edge_cutoff[TABLE_N_AXES][2];
+
+    /* If part of a joined cell would be cut off by breaking a table along
+       'axis' at the rule with offset 'z' (where 0 <= z <= n[axis]), then
+       join_crossing[axis][z] is the thickness of the rule that would be cut
+       off.
+
+       This is used to know to allocate extra space for breaking at such a
+       position, so that part of the cell's content is not lost.
+
+       This affects breaking a table only when headers are present.  When
+       headers are not present, the rule's thickness is used for cell content,
+       so no part of the cell's content is lost (and in fact it is duplicated
+       across both pages). */
+    int *join_crossing[TABLE_N_AXES];
+  };
+
+/* Returns the offset in struct render_page's cp[axis] array of the rule with
+   index RULE_IDX.  That is, if RULE_IDX is 0, then the offset is that of the
+   leftmost or topmost rule; if RULE_IDX is 1, then the offset is that of the
+   next rule to the right (or below); and so on. */
+static int
+rule_ofs (int rule_idx)
+{
+  return rule_idx * 2;
+}
+
+/* Returns the offset in struct render_page's cp[axis] array of the rule with
+   index RULE_IDX_R, which counts from the right side (or bottom) of the page
+   left (or up), according to whether AXIS is H or V, respectively.  That is,
+   if RULE_IDX_R is 0, then the offset is that of the rightmost or bottommost
+   rule; if RULE_IDX is 1, then the offset is that of the next rule to the left
+   (or above); and so on. */
+static int
+rule_ofs_r (const struct render_page *page, int axis, int rule_idx_r)
+{
+  return (page->n[axis] - rule_idx_r) * 2;
+}
+
+/* Returns the offset in struct render_page's cp[axis] array of the cell with
+   index CELL_IDX.  That is, if CELL_IDX is 0, then the offset is that of the
+   leftmost or topmost cell; if CELL_IDX is 1, then the offset is that of the
+   next cell to the right (or below); and so on. */
+static int
+cell_ofs (int cell_idx)
+{
+  return cell_idx * 2 + 1;
+}
+
+/* Returns the width of PAGE along AXIS from OFS0 to OFS1, exclusive. */
+static int
+axis_width (const struct render_page *page, int axis, int ofs0, int ofs1)
+{
+  return page->cp[axis][ofs1] - page->cp[axis][ofs0];
+}
+
+/* Returns the width of the headers in PAGE along AXIS. */
+static int
+headers_width (const struct render_page *page, int axis)
+{
+  int h0 = page->h[axis][0];
+  int w0 = axis_width (page, axis, rule_ofs (0), cell_ofs (h0));
+  int n = page->n[axis];
+  int h1 = page->h[axis][1];
+  int w1 = axis_width (page, axis, rule_ofs_r (page, axis, h1), cell_ofs (n));
+  return w0 + w1;
+}
+
+/* Returns the width of cell X along AXIS in PAGE. */
+static int
+cell_width (const struct render_page *page, int axis, int x)
+{
+  return axis_width (page, axis, cell_ofs (x), cell_ofs (x) + 1);
+}
+
+/* Returns the width of cells X0 through X1, exclusive, along AXIS in PAGE. */
+static int
+joined_width (const struct render_page *page, int axis, int x0, int x1)
+{
+  return axis_width (page, axis, cell_ofs (x0), cell_ofs (x1) - 1);
+}
+
+/* Returns the width of the widest cell, excluding headers, along AXIS in
+   PAGE. */
+static int
+max_cell_width (const struct render_page *page, int axis)
+{
+  int n = page->n[axis];
+  int x0 = page->h[axis][0];
+  int x1 = n - page->h[axis][1];
+  int x, max;
+
+  max = 0;
+  for (x = x0; x < x1; x++)
+    {
+      int w = cell_width (page, axis, x);
+      if (w > max)
+        max = w;
+    }
+  return max;
+}
+\f
+/* A cell that doesn't completely fit on the render_page. */
+struct render_overflow
+  {
+    struct hmap_node node;      /* In render_page's 'overflows' hmap. */
+
+    /* Occupied region of page.
+
+       d[H][0] is the leftmost column.
+       d[H][1] is the rightmost column, plus 1.
+       d[V][0] is the top row.
+       d[V][1] is the bottom row, plus 1.
+
+       The cell in its original table might occupy a larger region.  This
+       member reflects the size of the cell in the current render_page, after
+       trimming off any rows or columns due to page-breaking. */
+    int d[TABLE_N_AXES];
+
+    /* The space that has been trimmed off the cell:
+
+       overflow[H][0]: space trimmed off its left side.
+       overflow[H][1]: space trimmed off its right side.
+       overflow[V][0]: space trimmed off its top.
+       overflow[V][1]: space trimmed off its bottom.
+
+       During rendering, this information is used to position the rendered
+       portion of the cell within the available space.
+
+       When a cell is rendered, sometimes it is permitted to spill over into
+       space that is ordinarily reserved for rules.  Either way, this space is
+       still included in overflow values.
+
+       Suppose, for example, that a cell that joins 2 columns has a width of 60
+       pixels and content "abcdef", that the 2 columns that it joins have
+       widths of 20 and 30 pixels, respectively, and that therefore the rule
+       between the two joined columns has a width of 10 (20 + 10 + 30 = 60).
+       It might render like this, if each character is 10x10, and showing a few
+       extra table cells for context:
+
+                                     +------+
+                                     |abcdef|
+                                     +--+---+
+                                     |gh|ijk|
+                                     +--+---+
+
+       If this render_page is broken at the rule that separates "gh" from
+       "ijk", then the page that contains the left side of the "abcdef" cell
+       will have overflow[H][1] of 10 + 30 = 40 for its portion of the cell,
+       and the page that contains the right side of the cell will have
+       overflow[H][0] of 20 + 10 = 30.  The two resulting pages would look like
+       this:
+
+
+                                       +---
+                                       |abc
+                                       +--+
+                                       |gh|
+                                       +--+
+
+       and:
+
+                                       ----+
+                                       cdef|
+                                       +---+
+                                       |ijk|
+                                       +---+
+    */
+    int overflow[TABLE_N_AXES][2];
+  };
+
+/* Returns a hash value for (X,Y). */
+static unsigned int
+hash_overflow (int x, int y)
+{
+  return hash_int (x + (y << 16), 0);
+}
+
+/* Searches PAGE's set of render_overflow for one whose top-left cell is
+   (X,Y).  Returns it, if there is one, otherwise a null pointer. */
+static const struct render_overflow *
+find_overflow (const struct render_page *page, int x, int y)
+{
+  if (!hmap_is_empty (&page->overflows))
+    {
+      const struct render_overflow *of;
+
+      HMAP_FOR_EACH_WITH_HASH (of, struct render_overflow, node,
+                               hash_overflow (x, y), &page->overflows)
+        if (x == of->d[H] && y == of->d[V])
+          return of;
+    }
+
+  return NULL;
+}
+\f
+/* Row or column dimensions.  Used to figure the size of a table in
+   render_page_create() and discarded after that. */
+struct render_row
+  {
+    /* Width without considering rows (or columns) that span more than one (or
+       column). */
+    int unspanned;
+
+    /* Width taking spanned rows (or columns) into consideration. */
+    int width;
+  };
+
+/* Modifies the 'width' members of the N elements of ROWS so that their sum,
+   when added to rule widths RULES[1] through RULES[N - 1] inclusive, is at
+   least WIDTH. */
+static void
+distribute_spanned_width (int width,
+                          struct render_row *rows, const int *rules, int n)
+{
+  int total_unspanned;
+  double w, d0, d1, d;
+  int x;
+
+  /* Sum up the unspanned widths of the N rows for use as weights. */
+  total_unspanned = 0;
+  for (x = 0; x < n; x++)
+    total_unspanned += rows[x].unspanned;
+  for (x = 0; x < n - 1; x++)
+    total_unspanned += rules[x + 1];
+  if (total_unspanned >= width)
+    return;
+
+  /* The algorithm used here is based on the following description from HTML 4:
+
+         For cells that span multiple columns, a simple approach consists of
+         apportioning the min/max widths evenly to each of the constituent
+         columns.  A slightly more complex approach is to use the min/max
+         widths of unspanned cells to weight how spanned widths are
+         apportioned.  Experiments suggest that a blend of the two approaches
+         gives good results for a wide range of tables.
+
+     We blend the two approaches half-and-half, except that we cannot use the
+     unspanned weights when 'total_unspanned' is 0 (because that would cause a
+     division by zero).
+
+     This implementation uses floating-point types and operators, but all the
+     values involved are integers.  For integers smaller than 53 bits, this
+     should not lose any precision, and it should degrade gracefully for larger
+     values.
+
+     The calculation we want to do is this:
+
+        w0 = width / n
+        w1 = width * (column's unspanned width) / (total unspanned width)
+        (column's width) = (w0 + w1) / 2
+
+     We implement it as a precise calculation in integers by multiplying w0 and
+     w1 by the common denominator of all three calculations (d), dividing that
+     out in the column width calculation, and then keeping the remainder for
+     the next iteration.
+  */
+  d0 = n;
+  d1 = total_unspanned * 2.0;
+  d = d0 * d1;
+  if (total_unspanned > 0)
+    d *= 2.0;
+  w = floor (d / 2.0);
+  for (x = 0; x < n; x++)
+    {
+      w += width * d1;
+      if (total_unspanned > 0)
+        {
+          double unspanned = rows[x].unspanned * 2.0;
+          if (x < n - 1)
+            unspanned += rules[x + 1];
+          if (x > 0)
+            unspanned += rules[x];
+          w += width * unspanned * d0;
+        }
+
+      rows[x].width = w / d;
+      w -= rows[x].width * d;
+    }
+}
+
+/* Initializes PAGE->cp[AXIS] from the row widths in ROWS and the rule widths
+   in RULES. */
+static void
+accumulate_row_widths (const struct render_page *page, enum table_axis axis,
+                       const struct render_row *rows, const int *rules)
+{
+  int n = page->n[axis];
+  int *cp;
+  int z;
+
+  cp = page->cp[axis];
+  cp[0] = 0;
+  for (z = 0; z < n; z++)
+    {
+      cp[1] = cp[0] + rules[z];
+      cp[2] = cp[1] + rows[z].width;
+      cp += 2;
+    }
+  cp[1] = cp[0] + rules[n];
+}
+
+/* Returns the sum of widths of the N ROWS and N+1 RULES. */
+static int
+calculate_table_width (int n, const struct render_row *rows, int *rules)
+{
+  int width;
+  int x;
+
+  width = 0;
+  for (x = 0; x < n; x++)
+    width += rows[x].width;
+  for (x = 0; x <= n; x++)
+    width += rules[x];
+
+  return width;
+}
+\f
+/* Rendering utility functions. */
+
+/* Returns the line style to use for drawing a rule of the given TYPE. */
+static enum render_line_style
+rule_to_render_type (unsigned char type)
+{
+  switch (type)
+    {
+    case TAL_0:
+    case TAL_GAP:
+      return RENDER_LINE_NONE;
+    case TAL_1:
+      return RENDER_LINE_SINGLE;
+    case TAL_2:
+      return RENDER_LINE_DOUBLE;
+    default:
+      NOT_REACHED ();
+    }
+}
+
+/* Returns the width of the rule in TABLE that is at offset Z along axis A, if
+   rendered with PARAMS.  */
+static int
+measure_rule (const struct render_params *params, const struct table *table,
+              enum table_axis a, int z)
+{
+  enum table_axis b = !a;
+  unsigned int rules;
+  int d[TABLE_N_AXES];
+  int width, i;
+
+  /* Determine all types of rules that are present, as a bitmap in 'rules'
+     where rule type 't' is present if bit 2**t is set. */
+  rules = 0;
+  d[a] = z;
+  for (d[b] = 0; d[b] < table->n[b]; d[b]++)
+    rules |= 1u << table_get_rule (table, a, d[H], d[V]);
+
+  /* Calculate maximum width of the rules that are present. */
+  width = 0;
+  for (i = 0; i < N_LINES; i++)
+    if (rules & (1u << i))
+      width = MAX (width, params->line_widths[a][rule_to_render_type (i)]);
+
+  return width;
+}
+
+/* Allocates and returns a new render_page using PARAMS and TABLE.  Allocates
+   space for all of the members of the new page, but the caller must initialize
+   the 'cp' member itself. */
+static struct render_page *
+render_page_allocate (const struct render_params *params,
+                      struct table *table)
+{
+  struct render_page *page;
+  int i;
+
+  page = xmalloc (sizeof *page);
+  page->params = params;
+  page->table = table;
+  page->ref_cnt = 1;
+  page->n[H] = table->n[H];
+  page->n[V] = table->n[V];
+  page->h[H][0] = table->h[H][0];
+  page->h[H][1] = table->h[H][1];
+  page->h[V][0] = table->h[V][0];
+  page->h[V][1] = table->h[V][1];
+
+  for (i = 0; i < TABLE_N_AXES; i++)
+    {
+      page->cp[i] = xmalloc ((2 * page->n[i] + 2) * sizeof *page->cp[i]);
+      page->join_crossing[i] = xzalloc ((page->n[i] + 1) * sizeof *page->join_crossing[i]);
+    }
+
+  hmap_init (&page->overflows);
+  memset (page->is_edge_cutoff, 0, sizeof page->is_edge_cutoff);
+
+  return page;
+}
+
+/* Allocates and returns a new render_page for PARAMS and TABLE, initializing
+   cp[H] in the new page from ROWS and RULES.  The caller must still initialize
+   cp[V]. */
+static struct render_page *
+create_page_with_exact_widths (const struct render_params *params,
+                               struct table *table,
+                               const struct render_row *rows, int *rules)
+{
+  struct render_page *page = render_page_allocate (params, table);
+  accumulate_row_widths (page, H, rows, rules);
+  return page;
+}
+
+/* Allocates and returns a new render_page for PARAMS and TABLE.
+
+   Initializes cp[H] in the new page by setting the width of each row 'i' to
+   somewhere between the minimum cell width ROW_MIN[i].width and the maximum
+   ROW_MAX[i].width.  Sets the width of rules to those in RULES.
+
+   W_MIN is the sum of ROWS_MIN[].width.
+
+   W_MAX is the sum of ROWS_MAX[].width.
+
+   The caller must still initialize cp[V]. */
+static struct render_page *
+create_page_with_interpolated_widths (const struct render_params *params,
+                                      struct table *table,
+                                      const struct render_row *rows_min,
+                                      const struct render_row *rows_max,
+                                      int w_min, int w_max, const int *rules)
+{
+  /* This implementation uses floating-point types and operators, but all the
+     values involved are integers.  For integers smaller than 53 bits, this
+     should not lose any precision, and it should degrade gracefully for larger
+     values. */
+  const int n = table->n[H];
+  const double avail = params->size[H] - w_min;
+  const double wanted = w_max - w_min;
+  struct render_page *page;
+  double w;
+  int *cph;
+  int x;
+
+  assert (wanted > 0);
+
+  page = render_page_allocate (params, table);
+
+  cph = page->cp[H];
+  *cph = 0;
+  w = (int) wanted / 2;
+  for (x = 0; x < n; x++)
+    {
+      int extra;
+
+      w += avail * (rows_max[x].width - rows_min[x].width);
+      extra = w / wanted;
+      w -= extra * wanted;
+
+      cph[1] = cph[0] + rules[x];
+      cph[2] = cph[1] + rows_min[x].width + extra;
+      cph += 2;
+    }
+  cph[1] = cph[0] + rules[n];
+
+  assert (page->cp[H][n * 2 + 1] == params->size[H]);
+  return page;
+}
+
+\f
+static void
+set_join_crossings (struct render_page *page, enum table_axis axis,
+                    const struct table_cell *cell, int *rules)
+{
+  int z;
+
+  for (z = cell->d[axis][0] + 1; z <= cell->d[axis][1] - 1; z++)
+    page->join_crossing[axis][z] = rules[z];
+}
+
+/* Creates and returns a new render_page for rendering TABLE on a device
+   described by PARAMS.
+
+   The new render_page will be suitable for rendering on a device whose page
+   size is PARAMS->size, but the caller is responsible for actually breaking it
+   up to fit on such a device, using the render_break abstraction.  */
+struct render_page *
+render_page_create (const struct render_params *params,
+                    const struct table *table_)
+{
+  struct render_page *page;
+  struct table *table;
+  enum { MIN, MAX };
+  struct render_row *columns[2];
+  struct render_row *rows;
+  int table_widths[2];
+  int *rules[TABLE_N_AXES];
+  int nr, nc;
+  int x, y;
+  int i;
+  enum table_axis axis;
+
+  table = table_ref (table_);
+  nc = table_nc (table);
+  nr = table_nr (table);
+
+  /* Figure out rule widths. */
+  for (axis = 0; axis < TABLE_N_AXES; axis++)
+    {
+      int n = table->n[axis] + 1;
+      int z;
+
+      rules[axis] = xnmalloc (n, sizeof *rules);
+      for (z = 0; z < n; z++)
+        rules[axis][z] = measure_rule (params, table, axis, z);
+    }
+
+  /* Calculate minimum and maximum widths of cells that do not
+     span multiple columns. */
+  for (i = 0; i < 2; i++)
+    columns[i] = xzalloc (nc * sizeof *columns[i]);
+  for (y = 0; y < nr; y++)
+    for (x = 0; x < nc; )
+      {
+        struct table_cell cell;
+
+        table_get_cell (table, x, y, &cell);
+        if (y == cell.d[V][0] && table_cell_colspan (&cell) == 1)
+          {
+            int w[2];
+            int i;
+
+            params->measure_cell_width (params->aux, &cell, &w[MIN], &w[MAX]);
+            for (i = 0; i < 2; i++)
+              if (columns[i][x].unspanned < w[i])
+                columns[i][x].unspanned = w[i];
+          }
+        x = cell.d[H][1];
+        table_cell_free (&cell);
+      }
+
+  /* Distribute widths of spanned columns. */
+  for (i = 0; i < 2; i++)
+    for (x = 0; x < nc; x++)
+      columns[i][x].width = columns[i][x].unspanned;
+  for (y = 0; y < nr; y++)
+    for (x = 0; x < nc; )
+      {
+        struct table_cell cell;
+
+        table_get_cell (table, x, y, &cell);
+        if (y == cell.d[V][0] && table_cell_colspan (&cell) > 1)
+          {
+            int w[2];
+
+            params->measure_cell_width (params->aux, &cell, &w[MIN], &w[MAX]);
+            for (i = 0; i < 2; i++)
+              distribute_spanned_width (w[i], &columns[i][cell.d[H][0]],
+                                        rules[H], table_cell_colspan (&cell));
+          }
+        x = cell.d[H][1];
+        table_cell_free (&cell);
+      }
+
+  /* Decide final column widths. */
+  for (i = 0; i < 2; i++)
+    table_widths[i] = calculate_table_width (table_nc (table),
+                                             columns[i], rules[H]);
+  if (table_widths[MAX] <= params->size[H])
+    {
+      /* Fits even with maximum widths.  Use them. */
+      page = create_page_with_exact_widths (params, table, columns[MAX],
+                                            rules[H]);
+    }
+  else if (table_widths[MIN] <= params->size[H])
+    {
+      /* Fits with minimum widths, so distribute the leftover space. */
+      page = create_page_with_interpolated_widths (
+        params, table, columns[MIN], columns[MAX],
+        table_widths[MIN], table_widths[MAX], rules[H]);
+    }
+  else
+    {
+      /* Doesn't fit even with minimum widths.  Assign minimums for now, and
+         later we can break it horizontally into multiple pages. */
+      page = create_page_with_exact_widths (params, table, columns[MIN],
+                                            rules[H]);
+    }
+
+  /* Calculate heights of cells that do not span multiple rows. */
+  rows = xzalloc (nr * sizeof *rows);
+  for (y = 0; y < nr; y++)
+    {
+      for (x = 0; x < nc; )
+        {
+          struct render_row *r = &rows[y];
+          struct table_cell cell;
+
+          table_get_cell (table, x, y, &cell);
+          if (y == cell.d[V][0])
+            {
+              if (table_cell_rowspan (&cell) == 1)
+                {
+                  int w = joined_width (page, H, cell.d[H][0], cell.d[H][1]);
+                  int h = params->measure_cell_height (params->aux, &cell, w);
+                  if (h > r->unspanned)
+                    r->unspanned = r->width = h;
+                }
+              else
+                set_join_crossings (page, V, &cell, rules[V]);
+
+              if (table_cell_colspan (&cell) > 1)
+                set_join_crossings (page, H, &cell, rules[H]);
+            }
+          x = cell.d[H][1];
+          table_cell_free (&cell);
+        }
+    }
+  for (i = 0; i < 2; i++)
+    free (columns[i]);
+
+  /* Distribute heights of spanned rows. */
+  for (y = 0; y < nr; y++)
+    for (x = 0; x < nc; )
+      {
+        struct table_cell cell;
+
+        table_get_cell (table, x, y, &cell);
+        if (y == cell.d[V][0] && table_cell_rowspan (&cell) > 1)
+          {
+            int w = joined_width (page, H, cell.d[H][0], cell.d[H][1]);
+            int h = params->measure_cell_height (params->aux, &cell, w);
+            distribute_spanned_width (h, &rows[cell.d[V][0]], rules[V],
+                                      table_cell_rowspan (&cell));
+          }
+        x = cell.d[H][1];
+        table_cell_free (&cell);
+      }
+
+  /* Decide final row heights. */
+  accumulate_row_widths (page, V, rows, rules[V]);
+  free (rows);
+
+  /* Measure headers.  If they are "too big", get rid of them.  */
+  for (axis = 0; axis < TABLE_N_AXES; axis++)
+    {
+      int hw = headers_width (page, axis);
+      if (hw * 2 >= page->params->size[axis]
+          || hw + max_cell_width (page, axis) > page->params->size[axis])
+        {
+          page->table = table_unshare (page->table);
+          page->table->h[axis][0] = page->table->h[axis][1] = 0;
+          page->h[axis][0] = page->h[axis][1] = 0;
+        }
+    }
+
+  free (rules[H]);
+  free (rules[V]);
+
+  return page;
+}
+
+/* Increases PAGE's reference count. */
+struct render_page *
+render_page_ref (const struct render_page *page_)
+{
+  struct render_page *page = CONST_CAST (struct render_page *, page_);
+  page->ref_cnt++;
+  return page;
+}
+
+/* Decreases PAGE's reference count and destroys PAGE if this causes the
+   reference count to fall to zero. */
+void
+render_page_unref (struct render_page *page)
+{
+  if (page != NULL && --page->ref_cnt == 0)
+    {
+      struct render_overflow *overflow, *next;
+
+      HMAP_FOR_EACH_SAFE (overflow, next, struct render_overflow, node,
+                          &page->overflows)
+        free (overflow);
+      hmap_destroy (&page->overflows);
+
+      table_unref (page->table);
+      free (page->cp[H]);
+      free (page->cp[V]);
+      free (page);
+    }
+}
+
+/* Returns the size of PAGE along AXIS.  (This might be larger than the page
+   size specified in the parameters passed to render_page_create().  Use a
+   render_break to break up a render_page into page-sized chunks.) */
+int
+render_page_get_size (const struct render_page *page, enum table_axis axis)
+{
+  return page->cp[axis][page->n[axis] * 2 + 1];
+}
+\f
+/* Drawing render_pages. */
+
+static enum render_line_style
+get_rule (const struct render_page *page, enum table_axis axis,
+          const int d[TABLE_N_AXES])
+{
+  return rule_to_render_type (table_get_rule (page->table,
+                                              axis, d[H] / 2, d[V] / 2));
+}
+
+static bool
+is_rule (int z)
+{
+  return !(z & 1);
+}
+
+static void
+render_rule (const struct render_page *page, const int d[TABLE_N_AXES])
+{
+  enum render_line_style styles[TABLE_N_AXES][2];
+  enum table_axis a;
+
+  for (a = 0; a < TABLE_N_AXES; a++)
+    {
+      enum table_axis b = !a;
+
+      styles[a][0] = styles[a][1] = RENDER_LINE_NONE;
+
+      if (!is_rule (d[a])
+          || (page->is_edge_cutoff[a][0] && d[a] == 0)
+          || (page->is_edge_cutoff[a][1] && d[a] == page->n[a] * 2))
+        continue;
+
+      if (is_rule (d[b]))
+        {
+          if (d[b] > 0)
+            {
+              int e[TABLE_N_AXES];
+              e[H] = d[H];
+              e[V] = d[V];
+              e[b]--;
+              styles[a][0] = get_rule (page, a, e);
+            }
+
+          if (d[b] / 2 < page->table->n[b])
+            styles[a][1] = get_rule (page, a, d);
+        }
+      else
+        styles[a][0] = styles[a][1] = get_rule (page, a, d);
+    }
+
+  if (styles[H][0] != RENDER_LINE_NONE || styles[H][1] != RENDER_LINE_NONE
+      || styles[V][0] != RENDER_LINE_NONE || styles[V][1] != RENDER_LINE_NONE)
+    {
+      int bb[TABLE_N_AXES][2];
+
+      bb[H][0] = page->cp[H][d[H]];
+      bb[H][1] = page->cp[H][d[H] + 1];
+      bb[V][0] = page->cp[V][d[V]];
+      bb[V][1] = page->cp[V][d[V] + 1];
+      page->params->draw_line (page->params->aux, bb, styles);
+    }
+}
+
+static void
+render_cell (const struct render_page *page, const struct table_cell *cell)
+{
+  const struct render_overflow *of;
+  int bb[TABLE_N_AXES][2];
+  int clip[TABLE_N_AXES][2];
+
+  bb[H][0] = clip[H][0] = page->cp[H][cell->d[H][0] * 2 + 1];
+  bb[H][1] = clip[H][1] = page->cp[H][cell->d[H][1] * 2];
+  bb[V][0] = clip[V][0] = page->cp[V][cell->d[V][0] * 2 + 1];
+  bb[V][1] = clip[V][1] = page->cp[V][cell->d[V][1] * 2];
+
+  of = find_overflow (page, cell->d[H][0], cell->d[V][0]);
+  if (of)
+    {
+      enum table_axis axis;
+
+      for (axis = 0; axis < TABLE_N_AXES; axis++)
+        {
+          if (of->overflow[axis][0])
+            {
+              bb[axis][0] -= of->overflow[axis][0];
+              if (cell->d[axis][0] == 0)
+                clip[axis][0] = page->cp[axis][cell->d[axis][0] * 2];
+            }
+          if (of->overflow[axis][1])
+            {
+              bb[axis][1] += of->overflow[axis][1];
+              if (cell->d[axis][1] == page->n[axis])
+                clip[axis][1] = page->cp[axis][cell->d[axis][1] * 2 + 1];
+            }
+        }
+    }
+
+  page->params->draw_cell (page->params->aux, cell, bb, clip);
+}
+
+/* Renders PAGE, by calling the 'draw_line' and 'draw_cell' functions from the
+   render_params provided to render_page_create(). */
+void
+render_page_draw (const struct render_page *page)
+{
+  int x, y;
+
+  for (y = 0; y <= page->n[V] * 2; y++)
+    for (x = 0; x <= page->n[H] * 2; )
+      if (is_rule (x) || is_rule (y))
+        {
+          int d[TABLE_N_AXES];
+          d[H] = x;
+          d[V] = y;
+          render_rule (page, d);
+          x++;
+        }
+      else
+        {
+          struct table_cell cell;
+
+          table_get_cell (page->table, x / 2, y / 2, &cell);
+          if (y / 2 == cell.d[V][0])
+            render_cell (page, &cell);
+          x = rule_ofs (cell.d[H][1]);
+          table_cell_free (&cell);
+        }
+}
+\f
+/* Breaking up tables to fit on a page. */
+
+static int needed_size (const struct render_break *, int cell);
+static bool cell_is_breakable (const struct render_break *, int cell);
+static struct render_page *render_page_select (const struct render_page *,
+                                               enum table_axis,
+                                               int z0, int p0,
+                                               int z1, int p1);
+
+/* Initializes render_break B for breaking PAGE along AXIS.
+
+   Ownership of PAGE is transferred to B.  The caller must use
+   render_page_ref() if it needs to keep a copy of PAGE. */
+void
+render_break_init (struct render_break *b, struct render_page *page,
+                   enum table_axis axis)
+{
+  b->page = page;
+  b->axis = axis;
+  b->cell = page->h[axis][0];
+  b->pixel = 0;
+  b->hw = headers_width (page, axis);
+}
+
+/* Frees B and unrefs the render_page that it owns. */
+void
+render_break_destroy (struct render_break *b)
+{
+  if (b != NULL)
+    render_page_unref (b->page);
+}
+
+/* Returns true if B still has cells that are yet to be returned,
+   false if all of B's page has been processed. */
+bool
+render_break_has_next (const struct render_break *b)
+{
+  const struct render_page *page = b->page;
+  enum table_axis axis = b->axis;
+
+  return b->cell < page->n[axis] - page->h[axis][1];
+}
+
+/* Returns the minimum SIZE argument that, if passed to render_break_next(),
+   will avoid a null return value (if cells are still left). */
+int
+render_break_next_size (const struct render_break *b)
+{
+  const struct render_page *page = b->page;
+  enum table_axis axis = b->axis;
+
+  return (!render_break_has_next (b) ? 0
+          : !cell_is_breakable (b, b->cell) ? needed_size (b, b->cell + 1)
+          : b->hw + page->params->font_size[axis]);
+}
+
+/* Returns a new render_page that is up to SIZE pixels wide along B's axis.
+   Returns a null pointer if B has already been completely broken up, or if
+   SIZE is too small to reasonably render any cells.  The latter will never
+   happen if SIZE is at least as large as the page size passed to
+   render_page_create() along B's axis. */
+struct render_page *
+render_break_next (struct render_break *b, int size)
+{
+  const struct render_page *page = b->page;
+  enum table_axis axis = b->axis;
+  struct render_page *subpage;
+  int cell, pixel;
+
+  if (!render_break_has_next (b))
+    return NULL;
+
+  pixel = 0;
+  for (cell = b->cell; cell < page->n[axis] - page->h[axis][1]; cell++)
+    if (needed_size (b, cell + 1) > size)
+      {
+        if (!cell_is_breakable (b, cell))
+          {
+            if (cell == b->cell)
+              return NULL;
+          }
+        else
+          pixel = (cell == b->cell
+                   ? b->pixel + size - b->hw
+                   : size - needed_size (b, cell));
+        break;
+      }
+
+  subpage = render_page_select (page, axis, b->cell, b->pixel,
+                                pixel ? cell + 1 : cell,
+                                pixel ? cell_width (page, axis, cell) - pixel
+                                : 0);
+  b->cell = cell;
+  b->pixel = pixel;
+  return subpage;
+}
+
+/* Returns the width that would be required along B's axis to render a page
+   from B's current position up to but not including CELL. */
+static int
+needed_size (const struct render_break *b, int cell)
+{
+  const struct render_page *page = b->page;
+  enum table_axis axis = b->axis;
+  int size;
+
+  size = joined_width (page, axis, b->cell, cell) + b->hw - b->pixel;
+  if (page->h[axis][0] && page->h[axis][1])
+    size += page->join_crossing[axis][b->cell];
+
+  return size;
+}
+
+/* Returns true if CELL along B's axis may be broken across a page boundary.
+
+   This is just a heuristic.  Breaking cells across page boundaries can save
+   space, but it looks ugly. */
+static bool
+cell_is_breakable (const struct render_break *b, int cell)
+{
+  const struct render_page *page = b->page;
+  enum table_axis axis = b->axis;
+
+  return cell_width (page, axis, cell) > page->params->size[axis] / 2;
+}
+\f
+/* render_page_select() and helpers. */
+
+struct render_page_selection
+  {
+    const struct render_page *page; /* Page whose slice we are selecting. */
+    struct render_page *subpage; /* New page under construction. */
+    enum table_axis a;   /* Axis of 'page' along which 'subpage' is a slice. */
+    enum table_axis b;   /* The opposite of 'a'. */
+    int z0;              /* First cell along 'a' being selected. */
+    int z1;              /* Last cell being selected, plus 1. */
+    int p0;              /* Number of pixels to trim off left side of z0. */
+    int p1;              /* Number of pixels to trim off right side of z1-1. */
+  };
+
+static void cell_to_subpage (struct render_page_selection *,
+                             const struct table_cell *,
+                             int subcell[TABLE_N_AXES]);
+static const struct render_overflow *find_overflow_for_cell (
+  struct render_page_selection *, const struct table_cell *);
+static struct render_overflow *insert_overflow (struct render_page_selection *,
+                                                const struct table_cell *);
+
+/* Creates and returns a new render_page whose contents are a subregion of
+   PAGE's contents.  The new render_page includes cells Z0 through Z1 along
+   AXIS, plus any headers on AXIS.
+
+   If P0 is nonzero, then it is a number of pixels to exclude from the left or
+   top (according to AXIS) of cell Z0.  Similarly, P1 is a number of pixels to
+   exclude from the right or bottom of cell Z1 - 1.  (P0 and P1 are used to
+   render cells that are too large to fit on a single page.)
+
+   The whole of axis !AXIS is included.  (The caller may follow up with another
+   call to render_page_select() to select on !AXIS to select on that axis as
+   well.)
+
+   The caller retains ownership of PAGE, which is not modified. */
+static struct render_page *
+render_page_select (const struct render_page *page, enum table_axis axis,
+                    int z0, int p0, int z1, int p1)
+{
+  struct render_page_selection s;
+  enum table_axis a = axis;
+  enum table_axis b = !a;
+  struct render_page *subpage;
+  struct render_overflow *ro;
+  int *dcp, *scp;
+  int *jc;
+  int z;
+
+
+  /* Optimize case where all of PAGE is selected by just incrementing the
+     reference count. */
+  if (z0 == page->h[a][0] && p0 == 0
+      && z1 == page->n[a] - page->h[a][1] && p1 == 0)
+    {
+      struct render_page *page_rw = (struct render_page *) page;
+      page_rw->ref_cnt++;
+      return page_rw;
+    }
+
+  /* Allocate subpage. */
+  subpage = render_page_allocate (page->params,
+                                  table_select_slice (
+                                    table_ref (page->table),
+                                    a, z0, z1, true));
+
+  /* An edge is cut off if it was cut off in PAGE or if we're trimming pixels
+     off that side of the page and there are no headers. */
+  subpage->is_edge_cutoff[a][0] =
+    subpage->h[a][0] == 0 && (p0 || (z0 == 0 && page->is_edge_cutoff[a][0]));
+  subpage->is_edge_cutoff[a][1] =
+    subpage->h[a][1] == 0 && (p1 || (z1 == page->n[a]
+                                     && page->is_edge_cutoff[a][1]));
+  subpage->is_edge_cutoff[b][0] = page->is_edge_cutoff[b][0];
+  subpage->is_edge_cutoff[b][1] = page->is_edge_cutoff[b][1];
+
+  /* Select join crossings from PAGE into subpage. */
+  jc = subpage->join_crossing[a];
+  for (z = 0; z < page->h[a][0]; z++)
+    *jc++ = page->join_crossing[a][z];
+  for (z = z0; z <= z1; z++)
+    *jc++ = page->join_crossing[a][z];
+  for (z = page->n[a] - page->h[a][1]; z < page->n[a]; z++)
+    *jc++ = page->join_crossing[a][z];
+  assert (jc == &subpage->join_crossing[a][subpage->n[a] + 1]);
+
+  memcpy (subpage->join_crossing[b], page->join_crossing[b],
+          (subpage->n[b] + 1) * sizeof **subpage->join_crossing);
+
+  /* Select widths from PAGE into subpage. */
+  scp = page->cp[a];
+  dcp = subpage->cp[a];
+  *dcp = 0;
+  for (z = 0; z <= rule_ofs (subpage->h[a][0]); z++, dcp++)
+    dcp[1] = dcp[0] + (scp[z + 1] - scp[z]);
+  for (z = cell_ofs (z0); z <= cell_ofs (z1 - 1); z++, dcp++)
+    {
+      dcp[1] = dcp[0] + (scp[z + 1] - scp[z]);
+      if (z == cell_ofs (z0))
+        {
+          dcp[1] -= p0;
+          if (page->h[a][0] && page->h[a][1])
+            dcp[1] += page->join_crossing[a][z / 2];
+        }
+      if (z == cell_ofs (z1 - 1))
+        dcp[1] -= p1;
+    }
+  for (z = rule_ofs_r (page, a, subpage->h[a][1]);
+       z <= rule_ofs_r (page, a, 0); z++, dcp++)
+    dcp[1] = dcp[0] + (scp[z + 1] - scp[z]);
+  assert (dcp == &subpage->cp[a][2 * subpage->n[a] + 1]);
+
+  for (z = 0; z < page->n[b] * 2 + 2; z++)
+    subpage->cp[b][z] = page->cp[b][z];
+
+  /* Add new overflows. */
+  s.page = page;
+  s.a = a;
+  s.b = b;
+  s.z0 = z0;
+  s.z1 = z1;
+  s.p0 = p0;
+  s.p1 = p1;
+  s.subpage = subpage;
+
+  for (z = 0; z < page->n[b]; z++)
+    {
+      struct table_cell cell;
+      int d[TABLE_N_AXES];
+
+      d[a] = z0;
+      d[b] = z;
+      table_get_cell (page->table, d[H], d[V], &cell);
+      if ((z == cell.d[b][0] && (p0 || cell.d[a][0] < z0))
+          || (z == cell.d[b][1] - 1 && p1))
+        {
+          ro = insert_overflow (&s, &cell);
+          ro->overflow[a][0] += p0 + axis_width (page, a,
+                                                 cell_ofs (cell.d[a][0]),
+                                                 cell_ofs (z0));
+          if (z1 == z0 + 1)
+            ro->overflow[a][1] += p1;
+          if (page->h[a][0] && page->h[a][1])
+            ro->overflow[a][0] -= page->join_crossing[a][cell.d[a][0] + 1];
+          if (cell.d[a][1] > z1)
+            ro->overflow[a][1] += axis_width (page, a, cell_ofs (z1),
+                                              cell_ofs (cell.d[a][1]));
+        }
+      table_cell_free (&cell);
+    }
+
+  for (z = 0; z < page->n[b]; z++)
+    {
+      struct table_cell cell;
+      int d[TABLE_N_AXES];
+
+      /* XXX need to handle p1 below */
+      d[a] = z1 - 1;
+      d[b] = z;
+      table_get_cell (page->table, d[H], d[V], &cell);
+      if (z == cell.d[b][0] && cell.d[a][1] > z1
+          && find_overflow_for_cell (&s, &cell) == NULL)
+        {
+          ro = insert_overflow (&s, &cell);
+          ro->overflow[a][1] += axis_width (page, a, cell_ofs (z1),
+                                            cell_ofs (cell.d[a][1]));
+        }
+      table_cell_free (&cell);
+    }
+
+  /* Copy overflows from PAGE into subpage. */
+  HMAP_FOR_EACH (ro, struct render_overflow, node, &page->overflows)
+    {
+      struct table_cell cell;
+
+      table_get_cell (page->table, ro->d[H], ro->d[V], &cell);
+      if (cell.d[a][1] > z0 && cell.d[a][0] < z1
+          && find_overflow_for_cell (&s, &cell) == NULL)
+        insert_overflow (&s, &cell);
+      table_cell_free (&cell);
+    }
+
+  return subpage;
+}
+
+/* Given CELL, a table_cell within S->page, stores in SUBCELL the (x,y)
+   coordinates of the top-left cell as it will appear in S->subpage.
+
+   CELL must actually intersect the region of S->page that is being selected
+   by render_page_select() or the results will not make any sense. */
+static void
+cell_to_subpage (struct render_page_selection *s,
+                 const struct table_cell *cell, int subcell[TABLE_N_AXES])
+{
+  enum table_axis a = s->a;
+  enum table_axis b = s->b;
+  int ha0 = s->subpage->h[a][0];
+
+  subcell[a] = MAX (cell->d[a][0] - s->z0 + ha0, ha0);
+  subcell[b] = cell->d[b][0];
+}
+
+/* Given CELL, a table_cell within S->page, returns the render_overflow for
+   that cell in S->subpage, if there is one, and a null pointer otherwise.
+
+   CELL must actually intersect the region of S->page that is being selected
+   by render_page_select() or the results will not make any sense. */
+static const struct render_overflow *
+find_overflow_for_cell (struct render_page_selection *s,
+                        const struct table_cell *cell)
+{
+  int subcell[2];
+
+  cell_to_subpage (s, cell, subcell);
+  return find_overflow (s->subpage, subcell[H], subcell[V]);
+}
+
+/* Given CELL, a table_cell within S->page, inserts a render_overflow for that
+   cell in S->subpage (which must not already exist).  Initializes the new
+   render_overflow's 'overflow' member from the overflow for CELL in S->page,
+   if there is one.
+
+   CELL must actually intersect the region of S->page that is being selected
+   by render_page_select() or the results will not make any sense. */
+static struct render_overflow *
+insert_overflow (struct render_page_selection *s,
+                 const struct table_cell *cell)
+{
+  const struct render_overflow *old;
+  struct render_overflow *of;
+
+  of = xzalloc (sizeof *of);
+  cell_to_subpage (s, cell, of->d);
+  hmap_insert (&s->subpage->overflows, &of->node,
+               hash_overflow (of->d[H], of->d[V]));
+
+  old = find_overflow (s->page, cell->d[H][0], cell->d[V][0]);
+  if (old != NULL)
+    memcpy (of->overflow, old->overflow, sizeof of->overflow);
+
+  return of;
+}
+
diff --git a/src/output/render.h b/src/output/render.h
new file mode 100644 (file)
index 0000000..cfba52e
--- /dev/null
@@ -0,0 +1,115 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2009 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 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.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef OUTPUT_RENDER_H
+#define OUTPUT_RENDER_H 1
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <output/table-provider.h>
+
+struct table;
+
+enum render_line_style
+  {
+    RENDER_LINE_NONE,           /* No line. */
+    RENDER_LINE_SINGLE,         /* Single line. */
+    RENDER_LINE_DOUBLE,         /* Double line. */
+    RENDER_N_LINES
+  };
+
+struct render_params
+  {
+    /* Measures CELL's width.  Stores in *MIN_WIDTH the minimum width required
+       to avoid splitting a single word across multiple lines (normally, this
+       is the width of the longest word in the cell) and in *MAX_WIDTH the
+       minimum width required to avoid line breaks other than at new-lines. */
+    void (*measure_cell_width) (void *aux, const struct table_cell *cell,
+                                int *min_width, int *max_width);
+
+    /* Returns the height required to render CELL given a width of WIDTH. */
+    int (*measure_cell_height) (void *aux, const struct table_cell *cell,
+                                int width);
+
+    /* Draws a generalized intersection of lines in the rectangle whose
+       top-left corner is (BB[TABLE_HORZ][0], BB[TABLE_VERT][0]) and whose
+       bottom-right corner is (BB[TABLE_HORZ][1], BB[TABLE_VERT][1]).
+
+       STYLES is interpreted this way:
+
+       STYLES[TABLE_HORZ][0]: style of line from top of BB to its center.
+       STYLES[TABLE_HORZ][1]: style of line from bottom of BB to its center.
+       STYLES[TABLE_VERT][0]: style of line from left of BB to its center.
+       STYLES[TABLE_VERT][1]: style of line from right of BB to its center. */
+    void (*draw_line) (void *aux, int bb[TABLE_N_AXES][2],
+                       enum render_line_style styles[TABLE_N_AXES][2]);
+
+    /* Draws CELL within bounding box BB.  CLIP is the same as BB (the common
+       case) or a subregion enclosed by BB.  In the latter case only the part
+       of the cell that lies within CLIP should actually be drawn, although BB
+       should used to determine the layout of the cell. */
+    void (*draw_cell) (void *aux, const struct table_cell *cell,
+                       int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2]);
+
+    /* Auxiliary data passed to each of the above functions. */
+    void *aux;
+
+    /* Page size to try to fit the rendering into.  Some tables will, of
+       course, overflow this size. */
+    int size[TABLE_N_AXES];
+
+    /* Nominal size of a character in the most common font:
+       font_size[TABLE_HORZ]: Em width.
+       font_size[TABLE_VERT]: Line spacing. */
+    int font_size[TABLE_N_AXES];
+
+    /* Width of different kinds of lines. */
+    int line_widths[TABLE_N_AXES][RENDER_N_LINES];
+  };
+\f
+/* A "page" of content that is ready to be rendered.
+
+   A page's size is not limited to the size passed in as part of render_params.
+   Use render_break (see below) to break a too-big render_page into smaller
+   render_pages that will fit in the available space. */
+struct render_page *render_page_create (const struct render_params *,
+                                        const struct table *);
+
+struct render_page *render_page_ref (const struct render_page *);
+void render_page_unref (struct render_page *);
+
+int render_page_get_size (const struct render_page *, enum table_axis);
+void render_page_draw (const struct render_page *);
+\f
+/* An iterator for breaking render_pages into smaller chunks. */
+struct render_break
+  {
+    struct render_page *page;   /* Page being broken up. */
+    enum table_axis axis;       /* Axis along which 'page' is being broken. */
+    int cell;                   /* Next cell. */
+    int pixel;                  /* Pixel offset within 'cell' (usually 0). */
+    int hw;                     /* Width of headers of 'page' along 'axis'. */
+  };
+
+void render_break_init (struct render_break *, struct render_page *,
+                        enum table_axis);
+void render_break_destroy (struct render_break *);
+
+bool render_break_has_next (const struct render_break *);
+int render_break_next_size (const struct render_break *);
+struct render_page *render_break_next (struct render_break *, int size);
+
+#endif /* output/render.h */
diff --git a/src/output/tab.c b/src/output/tab.c
new file mode 100644 (file)
index 0000000..058cb97
--- /dev/null
@@ -0,0 +1,765 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 1997-9, 2000, 2006, 2009 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 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.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+
+#include <output/tab.h>
+
+#include <ctype.h>
+#include <stdarg.h>
+#include <limits.h>
+#include <stdlib.h>
+
+#include <data/data-out.h>
+#include <data/format.h>
+#include <data/settings.h>
+#include <data/value.h>
+#include <data/dictionary.h>
+#include <libpspp/assertion.h>
+#include <libpspp/compiler.h>
+#include <libpspp/misc.h>
+#include <libpspp/pool.h>
+#include <output/driver.h>
+#include <output/table-item.h>
+#include <output/table-provider.h>
+#include <output/text-item.h>
+
+#include "error.h"
+#include "minmax.h"
+#include "xalloc.h"
+
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
+\f
+/* Set in the options field of cells that  */
+#define TAB_JOIN (1u << TAB_FIRST_AVAILABLE)
+
+/* Joined cell. */
+struct tab_joined_cell
+  {
+    int d[TABLE_N_AXES][2];     /* Table region, same as struct table_cell. */
+    char *contents;
+  };
+
+static const struct table_class tab_table_class;
+
+/* Creates and returns a new table with NC columns and NR rows and initially no
+   header rows or columns.  The table's cells are initially empty. */
+struct tab_table *
+tab_create (int nc, int nr)
+{
+  struct tab_table *t;
+
+  t = pool_create_container (struct tab_table, container);
+  table_init (&t->table, &tab_table_class);
+  table_set_nc (&t->table, nc);
+  table_set_nr (&t->table, nr);
+
+  t->title = NULL;
+  t->cf = nc;
+  t->cc = pool_calloc (t->container, nr * nc, sizeof *t->cc);
+  t->ct = pool_malloc (t->container, nr * nc);
+  memset (t->ct, 0, nc * nr);
+
+  t->rh = pool_nmalloc (t->container, nc, nr + 1);
+  memset (t->rh, 0, nc * (nr + 1));
+
+  t->rv = pool_nmalloc (t->container, nr, nc + 1);
+  memset (t->rv, TAL_GAP, nr * (nc + 1));
+
+  t->col_ofs = t->row_ofs = 0;
+
+  return t;
+}
+
+/* Sets the width and height of a table, in columns and rows,
+   respectively.  Use only to reduce the size of a table, since it
+   does not change the amount of allocated memory.
+
+   This function is obsolete.  Please do not add new uses of it.  (Instead, use
+   table_select() or one of its helper functions.) */
+void
+tab_resize (struct tab_table *t, int nc, int nr)
+{
+  assert (t != NULL);
+  if (nc != -1)
+    {
+      assert (nc + t->col_ofs <= t->cf);
+      table_set_nc (&t->table, nc + t->col_ofs);
+    }
+  if (nr != -1)
+    {
+      assert (nr + t->row_ofs <= tab_nr (t));
+      table_set_nr (&t->table, nr + t->row_ofs);
+    }
+}
+
+/* Changes either or both dimensions of a table and reallocates memory as
+   necessary.
+
+   This function is obsolete.  Please do not add new uses of it.  (Instead, use
+   table_paste() or one of its helper functions to paste multiple tables
+   together into a larger one.) */
+void
+tab_realloc (struct tab_table *t, int nc, int nr)
+{
+  int ro, co;
+
+  assert (t != NULL);
+  ro = t->row_ofs;
+  co = t->col_ofs;
+  if (ro || co)
+    tab_offset (t, 0, 0);
+
+  if (nc == -1)
+    nc = tab_nc (t);
+  if (nr == -1)
+    nr = tab_nr (t);
+
+  assert (nc == tab_nc (t));
+
+  if (nc > t->cf)
+    {
+      int mr1 = MIN (nr, tab_nr (t));
+      int mc1 = MIN (nc, tab_nc (t));
+
+      void **new_cc;
+      unsigned char *new_ct;
+      int r;
+
+      new_cc = pool_calloc (t->container, nr * nc, sizeof *new_cc);
+      new_ct = pool_malloc (t->container, nr * nc);
+      for (r = 0; r < mr1; r++)
+       {
+         memcpy (&new_cc[r * nc], &t->cc[r * tab_nc (t)], mc1 * sizeof *t->cc);
+         memcpy (&new_ct[r * nc], &t->ct[r * tab_nc (t)], mc1);
+         memset (&new_ct[r * nc + tab_nc (t)], 0, nc - tab_nc (t));
+       }
+      pool_free (t->container, t->cc);
+      pool_free (t->container, t->ct);
+      t->cc = new_cc;
+      t->ct = new_ct;
+      t->cf = nc;
+    }
+  else if (nr != tab_nr (t))
+    {
+      t->cc = pool_nrealloc (t->container, t->cc, nr * nc, sizeof *t->cc);
+      t->ct = pool_realloc (t->container, t->ct, nr * nc);
+
+      t->rh = pool_nrealloc (t->container, t->rh, nc, nr + 1);
+      t->rv = pool_nrealloc (t->container, t->rv, nr, nc + 1);
+
+      if (nr > tab_nr (t))
+       {
+         memset (&t->rh[nc * (tab_nr (t) + 1)], TAL_0, (nr - tab_nr (t)) * nc);
+         memset (&t->rv[(nc + 1) * tab_nr (t)], TAL_GAP,
+                  (nr - tab_nr (t)) * (nc + 1));
+       }
+    }
+
+  memset (&t->ct[nc * tab_nr (t)], 0, nc * (nr - tab_nr (t)));
+  memset (&t->cc[nc * tab_nr (t)], 0, nc * (nr - tab_nr (t)) * sizeof *t->cc);
+
+  table_set_nr (&t->table, nr);
+  table_set_nc (&t->table, nc);
+
+  if (ro || co)
+    tab_offset (t, co, ro);
+}
+
+/* Sets the number of header rows on each side of TABLE to L on the
+   left, R on the right, T on the top, B on the bottom.  Header rows
+   are repeated when a table is broken across multiple columns or
+   multiple pages. */
+void
+tab_headers (struct tab_table *table, int l, int r, int t, int b)
+{
+  table_set_hl (&table->table, l);
+  table_set_hr (&table->table, r);
+  table_set_ht (&table->table, t);
+  table_set_hb (&table->table, b);
+}
+\f
+/* Rules. */
+
+/* Draws a vertical line to the left of cells at horizontal position X
+   from Y1 to Y2 inclusive in style STYLE, if style is not -1. */
+void
+tab_vline (struct tab_table *t, int style, int x, int y1, int y2)
+{
+  assert (t != NULL);
+
+#if DEBUGGING
+  if (x + t->col_ofs < 0 || x + t->col_ofs > tab_nc (t)
+      || y1 + t->row_ofs < 0 || y1 + t->row_ofs >= tab_nr (t)
+      || y2 + t->row_ofs < 0 || y2 + t->row_ofs >= tab_nr (t))
+    {
+      printf (_("bad vline: x=%d+%d=%d y=(%d+%d=%d,%d+%d=%d) in "
+               "table size (%d,%d)\n"),
+             x, t->col_ofs, x + t->col_ofs,
+             y1, t->row_ofs, y1 + t->row_ofs,
+             y2, t->row_ofs, y2 + t->row_ofs,
+             tab_nc (t), tab_nr (t));
+      return;
+    }
+#endif
+
+  x += t->col_ofs;
+  y1 += t->row_ofs;
+  y2 += t->row_ofs;
+
+  assert (x >= 0);
+  assert (x <= tab_nc (t));
+  assert (y1 >= 0);
+  assert (y2 >= y1);
+  assert (y2 <= tab_nr (t));
+
+  if (style != -1)
+    {
+      int y;
+      for (y = y1; y <= y2; y++)
+        t->rv[x + (t->cf + 1) * y] = style;
+    }
+}
+
+/* Draws a horizontal line above cells at vertical position Y from X1
+   to X2 inclusive in style STYLE, if style is not -1. */
+void
+tab_hline (struct tab_table * t, int style, int x1, int x2, int y)
+{
+  assert (t != NULL);
+
+  x1 += t->col_ofs;
+  x2 += t->col_ofs;
+  y += t->row_ofs;
+
+  assert (y >= 0);
+  assert (y <= tab_nr (t));
+  assert (x2 >= x1 );
+  assert (x1 >= 0 );
+  assert (x2 < tab_nc (t));
+
+  if (style != -1)
+    {
+      int x;
+      for (x = x1; x <= x2; x++)
+        t->rh[x + t->cf * y] = style;
+    }
+}
+
+/* Draws a box around cells (X1,Y1)-(X2,Y2) inclusive with horizontal
+   lines of style F_H and vertical lines of style F_V.  Fills the
+   interior of the box with horizontal lines of style I_H and vertical
+   lines of style I_V.  Any of the line styles may be -1 to avoid
+   drawing those lines.  This is distinct from 0, which draws a null
+   line. */
+void
+tab_box (struct tab_table *t, int f_h, int f_v, int i_h, int i_v,
+        int x1, int y1, int x2, int y2)
+{
+  assert (t != NULL);
+
+#if DEBUGGING
+  if (x1 + t->col_ofs < 0 || x1 + t->col_ofs >= tab_nc (t)
+      || x2 + t->col_ofs < 0 || x2 + t->col_ofs >= tab_nc (t)
+      || y1 + t->row_ofs < 0 || y1 + t->row_ofs >= tab_nr (t)
+      || y2 + t->row_ofs < 0 || y2 + t->row_ofs >= tab_nr (t))
+    {
+      printf (_("bad box: (%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) "
+               "in table size (%d,%d)\n"),
+             x1, t->col_ofs, x1 + t->col_ofs,
+             y1, t->row_ofs, y1 + t->row_ofs,
+             x2, t->col_ofs, x2 + t->col_ofs,
+             y2, t->row_ofs, y2 + t->row_ofs,
+             tab_nc (t), tab_nr (t));
+      NOT_REACHED ();
+    }
+#endif
+
+  x1 += t->col_ofs;
+  x2 += t->col_ofs;
+  y1 += t->row_ofs;
+  y2 += t->row_ofs;
+
+  assert (x2 >= x1);
+  assert (y2 >= y1);
+  assert (x1 >= 0);
+  assert (y1 >= 0);
+  assert (x2 < tab_nc (t));
+  assert (y2 < tab_nr (t));
+
+  if (f_h != -1)
+    {
+      int x;
+      for (x = x1; x <= x2; x++)
+        {
+          t->rh[x + t->cf * y1] = f_h;
+          t->rh[x + t->cf * (y2 + 1)] = f_h;
+        }
+    }
+  if (f_v != -1)
+    {
+      int y;
+      for (y = y1; y <= y2; y++)
+        {
+          t->rv[x1 + (t->cf + 1) * y] = f_v;
+          t->rv[(x2 + 1) + (t->cf + 1) * y] = f_v;
+        }
+    }
+
+  if (i_h != -1)
+    {
+      int y;
+
+      for (y = y1 + 1; y <= y2; y++)
+       {
+         int x;
+
+          for (x = x1; x <= x2; x++)
+            t->rh[x + t->cf * y] = i_h;
+       }
+    }
+  if (i_v != -1)
+    {
+      int x;
+
+      for (x = x1 + 1; x <= x2; x++)
+       {
+         int y;
+
+          for (y = y1; y <= y2; y++)
+            t->rv[x + (t->cf + 1) * y] = i_v;
+       }
+    }
+}
+\f
+/* Cells. */
+
+/* Sets cell (C,R) in TABLE, with options OPT, to have a value taken
+   from V, displayed with format spec F. */
+void
+tab_value (struct tab_table *table, int c, int r, unsigned char opt,
+          const union value *v, const struct dictionary *dict, 
+          const struct fmt_spec *f)
+{
+  char *contents;
+
+  assert (table != NULL && v != NULL && f != NULL);
+#if DEBUGGING
+  if (c + table->col_ofs < 0 || r + table->row_ofs < 0
+      || c + table->col_ofs >= tab_nc (table)
+      || r + table->row_ofs >= tab_nr (table))
+    {
+      printf ("tab_value(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
+             "(%d,%d)\n",
+             c, table->col_ofs, c + table->col_ofs,
+             r, table->row_ofs, r + table->row_ofs,
+             tab_nc (table), tab_nr (table));
+      return;
+    }
+#endif
+
+  contents = data_out_pool (v, dict_get_encoding (dict), f, table->container);
+
+  table->cc[c + r * table->cf] = contents;
+  table->ct[c + r * table->cf] = opt;
+}
+
+/* Sets cell (C,R) in TABLE, with options OPT, to have value VAL
+   with NDEC decimal places. */
+void
+tab_fixed (struct tab_table *table, int c, int r, unsigned char opt,
+          double val, int w, int d)
+{
+  struct fmt_spec f;
+  union value double_value;
+  char *s;
+
+  assert (c >= 0);
+  assert (c < tab_nc (table));
+  assert (r >= 0);
+  assert (r < tab_nr (table));
+
+  f = fmt_for_output (FMT_F, w, d);
+
+#if DEBUGGING
+  if (c + table->col_ofs < 0 || r + table->row_ofs < 0
+      || c + table->col_ofs >= tab_nc (table)
+      || r + table->row_ofs >= tab_nr (table))
+    {
+      printf ("tab_fixed(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
+             "(%d,%d)\n",
+             c, table->col_ofs, c + table->col_ofs,
+             r, table->row_ofs, r + table->row_ofs,
+             tab_nc (table), tab_nr (table));
+      return;
+    }
+#endif
+
+  double_value.f = val;
+  s = data_out_pool (&double_value, LEGACY_NATIVE, &f, table->container);
+
+  table->cc[c + r * table->cf] = s + strspn (s, " ");
+  table->ct[c + r * table->cf] = opt;
+}
+
+/* Sets cell (C,R) in TABLE, with options OPT, to have value VAL as
+   formatted by FMT.
+   If FMT is null, then the default print format will be used.
+*/
+void
+tab_double (struct tab_table *table, int c, int r, unsigned char opt,
+          double val, const struct fmt_spec *fmt)
+{
+  union value double_value ;
+  char *s;
+
+  assert (table != NULL);
+
+  assert (c >= 0);
+  assert (c < tab_nc (table));
+  assert (r >= 0);
+  assert (r < tab_nr (table));
+
+  if ( fmt == NULL)
+    fmt = settings_get_format ();
+
+  fmt_check_output (fmt);
+
+#if DEBUGGING
+  if (c + table->col_ofs < 0 || r + table->row_ofs < 0
+      || c + table->col_ofs >= tab_nc (table)
+      || r + table->row_ofs >= tab_nr (table))
+    {
+      printf ("tab_double(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
+             "(%d,%d)\n",
+             c, table->col_ofs, c + table->col_ofs,
+             r, table->row_ofs, r + table->row_ofs,
+             tab_nc (table), tab_nr (table));
+      return;
+    }
+#endif
+
+  double_value.f = val;
+  s = data_out_pool (&double_value, LEGACY_NATIVE, fmt, table->container);
+  table->cc[c + r * table->cf] = s + strspn (s, " ");
+  table->ct[c + r * table->cf] = opt;
+}
+
+
+static void
+do_tab_text (struct tab_table *table, int c, int r, unsigned opt, char *text)
+{
+  assert (c >= 0 );
+  assert (r >= 0 );
+  assert (c < tab_nc (table));
+  assert (r < tab_nr (table));
+
+#if DEBUGGING
+  if (c + table->col_ofs < 0 || r + table->row_ofs < 0
+      || c + table->col_ofs >= tab_nc (table)
+      || r + table->row_ofs >= tab_nr (table))
+    {
+      printf ("tab_text(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
+             "(%d,%d)\n",
+             c, table->col_ofs, c + table->col_ofs,
+             r, table->row_ofs, r + table->row_ofs,
+             tab_nc (table), tab_nr (table));
+      return;
+    }
+#endif
+
+  table->cc[c + r * table->cf] = text;
+  table->ct[c + r * table->cf] = opt;
+}
+
+/* Sets cell (C,R) in TABLE, with options OPT, to have text value
+   TEXT. */
+void
+tab_text (struct tab_table *table, int c, int r, unsigned opt,
+          const char *text)
+{
+  do_tab_text (table, c, r, opt, pool_strdup (table->container, text));
+}
+
+/* Sets cell (C,R) in TABLE, with options OPT, to have text value
+   FORMAT, which is formatted as if passed to printf. */
+void
+tab_text_format (struct tab_table *table, int c, int r, unsigned opt,
+                 const char *format, ...)
+{
+  va_list args;
+
+  va_start (args, format);
+  do_tab_text (table, c, r, opt,
+               pool_vasprintf (table->container, format, args));
+  va_end (args);
+}
+
+static void
+do_tab_joint_text (struct tab_table *table, int x1, int y1, int x2, int y2,
+                   unsigned opt, char *text)
+{
+  struct tab_joined_cell *j;
+
+  assert (x1 + table->col_ofs >= 0);
+  assert (y1 + table->row_ofs >= 0);
+  assert (y2 >= y1);
+  assert (x2 >= x1);
+  assert (y2 + table->row_ofs < tab_nr (table));
+  assert (x2 + table->col_ofs < tab_nc (table));
+
+#if DEBUGGING
+  if (x1 + table->col_ofs < 0 || x1 + table->col_ofs >= tab_nc (table)
+      || y1 + table->row_ofs < 0 || y1 + table->row_ofs >= tab_nr (table)
+      || x2 < x1 || x2 + table->col_ofs >= tab_nc (table)
+      || y2 < y2 || y2 + table->row_ofs >= tab_nr (table))
+    {
+      printf ("tab_joint_text(): bad cell "
+             "(%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) in table size (%d,%d)\n",
+             x1, table->col_ofs, x1 + table->col_ofs,
+             y1, table->row_ofs, y1 + table->row_ofs,
+             x2, table->col_ofs, x2 + table->col_ofs,
+             y2, table->row_ofs, y2 + table->row_ofs,
+             tab_nc (table), tab_nr (table));
+      return;
+    }
+#endif
+
+  tab_box (table, -1, -1, TAL_0, TAL_0, x1, y1, x2, y2);
+
+  j = pool_alloc (table->container, sizeof *j);
+  j->d[TABLE_HORZ][0] = x1 + table->col_ofs;
+  j->d[TABLE_VERT][0] = y1 + table->row_ofs;
+  j->d[TABLE_HORZ][1] = ++x2 + table->col_ofs;
+  j->d[TABLE_VERT][1] = ++y2 + table->row_ofs;
+  j->contents = text;
+
+  opt |= TAB_JOIN;
+
+  {
+    void **cc = &table->cc[x1 + y1 * table->cf];
+    unsigned char *ct = &table->ct[x1 + y1 * table->cf];
+    const int ofs = table->cf - (x2 - x1);
+
+    int y;
+
+    for (y = y1; y < y2; y++)
+      {
+       int x;
+
+       for (x = x1; x < x2; x++)
+         {
+           *cc++ = j;
+           *ct++ = opt;
+         }
+
+       cc += ofs;
+       ct += ofs;
+      }
+  }
+}
+
+/* Joins cells (X1,X2)-(Y1,Y2) inclusive in TABLE, and sets them with
+   options OPT to have text value TEXT. */
+void
+tab_joint_text (struct tab_table *table, int x1, int y1, int x2, int y2,
+                unsigned opt, const char *text)
+{
+  do_tab_joint_text (table, x1, y1, x2, y2, opt,
+                     pool_strdup (table->container, text));
+}
+
+/* Joins cells (X1,X2)-(Y1,Y2) inclusive in TABLE, and sets them
+   with options OPT to have text value FORMAT, which is formatted
+   as if passed to printf. */
+void
+tab_joint_text_format (struct tab_table *table, int x1, int y1, int x2, int y2,
+                       unsigned opt, const char *format, ...)
+{
+  va_list args;
+
+  va_start (args, format);
+  do_tab_joint_text (table, x1, y1, x2, y2, opt,
+                     pool_vasprintf (table->container, format, args));
+  va_end (args);
+}
+
+bool
+tab_cell_is_empty (const struct tab_table *table, int c, int r)
+{
+  return table->cc[c + r * table->cf] == NULL;
+}
+\f
+/* Miscellaneous. */
+
+/* Set the title of table T to TITLE, which is formatted as if
+   passed to printf(). */
+void
+tab_title (struct tab_table *t, const char *title, ...)
+{
+  va_list args;
+
+  free (t->title);
+  va_start (args, title);
+  t->title = xvasprintf (title, args);
+  va_end (args);
+}
+
+/* Easy, type-safe way to submit a tab table to som. */
+void
+tab_submit (struct tab_table *t)
+{
+  table_item_submit (table_item_create (&t->table, t->title));
+}
+\f
+/* Editing. */
+
+/* Set table row and column offsets for all functions that affect
+   cells or rules. */
+void
+tab_offset (struct tab_table *t, int col, int row)
+{
+  int diff = 0;
+
+  assert (t != NULL);
+#if DEBUGGING
+  if (row < -1 || row > tab_nr (t))
+    {
+      printf ("tab_offset(): row=%d in %d-row table\n", row, tab_nr (t));
+      NOT_REACHED ();
+    }
+  if (col < -1 || col > tab_nc (t))
+    {
+      printf ("tab_offset(): col=%d in %d-column table\n", col, tab_nc (t));
+      NOT_REACHED ();
+    }
+#endif
+
+  if (row != -1)
+    diff += (row - t->row_ofs) * t->cf, t->row_ofs = row;
+  if (col != -1)
+    diff += (col - t->col_ofs), t->col_ofs = col;
+
+  t->cc += diff;
+  t->ct += diff;
+}
+
+/* Increment the row offset by one. If the table is too small,
+   increase its size. */
+void
+tab_next_row (struct tab_table *t)
+{
+  assert (t != NULL);
+  t->cc += t->cf;
+  t->ct += t->cf;
+  if (++t->row_ofs >= tab_nr (t))
+    tab_realloc (t, -1, tab_nr (t) * 4 / 3);
+}
+\f
+/* Writes STRING to the output.  OPTIONS may be any valid combination of TAB_*
+   bits.
+
+   This function is obsolete.  Please do not add new uses of it.  Instead, use
+   a text_item (see output/text-item.h). */
+void
+tab_output_text (int options, const char *string)
+{
+  enum text_item_type type = (options & TAB_EMPH ? TEXT_ITEM_SUBHEAD
+                              : options & TAB_FIX ? TEXT_ITEM_MONOSPACE
+                              : TEXT_ITEM_PARAGRAPH);
+  text_item_submit (text_item_create (type, string));
+}
+
+/* Same as tab_output_text(), but FORMAT is passed through printf-like
+   formatting before output. */
+void
+tab_output_text_format (int options, const char *format, ...)
+{
+  va_list args;
+  char *text;
+
+  va_start (args, format);
+  text = xvasprintf (format, args);
+  va_end (args);
+
+  tab_output_text (options, text);
+
+  free (text);
+}
+\f
+/* Table class implementation. */
+
+static void
+tab_destroy (struct table *table)
+{
+  struct tab_table *t = tab_cast (table);
+  pool_destroy (t->container);
+}
+
+static void
+tab_get_cell (const struct table *table, int x, int y, struct table_cell *cell)
+{
+  const struct tab_table *t = tab_cast (table);
+  int index = x + y * t->cf;
+  unsigned char opt = t->ct[index];
+  const void *content = t->cc[index];
+
+  cell->options = opt;
+  if (opt & TAB_JOIN)
+    {
+      const struct tab_joined_cell *jc = content;
+      cell->d[TABLE_HORZ][0] = jc->d[TABLE_HORZ][0];
+      cell->d[TABLE_HORZ][1] = jc->d[TABLE_HORZ][1];
+      cell->d[TABLE_VERT][0] = jc->d[TABLE_VERT][0];
+      cell->d[TABLE_VERT][1] = jc->d[TABLE_VERT][1];
+      cell->contents = jc->contents;
+    }
+  else
+    {
+      cell->d[TABLE_HORZ][0] = x;
+      cell->d[TABLE_HORZ][1] = x + 1;
+      cell->d[TABLE_VERT][0] = y;
+      cell->d[TABLE_VERT][1] = y + 1;
+      cell->contents = content != NULL ? content : "";
+    }
+  cell->destructor = NULL;
+}
+
+static int
+tab_get_rule (const struct table *table, enum table_axis axis, int x, int y)
+{
+  const struct tab_table *t = tab_cast (table);
+  return (axis == TABLE_VERT
+          ? t->rh[x + t->cf * y]
+          : t->rv[x + (t->cf + 1) * y]);
+}
+
+static const struct table_class tab_table_class =
+  {
+    tab_destroy,
+    tab_get_cell,
+    tab_get_rule,
+    NULL,                       /* paste */
+    NULL,                       /* select */
+  };
+
+struct tab_table *
+tab_cast (const struct table *table)
+{
+  assert (table->class == &tab_table_class);
+  return UP_CAST (table, struct tab_table, table);
+}
diff --git a/src/output/tab.h b/src/output/tab.h
new file mode 100644 (file)
index 0000000..db38b6b
--- /dev/null
@@ -0,0 +1,150 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 1997, 1998, 1999, 2000, 2009 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 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.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef OUTPUT_TAB_H
+#define OUTPUT_TAB_H
+
+/* Simple table class.
+
+   This is a type of table (see output/table.h) whose content is composed
+   manually by the code that generates it, by filling in cells one by one.
+
+   Some of the features of this type of table are obsolete but have not yet
+   been removed, because some code still uses them.  These features are:
+
+       - The title.  The title (or caption, actually) is a property of the
+         table_item (see output/table-item.h) in which a table is embedded,
+         not a property of the table itself.
+
+       - Row and columns offsets (via tab_offset(), tab_next_row()).  This
+         feature simply isn't used enough to justify keeping it.
+
+       - Table resizing.  The code that does use this feature is just as well
+         served by creating multiple tables and pasting them together with
+         table_paste().  Eliminating this feature would also slightly simplify
+         the table code here.
+*/
+
+#include <libpspp/compiler.h>
+#include <output/table.h>
+
+/* A table. */
+struct tab_table
+  {
+    struct table table;
+    struct pool *container;
+
+    /* Table title, or a null pointer if no title has been set.
+
+       The table title is properly part of struct table_item, not struc*/
+    char *title;
+    int cf;                    /* Column factor for indexing purposes. */
+
+    /* Table contents.
+
+       Each array element in cc[] is ordinarily a "char *" pointer to a
+       string.  If TAB_JOIN (defined in tab.c) is set in ct[] for the element,
+       however, it is a joined cell and the corresponding element of cc[]
+       points to a struct tab_joined_cell. */
+    void **cc;                  /* Cell contents; void *[nr][nc]. */
+    unsigned char *ct;         /* Cell types; unsigned char[nr][nc]. */
+
+    /* Rules. */
+    unsigned char *rh;         /* Horiz rules; unsigned char[nr+1][nc]. */
+    unsigned char *rv;         /* Vert rules; unsigned char[nr][nc+1]. */
+
+    /* X and Y offsets. */
+    int col_ofs, row_ofs;
+  };
+
+struct tab_table *tab_cast (const struct table *);
+
+/* Number of rows or columns in TABLE. */
+static inline int tab_nr (const struct tab_table *table)
+        { return table_nr (&table->table); }
+static inline int tab_nc (const struct tab_table *table)
+        { return table_nc (&table->table); }
+
+/* Number of left/right/top/bottom header columns/rows in TABLE. */
+static inline int tab_l (const struct tab_table *table)
+        { return table_hl (&table->table); }
+static inline int tab_r (const struct tab_table *table)
+        { return table_hr (&table->table); }
+static inline int tab_t (const struct tab_table *table)
+        { return table_ht (&table->table); }
+static inline int tab_b (const struct tab_table *table)
+        { return table_hb (&table->table); }
+
+/* Tables. */
+struct tab_table *tab_create (int nc, int nr);
+void tab_resize (struct tab_table *, int nc, int nr);
+void tab_realloc (struct tab_table *, int nc, int nr);
+void tab_headers (struct tab_table *, int l, int r, int t, int b);
+void tab_title (struct tab_table *, const char *, ...)
+     PRINTF_FORMAT (2, 3);
+void tab_submit (struct tab_table *);
+
+/* Rules. */
+void tab_hline (struct tab_table *, int style, int x1, int x2, int y);
+void tab_vline (struct tab_table *, int style, int x, int y1, int y2);
+void tab_box (struct tab_table *, int f_h, int f_v, int i_h, int i_v,
+             int x1, int y1, int x2, int y2);
+
+/* Obsolete cell options. */
+#define TAT_TITLE TAB_EMPH      /* Title attributes. */
+
+/* Cells. */
+struct fmt_spec;
+struct dictionary;
+union value;
+void tab_value (struct tab_table *, int c, int r, unsigned char opt,
+               const union value *, const struct dictionary *dict,
+               const struct fmt_spec *);
+
+void tab_fixed (struct tab_table *, int c, int r, unsigned char opt,
+               double v, int w, int d);
+
+void tab_double (struct tab_table *, int c, int r, unsigned char opt,
+               double v, const struct fmt_spec *);
+
+void tab_text (struct tab_table *, int c, int r, unsigned opt, const char *);
+void tab_text_format (struct tab_table *, int c, int r, unsigned opt,
+                      const char *, ...)
+     PRINTF_FORMAT (5, 6);
+
+void tab_joint_text (struct tab_table *, int x1, int y1, int x2, int y2,
+                    unsigned opt, const char *);
+void tab_joint_text_format (struct tab_table *, int x1, int y1, int x2, int y2,
+                            unsigned opt, const char *, ...)
+     PRINTF_FORMAT (7, 8);
+
+bool tab_cell_is_empty (const struct tab_table *, int c, int r);
+
+/* Editing. */
+void tab_offset (struct tab_table *, int col, int row);
+void tab_next_row (struct tab_table *);
+
+/* Current row/column offset. */
+#define tab_row(TABLE) ((TABLE)->row_ofs)
+#define tab_col(TABLE) ((TABLE)->col_ofs)
+
+/* Simple output. */
+void tab_output_text (int options, const char *string);
+void tab_output_text_format (int options, const char *, ...)
+     PRINTF_FORMAT (2, 3);
+
+#endif /* output/tab.h */
+
diff --git a/src/output/table-casereader.c b/src/output/table-casereader.c
new file mode 100644 (file)
index 0000000..909a2b5
--- /dev/null
@@ -0,0 +1,154 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2009 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 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.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+
+#include <output/table-provider.h>
+
+#include <data/casereader.h>
+#include <data/data-out.h>
+#include <data/format.h>
+#include <libpspp/i18n.h>
+
+#include "gl/xalloc.h"
+
+struct table_casereader
+  {
+    struct table table;
+    struct casereader *reader;
+    char *heading;
+    struct fmt_spec format;
+  };
+
+static const struct table_class table_casereader_class;
+
+static struct table_casereader *
+table_casereader_cast (const struct table *table)
+{
+  assert (table->class == &table_casereader_class);
+  return UP_CAST (table, struct table_casereader, table);
+}
+
+/* Returns a new table that has one column and the same number of rows as
+   READER.  Each row in the table is derived from column COLUMN in the same row
+   of READER by formatting with data_out() using the specified FORMAT (which
+   must be a valid format for the column's width).
+
+   If HEADING is nonnull, adds an additional row above the first row of data
+   that contains HEADING, and sets that row as a header row.
+
+   The returned table has no rules, except that if HEADING is nonnull, a single
+   line (TAL_1) separates HEADING from the first row if data. */
+struct table *
+table_from_casereader (const struct casereader *reader, size_t column,
+                       const char *heading, const struct fmt_spec *format)
+{
+  struct table_casereader *tc;
+  struct table *t;
+
+  assert (fmt_check_width_compat (format,
+                                  caseproto_get_width (
+                                    casereader_get_proto (reader), column)));
+
+  tc = xmalloc (sizeof *tc);
+  t = &tc->table;
+  table_init (t, &table_casereader_class);
+  table_set_nc (t, 1);
+  table_set_nr (t, casereader_count_cases (reader));
+  tc->reader = casereader_project_1 (casereader_clone (reader), column);
+  tc->heading = NULL;
+  tc->format = *format;
+
+  if (heading != NULL)
+    {
+      tc->heading = xstrdup (heading);
+      table_set_nr (t, table_nr (t) + 1);
+      table_set_ht (t, 1);
+    }
+
+  return t;
+}
+
+static void
+table_casereader_destroy (struct table *t)
+{
+  struct table_casereader *tc = table_casereader_cast (t);
+  casereader_destroy (tc->reader);
+  free (tc->heading);
+  free (t);
+}
+
+static void
+free_string (void *s_)
+{
+  char *s = s_;
+  free (s);
+}
+
+static void
+table_casereader_get_cell (const struct table *t, int x, int y,
+                           struct table_cell *cell)
+{
+  struct table_casereader *tc = table_casereader_cast (t);
+  struct ccase *c;
+  char *s;
+
+  cell->d[TABLE_HORZ][0] = x;
+  cell->d[TABLE_HORZ][1] = x + 1;
+  cell->d[TABLE_VERT][0] = y;
+  cell->d[TABLE_VERT][1] = y + 1;
+  cell->options = TAB_RIGHT;
+  if (tc->heading != NULL)
+    {
+      if (y == 0)
+        {
+          cell->contents = xstrdup (tc->heading);
+          cell->destructor = free_string;
+          cell->destructor_aux = s;
+          return;
+        }
+      y--;
+    }
+
+  c = casereader_peek (tc->reader, y);
+  if (c == NULL)
+    s = xstrdup ("I/O Error");
+  else
+    {
+      s = data_out (case_data_idx (c, 0), UTF8, &tc->format);
+      case_unref (c);
+    }
+  cell->contents = s;
+  cell->destructor = free_string;
+  cell->destructor_aux = s;
+}
+
+static int
+table_casereader_get_rule (const struct table *t, enum table_axis axis,
+                           int x UNUSED, int y)
+{
+  struct table_casereader *tc = table_casereader_cast (t);
+  return axis == TABLE_VERT && tc->heading != NULL && y == 1 ? TAL_1 : TAL_0;
+}
+
+static const struct table_class table_casereader_class =
+  {
+    table_casereader_destroy,
+    table_casereader_get_cell,
+    table_casereader_get_rule,
+    NULL,                       /* paste */
+    NULL,                       /* select (XXX) */
+  };
diff --git a/src/output/table-item.c b/src/output/table-item.c
new file mode 100644 (file)
index 0000000..b709273
--- /dev/null
@@ -0,0 +1,92 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2009 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 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.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+
+#include <output/table-provider.h>
+
+#include <assert.h>
+
+#include <libpspp/assertion.h>
+#include <libpspp/cast.h>
+#include <output/driver.h>
+#include <output/output-item-provider.h>
+#include <output/table-item.h>
+
+#include "gl/xalloc.h"
+
+/* Initializes ITEM as a table item for rendering TABLE.  The new table item
+   initially has the specified CAPTION, which may be NULL if no caption is yet
+   available.  The caller retains ownership of CAPTION. */
+struct table_item *
+table_item_create (struct table *table, const char *caption)
+{
+  struct table_item *item = xmalloc (sizeof *item);
+  output_item_init (&item->output_item, &table_item_class);
+  item->table = table;
+  item->caption = caption != NULL ? xstrdup (caption) : NULL;
+  return item;
+}
+
+/* Returns the table contained by TABLE_ITEM.  The caller must not modify or
+   unref the returned table. */
+const struct table *
+table_item_get_table (const struct table_item *table_item)
+{
+  return table_item->table;
+}
+
+/* Returns ITEM's caption, which is a null pointer if no caption has been
+   set. */
+const char *
+table_item_get_caption (const struct table_item *item)
+{
+  return item->caption;
+}
+
+/* Sets ITEM's caption to CAPTION, replacing any previous caption.  Specify
+   NULL for CAPTION to clear any caption from ITEM.  The caller retains
+   ownership of CAPTION.
+
+   This function may only be used on a table_item that is unshared. */
+void
+table_item_set_caption (struct table_item *item, const char *caption)
+{
+  assert (!table_item_is_shared (item));
+  free (item->caption);
+  item->caption = caption != NULL ? xstrdup (caption) : NULL;
+}
+
+/* Submits TABLE_ITEM to the configured output drivers, and transfers ownership
+   to the output subsystem. */
+void
+table_item_submit (struct table_item *table_item)
+{
+  output_submit (&table_item->output_item);
+}
+\f
+static void
+table_item_destroy (struct output_item *output_item)
+{
+  struct table_item *item = to_table_item (output_item);
+  free (item->caption);
+  table_unref (item->table);
+}
+
+const struct output_item_class table_item_class =
+  {
+    table_item_destroy,
+  };
diff --git a/src/output/table-item.h b/src/output/table-item.h
new file mode 100644 (file)
index 0000000..01e9899
--- /dev/null
@@ -0,0 +1,103 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2009 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 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.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef OUTPUT_TABLE_ITEM_H
+#define OUTPUT_TABLE_ITEM_H 1
+
+/* Table items.
+
+   A table item is a subclass of an output item (see output-item.h) that
+   contains a table (see table.h) and some formatting properties (currently
+   just a caption). */
+
+#include <libpspp/compiler.h>
+#include <output/output-item.h>
+
+/* A table item.
+
+   The members of struct table_item should not be accessed directly.  Use one
+   of the accessor functions defined below. */
+struct table_item
+  {
+    struct output_item output_item; /* Superclass. */
+    struct table *table;        /* The table to be rendered. */
+    char *caption;              /* May be null if there is no caption. */
+  };
+
+struct table_item *table_item_create (struct table *, const char *caption);
+
+const struct table *table_item_get_table (const struct table_item *);
+
+const char *table_item_get_caption (const struct table_item *);
+void table_item_set_caption (struct table_item *, const char *);
+\f
+/* This boilerplate for table_item, a subclass of output_item, was
+   autogenerated by mk-class-boilerplate. */
+
+#include <assert.h>
+#include <libpspp/cast.h>
+
+extern const struct output_item_class table_item_class;
+
+/* Returns true if SUPER is a table_item, otherwise false. */
+static inline bool
+is_table_item (const struct output_item *super)
+{
+  return super->class == &table_item_class;
+}
+
+/* Returns SUPER converted to table_item.  SUPER must be a table_item, as
+   reported by is_table_item. */
+static inline struct table_item *
+to_table_item (const struct output_item *super)
+{
+  assert (is_table_item (super));
+  return UP_CAST (super, struct table_item, output_item);
+}
+
+/* Returns INSTANCE converted to output_item. */
+static inline struct output_item *
+table_item_super (const struct table_item *instance)
+{
+  return CONST_CAST (struct output_item *, &instance->output_item);
+}
+
+/* Increments INSTANCE's reference count and returns INSTANCE. */
+static inline struct table_item *
+table_item_ref (const struct table_item *instance)
+{
+  return to_table_item (output_item_ref (&instance->output_item));
+}
+
+/* Decrements INSTANCE's reference count, then destroys INSTANCE if
+   the reference count is now zero. */
+static inline void
+table_item_unref (struct table_item *instance)
+{
+  output_item_unref (&instance->output_item);
+}
+
+/* Returns true if INSTANCE's reference count is greater than 1,
+   false otherwise. */
+static inline bool
+table_item_is_shared (const struct table_item *instance)
+{
+  return output_item_is_shared (&instance->output_item);
+}
+
+void table_item_submit (struct table_item *);
+\f
+#endif /* output/table-item.h */
diff --git a/src/output/table-paste.c b/src/output/table-paste.c
new file mode 100644 (file)
index 0000000..102b514
--- /dev/null
@@ -0,0 +1,317 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2009 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 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.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+
+#include <libpspp/assertion.h>
+#include <libpspp/tower.h>
+#include <output/table-provider.h>
+
+#include "gl/minmax.h"
+#include "gl/xalloc.h"
+
+struct paste_subtable
+  {
+    struct tower_node node;
+    struct table *table;
+  };
+
+static struct paste_subtable *
+paste_subtable_cast (struct tower_node *node)
+{
+  return tower_data (node, struct paste_subtable, node);
+}
+
+struct table_paste
+  {
+    struct table table;
+    struct tower subtables;
+    enum table_axis orientation;
+  };
+
+static const struct table_class table_paste_class;
+
+static struct table_paste *
+table_paste_cast (const struct table *table)
+{
+  assert (table->class == &table_paste_class);
+  return UP_CAST (table, struct table_paste, table);
+}
+
+static bool
+is_table_paste (const struct table *table, int orientation)
+{
+  return (table->class == &table_paste_class
+          && table_paste_cast (table)->orientation == orientation);
+}
+
+static struct paste_subtable *
+paste_subtable_lookup (struct table_paste *tp, unsigned long int offset,
+                       unsigned long int *start)
+{
+  return paste_subtable_cast (tower_lookup (&tp->subtables, offset, start));
+}
+
+/* This must be called *before* adding TABLE to TP, otherwise the test for
+   whether TP is empty will not have the correct effect. */
+static void
+table_paste_increase_size (struct table_paste *tp,
+                           const struct table *table)
+{
+  int o = tp->orientation;
+  int h0, h1;
+
+  tp->table.n[o] += table->n[o];
+  tp->table.n[!o] = MAX (tp->table.n[!o], table->n[!o]);
+
+  h0 = table->h[!o][0];
+  h1 = table->h[!o][1];
+  if (tower_is_empty (&tp->subtables))
+    {
+      tp->table.h[!o][0] = h0;
+      tp->table.h[!o][1] = h1;
+    }
+  else
+    {
+      tp->table.h[!o][0] = MIN (tp->table.h[!o][0], h0);
+
+      /* XXX this is not quite right */
+      tp->table.h[!o][1] = MIN (tp->table.h[!o][1], h1);
+    }
+}
+
+static void
+reassess_headers (struct table_paste *tp)
+{
+  int o = tp->orientation;
+  if (tower_is_empty (&tp->subtables))
+    tp->table.h[o][0] = tp->table.h[o][1] = 0;
+  else
+    {
+      struct paste_subtable *h0, *h1;
+
+      h0 = paste_subtable_cast (tower_first (&tp->subtables));
+      tp->table.h[o][0] = h0->table->h[o][0];
+
+      h1 = paste_subtable_cast (tower_last (&tp->subtables));
+      tp->table.h[o][1] = h1->table->h[o][1];
+    }
+}
+
+static void
+table_paste_insert_subtable (struct table_paste *tp,
+                             struct table *table,
+                             struct tower_node *under)
+{
+  struct paste_subtable *subtable;
+
+  subtable = xmalloc (sizeof *subtable);
+  table_paste_increase_size (tp, table);
+  tower_insert (&tp->subtables, table->n[tp->orientation],
+                &subtable->node, under);
+  subtable->table = table;
+  reassess_headers (tp);
+}
+
+/* Takes ownership of A and B and returns a table that consists of tables A and
+   B "pasted together", that is, a table whose size is the sum of the sizes of
+   A and B along the axis specified by ORIENTATION.  A and B should have the
+   same size along the axis opposite ORIENTATION; the handling of tables that
+   have different sizes along that axis may vary.
+
+   The rules at the seam between A and B are combined.  The exact way in which
+   they are combined is unspecified, but the method of table_rule_combine() is
+   typical.
+
+   If A or B is null, returns the other argument. */
+struct table *
+table_paste (struct table *a, struct table *b, enum table_axis orientation)
+{
+  struct table_paste *tp;
+
+  /* Handle nulls. */
+  if (a == NULL)
+    return b;
+  if (b == NULL)
+    return a;
+
+  /* Handle tables that know how to paste themselves. */
+  if (!table_is_shared (a) && !table_is_shared (b) && a != b)
+    {
+      if (a->class->paste != NULL)
+        {
+          struct table *new = a->class->paste (a, b, orientation);
+          if (new != NULL)
+            return new;
+        }
+      if (b->class->paste != NULL && a->class != b->class)
+        {
+          struct table *new = b->class->paste (a, b, orientation);
+          if (new != NULL)
+            return new;
+        }
+    }
+
+  /* Create new table_paste and insert A and B into it. */
+  tp = xmalloc (sizeof *tp);
+  table_init (&tp->table, &table_paste_class);
+  tower_init (&tp->subtables);
+  tp->orientation = orientation;
+  table_paste_insert_subtable (tp, a, NULL);
+  table_paste_insert_subtable (tp, b, NULL);
+  return &tp->table;
+}
+
+/* Shorthand for table_paste (left, right, TABLE_HORZ). */
+struct table *
+table_hpaste (struct table *left, struct table *right)
+{
+  return table_paste (left, right, TABLE_HORZ);
+}
+
+/* Shorthand for table_paste (left, right, TABLE_VERT). */
+struct table *
+table_vpaste (struct table *left, struct table *right)
+{
+  return table_paste (left, right, TABLE_VERT);
+}
+
+static void
+table_paste_destroy (struct table *t)
+{
+  struct table_paste *tp = table_paste_cast (t);
+  struct tower_node *node, *next;
+
+  for (node = tower_first (&tp->subtables); node != NULL; node = next)
+    {
+      struct paste_subtable *ps = paste_subtable_cast (node);
+      table_unref (ps->table);
+      next = tower_delete (&tp->subtables, node);
+      free (node);
+    }
+  free (tp);
+}
+
+static void
+table_paste_get_cell (const struct table *t, int x, int y,
+                      struct table_cell *cell)
+{
+  struct table_paste *tp = table_paste_cast (t);
+  struct paste_subtable *ps;
+  unsigned long int start;
+  int d[TABLE_N_AXES];
+
+  d[TABLE_HORZ] = x;
+  d[TABLE_VERT] = y;
+  ps = paste_subtable_lookup (tp, d[tp->orientation], &start);
+  d[tp->orientation] -= start;
+  table_get_cell (ps->table, d[TABLE_HORZ], d[TABLE_VERT], cell);
+  cell->d[tp->orientation][0] += start;
+  cell->d[tp->orientation][1] += start;
+}
+
+static int
+table_paste_get_rule (const struct table *t,
+                      enum table_axis axis, int x, int y)
+{
+  struct table_paste *tp = table_paste_cast (t);
+  int h = tp->orientation == TABLE_HORZ ? x : y;
+  int k = tp->orientation == TABLE_HORZ ? y : x;
+  struct paste_subtable *ps;
+  unsigned long int start;
+
+  if (tp->orientation == axis)
+    {
+      int r;
+
+      ps = paste_subtable_lookup (tp, h == 0 ? 0 : h - 1, &start);
+      if (tp->orientation == TABLE_HORZ) /* XXX */
+        r = table_get_rule (ps->table, axis, h - start, k);
+      else
+        r = table_get_rule (ps->table, axis, k, h - start);
+      if (h == start + tower_node_get_size (&ps->node))
+        {
+          struct tower_node *ps2_ = tower_next (&tp->subtables, &ps->node);
+          if (ps2_ != NULL)
+            {
+              struct paste_subtable *ps2 = paste_subtable_cast (ps2_);
+              int r2;
+
+              if (tp->orientation == TABLE_HORZ) /* XXX */
+                r2 = table_get_rule (ps2->table, axis, 0, k);
+              else
+                r2 = table_get_rule (ps2->table, axis, k, 0);
+              return table_rule_combine (r, r2);
+            }
+        }
+      return r;
+    }
+  else
+    {
+      ps = paste_subtable_lookup (tp, h, &start);
+      if (tp->orientation == TABLE_HORZ) /* XXX */
+        return table_get_rule (ps->table, axis, h - start, k);
+      else
+        return table_get_rule (ps->table, axis, k, h - start);
+    }
+}
+
+static struct table *
+table_paste_paste (struct table *a, struct table *b,
+                   enum table_axis orientation)
+{
+  struct table_paste *ta, *tb;
+
+  ta = is_table_paste (a, orientation) ? table_paste_cast (a) : NULL;
+  tb = is_table_paste (b, orientation) ? table_paste_cast (b) : NULL;
+
+  if (ta != NULL)
+    {
+      if (tb != NULL)
+        {
+          /* Append all of B's subtables onto A, then destroy B. */
+          table_paste_increase_size (ta, b);
+          tower_splice (&ta->subtables, NULL,
+                        &tb->subtables, tower_first (&tb->subtables), NULL);
+          table_unref (b);
+        }
+      else
+        {
+          /* Append B to A's stack of subtables. */
+          table_paste_insert_subtable (ta, b, NULL);
+        }
+      reassess_headers (ta);
+      return a;
+    }
+  else if (tb != NULL)
+    {
+      /* Insert A at the beginning of B's stack of subtables. */
+      table_paste_insert_subtable (tb, a, tower_first (&tb->subtables));
+      reassess_headers (tb);
+      return b;
+    }
+  else
+    return NULL;
+}
+
+static const struct table_class table_paste_class =
+  {
+    table_paste_destroy,
+    table_paste_get_cell,
+    table_paste_get_rule,
+    table_paste_paste,
+    NULL,                       /* select */
+  };
diff --git a/src/output/table-provider.h b/src/output/table-provider.h
new file mode 100644 (file)
index 0000000..6d1a415
--- /dev/null
@@ -0,0 +1,177 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 1997, 1998, 1999, 2000, 2009 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 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.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef OUTPUT_TABLE_PROVIDER
+#define OUTPUT_TABLE_PROVIDER 1
+
+#include <output/table.h>
+
+/* A cell in a table. */
+struct table_cell
+  {
+    /* Occupied table region.
+
+       d[TABLE_HORZ][0] is the leftmost column.
+       d[TABLE_HORZ][1] is the rightmost column, plus 1.
+       d[TABLE_VERT][0] is the top row.
+       d[TABLE_VERT][1] is the bottom row, plus 1.
+
+       For an ordinary cell:
+           d[TABLE_HORZ][1] == d[TABLE_HORZ][0] + 1
+       and d[TABLE_VERT][1] == d[TABLE_VERT][0] + 1
+
+       For a joined cell:
+          d[TABLE_HORZ][1] > d[TABLE_HORZ][0] + 1
+       or d[TABLE_VERT][1] > d[TABLE_VERT][0] + 1
+       or both. */
+    int d[TABLE_N_AXES][2];
+
+    const char *contents;       /* Text string contents. */
+    unsigned int options;       /* TAB_* values. */
+
+    /* Called to free the cell's data, if nonnull. */
+    void (*destructor) (void *destructor_aux);
+    void *destructor_aux;
+  };
+
+void table_cell_free (struct table_cell *);
+
+/* Returns the number of columns that CELL spans.  This is 1 for an ordinary
+   cell and greater than one for a cell that joins multiple columns. */
+static inline int
+table_cell_colspan (const struct table_cell *cell)
+{
+  return cell->d[TABLE_HORZ][1] - cell->d[TABLE_HORZ][0];
+}
+
+/* Returns the number of rows that CELL spans.  This is 1 for an ordinary cell
+   and greater than one for a cell that joins multiple rows. */
+static inline int
+table_cell_rowspan (const struct table_cell *cell)
+{
+  return cell->d[TABLE_VERT][1] - cell->d[TABLE_VERT][0];
+}
+
+/* Returns true if CELL is a joined cell, that is, if it spans multiple rows
+   or columns.  Otherwise, returns false. */
+static inline bool
+table_cell_is_joined (const struct table_cell *cell)
+{
+  return table_cell_colspan (cell) > 1 || table_cell_rowspan (cell) > 1;
+}
+\f
+/* Declarations to allow defining table classes. */
+
+struct table_class
+  {
+    /* Frees TABLE.
+
+       The table class may assume that any cells that were retrieved by calling
+       the 'get_cell' function have been freed (by calling their destructors)
+       before this function is called. */
+    void (*destroy) (struct table *table);
+
+    /* Initializes CELL with the contents of the table cell at column X and row
+       Y within TABLE.  All members of CELL must be initialized, except that if
+       'destructor' is set to a null pointer, then 'destructor_aux' need not be
+       initialized.  The 'contents' member of CELL must be set to a nonnull
+       value.
+
+       The table class must allow any number of cells in the table to be
+       retrieved simultaneously; that is, TABLE must not assume that a given
+       cell will be freed before another one is retrieved using 'get_cell'.
+
+       The table class must allow joined cells to be retrieved, with identical
+       contents, using any (X,Y) location inside the cell.
+
+       The table class must not allow cells to overlap.
+
+       The table class should not allow a joined cell to cross the border
+       between header rows/columns and the interior of the table.  That is, a
+       joined cell should be entirely within headers rows and columns or
+       entirely outside them.
+
+       The table class may assume that CELL will be freed before TABLE is
+       destroyed. */
+    void (*get_cell) (const struct table *table, int x, int y,
+                      struct table_cell *cell);
+
+    /* Returns one of the TAL_* enumeration constants (declared in
+       output/table.h) representing a rule running alongside one of the cells
+       in TABLE.
+
+       See table_get_rule() in table.c for a detailed explanation of the
+       meaning of AXIS and X and Y, including a diagram. */
+    int (*get_rule) (const struct table *table,
+                     enum table_axis axis, int x, int y);
+
+    /* This function is optional and most table classes will not implement it.
+
+       If provided, this function must take ownership of A and B and return a
+       table that consists of tables A and B "pasted together", that is, a
+       table whose size is the sum of the sizes of A and B along the axis
+       specified by ORIENTATION.  A and B will ordinarily have the same size
+       along the axis opposite ORIENTATION; no particular handling of tables
+       that have different sizes along that axis is required.
+
+       The handling of rules at the seam between A and B is not specified, but
+       table_rule_combine() is one reasonable way to do it.
+
+       Called only if neither A and B is shared (as returned by
+       table_is_shared()).
+
+       Called if A or B or both is of the class defined by this table class.
+       That is, the implementation must be prepared to deal with the case where
+       A or B is not the ordinarily expected table class.
+
+       This function may return a null pointer if it cannot implement the paste
+       operation, in which case the caller will use a fallback
+       implementation.
+
+       This function is used to implement table_paste(). */
+    struct table *(*paste) (struct table *a, struct table *b,
+                            enum table_axis orientation);
+
+    /* This function is optional and most table classes will not implement it.
+
+       If provided, this function must take ownership of TABLE and return a new
+       table whose contents are the TABLE's rows RECT[TABLE_VERT][0] through
+       RECT[TABLE_VERT][1], exclusive, and the TABLE's columns
+       RECT[TABLE_HORZ][0] through RECT[TABLE_HORZ][1].
+
+       Called only if TABLE is not shared (as returned by table_is_shared()).p
+
+       This function may return a null pointer if it cannot implement the
+       select operation, in which case the caller will use a fallback
+       implementation.
+
+       This function is used to implement table_select(). */
+    struct table *(*select) (struct table *table, int rect[TABLE_N_AXES][2]);
+  };
+
+void table_init (struct table *, const struct table_class *);
+
+/* Table class implementations can call these functions or just set the
+   table's n[] and h[][] members directly. */
+void table_set_nc (struct table *, int nc);
+void table_set_nr (struct table *, int nr);
+\f
+/* For use primarily by output drivers. */
+
+void table_get_cell (const struct table *, int x, int y, struct table_cell *);
+int table_get_rule (const struct table *, enum table_axis, int x, int y);
+
+#endif /* output/table-provider.h */
diff --git a/src/output/table-select.c b/src/output/table-select.c
new file mode 100644 (file)
index 0000000..1c2956a
--- /dev/null
@@ -0,0 +1,236 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2009 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 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.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+
+#include <libpspp/assertion.h>
+#include <libpspp/cast.h>
+#include <output/table-provider.h>
+
+#include "gl/minmax.h"
+#include "gl/xalloc.h"
+
+struct table_select
+  {
+    struct table table;
+    struct table *subtable;
+    int ofs[2];
+  };
+
+static const struct table_class table_select_class;
+
+static struct table_select *
+table_select_cast (const struct table *table)
+{
+  assert (table->class == &table_select_class);
+  return UP_CAST (table, struct table_select, table);
+}
+
+/* Takes ownership of SUBTABLE and returns a new table whose contents are the
+   rectangular subregion of SUBTABLE that contains rows RECT[TABLE_VERT][0]
+   through RECT[TABLE_VERT][1], exclusive, and columns RECT[TABLE_HORZ][0]
+   through RECT[TABLE_HORZ][1]. */
+struct table *
+table_select (struct table *subtable, int rect[TABLE_N_AXES][2])
+{
+  struct table_select *ts;
+  int axis;
+
+  if (rect[TABLE_HORZ][0] == 0
+      && rect[TABLE_HORZ][1] == subtable->n[TABLE_HORZ]
+      && rect[TABLE_VERT][0] == 0
+      && rect[TABLE_VERT][1] == subtable->n[TABLE_VERT])
+    return subtable;
+
+  if (!table_is_shared (subtable) && subtable->class->select != NULL)
+    {
+      struct table *selected = subtable->class->select (subtable, rect);
+      if (selected != NULL)
+        return selected;
+    }
+
+  ts = xmalloc (sizeof *ts);
+  table_init (&ts->table, &table_select_class);
+  ts->subtable = subtable;
+  for (axis = 0; axis < TABLE_N_AXES; axis++)
+    {
+      int h1;
+      ts->ofs[axis] = rect[axis][0];
+      ts->table.n[axis] = rect[axis][1] - rect[axis][0];
+      if (subtable->h[axis][0] > rect[axis][0])
+        ts->table.h[axis][0] = subtable->h[axis][0] - rect[axis][0];
+      h1 = subtable->n[axis] - subtable->h[axis][1];
+      if (h1 < rect[axis][1])
+        ts->table.h[axis][1] = rect[axis][1] - h1;
+    }
+  return &ts->table;
+}
+
+/* Takes ownership of TABLE and returns a new table whose contents are:
+
+        - If AXIS is TABLE_HORZ, columns Z0 through Z1 (exclusive) of SUBTABLE.
+          If ADD_HEADERS is true, the returned table also includes any header
+          columns in SUBTABLE.
+
+        - If AXIS is TABLE_VERT, rows Z0 through Z1 (exclusive) of SUBTABLE.
+          If ADD_HEADERS is true, the returned table also includes any header
+          rows in SUBTABLE. */
+struct table *
+table_select_slice (struct table *subtable, enum table_axis axis,
+                    int z0, int z1, bool add_headers)
+{
+  struct table *table;
+  int rect[TABLE_N_AXES][2];
+
+  if (add_headers)
+    {
+      if (z0 == subtable->h[axis][0] && z1 == subtable->h[axis][1])
+        return subtable;
+
+      if (subtable->h[axis][0])
+        table_ref (subtable);
+      if (subtable->h[axis][1])
+        table_ref (subtable);
+    }
+  else
+    {
+      if (z0 == 0 && z1 == subtable->n[axis])
+        return subtable;
+    }
+
+  rect[TABLE_HORZ][0] = 0;
+  rect[TABLE_VERT][0] = 0;
+  rect[TABLE_HORZ][1] = subtable->n[TABLE_HORZ];
+  rect[TABLE_VERT][1] = subtable->n[TABLE_VERT];
+  rect[axis][0] = z0;
+  rect[axis][1] = z1;
+  table = table_select (subtable, rect);
+
+  if (add_headers)
+    {
+      if (subtable->h[axis][0])
+        table = table_paste (
+          table_select_slice (subtable, axis, 0, subtable->h[axis][0],
+                              false),
+          table, axis);
+
+      if (subtable->h[axis][1])
+        table = table_paste (
+          table,
+          table_select_slice (subtable, axis,
+                              subtable->n[axis] - subtable->h[axis][1],
+                              subtable->n[axis], false),
+          axis);
+    }
+
+  return table;
+}
+
+/* Takes ownership of TABLE and returns a new table whose contents are columns
+   X0 through X1 (exclusive) of SUBTABLE.  If ADD_HEADERS is true, the
+   returned table also includes any header columns in SUBTABLE. */
+struct table *
+table_select_columns (struct table *subtable, int x0, int x1,
+                      bool add_headers)
+{
+  return table_select_slice (subtable, TABLE_HORZ, x0, x1, add_headers);
+}
+
+/* Takes ownership of TABLE and returns a new table whose contents are rows Y0
+   through Y1 (exclusive) of SUBTABLE.  If ADD_HEADERS is true, the returned
+   table also includes any header rows in SUBTABLE. */
+struct table *
+table_select_rows (struct table *subtable, int y0, int y1,
+                   bool add_headers)
+{
+  return table_select_slice (subtable, TABLE_VERT, y0, y1, add_headers);
+}
+
+static void
+table_select_destroy (struct table *ti)
+{
+  struct table_select *ts = table_select_cast (ti);
+  table_unref (ts->subtable);
+  free (ts);
+}
+
+static void
+table_select_get_cell (const struct table *ti, int x, int y,
+                       struct table_cell *cell)
+{
+  struct table_select *ts = table_select_cast (ti);
+  int axis;
+
+  table_get_cell (ts->subtable,
+                  x + ts->ofs[TABLE_HORZ],
+                  y + ts->ofs[TABLE_VERT], cell);
+
+  for (axis = 0; axis < TABLE_N_AXES; axis++)
+    {
+      int *d = cell->d[axis];
+      int ofs = ts->ofs[axis];
+
+      d[0] = MAX (d[0] - ofs, 0);
+      d[1] = MIN (d[1] - ofs, ti->n[axis]);
+    }
+}
+
+static int
+table_select_get_rule (const struct table *ti,
+                       enum table_axis axis,
+                       int x, int y)
+{
+  struct table_select *ts = table_select_cast (ti);
+  return table_get_rule (ts->subtable, axis,
+                         x + ts->ofs[TABLE_HORZ],
+                         y + ts->ofs[TABLE_VERT]);
+}
+
+static struct table *
+table_select_select (struct table *ti, int rect[TABLE_N_AXES][2])
+{
+  struct table_select *ts = table_select_cast (ti);
+  int axis;
+
+  for (axis = 0; axis < TABLE_N_AXES; axis++)
+    {
+      int h1;
+
+      if (ts->table.h[axis][0] > rect[axis][0])
+        ts->table.h[axis][0] = ts->table.h[axis][0] - rect[axis][0];
+      else
+        ts->table.h[axis][0] = 0;
+
+      h1 = ts->table.n[axis] - ts->table.h[axis][1];
+      if (h1 < rect[axis][1])
+        ts->table.h[axis][1] = rect[axis][1] - h1;
+      else
+        ts->table.h[axis][1] = 0;
+
+      ts->ofs[axis] += rect[axis][0];
+      ts->table.n[axis] = rect[axis][1] - rect[axis][0];
+    }
+  return ti;
+}
+
+static const struct table_class table_select_class =
+  {
+    table_select_destroy,
+    table_select_get_cell,
+    table_select_get_rule,
+    NULL,                       /* paste */
+    table_select_select,
+  };
diff --git a/src/output/table-transpose.c b/src/output/table-transpose.c
new file mode 100644 (file)
index 0000000..5980194
--- /dev/null
@@ -0,0 +1,119 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2009 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 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.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+
+#include <libpspp/assertion.h>
+#include <libpspp/cast.h>
+#include <output/table-provider.h>
+
+#include "gl/minmax.h"
+#include "gl/xalloc.h"
+
+struct table_transpose
+  {
+    struct table table;
+    struct table *subtable;
+  };
+
+static const struct table_class table_transpose_class;
+
+static struct table_transpose *
+table_transpose_cast (const struct table *table)
+{
+  assert (table->class == &table_transpose_class);
+  return UP_CAST (table, struct table_transpose, table);
+}
+
+/* Takes ownership of SUBTABLE and returns a new table whose contents are
+   SUBTABLE with rows and columns transposed. */
+struct table *
+table_transpose (struct table *subtable)
+{
+  if (subtable->n[TABLE_HORZ] == subtable->n[TABLE_VERT]
+      && subtable->n[TABLE_HORZ] <= 1)
+    return subtable;
+  else if (subtable->class == &table_transpose_class)
+    {
+      struct table_transpose *tt = table_transpose_cast (subtable);
+      struct table *table = table_ref (tt->subtable);
+      table_unref (subtable);
+      return table;
+    }
+  else
+    {
+      struct table_transpose *tt;
+      int axis;
+
+      tt = xmalloc (sizeof *tt);
+      table_init (&tt->table, &table_transpose_class);
+      tt->subtable = subtable;
+
+      for (axis = 0; axis < TABLE_N_AXES; axis++)
+        {
+          tt->table.n[axis] = subtable->n[!axis];
+          tt->table.h[axis][0] = subtable->h[!axis][0];
+          tt->table.h[axis][1] = subtable->h[!axis][1];
+        }
+      return &tt->table;
+    }
+}
+
+static void
+table_transpose_destroy (struct table *ti)
+{
+  struct table_transpose *tt = table_transpose_cast (ti);
+  table_unref (tt->subtable);
+  free (tt);
+}
+
+static void
+swap (int *x, int *y)
+{
+  int t = *x;
+  *x = *y;
+  *y = t;
+}
+
+static void
+table_transpose_get_cell (const struct table *ti, int x, int y,
+                          struct table_cell *cell)
+{
+  struct table_transpose *tt = table_transpose_cast (ti);
+  int i;
+
+  table_get_cell (tt->subtable, y, x, cell);
+  for (i = 0; i < 2; i++)
+    swap (&cell->d[TABLE_HORZ][i], &cell->d[TABLE_VERT][i]);
+}
+
+static int
+table_transpose_get_rule (const struct table *ti,
+                          enum table_axis axis,
+                          int x, int y)
+{
+  struct table_transpose *tt = table_transpose_cast (ti);
+  return table_get_rule (tt->subtable, !axis, y, x);
+}
+
+static const struct table_class table_transpose_class =
+  {
+    table_transpose_destroy,
+    table_transpose_get_cell,
+    table_transpose_get_rule,
+    NULL,                       /* paste */
+    NULL,                       /* select */
+  };
index f8736e629c0148279ae8ed18b120c519d7de5271..696931ae50980147be4212dd1af477a73c2448d1 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000, 2006, 2009 Free Software Foundation, Inc.
+   Copyright (C) 2009 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
 
 #include <config.h>
 
-#include "table.h"
+#include <output/table.h>
+#include <output/table-provider.h>
 
-#include <ctype.h>
-#include <stdarg.h>
-#include <limits.h>
+#include <assert.h>
 #include <stdlib.h>
 
-#include "output.h"
-#include "manager.h"
-
-#include <data/data-out.h>
-#include <data/format.h>
-#include <data/value.h>
-#include <data/dictionary.h>
-#include <libpspp/assertion.h>
+#include <libpspp/cast.h>
 #include <libpspp/compiler.h>
-#include <libpspp/misc.h>
-#include <libpspp/pool.h>
-
-#include <data/settings.h>
-
-#include "error.h"
-#include "minmax.h"
-#include "xalloc.h"
 
-#include "gettext.h"
-#define _(msgid) gettext (msgid)
-\f
-const struct som_table_class tab_table_class;
+#include "gl/xalloc.h"
 
-/* Returns the font to use for a cell with the given OPTIONS. */
-static enum outp_font
-options_to_font (unsigned options)
+/* Increases TABLE's reference count, indicating that it has an additional
+   owner.  An table that is shared among multiple owners must not be
+   modified. */
+struct table *
+table_ref (const struct table *table_)
 {
-  return (options & TAB_FIX ? OUTP_FIXED
-          : options & TAB_EMPH ? OUTP_EMPHASIS
-          : OUTP_PROPORTIONAL);
+  struct table *table = CONST_CAST (struct table *, table_);
+  table->ref_cnt++;
+  return table;
 }
 
-/* Creates a table with NC columns and NR rows. */
-struct tab_table *
-tab_create (int nc, int nr)
-{
-  struct tab_table *t;
-
-  t = pool_create_container (struct tab_table, container);
-  t->ref_cnt = 1;
-  t->col_style = TAB_COL_NONE;
-  t->title = NULL;
-  t->flags = SOMF_NONE;
-  t->nr = nr;
-  t->nc = t->cf = nc;
-  t->l = t->r = t->t = t->b = 0;
-
-  t->cc = pool_nmalloc (t->container, nr * nc, sizeof *t->cc);
-  t->ct = pool_malloc (t->container, nr * nc);
-  memset (t->ct, TAB_EMPTY, nc * nr);
-
-  t->rh = pool_nmalloc (t->container, nc, nr + 1);
-  memset (t->rh, 0, nc * (nr + 1));
-
-  t->rv = pool_nmalloc (t->container, nr, nc + 1);
-  memset (t->rv, UCHAR_MAX, nr * (nc + 1));
-
-  t->dim = NULL;
-  t->col_ofs = t->row_ofs = 0;
-
-  return t;
-}
-
-/* Increases T's reference count and, if this causes T's
-   reference count to reach 0, destroys T. */
-void
-tab_destroy (struct tab_table *t)
-{
-  assert (t->ref_cnt > 0);
-  if (--t->ref_cnt > 0)
-    return;
-  if (t->dim_free != NULL)
-    t->dim_free (t->dim_aux);
-  free (t->title);
-  pool_destroy (t->container);
-}
-
-/* Increases T's reference count. */
+/* Decreases TABLE's reference count, indicating that it has one fewer owner.
+   If TABLE no longer has any owners, it is freed. */
 void
-tab_ref (struct tab_table *t)
+table_unref (struct table *table)
 {
-  assert (t->ref_cnt > 0);
-  t->ref_cnt++;
-}
-
-/* Sets the width and height of a table, in columns and rows,
-   respectively.  Use only to reduce the size of a table, since it
-   does not change the amount of allocated memory. */
-void
-tab_resize (struct tab_table *t, int nc, int nr)
-{
-  assert (t != NULL);
-  if (nc != -1)
-    {
-      assert (nc + t->col_ofs <= t->cf);
-      t->nc = nc + t->col_ofs;
-    }
-  if (nr != -1)
-    {
-      assert (nr + t->row_ofs <= tab_nr (t));
-      t->nr = nr + t->row_ofs;
-    }
-}
-
-/* Changes either or both dimensions of a table.  Consider using the
-   above routine instead if it won't waste a lot of space.
-
-   Changing the number of columns in a table is particularly expensive
-   in space and time.  Avoid doing such.  FIXME: In fact, transferring
-   of rules isn't even implemented yet. */
-void
-tab_realloc (struct tab_table *t, int nc, int nr)
-{
-  int ro, co;
-
-  assert (t != NULL);
-  ro = t->row_ofs;
-  co = t->col_ofs;
-  if (ro || co)
-    tab_offset (t, 0, 0);
-
-  if (nc == -1)
-    nc = tab_nc (t);
-  if (nr == -1)
-    nr = tab_nr (t);
-
-  assert (nc == tab_nc (t));
-
-  if (nc > t->cf)
-    {
-      int mr1 = MIN (nr, tab_nr (t));
-      int mc1 = MIN (nc, tab_nc (t));
-
-      struct substring *new_cc;
-      unsigned char *new_ct;
-      int r;
-
-      new_cc = pool_nmalloc (t->container, nr * nc, sizeof *new_cc);
-      new_ct = pool_malloc (t->container, nr * nc);
-      for (r = 0; r < mr1; r++)
-       {
-         memcpy (&new_cc[r * nc], &t->cc[r * tab_nc (t)], mc1 * sizeof *t->cc);
-         memcpy (&new_ct[r * nc], &t->ct[r * tab_nc (t)], mc1);
-         memset (&new_ct[r * nc + tab_nc (t)], TAB_EMPTY, nc - tab_nc (t));
-       }
-      pool_free (t->container, t->cc);
-      pool_free (t->container, t->ct);
-      t->cc = new_cc;
-      t->ct = new_ct;
-      t->cf = nc;
-    }
-  else if (nr != tab_nr (t))
-    {
-      t->cc = pool_nrealloc (t->container, t->cc, nr * nc, sizeof *t->cc);
-      t->ct = pool_realloc (t->container, t->ct, nr * nc);
-
-      t->rh = pool_nrealloc (t->container, t->rh, nc, nr + 1);
-      t->rv = pool_nrealloc (t->container, t->rv, nr, nc + 1);
-
-      if (nr > tab_nr (t))
-       {
-         memset (&t->rh[nc * (tab_nr (t) + 1)], TAL_0, (nr - tab_nr (t)) * nc);
-         memset (&t->rv[(nc + 1) * tab_nr (t)], UCHAR_MAX,
-                  (nr - tab_nr (t)) * (nc + 1));
-       }
-    }
-
-  memset (&t->ct[nc * tab_nr (t)], TAB_EMPTY, nc * (nr - tab_nr (t)));
-
-  t->nr = nr;
-  t->nc = nc;
-
-  if (ro || co)
-    tab_offset (t, co, ro);
-}
-
-/* Sets the number of header rows on each side of TABLE to L on the
-   left, R on the right, T on the top, B on the bottom.  Header rows
-   are repeated when a table is broken across multiple columns or
-   multiple pages. */
-void
-tab_headers (struct tab_table *table, int l, int r, int t, int b)
-{
-  assert (table != NULL);
-  assert (l < table->nc);
-  assert (r < table->nc);
-  assert (t < table->nr);
-  assert (b < table->nr);
-
-
-  table->l = l;
-  table->r = r;
-  table->t = t;
-  table->b = b;
-}
-
-/* Set up table T so that, when it is an appropriate size, it will be
-   displayed across the page in columns.
-
-   STYLE is a TAB_COL_* constant. */
-void
-tab_columns (struct tab_table *t, int style)
-{
-  assert (t != NULL);
-  t->col_style = style;
-}
-\f
-/* Rules. */
-
-/* Draws a vertical line to the left of cells at horizontal position X
-   from Y1 to Y2 inclusive in style STYLE, if style is not -1. */
-void
-tab_vline (struct tab_table *t, int style, int x, int y1, int y2)
-{
-  assert (t != NULL);
-
-#if DEBUGGING
-  if (x + t->col_ofs < 0 || x + t->col_ofs > tab_nc (t)
-      || y1 + t->row_ofs < 0 || y1 + t->row_ofs >= tab_nr (t)
-      || y2 + t->row_ofs < 0 || y2 + t->row_ofs >= tab_nr (t))
-    {
-      printf (_("bad vline: x=%d+%d=%d y=(%d+%d=%d,%d+%d=%d) in "
-               "table size (%d,%d)\n"),
-             x, t->col_ofs, x + t->col_ofs,
-             y1, t->row_ofs, y1 + t->row_ofs,
-             y2, t->row_ofs, y2 + t->row_ofs,
-             tab_nc (t), tab_nr (t));
-      return;
-    }
-#endif
-
-  x += t->col_ofs;
-  y1 += t->row_ofs;
-  y2 += t->row_ofs;
-
-  assert (x  > 0);
-  assert (x  < tab_nc (t));
-  assert (y1 >= 0);
-  assert (y2 >= y1);
-  assert (y2 <=  tab_nr (t));
-
-  if (style != -1)
-    {
-      int y;
-      for (y = y1; y <= y2; y++)
-        t->rv[x + (t->cf + 1) * y] = style;
-    }
-}
-
-/* Draws a horizontal line above cells at vertical position Y from X1
-   to X2 inclusive in style STYLE, if style is not -1. */
-void
-tab_hline (struct tab_table * t, int style, int x1, int x2, int y)
-{
-  assert (t != NULL);
-
-  x1 += t->col_ofs;
-  x2 += t->col_ofs;
-  y += t->row_ofs;
-
-  assert (y >= 0);
-  assert (y <= tab_nr (t));
-  assert (x2 >= x1 );
-  assert (x1 >= 0 );
-  assert (x2 < tab_nc (t));
-
-  if (style != -1)
-    {
-      int x;
-      for (x = x1; x <= x2; x++)
-        t->rh[x + t->cf * y] = style;
-    }
-}
-
-/* Draws a box around cells (X1,Y1)-(X2,Y2) inclusive with horizontal
-   lines of style F_H and vertical lines of style F_V.  Fills the
-   interior of the box with horizontal lines of style I_H and vertical
-   lines of style I_V.  Any of the line styles may be -1 to avoid
-   drawing those lines.  This is distinct from 0, which draws a null
-   line. */
-void
-tab_box (struct tab_table *t, int f_h, int f_v, int i_h, int i_v,
-        int x1, int y1, int x2, int y2)
-{
-  assert (t != NULL);
-
-#if DEBUGGING
-  if (x1 + t->col_ofs < 0 || x1 + t->col_ofs >= tab_nc (t)
-      || x2 + t->col_ofs < 0 || x2 + t->col_ofs >= tab_nc (t)
-      || y1 + t->row_ofs < 0 || y1 + t->row_ofs >= tab_nr (t)
-      || y2 + t->row_ofs < 0 || y2 + t->row_ofs >= tab_nr (t))
-    {
-      printf (_("bad box: (%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) "
-               "in table size (%d,%d)\n"),
-             x1, t->col_ofs, x1 + t->col_ofs,
-             y1, t->row_ofs, y1 + t->row_ofs,
-             x2, t->col_ofs, x2 + t->col_ofs,
-             y2, t->row_ofs, y2 + t->row_ofs,
-             tab_nc (t), tab_nr (t));
-      NOT_REACHED ();
-    }
-#endif
-
-  x1 += t->col_ofs;
-  x2 += t->col_ofs;
-  y1 += t->row_ofs;
-  y2 += t->row_ofs;
-
-  assert (x2 >= x1);
-  assert (y2 >= y1);
-  assert (x1 >= 0);
-  assert (y1 >= 0);
-  assert (x2 < tab_nc (t));
-  assert (y2 < tab_nr (t));
-
-  if (f_h != -1)
-    {
-      int x;
-      for (x = x1; x <= x2; x++)
-        {
-          t->rh[x + t->cf * y1] = f_h;
-          t->rh[x + t->cf * (y2 + 1)] = f_h;
-        }
-    }
-  if (f_v != -1)
-    {
-      int y;
-      for (y = y1; y <= y2; y++)
-        {
-          t->rv[x1 + (t->cf + 1) * y] = f_v;
-          t->rv[(x2 + 1) + (t->cf + 1) * y] = f_v;
-        }
-    }
-
-  if (i_h != -1)
-    {
-      int y;
-
-      for (y = y1 + 1; y <= y2; y++)
-       {
-         int x;
-
-          for (x = x1; x <= x2; x++)
-            t->rh[x + t->cf * y] = i_h;
-       }
-    }
-  if (i_v != -1)
-    {
-      int x;
-
-      for (x = x1 + 1; x <= x2; x++)
-       {
-         int y;
-
-          for (y = y1; y <= y2; y++)
-            t->rv[x + (t->cf + 1) * y] = i_v;
-       }
-    }
-}
-
-/* Set the title of table T to TITLE, which is formatted as if
-   passed to printf(). */
-void
-tab_title (struct tab_table *t, const char *title, ...)
-{
-  va_list args;
-
-  assert (t != NULL && title != NULL);
-  va_start (args, title);
-  t->title = xvasprintf (title, args);
-  va_end (args);
-}
-
-/* Set DIM_FUNC, which will be passed auxiliary data AUX, as the
-   dimension function for table T.
-
-   DIM_FUNC must not assume that it is called from the same
-   context as tab_dim; for example, table T might be kept in
-   memory and, thus, DIM_FUNC might be called after the currently
-   running command completes.  If it is non-null, FREE_FUNC is
-   called when the table is destroyed, to allow any data
-   allocated for use by DIM_FUNC to be freed.  */
-void
-tab_dim (struct tab_table *t,
-         tab_dim_func *dim_func, tab_dim_free_func *free_func, void *aux)
-{
-  assert (t->dim == NULL);
-  t->dim = dim_func;
-  t->dim_free = free_func;
-  t->dim_aux = aux;
-}
-
-/* Returns the natural width of column C in table T for driver D, that
-   is, the smallest width necessary to display all its cells without
-   wrapping.  The width will be no larger than the page width minus
-   left and right rule widths. */
-int
-tab_natural_width (const struct tab_rendering *r, int col)
-{
-  const struct tab_table *t = r->table;
-  int width, row, max_width;
-
-  assert (col >= 0 && col < tab_nc (t));
-
-  width = 0;
-  for (row = 0; row < tab_nr (t); row++)
-    {
-      struct outp_text text;
-      unsigned char opt = t->ct[col + row * t->cf];
-      int w;
-
-      if (opt & (TAB_JOIN | TAB_EMPTY))
-        continue;
-
-      text.string = t->cc[col + row * t->cf];
-      text.justification = OUTP_LEFT;
-      text.font = options_to_font (opt);
-      text.h = text.v = INT_MAX;
-
-      r->driver->class->text_metrics (r->driver, &text, &w, NULL);
-      if (w > width)
-        width = w;
-    }
-
-  if (width == 0)
-    {
-      /* FIXME: This is an ugly kluge to compensate for the fact
-         that we don't let joined cells contribute to column
-         widths. */
-      width = r->driver->prop_em_width * 8;
-    }
-
-  max_width = r->driver->width - r->wrv[0] - r->wrv[tab_nc (t)];
-  return MIN (width, max_width);
-}
-
-/* Returns the natural height of row R in table T for driver D, that
-   is, the minimum height necessary to display the information in the
-   cell at the widths set for each column. */
-int
-tab_natural_height (const struct tab_rendering *r, int row)
-{
-  const struct tab_table *t = r->table;
-  int height, col;
-
-  assert (row >= 0 && row < tab_nr (t));
-
-  height = r->driver->font_height;
-  for (col = 0; col < tab_nc (t); col++)
-    {
-      struct outp_text text;
-      unsigned char opt = t->ct[col + row * t->cf];
-      int h;
-
-      if (opt & (TAB_JOIN | TAB_EMPTY))
-        continue;
-
-      text.string = t->cc[col + row * t->cf];
-      text.justification = OUTP_LEFT;
-      text.font = options_to_font (opt);
-      text.h = r->w[col];
-      text.v = INT_MAX;
-      r->driver->class->text_metrics (r->driver, &text, NULL, &h);
-
-      if (h > height)
-        height = h;
-    }
-
-  return height;
-}
-
-/* Callback function to set all columns and rows to their natural
-   dimensions.  Not really meant to be called directly.  */
-void
-tab_natural_dimensions (struct tab_rendering *r, void *aux UNUSED)
-{
-  const struct tab_table *t = r->table;
-  int i;
-
-  for (i = 0; i < tab_nc (t); i++)
-    r->w[i] = tab_natural_width (r, i);
-
-  for (i = 0; i < tab_nr (t); i++)
-    r->h[i] = tab_natural_height (r, i);
-}
-
-\f
-/* Cells. */
-
-/* Sets cell (C,R) in TABLE, with options OPT, to have a value taken
-   from V, displayed with format spec F. */
-void
-tab_value (struct tab_table *table, int c, int r, unsigned char opt,
-          const union value *v, const struct dictionary *dict, 
-          const struct fmt_spec *f)
-{
-  char *contents;
-
-  assert (table != NULL && v != NULL && f != NULL);
-#if DEBUGGING
-  if (c + table->col_ofs < 0 || r + table->row_ofs < 0
-      || c + table->col_ofs >= tab_nc (table)
-      || r + table->row_ofs >= tab_nr (table))
-    {
-      printf ("tab_value(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
-             "(%d,%d)\n",
-             c, table->col_ofs, c + table->col_ofs,
-             r, table->row_ofs, r + table->row_ofs,
-             tab_nc (table), tab_nr (table));
-      return;
-    }
-#endif
-
-  contents = data_out_pool (v, dict_get_encoding (dict), f, table->container);
-
-  table->cc[c + r * table->cf] = ss_cstr (contents);
-  table->ct[c + r * table->cf] = opt;
-}
-
-/* Sets cell (C,R) in TABLE, with options OPT, to have value VAL
-   with NDEC decimal places. */
-void
-tab_fixed (struct tab_table *table, int c, int r, unsigned char opt,
-          double val, int w, int d)
-{
-  char *s, *cp;
-
-  struct fmt_spec f;
-  union value double_value;
-
-  assert (table != NULL && w <= 40);
-
-  assert (c >= 0);
-  assert (c < tab_nc (table));
-  assert (r >= 0);
-  assert (r < tab_nr (table));
-
-  f = fmt_for_output (FMT_F, w, d);
-
-#if DEBUGGING
-  if (c + table->col_ofs < 0 || r + table->row_ofs < 0
-      || c + table->col_ofs >= tab_nc (table)
-      || r + table->row_ofs >= tab_nr (table))
-    {
-      printf ("tab_fixed(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
-             "(%d,%d)\n",
-             c, table->col_ofs, c + table->col_ofs,
-             r, table->row_ofs, r + table->row_ofs,
-             tab_nc (table), tab_nr (table));
-      return;
-    }
-#endif
-
-  double_value.f = val;
-  s = data_out_pool (&double_value, LEGACY_NATIVE, &f, table->container);
-
-  cp = s;
-  while (isspace ((unsigned char) *cp) && cp < &s[w])
-    cp++;
-  f.w = w - (cp - s);
-
-  table->cc[c + r * table->cf] = ss_buffer (cp, f.w);
-  table->ct[c + r * table->cf] = opt;
-}
-
-/* Sets cell (C,R) in TABLE, with options OPT, to have value VAL as
-   formatted by FMT.
-   If FMT is null, then the default print format will be used.
-*/
-void
-tab_double (struct tab_table *table, int c, int r, unsigned char opt,
-          double val, const struct fmt_spec *fmt)
-{
-  struct substring ss;
-  union value double_value ;
-
-  assert (table != NULL);
-
-  assert (c >= 0);
-  assert (c < tab_nc (table));
-  assert (r >= 0);
-  assert (r < tab_nr (table));
-
-  if ( fmt == NULL)
-    fmt = settings_get_format ();
-
-  fmt_check_output (fmt);
-
-#if DEBUGGING
-  if (c + table->col_ofs < 0 || r + table->row_ofs < 0
-      || c + table->col_ofs >= tab_nc (table)
-      || r + table->row_ofs >= tab_nr (table))
+  if (table != NULL)
     {
-      printf ("tab_double(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
-             "(%d,%d)\n",
-             c, table->col_ofs, c + table->col_ofs,
-             r, table->row_ofs, r + table->row_ofs,
-             tab_nc (table), tab_nr (table));
-      return;
+      assert (table->ref_cnt > 0);
+      if (--table->ref_cnt == 0)
+        table->class->destroy (table);
     }
-#endif
-
-  double_value.f = val;
-  ss = ss_cstr (data_out_pool (&double_value, LEGACY_NATIVE, fmt, table->container));
-
-  ss_ltrim (&ss, ss_cstr (" "));
-
-  table->cc[c + r * table->cf] = ss;
-  table->ct[c + r * table->cf] = opt;
 }
 
-
-static void
-do_tab_text (struct tab_table *table, int c, int r, unsigned opt, char *text)
+/* Returns true if TABLE has more than one owner.  A table item that is shared
+   among multiple owners must not be modified. */
+bool
+table_is_shared (const struct table *table)
 {
-  assert (c >= 0 );
-  assert (r >= 0 );
-  assert (c < tab_nc (table));
-  assert (r < tab_nr (table));
-
-#if DEBUGGING
-  if (c + table->col_ofs < 0 || r + table->row_ofs < 0
-      || c + table->col_ofs >= tab_nc (table)
-      || r + table->row_ofs >= tab_nr (table))
-    {
-      printf ("tab_text(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
-             "(%d,%d)\n",
-             c, table->col_ofs, c + table->col_ofs,
-             r, table->row_ofs, r + table->row_ofs,
-             tab_nc (table), tab_nr (table));
-      return;
-    }
-#endif
-
-  table->cc[c + r * table->cf] = ss_cstr (text);
-  table->ct[c + r * table->cf] = opt;
+  return table->ref_cnt > 1;
 }
 
-/* Sets cell (C,R) in TABLE, with options OPT, to have text value
-   TEXT. */
+/* Sets the number of left header columns in TABLE to HL. */
 void
-tab_text (struct tab_table *table, int c, int r, unsigned opt,
-          const char *text)
+table_set_hl (struct table *table, int hl)
 {
-  do_tab_text (table, c, r, opt, pool_strdup (table->container, text));
+  assert (!table_is_shared (table));
+  table->h[TABLE_HORZ][0] = hl;
 }
 
-/* Sets cell (C,R) in TABLE, with options OPT, to have text value
-   FORMAT, which is formatted as if passed to printf. */
+/* Sets the number of right header columns in TABLE to HR. */
 void
-tab_text_format (struct tab_table *table, int c, int r, unsigned opt,
-                 const char *format, ...)
+table_set_hr (struct table *table, int hr)
 {
-  va_list args;
-
-  va_start (args, format);
-  do_tab_text (table, c, r, opt,
-               pool_vasprintf (table->container, format, args));
-  va_end (args);
+  assert (!table_is_shared (table));
+  table->h[TABLE_HORZ][1] = hr;
 }
 
-static void
-do_tab_joint_text (struct tab_table *table, int x1, int y1, int x2, int y2,
-                   unsigned opt, char *text)
-{
-  struct tab_joined_cell *j;
-
-  assert (x1 + table->col_ofs >= 0);
-  assert (y1 + table->row_ofs >= 0);
-  assert (y2 >= y1);
-  assert (x2 >= x1);
-  assert (y2 + table->row_ofs < tab_nr (table));
-  assert (x2 + table->col_ofs < tab_nc (table));
-
-#if DEBUGGING
-  if (x1 + table->col_ofs < 0 || x1 + table->col_ofs >= tab_nc (table)
-      || y1 + table->row_ofs < 0 || y1 + table->row_ofs >= tab_nr (table)
-      || x2 < x1 || x2 + table->col_ofs >= tab_nc (table)
-      || y2 < y2 || y2 + table->row_ofs >= tab_nr (table))
-    {
-      printf ("tab_joint_text(): bad cell "
-             "(%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) in table size (%d,%d)\n",
-             x1, table->col_ofs, x1 + table->col_ofs,
-             y1, table->row_ofs, y1 + table->row_ofs,
-             x2, table->col_ofs, x2 + table->col_ofs,
-             y2, table->row_ofs, y2 + table->row_ofs,
-             tab_nc (table), tab_nr (table));
-      return;
-    }
-#endif
-
-  tab_box (table, -1, -1, TAL_0, TAL_0, x1, y1, x2, y2);
-
-  j = pool_alloc (table->container, sizeof *j);
-  j->x1 = x1 + table->col_ofs;
-  j->y1 = y1 + table->row_ofs;
-  j->x2 = ++x2 + table->col_ofs;
-  j->y2 = ++y2 + table->row_ofs;
-  j->contents = ss_cstr (text);
-
-  opt |= TAB_JOIN;
-
-  {
-    struct substring *cc = &table->cc[x1 + y1 * table->cf];
-    unsigned char *ct = &table->ct[x1 + y1 * table->cf];
-    const int ofs = table->cf - (x2 - x1);
-
-    int y;
-
-    for (y = y1; y < y2; y++)
-      {
-       int x;
-
-       for (x = x1; x < x2; x++)
-         {
-           *cc++ = ss_buffer ((char *) j, 0);
-           *ct++ = opt;
-         }
-
-       cc += ofs;
-       ct += ofs;
-      }
-  }
-}
-
-/* Joins cells (X1,X2)-(Y1,Y2) inclusive in TABLE, and sets them with
-   options OPT to have text value TEXT. */
+/* Sets the number of top header rows in TABLE to HT. */
 void
-tab_joint_text (struct tab_table *table, int x1, int y1, int x2, int y2,
-                unsigned opt, const char *text)
+table_set_ht (struct table *table, int ht)
 {
-  do_tab_joint_text (table, x1, y1, x2, y2, opt,
-                     pool_strdup (table->container, text));
+  assert (!table_is_shared (table));
+  table->h[TABLE_VERT][0] = ht;
 }
 
-/* Joins cells (X1,X2)-(Y1,Y2) inclusive in TABLE, and sets them
-   with options OPT to have text value FORMAT, which is formatted
-   as if passed to printf. */
+/* Sets the number of top header rows in TABLE to HB. */
 void
-tab_joint_text_format (struct tab_table *table, int x1, int y1, int x2, int y2,
-                       unsigned opt, const char *format, ...)
+table_set_hb (struct table *table, int hb)
 {
-  va_list args;
-
-  va_start (args, format);
-  do_tab_joint_text (table, x1, y1, x2, y2, opt,
-                     pool_vasprintf (table->container, format, args));
-  va_end (args);
+  assert (!table_is_shared (table));
+  table->h[TABLE_VERT][1] = hb;
 }
 \f
-/* Miscellaneous. */
-
-/* Sets the widths of all the columns and heights of all the rows in
-   table T for driver D. */
-static void
-nowrap_dim (struct tab_rendering *r, void *aux UNUSED)
-{
-  r->w[0] = tab_natural_width (r, 0);
-  r->h[0] = r->driver->font_height;
-}
-
-/* Sets the widths of all the columns and heights of all the rows in
-   table T for driver D. */
-static void
-wrap_dim (struct tab_rendering *r, void *aux UNUSED)
-{
-  r->w[0] = tab_natural_width (r, 0);
-  r->h[0] = tab_natural_height (r, 0);
-}
+/* Initializes TABLE as a table of the specified CLASS, initially with a
+   reference count of 1.
 
-static void
-do_tab_output_text (struct tab_table *t, int options, char *text)
-{
-  do_tab_text (t, 0, 0, options, text);
-  tab_flags (t, SOMF_NO_TITLE | SOMF_NO_SPACING);
-  tab_dim (t, options & TAT_NOWRAP ? nowrap_dim : wrap_dim, NULL, NULL);
-  tab_submit (t);
-}
+   TABLE initially has 0 rows and columns and no headers.  The table
+   implementation should update the numbers of rows and columns.  The table
+   implementation (or its client) may update the header rows and columns.
 
-/* Outputs TEXT as a table with a single cell having cell options
-   OPTIONS, which is a combination of the TAB_* and TAT_*
-   constants.  */
+   A table is an abstract class, that is, a plain struct table is not useful on
+   its own.  Thus, this function is normally called from the initialization
+   function of some subclass of table. */
 void
-tab_output_text (int options, const char *text)
+table_init (struct table *table, const struct table_class *class)
 {
-  struct tab_table *table = tab_create (1, 1);
-  do_tab_output_text (table, options, pool_strdup (table->container, text));
+  table->class = class;
+  table->n[TABLE_HORZ] = table->n[TABLE_VERT] = 0;
+  table->h[TABLE_HORZ][0] = table->h[TABLE_HORZ][1] = 0;
+  table->h[TABLE_VERT][0] = table->h[TABLE_VERT][1] = 0;
+  table->ref_cnt = 1;
 }
 
-/* Outputs FORMAT as a table with a single cell having cell
-   options OPTIONS, which is a combination of the TAB_* and TAT_*
-   constants.  FORMAT is formatted as if it was passed through
-   printf. */
+/* Sets the number of columns in TABLE to NC. */
 void
-tab_output_text_format (int options, const char *format, ...)
+table_set_nc (struct table *table, int nc)
 {
-  struct tab_table *table;
-  va_list args;
-
-  table = tab_create (1, 1);
-
-  va_start (args, format);
-  do_tab_output_text (table, options,
-                      pool_vasprintf (table->container, format, args));
-  va_end (args);
+  assert (!table_is_shared (table));
+  table->n[TABLE_HORZ] = nc;
 }
 
-/* Set table flags to FLAGS. */
+/* Sets the number of rows in TABLE to NR. */
 void
-tab_flags (struct tab_table *t, unsigned flags)
+table_set_nr (struct table *table, int nr)
 {
-  assert (t != NULL);
-  t->flags = flags;
-}
-
-/* Easy, type-safe way to submit a tab table to som. */
-void
-tab_submit (struct tab_table *t)
-{
-  struct som_entity s;
-
-  assert (t != NULL);
-  s.class = &tab_table_class;
-  s.ext = t;
-  s.type = SOM_TABLE;
-  som_submit (&s);
-  tab_destroy (t);
+  assert (!table_is_shared (table));
+  table->n[TABLE_VERT] = nr;
 }
 \f
-/* Editing. */
+/* Initializes CELL with the contents of the table cell at column X and row Y
+   within TABLE.  When CELL is no longer needed, the caller is responsible for
+   freeing it by calling table_cell_free(CELL).
 
-/* Set table row and column offsets for all functions that affect
-   cells or rules. */
+   The caller must ensure that CELL is destroyed before TABLE is unref'ed. */
 void
-tab_offset (struct tab_table *t, int col, int row)
+table_get_cell (const struct table *table, int x, int y,
+                struct table_cell *cell)
 {
-  int diff = 0;
-
-  assert (t != NULL);
-#if DEBUGGING
-  if (row < -1 || row > tab_nr (t))
-    {
-      printf ("tab_offset(): row=%d in %d-row table\n", row, tab_nr (t));
-      NOT_REACHED ();
-    }
-  if (col < -1 || col > tab_nc (t))
-    {
-      printf ("tab_offset(): col=%d in %d-column table\n", col, tab_nc (t));
-      NOT_REACHED ();
-    }
-#endif
-
-  if (row != -1)
-    diff += (row - t->row_ofs) * t->cf, t->row_ofs = row;
-  if (col != -1)
-    diff += (col - t->col_ofs), t->col_ofs = col;
-
-  t->cc += diff;
-  t->ct += diff;
+  assert (x >= 0 && x < table->n[TABLE_HORZ]);
+  assert (y >= 0 && y < table->n[TABLE_VERT]);
+  table->class->get_cell (table, x, y, cell);
 }
 
-/* Increment the row offset by one. If the table is too small,
-   increase its size. */
+/* Frees CELL, which should have been initialized by calling
+   table_get_cell(). */
 void
-tab_next_row (struct tab_table *t)
+table_cell_free (struct table_cell *cell)
+{
+  if (cell->destructor != NULL)
+    cell->destructor (cell->destructor_aux);
+}
+
+/* Returns one of the TAL_* enumeration constants (declared in output/table.h)
+   representing a rule running alongside one of the cells in TABLE.
+
+   Suppose NC is the number of columns in TABLE and NR is the number of rows.
+   Then, if AXIS is TABLE_HORZ, then 0 <= X <= NC and 0 <= Y < NR.  If (X,Y) =
+   (0,0), the return value is the rule that runs vertically on the left side of
+   cell (0,0); if (X,Y) = (1,0), it is the vertical rule between that cell and
+   cell (1,0); and so on, up to (NC,0), which runs vertically on the right of
+   cell (NC-1,0).
+
+   The following diagram illustrates the meaning of (X,Y) for AXIS = TABLE_HORZ
+   within a 7x7 table.  The '|' characters at the intersection of the X labels
+   and Y labels show the rule whose style would be returned by calling
+   table_get_rule with those X and Y values:
+
+                           0  1  2  3  4  5  6  7
+                           +--+--+--+--+--+--+--+
+                         0 |  |  |  |  |  |  |  |
+                           +--+--+--+--+--+--+--+
+                         1 |  |  |  |  |  |  |  |
+                           +--+--+--+--+--+--+--+
+                         2 |  |  |  |  |  |  |  |
+                           +--+--+--+--+--+--+--+
+                         3 |  |  |  |  |  |  |  |
+                           +--+--+--+--+--+--+--+
+                         4 |  |  |  |  |  |  |  |
+                           +--+--+--+--+--+--+--+
+                         5 |  |  |  |  |  |  |  |
+                           +--+--+--+--+--+--+--+
+                         6 |  |  |  |  |  |  |  |
+                           +--+--+--+--+--+--+--+
+
+   Similarly, if AXIS is TABLE_VERT, then 0 <= X < NC and 0 <= Y <= NR.  If
+   (X,Y) = (0,0), the return value is the rule that runs horizontally above
+   the top of cell (0,0); if (X,Y) = (0,1), it is the horizontal rule
+   between that cell and cell (0,1); and so on, up to (0,NR), which runs
+   horizontally below cell (0,NR-1). */
+int
+table_get_rule (const struct table *table, enum table_axis axis, int x, int y)
 {
-  assert (t != NULL);
-  t->cc += t->cf;
-  t->ct += t->cf;
-  if (++t->row_ofs >= tab_nr (t))
-    tab_realloc (t, -1, tab_nr (t) * 4 / 3);
+  assert (x >= 0 && x < table->n[TABLE_HORZ] + (axis == TABLE_HORZ));
+  assert (y >= 0 && y < table->n[TABLE_VERT] + (axis == TABLE_VERT));
+  return table->class->get_rule (table, axis, x, y);
 }
 \f
-/* Return the number of columns and rows in the table into N_COLUMNS
-   and N_ROWS, respectively. */
-static void
-tabi_count (struct som_entity *t_, int *n_columns, int *n_rows)
-{
-  struct tab_table *t = t_->ext;
-  *n_columns = t->nc;
-  *n_rows = t->nr;
-}
+struct table_unshared
+  {
+    struct table table;
+    struct table *subtable;
+  };
 
-/* Return the column style for this table into STYLE. */
-static void
-tabi_columns (struct som_entity *t_, int *style)
-{
-  struct tab_table *t = t_->ext;
-  *style = t->col_style;
-}
+static const struct table_class table_unshared_class;
 
-/* Return the number of header rows/columns on the left, right, top,
-   and bottom sides into HL, HR, HT, and HB, respectively. */
-static void
-tabi_headers (struct som_entity *t_, int *hl, int *hr, int *ht, int *hb)
-{
-  struct tab_table *t = t_->ext;
-  *hl = t->l;
-  *hr = t->r;
-  *ht = t->t;
-  *hb = t->b;
-}
+/* Takes ownership of TABLE and returns a table with the same contents but
+   which is guaranteed not to be shared (as returned by table_is_shared()).
 
-/* Return flags set for the current table into FLAGS. */
-static void
-tabi_flags (struct som_entity *t_, unsigned *flags)
-{
-  struct tab_table *t = t_->ext;
-  *flags = t->flags;
-}
+   If TABLE is unshared, just returns TABLE.
 
-/* Returns the line style to use for spacing purposes for a rule
-   of the given TYPE. */
-static enum outp_line_style
-rule_to_spacing_type (unsigned char type)
+   The only real use for this function is to create a copy of TABLE in which
+   the headers can be adjusted, which is a pretty specialized use case. */
+struct table *
+table_unshare (struct table *table)
 {
-  switch (type)
+  if (!table_is_shared (table))
+    return table;
+  else
     {
-    case TAL_0:
-      return OUTP_L_NONE;
-    case TAL_GAP:
-    case TAL_1:
-      return OUTP_L_SINGLE;
-    case TAL_2:
-      return OUTP_L_DOUBLE;
-    default:
-      NOT_REACHED ();
+      struct table_unshared *tiu = xmalloc (sizeof *tiu);
+      table_init (&tiu->table, &table_unshared_class);
+      table_set_nc (&tiu->table, table_nc (table));
+      table_set_nr (&tiu->table, table_nr (table));
+      table_set_hl (&tiu->table, table_hl (table));
+      table_set_hr (&tiu->table, table_hr (table));
+      table_set_ht (&tiu->table, table_ht (table));
+      table_set_hb (&tiu->table, table_hb (table));
+      tiu->subtable = table;
+      return &tiu->table;
     }
 }
 
-static void *
-tabi_render_init (struct som_entity *t_, struct outp_driver *driver,
-                  int hl, int hr, int ht, int hb)
+static struct table_unshared *
+table_unshared_cast (const struct table *table)
 {
-  const struct tab_table *t = t_->ext;
-  struct tab_rendering *r;
-  int col, row;
-  int i;
-
-  tab_offset (t_->ext, 0, 0);
-
-  r = xmalloc (sizeof *r);
-  r->table = t;
-  r->driver = driver;
-  r->w = xnmalloc (tab_nc (t), sizeof *r->w);
-  r->h = xnmalloc (tab_nr (t), sizeof *r->h);
-  r->hrh = xnmalloc (tab_nr (t) + 1, sizeof *r->hrh);
-  r->wrv = xnmalloc (tab_nc (t) + 1, sizeof *r->wrv);
-  r->l = hl;
-  r->r = hr;
-  r->t = ht;
-  r->b = hb;
-
-  /* Figure out sizes of rules. */
-  for (row = 0; row <= tab_nr (t); row++)
-    {
-      int width = 0;
-      for (col = 0; col < tab_nc (t); col++)
-        {
-          unsigned char rh = t->rh[col + row * t->cf];
-          int w = driver->horiz_line_width[rule_to_spacing_type (rh)];
-          if (w > width)
-            width = w;
-        }
-      r->hrh[row] = width;
-    }
-
-  for (col = 0; col <= tab_nc (t); col++)
-    {
-      int width = 0;
-      for (row = 0; row < tab_nr (t); row++)
-        {
-          unsigned char *rv = &t->rv[col + row * (t->cf + 1)];
-          int w;
-          if (*rv == UCHAR_MAX)
-            *rv = col != 0 && col != tab_nc (t) ? TAL_GAP : TAL_0;
-          w = driver->vert_line_width[rule_to_spacing_type (*rv)];
-          if (w > width)
-            width = w;
-        }
-      r->wrv[col] = width;
-    }
-
-  /* Determine row heights and columns widths. */
-  for (i = 0; i < tab_nr (t); i++)
-    r->h[i] = -1;
-  for (i = 0; i < tab_nc (t); i++)
-    r->w[i] = -1;
-
-  t->dim (r, t->dim_aux);
-
-  for (i = 0; i < tab_nr (t); i++)
-    if (r->h[i] < 0)
-      error (0, 0, "height of table row %d is %d (not initialized?)",
-             i, r->h[i]);
-  for (i = 0; i < tab_nc (t); i++)
-    if (r->w[i] < 0)
-      error (0, 0, "width of table column %d is %d (not initialized?)",
-             i, r->w[i]);
-
-  /* Add up header sizes. */
-  for (i = 0, r->wl = r->wrv[0]; i < r->l; i++)
-    r->wl += r->w[i] + r->wrv[i + 1];
-  for (i = 0, r->ht = r->hrh[0]; i < r->t; i++)
-    r->ht += r->h[i] + r->hrh[i + 1];
-  for (i = tab_nc (t) - r->r, r->wr = r->wrv[i]; i < tab_nc (t); i++)
-    r->wr += r->w[i] + r->wrv[i + 1];
-  for (i = tab_nr (t) - r->b, r->hb = r->hrh[i]; i < tab_nr (t); i++)
-    r->hb += r->h[i] + r->hrh[i + 1];
-
-  /* Title. */
-  if (!(t->flags & SOMF_NO_TITLE))
-    r->ht += driver->font_height;
-
-  return r;
+  assert (table->class == &table_unshared_class);
+  return UP_CAST (table, struct table_unshared, table);
 }
 
 static void
-tabi_render_free (void *r_)
+table_unshared_destroy (struct table *tiu_)
 {
-  struct tab_rendering *r = r_;
-
-  free (r->w);
-  free (r->h);
-  free (r->hrh);
-  free (r->wrv);
-  free (r);
+  struct table_unshared *tiu = table_unshared_cast (tiu_);
+  table_unref (tiu->subtable);
+  free (tiu);
 }
 
-/* Return the horizontal and vertical size of the entire table,
-   including headers, for the current output device, into HORIZ and
-   VERT. */
 static void
-tabi_area (void *r_, int *horiz, int *vert)
+table_unshared_get_cell (const struct table *tiu_, int x, int y,
+                              struct table_cell *cell)
 {
-  struct tab_rendering *r = r_;
-  const struct tab_table *t = r->table;
-  int width, col;
-  int height, row;
-
-  width = 0;
-  for (col = r->l + 1, width = r->wl + r->wr + r->w[tab_l (t)];
-       col < tab_nc (t) - r->r; col++)
-    width += r->w[col] + r->wrv[col];
-  *horiz = width;
-
-  height = 0;
-  for (row = r->t + 1, height = r->ht + r->hb + r->h[tab_t (t)];
-       row < tab_nr (t) - tab_b (t); row++)
-    height += r->h[row] + r->hrh[row];
-  *vert = height;
+  struct table_unshared *tiu = table_unshared_cast (tiu_);
+  table_get_cell (tiu->subtable, x, y, cell);
 }
 
-/* Determines the number of rows or columns (including appropriate
-   headers), depending on CUMTYPE, that will fit into the space
-   specified.  Takes rows/columns starting at index START and attempts
-   to fill up available space MAX.  Returns in END the index of the
-   last row/column plus one; returns in ACTUAL the actual amount of
-   space the selected rows/columns (including appropriate headers)
-   filled. */
-static void
-tabi_cumulate (void *r_, int cumtype, int start, int *end,
-               int max, int *actual)
-{
-  const struct tab_rendering *r = r_;
-  const struct tab_table *t = r->table;
-  int limit;
-  int *cells, *rules;
-  int total;
-  int idx;
-
-  assert (end != NULL && (cumtype == SOM_ROWS || cumtype == SOM_COLUMNS));
-  if (cumtype == SOM_ROWS)
-    {
-      assert (start >= 0 && start < tab_nr (t));
-      limit = tab_nr (t) - r->b;
-      cells = &r->h[start];
-      rules = &r->hrh[start + 1];
-      total = r->ht + r->hb;
-    }
-  else
-    {
-      assert (start >= 0 && start < tab_nc (t));
-      limit = tab_nc (t) - tab_r (t);
-      cells = &r->w[start];
-      rules = &r->wrv[start + 1];
-      total = r->wl + r->wr;
-    }
-
-  total += *cells++;
-  if (total > max)
-    {
-      if (end)
-       *end = start;
-      if (actual)
-       *actual = 0;
-      return;
-    }
-
-  for (idx = start + 1; idx < limit; idx++)
-    {
-      int amt = *cells++ + *rules++;
-
-      total += amt;
-      if (total > max)
-        {
-          total -= amt;
-          break;
-        }
-    }
-
-  if (end)
-    *end = idx;
-
-  if (actual)
-    *actual = total;
-}
-
-/* Render title for current table, with major index X and minor index
-   Y.  Y may be zero, or X and Y may be zero, but X should be nonzero
-   if Y is nonzero. */
-static void
-tabi_title (void *r_, int x, int y, int table_num, int subtable_num,
-            const char *command_name)
-{
-  const struct tab_rendering *r = r_;
-  const struct tab_table *t = r->table;
-  struct outp_text text;
-  struct string title;
-
-  if (t->flags & SOMF_NO_TITLE)
-    return;
-
-  ds_init_empty (&title);
-  ds_put_format (&title,"%d.%d", table_num, subtable_num);
-  if (x && y)
-    ds_put_format (&title, "(%d:%d)", x, y);
-  else if (x)
-    ds_put_format (&title, "(%d)", x);
-  if (command_name != NULL)
-    ds_put_format (&title, " %s", command_name);
-  ds_put_cstr (&title, ".  ");
-  if (t->title != NULL)
-    ds_put_cstr (&title, t->title);
-
-  text.font = OUTP_PROPORTIONAL;
-  text.justification = OUTP_LEFT;
-  text.string = ds_ss (&title);
-  text.h = r->driver->width;
-  text.v = r->driver->font_height;
-  text.x = 0;
-  text.y = r->driver->cp_y;
-  r->driver->class->text_draw (r->driver, &text);
-
-  ds_destroy (&title);
-}
-
-static int render_strip (const struct tab_rendering *,
-                         int x, int y, int r, int c1, int c2, int r1, int r2);
-
-static void
-add_range (int ranges[][2], int *np, int start, int end)
-{
-  int n = *np;
-  if (n == 0 || start > ranges[n - 1][1])
-    {
-      ranges[n][0] = start;
-      ranges[n][1] = end;
-      ++*np;
-    }
-  else
-    ranges[n - 1][1] = end;
-}
-
-/* Draws table region (C0,R0)-(C1,R1), plus headers, at the
-   current position on the current output device.  */
-static void
-tabi_render (void *r_, int c0, int r0, int c1, int r1)
+static int
+table_unshared_get_rule (const struct table *tiu_,
+                              enum table_axis axis, int x, int y)
 {
-  const struct tab_rendering *r = r_;
-  const struct tab_table *t = r->table;
-  int rows[3][2], cols[3][2];
-  int n_row_ranges, n_col_ranges;
-  int y, i;
-
-  /* Rows to render, counting horizontal rules as rows.  */
-  n_row_ranges = 0;
-  add_range (rows, &n_row_ranges, 0, tab_t (t) * 2 + 1);
-  add_range (rows, &n_row_ranges, r0 * 2 + 1, r1 * 2);
-  add_range (rows, &n_row_ranges, (tab_nr (t) - tab_b (t)) * 2,
-             tab_nr (t) * 2 + 1);
-
-  /* Columns to render, counting vertical rules as columns. */
-  n_col_ranges = 0;
-  add_range (cols, &n_col_ranges, 0, r->l * 2 + 1);
-  add_range (cols, &n_col_ranges, c0 * 2 + 1, c1 * 2);
-  add_range (cols, &n_col_ranges, (tab_nc (t) - r->r) * 2, tab_nc (t) * 2 + 1);
-
-  y = r->driver->cp_y;
-  if (!(t->flags & SOMF_NO_TITLE))
-    y += r->driver->font_height;
-  for (i = 0; i < n_row_ranges; i++)
-    {
-      int row;
-
-      for (row = rows[i][0]; row < rows[i][1]; row++)
-        {
-          int x, j;
-
-          x = r->driver->cp_x;
-          for (j = 0; j < n_col_ranges; j++)
-            x = render_strip (r, x, y, row,
-                              cols[j][0], cols[j][1],
-                              rows[i][0], rows[i][1]);
-
-          y += (row & 1) ? r->h[row / 2] : r->hrh[row / 2];
-        }
-    }
+  struct table_unshared *tiu = table_unshared_cast (tiu_);
+  return table_get_rule (tiu->subtable, axis, x, y);
 }
 
-const struct som_table_class tab_table_class =
+static const struct table_class table_unshared_class =
   {
-    tabi_count,
-    tabi_columns,
-    tabi_headers,
-    tabi_flags,
-
-    tabi_render_init,
-    tabi_render_free,
-
-    tabi_area,
-    tabi_cumulate,
-    tabi_title,
-    tabi_render,
+    table_unshared_destroy,
+    table_unshared_get_cell,
+    table_unshared_get_rule,
+    NULL,                       /* paste */
+    NULL,                       /* select */
   };
 \f
-static enum outp_justification
-translate_justification (unsigned int opt)
-{
-  switch (opt & TAB_ALIGN_MASK)
-    {
-    case TAB_RIGHT:
-      return OUTP_RIGHT;
-    case TAB_LEFT:
-      return OUTP_LEFT;
-    case TAB_CENTER:
-      return OUTP_CENTER;
-    default:
-      NOT_REACHED ();
-    }
-}
-
-/* Returns the line style to use for drawing a rule of the given
-   TYPE. */
-static enum outp_line_style
-rule_to_draw_type (unsigned char type)
-{
-  switch (type)
-    {
-    case TAL_0:
-    case TAL_GAP:
-      return OUTP_L_NONE;
-    case TAL_1:
-      return OUTP_L_SINGLE;
-    case TAL_2:
-      return OUTP_L_DOUBLE;
-    default:
-      NOT_REACHED ();
-    }
-}
+struct table_string
+  {
+    struct table table;
+    char *string;
+    unsigned int options;
+  };
 
-/* Returns the horizontal rule at the given column and row. */
-static int
-get_hrule (const struct tab_table *t, int col, int row)
-{
-  return t->rh[col + row * t->cf];
-}
+static const struct table_class table_string_class;
 
-/* Returns the vertical rule at the given column and row. */
-static int
-get_vrule (const struct tab_table *t, int col, int row)
+/* Returns a table that contains a single cell, whose contents are S with
+   options OPTIONS (a combination of TAB_* values).  */
+struct table *
+table_from_string (unsigned int options, const char *s)
 {
-  return t->rv[col + row * (t->cf + 1)];
+  struct table_string *ts = xmalloc (sizeof *ts);
+  table_init (&ts->table, &table_string_class);
+  ts->table.n[TABLE_HORZ] = ts->table.n[TABLE_VERT] = 1;
+  ts->string = xstrdup (s);
+  ts->options = options;
+  return &ts->table;
 }
 
-/* Renders the horizontal rule at the given column and row
-   at (X,Y) on the page. */
-static void
-render_horz_rule (const struct tab_rendering *r,
-                  int x, int y, int col, int row)
+static struct table_string *
+table_string_cast (const struct table *table)
 {
-  enum outp_line_style style;
-  style = rule_to_draw_type (get_hrule (r->table, col, row));
-  if (style != OUTP_L_NONE)
-    r->driver->class->line (r->driver, x, y, x + r->w[col], y + r->hrh[row],
-                            OUTP_L_NONE, style, OUTP_L_NONE, style);
+  assert (table->class == &table_string_class);
+  return UP_CAST (table, struct table_string, table);
 }
 
-/* Renders the vertical rule at the given column and row
-   at (X,Y) on the page. */
 static void
-render_vert_rule (const struct tab_rendering *r,
-                  int x, int y, int col, int row)
+table_string_destroy (struct table *ts_)
 {
-  enum outp_line_style style;
-  style = rule_to_draw_type (get_vrule (r->table, col, row));
-  if (style != OUTP_L_NONE)
-    r->driver->class->line (r->driver, x, y, x + r->wrv[col], y + r->h[row],
-                            style, OUTP_L_NONE, style, OUTP_L_NONE);
+  struct table_string *ts = table_string_cast (ts_);
+  free (ts->string);
+  free (ts);
 }
 
-/* Renders the rule intersection at the given column and row
-   at (X,Y) on the page. */
 static void
-render_rule_intersection (const struct tab_rendering *r,
-                          int x, int y, int col, int row)
+table_string_get_cell (const struct table *ts_, int x UNUSED, int y UNUSED,
+                       struct table_cell *cell)
 {
-  const struct tab_table *t = r->table;
-
-  /* Bounds of intersection. */
-  int x0 = x;
-  int y0 = y;
-  int x1 = x + r->wrv[col];
-  int y1 = y + r->hrh[row];
-
-  /* Lines on each side of intersection. */
-  int top = row > 0 ? get_vrule (t, col, row - 1) : TAL_0;
-  int left = col > 0 ? get_hrule (t, col - 1, row) : TAL_0;
-  int bottom = row < tab_nr (t) ? get_vrule (t, col, row) : TAL_0;
-  int right = col < tab_nc (t) ? get_hrule (t, col, row) : TAL_0;
-
-  /* Output style for each line. */
-  enum outp_line_style o_top = rule_to_draw_type (top);
-  enum outp_line_style o_left = rule_to_draw_type (left);
-  enum outp_line_style o_bottom = rule_to_draw_type (bottom);
-  enum outp_line_style o_right = rule_to_draw_type (right);
-
-  if (o_top != OUTP_L_NONE || o_left != OUTP_L_NONE
-      || o_bottom != OUTP_L_NONE || o_right != OUTP_L_NONE)
-    r->driver->class->line (r->driver, x0, y0, x1, y1,
-                            o_top, o_left, o_bottom, o_right);
+  struct table_string *ts = table_string_cast (ts_);
+  cell->d[TABLE_HORZ][0] = 0;
+  cell->d[TABLE_HORZ][1] = 1;
+  cell->d[TABLE_VERT][0] = 0;
+  cell->d[TABLE_VERT][1] = 1;
+  cell->contents = ts->string;
+  cell->options = ts->options;
+  cell->destructor = NULL;
 }
 
-/* Returns the width of columns C1...C2 exclusive,
-   including interior but not exterior rules. */
-static int
-strip_width (const struct tab_rendering *r, int c1, int c2)
-{
-  int width = 0;
-  int c;
-
-  for (c = c1; c < c2; c++)
-    width += r->w[c] + r->wrv[c + 1];
-  if (c1 < c2)
-    width -= r->wrv[c2];
-  return width;
-}
 
-/* Returns the height of rows R1...R2 exclusive,
-   including interior but not exterior rules. */
 static int
-strip_height (const struct tab_rendering *r, int r1, int r2)
+table_string_get_rule (const struct table *ts UNUSED,
+                       enum table_axis axis UNUSED, int x UNUSED, int y UNUSED)
 {
-  int height = 0;
-  int row;
-
-  for (row = r1; row < r2; row++)
-    height += r->h[row] + r->hrh[row + 1];
-  if (r1 < r2)
-    height -= r->hrh[r2];
-  return height;
-}
-
-/* Renders the cell at the given column and row at (X,Y) on the
-   page.  Also renders joined cells that extend as far to the
-   right as C1 and as far down as R1. */
-static void
-render_cell (const struct tab_rendering *r,
-             int x, int y, int col, int row, int c1, int r1)
-{
-  const struct tab_table *t = r->table;
-  const int index = col + (row * t->cf);
-  unsigned char type = t->ct[index];
-  struct substring *content = &t->cc[index];
-
-  if (!(type & TAB_JOIN))
-    {
-      if (!(type & TAB_EMPTY))
-        {
-          struct outp_text text;
-          text.font = options_to_font (type);
-          text.justification = translate_justification (type);
-          text.string = *content;
-          text.h = r->w[col];
-          text.v = r->h[row];
-          text.x = x;
-          text.y = y;
-          r->driver->class->text_draw (r->driver, &text);
-        }
-    }
-  else
-    {
-      struct tab_joined_cell *j
-        = (struct tab_joined_cell *) ss_data (*content);
-
-      if (j->x1 == col && j->y1 == row)
-        {
-          struct outp_text text;
-          text.font = options_to_font (type);
-          text.justification = translate_justification (type);
-          text.string = j->contents;
-          text.x = x;
-          text.y = y;
-          text.h = strip_width (r, j->x1, MIN (j->x2, c1));
-          text.v = strip_height (r, j->y1, MIN (j->y2, r1));
-          r->driver->class->text_draw (r->driver, &text);
-        }
-    }
+  return TAL_0;
 }
 
-/* Render contiguous strip consisting of columns C0...C1, exclusive,
-   on row ROW, at (X,Y).  Returns X position after rendering.
-   Also renders joined cells that extend beyond that strip,
-   cropping them to lie within rendering region (C0,R0)-(C1,R1).
-   C0 and C1 count vertical rules as columns.
-   ROW counts horizontal rules as rows, but R0 and R1 do not. */
-static int
-render_strip (const struct tab_rendering *r,
-              int x, int y, int row, int c0, int c1, int r0 UNUSED, int r1)
-{
-  int col;
-
-  for (col = c0; col < c1; col++)
-    if (col & 1)
-      {
-        if (row & 1)
-          render_cell (r, x, y, col / 2, row / 2, c1 / 2, r1);
-        else
-          render_horz_rule (r, x, y, col / 2, row / 2);
-        x += r->w[col / 2];
-      }
-    else
-      {
-        if (row & 1)
-          render_vert_rule (r, x, y, col / 2, row / 2);
-        else
-          render_rule_intersection (r, x, y, col / 2, row / 2);
-        x += r->wrv[col / 2];
-      }
-
-  return x;
-}
+static const struct table_class table_string_class =
+  {
+    table_string_destroy,
+    table_string_get_cell,
+    table_string_get_rule,
+    NULL,                       /* paste */
+    NULL,                       /* select */
+  };
index f4fbc786f0440ea39da04ebfa0c54070ccec7cd1..06427ac32b75bac0d91670840c3a10c4307b8484 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000, 2009 Free Software Foundation, Inc.
+   Copyright (C) 1997, 1998, 1999, 2000, 2009 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
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
 
-#if !tab_h
-#define tab_h 1
+#ifndef OUTPUT_TABLE_H
+#define OUTPUT_TABLE_H 1
 
-#include <limits.h>
-#include <libpspp/str.h>
+/* Tables.
 
-/* Cell options. */
-enum
-  {
-    TAB_NONE = 0,
+.  A table is a rectangular grid of cells.  Cells can be joined to form larger
+   cells.  Rows and columns can be separated by rules of various types.  Rows
+   at the top and bottom of a table and columns at the left and right edges of
+   a table can be designated as headers, which means that if the table must be
+   broken across more than one page, those rows or columns are repeated on each
+   page.
 
-    TAB_ALIGN_MASK = 03,       /* Alignment mask. */
-    TAB_RIGHT = 00,            /* Right justify. */
-    TAB_LEFT = 01,             /* Left justify. */
-    TAB_CENTER = 02,           /* Center. */
+   Every table is an instance of a particular table class that is responsible
+   for keeping track of cell data.  By far the most common table class is
+   struct tab_table (see output/tab.h).  This header also declares some other
+   kinds of table classes, near the end of the file.
 
-    /* Cell types. */
-    TAB_JOIN = 004,            /* Joined cell. */
-    TAB_EMPTY = 010,           /* Empty cell. */
+   A table is not itself an output_item, and thus a table cannot by itself be
+   used for output, but they can be embedded inside struct table_item (see
+   table-item.h) for that purpose. */
 
-    /* Flags. */
-    TAB_EMPH = 020,             /* Emphasize cell contents. */
-    TAB_FIX = 040,              /* Use fixed font. */
-  };
+#include <stdbool.h>
+#include <stddef.h>
 
-/* Line styles. */
-enum
-  {
-    TAL_0 = 0,                 /* No line. */
-    TAL_1 = 1,                 /* Single line. */
-    TAL_2 = 2,                 /* Double line. */
-    TAL_GAP = 3,                /* Spacing but no line. */
-    TAL_COUNT,                 /* Number of line styles. */
-  };
+struct casereader;
+struct fmt_spec;
+struct variable;
 
-/* Column styles.  Must correspond to SOM_COL_*. */
+/* Properties of a table cell. */
 enum
   {
-    TAB_COL_NONE,                      /* No columns. */
-    TAB_COL_DOWN                       /* Columns down first. */
-  };
+    TAB_NONE = 0,
 
-/* Joined cell. */
-struct tab_joined_cell
-  {
-    int x1, y1;
-    int x2, y2;
-    struct substring contents;
-  };
+    /* Alignment of cell contents. */
+    TAB_RIGHT      = 0 << 0,    /* Right justify. */
+    TAB_LEFT       = 1 << 0,    /* Left justify. */
+    TAB_CENTER     = 2 << 0,    /* Centered. */
+    TAB_ALIGNMENT  = 3 << 0,   /* Alignment mask. */
 
-struct outp_driver;
-struct tab_table;
-struct tab_rendering;
+    /* These flags may be combined with any alignment. */
+    TAB_EMPH       = 1 << 2,    /* Emphasize cell contents. */
+    TAB_FIX        = 1 << 3,    /* Use fixed font. */
 
-typedef void tab_dim_func (struct tab_rendering *, void *aux);
-typedef void tab_dim_free_func (void *aux);
+    /* Bits with values (1 << TAB_FIRST_AVAILABLE) and higher are
+       not used, so they are available for subclasses to use as
+       they wish. */
+    TAB_FIRST_AVAILABLE = 4
+  };
 
-/* A table. */
-struct tab_table
+/* Styles for the rules around table cells. */
+enum
   {
-    struct pool *container;
-    int ref_cnt;                /* Reference count. */
-
-    /* Contents. */
-    int col_style;             /* Columns: One of TAB_COL_*. */
-    char *title;                /* Table title. */
-    unsigned flags;            /* SOMF_*. */
-    int nc, nr;                        /* Number of columns, rows. */
-    int cf;                    /* Column factor for indexing purposes. */
-    int l, r, t, b;            /* Number of header rows on each side. */
-    struct substring *cc;      /* Cell contents; substring *[nr][nc]. */
-    unsigned char *ct;         /* Cell types; unsigned char[nr][nc]. */
-    unsigned char *rh;         /* Horiz rules; unsigned char[nr+1][nc]. */
-    unsigned char *rv;         /* Vert rules; unsigned char[nr][nc+1]. */
-
-    /* Calculating row and column dimensions. */
-    tab_dim_func *dim;         /* Calculates cell widths and heights. */
-    tab_dim_free_func *dim_free; /* Frees space allocated for dim function. */
-    void *dim_aux;              /* Auxiliary data for dim function. */
-
-    /* Editing info. */
-    int col_ofs, row_ofs;      /* X and Y offsets. */
+    TAL_0,                     /* No line. */
+    TAL_GAP,                    /* Spacing but no line. */
+    TAL_1,                     /* Single line. */
+    TAL_2,                     /* Double line. */
+    N_LINES
   };
 
-/* Number of rows or columns in TABLE. */
-static inline int tab_nr (const struct tab_table *table) { return table->nr; }
-static inline int tab_nc (const struct tab_table *table) { return table->nc; }
+/* Given line styles A and B (each one of the TAL_* enumeration constants
+   above), returns a line style that "combines" them, that is, that gives a
+   reasonable line style choice for a rule for different reasons should have
+   both styles A and B.
 
-/* Number of left/right/top/bottom header columns/rows in TABLE. */
-static inline int tab_l (const struct tab_table *table) { return table->l; }
-static inline int tab_r (const struct tab_table *table) { return table->r; }
-static inline int tab_t (const struct tab_table *table) { return table->t; }
-static inline int tab_b (const struct tab_table *table) { return table->b; }
+   Used especially for pasting tables together (see table_paste()). */
+static inline int table_rule_combine (int a, int b)
+{
+  return a > b ? a : b;
+}
 
-struct tab_rendering
-  {
-    const struct tab_table *table;
-    struct outp_driver *driver;
-
-    int *w;                    /* Column widths; [nc]. */
-    int *h;                    /* Row heights; [nr]. */
-    int *hrh;                  /* Heights of horizontal rules; [nr+1]. */
-    int *wrv;                  /* Widths of vertical rules; [nc+1]. */
-
-    /* These fields would be redundant with those in struct tab_table, except
-       that a table will be rendered with fewer header rows or columns than
-       requested when we are pressed for space. */
-    int l, r, t, b;            /* Number of header rows/columns. */
-    int wl, wr, ht, hb;                /* Width/height of header rows/columns. */
-  };
+/* A table axis.
 
-/* Tables. */
-struct tab_table *tab_create (int nc, int nr);
-void tab_destroy (struct tab_table *);
-void tab_ref (struct tab_table *);
-void tab_resize (struct tab_table *, int nc, int nr);
-void tab_realloc (struct tab_table *, int nc, int nr);
-void tab_headers (struct tab_table *, int l, int r, int t, int b);
-void tab_columns (struct tab_table *, int style);
-void tab_title (struct tab_table *, const char *, ...)
-     PRINTF_FORMAT (2, 3);
-void tab_flags (struct tab_table *, unsigned);
-void tab_submit (struct tab_table *);
-
-/* Dimensioning. */
-tab_dim_func tab_natural_dimensions;
-int tab_natural_width (const struct tab_rendering *, int c);
-int tab_natural_height (const struct tab_rendering *, int r);
-void tab_dim (struct tab_table *,
-              tab_dim_func *, tab_dim_free_func *, void *aux);
-
-/* Rules. */
-void tab_hline (struct tab_table *, int style, int x1, int x2, int y);
-void tab_vline (struct tab_table *, int style, int x, int y1, int y2);
-void tab_box (struct tab_table *, int f_h, int f_v, int i_h, int i_v,
-             int x1, int y1, int x2, int y2);
-
-/* Text options, passed in the `opt' argument. */
-enum
+   Many table-related declarations use 2-element arrays in place of "x" and "y"
+   variables.  This reduces code duplication significantly, because much table
+   code has treat rows and columns the same way.
+
+   A lot of code that uses these enumerations assumes that the two values are 0
+   and 1, so don't change them to other values. */
+enum table_axis
   {
-    TAT_NONE = 0,              /* No options. */
-    TAT_TITLE = 0x0200 | TAB_EMPH, /* Title attributes. */
-    TAT_NOWRAP = 0x0800         /* No text wrap (tab_output_text() only). */
+    TABLE_HORZ,
+    TABLE_VERT,
+    TABLE_N_AXES
   };
 
-/* Cells. */
-struct fmt_spec;
-struct dictionary;
-union value;
-void tab_value (struct tab_table *, int c, int r, unsigned char opt,
-               const union value *, const struct dictionary *dict,
-               const struct fmt_spec *);
-
-void tab_fixed (struct tab_table *, int c, int r, unsigned char opt,
-               double v, int w, int d);
-
-void tab_double (struct tab_table *, int c, int r, unsigned char opt,
-               double v, const struct fmt_spec *);
+/* A table. */
+struct table
+  {
+    const struct table_class *class;
 
-void tab_text (struct tab_table *, int c, int r, unsigned opt, const char *);
-void tab_text_format (struct tab_table *, int c, int r, unsigned opt,
-                      const char *, ...)
-     PRINTF_FORMAT (5, 6);
+    /* Table size.
 
-void tab_joint_text (struct tab_table *, int x1, int y1, int x2, int y2,
-                    unsigned opt, const char *);
-void tab_joint_text_format (struct tab_table *, int x1, int y1, int x2, int y2,
-                            unsigned opt, const char *, ...)
-     PRINTF_FORMAT (7, 8);
+       n[TABLE_HORZ]: Number of columns.
+       n[TABLE_VERT]: Number of rows. */
+    int n[TABLE_N_AXES];
 
-/* Editing. */
-void tab_offset (struct tab_table *, int col, int row);
-void tab_next_row (struct tab_table *);
+    /* Table headers.
 
-/* Current row/column offset. */
-#define tab_row(TABLE) ((TABLE)->row_ofs)
-#define tab_col(TABLE) ((TABLE)->col_ofs)
+       Rows at the top and bottom of a table and columns at the left and right
+       edges of a table can be designated as headers.  If the table must be
+       broken across more than one page for output, headers rows and columns
+       are repeated on each page.
 
-/* Simple output. */
-void tab_output_text (int options, const char *string);
-void tab_output_text_format (int options, const char *, ...)
-     PRINTF_FORMAT (2, 3);
+       h[TABLE_HORZ][0]: Left header columns.
+       h[TABLE_HORZ][1]: Right header columns.
+       h[TABLE_VERT][0]: Top header rows.
+       h[TABLE_VERT][1]: Bottom header rows. */
+    int h[TABLE_N_AXES][2];
 
-#endif /* tab_h */
+    /* Reference count.  A table may be shared between multiple owners,
+       indicated by a reference count greater than 1.  When this is the case,
+       the table must not be modified. */
+    int ref_cnt;
+  };
 
+/* Reference counting. */
+struct table *table_ref (const struct table *);
+void table_unref (struct table *);
+bool table_is_shared (const struct table *);
+struct table *table_unshare (struct table *);
+
+/* Returns the number of columns or rows, respectively, in T. */
+static inline int table_nc (const struct table *t)
+        { return t->n[TABLE_HORZ]; }
+static inline int table_nr (const struct table *t)
+        { return t->n[TABLE_VERT]; }
+
+/* Returns the number of left, right, top, or bottom headers, respectively, in
+   T.  */
+static inline int table_hl (const struct table *t)
+        { return t->h[TABLE_HORZ][0]; }
+static inline int table_hr (const struct table *t)
+        { return t->h[TABLE_HORZ][1]; }
+static inline int table_ht (const struct table *t)
+        { return t->h[TABLE_VERT][0]; }
+static inline int table_hb (const struct table *t)
+        { return t->h[TABLE_VERT][1]; }
+
+/* Set headers. */
+void table_set_hl (struct table *, int hl);
+void table_set_hr (struct table *, int hr);
+void table_set_ht (struct table *, int ht);
+void table_set_hb (struct table *, int hb);
+\f
+/* Table classes. */
+
+/* Simple kinds of tables. */
+struct table *table_from_string (unsigned int options, const char *);
+struct table *table_from_variables (unsigned int options,
+                                    struct variable **, size_t);
+struct table *table_from_casereader (const struct casereader *,
+                                     size_t column,
+                                     const char *heading,
+                                     const struct fmt_spec *);
+
+/* Combining tables. */
+struct table *table_paste (struct table *, struct table *,
+                           enum table_axis orientation);
+struct table *table_hpaste (struct table *left, struct table *right);
+struct table *table_vpaste (struct table *top, struct table *bottom);
+
+/* Taking subsets of tables. */
+struct table *table_select (struct table *, int rect[TABLE_N_AXES][2]);
+struct table *table_select_slice (struct table *, enum table_axis,
+                                  int z0, int z1, bool add_headers);
+struct table *table_select_columns (struct table *,
+                                    int x0, int x1, bool add_headers);
+struct table *table_select_rows (struct table *,
+                                 int y0, int y1, bool add_headers);
+
+/* Miscellaneous table operations. */
+struct table *table_transpose (struct table *);
+
+#endif /* output/table.h */
diff --git a/src/output/text-item.c b/src/output/text-item.c
new file mode 100644 (file)
index 0000000..16588ae
--- /dev/null
@@ -0,0 +1,102 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2009 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 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.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+
+#include <output/text-item.h>
+
+#include <assert.h>
+#include <stdarg.h>
+#include <stdlib.h>
+
+#include <libpspp/cast.h>
+#include <output/driver.h>
+#include <output/output-item-provider.h>
+
+#include "xalloc.h"
+#include "xvasprintf.h"
+
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
+
+static struct text_item *
+allocate_text_item (enum text_item_type type, char *text)
+{
+  struct text_item *item = xmalloc (sizeof *item);
+  output_item_init (&item->output_item, &text_item_class);
+  item->text = text;
+  item->type = type;
+  return item;
+}
+
+/* Creates and returns a new text item containing a copy of TEXT and the
+   specified TYPE.  The caller retains ownership of TEXT. */
+struct text_item *
+text_item_create (enum text_item_type type, const char *text)
+{
+  return allocate_text_item (type, xstrdup (text));
+}
+
+/* Creates and returns a new text item containing a copy of FORMAT, which is
+   formatted as if by printf(), and the specified TYPE.  The caller retains
+   ownership of FORMAT. */
+struct text_item *
+text_item_create_format (enum text_item_type type, const char *format, ...)
+{
+  struct text_item *item;
+  va_list args;
+
+  va_start (args, format);
+  item = allocate_text_item (type, xvasprintf (format, args));
+  va_end (args);
+
+  return item;
+}
+
+/* Returns ITEM's type. */
+enum text_item_type
+text_item_get_type (const struct text_item *item)
+{
+  return item->type;
+}
+
+/* Returns ITEM's text, which the caller may not modify or free. */
+const char *
+text_item_get_text (const struct text_item *item)
+{
+  return item->text;
+}
+
+/* Submits ITEM to the configured output drivers, and transfers ownership to
+   the output subsystem. */
+void
+text_item_submit (struct text_item *item)
+{
+  output_submit (&item->output_item);
+}
+\f
+static void
+text_item_destroy (struct output_item *output_item)
+{
+  struct text_item *item = to_text_item (output_item);
+  free (item->text);
+  free (item);
+}
+
+const struct output_item_class text_item_class =
+  {
+    text_item_destroy,
+  };
diff --git a/src/output/text-item.h b/src/output/text-item.h
new file mode 100644 (file)
index 0000000..b62e7dc
--- /dev/null
@@ -0,0 +1,133 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2009 Free Sonftware 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 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.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef OUTPUT_TEXT_ITEM_H
+#define OUTPUT_TEXT_ITEM_H 1
+
+/* Text items.
+
+   A text item is a subclass of an output item (see
+   output/output-item.h).
+
+   A text item is just a text string. */
+
+#include <stdbool.h>
+#include <libpspp/compiler.h>
+#include <output/output-item.h>
+
+enum text_item_type
+  {
+    /* Each PSPP command is bracketed between a pair of these text items.  The
+       text item's string is the full name of the command.  The syntax text
+       items associated with the command, as well as all output produced
+       directly by the command, are contained within the pair.  There is no
+       nesting. */
+    TEXT_ITEM_COMMAND_OPEN,     /* Command starting. */
+    TEXT_ITEM_COMMAND_CLOSE,    /* Command completed. */
+
+    /* Headings. */
+    TEXT_ITEM_TITLE,            /* TITLE command. */
+    TEXT_ITEM_SUBTITLE,         /* SUBTITLE command. */
+    TEXT_ITEM_SUBHEAD,          /* Heading within a command's output.*/
+
+    /* Syntax. */
+    TEXT_ITEM_SYNTAX,           /* A single line of PSPP syntax. */
+    TEXT_ITEM_COMMENT,          /* COMMENT command. */
+    TEXT_ITEM_ECHO,             /* ECHO command. */
+
+    /* Ordinary text. */
+    TEXT_ITEM_PARAGRAPH,        /* Normal paragraph of text. */
+    TEXT_ITEM_MONOSPACE,        /* Paragraph of monospaced text. */
+
+    /* Spacing.  Some output drivers that are not based on lines and pages
+       (e.g. CSV, HTML) may ignore these. */
+    TEXT_ITEM_BLANK_LINE,       /* Blank line. */
+    TEXT_ITEM_EJECT_PAGE        /* Eject page. */
+  };
+
+/* A text item. */
+struct text_item
+  {
+    struct output_item output_item;
+    char *text;                 /* The content. */
+    enum text_item_type type;   /* Type. */
+  };
+
+struct text_item *text_item_create (enum text_item_type, const char *text);
+struct text_item *text_item_create_format (enum text_item_type,
+                                           const char *format, ...)
+  PRINTF_FORMAT (2, 3);
+
+enum text_item_type text_item_get_type (const struct text_item *);
+const char *text_item_get_text (const struct text_item *);
+\f
+/* This boilerplate for text_item, a subclass of output_item, was
+   autogenerated by mk-class-boilerplate. */
+
+#include <assert.h>
+#include <libpspp/cast.h>
+
+extern const struct output_item_class text_item_class;
+
+/* Returns true if SUPER is a text_item, otherwise false. */
+static inline bool
+is_text_item (const struct output_item *super)
+{
+  return super->class == &text_item_class;
+}
+
+/* Returns SUPER converted to text_item.  SUPER must be a text_item, as
+   reported by is_text_item. */
+static inline struct text_item *
+to_text_item (const struct output_item *super)
+{
+  assert (is_text_item (super));
+  return UP_CAST (super, struct text_item, output_item);
+}
+
+/* Returns INSTANCE converted to output_item. */
+static inline struct output_item *
+text_item_super (const struct text_item *instance)
+{
+  return CONST_CAST (struct output_item *, &instance->output_item);
+}
+
+/* Increments INSTANCE's reference count and returns INSTANCE. */
+static inline struct text_item *
+text_item_ref (const struct text_item *instance)
+{
+  return to_text_item (output_item_ref (&instance->output_item));
+}
+
+/* Decrements INSTANCE's reference count, then destroys INSTANCE if
+   the reference count is now zero. */
+static inline void
+text_item_unref (struct text_item *instance)
+{
+  output_item_unref (&instance->output_item);
+}
+
+/* Returns true if INSTANCE's reference count is greater than 1,
+   false otherwise. */
+static inline bool
+text_item_is_shared (const struct text_item *instance)
+{
+  return output_item_is_shared (&instance->output_item);
+}
+
+void text_item_submit (struct text_item *);
+\f
+#endif /* output/text-item.h */
index aa2fae51ce127b95cfe6c607da50c2da95fe406f..6ccb5a534deca402d8e879dd2f3f6376a13e30e3 100644 (file)
@@ -23,7 +23,7 @@
 #include <libpspp/getl.h>
 #include <language/lexer/lexer.h>
 #include <language/command.h>
-#include <output/manager.h>
+#include <output/driver.h>
 #include "psppire-output-window.h"
 
 extern struct dataset *the_dataset;
@@ -102,7 +102,7 @@ execute_syntax (struct getl_interface *sss)
   if (!lazy_casereader_destroy (reader, lazy_serial))
     psppire_data_store_set_reader (the_data_store, reader);
 
-  som_flush ();
+  output_flush ();
 
   return retval;
 }
index 35216d4e518502d809fd8e02965d82945fd53f86..af47d3be51079a1e63c344824e74df20e4d5d322 100644 (file)
 #include <gtk/gtkbox.h>
 #include "helper.h"
 
+#include <libpspp/cast.h>
 #include <libpspp/message.h>
 #include <output/cairo.h>
-#include <output/manager.h>
-#include <output/output.h>
-#include <output/table.h>
+#include <output/driver-provider.h>
+#include <output/tab.h>
 #include <stdlib.h>
 
 #include "about.h"
@@ -118,147 +118,105 @@ psppire_output_window_base_finalize (PsppireOutputWindowClass *class,
 \f
 /* Output driver class. */
 
-static PsppireOutputWindow *the_output_viewer = NULL;
+struct psppire_output_driver
+  {
+    struct output_driver driver;
+    PsppireOutputWindow *viewer;
+    struct xr_driver *xr;
+  };
+
+static struct output_driver_class psppire_output_class;
+
+static struct psppire_output_driver *
+psppire_output_cast (struct output_driver *driver)
+{
+  assert (driver->class == &psppire_output_class);
+  return UP_CAST (driver, struct psppire_output_driver, driver);
+}
 
 static gboolean
 expose_event_callback (GtkWidget *widget, GdkEventExpose *event, gpointer data)
 {
-  struct som_entity *entity = g_object_get_data (G_OBJECT (widget), "entity");
-  GdkWindow *window = widget->window;
-  cairo_t *cairo = gdk_cairo_create (GDK_DRAWABLE (window));
-  struct outp_driver *driver = xr_create_driver (cairo); /* XXX can fail */
-  struct tab_table *t = entity->ext;
-  void *rendering;
-
-  rendering = entity->class->render_init (entity, driver, tab_l (t),
-                                          tab_r (t), tab_t (t), tab_b (t));
-
-  entity->class->title (rendering, 0, 0,
-                        entity->table_num, entity->subtable_num,
-                        entity->command_name);
-  entity->class->render (rendering, tab_l (t), tab_t (t),
-                         tab_nc (t) - tab_r (t),
-                         tab_nr (t) - tab_b (t));
-
-  entity->class->render_free (rendering);
-  driver->class->close_driver (driver);
-  outp_free_driver (driver);
+  struct xr_rendering *r = g_object_get_data (G_OBJECT (widget), "rendering");
+  cairo_t *cr;
+
+  cr = gdk_cairo_create (widget->window);
+  xr_rendering_draw (r, cr);
+  cairo_destroy (cr);
+
   return TRUE;
 }
 
 static void
-psppire_output_submit (struct outp_driver *this, struct som_entity *entity)
+psppire_output_submit (struct output_driver *this,
+                       const struct output_item *item)
 {
-  if (the_output_viewer == NULL)
-    {
-      the_output_viewer = PSPPIRE_OUTPUT_WINDOW (psppire_output_window_new ());
-      gtk_widget_show_all (GTK_WIDGET (the_output_viewer));
-    }
+  struct psppire_output_driver *pod = psppire_output_cast (this);
+  GtkWidget *drawing_area;
+  struct xr_rendering *r;
+  cairo_t *cr;
+  int tw, th;
 
-  if (entity->type == SOM_TABLE)
+  if (pod->viewer == NULL)
     {
-      GdkWindow *window = GTK_WIDGET (the_output_viewer)->window;
-      cairo_t *cairo = gdk_cairo_create (GDK_DRAWABLE (window));
-      struct outp_driver *driver = xr_create_driver (cairo); /* XXX can fail */
-      struct tab_table *t = entity->ext;
-      GtkTreeStore *store;
-      GtkTreeIter item;
-      GtkTreePath *path;
-      GtkWidget *drawing_area;
-      void *rendering;
-      struct string title;
-      int tw, th;
-
-      tab_ref (t);
-      rendering = entity->class->render_init (entity, driver, tab_l (t),
-                                              tab_r (t), tab_t (t), tab_b (t));
-      entity->class->area (rendering, &tw, &th);
-
-      drawing_area = gtk_drawing_area_new ();
-      gtk_widget_modify_bg (GTK_WIDGET (drawing_area), GTK_STATE_NORMAL,
-                            &gtk_widget_get_style (drawing_area)->base[GTK_STATE_NORMAL]);
-      g_object_set_data (G_OBJECT (drawing_area),
-                         "entity", som_entity_clone (entity));
-      gtk_widget_set_size_request (drawing_area, tw / 1024, th / 1024);
-      gtk_layout_put (the_output_viewer->output, drawing_area,
-                      0, the_output_viewer->y);
-      gtk_widget_show (drawing_area);
-      g_signal_connect (G_OBJECT (drawing_area), "expose_event",
-                        G_CALLBACK (expose_event_callback), NULL);
-
-      entity->class->render_free (rendering);
-      driver->class->close_driver (driver);
-      outp_free_driver (driver);
-
-      store = GTK_TREE_STORE (gtk_tree_view_get_model (
-                                the_output_viewer->overview));
-
-      ds_init_empty (&title);
-      if (entity->table_num != the_output_viewer->last_table_num)
-        {
-          gtk_tree_store_append (store, &item, NULL);
-
-          ds_put_format (&title, "%d %s",
-                         entity->table_num, entity->command_name);
-          gtk_tree_store_set (store, &item,
-                              COL_TITLE, ds_cstr (&title),
-                              COL_Y, the_output_viewer->y,
-                              -1);
-
-          /* XXX shouldn't save a GtkTreeIter */
-          the_output_viewer->last_table_num = entity->table_num;
-          the_output_viewer->last_top_level = item;
-        }
-
-      gtk_tree_store_append (store, &item,
-                             &the_output_viewer->last_top_level);
-      ds_clear (&title);
-      ds_put_format (&title, "%d.%d %s",
-                     entity->table_num, entity->subtable_num,
-                     t->title ? t->title : entity->command_name);
-      gtk_tree_store_set (store, &item,
-                          COL_TITLE, ds_cstr (&title),
-                          COL_Y, the_output_viewer->y,
-                          -1);
-      ds_destroy (&title);
-
-      path = gtk_tree_model_get_path (GTK_TREE_MODEL (store),
-                                      &the_output_viewer->last_top_level);
-      gtk_tree_view_expand_row (the_output_viewer->overview, path, TRUE);
-      gtk_tree_path_free (path);
-
-      if (tw / 1024 > the_output_viewer->max_width)
-        the_output_viewer->max_width = tw / 1024;
-      the_output_viewer->y += th / 1024;
-
-      gtk_layout_set_size (the_output_viewer->output,
-                           the_output_viewer->max_width, the_output_viewer->y);
+      pod->viewer = PSPPIRE_OUTPUT_WINDOW (psppire_output_window_new ());
+      gtk_widget_show_all (GTK_WIDGET (pod->viewer));
+      pod->viewer->driver = pod;
     }
 
-  gtk_window_set_urgency_hint (GTK_WINDOW (the_output_viewer), TRUE);
+  cr = gdk_cairo_create (GTK_WIDGET (pod->viewer)->window);
+  if (pod->xr == NULL)
+    pod->xr = xr_create_driver (cr);
+
+  r = xr_rendering_create (pod->xr, item, cr);
+  if (r == NULL)
+    goto done;
+
+  xr_rendering_measure (r, &tw, &th);
+
+  drawing_area = gtk_drawing_area_new ();
+  gtk_widget_modify_bg (
+    GTK_WIDGET (drawing_area), GTK_STATE_NORMAL,
+    &gtk_widget_get_style (drawing_area)->base[GTK_STATE_NORMAL]);
+  g_object_set_data (G_OBJECT (drawing_area), "rendering", r);
+  gtk_widget_set_size_request (drawing_area, tw, th);
+  gtk_layout_put (pod->viewer->output, drawing_area, 0, pod->viewer->y);
+  gtk_widget_show (drawing_area);
+  g_signal_connect (G_OBJECT (drawing_area), "expose_event",
+                     G_CALLBACK (expose_event_callback), NULL);
+
+  if (pod->viewer->max_width < tw)
+    pod->viewer->max_width = tw;
+  pod->viewer->y += th;
+
+  gtk_layout_set_size (pod->viewer->output,
+                       pod->viewer->max_width, pod->viewer->y);
+
+  gtk_window_set_urgency_hint (GTK_WINDOW (pod->viewer), TRUE);
+
+done:
+  cairo_destroy (cr);
 }
 
-static struct outp_class psppire_output_class =
+static struct output_driver_class psppire_output_class =
   {
     "PSPPIRE",                  /* name */
-    true,                       /* special */
-    NULL,                       /* open_driver */
-    NULL,                       /* close_driver */
-    NULL,                       /* open_page */
-    NULL,                       /* close_page */
-    NULL,                       /* flush */
-    NULL,                       /* output_chart */
+    NULL,                       /* create */
+    NULL,                       /* destroy */
     psppire_output_submit,      /* submit */
-    NULL,                       /* line */
-    NULL,                       /* text_metrics */
-    NULL,                       /* text_draw */
+    NULL,                       /* flush */
   };
 
 void
 psppire_output_window_setup (void)
 {
-  outp_register_driver (outp_allocate_driver (&psppire_output_class,
-                                              "PSPPIRE", 0));
+  struct psppire_output_driver *pod;
+  struct output_driver *d;
+
+  pod = xzalloc (sizeof *pod);
+  d = &pod->driver;
+  output_driver_init (d, &psppire_output_class, "PSPPIRE", 0);
+  output_driver_register (d);
 }
 \f
 int viewer_length = 16;
@@ -273,7 +231,7 @@ on_delete (GtkWidget *w, GdkEvent *event, gpointer user_data)
 
   gtk_widget_destroy (GTK_WIDGET (ow));
 
-  the_output_viewer = NULL;
+  ow->driver->viewer = NULL;
 
   return FALSE;
 }
index 606a950475fc6ecc1d4833fb7eea46e9faf8ccdf..16752b14f4d79164c000948de2fbb4f49d928c51 100644 (file)
@@ -52,6 +52,7 @@ struct _PsppireOutputWindow
   PsppireWindow parent;
 
   /* <private> */
+  struct psppire_output_driver *driver;
   GtkLayout *output;
   int max_width;
   int y;
index 3f6d49a65db2aec01fdef067d502954707484d8a..ddf915f272eddf4dcc427b24faa8da1a8cd590fc 100644 (file)
@@ -41,7 +41,7 @@
 #include <libpspp/getl.h>
 #include <language/lexer/lexer.h>
 #include <libpspp/version.h>
-#include <output/output.h>
+#include <output/driver.h>
 #include <output/journal.h>
 #include <language/syntax-string-source.h>
 
@@ -98,7 +98,6 @@ initialize (struct command_line_processor *clp, int argc, char **argv)
 
   gsl_set_error_handler_off ();
   fn_init ();
-  outp_init ();
   settings_init (&viewer_width, &viewer_length);
   fh_init ();
   the_source_stream =
@@ -154,7 +153,7 @@ de_initialize (void)
   destroy_source_stream (the_source_stream);
   message_dialog_done ();
   settings_done ();
-  outp_done ();
+  output_close ();
   i18n_done ();
 }
 
index 43ae5c629ac1098998c480a09175af95bd457326..61a108e0eec65cb4686fca5d8f1932024dfd771a 100644 (file)
@@ -20,7 +20,6 @@
 #include <stdbool.h>
 #include <xalloc.h>
 #include <string.h>
-#include <output/output.h>
 #include <data/file-name.h>
 #include <libpspp/getl.h>
 #include <language/syntax-file.h>
index 7ad162fca6d40b746a0155d891d8e08784e2e4df..1366bd7aad307090b7118c1473771b37a5905f7c 100644 (file)
@@ -46,7 +46,7 @@
 #include <libpspp/message.h>
 #include <libpspp/version.h>
 #include <math/random.h>
-#include <output/output.h>
+#include <output/driver.h>
 #include <ui/debugger.h>
 #include <ui/terminal/msg-ui.h>
 #include <ui/terminal/read-line.h>
@@ -96,7 +96,6 @@ main (int argc, char **argv)
   fpu_init ();
   gsl_set_error_handler_off ();
 
-  outp_init ();
   fn_init ();
   fh_init ();
   the_source_stream =
@@ -129,20 +128,6 @@ main (int argc, char **argv)
 
   msg_ui_init (the_source_stream);
 
-  if (!settings_get_testing_mode ())
-    {
-      outp_read_devices ();
-    }
-  else
-    {
-      outp_configure_driver_line
-       (
-        ss_cstr ("raw-ascii:ascii:listing:width=9999 length=9999 "
-                 "output-file=\"pspp.list\" emphasis=none "
-                 "headers=off paginate=off squeeze=on "
-                 "top-margin=0 bottom-margin=0"));
-    }
-
   the_lexer = lex_create (the_source_stream);
 
   for (;;)
@@ -219,7 +204,7 @@ clean_up (void)
       destroy_source_stream (the_source_stream);
       prompt_done ();
       readln_uninitialize ();
-      outp_done ();
+      output_close ();
       msg_ui_done ();
       i18n_done ();
     }
index 682d753d8fbd36200f68e33677c239e5ef5b6c17..1a5e2764be12579b308c29b60e7cfbe47cac518a 100644 (file)
 
 #include "msg-ui.h"
 
-#include "unilbrk.h"
-#include "localcharset.h"
-
-#include <libpspp/msg-locator.h>
-#include <libpspp/getl.h>
 #include <data/settings.h>
+#include <libpspp/getl.h>
 #include <libpspp/message.h>
+#include <libpspp/msg-locator.h>
 #include <libpspp/str.h>
 #include <output/journal.h>
-#include <output/output.h>
-#include <output/table.h>
+#include <output/driver.h>
+#include <output/tab.h>
+
 #include <errno.h>
+#include <limits.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
 
+#include "unilbrk.h"
+#include "localcharset.h"
+
 #include "gettext.h"
 #define _(msgid) gettext (msgid)
 #define N_(msgid) msgid
@@ -187,9 +189,9 @@ handle_msg (const struct msg *m)
       /* Disable screen output devices, because the error should
          already have been reported to the screen with the
          dump_message call above. */
-      outp_enable_device (false, OUTP_DEV_SCREEN);
+      output_set_type_enabled (false, OUTPUT_DEVICE_SCREEN);
       tab_output_text (TAB_LEFT, ds_cstr (&string));
-      outp_enable_device (true, OUTP_DEV_SCREEN);
+      output_set_type_enabled (true, OUTPUT_DEVICE_SCREEN);
     }
 
   ds_destroy (&string);
index f85e4e76b17a2aef2e9ca0c8aac27d8296341fcd..86c75dea4beee72e2e15b4d102391cea53acc87d 100644 (file)
@@ -37,7 +37,7 @@
 #include <libpspp/version.h>
 #include <language/prompt.h>
 #include <output/journal.h>
-#include <output/manager.h>
+#include <output/driver.h>
 #include <ui/terminal/terminal.h>
 
 #include "xalloc.h"
@@ -156,7 +156,7 @@ readln_read (struct string *line, enum prompt_style style)
   welcome ();
 
   if (style == PROMPT_FIRST)
-    som_flush ();
+    output_flush ();
 
 #if HAVE_READLINE
   rl_attempted_completion_function = (style == PROMPT_FIRST
index d2ddc6eeffa2b611f9d524d5b13fb8453d4dcb9e..955a6b0e9c7fda53e2daa1a9b67e9b528f2189bd 100644 (file)
    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
 
 #include <config.h>
+
+#include "terminal-opts.h"
+
 #include <argp.h>
 #include <stdbool.h>
 #include <xalloc.h>
 #include <stdlib.h>
+
 #include <data/settings.h>
-#include <output/output.h>
-#include "msg-ui.h"
-#include <ui/command-line.h>
-#include <libpspp/verbose-msg.h>
-#include <libpspp/llx.h>
 #include <data/file-name.h>
-#include "terminal-opts.h"
-#include <libpspp/getl.h>
 #include <language/syntax-file.h>
-#include "read-line.h"
+#include <libpspp/getl.h>
+#include <libpspp/llx.h>
+#include <libpspp/string-map.h>
+#include <libpspp/string-set.h>
+#include <libpspp/verbose-msg.h>
+#include <output/driver.h>
+#include <ui/command-line.h>
+#include <ui/terminal/msg-ui.h>
+#include <ui/terminal/read-line.h>
+
+#include "gl/error.h"
 
 #include "gettext.h"
 #define _(msgid) gettext (msgid)
 #define N_(msgid) msgid
 
-
 static const struct argp_option test_options [] =
   {
     {"verbose", 'v', 0, 0, N_("Increase diagnostic verbosity level"), 0},
@@ -85,8 +91,11 @@ parse_io_opts (int key, char *arg, struct argp_state *state)
   struct source_init
   {
     struct llx_list file_list;
-    bool cleared_device_defaults;
     bool interactive;
+
+    /* Output devices. */
+    struct string_map macros;
+    struct string_set drivers;
   };
 
   struct fn_element {
@@ -105,10 +114,15 @@ parse_io_opts (int key, char *arg, struct argp_state *state)
     case ARGP_KEY_INIT:
       state->hook = sip = xzalloc (sizeof (struct source_init));
       llx_init (&sip->file_list);
+      string_map_init (&sip->macros);
+      string_set_init (&sip->drivers);
       break;
     case ARGP_KEY_ARG:
       if (strchr (arg, '='))
-       outp_configure_macro (arg);
+        {
+          if (!output_define_macro (arg, &sip->macros))
+            error (0, 0, _("\"%s\" is not a valid macro definition"), arg);
+        }
       else
        {
          llx_push_tail (&sip->file_list, arg, &llx_malloc_mgr);
@@ -136,9 +150,16 @@ parse_io_opts (int key, char *arg, struct argp_state *state)
                              ERRMODE_CONTINUE
                              );
 
-         if (!sip->cleared_device_defaults)
-           outp_configure_add ("interactive");
+          string_set_insert (&sip->drivers, "interactive");
        }
+
+      if (!settings_get_testing_mode ())
+        output_read_configuration (&sip->macros, &sip->drivers);
+      else
+        output_configure_driver ("csv:csv::");
+
+      string_map_destroy (&sip->macros);
+      string_set_destroy (&sip->drivers);
       }
       break;
     case ARGP_KEY_FINI:
@@ -151,15 +172,10 @@ parse_io_opts (int key, char *arg, struct argp_state *state)
       sip->interactive = true;
       break;
     case 'l':
-      outp_list_classes ();
+      output_list_classes ();
       break;
     case 'o':
-      if (! sip->cleared_device_defaults)
-       {
-         outp_configure_clear ();
-         sip->cleared_device_defaults = true;
-       }
-      outp_configure_add (arg);
+      string_set_insert (&sip->drivers, arg);
       break;
     default:
       return ARGP_ERR_UNKNOWN;
index 282522db0342d8750454b3dc162493b5fc709cc8..8174e64562800e55a632a7c48f789d18013920b1 100644 (file)
@@ -1,2 +1,3 @@
 Makefile
 Makefile.in
+/testsuite
diff --git a/tests/atlocal.in b/tests/atlocal.in
new file mode 100644 (file)
index 0000000..97e1ae3
--- /dev/null
@@ -0,0 +1,4 @@
+# -*- shell-script -*-
+PERL='@PERL@'
+CHARSETALIASDIR="$abs_top_builddir/gl"
+export CHARSETALIASDIR
index 76ee1f2356b507fed397aec65d6e58f315065a1c..96a2dae17bbe946e3b525c3091ce6c6eeca334e9 100644 (file)
@@ -204,7 +204,8 @@ check_PROGRAMS += \
        $(nodist_TESTS) \
        tests/data/datasheet-test \
        tests/formats/inexactify \
-       tests/libpspp/sparse-xarray-test
+       tests/libpspp/sparse-xarray-test \
+       tests/output/render-test
 
 tests_data_datasheet_test_SOURCES = \
        tests/data/datasheet-test.c
@@ -352,6 +353,15 @@ tests_dissect_sysfile_SOURCES = \
 tests_dissect_sysfile_LDADD = gl/libgl.la $(LIBINTL) 
 tests_dissect_sysfile_CPPFLAGS = $(AM_CPPFLAGS) -DINSTALLDIR=\"$(bindir)\"
 
+check_PROGRAMS += tests/output/render-test
+tests_output_render_test_SOURCES = tests/output/render-test.c
+tests_output_render_test_LDADD = \
+       src/libpspp.la \
+       src/libpspp-core.la \
+       $(CAIRO_LIBS) \
+       $(LIBICONV) \
+       $(LIBINTL)
+
 EXTRA_DIST += \
        $(dist_TESTS) \
         tests/Book1.gnm.unzipped \
@@ -359,9 +369,6 @@ EXTRA_DIST += \
        tests/no_case_size.sav \
        tests/coverage.sh tests/test_template \
        tests/v13.sav tests/v14.sav \
-       tests/bugs/computebug.stat tests/bugs/computebug.out \
-       tests/bugs/recode-copy-bug-1.stat tests/bugs/recode-copy-bug-2.stat \
-       tests/bugs/recode-copy-bug-1.out tests/bugs/recode-copy-bug-2.out \
        tests/expressions/randist/beta.out \
        tests/expressions/randist/cauchy.out \
        tests/expressions/randist/chisq.out \
@@ -401,3 +408,43 @@ check-for-export-var-val:
 DIST_HOOKS += check-for-export-var-val
 
 EXTRA_DIST += tests/OChangeLog
+\f
+# Autotest testsuite
+
+EXTRA_DIST += \
+       $(TESTSUITE_AT) \
+       $(TESTSUITE) \
+       tests/atlocal.in \
+       $(srcdir)/package.m4 \
+       $(TESTSUITE)
+TESTSUITE_AT = \
+       tests/testsuite.at \
+       tests/output/render.at
+TESTSUITE = $(srcdir)/tests/testsuite
+DISTCLEANFILES += tests/atconfig tests/atlocal $(TESTSUITE)
+
+CHECK_LOCAL += tests_check
+tests_check: tests/atconfig tests/atlocal $(TESTSUITE)
+       $(SHELL) '$(TESTSUITE)' -C tests AUTOTEST_PATH=tests/output $(TESTSUITEFLAGS)
+
+CLEAN_LOCAL += tests_clean
+tests_clean:
+       test ! -f '$(TESTSUITE)' || $(SHELL) '$(TESTSUITE)' -C tests --clean
+
+AUTOM4TE = $(SHELL) $(srcdir)/missing --run autom4te
+AUTOTEST = $(AUTOM4TE) --language=autotest
+$(TESTSUITE): package.m4 $(TESTSUITE_AT)
+       $(AUTOTEST) -I '$(srcdir)' -o $@.tmp $@.at
+       mv $@.tmp $@
+
+# The `:;' works around a Bash 3.2 bug when the output is not writeable.
+$(srcdir)/package.m4: $(top_srcdir)/configure.ac
+       :;{ \
+         echo '# Signature of the current package.' && \
+         echo 'm4_define([AT_PACKAGE_NAME],      [@PACKAGE_NAME@])' && \
+         echo 'm4_define([AT_PACKAGE_TARNAME],   [@PACKAGE_TARNAME@])' && \
+         echo 'm4_define([AT_PACKAGE_VERSION],   [@PACKAGE_VERSION@])' && \
+         echo 'm4_define([AT_PACKAGE_STRING],    [@PACKAGE_STRING@])' && \
+         echo 'm4_define([AT_PACKAGE_BUGREPORT], [@PACKAGE_BUGREPORT@])' && \
+         echo 'm4_define([AT_PACKAGE_URL],       [@PACKAGE_URL@])'; \
+       } >'$(srcdir)/package.m4'
index 0b006780f319de5ac506e877543c2e25be7d94da..0bb8962f38105c0f839892514a22eedea6403e83 100755 (executable)
@@ -77,19 +77,17 @@ if [ $? -ne 0 ] ; then no_result ; fi
 $SUPERVISOR $PSPP --testing-mode $TESTFILE
 if [ $? -ne 0 ] ; then no_result ; fi
 
-perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
-diff -b  -w $TEMPDIR/pspp.list - << EOF
-1.1 DATA LIST.  Reading free-form data from INLINE.
-+--------+------+
-|Variable|Format|
-#========#======#
-|X       |F8.2  |
-|Y       |A25   |
-+--------+------+
-        X                         Y
---------- -------------------------
-    87.34 bar                       
-    87.50 foo                       
+activity="compare output"
+diff $TEMPDIR/pspp.csv - << EOF | cat -E
+Table: Reading free-form data from INLINE.
+Variable,Format
+X,F8.2
+Y,A25
+
+Table: Data List
+X,Y
+87.34,bar                      
+87.50,foo                      
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
index b0baf4f09cd4272c5e77c773032cd852f75d75c4..88c42c3c1af607e51829d01ab38497200e200319 100755 (executable)
@@ -79,19 +79,16 @@ if [ $? -ne 0 ] ; then fail ; fi
 
 
 activity="compare output"
-perl -pi -e 's/^\s*$//g' pspp.list
-diff -b pspp.list - << EOF
-1.1 DATA LIST.  Reading free-form data from INLINE.
-+----------------+------+
-|    Variable    |Format|
-#================#======#
-|longVariablename|F8.0  |
-|x               |F8.0  |
-+----------------+------+
-longVariablename        x
----------------- -------- 
-           99.00     2.00
-           97.00     4.00
+diff pspp.csv - << EOF
+Table: Reading free-form data from INLINE.
+Variable,Format
+longVariablename,F8.0
+x,F8.0
+
+Table: Data List
+longVariablename,x
+99.00,2.00
+97.00,4.00
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
diff --git a/tests/bugs/computebug.out b/tests/bugs/computebug.out
deleted file mode 100644 (file)
index 61fc7d0..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-1.1 DATA LIST.  Reading free-form data from INLINE.
-+--------+------+
-|Variable|Format|
-#========#======#
-|A       |A161  |
-|B       |A3    |
-+--------+------+
-                                                                                                                                                                A   B
------------------------------------------------------------------------------------------------------------------------------------------------------------------ ---
-ABC                                                                                                                                                               def 
-GHI                                                                                                                                                               jkl 
index dfa5990a759cc416209552e6ba60b4f1eb6379cd..4f8a5731fb726a3f6545cad5e8d5c90d7cf02976 100755 (executable)
@@ -53,24 +53,42 @@ pass()
 }
 
 mkdir -p $TEMPDIR
-
-activity="copy file"
-cp $top_srcdir/tests/bugs/computebug.stat $TEMPDIR
-if [ $? -ne 0 ] ; then no_result ; fi
-
-activity="chdir"
 cd $TEMPDIR
 if [ $? -ne 0 ] ; then no_result ; fi
 
+activity="create input"
+cat > $TESTFILE <<EOF
+DATA LIST LIST
+ /A (A161)
+ B (A3).
+
+BEGIN DATA
+abc   def
+ghi   jkl
+END DATA.
+
+COMPUTE A=upcase(A).
+EXECUTE.
+LIST.
+EOF
 
 activity="run program"
-$SUPERVISOR $PSPP --testing-mode $TEMPDIR/computebug.stat
+$SUPERVISOR $PSPP --testing-mode $TESTFILE
 if [ $? -ne 0 ] ; then no_result ; fi
 
 
 activity="compare output"
-perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list $top_srcdir/tests/bugs/computebug.out
-diff -b -w $TEMPDIR/pspp.list $top_srcdir/tests/bugs/computebug.out
+diff -c $TEMPDIR/pspp.csv - <<EOF
+Table: Reading free-form data from INLINE.
+Variable,Format
+A,A161
+B,A3
+
+Table: Data List
+A,B
+ABC                                                                                                                                                              ,def
+GHI                                                                                                                                                              ,jkl
+EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
 pass;
diff --git a/tests/bugs/computebug.stat b/tests/bugs/computebug.stat
deleted file mode 100644 (file)
index e308301..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-DATA LIST LIST
- /A (A161)
- B (A3).
-
-BEGIN DATA
-abc   def
-ghi   jkl
-END DATA.
-
-COMPUTE A=upcase(A).
-EXECUTE.
-LIST.
-
-
index 4454319c709420bb47181ef010698499a87bdb20..f7d19d834cf7bd87d78a3399e2c4ceeceb8cabc8 100755 (executable)
@@ -74,42 +74,31 @@ $SUPERVISOR $PSPP --testing-mode $TESTFILE
 if [ $? -ne 0 ] ; then no_result ; fi
 
 
-perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
-diff -b  -w $TEMPDIR/pspp.list - << EOF
-1.1 DATA LIST.  Reading free-form data from INLINE.
-+--------+------+
-|Variable|Format|
-#========#======#
-|A       |F8.0  |
-|B       |F8.0  |
-|X       |F8.0  |
-|Y       |F8.0  |
-+--------+------+
-2.1 CROSSTABS.  Summary.
-#===============#=====================================================#
-#               #                        Cases                        #
-#               #-----------------+-----------------+-----------------#
-#               #      Valid      |     Missing     |      Total      #
-#               #--------+--------+--------+--------+--------+--------#
-#               #       N| Percent|       N| Percent|       N| Percent#
-#---------------#--------+--------+--------+--------+--------+--------#
-#X * Y          #       1|  100.0%|       0|    0.0%|       1|  100.0%#
-#===============#========#========#========#========#========#========#
-2.2 CROSSTABS.  X * Y [count].
-#===============#==============================================================#========#
-#               #                               Y                              |        #
-#               #--------+--------+--------+--------+--------+--------+--------+        #
-#              X#    1.00|    2.00|    3.00|    4.00|    5.00|    6.00|    7.00|  Total #
-#---------------#--------+--------+--------+--------+--------+--------+--------+--------#
-#           1.00#      .0|      .0|      .0|      .0|      .0|      .0|      .0|      .0#
-#           2.00#      .0|      .0|      .0|      .0|      .0|      .0|      .0|      .0#
-#           3.00#      .0|      .0|      .0|      .0|      .0|      .0|      .0|      .0#
-#           4.00#      .0|      .0|      .0|      .0|     1.0|      .0|      .0|     1.0#
-#           5.00#      .0|      .0|      .0|      .0|      .0|      .0|      .0|      .0#
-#           6.00#      .0|      .0|      .0|      .0|      .0|      .0|      .0|      .0#
-#           7.00#      .0|      .0|      .0|      .0|      .0|      .0|      .0|      .0#
-#Total          #      .0|      .0|      .0|      .0|     1.0|      .0|      .0|     1.0#
-#===============#========#========#========#========#========#========#========#========#
+diff -c $TEMPDIR/pspp.csv - << EOF
+Table: Reading free-form data from INLINE.
+Variable,Format
+A,F8.0
+B,F8.0
+X,F8.0
+Y,F8.0
+
+Table: Summary.
+,Cases,,,,,
+,Valid,,Missing,,Total,
+,N,Percent,N,Percent,N,Percent
+X * Y,1,100.0%,0,0.0%,1,100.0%
+
+Table: X * Y [count].
+,Y,,,,,,,
+X,1.00,2.00,3.00,4.00,5.00,6.00,7.00,Total
+1.00,.0,.0,.0,.0,.0,.0,.0,.0
+2.00,.0,.0,.0,.0,.0,.0,.0,.0
+3.00,.0,.0,.0,.0,.0,.0,.0,.0
+4.00,.0,.0,.0,.0,1.0,.0,.0,1.0
+5.00,.0,.0,.0,.0,.0,.0,.0,.0
+6.00,.0,.0,.0,.0,.0,.0,.0,.0
+7.00,.0,.0,.0,.0,.0,.0,.0,.0
+Total,.0,.0,.0,.0,1.0,.0,.0,1.0
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
index b0db128a1c54e01947a704fdacb7bb6f24d59d19..910ec0bbc89a334fe161b200657187df60d0460b 100755 (executable)
@@ -78,40 +78,29 @@ if [ $? -ne 0 ] ; then no_result ; fi
 $SUPERVISOR $PSPP --testing-mode $TESTFILE > /dev/null
 if [ $? -ne 0 ] ; then no_result ; fi
 
-perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
-diff -b  -w $TEMPDIR/pspp.list - << EOF
-1.1 DATA LIST.  Reading free-form data from INLINE.
-+--------+------+
-|Variable|Format|
-#========#======#
-|x       |F8.0  |
-|y       |A18   |
-+--------+------+
-$TEMPDIR/crosstabs-crash2.sh.sps:4: warning: BEGIN DATA: Missing value(s) for all variables from x onward.  These will be filled with the system-missing value or blanks, as appropriate.
-$TEMPDIR/crosstabs-crash2.sh.sps:6: warning: BEGIN DATA: Missing value(s) for all variables from x onward.  These will be filled with the system-missing value or blanks, as appropriate.
-2.1 CROSSTABS.  Summary.
-#===============#=====================================================#
-#               #                        Cases                        #
-#               #-----------------+-----------------+-----------------#
-#               #      Valid      |     Missing     |      Total      #
-#               #--------+--------+--------+--------+--------+--------#
-#               #       N| Percent|       N| Percent|       N| Percent#
-#---------------#--------+--------+--------+--------+--------+--------#
-#x * y          #       4|   66.7%|       2|   33.3%|       6|  100.0%#
-#===============#========#========#========#========#========#========#
-2.2 CROSSTABS.  x * y [count].
-#===============#===================================#========#
-#               #                 y                 |        #
-#               #--------+--------+--------+--------+        #
-#              x#     one|   three|     two|    zero|  Total #
-#               #unity   |lots    |duality |none    |        #
-#               #        |        |        |        |        #
-#---------------#--------+--------+--------+--------+--------#
-#           1.00#     1.0|      .0|      .0|     1.0|     2.0#
-#           2.00#      .0|      .0|     1.0|      .0|     1.0#
-#           3.00#      .0|     1.0|      .0|      .0|     1.0#
-#Total          #     1.0|     1.0|     1.0|     1.0|     4.0#
-#===============#========#========#========#========#========#
+diff -c $TEMPDIR/pspp.csv - << EOF
+Table: Reading free-form data from INLINE.
+Variable,Format
+x,F8.0
+y,A18
+
+"$TEMPDIR/crosstabs-crash2.sh.sps:4: warning: BEGIN DATA: Missing value(s) for all variables from x onward.  These will be filled with the system-missing value or blanks, as appropriate."
+
+"$TEMPDIR/crosstabs-crash2.sh.sps:6: warning: BEGIN DATA: Missing value(s) for all variables from x onward.  These will be filled with the system-missing value or blanks, as appropriate."
+
+Table: Summary.
+,Cases,,,,,
+,Valid,,Missing,,Total,
+,N,Percent,N,Percent,N,Percent
+x * y,4,66.7%,2,33.3%,6,100.0%
+
+Table: x * y [count].
+,y,,,,
+x,one unity         ,three lots        ,two duality       ,zero none         ,Total
+1.00,1.0,.0,.0,1.0,2.0
+2.00,.0,.0,1.0,.0,1.0
+3.00,.0,1.0,.0,.0,1.0
+Total,1.0,1.0,1.0,1.0,4.0
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
index 3e6629985913517b6645e951c927e87968b99c20..964cf23c061d4bc29a30e897c67f5c75929cf8ee 100755 (executable)
@@ -80,52 +80,32 @@ $SUPERVISOR $PSPP --testing-mode $TESTFILE
 if [ $? -ne 0 ] ; then no_result ; fi
 
 
-perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
-diff -b  -w $TEMPDIR/pspp.list - << EOF
-1.1 DATA LIST.  Reading free-form data from INLINE.
-+--------+------+
-|Variable|Format|
-#========#======#
-|x       |F8.0  |
-|y       |F8.0  |
-+--------+------+
-2.1 CROSSTABS.  Summary.
-#===============#=====================================================#
-#               #                        Cases                        #
-#               #-----------------+-----------------+-----------------#
-#               #      Valid      |     Missing     |      Total      #
-#               #--------+--------+--------+--------+--------+--------#
-#               #       N| Percent|       N| Percent|       N| Percent#
-#---------------#--------+--------+--------+--------+--------+--------#
-#x * y          #       4|  100.0%|       0|    0.0%|       4|  100.0%#
-#===============#========#========#========#========#========#========#
-2.2 CROSSTABS.  x * y [count].
-#===============#=================#========#
-#               #        y        |        #
-#               #--------+--------+        #
-#              x#    1.00|    2.00|  Total #
-#---------------#--------+--------+--------#
-#           2.00#      .0|     1.0|     1.0#
-#           3.00#     1.0|      .0|     1.0#
-#           4.00#     1.0|     1.0|     2.0#
-#Total          #     2.0|     2.0|     4.0#
-#===============#========#========#========#
-2.3 CROSSTABS.  Chi-square tests.
-#===============#========#========#========#
-#Statistic      #   Value|      df|  Asymp.#
-#               #        |        |    Sig.#
-#               #        |        |(2-sided#
-#               #        |        |       )#
-#---------------#--------+--------+--------#
-#Pearson        #    2.00|       2|     .37#
-#Chi-Square     #        |        |        #
-#Likelihood     #    2.77|       2|     .25#
-#Ratio          #        |        |        #
-#Linear-by-Linea#     .27|       1|     .60#
-#r Association  #        |        |        #
-#N of Valid     #       4|        |        #
-#Cases          #        |        |        #
-#===============#========#========#========#
+diff -c $TEMPDIR/pspp.csv - << EOF
+Table: Reading free-form data from INLINE.
+Variable,Format
+x,F8.0
+y,F8.0
+
+Table: Summary.
+,Cases,,,,,
+,Valid,,Missing,,Total,
+,N,Percent,N,Percent,N,Percent
+x * y,4,100.0%,0,0.0%,4,100.0%
+
+Table: x * y [count].
+,y,,
+x,1.00,2.00,Total
+2.00,.0,1.0,1.0
+3.00,1.0,.0,1.0
+4.00,1.0,1.0,2.0
+Total,2.0,2.0,4.0
+
+Table: Chi-square tests.
+Statistic,Value,df,Asymp. Sig. (2-sided)
+Pearson Chi-Square,2.00,2,.37
+Likelihood Ratio,2.77,2,.25
+Linear-by-Linear Association,.27,1,.60
+N of Valid Cases,4,,
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
index 2cd6c14a6a00d930722149a77d5fc5da0d86fe7e..c95cca1be8d6367d49bc66a4a79b2dcd5e8ebe0f 100755 (executable)
@@ -82,36 +82,24 @@ $SUPERVISOR $PSPP --testing-mode -o raw-ascii $TESTFILE
 if [ $? -ne 0 ] ; then no_result ; fi
 
 activity="compare results"
-perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
-diff -b  $TEMPDIR/pspp.list - <<EOF
-1.1 DATA LIST.  Reading free-form data from INLINE.
-+--------+------+
-|Variable|Format|
-#========#======#
-|x       |F8.0  |
-|y       |F8.0  |
-+--------+------+
-2.1 EXAMINE.  Case Processing Summary
-#=#===============================#
-# #             Cases             #
-# #----------+----------+---------#
-# #   Valid  |  Missing |  Total  #
-# #-+--------+-+--------+-+-------#
-# #N| Percent|N| Percent|N|Percent#
-#=#=#========#=#========#=#=======#
-#x#6|85.7143%|1|14.2857%|7|   100%#
-#=#=#========#=#========#=#=======#
-2.2 EXAMINE.  Case Processing Summary
-#==========#===============================#
-#          #             Cases             #
-#          #----------+----------+---------#
-#          #   Valid  |  Missing |  Total  #
-#          #-+--------+-+--------+-+-------#
-#      y   #N| Percent|N| Percent|N|Percent#
-#==========#=#========#=#========#=#=======#
-#x     1.00#4|    100%|0|      0%|4|   100%#
-#      2.00#2|66.6667%|1|33.3333%|3|   100%#
-#==========#=#========#=#========#=#=======#
+diff -c $TEMPDIR/pspp.csv - <<EOF
+Table: Reading free-form data from INLINE.
+Variable,Format
+x,F8.0
+y,F8.0
+
+Table: Case Processing Summary
+,Cases,,,,,
+,Valid,,Missing,,Total,
+,N,Percent,N,Percent,N,Percent
+x,6,85.7143%,1,14.2857%,7,100%
+
+Table: Case Processing Summary
+,,Cases,,,,,
+,,Valid,,Missing,,Total,
+,y,N,Percent,N,Percent,N,Percent
+x,1.00,4,100%,0,0%,4,100%
+,2.00,2,66.6667%,1,33.3333%,3,100%
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
index 2cc6cfc5a3e26ec7f0b8d24a629fb75728e4f2d5..86b39c661df71f8d6e4f51091b922b836255c262 100755 (executable)
@@ -94,28 +94,28 @@ if [ $? -ne 0 ] ; then no_result ; fi
 
 
 activity="compare output"
-perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
-diff -b  -w $TEMPDIR/pspp.list - << EOF
-LOCATION   EDITOR    SHELL     FREQ
- -------- -------- -------- --------
-     1.00     1.00     1.00     2.00 
-     1.00     1.00     2.00    30.00 
-     1.00     2.00     1.00     8.00 
-     1.00     2.00     2.00    20.00 
-     2.00     1.00     1.00     2.00 
-     2.00     1.00     2.00    22.00 
-     2.00     2.00     1.00     1.00 
-     2.00     2.00     2.00     3.00 
-LOCATION   EDITOR    SHELL     FREQ
- -------- -------- -------- --------
-     1.00     1.00     1.00     2.00 
-     1.00     1.00     2.00    30.00 
-     1.00     2.00     1.00     8.00 
-     1.00     2.00     2.00    20.00 
-     2.00     1.00     1.00     2.00 
-     2.00     1.00     2.00    22.00 
-     2.00     2.00     1.00     1.00 
-     2.00     2.00     2.00     3.00 
+diff -c $TEMPDIR/pspp.csv - << EOF
+Table: Data List
+LOCATION,EDITOR,SHELL,FREQ
+1.00,1.00,1.00,2.00
+1.00,1.00,2.00,30.00
+1.00,2.00,1.00,8.00
+1.00,2.00,2.00,20.00
+2.00,1.00,1.00,2.00
+2.00,1.00,2.00,22.00
+2.00,2.00,1.00,1.00
+2.00,2.00,2.00,3.00
+
+Table: Data List
+LOCATION,EDITOR,SHELL,FREQ
+1.00,1.00,1.00,2.00
+1.00,1.00,2.00,30.00
+1.00,2.00,1.00,8.00
+1.00,2.00,2.00,20.00
+2.00,1.00,1.00,2.00
+2.00,1.00,2.00,22.00
+2.00,2.00,1.00,1.00
+2.00,2.00,2.00,3.00
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
index f64674ded977c02b35b080e4ac00d8bb68247c74..408255d2a29f92f319d1146b22fd77a27e8027fa 100755 (executable)
@@ -78,14 +78,14 @@ EOF
 
 
     activity="compare output ($mode)"
-    perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
-    diff -u -b  -w $TEMPDIR/pspp.list - << EOF
- a  b  c  d  e  f  g  h  i  j  k  l  m  n  o  p  q  r  s  t  u  v  w  x  y  z
--- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
- 1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 
- x  y  z  a  b  c  d  e  f  g  h  i  j  k  l  m  n  o  p  q  r  s  t  u  v  w
--- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
-24 25 26  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 
+    diff -c $TEMPDIR/pspp.csv - << EOF
+Table: Data List
+a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z
+1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26
+
+Table: Data List
+x,y,z,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w
+24,25,26,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23
 EOF
     if [ $? -ne 0 ] ; then fail ; fi
 done
index dd95c2c0772f9a404fb2e30a7dfd2d4bac5d8602..6eb5b43994c24f3d38558b822c8063e14e19d43b 100755 (executable)
@@ -82,20 +82,17 @@ $SUPERVISOR $PSPP --testing-mode $TESTFILE
 if [ $? -ne 0 ] ; then no_result ; fi
 
 
-perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
-diff -b  -w $TEMPDIR/pspp.list - << EOF
-1.1 DATA LIST.  Reading free-form data from INLINE.
-+--------+------+
-|Variable|Format|
-#========#======#
-|w       |F8.0  |
-|x       |F8.0  |
-|y       |F8.0  |
-+--------+------+
-       x        y        j
--------- -------- --------
-    5.00     6.00    55.00 
-    2.00     3.00    55.00 
+diff -c $TEMPDIR/pspp.csv - << EOF
+Table: Reading free-form data from INLINE.
+Variable,Format
+w,F8.0
+x,F8.0
+y,F8.0
+
+Table: Data List
+x,y,j
+5.00,6.00,55.00
+2.00,3.00,55.00
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
index 5272063094049e75f1513c7c8b2538bafb020f93..11e274067e8131ec1d165ac8afec77142ada6b05 100755 (executable)
@@ -82,21 +82,15 @@ $SUPERVISOR $PSPP --testing-mode $TESTFILE
 if [ $? -ne 0 ] ; then no_result ; fi
 
 
-perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
-diff -b  -w $TEMPDIR/pspp.list - << EOF
-1.1 DATA LIST.  Reading free-form data from INLINE.
-+--------+------+
-|Variable|Format|
-#========#======#
-|ID      |F8.0  |
-|ABC     |F8.0  |
-+--------+------+
-2.1 DESCRIPTIVES.  Valid cases = 6; cases with missing value(s) = 0.
-+--------#-+----+-------+-------+-------+
-|Variable#N|Mean|Std Dev|Minimum|Maximum|
-#========#=#====#=======#=======#=======#
-|ABC     #6|3.00|    .84|   2.00|   4.00|
-+--------#-+----+-------+-------+-------+
+diff -c $TEMPDIR/pspp.csv - << EOF
+Table: Reading free-form data from INLINE.
+Variable,Format
+ID,F8.0
+ABC,F8.0
+
+Table: Valid cases = 6; cases with missing value(s) = 0.
+Variable,N,Mean,Std Dev,Minimum,Maximum
+ABC,6,3.00,.84,2.00,4.00
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
index d9f3e59cb5ce81d0ad33c56ba807be4e3117b5b5..a255b2fcda10aac263741dbafebf0cf197022baf 100755 (executable)
@@ -166,29 +166,30 @@ $SUPERVISOR $PSPP --testing-mode $TESTFILE -e /dev/null
 if [ $? -ne 0 ] ; then no_result ; fi
 
 activity="compare output"
-perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
-diff -b pspp.list - << EOF
-       X        Y
--------- --------
-    1.00     2.00
-    2.00     3.00
-    3.00     4.00
-    4.00     5.00
-    5.00     6.00
-X        Y
-- --------
-1     3.00
-2     4.00
-3     5.00
-4     6.00
-5     7.00
-X        Y
-- --------
-1     4.00
-2     5.00
-3     6.00
-4     7.00
-5     8.00
+diff -c pspp.csv - << EOF
+Table: Data List
+X,Y
+1.00,2.00
+2.00,3.00
+3.00,4.00
+4.00,5.00
+5.00,6.00
+
+Table: Data List
+X,Y
+1,3.00
+2,4.00
+3,5.00
+4,6.00
+5,7.00
+
+Table: Data List
+X,Y
+1,4.00
+2,5.00
+3,6.00
+4,7.00
+5,8.00
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
index 2bac4413014e0d57e0bb084cf0953ae4e0916511..a40eae66eb317ac01275d6b89221e3b814c63eae 100755 (executable)
@@ -88,7 +88,6 @@ $SUPERVISOR $PSPP --testing-mode $TESTFILE
 if [ $? -ne 0 ] ; then no_result ; fi
 
 activity="check that foo2.out was created"
-perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
 diff -b foo2.out - << EOF
  1
  2
index e423f3e8b4c6318241adc54718902d22a1697b41..d2016edb4238fc1f2226268b1971d5482a2f319e 100755 (executable)
@@ -80,30 +80,30 @@ if [ $? -ne 0 ] ; then no_result ; fi
 
 
 activity="compare output"
-perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
-diff -b  -w $TEMPDIR/pspp.list - << EOF
-      R1
---------
-     7.71 
-     2.99 
-      .21 
-     4.95 
-     6.34 
-     4.43 
-     7.49 
-     8.32 
-     4.99 
-     5.83 
-     2.25 
-      .25 
-     1.98 
-     7.09 
-     7.61 
-     2.66 
-     1.69 
-     2.64 
-      .88 
-     1.50 
+perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.csv
+diff -b  -w $TEMPDIR/pspp.csv - << EOF
+Table: Data List
+R1
+7.71
+2.99
+.21
+4.95
+6.34
+4.43
+7.49
+8.32
+4.99
+5.83
+2.25
+.25
+1.98
+7.09
+7.61
+2.66
+1.69
+2.64
+.88
+1.50
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
diff --git a/tests/bugs/recode-copy-bug-1.out b/tests/bugs/recode-copy-bug-1.out
deleted file mode 100644 (file)
index 659eba5..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-1.1 DATA LIST.  Reading free-form data from INLINE.
-+--------+------+
-|Variable|Format|
-#========#======#
-|A       |A1    |
-|B       |A1    |
-+--------+------+
-A B
-- -
-3 2 
-2 3 
-1 4 
diff --git a/tests/bugs/recode-copy-bug-1.stat b/tests/bugs/recode-copy-bug-1.stat
deleted file mode 100644 (file)
index c493a15..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-TITLE 'Test for regression of recode COPY bug'
-
-DATA LIST LIST
- /A (A1)
- B (A1).
-
-BEGIN DATA
-1     2
-2     3
-3     4
-END DATA.
-
-** Clearly, the else=copy is superfluous here
-RECODE A ("1"="3") ("3"="1") (ELSE=COPY).
-EXECUTE.
-LIST.
-
diff --git a/tests/bugs/recode-copy-bug-2.out b/tests/bugs/recode-copy-bug-2.out
deleted file mode 100644 (file)
index 50899a8..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-1.1 DATA LIST.  Reading free-form data from INLINE.
-+--------+------+
-|Variable|Format|
-#========#======#
-|A       |A1    |
-|B       |A1    |
-+--------+------+
-A B A1
-- - --
-1 2  3 
-2 3  2 
-3 4  1 
diff --git a/tests/bugs/recode-copy-bug-2.stat b/tests/bugs/recode-copy-bug-2.stat
deleted file mode 100644 (file)
index 95873d7..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-DATA LIST LIST
- /A (A1)
- B (A1).
-
-BEGIN DATA
-1     2
-2     3
-3     4
-END DATA.
-
-STRING A1 (A1).
-RECODE A ("1"="3") ("3"="1") (ELSE=COPY) INTO a1.
-EXECUTE.
-LIST.
-
-
index 912590a34c0869d6260a107a5483f4002c7f0167..e0a18b8ff6a4d28cb13c674b756b5bf7ce4b8a9c 100755 (executable)
@@ -53,39 +53,88 @@ pass()
 }
 
 mkdir -p $TEMPDIR
-
-activity="copy template 1" 
-cp $top_srcdir/tests/bugs/recode-copy-bug-1.stat $TEMPDIR
+cd $TEMPDIR
 if [ $? -ne 0 ] ; then no_result ; fi
 
-activity="copy template 2" 
-cp $top_srcdir/tests/bugs/recode-copy-bug-2.stat $TEMPDIR
+activity="create syntax 1"
+cat > recode-copy-bug-1.stat <<EOF
+TITLE 'Test for regression of recode COPY bug'
+
+DATA LIST LIST
+ /A (A1)
+ B (A1).
+
+BEGIN DATA
+1     2
+2     3
+3     4
+END DATA.
+
+** Clearly, the else=copy is superfluous here
+RECODE A ("1"="3") ("3"="1") (ELSE=COPY).
+EXECUTE.
+LIST.
+EOF
 if [ $? -ne 0 ] ; then no_result ; fi
 
-activity="chdir"
-cd $TEMPDIR
+activity="create syntax 2" 
+cat > recode-copy-bug-2.stat <<EOF
+DATA LIST LIST
+ /A (A1)
+ B (A1).
+
+BEGIN DATA
+1     2
+2     3
+3     4
+END DATA.
+
+STRING A1 (A1).
+RECODE A ("1"="3") ("3"="1") (ELSE=COPY) INTO a1.
+EXECUTE.
+LIST.
+EOF
 if [ $? -ne 0 ] ; then no_result ; fi
 
-
 activity="run program 1"
-$SUPERVISOR $PSPP --testing-mode $TEMPDIR/recode-copy-bug-1.stat
+$SUPERVISOR $PSPP --testing-mode recode-copy-bug-1.stat
 if [ $? -ne 0 ] ; then no_result ; fi
 
-
 activity="compare output 1"
-perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list $top_srcdir/tests/bugs/recode-copy-bug-1.out
-diff -b -w $TEMPDIR/pspp.list $top_srcdir/tests/bugs/recode-copy-bug-1.out
+diff -c $TEMPDIR/pspp.csv - <<EOF
+Title: Test for regression of recode COPY bug
+
+Table: Reading free-form data from INLINE.
+Variable,Format
+A,A1
+B,A1
+
+Table: Data List
+A,B
+3,2
+2,3
+1,4
+EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
 
 activity="run program 2"
-$SUPERVISOR $PSPP --testing-mode $TEMPDIR/recode-copy-bug-2.stat
+$SUPERVISOR $PSPP --testing-mode recode-copy-bug-2.stat
 if [ $? -ne 0 ] ; then no_result ; fi
 
-
 activity="compare output 2"
-perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list $top_srcdir/tests/bugs/recode-copy-bug-2.out
-diff -b -w $TEMPDIR/pspp.list $top_srcdir/tests/bugs/recode-copy-bug-2.out
+diff -c $TEMPDIR/pspp.csv - <<EOF
+Table: Reading free-form data from INLINE.
+Variable,Format
+A,A1
+B,A1
+
+Table: Data List
+A,B,A1
+1,2,3
+2,3,2
+3,4,1
+EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
 pass;
index 826d5dc4d898cff3983404316b677d5fc84259f7..cc3fc5e2d7644b754371b586a3e22341265b1059 100755 (executable)
@@ -74,12 +74,12 @@ if [ $? -ne 0 ] ; then fail ; fi
 
 
 activity="compare output"
-perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
-diff -b  -w $TEMPDIR/pspp.list - << EOF
-       a
---------
-    1.00
-    2.00
+perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.csv
+diff -b  -w $TEMPDIR/pspp.csv - << EOF
+Table: Data List
+a
+1.00
+2.00
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
index 5565d31ad4b5faf6ca12db873d1d933e7fb72c12..2a1693b5ac6147ee1e701bc89266122cc77d5eab 100755 (executable)
@@ -86,39 +86,29 @@ $SUPERVISOR $PSPP --testing-mode $TESTFILE
 if [ $? -ne 0 ] ; then no_result ; fi
 
 activity="compare output"
-perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
-diff  -b $TEMPDIR/pspp.list - <<EOF
-1.1 DATA LIST.  Reading free-form data from INLINE.
-+--------+------+
-|Variable|Format|
-#========#======#
-|ID      |F8.0  |
-|INDEP   |A1    |
-|DEP1    |F8.0  |
-|DEP2    |F8.0  |
-+--------+------+
-2.1 T-TEST.  Group Statistics
-#==========#=#====#==============#=========#
-#     INDEP|N|Mean|Std. Deviation|S.E. Mean#
-#==========#=#====#==============#=========#
-#DEP1 a    |5|2.00|           .71|      .32#
-#     b    |5|4.00|           .71|      .32#
-#DEP2 a    |5|4.00|           .71|      .32#
-#     b    |5|2.00|           .71|      .32#
-#==========#=#====#==============#=========#
-2.2 T-TEST.  Independent Samples Test
-#===============================#========#============================================================================#
-#                               #Levene's|                        t-test for Equality of Means                        #
-#                               #---+----+-----+----+---------------+---------------+---------------------+-----------#
-#                               #   |    |     |    |               |               |                     |    95%    #
-#                               #   |    |     |    |               |               |                     +-----+-----#
-#                               # F |Sig.|  t  | df |Sig. (2-tailed)|Mean Difference|Std. Error Difference|Lower|Upper#
-#===============================#===#====#=====#====#===============#===============#=====================#=====#=====#
-#DEP1Equal variances assumed    #.00|1.00|-4.47|8.00|            .00|          -2.00|                  .45|-3.03| -.97#
-#    Equal variances not assumed#   |    |-4.47|8.00|            .00|          -2.00|                  .45|-3.03| -.97#
-#DEP2Equal variances assumed    #.00|1.00| 4.47|8.00|            .00|           2.00|                  .45|  .97| 3.03#
-#    Equal variances not assumed#   |    | 4.47|8.00|            .00|           2.00|                  .45|  .97| 3.03#
-#===============================#===#====#=====#====#===============#===============#=====================#=====#=====#
+diff -c $TEMPDIR/pspp.csv - <<EOF
+Table: Reading free-form data from INLINE.
+Variable,Format
+ID,F8.0
+INDEP,A1
+DEP1,F8.0
+DEP2,F8.0
+
+Table: Group Statistics
+,INDEP,N,Mean,Std. Deviation,S.E. Mean
+DEP1,a,5,2.00,.71,.32
+,b,5,4.00,.71,.32
+DEP2,a,5,4.00,.71,.32
+,b,5,2.00,.71,.32
+
+Table: Independent Samples Test
+,,Levene's Test for Equality of Variances,,t-test for Equality of Means,,,,,,
+,,,,,,,,,95% Confidence Interval of the Difference,
+,,F,Sig.,t,df,Sig. (2-tailed),Mean Difference,Std. Error Difference,Lower,Upper
+DEP1,Equal variances assumed,.00,1.00,-4.47,8.00,.00,-2.00,.45,-3.03,-.97
+,Equal variances not assumed,,,-4.47,8.00,.00,-2.00,.45,-3.03,-.97
+DEP2,Equal variances assumed,.00,1.00,4.47,8.00,.00,2.00,.45,.97,3.03
+,Equal variances not assumed,,,4.47,8.00,.00,2.00,.45,.97,3.03
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
index c54bba3cf541a1ce7c738487e88ec6db09bb7a1e..b197c574f3afd8f838b05a8e8caa13435fa9881a 100755 (executable)
@@ -84,33 +84,23 @@ if [ $? -ne 0 ] ; then no_result ; fi
 
 
 activity="compare output"
-perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
-diff  -b  $TEMPDIR/pspp.list - << EOF
-1.1 DATA LIST.  Reading free-form data from INLINE.
-+--------+------+
-|Variable|Format|
-#========#======#
-|x       |F8.0  |
-|gv      |A8    |
-+--------+------+
-2.1 T-TEST.  Group Statistics
-#==========#=#====#==============#=========#
-#     gv   |N|Mean|Std. Deviation|S.E. Mean#
-#==========#=#====#==============#=========#
-#x One     |5|2.60|           .55|      .24#
-#  Two     |3|3.50|           .50|      .29#
-#==========#=#====#==============#=========#
-2.2 T-TEST.  Independent Samples Test
-#============================#=========#============================================================================#
-#                            # Levene's|                        t-test for Equality of Means                        #
-#                            #----+----+-----+----+---------------+---------------+---------------------+-----------#
-#                            #    |    |     |    |               |               |                     |    95%    #
-#                            #    |    |     |    |               |               |                     +-----+-----#
-#                            #  F |Sig.|  t  | df |Sig. (2-tailed)|Mean Difference|Std. Error Difference|Lower|Upper#
-#============================#====#====#=====#====#===============#===============#=====================#=====#=====#
-#xEqual variances assumed    #1.13| .33|-2.32|6.00|            .06|           -.90|                  .38|-1.83|  .03#
-# Equal variances not assumed#    |    |-2.38|4.70|            .07|           -.90|                  .38|-1.89|  .09#
-#============================#====#====#=====#====#===============#===============#=====================#=====#=====#
+diff -c $TEMPDIR/pspp.csv - << EOF
+Table: Reading free-form data from INLINE.
+Variable,Format
+x,F8.0
+gv,A8
+
+Table: Group Statistics
+,gv,N,Mean,Std. Deviation,S.E. Mean
+x,One     ,5,2.60,.55,.24
+,Two     ,3,3.50,.50,.29
+
+Table: Independent Samples Test
+,,Levene's Test for Equality of Variances,,t-test for Equality of Means,,,,,,
+,,,,,,,,,95% Confidence Interval of the Difference,
+,,F,Sig.,t,df,Sig. (2-tailed),Mean Difference,Std. Error Difference,Lower,Upper
+x,Equal variances assumed,1.13,.33,-2.32,6.00,.06,-.90,.38,-1.83,.03
+,Equal variances not assumed,,,-2.38,4.70,.07,-.90,.38,-1.89,.09
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
index a9a311b7cc63290d4377f2c49e7208cbf741586e..c5555f6f58442950cf1f0ea01c7801271021b3d9 100755 (executable)
@@ -78,38 +78,26 @@ if [ $? -ne 0 ] ; then no_result ; fi
 
 
 activity="compare output"
-perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
-diff  -b  $TEMPDIR/pspp.list - << EOF
-1.1 DATA LIST.  Reading free-form data from INLINE.
-+--------+------+
-|Variable|Format|
-#========#======#
-|A       |F8.0  |
-|B       |F8.0  |
-+--------+------+
-2.1 T-TEST.  Paired Sample Statistics
-#========#=====#=#==============#=========#
-#        # Mean|N|Std. Deviation|S.E. Mean#
-#========#=====#=#==============#=========#
-#Pair 0 A#4.333|3|         5.774|    3.333#
-#       B#1.333|3|          .577|     .333#
-#========#=====#=#==============#=========#
-2.2 T-TEST.  Paired Samples Correlations
-#======#=====#=#===========#====#
-#      |     #N|Correlation|Sig.#
-#======#=====#=#===========#====#
-#Pair 0|A & B#3|      1.000|.000#
-#======#=====#=#===========#====#
-2.3 T-TEST.  Paired Samples Test
-#===========#==================================================#=====#==#===============#
-#           #                Paired Differences                |     |  |               #
-#           #-----+--------------+---------------+-------------+     |  |               #
-#           #     |              |               |     95%     |     |  |               #
-#           #     |              |               +------+------+     |  |               #
-#           # Mean|Std. Deviation|Std. Error Mean| Lower| Upper|  t  |df|Sig. (2-tailed)#
-#===========#=====#==============#===============#======#======#=====#==#===============#
-#Pair 0A - B#3.000|         5.196|          3.000|-9.908|15.908|1.000| 2|           .423#
-#===========#=====#==============#===============#======#======#=====#==#===============#
+diff -c $TEMPDIR/pspp.csv - << EOF
+Table: Reading free-form data from INLINE.
+Variable,Format
+A,F8.0
+B,F8.0
+
+Table: Paired Sample Statistics
+,,Mean,N,Std. Deviation,S.E. Mean
+Pair 0,A,4.333,3,5.774,3.333
+,B,1.333,3,.577,.333
+
+Table: Paired Samples Correlations
+,,N,Correlation,Sig.
+Pair 0,A & B,3,1.000,.000
+
+Table: Paired Samples Test
+,,Paired Differences,,,,,,,
+,,,,,95% Confidence Interval of the Difference,,,,
+,,Mean,Std. Deviation,Std. Error Mean,Lower,Upper,t,df,Sig. (2-tailed)
+Pair 0,A - B,3.000,5.196,3.000,-9.908,15.908,1.000,2,.423
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
index 92dc3fb02b057f8215dd2907e8e340732b4521a1..ba355580a62df84490f27977f4d3cebedd618730 100755 (executable)
@@ -79,7 +79,7 @@ $SUPERVISOR $PSPP --testing-mode $TESTFILE
 if [ $? -ne 0 ] ; then no_result ; fi
 
 activity="copy output"
-cp $TEMPDIR/pspp.list $TEMPDIR/first.list
+cp $TEMPDIR/pspp.csv $TEMPDIR/first.csv
 if [ $? -ne 0 ] ; then no_result ; fi
 
 activity="create program 2"
@@ -103,8 +103,8 @@ EOF
 if [ $? -ne 0 ] ; then no_result ; fi
 
 activity="compare output"
-perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list $TEMPDIR/first.list
-diff -b $TEMPDIR/pspp.list $TEMPDIR/first.list
+perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.csv $TEMPDIR/first.csv
+diff -b $TEMPDIR/pspp.csv $TEMPDIR/first.csv
 if [ $? -ne 0 ] ; then fail ; fi
 
 
index 6168f4bb86fa6149a72062c363e848ff8485798b..b0c91fd3f6e16a9a61aa1bf091b72cc7d53266a1 100755 (executable)
@@ -86,34 +86,26 @@ if [ $? -ne 0 ] ; then no_result ; fi
 $SUPERVISOR $PSPP --testing-mode $TESTFILE
 if [ $? -ne 0 ] ; then no_result ; fi
 
-perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
-diff -b  -w $TEMPDIR/pspp.list - << EOF
-1.1 DATA LIST.  Reading free-form data from INLINE.
-+--------+------+
-|Variable|Format|
-#========#======#
-|SEX     |A1    |
-|X       |F8.0  |
-+--------+------+
-2.1 FREQUENCIES.  X
-+-----------+--------+---------+--------+-------------+-----------+
-|Value Label|  Value |Frequency| Percent|Valid Percent|Cum Percent|
-#===========#========#=========#========#=============#===========#
-|           |   12.00|        1|   25.00|        25.00|      25.00|
-|           |   13.00|        1|   25.00|        25.00|      50.00|
-|           |   21.00|        1|   25.00|        25.00|      75.00|
-|           |   31.00|        1|   25.00|        25.00|     100.00|
-#===========#========#=========#========#=============#===========#
-|               Total|        4|   100.0|        100.0|           |
-+--------------------+---------+--------+-------------+-----------+
-+---------------+-----+
-|N       Valid  |    4|
-|        Missing|    0|
-|Mean           |19.25|
-|Std Dev        | 8.81|
-|Minimum        |12.00|
-|Maximum        |31.00|
-+---------------+-----+
+diff -c $TEMPDIR/pspp.csv - << EOF
+Table: Reading free-form data from INLINE.
+Variable,Format
+SEX,A1
+X,F8.0
+
+Table: X
+Value Label,Value,Frequency,Percent,Valid Percent,Cum Percent
+,12.00,1,25.00,25.00,25.00
+,13.00,1,25.00,25.00,50.00
+,21.00,1,25.00,25.00,75.00
+,31.00,1,25.00,25.00,100.00
+Total,,4,100.0,100.0,
+
+N,Valid,4
+,Missing,0
+Mean,,19.25
+Std Dev,,8.81
+Minimum,,12.00
+Maximum,,31.00
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
index 8ffa86115999566dbca7c821aca16e67cb2aa793..345ed852caf18c3eb159713e7104f115c47f0a9d 100755 (executable)
@@ -84,25 +84,25 @@ $SUPERVISOR $PSPP --testing-mode $TESTFILE
 if [ $? -ne 0 ] ; then no_result ; fi
 
 
-perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
-diff -b  -w $TEMPDIR/pspp.list - << EOF
-       X
---------
-    6.00 
-    7.00 
-    8.00 
-    9.00 
-       X
---------
-    1.00 
-    2.00 
-    3.00 
-    4.00 
-    5.00 
-    6.00 
-    7.00 
-    8.00 
-    9.00 
+diff -c $TEMPDIR/pspp.csv - << EOF
+Table: Data List
+X
+6.00
+7.00
+8.00
+9.00
+
+Table: Data List
+X
+1.00
+2.00
+3.00
+4.00
+5.00
+6.00
+7.00
+8.00
+9.00
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
index 48232f2e4828303d4b0862754b73375478ff5f03..f0542c0b005e90d7af31cd09af2d1f6541dda58c 100755 (executable)
@@ -84,52 +84,52 @@ cat > b.data <<EOF
 EOF
 if [ $? -ne 0 ] ; then no_result ; fi
 
-cat > concatenate.out <<EOF
-A B C D INA INB
-- - - - --- ---
-1 a B     1   0
-8 a M     1   0
-3 a E     1   0
-5 a G     1   0
-0 a A     1   0
-5 a H     1   0
-6 a I     1   0
-7 a J     1   0
-2 a D     1   0
-7 a K     1   0
-1 a C     1   0
-7 a L     1   0
-4 a F     1   0
-1 b   N   0   1
-3 b   O   0   1
-4 b   P   0   1
-6 b   Q   0   1
-7 b   R   0   1
-9 b   S   0   1
+cat > concatenate.csv <<EOF
+Table: Data List
+A,B,C,D,INA,INB
+1,a,B,,1,0
+8,a,M,,1,0
+3,a,E,,1,0
+5,a,G,,1,0
+0,a,A,,1,0
+5,a,H,,1,0
+6,a,I,,1,0
+7,a,J,,1,0
+2,a,D,,1,0
+7,a,K,,1,0
+1,a,C,,1,0
+7,a,L,,1,0
+4,a,F,,1,0
+1,b,,N,0,1
+3,b,,O,0,1
+4,b,,P,0,1
+6,b,,Q,0,1
+7,b,,R,0,1
+9,b,,S,0,1
 EOF
 
-cat > interleave.out <<EOF
-A B C D INA INB FIRST LAST
-- - - - --- --- ----- ----
-0 a A     1   0     1   1
-1 a B     1   0            1    0
-1 a C     1   0            0    0
-1 b   N   0   1            0    1
-2 a D     1   0            1    1
-3 a E     1   0            1    0
-3 b   O   0   1            0    1
-4 a F     1   0            1    0
-4 b   P   0   1            0    1
-5 a G     1   0            1    0
-5 a H     1   0            0    1
-6 a I     1   0            1    0
-6 b   Q   0   1            0    1
-7 a J     1   0            1    0
-7 a K     1   0            0    0
-7 a L     1   0            0    0
-7 b   R   0   1            0    1
-8 a M     1   0            1    1
-9 b   S   0   1            1    1
+cat > interleave.csv <<EOF
+Table: Data List
+A,B,C,D,INA,INB,FIRST,LAST
+0,a,A,,1,0,1,1
+1,a,B,,1,0,1,0
+1,a,C,,1,0,0,0
+1,b,,N,0,1,0,1
+2,a,D,,1,0,1,1
+3,a,E,,1,0,1,0
+3,b,,O,0,1,0,1
+4,a,F,,1,0,1,0
+4,b,,P,0,1,0,1
+5,a,G,,1,0,1,0
+5,a,H,,1,0,0,1
+6,a,I,,1,0,1,0
+6,b,,Q,0,1,0,1
+7,a,J,,1,0,1,0
+7,a,K,,1,0,0,0
+7,a,L,,1,0,0,0
+7,b,,R,0,1,0,1
+8,a,M,,1,0,1,1
+9,b,,S,0,1,1,1
 EOF
 
 # Test ADD FILES.
@@ -190,9 +190,9 @@ EOF
        if [ $? -ne 0 ] ; then no_result ; fi
 
        activity="check $name output"
-       perl -pi -e 's/^\s*$//g' pspp.list
-       perl -pi -e 's/^\s*$//g' $type.out
-       diff -u -b -w pspp.list $type.out
+       perl -pi -e 's/^\s*$//g' pspp.csv
+       perl -pi -e 's/^\s*$//g' $type.csv
+       diff -u -b -w pspp.csv $type.csv
        if [ $? -ne 0 ] ; then fail ; fi
     done
 done
index 63abc6f3b003ac093ee59434736522d9d64d27c8..48b0ef6e39f9d844e5f452d33dfd8daf693e1d1d 100755 (executable)
@@ -155,32 +155,39 @@ cat > agg-skel.pspp <<EOF
 EOF
 
 activity="expected output (itemwise missing) create"
-cat > agg-itemwise.out <<EOF
+cat > agg-itemwise.csv <<EOF
 warning: AGGREGATE: The value arguments passed to the FOUT function are out-of-order.  They will be treated as if they had been specified in the correct order.
+
 warning: AGGREGATE: The value arguments passed to the FOUT function are out-of-order.  They will be treated as if they had been specified in the correct order.
+
 warning: AGGREGATE: The value arguments passed to the FOUT function are out-of-order.  They will be treated as if they had been specified in the correct order.
+
 warning: AGGREGATE: The value arguments passed to the FOUT function are out-of-order.  They will be treated as if they had been specified in the correct order.
-G        N       NI      NU     NUI NFGT2 NFGT2I SFGT2 SFGT2I NFIN23 NFIN23I SFIN23 SFIN23I NFLT2 NFLT2I SFLT2 SFLT2I NFIRST NFIRSTI SFIRST SFIRSTI NFOUT23 NFOUT23I SFOUT23 SFOUT23I NLAST NLASTI SLAST SLASTI NMAX NMAXI SMAX SMAXI    NMEAN   NMEANI NMIN NMINI SMIN SMINI       NN      NNI       SN      SNI   NNMISS  NNMISSI   SNMISS  SNMISSI     NNU    NNUI     SNU    SNUI NNUMISS NNUMISSI SNUMISS SNUMISSI NPGT2 NPGT2I SPGT2 SPGT2I NPIN23 NPIN23I SPIN23 SPIN23I NPLT2 NPLT2I SPLT2 SPLT2I NPOUT23 NPOUT23I SPOUT23 SPOUT23I  NMEDIAN NMEDIANI      NSD     NSDI     NSUM    NSUMI
-- -------- -------- ------- ------- ----- ------ ----- ------ ------ ------- ------ ------- ----- ------ ----- ------ ------ ------- ------ ------- ------- -------- ------- -------- ----- ------ ----- ------ ---- ----- ---- ----- -------- -------- ---- ----- ---- ----- -------- -------- -------- -------- -------- -------- -------- -------- ------- ------- ------- ------- ------- -------- ------- -------- ----- ------ ----- ------ ------ ------- ------ ------- ----- ------ ----- ------ ------- -------- ------- -------- -------- -------- -------- -------- -------- --------
-1     7.00     7.00       6       6  .333   .429  .333   .429   .333    .286   .333    .286  .500   .429  .500   .429      0       0      0       0    .667     .714    .667     .714     5      5     5      5    5     5    5     5     2.00     2.29    0     0    0     0     6.00     7.00     6.00     7.00     1.00      .00     1.00      .00       5       6       5       6       1        0       1        0  33.3   42.9  33.3   42.9   33.3    28.6   33.3    28.6  50.0   42.9  50.0   42.9    66.7     71.4    66.7     71.4     1.50     2.00     1.79     1.80    12.00    16.00 
-2     5.00     5.00       4       4 1.000  1.000 1.000  1.000   .000    .000   .000    .000  .000   .000  .000   .000      6       6      6       4   1.000    1.000   1.000    1.000     8      8     8      8    8     8    8     8     7.00     7.00    6     6    6     4     3.00     3.00     3.00     5.00     2.00     2.00     2.00      .00       3       3       3       4       1        1       1        0 100.0  100.0 100.0  100.0     .0      .0     .0      .0    .0     .0    .0     .0   100.0    100.0   100.0    100.0     7.00     7.00     1.00     1.00    21.00    21.00 
-3     2.00     2.00       1       1  .000   .000  .000   .000   .000    .000   .000    .000 1.000  1.000 1.000  1.000      1       1      1       1   1.000    1.000   1.000    1.000     1      1     1      1    1     1    1     1     1.00     1.00    1     1    1     1     2.00     2.00     2.00     2.00      .00      .00      .00      .00       1       1       1       1       0        0       0        0    .0     .0    .0     .0     .0      .0     .0      .0 100.0  100.0 100.0  100.0   100.0    100.0   100.0    100.0     1.00     1.00      .00      .00     2.00     2.00 
-4     1.00     1.00       1       1  .      .     .     1.000   .       .      .       .000  .      .     .      .000      .       .              4    .        .       .       1.000     .      .            4    .     .          4      .        .      .     .          4      .00      .00      .00     1.00     1.00     1.00     1.00      .00       0       0       0       1       1        1       1        0    .      .     .   100.0     .       .      .       .0    .      .     .      .0      .        .       .     100.0      NaN      NaN      .        .        .        .   
 
+Table: Data List
+G,N,NI,NU,NUI,NFGT2,NFGT2I,SFGT2,SFGT2I,NFIN23,NFIN23I,SFIN23,SFIN23I,NFLT2,NFLT2I,SFLT2,SFLT2I,NFIRST,NFIRSTI,SFIRST,SFIRSTI,NFOUT23,NFOUT23I,SFOUT23,SFOUT23I,NLAST,NLASTI,SLAST,SLASTI,NMAX,NMAXI,SMAX,SMAXI,NMEAN,NMEANI,NMIN,NMINI,SMIN,SMINI,NN,NNI,SN,SNI,NNMISS,NNMISSI,SNMISS,SNMISSI,NNU,NNUI,SNU,SNUI,NNUMISS,NNUMISSI,SNUMISS,SNUMISSI,NPGT2,NPGT2I,SPGT2,SPGT2I,NPIN23,NPIN23I,SPIN23,SPIN23I,NPLT2,NPLT2I,SPLT2,SPLT2I,NPOUT23,NPOUT23I,SPOUT23,SPOUT23I,NMEDIAN,NMEDIANI,NSD,NSDI,NSUM,NSUMI
+1,7.00,7.00,6,6,.333,.429,.333,.429,.333,.286,.333,.286,.500,.429,.500,.429,0,0,0,0,.667,.714,.667,.714,5,5,5,5,5,5,5,5,2.00,2.29,0,0,0,0,6.00,7.00,6.00,7.00,1.00,.00,1.00,.00,5,6,5,6,1,0,1,0,33.3,42.9,33.3,42.9,33.3,28.6,33.3,28.6,50.0,42.9,50.0,42.9,66.7,71.4,66.7,71.4,1.50,2.00,1.79,1.80,12.00,16.00
+2,5.00,5.00,4,4,1.000,1.000,1.000,1.000,.000,.000,.000,.000,.000,.000,.000,.000,6,6,6,4,1.000,1.000,1.000,1.000,8,8,8,8,8,8,8,8,7.00,7.00,6,6,6,4,3.00,3.00,3.00,5.00,2.00,2.00,2.00,.00,3,3,3,4,1,1,1,0,100.0,100.0,100.0,100.0,.0,.0,.0,.0,.0,.0,.0,.0,100.0,100.0,100.0,100.0,7.00,7.00,1.00,1.00,21.00,21.00
+3,2.00,2.00,1,1,.000,.000,.000,.000,.000,.000,.000,.000,1.000,1.000,1.000,1.000,1,1,1,1,1.000,1.000,1.000,1.000,1,1,1,1,1,1,1,1,1.00,1.00,1,1,1,1,2.00,2.00,2.00,2.00,.00,.00,.00,.00,1,1,1,1,0,0,0,0,.0,.0,.0,.0,.0,.0,.0,.0,100.0,100.0,100.0,100.0,100.0,100.0,100.0,100.0,1.00,1.00,.00,.00,2.00,2.00
+4,1.00,1.00,1,1,.   ,.   ,.   ,1.000,.   ,.   ,.   ,.000,.   ,.   ,.   ,.000,.,.,,4,.   ,.   ,.   ,1.000,.,.,,4,.,.,,4,.  ,.  ,.,.,,4,.00,.00,.00,1.00,1.00,1.00,1.00,.00,0,0,0,1,1,1,1,0,. ,. ,. ,100.0,. ,. ,. ,.0,. ,. ,. ,.0,. ,. ,. ,100.0,NaN,NaN,.  ,.  ,.  ,.  
 EOF
 
 activity="expected output (columnwise missing) create"
-cat > agg-columnwise.out <<EOF
+cat > agg-columnwise.csv <<EOF
 warning: AGGREGATE: The value arguments passed to the FOUT function are out-of-order.  They will be treated as if they had been specified in the correct order.
+
 warning: AGGREGATE: The value arguments passed to the FOUT function are out-of-order.  They will be treated as if they had been specified in the correct order.
+
 warning: AGGREGATE: The value arguments passed to the FOUT function are out-of-order.  They will be treated as if they had been specified in the correct order.
+
 warning: AGGREGATE: The value arguments passed to the FOUT function are out-of-order.  They will be treated as if they had been specified in the correct order.
-G        N       NI      NU     NUI NFGT2 NFGT2I SFGT2 SFGT2I NFIN23 NFIN23I SFIN23 SFIN23I NFLT2 NFLT2I SFLT2 SFLT2I NFIRST NFIRSTI SFIRST SFIRSTI NFOUT23 NFOUT23I SFOUT23 SFOUT23I NLAST NLASTI SLAST SLASTI NMAX NMAXI SMAX SMAXI    NMEAN   NMEANI NMIN NMINI SMIN SMINI       NN      NNI       SN      SNI   NNMISS  NNMISSI   SNMISS  SNMISSI     NNU    NNUI     SNU    SNUI NNUMISS NNUMISSI SNUMISS SNUMISSI NPGT2 NPGT2I SPGT2 SPGT2I NPIN23 NPIN23I SPIN23 SPIN23I NPLT2 NPLT2I SPLT2 SPLT2I NPOUT23 NPOUT23I SPOUT23 SPOUT23I  NMEDIAN NMEDIANI      NSD     NSDI     NSUM    NSUMI
-- -------- -------- ------- ------- ----- ------ ----- ------ ------ ------- ------ ------- ----- ------ ----- ------ ------ ------- ------ ------- ------- -------- ------- -------- ----- ------ ----- ------ ---- ----- ---- ----- -------- -------- ---- ----- ---- ----- -------- -------- -------- -------- -------- -------- -------- -------- ------- ------- ------- ------- ------- -------- ------- -------- ----- ------ ----- ------ ------ ------- ------ ------- ----- ------ ----- ------ ------- -------- ------- -------- -------- -------- -------- -------- -------- --------
-1     7.00     7.00       6       6  .      .429  .      .429   .       .286   .       .286  .      .429  .      .429      .       0              0    .        .714    .        .714     .      5            5    .     5          5      .       2.29    .     0          0     6.00     7.00     6.00     7.00     1.00      .00     1.00      .00       5       6       5       6       1        0       1        0    .    42.9    .    42.9     .     28.6     .     28.6    .    42.9    .    42.9      .      71.4      .      71.4      .       2.00      .       1.80      .      16.00 
-2     5.00     5.00       4       4  .      .     .     1.000   .       .      .       .000  .      .     .      .000      .       .              4    .        .       .       1.000     .      .            8    .     .          8      .        .      .     .          4     3.00     3.00     3.00     5.00     2.00     2.00     2.00      .00       3       3       3       4       1        1       1        0    .      .     .   100.0     .       .      .       .0    .      .     .      .0      .        .       .     100.0      .        .        .        .        .        .   
-3     2.00     2.00       1       1  .000   .000  .000   .000   .000    .000   .000    .000 1.000  1.000 1.000  1.000      1       1      1       1   1.000    1.000   1.000    1.000     1      1     1      1    1     1    1     1     1.00     1.00    1     1    1     1     2.00     2.00     2.00     2.00      .00      .00      .00      .00       1       1       1       1       0        0       0        0    .0     .0    .0     .0     .0      .0     .0      .0 100.0  100.0 100.0  100.0   100.0    100.0   100.0    100.0     1.00     1.00      .00      .00     2.00     2.00 
-4     1.00     1.00       1       1  .      .     .     1.000   .       .      .       .000  .      .     .      .000      .       .              4    .        .       .       1.000     .      .            4    .     .          4      .        .      .     .          4      .00      .00      .00     1.00     1.00     1.00     1.00      .00       0       0       0       1       1        1       1        0    .      .     .   100.0     .       .      .       .0    .      .     .      .0      .        .       .     100.0      .        .        .        .        .        .   
+
+Table: Data List
+G,N,NI,NU,NUI,NFGT2,NFGT2I,SFGT2,SFGT2I,NFIN23,NFIN23I,SFIN23,SFIN23I,NFLT2,NFLT2I,SFLT2,SFLT2I,NFIRST,NFIRSTI,SFIRST,SFIRSTI,NFOUT23,NFOUT23I,SFOUT23,SFOUT23I,NLAST,NLASTI,SLAST,SLASTI,NMAX,NMAXI,SMAX,SMAXI,NMEAN,NMEANI,NMIN,NMINI,SMIN,SMINI,NN,NNI,SN,SNI,NNMISS,NNMISSI,SNMISS,SNMISSI,NNU,NNUI,SNU,SNUI,NNUMISS,NNUMISSI,SNUMISS,SNUMISSI,NPGT2,NPGT2I,SPGT2,SPGT2I,NPIN23,NPIN23I,SPIN23,SPIN23I,NPLT2,NPLT2I,SPLT2,SPLT2I,NPOUT23,NPOUT23I,SPOUT23,SPOUT23I,NMEDIAN,NMEDIANI,NSD,NSDI,NSUM,NSUMI
+1,7.00,7.00,6,6,.   ,.429,.   ,.429,.   ,.286,.   ,.286,.   ,.429,.   ,.429,.,0,,0,.   ,.714,.   ,.714,.,5,,5,.,5,,5,.  ,2.29,.,0,,0,6.00,7.00,6.00,7.00,1.00,.00,1.00,.00,5,6,5,6,1,0,1,0,. ,42.9,. ,42.9,. ,28.6,. ,28.6,. ,42.9,. ,42.9,. ,71.4,. ,71.4,.  ,2.00,.  ,1.80,.  ,16.00
+2,5.00,5.00,4,4,.   ,.   ,.   ,1.000,.   ,.   ,.   ,.000,.   ,.   ,.   ,.000,.,.,,4,.   ,.   ,.   ,1.000,.,.,,8,.,.,,8,.  ,.  ,.,.,,4,3.00,3.00,3.00,5.00,2.00,2.00,2.00,.00,3,3,3,4,1,1,1,0,. ,. ,. ,100.0,. ,. ,. ,.0,. ,. ,. ,.0,. ,. ,. ,100.0,.  ,.  ,.  ,.  ,.  ,.  
+3,2.00,2.00,1,1,.000,.000,.000,.000,.000,.000,.000,.000,1.000,1.000,1.000,1.000,1,1,1,1,1.000,1.000,1.000,1.000,1,1,1,1,1,1,1,1,1.00,1.00,1,1,1,1,2.00,2.00,2.00,2.00,.00,.00,.00,.00,1,1,1,1,0,0,0,0,.0,.0,.0,.0,.0,.0,.0,.0,100.0,100.0,100.0,100.0,100.0,100.0,100.0,100.0,1.00,1.00,.00,.00,2.00,2.00
+4,1.00,1.00,1,1,.   ,.   ,.   ,1.000,.   ,.   ,.   ,.000,.   ,.   ,.   ,.000,.,.,,4,.   ,.   ,.   ,1.000,.,.,,4,.,.,,4,.  ,.  ,.,.,,4,.00,.00,.00,1.00,1.00,1.00,1.00,.00,0,0,0,1,1,1,1,0,. ,. ,. ,100.0,. ,. ,. ,.0,. ,. ,. ,.0,. ,. ,. ,100.0,.  ,.  ,.  ,.  ,.  ,.  
 EOF
 
 for outfile in scratch active external; do
@@ -225,8 +232,8 @@ for outfile in scratch active external; do
            if [ $? -ne 0 ] ; then no_result ; fi
 
            activity="check $name output"
-           perl -pi -e 's/^\s*$//g;s/^.*:\d+: //;' pspp.list agg-$missing.out
-           diff -b -w pspp.list agg-$missing.out
+           perl -pi -e 's/^.*:\d+: //;' pspp.csv
+           diff -c pspp.csv agg-$missing.csv
            if [ $? -ne 0 ] ; then fail ; fi
        done
     done
index d4d36e603a737ce4e64081fb174266e91314c390..f3a39e2909a2edf1eb3502827c75ea2bfb9eebd9 100755 (executable)
@@ -100,48 +100,33 @@ $SUPERVISOR $PSPP --testing-mode $TESTFILE
 if [ $? -ne 0 ] ; then no_result ; fi
 
 activity="compare output"
-perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
-diff -b  -w $TEMPDIR/pspp.list - << EOF
-1.1 DISPLAY.  
-+--------+-----------------+-----------------------------------+
-|Variable|Description      |                                   |
-#========#=================#===================================#
-|a       |Custom attributes|                                   |
-|        |ValidationRule[1]|a * b > 3                          |
-|        |ValidationRule[2]|a + b > 2                          |
-+--------+-----------------+-----------------------------------+
-|b       |Custom attributes|                                   |
-|        |ValidationRule[1]|a * b > 3                          |
-|        |ValidationRule[2]|a + b > 2                          |
-+--------+-----------------+-----------------------------------+
-|c       |Custom attributes|                                   |
-|        |QuestionWording  |X or Y?                            |
-+--------+-----------------+-----------------------------------+
-1.2 DISPLAY.  Custom data file attributes.
-+---------+---------------+
-|Attribute|Value          |
-#=========#===============#
-|array[1] |array element 1|
-|array[2] |array element 2|
-|key      |value          |
-+---------+---------------+
-2.1 DISPLAY.  
-+--------+---------------+-----------------------------------+
-|Variable|Description    |                                   |
-#========#===============#===================================#
-|b       |Custom attribut|s:                                 |
-|        |ValidationRule |a * b > 3                          |
-+--------+---------------+-----------------------------------+
-|c       |Custom attribut|s:                                 |
-|        |QuestionWording|X or Y?                            |
-+--------+---------------+-----------------------------------+
-2.2 DISPLAY.  Custom data file attributes.
-+---------+---------------+
-|Attribute|Value          |
-#=========#===============#
-|array    |array element 2|
-|key      |value          |
-+---------+---------------+
+diff -c $TEMPDIR/pspp.csv - << EOF
+Variable,Description,
+a,Custom attributes:,
+,ValidationRule[1],a * b > 3
+,ValidationRule[2],a + b > 2
+b,Custom attributes:,
+,ValidationRule[1],a * b > 3
+,ValidationRule[2],a + b > 2
+c,Custom attributes:,
+,QuestionWording,X or Y?
+
+Table: Custom data file attributes.
+Attribute,Value
+array[1],array element 1
+array[2],array element 2
+key,value
+
+Variable,Description,
+b,Custom attributes:,
+,ValidationRule,a * b > 3
+c,Custom attributes:,
+,QuestionWording,X or Y?
+
+Table: Custom data file attributes.
+Attribute,Value
+array,array element 2
+key,value
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
index e956fcfa0403363b6ef61b9d7955855728185ff3..e4a86dfa6818e85bc2accde1fec975d41c0e3c5f 100755 (executable)
@@ -91,37 +91,35 @@ $SUPERVISOR $PSPP --testing-mode $TESTFILE
 if [ $? -ne 0 ] ; then no_result ; fi
 
 activity="test output"
-perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
-diff -b $TEMPDIR/pspp.list - <<EOF
-1.1 DATA LIST.  Reading 1 record from INLINE.
-+--------+------+-------+------+
-|Variable|Record|Columns|Format|
-#========#======#=======#======#
-|X       |     1|  1-  5|A5    |
-|Y       |     1|  7-  7|F1.0  |
-+--------+------+-------+------+
-    X Y        A        B
------ - -------- --------
-lasdj 1     1.00     3.00 
-asdfk 0     3.00     4.00 
-asdfj 2     4.00     2.00 
-asdfj 1     4.00     3.00 
-asdfk 2     3.00     2.00 
-asdfj 9     4.00     1.00 
-lajks 9     2.00     1.00 
-asdfk 0     3.00     4.00 
-asdfk 1     3.00     3.00 
-    X Y        A        B        Z        W
------ - -------- -------- -------- --------
-lasdj 1     1.00     3.00      .00     1.00 
-asdfk 0     3.00     4.00      .00     1.00 
-asdfj 2     4.00     2.00     1.00     2.00 
-asdfj 1     4.00     3.00      .00     1.00 
-asdfk 2     3.00     2.00     1.00     2.00 
-asdfj 9     4.00     1.00     4.00     3.00 
-lajks 9     2.00     1.00     4.00     3.00 
-asdfk 0     3.00     4.00      .00     1.00 
-asdfk 1     3.00     3.00      .00     1.00 
+diff -b $TEMPDIR/pspp.csv - <<EOF
+Table: Reading 1 record from INLINE.
+Variable,Record,Columns,Format
+X,1,1-  5,A5
+Y,1,7-  7,F1.0
+
+Table: Data List
+X,Y,A,B
+lasdj,1,1.00,3.00
+asdfk,0,3.00,4.00
+asdfj,2,4.00,2.00
+asdfj,1,4.00,3.00
+asdfk,2,3.00,2.00
+asdfj,9,4.00,1.00
+lajks,9,2.00,1.00
+asdfk,0,3.00,4.00
+asdfk,1,3.00,3.00
+
+Table: Data List
+X,Y,A,B,Z,W
+lasdj,1,1.00,3.00,.00,1.00
+asdfk,0,3.00,4.00,.00,1.00
+asdfj,2,4.00,2.00,1.00,2.00
+asdfj,1,4.00,3.00,.00,1.00
+asdfk,2,3.00,2.00,1.00,2.00
+asdfj,9,4.00,1.00,4.00,3.00
+lajks,9,2.00,1.00,4.00,3.00
+asdfk,0,3.00,4.00,.00,1.00
+asdfk,1,3.00,3.00,.00,1.00
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
index 6d835015e7e3d6ade18ccbe7cf0b45caa957cc70..e3933d8f2ddd3b66a3d6d144d625f42a2ca1dc4f 100755 (executable)
@@ -90,36 +90,34 @@ $SUPERVISOR $PSPP --testing-mode $TESTFILE
 if [ $? -ne 0 ] ; then no_result ; fi
 
 activity="compare data"
-perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
-diff -b $TEMPDIR/pspp.list - << foobar
-1.1 DATA LIST.  Reading 1 record from INLINE.
-+--------+------+-------+------+
-|Variable|Record|Columns|Format|
-#========#======#=======#======#
-|A       |     1|  1-  1|F1.0  |
-|B       |     1|  2-  2|F1.0  |
-+--------+------+-------+------+
-A B
-- -
-1 2 
-3 4 
-5 6 
-7 8 
-9 0 
-2.1 DATA LIST.  Reading 1 record from INLINE.
-+--------+------+-------+------+
-|Variable|Record|Columns|Format|
-#========#======#=======#======#
-|A       |     1|  1-  1|F1.0  |
-|B       |     1|  2-  2|F1.0  |
-+--------+------+-------+------+
-A B
-- -
-0 9 
-8 7 
-6 5 
-4 3 
-2 1 
+diff -c $TEMPDIR/pspp.csv - << foobar
+Title: Test BEGIN DATA ... END DATA
+
+Table: Reading 1 record from INLINE.
+Variable,Record,Columns,Format
+A,1,1-  1,F1.0
+B,1,2-  2,F1.0
+
+Table: Data List
+A,B
+1,2
+3,4
+5,6
+7,8
+9,0
+
+Table: Reading 1 record from INLINE.
+Variable,Record,Columns,Format
+A,1,1-  1,F1.0
+B,1,2-  2,F1.0
+
+Table: Data List
+A,B
+0,9
+8,7
+6,5
+4,3
+2,1
 foobar
 if [ $? -ne 0 ] ; then fail ; fi
 
index d8fcb1cbbcb1893dfc8ff31c65d1be4b060b418f..daec5de2aaed5ef776f5370cbd431caf8c1dfcf2 100755 (executable)
@@ -98,54 +98,39 @@ $SUPERVISOR $PSPP --testing-mode -o raw-ascii $TESTFILE
 if [ $? -ne 0 ] ; then no_result ; fi
 
 activity="compare results 1"
-perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
-diff -b  $TEMPDIR/pspp.list - << EOF
-1.1 CORRELATIONS.  Correlations
-#========================#=====#=====#=====#=====#
-#                        #foo  |bar  |wiz  |bang #
-#----+-------------------#-----+-----+-----+-----#
-#foo |Pearson Correlation#1.000| .802| .890|-.308#
-#    |Sig. (2-tailed)    #     | .055| .017| .553#
-#----+-------------------#-----+-----+-----+-----#
-#bar |Pearson Correlation# .802|1.000| .519| .118#
-#    |Sig. (2-tailed)    # .055|     | .291| .824#
-#----+-------------------#-----+-----+-----+-----#
-#wiz |Pearson Correlation# .890| .519|1.000|-.344#
-#    |Sig. (2-tailed)    # .017| .291|     | .505#
-#----+-------------------#-----+-----+-----+-----#
-#bang|Pearson Correlation#-.308| .118|-.344|1.000#
-#    |Sig. (2-tailed)    # .553| .824| .505|     #
-#====#===================#=====#=====#=====#=====#
-2.1 CORRELATIONS.  Correlations
-#=======================#=====#=====#
-#                       #bar  |wiz  #
-#---+-------------------#-----+-----#
-#bar|Pearson Correlation#1.000| .497#
-#   |Sig. (2-tailed)    #     | .210#
-#---+-------------------#-----+-----#
-#wiz|Pearson Correlation# .497|1.000#
-#   |Sig. (2-tailed)    # .210|     #
-#===#===================#=====#=====#
-3.1 CORRELATIONS.  Correlations
-#========================#=====#=====#=====#=====#
-#                        #foo  |bar  |wiz  |bang #
-#----+-------------------#-----+-----+-----+-----#
-#foo |Pearson Correlation#1.000| .805| .883|-.308#
-#    |Sig. (2-tailed)    #     | .029| .008| .553#
-#    |N                  #    7|    7|    7|    6#
-#----+-------------------#-----+-----+-----+-----#
-#bar |Pearson Correlation# .805|1.000| .497| .164#
-#    |Sig. (2-tailed)    # .029|     | .210| .725#
-#    |N                  #    7|    8|    8|    7#
-#----+-------------------#-----+-----+-----+-----#
-#wiz |Pearson Correlation# .883| .497|1.000|-.337#
-#    |Sig. (2-tailed)    # .008| .210|     | .460#
-#    |N                  #    7|    8|    8|    7#
-#----+-------------------#-----+-----+-----+-----#
-#bang|Pearson Correlation#-.308| .164|-.337|1.000#
-#    |Sig. (2-tailed)    # .553| .725| .460|     #
-#    |N                  #    6|    7|    7|    7#
-#====#===================#=====#=====#=====#=====#
+diff -c $TEMPDIR/pspp.csv - << EOF
+Table: Correlations
+,,foo,bar,wiz,bang
+foo,Pearson Correlation,1.000,.802,.890,-.308
+,Sig. (2-tailed),,.055,.017,.553
+bar,Pearson Correlation,.802,1.000,.519,.118
+,Sig. (2-tailed),.055,,.291,.824
+wiz,Pearson Correlation,.890,.519,1.000,-.344
+,Sig. (2-tailed),.017,.291,,.505
+bang,Pearson Correlation,-.308,.118,-.344,1.000
+,Sig. (2-tailed),.553,.824,.505,
+
+Table: Correlations
+,,bar,wiz
+bar,Pearson Correlation,1.000,.497
+,Sig. (2-tailed),,.210
+wiz,Pearson Correlation,.497,1.000
+,Sig. (2-tailed),.210,
+
+Table: Correlations
+,,foo,bar,wiz,bang
+foo,Pearson Correlation,1.000,.805,.883,-.308
+,Sig. (2-tailed),,.029,.008,.553
+,N,7,7,7,6
+bar,Pearson Correlation,.805,1.000,.497,.164
+,Sig. (2-tailed),.029,,.210,.725
+,N,7,8,8,7
+wiz,Pearson Correlation,.883,.497,1.000,-.337
+,Sig. (2-tailed),.008,.210,,.460
+,N,7,8,8,7
+bang,Pearson Correlation,-.308,.164,-.337,1.000
+,Sig. (2-tailed),.553,.725,.460,
+,N,6,7,7,7
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
@@ -181,7 +166,7 @@ $SUPERVISOR $PSPP --testing-mode -o raw-ascii $TESTFILE
 if [ $? -ne 0 ] ; then no_result ; fi
 
 activity="copy results"
-cp $TEMPDIR/pspp.list $TEMPDIR/weighted
+cp $TEMPDIR/pspp.csv $TEMPDIR/weighted
 if [ $? -ne 0 ] ; then no_result ; fi
 
 activity="create program 3"
@@ -215,7 +200,7 @@ $SUPERVISOR $PSPP --testing-mode -o raw-ascii $TESTFILE
 if [ $? -ne 0 ] ; then no_result ; fi
 
 activity="Compare weighted and unweighted results"
-diff $TEMPDIR/pspp.list $TEMPDIR/weighted
+diff $TEMPDIR/pspp.csv $TEMPDIR/weighted
 if [ $? -ne 0 ] ; then fail ; fi
 
 pass;
index 0813c428aa647d9bfc40dad03b4f60b7b4a818d7..d6fd1db85e23a82fc1a420c2be9f0486091b022c 100755 (executable)
@@ -85,24 +85,23 @@ if [ $? -ne 0 ] ; then no_result ; fi
 
 
 activity="compare results 1"
-perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
-diff -b  $TEMPDIR/pspp.list - <<EOF
-1.1 DATA LIST.  Reading 1 record from INLINE.
-+--------+------+-------+------+
-|Variable|Record|Columns|Format|
-#========#======#=======#======#
-|V1      |     1|  1-  2|A2    |
-|V2      |     1|  3-  4|A2    |
-+--------+------+-------+------+
-V1 V2        C
--- -- --------
-12 34      .00 
-32 1      1.00 
-2  13     1.00 
-41 21      .00 
-11 04      .00 
-03  4     1.00 
-01 93      .00 
+diff -c $TEMPDIR/pspp.csv - <<EOF
+Title: Test COUNT transformation
+
+Table: Reading 1 record from INLINE.
+Variable,Record,Columns,Format
+V1,1,1-  2,A2
+V2,1,3-  4,A2
+
+Table: Data List
+V1,V2,C
+12,34,.00
+32,1 ,1.00
+2 ,13,1.00
+41,21,.00
+11,04,.00
+03,4,1.00
+01,93,.00
 EOF
 if [ $? -ne 0 ] ; then no_result ; fi
 
@@ -133,22 +132,19 @@ if [ $? -ne 0 ] ; then no_result ; fi
 
 
 activity="compare results"
-perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
-diff -b  $TEMPDIR/pspp.list - <<EOF
-1.1 DATA LIST.  Reading free-form data from INLINE.
-+--------+------+
-|Variable|Format|
-#========#======#
-|x       |F8.0  |
-|y       |F8.0  |
-+--------+------+
-       x        y        C
--------- -------- --------
-    1.00     2.00     1.00 
-    2.00     3.00     1.00 
-    4.00     5.00      .00 
-    2.00     2.00     2.00 
-    5.00     6.00      .00 
+diff -c $TEMPDIR/pspp.csv - <<EOF
+Table: Reading free-form data from INLINE.
+Variable,Format
+x,F8.0
+y,F8.0
+
+Table: Data List
+x,y,C
+1.00,2.00,1.00
+2.00,3.00,1.00
+4.00,5.00,.00
+2.00,2.00,2.00
+5.00,6.00,.00
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
index 3f821199ff3e2d10f22627e6ce35c93ec7b1f157..52b3dcb8c999c84af08b117352a9b5f13f30f5c3 100755 (executable)
@@ -151,60 +151,61 @@ $SUPERVISOR $PSPP --testing-mode $TESTFILE
 if [ $? -ne 0 ] ; then fail ; fi
 
 activity="compare output"
-perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
-diff -b  $TEMPDIR/pspp.list - << EOF
-1.1 DATA LIST.  Reading free-form data from INLINE.
-+--------+------+
-|Variable|Format|
-#========#======#
-|A       |F8.0  |
-|B       |F8.0  |
-|C       |F8.0  |
-|D       |F8.0  |
-+--------+------+
-       A        B        C        D
--------- -------- -------- --------
-    1.00    23.00    45.00     2.03 
-    2.00    22.00    34.00    23.00 
-    3.00    34.00    34.00    34.00 
-       A        B        C        D
--------- -------- -------- --------
-     .       1.00     2.00     3.00 
-     .       4.00      .       5.00 
-    6.00     7.00     8.00     9.00 
-     .00     1.00      .        .   
-     .        .        .        .   
-    2.00     3.00     4.00     5.00 
-       A        B        C        D
--------- -------- -------- --------
-    1.00     2.00     3.00     4.00 
-    1.00     2.00     3.00      .   
-    1.00     2.00      .       4.00 
-    1.00     2.00      .        .   
-    1.00      .       3.00     4.00 
-    1.00      .       3.00      .   
-    1.00      .        .       4.00 
-    1.00      .        .        .   
-     .       2.00     3.00     4.00 
-     .       2.00     3.00      .   
-     .       2.00      .       4.00 
-     .       2.00      .        .   
-     .        .       3.00     4.00 
-     .        .       3.00      .   
-     .        .        .       4.00 
-     .        .        .        .   
-               start                  end count
--------------------- -------------------- -----
-          07/22/2007           10/06/2007   321
-          07/14/1789           08/26/1789     4
-          01/01/1972           12/31/1999   682
-x y
-- -
-1 2
-3 4
-5 6
-7 8
-9 0
+diff -c $TEMPDIR/pspp.csv - << EOF
+Table: Reading free-form data from INLINE.
+Variable,Format
+A,F8.0
+B,F8.0
+C,F8.0
+D,F8.0
+
+Table: Data List
+A,B,C,D
+1.00,23.00,45.00,2.03
+2.00,22.00,34.00,23.00
+3.00,34.00,34.00,34.00
+
+Table: Data List
+A,B,C,D
+.  ,1.00,2.00,3.00
+.  ,4.00,.  ,5.00
+6.00,7.00,8.00,9.00
+.00,1.00,.  ,.  
+.  ,.  ,.  ,.  
+2.00,3.00,4.00,5.00
+
+Table: Data List
+A,B,C,D
+1.00,2.00,3.00,4.00
+1.00,2.00,3.00,.  
+1.00,2.00,.  ,4.00
+1.00,2.00,.  ,.  
+1.00,.  ,3.00,4.00
+1.00,.  ,3.00,.  
+1.00,.  ,.  ,4.00
+1.00,.  ,.  ,.  
+.  ,2.00,3.00,4.00
+.  ,2.00,3.00,.  
+.  ,2.00,.  ,4.00
+.  ,2.00,.  ,.  
+.  ,.  ,3.00,4.00
+.  ,.  ,3.00,.  
+.  ,.  ,.  ,4.00
+.  ,.  ,.  ,.  
+
+Table: Data List
+start,end,count
+07/22/2007,10/06/2007,321
+07/14/1789,08/26/1789,4
+01/01/1972,12/31/1999,682
+
+Table: Data List
+x,y
+1,2
+3,4
+5,6
+7,8
+9,0
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
index 8cb6b824016542a45e54326d9f52091e05a1ce23..b3bbed7fac36d8085367b9fbdd0ac7a71fc73862 100755 (executable)
@@ -112,7 +112,6 @@ $SUPERVISOR $PSPP --testing-mode test1.pspp
 if [ $? -ne 0 ] ; then no_result ; fi
 
 activity="compare test1 results"
-perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
 diff -u $TEMPDIR/test1.out $TEMPDIR/test1.expected
 if [ $? -ne 0 ] ; then fail ; fi
 
index 0f395422f50180cb5b736f86ab9c37358e063c5d..07d67ec978148340f4d4fd2fa4022bcf35ba789c 100755 (executable)
@@ -88,11 +88,11 @@ $SUPERVISOR $PSPP --testing-mode $TESTFILE
 if [ $? -ne 0 ] ; then no_result ; fi
 
 activity="compare results"
-perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
-diff -b  $TEMPDIR/pspp.list - <<EOF
-a       h0       h1       h2       h3       v1       v2       v3       v4       v5       v6
-- -------- -------- -------- -------- -------- -------- -------- -------- -------- --------
-0     8.00     8.50     8.00     8.00     4.00     5.00     6.00     5.00     6.00     7.00 
+perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.csv
+diff -b  $TEMPDIR/pspp.csv - <<EOF
+Table: Data List
+a,h0,h1,h2,h3,v1,v2,v3,v4,v5,v6
+0,8.00,8.50,8.00,8.00,4.00,5.00,6.00,5.00,6.00,7.00
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
index 67dfed0a5757fa0d859d743e6a407ef26a3a10eb..f771c66d01774a4d21ab72b0474cd9b4eee47e03 100755 (executable)
@@ -99,36 +99,27 @@ $SUPERVISOR $PSPP --testing-mode $TESTFILE
 if [ $? -ne 0 ] ; then no_result ; fi
 
 activity="compare results"
-perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
-diff -b  $TEMPDIR/pspp.list - << EOF
-1.1 EXAMINE.  Case Processing Summary
-#==#=======================================#
-#  #                 Cases                 #
-#  #-------------+-----------+-------------#
-#  #    Valid    |  Missing  |    Total    #
-#  #-----+-------+---+-------+-----+-------#
-#  #  N  |Percent| N |Percent|  N  |Percent#
-#==#=====#=======#===#=======#=====#=======#
-#V1#23.00|   100%|.00|     0%|23.00|   100%#
-#==#=====#=======#===#=======#=====#=======#
-1.2 EXAMINE.  Extreme Values
-#============#===========#=====#
-#            #Case Number|Value#
-#============#===========#=====#
-#V1 Highest 1#         21|20.00#
-#           2#         20|19.00#
-#           3#         19|18.00#
-#           4#         19|18.00#
-#           5#         18|17.00#
-#           6#         17|16.00#
-#  ----------#-----------+-----#
-#    Lowest 1#          1| 1.00#
-#           2#          2| 2.00#
-#           3#          3| 3.00#
-#           4#          3| 3.00#
-#           5#          4| 3.00#
-#           6#          5| 4.00#
-#============#===========#=====#
+diff -c $TEMPDIR/pspp.csv - << EOF
+Table: Case Processing Summary
+,Cases,,,,,
+,Valid,,Missing,,Total,
+,N,Percent,N,Percent,N,Percent
+V1,23.00,100%,.00,0%,23.00,100%
+
+Table: Extreme Values
+,,,Case Number,Value
+V1,Highest,1,21,20.00
+,,2,20,19.00
+,,3,19,18.00
+,,4,19,18.00
+,,5,18,17.00
+,,6,17,16.00
+,Lowest,1,1,1.00
+,,2,2,2.00
+,,3,3,3.00
+,,4,3,3.00
+,,5,4,3.00
+,,6,5,4.00
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
index 708e5342b27c1f5a9a722acc89023233a03b4fad..bf2fcbf95c60d44611dceb53d8c13c1de130db70 100755 (executable)
@@ -93,109 +93,70 @@ if [ $? -ne 0 ] ; then no_result ; fi
 
 
 activity="compare results"
-perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
-diff -b  $TEMPDIR/pspp.list - << EOF
-1.1 DATA LIST.  Reading free-form data from INLINE.
-+--------+------+
-|Variable|Format|
-#========#======#
-|X       |F8.0  |
-+--------+------+
-2.1 EXAMINE.  Case Processing Summary
-#=#=============================#
-# #            Cases            #
-# #---------+---------+---------#
-# #  Valid  | Missing |  Total  #
-# #-+-------+-+-------+-+-------#
-# #N|Percent|N|Percent|N|Percent#
-#=#=#=======#=#=======#=#=======#
-#X#3|   100%|0|     0%|3|   100%#
-#=#=#=======#=#=======#=#=======#
-2.2 EXAMINE.  Percentiles
-#================#================================#
-#                #             Percentiles        #
-#                #---+---+----+----+----+----+----#
-#                # 5 | 10| 25 | 50 | 75 | 90 | 95 #
-#=#==============#===#===#====#====#====#====#====#
-#X|HAverage      #.40|.80|2.00|5.00|8.00|8.00|8.00#
-# |Tukey's Hinges#   |   |3.50|5.00|6.50|    |    #
-#=#==============#===#===#====#====#====#====#====#
-3.1 EXAMINE.  Case Processing Summary
-#=#=============================#
-# #            Cases            #
-# #---------+---------+---------#
-# #  Valid  | Missing |  Total  #
-# #-+-------+-+-------+-+-------#
-# #N|Percent|N|Percent|N|Percent#
-#=#=#=======#=#=======#=#=======#
-#X#3|   100%|0|     0%|3|   100%#
-#=#=#=======#=#=======#=#=======#
-3.2 EXAMINE.  Percentiles
-#==================#================================#
-#                  #             Percentiles        #
-#                  #---+---+----+----+----+----+----#
-#                  # 5 | 10| 25 | 50 | 75 | 90 | 95 #
-#=#================#===#===#====#====#====#====#====#
-#X|Weighted Average#.30|.60|1.50|3.50|5.75|7.10|7.55#
-# |Tukey's Hinges  #   |   |3.50|5.00|6.50|    |    #
-#=#================#===#===#====#====#====#====#====#
-4.1 EXAMINE.  Case Processing Summary
-#=#=============================#
-# #            Cases            #
-# #---------+---------+---------#
-# #  Valid  | Missing |  Total  #
-# #-+-------+-+-------+-+-------#
-# #N|Percent|N|Percent|N|Percent#
-#=#=#=======#=#=======#=#=======#
-#X#3|   100%|0|     0%|3|   100%#
-#=#=#=======#=#=======#=#=======#
-4.2 EXAMINE.  Percentiles
-#================#================================#
-#                #             Percentiles        #
-#                #---+---+----+----+----+----+----#
-#                # 5 | 10| 25 | 50 | 75 | 90 | 95 #
-#=#==============#===#===#====#====#====#====#====#
-#X|Rounded       #.00|.00|2.00|5.00|5.00|8.00|8.00#
-# |Tukey's Hinges#   |   |3.50|5.00|6.50|    |    #
-#=#==============#===#===#====#====#====#====#====#
-5.1 EXAMINE.  Case Processing Summary
-#=#=============================#
-# #            Cases            #
-# #---------+---------+---------#
-# #  Valid  | Missing |  Total  #
-# #-+-------+-+-------+-+-------#
-# #N|Percent|N|Percent|N|Percent#
-#=#=#=======#=#=======#=#=======#
-#X#3|   100%|0|     0%|3|   100%#
-#=#=#=======#=#=======#=#=======#
-5.2 EXAMINE.  Percentiles
-#================#==================================#
-#                #              Percentiles         #
-#                #----+----+----+----+----+----+----#
-#                #  5 | 10 | 25 | 50 | 75 | 90 | 95 #
-#=#==============#====#====#====#====#====#====#====#
-#X|Empirical     #2.00|2.00|2.00|5.00|8.00|8.00|8.00#
-# |Tukey's Hinges#    |    |3.50|5.00|6.50|    |    #
-#=#==============#====#====#====#====#====#====#====#
-6.1 EXAMINE.  Case Processing Summary
-#=#=============================#
-# #            Cases            #
-# #---------+---------+---------#
-# #  Valid  | Missing |  Total  #
-# #-+-------+-+-------+-+-------#
-# #N|Percent|N|Percent|N|Percent#
-#=#=#=======#=#=======#=#=======#
-#X#3|   100%|0|     0%|3|   100%#
-#=#=#=======#=#=======#=#=======#
-6.2 EXAMINE.  Percentiles
-#==========================#==================================#
-#                          #              Percentiles         #
-#                          #----+----+----+----+----+----+----#
-#                          #  5 | 10 | 25 | 50 | 75 | 90 | 95 #
-#=#========================#====#====#====#====#====#====#====#
-#X|Empirical with averaging#2.00|2.00|2.00|5.00|8.00|8.00|8.00#
-# |Tukey's Hinges          #    |    |3.50|5.00|6.50|    |    #
-#=#========================#====#====#====#====#====#====#====#
+diff -c $TEMPDIR/pspp.csv - << EOF
+Table: Reading free-form data from INLINE.
+Variable,Format
+X,F8.0
+
+Table: Case Processing Summary
+,Cases,,,,,
+,Valid,,Missing,,Total,
+,N,Percent,N,Percent,N,Percent
+X,3,100%,0,0%,3,100%
+
+Table: Percentiles
+,,Percentiles,,,,,,
+,,5,10,25,50,75,90,95
+X,HAverage,.40,.80,2.00,5.00,8.00,8.00,8.00
+,Tukey's Hinges,,,3.50,5.00,6.50,,
+
+Table: Case Processing Summary
+,Cases,,,,,
+,Valid,,Missing,,Total,
+,N,Percent,N,Percent,N,Percent
+X,3,100%,0,0%,3,100%
+
+Table: Percentiles
+,,Percentiles,,,,,,
+,,5,10,25,50,75,90,95
+X,Weighted Average,.30,.60,1.50,3.50,5.75,7.10,7.55
+,Tukey's Hinges,,,3.50,5.00,6.50,,
+
+Table: Case Processing Summary
+,Cases,,,,,
+,Valid,,Missing,,Total,
+,N,Percent,N,Percent,N,Percent
+X,3,100%,0,0%,3,100%
+
+Table: Percentiles
+,,Percentiles,,,,,,
+,,5,10,25,50,75,90,95
+X,Rounded,.00,.00,2.00,5.00,5.00,8.00,8.00
+,Tukey's Hinges,,,3.50,5.00,6.50,,
+
+Table: Case Processing Summary
+,Cases,,,,,
+,Valid,,Missing,,Total,
+,N,Percent,N,Percent,N,Percent
+X,3,100%,0,0%,3,100%
+
+Table: Percentiles
+,,Percentiles,,,,,,
+,,5,10,25,50,75,90,95
+X,Empirical,2.00,2.00,2.00,5.00,8.00,8.00,8.00
+,Tukey's Hinges,,,3.50,5.00,6.50,,
+
+Table: Case Processing Summary
+,Cases,,,,,
+,Valid,,Missing,,Total,
+,N,Percent,N,Percent,N,Percent
+X,3,100%,0,0%,3,100%
+
+Table: Percentiles
+,,Percentiles,,,,,,
+,,5,10,25,50,75,90,95
+X,Empirical with averaging,2.00,2.00,2.00,5.00,8.00,8.00,8.00
+,Tukey's Hinges,,,3.50,5.00,6.50,,
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
index 11534385ce509ff3f8f5153aa6cb88c09b0cfd0e..8fc9f7ca8f3791a20416d1ef354fcdfc3241b53a 100755 (executable)
@@ -104,160 +104,133 @@ if [ $? -ne 0 ] ; then no_result ; fi
 # NOTE:  In the following data: Only the extreme values have been checked
 # The descriptives have been blindly pasted.
 activity="compare results"
-perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
-diff -b  $TEMPDIR/pspp.list - << EOF
-1.1 DATA LIST.  Reading free-form data from INLINE.
-+--------+------+
-|Variable|Format|
-#========#======#
-|QUALITY |F8.0  |
-|W       |F8.0  |
-|BRAND   |F8.0  |
-+--------+------+
-Case#  QUALITY        W    BRAND
------ -------- -------- --------
-    1     3.00     1.00     1.00 
-    2     2.00     2.00     1.00 
-    3     1.00     2.00     1.00 
-    4     1.00     1.00     1.00 
-    5     4.00     1.00     1.00 
-    6     4.00     1.00     1.00 
-    7     5.00     1.00     2.00 
-    8     2.00     1.00     2.00 
-    9     4.00     4.00     2.00 
-   10     2.00     1.00     2.00 
-   11     3.00     1.00     2.00 
-   12     7.00     1.00     3.00 
-   13     4.00     2.00     3.00 
-   14     5.00     3.00     3.00 
-   15     3.00     1.00     3.00 
-   16     6.00     1.00     3.00 
-2.1 EXAMINE.  Case Processing Summary
-#===============#=======================================#
-#               #                 Cases                 #
-#               #-------------+-----------+-------------#
-#               #    Valid    |  Missing  |    Total    #
-#               #-----+-------+---+-------+-----+-------#
-#               #  N  |Percent| N |Percent|  N  |Percent#
-#===============#=====#=======#===#=======#=====#=======#
-#Breaking Strain#24.00|   100%|.00|     0%|24.00|   100%#
-#===============#=====#=======#===#=======#=====#=======#
-2.2 EXAMINE.  Extreme Values
-#=========================#===========#=====#
-#                         #Case Number|Value#
-#=========================#===========#=====#
-#Breaking Strain Highest 1#         12| 7.00#
-#                        2#         16| 6.00#
-#                               3#          7| 5.00#
-#               ----------#-----------+-----#
-#                 Lowest 1#          3| 1.00#
-#                        2#          3| 1.00#
-#                        3#          4| 1.00#
-#=========================#===========#=====#
-2.3 EXAMINE.  Descriptives
-#============================================================#=========#==========#
-#                                                            #Statistic|Std. Error#
-#============================================================#=========#==========#
-#Breaking Strain Mean                                        #   3.54  |    .32   #
-#                95% Confidence Interval for Mean Lower Bound#   2.87  |          #
-#                                                 Upper Bound#   4.21  |          #
-#                5% Trimmed Mean                             #   3.50  |          #
-#                Median                                      #   4.00  |          #
-#                Variance                                    #   2.52  |          #
-#                Std. Deviation                              #   1.59  |          #
-#                Minimum                                     #   1.00  |          #
-#                Maximum                                     #   7.00  |          #
-#                Range                                       #   6.00  |          #
-#                Interquartile Range                         #   2.75  |          #
-#                Skewness                                    #   .06   |    .47   #
-#                Kurtosis                                    #   -.36  |    .92   #
-#============================================================#=========#==========#
-2.4 EXAMINE.  Case Processing Summary
-#============================#=====================================#
-#                            #                Cases                #
-#                            #------------+-----------+------------#
-#                            #    Valid   |  Missing  |    Total   #
-#                            #----+-------+---+-------+----+-------#
-#                Manufacturer#  N |Percent| N |Percent|  N |Percent#
-#============================#====#=======#===#=======#====#=======#
-#Breaking Strain Aspeger     #8.00|   100%|.00|     0%|8.00|   100%#
-#                Bloggs      #8.00|   100%|.00|     0%|8.00|   100%#
-#                Charlies    #8.00|   100%|.00|     0%|8.00|   100%#
-#============================#====#=======#===#=======#====#=======#
-2.5 EXAMINE.  Extreme Values
-#======================================#===========#=====#
-#                Manufacturer          #Case Number|Value#
-#======================================#===========#=====#
-#Breaking Strain Aspeger      Highest 1#          5| 4.00#
-#                                     2#          6| 4.00#
-#                                     3#          1| 3.00#
-#                            ----------#-----------+-----#
-#                              Lowest 1#          3| 1.00#
-#                                     2#          3| 1.00#
-#                                     3#          4| 1.00#
-#               -----------------------#-----------+-----#
-#                Bloggs       Highest 1#          7| 5.00#
-#                                     2#          9| 4.00#
-#                                     3#          9| 4.00#
-#                            ----------#-----------+-----#
-#                              Lowest 1#          8| 2.00#
-#                                     2#         10| 2.00#
-#                                     3#         11| 3.00#
-#               -----------------------#-----------+-----#
-#                       Charlies     Highest 1#         12| 7.00#
-#                                     2#         16| 6.00#
-#                                     3#         14| 5.00#
-#                            ----------#-----------+-----#
-#                              Lowest 1#         15| 3.00#
-#                                     2#         13| 4.00#
-#                                     3#         13| 4.00#
-#======================================#===========#=====#
-2.6 EXAMINE.  Descriptives
-#=========================================================================#=========#==========#
-#                Manufacturer                                             #Statistic|Std. Error#
-#=========================================================================#=========#==========#
-#Breaking Strain Aspeger      Mean                                        #   2.25  |    .45   #
-#                             95% Confidence Interval for Mean Lower Bound#   1.18  |          #
-#                                                              Upper Bound#   3.32  |          #
-#                             5% Trimmed Mean                             #   2.22  |          #
-#                             Median                                      #   2.00  |          #
-#                             Variance                                    #   1.64  |          #
-#                             Std. Deviation                              #   1.28  |          #
-#                             Minimum                                     #   1.00  |          #
-#                             Maximum                                     #   4.00  |          #
-#                             Range                                       #   3.00  |          #
-#                             Interquartile Range                         #   2.75  |          #
-#                             Skewness                                    #   .47   |    .75   #
-#                             Kurtosis                                    #  -1.55  |   1.48   #
-#               ----------------------------------------------------------#---------+----------#
-#                Bloggs       Mean                                        #   3.50  |    .38   #
-#                             95% Confidence Interval for Mean Lower Bound#   2.61  |          #
-#                                                              Upper Bound#   4.39  |          #
-#                             5% Trimmed Mean                             #   3.50  |          #
-#                             Median                                      #   4.00  |          #
-#                             Variance                                    #   1.14  |          #
-#                             Std. Deviation                              #   1.07  |          #
-#                             Minimum                                     #   2.00  |          #
-#                             Maximum                                     #   5.00  |          #
-#                             Range                                       #   3.00  |          #
-#                             Interquartile Range                         #   1.75  |          #
-#                             Skewness                                    #   -.47  |    .75   #
-#                             Kurtosis                                    #   -.83  |   1.48   #
-#               ----------------------------------------------------------#---------+----------#
-#                Charlies     Mean                                        #   4.88  |    .44   #
-#                             95% Confidence Interval for Mean Lower Bound#   3.83  |          #
-#                                                              Upper Bound#   5.92  |          #
-#                             5% Trimmed Mean                             #   4.86  |          #
-#                             Median                                      #   5.00  |          #
-#                             Variance                                    #   1.55  |          #
-#                             Std. Deviation                              #   1.25  |          #
-#                             Minimum                                     #   3.00  |          #
-#                             Maximum                                     #   7.00  |          #
-#                             Range                                       #   4.00  |          #
-#                             Interquartile Range                         #   1.75  |          #
-#                             Skewness                                    #   .30   |    .75   #
-#                             Kurtosis                                    #   .15   |   1.48   #
-#=========================================================================#=========#==========#
+diff -c $TEMPDIR/pspp.csv - << EOF
+Table: Reading free-form data from INLINE.
+Variable,Format
+QUALITY,F8.0
+W,F8.0
+BRAND,F8.0
+
+Table: Data List
+Case Number,QUALITY,W,BRAND
+1,3.00,1.00,1.00
+2,2.00,2.00,1.00
+3,1.00,2.00,1.00
+4,1.00,1.00,1.00
+5,4.00,1.00,1.00
+6,4.00,1.00,1.00
+7,5.00,1.00,2.00
+8,2.00,1.00,2.00
+9,4.00,4.00,2.00
+10,2.00,1.00,2.00
+11,3.00,1.00,2.00
+12,7.00,1.00,3.00
+13,4.00,2.00,3.00
+14,5.00,3.00,3.00
+15,3.00,1.00,3.00
+16,6.00,1.00,3.00
+
+Table: Case Processing Summary
+,Cases,,,,,
+,Valid,,Missing,,Total,
+,N,Percent,N,Percent,N,Percent
+Breaking Strain,24.00,100%,.00,0%,24.00,100%
+
+Table: Extreme Values
+,,,Case Number,Value
+Breaking Strain,Highest,1,12,7.00
+,,2,16,6.00
+,,3,7,5.00
+,Lowest,1,3,1.00
+,,2,3,1.00
+,,3,4,1.00
+
+Table: Descriptives
+,,,Statistic,Std. Error
+Breaking Strain,Mean,,3.54,.32
+,95% Confidence Interval for Mean,Lower Bound,2.87,
+,,Upper Bound,4.21,
+,5% Trimmed Mean,,3.50,
+,Median,,4.00,
+,Variance,,2.52,
+,Std. Deviation,,1.59,
+,Minimum,,1.00,
+,Maximum,,7.00,
+,Range,,6.00,
+,Interquartile Range,,2.75,
+,Skewness,,.06,.47
+,Kurtosis,,-.36,.92
+
+Table: Case Processing Summary
+,,Cases,,,,,
+,,Valid,,Missing,,Total,
+,Manufacturer,N,Percent,N,Percent,N,Percent
+Breaking Strain,Aspeger,8.00,100%,.00,0%,8.00,100%
+,Bloggs,8.00,100%,.00,0%,8.00,100%
+,Charlies,8.00,100%,.00,0%,8.00,100%
+
+Table: Extreme Values
+,Manufacturer,,,Case Number,Value
+Breaking Strain,Aspeger,Highest,1,5,4.00
+,,,2,6,4.00
+,,,3,1,3.00
+,,Lowest,1,3,1.00
+,,,2,3,1.00
+,,,3,4,1.00
+,Bloggs,Highest,1,7,5.00
+,,,2,9,4.00
+,,,3,9,4.00
+,,Lowest,1,8,2.00
+,,,2,10,2.00
+,,,3,11,3.00
+,Charlies,Highest,1,12,7.00
+,,,2,16,6.00
+,,,3,14,5.00
+,,Lowest,1,15,3.00
+,,,2,13,4.00
+,,,3,13,4.00
+
+Table: Descriptives
+,Manufacturer,,,Statistic,Std. Error
+Breaking Strain,Aspeger,Mean,,2.25,.45
+,,95% Confidence Interval for Mean,Lower Bound,1.18,
+,,,Upper Bound,3.32,
+,,5% Trimmed Mean,,2.22,
+,,Median,,2.00,
+,,Variance,,1.64,
+,,Std. Deviation,,1.28,
+,,Minimum,,1.00,
+,,Maximum,,4.00,
+,,Range,,3.00,
+,,Interquartile Range,,2.75,
+,,Skewness,,.47,.75
+,,Kurtosis,,-1.55,1.48
+,Bloggs,Mean,,3.50,.38
+,,95% Confidence Interval for Mean,Lower Bound,2.61,
+,,,Upper Bound,4.39,
+,,5% Trimmed Mean,,3.50,
+,,Median,,4.00,
+,,Variance,,1.14,
+,,Std. Deviation,,1.07,
+,,Minimum,,2.00,
+,,Maximum,,5.00,
+,,Range,,3.00,
+,,Interquartile Range,,1.75,
+,,Skewness,,-.47,.75
+,,Kurtosis,,-.83,1.48
+,Charlies,Mean,,4.88,.44
+,,95% Confidence Interval for Mean,Lower Bound,3.83,
+,,,Upper Bound,5.92,
+,,5% Trimmed Mean,,4.86,
+,,Median,,5.00,
+,,Variance,,1.55,
+,,Std. Deviation,,1.25,
+,,Minimum,,3.00,
+,,Maximum,,7.00,
+,,Range,,4.00,
+,,Interquartile Range,,1.75,
+,,Skewness,,.30,.75
+,,Kurtosis,,.15,1.48
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
index 3dfe52f10be98f1d213041303d85bac8fae4cff6..ed44ba6b1374f93b62dd974b6d2e9f6a9985eb2b 100755 (executable)
@@ -81,21 +81,17 @@ if [ $? -ne 0 ] ; then no_result ; fi
 
 
 activity="compare output"
-diff  -b  $TEMPDIR/pspp.list - << EOF 
-1.1 DATA LIST.  Reading free-form data from myhandle.
-+--------+------+
-|Variable|Format|
-#========#======#
-|x       |F8.0  |
-+--------+------+
-
-       x
---------
-    1.00 
-    2.00 
-    5.00 
-  109.00 
-
+diff -c $TEMPDIR/pspp.csv - << EOF 
+Table: Reading free-form data from myhandle.
+Variable,Format
+x,F8.0
+
+Table: Data List
+x
+1.00
+2.00
+5.00
+109.00
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
index e6211b4d4b8090189628ddd8c11e6fcc4de98122..4dc929eaa39833a23d1ee8158c5dcd914f9d2a98 100755 (executable)
@@ -124,49 +124,93 @@ if [ $? -ne 0 ] ; then no_result ; fi
 
 # We need to filter out the dates/times
 activity="date filter"
-grep -v '[Ee]ntered' $TEMPDIR/pspp.list > $TEMPDIR/pspp.filtered
+sed 's/(Entered [^)]*)/(Entered <date>)/' $TEMPDIR/pspp.csv > $TEMPDIR/pspp.filtered
 if [ $? -ne 0 ] ; then no_result ; fi
 
 
 activity="compare results"
-perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.filtered
-diff -b  $TEMPDIR/pspp.filtered - <<EOF
-1.1 DATA LIST.  Reading 1 record from INLINE.
-+--------+------+-------+------+
-|Variable|Record|Columns|Format|
-#========#======#=======#======#
-|X       |     1|  1-  1|F1.0  |
-|Y       |     1|  2-  2|F1.0  |
-+--------+------+-------+------+
+diff -c $TEMPDIR/pspp.filtered - <<EOF
+Table: Reading 1 record from INLINE.
+Variable,Record,Columns,Format
+X,1,1-  1,F1.0
+Y,1,2-  2,F1.0
+
 Documents in the active file:
+
 document First line of a document
+
 Second line of a document
+
 The last line should end with a period: .
+
+(Entered <date>)
+
 File label:
+
 This is a test file label
+
 Documents in the active file:
+
 document First line of a document
+
 Second line of a document
+
 The last line should end with a period: .
+
+(Entered <date>)
+
 Line one
+
 Line two
+
+(Entered <date>)
+
 File label:
+
 This is a test file label
+
 Documents in the active file:
+
 document First line of a document
+
 Second line of a document
+
 The last line should end with a period: .
+
+(Entered <date>)
+
 Line one
+
 Line two
+
+(Entered <date>)
+
 document There should be another document now.
+
+(Entered <date>)
+
 Documents in the active file:
+
 document First line of a document
+
 Second line of a document
+
 The last line should end with a period: .
+
+(Entered <date>)
+
 Line one
+
 Line two
+
+(Entered <date>)
+
 document There should be another document now.
+
+(Entered <date>)
+
 File label:
+
 This is a test file label
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
index ec76fba9f099c08157cd123ec647a01df6c8450d..4c9c416151405e6e307f533038217f3cda5b6c7d 100755 (executable)
@@ -91,34 +91,35 @@ if [ $? -ne 0 ] ; then no_result ; fi
 
 
 activity="check results"
-perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
-diff  -b $TEMPDIR/pspp.list - << EOF
- X FILTER_$
--- --------
- 1     1.00 
- 3     1.00 
- 5     1.00 
- 7     1.00 
- 9     1.00 
- X FILTER_$
--- --------
- 1     1.00 
- 2      .00 
- 3     1.00 
- 4      .00 
- 5     1.00 
- 6      .00 
- 7     1.00 
- 8      .00 
- 9     1.00 
-10      .00 
- X FILTER_$
--- --------
- 2     1.00 
- 4     1.00 
- 6     1.00 
- 8     1.00 
-10     1.00 
+diff -c $TEMPDIR/pspp.csv - << EOF
+Table: Data List
+X,FILTER_$
+1,1.00
+3,1.00
+5,1.00
+7,1.00
+9,1.00
+
+Table: Data List
+X,FILTER_$
+1,1.00
+2,.00
+3,1.00
+4,.00
+5,1.00
+6,.00
+7,1.00
+8,.00
+9,1.00
+10,.00
+
+Table: Data List
+X,FILTER_$
+2,1.00
+4,1.00
+6,1.00
+8,1.00
+10,1.00
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
index a3e5caf2c896cc84ebad3058ad7bb59d775a398c..7766ad446403f7474f92073dadcf0bd158d59372 100755 (executable)
@@ -96,44 +96,47 @@ $SUPERVISOR $PSPP --testing-mode $TEMPDIR/flip.stat
 if [ $? -ne 0 ] ; then no_result ; fi
 
 activity="compare output"
-perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
-diff  -b  $TEMPDIR/pspp.list - << EOF
-N  A  B  C  D
-- -- -- -- --
-v  1  2  3  4 
-w  6  7  8  9 
-x 11 12 13 14 
-y 16 17 18 19 
-z 21 22 23 24 
-CASE_LBL        V        W        X        Y        Z
--------- -------- -------- -------- -------- --------
-A            1.00     6.00    11.00    16.00    21.00 
-B            2.00     7.00    12.00    17.00    22.00 
-C            3.00     8.00    13.00    18.00    23.00 
-D            4.00     9.00    14.00    19.00    24.00 
-CASE_LBL        A        B        C        D
--------- -------- -------- -------- --------
-V            1.00     2.00     3.00     4.00 
-W            6.00     7.00     8.00     9.00 
-X           11.00    12.00    13.00    14.00 
-Y           16.00    17.00    18.00    19.00 
-Z           21.00    22.00    23.00    24.00 
-v1 v2 v3 v4 v5 v6 v7 v8 v9 v10
--- -- -- -- -- -- -- -- -- ---
- 1  2  3  4  5  6  7  8  9  10 
- 4  5  6  7  8  9 10 11 12  13 
-CASE_LBL   VAR000   VAR001
--------- -------- --------
-v1           1.00     4.00 
-v2           2.00     5.00 
-v3           3.00     6.00 
-v4           4.00     7.00 
-v5           5.00     8.00 
-v6           6.00     9.00 
-v7           7.00    10.00 
-v8           8.00    11.00 
-v9           9.00    12.00 
-v10         10.00    13.00 
+diff -c $TEMPDIR/pspp.csv - << EOF
+Table: Data List
+N,A,B,C,D
+v,1,2,3,4
+w,6,7,8,9
+x,11,12,13,14
+y,16,17,18,19
+z,21,22,23,24
+
+Table: Data List
+CASE_LBL,V,W,X,Y,Z
+A       ,1.00,6.00,11.00,16.00,21.00
+B       ,2.00,7.00,12.00,17.00,22.00
+C       ,3.00,8.00,13.00,18.00,23.00
+D       ,4.00,9.00,14.00,19.00,24.00
+
+Table: Data List
+CASE_LBL,A,B,C,D
+V       ,1.00,2.00,3.00,4.00
+W       ,6.00,7.00,8.00,9.00
+X       ,11.00,12.00,13.00,14.00
+Y       ,16.00,17.00,18.00,19.00
+Z       ,21.00,22.00,23.00,24.00
+
+Table: Data List
+v1,v2,v3,v4,v5,v6,v7,v8,v9,v10
+1,2,3,4,5,6,7,8,9,10
+4,5,6,7,8,9,10,11,12,13
+
+Table: Data List
+CASE_LBL,VAR000,VAR001
+v1      ,1.00,4.00
+v2      ,2.00,5.00
+v3      ,3.00,6.00
+v4      ,4.00,7.00
+v5      ,5.00,8.00
+v6      ,6.00,9.00
+v7      ,7.00,10.00
+v8      ,8.00,11.00
+v9      ,9.00,12.00
+v10     ,10.00,13.00
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
index c28c8dd478c9f6dc4dd04ea8ddd5dbb2ac27a069..cf4e6ca8ec9e9a4d4e4368c3c0179220a4a0d1bb 100755 (executable)
@@ -92,120 +92,94 @@ if [ $? -ne 0 ] ; then no_result ; fi
 
 
 activity="compare output 1"
-diff $TEMPDIR/pspp.list - <<EOF
-1.1 DISPLAY.  
-+--------+-------------------------------------------+--------+
-|Variable|Description                                |Position|
-#========#===========================================#========#
-|VAR001  |Format: F8.2                               |       1|
-|        |Measure: Scale                             |        |
-|        |Display Alignment: Right                   |        |
-|        |Display Width: 8                           |        |
-+--------+-------------------------------------------+--------+
-|VAR002  |Format: A8                                 |       2|
-|        |Measure: Nominal                           |        |
-|        |Display Alignment: Left                    |        |
-|        |Display Width: 8                           |        |
-+--------+-------------------------------------------+--------+
-|VAR003  |Format: F8.2                               |       3|
-|        |Measure: Scale                             |        |
-|        |Display Alignment: Right                   |        |
-|        |Display Width: 8                           |        |
-+--------+-------------------------------------------+--------+
-
-  VAR001   VAR002   VAR003
--------- -------- --------
-     .00 fred        20.00 
-    1.00 11          21.00 
-    2.00 twelve      22.00 
-    3.00 13          23.00 
-    4.00 14          24.00 
-
-2.1 DISPLAY.  
-+--------+-------------------------------------------+--------+
-|Variable|Description                                |Position|
-#========#===========================================#========#
-|V1      |Format: F8.2                               |       1|
-|        |Measure: Scale                             |        |
-|        |Display Alignment: Right                   |        |
-|        |Display Width: 8                           |        |
-+--------+-------------------------------------------+--------+
-|V2      |Format: A8                                 |       2|
-|        |Measure: Nominal                           |        |
-|        |Display Alignment: Left                    |        |
-|        |Display Width: 8                           |        |
-+--------+-------------------------------------------+--------+
-|VAR001  |Format: F8.2                               |       3|
-|        |Measure: Scale                             |        |
-|        |Display Alignment: Right                   |        |
-|        |Display Width: 8                           |        |
-+--------+-------------------------------------------+--------+
-
-      V1       V2   VAR001
--------- -------- --------
-     .00 fred        20.00 
-    1.00 11          21.00 
-    2.00 twelve      22.00 
-    3.00 13          23.00 
-    4.00 14          24.00 
-
-3.1 DISPLAY.  
-+--------+-------------------------------------------+--------+
-|Variable|Description                                |Position|
-#========#===========================================#========#
-|name    |Format: A8                                 |       1|
-|        |Measure: Nominal                           |        |
-|        |Display Alignment: Left                    |        |
-|        |Display Width: 8                           |        |
-+--------+-------------------------------------------+--------+
-|id      |Format: F8.2                               |       2|
-|        |Measure: Scale                             |        |
-|        |Display Alignment: Right                   |        |
-|        |Display Width: 8                           |        |
-+--------+-------------------------------------------+--------+
-|height  |Format: F8.2                               |       3|
-|        |Measure: Scale                             |        |
-|        |Display Alignment: Right                   |        |
-|        |Display Width: 8                           |        |
-+--------+-------------------------------------------+--------+
-
-    name       id   height
--------- -------- --------
-fred          .00    23.40 
-bert         1.00      .56 
-charlie      2.00      .   
-dick         3.00   -34.09 
-
-4.1 DISPLAY.  
-+--------+-------------------------------------------+--------+
-|Variable|Description                                |Position|
-#========#===========================================#========#
-|vone    |Format: F8.2                               |       1|
-|        |Measure: Scale                             |        |
-|        |Display Alignment: Right                   |        |
-|        |Display Width: 8                           |        |
-+--------+-------------------------------------------+--------+
-|vtwo    |Format: F8.2                               |       2|
-|        |Measure: Scale                             |        |
-|        |Display Alignment: Right                   |        |
-|        |Display Width: 8                           |        |
-+--------+-------------------------------------------+--------+
-|vthree  |Format: A8                                 |       3|
-|        |Measure: Nominal                           |        |
-|        |Display Alignment: Left                    |        |
-|        |Display Width: 8                           |        |
-+--------+-------------------------------------------+--------+
-|v4      |Format: F8.2                               |       4|
-|        |Measure: Scale                             |        |
-|        |Display Alignment: Right                   |        |
-|        |Display Width: 8                           |        |
-+--------+-------------------------------------------+--------+
-
-    vone     vtwo   vthree       v4
--------- -------- -------- --------
-    1.00     3.00              5.00 
-    2.00     4.00              6.00 
-
+diff -c $TEMPDIR/pspp.csv - <<EOF
+Variable,Description,,Position
+VAR001,Format: F8.2,,1
+,Measure: Scale,,
+,Display Alignment: Right,,
+,Display Width: 8,,
+VAR002,Format: A8,,2
+,Measure: Nominal,,
+,Display Alignment: Left,,
+,Display Width: 8,,
+VAR003,Format: F8.2,,3
+,Measure: Scale,,
+,Display Alignment: Right,,
+,Display Width: 8,,
+
+Table: Data List
+VAR001,VAR002,VAR003
+.00,fred    ,20.00
+1.00,11      ,21.00
+2.00,twelve  ,22.00
+3.00,13      ,23.00
+4.00,14      ,24.00
+
+Variable,Description,,Position
+V1,Format: F8.2,,1
+,Measure: Scale,,
+,Display Alignment: Right,,
+,Display Width: 8,,
+V2,Format: A8,,2
+,Measure: Nominal,,
+,Display Alignment: Left,,
+,Display Width: 8,,
+VAR001,Format: F8.2,,3
+,Measure: Scale,,
+,Display Alignment: Right,,
+,Display Width: 8,,
+
+Table: Data List
+V1,V2,VAR001
+.00,fred    ,20.00
+1.00,11      ,21.00
+2.00,twelve  ,22.00
+3.00,13      ,23.00
+4.00,14      ,24.00
+
+Variable,Description,,Position
+name,Format: A8,,1
+,Measure: Nominal,,
+,Display Alignment: Left,,
+,Display Width: 8,,
+id,Format: F8.2,,2
+,Measure: Scale,,
+,Display Alignment: Right,,
+,Display Width: 8,,
+height,Format: F8.2,,3
+,Measure: Scale,,
+,Display Alignment: Right,,
+,Display Width: 8,,
+
+Table: Data List
+name,id,height
+fred    ,.00,23.40
+bert    ,1.00,.56
+charlie ,2.00,.  
+dick    ,3.00,-34.09
+
+Variable,Description,,Position
+vone,Format: F8.2,,1
+,Measure: Scale,,
+,Display Alignment: Right,,
+,Display Width: 8,,
+vtwo,Format: F8.2,,2
+,Measure: Scale,,
+,Display Alignment: Right,,
+,Display Width: 8,,
+vthree,Format: A8,,3
+,Measure: Nominal,,
+,Display Alignment: Left,,
+,Display Width: 8,,
+v4,Format: F8.2,,4
+,Measure: Scale,,
+,Display Alignment: Right,,
+,Display Width: 8,,
+
+Table: Data List
+vone,vtwo,vthree,v4
+1.00,3.00,,5.00
+2.00,4.00,,6.00
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
@@ -227,10 +201,10 @@ $SUPERVISOR $PSPP --testing-mode -o raw-ascii $TESTFILE > /dev/null
 if [ $? -ne 0 ] ; then fail ; fi
 
 activity="compare output 2"
-diff $TEMPDIR/pspp.list - <<EOF
-warning: Selected sheet or range of spreadsheet "$TEMPDIR/Book1.gnumeric" is empty.
-warning: Selected sheet or range of spreadsheet "$TEMPDIR/Book1.gnumeric" is empty.
+diff -c $TEMPDIR/pspp.csv - <<EOF
+"warning: Selected sheet or range of spreadsheet ""$TEMPDIR/Book1.gnumeric"" is empty."
 
+"warning: Selected sheet or range of spreadsheet ""$TEMPDIR/Book1.gnumeric"" is empty."
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
index 1d8afadaa815f2ece469e7d488845844540f8951..081c79745adb0f2dfe50ae5c8ed6e82b3581bfa1 100755 (executable)
@@ -206,127 +206,102 @@ $SUPERVISOR $PSPP --testing-mode -o raw-ascii $TESTFILE
 if [ $? -ne 0 ] ; then no_result ; fi
 
 activity="compare output 1"
-perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
-diff -b  $TEMPDIR/pspp.list - << 'EOF'
-1.1 DISPLAY.  
-+---------------+-------------------------------------------+--------+
-|Variable       |Description                                |Position|
-#===============#===========================================#========#
-|bool           |Format: F8.2                               |       1|
-|               |Measure: Scale                             |        |
-|               |Display Alignment: Right                   |        |
-|               |Display Width: 8                           |        |
-+---------------+-------------------------------------------+--------+
-|bytea          |Format: AHEX2                              |       2|
-|               |Measure: Nominal                           |        |
-|               |Display Alignment: Left                    |        |
-|               |Display Width: 1                           |        |
-+---------------+-------------------------------------------+--------+
-|char           |Format: A8                                 |       3|
-|               |Measure: Nominal                           |        |
-|               |Display Alignment: Left                    |        |
-|               |Display Width: 8                           |        |
-+---------------+-------------------------------------------+--------+
-|int8           |Format: F8.2                               |       4|
-|               |Measure: Scale                             |        |
-|               |Display Alignment: Right                   |        |
-|               |Display Width: 8                           |        |
-+---------------+-------------------------------------------+--------+
-|int2           |Format: F8.2                               |       5|
-|               |Measure: Scale                             |        |
-|               |Display Alignment: Right                   |        |
-|               |Display Width: 8                           |        |
-+---------------+-------------------------------------------+--------+
-|int4           |Format: F8.2                               |       6|
-|               |Measure: Scale                             |        |
-|               |Display Alignment: Right                   |        |
-|               |Display Width: 8                           |        |
-+---------------+-------------------------------------------+--------+
-|numeric        |Format: E40.6                              |       7|
-|               |Measure: Scale                             |        |
-|               |Display Alignment: Right                   |        |
-|               |Display Width: 8                           |        |
-+---------------+-------------------------------------------+--------+
-|text           |Format: A16                                |       8|
-|               |Measure: Nominal                           |        |
-|               |Display Alignment: Left                    |        |
-|               |Display Width: 16                          |        |
-+---------------+-------------------------------------------+--------+
-|oid            |Format: F8.2                               |       9|
-|               |Measure: Scale                             |        |
-|               |Display Alignment: Right                   |        |
-|               |Display Width: 8                           |        |
-+---------------+-------------------------------------------+--------+
-|float4         |Format: F8.2                               |      10|
-|               |Measure: Scale                             |        |
-|               |Display Alignment: Right                   |        |
-|               |Display Width: 8                           |        |
-+---------------+-------------------------------------------+--------+
-|float8         |Format: F8.2                               |      11|
-|               |Measure: Scale                             |        |
-|               |Display Alignment: Right                   |        |
-|               |Display Width: 8                           |        |
-+---------------+-------------------------------------------+--------+
-|money          |Format: DOLLAR8.2                          |      12|
-|               |Measure: Scale                             |        |
-|               |Display Alignment: Right                   |        |
-|               |Display Width: 8                           |        |
-+---------------+-------------------------------------------+--------+
-|pbchar         |Format: A8                                 |      13|
-|               |Measure: Nominal                           |        |
-|               |Display Alignment: Left                    |        |
-|               |Display Width: 8                           |        |
-+---------------+-------------------------------------------+--------+
-|varchar        |Format: A8                                 |      14|
-|               |Measure: Nominal                           |        |
-|               |Display Alignment: Left                    |        |
-|               |Display Width: 8                           |        |
-+---------------+-------------------------------------------+--------+
-|date           |Format: DATE11                             |      15|
-|               |Measure: Scale                             |        |
-|               |Display Alignment: Right                   |        |
-|               |Display Width: 8                           |        |
-+---------------+-------------------------------------------+--------+
-|time           |Format: TIME11.0                           |      16|
-|               |Measure: Scale                             |        |
-|               |Display Alignment: Right                   |        |
-|               |Display Width: 8                           |        |
-+---------------+-------------------------------------------+--------+
-|timestamp      |Format: DATETIME22.0                       |      17|
-|               |Measure: Scale                             |        |
-|               |Display Alignment: Right                   |        |
-|               |Display Width: 8                           |        |
-+---------------+-------------------------------------------+--------+
-|timestamptz    |Format: DATETIME22.0                       |      18|
-|               |Measure: Scale                             |        |
-|               |Display Alignment: Right                   |        |
-|               |Display Width: 8                           |        |
-+---------------+-------------------------------------------+--------+
-|interval       |Format: DTIME13.0                          |      19|
-|               |Measure: Scale                             |        |
-|               |Display Alignment: Right                   |        |
-|               |Display Width: 8                           |        |
-+---------------+-------------------------------------------+--------+
-|interval_months|Format: F3.0                               |      20|
-|               |Measure: Scale                             |        |
-|               |Display Alignment: Right                   |        |
-|               |Display Width: 8                           |        |
-+---------------+-------------------------------------------+--------+
-|timetz         |Format: TIME11.0                           |      21|
-|               |Measure: Scale                             |        |
-|               |Display Alignment: Right                   |        |
-|               |Display Width: 8                           |        |
-+---------------+-------------------------------------------+--------+
-|timetz_zone    |Format: F8.2                               |      22|
-|               |Measure: Scale                             |        |
-|               |Display Alignment: Right                   |        |
-|               |Display Width: 8                           |        |
-+---------------+-------------------------------------------+--------+
-    bool bytea     char     int8     int2     int4                                  numeric             text      oid   float4   float8    money   pbchar  varchar        date        time              timestamp            timestamptz      interval interval_months      timetz timetz_zone
--------- ----- -------- -------- -------- -------- ---------------------------------------- ---------------- -------- -------- -------- -------- -------- -------- ----------- ----------- ---------------------- ---------------------- ------------- --------------- ----------- -----------
-     .00    30 a             .00      .00      .00                           -2.560980E+002 this-long-text        .00      .00      .00     $.01 a        A        01-JAN-2000     0:00:00   08-JAN-1999 04:05:06   08-JAN-1999 12:05:06    0 00:01:00               0    10:09:00        4.00 
-     .      20               .        .        .                                .                                 .        .        .        .                               .           .                      .                      .             .               .           .         .   
-    1.00    31 b            1.00     1.00     1.00                            6.553500E+004 that-long-text        .00     1.00     1.00    $1.23 b        B        10-JAN-1963     1:05:02   10-JAN-1963 23:58:00   10-JAN-1963 22:58:00   12 01:03:04              25     1:05:02       -7.00 
+diff -c $TEMPDIR/pspp.csv - << 'EOF'
+Variable,Description,,Position
+bool,Format: F8.2,,1
+,Measure: Scale,,
+,Display Alignment: Right,,
+,Display Width: 8,,
+bytea,Format: AHEX2,,2
+,Measure: Nominal,,
+,Display Alignment: Left,,
+,Display Width: 1,,
+char,Format: A8,,3
+,Measure: Nominal,,
+,Display Alignment: Left,,
+,Display Width: 8,,
+int8,Format: F8.2,,4
+,Measure: Scale,,
+,Display Alignment: Right,,
+,Display Width: 8,,
+int2,Format: F8.2,,5
+,Measure: Scale,,
+,Display Alignment: Right,,
+,Display Width: 8,,
+int4,Format: F8.2,,6
+,Measure: Scale,,
+,Display Alignment: Right,,
+,Display Width: 8,,
+numeric,Format: E40.6,,7
+,Measure: Scale,,
+,Display Alignment: Right,,
+,Display Width: 8,,
+text,Format: A16,,8
+,Measure: Nominal,,
+,Display Alignment: Left,,
+,Display Width: 16,,
+oid,Format: F8.2,,9
+,Measure: Scale,,
+,Display Alignment: Right,,
+,Display Width: 8,,
+float4,Format: F8.2,,10
+,Measure: Scale,,
+,Display Alignment: Right,,
+,Display Width: 8,,
+float8,Format: F8.2,,11
+,Measure: Scale,,
+,Display Alignment: Right,,
+,Display Width: 8,,
+money,Format: DOLLAR8.2,,12
+,Measure: Scale,,
+,Display Alignment: Right,,
+,Display Width: 8,,
+pbchar,Format: A8,,13
+,Measure: Nominal,,
+,Display Alignment: Left,,
+,Display Width: 8,,
+varchar,Format: A8,,14
+,Measure: Nominal,,
+,Display Alignment: Left,,
+,Display Width: 8,,
+date,Format: DATE11,,15
+,Measure: Scale,,
+,Display Alignment: Right,,
+,Display Width: 8,,
+time,Format: TIME11.0,,16
+,Measure: Scale,,
+,Display Alignment: Right,,
+,Display Width: 8,,
+timestamp,Format: DATETIME22.0,,17
+,Measure: Scale,,
+,Display Alignment: Right,,
+,Display Width: 8,,
+timestamptz,Format: DATETIME22.0,,18
+,Measure: Scale,,
+,Display Alignment: Right,,
+,Display Width: 8,,
+interval,Format: DTIME13.0,,19
+,Measure: Scale,,
+,Display Alignment: Right,,
+,Display Width: 8,,
+interval_months,Format: F3.0,,20
+,Measure: Scale,,
+,Display Alignment: Right,,
+,Display Width: 8,,
+timetz,Format: TIME11.0,,21
+,Measure: Scale,,
+,Display Alignment: Right,,
+,Display Width: 8,,
+timetz_zone,Format: F8.2,,22
+,Measure: Scale,,
+,Display Alignment: Right,,
+,Display Width: 8,,
+
+Table: Data List
+bool,bytea,char,int8,int2,int4,numeric,text,oid,float4,float8,money,pbchar,varchar,date,time,timestamp,timestamptz,interval,interval_months,timetz,timetz_zone
+.00,30,a       ,.00,.00,.00,-2.560980E+002,this-long-text  ,.00,.00,.00,$.01,a       ,A       ,01-JAN-2000,0:00:00,08-JAN-1999 04:05:06,08-JAN-1999 12:05:06,0 00:01:00,0,10:09:00,4.00
+.  ,20,,.  ,.  ,.  ,.          ,,.  ,.  ,.  ,.  ,,,.,.,.,.,.,.,.,.  
+1.00,31,b       ,1.00,1.00,1.00,6.553500E+004,that-long-text  ,.00,1.00,1.00,$1.23,b       ,B       ,10-JAN-1963,1:05:02,10-JAN-1963 23:58:00,10-JAN-1963 22:58:00,12 01:03:04,25,1:05:02,-7.00
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
@@ -349,27 +324,20 @@ $SUPERVISOR $PSPP --testing-mode -o raw-ascii $TESTFILE
 if [ $? -ne 0 ] ; then no_result ; fi
 
 activity="compare output 2"
-perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
-diff -b  $TEMPDIR/pspp.list - << 'EOF'
-1.1 DISPLAY.  
-+--------+-------------------------------------------+--------+
-|Variable|Description                                |Position|
-#========#===========================================#========#
-|a       |Format: F8.2                               |       1|
-|        |Measure: Scale                             |        |
-|        |Display Alignment: Right                   |        |
-|        |Display Width: 8                           |        |
-+--------+-------------------------------------------+--------+
-|b       |Format: DATE11                             |       2|
-|        |Measure: Scale                             |        |
-|        |Display Alignment: Right                   |        |
-|        |Display Width: 8                           |        |
-+--------+-------------------------------------------+--------+
-|c       |Format: E40.2                              |       3|
-|        |Measure: Scale                             |        |
-|        |Display Alignment: Right                   |        |
-|        |Display Width: 8                           |        |
-+--------+-------------------------------------------+--------+
+diff -c $TEMPDIR/pspp.csv - << 'EOF'
+Variable,Description,,Position
+a,Format: F8.2,,1
+,Measure: Scale,,
+,Display Alignment: Right,,
+,Display Width: 8,,
+b,Format: DATE11,,2
+,Measure: Scale,,
+,Display Alignment: Right,,
+,Display Width: 8,,
+c,Format: E40.2,,3
+,Measure: Scale,,
+,Display Alignment: Right,,
+,Display Width: 8,,
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
@@ -406,24 +374,24 @@ $SUPERVISOR $PSPP --testing-mode -o raw-ascii $TESTFILE
 if [ $? -ne 0 ] ; then no_result ; fi
 
 activity="compare output 3"
-perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
-diff -b  $TEMPDIR/pspp.list - << 'EOF'
-       x     diff
--------- --------
-    1.00      .   
-    2.00     1.00 
-    3.00     1.00 
-    4.00     1.00 
-    5.00     1.00 
-    6.00     1.00 
-       x     diff
--------- --------
- 1000.00     1.00 
-  999.00     1.00 
-  998.00     1.00 
-  997.00     1.00 
-  996.00     1.00 
-  995.00     1.00 
+diff -c $TEMPDIR/pspp.csv - << 'EOF'
+Table: Data List
+x,diff
+1.00,.  
+2.00,1.00
+3.00,1.00
+4.00,1.00
+5.00,1.00
+6.00,1.00
+
+Table: Data List
+x,diff
+1000.00,1.00
+999.00,1.00
+998.00,1.00
+997.00,1.00
+996.00,1.00
+995.00,1.00
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
index c601b50672bce576545f530392d815b59937af7e..1e1a51648847ed8eda4ad1c7660bbdc0c7ac25be 100755 (executable)
@@ -136,32 +136,34 @@ $SUPERVISOR $PSPP --testing-mode test.pspp
 if [ $? -ne 0 ] ; then no_result ; fi
 
 activity="compare test results"
-perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
-diff -b  $TEMPDIR/pspp.list - <<'EOF'
-            username                                 password        uid        gid                                    gecos                                     home                                    shell
--------------------- ---------------------------------------- ---------- ---------- ---------------------------------------- ---------------------------------------- ----------------------------------------
-root                 $1$nyeSP5gD$pDq/                                  0          0 ,,,                                      /root                                    /bin/bash
-blp                  $1$BrP/pFg4$g7OG                               1000       1000 Ben Pfaff,,,                             /home/blp                                /bin/bash
-john                 $1$JBuq/Fioq$g4A                               1001       1001 John Darrington,,,                       /home/john                               /bin/bash
-jhs                  $1$D3li4hPL$88X1                               1002       1002 Jason Stover,,,                          /home/jhs                                /bin/csh
-   model year mileage price type age
--------- ---- ------- ----- ---- ---
-Civic    2002   29883 15900 Si     2
-Civic    2003   13415 15900 EX     1
-Civic    1992  107000  3800 n/a   12
-Accord   2002   26613 17900 EX     1
-   model     year  mileage    price     type      age
--------- -------- -------- -------- -------- --------
-Civic        2002    29883    15900 Si              2
-Civic        2003    13415    15900 EX              1
-Civic        1992   107000     3800 n/a            12
-Accord       2002    26613    17900 EX              1
-      name  age color   received  price height       type
----------- ---- ----- ---------- ------ ------ ----------
-Rover       4.5 Brown 12.02.2004  80.00  1'4"  Dog
-Charlie      .  Gold  05.04.2007  12.30  3"    Fish
-Molly       2.0 Black 12.12.2006  25.00  5"    Cat
-Gilly        .  White 10.04.2007  10.00  3"    Guinea Pig
+diff -c $TEMPDIR/pspp.csv - <<'EOF'
+Table: Data List
+username,password,uid,gid,gecos,home,shell
+root                ,$1$nyeSP5gD$pDq/                        ,0,0,",,,                                     ",/root                                   ,/bin/bash                               
+blp                 ,$1$BrP/pFg4$g7OG                        ,1000,1000,"Ben Pfaff,,,                            ",/home/blp                               ,/bin/bash                               
+john                ,$1$JBuq/Fioq$g4A                        ,1001,1001,"John Darrington,,,                      ",/home/john                              ,/bin/bash                               
+jhs                 ,$1$D3li4hPL$88X1                        ,1002,1002,"Jason Stover,,,                         ",/home/jhs                               ,/bin/csh                                
+
+Table: Data List
+model,year,mileage,price,type,age
+Civic   ,2002,29883,15900,Si  ,2
+Civic   ,2003,13415,15900,EX  ,1
+Civic   ,1992,107000,3800,n/a ,12
+Accord  ,2002,26613,17900,EX  ,1
+
+Table: Data List
+model,year,mileage,price,type,age
+Civic   ,2002,29883,15900,Si      ,2
+Civic   ,2003,13415,15900,EX      ,1
+Civic   ,1992,107000,3800,n/a     ,12
+Accord  ,2002,26613,17900,EX      ,1
+
+Table: Data List
+name,age,color,received,price,height,type
+Rover     ,4.5,Brown,12.02.2004,80.00,"1'4"" ",Dog       
+Charlie   ,. ,Gold ,05.04.2007,12.30,"3""   ",Fish      
+Molly     ,2.0,Black,12.12.2006,25.00,"5""   ",Cat       
+Gilly     ,. ,White,10.04.2007,10.00,"3""   ",Guinea Pig
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
index 1c7b7cefca8e99ce9b4b3682ca37292769ca653e..8293f290f119b654508fdc8f682ce46dd1b99bac 100755 (executable)
@@ -90,262 +90,265 @@ $SUPERVISOR $PSPP --testing-mode $TESTFILE
 if [ $? -ne 0 ] ; then fail ; fi
 
 activity="compare output"
-perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
-diff -b  $TEMPDIR/pspp.list - << EOF
-       x
---------
-       1
-       2
-       3
-       4
-       5
-       6
-       7
-       8
-       9
-      10
-       x
---------
-       1
-       2
-       x
---------
-       1
-       2
-       3
-       4
-       5
-       6
-       7
-       8
-       9
-      10
-      11
-      12
-      13
-      14
-      15
-      16
-      17
-      18
-      19
-      20
-      21
-      22
-      23
-      24
-      25
-      26
-      27
-      28
-      29
-      30
-      31
-      32
-      33
-      34
-      35
-      36
-       x
---------
-       1
-       2
-       3
-       4
-       5
-       6
-       7
-       8
-       9
-      10
-      11
-      12
-      13
-      14
-      15
-      16
-      17
-      18
-      19
-      20
-      21
-      22
-      23
-      24
-      25
-      26
-      27
-      28
-      29
-      30
-      31
-      32
-      33
-      34
-      35
-      36
-      37
-      38
-      39
-      40
-      41
-      42
-      43
-      44
-      45
-      46
-      47
-      48
-      49
-      50
-      51
-      52
-      53
-      54
-      55
-      56
-      57
-      58
-      59
-      60
-      61
-      62
-      63
-      64
-      65
-      66
-      67
-      68
-      69
-      70
-      71
-      72
-      73
-      74
-      75
-      76
-      77
-      78
-      79
-      80
-      81
-      82
-      83
-      84
-      85
-      86
-      87
-      88
-      89
-      90
-      91
-      92
-      93
-      94
-      95
-      96
-       x
---------
-       1
-       2
-       3
-       4
-       5
-       6
-       7
-       8
-       9
-      10
-      11
-      12
-      13
-      14
-      15
-      16
-      17
-      18
-      19
-      20
-      21
-      22
-      23
-      24
-      25
-      26
-      27
-      28
-      29
-      30
-      31
-      32
-      33
-      34
-      35
-      36
-      37
-      38
-      39
-      40
-      41
-      42
-      43
-      44
-      45
-      46
-      47
-      48
-      49
-      50
-      51
-      52
-      53
-      54
-      55
-      56
-      57
-      58
-      59
-      60
-      61
-      62
-      63
-      64
-      65
-      66
-      67
-      68
-      69
-      70
-      71
-      72
-      73
-      74
-      75
-      76
-      77
-      78
-      79
-      80
-      81
-      82
-      83
-      84
-      85
-      86
-      87
-      88
-      89
-      90
-      91
-      92
-      93
-      94
-      95
-      96
-      97
-      98
-      99
-     100
+diff -c $TEMPDIR/pspp.csv - << EOF
+Table: Data List
+x
+1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+
+Table: Data List
+x
+1
+2
+
+Table: Data List
+x
+1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+
+Table: Data List
+x
+1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+
+Table: Data List
+x
+1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
index 2bd0346b52f826eafe95b7e0cdca3fbcf03bb465..01bd196c39f0bc88d37daee3e9d2b3504594aba2 100755 (executable)
@@ -156,52 +156,55 @@ $SUPERVISOR $PSPP --testing-mode $TESTFILE
 if [ $? -ne 0 ] ; then fail ; fi
 
 activity="compare output"
-perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
-diff -b  $TEMPDIR/pspp.list - << EOF
-       A        B        C        D
--------- -------- -------- --------
-    1.00    23.00    45.00     2.03
-    2.00    22.00    34.00    23.00
-    3.00    34.00    34.00    34.00
-       A        B        C        D
--------- -------- -------- --------
-     .       1.00     2.00     3.00
-     .       4.00      .       5.00
-    6.00     7.00      .       8.00
-    9.00      .00     1.00      .
-     .        .        .        .
-     .        .        .       2.00
-     .       3.00     4.00     5.00
-       A        B        C        D
--------- -------- -------- --------
-    1.00     2.00     3.00     4.00
-    1.00     2.00     3.00      .
-    1.00     2.00      .       4.00
-    1.00     2.00      .        .
-    1.00      .       3.00     4.00
-    1.00      .       3.00      .
-    1.00      .        .       4.00
-    1.00      .        .        .
-     .       2.00     3.00     4.00
-     .       2.00     3.00      .
-     .       2.00      .       4.00
-     .       2.00      .        .
-     .        .       3.00     4.00
-     .        .       3.00      .
-     .        .        .       4.00
-     .        .        .        .
-               start                  end count
--------------------- -------------------- -----
-          07/22/2007           10/06/2007   321
-          07/14/1789           08/26/1789     4
-          01/01/1972           12/31/1999   682
-x y
-- -
-1 2
-3 4
-5 6
-7 8
-9 0
+diff -b  $TEMPDIR/pspp.csv - << EOF
+Table: Data List
+A,B,C,D
+1.00,23.00,45.00,2.03
+2.00,22.00,34.00,23.00
+3.00,34.00,34.00,34.00
+
+Table: Data List
+A,B,C,D
+.  ,1.00,2.00,3.00
+.  ,4.00,.  ,5.00
+6.00,7.00,.  ,8.00
+9.00,.00,1.00,.
+.  ,.  ,.  ,.
+.  ,.  ,.  ,2.00
+.  ,3.00,4.00,5.00
+
+Table: Data List
+A,B,C,D
+1.00,2.00,3.00,4.00
+1.00,2.00,3.00,.
+1.00,2.00,.  ,4.00
+1.00,2.00,.  ,.
+1.00,.  ,3.00,4.00
+1.00,.  ,3.00,.
+1.00,.  ,.  ,4.00
+1.00,.  ,.  ,.
+.  ,2.00,3.00,4.00
+.  ,2.00,3.00,.
+.  ,2.00,.  ,4.00
+.  ,2.00,.  ,.
+.  ,.  ,3.00,4.00
+.  ,.  ,3.00,.
+.  ,.  ,.  ,4.00
+.  ,.  ,.  ,.
+
+Table: Data List
+start,end,count
+07/22/2007,10/06/2007,321
+07/14/1789,08/26/1789,4
+01/01/1972,12/31/1999,682
+
+Table: Data List
+x,y
+1,2
+3,4
+5,6
+7,8
+9,0
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
index 0cb178bf5776ade7e26ac28b5218f2eefd18e70f..9a5814027359c949933dffe35460689549ab2a5d 100755 (executable)
@@ -78,13 +78,12 @@ $SUPERVISOR $PSPP --testing-mode $TESTFILE
 if [ $? -ne 0 ] ; then no_result ; fi
 
 activity="compare output"
-perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
-diff -b  $TEMPDIR/pspp.list - << EOF
-       X        Y
--------- --------
-    1.00     2.00 
-    3.00     .
-    5.00     6.00 
+diff -c $TEMPDIR/pspp.csv - << EOF
+Table: Data List
+X,Y
+1.00,2.00
+3.00,.  
+5.00,6.00
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
index a6e8c7784a2c1666793fe934fcd02eb58c808bc6..4aa5ebd260c4d43a799b59a47fbee63266119bc7 100755 (executable)
@@ -91,13 +91,12 @@ $SUPERVISOR $PSPP --testing-mode test1.pspp
 if [ $? -ne 0 ] ; then no_result ; fi
 
 activity="compare test1 results"
-perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
-diff -b  $TEMPDIR/pspp.list - << EOF
-         X          Y
----------- ----------
-         1          4
-         2          5
-         3          6
+diff -c $TEMPDIR/pspp.csv - << EOF
+Table: Data List
+X,Y
+1,4
+2,5
+3,6
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
@@ -126,15 +125,15 @@ $SUPERVISOR $PSPP --testing-mode test2.pspp
 if [ $? -ne 0 ] ; then no_result ; fi
 
 activity="compare test2 results"
-perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
-diff -b  $TEMPDIR/pspp.list - << EOF
-         X          Y
----------- ----------
-         1          4
-         2          5
-         3          6
-         .          7
-         .          8
+perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.csv
+diff -c $TEMPDIR/pspp.csv - << EOF
+Table: Data List
+X,Y
+1,4
+2,5
+3,6
+.,7
+.,8
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
@@ -166,18 +165,18 @@ $SUPERVISOR $PSPP --testing-mode test3.pspp
 if [ $? -ne 0 ] ; then no_result ; fi
 
 activity="compare test3 results"
-perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
-diff -b  $TEMPDIR/pspp.list - << EOF
-         X
-----------
-         1
-         2
-         3
-         4
-         5
-         6
-         7
-         8
+perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.csv
+diff -c $TEMPDIR/pspp.csv - << EOF
+Table: Data List
+X
+1
+2
+3
+4
+5
+6
+7
+8
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
@@ -212,18 +211,18 @@ $SUPERVISOR $PSPP --testing-mode test4.pspp
 if [ $? -ne 0 ] ; then no_result ; fi
 
 activity="compare test4 results"
-perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
-diff -b  $TEMPDIR/pspp.list - << EOF
-         X
-----------
-         1
-         2
-         3
-         4
-         5
-         6
-         7
-         8
+perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.csv
+diff -c $TEMPDIR/pspp.csv - << EOF
+Table: Data List
+X
+1
+2
+3
+4
+5
+6
+7
+8
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
@@ -249,60 +248,60 @@ $SUPERVISOR $PSPP --testing-mode test5.pspp
 if [ $? -ne 0 ] ; then no_result ; fi
 
 activity="compare test5 results"
-perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
-diff -b  $TEMPDIR/pspp.list - << EOF
-Case#        X
------ --------
-    1     3.00
-    2     6.00
-    3     9.00
-    4    12.00
-    5    15.00
-    6    18.00
-    7    21.00
-    8    24.00
-    9    27.00
-   10    30.00
-   11    33.00
-   12    36.00
-   13    39.00
-   14    42.00
-   15    45.00
-   16    48.00
-   17    51.00
-   18    54.00
-   19    57.00
-   20    60.00
-   21    63.00
-   22    66.00
-   23    69.00
-   24    72.00
-   25    75.00
-   26    78.00
-   27    81.00
-   28    84.00
-   29    87.00
-   30    90.00
-   31    93.00
-   32    96.00
-   33    99.00
-   34   102.00
-   35   105.00
-   36   108.00
-   37   111.00
-   38   114.00
-   39   117.00
-   40   120.00
-   41   123.00
-   42   126.00
-   43   129.00
-   44   132.00
-   45   135.00
-   46   138.00
-   47   141.00
-   48   144.00
-   49   147.00
-   50   150.00
+perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.csv
+diff -c $TEMPDIR/pspp.csv - << EOF
+Table: Data List
+Case Number,X
+1,3.00
+2,6.00
+3,9.00
+4,12.00
+5,15.00
+6,18.00
+7,21.00
+8,24.00
+9,27.00
+10,30.00
+11,33.00
+12,36.00
+13,39.00
+14,42.00
+15,45.00
+16,48.00
+17,51.00
+18,54.00
+19,57.00
+20,60.00
+21,63.00
+22,66.00
+23,69.00
+24,72.00
+25,75.00
+26,78.00
+27,81.00
+28,84.00
+29,87.00
+30,90.00
+31,93.00
+32,96.00
+33,99.00
+34,102.00
+35,105.00
+36,108.00
+37,111.00
+38,114.00
+39,117.00
+40,120.00
+41,123.00
+42,126.00
+43,129.00
+44,132.00
+45,135.00
+46,138.00
+47,141.00
+48,144.00
+49,147.00
+50,150.00
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
index f80dc5fc4a0df3f1c4250ef32e0bf3bb45e7a524..c6d1464cdc1c738cfdd9509740cbfd61b0288347 100755 (executable)
@@ -214,11 +214,12 @@ $SUPERVISOR $PSPP --testing-mode -o raw-ascii $TESTFILE > /dev/null
 if [ $? -ne 1 ] ; then no_result ; fi
 
 activity="examine output 1"
-diff $TEMPDIR/pspp.list - <<EOF
+diff -c $TEMPDIR/pspp.csv - <<EOF
 $TEMPDIR/foo.sps:10: error: DISPLAY: AKSDJ is not a variable name.
+
 warning: Error encountered while ERROR=STOP is effective.
-$TEMPDIR/foo.sps:10: error: Stopping syntax file processing here to avoid a cascade of dependent command failures.
 
+$TEMPDIR/foo.sps:10: error: Stopping syntax file processing here to avoid a cascade of dependent command failures.
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
@@ -240,15 +241,14 @@ $SUPERVISOR $PSPP --testing-mode -o raw-ascii $TESTFILE > /dev/null
 if [ $? -ne 1 ] ; then no_result ; fi
 
 activity="examine output 2"
-diff $TEMPDIR/pspp.list - <<EOF
+diff $TEMPDIR/pspp.csv - <<EOF
 $TEMPDIR/foo.sps:10: error: DISPLAY: AKSDJ is not a variable name.
 
-       x
---------
-    1.00 
-    2.00 
-    3.00 
-
+Table: Data List
+x
+1.00
+2.00
+3.00
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
index bc14e1a93dfc2a6b1b5ef1c7b733b5e31b82956b..de197c97e47986e8c174f5c2003d5502e06d5ee9 100755 (executable)
@@ -81,21 +81,18 @@ $SUPERVISOR $PSPP --testing-mode $TEMPDIR/lag.stat
 if [ $? -ne 0 ] ; then no_result ; fi
 
 activity="compare result"
-perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
-diff -b  $TEMPDIR/pspp.list - <<EOF
-1.1 DATA LIST.  Reading 1 record from INLINE.
-+--------+------+-------+------+
-|Variable|Record|Columns|Format|
-#========#======#=======#======#
-|W       |     1|  1-  1|F1.0  |
-+--------+------+-------+------+
-W        X        Y        Z
-- -------- -------- --------
-1      .        .        .   
-2     1.00      .        .   
-3     2.00     1.00     1.00 
-4     3.00     2.00     2.00 
-5     4.00     3.00     3.00 
+diff -c $TEMPDIR/pspp.csv - <<EOF
+Table: Reading 1 record from INLINE.
+Variable,Record,Columns,Format
+W,1,1-  1,F1.0
+
+Table: Data List
+W,X,Y,Z
+1,.  ,.  ,.  
+2,1.00,.  ,.  
+3,2.00,1.00,1.00
+4,3.00,2.00,2.00
+5,4.00,3.00,3.00
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
index ca03b5cad88ae341abd2eb833ad8bdc5ce4b2eea..e78e4ae4f928dd74928a113561071a92d237a0f3 100755 (executable)
@@ -88,16 +88,15 @@ if [ $? -ne 0 ] ; then fail ; fi
 
 
 activity="compare output"
-perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
-diff -b  $TEMPDIR/pspp.list - << EOF
-       a        b        c
--------- -------- --------
-    1.00     2.00     3.00
-    4.00     5.00     6.00
-    7.00     8.00     9.00
-   10.00    11.00    12.00
-   13.00    14.00    15.00
-   16.00    17.00    18.00
+diff -c $TEMPDIR/pspp.csv - << EOF
+Table: Data List
+a,b,c
+1.00,2.00,3.00
+4.00,5.00,6.00
+7.00,8.00,9.00
+10.00,11.00,12.00
+13.00,14.00,15.00
+16.00,17.00,18.00
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
index 57eea9d450c11c8f4731f3ee48037a51cab1ff4f..dfeb29c332d20b121a2f77c7c4eada132937e297 100755 (executable)
@@ -83,177 +83,177 @@ if [ $? -ne 0 ] ; then no_result ; fi
 
 
 activity="compare results"
-perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
-diff -b  $TEMPDIR/pspp.list - <<EOF
-1.1 DATA LIST.  Reading 1 record from "$top_srcdir/tests/weighting.data".
-+--------+------+-------+------+
-|Variable|Record|Columns|Format|
-#========#======#=======#======#
-|AVAR    |     1|  1-  5|F5.0  |
-|BVAR    |     1|  6- 10|F5.0  |
-+--------+------+-------+------+
- AVAR  BVAR
------ -----
-   18     1 
-   19     7 
-   20    26 
-   21    76 
-   22    57 
-   23    58 
-   24    38 
-   25    38 
-   26    30 
-   27    21 
-   28    23 
-   29    24 
-   30    23 
-   31    14 
-   32    21 
-   33    21 
-   34    14 
-   35    14 
-   36    17 
-   37    11 
-   38    16 
-   39    14 
-   40    15 
-   41    14 
-   42    14 
-   43     8 
-   44    15 
-   45    10 
-   46    12 
-   47    13 
-   48    13 
-   49     5 
-   50     5 
-   51     3 
-   52     7 
-   53     6 
-   54     2 
-   55     2 
-   56     2 
-   57     3 
-   58     1 
-   59     3 
-   61     1 
-   62     3 
-   63     1 
-   64     1 
-   65     2 
-   70     1 
-   78     1 
-   79     1 
-   80     1 
-   94     1 
-Case#  AVAR  BVAR
------ ----- -----
-    1    18     1 
-    2    19     7 
-    3    20    26 
-    4    21    76 
-    5    22    57 
-    6    23    58 
-    7    24    38 
-    8    25    38 
-    9    26    30 
-   10    27    21 
-   11    28    23 
-   12    29    24 
-   13    30    23 
-   14    31    14 
-   15    32    21 
-   16    33    21 
-   17    34    14 
-   18    35    14 
-   19    36    17 
-   20    37    11 
-   21    38    16 
-   22    39    14 
-   23    40    15 
-   24    41    14 
-   25    42    14 
-   26    43     8 
-   27    44    15 
-   28    45    10 
-   29    46    12 
-   30    47    13 
-   31    48    13 
-   32    49     5 
-   33    50     5 
-   34    51     3 
-   35    52     7 
-   36    53     6 
-   37    54     2 
-   38    55     2 
-   39    56     2 
-   40    57     3 
-   41    58     1 
-   42    59     3 
-   43    61     1 
-   44    62     3 
-   45    63     1 
-   46    64     1 
-   47    65     2 
-   48    70     1 
-   49    78     1 
-   50    79     1 
-   51    80     1 
-   52    94     1 
-X000 X001 X002 X003 X004 X005 X006 X007 X008 X009 X010 X011 X012 X013 X014 X015 X016 X017 X018 X019 X020 X021 X022 X023 X024 X025 X026 X027 X028 X029 X030
----- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ----
-   7    6    7    5    3    2    4    6    6    3    4    8    5    1    3    7    8    9    0    7    3    4    8    3    1    0    6    4    0    9    1 
-   8    8    8    6    9    3    0    8    9    4    2    4    1    7    7    5    4    2    3    7    8    3    3    4    1    8    6    7    6    0    3 
-   4    9    2    6    1    1    5    0    7    9    0    9    1    8    7    1    5    2    7    2    6    4    2    7    8    5    2    2    4    2    4 
-   8    1    9    8    4    8    8    9    2    0    2    3    1    9    5    8    7    5    8    7    9    3    3    2    0    0    1    4    9    1    7 
-   4    5    2    7    7    7    8    9    8    7    0    9    5    6    3    7    2    9    8    4    5    5    4    1    5    1    6    6    5    0    6 
-   2    3    9    9    6    1    9    6    7    0    7    7    7    3    2    7    6    0    6    6    3    5    2    5    1    1    5    0    7    3    5 
-   1    6    6    7    7    9    9    6    9    1    2    6    6    4    7    6    9    9    4    4    0    4    7    4    3    9    8    9    2    3    7 
-   1    6    2    3    9    1    4    6    8    4    1    9    6    8    9    2    3    1    6    8    4    7    1    1    7    0    1    1    5    4    3 
-   3    6    8    1    3    9    3    2    3    3    7    6    0    1    2    9    4    8    9    1    1    3    1    2    1    8    2    9    5    9    9 
-   6    4    1    8    7    3    1    1    4    5    4    3    1    0    8    2    9    9    4    8    5    6    8    1    6    5    0    5    0    3    5 
-   2    2    8    4    5    3    4    0    8    3    7    4    9    5    0    7    7    1    6    6    5    1    0    8    6    4    2    9    0    7    1 
-   6    6    1    7    6    3    7    4    5    2    0    4    0    7    4    9    1    8    1    3    4    9    9    1    1    7    8    8    9    7    4 
-   9    8    6    5    7    1    3    5    8    2    6    8    6    6    1    2    0    0    7    2    2    2    0    1    0    7    8    2    6    8    2 
-   1    1    6    3    2    3    4    5    3    7    7    6    2    2    0    0    8    0    7    7    9    4    9    6    0    2    5    2    4    4    7 
-   9    9    8    1    6    6    3    6    3    7    5    6    3    8    3    3    3    0    0    0    3    5    4    2    6    1    3    6    7    0    2 
-   6    8    2    1    5    6    7    7    4    6    0    5    9    1    0    3    5    6    5    0    0    5    7    3    8    9    6    0    2    4    8 
-   0    9    5    2    7    7    4    9    5    2    6    7    5    2    6    1    5    4    5    9    5    5    2    8    0    8    0    5    3    4    0 
-   1    6    4    1    7    9    0    1    9    3    2    1    1    8    6    1    5    0    9    1    0    6    8    3    9    2    1    7    1    1    9 
-   3    7    6    3    1    8    2    8    7    1    5    8    0    1    7    4    7    8    9    3    2    8    8    3    7    1    9    4    9    6    8 
-   2    0    4    6    8    2    0    7    5    3    0    6    2    2    2    4    0    4    5    5    3    5    8    9    0    9    3    2    7    2    1 
-   7    9    7    0    6    2    0    0    9    1    9    4    0    3    8    5    9    2    8    7    6    2    6    3    2    7    6    4    6    1    8 
-   4    8    4    1    1    7    6    0    1    7    0    2    5    1    0    5    7    7    4    5    0    6    5    0    0    8    9    6    2    5    2 
-   6    9    4    9    9    7    3    7    9    7    9    9    0    9    5    6    2    9    1    0    7    2    1    5    8    1    2    3    8    8    7 
-   1    3    9    6    2    8    5    9    9    6    5    3    5    4    8    9    4    4    0    8    1    6    1    2    4    7    0    0    6    8    2 
-   0    7    0    0    4    8    9    5    2    4    3    5    8    2    0    8    3    5    8    6    9    7    3    4    9    4    5    0    0    3    6 
-X000 X001 X002 X003 X004 X005 X006 X007 X008 X009 X010 X011 X012 X013 X014 X015 X016 X017 X018 X019 X020 X021 X022 X023 X024 X025 X026 X027 X028 X029 X030 X031 X032 X033 X034 X035 X036 X037 X038 X039 X040 X041 X042 X043 X044 X045 X046 X047 X048 X049 X050 X051 X052 X053 X054 X055 X056 X057 X058 X059 X060 X061 X062 X063 X064 X065 X066 X067 X068 X069 X070 X071 X072 X073 X074 X075 X076 X077 X078 X079 X080 X081 X082 X083 X084 X085 X086 X087 X088 X089 X090 X091 X092 X093 X094 X095 X096 X097 X098 X099 X100 X101 X102 X103 X104 X105 X106 X107 X108 X109 X110 X111 X112 X113 X114 X115 X116 X117 X118 X119 X120 X121 X122 X123 X124 X125 X126
----- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ----
-   7    6    7    5    3    2    4    6    6    3    4    8    5    1    3    7    8    9    0    7    3    4    8    3    1    0    6    4    0    9    1    7    5    8    5    9    2    9    5    8    4    2    8    1    5    2    9    5    1    1    3    7    5    3    2    6    5    9    4    1    8    7    5    2    3    3    8    1    5    7    6    7    5    3    2    4    6    6    3    4    8    5    1    3    7    8    9    0    7    3    4    8    3    1    0    6    4    0    9    1    7    5    8    5    9    2    9    5    8    4    2    8    1    5    2    9    5    1    1    3    7    5    3    2    6    5    8 
-   8    8    8    6    9    3    0    8    9    4    2    4    1    7    7    5    4    2    3    7    8    3    3    4    1    8    6    7    6    0    3    6    8    1    4    7    5    5    8    6    9    0    0    2    7    9    7    3    1    0    2    2    6    6    8    7    4    1    6    8    4    5    5    5    0    6    7    1    4    8    8    8    6    9    3    0    8    9    4    2    4    1    7    7    5    4    2    3    7    8    3    3    4    1    8    6    7    6    0    3    6    8    1    4    7    5    5    8    6    9    0    0    2    7    9    7    3    1    0    2    2    6    6    8    7    4    6 
-   4    9    2    6    1    1    5    0    7    9    0    9    1    8    7    1    5    2    7    2    6    4    2    7    8    5    2    2    4    2    4    8    3    8    5    6    2    0    6    9    9    8    0    2    7    8    3    4    2    1    8    8    7    2    5    5    8    6    2    6    0    0    4    1    5    2    6    0    2    4    9    2    6    1    1    5    0    7    9    0    9    1    8    7    1    5    2    7    2    6    4    2    7    8    5    2    2    4    2    4    8    3    8    5    6    2    0    6    9    9    8    0    2    7    8    3    4    2    1    8    8    7    2    5    5    8    9 
-   8    1    9    8    4    8    8    9    2    0    2    3    1    9    5    8    7    5    8    7    9    3    3    2    0    0    1    4    9    1    7    7    3    6    8    9    6    8    8    0    1    3    3    2    2    1    0    1    6    0    8    8    4    4    0    6    4    0    2    3    6    2    6    5    7    2    7    0    0    8    1    9    8    4    8    8    9    2    0    2    3    1    9    5    8    7    5    8    7    9    3    3    2    0    0    1    4    9    1    7    7    3    6    8    9    6    8    8    0    1    3    3    2    2    1    0    1    6    0    8    8    4    4    0    6    4    1 
-   4    5    2    7    7    7    8    9    8    7    0    9    5    6    3    7    2    9    8    4    5    5    4    1    5    1    6    6    5    0    6    9    5    0    6    4    7    7    4    6    6    4    5    1    2    0    8    4    9    8    8    7    6    4    0    1    6    0    6    5    9    5    0    5    3    9    1    4    9    4    5    2    7    7    7    8    9    8    7    0    9    5    6    3    7    2    9    8    4    5    5    4    1    5    1    6    6    5    0    6    9    5    0    6    4    7    7    4    6    6    4    5    1    2    0    8    4    9    8    8    7    6    4    0    1    6    6 
-   2    3    9    9    6    1    9    6    7    0    7    7    7    3    2    7    6    0    6    6    3    5    2    5    1    1    5    0    7    3    5    1    8    6    0    5    9    1    1    8    1    8    6    9    1    0    8    8    0    4    6    5    1    9    0    3    2    8    1    9    6    2    4    6    8    9    6    7    5    2    3    9    9    6    1    9    6    7    0    7    7    7    3    2    7    6    0    6    6    3    5    2    5    1    1    5    0    7    3    5    1    8    6    0    5    9    1    1    8    1    8    6    9    1    0    8    8    0    4    6    5    1    9    0    3    2    5 
-   1    6    6    7    7    9    9    6    9    1    2    6    6    4    7    6    9    9    4    4    0    4    7    4    3    9    8    9    2    3    7    3    1    5    3    9    4    2    7    6    4    1    2    0    7    9    7    6    0    3    1    0    7    0    6    3    9    5    1    0    3    2    9    9    4    4    1    5    7    1    6    6    7    7    9    9    6    9    1    2    6    6    4    7    6    9    9    4    4    0    4    7    4    3    9    8    9    2    3    7    3    1    5    3    9    4    2    7    6    4    1    2    0    7    9    7    6    0    3    1    0    7    0    6    3    9    4 
-   1    6    2    3    9    1    4    6    8    4    1    9    6    8    9    2    3    1    6    8    4    7    1    1    7    0    1    1    5    4    3    6    2    7    1    1    9    5    9    7    9    0    8    5    9    9    7    4    0    5    2    5    2    4    6    1    6    4    1    9    1    5    0    8    0    1    3    2    0    1    6    2    3    9    1    4    6    8    4    1    9    6    8    9    2    3    1    6    8    4    7    1    1    7    0    1    1    5    4    3    6    2    7    1    1    9    5    9    7    9    0    8    5    9    9    7    4    0    5    2    5    2    4    6    1    6    8 
-   3    6    8    1    3    9    3    2    3    3    7    6    0    1    2    9    4    8    9    1    1    3    1    2    1    8    2    9    5    9    9    8    5    7    2    8    8    5    0    1    0    9    9    1    2    3    2    8    3    1    9    6    6    2    8    7    1    4    1    4    8    9    6    5    0    8    4    5    7    3    6    8    1    3    9    3    2    3    3    7    6    0    1    2    9    4    8    9    1    1    3    1    2    1    8    2    9    5    9    9    8    5    7    2    8    8    5    0    1    0    9    9    1    2    3    2    8    3    1    9    6    6    2    8    7    1    0 
-   6    4    1    8    7    3    1    1    4    5    4    3    1    0    8    2    9    9    4    8    5    6    8    1    6    5    0    5    0    3    5    9    9    7    9    8    2    0    9    6    7    3    2    1    5    0    3    5    9    7    5    4    5    4    7    2    9    9    6    1    8    4    8    7    8    8    5    3    0    6    4    1    8    7    3    1    1    4    5    4    3    1    0    8    2    9    9    4    8    5    6    8    1    6    5    0    5    0    3    5    9    9    7    9    8    2    0    9    6    7    3    2    1    5    0    3    5    9    7    5    4    5    4    7    2    9    7 
-   2    2    8    4    5    3    4    0    8    3    7    4    9    5    0    7    7    1    6    6    5    1    0    8    6    4    2    9    0    7    1    2    1    9    7    6    5    1    6    3    7    5    9    8    2    9    7    9    3    4    7    8    5    8    7    1    4    7    2    3    4    3    4    1    2    3    4    4    2    2    2    8    4    5    3    4    0    8    3    7    4    9    5    0    7    7    1    6    6    5    1    0    8    6    4    2    9    0    7    1    2    1    9    7    6    5    1    6    3    7    5    9    8    2    9    7    9    3    4    7    8    5    8    7    1    4    9 
-   6    6    1    7    6    3    7    4    5    2    0    4    0    7    4    9    1    8    1    3    4    9    9    1    1    7    8    8    9    7    4    7    5    7    5    2    2    4    6    9    6    6    4    8    3    8    8    6    7    9    0    1    0    1    4    1    8    2    4    8    6    6    9    7    5    7    2    9    5    6    6    1    7    6    3    7    4    5    2    0    4    0    7    4    9    1    8    1    3    4    9    9    1    1    7    8    8    9    7    4    7    5    7    5    2    2    4    6    9    6    6    4    8    3    8    8    6    7    9    0    1    0    1    4    1    8    5 
-   9    8    6    5    7    1    3    5    8    2    6    8    6    6    1    2    0    0    7    2    2    2    0    1    0    7    8    2    6    8    2    7    7    8    2    6    9    8    3    9    2    9    9    8    7    1    3    9    3    0    1    5    4    3    6    4    0    2    0    2    6    9    8    5    4    0    9    0    8    9    8    6    5    7    1    3    5    8    2    6    8    6    6    1    2    0    0    7    2    2    2    0    1    0    7    8    2    6    8    2    7    7    8    2    6    9    8    3    9    2    9    9    8    7    1    3    9    3    0    1    5    4    3    6    4    0    6 
-   1    1    6    3    2    3    4    5    3    7    7    6    2    2    0    0    8    0    7    7    9    4    9    6    0    2    5    2    4    4    7    7    7    3    0    9    8    4    4    3    3    4    0    7    6    2    8    4    4    7    3    4    3    5    0    3    7    8    7    5    0    4    4    0    9    0    2    9    5    1    1    6    3    2    3    4    5    3    7    7    6    2    2    0    0    8    0    7    7    9    4    9    6    0    2    5    2    4    4    7    7    7    3    0    9    8    4    4    3    3    4    0    7    6    2    8    4    4    7    3    4    3    5    0    3    7    7 
-   9    9    8    1    6    6    3    6    3    7    5    6    3    8    3    3    3    0    0    0    3    5    4    2    6    1    3    6    7    0    2    8    9    3    9    8    9    4    6    4    1    2    3    5    2    6    0    8    7    3    8    0    8    3    4    4    4    5    1    3    2    8    0    7    9    0    5    5    4    9    9    8    1    6    6    3    6    3    7    5    6    3    8    3    3    3    0    0    0    3    5    4    2    6    1    3    6    7    0    2    8    9    3    9    8    9    4    6    4    1    2    3    5    2    6    0    8    7    3    8    0    8    3    4    4    4    9 
-   6    8    2    1    5    6    7    7    4    6    0    5    9    1    0    3    5    6    5    0    0    5    7    3    8    9    6    0    2    4    8    8    4    2    1    9    8    9    9    5    5    9    0    6    0    2    2    8    8    7    0    0    4    7    6    2    8    2    3    0    7    1    1    0    2    9    1    6    8    6    8    2    1    5    6    7    7    4    6    0    5    9    1    0    3    5    6    5    0    0    5    7    3    8    9    6    0    2    4    8    8    4    2    1    9    8    9    9    5    5    9    0    6    0    2    2    8    8    7    0    0    4    7    6    2    8    4 
-   0    9    5    2    7    7    4    9    5    2    6    7    5    2    6    1    5    4    5    9    5    5    2    8    0    8    0    5    3    4    0    3    5    7    5    4    5    9    4    2    4    0    0    1    5    6    2    0    1    9    1    8    6    3    8    7    4    2    0    8    2    1    3    4    2    4    3    3    3    0    9    5    2    7    7    4    9    5    2    6    7    5    2    6    1    5    4    5    9    5    5    2    8    0    8    0    5    3    4    0    3    5    7    5    4    5    9    4    2    4    0    0    1    5    6    2    0    1    9    1    8    6    3    8    7    4    8 
-   1    6    4    1    7    9    0    1    9    3    2    1    1    8    6    1    5    0    9    1    0    6    8    3    9    2    1    7    1    1    9    4    9    6    8    6    5    8    7    7    1    1    8    4    0    6    5    7    9    6    1    9    4    9    2    6    1    4    7    4    4    1    1    4    8    6    9    0    2    1    6    4    1    7    9    0    1    9    3    2    1    1    8    6    1    5    0    9    1    0    6    8    3    9    2    1    7    1    1    9    4    9    6    8    6    5    8    7    7    1    1    8    4    0    6    5    7    9    6    1    9    4    9    2    6    1    5 
-   3    7    6    3    1    8    2    8    7    1    5    8    0    1    7    4    7    8    9    3    2    8    8    3    7    1    9    4    9    6    8    5    3    6    8    7    6    0    7    4    3    4    4    5    6    2    9    3    2    1    8    7    9    6    0    8    9    3    2    7    5    8    8    1    6    5    6    4    4    3    7    6    3    1    8    2    8    7    1    5    8    0    1    7    4    7    8    9    3    2    8    8    3    7    1    9    4    9    6    8    5    3    6    8    7    6    0    7    4    3    4    4    5    6    2    9    3    2    1    8    7    9    6    0    8    9    1 
-   2    0    4    6    8    2    0    7    5    3    0    6    2    2    2    4    0    4    5    5    3    5    8    9    0    9    3    2    7    2    1    1    3    7    8    1    9    8    0    7    3    3    3    7    5    7    1    7    1    9    2    6    4    2    5    4    4    2    9    7    3    4    3    9    4    2    6    7    9    2    0    4    6    8    2    0    7    5    3    0    6    2    2    2    4    0    4    5    5    3    5    8    9    0    9    3    2    7    2    1    1    3    7    8    1    9    8    0    7    3    3    3    7    5    7    1    7    1    9    2    6    4    2    5    4    4    1 
-   7    9    7    0    6    2    0    0    9    1    9    4    0    3    8    5    9    2    8    7    6    2    6    3    2    7    6    4    6    1    8    5    2    5    8    9    9    8    9    0    1    8    6    1    3    5    9    2    9    7    9    7    1    7    0    4    5    6    3    3    9    5    8    9    3    1    8    3    4    7    9    7    0    6    2    0    0    9    1    9    4    0    3    8    5    9    2    8    7    6    2    6    3    2    7    6    4    6    1    8    5    2    5    8    9    9    8    9    0    1    8    6    1    3    5    9    2    9    7    9    7    1    7    0    4    5    8 
-   4    8    4    1    1    7    6    0    1    7    0    2    5    1    0    5    7    7    4    5    0    6    5    0    0    8    9    6    2    5    2    7    5    7    0    7    6    6    9    0    3    9    2    0    3    4    6    0    1    2    8    3    8    3    4    0    4    8    3    0    8    8    4    3    6    3    2    6    4    4    8    4    1    1    7    6    0    1    7    0    2    5    1    0    5    7    7    4    5    0    6    5    0    0    8    9    6    2    5    2    7    5    7    0    7    6    6    9    0    3    9    2    0    3    4    6    0    1    2    8    3    8    3    4    0    4    5 
-   6    9    4    9    9    7    3    7    9    7    9    9    0    9    5    6    2    9    1    0    7    2    1    5    8    1    2    3    8    8    7    4    7    3    5    8    2    9    6    2    6    7    3    8    7    8    5    1    9    6    1    9    8    3    4    8    6    8    8    0    1    5    6    8    5    3    6    3    2    6    9    4    9    9    7    3    7    9    7    9    9    0    9    5    6    2    9    1    0    7    2    1    5    8    1    2    3    8    8    7    4    7    3    5    8    2    9    6    2    6    7    3    8    7    8    5    1    9    6    1    9    8    3    4    8    6    8 
-   1    3    9    6    2    8    5    9    9    6    5    3    5    4    8    9    4    4    0    8    1    6    1    2    4    7    0    0    6    8    2    9    3    3    8    7    4    3    6    5    1    2    8    7    8    6    8    2    3    8    2    4    7    5    8    1    3    3    4    6    1    1    5    6    6    4    9    7    2    1    3    9    6    2    8    5    9    9    6    5    3    5    4    8    9    4    4    0    8    1    6    1    2    4    7    0    0    6    8    2    9    3    3    8    7    4    3    6    5    1    2    8    7    8    6    8    2    3    8    2    4    7    5    8    1    3    8 
-   0    7    0    0    4    8    9    5    2    4    3    5    8    2    0    8    3    5    8    6    9    7    3    4    9    4    5    0    0    3    6    2    0    8    3    7    8    4    2    1    8    7    8    8    0    0    6    3    6    4    2    7    1    5    1    2    1    1    1    8    5    3    2    0    1    9    4    6    6    0    7    0    0    4    8    9    5    2    4    3    5    8    2    0    8    3    5    8    6    9    7    3    4    9    4    5    0    0    3    6    2    0    8    3    7    8    4    2    1    8    7    8    8    0    0    6    3    6    4    2    7    1    5    1    2    1    8 
+diff -c $TEMPDIR/pspp.csv - <<EOF
+"Table: Reading 1 record from ""$top_srcdir/tests/weighting.data""."
+Variable,Record,Columns,Format
+AVAR,1,1-  5,F5.0
+BVAR,1,6- 10,F5.0
+
+Table: Data List
+AVAR,BVAR
+18,1
+19,7
+20,26
+21,76
+22,57
+23,58
+24,38
+25,38
+26,30
+27,21
+28,23
+29,24
+30,23
+31,14
+32,21
+33,21
+34,14
+35,14
+36,17
+37,11
+38,16
+39,14
+40,15
+41,14
+42,14
+43,8
+44,15
+45,10
+46,12
+47,13
+48,13
+49,5
+50,5
+51,3
+52,7
+53,6
+54,2
+55,2
+56,2
+57,3
+58,1
+59,3
+61,1
+62,3
+63,1
+64,1
+65,2
+70,1
+78,1
+79,1
+80,1
+94,1
+
+Table: Data List
+Case Number,AVAR,BVAR
+1,18,1
+2,19,7
+3,20,26
+4,21,76
+5,22,57
+6,23,58
+7,24,38
+8,25,38
+9,26,30
+10,27,21
+11,28,23
+12,29,24
+13,30,23
+14,31,14
+15,32,21
+16,33,21
+17,34,14
+18,35,14
+19,36,17
+20,37,11
+21,38,16
+22,39,14
+23,40,15
+24,41,14
+25,42,14
+26,43,8
+27,44,15
+28,45,10
+29,46,12
+30,47,13
+31,48,13
+32,49,5
+33,50,5
+34,51,3
+35,52,7
+36,53,6
+37,54,2
+38,55,2
+39,56,2
+40,57,3
+41,58,1
+42,59,3
+43,61,1
+44,62,3
+45,63,1
+46,64,1
+47,65,2
+48,70,1
+49,78,1
+50,79,1
+51,80,1
+52,94,1
+
+Table: Data List
+X000,X001,X002,X003,X004,X005,X006,X007,X008,X009,X010,X011,X012,X013,X014,X015,X016,X017,X018,X019,X020,X021,X022,X023,X024,X025,X026,X027,X028,X029,X030
+7,6,7,5,3,2,4,6,6,3,4,8,5,1,3,7,8,9,0,7,3,4,8,3,1,0,6,4,0,9,1
+8,8,8,6,9,3,0,8,9,4,2,4,1,7,7,5,4,2,3,7,8,3,3,4,1,8,6,7,6,0,3
+4,9,2,6,1,1,5,0,7,9,0,9,1,8,7,1,5,2,7,2,6,4,2,7,8,5,2,2,4,2,4
+8,1,9,8,4,8,8,9,2,0,2,3,1,9,5,8,7,5,8,7,9,3,3,2,0,0,1,4,9,1,7
+4,5,2,7,7,7,8,9,8,7,0,9,5,6,3,7,2,9,8,4,5,5,4,1,5,1,6,6,5,0,6
+2,3,9,9,6,1,9,6,7,0,7,7,7,3,2,7,6,0,6,6,3,5,2,5,1,1,5,0,7,3,5
+1,6,6,7,7,9,9,6,9,1,2,6,6,4,7,6,9,9,4,4,0,4,7,4,3,9,8,9,2,3,7
+1,6,2,3,9,1,4,6,8,4,1,9,6,8,9,2,3,1,6,8,4,7,1,1,7,0,1,1,5,4,3
+3,6,8,1,3,9,3,2,3,3,7,6,0,1,2,9,4,8,9,1,1,3,1,2,1,8,2,9,5,9,9
+6,4,1,8,7,3,1,1,4,5,4,3,1,0,8,2,9,9,4,8,5,6,8,1,6,5,0,5,0,3,5
+2,2,8,4,5,3,4,0,8,3,7,4,9,5,0,7,7,1,6,6,5,1,0,8,6,4,2,9,0,7,1
+6,6,1,7,6,3,7,4,5,2,0,4,0,7,4,9,1,8,1,3,4,9,9,1,1,7,8,8,9,7,4
+9,8,6,5,7,1,3,5,8,2,6,8,6,6,1,2,0,0,7,2,2,2,0,1,0,7,8,2,6,8,2
+1,1,6,3,2,3,4,5,3,7,7,6,2,2,0,0,8,0,7,7,9,4,9,6,0,2,5,2,4,4,7
+9,9,8,1,6,6,3,6,3,7,5,6,3,8,3,3,3,0,0,0,3,5,4,2,6,1,3,6,7,0,2
+6,8,2,1,5,6,7,7,4,6,0,5,9,1,0,3,5,6,5,0,0,5,7,3,8,9,6,0,2,4,8
+0,9,5,2,7,7,4,9,5,2,6,7,5,2,6,1,5,4,5,9,5,5,2,8,0,8,0,5,3,4,0
+1,6,4,1,7,9,0,1,9,3,2,1,1,8,6,1,5,0,9,1,0,6,8,3,9,2,1,7,1,1,9
+3,7,6,3,1,8,2,8,7,1,5,8,0,1,7,4,7,8,9,3,2,8,8,3,7,1,9,4,9,6,8
+2,0,4,6,8,2,0,7,5,3,0,6,2,2,2,4,0,4,5,5,3,5,8,9,0,9,3,2,7,2,1
+7,9,7,0,6,2,0,0,9,1,9,4,0,3,8,5,9,2,8,7,6,2,6,3,2,7,6,4,6,1,8
+4,8,4,1,1,7,6,0,1,7,0,2,5,1,0,5,7,7,4,5,0,6,5,0,0,8,9,6,2,5,2
+6,9,4,9,9,7,3,7,9,7,9,9,0,9,5,6,2,9,1,0,7,2,1,5,8,1,2,3,8,8,7
+1,3,9,6,2,8,5,9,9,6,5,3,5,4,8,9,4,4,0,8,1,6,1,2,4,7,0,0,6,8,2
+0,7,0,0,4,8,9,5,2,4,3,5,8,2,0,8,3,5,8,6,9,7,3,4,9,4,5,0,0,3,6
+
+Table: Data List
+X000,X001,X002,X003,X004,X005,X006,X007,X008,X009,X010,X011,X012,X013,X014,X015,X016,X017,X018,X019,X020,X021,X022,X023,X024,X025,X026,X027,X028,X029,X030,X031,X032,X033,X034,X035,X036,X037,X038,X039,X040,X041,X042,X043,X044,X045,X046,X047,X048,X049,X050,X051,X052,X053,X054,X055,X056,X057,X058,X059,X060,X061,X062,X063,X064,X065,X066,X067,X068,X069,X070,X071,X072,X073,X074,X075,X076,X077,X078,X079,X080,X081,X082,X083,X084,X085,X086,X087,X088,X089,X090,X091,X092,X093,X094,X095,X096,X097,X098,X099,X100,X101,X102,X103,X104,X105,X106,X107,X108,X109,X110,X111,X112,X113,X114,X115,X116,X117,X118,X119,X120,X121,X122,X123,X124,X125,X126
+7,6,7,5,3,2,4,6,6,3,4,8,5,1,3,7,8,9,0,7,3,4,8,3,1,0,6,4,0,9,1,7,5,8,5,9,2,9,5,8,4,2,8,1,5,2,9,5,1,1,3,7,5,3,2,6,5,9,4,1,8,7,5,2,3,3,8,1,5,7,6,7,5,3,2,4,6,6,3,4,8,5,1,3,7,8,9,0,7,3,4,8,3,1,0,6,4,0,9,1,7,5,8,5,9,2,9,5,8,4,2,8,1,5,2,9,5,1,1,3,7,5,3,2,6,5,8
+8,8,8,6,9,3,0,8,9,4,2,4,1,7,7,5,4,2,3,7,8,3,3,4,1,8,6,7,6,0,3,6,8,1,4,7,5,5,8,6,9,0,0,2,7,9,7,3,1,0,2,2,6,6,8,7,4,1,6,8,4,5,5,5,0,6,7,1,4,8,8,8,6,9,3,0,8,9,4,2,4,1,7,7,5,4,2,3,7,8,3,3,4,1,8,6,7,6,0,3,6,8,1,4,7,5,5,8,6,9,0,0,2,7,9,7,3,1,0,2,2,6,6,8,7,4,6
+4,9,2,6,1,1,5,0,7,9,0,9,1,8,7,1,5,2,7,2,6,4,2,7,8,5,2,2,4,2,4,8,3,8,5,6,2,0,6,9,9,8,0,2,7,8,3,4,2,1,8,8,7,2,5,5,8,6,2,6,0,0,4,1,5,2,6,0,2,4,9,2,6,1,1,5,0,7,9,0,9,1,8,7,1,5,2,7,2,6,4,2,7,8,5,2,2,4,2,4,8,3,8,5,6,2,0,6,9,9,8,0,2,7,8,3,4,2,1,8,8,7,2,5,5,8,9
+8,1,9,8,4,8,8,9,2,0,2,3,1,9,5,8,7,5,8,7,9,3,3,2,0,0,1,4,9,1,7,7,3,6,8,9,6,8,8,0,1,3,3,2,2,1,0,1,6,0,8,8,4,4,0,6,4,0,2,3,6,2,6,5,7,2,7,0,0,8,1,9,8,4,8,8,9,2,0,2,3,1,9,5,8,7,5,8,7,9,3,3,2,0,0,1,4,9,1,7,7,3,6,8,9,6,8,8,0,1,3,3,2,2,1,0,1,6,0,8,8,4,4,0,6,4,1
+4,5,2,7,7,7,8,9,8,7,0,9,5,6,3,7,2,9,8,4,5,5,4,1,5,1,6,6,5,0,6,9,5,0,6,4,7,7,4,6,6,4,5,1,2,0,8,4,9,8,8,7,6,4,0,1,6,0,6,5,9,5,0,5,3,9,1,4,9,4,5,2,7,7,7,8,9,8,7,0,9,5,6,3,7,2,9,8,4,5,5,4,1,5,1,6,6,5,0,6,9,5,0,6,4,7,7,4,6,6,4,5,1,2,0,8,4,9,8,8,7,6,4,0,1,6,6
+2,3,9,9,6,1,9,6,7,0,7,7,7,3,2,7,6,0,6,6,3,5,2,5,1,1,5,0,7,3,5,1,8,6,0,5,9,1,1,8,1,8,6,9,1,0,8,8,0,4,6,5,1,9,0,3,2,8,1,9,6,2,4,6,8,9,6,7,5,2,3,9,9,6,1,9,6,7,0,7,7,7,3,2,7,6,0,6,6,3,5,2,5,1,1,5,0,7,3,5,1,8,6,0,5,9,1,1,8,1,8,6,9,1,0,8,8,0,4,6,5,1,9,0,3,2,5
+1,6,6,7,7,9,9,6,9,1,2,6,6,4,7,6,9,9,4,4,0,4,7,4,3,9,8,9,2,3,7,3,1,5,3,9,4,2,7,6,4,1,2,0,7,9,7,6,0,3,1,0,7,0,6,3,9,5,1,0,3,2,9,9,4,4,1,5,7,1,6,6,7,7,9,9,6,9,1,2,6,6,4,7,6,9,9,4,4,0,4,7,4,3,9,8,9,2,3,7,3,1,5,3,9,4,2,7,6,4,1,2,0,7,9,7,6,0,3,1,0,7,0,6,3,9,4
+1,6,2,3,9,1,4,6,8,4,1,9,6,8,9,2,3,1,6,8,4,7,1,1,7,0,1,1,5,4,3,6,2,7,1,1,9,5,9,7,9,0,8,5,9,9,7,4,0,5,2,5,2,4,6,1,6,4,1,9,1,5,0,8,0,1,3,2,0,1,6,2,3,9,1,4,6,8,4,1,9,6,8,9,2,3,1,6,8,4,7,1,1,7,0,1,1,5,4,3,6,2,7,1,1,9,5,9,7,9,0,8,5,9,9,7,4,0,5,2,5,2,4,6,1,6,8
+3,6,8,1,3,9,3,2,3,3,7,6,0,1,2,9,4,8,9,1,1,3,1,2,1,8,2,9,5,9,9,8,5,7,2,8,8,5,0,1,0,9,9,1,2,3,2,8,3,1,9,6,6,2,8,7,1,4,1,4,8,9,6,5,0,8,4,5,7,3,6,8,1,3,9,3,2,3,3,7,6,0,1,2,9,4,8,9,1,1,3,1,2,1,8,2,9,5,9,9,8,5,7,2,8,8,5,0,1,0,9,9,1,2,3,2,8,3,1,9,6,6,2,8,7,1,0
+6,4,1,8,7,3,1,1,4,5,4,3,1,0,8,2,9,9,4,8,5,6,8,1,6,5,0,5,0,3,5,9,9,7,9,8,2,0,9,6,7,3,2,1,5,0,3,5,9,7,5,4,5,4,7,2,9,9,6,1,8,4,8,7,8,8,5,3,0,6,4,1,8,7,3,1,1,4,5,4,3,1,0,8,2,9,9,4,8,5,6,8,1,6,5,0,5,0,3,5,9,9,7,9,8,2,0,9,6,7,3,2,1,5,0,3,5,9,7,5,4,5,4,7,2,9,7
+2,2,8,4,5,3,4,0,8,3,7,4,9,5,0,7,7,1,6,6,5,1,0,8,6,4,2,9,0,7,1,2,1,9,7,6,5,1,6,3,7,5,9,8,2,9,7,9,3,4,7,8,5,8,7,1,4,7,2,3,4,3,4,1,2,3,4,4,2,2,2,8,4,5,3,4,0,8,3,7,4,9,5,0,7,7,1,6,6,5,1,0,8,6,4,2,9,0,7,1,2,1,9,7,6,5,1,6,3,7,5,9,8,2,9,7,9,3,4,7,8,5,8,7,1,4,9
+6,6,1,7,6,3,7,4,5,2,0,4,0,7,4,9,1,8,1,3,4,9,9,1,1,7,8,8,9,7,4,7,5,7,5,2,2,4,6,9,6,6,4,8,3,8,8,6,7,9,0,1,0,1,4,1,8,2,4,8,6,6,9,7,5,7,2,9,5,6,6,1,7,6,3,7,4,5,2,0,4,0,7,4,9,1,8,1,3,4,9,9,1,1,7,8,8,9,7,4,7,5,7,5,2,2,4,6,9,6,6,4,8,3,8,8,6,7,9,0,1,0,1,4,1,8,5
+9,8,6,5,7,1,3,5,8,2,6,8,6,6,1,2,0,0,7,2,2,2,0,1,0,7,8,2,6,8,2,7,7,8,2,6,9,8,3,9,2,9,9,8,7,1,3,9,3,0,1,5,4,3,6,4,0,2,0,2,6,9,8,5,4,0,9,0,8,9,8,6,5,7,1,3,5,8,2,6,8,6,6,1,2,0,0,7,2,2,2,0,1,0,7,8,2,6,8,2,7,7,8,2,6,9,8,3,9,2,9,9,8,7,1,3,9,3,0,1,5,4,3,6,4,0,6
+1,1,6,3,2,3,4,5,3,7,7,6,2,2,0,0,8,0,7,7,9,4,9,6,0,2,5,2,4,4,7,7,7,3,0,9,8,4,4,3,3,4,0,7,6,2,8,4,4,7,3,4,3,5,0,3,7,8,7,5,0,4,4,0,9,0,2,9,5,1,1,6,3,2,3,4,5,3,7,7,6,2,2,0,0,8,0,7,7,9,4,9,6,0,2,5,2,4,4,7,7,7,3,0,9,8,4,4,3,3,4,0,7,6,2,8,4,4,7,3,4,3,5,0,3,7,7
+9,9,8,1,6,6,3,6,3,7,5,6,3,8,3,3,3,0,0,0,3,5,4,2,6,1,3,6,7,0,2,8,9,3,9,8,9,4,6,4,1,2,3,5,2,6,0,8,7,3,8,0,8,3,4,4,4,5,1,3,2,8,0,7,9,0,5,5,4,9,9,8,1,6,6,3,6,3,7,5,6,3,8,3,3,3,0,0,0,3,5,4,2,6,1,3,6,7,0,2,8,9,3,9,8,9,4,6,4,1,2,3,5,2,6,0,8,7,3,8,0,8,3,4,4,4,9
+6,8,2,1,5,6,7,7,4,6,0,5,9,1,0,3,5,6,5,0,0,5,7,3,8,9,6,0,2,4,8,8,4,2,1,9,8,9,9,5,5,9,0,6,0,2,2,8,8,7,0,0,4,7,6,2,8,2,3,0,7,1,1,0,2,9,1,6,8,6,8,2,1,5,6,7,7,4,6,0,5,9,1,0,3,5,6,5,0,0,5,7,3,8,9,6,0,2,4,8,8,4,2,1,9,8,9,9,5,5,9,0,6,0,2,2,8,8,7,0,0,4,7,6,2,8,4
+0,9,5,2,7,7,4,9,5,2,6,7,5,2,6,1,5,4,5,9,5,5,2,8,0,8,0,5,3,4,0,3,5,7,5,4,5,9,4,2,4,0,0,1,5,6,2,0,1,9,1,8,6,3,8,7,4,2,0,8,2,1,3,4,2,4,3,3,3,0,9,5,2,7,7,4,9,5,2,6,7,5,2,6,1,5,4,5,9,5,5,2,8,0,8,0,5,3,4,0,3,5,7,5,4,5,9,4,2,4,0,0,1,5,6,2,0,1,9,1,8,6,3,8,7,4,8
+1,6,4,1,7,9,0,1,9,3,2,1,1,8,6,1,5,0,9,1,0,6,8,3,9,2,1,7,1,1,9,4,9,6,8,6,5,8,7,7,1,1,8,4,0,6,5,7,9,6,1,9,4,9,2,6,1,4,7,4,4,1,1,4,8,6,9,0,2,1,6,4,1,7,9,0,1,9,3,2,1,1,8,6,1,5,0,9,1,0,6,8,3,9,2,1,7,1,1,9,4,9,6,8,6,5,8,7,7,1,1,8,4,0,6,5,7,9,6,1,9,4,9,2,6,1,5
+3,7,6,3,1,8,2,8,7,1,5,8,0,1,7,4,7,8,9,3,2,8,8,3,7,1,9,4,9,6,8,5,3,6,8,7,6,0,7,4,3,4,4,5,6,2,9,3,2,1,8,7,9,6,0,8,9,3,2,7,5,8,8,1,6,5,6,4,4,3,7,6,3,1,8,2,8,7,1,5,8,0,1,7,4,7,8,9,3,2,8,8,3,7,1,9,4,9,6,8,5,3,6,8,7,6,0,7,4,3,4,4,5,6,2,9,3,2,1,8,7,9,6,0,8,9,1
+2,0,4,6,8,2,0,7,5,3,0,6,2,2,2,4,0,4,5,5,3,5,8,9,0,9,3,2,7,2,1,1,3,7,8,1,9,8,0,7,3,3,3,7,5,7,1,7,1,9,2,6,4,2,5,4,4,2,9,7,3,4,3,9,4,2,6,7,9,2,0,4,6,8,2,0,7,5,3,0,6,2,2,2,4,0,4,5,5,3,5,8,9,0,9,3,2,7,2,1,1,3,7,8,1,9,8,0,7,3,3,3,7,5,7,1,7,1,9,2,6,4,2,5,4,4,1
+7,9,7,0,6,2,0,0,9,1,9,4,0,3,8,5,9,2,8,7,6,2,6,3,2,7,6,4,6,1,8,5,2,5,8,9,9,8,9,0,1,8,6,1,3,5,9,2,9,7,9,7,1,7,0,4,5,6,3,3,9,5,8,9,3,1,8,3,4,7,9,7,0,6,2,0,0,9,1,9,4,0,3,8,5,9,2,8,7,6,2,6,3,2,7,6,4,6,1,8,5,2,5,8,9,9,8,9,0,1,8,6,1,3,5,9,2,9,7,9,7,1,7,0,4,5,8
+4,8,4,1,1,7,6,0,1,7,0,2,5,1,0,5,7,7,4,5,0,6,5,0,0,8,9,6,2,5,2,7,5,7,0,7,6,6,9,0,3,9,2,0,3,4,6,0,1,2,8,3,8,3,4,0,4,8,3,0,8,8,4,3,6,3,2,6,4,4,8,4,1,1,7,6,0,1,7,0,2,5,1,0,5,7,7,4,5,0,6,5,0,0,8,9,6,2,5,2,7,5,7,0,7,6,6,9,0,3,9,2,0,3,4,6,0,1,2,8,3,8,3,4,0,4,5
+6,9,4,9,9,7,3,7,9,7,9,9,0,9,5,6,2,9,1,0,7,2,1,5,8,1,2,3,8,8,7,4,7,3,5,8,2,9,6,2,6,7,3,8,7,8,5,1,9,6,1,9,8,3,4,8,6,8,8,0,1,5,6,8,5,3,6,3,2,6,9,4,9,9,7,3,7,9,7,9,9,0,9,5,6,2,9,1,0,7,2,1,5,8,1,2,3,8,8,7,4,7,3,5,8,2,9,6,2,6,7,3,8,7,8,5,1,9,6,1,9,8,3,4,8,6,8
+1,3,9,6,2,8,5,9,9,6,5,3,5,4,8,9,4,4,0,8,1,6,1,2,4,7,0,0,6,8,2,9,3,3,8,7,4,3,6,5,1,2,8,7,8,6,8,2,3,8,2,4,7,5,8,1,3,3,4,6,1,1,5,6,6,4,9,7,2,1,3,9,6,2,8,5,9,9,6,5,3,5,4,8,9,4,4,0,8,1,6,1,2,4,7,0,0,6,8,2,9,3,3,8,7,4,3,6,5,1,2,8,7,8,6,8,2,3,8,2,4,7,5,8,1,3,8
+0,7,0,0,4,8,9,5,2,4,3,5,8,2,0,8,3,5,8,6,9,7,3,4,9,4,5,0,0,3,6,2,0,8,3,7,8,4,2,1,8,7,8,8,0,0,6,3,6,4,2,7,1,5,1,2,1,1,1,8,5,3,2,0,1,9,4,6,6,0,7,0,0,4,8,9,5,2,4,3,5,8,2,0,8,3,5,8,6,9,7,3,4,9,4,5,0,0,3,6,2,0,8,3,7,8,4,2,1,8,7,8,8,0,0,6,3,6,4,2,7,1,5,1,2,1,8
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
index 3501f2183f04034748f9c6da83f6499952e815ba..8040f44a25f285628896290536f14ffb3944f534 100755 (executable)
@@ -81,46 +81,35 @@ $SUPERVISOR $PSPP --testing-mode $TESTFILE
 if [ $? -ne 0 ] ; then no_result ; fi
 
 activity="compare output"
-perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
-diff  -b $TEMPDIR/pspp.list - <<EOF
-1.1 DATA LIST.  Reading free-form data from INLINE.
-+--------------+------+
-|   Variable   |Format|
-#==============#======#
-|AlphaBetaGamma|F8.0  |
-|B             |F8.0  |
-|X             |F8.0  |
-|Yabbadabbadoo |F8.0  |
-+--------------+------+
-AlphaBetaGamma        B        X Yabbadabbadoo
--------------- -------- -------- -------------
-          2.00     3.00     4.00          5.00 
-2.1 CROSSTABS.  Summary.
-#===============#=====================================================#
-#               #                        Cases                        #
-#               #-----------------+-----------------+-----------------#
-#               #      Valid      |     Missing     |      Total      #
-#               #--------+--------+--------+--------+--------+--------#
-#               #       N| Percent|       N| Percent|       N| Percent#
-#---------------#--------+--------+--------+--------+--------+--------#
-#X *            #       1|  100.0%|       0|    0.0%|       1|  100.0%#
-#Yabbadabbadoo  #        |        |        |        |        |        #
-#===============#========#========#========#========#========#========#
-2.2 CROSSTABS.  X * Yabbadabbadoo [count].
-#===============#==============================================================#========#
-#               #                         Yabbadabbadoo                        |        #
-#               #--------+--------+--------+--------+--------+--------+--------+        #
-#              X#    1.00|    2.00|    3.00|    4.00|    5.00|    6.00|    7.00|  Total #
-#---------------#--------+--------+--------+--------+--------+--------+--------+--------#
-#           1.00#      .0|      .0|      .0|      .0|      .0|      .0|      .0|      .0#
-#           2.00#      .0|      .0|      .0|      .0|      .0|      .0|      .0|      .0#
-#           3.00#      .0|      .0|      .0|      .0|      .0|      .0|      .0|      .0#
-#           4.00#      .0|      .0|      .0|      .0|     1.0|      .0|      .0|     1.0#
-#           5.00#      .0|      .0|      .0|      .0|      .0|      .0|      .0|      .0#
-#           6.00#      .0|      .0|      .0|      .0|      .0|      .0|      .0|      .0#
-#           7.00#      .0|      .0|      .0|      .0|      .0|      .0|      .0|      .0#
-#Total          #      .0|      .0|      .0|      .0|     1.0|      .0|      .0|     1.0#
-#===============#========#========#========#========#========#========#========#========#
+diff -c $TEMPDIR/pspp.csv - <<EOF
+Table: Reading free-form data from INLINE.
+Variable,Format
+AlphaBetaGamma,F8.0
+B,F8.0
+X,F8.0
+Yabbadabbadoo,F8.0
+
+Table: Data List
+AlphaBetaGamma,B,X,Yabbadabbadoo
+2.00,3.00,4.00,5.00
+
+Table: Summary.
+,Cases,,,,,
+,Valid,,Missing,,Total,
+,N,Percent,N,Percent,N,Percent
+X * Yabbadabbadoo,1,100.0%,0,0.0%,1,100.0%
+
+Table: X * Yabbadabbadoo [count].
+,Yabbadabbadoo,,,,,,,
+X,1.00,2.00,3.00,4.00,5.00,6.00,7.00,Total
+1.00,.0,.0,.0,.0,.0,.0,.0,.0
+2.00,.0,.0,.0,.0,.0,.0,.0,.0
+3.00,.0,.0,.0,.0,.0,.0,.0,.0
+4.00,.0,.0,.0,.0,1.0,.0,.0,1.0
+5.00,.0,.0,.0,.0,.0,.0,.0,.0
+6.00,.0,.0,.0,.0,.0,.0,.0,.0
+7.00,.0,.0,.0,.0,.0,.0,.0,.0
+Total,.0,.0,.0,.0,1.0,.0,.0,1.0
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
index 813777f4acea547542e62ce827faecb439877bac..21d883e8ff312454a6a495dd4ba3c1f3e23c070a 100755 (executable)
@@ -141,82 +141,155 @@ EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
 activity="compare results"
-perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
-diff  -b $TEMPDIR/pspp.list  - <<EOF
+diff -c $TEMPDIR/pspp.csv  - <<EOF
 Loop with index
-    1.00 
-    2.00 
+
+1.00 
+
+2.00 
+
 --------
-    2.00 
-    4.00 
+
+2.00 
+
+4.00 
+
 --------
-    3.00 
-    6.00 
-    9.00 
+
+3.00 
+
+6.00 
+
+9.00 
+
 --------
+
 --------
+
 Loop with IF condition
-    1.00 
-    2.00 
+
+1.00 
+
+2.00 
+
 --------
-    2.00 
-    4.00 
+
+2.00 
+
+4.00 
+
 --------
-    3.00 
-    6.00 
-    9.00 
+
+3.00 
+
+6.00 
+
+9.00 
+
 --------
+
 --------
+
 Loop with END IF condition
-    1.00 
-    2.00 
+
+1.00 
+
+2.00 
+
 --------
-    2.00 
-    4.00 
+
+2.00 
+
+4.00 
+
 --------
-    3.00 
-    6.00 
-    9.00 
+
+3.00 
+
+6.00 
+
+9.00 
+
 --------
-    4.00 
+
+4.00 
+
 --------
+
 Loop with index and IF condition based on index
-    1.00 
-    2.00 
+
+1.00 
+
+2.00 
+
 --------
-    2.00 
+
+2.00 
+
 --------
-    3.00 
+
+3.00 
+
 --------
+
 --------
+
 Loop with index and END IF condition based on index
-    1.00 
-    2.00 
+
+1.00 
+
+2.00 
+
 --------
-    2.00 
-    4.00 
+
+2.00 
+
+4.00 
+
 --------
-    3.00 
-    6.00 
+
+3.00 
+
+6.00 
+
 --------
+
 --------
+
 Loop with index and IF and END IF condition based on index
+
 --------
-    2.00 
-    4.00 
+
+2.00 
+
+4.00 
+
 --------
+
 --------
+
 --------
+
 Loop with no conditions
-    1.00 
+
+1.00 
+
 --------
-    2.00 
-    4.00 
+
+2.00 
+
+4.00 
+
 --------
-    3.00 
-    6.00 
+
+3.00 
+
+6.00 
+
 --------
-    4.00 
+
+4.00 
+
 --------
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
index 9726b065c7da65d9bf09a13e188da46d062d8ad8..a125cbd6e0db53006e1929f6dd6c799c95daae6a 100755 (executable)
@@ -85,40 +85,40 @@ EOF
 if [ $? -ne 0 ] ; then no_result ; fi
 
 cat > ff.out <<EOF
-A B C D INA INB FIRST LAST
-- - - - --- --- ----- ----
-0 a A     1   0     1    1
-1 a B N   1   1     1    0
-1 a C     1   0     0    1
-2 a D     1   0     1    1
-3 a E O   1   1     1    1
-4 a F P   1   1     1    1
-5 a G     1   0     1    0
-5 a H     1   0     0    1
-6 a I Q   1   1     1    1
-7 a J R   1   1     1    0
-7 a K     1   0     0    0
-7 a L     1   0     0    1
-8 a M     1   0     1    1
-9 b   S   0   1     1    1
+Table: Data List
+A,B,C,D,INA,INB,FIRST,LAST
+0,a,A,,1,0,1,1
+1,a,B,N,1,1,1,0
+1,a,C,,1,0,0,1
+2,a,D,,1,0,1,1
+3,a,E,O,1,1,1,1
+4,a,F,P,1,1,1,1
+5,a,G,,1,0,1,0
+5,a,H,,1,0,0,1
+6,a,I,Q,1,1,1,1
+7,a,J,R,1,1,1,0
+7,a,K,,1,0,0,0
+7,a,L,,1,0,0,1
+8,a,M,,1,0,1,1
+9,b,,S,0,1,1,1
 EOF
 
 cat > ft.out <<EOF
-A B C D INA INB FIRST LAST
-- - - - --- --- ----- ----
-0 a A     1   0     1    1
-1 a B N   1   1     1    0
-1 a C N   1   1     0    1
-2 a D     1   0     1    1
-3 a E O   1   1     1    1
-4 a F P   1   1     1    1
-5 a G     1   0     1    0
-5 a H     1   0     0    1
-6 a I Q   1   1     1    1
-7 a J R   1   1     1    0
-7 a K R   1   1     0    0
-7 a L R   1   1     0    1
-8 a M     1   0     1    1
+Table: Data List
+A,B,C,D,INA,INB,FIRST,LAST
+0,a,A,,1,0,1,1
+1,a,B,N,1,1,1,0
+1,a,C,N,1,1,0,1
+2,a,D,,1,0,1,1
+3,a,E,O,1,1,1,1
+4,a,F,P,1,1,1,1
+5,a,G,,1,0,1,0
+5,a,H,,1,0,0,1
+6,a,I,Q,1,1,1,1
+7,a,J,R,1,1,1,0
+7,a,K,R,1,1,0,0
+7,a,L,R,1,1,0,1
+8,a,M,,1,0,1,1
 EOF
 
 # Test nonparallel match and table lookup.
@@ -178,9 +178,7 @@ EOF
        if [ $? -ne 0 ] ; then no_result ; fi
 
        activity="check $name output"
-       perl -pi -e 's/^\s*$//g' pspp.list
-       perl -pi -e 's/^\s*$//g' $types.out
-       diff -b -w pspp.list $types.out
+       diff -c pspp.csv $types.out
        if [ $? -ne 0 ] ; then fail ; fi
     done
 done
@@ -203,23 +201,22 @@ $SUPERVISOR $PSPP --testing-mode -e /dev/null $name.pspp
 if [ $? -ne 0 ] ; then no_result ; fi
 
 activity="check $name output"
-perl -pi -e 's/^\s*$//g' pspp.list
-diff -b -w - pspp.list <<EOF
-A B C D E F
-- - - - - -
-1 a B 1 b N
-8 a M 3 b O
-3 a E 4 b P
-5 a G 6 b Q
-0 a A 7 b R
-5 a H 9 b S
-6 a I
-7 a J
-2 a D
-7 a K
-1 a C
-7 a L
-4 a F
+diff -c - pspp.csv <<EOF
+Table: Data List
+A,B,C,D,E,F
+1,a,B,1,b,N
+8,a,M,3,b,O
+3,a,E,4,b,P
+5,a,G,6,b,Q
+0,a,A,7,b,R
+5,a,H,9,b,S
+6,a,I,,,
+7,a,J,,,
+2,a,D,,,
+7,a,K,,,
+1,a,C,,,
+7,a,L,,,
+4,a,F,,,
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
@@ -252,13 +249,12 @@ $SUPERVISOR $PSPP --testing-mode -e /dev/null $name.pspp
 if [ $? -ne 0 ] ; then no_result ; fi
 
 activity="check $name output"
-perl -pi -e 's/^\s*$//g' pspp.list
-diff -b -w - pspp.list <<EOF | perl -e 's/^\s*$//g'
-        x        z        y
- -------- -------- --------
-     3.00     8.00    30.00 
-     2.00      .      21.00 
-     1.00      .      22.00 
+diff -c - pspp.csv <<EOF
+Table: Data List
+x,z,y
+3.00,8.00,30.00
+2.00,.  ,21.00
+1.00,.  ,22.00
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
index 547135559f13de4dc1b815d9cbcdbda13ca6eced..24a3429b5b5b5b23b6c26714e3afad209483ead0 100755 (executable)
@@ -81,25 +81,24 @@ if [ $? -ne 0 ] ; then no_result ; fi
 
 
 activity="compare output"
-perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
-diff -b  $TEMPDIR/pspp.list - <<EOF
-       X
---------
-    1.00 
-    2.00 
-    3.00 
-    4.00 
-    5.00 
-    6.00 
-    7.00 
-    8.00 
-    9.00 
-   10.00 
-   11.00 
-   12.00 
-   13.00 
-   14.00 
-   15.00 
+diff -c $TEMPDIR/pspp.csv - <<EOF
+Table: Data List
+X
+1.00
+2.00
+3.00
+4.00
+5.00
+6.00
+7.00
+8.00
+9.00
+10.00
+11.00
+12.00
+13.00
+14.00
+15.00
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
index 0c94051a1eb8e5e8098e42d5b50cee7b5938a483..4243f326edc4c3e10fb631dd742168c1e837314b 100755 (executable)
@@ -72,45 +72,38 @@ $SUPERVISOR $PSPP --testing-mode $TESTFILE
 if [ $? -ne 0 ] ; then no_result ; fi
 
 activity="compare output"
-perl -pi -e 's/^\s*$//g' pspp.list
-diff -b -w pspp.list - <<EOF
-1.1 DISPLAY.  
-+--------+-------------------------------------------+--------+
-|Variable|Description                                |Position|
-#========#===========================================#========#
-|cont    |continents of the world                    |       1|
-|        |Format: A32                                |        |
-|        |Measure: Nominal                           |        |
-|        |Display Alignment: Left                    |        |
-|        |Display Width: 8                           |        |
-+--------+-------------------------------------------+--------+
-|size    |sq km                                      |       2|
-|        |Format: F8.2                               |        |
-|        |Measure: Nominal                           |        |
-|        |Display Alignment: Left                    |        |
-|        |Display Width: 8                           |        |
-+--------+-------------------------------------------+--------+
-|pop     |population                                 |       3|
-|        |Format: F8.2                               |        |
-|        |Measure: Nominal                           |        |
-|        |Display Alignment: Left                    |        |
-|        |Display Width: 8                           |        |
-+--------+-------------------------------------------+--------+
-|count   |number of countries                        |       4|
-|        |Format: F8.2                               |        |
-|        |Measure: Nominal                           |        |
-|        |Display Alignment: Left                    |        |
-|        |Display Width: 8                           |        |
-+--------+-------------------------------------------+--------+
-                            cont     size      pop    count
--------------------------------- -------- -------- --------
-Asia                             44579000 3.7E+009    44.00 
-Africa                           30065000 7.8E+008    53.00 
-North America                    24256000 4.8E+008    23.00 
-South America                    17819000 3.4E+008    12.00 
-Antarctica                       13209000      .00      .00 
-Europe                            9938000 7.3E+008    46.00 
-Australia/Oceania                 7687000 31000000    14.00 
+diff -c pspp.csv - <<EOF
+Variable,Description,,Position
+cont,continents of the world,,1
+,Format: A32,,
+,Measure: Nominal,,
+,Display Alignment: Left,,
+,Display Width: 8,,
+size,sq km,,2
+,Format: F8.2,,
+,Measure: Nominal,,
+,Display Alignment: Left,,
+,Display Width: 8,,
+pop,population,,3
+,Format: F8.2,,
+,Measure: Nominal,,
+,Display Alignment: Left,,
+,Display Width: 8,,
+count,number of countries,,4
+,Format: F8.2,,
+,Measure: Nominal,,
+,Display Alignment: Left,,
+,Display Width: 8,,
+
+Table: Data List
+cont,size,pop,count
+Asia                            ,44579000,3.7E+009,44.00
+Africa                          ,30065000,7.8E+008,53.00
+North America                   ,24256000,4.8E+008,23.00
+South America                   ,17819000,3.4E+008,12.00
+Antarctica                      ,13209000,.00,.00
+Europe                          ,9938000,7.3E+008,46.00
+Australia/Oceania               ,7687000,31000000,14.00
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
index 0db048dd34703054bd88b7da91a17f81ae579812..9a776f6ba6ab6584ab1206e377614149bc3eac72 100755 (executable)
@@ -251,107 +251,94 @@ $SUPERVISOR $PSPP --testing-mode $TESTFILE
 if [ $? -ne 0 ] ; then no_result ; fi
 
 activity="compare output 1"
-perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
-diff -b $TEMPDIR/pspp.list - << EOF
+diff -c $TEMPDIR/pspp.csv - << EOF
 P < 0.5; N1/N2 < 1
-1.1 NPAR TESTS.  Binomial Test
-+-+------#--------+-----+--------------+----------+---------------------+
-| |      #Category|  N  |Observed Prop.|Test Prop.|Exact Sig. (1-tailed)|
-+-+------#--------+-----+--------------+----------+---------------------+
-|x|Group1#    1.00| 6.00|          .286|      .300|                 .551|
-| |Group2#    2.00|15.00|          .714|          |                     |
-| |Total #        |21.00|         1.000|          |                     |
-+-+------#--------+-----+--------------+----------+---------------------+
+
+Table: Binomial Test
+,,Category,N,Observed Prop.,Test Prop.,Exact Sig. (1-tailed)
+x,Group1,1.00,6.00,.286,.300,.551
+,Group2,2.00,15.00,.714,,
+,Total,,21.00,1.000,,
+
 P < 0.5; N1/N2 > 1
-2.1 NPAR TESTS.  Binomial Test
-+-+------#--------+--+--------------+----------+---------------------+
-| |      #Category| N|Observed Prop.|Test Prop.|Exact Sig. (1-tailed)|
-+-+------#--------+--+--------------+----------+---------------------+
-|x|Group1#       1| 7|          .538|      .400|                 .229|
-| |Group2#       2| 6|          .462|          |                     |
-| |Total #        |13|         1.000|          |                     |
-+-+------#--------+--+--------------+----------+---------------------+
+
+Table: Binomial Test
+,,Category,N,Observed Prop.,Test Prop.,Exact Sig. (1-tailed)
+x,Group1,1,7,.538,.400,.229
+,Group2,2,6,.462,,
+,Total,,13,1.000,,
+
 P < 0.5; N1/N2 = 1
-3.1 NPAR TESTS.  Binomial Test
-+-+------#--------+--+--------------+----------+---------------------+
-| |      #Category| N|Observed Prop.|Test Prop.|Exact Sig. (1-tailed)|
-+-+------#--------+--+--------------+----------+---------------------+
-|x|Group1#       1| 8|          .500|      .400|                 .284|
-| |Group2#       2| 8|          .500|          |                     |
-| |Total #        |16|         1.000|          |                     |
-+-+------#--------+--+--------------+----------+---------------------+
+
+Table: Binomial Test
+,,Category,N,Observed Prop.,Test Prop.,Exact Sig. (1-tailed)
+x,Group1,1,8,.500,.400,.284
+,Group2,2,8,.500,,
+,Total,,16,1.000,,
+
 P > 0.5; N1/N2 < 1
-4.1 NPAR TESTS.  Binomial Test
-+-+------#--------+--+--------------+----------+---------------------+
-| |      #Category| N|Observed Prop.|Test Prop.|Exact Sig. (1-tailed)|
-+-+------#--------+--+--------------+----------+---------------------+
-|x|Group1#       1|11|          .478|      .600|                 .164|
-| |Group2#       2|12|          .522|          |                     |
-| |Total #        |23|         1.000|          |                     |
-+-+------#--------+--+--------------+----------+---------------------+
+
+Table: Binomial Test
+,,Category,N,Observed Prop.,Test Prop.,Exact Sig. (1-tailed)
+x,Group1,1,11,.478,.600,.164
+,Group2,2,12,.522,,
+,Total,,23,1.000,,
+
 P > 0.5; N1/N2 > 1
-5.1 NPAR TESTS.  Binomial Test
-+-+------#--------+--+--------------+----------+---------------------+
-| |      #Category| N|Observed Prop.|Test Prop.|Exact Sig. (1-tailed)|
-+-+------#--------+--+--------------+----------+---------------------+
-|x|Group1#       1|11|          .550|      .600|                 .404|
-| |Group2#       2| 9|          .450|          |                     |
-| |Total #        |20|         1.000|          |                     |
-+-+------#--------+--+--------------+----------+---------------------+
+
+Table: Binomial Test
+,,Category,N,Observed Prop.,Test Prop.,Exact Sig. (1-tailed)
+x,Group1,1,11,.550,.600,.404
+,Group2,2,9,.450,,
+,Total,,20,1.000,,
+
 P > 0.5; N1/N2 == 1
-6.1 NPAR TESTS.  Binomial Test
-+-+------#--------+--+--------------+----------+---------------------+
-| |      #Category| N|Observed Prop.|Test Prop.|Exact Sig. (1-tailed)|
-+-+------#--------+--+--------------+----------+---------------------+
-|x|Group1#       1|11|          .500|      .600|                 .228|
-| |Group2#       2|11|          .500|          |                     |
-| |Total #        |22|         1.000|          |                     |
-+-+------#--------+--+--------------+----------+---------------------+
+
+Table: Binomial Test
+,,Category,N,Observed Prop.,Test Prop.,Exact Sig. (1-tailed)
+x,Group1,1,11,.500,.600,.228
+,Group2,2,11,.500,,
+,Total,,22,1.000,,
+
 P == 0.5; N1/N2 < 1
-7.1 NPAR TESTS.  Binomial Test
-+-+------#--------+--+--------------+----------+---------------------+
-| |      #Category| N|Observed Prop.|Test Prop.|Exact Sig. (2-tailed)|
-+-+------#--------+--+--------------+----------+---------------------+
-|x|Group1#       1| 8|          .348|      .500|                 .210|
-| |Group2#       2|15|          .652|          |                     |
-| |Total #        |23|         1.000|          |                     |
-+-+------#--------+--+--------------+----------+---------------------+
+
+Table: Binomial Test
+,,Category,N,Observed Prop.,Test Prop.,Exact Sig. (2-tailed)
+x,Group1,1,8,.348,.500,.210
+,Group2,2,15,.652,,
+,Total,,23,1.000,,
+
 P == 0.5; N1/N2 > 1
-8.1 NPAR TESTS.  Binomial Test
-+-+------#--------+--+--------------+----------+---------------------+
-| |      #Category| N|Observed Prop.|Test Prop.|Exact Sig. (2-tailed)|
-+-+------#--------+--+--------------+----------+---------------------+
-|x|Group1#       1|12|          .667|      .500|                 .238|
-| |Group2#       2| 6|          .333|          |                     |
-| |Total #        |18|         1.000|          |                     |
-+-+------#--------+--+--------------+----------+---------------------+
+
+Table: Binomial Test
+,,Category,N,Observed Prop.,Test Prop.,Exact Sig. (2-tailed)
+x,Group1,1,12,.667,.500,.238
+,Group2,2,6,.333,,
+,Total,,18,1.000,,
+
 P == 0.5; N1/N2 == 1
-9.1 NPAR TESTS.  Binomial Test
-+-+------#--------+--+--------------+----------+---------------------+
-| |      #Category| N|Observed Prop.|Test Prop.|Exact Sig. (2-tailed)|
-+-+------#--------+--+--------------+----------+---------------------+
-|x|Group1#       1|10|          .500|      .500|                1.000|
-| |Group2#       2|10|          .500|          |                     |
-| |Total #        |20|         1.000|          |                     |
-+-+------#--------+--+--------------+----------+---------------------+
+
+Table: Binomial Test
+,,Category,N,Observed Prop.,Test Prop.,Exact Sig. (2-tailed)
+x,Group1,1,10,.500,.500,1.000
+,Group2,2,10,.500,,
+,Total,,20,1.000,,
+
 P == 0.5; N1/N2 == 1 Cutpoint
-10.1 NPAR TESTS.  Binomial Test
-+-+------#--------+------+--------------+----------+---------------------+
-| |      #Category|   N  |Observed Prop.|Test Prop.|Exact Sig. (2-tailed)|
-+-+------#--------+------+--------------+----------+---------------------+
-|x|Group1#   <= 10|10.000|          .385|      .500|                 .327|
-| |Group2#        |16.000|          .615|          |                     |
-| |Total #        |26.000|         1.000|          |                     |
-+-+------#--------+------+--------------+----------+---------------------+
+
+Table: Binomial Test
+,,Category,N,Observed Prop.,Test Prop.,Exact Sig. (2-tailed)
+x,Group1,<= 10,10.000,.385,.500,.327
+,Group2,,16.000,.615,,
+,Total,,26.000,1.000,,
+
 P == 0.5; N1/N2 == 1 Named values
-11.1 NPAR TESTS.  Binomial Test
-+-+------#--------+------+--------------+----------+---------------------+
-| |      #Category|   N  |Observed Prop.|Test Prop.|Exact Sig. (2-tailed)|
-+-+------#--------+------+--------------+----------+---------------------+
-|x|Group1#  10.000|10.000|          .435|      .500|                 .678|
-| |Group2#  20.000|13.000|          .565|          |                     |
-| |Total #        |23.000|         1.000|          |                     |
-+-+------#--------+------+--------------+----------+---------------------+
+
+Table: Binomial Test
+,,Category,N,Observed Prop.,Test Prop.,Exact Sig. (2-tailed)
+x,Group1,10.000,10.000,.435,.500,.678
+,Group2,20.000,13.000,.565,,
+,Total,,23.000,1.000,,
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
index 33e2311bc9b49bfcd3756b5ccc91373d35db4fc1..0f4b3facbe331a8f709c9fe3eb687addb8251640 100755 (executable)
@@ -96,75 +96,58 @@ $SUPERVISOR $PSPP --testing-mode $TESTFILE
 if [ $? -ne 0 ] ; then no_result ; fi
 
 activity="compare output 1"
-perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
-diff -b $TEMPDIR/pspp.list - << EOF
-1.1 NPAR TESTS.  x
-+--------#----------+----------+--------+
-|        #Observed N|Expected N|Residual|
-+--------#----------+----------+--------+
-|    1.00#      3.00|      2.33|     .67|
-|    2.00#      3.00|      2.33|     .67|
-|    3.10#      4.00|      2.33|    1.67|
-|    3.20#      1.00|      2.33|   -1.33|
-|    4.00#      2.00|      2.33|    -.33|
-|    5.00#      1.00|      2.33|   -1.33|
-|Total   #     14.00|          |        |
-+--------#----------+----------+--------+
-1.2 NPAR TESTS.  y
-+--------#----------+----------+--------+
-|        #Observed N|Expected N|Residual|
-+--------#----------+----------+--------+
-|    1.00#      7.00|      3.50|    3.50|
-|    2.00#      4.00|      3.50|     .50|
-|    3.00#      1.00|      3.50|   -2.50|
-|    4.00#      2.00|      3.50|   -1.50|
-|Total   #     14.00|          |        |
-+--------#----------+----------+--------+
-1.3 NPAR TESTS.  Test Statistics
-+-----------#----+----+
-|           #  x |  y |
-+-----------#----+----+
-|Chi-Square #3.14|6.00|
-|df         #   5|   3|
-|Asymp. Sig.# .68| .11|
-+-----------#----+----+
-2.1 NPAR TESTS.  y
-+--------#----------+----------+--------+
-|        #Observed N|Expected N|Residual|
-+--------#----------+----------+--------+
-|    1.00#      7.00|      2.63|    4.38|
-|    2.00#      4.00|      3.50|     .50|
-|    3.00#      1.00|      4.38|   -3.38|
-|    4.00#      2.00|      3.50|   -1.50|
-|Total   #     14.00|          |        |
-+--------#----------+----------+--------+
-2.2 NPAR TESTS.  Test Statistics
-+-----------#-----+
-|           #  y  |
-+-----------#-----+
-|Chi-Square #10.61|
-|df         #    3|
-|Asymp. Sig.#  .01|
-+-----------#-----+
-3.1 NPAR TESTS.  Frequencies
-+-----#---------------------------------------#---------------------------------------+
-|     #                   x                   #                   y                   |
-|     #--------+----------+----------+--------#--------+----------+----------+--------+
-|     #Category|Observed N|Expected N|Residual#Category|Observed N|Expected N|Residual|
-+-----#--------+----------+----------+--------#--------+----------+----------+--------+
-|1    #    2.00|      3.00|      3.16|    -.16#    2.00|      4.00|      2.21|    1.79|
-|2    #    3.00|      5.00|      5.26|    -.26#    3.00|      1.00|      3.68|   -2.68|
-|3    #    4.00|      2.00|      1.58|     .42#    4.00|      2.00|      1.11|     .89|
-|Total#        |     10.00|          |        #        |      7.00|          |        |
-+-----#--------+----------+----------+--------#--------+----------+----------+--------+
-3.2 NPAR TESTS.  Test Statistics
-+-----------#---+----+
-|           # x |  y |
-+-----------#---+----+
-|Chi-Square #.13|4.13|
-|df         #  2|   2|
-|Asymp. Sig.#.94| .13|
-+-----------#---+----+
+diff -c $TEMPDIR/pspp.csv - << EOF
+Table: x
+,Observed N,Expected N,Residual
+1.00,3.00,2.33,.67
+2.00,3.00,2.33,.67
+3.10,4.00,2.33,1.67
+3.20,1.00,2.33,-1.33
+4.00,2.00,2.33,-.33
+5.00,1.00,2.33,-1.33
+Total,14.00,,
+
+Table: y
+,Observed N,Expected N,Residual
+1.00,7.00,3.50,3.50
+2.00,4.00,3.50,.50
+3.00,1.00,3.50,-2.50
+4.00,2.00,3.50,-1.50
+Total,14.00,,
+
+Table: Test Statistics
+,x,y
+Chi-Square,3.14,6.00
+df,5,3
+Asymp. Sig.,.68,.11
+
+Table: y
+,Observed N,Expected N,Residual
+1.00,7.00,2.63,4.38
+2.00,4.00,3.50,.50
+3.00,1.00,4.38,-3.38
+4.00,2.00,3.50,-1.50
+Total,14.00,,
+
+Table: Test Statistics
+,y
+Chi-Square,10.61
+df,3
+Asymp. Sig.,.01
+
+Table: Frequencies
+,x,,,,y,,,
+,Category,Observed N,Expected N,Residual,Category,Observed N,Expected N,Residual
+1,2.00,3.00,3.16,-.16,2.00,4.00,2.21,1.79
+2,3.00,5.00,5.26,-.26,3.00,1.00,3.68,-2.68
+3,4.00,2.00,1.58,.42,4.00,2.00,1.11,.89
+Total,,10.00,,,,7.00,,
+
+Table: Test Statistics
+,x,y
+Chi-Square,.13,4.13
+df,2,2
+Asymp. Sig.,.94,.13
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
@@ -237,40 +220,31 @@ if [ $? -ne 0 ] ; then no_result ; fi
 
 
 activity="compare output 3"
-perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
-diff -b $TEMPDIR/pspp.list - <<EOF
-1.1 NPAR TESTS.  Frequencies
-+-----#---------------------------------------#---------------------------------------+
-|     #                   x                   #                   y                   |
-|     #--------+----------+----------+--------#--------+----------+----------+--------+
-|     #Category|Observed N|Expected N|Residual#Category|Observed N|Expected N|Residual|
-+-----#--------+----------+----------+--------#--------+----------+----------+--------+
-|1    #   -2.00|       .00|      1.50|   -1.50#   -2.00|       .00|      1.88|   -1.88|
-|2    #   -1.00|       .00|      1.50|   -1.50#   -1.00|       .00|      1.88|   -1.88|
-|3    #     .00|       .00|      1.50|   -1.50#     .00|       .00|      1.88|   -1.88|
-|4    #    1.00|      3.00|      1.50|    1.50#    1.00|      7.00|      1.88|    5.13|
-|5    #    2.00|      3.00|      1.50|    1.50#    2.00|      4.00|      1.88|    2.13|
-|6    #    3.00|      5.00|      1.50|    3.50#    3.00|      1.00|      1.88|    -.88|
-|7    #    4.00|       .00|      1.50|   -1.50#    4.00|      2.00|      1.88|     .13|
-|8    #    5.00|      1.00|      1.50|    -.50#    5.00|      1.00|      1.88|    -.88|
-|Total#        |     12.00|          |        #        |     15.00|          |        |
-+-----#--------+----------+----------+--------#--------+----------+----------+--------+
-1.2 NPAR TESTS.  Test Statistics
-+-----------#-----+-----+
-|           #  x  |  y  |
-+-----------#-----+-----+
-|Chi-Square #17.33|22.87|
-|df         #    7|    7|
-|Asymp. Sig.#  .02|  .00|
-+-----------#-----+-----+
-1.3 NPAR TESTS.  Descriptive Statistics
-+-#-----+----+----+----+----+
-| #  N  |Mean|Std.|Mini|Maxi|
-| #     |    |Devi| mum| mum|
-#=#=====#====#====#====#====#
-|x#12.00|2.47|1.19|1.00|5.00|
-|y#15.00|2.07|1.33|1.00|5.00|
-+-#-----+----+----+----+----+
+diff -c $TEMPDIR/pspp.csv - <<EOF
+Table: Frequencies
+,x,,,,y,,,
+,Category,Observed N,Expected N,Residual,Category,Observed N,Expected N,Residual
+1,-2.00,.00,1.50,-1.50,-2.00,.00,1.88,-1.88
+2,-1.00,.00,1.50,-1.50,-1.00,.00,1.88,-1.88
+3,.00,.00,1.50,-1.50,.00,.00,1.88,-1.88
+4,1.00,3.00,1.50,1.50,1.00,7.00,1.88,5.13
+5,2.00,3.00,1.50,1.50,2.00,4.00,1.88,2.13
+6,3.00,5.00,1.50,3.50,3.00,1.00,1.88,-.88
+7,4.00,.00,1.50,-1.50,4.00,2.00,1.88,.13
+8,5.00,1.00,1.50,-.50,5.00,1.00,1.88,-.88
+Total,,12.00,,,,15.00,,
+
+Table: Test Statistics
+,x,y
+Chi-Square,17.33,22.87
+df,7,7
+Asymp. Sig.,.02,.00
+
+Table: Descriptive Statistics
+,N,Mean,Std. Deviation,Minimum,Maximum
+,,,,,
+x,12.00,2.47,1.19,1.00,5.00
+y,15.00,2.07,1.33,1.00,5.00
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
@@ -308,40 +282,31 @@ if [ $? -ne 0 ] ; then no_result ; fi
 
 
 activity="compare output 4"
-perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
-diff -b $TEMPDIR/pspp.list - <<EOF
-1.1 NPAR TESTS.  Frequencies
-+-----#---------------------------------------#---------------------------------------+
-|     #                   x                   #                   y                   |
-|     #--------+----------+----------+--------#--------+----------+----------+--------+
-|     #Category|Observed N|Expected N|Residual#Category|Observed N|Expected N|Residual|
-+-----#--------+----------+----------+--------#--------+----------+----------+--------+
-|1    #   -2.00|       .00|      1.75|   -1.75#   -2.00|       .00|      1.75|   -1.75|
-|2    #   -1.00|       .00|      1.75|   -1.75#   -1.00|       .00|      1.75|   -1.75|
-|3    #     .00|       .00|      1.75|   -1.75#     .00|       .00|      1.75|   -1.75|
-|4    #    1.00|      3.00|      1.75|    1.25#    1.00|      7.00|      1.75|    5.25|
-|5    #    2.00|      3.00|      1.75|    1.25#    2.00|      4.00|      1.75|    2.25|
-|6    #    3.00|      5.00|      1.75|    3.25#    3.00|      1.00|      1.75|    -.75|
-|7    #    4.00|      2.00|      1.75|     .25#    4.00|      2.00|      1.75|     .25|
-|8    #    5.00|      1.00|      1.75|    -.75#    5.00|       .00|      1.75|   -1.75|
-|Total#        |     14.00|          |        #        |     14.00|          |        |
-+-----#--------+----------+----------+--------#--------+----------+----------+--------+
-1.2 NPAR TESTS.  Test Statistics
-+-----------#-----+-----+
-|           #  x  |  y  |
-+-----------#-----+-----+
-|Chi-Square #13.43|26.00|
-|df         #    7|    7|
-|Asymp. Sig.#  .06|  .00|
-+-----------#-----+-----+
-1.3 NPAR TESTS.  Descriptive Statistics
-+-#-----+----+----+----+----+
-| #  N  |Mean|Std.|Mini|Maxi|
-| #     |    |Devi| mum| mum|
-#=#=====#====#====#====#====#
-|x#14.00|2.69|1.23|1.00|5.00|
-|y#14.00|1.86|1.10|1.00|4.00|
-+-#-----+----+----+----+----+
+diff -c $TEMPDIR/pspp.csv - <<EOF
+Table: Frequencies
+,x,,,,y,,,
+,Category,Observed N,Expected N,Residual,Category,Observed N,Expected N,Residual
+1,-2.00,.00,1.75,-1.75,-2.00,.00,1.75,-1.75
+2,-1.00,.00,1.75,-1.75,-1.00,.00,1.75,-1.75
+3,.00,.00,1.75,-1.75,.00,.00,1.75,-1.75
+4,1.00,3.00,1.75,1.25,1.00,7.00,1.75,5.25
+5,2.00,3.00,1.75,1.25,2.00,4.00,1.75,2.25
+6,3.00,5.00,1.75,3.25,3.00,1.00,1.75,-.75
+7,4.00,2.00,1.75,.25,4.00,2.00,1.75,.25
+8,5.00,1.00,1.75,-.75,5.00,.00,1.75,-1.75
+Total,,14.00,,,,14.00,,
+
+Table: Test Statistics
+,x,y
+Chi-Square,13.43,26.00
+df,7,7
+Asymp. Sig.,.06,.00
+
+Table: Descriptive Statistics
+,N,Mean,Std. Deviation,Minimum,Maximum
+,,,,,
+x,14.00,2.69,1.23,1.00,5.00
+y,14.00,1.86,1.10,1.00,4.00
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
index 17c0c239985023f41d098e3e29596bc6956c01a8..1c7c629a6704775375a21edc0414261c522ce47f 100755 (executable)
@@ -85,31 +85,23 @@ $SUPERVISOR $PSPP --testing-mode -o raw-ascii $TESTFILE
 if [ $? -ne 0 ] ; then no_result ; fi
 
 activity="compare output 1"
-diff - $TEMPDIR/pspp.list <<EOF
-1.1 NPAR TESTS.  Frequencies
-#=================================#=#
-#                                 |N#
-#---------------------------------+-#
-#height - age Negative Differences|1#
-#             Positive Differences|3#
-#             Ties                |2#
-#             Total               |6#
-#---------------------------------+-#
-#rank - heightNegative Differences|3#
-#             Positive Differences|2#
-#             Ties                |1#
-#             Total               |6#
-#=================================#=#
-
-1.2 NPAR TESTS.  Test Statistics
-#=====================#============#=============#
-#                     |height - age|rank - height#
-#=====================#============#=============#
-#Exact Sig. (2-tailed)|        .625|        1.000#
-#Exact Sig. (1-tailed)|        .312|         .500#
-#Point Probability    |        .250|         .312#
-#=====================#============#=============#
-
+diff -c - $TEMPDIR/pspp.csv <<EOF
+Table: Frequencies
+,,N
+height - age,Negative Differences,1
+,Positive Differences,3
+,Ties,2
+,Total,6
+rank - height,Negative Differences,3
+,Positive Differences,2
+,Ties,1
+,Total,6
+
+Table: Test Statistics
+,height - age,rank - height
+Exact Sig. (2-tailed),.625,1.000
+Exact Sig. (1-tailed),.312,.500
+Point Probability,.250,.312
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
index 4242b55b6581dcd82e551392856857c7fc3919e0..f4434a6454bcb8ad9f3c0a4a992810363b66ab4f 100755 (executable)
@@ -95,33 +95,26 @@ $SUPERVISOR $PSPP --testing-mode -o raw-ascii $TESTFILE
 if [ $? -ne 0 ] ; then no_result ; fi
 
 activity="generate results"
-cat > $TEMPDIR/results.txt <<EOF
-1.1 NPAR TESTS.  Ranks
-#============================#==#=========#============#
-#                            # N|Mean Rank|Sum of Ranks#
-#============================#==#=========#============#
-#second - firstNegative Ranks# 5|     8.60|       43.00#
-#              Positive Ranks# 8|     6.00|       48.00#
-#              Ties          # 2|         |            #
-#              Total         #15|         |            #
-#============================#==#=========#============#
-
-1.2 NPAR TESTS.  Test Statistics
-#======================#==============#
-#                      #second - first#
-#======================#==============#
-#Z                     #          -.18#
-#Asymp. Sig. (2-tailed)#           .86#
-#Exact Sig. (2-tailed) #           .89#
-#Exact Sig. (1-tailed) #           .45#
-#======================#==============#
-
+cat > $TEMPDIR/results.csv <<EOF
+Table: Ranks
+,,N,Mean Rank,Sum of Ranks
+second - first,Negative Ranks,5,8.60,43.00
+,Positive Ranks,8,6.00,48.00
+,Ties,2,,
+,Total,15,,
+
+Table: Test Statistics
+,second - first
+Z,-.18
+Asymp. Sig. (2-tailed),.86
+Exact Sig. (2-tailed),.89
+Exact Sig. (1-tailed),.45
 EOF
 if [ $? -ne 0 ] ; then no_result ; fi
 
 
 activity="compare output 1"
-diff pspp.list $TEMPDIR/results.txt
+diff -c pspp.csv $TEMPDIR/results.csv
 if [ $? -ne 0 ] ; then fail ; fi
 
 
@@ -165,7 +158,7 @@ $SUPERVISOR $PSPP --testing-mode -o raw-ascii $TESTFILE
 if [ $? -ne 0 ] ; then no_result ; fi
 
 activity="compare output 2"
-diff pspp.list $TEMPDIR/results.txt
+diff pspp.csv $TEMPDIR/results.csv
 if [ $? -ne 0 ] ; then fail ; fi
 
 
index 2b16368b121d4c3b270055fe3b87fc381258a6bc..d4a4c6fd9b648f31a9b8f0e1d258cfc68f1277ec 100755 (executable)
@@ -92,7 +92,7 @@ $SUPERVISOR $PSPP --testing-mode $TESTFILE
 if [ $? -ne 0 ] ; then no_result ; fi
 
 activity="copy output"
-cp $TEMPDIR/pspp.list $TEMPDIR/pspp.list1
+cp $TEMPDIR/pspp.csv $TEMPDIR/pspp.csv1
 if [ $? -ne 0 ] ; then no_result ; fi
 
 activity="create program 2"
@@ -130,7 +130,7 @@ $SUPERVISOR $PSPP --testing-mode $TESTFILE
 if [ $? -ne 0 ] ; then no_result ; fi
 
 activity="compare outputs"
-diff $TEMPDIR/pspp.list $TEMPDIR/pspp.list1
+diff $TEMPDIR/pspp.csv $TEMPDIR/pspp.csv1
 if [ $? -ne 0 ] ; then fail ; fi
 
 # Now try a missing dependent variable
@@ -170,7 +170,7 @@ $SUPERVISOR $PSPP --testing-mode $TESTFILE
 if [ $? -ne 0 ] ; then no_result ; fi
 
 activity="compare outputs"
-diff $TEMPDIR/pspp.list $TEMPDIR/pspp.list1
+diff $TEMPDIR/pspp.csv $TEMPDIR/pspp.csv1
 if [ $? -ne 0 ] ; then fail ; fi
 
 
index 7197a096f8ee59709e9ef7529ed7baec9c2bbf5d..8466d8f5944b44af224376a32ecd83002b07232f 100755 (executable)
@@ -99,117 +99,78 @@ activity="run program"
 $SUPERVISOR $PSPP --testing-mode $TESTFILE
 if [ $? -ne 0 ] ; then no_result ; fi
 
-perl -pi -e s/^\s*\$//g $TEMPDIR/pspp.list
-diff -b  $TEMPDIR/pspp.list - << EOF | perl -e 's/^\s*$//g'
-1.1 DATA LIST.  Reading free-form data from INLINE.
-+--------+------+
-|Variable|Format|
-#========#======#
-|QUALITY |F8.0  |
-|BRAND   |F8.0  |
-|S       |F8.0  |
-+--------+------+
-
-Variable Value    Label
-S            1.00
-
-2.1 ONEWAY.  Descriptives
-#===============#=======#=#====#==============#==========#=======================#=======#=======#
-#               |       # |    |              |          |    95% Confidence     |       |       #
-#               |       # |    |              |          +-----------+-----------+       |       #
-#               |       #N|Mean|Std. Deviation|Std. Error|Lower Bound|Upper Bound|Minimum|Maximum#
-#===============#=======#=#====#==============#==========#===========#===========#=======#=======#
-#Breaking Strain|Aspeger#5|2.20|          1.30|       .58|        .58|       3.82|   1.00|   4.00#
-#               |Bloggs #2|3.50|          2.12|      1.50|     -15.56|      22.56|   2.00|   5.00#
-#               |Total  #7|2.57|          1.51|       .57|       1.17|       3.97|   1.00|   5.00#
-#===============#=======#=#====#==============#==========#===========#===========#=======#=======#
-
-2.2 ONEWAY.  Test of Homogeneity of Variances
-#===============#================#===#===#============#
-#               #Levene Statistic|df1|df2|Significance#
-#===============#================#===#===#============#
-#Breaking Strain#           1.086|  1|  5|        .345#
-#===============#================#===#===#============#
-
-2.3 ONEWAY.  ANOVA
-#==============================#==============#==#===========#=====#============#
-#                              #Sum of Squares|df|Mean Square|  F  |Significance#
-#===============#==============#==============#==#===========#=====#============#
-#Breaking Strain|Between Groups#          2.41| 1|      2.414|1.068|        .349#
-#               |Within Groups #         11.30| 5|      2.260|     |            #
-#               |Total         #         13.71| 6|           |     |            #
-#===============#==============#==============#==#===========#=====#============#
-
-2.4 ONEWAY.  Contrast Coefficients
-#==========#==============#
-#          # Manufacturer #
-#          #-------+------#
-#          #Aspeger|Bloggs#
-#========#=#=======#======#
-#Contrast|1#     -2|     2#
-#        |2#     -1|     1#
-#========#=#=======#======#
-
-2.5 ONEWAY.  Contrast Tests
-#===============================================#=================#==========#=====#=====#===============#
-#                                       Contrast#Value of Contrast|Std. Error|  t  |  df |Sig. (2-tailed)#
-#===============#======================#========#=================#==========#=====#=====#===============#
-#Breaking Strain|Assume equal variances|    1   #             2.60|     2.516|1.034|    5|           .349#
-#               |                      |    2   #             1.30|     1.258|1.034|    5|           .349#
-#               |Does not assume equal |    1   #             2.60|     3.219| .808|1.318|           .539#
-#               |                      |    2   #             1.30|     1.609| .808|1.318|           .539#
-#===============#======================#========#=================#==========#=====#=====#===============#
-
-Variable Value    Label
-S            2.00
-
-2.6 ONEWAY.  Descriptives
-#===============#========#=#====#==============#==========#=======================#=======#=======#
-#               |        # |    |              |          |    95% Confidence     |       |       #
-#               |        # |    |              |          +-----------+-----------+       |       #
-#               |        #N|Mean|Std. Deviation|Std. Error|Lower Bound|Upper Bound|Minimum|Maximum#
-#===============#========#=#====#==============#==========#===========#===========#=======#=======#
-#Breaking Strain|Bloggs  #3|3.00|          1.00|       .58|        .52|       5.48|   2.00|   4.00#
-#               |Charlies#5|5.00|          1.58|       .71|       3.04|       6.96|   3.00|   7.00#
-#               |Total   #8|4.25|          1.67|       .59|       2.85|       5.65|   2.00|   7.00#
-#===============#========#=#====#==============#==========#===========#===========#=======#=======#
-
-2.7 ONEWAY.  Test of Homogeneity of Variances
-#===============#================#===#===#============#
-#               #Levene Statistic|df1|df2|Significance#
-#===============#================#===#===#============#
-#Breaking Strain#            .923|  1|  6|        .374#
-#===============#================#===#===#============#
-
-2.8 ONEWAY.  ANOVA
-#==============================#==============#==#===========#=====#============#
-#                              #Sum of Squares|df|Mean Square|  F  |Significance#
-#===============#==============#==============#==#===========#=====#============#
-#Breaking Strain|Between Groups#          7.50| 1|      7.500|3.750|        .101#
-#               |Within Groups #         12.00| 6|      2.000|     |            #
-#               |Total         #         19.50| 7|           |     |            #
-#===============#==============#==============#==#===========#=====#============#
-
-2.9 ONEWAY.  Contrast Coefficients
-#==========#===============#
-#          #  Manufacturer #
-#          #------+--------#
-#          #Bloggs|Charlies#
-#========#=#======#========#
-#Contrast|1#    -2|       2#
-#        |2#    -1|       1#
-#========#=#======#========#
-
-2.10 ONEWAY.  Contrast Tests
-#===============================================#=================#==========#=====#=====#===============#
-#                                       Contrast#Value of Contrast|Std. Error|  t  |  df |Sig. (2-tailed)#
-#===============#======================#========#=================#==========#=====#=====#===============#
-#Breaking Strain|Assume equal variances|    1   #             4.00|     2.066|1.936|    6|           .101#
-#               |                      |    2   #             2.00|     1.033|1.936|    6|           .101#
-#               |Does not assume equal |    1   #             4.00|     1.826|2.191|5.882|           .072#
-#               |                      |    2   #             2.00|      .913|2.191|5.882|           .072#
-#===============#======================#========#=================#==========#=====#=====#===============#
-
+diff -c $TEMPDIR/pspp.csv - << EOF
+Table: Reading free-form data from INLINE.
+Variable,Format
+QUALITY,F8.0
+BRAND,F8.0
+S,F8.0
+
+Variable,Value,Label
+S,1.00,
+
+Table: Descriptives
+,,,,,,95% Confidence Interval for Mean,,,
+,,N,Mean,Std. Deviation,Std. Error,Lower Bound,Upper Bound,Minimum,Maximum
+Breaking Strain,Aspeger,5,2.20,1.30,.58,.58,3.82,1.00,4.00
+,Bloggs,2,3.50,2.12,1.50,-15.56,22.56,2.00,5.00
+,Total,7,2.57,1.51,.57,1.17,3.97,1.00,5.00
+
+Table: Test of Homogeneity of Variances
+,Levene Statistic,df1,df2,Significance
+Breaking Strain,1.09,1,5,.35
+
+Table: ANOVA
+,,Sum of Squares,df,Mean Square,F,Significance
+Breaking Strain,Between Groups,2.41,1,2.41,1.07,.35
+,Within Groups,11.30,5,2.26,,
+,Total,13.71,6,,,
+
+Table: Contrast Coefficients
+,,Manufacturer,
+,,Aspeger,Bloggs
+Contrast,1,-2,2
+,2,-1,1
+
+Table: Contrast Tests
+,,Contrast,Value of Contrast,Std. Error,t,df,Sig. (2-tailed)
+Breaking Strain,Assume equal variances,1,2.60,2.52,1.03,5,.35
+,,2,1.30,1.26,1.03,5,.35
+,Does not assume equal,1,2.60,3.22,.81,1.32,.54
+,,2,1.30,1.61,.81,1.32,.54
+
+Variable,Value,Label
+S,2.00,
+
+Table: Descriptives
+,,,,,,95% Confidence Interval for Mean,,,
+,,N,Mean,Std. Deviation,Std. Error,Lower Bound,Upper Bound,Minimum,Maximum
+Breaking Strain,Bloggs,3,3.00,1.00,.58,.52,5.48,2.00,4.00
+,Charlies,5,5.00,1.58,.71,3.04,6.96,3.00,7.00
+,Total,8,4.25,1.67,.59,2.85,5.65,2.00,7.00
+
+Table: Test of Homogeneity of Variances
+,Levene Statistic,df1,df2,Significance
+Breaking Strain,.92,1,6,.37
+
+Table: ANOVA
+,,Sum of Squares,df,Mean Square,F,Significance
+Breaking Strain,Between Groups,7.50,1,7.50,3.75,.10
+,Within Groups,12.00,6,2.00,,
+,Total,19.50,7,,,
+
+Table: Contrast Coefficients
+,,Manufacturer,
+,,Bloggs,Charlies
+Contrast,1,-2,2
+,2,-1,1
+
+Table: Contrast Tests
+,,Contrast,Value of Contrast,Std. Error,t,df,Sig. (2-tailed)
+Breaking Strain,Assume equal variances,1,4.00,2.07,1.94,6,.10
+,,2,2.00,1.03,1.94,6,.10
+,Does not assume equal,1,4.00,1.83,2.19,5.88,.07
+,,2,2.00,.91,2.19,5.88,.07
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
index 339456d3d9680dd327fbeebc34344558d3504db4..c55101d5733f8d319c588a2f857fc17eed6cfd53 100755 (executable)
@@ -96,58 +96,42 @@ activity="run program"
 $SUPERVISOR $PSPP --testing-mode $TESTFILE
 if [ $? -ne 0 ] ; then no_result ; fi
 
-perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
-diff -b  $TEMPDIR/pspp.list - << EOF
-1.1 DATA LIST.  Reading free-form data from INLINE.
-+--------+------+
-|Variable|Format|
-#========#======#
-|QUALITY |F8.0  |
-|BRAND   |F8.0  |
-+--------+------+
-2.1 ONEWAY.  Descriptives
-#===============#========#==#====#==============#==========#=======================#=======#=======#
-#               |        #  |    |              |          |95% Confidence Interval|       |       #
-#               |        #  |    |              |          +-----------+-----------+       |       #
-#               |        # N|Mean|Std. Deviation|Std. Error|Lower Bound|Upper Bound|Minimum|Maximum#
-#===============#========#==#====#==============#==========#===========#===========#=======#=======#
-#Breaking Strain|Aspeger # 5|2.20|          1.30|       .58|        .58|       3.82|   1.00|   4.00#
-#               |Bloggs  # 5|3.20|          1.30|       .58|       1.58|       4.82|   2.00|   5.00#
-#               |Charlies# 5|5.00|          1.58|       .71|       3.04|       6.96|   3.00|   7.00#
-#               |Total   #15|3.47|          1.77|       .46|       2.49|       4.45|   1.00|   7.00#
-#===============#========#==#====#==============#==========#===========#===========#=======#=======#
-2.2 ONEWAY.  Test of Homogeneity of Variances
-#===============#================#===#===#============#
-#               #Levene Statistic|df1|df2|Significance#
-#===============#================#===#===#============#
-#Breaking Strain#             .09|  2| 12|         .91#
-#===============#================#===#===#============#
-2.3 ONEWAY.  ANOVA
-#==============================#==============#==#===========#====#============#
-#                              #Sum of Squares|df|Mean Square|  F |Significance#
-#===============#==============#==============#==#===========#====#============#
-#Breaking Strain|Between Groups#         20.13| 2|      10.07|5.12|         .02#
-#               |Within Groups #         23.60|12|       1.97|    |            #
-#               |Total         #         43.73|14|           |    |            #
-#===============#==============#==============#==#===========#====#============#
-2.4 ONEWAY.  Contrast Coefficients
-#==========#=======================#
-#          #      Manufacturer     #
-#          #-------+------+--------#
-#          #Aspeger|Bloggs|Charlies#
-#========#=#=======#======#========#
-#Contrast|1#     -2|     1|       1#
-#        |2#      0|    -1|       1#
-#========#=#=======#======#========#
-2.5 ONEWAY.  Contrast Tests
-#===============================================#=================#==========#====#====#===============#
-#                                       Contrast#Value of Contrast|Std. Error|  t | df |Sig. (2-tailed)#
-#===============#======================#========#=================#==========#====#====#===============#
-#Breaking Strain|Assume equal variances|    1   #             3.80|      1.54|2.47|  12|            .03#
-#               |                      |    2   #             1.80|       .89|2.03|  12|            .07#
-#               |Does not assume equal |    1   #             3.80|      1.48|2.56|8.74|            .03#
-#               |                      |    2   #             1.80|       .92|1.96|7.72|            .09#
-#===============#======================#========#=================#==========#====#====#===============#
+diff -c $TEMPDIR/pspp.csv - << EOF
+Table: Reading free-form data from INLINE.
+Variable,Format
+QUALITY,F8.0
+BRAND,F8.0
+
+Table: Descriptives
+,,,,,,95% Confidence Interval for Mean,,,
+,,N,Mean,Std. Deviation,Std. Error,Lower Bound,Upper Bound,Minimum,Maximum
+Breaking Strain,Aspeger,5,2.20,1.30,.58,.58,3.82,1.00,4.00
+,Bloggs,5,3.20,1.30,.58,1.58,4.82,2.00,5.00
+,Charlies,5,5.00,1.58,.71,3.04,6.96,3.00,7.00
+,Total,15,3.47,1.77,.46,2.49,4.45,1.00,7.00
+
+Table: Test of Homogeneity of Variances
+,Levene Statistic,df1,df2,Significance
+Breaking Strain,.09,2,12,.91
+
+Table: ANOVA
+,,Sum of Squares,df,Mean Square,F,Significance
+Breaking Strain,Between Groups,20.13,2,10.07,5.12,.02
+,Within Groups,23.60,12,1.97,,
+,Total,43.73,14,,,
+
+Table: Contrast Coefficients
+,,Manufacturer,,
+,,Aspeger,Bloggs,Charlies
+Contrast,1,-2,1,1
+,2,0,-1,1
+
+Table: Contrast Tests
+,,Contrast,Value of Contrast,Std. Error,t,df,Sig. (2-tailed)
+Breaking Strain,Assume equal variances,1,3.80,1.54,2.47,12,.03
+,,2,1.80,.89,2.03,12,.07
+,Does not assume equal,1,3.80,1.48,2.56,8.74,.03
+,,2,1.80,.92,1.96,7.72,.09
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
index 4a0527588de2cb321887549d094b5927433e6cbb..f135b4c144b22a1f132d4ffe73729bbaf77ea304 100755 (executable)
@@ -159,32 +159,56 @@ EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
 activity="compare output"
-diff $TEMPDIR/pspp.list - << EOF
+diff $TEMPDIR/pspp.csv - << EOF
 1 2 
 
+
+
 12
+
 1 -2 
 
+
+
 3 4 
 
+
+
 34
+
 3 -4 
 
+
+
 . 6 
 
+
+
 .6
+
 . -6 
 
+
+
 7 . 
 
+
+
 7.
+
 7 -. 
 
+
+
 9 0 
 
+
+
 90
+
 9 -0 
 
+
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
index d98c8e2211c67b0316334b2973f84ed9e726f00f..bc208af8a73f9db5736779c8d16e6ce22f3e5675 100755 (executable)
@@ -136,7 +136,7 @@ if [ $? -ne 1 ] ; then fail ; fi
 
 activity="compare errors"
 perl -pi -e 's/^\s*$//g' $TEMPDIR/errs
-diff  -b $TEMPDIR/errs - << EOF
+diff -b $TEMPDIR/errs - << EOF
 $TEMPDIR/rank.sh.sps:15: error: RANK: Syntax error expecting \`(' at end of command.
 $TEMPDIR/rank.sh.sps:19: error: RANK: Syntax error expecting integer at \`d'.
 $TEMPDIR/rank.sh.sps:25: error: RANK: Variable x already exists.
@@ -364,194 +364,242 @@ if [ $? -ne 0 ] ; then no_result ; fi
 
 
 activity="compare output 3"
-perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
-diff  -b $TEMPDIR/pspp.list - <<EOF
+diff -c $TEMPDIR/pspp.csv - <<EOF
 Simple example using defaults
+
 Variables Created By RANK
+
+
+
 x into Rx(RANK of x)
-       x        Rx
--------- ---------
-      -1     1.000 
-       0     2.000 
-       1     3.000 
-       2     4.500 
-       2     4.500 
-       4     6.000 
-       5     7.000 
+
+Table: Data List
+x,Rx
+-1,1.000
+0,2.000
+1,3.000
+2,4.500
+2,4.500
+4,6.000
+5,7.000
+
 Variables Created By RANK
+
+
+
 a into Ra(RANK of a)
+
 b into Rb(RANK of b)
+
 a into RFR001(RFRACTION of a)
+
 b into RFR002(RFRACTION of b)
+
 a into count(N of a)
+
 b into Nb(N of b)
-1.1 DISPLAY.  
-+--------+-------------------------------------------+--------+
-|Variable|Description                                |Position|
-#========#===========================================#========#
-|a       |Format: F8.2                               |       1|
-|        |Measure: Scale                             |        |
-|        |Display Alignment: Right                   |        |
-|        |Display Width: 8                           |        |
-+--------+-------------------------------------------+--------+
-|b       |Format: F8.2                               |       2|
-|        |Measure: Scale                             |        |
-|        |Display Alignment: Right                   |        |
-|        |Display Width: 8                           |        |
-+--------+-------------------------------------------+--------+
-|count   |N of a                                     |       3|
-|        |Format: F6.0                               |        |
-|        |Measure: Scale                             |        |
-|        |Display Alignment: Right                   |        |
-|        |Display Width: 8                           |        |
-+--------+-------------------------------------------+--------+
-|Ra      |RANK of a                                  |       4|
-|        |Format: F9.3                               |        |
-|        |Measure: Scale                             |        |
-|        |Display Alignment: Right                   |        |
-|        |Display Width: 8                           |        |
-+--------+-------------------------------------------+--------+
-|Rb      |RANK of b                                  |       5|
-|        |Format: F9.3                               |        |
-|        |Measure: Scale                             |        |
-|        |Display Alignment: Right                   |        |
-|        |Display Width: 8                           |        |
-+--------+-------------------------------------------+--------+
-|RFR001  |RFRACTION of a                             |       6|
-|        |Format: F6.4                               |        |
-|        |Measure: Scale                             |        |
-|        |Display Alignment: Right                   |        |
-|        |Display Width: 8                           |        |
-+--------+-------------------------------------------+--------+
-|RFR002  |RFRACTION of b                             |       7|
-|        |Format: F6.4                               |        |
-|        |Measure: Scale                             |        |
-|        |Display Alignment: Right                   |        |
-|        |Display Width: 8                           |        |
-+--------+-------------------------------------------+--------+
-|Nb      |N of b                                     |       8|
-|        |Format: F6.0                               |        |
-|        |Measure: Scale                             |        |
-|        |Display Alignment: Right                   |        |
-|        |Display Width: 8                           |        |
-+--------+-------------------------------------------+--------+
-       a        b  count        Ra        Rb RFR001 RFR002     Nb
--------- -------- ------ --------- --------- ------ ------ ------
-     .00    24.00     10    10.000     8.000 1.0000  .8889      9 
-    1.00    32.00     10     9.000     4.000  .9000  .4444      9 
-    2.00    31.00     10     8.000     5.000  .8000  .5556      9 
-    2.00    32.00     10     8.000     4.000  .8000  .4444      9 
-    4.00    30.00     10     6.000     6.000  .6000  .6667      9 
-    5.00    29.00     10     5.000     7.000  .5000  .7778      9 
-    6.00     1.00     10     4.000     9.000  .4000 1.0000      9 
-    7.00    43.00     10     3.000     2.000  .3000  .2222      9 
-    8.00      .       10     2.000      .     .2000  .          . 
-    9.00    45.00     10     1.000     1.000  .1000  .1111      9 
+
+Variable,Description,,Position
+a,Format: F8.2,,1
+,Measure: Scale,,
+,Display Alignment: Right,,
+,Display Width: 8,,
+b,Format: F8.2,,2
+,Measure: Scale,,
+,Display Alignment: Right,,
+,Display Width: 8,,
+count,N of a,,3
+,Format: F6.0,,
+,Measure: Scale,,
+,Display Alignment: Right,,
+,Display Width: 8,,
+Ra,RANK of a,,4
+,Format: F9.3,,
+,Measure: Scale,,
+,Display Alignment: Right,,
+,Display Width: 8,,
+Rb,RANK of b,,5
+,Format: F9.3,,
+,Measure: Scale,,
+,Display Alignment: Right,,
+,Display Width: 8,,
+RFR001,RFRACTION of a,,6
+,Format: F6.4,,
+,Measure: Scale,,
+,Display Alignment: Right,,
+,Display Width: 8,,
+RFR002,RFRACTION of b,,7
+,Format: F6.4,,
+,Measure: Scale,,
+,Display Alignment: Right,,
+,Display Width: 8,,
+Nb,N of b,,8
+,Format: F6.0,,
+,Measure: Scale,,
+,Display Alignment: Right,,
+,Display Width: 8,,
+
+Table: Data List
+a,b,count,Ra,Rb,RFR001,RFR002,Nb
+.00,24.00,10,10.000,8.000,1.0000,.8889,9
+1.00,32.00,10,9.000,4.000,.9000,.4444,9
+2.00,31.00,10,8.000,5.000,.8000,.5556,9
+2.00,32.00,10,8.000,4.000,.8000,.4444,9
+4.00,30.00,10,6.000,6.000,.6000,.6667,9
+5.00,29.00,10,5.000,7.000,.5000,.7778,9
+6.00,1.00,10,4.000,9.000,.4000,1.0000,9
+7.00,43.00,10,3.000,2.000,.3000,.2222,9
+8.00,.  ,10,2.000,.   ,.2000,.    ,.
+9.00,45.00,10,1.000,1.000,.1000,.1111,9
+
 Test variable name fallback
+
 Variables Created By RANK
+
+
+
 foo into RAN001(RANK of foo)
-2.1 DISPLAY.  
-+--------+-------------------------------------------+--------+
-|Variable|Description                                |Position|
-#========#===========================================#========#
-|foo     |Format: F8.2                               |       1|
-|        |Measure: Scale                             |        |
-|        |Display Alignment: Right                   |        |
-|        |Display Width: 8                           |        |
-+--------+-------------------------------------------+--------+
-|rfoo    |Format: F8.2                               |       2|
-|        |Measure: Scale                             |        |
-|        |Display Alignment: Right                   |        |
-|        |Display Width: 8                           |        |
-+--------+-------------------------------------------+--------+
-|ran003  |Format: F8.2                               |       3|
-|        |Measure: Scale                             |        |
-|        |Display Alignment: Right                   |        |
-|        |Display Width: 8                           |        |
-+--------+-------------------------------------------+--------+
-|RAN001  |RANK of foo                                |       4|
-|        |Format: F9.3                               |        |
-|        |Measure: Scale                             |        |
-|        |Display Alignment: Right                   |        |
-|        |Display Width: 8                           |        |
-+--------+-------------------------------------------+--------+
+
+Variable,Description,,Position
+foo,Format: F8.2,,1
+,Measure: Scale,,
+,Display Alignment: Right,,
+,Display Width: 8,,
+rfoo,Format: F8.2,,2
+,Measure: Scale,,
+,Display Alignment: Right,,
+,Display Width: 8,,
+ran003,Format: F8.2,,3
+,Measure: Scale,,
+,Display Alignment: Right,,
+,Display Width: 8,,
+RAN001,RANK of foo,,4
+,Format: F9.3,,
+,Measure: Scale,,
+,Display Alignment: Right,,
+,Display Width: 8,,
+
 Variables Created By RANK
+
+
+
 a into Sa(SAVAGE of a)
+
 a into Pa(PERCENT of a)
+
 a into PRO001(PROPORTION of a using BLOM)
+
 a into Na(NTILES of a)
+
 a into NOR001(NORMAL of a using BLOM)
-       a        b       Sa     Pa PRO001  Na NOR001
--------- -------- -------- ------ ------ --- ------
-     .00    24.00   -.9000  10.00  .0610   1 -1.547 
-    1.00    32.00   -.7889  20.00  .1585   1 -1.000 
-    2.00    31.00   -.5925  30.00  .2561   2 -.6554 
-    2.00    32.00   -.5925  30.00  .2561   2 -.6554 
-    4.00    30.00   -.3544  40.00  .3537   2 -.3755 
-    5.00    29.00   -.1544  50.00  .4512   2 -.1226 
-    6.00     1.00    .0956  60.00  .5488   3  .1226 
-    7.00    43.00    .4290  70.00  .6463   3  .3755 
-    8.00     8.00    .9290  80.00  .7439   3  .6554 
-    9.00    45.00   1.9290  90.00  .8415   4 1.0005 
+
+Table: Data List
+a,b,Sa,Pa,PRO001,Na,NOR001
+.00,24.00,-.9000,10.00,.0610,1,-1.547
+1.00,32.00,-.7889,20.00,.1585,1,-1.000
+2.00,31.00,-.5925,30.00,.2561,2,-.6554
+2.00,32.00,-.5925,30.00,.2561,2,-.6554
+4.00,30.00,-.3544,40.00,.3537,2,-.3755
+5.00,29.00,-.1544,50.00,.4512,2,-.1226
+6.00,1.00,.0956,60.00,.5488,3,.1226
+7.00,43.00,.4290,70.00,.6463,3,.3755
+8.00,8.00,.9290,80.00,.7439,3,.6554
+9.00,45.00,1.9290,90.00,.8415,4,1.0005
+
 Variables Created By RANK
+
+
+
 a into Ra(RANK of a BY g2 g1)
+
 a into Na(NORMAL of a using RANKIT BY g2 g1)
+
 Variables Created By RANK
+
+
+
 a into RAN001(RANK of a BY g2)
+
 a into NOR001(NORMAL of a using RANKIT BY g2)
-       a       g1       g2        Ra     Na    RAN001 NOR001 
--------- -------- -------- --------- ------ --------- ------ 
-    2.00     1.00     2.00     8.000  .9674     4.000  .5244  
-    2.00     1.00     2.00     8.000  .9674     4.000  .5244  
-    3.00     1.00     2.00     7.000  .5895     3.000  .0000  
-    4.00     1.00     2.00     6.000  .2822     2.000 -.5244  
-    5.00     1.00     2.00     5.000  .0000     1.000 -1.282  
-    1.00      .00     2.00     8.000 1.5341     8.000 1.5341  
-    2.00      .00     2.00     7.000  .8871     7.000  .8871  
-    3.00      .00     2.00     6.000  .4888     6.000  .4888  
-    4.00      .00     2.00     5.000  .1573     5.000  .1573  
-    5.00      .00     2.00     4.000 -.1573     4.000 -.1573  
-    6.00      .00     2.00     3.000 -.4888     3.000 -.4888  
-    7.00      .00     2.00     2.000 -.8871     2.000 -.8871  
-    8.00      .00     2.00     1.000 -1.534     1.000 -1.534  
-    6.00     1.00     2.00     4.000 -.2822     4.000 1.1503  
-    7.00     1.00     2.00     2.000 -.9674     2.000 -.3186  
-    7.00     1.00     2.00     2.000 -.9674     2.000 -.3186  
-    8.00     1.00     2.00     1.000 -1.593     1.000 -1.150  
-    9.00     1.00     1.00     1.000  .0000     1.000  .0000  
+
+Table: Data List
+a,g1,g2,Ra,Na,RAN001,NOR001
+2.00,1.00,2.00,8.000,.9674,4.000,.5244
+2.00,1.00,2.00,8.000,.9674,4.000,.5244
+3.00,1.00,2.00,7.000,.5895,3.000,.0000
+4.00,1.00,2.00,6.000,.2822,2.000,-.5244
+5.00,1.00,2.00,5.000,.0000,1.000,-1.282
+1.00,.00,2.00,8.000,1.5341,8.000,1.5341
+2.00,.00,2.00,7.000,.8871,7.000,.8871
+3.00,.00,2.00,6.000,.4888,6.000,.4888
+4.00,.00,2.00,5.000,.1573,5.000,.1573
+5.00,.00,2.00,4.000,-.1573,4.000,-.1573
+6.00,.00,2.00,3.000,-.4888,3.000,-.4888
+7.00,.00,2.00,2.000,-.8871,2.000,-.8871
+8.00,.00,2.00,1.000,-1.534,1.000,-1.534
+6.00,1.00,2.00,4.000,-.2822,4.000,1.1503
+7.00,1.00,2.00,2.000,-.9674,2.000,-.3186
+7.00,1.00,2.00,2.000,-.9674,2.000,-.3186
+8.00,1.00,2.00,1.000,-1.593,1.000,-1.150
+9.00,1.00,1.00,1.000,.0000,1.000,.0000
+
 fractional ranks ( including small ones for special case of SAVAGE ranks)
+
 Variables Created By RANK
+
+
+
 a into Pa(PROPORTION of a using TUKEY)
+
 a into Sa(SAVAGE of a)
-       a        w     Pa       Sa
--------- -------- ------ --------
-    1.00     1.50  .1285   -.8016 
-    2.00      .20  .1776   -.6905 
-    3.00      .10  .1986   -.6905 
-    4.00     1.00  .3458   -.5305 
-    5.00     1.00  .4860   -.2905 
-    6.00     1.00  .6262    .0262 
-    7.00     1.00  .7664    .4929 
-    8.00     1.00  .9065   1.3929 
+
+Table: Data List
+a,w,Pa,Sa
+1.00,1.50,.1285,-.8016
+2.00,.20,.1776,-.6905
+3.00,.10,.1986,-.6905
+4.00,1.00,.3458,-.5305
+5.00,1.00,.4860,-.2905
+6.00,1.00,.6262,.0262
+7.00,1.00,.7664,.4929
+8.00,1.00,.9065,1.3929
+
 test all the ties cases with low caseweight values
+
 Variables Created By RANK
+
+
+
 x into xl(RANK of x)
+
 Variables Created By RANK
+
+
+
 x into xh(RANK of x)
+
 Variables Created By RANK
+
+
+
 x into xc(RANK of x)
+
 Variables Created By RANK
+
+
+
 x into Nx(NORMAL of x using VW)
-       x        w        xl        xh        xc     Nx
--------- -------- --------- --------- --------- ------
-    1.00      .10      .000      .100     1.000 -1.938 
-    2.00      .10      .100      .200     2.000 -1.412 
-    3.00      .10      .200      .300     3.000 -1.119 
-    4.00      .20      .300      .500     4.000 -.8046 
-    5.00      .10      .500      .600     5.000 -.5549 
-    6.00      .10      .600      .700     6.000 -.4067 
-    7.00      .10      .700      .800     7.000 -.2670 
-    8.00      .10      .800      .900     8.000 -.1323 
+
+Table: Data List
+x,w,xl,xh,xc,Nx
+1.00,.10,.000,.100,1.000,-1.938
+2.00,.10,.100,.200,2.000,-1.412
+3.00,.10,.200,.300,3.000,-1.119
+4.00,.20,.300,.500,4.000,-.8046
+5.00,.10,.500,.600,5.000,-.5549
+6.00,.10,.600,.700,6.000,-.4067
+7.00,.10,.700,.800,7.000,-.2670
+8.00,.10,.800,.900,8.000,-.1323
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
@@ -586,26 +634,36 @@ if [ $? -ne 0 ] ; then fail ; fi
 
 
 activity="compare output 4"
-perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
-diff  -b $TEMPDIR/pspp.list - << EOF
-$TEMPDIR/rank.sh.sps:3: warning: BEGIN DATA: Missing value(s) for all variables from rx onward.  These will be filled with the system-missing value or blanks, as appropriate.
-$TEMPDIR/rank.sh.sps:4: warning: BEGIN DATA: Missing value(s) for all variables from rx onward.  These will be filled with the system-missing value or blanks, as appropriate.
-$TEMPDIR/rank.sh.sps:5: warning: BEGIN DATA: Missing value(s) for all variables from rx onward.  These will be filled with the system-missing value or blanks, as appropriate.
-$TEMPDIR/rank.sh.sps:6: warning: BEGIN DATA: Missing value(s) for all variables from rx onward.  These will be filled with the system-missing value or blanks, as appropriate.
-$TEMPDIR/rank.sh.sps:7: warning: BEGIN DATA: Missing value(s) for all variables from rx onward.  These will be filled with the system-missing value or blanks, as appropriate.
-$TEMPDIR/rank.sh.sps:8: warning: BEGIN DATA: Missing value(s) for all variables from rx onward.  These will be filled with the system-missing value or blanks, as appropriate.
-$TEMPDIR/rank.sh.sps:9: warning: BEGIN DATA: Missing value(s) for all variables from rx onward.  These will be filled with the system-missing value or blanks, as appropriate.
+diff -c $TEMPDIR/pspp.csv - << EOF
+"$TEMPDIR/rank.sh.sps:3: warning: BEGIN DATA: Missing value(s) for all variables from rx onward.  These will be filled with the system-missing value or blanks, as appropriate."
+
+"$TEMPDIR/rank.sh.sps:4: warning: BEGIN DATA: Missing value(s) for all variables from rx onward.  These will be filled with the system-missing value or blanks, as appropriate."
+
+"$TEMPDIR/rank.sh.sps:5: warning: BEGIN DATA: Missing value(s) for all variables from rx onward.  These will be filled with the system-missing value or blanks, as appropriate."
+
+"$TEMPDIR/rank.sh.sps:6: warning: BEGIN DATA: Missing value(s) for all variables from rx onward.  These will be filled with the system-missing value or blanks, as appropriate."
+
+"$TEMPDIR/rank.sh.sps:7: warning: BEGIN DATA: Missing value(s) for all variables from rx onward.  These will be filled with the system-missing value or blanks, as appropriate."
+
+"$TEMPDIR/rank.sh.sps:8: warning: BEGIN DATA: Missing value(s) for all variables from rx onward.  These will be filled with the system-missing value or blanks, as appropriate."
+
+"$TEMPDIR/rank.sh.sps:9: warning: BEGIN DATA: Missing value(s) for all variables from rx onward.  These will be filled with the system-missing value or blanks, as appropriate."
+
 Variables Created By RANK
+
+
+
 x into RNKRA01(RANK of x)
-       x       rx   RNKRA01
--------- -------- ---------
-    1.00      .       1.000 
-    2.00      .       2.000 
-    3.00      .       3.000 
-    4.00      .       4.000 
-    5.00      .       5.000 
-    6.00      .       6.000 
-    7.00      .       7.000 
+
+Table: Data List
+x,rx,RNKRA01
+1.00,.  ,1.000
+2.00,.  ,2.000
+3.00,.  ,3.000
+4.00,.  ,4.000
+5.00,.  ,5.000
+6.00,.  ,6.000
+7.00,.  ,7.000
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
index 6f9b3490cb96cc1716935fdf46009904919b4fa4..1c4211abd8569123074e9d0adca7d086a783cc70 100755 (executable)
@@ -1569,37 +1569,27 @@ activity="run program"
 $SUPERVISOR $PSPP --testing-mode $TESTFILE
 if [ $? -ne 0 ] ; then no_result ; fi
 
-perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
-diff -b  $TEMPDIR/pspp.list - << EOF
-1.1 DATA LIST.  Reading free-form data from INLINE.
-+--------+------+
-|Variable|Format|
-#========#======#
-|v0      |F8.0  |
-|v1      |F8.0  |
-+--------+------+
-2.1 REGRESSION.  Model Summary
-#============#========#=================#==========================#
-#          R #R Square|Adjusted R Square|Std. Error of the Estimate#
-#========#===#========#=================#==========================#
-#        |.05#     .00|              .00|                      8.11#
-#========#===#========#=================#==========================#
-2.2 REGRESSION.  ANOVA
-#===================#==============#====#===========#====#============#
-#                   #Sum of Squares| df |Mean Square|  F |Significance#
-#========#==========#==============#====#===========#====#============#
-#        |Regression#        235.23|   1|     235.23|3.58|         .06#
-#        |Residual  #      98438.40|1498|      65.71|    |            #
-#        |Total     #      98673.63|1499|           |    |            #
-#========#==========#==============#====#===========#====#============#
-2.3 REGRESSION.  Coefficients
-#===================#====#==========#====#====#============#
-#                   #  B |Std. Error|Beta|  t |Significance#
-#========#==========#====#==========#====#====#============#
-#        |(Constant)#1.24|       .42| .00|2.95|         .21#
-#        |    v1    #1.37|       .72| .05|1.89|         .06#
-#        |          #    |          |    |    |            #
-#========#==========#====#==========#====#====#============#
+diff -c $TEMPDIR/pspp.csv - << EOF
+Table: Reading free-form data from INLINE.
+Variable,Format
+v0,F8.0
+v1,F8.0
+
+Table: Model Summary
+,R,R Square,Adjusted R Square,Std. Error of the Estimate
+,.05,.00,.00,8.11
+
+Table: ANOVA
+,,Sum of Squares,df,Mean Square,F,Significance
+,Regression,235.23,1,235.23,3.58,.06
+,Residual,98438.40,1498,65.71,,
+,Total,98673.63,1499,,,
+
+Table: Coefficients
+,,B,Std. Error,Beta,t,Significance
+,(Constant),1.24,.42,.00,2.95,.21
+,v1,1.37,.72,.05,1.89,.06
+,,,,,,
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
index 9f8da00249a9682aa106bf521f0bf05203d8ddc2..88b4cd8318db1ba4daece8798460600137e0a2cc 100755 (executable)
@@ -81,51 +81,42 @@ activity="run program"
 $SUPERVISOR $PSPP --testing-mode $TESTFILE
 if [ $? -ne 0 ] ; then no_result ; fi
 
-perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
-diff -b  $TEMPDIR/pspp.list - << EOF
-1.1 DATA LIST.  Reading free-form data from INLINE.
-+--------+------+
-|Variable|Format|
-#========#======#
-|v0      |F8.0  |
-|v1      |F8.0  |
-|v2      |F8.0  |
-+--------+------+
-2.1 REGRESSION.  Model Summary
-#============#========#=================#==========================#
-#          R #R Square|Adjusted R Square|Std. Error of the Estimate#
-#========#===#========#=================#==========================#
-#        |.97#     .94|              .93|                      1.34#
-#========#===#========#=================#==========================#
-2.2 REGRESSION.  ANOVA
-#===================#==============#==#===========#=====#============#
-#                   #Sum of Squares|df|Mean Square|  F  |Significance#
-#========#==========#==============#==#===========#=====#============#
-#        |Regression#        202.75| 2|     101.38|56.75|         .00#
-#        |Residual  #         12.50| 7|       1.79|     |            #
-#        |Total     #        215.26| 9|           |     |            #
-#========#==========#==============#==#===========#=====#============#
-2.3 REGRESSION.  Coefficients
-#===================#=====#==========#=====#======#============#
-#                   #  B  |Std. Error| Beta|   t  |Significance#
-#========#==========#=====#==========#=====#======#============#
-#        |(Constant)# 2.19|      2.36|  .00|   .93|         .52#
-#        |    v0    # 1.81|      1.05|  .17|  1.72|         .12#
-#        |    v1    #-3.43|       .33|-1.03|-10.33|         .00#
-#        |          #     |          |     |      |            #
-#========#==========#=====#==========#=====#======#============#
-      v0       v1       v2     RES1    PRED1
--------- -------- -------- -------- --------
-     .65     7.74   -23.98     -.84   -23.13 
-    -.13     6.14   -19.64     -.54   -19.10 
-     .35     7.65   -25.27    -1.87   -23.40 
-     .69     6.13   -16.57      .97   -17.54 
-    -.07     8.25   -25.80      .40   -26.20 
-    -.34     6.03   -17.57     1.53   -19.10 
-     .76     9.83   -28.36     1.77   -30.13 
-    -.47     5.34   -16.80      .18   -16.97 
-    -.06     8.84   -29.26    -1.05   -28.21 
-     .56     6.20   -18.58     -.54   -18.04 
+diff -c $TEMPDIR/pspp.csv - << EOF
+Table: Reading free-form data from INLINE.
+Variable,Format
+v0,F8.0
+v1,F8.0
+v2,F8.0
+
+Table: Model Summary
+,R,R Square,Adjusted R Square,Std. Error of the Estimate
+,.97,.94,.93,1.34
+
+Table: ANOVA
+,,Sum of Squares,df,Mean Square,F,Significance
+,Regression,202.75,2,101.38,56.75,.00
+,Residual,12.50,7,1.79,,
+,Total,215.26,9,,,
+
+Table: Coefficients
+,,B,Std. Error,Beta,t,Significance
+,(Constant),2.19,2.36,.00,.93,.52
+,v0,1.81,1.05,.17,1.72,.12
+,v1,-3.43,.33,-1.03,-10.33,.00
+,,,,,,
+
+Table: Data List
+v0,v1,v2,RES1,PRED1
+.65,7.74,-23.98,-.84,-23.13
+-.13,6.14,-19.64,-.54,-19.10
+.35,7.65,-25.27,-1.87,-23.40
+.69,6.13,-16.57,.97,-17.54
+-.07,8.25,-25.80,.40,-26.20
+-.34,6.03,-17.57,1.53,-19.10
+.76,9.83,-28.36,1.77,-30.13
+-.47,5.34,-16.80,.18,-16.97
+-.06,8.84,-29.26,-1.05,-28.21
+.56,6.20,-18.58,-.54,-18.04
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
index ed4b4b854c4a67f416be869d0705b8839fda9318..c93440421a613d4ede40d2a7d39e3d3f6dd08eaa 100755 (executable)
@@ -249,95 +249,68 @@ $SUPERVISOR $PSPP --testing-mode -o raw-ascii $TESTFILE
 if [ $? -ne 0 ] ; then no_result ; fi
 
 activity="compare output"
-diff pspp.list - << EOF
+diff -c pspp.csv - << EOF
 Scale: Everything
 
-1.1 RELIABILITY.  Case Processing Summary
-#==============#===#======#
-#              # N |   %  #
-#==============#===#======#
-#Cases Valid   #131| 92.91#
-#      Excluded# 10|  7.09#
-#      Total   #141|100.00#
-#==============#===#======#
-
-1.2 RELIABILITY.  Reliability Statistics
-#================#==========#
-#Cronbach's Alpha#N of items#
-#================#==========#
-#             .75#         4#
-#================#==========#
+Table: Case Processing Summary
+,,N,%
+Cases,Valid,131,92.91
+,Excluded,10,7.09
+,Total,141,100.00
+
+Table: Reliability Statistics
+Cronbach's Alpha,N of items
+.75,4
 
 Scale: Nothing
 
-2.1 RELIABILITY.  Case Processing Summary
-#==============#===#======#
-#              # N |   %  #
-#==============#===#======#
-#Cases Valid   #131| 92.91#
-#      Excluded# 10|  7.09#
-#      Total   #141|100.00#
-#==============#===#======#
-
-2.2 RELIABILITY.  Reliability Statistics
-#==========================================================#===#
-#Cronbach's Alpha               Part 1           Value     #.55#
-#                                                N of Items#  2#
-#                               Part 2           Value     #.63#
-#                                                N of Items#  2#
-#                               Total N of Items           #  4#
-#Correlation Between Forms                                 #.61#
-#Spearman-Brown Coefficient     Equal Length               #.75#
-#                               Unequal Length             #.75#
-#Guttman Split-Half Coefficient                            #.75#
-#==========================================================#===#
+Table: Case Processing Summary
+,,N,%
+Cases,Valid,131,92.91
+,Excluded,10,7.09
+,Total,141,100.00
+
+Table: Reliability Statistics
+Cronbach's Alpha,Part 1,Value,.55
+,,N of Items,2
+,Part 2,Value,.63
+,,N of Items,2
+,Total N of Items,,4
+Correlation Between Forms,,,.61
+Spearman-Brown Coefficient,Equal Length,,.75
+,Unequal Length,,.75
+Guttman Split-Half Coefficient,,,.75
 
 Scale: Totals
 
-3.1 RELIABILITY.  Case Processing Summary
-#==============#===#======#
-#              # N |   %  #
-#==============#===#======#
-#Cases Valid   #131| 92.91#
-#      Excluded# 10|  7.09#
-#      Total   #141|100.00#
-#==============#===#======#
-
-3.2 RELIABILITY.  Reliability Statistics
-#================#==========#
-#Cronbach's Alpha#N of items#
-#================#==========#
-#             .75#         4#
-#================#==========#
-
-3.3 RELIABILITY.  Item-Total Statistics
-#=====#==========================#==============================#================================#================================#
-#     #Scale Mean if Item Deleted|Scale Variance if Item Deleted|Corrected Item-Total Correlation|Cronbach's Alpha if Item Deleted#
-#=====#==========================#==============================#================================#================================#
-#var6 #                     15.97|                          8.43|                             .51|                             .71#
-#var8 #                     16.56|                          7.86|                             .53|                             .70#
-#var15#                     16.47|                          8.45|                             .56|                             .68#
-#var17#                     16.60|                          8.00|                             .57|                             .67#
-#=====#==========================#==============================#================================#================================#
+Table: Case Processing Summary
+,,N,%
+Cases,Valid,131,92.91
+,Excluded,10,7.09
+,Total,141,100.00
+
+Table: Reliability Statistics
+Cronbach's Alpha,N of items
+.75,4
+
+Table: Item-Total Statistics
+,Scale Mean if Item Deleted,Scale Variance if Item Deleted,Corrected Item-Total Correlation,Cronbach's Alpha if Item Deleted
+var6,15.97,8.43,.51,.71
+var8,16.56,7.86,.53,.70
+var15,16.47,8.45,.56,.68
+var17,16.60,8.00,.57,.67
 
 Scale: ANY
 
-4.1 RELIABILITY.  Case Processing Summary
-#==============#===#======#
-#              # N |   %  #
-#==============#===#======#
-#Cases Valid   #131| 92.91#
-#      Excluded# 10|  7.09#
-#      Total   #141|100.00#
-#==============#===#======#
-
-4.2 RELIABILITY.  Reliability Statistics
-#================#==========#
-#Cronbach's Alpha#N of items#
-#================#==========#
-#             .75#         4#
-#================#==========#
+Table: Case Processing Summary
+,,N,%
+Cases,Valid,131,92.91
+,Excluded,10,7.09
+,Total,141,100.00
 
+Table: Reliability Statistics
+Cronbach's Alpha,N of items
+.75,4
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
index ff46486a3f0ef81ae420e90a6bf2da75539f42e0..a4b9646ec351246a94f7c1ccbaaf5c3e8dc2e600 100755 (executable)
@@ -88,27 +88,25 @@ grep -i Brake $TEMPDIR/rename.sav
 if [ $? -eq 0 ] ; then fail ; fi
 
 activity="compare output"
-perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
-diff  -b $TEMPDIR/pspp.list - <<EOF
-1.1 DATA LIST.  Reading free-form data from INLINE.
-+----------+------+
-| Variable |Format|
-#==========#======#
-|brakeFluid|F8.0  |
-|y         |F8.0  |
-+----------+------+
-brakeFluid        y
----------- --------
-      1.00     3.00 
-      2.00     3.00 
-      3.00     3.00 
-      4.00     3.00 
-applecarts        y
----------- --------
-      1.00     3.00 
-      2.00     3.00 
-      3.00     3.00 
-      4.00     3.00 
+diff -c $TEMPDIR/pspp.csv - <<EOF
+Table: Reading free-form data from INLINE.
+Variable,Format
+brakeFluid,F8.0
+y,F8.0
+
+Table: Data List
+brakeFluid,y
+1.00,3.00
+2.00,3.00
+3.00,3.00
+4.00,3.00
+
+Table: Data List
+applecarts,y
+1.00,3.00
+2.00,3.00
+3.00,3.00
+4.00,3.00
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
index 0c24f0c585961f799c6ccd23fdddfd0ab1dd3381..081263ee8c96a7f91a94484caf8bd1ea332d291f 100755 (executable)
@@ -99,74 +99,54 @@ if [ $? -ne 0 ] ; then no_result ; fi
 
 
 activity="compare results"
-perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
-diff -b  $TEMPDIR/pspp.list - << EOF
-1.1 ROC.  Case Summary
-#========#===================#
-#        # Valid N (listwise)#
-#        #==========#========#
-#a       #Unweighted|Weighted#
-#========#==========#========#
-#Positive#         5|  50.000#
-#Negative#         5|  50.000#
-#========#==========#========#
-1.2 ROC.  Area Under the Curve (x)
-#====#==========#===============#=======================#
-#    |          |               | Asymp. 99% Confidence #
-#    |          |               +-----------+-----------#
-#Area|Std. Error|Asymptotic Sig.|Lower Bound|Upper Bound#
-#====#==========#===============#===========#===========#
-#.910|      .030|           .000|       .839|       .981#
-#====#==========#===============#===========#===========#
-1.3 ROC.  Coordinates of the Curve (x)
-#====================================#===========#===============#
-#Positive if greater than or equal to|Sensitivity|1 - Specificity#
-#====================================#===========#===============#
-#                                .000|      1.000|          1.000#
-#                               1.500|       .960|           .440#
-#                               2.500|       .880|           .160#
-#                               3.500|       .680|           .060#
-#                               4.500|       .400|           .020#
-#                               6.000|       .000|           .000#
-#====================================#===========#===============#
-2.1 ROC.  Case Summary
-#========#===================#
-#        # Valid N (listwise)#
-#        #==========#========#
-#a       #Unweighted|Weighted#
-#========#==========#========#
-#Positive#         5|  50.000#
-#Negative#         5|  50.000#
-#========#==========#========#
-See pspp-1.png for a chart.
-2.2 ROC.  Area Under the Curve
-#===================#====#==========#===============#=======================#
-#                   #    |          |               | Asymp. 95%            #
-#                   #    |          |               +-----------+-----------#
-#Variable under test#Area|Std. Error|Asymptotic Sig.|Lower Bound|Upper Bound#
-#===================#====#==========#===============#===========#===========#
-#                  x#.910|      .030|           .000|       .860|       .960#
-#                  y#.697|      .052|           .001|       .611|       .783#
-#===================#====#==========#===============#===========#===========#
-2.3 ROC.  Coordinates of the Curve
-#=============#====================================#===========#===============#
-#Test variable#Positive if greater than or equal to|Sensitivity|1 - Specificity#
-#=============#====================================#===========#===============#
-#            x#                                .000|      1.000|          1.000#
-#             #                               1.500|       .960|           .440#
-#             #                               2.500|       .880|           .160#
-#             #                               3.500|       .680|           .060#
-#             #                               4.500|       .400|           .020#
-#             #                               6.000|       .000|           .000#
-#-------------#------------------------------------+-----------+---------------#
-#            y#                                .000|      1.000|          1.000#
-#             #                               1.500|       .960|           .900#
-#             #                               2.500|       .680|           .340#
-#             #                               3.000|       .600|           .340#
-#             #                               3.500|       .600|           .300#
-#             #                               4.500|       .200|           .020#
-#             #                               6.000|       .000|           .000#
-#=============#====================================#===========#===============#
+diff -c $TEMPDIR/pspp.csv - << EOF
+Table: Case Summary
+,Valid N (listwise),
+a,Unweighted,Weighted
+Positive,5,50.000
+Negative,5,50.000
+
+Table: Area Under the Curve (x)
+,,,Asymp. 99% Confidence Interval,
+Area,Std. Error,Asymptotic Sig.,Lower Bound,Upper Bound
+.910,.030,.000,.839,.981
+
+Table: Coordinates of the Curve (x)
+Positive if greater than or equal to,Sensitivity,1 - Specificity
+.000,1.000,1.000
+1.500,.960,.440
+2.500,.880,.160
+3.500,.680,.060
+4.500,.400,.020
+6.000,.000,.000
+
+Table: Case Summary
+,Valid N (listwise),
+a,Unweighted,Weighted
+Positive,5,50.000
+Negative,5,50.000
+
+Table: Area Under the Curve
+,,,,Asymp. 95% Confidence Interval,
+Variable under test,Area,Std. Error,Asymptotic Sig.,Lower Bound,Upper Bound
+x,.910,.030,.000,.860,.960
+y,.697,.052,.001,.611,.783
+
+Table: Coordinates of the Curve
+Test variable,Positive if greater than or equal to,Sensitivity,1 - Specificity
+x,.000,1.000,1.000
+,1.500,.960,.440
+,2.500,.880,.160
+,3.500,.680,.060
+,4.500,.400,.020
+,6.000,.000,.000
+y,.000,1.000,1.000
+,1.500,.960,.900
+,2.500,.680,.340
+,3.000,.600,.340
+,3.500,.600,.300
+,4.500,.200,.020
+,6.000,.000,.000
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
index a8020b0c26c6c6f901cf1ce203fb69c5a35ac859..ffed0fe6a7fd243dd2936e14ab64c8c50fd94d10 100755 (executable)
@@ -106,25 +106,17 @@ if [ $? -ne 0 ] ; then no_result ; fi
 
 
 activity="compare results"
-perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
-diff -b  $TEMPDIR/pspp.list - << EOF
-1.1 ROC.  Case Summary
-#========#===================#
-#        # Valid N (listwise)#
-#        #==========#========#
-#a       #Unweighted|Weighted#
-#========#==========#========#
-#Positive#        14|  14.000#
-#Negative#        14|  14.000#
-#========#==========#========#
-1.2 ROC.  Area Under the Curve (x)
-#====#==========#===============#=======================#
-#    |          |               | Asymp. 95% Confidence #
-#    |          |               +-----------+-----------#
-#Area|Std. Error|Asymptotic Sig.|Lower Bound|Upper Bound#
-#====#==========#===============#===========#===========#
-#.490|      .111|           .927|       .307|       .673#
-#====#==========#===============#===========#===========#
+diff -c $TEMPDIR/pspp.csv - << EOF
+Table: Case Summary
+,Valid N (listwise),
+a,Unweighted,Weighted
+Positive,14,14.000
+Negative,14,14.000
+
+Table: Area Under the Curve (x)
+,,,Asymp. 95% Confidence Interval,
+Area,Std. Error,Asymptotic Sig.,Lower Bound,Upper Bound
+.490,.111,.927,.307,.673
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
index 4f4515e2be05073a21a85232970a2bdbe77b5069..4d001bf7ff69004775758c5d16dadad5b7606e21 100755 (executable)
@@ -85,19 +85,17 @@ $SUPERVISOR $PSPP --testing-mode $TEMPDIR/sample.stat
 if [ $? -ne 0 ] ; then no_result ; fi
 
 activity="create head"
-grep -v '^\ *$' $TEMPDIR/pspp.list | head -2 > $TEMPDIR/head
+grep -v '^\ *$' $TEMPDIR/pspp.csv | head -1 > $TEMPDIR/head
 if [ $? -ne 0 ] ; then no_result ; fi
 
 activity="extract data"
-grep  '[0-9][0-9]*' $TEMPDIR/pspp.list > $TEMPDIR/data
+grep  '[0-9][0-9]*' $TEMPDIR/pspp.csv > $TEMPDIR/data
 if [ $? -ne 0 ] ; then no_result ; fi
 
 
 activity="compare head"
-perl -pi -e 's/^\s*$//g' $TEMPDIR/head
 diff -b $TEMPDIR/head - << EOF
- A
---
+Table: Data List
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
index eb2c991c1e97d328d120c76fe9915c67fbde93d3..1452588d36752c29cfec480d2ac5eee2085a08ea 100755 (executable)
@@ -89,29 +89,33 @@ $SUPERVISOR $PSPP --testing-mode $TEMPDIR/split.stat
 if [ $? -ne 0 ] ; then no_result ; fi
 
 activity="compare results"
-perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
-diff  -b $TEMPDIR/pspp.list - <<EOF
-Variable Value Label
-X            1
-X Y
-- -
-1 2 
-1 6 
-1 7 
-1 9 
-1 5 
-1 4 
-Variable Value Label
-X            2
-X Y
-- -
-2 7 
-2 0 
-2 6 
-2 5 
-2 8 
-2 9 
-2 4
+diff -c $TEMPDIR/pspp.csv - <<EOF
+Title: Test SPLIT FILE utility
+
+Variable,Value,Label
+X,1,
+
+Table: Data List
+X,Y
+1,2
+1,6
+1,7
+1,9
+1,5
+1,4
+
+Variable,Value,Label
+X,2,
+
+Table: Data List
+X,Y
+2,7
+2,0
+2,6
+2,5
+2,8
+2,9
+2,4
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
index d8f8ae3647ffe306e531c945184190eb9d0478a8..d5768252ffd4d667d0b29c1c4e6966cf074a3885 100755 (executable)
@@ -78,43 +78,34 @@ $SUPERVISOR $PSPP --testing-mode $TESTFILE
 if [ $? -ne 0 ] ; then no_result ; fi
 
 activity="filter output"
-egrep -v '^(Created|Endian|Integer Format|Real Format): ' $TEMPDIR/pspp.list > $TEMPDIR/out-filtered
+egrep -v '^(Created|Endian|Integer Format|Real Format):,' $TEMPDIR/pspp.csv > $TEMPDIR/out-filtered
 if [ $? -ne 0 ] ; then no_result ; fi
 
 activity="compare output"
-perl -pi -e 's/^\s*$//g' $TEMPDIR/out-filtered
-diff -b -w $TEMPDIR/out-filtered - << EOF
-1.1 DATA LIST.  Reading free-form data from INLINE.
-+--------+------+
-|Variable|Format|
-#========#======#
-|x       |F8.0  |
-|name    |A10   |
-+--------+------+
-2.1 SYSFILE INFO.  
-File:           pro.sav
-Label:          No label.
-Variables:      2
-Cases:          3
-Type:           System File
-Weight:         Not weighted.
-Mode:           Compression on.
-Charset:        Unknown
-+--------+-------------+---+
-|Variable|Description  |Pos|
-|        |             |iti|
-|        |             |on |
-#========#=============#===#
-|x       |Format: F8.2 |  1|
-|        |Measure:     |   |
-|        |Display      |   |
-|        |Display      |   |
-+--------+-------------+---+
-|name    |Format: A10  |  2|
-|        |Measure:     |   |
-|        |Display      |   |
-|        |Display      |   |
-+--------+-------------+---+
+diff -c $TEMPDIR/out-filtered - << EOF
+Table: Reading free-form data from INLINE.
+Variable,Format
+x,F8.0
+name,A10
+
+File:,pro.sav
+Label:,No label.
+Variables:,2
+Cases:,3
+Type:,System File
+Weight:,Not weighted.
+Mode:,Compression on.
+Charset:,Unknown
+
+Variable,Description,,Position
+x,Format: F8.2,,1
+,Measure: Scale,,
+,Display Alignment: Right,,
+,Display Width: 8,,
+name,Format: A10,,2
+,Measure: Nominal,,
+,Display Alignment: Left,,
+,Display Width: 10,,
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
index df16690b01e18bba127ff1f7705e95dd528ecbf2..e136a0b209eefc347b8ec371e57e49d4059a35ea 100755 (executable)
@@ -97,18 +97,17 @@ grep  'X=X' $TEMPDIR/foo.sav
 if [ $? -eq 0 ] ; then fail ; fi
 
 activity="compare output"
-perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
-diff -b  -w $TEMPDIR/pspp.list - << EOF
-       x variable variab_a variab_b
--------- -------- -------- --------
-    1.00     1.00     1.00     2.00 
-    1.00     1.00     2.00    30.00 
-    1.00     2.00     1.00     8.00 
-    1.00     2.00     2.00    20.00 
-    2.00     1.00     1.00     2.00 
-    2.00     1.00     2.00    22.00 
-    2.00     2.00     1.00     1.00 
-    2.00     2.00     2.00     3.00 
+diff -c $TEMPDIR/pspp.csv - << EOF
+Table: Data List
+x,variable,variab_a,variab_b
+1.00,1.00,1.00,2.00
+1.00,1.00,2.00,30.00
+1.00,2.00,1.00,8.00
+1.00,2.00,2.00,20.00
+2.00,1.00,1.00,2.00
+2.00,1.00,2.00,22.00
+2.00,2.00,1.00,1.00
+2.00,2.00,2.00,3.00
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
index d4f59052de98b9d5e99820a2dce6beffbe221a89..a671dff5244f5b79f1920b3ba44b9b4a8d45a5c1 100755 (executable)
@@ -87,18 +87,17 @@ $SUPERVISOR $PSPP --testing-mode $TESTFILE
 if [ $? -ne 0 ] ; then no_result ; fi
 
 activity="compare output"
-perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
-diff -b  -w $TEMPDIR/pspp.list - << EOF
-variable001 variable002 variable003 variable004
------------ ----------- ----------- -----------
-       1.00        1.00        1.00        2.00  
-       1.00        1.00        2.00       30.00 
-       1.00        2.00        1.00       8.00 
-       1.00        2.00        2.00      20.00 
-       2.00        1.00        1.00       2.00 
-       2.00        1.00        2.00      22.00 
-       2.00        2.00        1.00       1.00 
-       2.00        2.00        2.00       3.00 
+diff -c $TEMPDIR/pspp.csv - << EOF
+Table: Data List
+variable001,variable002,variable003,variable004
+1.00,1.00,1.00,2.00
+1.00,1.00,2.00,30.00
+1.00,2.00,1.00,8.00
+1.00,2.00,2.00,20.00
+2.00,1.00,1.00,2.00
+2.00,1.00,2.00,22.00
+2.00,2.00,1.00,1.00
+2.00,2.00,2.00,3.00
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
index ba3e5c5f4e15aabccc4e8c96da676f4327e29da7..0dcd194ba2b45eeb3eec557bf8d724116ad5c8b9 100755 (executable)
@@ -95,33 +95,23 @@ if [ $? -ne 0 ] ; then no_result ; fi
 
 
 activity="compare output"
-perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
-diff  -b $TEMPDIR/pspp.list - <<EOF
-1.1 DATA LIST.  Reading free-form data from INLINE.
-+--------+------+
-|Variable|Format|
-#========#======#
-|INDEP   |F8.0  |
-|DEP     |F8.0  |
-+--------+------+
-2.1 T-TEST.  Group Statistics
-#===========#==#====#==============#=========#
-#     INDEP | N|Mean|Std. Deviation|S.E. Mean#
-#===========#==#====#==============#=========#
-#DEP >=1.514|11|9.00|          3.82|     1.15#
-#    <1.514 |11|8.00|          2.86|      .86#
-#===========#==#====#==============#=========#
-2.2 T-TEST.  Independent Samples Test
-#==============================#========#============================================================================#
-#                              #Levene's|                        t-test for Equality of Means                        #
-#                              #---+----+----+-----+---------------+---------------+---------------------+-----------#
-#                              #   |    |    |     |               |               |                     |    95%    #
-#                              #   |    |    |     |               |               |                     +-----+-----#
-#                              # F |Sig.|  t |  df |Sig. (2-tailed)|Mean Difference|Std. Error Difference|Lower|Upper#
-#==============================#===#====#====#=====#===============#===============#=====================#=====#=====#
-#DEPEqual variances assumed    #.17| .68|-.69|20.00|            .50|          -1.00|                 1.44|-4.00| 2.00#
-#   Equal variances not assumed#   |    |-.69|18.54|            .50|          -1.00|                 1.44|-4.02| 2.02#
-#==============================#===#====#====#=====#===============#===============#=====================#=====#=====#
+diff -c $TEMPDIR/pspp.csv - <<EOF
+Table: Reading free-form data from INLINE.
+Variable,Format
+INDEP,F8.0
+DEP,F8.0
+
+Table: Group Statistics
+,INDEP,N,Mean,Std. Deviation,S.E. Mean
+DEP,>=1.514,11,9.00,3.82,1.15
+,<1.514,11,8.00,2.86,.86
+
+Table: Independent Samples Test
+,,Levene's Test for Equality of Variances,,t-test for Equality of Means,,,,,,
+,,,,,,,,,95% Confidence Interval of the Difference,
+,,F,Sig.,t,df,Sig. (2-tailed),Mean Difference,Std. Error Difference,Lower,Upper
+DEP,Equal variances assumed,.17,.68,-.69,20.00,.50,-1.00,1.44,-4.00,2.00
+,Equal variances not assumed,,,-.69,18.54,.50,-1.00,1.44,-4.02,2.02
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
index 4cc2effa5b9c946e0425f714bf1feb1329350060..cbac8dba2537b6f4a44291d88b3eb9615262f148 100755 (executable)
@@ -79,7 +79,7 @@ $SUPERVISOR $PSPP --testing-mode $TESTFILE
 if [ $? -ne 0 ] ; then no_result ; fi
 
 activity="copy output"
-cp $TEMPDIR/pspp.list $TEMPDIR/ref.list
+cp $TEMPDIR/pspp.csv $TEMPDIR/ref.csv
 if [ $? -ne 0 ] ; then no_result ; fi
 
 
@@ -105,7 +105,7 @@ $SUPERVISOR $PSPP --testing-mode $TESTFILE
 if [ $? -ne 0 ] ; then no_result ; fi
 
 activity="compare outputs"
-diff $TEMPDIR/ref.list $TEMPDIR/pspp.list
+diff $TEMPDIR/ref.csv $TEMPDIR/pspp.csv
 if [ $? -ne 0 ] ; then fail ; fi
 
 
index 07471484868d9e27af76a784d8bdca77858275bc..0db7e8e9c2f55d9b5e6535eb18269404772e301b 100755 (executable)
@@ -78,7 +78,7 @@ $SUPERVISOR $PSPP --testing-mode $TESTFILE
 if [ $? -ne 0 ] ; then no_result ; fi
 
 activity="copy output"
-cp $TEMPDIR/pspp.list $TEMPDIR/ref.list
+cp $TEMPDIR/pspp.csv $TEMPDIR/ref.csv
 if [ $? -ne 0 ] ; then no_result ; fi
 
 
@@ -105,7 +105,7 @@ $SUPERVISOR $PSPP --testing-mode $TESTFILE
 if [ $? -ne 0 ] ; then no_result ; fi
 
 activity="compare outputs"
-diff $TEMPDIR/ref.list $TEMPDIR/pspp.list
+diff $TEMPDIR/ref.csv $TEMPDIR/pspp.csv
 if [ $? -ne 0 ] ; then fail ; fi
 
 
index 843698182f461315511502fd77861d7c725237b2..c20d0f66113183c9e8f0f23dd311d23a93f5c56c 100755 (executable)
@@ -78,31 +78,21 @@ $SUPERVISOR $PSPP --testing-mode $TESTFILE
 if [ $? -ne 0 ] ; then no_result ; fi
 
 activity="compare output"
-perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
-diff  -b $TEMPDIR/pspp.list - <<EOF
-1.1 DATA LIST.  Reading free-form data from INLINE.
-+--------+------+
-|Variable|Format|
-#========#======#
-|ID      |F8.0  |
-|ABC     |F8.0  |
-+--------+------+
-2.1 T-TEST.  One-Sample Statistics
-#===#=#====#==============#=========#
-#   #N|Mean|Std. Deviation|S.E. Mean#
-#===#=#====#==============#=========#
-#ABC#6|3.00|           .84|      .34#
-#===#=#====#==============#=========#
-2.2 T-TEST.  One-Sample Test
-#===#===================================================#
-#   #               Test Value = 2.000000               #
-#   #----+--+---------------+---------------+-----------#
-#   #    |  |               |               |    95%    #
-#   #    |  |               |               +-----+-----#
-#   #  t |df|Sig. (2-tailed)|Mean Difference|Lower|Upper#
-#===#====#==#===============#===============#=====#=====#
-#ABC#2.93| 5|            .03|           1.00|  .12| 1.88#
-#===#====#==#===============#===============#=====#=====#
+diff -c $TEMPDIR/pspp.csv - <<EOF
+Table: Reading free-form data from INLINE.
+Variable,Format
+ID,F8.0
+ABC,F8.0
+
+Table: One-Sample Statistics
+,N,Mean,Std. Deviation,S.E. Mean
+ABC,6,3.00,.84,.34
+
+Table: One-Sample Test
+,Test Value = 2.000000,,,,,
+,,,,,95% Confidence Interval of the Difference,
+,t,df,Sig. (2-tailed),Mean Difference,Lower,Upper
+ABC,2.93,5,.03,1.00,.12,1.88
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
index 53080bdf8b12c31c6bb4dfd35cca650886cc8a39..57aef96a0fc12fbe2a2aa5c83d1e0c9100b9e5b4 100755 (executable)
@@ -85,41 +85,29 @@ $SUPERVISOR $PSPP --testing-mode $TESTFILE
 if [ $? -ne 0 ] ; then no_result ; fi
 
 activity="compare output"
-perl -pi -e s/^\s*\$//g $TEMPDIR/pspp.list
-diff  -b $TEMPDIR/pspp.list - <<EOF | perl -e 's/^\s*$//g'
-1.1 DATA LIST.  Reading free-form data from INLINE.
-+--------+------+
-|Variable|Format|
-#========#======#
-|ID      |F8.0  |
-|INDEP   |F8.0  |
-|DEP1    |F8.0  |
-|DEP2    |F8.0  |
-+--------+------+
-
-2.1 T-TEST.  Group Statistics
-#==========#=#====#==============#========#
-#     INDEP|N|Mean|Std. Deviation|SE. Mean#
-#==========#=#====#==============#========#
-#DEP1 1.1  |5|2.00|          .707|    .316#
-#     2.1  |5|4.00|          .707|    .316#
-#DEP2 1.1  |5|4.00|          .707|    .316#
-#     2.1  |5|2.00|          .707|    .316#
-#==========#=#====#==============#========#
-
-2.2 T-TEST.  Independent Samples Test
-#===============================#==========#===============================================================================#
-#                               # Levene's |                          t-test for Equality of Means                         #
-#                               #----+-----+------+-----+---------------+---------------+---------------------+------------#
-#                               #    |     |      |     |               |               |                     |    95%     #
-#                               #    |     |      |     |               |               |                     +------+-----#
-#                               # F  | Sig.|   t  |  df |Sig. (2-tailed)|Mean Difference|Std. Error Difference| Lower|Upper#
-#===============================#====#=====#======#=====#===============#===============#=====================#======#=====#
-#DEP1Equal variances assumed    #.000|1.000|-4.472|    8|           .002|         -2.000|                 .447|-3.031|-.969#
-#    Equal variances not assumed#    |     |-4.472|8.000|           .002|         -2.000|                 .447|-3.031|-.969#
-#DEP2Equal variances assumed    #.000|1.000| 4.472|    8|           .002|          2.000|                 .447|  .969|3.031#
-#    Equal variances not assumed#    |     | 4.472|8.000|           .002|          2.000|                 .447|  .969|3.031#
-#===============================#====#=====#======#=====#===============#===============#=====================#======#=====#
+diff -c $TEMPDIR/pspp.csv - <<EOF
+Table: Reading free-form data from INLINE.
+Variable,Format
+ID,F8.0
+INDEP,F8.0
+DEP1,F8.0
+DEP2,F8.0
+
+Table: Group Statistics
+,INDEP,N,Mean,Std. Deviation,S.E. Mean
+DEP1,1.1,5,2.00,.71,.32
+,2.1,5,4.00,.71,.32
+DEP2,1.1,5,4.00,.71,.32
+,2.1,5,2.00,.71,.32
+
+Table: Independent Samples Test
+,,Levene's Test for Equality of Variances,,t-test for Equality of Means,,,,,,
+,,,,,,,,,95% Confidence Interval of the Difference,
+,,F,Sig.,t,df,Sig. (2-tailed),Mean Difference,Std. Error Difference,Lower,Upper
+DEP1,Equal variances assumed,.00,1.00,-4.47,8.00,.00,-2.00,.45,-3.03,-.97
+,Equal variances not assumed,,,-4.47,8.00,.00,-2.00,.45,-3.03,-.97
+DEP2,Equal variances assumed,.00,1.00,4.47,8.00,.00,2.00,.45,.97,3.03
+,Equal variances not assumed,,,4.47,8.00,.00,2.00,.45,.97,3.03
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
index fd307ff339104fc3182b7feb5767ece35152f2a7..aab5ecc4dd2b7b95d742816c4743c799b8e0ca4b 100755 (executable)
@@ -78,7 +78,7 @@ $SUPERVISOR $PSPP --testing-mode $TESTFILE
 if [ $? -ne 0 ] ; then no_result ; fi
 
 activity="copy output"
-cp $TEMPDIR/pspp.list $TEMPDIR/ref.list
+cp $TEMPDIR/pspp.csv $TEMPDIR/ref.csv
 if [ $? -ne 0 ] ; then no_result ; fi
 
 activity="create program 2"
@@ -107,7 +107,7 @@ if [ $? -ne 0 ] ; then no_result ; fi
 
 
 activity="compare outputs"
-diff $TEMPDIR/ref.list $TEMPDIR/pspp.list 
+diff $TEMPDIR/ref.csv $TEMPDIR/pspp.csv 
 if [ $? -ne 0 ] ; then fail ; fi
 
 
index 2e62b4d2fb6137106cc13bcf43d8b0bef7aad2b3..026dba7fe6fe6f34e039d610d65f530e1ddd6d2b 100755 (executable)
@@ -79,7 +79,7 @@ $SUPERVISOR $PSPP --testing-mode $TESTFILE
 if [ $? -ne 0 ] ; then no_result ; fi
 
 activity="copy output"
-cp $TEMPDIR/pspp.list $TEMPDIR/ref.list
+cp $TEMPDIR/pspp.csv $TEMPDIR/ref.csv
 if [ $? -ne 0 ] ; then no_result ; fi
 
 activity="create program 2"
@@ -105,7 +105,7 @@ if [ $? -ne 0 ] ; then no_result ; fi
 
 
 activity="compare outputs"
-diff $TEMPDIR/ref.list $TEMPDIR/pspp.list 
+diff $TEMPDIR/ref.csv $TEMPDIR/pspp.csv 
 if [ $? -ne 0 ] ; then fail ; fi
 
 
index a747d4fddc2cd754c1df1050764960a9887943a2..b1e4157bec08890da40343bd3eacf63027a5d256 100755 (executable)
@@ -78,7 +78,7 @@ $SUPERVISOR $PSPP --testing-mode $TESTFILE
 if [ $? -ne 0 ] ; then no_result ; fi
 
 activity="copy output"
-mv $TEMPDIR/pspp.list $TEMPDIR/ref.list
+mv $TEMPDIR/pspp.csv $TEMPDIR/ref.csv
 if [ $? -ne 0 ] ; then no_result ; fi
 
 activity="create program 2"
@@ -105,7 +105,7 @@ if [ $? -ne 0 ] ; then no_result ; fi
 
 
 activity="compare outputs"
-diff $TEMPDIR/ref.list $TEMPDIR/pspp.list
+diff $TEMPDIR/ref.csv $TEMPDIR/pspp.csv
 if [ $? -ne 0 ] ; then fail ; fi
 
 
index 0f96be4e2b49cf0bfb6ecb4a4ed73b5594a4d230..f034651a9438af4304a85f6fba5d8b71c37d9360 100755 (executable)
@@ -78,7 +78,7 @@ $SUPERVISOR $PSPP --testing-mode $TESTFILE
 if [ $? -ne 0 ] ; then no_result ; fi
 
 activity="copy output"
-mv $TEMPDIR/pspp.list $TEMPDIR/ref.list
+mv $TEMPDIR/pspp.csv $TEMPDIR/ref.csv
 if [ $? -ne 0 ] ; then no_result ; fi
 
 activity="create program 2"
@@ -105,7 +105,7 @@ if [ $? -ne 0 ] ; then no_result ; fi
 
 
 activity="compare outputs"
-diff $TEMPDIR/ref.list $TEMPDIR/pspp.list
+diff $TEMPDIR/ref.csv $TEMPDIR/pspp.csv
 if [ $? -ne 0 ] ; then fail ; fi
 
 
index 37f833049b0842adb2d2fc83216c571070809d59..eb23b6a1d05ee19241ff67fb38ac441d8895f4e1 100755 (executable)
@@ -77,39 +77,27 @@ $SUPERVISOR $PSPP --testing-mode $TESTFILE
 if [ $? -ne 0 ] ; then no_result ; fi
 
 activity="compare output"
-perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
-diff  -b $TEMPDIR/pspp.list - <<EOF
-1.1 DATA LIST.  Reading free-form data from INLINE.
-+--------+------+
-|Variable|Format|
-#========#======#
-|ID      |F8.0  |
-|A       |F8.0  |
-|B       |F8.0  |
-+--------+------+
-2.1 T-TEST.  Paired Sample Statistics
-#========#====#=#==============#=========#
-#        #Mean|N|Std. Deviation|S.E. Mean#
-#========#====#=#==============#=========#
-#Pair 0 A#2.00|5|           .71|      .32#
-#       B#4.00|5|          1.54|      .69#
-#========#====#=#==============#=========#
-2.2 T-TEST.  Paired Samples Correlations
-#======#=====#=#===========#====#
-#      |     #N|Correlation|Sig.#
-#======#=====#=#===========#====#
-#Pair 0|A & B#5|        .92| .03#
-#======#=====#=#===========#====#
-2.3 T-TEST.  Paired Samples Test
-#===========#================================================#=====#==#===============#
-#           #               Paired Differences               |     |  |               #
-#           #-----+--------------+---------------+-----------+     |  |               #
-#           #     |              |               |    95%    |     |  |               #
-#           #     |              |               +-----+-----+     |  |               #
-#           # Mean|Std. Deviation|Std. Error Mean|Lower|Upper|  t  |df|Sig. (2-tailed)#
-#===========#=====#==============#===============#=====#=====#=====#==#===============#
-#Pair 0A - B#-2.00|           .94|            .42|-3.16| -.84|-4.78| 4|            .01#
-#===========#=====#==============#===============#=====#=====#=====#==#===============#
+diff -c $TEMPDIR/pspp.csv - <<EOF
+Table: Reading free-form data from INLINE.
+Variable,Format
+ID,F8.0
+A,F8.0
+B,F8.0
+
+Table: Paired Sample Statistics
+,,Mean,N,Std. Deviation,S.E. Mean
+Pair 0,A,2.00,5,.71,.32
+,B,4.00,5,1.54,.69
+
+Table: Paired Samples Correlations
+,,N,Correlation,Sig.
+Pair 0,A & B,5,.92,.03
+
+Table: Paired Samples Test
+,,Paired Differences,,,,,,,
+,,,,,95% Confidence Interval of the Difference,,,,
+,,Mean,Std. Deviation,Std. Error Mean,Lower,Upper,t,df,Sig. (2-tailed)
+Pair 0,A - B,-2.00,.94,.42,-3.16,-.84,-4.78,4,.01
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
index ad530f45c83cb6cbfbb89881911e3b841ce8b8e7..addb170b0a2452a0ad6a40e9436d5c768846242f 100755 (executable)
@@ -82,14 +82,11 @@ $SUPERVISOR $PSPP --testing-mode $TEMPDIR/tabs.stat
 if [ $? -ne 0 ] ; then no_result ; fi
 
 
-perl -pi -e s/^\s*\$//g $TEMPDIR/pspp.list
-diff  -b $TEMPDIR/pspp.list - << EOF | perl -e 's/^\s*$//g'
-1.1 DATA LIST.  Reading 1 record from INLINE.
-+--------+------+-------+------+
-|Variable|Record|Columns|Format|
-#========#======#=======#======#
-|X       |     1|  1- 80|A80   |
-+--------+------+-------+------+
+diff -c $TEMPDIR/pspp.csv - << EOF | perl -e 's/^\s*$//g'
+Table: Reading 1 record from INLINE.
+Variable,Record,Columns,Format
+X,1,1- 80,A80
+
     1   12  123 1234    12345
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
index 660752dd914eda2d4146a9aea42ebc7695597515..2884392079dd727ad1431000b75dc8d8c6c2555f 100755 (executable)
@@ -86,43 +86,33 @@ if [ $? -ne 0 ] ; then no_result ; fi
 
 
 activity="compare results"
-perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
-diff -b  $TEMPDIR/pspp.list - << EOF
-1.1 DATA LIST.  Reading free-form data from INLINE.
-+--------+------+
-|Variable|Format|
-#========#======#
-|X       |F8.0  |
-|C       |F8.0  |
-+--------+------+
-2.1 EXAMINE.  Case Processing Summary
-#=#=======================================#
-# #                 Cases                 #
-# #-------------+-----------+-------------#
-# #    Valid    |  Missing  |    Total    #
-# #-----+-------+---+-------+-----+-------#
-# #  N  |Percent| N |Percent|  N  |Percent#
-#=#=====#=======#===#=======#=====#=======#
-#X#52.00|   100%|.00|     0%|52.00|   100%#
-#=#=====#=======#===#=======#=====#=======#
-2.2 EXAMINE.  Descriptives
-#==============================================#=========#==========#
-#                                              #Statistic|Std. Error#
-#==============================================#=========#==========#
-#X Mean                                        #   2.02  |    .03   #
-#  95% Confidence Interval for Mean Lower Bound#   1.95  |          #
-#                                   Upper Bound#   2.09  |          #
-#  5% Trimmed Mean                             #   2.00  |          #
-#  Median                                      #   2.00  |          #
-#  Variance                                    #   .06   |          #
-#  Std. Deviation                              #   .24   |          #
-#  Minimum                                     #   1.00  |          #
-#  Maximum                                     #   3.00  |          #
-#  Range                                       #   2.00  |          #
-#  Interquartile Range                         #   .00   |          #
-#  Skewness                                    #   1.19  |    .33   #
-#  Kurtosis                                    #  15.73  |    .65   #
-#==============================================#=========#==========#
+diff -c $TEMPDIR/pspp.csv - << EOF
+Table: Reading free-form data from INLINE.
+Variable,Format
+X,F8.0
+C,F8.0
+
+Table: Case Processing Summary
+,Cases,,,,,
+,Valid,,Missing,,Total,
+,N,Percent,N,Percent,N,Percent
+X,52.00,100%,.00,0%,52.00,100%
+
+Table: Descriptives
+,,,Statistic,Std. Error
+X,Mean,,2.02,.03
+,95% Confidence Interval for Mean,Lower Bound,1.95,
+,,Upper Bound,2.09,
+,5% Trimmed Mean,,2.00,
+,Median,,2.00,
+,Variance,,.06,
+,Std. Deviation,,.24,
+,Minimum,,1.00,
+,Maximum,,3.00,
+,Range,,2.00,
+,Interquartile Range,,.00,
+,Skewness,,1.19,.33
+,Kurtosis,,15.73,.65
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
index 8bcb56715d5c8980d7f83bdc2a9450c4621f4f17..92615a641b36fd2c507a2b7b924983ad4b19f587 100755 (executable)
@@ -84,25 +84,24 @@ cat > b.data <<EOF
 EOF
 if [ $? -ne 0 ] ; then no_result ; fi
 
-cat > update.out <<EOF
-A B C D INA INB
-- - - - --- ---
-0 a A     1   0
-1 b B N   1   1
-1 a C     1   0
-2 a D     1   0
-3 b E O   1   1
-4 b F P   1   1
-5 a G     1   0
-5 a H     1   0
-6 b I Q   1   1
-7 b J R   1   1
-7 a K     1   0
-7 a L     1   0
-8 a M     1   0
-9 b   S   0   1
+cat > update.csv <<EOF
+Table: Data List
+A,B,C,D,INA,INB
+0,a,A,,1,0
+1,b,B,N,1,1
+1,a,C,,1,0
+2,a,D,,1,0
+3,b,E,O,1,1
+4,b,F,P,1,1
+5,a,G,,1,0
+5,a,H,,1,0
+6,b,I,Q,1,1
+7,b,J,R,1,1
+7,a,K,,1,0
+7,a,L,,1,0
+8,a,M,,1,0
+9,b,,S,0,1
 EOF
-perl -pi -e 's/^\s*$//g' update.out
 
 # Test UPDATE.
 dla="data list notable file='a.data' /A B C 1-3 (a)."
@@ -160,8 +159,8 @@ EOF
     if [ $? -ne 0 ] ; then no_result ; fi
 
     activity="check $name output"
-    perl -pi -e 's/^\s*$//g' pspp.list
-    diff -c -b -w pspp.list update.out
+    perl -pi -e 's/^\s*$//g' pspp.csv
+    diff -c pspp.csv update.csv
     if [ $? -ne 0 ] ; then fail ; fi
     diff -c -b -w - errors <<EOF
 $name.pspp:8: warning: UPDATE: Encountered 3 sets of duplicate cases in the master file.
index 18b66f0f158054209c6b9eec653c08c2a062b663..b6bc661ffdff590e7b8546dea0bae594e425a9c9 100755 (executable)
@@ -84,19 +84,18 @@ if [ $? -ne 0 ] ; then no_result ; fi
 
 
 activity="check results"
-perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
-diff  -b $TEMPDIR/pspp.list - << EOF
- X
---
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
+diff -c $TEMPDIR/pspp.csv - << EOF
+Table: Data List
+X
+1
+2
+3
+4
+5
+6
+7
+8
+9
 10
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
index d3f680245315d5eb00ed7b0e2d83c1d781e3cb51..1cf74f34881b6bd67319352458e8747f3b5d45de 100755 (executable)
@@ -74,27 +74,20 @@ $SUPERVISOR $PSPP --testing-mode $TESTFILE
 if [ $? -ne 0 ] ; then fail ; fi
 
 activity="compare output"
-perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
-diff -b  $TEMPDIR/pspp.list - << EOF
-1.1 DISPLAY.
-+--------+-------------------------------------------+--------+
-|Variable|Description                                |Position|
-#========#===========================================#========#
-|x       |Format: F8.2                               |       1|
-|        |Measure: Scale                             |        |
-|        |Display Alignment: Left                    |        |
-|        |Display Width: 10                          |        |
-+--------+-------------------------------------------+--------+
-|y       |Format: F8.2                               |       2|
-|        |Measure: Ordinal                           |        |
-|        |Display Alignment: Right                   |        |
-|        |Display Width: 12                          |        |
-+--------+-------------------------------------------+--------+
-|z       |Format: F8.2                               |       3|
-|        |Measure: Nominal                           |        |
-|        |Display Alignment: Center                  |        |
-|        |Display Width: 14                          |        |
-+--------+-------------------------------------------+--------+
+diff -c $TEMPDIR/pspp.csv - << EOF
+Variable,Description,,Position
+x,Format: F8.2,,1
+,Measure: Scale,,
+,Display Alignment: Left,,
+,Display Width: 10,,
+y,Format: F8.2,,2
+,Measure: Ordinal,,
+,Display Alignment: Right,,
+,Display Width: 12,,
+z,Format: F8.2,,3
+,Measure: Nominal,,
+,Display Alignment: Center,,
+,Display Width: 14,,
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
index e99fab4b3a3265abcda0b81b3871c8cfa53f89ed..06a318670a4bebe0d753f9f6ea496dc9e44f532b 100755 (executable)
@@ -94,61 +94,47 @@ EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
 activity="compare results"
-perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
-diff  -b $TEMPDIR/pspp.list  - <<EOF
-+------+--------+--------+------------+
-|Vector|Position|Variable|Print Format|
-#======#========#========#============#
-|v     |       1|v1      |F8.2        |
-|      |       2|v2      |F8.2        |
-|      |       3|v3      |F8.2        |
-|      |       4|v4      |F8.2        |
-+------+--------+--------+------------+
-+------+--------+--------+------------+
-|Vector|Position|Variable|Print Format|
-#======#========#========#============#
-|#vec  |       1|#vec1   |COMMA10.2   |
-|      |       2|#vec2   |COMMA10.2   |
-|      |       3|#vec3   |COMMA10.2   |
-|      |       4|#vec4   |COMMA10.2   |
-+------+--------+--------+------------+
-1.1 DATA LIST.  Reading 1 record from INLINE.
-+--------+------+-------+------+
-|Variable|Record|Columns|Format|
-#========#======#=======#======#
-|x5      |     1|  1-  1|F1.0  |
-|x2      |     1|  2-  2|F1.0  |
-|x3      |     1|  3-  3|F1.0  |
-|x1      |     1|  4-  4|F1.0  |
-|x4      |     1|  5-  5|F1.0  |
-+--------+------+-------+------+
-+------+--------+--------+------------+
-|Vector|Position|Variable|Print Format|
-#======#========#========#============#
-|x     |       1|x1      |F8.2        |
-|      |       2|x2      |F8.2        |
-|      |       3|x3      |F8.2        |
-|      |       4|x4      |F8.2        |
-|      |       5|x5      |F8.2        |
-+------+--------+--------+------------+
-+------+--------+--------+------------+
-|Vector|Position|Variable|Print Format|
-#======#========#========#============#
-|a     |       1|u       |F1.0        |
-|      |       2|w       |F1.0        |
-|      |       3|x       |F1.0        |
-|      |       4|y       |F1.0        |
-+------+--------+--------+------------+
-|b     |       1|x       |F1.0        |
-|      |       2|y       |F1.0        |
-|      |       3|z       |F1.0        |
-+------+--------+--------+------------+
-|c     |       1|u       |F1.0        |
-|      |       2|w       |F1.0        |
-|      |       3|x       |F1.0        |
-|      |       4|y       |F1.0        |
-|      |       5|z       |F1.0        |
-+------+--------+--------+------------+
+diff -c $TEMPDIR/pspp.csv  - <<EOF
+Vector,Position,Variable,Print Format
+v,1,v1,F8.2
+,2,v2,F8.2
+,3,v3,F8.2
+,4,v4,F8.2
+
+Vector,Position,Variable,Print Format
+#vec,1,#vec1,COMMA10.2
+,2,#vec2,COMMA10.2
+,3,#vec3,COMMA10.2
+,4,#vec4,COMMA10.2
+
+Table: Reading 1 record from INLINE.
+Variable,Record,Columns,Format
+x5,1,1-  1,F1.0
+x2,1,2-  2,F1.0
+x3,1,3-  3,F1.0
+x1,1,4-  4,F1.0
+x4,1,5-  5,F1.0
+
+Vector,Position,Variable,Print Format
+x,1,x1,F8.2
+,2,x2,F8.2
+,3,x3,F8.2
+,4,x4,F8.2
+,5,x5,F8.2
+
+Vector,Position,Variable,Print Format
+a,1,u,F1.0
+,2,w,F1.0
+,3,x,F1.0
+,4,y,F1.0
+b,1,x,F1.0
+,2,y,F1.0
+,3,z,F1.0
+c,1,u,F1.0
+,2,w,F1.0
+,3,x,F1.0
+,4,y,F1.0
+,5,z,F1.0
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
index 7c09d8dc533265ef1e868955b518a0031d4dec11..e9d20e91fbeb66f7f3928f17f25f70f437dd74ad 100755 (executable)
@@ -77,63 +77,51 @@ $SUPERVISOR $PSPP --testing-mode -e /dev/null $TESTFILE
 if [ $? -ne 0 ] ; then fail ; fi
 
 activity="compare variable display 0"
-perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
-diff -b $TEMPDIR/pspp.list - <<EOF
-1.1 DISPLAY.  
-+--------+-------------------------------------------+--------+
-|Variable|Description                                |Position|
-#========#===========================================#========#
-|N       |Format: F8.2                               |       1|
-|        |Measure: Scale                             |        |
-|        |Display Alignment: Right                   |        |
-|        |Display Width: 10                          |        |
-+--------+-------------------------------------------+--------+
-|A255    |Format: A255                               |       2|
-|        |Measure: Nominal                           |        |
-|        |Display Alignment: Left                    |        |
-|        |Display Width: 32                          |        |
-+--------+-------------------------------------------+--------+
-|A258    |Format: A258                               |       3|
-|        |Measure: Nominal                           |        |
-|        |Display Alignment: Left                    |        |
-|        |Display Width: 32                          |        |
-+--------+-------------------------------------------+--------+
-|A2000   |Format: A2000                              |       4|
-|        |Measure: Nominal                           |        |
-|        |Display Alignment: Left                    |        |
-|        |Display Width: 32                          |        |
-+--------+-------------------------------------------+--------+
-       N                                                                                                                                                                                                                                                            A255                                                                                                                                                                                                                                                               A258                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            A2000
--------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-    1.00 a1AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA b1BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB c1CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC 
-    2.00 a2XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX b2YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY c2ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ 
-2.1 DISPLAY.  
-+--------+-------------------------------------------+--------+
-|Variable|Description                                |Position|
-#========#===========================================#========#
-|vl255   |Format: A255                               |       1|
-|        |Measure: Nominal                           |        |
-|        |Display Alignment: Left                    |        |
-|        |Display Width: 26                          |        |
-+--------+-------------------------------------------+--------+
-|vl256   |Format: A256                               |       2|
-|        |Measure: Nominal                           |        |
-|        |Display Alignment: Left                    |        |
-|        |Display Width: 26                          |        |
-+--------+-------------------------------------------+--------+
-|vl1335  |Format: A1335                              |       3|
-|        |Measure: Nominal                           |        |
-|        |Display Alignment: Left                    |        |
-|        |Display Width: 26                          |        |
-+--------+-------------------------------------------+--------+
-|vl2000  |Format: A2000                              |       4|
-|        |Measure: Nominal                           |        |
-|        |Display Alignment: Left                    |        |
-|        |Display Width: 26                          |        |
-+--------+-------------------------------------------+--------+
-                                                                                                                                                                                                                                                          vl255                                                                                                                                                                                                                                                            vl256                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  vl1335                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           vl2000
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM 
+diff -c $TEMPDIR/pspp.csv - <<EOF
+Variable,Description,,Position
+N,Format: F8.2,,1
+,Measure: Scale,,
+,Display Alignment: Right,,
+,Display Width: 10,,
+A255,Format: A255,,2
+,Measure: Nominal,,
+,Display Alignment: Left,,
+,Display Width: 32,,
+A258,Format: A258,,3
+,Measure: Nominal,,
+,Display Alignment: Left,,
+,Display Width: 32,,
+A2000,Format: A2000,,4
+,Measure: Nominal,,
+,Display Alignment: Left,,
+,Display Width: 32,,
+
+Table: Data List
+N,A255,A258,A2000
+1.00,a1AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA,b1BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB,c1CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
+2.00,a2XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX,b2YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY,c2ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ
+
+Variable,Description,,Position
+vl255,Format: A255,,1
+,Measure: Nominal,,
+,Display Alignment: Left,,
+,Display Width: 26,,
+vl256,Format: A256,,2
+,Measure: Nominal,,
+,Display Alignment: Left,,
+,Display Width: 26,,
+vl1335,Format: A1335,,3
+,Measure: Nominal,,
+,Display Alignment: Left,,
+,Display Width: 26,,
+vl2000,Format: A2000,,4
+,Measure: Nominal,,
+,Display Alignment: Left,,
+,Display Width: 26,,
+
+Table: Data List
+vl255,vl256,vl1335,vl2000
+MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM,MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM,MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM,MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
@@ -197,32 +185,24 @@ EOF
 
 
     activity="compare variable display ($options)"
-    perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
-    diff -b $TEMPDIR/pspp.list - <<EOF
-1.1 DISPLAY.  
-+--------+-------------------------------------------+--------+
-|Variable|Description                                |Position|
-#========#===========================================#========#
-|a       |Format: A10                                |       1|
-|        |Measure: Nominal                           |        |
-|        |Display Alignment: Left                    |        |
-|        |Display Width: 10                          |        |
-+--------+-------------------------------------------+--------+
-|b       |Format: A256                               |       2|
-|        |Measure: Nominal                           |        |
-|        |Display Alignment: Left                    |        |
-|        |Display Width: 32                          |        |
-+--------+-------------------------------------------+--------+
-|c       |Format: A200                               |       3|
-|        |Measure: Nominal                           |        |
-|        |Display Alignment: Left                    |        |
-|        |Display Width: 32                          |        |
-+--------+-------------------------------------------+--------+
-|d       |Format: A32767                             |       4|
-|        |Measure: Nominal                           |        |
-|        |Display Alignment: Left                    |        |
-|        |Display Width: 32                          |        |
-+--------+-------------------------------------------+--------+
+    diff -c $TEMPDIR/pspp.csv - <<EOF
+Variable,Description,,Position
+a,Format: A10,,1
+,Measure: Nominal,,
+,Display Alignment: Left,,
+,Display Width: 10,,
+b,Format: A256,,2
+,Measure: Nominal,,
+,Display Alignment: Left,,
+,Display Width: 32,,
+c,Format: A200,,3
+,Measure: Nominal,,
+,Display Alignment: Left,,
+,Display Width: 32,,
+d,Format: A32767,,4
+,Measure: Nominal,,
+,Display Alignment: Left,,
+,Display Width: 32,,
 EOF
     if [ $? -ne 0 ] ; then fail ; fi
 done
index 0d4fb3970b21fc685f0c059d72651ff177a75b46..40a3ca9c4e32e77dfcf27643d4bdf0babcddc15c 100755 (executable)
@@ -77,97 +77,88 @@ $SUPERVISOR $PSPP --testing-mode $TESTFILE
 if [ $? -ne 0 ] ; then no_result ; fi
 
 activity="compare results"
-perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
-diff  -b $TEMPDIR/pspp.list - <<EOF
-1.1 DATA LIST.  Reading 1 record from "$top_srcdir/tests/weighting.data".
-+--------+------+-------+------+
-|Variable|Record|Columns|Format|
-#========#======#=======#======#
-|AVAR    |     1|  1-  5|F5.0  |
-|BVAR    |     1|  6- 10|F5.0  |
-+--------+------+-------+------+
-2.1 DESCRIPTIVES.  Valid cases = 730; cases with missing value(s) = 0.
-+--------#-------+---------+------+---------+-------+--------+--------+---------+--------+---------+------+-------+-------+--------+
-|Variable#Valid N|Missing N| Mean |S.E. Mean|Std Dev|Variance|Kurtosis|S.E. Kurt|Skewness|S.E. Skew| Range|Minimum|Maximum|   Sum  |
-#========#=======#=========#======#=========#=======#========#========#=========#========#=========#======#=======#=======#========#
-|AVAR    #    730|        0|31.515|     .405| 10.937| 119.608|   2.411|     .181|   1.345|     .090|76.000| 18.000| 94.000|23006.00|
-+--------#-------+---------+------+---------+-------+--------+--------+---------+--------+---------+------+-------+-------+--------+
-3.1 FREQUENCIES.  AVAR
-+--------+--------+---+---+
-|        |        |   |Cum|
-|  Value |  Freq  |Pct|Pct|
-#========#========#===#===#
-|      18|       1|.13|.13|
-|      19|       7|.95|1.0|
-|      20|      26|3.5|4.6|
-|      21|      76|10.|15.|
-|      22|      57|7.8|22.|
-|      23|      58|7.9|30.|
-|      24|      38|5.2|36.|
-|      25|      38|5.2|41.|
-|      26|      30|4.1|45.|
-|      27|      21|2.8|48.|
-|      28|      23|3.1|51.|
-|      29|      24|3.2|54.|
-|      30|      23|3.1|57.|
-|      31|      14|1.9|59.|
-|      32|      21|2.8|62.|
-|      33|      21|2.8|65.|
-|      34|      14|1.9|67.|
-|      35|      14|1.9|69.|
-|      36|      17|2.3|71.|
-|      37|      11|1.5|73.|
-|      38|      16|2.1|75.|
-|      39|      14|1.9|77.|
-|      40|      15|2.0|79.|
-|      41|      14|1.9|81.|
-|      42|      14|1.9|83.|
-|      43|       8|1.0|84.|
-|      44|      15|2.0|86.|
-|      45|      10|1.3|87.|
-|      46|      12|1.6|89.|
-|      47|      13|1.7|91.|
-|      48|      13|1.7|92.|
-|      49|       5|.68|93.|
-|      50|       5|.68|94.|
-|      51|       3|.41|94.|
-|      52|       7|.95|95.|
-|      53|       6|.82|96.|
-|      54|       2|.27|96.|
-|      55|       2|.27|96.|
-|      56|       2|.27|97.|
-|      57|       3|.41|97.|
-|      58|       1|.13|97.|
-|      59|       3|.41|98.|
-|      61|       1|.13|98.|
-|      62|       3|.41|98.|
-|      63|       1|.13|98.|
-|      64|       1|.13|99.|
-|      65|       2|.27|99.|
-|      70|       1|.13|99.|
-|      78|       1|.13|99.|
-|      79|       1|.13|99.|
-|      80|       1|.13|99.|
-|      94|       1|.13|100|
-+--------+--------+---+---+
-+-----------------------+--------+
-|N           Valid      |     730|
-|            Missing    |       0|
-|Mean                   |  31.515|
-|S.E. Mean              |    .405|
-|Mode                   |  21.000|
-|Std Dev                |  10.937|
-|Variance               | 119.608|
-|Kurtosis               |   2.411|
-|S.E. Kurt              |    .181|
-|Skewness               |   1.345|
-|S.E. Skew              |    .090|
-|Range                  |  76.000|
-|Minimum                |  18.000|
-|Maximum                |  94.000|
-|Sum                    |23006.00|
-|Percentiles 50 (Median)|      29|
-+-----------------------+--------+
+diff -c $TEMPDIR/pspp.csv - <<EOF
+"Table: Reading 1 record from ""$top_srcdir/tests/weighting.data""."
+Variable,Record,Columns,Format
+AVAR,1,1-  5,F5.0
+BVAR,1,6- 10,F5.0
+
+Table: Valid cases = 730; cases with missing value(s) = 0.
+Variable,Valid N,Missing N,Mean,S.E. Mean,Std Dev,Variance,Kurtosis,S.E. Kurt,Skewness,S.E. Skew,Range,Minimum,Maximum,Sum
+AVAR,730,0,31.515,.405,10.937,119.608,2.411,.181,1.345,.090,76.000,18.000,94.000,23006.00
+
+Table: AVAR
+,,,Cum
+Value,Freq,Pct,Pct
+18,1,.137,.137
+19,7,.959,1.096
+20,26,3.562,4.658
+21,76,10.411,15.068
+22,57,7.808,22.877
+23,58,7.945,30.822
+24,38,5.205,36.027
+25,38,5.205,41.233
+26,30,4.110,45.342
+27,21,2.877,48.219
+28,23,3.151,51.370
+29,24,3.288,54.658
+30,23,3.151,57.808
+31,14,1.918,59.726
+32,21,2.877,62.603
+33,21,2.877,65.479
+34,14,1.918,67.397
+35,14,1.918,69.315
+36,17,2.329,71.644
+37,11,1.507,73.151
+38,16,2.192,75.342
+39,14,1.918,77.260
+40,15,2.055,79.315
+41,14,1.918,81.233
+42,14,1.918,83.151
+43,8,1.096,84.247
+44,15,2.055,86.301
+45,10,1.370,87.671
+46,12,1.644,89.315
+47,13,1.781,91.096
+48,13,1.781,92.877
+49,5,.685,93.562
+50,5,.685,94.247
+51,3,.411,94.658
+52,7,.959,95.616
+53,6,.822,96.438
+54,2,.274,96.712
+55,2,.274,96.986
+56,2,.274,97.260
+57,3,.411,97.671
+58,1,.137,97.808
+59,3,.411,98.219
+61,1,.137,98.356
+62,3,.411,98.767
+63,1,.137,98.904
+64,1,.137,99.041
+65,2,.274,99.315
+70,1,.137,99.452
+78,1,.137,99.589
+79,1,.137,99.726
+80,1,.137,99.863
+94,1,.137,100.000
+
+N,Valid,730
+,Missing,0
+Mean,,31.515
+S.E. Mean,,.405
+Mode,,21.000
+Std Dev,,10.937
+Variance,,119.608
+Kurtosis,,2.411
+S.E. Kurt,,.181
+Skewness,,1.345
+S.E. Skew,,.090
+Range,,76.000
+Minimum,,18.000
+Maximum,,94.000
+Sum,,23006.00
+Percentiles,50 (Median),29
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
index cb0f5ba3a04161a27ca712c4b43df36c7e83e2a8..1b5b92e2ac56cb264fc71ae14b2faa7a39661a10 100755 (executable)
@@ -92,18 +92,17 @@ $SUPERVISOR $PSPP --testing-mode $TEMPDIR/valuelabel.stat
 if [ $? -ne 0 ] ; then fail ; fi
 
 activity="compare results"
-perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
-diff -b  $TEMPDIR/pspp.list - <<EOF
-n s     nlabel     slabel
-- - ---------- ----------
-.                         
-0 a Very dissa Wouldn't b 
-1 b Dissatisfi Unhappy    
-2 c Neutral    Bored      
-3 d Satisfied  Satiated   
-4 e Very satis Elated     
-5 f                       
-6 g                       
+diff -c $TEMPDIR/pspp.csv - <<EOF
+Table: Data List
+n,s,nlabel,slabel
+.,,,
+0,a,Very dissa,Wouldn't b
+1,b,Dissatisfi,Unhappy   
+2,c,Neutral   ,Bored     
+3,d,Satisfied ,Satiated  
+4,e,Very satis,Elated    
+5,f,,
+6,g,,
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
index c4a2d5d46e5908aabd05e5fc3da29f1e61d419c6..b4f3ffca20bf634d6f4794b0b81bca97445bd965 100755 (executable)
@@ -105,22 +105,19 @@ $SUPERVISOR $PSPP --testing-mode $TEMPDIR/variables.stat > $TEMPDIR/variables.er
 if [ $? -ne 0 ] ; then fail ; fi
 
 activity="compare results"
-perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
-diff -b  $TEMPDIR/pspp.list - <<EOF
-1.1 DATA LIST.  Reading 1 record from INLINE.
-+--------+------+-------+------+
-|Variable|Record|Columns|Format|
-#========#======#=======#======#
-|N1      |     1|  1-  1|F1.0  |
-|N2      |     1|  2-  2|F1.0  |
-|N3      |     1|  3-  3|F1.0  |
-|N4      |     1|  4-  4|F1.0  |
-|N5      |     1|  5-  5|F1.0  |
-+--------+------+-------+------+
-N1 N2 N3 N4 N5 P1 P2 P3 P4 P5 MC VC S1 S2 S3 S4 S5 M1 M2 M3 M4 M5 V1 V2 V3 V4 V5
--- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
- 1  2  3  4  5  .  2  .  .  .  4  1  0  0  0  0  0  1  0  1  1  1  1  2  3  4  5 
- 6  7  8  9  .  6  7  8  9  .  1  4  0  0  0  0  1  0  0  0  0  1  6  7  8  9  . 
+diff -c $TEMPDIR/pspp.csv - <<EOF
+Table: Reading 1 record from INLINE.
+Variable,Record,Columns,Format
+N1,1,1-  1,F1.0
+N2,1,2-  2,F1.0
+N3,1,3-  3,F1.0
+N4,1,4-  4,F1.0
+N5,1,5-  5,F1.0
+
+Table: Data List
+N1,N2,N3,N4,N5,P1,P2,P3,P4,P5,MC,VC,S1,S2,S3,S4,S5,M1,M2,M3,M4,M5,V1,V2,V3,V4,V5
+1,2,3,4,5,.,2,.,.,.,4,1,0,0,0,0,0,1,0,1,1,1,1,2,3,4,5
+6,7,8,9,.,6,7,8,9,.,1,4,0,0,0,0,1,0,0,0,0,1,6,7,8,9,.
 EOF
 
 if [ $? -ne 0 ] ; then no_result ; fi
index 57cef8b6607da8d3cba4f6ab98db333088d2e468..9205f7fca6a40e10c836d0967e1d67cc76c54c67 100755 (executable)
@@ -82,22 +82,19 @@ $SUPERVISOR $PSPP --testing-mode $TEMPDIR/vectors.stat > $TEMPDIR/vectors.err 2>
 if [ $? -ne 0 ] ; then fail ; fi
 
 activity="compare results"
-perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
-diff -b  $TEMPDIR/pspp.list - <<EOF
-1.1 DATA LIST.  Reading 1 record from INLINE.
-+--------+------+-------+------+
-|Variable|Record|Columns|Format|
-#========#======#=======#======#
-|N1      |     1|  1-  1|F1.0  |
-|N2      |     1|  2-  2|F1.0  |
-|N3      |     1|  3-  3|F1.0  |
-|N4      |     1|  4-  4|F1.0  |
-|N5      |     1|  5-  5|F1.0  |
-+--------+------+-------+------+
-N1 N2 N3 N4 N5 X1 X2 X3 X4 X5  I
--- -- -- -- -- -- -- -- -- -- --
- 1  2  3  4  5  .  3  .  .  .  5 
- 6  7  8  9  .  7  8  9 10  .  5 
+diff -c $TEMPDIR/pspp.csv - <<EOF
+Table: Reading 1 record from INLINE.
+Variable,Record,Columns,Format
+N1,1,1-  1,F1.0
+N2,1,2-  2,F1.0
+N3,1,3-  3,F1.0
+N4,1,4-  4,F1.0
+N5,1,5-  5,F1.0
+
+Table: Data List
+N1,N2,N3,N4,N5,X1,X2,X3,X4,X5,I
+1,2,3,4,5,.,3,.,.,.,5
+6,7,8,9,.,7,8,9,10,.,5
 EOF
 
 if [ $? -ne 0 ] ; then no_result ; fi
index 5139dd30abcd6ba7565033593913a775119280da..9c156efdfd465526331bf565375a9591f430a0af 100755 (executable)
@@ -231,26 +231,26 @@ list.
 EOF
     if [ $? -ne 0 ] ; then no_result ; fi
 
-    # Make sure that pspp.list isn't left over from another run.
-    rm -f pspp.list
+    # Make sure that pspp.csv isn't left over from another run.
+    rm -f pspp.csv
 
     activity="run $type.pspp"
     $SUPERVISOR $PSPP --testing-mode $type.pspp
     if [ $? -ne 0 ] ; then fail ; fi
 
     activity="compare $type.pspp output"
-    perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
-    diff -b  $TEMPDIR/pspp.list - << EOF
-     start        end count
----------- ---------- -----
-07/22/2007 10/06/2007   321
-07/14/1789 08/26/1789     4
-01/01/1972 12/31/1999   682
-     start        end count
----------- ---------- -----
-07/22/2007 10/06/2007   322
-07/14/1789 08/26/1789     5
-01/01/1972 12/31/1999   683
+    diff -c $TEMPDIR/pspp.csv - << EOF
+Table: Data List
+start,end,count
+07/22/2007,10/06/2007,321
+07/14/1789,08/26/1789,4
+01/01/1972,12/31/1999,682
+
+Table: Data List
+start,end,count
+07/22/2007,10/06/2007,322
+07/14/1789,08/26/1789,5
+01/01/1972,12/31/1999,683
 EOF
     if [ $? -ne 0 ] ; then fail ; fi
 done
diff --git a/tests/output/render-test.c b/tests/output/render-test.c
new file mode 100644 (file)
index 0000000..5d8a291
--- /dev/null
@@ -0,0 +1,284 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2009 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 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.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+
+#include <errno.h>
+#include <getopt.h>
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <libpspp/assertion.h>
+#include <libpspp/compiler.h>
+#include <output/driver.h>
+#include <output/tab.h>
+#include <output/table-item.h>
+
+#include "gl/error.h"
+#include "gl/progname.h"
+#include "gl/xvasprintf.h"
+
+/* --transpose: Transpose the table before outputting? */
+static int transpose;
+
+static const char *parse_options (int argc, char **argv);
+static void usage (void) NO_RETURN;
+static struct table *read_table (FILE *);
+
+int
+main (int argc, char **argv)
+{
+  struct table *table;
+  const char *input_file_name;
+  FILE *input;
+
+  set_program_name (argv[0]);
+  input_file_name = parse_options (argc, argv);
+
+  if (!strcmp (input_file_name, "-"))
+    input = stdin;
+  else
+    {
+      input = fopen (input_file_name, "r");
+      if (input == NULL)
+        error (1, errno, "%s: open failed", input_file_name);
+    }
+  table = read_table (input);
+  if (input != stdin)
+    fclose (input);
+
+  if (transpose)
+    table = table_transpose (table);
+
+  table_item_submit (table_item_create (table, NULL));
+  output_close ();
+
+  return 0;
+}
+
+static const char *
+parse_options (int argc, char **argv)
+{
+  bool configured_driver = false;
+  int width = 79;
+  int length = 66;
+
+  for (;;)
+    {
+      enum {
+        OPT_DRIVER = UCHAR_MAX + 1,
+        OPT_WIDTH,
+        OPT_LENGTH,
+        OPT_HELP
+      };
+      static const struct option options[] =
+        {
+          {"driver", required_argument, NULL, OPT_DRIVER},
+          {"width", required_argument, NULL, OPT_WIDTH},
+          {"length", required_argument, NULL, OPT_LENGTH},
+          {"transpose", no_argument, &transpose, 1},
+          {"help", no_argument, NULL, OPT_HELP},
+          {NULL, 0, NULL, 0},
+        };
+
+      int c = getopt_long (argc, argv, "", options, NULL);
+      if (c == -1)
+        break;
+
+      switch (c)
+        {
+        case OPT_DRIVER:
+          output_configure_driver (optarg);
+          configured_driver = true;
+          break;
+
+        case OPT_WIDTH:
+          width = atoi (optarg);
+          break;
+
+        case OPT_LENGTH:
+          length = atoi (optarg);
+          break;
+
+        case OPT_HELP:
+          usage ();
+
+        case 0:
+          break;
+
+        case '?':
+          exit(EXIT_FAILURE);
+          break;
+
+        default:
+          NOT_REACHED ();
+        }
+
+    }
+
+  if (!configured_driver)
+    {
+      char *config;
+
+#if 1
+      config = xasprintf ("ascii:ascii:listing:headers=off top-margin=0 "
+                          "bottom-margin=0 output-file=- emphasis=none "
+                          "paginate=off squeeze=on width=%d length=%d",
+                          width, length);
+      output_configure_driver (config);
+      free (config);
+
+      config = xasprintf ("ascii:ascii:listing:headers=off top-margin=0 "
+                          "bottom-margin=0 output-file=render.txt "
+                          "emphasis=none paginate=off squeeze=on");
+      output_configure_driver (config);
+      free (config);
+#endif
+
+      config = xasprintf ("pdf:cairo:listing:headers=off top-margin=0 "
+                          "bottom-margin=0 left-margin=0 right-margin=0 "
+                          "output-file=render.pdf paper-size=%dx%dpt",
+                          width * 5, length * 6);
+      output_configure_driver (config);
+      free (config);
+    }
+
+  if (optind + 1 != argc)
+    error (1, 0, "exactly one non-option argument required; "
+           "use --help for help");
+  return argv[optind];
+}
+
+static void
+usage (void)
+{
+  printf ("%s, to test rendering of PSPP tables\n"
+          "usage: %s [OPTIONS] INPUT\n"
+          "\nOptions:\n"
+          "  --driver=NAME:CLASS:DEVICE:OPTIONS  set output driver\n",
+          program_name, program_name);
+  exit (EXIT_SUCCESS);
+}
+
+static void
+replace_newlines (char *p)
+{
+  char *q;
+
+  for (q = p; *p != '\0'; )
+    if (*p == '\\' && p[1] == 'n')
+      {
+        *q++ = '\n';
+        p += 2;
+      }
+    else
+      *q++ = *p++;
+  *q = '\0';
+}
+
+static struct table *
+read_table (FILE *stream)
+{
+  struct tab_table *tab;
+  char buffer[1024];
+  int input[6];
+  int n_input = 0;
+  int nr, nc, hl, hr, ht, hb;
+  int r, c;
+
+  if (fgets (buffer, sizeof buffer, stream) == NULL
+      || (n_input = sscanf (buffer, "%d %d %d %d %d %d",
+                            &input[0], &input[1], &input[2],
+                            &input[3], &input[4], &input[5])) < 2)
+    error (1, 0, "syntax error reading row and column count");
+
+  nr = input[0];
+  nc = input[1];
+  hl = n_input >= 3 ? input[2] : 0;
+  hr = n_input >= 4 ? input[3] : 0;
+  ht = n_input >= 5 ? input[4] : 0;
+  hb = n_input >= 6 ? input[5] : 0;
+
+  tab = tab_create (nc, nr);
+  tab_headers (tab, hl, hr, ht, hb);
+  for (r = 0; r < nr; r++)
+    for (c = 0; c < nc; c++)
+      if (tab_cell_is_empty (tab, c, r))
+        {
+          char *new_line;
+          char *text;
+          int rs, cs;
+
+          if (fgets (buffer, sizeof buffer, stream) == NULL)
+            error (1, 0, "unexpected end of input reading row %d, column %d",
+                   r, c);
+          new_line = strchr (buffer, '\n');
+          if (new_line != NULL)
+            *new_line = '\0';
+
+          text = buffer;
+          if (sscanf (text, "%d*%d", &rs, &cs) == 2)
+            {
+              while (*text != ' ' && *text != '\0')
+                text++;
+              if (*text == ' ')
+                text++;
+            }
+          else
+            {
+              rs = 1;
+              cs = 1;
+            }
+
+          while (*text && strchr ("<>^,@", *text))
+            switch (*text++)
+              {
+              case '<':
+                tab_vline (tab, TAL_1, c, r, r + rs - 1);
+                break;
+
+              case '>':
+                tab_vline (tab, TAL_1, c + cs, r, r + rs - 1);
+                break;
+
+              case '^':
+                tab_hline (tab, TAL_1, c, c + cs - 1, r);
+                break;
+
+              case ',':
+                tab_hline (tab, TAL_1, c, c + cs - 1, r + rs);
+                break;
+
+              case '@':
+                tab_box (tab, TAL_1, TAL_1, -1, -1, c, r,
+                         c + cs - 1, r + rs - 1);
+                break;
+
+              default:
+                NOT_REACHED ();
+              }
+
+          replace_newlines (text);
+
+          tab_joint_text (tab, c, r, c + cs - 1, r + rs - 1, 0, text);
+        }
+
+  if (getc (stream) != EOF)
+    error (1, 0, "unread data at end of input");
+
+  return &tab->table;
+}
diff --git a/tests/output/render.at b/tests/output/render.at
new file mode 100644 (file)
index 0000000..b9f8e62
--- /dev/null
@@ -0,0 +1,1710 @@
+m4_define([RENDER_WEAVE_6X6],
+  [AT_DATA([input], [6 6 $1
+@a
+1*2 @bcd
+@e
+2*1 @f\ng\nh
+@i
+2*1 @j\nk\nl
+@m
+1*2 @nop
+2*1 @q\nr\ns
+2*1 @t\nu\nv
+@w
+1*2 @xyz
+@A
+2*1 @B\nC\nD
+@E
+1*2 @FGH
+1*2 @IJK
+2*1 @L\nM\nN
+@O
+@P
+@Q
+1*2 @RST
+@U
+@V
+])])
+
+m4_define([RENDER_8X8],
+  [AT_DATA([input], [8 8 $1
+@a
+@b
+@c
+@d
+@e
+@f
+@g
+@h
+@i
+1*2 @jkl
+@m
+1*2 @nop
+2*1 @q\nr\ns
+@t
+@u
+@v
+1*2 @wxy
+@z
+2*1 @A\nB\nC
+@D
+@E
+2*1 @F\nG\nH
+@I
+1*2 @JKL
+2*1 @M\nN\nO
+@P
+@Q
+2*1 @R\nS\nT
+1*2 @UVW
+@X
+@Y
+@Z
+2*1 @0\n1\n2
+@3
+1*2 @456
+@7
+@8
+@9
+1*2 @abc
+@d
+1*2 @efg
+@h
+@i
+@j
+@k
+@l
+@m
+@n
+@o
+@p
+])])
+
+# This input is something of a counterexample, in that it could render
+# compactly as this if the algorithm for choosing cell widths and
+# heights were smarter:
+#
+# +---+---+---+-+-+
+# |abc|jkl|mno|v|x|
+# |def+---+pqr+-+-+
+# |ghi|yzA|stu|HIJ|
+# +-+-+BCD+-+-+KLM|
+# |Q|V|EFG|W|Z|NOP|
+# |R+-+-+-+X+-+-+-+
+# |S|012|9|Y|abc|j|
+# |T|345+-+-+def|k|
+# |U|678|opq|ghi|l|
+# +-+-+-+rst+---+m|
+# |xyz|G|uvw|JKL|n|
+# |ABC|H+---+-+-+-+
+# |DEF|I|MNOPQ|123|
+# +---+-+RSTUV|456|
+# |abcde|WXYZ0|789|
+# +-----+-----+---+
+m4_define([RENDER_8X8_2],
+  [AT_DATA([input], [8 8 $1
+2*2 @abc\ndef\nghi
+1*2 @jkl
+2*2 @mno\npqr\nstu
+1*2 @vwx
+2*2 @yzA\nBCD\nEFG
+2*2 @HIJ\nKLM\nNOP
+3*1 @Q\nR\nS\nT\nU
+@V
+2*1 @W\nX\nY
+@Z
+2*2 @012\n345\n678
+@9
+2*2 @abc\ndef\nghi
+3*1 @j\nk\nl\nm\nn
+2*2 @opq\nrst\nuvw
+2*2 @xyz\nABC\nDEF
+2*1 @G\nH\nI
+1*2 @JKL
+2*3 @MNOPQ\nRSTUV\nWXYZ0
+2*2 @123\n456\n789
+1*3 @abcde
+])])
+\f
+AT_BANNER([output rendering -- no page breaking])
+
+AT_SETUP([single cell])
+AT_DATA([input], [1 1
+abc
+])
+AT_CHECK([render-test input], [0], [abc
+])
+AT_CLEANUP
+
+AT_SETUP([single cell with border])
+AT_DATA([input], [1 1
+@abc
+])
+AT_CHECK([render-test input], [0], [dnl
++---+
+|abc|
++---+
+])
+AT_CLEANUP
+
+AT_SETUP([joined columns])
+AT_DATA([input], [2 2
+1*2 @abcdefg
+@hij
+@klm
+])
+AT_CHECK([render-test input], [0], [dnl
++-------+
+|abcdefg|
++---+---+
+|hij|klm|
++---+---+
+])
+AT_CLEANUP
+
+AT_SETUP([3x3, joined rows and columns])
+AT_DATA([input], [3 3
+1*2 @abc
+2*1 @d\ne\nf
+2*1 @g\nh\ni
+@j
+1*2 @klm
+])
+AT_CHECK([render-test input], [0], [dnl
++---+-+
+|abc|d|
++-+-+e|
+|g|j|f|
+|h+-+-+
+|i|klm|
++-+---+
+])
+AT_CLEANUP
+
+AT_SETUP([6x6, joined rows and columns])
+RENDER_WEAVE_6X6
+AT_CHECK([render-test input], [0], [dnl
++-+---+-+-+-+
+|a|bcd|e|f|i|
++-+-+-+-+g+-+
+|j|m|nop|h|q|
+|k+-+-+-+-+r|
+|l|t|w|xyz|s|
++-+u+-+-+-+-+
+|A|v|B|E|FGH|
++-+-+C+-+-+-+
+|IJK|D|L|O|P|
++-+-+-+M+-+-+
+|Q|RST|N|U|V|
++-+---+-+-+-+
+])
+AT_CLEANUP
+
+AT_SETUP([3 rows with many joined cells])
+AT_CAPTURE_FILE([input])
+AT_DATA([input], [3 19
+m4_foreach([x], [a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s], [x
+])@1
+m4_for([x], [2], [19], [1], [1*2 @x
+])@20
+])
+AT_CHECK([render-test input], [0], [dnl
+ a b c d e f g h i j k l m n o p q r  s
++-+---+---+---+---+---+---+---+---+----+
+|1|  2|  3|  4|  5|  6|  7|  8|  9|  10|
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+--+
+| 11| 12| 13| 14| 15| 16| 17| 18| 19|20|
++---+---+---+---+---+---+---+---+---+--+
+])
+AT_CLEANUP
+
+AT_SETUP([3 columns with many joined cells])
+AT_CAPTURE_FILE([input])
+AT_DATA([input], [3 19
+m4_foreach([x], [a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s], [x
+])@1
+m4_for([x], [2], [19], [1], [1*2 @x\nab\ncd
+])@20
+])
+AT_CHECK([render-test --transpose input], [0], [dnl
+ +--+--+
+a| 1|11|
+ +--+ab|
+b| 2|cd|
+ |ab+--+
+c|cd|12|
+ +--+ab|
+d| 3|cd|
+ |ab+--+
+e|cd|13|
+ +--+ab|
+f| 4|cd|
+ |ab+--+
+g|cd|14|
+ +--+ab|
+h| 5|cd|
+ |ab+--+
+i|cd|15|
+ +--+ab|
+j| 6|cd|
+ |ab+--+
+k|cd|16|
+ +--+ab|
+l| 7|cd|
+ |ab+--+
+m|cd|17|
+ +--+ab|
+n| 8|cd|
+ |ab+--+
+o|cd|18|
+ +--+ab|
+p| 9|cd|
+ |ab+--+
+q|cd|19|
+ +--+ab|
+r|10|cd|
+ |ab+--+
+s|cd|20|
+ +--+--+
+])
+AT_CLEANUP
+
+AT_SETUP([joined rows])
+AT_DATA([input], [2 2
+2*1 @ab\ncd\nef
+@hij
+@klm
+])
+AT_CHECK([render-test input], [0], [dnl
++--+---+
+|ab|hij|
+|cd+---+
+|ef|klm|
++--+---+
+])
+AT_CLEANUP
+
+AT_SETUP([5 big narrow cells])
+AT_DATA([input], [1 5
+@This cell has a lot of text but its minimum width is pretty narrow.
+@This cell also has a lot of text but its minimum width is pretty narrow.
+@A third cell with a lot of text but a pretty narrow minimum width.
+@A fourth cell with a lot of text but a pretty narrow minimum width.
+@A fifth cell with a lot of text but a pretty narrow minimum width.
+])
+AT_CHECK([render-test input], [0], [dnl
++---------------+---------------+--------------+---------------+--------------+
+|This cell has a| This cell also|  A third cell|  A fourth cell|  A fifth cell|
+|lot of text but|   has a lot of| with a lot of|  with a lot of| with a lot of|
+|    its minimum|   text but its|    text but a|     text but a|    text but a|
+|width is pretty|  minimum width| pretty narrow|  pretty narrow| pretty narrow|
+|        narrow.|      is pretty|minimum width.| minimum width.|minimum width.|
+|               |        narrow.|              |               |              |
++---------------+---------------+--------------+---------------+--------------+
+])
+AT_CLEANUP
+
+AT_SETUP([9 big narrow cells])
+AT_DATA([input], [1 9
+@This cell has a lot of text but its minimum width is pretty narrow.
+@This cell also has a lot of text but its minimum width is pretty narrow.
+@A third cell with a lot of text but a pretty narrow minimum width.
+@A fourth cell with a lot of text but a pretty narrow minimum width.
+@A fifth cell with a lot of text but a pretty narrow minimum width.
+@A sixth cell with a lot of text but a pretty narrow minimum width.
+@A seventh cell with a lot of text but a pretty narrow minimum width.
+@A eighth cell with a lot of text but a pretty narrow minimum width.
+@A ninth cell with a lot of text but a pretty narrow minimum width.
+])
+AT_CHECK([render-test input], [0], [dnl
++--------+-------+--------+--------+-------+--------+--------+-------+--------+
+|    This|   This| A third|A fourth|A fifth| A sixth|       A|      A| A ninth|
+|cell has|   cell|    cell|    cell|   cell|    cell| seventh| eighth|    cell|
+|a lot of|   also|  with a|  with a| with a|  with a|    cell|   cell|  with a|
+|text but|  has a|  lot of|  lot of| lot of|  lot of|  with a| with a|  lot of|
+|     its| lot of|text but|text but|   text|text but|  lot of| lot of|text but|
+| minimum|   text|a pretty|a pretty|  but a|a pretty|text but|   text|a pretty|
+|width is|but its|  narrow|  narrow| pretty|  narrow|a pretty|  but a|  narrow|
+|  pretty|minimum| minimum| minimum| narrow| minimum|  narrow| pretty| minimum|
+| narrow.|  width|  width.|  width.|minimum|  width.| minimum| narrow|  width.|
+|        |     is|        |        | width.|        |  width.|minimum|        |
+|        | pretty|        |        |       |        |        | width.|        |
+|        |narrow.|        |        |       |        |        |       |        |
++--------+-------+--------+--------+-------+--------+--------+-------+--------+
+])
+AT_CLEANUP
+
+AT_SETUP([2 big cells with new-lines])
+AT_DATA([input], [1 2
+@PSPP does not place many restrictions on ordering of commands. The main restriction is that variables must be defined before they are otherwise referenced.  This section describes the details of command ordering, but most users will have no need to refer to them. PSPP possesses five internal states, called initial, INPUT PROGRAM, FILE TYPE, transformation, and procedure states.
+@PSPP includes special support\nfor unknown numeric data values.\nMissing observations are assigned\na special value, called the\n``system-missing value''.  This\n``value'' actually indicates the\nabsence of a value; it\nmeans that the actual\nvalue is unknown.
+])
+AT_CHECK([render-test input], [0], [dnl
++----------------------------------------------------------+------------------+
+|      PSPP does not place many restrictions on ordering of|     PSPP includes|
+|  commands. The main restriction is that variables must be|   special support|
+|       defined before they are otherwise referenced.  This|       for unknown|
+|    section describes the details of command ordering, but|      numeric data|
+|       most users will have no need to refer to them. PSPP|           values.|
+|     possesses five internal states, called initial, INPUT|           Missing|
+| PROGRAM, FILE TYPE, transformation, and procedure states.|  observations are|
+|                                                          |          assigned|
+|                                                          |  a special value,|
+|                                                          |        called the|
+|                                                          |  ``system-missing|
+|                                                          |    value''.  This|
+|                                                          |``value'' actually|
+|                                                          |     indicates the|
+|                                                          |      absence of a|
+|                                                          |         value; it|
+|                                                          |    means that the|
+|                                                          |            actual|
+|                                                          | value is unknown.|
++----------------------------------------------------------+------------------+
+])
+AT_CLEANUP
+
+AT_SETUP([8x8 with many 2x2 joins])
+RENDER_8X8_2
+AT_CHECK([render-test input], [0],[dnl
++---+---+----+----+
+|abc|jkl| mno| vwx|
+|def|   | pqr|    |
+|ghi+---+ stu+----+
+|   |yzA|    | HIJ|
++-+-+BCD+-+--+ KLM|
+|Q|V|EFG|W| Z| NOP|
+|R| |   |X|  |    |
+|S+-+-+-+Y+--+-+--+
+|T|012|9| | abc| j|
+|U|345| | | def| k|
+| |678+-+-+ ghi| l|
+| |   |opq|    | m|
++-+-+-+rst+----+ n|
+|xyz|G|uvw| JKL|  |
+|ABC|H|   |    |  |
+|DEF|I+---+--+-+--+
+|   | | MNOPQ| 123|
++---+-+ RSTUV| 456|
+|abcde| WXYZ0| 789|
+|     |      |    |
++-----+------+----+
+])
+AT_CLEANUP
+\f
+AT_BANNER([output rendering -- horizontal page breaks])
+
+AT_SETUP([breaking row of many small cells])
+AT_CAPTURE_FILE([input])
+AT_DATA([input], [1 50
+m4_for([x], [1], [50], [1], [@x
+])])
+AT_CHECK([render-test input], [0], [dnl
++-+-+-+-+-+-+-+-+-+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|
++-+-+-+-+-+-+-+-+-+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
++--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+|30|31|32|33|34|35|36|37|38|39|40|41|42|43|44|45|46|47|48|49|50|
++--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+])
+AT_CLEANUP
+
+AT_SETUP([breaking row of many small cells, with headers])
+AT_CAPTURE_FILE([input])
+AT_DATA([input], [1 54 2 2
+@ha
+@hb
+m4_for([x], [1], [50], [1], [@x
+])dnl
+@hc
+@hd
+])
+AT_CHECK([render-test input], [0], [dnl
++--+--+-+-+-+-+-+-+-+-+-+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+|ha|hb|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25|hc|hd|
++--+--+-+-+-+-+-+-+-+-+-+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
++--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+|ha|hb|26|27|28|29|30|31|32|33|34|35|36|37|38|39|40|41|42|43|44|45|46|47|hc|hd|
++--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
++--+--+--+--+--+--+--+
+|ha|hb|48|49|50|hc|hd|
++--+--+--+--+--+--+--+
+])
+AT_CLEANUP
+
+AT_SETUP([breaking row of many medium-size cells])
+AT_CAPTURE_FILE([input])
+AT_DATA([input], [1 50
+m4_for([x], [1], [50], [1], [@cell x
+])])
+AT_CHECK([render-test input], [0], [dnl
++----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
+|cell|cell|cell|cell|cell|cell|cell|cell|cell|cell|cell|cell|cell|cell|cell|
+|   1|   2|   3|   4|   5|   6|   7|   8|   9|  10|  11|  12|  13|  14|  15|
++----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
+
++----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
+|cell|cell|cell|cell|cell|cell|cell|cell|cell|cell|cell|cell|cell|cell|cell|
+|  16|  17|  18|  19|  20|  21|  22|  23|  24|  25|  26|  27|  28|  29|  30|
++----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
+
++----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
+|cell|cell|cell|cell|cell|cell|cell|cell|cell|cell|cell|cell|cell|cell|cell|
+|  31|  32|  33|  34|  35|  36|  37|  38|  39|  40|  41|  42|  43|  44|  45|
++----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
+
++----+----+----+----+----+
+|cell|cell|cell|cell|cell|
+|  46|  47|  48|  49|  50|
++----+----+----+----+----+
+])
+AT_CLEANUP
+
+AT_SETUP([breaking row of many medium-size cells, with headers])
+AT_CAPTURE_FILE([input])
+AT_DATA([input], [1 52 1 1
+header1
+m4_for([x], [1], [50], [1], [@cell x
+])dnl
+header2
+])
+AT_CHECK([render-test input], [0], [dnl
+       +----+----+----+----+----+----+----+----+----+----+----+----+
+header1|cell|cell|cell|cell|cell|cell|cell|cell|cell|cell|cell|cell|header2
+       |   1|   2|   3|   4|   5|   6|   7|   8|   9|  10|  11|  12|
+       +----+----+----+----+----+----+----+----+----+----+----+----+
+
+       +----+----+----+----+----+----+----+----+----+----+----+----+
+header1|cell|cell|cell|cell|cell|cell|cell|cell|cell|cell|cell|cell|header2
+       |  13|  14|  15|  16|  17|  18|  19|  20|  21|  22|  23|  24|
+       +----+----+----+----+----+----+----+----+----+----+----+----+
+
+       +----+----+----+----+----+----+----+----+----+----+----+----+
+header1|cell|cell|cell|cell|cell|cell|cell|cell|cell|cell|cell|cell|header2
+       |  25|  26|  27|  28|  29|  30|  31|  32|  33|  34|  35|  36|
+       +----+----+----+----+----+----+----+----+----+----+----+----+
+
+       +----+----+----+----+----+----+----+----+----+----+----+----+
+header1|cell|cell|cell|cell|cell|cell|cell|cell|cell|cell|cell|cell|header2
+       |  37|  38|  39|  40|  41|  42|  43|  44|  45|  46|  47|  48|
+       +----+----+----+----+----+----+----+----+----+----+----+----+
+
+       +----+----+
+header1|cell|cell|header2
+       |  49|  50|
+       +----+----+
+])
+AT_CLEANUP
+
+AT_SETUP([breaking row of many big narrow cells])
+AT_CAPTURE_FILE([input])
+AT_DATA([input], [1 50
+m4_for([x], [1], [50], [1], [@This is cell x in a series of 50.
+])])
+AT_CHECK([render-test input], [0], [dnl
++------+------+------+------+------+------+------+------+------+------+------+
+|  This|  This|  This|  This|  This|  This|  This|  This|  This|  This|  This|
+|    is|    is|    is|    is|    is|    is|    is|    is|    is|    is|    is|
+|cell 1|cell 2|cell 3|cell 4|cell 5|cell 6|cell 7|cell 8|cell 9|  cell|  cell|
+|  in a|  in a|  in a|  in a|  in a|  in a|  in a|  in a|  in a| 10 in| 11 in|
+|series|series|series|series|series|series|series|series|series|     a|     a|
+|of 50.|of 50.|of 50.|of 50.|of 50.|of 50.|of 50.|of 50.|of 50.|series|series|
+|      |      |      |      |      |      |      |      |      |of 50.|of 50.|
++------+------+------+------+------+------+------+------+------+------+------+
+
++------+------+------+------+------+------+------+------+------+------+------+
+|  This|  This|  This|  This|  This|  This|  This|  This|  This|  This|  This|
+|    is|    is|    is|    is|    is|    is|    is|    is|    is|    is|    is|
+|  cell|  cell|  cell|  cell|  cell|  cell|  cell|  cell|  cell|  cell|  cell|
+| 12 in| 13 in| 14 in| 15 in| 16 in| 17 in| 18 in| 19 in| 20 in| 21 in| 22 in|
+|     a|     a|     a|     a|     a|     a|     a|     a|     a|     a|     a|
+|series|series|series|series|series|series|series|series|series|series|series|
+|of 50.|of 50.|of 50.|of 50.|of 50.|of 50.|of 50.|of 50.|of 50.|of 50.|of 50.|
++------+------+------+------+------+------+------+------+------+------+------+
+
++------+------+------+------+------+------+------+------+------+------+------+
+|  This|  This|  This|  This|  This|  This|  This|  This|  This|  This|  This|
+|    is|    is|    is|    is|    is|    is|    is|    is|    is|    is|    is|
+|  cell|  cell|  cell|  cell|  cell|  cell|  cell|  cell|  cell|  cell|  cell|
+| 23 in| 24 in| 25 in| 26 in| 27 in| 28 in| 29 in| 30 in| 31 in| 32 in| 33 in|
+|     a|     a|     a|     a|     a|     a|     a|     a|     a|     a|     a|
+|series|series|series|series|series|series|series|series|series|series|series|
+|of 50.|of 50.|of 50.|of 50.|of 50.|of 50.|of 50.|of 50.|of 50.|of 50.|of 50.|
++------+------+------+------+------+------+------+------+------+------+------+
+
++------+------+------+------+------+------+------+------+------+------+------+
+|  This|  This|  This|  This|  This|  This|  This|  This|  This|  This|  This|
+|    is|    is|    is|    is|    is|    is|    is|    is|    is|    is|    is|
+|  cell|  cell|  cell|  cell|  cell|  cell|  cell|  cell|  cell|  cell|  cell|
+| 34 in| 35 in| 36 in| 37 in| 38 in| 39 in| 40 in| 41 in| 42 in| 43 in| 44 in|
+|     a|     a|     a|     a|     a|     a|     a|     a|     a|     a|     a|
+|series|series|series|series|series|series|series|series|series|series|series|
+|of 50.|of 50.|of 50.|of 50.|of 50.|of 50.|of 50.|of 50.|of 50.|of 50.|of 50.|
++------+------+------+------+------+------+------+------+------+------+------+
+
++------+------+------+------+------+------+
+|  This|  This|  This|  This|  This|  This|
+|    is|    is|    is|    is|    is|    is|
+|  cell|  cell|  cell|  cell|  cell|  cell|
+| 45 in| 46 in| 47 in| 48 in| 49 in| 50 in|
+|     a|     a|     a|     a|     a|     a|
+|series|series|series|series|series|series|
+|of 50.|of 50.|of 50.|of 50.|of 50.|of 50.|
++------+------+------+------+------+------+
+])
+AT_CLEANUP
+
+AT_SETUP([breaking 2 rows of many small cells])
+AT_CAPTURE_FILE([input])
+AT_DATA([input], [2 50
+m4_for([x], [1], [100], [1], [@x
+])])
+AT_CHECK([render-test input], [0], [dnl
++--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+| 1| 2| 3| 4| 5| 6| 7| 8| 9|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25|26|
++--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+|51|52|53|54|55|56|57|58|59|60|61|62|63|64|65|66|67|68|69|70|71|72|73|74|75|76|
++--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+
++--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+---+
+|27|28|29|30|31|32|33|34|35|36|37|38|39|40|41|42|43|44|45|46|47|48|49| 50|
++--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+---+
+|77|78|79|80|81|82|83|84|85|86|87|88|89|90|91|92|93|94|95|96|97|98|99|100|
++--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+---+
+])
+AT_CLEANUP
+
+AT_SETUP([breaking 3 rows with many joined cells])
+AT_CAPTURE_FILE([input])
+AT_DATA([input], [3 49
+m4_foreach([var], [a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,dnl
+A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W], [var
+])@1
+m4_for([x], [2], [49], [1], [1*2 @x
+])@50
+])
+AT_CHECK([render-test input], [0], [dnl
+ a b c d e f g h i j k l m n o p q r s t u v w x y z A B C D E F G H I J K L M
++-+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
+|1|  2|  3|  4|  5|  6|  7|  8|  9| 10| 11| 12| 13| 14| 15| 16| 17| 18| 19| 20|
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+| 26| 27| 28| 29| 30| 31| 32| 33| 34| 35| 36| 37| 38| 39| 40| 41| 42| 43| 44| 4
++---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+--
+
+ N O P Q R S T U V  W
++---+---+---+---+----+
+| 21| 22| 23| 24|  25|
++-+-+-+-+-+-+-+-+-+--+
+45| 46| 47| 48| 49|50|
+--+---+---+---+---+--+
+])
+AT_CLEANUP
+
+AT_SETUP([horz break 6x6, joined rows and columns])
+RENDER_WEAVE_6X6
+AT_CHECK([render-test --width=6 input], [0], [dnl
++-+--
+|a|bc
++-+-+
+|j|m|
+|k+-+
+|l|t|
++-+u|
+|A|v|
++-+-+
+|IJK|
++-+-+
+|Q|RS
++-+--
+
+--+-+
+cd|e|
++-+-+
+|nop|
++-+-+
+|w|xy
++-+-+
+|B|E|
+|C+-+
+|D|L|
++-+M|
+ST|N|
+--+-+
+
++-+-+
+|f|i|
+|g+-+
+|h|q|
++-+r|
+yz|s|
++-+-+
+|FGH|
++-+-+
+|O|P|
++-+-+
+|U|V|
++-+-+
+])
+AT_CLEANUP
+
+AT_SETUP([horz break 6x6, joined rows and columns, left header])
+RENDER_WEAVE_6X6([1 0 0 0])
+AT_CHECK([render-test --width=10 input], [0], [dnl
++-+---+-+
+|a|bcd|e|
++-+-+-+-+
+|j|m|nop|
+|k+-+-+-+
+|l|t|w|xy
++-+u+-+-+
+|A|v|B|E|
++-+-+C+-+
+|K K|D|L|
++-+-+-+M|
+|Q|RST|N|
++-+---+-+
+
++-+-+-+
+|a|f|i|
++-+g+-+
+|j|h|q|
+|k+-+r|
+|l|z|s|
++-+-+-+
+|A|FGH|
++-+-+-+
+|K|O|P|
++-+-+-+
+|Q|U|V|
++-+-+-+
+])
+AT_CLEANUP
+
+AT_SETUP([horz break 6x6, joined rows and columns, right header])
+RENDER_WEAVE_6X6([0 1 0 0])
+AT_CHECK([render-test --width=10 input], [0], [dnl
++-+---+-+
+|a|bcd|i|
++-+-+-+-+
+|j|m|n|q|
+|k+-+-+r|
+|l|t|w|s|
++-+u+-+-+
+|A|v|B|H|
++-+-+C+-+
+|IJK|D|P|
++-+-+-+-+
+|Q|RST|V|
++-+---+-+
+
++-+-+-+
+|e|f|i|
++-+g+-+
+op|h|q|
++-+-+r|
+|xyz|s|
++-+-+-+
+|E|F H|
++-+-+-+
+|L|O|P|
+|M+-+-+
+|N|U|V|
++-+-+-+
+])
+AT_CLEANUP
+
+AT_SETUP([breaking joined cells too wide for page])
+AT_DATA([input], [4 6
+1*6 @abc def ghi jkl
+1*3 @mno pqr
+1*3 @stu vwx
+1*2 @yzA
+1*2 @BCD
+1*2 @EFG
+@H
+@I
+@J
+@K
+@L
+@M
+])
+AT_CHECK([render-test --width=10 input], [0], [dnl
++--------
+|abc def
+|
++-----+--
+|  mno|
+|  pqr|
++---+-+-+
+|yzA|BCD|
++-+-+-+-+
+|H|I|J|K|
++-+-+-+-+
+
+----+
+ ghi|
+ jkl|
+----+
+ stu|
+ vwx|
++---+
+|EFG|
++-+-+
+|L|M|
++-+-+
+])
+AT_CLEANUP
+
+AT_SETUP([breaking joined cells much too wide for page])
+AT_DATA([input], [4 6
+1*6 @abc def ghi jkl
+1*3 @mno pqr
+1*3 @stu vwx
+1*2 @yzA
+1*2 @BCD
+1*2 @EFG
+@H
+@I
+@J
+@K
+@L
+@M
+])
+AT_CHECK([render-test --width=6 input], [0], [dnl
++----
+|abc
+|
++----
+|  mn
+|  pq
++---+
+|yzA|
++-+-+
+|H|I|
++-+-+
+
+-----
+ def
+
+--+--
+no|
+qr|
++-+-+
+|BCD|
++-+-+
+|J|K|
++-+-+
+
+----+
+ ghi|
+ jkl|
+----+
+ stu|
+ vwx|
++---+
+|EFG|
++-+-+
+|L|M|
++-+-+
+])
+AT_CLEANUP
+
+AT_SETUP([breaking cell too wide for page, no border])
+AT_CAPTURE_FILE([input])
+AT_DATA([input], [1 1
+abcdefghijklmnopqrstuvwxyz
+])
+AT_CHECK([render-test --width=6 input], [0], [dnl
+abcdef
+
+ghijkl
+
+mnopqr
+
+stuvwx
+
+yz
+])
+AT_CLEANUP
+
+AT_SETUP([breaking cell too wide for page, with border])
+AT_CAPTURE_FILE([input])
+AT_DATA([input], [1 1
+@abcdefghijklmnopqrstuvwxyz
+])
+AT_CHECK([render-test --width=6 input], [0], [dnl
++-----
+|abcde
++-----
+
+------
+defghi
+------
+
+------
+hijklm
+------
+
+------
+lmnopq
+------
+
+------
+pqrstu
+------
+
+------
+tuvwxy
+------
+
+---+
+xyz|
+---+
+])
+AT_CLEANUP
+
+AT_SETUP([horz break 8x8 with many 2x2 joins])
+RENDER_8X8_2
+AT_CHECK([render-test --width=8 input], [0],[dnl
++---+--
+|abc|jk
+|def|  
+|ghi+--
+|   |yz
++-+-+BC
+|Q|V|EF
+|R| |  
+|S+-+-+
+|T|012|
+|U|345|
+| |678|
+| |   |
++-+-+-+
+|xyz|G|
+|ABC|H|
+|DEF|I|
+|   | |
++---+-+
+|abcde|
+|     |
++-----+
+
+--+----+
+kl| mno|
+  | pqr|
+--+ stu|
+zA|    |
+CD+-+--+
+FG|W| Z|
+  |X|  |
++-+Y+--+
+|9| | ab
+| | | de
++-+-+ gh
+|opq|   
+|rst+---
+|uvw| JK
+|   |   
++---+--+
+| MNOPQ|
+| RSTUV|
+| WXYZ0|
+|      |
++------+
+
++----+
+| vwx|
+|    |
++----+
+| HIJ|
+| KLM|
+| NOP|
+|    |
++-+--+
+bc| j|
+ef| k|
+hi| l|
+  | m|
+--+ n|
+KL|  |
+  |  |
++-+--+
+| 123|
+| 456|
+
+| 456|
+| 789|
+|    |
++----+
+])
+AT_CLEANUP
+\f
+AT_BANNER([output rendering -- vertical page breaks])
+
+AT_SETUP([breaking column of many small cells])
+AT_CAPTURE_FILE([input])
+AT_DATA([input], [20 1
+m4_for([x], [1], [20], [1], [@x
+])])
+AT_CHECK([render-test --length=10 input], [0], [dnl
++--+
+| 1|
++--+
+| 2|
++--+
+| 3|
++--+
+| 4|
++--+
+
++--+
+| 5|
++--+
+| 6|
++--+
+| 7|
++--+
+| 8|
++--+
+
++--+
+| 9|
++--+
+|10|
++--+
+|11|
++--+
+|12|
++--+
+
++--+
+|13|
++--+
+|14|
++--+
+|15|
++--+
+|16|
++--+
+
++--+
+|17|
++--+
+|18|
++--+
+|19|
++--+
+|20|
++--+
+])
+AT_CLEANUP
+
+AT_SETUP([breaking column of many small cells, with headers])
+AT_CAPTURE_FILE([input])
+AT_DATA([input], [17 1 0 0 1 1
+@a
+m4_for([x], [1], [15], [1], [@x
+])@b
+])
+AT_CHECK([render-test --length=13 input], [0], [dnl
++--+
+| a|
++--+
+| 1|
++--+
+| 2|
++--+
+| 3|
++--+
+| 4|
++--+
+| b|
++--+
+
++--+
+| a|
++--+
+| 5|
++--+
+| 6|
++--+
+| 7|
++--+
+| 8|
++--+
+| b|
++--+
+
++--+
+| a|
++--+
+| 9|
++--+
+|10|
++--+
+|11|
++--+
+|12|
++--+
+| b|
++--+
+
++--+
+| a|
++--+
+|13|
++--+
+|14|
++--+
+|15|
++--+
+| b|
++--+
+])
+AT_CLEANUP
+
+AT_SETUP([disabling too-big headers])
+AT_CAPTURE_FILE([input])
+AT_DATA([input], [17 1 0 0 1 1
+@a
+m4_for([x], [1], [15], [1], [@x
+])@b
+])
+AT_CHECK([render-test --length=10 input], [0], [dnl
++--+
+| a|
++--+
+| 1|
++--+
+| 2|
++--+
+| 3|
++--+
+
++--+
+| 4|
++--+
+| 5|
++--+
+| 6|
++--+
+| 7|
++--+
+
++--+
+| 8|
++--+
+| 9|
++--+
+|10|
++--+
+|11|
++--+
+
++--+
+|12|
++--+
+|13|
++--+
+|14|
++--+
+|15|
++--+
+
++--+
+| b|
++--+
+])
+AT_CLEANUP
+
+AT_SETUP([breaking column of many medium-size cells])
+AT_CAPTURE_FILE([input])
+AT_DATA([input], [20 1
+m4_for([x], [1], [20], [1], [@top x\ncell x\nbottom x
+])])
+AT_CHECK([render-test --length 10 input], [0], [dnl
++---------+
+|    top 1|
+|   cell 1|
+| bottom 1|
++---------+
+|    top 2|
+|   cell 2|
+| bottom 2|
++---------+
+
++---------+
+|    top 3|
+|   cell 3|
+| bottom 3|
++---------+
+|    top 4|
+|   cell 4|
+| bottom 4|
++---------+
+
++---------+
+|    top 5|
+|   cell 5|
+| bottom 5|
++---------+
+|    top 6|
+|   cell 6|
+| bottom 6|
++---------+
+
++---------+
+|    top 7|
+|   cell 7|
+| bottom 7|
++---------+
+|    top 8|
+|   cell 8|
+| bottom 8|
++---------+
+
++---------+
+|    top 9|
+|   cell 9|
+| bottom 9|
++---------+
+|   top 10|
+|  cell 10|
+|bottom 10|
++---------+
+
++---------+
+|   top 11|
+|  cell 11|
+|bottom 11|
++---------+
+|   top 12|
+|  cell 12|
+|bottom 12|
++---------+
+
++---------+
+|   top 13|
+|  cell 13|
+|bottom 13|
++---------+
+|   top 14|
+|  cell 14|
+|bottom 14|
++---------+
+
++---------+
+|   top 15|
+|  cell 15|
+|bottom 15|
++---------+
+|   top 16|
+|  cell 16|
+|bottom 16|
++---------+
+
++---------+
+|   top 17|
+|  cell 17|
+|bottom 17|
++---------+
+|   top 18|
+|  cell 18|
+|bottom 18|
++---------+
+
++---------+
+|   top 19|
+|  cell 19|
+|bottom 19|
++---------+
+|   top 20|
+|  cell 20|
+|bottom 20|
++---------+
+])
+AT_CLEANUP
+
+AT_SETUP([breaking 3 columns with many joined cells])
+AT_CAPTURE_FILE([input])
+AT_DATA([input], [3 19
+m4_foreach([x], [a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s], [x
+])@1
+m4_for([x], [2], [19], [1], [1*2 @x\nab\ncd
+])@20
+])
+AT_CHECK([render-test --length=6 --transpose input], [0], [dnl
+ +--+--+
+a| 1|11|
+ +--+ab|
+b| 2|cd|
+ |ab+--+
+
+ |ab+--+
+c|cd|12|
+ +--+ab|
+d| 3|cd|
+ |ab+--+
+
+ |ab+--+
+e|cd|13|
+ +--+ab|
+f| 4|cd|
+ |ab+--+
+
+ |ab+--+
+g|cd|14|
+ +--+ab|
+h| 5|cd|
+ |ab+--+
+
+ |ab+--+
+i|cd|15|
+ +--+ab|
+j| 6|cd|
+ |ab+--+
+
+ |ab+--+
+k|cd|16|
+ +--+ab|
+l| 7|cd|
+ |ab+--+
+
+ |ab+--+
+m|cd|17|
+ +--+ab|
+n| 8|cd|
+ |ab+--+
+
+ |ab+--+
+o|cd|18|
+ +--+ab|
+p| 9|cd|
+ |ab+--+
+
+ |ab+--+
+q|cd|19|
+ +--+ab|
+r|10|cd|
+ |ab+--+
+
+ |ab+--+
+s|cd|20|
+ +--+--+
+])
+AT_CLEANUP
+
+AT_SETUP([vert break 6x6, joined rows and columns])
+RENDER_WEAVE_6X6
+AT_CHECK([render-test --length=6 input], [0], [dnl
++-+---+-+-+-+
+|a|bcd|e|f|i|
++-+-+-+-+g+-+
+|j|m|nop|h|q|
+|k+-+---+-+r|
+
+|k+-+-+---+r|
+|l|t|w|xyz|s|
++-+u+-+-+-+-+
+|A|v|B|E|FGH|
++-+-+C+-+---+
+
++---+C+-+-+-+
+|IJK|D|L|O|P|
++-+-+-+M+-+-+
+|Q|RST|N|U|V|
++-+---+-+-+-+
+])
+AT_CLEANUP
+
+AT_SETUP([breaking joined cells too tall for page])
+AT_DATA([input], [4 6
+1*6 @abc\ndef\nghi\njkl\nmno\npqr\nstu\nvwx\nyzA\nBCD\nEFG
+1*3 @HIJ\nKLM\nOPQ\nRST\nUVW
+1*3 @XYZ\n012\n345\n678\n90a
+1*2 @bcd\nefg\nhij
+1*2 @klm\nnop\nqrs
+1*2 @tuv\nwxy\nzAB
+@C
+@D
+@E
+@F
+@G
+@H
+])
+AT_CHECK([render-test --transpose --length=6 input], [0], [dnl
++---+---+---+-+
+|abc|HIJ|bcd|C|
+|def|KLM|efg+-+
+|ghi|OPQ|hij|D|
+|jkl|RST+---+-+
+
+|jkl|RST+---+-+
+|mno|UVW|klm|E|
+|pqr+---+nop+-+
+|stu|XYZ|qrs|F|
+|vwx|012+---+-+
+
+|vwx|012+---+-+
+|yzA|345|tuv|G|
+|BCD|678|wxy+-+
+|EFG|90a|zAB|H|
++---+---+---+-+
+])
+AT_CLEANUP
+
+AT_SETUP([breaking cell too tall for page, no border])
+AT_CAPTURE_FILE([input])
+AT_DATA([input], [1 1
+abc defg hij klmn opq rstu vwx yz ABCDE FGH I JK LMNOP QR STU VWXYZ
+])
+AT_CHECK([render-test --width=6 --length=6 input], [0], [dnl
+   abc
+  defg
+   hij
+  klmn
+   opq
+  rstu
+
+vwx yz
+ ABCDE
+ FGH I
+    JK
+ LMNOP
+QR STU
+
+ VWXYZ
+])
+AT_CLEANUP
+
+AT_SETUP([breaking cell too tall for page, with border])
+AT_CAPTURE_FILE([input])
+AT_DATA([input], [1 1
+@abc defg hij klmn opq rstu vwx yz ABCDE FGH I JK LMNOP QR STU VWXYZ
+])
+AT_CHECK([render-test --width=7 --length=6 input], [0], [dnl
++-----+
+|  abc|
+| defg|
+|  hij|
+| klmn|
+|  opq|
+
+| klmn|
+|  opq|
+| rstu|
+|  vwx|
+|   yz|
+|ABCDE|
+
+|   yz|
+|ABCDE|
+|FGH I|
+|   JK|
+|LMNOP|
+|   QR|
+
+|LMNOP|
+|   QR|
+|  STU|
+|VWXYZ|
++-----+
+])
+AT_CLEANUP
+\f
+AT_BANNER([output rendering -- double page breaks])
+
+AT_SETUP([double break 6x6, joined rows and columns])
+RENDER_WEAVE_6X6
+AT_CHECK([render-test --width=6 --length=6 input], [0], [dnl
++-+--
+|a|bc
++-+-+
+|j|m|
+|k+-+
+
+|k+-+
+|l|t|
++-+u|
+|A|v|
++-+-+
+
++---+
+|IJK|
++-+-+
+|Q|RS
++-+--
+
+--+-+
+cd|e|
++-+-+
+|nop|
++---+
+
++-+--
+|w|xy
++-+-+
+|B|E|
+|C+-+
+
+|C+-+
+|D|L|
++-+M|
+ST|N|
+--+-+
+
++-+-+
+|f|i|
+|g+-+
+|h|q|
++-+r|
+
+--+r|
+yz|s|
++-+-+
+|FGH|
++---+
+
++-+-+
+|O|P|
++-+-+
+|U|V|
++-+-+
+])
+AT_CLEANUP
+
+AT_SETUP([double break 8x8, with joins, left and right headers])
+RENDER_8X8([1 1 0 0])
+AT_CHECK([render-test input --width=14 --length=14], [0], [dnl
++-+-+-+-+-+-+
+|a|b|c|d|e|h|
++-+-+-+-+-+-+
+|i|jkl|m|n|t|
++-+-+-+-+-+-+
+|u|v|wxy|z|D|
++-+-+-+-+-+-+
+|E|F|I|JKL|P|
++-+G+-+---+-+
+|Q|H|R|UVW|Y|
++-+-+S+-+-+-+
+|Z|0|T|3|4|8|
++-+1+-+-+-+-+
+
++-+1+---+-+-+
+|9|2|abc|d|h|
++-+-+-+-+-+-+
+|i|j|k|l|m|p|
++-+-+-+-+-+-+
+
++-+--+-+-+
+|a| f|g|h|
++-+--+-+-+
+|i|op|q|t|
++-+--+r+-+
+|u| A|s|D|
++-+ B+-+-+
+
++-+ B+-+-+
+|E| C|M|P|
++-+--+N+-+
+|Q| X|O|Y|
++-+--+-+-+
+|Z|56|7|8|
++-+--+-+-+
+|9| efg|h|
++-+--+-+-+
+|i| n|o|p|
++-+--+-+-+
+])
+AT_CLEANUP
+
+AT_SETUP([double break 8x8, with joins, top and bottom headers])
+RENDER_8X8([0 0 1 1])
+AT_CHECK([render-test input --width=14 --length=14], [0], [dnl
++-+-+-+-+-+-+
+|a|b|c|d|e|f|
++-+-+-+-+-+-+
+|i|jkl|m|nop|
++-+-+-+-+-+-+
+|u|v|wxy|z|A|
++-+-+-+-+-+B|
+|E|F|I|JKL|C|
++-+G+-+---+-+
+|Q|H|R|UVW|X|
++-+-+-+-+-+-+
+|i|j|k|l|m|n|
++-+-+-+-+-+-+
+
++-+-+-+-+-+-+
+|a|b|c|d|e|f|
++-+-+-+-+-+-+
+|Z|0|S|3|456|
+| |1|T| |   |
++-+2+-+-+-+-+
+|9| |abc|d|ef
++-+-+-+-+-+-+
+|i|j|k|l|m|n|
++-+-+-+-+-+-+
+
++-+-+
+|g|h|
++-+-+
+|q|t|
+|r+-+
+|s|D|
++-+-+
+|M|P|
+|N+-+
+|O|Y|
++-+-+
+|o|p|
++-+-+
+
++-+-+
+|g|h|
++-+-+
+|7|8|
+| | |
++-+-+
+fg|h|
++-+-+
+|o|p|
++-+-+
+])
+AT_CLEANUP
+
+AT_SETUP([double break 8x8, with joins, all headers])
+RENDER_8X8([1 1 1 1])
+AT_CHECK([render-test input --width=14 --length=14], [0], [dnl
++-+-+-+-+-+-+
+|a|b|c|d|e|h|
++-+-+-+-+-+-+
+|i|jkl|m|n|t|
++-+-+-+-+-+-+
+|u|v|wxy|z|D|
++-+-+-+-+-+-+
+|E|F|I|JKL|P|
++-+G+-+---+-+
+|Q|H|R|UVW|Y|
++-+-+-+-+-+-+
+|i|j|k|l|m|p|
++-+-+-+-+-+-+
+
++-+-+-+-+-+-+
+|a|b|c|d|e|h|
++-+-+-+-+-+-+
+|Z|0|S|3|4|8|
+| |1|T| | | |
++-+2+-+-+-+-+
+|9| |abc|d|h|
++-+-+-+-+-+-+
+|i|j|k|l|m|p|
++-+-+-+-+-+-+
+
++-+--+-+-+
+|a| f|g|h|
++-+--+-+-+
+|i|op|q|t|
++-+--+r+-+
+|u| A|s|D|
++-+ B+-+-+
+|E| C|M|P|
++-+--+N+-+
+|Q| X|O|Y|
++-+--+-+-+
+|i| n|o|p|
++-+--+-+-+
+
++-+--+-+-+
+|a| f|g|h|
++-+--+-+-+
+|Z|56|7|8|
+| |  | | |
++-+--+-+-+
+|9| efg|h|
++-+--+-+-+
+|i| n|o|p|
++-+--+-+-+
+])
+AT_CLEANUP
+
+AT_SETUP([double break joined cells too big for page])
+AT_DATA([input], [7 7
+@a
+@b
+@c
+@d
+@e
+@f
+@g
+@h
+6*6 @The MISSING subcommand determines the handling of missing variables.  If INCLUDE is set, then user-missing values are included in the calculations.  If NOINCLUDE is set, which is the default, user-missing values are excluded.
+@i
+@j
+@k
+@l
+@m
+])
+AT_CHECK([render-test --width=15 --length=15 input], [0], [dnl
++-+--+--+---+
+|a| b| c|  d|
++-+--+--+---+
+|h|       The
+| |        su
+| |    determ
++-+       han
+|i|missing va
+| |     If IN
+| |         s
++-+      user
+|j|        va
+| |   include
+| | calculati
++-+ NOINCLUDE
+
++-+ NOINCLUDE
+|k|      whic
+| |
++-+      user
+|l|        va
+| |         e
+| |
++-+
+|m|
+| |
+| |
++-+----------
+
++--+--+--+
+| e| f| g|
++--+--+--+
+e MISSING|
+ubcommand|
+mines the|
+ndling of|
+ariables.|
+NCLUDE is|
+set, then|
+r-missing|
+alues are|
+ed in the|
+ions.  If|
+E is set,|
+
+E is set,|
+ch is the|
+ default,|
+r-missing|
+alues are|
+excluded.|
+         |
+         |
+         |
+         |
+         |
+---------+
+])
+AT_CLEANUP
index a1f4a64e6aaf18d52fae489164f38f56e1c250c1..fedb73f6d2d72c0973094de28a0510070d67e472 100755 (executable)
@@ -86,52 +86,48 @@ $SUPERVISOR $PSPP --testing-mode $TEMPDIR/descript.stat
 if [ $? -ne 0 ] ; then no_result ; fi
 
 activity="compare output"
-perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
-diff -b $TEMPDIR/pspp.list - <<EOF
-1.1 DATA LIST.  Reading 1 record from INLINE.
-+--------+------+-------+------+
-|Variable|Record|Columns|Format|
-#========#======#=======#======#
-|V0      |     1|  1-  1|F1.0  |
-|V1      |     1|  2-  2|F1.0  |
-|V2      |     1|  3-  3|F1.0  |
-|V3      |     1|  4-  4|F1.0  |
-|V4      |     1|  5-  5|F1.0  |
-|V5      |     1|  6-  6|F1.0  |
-|V6      |     1|  7-  7|F1.0  |
-|V7      |     1|  8-  8|F1.0  |
-|V8      |     1|  9-  9|F1.0  |
-|V9      |     1| 10- 10|F1.0  |
-|V10     |     1| 11- 11|F1.0  |
-|V11     |     1| 12- 12|F1.0  |
-|V12     |     1| 13- 13|F1.0  |
-|V13     |     1| 14- 14|F1.0  |
-|V14     |     1| 15- 15|F1.0  |
-|V15     |     1| 16- 16|F1.0  |
-|V16     |     1| 17- 17|F1.0  |
-+--------+------+-------+------+
-2.1 DESCRIPTIVES.  Valid cases = 10; cases with missing value(s) = 0.
-+--------#-------+---------+----+---------+-------+--------+--------+---------+--------+---------+-----+-------+-------+-----+
-|Variable#Valid N|Missing N|Mean|S.E. Mean|Std Dev|Variance|Kurtosis|S.E. Kurt|Skewness|S.E. Skew|Range|Minimum|Maximum| Sum |
-#========#=======#=========#====#=========#=======#========#========#=========#========#=========#=====#=======#=======#=====#
-|V0      #     10|        0|3.80|      .84|   2.66|    7.07|    -.03|     1.33|     .89|      .69| 8.00|   1.00|   9.00|38.00|
-|V1      #     10|        0|4.60|      .96|   3.03|    9.16|   -1.39|     1.33|    -.03|      .69| 9.00|    .00|   9.00|46.00|
-|V2      #     10|        0|4.10|     1.16|   3.67|   13.43|   -2.02|     1.33|     .48|      .69| 8.00|   1.00|   9.00|41.00|
-|V3      #     10|        0|4.10|      .87|   2.77|    7.66|   -2.05|     1.33|     .42|      .69| 7.00|   1.00|   8.00|41.00|
-|V4      #     10|        0|7.00|      .47|   1.49|    2.22|    7.15|     1.33|   -2.52|      .69| 5.00|   3.00|   8.00|70.00|
-|V5      #     10|        0|4.90|     1.03|   3.25|   10.54|   -1.40|     1.33|    -.20|      .69| 9.00|    .00|   9.00|49.00|
-|V6      #     10|        0|5.90|      .80|   2.51|    6.32|    -.29|     1.33|    -.96|      .69| 7.00|   1.00|   8.00|59.00|
-|V7      #     10|        0|4.70|     1.10|   3.47|   12.01|   -1.99|     1.33|    -.16|      .69| 9.00|    .00|   9.00|47.00|
-|V8      #     10|        0|4.10|     1.10|   3.48|   12.10|   -1.93|     1.33|     .37|      .69| 9.00|    .00|   9.00|41.00|
-|V9      #     10|        0|4.30|      .87|   2.75|    7.57|    -.87|     1.33|     .73|      .69| 8.00|   1.00|   9.00|43.00|
-|V10     #     10|        0|5.50|      .85|   2.68|    7.17|   -1.84|     1.33|    -.33|      .69| 7.00|   2.00|   9.00|55.00|
-|V11     #     10|        0|6.50|      .78|   2.46|    6.06|   -1.28|     1.33|    -.89|      .69| 6.00|   3.00|   9.00|65.00|
-|V12     #     10|        0|7.90|      .60|   1.91|    3.66|    5.24|     1.33|   -2.21|      .69| 6.00|   3.00|   9.00|79.00|
-|V13     #     10|        0|4.30|      .99|   3.13|    9.79|   -1.25|     1.33|     .33|      .69| 9.00|    .00|   9.00|43.00|
-|V14     #     10|        0|3.60|     1.01|   3.20|   10.27|    -.96|     1.33|     .81|      .69| 9.00|    .00|   9.00|36.00|
-|V15     #     10|        0|3.70|      .92|   2.91|    8.46|   -1.35|     1.33|     .71|      .69| 7.00|   1.00|   8.00|37.00|
-|V16     #     10|        0|6.40|      .91|   2.88|    8.27|   -1.14|     1.33|    -.92|      .69| 7.00|   2.00|   9.00|64.00|
-+--------#-------+---------+----+---------+-------+--------+--------+---------+--------+---------+-----+-------+-------+-----+
+diff -c $TEMPDIR/pspp.csv - <<EOF
+Title: Test DESCRIPTIVES procedure
+
+Table: Reading 1 record from INLINE.
+Variable,Record,Columns,Format
+V0,1,1-  1,F1.0
+V1,1,2-  2,F1.0
+V2,1,3-  3,F1.0
+V3,1,4-  4,F1.0
+V4,1,5-  5,F1.0
+V5,1,6-  6,F1.0
+V6,1,7-  7,F1.0
+V7,1,8-  8,F1.0
+V8,1,9-  9,F1.0
+V9,1,10- 10,F1.0
+V10,1,11- 11,F1.0
+V11,1,12- 12,F1.0
+V12,1,13- 13,F1.0
+V13,1,14- 14,F1.0
+V14,1,15- 15,F1.0
+V15,1,16- 16,F1.0
+V16,1,17- 17,F1.0
+
+Table: Valid cases = 10; cases with missing value(s) = 0.
+Variable,Valid N,Missing N,Mean,S.E. Mean,Std Dev,Variance,Kurtosis,S.E. Kurt,Skewness,S.E. Skew,Range,Minimum,Maximum,Sum
+V0,10,0,3.80,.84,2.66,7.07,-.03,1.33,.89,.69,8.00,1.00,9.00,38.00
+V1,10,0,4.60,.96,3.03,9.16,-1.39,1.33,-.03,.69,9.00,.00,9.00,46.00
+V2,10,0,4.10,1.16,3.67,13.43,-2.02,1.33,.48,.69,8.00,1.00,9.00,41.00
+V3,10,0,4.10,.87,2.77,7.66,-2.05,1.33,.42,.69,7.00,1.00,8.00,41.00
+V4,10,0,7.00,.47,1.49,2.22,7.15,1.33,-2.52,.69,5.00,3.00,8.00,70.00
+V5,10,0,4.90,1.03,3.25,10.54,-1.40,1.33,-.20,.69,9.00,.00,9.00,49.00
+V6,10,0,5.90,.80,2.51,6.32,-.29,1.33,-.96,.69,7.00,1.00,8.00,59.00
+V7,10,0,4.70,1.10,3.47,12.01,-1.99,1.33,-.16,.69,9.00,.00,9.00,47.00
+V8,10,0,4.10,1.10,3.48,12.10,-1.93,1.33,.37,.69,9.00,.00,9.00,41.00
+V9,10,0,4.30,.87,2.75,7.57,-.87,1.33,.73,.69,8.00,1.00,9.00,43.00
+V10,10,0,5.50,.85,2.68,7.17,-1.84,1.33,-.33,.69,7.00,2.00,9.00,55.00
+V11,10,0,6.50,.78,2.46,6.06,-1.28,1.33,-.89,.69,6.00,3.00,9.00,65.00
+V12,10,0,7.90,.60,1.91,3.66,5.24,1.33,-2.21,.69,6.00,3.00,9.00,79.00
+V13,10,0,4.30,.99,3.13,9.79,-1.25,1.33,.33,.69,9.00,.00,9.00,43.00
+V14,10,0,3.60,1.01,3.20,10.27,-.96,1.33,.81,.69,9.00,.00,9.00,36.00
+V15,10,0,3.70,.92,2.91,8.46,-1.35,1.33,.71,.69,7.00,1.00,8.00,37.00
+V16,10,0,6.40,.91,2.88,8.27,-1.14,1.33,-.92,.69,7.00,2.00,9.00,64.00
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
index eb12331b9308ca30e67bf4deb534f0969f3c6ba2..532abed0fffb6ed0a02150f6ff0e7d87f115b5d2 100755 (executable)
@@ -81,14 +81,10 @@ $SUPERVISOR $PSPP --testing-mode $TEMPDIR/descript.stat
 if [ $? -ne 0 ] ; then no_result ; fi
 
 activity="compare output"
-perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
-diff  -b $TEMPDIR/pspp.list - <<EOF
-1.1 DESCRIPTIVES.  Valid cases = 6; cases with missing value(s) = 0.
-+--------#-+-----+
-|Variable#N| Mean|
-#========#=#=====#
-|X       #6|2.500|
-+--------#-+-----+
+diff -c $TEMPDIR/pspp.csv - <<EOF
+Table: Valid cases = 6; cases with missing value(s) = 0.
+Variable,N,Mean
+X,6,2.500
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
index 9de2770e1e5ae3bf1007636d84db803278926f19..fce8a17ecdf843e15424e193885173d23bfe092e 100755 (executable)
@@ -87,48 +87,38 @@ $SUPERVISOR $PSPP --testing-mode $TEMPDIR/descript.stat
 if [ $? -ne 0 ] ; then no_result ; fi
 
 activity="compare output"
-perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
-diff -b $TEMPDIR/pspp.list - <<EOF
-1.1 DATA LIST.  Reading 1 record from INLINE.
-+--------+------+-------+------+
-|Variable|Record|Columns|Format|
-#========#======#=======#======#
-|V1      |     1|  1-  1|F1.0  |
-|V2      |     1|  2-  2|F1.0  |
-|V3      |     1|  3-  3|F1.0  |
-+--------+------+-------+------+
-2.1 DESCRIPTIVES.  Valid cases = 7; cases with missing value(s) = 6.
-+--------#-------+---------+----+---------+-------+--------+--------+---------+--------+---------+-----+-------+-------+----+
-|Variable#Valid N|Missing N|Mean|S.E. Mean|Std Dev|Variance|Kurtosis|S.E. Kurt|Skewness|S.E. Skew|Range|Minimum|Maximum| Sum|
-#========#=======#=========#====#=========#=======#========#========#=========#========#=========#=====#=======#=======#====#
-|V1      #      1|        6|2.00|      .  |    .  |     .  |     .  |      .  |     .  |      .  |  .00|   2.00|   2.00|2.00|
-|V2      #      2|        5|2.50|      .50|    .71|     .50|     .  |      .  |     .  |      .  | 1.00|   2.00|   3.00|5.00|
-|V3      #      3|        4|3.00|      .58|   1.00|    1.00|     .  |      .  |     .00|     1.22| 2.00|   2.00|   4.00|9.00|
-+--------#-------+---------+----+---------+-------+--------+--------+---------+--------+---------+-----+-------+-------+----+
-3.1 DESCRIPTIVES.  Valid cases = 7; cases with missing value(s) = 3.
-+--------#-------+---------+----+---------+-------+--------+--------+---------+--------+---------+-----+-------+-------+-----+
-|Variable#Valid N|Missing N|Mean|S.E. Mean|Std Dev|Variance|Kurtosis|S.E. Kurt|Skewness|S.E. Skew|Range|Minimum|Maximum| Sum |
-#========#=======#=========#====#=========#=======#========#========#=========#========#=========#=====#=======#=======#=====#
-|V1      #      5|        2|1.20|      .20|    .45|     .20|    5.00|     2.00|    2.24|      .91| 1.00|   1.00|   2.00| 6.00|
-|V2      #      5|        2|1.60|      .40|    .89|     .80|     .31|     2.00|    1.26|      .91| 2.00|   1.00|   3.00| 8.00|
-|V3      #      5|        2|2.20|      .58|   1.30|    1.70|   -1.49|     2.00|     .54|      .91| 3.00|   1.00|   4.00|11.00|
-+--------#-------+---------+----+---------+-------+--------+--------+---------+--------+---------+-----+-------+-------+-----+
-4.1 DESCRIPTIVES.  Valid cases = 1; cases with missing value(s) = 6.
-+--------#-------+---------+----+---------+-------+--------+--------+---------+--------+---------+-----+-------+-------+----+
-|Variable#Valid N|Missing N|Mean|S.E. Mean|Std Dev|Variance|Kurtosis|S.E. Kurt|Skewness|S.E. Skew|Range|Minimum|Maximum| Sum|
-#========#=======#=========#====#=========#=======#========#========#=========#========#=========#=====#=======#=======#====#
-|V1      #      1|        0|2.00|      .  |    .  |     .  |     .  |      .  |     .  |      .  |  .00|   2.00|   2.00|2.00|
-|V2      #      1|        0|3.00|      .  |    .  |     .  |     .  |      .  |     .  |      .  |  .00|   3.00|   3.00|3.00|
-|V3      #      1|        0|4.00|      .  |    .  |     .  |     .  |      .  |     .  |      .  |  .00|   4.00|   4.00|4.00|
-+--------#-------+---------+----+---------+-------+--------+--------+---------+--------+---------+-----+-------+-------+----+
-5.1 DESCRIPTIVES.  Valid cases = 4; cases with missing value(s) = 3.
-+--------#-------+---------+----+---------+-------+--------+--------+---------+--------+---------+-----+-------+-------+-----+
-|Variable#Valid N|Missing N|Mean|S.E. Mean|Std Dev|Variance|Kurtosis|S.E. Kurt|Skewness|S.E. Skew|Range|Minimum|Maximum| Sum |
-#========#=======#=========#====#=========#=======#========#========#=========#========#=========#=====#=======#=======#=====#
-|V1      #      4|        0|1.25|      .25|    .50|     .25|    4.00|     2.62|    2.00|     1.01| 1.00|   1.00|   2.00| 5.00|
-|V2      #      4|        0|1.75|      .48|    .96|     .92|   -1.29|     2.62|     .85|     1.01| 2.00|   1.00|   3.00| 7.00|
-|V3      #      4|        0|2.50|      .65|   1.29|    1.67|   -1.20|     2.62|     .00|     1.01| 3.00|   1.00|   4.00|10.00|
-+--------#-------+---------+----+---------+-------+--------+--------+---------+--------+---------+-----+-------+-------+-----+
+diff -c $TEMPDIR/pspp.csv - <<EOF
+Title: Test DESCRIPTIVES procedure
+
+Table: Reading 1 record from INLINE.
+Variable,Record,Columns,Format
+V1,1,1-  1,F1.0
+V2,1,2-  2,F1.0
+V3,1,3-  3,F1.0
+
+Table: Valid cases = 7; cases with missing value(s) = 6.
+Variable,Valid N,Missing N,Mean,S.E. Mean,Std Dev,Variance,Kurtosis,S.E. Kurt,Skewness,S.E. Skew,Range,Minimum,Maximum,Sum
+V1,1,6,2.00,.  ,.  ,.  ,.  ,.  ,.  ,.  ,.00,2.00,2.00,2.00
+V2,2,5,2.50,.50,.71,.50,.  ,.  ,.  ,.  ,1.00,2.00,3.00,5.00
+V3,3,4,3.00,.58,1.00,1.00,.  ,.  ,.00,1.22,2.00,2.00,4.00,9.00
+
+Table: Valid cases = 7; cases with missing value(s) = 3.
+Variable,Valid N,Missing N,Mean,S.E. Mean,Std Dev,Variance,Kurtosis,S.E. Kurt,Skewness,S.E. Skew,Range,Minimum,Maximum,Sum
+V1,5,2,1.20,.20,.45,.20,5.00,2.00,2.24,.91,1.00,1.00,2.00,6.00
+V2,5,2,1.60,.40,.89,.80,.31,2.00,1.26,.91,2.00,1.00,3.00,8.00
+V3,5,2,2.20,.58,1.30,1.70,-1.49,2.00,.54,.91,3.00,1.00,4.00,11.00
+
+Table: Valid cases = 1; cases with missing value(s) = 6.
+Variable,Valid N,Missing N,Mean,S.E. Mean,Std Dev,Variance,Kurtosis,S.E. Kurt,Skewness,S.E. Skew,Range,Minimum,Maximum,Sum
+V1,1,0,2.00,.  ,.  ,.  ,.  ,.  ,.  ,.  ,.00,2.00,2.00,2.00
+V2,1,0,3.00,.  ,.  ,.  ,.  ,.  ,.  ,.  ,.00,3.00,3.00,3.00
+V3,1,0,4.00,.  ,.  ,.  ,.  ,.  ,.  ,.  ,.00,4.00,4.00,4.00
+
+Table: Valid cases = 4; cases with missing value(s) = 3.
+Variable,Valid N,Missing N,Mean,S.E. Mean,Std Dev,Variance,Kurtosis,S.E. Kurt,Skewness,S.E. Skew,Range,Minimum,Maximum,Sum
+V1,4,0,1.25,.25,.50,.25,4.00,2.62,2.00,1.01,1.00,1.00,2.00,5.00
+V2,4,0,1.75,.48,.96,.92,-1.29,2.62,.85,1.01,2.00,1.00,3.00,7.00
+V3,4,0,2.50,.65,1.29,1.67,-1.20,2.62,.00,1.01,3.00,1.00,4.00,10.00
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
index dd00a361368128d3d02dbe834514a971b8c7a003..4f4eedb15eedc208deb0bc9bba4e4e9279de0284 100755 (executable)
@@ -81,7 +81,7 @@ $SUPERVISOR $PSPP --testing-mode $TEMPDIR/prog.sps
 if [ $? -ne 0 ] ; then no_result ; fi
 
 activity="move output"
-cp $TEMPDIR/pspp.list $TEMPDIR/list.ref
+cp $TEMPDIR/pspp.csv $TEMPDIR/list.ref
 if [ $? -ne 0 ] ; then no_result ; fi
 
 i=$[$i+1];
@@ -110,7 +110,7 @@ $SUPERVISOR $PSPP --testing-mode $TEMPDIR/prog.sps
 if [ $? -ne 0 ] ; then no_result ; fi
 
 activity="compare output"
-diff $TEMPDIR/pspp.list $TEMPDIR/list.ref
+diff $TEMPDIR/pspp.csv $TEMPDIR/list.ref
 if [ $? -ne 0 ] ; then fail; fi
 
 
index 9b170240538561a34803fbe91e38f093356081cf..2d539a1d24ce6858eb45778051f36e405932e26d 100755 (executable)
@@ -86,33 +86,27 @@ $SUPERVISOR $PSPP --testing-mode $TEMPDIR/prog.sps
 if [ $? -ne 0 ] ; then no_result ; fi
 
 activity="compare output $i"
-perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
-diff  -b $TEMPDIR/pspp.list - <<EOF
-1.1 FREQUENCIES.  X
-+-----------+--------+---------+--------+-------------+-----------+
-|Value Label|  Value |Frequency| Percent|Valid Percent|Cum Percent|
-#===========#========#=========#========#=============#===========#
-|           |    1.00|        1|   20.00|        20.00|      20.00|
-|           |    2.00|        1|   20.00|        20.00|      40.00|
-|           |    3.00|        1|   20.00|        20.00|      60.00|
-|           |    4.00|        1|   20.00|        20.00|      80.00|
-|           |    5.00|        1|   20.00|        20.00|     100.00|
-#===========#========#=========#========#=============#===========#
-|               Total|        5|   100.0|        100.0|           |
-+--------------------+---------+--------+-------------+-----------+
-+-----------------------+----+
-|N           Valid      |   5|
-|            Missing    |   0|
-|Mean                   |3.00|
-|Std Dev                |1.58|
-|Minimum                |1.00|
-|Maximum                |5.00|
-|Percentiles 0          |1.00|
-|            25         |1.50|
-|            50 (Median)|3.00|
-|            75         |4.50|
-|            100        |5.00|
-+-----------------------+----+
+diff -c $TEMPDIR/pspp.csv - <<EOF
+Table: X
+Value Label,Value,Frequency,Percent,Valid Percent,Cum Percent
+,1.00,1,20.00,20.00,20.00
+,2.00,1,20.00,20.00,40.00
+,3.00,1,20.00,20.00,60.00
+,4.00,1,20.00,20.00,80.00
+,5.00,1,20.00,20.00,100.00
+Total,,5,100.0,100.0,
+
+N,Valid,5
+,Missing,0
+Mean,,3.00
+Std Dev,,1.58
+Minimum,,1.00
+Maximum,,5.00
+Percentiles,0,1.00
+,25,1.50
+,50 (Median),3.00
+,75,4.50
+,100,5.00
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
index 9ee5a8c9b7d084a74055e099afc61a97e457db5f..5c5accbbcf636269d497307983571ba1fc1eedcb 100755 (executable)
@@ -85,33 +85,27 @@ $SUPERVISOR $PSPP --testing-mode $TEMPDIR/prog.sps
 if [ $? -ne 0 ] ; then no_result ; fi
 
 activity="compare output $i"
-perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
-diff  -b $TEMPDIR/pspp.list - <<EOF
-1.1 FREQUENCIES.  X
-+-----------+--------+---------+--------+-------------+-----------+
-|Value Label|  Value |Frequency| Percent|Valid Percent|Cum Percent|
-#===========#========#=========#========#=============#===========#
-|           |    1.00|        1|   20.00|        20.00|      20.00|
-|           |    2.00|        1|   20.00|        20.00|      40.00|
-|           |    3.00|        1|   20.00|        20.00|      60.00|
-|           |    4.00|        1|   20.00|        20.00|      80.00|
-|           |    5.00|        1|   20.00|        20.00|     100.00|
-#===========#========#=========#========#=============#===========#
-|               Total|        5|   100.0|        100.0|           |
-+--------------------+---------+--------+-------------+-----------+
-+-----------------------+----+
-|N           Valid      |   5|
-|            Missing    |   0|
-|Mean                   |3.00|
-|Std Dev                |1.58|
-|Minimum                |1.00|
-|Maximum                |5.00|
-|Percentiles 0          |1.00|
-|            25         |2.00|
-|            50 (Median)|3.00|
-|            75         |4.00|
-|            100        |5.00|
-+-----------------------+----+
+diff -c $TEMPDIR/pspp.csv - <<EOF
+Table: X
+Value Label,Value,Frequency,Percent,Valid Percent,Cum Percent
+,1.00,1,20.00,20.00,20.00
+,2.00,1,20.00,20.00,40.00
+,3.00,1,20.00,20.00,60.00
+,4.00,1,20.00,20.00,80.00
+,5.00,1,20.00,20.00,100.00
+Total,,5,100.0,100.0,
+
+N,Valid,5
+,Missing,0
+Mean,,3.00
+Std Dev,,1.58
+Minimum,,1.00
+Maximum,,5.00
+Percentiles,0,1.00
+,25,2.00
+,50 (Median),3.00
+,75,4.00
+,100,5.00
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
@@ -147,33 +141,27 @@ $SUPERVISOR $PSPP --testing-mode $TEMPDIR/prog.sps
 if [ $? -ne 0 ] ; then no_result ; fi
 
 activity="compare output $i"
-perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
-diff  -b $TEMPDIR/pspp.list - <<EOF
-1.1 FREQUENCIES.  X
-+-----------+--------+---------+--------+-------------+-----------+
-|Value Label|  Value |Frequency| Percent|Valid Percent|Cum Percent|
-#===========#========#=========#========#=============#===========#
-|           |    1.00|     2.00|   20.00|        20.00|      20.00|
-|           |    2.00|     2.00|   20.00|        20.00|      40.00|
-|           |    3.00|     2.00|   20.00|        20.00|      60.00|
-|           |    4.00|     2.00|   20.00|        20.00|      80.00|
-|           |    5.00|     2.00|   20.00|        20.00|     100.00|
-#===========#========#=========#========#=============#===========#
-|               Total|    10.00|   100.0|        100.0|           |
-+--------------------+---------+--------+-------------+-----------+
-+-----------------------+-----+
-|N           Valid      |10.00|
-|            Missing    |  .00|
-|Mean                   | 3.00|
-|Std Dev                | 1.49|
-|Minimum                | 1.00|
-|Maximum                | 5.00|
-|Percentiles 0          | 1.00|
-|            25         | 2.00|
-|            50 (Median)| 3.00|
-|            75         | 4.00|
-|            100        | 5.00|
-+-----------------------+-----+
+diff -c $TEMPDIR/pspp.csv - <<EOF
+Table: X
+Value Label,Value,Frequency,Percent,Valid Percent,Cum Percent
+,1.00,2.00,20.00,20.00,20.00
+,2.00,2.00,20.00,20.00,40.00
+,3.00,2.00,20.00,20.00,60.00
+,4.00,2.00,20.00,20.00,80.00
+,5.00,2.00,20.00,20.00,100.00
+Total,,10.00,100.0,100.0,
+
+N,Valid,10.00
+,Missing,.00
+Mean,,3.00
+Std Dev,,1.49
+Minimum,,1.00
+Maximum,,5.00
+Percentiles,0,1.00
+,25,2.00
+,50 (Median),3.00
+,75,4.00
+,100,5.00
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
@@ -207,32 +195,26 @@ $SUPERVISOR $PSPP --testing-mode $TEMPDIR/prog.sps
 if [ $? -ne 0 ] ; then no_result ; fi
 
 activity="compare output $i"
-perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
-diff  -b $TEMPDIR/pspp.list - <<EOF
-1.1 FREQUENCIES.  X
-+-----------+--------+---------+--------+-------------+-----------+
-|Value Label|  Value |Frequency| Percent|Valid Percent|Cum Percent|
-#===========#========#=========#========#=============#===========#
-|           |    1.00|     1.00|   16.67|        16.67|      16.67|
-|           |    3.00|     2.00|   33.33|        33.33|      50.00|
-|           |    4.00|     1.00|   16.67|        16.67|      66.67|
-|           |    5.00|     2.00|   33.33|        33.33|     100.00|
-#===========#========#=========#========#=============#===========#
-|               Total|     6.00|   100.0|        100.0|           |
-+--------------------+---------+--------+-------------+-----------+
-+-----------------------+----+
-|N           Valid      |6.00|
-|            Missing    | .00|
-|Mean                   |3.50|
-|Std Dev                |1.52|
-|Minimum                |1.00|
-|Maximum                |5.00|
-|Percentiles 0          |1.00|
-|            25         |3.00|
-|            50 (Median)|3.50|
-|            75         |4.75|
-|            100        |5.00|
-+-----------------------+----+
+diff -c $TEMPDIR/pspp.csv - <<EOF
+Table: X
+Value Label,Value,Frequency,Percent,Valid Percent,Cum Percent
+,1.00,1.00,16.67,16.67,16.67
+,3.00,2.00,33.33,33.33,50.00
+,4.00,1.00,16.67,16.67,66.67
+,5.00,2.00,33.33,33.33,100.00
+Total,,6.00,100.0,100.0,
+
+N,Valid,6.00
+,Missing,.00
+Mean,,3.50
+Std Dev,,1.52
+Minimum,,1.00
+Maximum,,5.00
+Percentiles,0,1.00
+,25,3.00
+,50 (Median),3.50
+,75,4.75
+,100,5.00
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
@@ -266,33 +248,27 @@ $SUPERVISOR $PSPP --testing-mode $TEMPDIR/prog.sps
 if [ $? -ne 0 ] ; then no_result ; fi
 
 activity="compare output $i"
-perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
-diff -b $TEMPDIR/pspp.list - <<EOF
-1.1 FREQUENCIES.  X
-+-----------+--------+---------+--------+-------------+-----------+
-|Value Label|  Value |Frequency| Percent|Valid Percent|Cum Percent|
-#===========#========#=========#========#=============#===========#
-|           |    1.00|     1.00|   10.00|        16.67|      16.67|
-|           |    3.00|     2.00|   20.00|        33.33|      50.00|
-|           |    4.00|     1.00|   10.00|        16.67|      66.67|
-|           |    5.00|     2.00|   20.00|        33.33|     100.00|
-|           |   99.00|     4.00|   40.00|      Missing|           |
-#===========#========#=========#========#=============#===========#
-|               Total|    10.00|   100.0|        100.0|           |
-+--------------------+---------+--------+-------------+-----------+
-+-----------------------+----+
-|N           Valid      |6.00|
-|            Missing    |4.00|
-|Mean                   |3.50|
-|Std Dev                |1.52|
-|Minimum                |1.00|
-|Maximum                |5.00|
-|Percentiles 0          |1.00|
-|            25         |3.00|
-|            50 (Median)|3.50|
-|            75         |4.75|
-|            100        |5.00|
-+-----------------------+----+
+diff -c $TEMPDIR/pspp.csv - <<EOF
+Table: X
+Value Label,Value,Frequency,Percent,Valid Percent,Cum Percent
+,1.00,1.00,10.00,16.67,16.67
+,3.00,2.00,20.00,33.33,50.00
+,4.00,1.00,10.00,16.67,66.67
+,5.00,2.00,20.00,33.33,100.00
+,99.00,4.00,40.00,Missing,
+Total,,10.00,100.0,100.0,
+
+N,Valid,6.00
+,Missing,4.00
+Mean,,3.50
+Std Dev,,1.52
+Minimum,,1.00
+Maximum,,5.00
+Percentiles,0,1.00
+,25,3.00
+,50 (Median),3.50
+,75,4.75
+,100,5.00
 EOF
 if [ $? -ne 0 ] ; then fail ; fi
 
diff --git a/tests/testsuite.at b/tests/testsuite.at
new file mode 100644 (file)
index 0000000..fbf821a
--- /dev/null
@@ -0,0 +1,5 @@
+AT_INIT
+
+AT_TESTED([pspp])
+
+m4_include([tests/output/render.at])
index ae29cc4d1d48cc0bf962facc9072cc2ceb93628b..f36e5f2a113867843ac5366ef5bfcefd6e9230b5 100755 (executable)
@@ -166,99 +166,104 @@ $SUPERVISOR $PSPP --testing-mode $TESTFILE
 if [ $? -ne 0 ] ; then no_result ; fi
 
 activity="test output"
-perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
-diff -bu $TEMPDIR/pspp.list - <<EOF
-x  x0  x1  x2  x3  x4  x5  x6  x7  x8
-- --- --- --- --- --- --- --- --- ---
-0   0   0   0   0   0   1   0   3   0 
-1   9   9   8  10  10   1   1   3   1 
-2   2   2   9  10  10   1   2   3   2 
-3   3   8   9  10  10   1   3   3   3 
-4   4   4   9  10  10   1   2   3   4 
-5   5   7   9  10  10   1   2   3   5 
-6   6   6   9  10  10   6   2   3   6 
-7   7   7   7  10  10   7   2   3   7 
-8   8   8   9  10  10   8   2   3   8 
-9   9   9   1  10  11   9   2   3   9 
-.   .   .   .  11  11   .   .   .   4 
-x ix0 ix1 ix2 ix3 ix4 ix5 ix6 ix7 ix8
-- --- --- --- --- --- --- --- --- ---
-0   .   .   .   .   .   1   .   3   . 
-1   9   9   8  10  10   1   .   3   . 
-2   .   .   9  10  10   1   .   3   . 
-3   .   8   9  10  10   1   .   3   . 
-4   .   .   9  10  10   1   2   3   . 
-5   .   7   9  10  10   1   2   3   . 
-6   .   .   9  10  10   .   2   3   . 
-7   .   .   .  10  10   .   2   3   . 
-8   .   .   9  10  10   .   2   3   . 
-9   .   .   1  10  11   .   2   3   . 
-.   .   .   .  11  11   .   .   .   4 
-x cx0 cx1 cx2 cx3 cx4 cx5 cx6 cx7 cx8      cx9
-- --- --- --- --- --- --- --- --- --- --------
-0   0   0   0   0   0   1   0   3   0    22.00 
-1   9   9   8  10  10   1   1   3   1    22.00 
-2   2   2   9  10  10   1   2   3   2    22.00 
-3   3   8   9  10  10   1   3   3   3    22.00 
-4   4   4   9  10  10   1   2   3   4    22.00 
-5   5   7   9  10  10   1   2   3   5     5.00 
-6   6   6   9  10  10   6   2   3   6    22.00 
-7   7   7   7  10  10   7   2   3   7    22.00 
-8   8   8   9  10  10   8   2   3   8    22.00 
-9   9   9   1  10  11   9   2   3   9    22.00 
-.   .   .   .  11  11   .   .   .   4    22.00 
-   s          t   s0   s1   s2   s3         t0         t1         t2         t3
----- ---------- ---- ---- ---- ---- ---------- ---------- ---------- ----------
-                          xyz                             xyz                   
-a    a          b         xyz       b                     xyz        b          
-ab   ab         bc        xyz       bc                    xyz                   
-abc  abc                  def                             def                   
-abcd abcd            xyzw xyz                  xyzw       xyz                   
-123  123                  xyz                             xyz                   
- 123  123                 xyz                             xyz                   
-+1   +1                   xyz                             xyz                   
-1x   1x                   xyz                             xyz                   
-abcd abcdefghi       xyzw xyz                             xyz        xyz        
-xxx  abcdefghij           xyz  gone                       xyz        jklmnopqr  
-   s          t  cs0  cs1  cs2        ct0        ct1        ct2        ct3
----- ---------- ---- ---- ---- ---------- ---------- ---------- ----------
-                          xyz                        xyz                   
-a    a          b    a    xyz  b          a          xyz        b          
-ab   ab         bc   ab   xyz  bc         ab         xyz        ab         
-abc  abc        abc  abc  def  abc        abc        def        abc        
-abcd abcd       abcd xyzw xyz  abcd       xyzw       xyz        abcd       
-123  123        123  123  xyz  123        123        xyz        123        
- 123  123        123  123 xyz   123        123       xyz         123       
-+1   +1         +1   +1   xyz  +1         +1         xyz        +1         
-1x   1x         1x   1x   xyz  1x         1x         xyz        1x         
-abcd abcdefghi  abcd xyzw xyz  abcdefghi  abcdefghi  xyz        xyz        
-xxx  abcdefghij xxx  xxx  xyz  abcdefghij abcdefghij xyz        jklmnopqr  
-   s          t ns0 ns1 ns2 nt0 nt1 nt2
----- ---------- --- --- --- --- --- ---
-                  .   0   3   .   0   3 
-a    a            .   .   3   .   .   3 
-ab   ab           .   .   3   .   .   3 
-abc  abc          .   .   3   .   .   3 
-abcd abcd         1   1   2   1   1   2 
-123  123        123 123   3 123 123   3 
- 123  123       123 123   3 123 123   3 
-+1   +1           1   1   3   1   1   3 
-1x   1x           .   .   1   .   .   1 
-abcd abcdefghi    1   1   2   .   .   3 
-xxx  abcdefghij   .   .   3   .   .   3 
-x        sx0        sx1        sx2
-- ---------- ---------- ----------
-0            xxx        foobar     
-1 abcdefghij xxx        foobar     
-2 abcdefghij            xyz        
-3 abcdefghij xxx        xyz        
-4 abcdefghij            xyz        
-5 abcdefghij xxx        xyz        
-6 abcdefghij            xyz        
-7 abcdefghij xxx        foobar     
-8 abcdefghij            foobar     
-9 abcdefghij xxx        foobar     
-.            xxx        xyz        
+diff -c $TEMPDIR/pspp.csv - <<EOF
+Table: Data List
+x,x0,x1,x2,x3,x4,x5,x6,x7,x8
+0,0,0,0,0,0,1,0,3,0
+1,9,9,8,10,10,1,1,3,1
+2,2,2,9,10,10,1,2,3,2
+3,3,8,9,10,10,1,3,3,3
+4,4,4,9,10,10,1,2,3,4
+5,5,7,9,10,10,1,2,3,5
+6,6,6,9,10,10,6,2,3,6
+7,7,7,7,10,10,7,2,3,7
+8,8,8,9,10,10,8,2,3,8
+9,9,9,1,10,11,9,2,3,9
+.,.,.,.,11,11,.,.,.,4
+
+Table: Data List
+x,ix0,ix1,ix2,ix3,ix4,ix5,ix6,ix7,ix8
+0,.,.,.,.,.,1,.,3,.
+1,9,9,8,10,10,1,.,3,.
+2,.,.,9,10,10,1,.,3,.
+3,.,8,9,10,10,1,.,3,.
+4,.,.,9,10,10,1,2,3,.
+5,.,7,9,10,10,1,2,3,.
+6,.,.,9,10,10,.,2,3,.
+7,.,.,.,10,10,.,2,3,.
+8,.,.,9,10,10,.,2,3,.
+9,.,.,1,10,11,.,2,3,.
+.,.,.,.,11,11,.,.,.,4
+
+Table: Data List
+x,cx0,cx1,cx2,cx3,cx4,cx5,cx6,cx7,cx8,cx9
+0,0,0,0,0,0,1,0,3,0,22.00
+1,9,9,8,10,10,1,1,3,1,22.00
+2,2,2,9,10,10,1,2,3,2,22.00
+3,3,8,9,10,10,1,3,3,3,22.00
+4,4,4,9,10,10,1,2,3,4,22.00
+5,5,7,9,10,10,1,2,3,5,5.00
+6,6,6,9,10,10,6,2,3,6,22.00
+7,7,7,7,10,10,7,2,3,7,22.00
+8,8,8,9,10,10,8,2,3,8,22.00
+9,9,9,1,10,11,9,2,3,9,22.00
+.,.,.,.,11,11,.,.,.,4,22.00
+
+Table: Data List
+s,t,s0,s1,s2,s3,t0,t1,t2,t3
+,,,,xyz ,,,,xyz       ,
+a   ,a         ,b   ,,xyz ,,b         ,,xyz       ,b         
+ab  ,ab        ,bc  ,,xyz ,,bc        ,,xyz       ,
+abc ,abc       ,,,def ,,,,def       ,
+abcd,abcd      ,,xyzw,xyz ,,,xyzw      ,xyz       ,
+123 ,123       ,,,xyz ,,,,xyz       ,
+123,123      ,,,xyz ,,,,xyz       ,
++1  ,+1        ,,,xyz ,,,,xyz       ,
+1x  ,1x        ,,,xyz ,,,,xyz       ,
+abcd,abcdefghi ,,xyzw,xyz ,,,,xyz       ,xyz       
+xxx ,abcdefghij,,,xyz ,gone,,,xyz       ,jklmnopqr 
+
+Table: Data List
+s,t,cs0,cs1,cs2,ct0,ct1,ct2,ct3
+,,,,xyz ,,,xyz       ,
+a   ,a         ,b   ,a   ,xyz ,b         ,a         ,xyz       ,b         
+ab  ,ab        ,bc  ,ab  ,xyz ,bc        ,ab        ,xyz       ,ab        
+abc ,abc       ,abc ,abc ,def ,abc       ,abc       ,def       ,abc       
+abcd,abcd      ,abcd,xyzw,xyz ,abcd      ,xyzw      ,xyz       ,abcd      
+123 ,123       ,123 ,123 ,xyz ,123       ,123       ,xyz       ,123       
+123,123      ,123,123,xyz ,123      ,123      ,xyz       ,123      
++1  ,+1        ,+1  ,+1  ,xyz ,+1        ,+1        ,xyz       ,+1        
+1x  ,1x        ,1x  ,1x  ,xyz ,1x        ,1x        ,xyz       ,1x        
+abcd,abcdefghi ,abcd,xyzw,xyz ,abcdefghi ,abcdefghi ,xyz       ,xyz       
+xxx ,abcdefghij,xxx ,xxx ,xyz ,abcdefghij,abcdefghij,xyz       ,jklmnopqr 
+
+Table: Data List
+s,t,ns0,ns1,ns2,nt0,nt1,nt2
+,,.,0,3,.,0,3
+a   ,a         ,.,.,3,.,.,3
+ab  ,ab        ,.,.,3,.,.,3
+abc ,abc       ,.,.,3,.,.,3
+abcd,abcd      ,1,1,2,1,1,2
+123 ,123       ,123,123,3,123,123,3
+123,123      ,123,123,3,123,123,3
++1  ,+1        ,1,1,3,1,1,3
+1x  ,1x        ,.,.,1,.,.,1
+abcd,abcdefghi ,1,1,2,.,.,3
+xxx ,abcdefghij,.,.,3,.,.,3
+
+Table: Data List
+x,sx0,sx1,sx2
+0,,xxx       ,foobar    
+1,abcdefghij,xxx       ,foobar    
+2,abcdefghij,,xyz       
+3,abcdefghij,xxx       ,xyz       
+4,abcdefghij,,xyz       
+5,abcdefghij,xxx       ,xyz       
+6,abcdefghij,,xyz       
+7,abcdefghij,xxx       ,foobar    
+8,abcdefghij,,foobar    
+9,abcdefghij,xxx       ,foobar    
+.,,xxx       ,xyz       
 EOF
 if [ $? -ne 0 ] ; then fail ; fi