From dfd1972f7bcb550a4fc3b05dbe7e71d12334b0a7 Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Sat, 16 Jan 2010 18:07:31 -0800 Subject: [PATCH] Rewrite PSPP output engine. 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. --- .gitignore | 1 + Makefile.am | 2 + NEWS | 7 +- README | 6 +- configure.ac | 5 +- doc/introduction.texi | 12 +- perl-module/automake.mk | 12 +- perl-module/t/Pspp.t | 97 +- src/data/make-file.c | 1 + src/language/command.c | 58 +- src/language/data-io/data-parser.c | 6 +- src/language/data-io/list.q | 664 +------ src/language/data-io/print-space.c | 4 +- src/language/data-io/print.c | 10 +- src/language/dictionary/split-file.c | 5 +- src/language/dictionary/sys-file-info.c | 72 +- src/language/lexer/lexer.c | 14 +- src/language/prompt.c | 2 +- src/language/stats/binomial.c | 4 +- src/language/stats/chisquare.c | 5 +- src/language/stats/correlations.c | 5 +- src/language/stats/crosstabs.q | 86 +- src/language/stats/descriptives.c | 6 +- src/language/stats/examine.q | 35 +- src/language/stats/factor.c | 15 +- src/language/stats/frequencies.q | 92 +- src/language/stats/glm.q | 2 +- src/language/stats/npar-summary.c | 15 +- src/language/stats/oneway.q | 10 +- src/language/stats/rank.q | 5 +- src/language/stats/regression.q | 6 +- src/language/stats/reliability.q | 24 +- src/language/stats/roc.c | 29 +- src/language/stats/sign.c | 6 +- src/language/stats/t-test.q | 8 +- src/language/stats/wilcoxon.c | 6 +- src/language/syntax-file.c | 2 +- src/language/tests/paper-size.c | 9 +- src/language/utilities/echo.c | 6 +- src/language/utilities/include.c | 11 +- src/language/utilities/set.q | 4 +- src/language/utilities/title.c | 35 +- src/output/ascii.c | 1320 +++++++------ src/output/automake.mk | 52 +- .../{charts/plot-chart.c => cairo-chart.c} | 213 +- src/output/cairo-chart.h | 165 ++ src/output/cairo.c | 1224 +++++++----- src/output/cairo.h | 17 +- src/output/{htmlP.h => chart-item-provider.h} | 27 +- src/output/chart-item.c | 88 + src/output/chart-item.h | 100 + src/output/chart-provider.h | 89 - src/output/chart.c | 181 -- src/output/charts/boxplot-cairo.c | 179 ++ src/output/charts/boxplot.c | 207 +- src/output/charts/boxplot.h | 85 +- src/output/charts/cartesian.c | 130 -- src/output/charts/cartesian.h | 53 - src/output/charts/np-plot-cairo.c | 90 + src/output/charts/np-plot.c | 117 +- src/output/charts/np-plot.h | 92 +- src/output/charts/piechart-cairo.c | 123 ++ src/output/charts/piechart.c | 145 +- src/output/charts/piechart.h | 82 +- src/output/charts/plot-chart.h | 98 - src/output/charts/plot-hist-cairo.c | 162 ++ src/output/charts/plot-hist.c | 174 +- src/output/charts/plot-hist.h | 82 +- src/output/charts/roc-chart-cairo.c | 73 + src/output/charts/roc-chart.c | 83 +- src/output/charts/roc-chart.h | 78 +- src/output/charts/scree-cairo.c | 65 + src/output/charts/scree.c | 80 +- src/output/charts/scree.h | 72 +- src/output/csv.c | 245 +++ src/output/driver-provider.h | 99 + src/output/driver.c | 722 +++++++ src/output/driver.h | 53 + src/output/html.c | 589 +++--- src/output/manager.c | 390 ---- src/output/manager.h | 113 -- src/output/measure.c | 311 +++ src/output/{chart.h => measure.h} | 18 +- src/output/mk-class-boilerplate | 77 + src/output/odt.c | 406 ++-- src/output/options.c | 311 +++ src/output/options.h | 55 + src/output/output-item-provider.h | 35 + src/output/output-item.c | 73 + src/output/output-item.h | 50 + src/output/output.c | 1180 ------------ src/output/output.h | 179 -- src/output/render.c | 1302 +++++++++++++ src/output/render.h | 115 ++ src/output/tab.c | 765 ++++++++ src/output/tab.h | 150 ++ src/output/table-casereader.c | 154 ++ src/output/table-item.c | 92 + src/output/table-item.h | 103 + src/output/table-paste.c | 317 +++ src/output/table-provider.h | 177 ++ src/output/table-select.c | 236 +++ src/output/table-transpose.c | 119 ++ src/output/table.c | 1574 +++------------ src/output/table.h | 303 ++- src/output/text-item.c | 102 + src/output/text-item.h | 133 ++ src/ui/gui/executor.c | 4 +- src/ui/gui/psppire-output-window.c | 202 +- src/ui/gui/psppire-output-window.h | 1 + src/ui/gui/psppire.c | 5 +- src/ui/source-init-opts.c | 1 - src/ui/terminal/main.c | 19 +- src/ui/terminal/msg-ui.c | 20 +- src/ui/terminal/read-line.c | 4 +- src/ui/terminal/terminal-opts.c | 56 +- tests/.gitignore | 1 + tests/atlocal.in | 4 + tests/automake.mk | 55 +- tests/bugs/agg-crash-2.sh | 24 +- tests/bugs/compute-lv.sh | 23 +- tests/bugs/computebug.out | 11 - tests/bugs/computebug.sh | 36 +- tests/bugs/computebug.stat | 14 - tests/bugs/crosstabs-crash.sh | 61 +- tests/bugs/crosstabs-crash2.sh | 57 +- tests/bugs/crosstabs2.sh | 72 +- tests/bugs/examine-missing2.sh | 48 +- tests/bugs/get.sh | 44 +- tests/bugs/keep-all.sh | 16 +- tests/bugs/match-files-scratch.sh | 25 +- tests/bugs/multipass.sh | 24 +- tests/bugs/overwrite-input-file.sh | 47 +- tests/bugs/overwrite-special-file.sh | 1 - tests/bugs/random.sh | 48 +- tests/bugs/recode-copy-bug-1.out | 12 - tests/bugs/recode-copy-bug-1.stat | 17 - tests/bugs/recode-copy-bug-2.out | 12 - tests/bugs/recode-copy-bug-2.stat | 16 - tests/bugs/recode-copy-bug.sh | 81 +- tests/bugs/shbang.sh | 12 +- tests/bugs/t-test-alpha.sh | 56 +- tests/bugs/t-test-alpha3.sh | 44 +- tests/bugs/t-test-paired.sh | 52 +- tests/bugs/t-test-with-temp.sh | 6 +- tests/bugs/temp-freq.sh | 48 +- tests/bugs/temporary.sh | 38 +- tests/command/add-files.sh | 94 +- tests/command/aggregate.sh | 39 +- tests/command/attributes.sh | 69 +- tests/command/autorecod.sh | 60 +- tests/command/beg-data.sh | 58 +- tests/command/correlation.sh | 85 +- tests/command/count.sh | 64 +- tests/command/data-list.sh | 109 +- tests/command/do-if.sh | 1 - tests/command/do-repeat.sh | 10 +- tests/command/examine-extremes.sh | 51 +- tests/command/examine-percentiles.sh | 167 +- tests/command/examine.sh | 281 ++- tests/command/file-handle.sh | 26 +- tests/command/file-label.sh | 64 +- tests/command/filter.sh | 57 +- tests/command/flip.sh | 79 +- tests/command/get-data-gnm.sh | 208 +- tests/command/get-data-psql.sh | 288 ++- tests/command/get-data-txt-examples.sh | 54 +- tests/command/get-data-txt-importcases.sh | 515 ++--- tests/command/get-data-txt.sh | 95 +- tests/command/import-export.sh | 13 +- tests/command/input-program.sh | 187 +- tests/command/insert.sh | 18 +- tests/command/lag.sh | 27 +- tests/command/line-ends.sh | 19 +- tests/command/list.sh | 342 ++-- tests/command/longvars.sh | 69 +- tests/command/loop.sh | 157 +- tests/command/match-files.sh | 112 +- tests/command/n_of_cases.sh | 37 +- tests/command/no_case_size.sh | 71 +- tests/command/npar-binomial.sh | 167 +- tests/command/npar-chisquare.sh | 239 +-- tests/command/npar-sign.sh | 42 +- tests/command/npar-wilcoxon.sh | 39 +- tests/command/oneway-missing.sh | 6 +- tests/command/oneway-with-splits.sh | 183 +- tests/command/oneway.sh | 88 +- tests/command/print.sh | 26 +- tests/command/rank.sh | 396 ++-- tests/command/regression-qr.sh | 52 +- tests/command/regression.sh | 81 +- tests/command/reliability.sh | 127 +- tests/command/rename.sh | 40 +- tests/command/roc.sh | 116 +- tests/command/roc2.sh | 30 +- tests/command/sample.sh | 8 +- tests/command/split-file.sh | 50 +- tests/command/sysfile-info.sh | 59 +- tests/command/sysfiles-old.sh | 23 +- tests/command/sysfiles.sh | 23 +- tests/command/t-test-1-indep-val.sh | 44 +- tests/command/t-test-1-sample-missing-anal.sh | 4 +- tests/command/t-test-1-sample-missing-list.sh | 4 +- tests/command/t-test-1s.sh | 40 +- tests/command/t-test-groups.sh | 58 +- tests/command/t-test-indep-missing-anal.sh | 4 +- tests/command/t-test-indep-missing-list.sh | 4 +- tests/command/t-test-paired-missing-anal.sh | 4 +- tests/command/t-test-paired-missing-list.sh | 4 +- tests/command/t-test-pairs.sh | 54 +- tests/command/tabs.sh | 13 +- tests/command/trimmed-mean.sh | 64 +- tests/command/update.sh | 39 +- tests/command/use.sh | 25 +- tests/command/variable-display.sh | 35 +- tests/command/vector.sh | 96 +- tests/command/very-long-strings.sh | 146 +- tests/command/weight.sh | 173 +- tests/expressions/valuelabel.sh | 23 +- tests/expressions/variables.sh | 29 +- tests/expressions/vectors.sh | 29 +- tests/formats/360.sh | 28 +- tests/output/render-test.c | 284 +++ tests/output/render.at | 1710 +++++++++++++++++ tests/stats/descript-basic.sh | 88 +- tests/stats/descript-mean-bug.sh | 12 +- tests/stats/descript-missing.sh | 74 +- tests/stats/ntiles.sh | 4 +- tests/stats/percentiles-compatible.sh | 48 +- tests/stats/percentiles-enhanced.sh | 190 +- tests/testsuite.at | 5 + tests/xforms/recode.sh | 191 +- 232 files changed, 15953 insertions(+), 11909 deletions(-) rename src/output/{charts/plot-chart.c => cairo-chart.c} (60%) create mode 100644 src/output/cairo-chart.h rename src/output/{htmlP.h => chart-item-provider.h} (59%) create mode 100644 src/output/chart-item.c create mode 100644 src/output/chart-item.h delete mode 100644 src/output/chart-provider.h delete mode 100644 src/output/chart.c create mode 100644 src/output/charts/boxplot-cairo.c delete mode 100644 src/output/charts/cartesian.c delete mode 100644 src/output/charts/cartesian.h create mode 100644 src/output/charts/np-plot-cairo.c create mode 100644 src/output/charts/piechart-cairo.c delete mode 100644 src/output/charts/plot-chart.h create mode 100644 src/output/charts/plot-hist-cairo.c create mode 100644 src/output/charts/roc-chart-cairo.c create mode 100644 src/output/charts/scree-cairo.c create mode 100644 src/output/csv.c create mode 100644 src/output/driver-provider.h create mode 100644 src/output/driver.c create mode 100644 src/output/driver.h delete mode 100644 src/output/manager.c delete mode 100644 src/output/manager.h create mode 100644 src/output/measure.c rename src/output/{chart.h => measure.h} (70%) create mode 100755 src/output/mk-class-boilerplate create mode 100644 src/output/options.c create mode 100644 src/output/options.h create mode 100644 src/output/output-item-provider.h create mode 100644 src/output/output-item.c create mode 100644 src/output/output-item.h delete mode 100644 src/output/output.c delete mode 100644 src/output/output.h create mode 100644 src/output/render.c create mode 100644 src/output/render.h create mode 100644 src/output/tab.c create mode 100644 src/output/tab.h create mode 100644 src/output/table-casereader.c create mode 100644 src/output/table-item.c create mode 100644 src/output/table-item.h create mode 100644 src/output/table-paste.c create mode 100644 src/output/table-provider.h create mode 100644 src/output/table-select.c create mode 100644 src/output/table-transpose.c create mode 100644 src/output/text-item.c create mode 100644 src/output/text-item.h create mode 100644 tests/atlocal.in delete mode 100644 tests/bugs/computebug.out delete mode 100644 tests/bugs/computebug.stat delete mode 100644 tests/bugs/recode-copy-bug-1.out delete mode 100644 tests/bugs/recode-copy-bug-1.stat delete mode 100644 tests/bugs/recode-copy-bug-2.out delete mode 100644 tests/bugs/recode-copy-bug-2.stat create mode 100644 tests/output/render-test.c create mode 100644 tests/output/render.at create mode 100644 tests/testsuite.at diff --git a/.gitignore b/.gitignore index ad89bf36..56d869b2 100644 --- a/.gitignore +++ b/.gitignore @@ -39,3 +39,4 @@ gitlog-to-changelog *.deps *.la *.libs +/package.m4 diff --git a/Makefile.am b/Makefile.am index 02ae7d5b..0022e1d7 100644 --- a/Makefile.am +++ b/Makefile.am @@ -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 ab21afde..f1a0ee4e 100644 --- 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 02e81d77..e54877a1 100644 --- 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 diff --git a/configure.ac b/configure.ac index fd60137c..ea9f69fd 100644 --- a/configure.ac +++ b/configure.ac @@ -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." diff --git a/doc/introduction.texi b/doc/introduction.texi index 735a54c5..f96a2329 100644 --- a/doc/introduction.texi +++ b/doc/introduction.texi @@ -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. diff --git a/perl-module/automake.mk b/perl-module/automake.mk index ef9acc37..8eeb0a34 100644 --- a/perl-module/automake.mk +++ b/perl-module/automake.mk @@ -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 \ diff --git a/perl-module/t/Pspp.t b/perl-module/t/Pspp.t index 92180aba..4f65a4b5 100644 --- a/perl-module/t/Pspp.t +++ b/perl-module/t/Pspp.t @@ -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, < #include #include +#include #include #include diff --git a/src/language/command.c b/src/language/command.c index 4c3612fb..803b5398 100644 --- a/src/language/command.c +++ b/src/language/command.c @@ -23,6 +23,12 @@ #include #include #include +#if HAVE_SYS_WAIT_H +#include +#endif +#if HAVE_READLINE +#include +#endif #include #include @@ -36,16 +42,8 @@ #include #include #include -#include #include - -#if HAVE_SYS_WAIT_H -#include -#endif - -#if HAVE_READLINE -#include -#endif +#include #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; } diff --git a/src/language/data-io/data-parser.c b/src/language/data-io/data-parser.c index fe78aeb3..eb578e27 100644 --- a/src/language/data-io/data-parser.c +++ b/src/language/data-io/data-parser.c @@ -31,7 +31,7 @@ #include #include #include -#include +#include #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++) { diff --git a/src/language/data-io/list.q b/src/language/data-io/list.q index 871fa69e..60f51b52 100644 --- a/src/language/data-io/list.q +++ b/src/language/data-io/list.q @@ -29,7 +29,7 @@ #include #include #include -#include +#include #include #include #include @@ -38,10 +38,8 @@ #include #include #include -#include -#include -#include -#include +#include +#include #include "minmax.h" #include "xalloc.h" @@ -61,85 +59,18 @@ /* (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 ("\n \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, " \n", - var_get_name (cmd.v_variables[i])); - } - - fputs (" \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 ("
%s
\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 (" \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 (" ", x->file); - html_put_cell_contents (d, TAB_FIX, ss_cstr (s)); - fputs ("\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 (" \n", x->file); - } - else - NOT_REACHED (); - } + return ok ? CMD_SUCCESS : CMD_CASCADING_FAILURE; } - /* Local Variables: mode: c diff --git a/src/language/data-io/print-space.c b/src/language/data-io/print-space.c index 3f73ee25..25bcc750 100644 --- a/src/language/data-io/print-space.c +++ b/src/language/data-io/print-space.c @@ -26,8 +26,8 @@ #include #include #include -#include #include +#include #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); diff --git a/src/language/data-io/print.c b/src/language/data-io/print.c index 9c8f56e8..fd98eaed 100644 --- a/src/language/data-io/print.c +++ b/src/language/data-io/print.c @@ -38,8 +38,8 @@ #include #include #include -#include -#include +#include +#include #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) diff --git a/src/language/dictionary/split-file.c b/src/language/dictionary/split-file.c index d2f59ccc..d27ab3a6 100644 --- a/src/language/dictionary/split-file.c +++ b/src/language/dictionary/split-file.c @@ -31,8 +31,7 @@ #include #include #include -#include -#include +#include #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); } diff --git a/src/language/dictionary/sys-file-info.c b/src/language/dictionary/sys-file-info.c index 6c44a0c3..1f47e1af 100644 --- a/src/language/dictionary/sys-file-info.c +++ b/src/language/dictionary/sys-file-info.c @@ -39,9 +39,7 @@ #include #include #include -#include -#include -#include +#include #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); } @@ -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++) diff --git a/src/language/lexer/lexer.c b/src/language/lexer/lexer.c index 8b3f2a48..5ff0b8bc 100644 --- a/src/language/lexer/lexer.c +++ b/src/language/lexer/lexer.c @@ -32,6 +32,7 @@ #include #include #include +#include #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; } diff --git a/src/language/prompt.c b/src/language/prompt.c index 5ecd42fb..34690d50 100644 --- a/src/language/prompt.c +++ b/src/language/prompt.c @@ -33,7 +33,7 @@ #include #include #include -#include +#include #include "xalloc.h" diff --git a/src/language/stats/binomial.c b/src/language/stats/binomial.c index bc87a94d..3c892580 100644 --- a/src/language/stats/binomial.c +++ b/src/language/stats/binomial.c @@ -16,7 +16,7 @@ #include #include -#include +#include #include #include @@ -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); diff --git a/src/language/stats/chisquare.c b/src/language/stats/chisquare.c index bc1b6474..1f2df8d2 100644 --- a/src/language/stats/chisquare.c +++ b/src/language/stats/chisquare.c @@ -36,7 +36,7 @@ #include #include #include -#include +#include #include @@ -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); diff --git a/src/language/stats/correlations.c b/src/language/stats/correlations.c index c575205c..7fcca77c 100644 --- a/src/language/stats/correlations.c +++ b/src/language/stats/correlations.c @@ -30,8 +30,7 @@ #include #include #include -#include -#include +#include #include #include #include @@ -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); diff --git a/src/language/stats/crosstabs.q b/src/language/stats/crosstabs.q index 5695ed64..bb4acafa 100644 --- a/src/language/stats/crosstabs.q +++ b/src/language/stats/crosstabs.q @@ -56,8 +56,7 @@ #include #include #include -#include -#include +#include #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); } /* 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) { diff --git a/src/language/stats/descriptives.c b/src/language/stats/descriptives.c index e78d771e..eb04bfa6 100644 --- a/src/language/stats/descriptives.c +++ b/src/language/stats/descriptives.c @@ -35,8 +35,7 @@ #include #include #include -#include -#include +#include #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")); diff --git a/src/language/stats/examine.q b/src/language/stats/examine.q index 4ca3af96..d06f98ef 100644 --- a/src/language/stats/examine.q +++ b/src/language/stats/examine.q @@ -48,10 +48,10 @@ #include #include #include +#include #include #include -#include -#include +#include #include "minmax.h" #include "xalloc.h" @@ -61,9 +61,7 @@ #define N_(msgid) msgid /* (headers) */ -#include #include -#include #include /* (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, diff --git a/src/language/stats/factor.c b/src/language/stats/factor.c index 2dbf3b69..29e30c16 100644 --- a/src/language/stats/factor.c +++ b/src/language/stats/factor.c @@ -44,10 +44,10 @@ #include #include -#include +#include #include -#include +#include #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); diff --git a/src/language/stats/frequencies.q b/src/language/stats/frequencies.q index e1d6fb0e..e52aaf0f 100644 --- a/src/language/stats/frequencies.q +++ b/src/language/stats/frequencies.q @@ -48,12 +48,10 @@ #include #include #include -#include +#include #include #include -#include -#include -#include +#include #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_) /* 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); } @@ -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); diff --git a/src/language/stats/glm.q b/src/language/stats/glm.q index fd360416..0804945f 100644 --- a/src/language/stats/glm.q +++ b/src/language/stats/glm.q @@ -43,7 +43,7 @@ #include #include #include -#include +#include #include "xalloc.h" #include "gettext.h" diff --git a/src/language/stats/npar-summary.c b/src/language/stats/npar-summary.c index 8e6f11a8..05fa1d02 100644 --- a/src/language/stats/npar-summary.c +++ b/src/language/stats/npar-summary.c @@ -17,7 +17,7 @@ #include #include -#include +#include #include #include #include @@ -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); } diff --git a/src/language/stats/oneway.q b/src/language/stats/oneway.q index ddb84d89..2d55edff 100644 --- a/src/language/stats/oneway.q +++ b/src/language/stats/oneway.q @@ -40,8 +40,7 @@ #include #include #include -#include -#include +#include #include "sort-criteria.h" #include @@ -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, diff --git a/src/language/stats/rank.q b/src/language/stats/rank.q index c225370e..ec3052c0 100644 --- a/src/language/stats/rank.q +++ b/src/language/stats/rank.q @@ -35,8 +35,7 @@ #include #include #include -#include -#include +#include #include @@ -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 ) { diff --git a/src/language/stats/regression.q b/src/language/stats/regression.q index b61ae586..2487b902 100644 --- a/src/language/stats/regression.q +++ b/src/language/stats/regression.q @@ -43,7 +43,7 @@ #include #include #include -#include +#include #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); diff --git a/src/language/stats/reliability.q b/src/language/stats/reliability.q index dfb81367..478f5c5b 100644 --- a/src/language/stats/reliability.q +++ b/src/language/stats/reliability.q @@ -27,8 +27,8 @@ #include #include #include -#include -#include +#include +#include #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 */ diff --git a/src/language/stats/roc.c b/src/language/stats/roc.c index 1f2691d8..724bc523 100644 --- a/src/language/stats/roc.c +++ b/src/language/stats/roc.c @@ -18,28 +18,24 @@ #include -#include -#include -#include -#include -#include - #include #include #include #include #include -#include +#include #include - - +#include +#include +#include +#include #include +#include +#include +#include +#include #include -#include - -#include -#include #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); diff --git a/src/language/stats/sign.c b/src/language/stats/sign.c index d5970c6f..754b0d7d 100644 --- a/src/language/stats/sign.c +++ b/src/language/stats/sign.c @@ -19,7 +19,7 @@ #include #include -#include +#include #include #include #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); diff --git a/src/language/stats/t-test.q b/src/language/stats/t-test.q index a483bd9e..8aee3b15 100644 --- a/src/language/stats/t-test.q +++ b/src/language/stats/t-test.q @@ -44,8 +44,7 @@ #include #include #include -#include -#include +#include #include #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); } /* 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 */ diff --git a/src/language/stats/wilcoxon.c b/src/language/stats/wilcoxon.c index c7e30277..e1dab91c 100644 --- a/src/language/stats/wilcoxon.c +++ b/src/language/stats/wilcoxon.c @@ -38,7 +38,7 @@ #include #include #include -#include +#include #include #include @@ -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); diff --git a/src/language/syntax-file.c b/src/language/syntax-file.c index 9b5fd043..9470e508 100644 --- a/src/language/syntax-file.c +++ b/src/language/syntax-file.c @@ -35,7 +35,7 @@ #include #include #include -#include +#include #include diff --git a/src/language/tests/paper-size.c b/src/language/tests/paper-size.c index d8c49a59..5aea2c33 100644 --- a/src/language/tests/paper-size.c +++ b/src/language/tests/paper-size.c @@ -21,19 +21,22 @@ #include #include #include -#include +#include +#include /* 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)); diff --git a/src/language/utilities/echo.c b/src/language/utilities/echo.c index 3d3c2c40..ebd6a990 100644 --- a/src/language/utilities/echo.c +++ b/src/language/utilities/echo.c @@ -19,8 +19,7 @@ #include #include #include -#include -#include +#include #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); diff --git a/src/language/utilities/include.c b/src/language/utilities/include.c index fe98aeb8..ceb78771 100644 --- a/src/language/utilities/include.c +++ b/src/language/utilities/include.c @@ -15,17 +15,20 @@ along with this program. If not, see . */ #include + #include #include #include #include +#include + +#include #include -#include -#include -#include #include +#include +#include +#include #include -#include #include "dirname.h" #include "xalloc.h" diff --git a/src/language/utilities/set.q b/src/language/utilities/set.q index 5c2b571e..46d5cd8d 100644 --- a/src/language/utilities/set.q +++ b/src/language/utilities/set.q @@ -40,8 +40,8 @@ #include #include #include +#include #include -#include #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; } diff --git a/src/language/utilities/title.c b/src/language/utilities/title.c index 1bdb0e9a..fe826db1 100644 --- a/src/language/utilities/title.c +++ b/src/language/utilities/title.c @@ -27,29 +27,30 @@ #include #include #include -#include +#include #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) diff --git a/src/output/ascii.c b/src/output/ascii.c index b224b3eb..4978e125 100644 --- a/src/output/ascii.c +++ b/src/output/ascii.c @@ -26,12 +26,17 @@ #include #include #include -#include #include +#include #include -#include -#include -#include +#include +#include +#include "output/options.h" +#include +#include +#include +#include +#include #include "error.h" #include "minmax.h" @@ -40,31 +45,9 @@ #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 (¶ms, 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, + }; + +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]; } + +/* 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; } - -/* 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, -}; diff --git a/src/output/automake.mk b/src/output/automake.mk index b89dc05c..1d9f6586 100644 --- a/src/output/automake.mk +++ b/src/output/automake.mk @@ -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/charts/plot-chart.c b/src/output/cairo-chart.c similarity index 60% rename from src/output/charts/plot-chart.c rename to src/output/cairo-chart.c index cda6d1ec..b5e17e0f 100644 --- a/src/output/charts/plot-chart.c +++ b/src/output/cairo-chart.c @@ -16,28 +16,72 @@ #include -#include +#include "output/cairo-chart.h" #include -#include -#include -#include -#include +#include #include #include +#include +#include +#include #include #include -#include +#include #include -#include -#include -#include -#include -#include -#include +#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); @@ -59,7 +103,7 @@ pango_layout_get_baseline (PangoLayout *layout) -const struct chart_colour data_colour[N_CHART_COLOURS] = +const struct xrchart_colour data_colour[XRCHART_N_COLOURS] = { { 165, 42, 42 }, /* brown */ { 255, 0, 0 }, /* red */ @@ -73,8 +117,8 @@ const struct chart_colour data_colour[N_CHART_COLOURS] = }; void -chart_draw_marker (cairo_t *cr, double x, double y, enum marker_type marker, - double size) +xrchart_draw_marker (cairo_t *cr, double x, double y, + enum xrmarker_type marker, double size) { cairo_save (cr); cairo_translate (cr, x, y); @@ -82,12 +126,12 @@ chart_draw_marker (cairo_t *cr, double x, double y, enum marker_type marker, cairo_set_line_width (cr, cairo_get_line_width (cr) / (size / 2.0)); switch (marker) { - case MARKER_CIRCLE: + case XRMARKER_CIRCLE: cairo_arc (cr, 0, 0, 1.0, 0, 2 * M_PI); cairo_stroke (cr); break; - case MARKER_ASTERISK: + 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); /* / */ @@ -97,7 +141,7 @@ chart_draw_marker (cairo_t *cr, double x, double y, enum marker_type marker, cairo_stroke (cr); break; - case MARKER_SQUARE: + case XRMARKER_SQUARE: cairo_rectangle (cr, -1.0, -1.0, 2.0, 2.0); cairo_stroke (cr); break; @@ -106,8 +150,8 @@ chart_draw_marker (cairo_t *cr, double x, double y, enum marker_type marker, } void -chart_label (cairo_t *cr, int horz_justify, int vert_justify, double font_size, - const char *string) +xrchart_label (cairo_t *cr, int horz_justify, int vert_justify, + double font_size, const char *string) { PangoFontDescription *desc; PangoLayout *layout; @@ -174,7 +218,7 @@ chart_label (cairo_t *cr, int horz_justify, int vert_justify, double font_size, If label is non zero, then print it at the tick mark */ void -draw_tick (cairo_t *cr, const struct chart_geometry *geom, +draw_tick (cairo_t *cr, const struct xrchart_geometry *geom, enum tick_orientation orientation, double position, const char *label, ...) @@ -210,12 +254,12 @@ draw_tick (cairo_t *cr, const struct chart_geometry *geom, va_start (ap, label); s = xvasprintf (label, ap); if (orientation == TICK_ABSCISSA) - chart_label (cr, 'c', 't', geom->font_size, s); + 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); - chart_label (cr, 'r', 'c', geom->font_size, s); + xrchart_label (cr, 'r', 'c', geom->font_size, s); } free (s); va_end (ap); @@ -225,7 +269,7 @@ draw_tick (cairo_t *cr, const struct chart_geometry *geom, /* Write the title on a chart*/ void -chart_write_title (cairo_t *cr, const struct chart_geometry *geom, +xrchart_write_title (cairo_t *cr, const struct xrchart_geometry *geom, const char *title, ...) { va_list ap; @@ -236,7 +280,7 @@ chart_write_title (cairo_t *cr, const struct chart_geometry *geom, va_start(ap, title); s = xvasprintf (title, ap); - chart_label (cr, 'l', 'x', geom->font_size * 1.5, s); + xrchart_label (cr, 'l', 'x', geom->font_size * 1.5, s); free (s); va_end (ap); @@ -246,7 +290,7 @@ chart_write_title (cairo_t *cr, const struct chart_geometry *geom, /* Set the scale for the abscissa */ void -chart_write_xscale (cairo_t *cr, struct chart_geometry *geom, +xrchart_write_xscale (cairo_t *cr, struct xrchart_geometry *geom, double min, double max, int ticks) { double x; @@ -267,7 +311,7 @@ chart_write_xscale (cairo_t *cr, struct chart_geometry *geom, /* Set the scale for the ordinate */ void -chart_write_yscale (cairo_t *cr, struct chart_geometry *geom, +xrchart_write_yscale (cairo_t *cr, struct xrchart_geometry *geom, double smin, double smax, int ticks) { double y; @@ -289,29 +333,29 @@ chart_write_yscale (cairo_t *cr, struct chart_geometry *geom, /* Write the abscissa label */ void -chart_write_xlabel (cairo_t *cr, const struct chart_geometry *geom, +xrchart_write_xlabel (cairo_t *cr, const struct xrchart_geometry *geom, const char *label) { cairo_move_to (cr, geom->data_left, geom->abscissa_top); - chart_label (cr, 'l', 't', geom->font_size, label); + xrchart_label (cr, 'l', 't', geom->font_size, label); } /* Write the ordinate label */ void -chart_write_ylabel (cairo_t *cr, const struct chart_geometry *geom, +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); - chart_label (cr, 'l', 'x', geom->font_size, label); + xrchart_label (cr, 'l', 'x', geom->font_size, label); cairo_restore (cr); } void -chart_write_legend (cairo_t *cr, const struct chart_geometry *geom) +xrchart_write_legend (cairo_t *cr, const struct xrchart_geometry *geom) { int i; const int vstep = geom->font_size * 2; @@ -333,12 +377,12 @@ chart_write_legend (cairo_t *cr, const struct chart_geometry *geom) { const int ypos = legend_top - vstep * (i + 1); const int xpos = geom->legend_left + xpad; - const struct chart_colour *colour; + const struct xrchart_colour *colour; cairo_move_to (cr, xpos, ypos); cairo_save (cr); - colour = &data_colour [ i % N_CHART_COLOURS]; + colour = &data_colour [ i % XRCHART_N_COLOURS]; cairo_set_source_rgb (cr, colour->red / 255.0, colour->green / 255.0, @@ -349,8 +393,107 @@ chart_write_legend (cairo_t *cr, const struct chart_geometry *geom) cairo_restore (cr); cairo_move_to (cr, xpos + swatch * 1.5, ypos); - chart_label (cr, 'l', 'x', geom->font_size, geom->dataset[i]); + 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 index 00000000..041a1691 --- /dev/null +++ b/src/output/cairo-chart.h @@ -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 . */ + +#ifndef OUTPUT_CAIRO_CHART_H +#define OUTPUT_CAIRO_CHART_H 1 + +#include +#include +#include +#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 */ diff --git a/src/output/cairo.c b/src/output/cairo.c index 162009c4..61287803 100644 --- a/src/output/cairo.c +++ b/src/output/cairo.c @@ -19,11 +19,26 @@ #include #include +#include #include +#include +#include #include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include @@ -43,28 +58,9 @@ #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]); /* 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; -} - -/* 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; } -/* 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); +} + /* 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); } -/* 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); -} /* 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, +}; + +/* 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; +} diff --git a/src/output/cairo.h b/src/output/cairo.h index c4f8a813..80b2b87e 100644 --- a/src/output/cairo.h +++ b/src/output/cairo.h @@ -19,6 +19,21 @@ #include -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/htmlP.h b/src/output/chart-item-provider.h similarity index 59% rename from src/output/htmlP.h rename to src/output/chart-item-provider.h index 05340f4e..4f13fcfe 100644 --- a/src/output/htmlP.h +++ b/src/output/chart-item-provider.h @@ -1,5 +1,5 @@ /* PSPP - a program for statistical analysis. - Copyright (C) 1997-9, 2000 Free Software Foundation, Inc. + 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 @@ -14,25 +14,18 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ -#if !htmlP_h -#define htmlP_h 1 +#ifndef OUTPUT_CHART_ITEM_PROVIDER_H +#define OUTPUT_CHART_ITEM_PROVIDER_H 1 -#include +#include +#include -/* HTML output driver extension record. */ -struct html_driver_ext +struct chart_item_class { - char *file_name; - char *chart_file_name; - FILE *file; - - size_t chart_cnt; + void (*destroy) (struct chart_item *); }; -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); +void chart_item_init (struct chart_item *, const struct chart_item_class *, + const char *title); -#endif /* !htmlP_h */ +#endif /* output/chart-provider.h */ diff --git a/src/output/chart-item.c b/src/output/chart-item.c new file mode 100644 index 00000000..227994dd --- /dev/null +++ b/src/output/chart-item.c @@ -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 . */ + +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include + +#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); +} + +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 index 00000000..c005a46c --- /dev/null +++ b/src/output/chart-item.h @@ -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 . */ + +#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 +#include + +/* 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 *); + +/* This boilerplate for chart_item, a subclass of output_item, was + autogenerated by mk-class-boilerplate. */ + +#include +#include + +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 *); + +#endif /* output/chart-item.h */ diff --git a/src/output/chart-provider.h b/src/output/chart-provider.h deleted file mode 100644 index 9becb6f5..00000000 --- a/src/output/chart-provider.h +++ /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 . */ - -#ifndef OUTPUT_CHART_PROVIDER_H -#define OUTPUT_CHART_PROVIDER_H 1 - -#include -#include -#include -#include - -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 index e31422bc..00000000 --- a/src/output/chart.c +++ /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 . */ - -#include - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#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/charts/boxplot-cairo.c b/src/output/charts/boxplot-cairo.c new file mode 100644 index 00000000..ddecbfcc --- /dev/null +++ b/src/output/charts/boxplot-cairo.c @@ -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 . */ + +#include + +#include + +#include + +#include +#include +#include + +/* 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); + } +} diff --git a/src/output/charts/boxplot.c b/src/output/charts/boxplot.c index 015385e7..7e924800 100644 --- a/src/output/charts/boxplot.c +++ b/src/output/charts/boxplot.c @@ -19,47 +19,16 @@ #include -#include -#include -#include - -#include -#include -#include #include -#include -#include -#include - -/* 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 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 }; diff --git a/src/output/charts/boxplot.h b/src/output/charts/boxplot.h index 1e9d0070..bd5a7a32 100644 --- a/src/output/charts/boxplot.h +++ b/src/output/charts/boxplot.h @@ -17,11 +17,92 @@ #ifndef OUTPUT_CHARTS_BOXPLOT_H #define OUTPUT_CHARTS_BOXPLOT_H 1 -struct box_whisker; +#include +#include + +/* 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 *); + +/* This boilerplate for boxplot, a subclass of chart_item, was + autogenerated by mk-class-boilerplate. */ + +#include +#include + +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); +} + #endif /* output/charts/boxplot.h */ diff --git a/src/output/charts/cartesian.c b/src/output/charts/cartesian.c deleted file mode 100644 index eabcf516..00000000 --- a/src/output/charts/cartesian.c +++ /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 . */ - - -#include - -#include - -#include -#include -#include - -#include -#include -#include -#include - -#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 index 3c21db6e..00000000 --- a/src/output/charts/cartesian.h +++ /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 . */ - - - -#ifndef CARTESIAN_H -#define CARTESIAN_H - -#include -#include -#include - -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 index 00000000..a9186527 --- /dev/null +++ b/src/output/charts/np-plot-cairo.c @@ -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 . */ + +#include + +#include + +#include +#include +#include +#include + +#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); +} diff --git a/src/output/charts/np-plot.c b/src/output/charts/np-plot.c index c077b872..e912479a 100644 --- a/src/output/charts/np-plot.c +++ b/src/output/charts/np-plot.c @@ -21,44 +21,15 @@ #include #include -#include #include -#include #include -#include -#include -#include +#include #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 }; diff --git a/src/output/charts/np-plot.h b/src/output/charts/np-plot.h index c9742359..82194e20 100644 --- a/src/output/charts/np-plot.h +++ b/src/output/charts/np-plot.h @@ -17,12 +17,94 @@ #ifndef OUTPUT_CHARTS_NP_PLOT_H #define OUTPUT_CHARTS_NP_PLOT_H 1 -struct casereader; +#include + 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); + +/* This boilerplate for np_plot_chart, a subclass of chart_item, was + autogenerated by mk-class-boilerplate. */ + +#include +#include + +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); +} + #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 index 00000000..501c0bd4 --- /dev/null +++ b/src/output/charts/piechart-cairo.c @@ -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 . */ + +#include + +#include + +#include + +#include + +#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); +} + diff --git a/src/output/charts/piechart.c b/src/output/charts/piechart.c index 935c6eb0..0b9153d5 100644 --- a/src/output/charts/piechart.c +++ b/src/output/charts/piechart.c @@ -14,56 +14,28 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ - #include #include -#include -#include -#include -#include -#include +#include -#include #include #include -#include -#include - -#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 +#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 }; diff --git a/src/output/charts/piechart.h b/src/output/charts/piechart.h index 39a0c2d5..312a45ee 100644 --- a/src/output/charts/piechart.h +++ b/src/output/charts/piechart.h @@ -18,14 +18,82 @@ #define PIECHART_H #include +#include -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); + +/* This boilerplate for piechart, a subclass of chart_item, was + autogenerated by mk-class-boilerplate. */ +#include +#include + +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); +} + +#endif /* output/charts/piechart.h */ diff --git a/src/output/charts/plot-chart.h b/src/output/charts/plot-chart.h deleted file mode 100644 index 896b630b..00000000 --- a/src/output/charts/plot-chart.h +++ /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 . */ - -#ifndef PLOT_CHART_H -#define PLOT_CHART_H - -#include -#include -#include -#include -#include -#include -#include -#include - - -#include -#include -#include - -#include -#include -#include -#include - -#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 index 00000000..a70fa174 --- /dev/null +++ b/src/output/charts/plot-hist-cairo.c @@ -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 . */ + +#include + +#include + +#include + +#include +#include + +#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, ¬_used); + range = not_used - x_min; + gsl_histogram_get_range (h->gsl_hist, bins - 1, ¬_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); + } +} diff --git a/src/output/charts/plot-hist.c b/src/output/charts/plot-hist.c index 334818b4..5925df6d 100644 --- a/src/output/charts/plot-hist.c +++ b/src/output/charts/plot-hist.c @@ -19,205 +19,51 @@ #include #include -#include #include #include #include -#include -#include +#include -#include #include -#include -#include #include #include #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, ¬_used); - range = not_used - x_min; - gsl_histogram_get_range (h->gsl_hist, bins - 1, ¬_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 }; diff --git a/src/output/charts/plot-hist.h b/src/output/charts/plot-hist.h index 1e5b59ed..237b8b3c 100644 --- a/src/output/charts/plot-hist.h +++ b/src/output/charts/plot-hist.h @@ -17,19 +17,89 @@ #ifndef OUTPUT_PLOT_HIST_H #define OUTPUT_PLOT_HIST_H +#include #include -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); + +/* This boilerplate for histogram_chart, a subclass of chart_item, was + autogenerated by mk-class-boilerplate. */ + +#include +#include + +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); +} + #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 index 00000000..b5c22aca --- /dev/null +++ b/src/output/charts/roc-chart-cairo.c @@ -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 . */ + +#include + +#include + +#include +#include +#include +#include + +#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); +} + diff --git a/src/output/charts/roc-chart.c b/src/output/charts/roc-chart.c index 2094ede5..5af8f27f 100644 --- a/src/output/charts/roc-chart.c +++ b/src/output/charts/roc-chart.c @@ -18,9 +18,7 @@ #include -#include -#include -#include +#include #include #include @@ -29,28 +27,11 @@ #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 }; - - diff --git a/src/output/charts/roc-chart.h b/src/output/charts/roc-chart.h index dca84207..a995f930 100644 --- a/src/output/charts/roc-chart.h +++ b/src/output/charts/roc-chart.h @@ -18,12 +18,86 @@ #define OUTPUT_CHARTS_ROC_CHART_H 1 #include +#include +#include -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 *); + +/* This boilerplate for roc_chart, a subclass of chart_item, was + autogenerated by mk-class-boilerplate. */ + +#include +#include + +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); +} + #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 index 00000000..d48a3642 --- /dev/null +++ b/src/output/charts/scree-cairo.c @@ -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 . */ + +#include + +#include + +#include + +#include + +#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); + } +} diff --git a/src/output/charts/scree.c b/src/output/charts/scree.c index 44f7c7a6..ffa77568 100644 --- a/src/output/charts/scree.c +++ b/src/output/charts/scree.c @@ -18,94 +18,38 @@ #include -#include -#include -#include +#include -#include - -#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 }; - - diff --git a/src/output/charts/scree.h b/src/output/charts/scree.h index 93244998..43f5b822 100644 --- a/src/output/charts/scree.h +++ b/src/output/charts/scree.h @@ -18,14 +18,80 @@ #define OUTPUT_CHARTS_SCREE_H 1 #include +#include -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); + +/* This boilerplate for scree, a subclass of chart_item, was + autogenerated by mk-class-boilerplate. */ -#endif +#include +#include + +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); +} + +#endif /* output/charts/scree.h */ diff --git a/src/output/csv.c b/src/output/csv.c new file mode 100644 index 00000000..158c7895 --- /dev/null +++ b/src/output/csv.c @@ -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 . */ + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 index 00000000..92929b54 --- /dev/null +++ b/src/output/driver-provider.h @@ -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 . */ + +#ifndef OUTPUT_DRIVER_PROVIDER_H +#define OUTPUT_DRIVER_PROVIDER_H 1 + +#include +#include +#include + +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 index 00000000..6dd25174 --- /dev/null +++ b/src/output/driver.c @@ -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 . */ + +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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, ¯os, &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 (¯os, 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], ¯os)) + 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, ¯os, &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 (¯os); + + 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; +} + +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; +} + +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; +} + +/* 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 index 00000000..3fb4cabf --- /dev/null +++ b/src/output/driver.h @@ -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 . */ + +#ifndef OUTPUT_DRIVER_H +#define OUTPUT_DRIVER_H 1 + +#include + +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 */ diff --git a/src/output/html.c b/src/output/html.c index dfa524a7..57d77a76 100644 --- a/src/output/html.c +++ b/src/output/html.c @@ -15,8 +15,7 @@ along with this program. If not, see . */ #include -#include "chart.h" -#include "htmlP.h" + #include #include #include @@ -24,14 +23,19 @@ #include #include +#include #include +#include #include -#include -#include -#include -#include -#include #include +#include +#include +#include +#include +#include +#include +#include +#include #include "error.h" #include "xalloc.h" @@ -39,66 +43,136 @@ #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 ("\n", x->file); - fputs ("\n", x->file); - fputs ("\n", x->file); - /* The 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 (""", 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), " "); - 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), " "); + 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/manager.c b/src/output/manager.c deleted file mode 100644 index 9b901393..00000000 --- a/src/output/manager.c +++ /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; - -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; -} - -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 index e7276982..00000000 --- a/src/output/manager.h +++ /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 index 00000000..8f6e666f --- /dev/null +++ b/src/output/measure.c @@ -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; +} + +/* 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/chart.h b/src/output/measure.h similarity index 70% rename from src/output/chart.h rename to src/output/measure.h index d6b9b3d4..4b6fab38 100644 --- a/src/output/chart.h +++ b/src/output/measure.h @@ -1,5 +1,5 @@ /* PSPP - a program for statistical analysis. - Copyright (C) 2009 Free Software Foundation, Inc. + 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 @@ -14,16 +14,12 @@ 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 +#ifndef OUTPUT_MEASURE_H +#define OUTPUT_MEASURE_H 1 -#include <cairo/cairo.h> +#include <stdbool.h> -struct chart; +int measure_dimension (const char *dimen); +bool measure_paper (const char *size, int *h, int *v); -struct chart *chart_ref (const struct chart *); -void chart_unref (struct chart *); - -void chart_submit (struct chart *); - -#endif /* output/chart.h */ +#endif /* output/measure.h */ diff --git a/src/output/mk-class-boilerplate b/src/output/mk-class-boilerplate new file mode 100755 index 00000000..b2191aae --- /dev/null +++ b/src/output/mk-class-boilerplate @@ -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 / /; + } + 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; +} diff --git a/src/output/odt.c b/src/output/odt.c index 8471b6b3..47b8c43e 100644 --- a/src/output/odt.c +++ b/src/output/odt.c @@ -16,45 +16,42 @@ #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 index 00000000..918f718c --- /dev/null +++ b/src/output/options.c @@ -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 index 00000000..3f9e7056 --- /dev/null +++ b/src/output/options.h @@ -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 index 00000000..a810f97c --- /dev/null +++ b/src/output/output-item-provider.h @@ -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 index 00000000..e1c889ff --- /dev/null +++ b/src/output/output-item.c @@ -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" + +/* 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; +} + +/* 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 index 00000000..57ced303 --- /dev/null +++ b/src/output/output-item.h @@ -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 index 2f4788f9..00000000 --- a/src/output/output.c +++ /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 index 398781f0..00000000 --- a/src/output/output.h +++ /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 index 00000000..05db149a --- /dev/null +++ b/src/output/render.c @@ -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 + +/* 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; +} + +/* 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; +} + +/* 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; +} + +/* 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; +} + + +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]; +} + +/* 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); + } +} + +/* 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; +} + +/* 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 index 00000000..cfba52e9 --- /dev/null +++ b/src/output/render.h @@ -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]; + }; + +/* 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 *); + +/* 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 index 00000000..058cb974 --- /dev/null +++ b/src/output/tab.c @@ -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) + +/* 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); +} + +/* 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; + } + } +} + +/* 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; +} + +/* 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)); +} + +/* 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); +} + +/* 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); +} + +/* 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 index 00000000..db38b6b8 --- /dev/null +++ b/src/output/tab.h @@ -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 index 00000000..909a2b5f --- /dev/null +++ b/src/output/table-casereader.c @@ -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 index 00000000..b709273f --- /dev/null +++ b/src/output/table-item.c @@ -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); +} + +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 index 00000000..01e9899b --- /dev/null +++ b/src/output/table-item.h @@ -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 *); + +/* 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 *); + +#endif /* output/table-item.h */ diff --git a/src/output/table-paste.c b/src/output/table-paste.c new file mode 100644 index 00000000..102b5140 --- /dev/null +++ b/src/output/table-paste.c @@ -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 index 00000000..6d1a4156 --- /dev/null +++ b/src/output/table-provider.h @@ -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; +} + +/* 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); + +/* 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 index 00000000..1c2956a8 --- /dev/null +++ b/src/output/table-select.c @@ -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 index 00000000..59801942 --- /dev/null +++ b/src/output/table-transpose.c @@ -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 */ + }; diff --git a/src/output/table.c b/src/output/table.c index f8736e62..696931ae 100644 --- a/src/output/table.c +++ b/src/output/table.c @@ -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 @@ -16,1462 +16,324 @@ #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) - -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; -} - -/* 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); -} - - -/* 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; } -/* 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; } -/* 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); } -/* 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 */ }; -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 */ + }; diff --git a/src/output/table.h b/src/output/table.h index f4fbc786..06427ac3 100644 --- a/src/output/table.h +++ b/src/output/table.h @@ -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 @@ -14,188 +14,177 @@ 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); + +/* 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 index 00000000..16588ae5 --- /dev/null +++ b/src/output/text-item.c @@ -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); +} + +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 index 00000000..b62e7dc4 --- /dev/null +++ b/src/output/text-item.h @@ -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 *); + +/* 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 *); + +#endif /* output/text-item.h */ diff --git a/src/ui/gui/executor.c b/src/ui/gui/executor.c index aa2fae51..6ccb5a53 100644 --- a/src/ui/gui/executor.c +++ b/src/ui/gui/executor.c @@ -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; } diff --git a/src/ui/gui/psppire-output-window.c b/src/ui/gui/psppire-output-window.c index 35216d4e..af47d3be 100644 --- a/src/ui/gui/psppire-output-window.c +++ b/src/ui/gui/psppire-output-window.c @@ -20,11 +20,11 @@ #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, /* 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, - >k_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, + >k_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); } 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; } diff --git a/src/ui/gui/psppire-output-window.h b/src/ui/gui/psppire-output-window.h index 606a9504..16752b14 100644 --- a/src/ui/gui/psppire-output-window.h +++ b/src/ui/gui/psppire-output-window.h @@ -52,6 +52,7 @@ struct _PsppireOutputWindow PsppireWindow parent; /* <private> */ + struct psppire_output_driver *driver; GtkLayout *output; int max_width; int y; diff --git a/src/ui/gui/psppire.c b/src/ui/gui/psppire.c index 3f6d49a6..ddf915f2 100644 --- a/src/ui/gui/psppire.c +++ b/src/ui/gui/psppire.c @@ -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 (); } diff --git a/src/ui/source-init-opts.c b/src/ui/source-init-opts.c index 43ae5c62..61a108e0 100644 --- a/src/ui/source-init-opts.c +++ b/src/ui/source-init-opts.c @@ -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> diff --git a/src/ui/terminal/main.c b/src/ui/terminal/main.c index 7ad162fc..1366bd7a 100644 --- a/src/ui/terminal/main.c +++ b/src/ui/terminal/main.c @@ -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 (); } diff --git a/src/ui/terminal/msg-ui.c b/src/ui/terminal/msg-ui.c index 682d753d..1a5e2764 100644 --- a/src/ui/terminal/msg-ui.c +++ b/src/ui/terminal/msg-ui.c @@ -18,22 +18,24 @@ #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); diff --git a/src/ui/terminal/read-line.c b/src/ui/terminal/read-line.c index f85e4e76..86c75dea 100644 --- a/src/ui/terminal/read-line.c +++ b/src/ui/terminal/read-line.c @@ -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 diff --git a/src/ui/terminal/terminal-opts.c b/src/ui/terminal/terminal-opts.c index d2ddc6ee..955a6b0e 100644 --- a/src/ui/terminal/terminal-opts.c +++ b/src/ui/terminal/terminal-opts.c @@ -15,27 +15,33 @@ 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; diff --git a/tests/.gitignore b/tests/.gitignore index 282522db..8174e645 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -1,2 +1,3 @@ Makefile Makefile.in +/testsuite diff --git a/tests/atlocal.in b/tests/atlocal.in new file mode 100644 index 00000000..97e1ae31 --- /dev/null +++ b/tests/atlocal.in @@ -0,0 +1,4 @@ +# -*- shell-script -*- +PERL='@PERL@' +CHARSETALIASDIR="$abs_top_builddir/gl" +export CHARSETALIASDIR diff --git a/tests/automake.mk b/tests/automake.mk index 76ee1f23..96a2dae1 100644 --- a/tests/automake.mk +++ b/tests/automake.mk @@ -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 + +# 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' diff --git a/tests/bugs/agg-crash-2.sh b/tests/bugs/agg-crash-2.sh index 0b006780..0bb8962f 100755 --- a/tests/bugs/agg-crash-2.sh +++ b/tests/bugs/agg-crash-2.sh @@ -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 diff --git a/tests/bugs/compute-lv.sh b/tests/bugs/compute-lv.sh index b0baf4f0..88c42c3c 100755 --- a/tests/bugs/compute-lv.sh +++ b/tests/bugs/compute-lv.sh @@ -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 index 61fc7d07..00000000 --- a/tests/bugs/computebug.out +++ /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 diff --git a/tests/bugs/computebug.sh b/tests/bugs/computebug.sh index dfa5990a..4f8a5731 100755 --- a/tests/bugs/computebug.sh +++ b/tests/bugs/computebug.sh @@ -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 index e308301c..00000000 --- a/tests/bugs/computebug.stat +++ /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. - - diff --git a/tests/bugs/crosstabs-crash.sh b/tests/bugs/crosstabs-crash.sh index 4454319c..f7d19d83 100755 --- a/tests/bugs/crosstabs-crash.sh +++ b/tests/bugs/crosstabs-crash.sh @@ -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 diff --git a/tests/bugs/crosstabs-crash2.sh b/tests/bugs/crosstabs-crash2.sh index b0db128a..910ec0bb 100755 --- a/tests/bugs/crosstabs-crash2.sh +++ b/tests/bugs/crosstabs-crash2.sh @@ -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 diff --git a/tests/bugs/crosstabs2.sh b/tests/bugs/crosstabs2.sh index 3e662998..964cf23c 100755 --- a/tests/bugs/crosstabs2.sh +++ b/tests/bugs/crosstabs2.sh @@ -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 diff --git a/tests/bugs/examine-missing2.sh b/tests/bugs/examine-missing2.sh index 2cd6c14a..c95cca1b 100755 --- a/tests/bugs/examine-missing2.sh +++ b/tests/bugs/examine-missing2.sh @@ -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 diff --git a/tests/bugs/get.sh b/tests/bugs/get.sh index 2cc6cfc5..86b39c66 100755 --- a/tests/bugs/get.sh +++ b/tests/bugs/get.sh @@ -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 diff --git a/tests/bugs/keep-all.sh b/tests/bugs/keep-all.sh index f64674de..408255d2 100755 --- a/tests/bugs/keep-all.sh +++ b/tests/bugs/keep-all.sh @@ -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 diff --git a/tests/bugs/match-files-scratch.sh b/tests/bugs/match-files-scratch.sh index dd95c2c0..6eb5b439 100755 --- a/tests/bugs/match-files-scratch.sh +++ b/tests/bugs/match-files-scratch.sh @@ -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 diff --git a/tests/bugs/multipass.sh b/tests/bugs/multipass.sh index 52720630..11e27406 100755 --- a/tests/bugs/multipass.sh +++ b/tests/bugs/multipass.sh @@ -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 diff --git a/tests/bugs/overwrite-input-file.sh b/tests/bugs/overwrite-input-file.sh index d9f3e59c..a255b2fc 100755 --- a/tests/bugs/overwrite-input-file.sh +++ b/tests/bugs/overwrite-input-file.sh @@ -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 diff --git a/tests/bugs/overwrite-special-file.sh b/tests/bugs/overwrite-special-file.sh index 2bac4413..a40eae66 100755 --- a/tests/bugs/overwrite-special-file.sh +++ b/tests/bugs/overwrite-special-file.sh @@ -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 diff --git a/tests/bugs/random.sh b/tests/bugs/random.sh index e423f3e8..d2016edb 100755 --- a/tests/bugs/random.sh +++ b/tests/bugs/random.sh @@ -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 index 659eba5f..00000000 --- a/tests/bugs/recode-copy-bug-1.out +++ /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 index c493a151..00000000 --- a/tests/bugs/recode-copy-bug-1.stat +++ /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 index 50899a81..00000000 --- a/tests/bugs/recode-copy-bug-2.out +++ /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 index 95873d79..00000000 --- a/tests/bugs/recode-copy-bug-2.stat +++ /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. - - diff --git a/tests/bugs/recode-copy-bug.sh b/tests/bugs/recode-copy-bug.sh index 912590a3..e0a18b8f 100755 --- a/tests/bugs/recode-copy-bug.sh +++ b/tests/bugs/recode-copy-bug.sh @@ -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; diff --git a/tests/bugs/shbang.sh b/tests/bugs/shbang.sh index 826d5dc4..cc3fc5e2 100755 --- a/tests/bugs/shbang.sh +++ b/tests/bugs/shbang.sh @@ -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 diff --git a/tests/bugs/t-test-alpha.sh b/tests/bugs/t-test-alpha.sh index 5565d31a..2a1693b5 100755 --- a/tests/bugs/t-test-alpha.sh +++ b/tests/bugs/t-test-alpha.sh @@ -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 diff --git a/tests/bugs/t-test-alpha3.sh b/tests/bugs/t-test-alpha3.sh index c54bba3c..b197c574 100755 --- a/tests/bugs/t-test-alpha3.sh +++ b/tests/bugs/t-test-alpha3.sh @@ -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 diff --git a/tests/bugs/t-test-paired.sh b/tests/bugs/t-test-paired.sh index a9a311b7..c5555f6f 100755 --- a/tests/bugs/t-test-paired.sh +++ b/tests/bugs/t-test-paired.sh @@ -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 diff --git a/tests/bugs/t-test-with-temp.sh b/tests/bugs/t-test-with-temp.sh index 92dc3fb0..ba355580 100755 --- a/tests/bugs/t-test-with-temp.sh +++ b/tests/bugs/t-test-with-temp.sh @@ -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 diff --git a/tests/bugs/temp-freq.sh b/tests/bugs/temp-freq.sh index 6168f4bb..b0c91fd3 100755 --- a/tests/bugs/temp-freq.sh +++ b/tests/bugs/temp-freq.sh @@ -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 diff --git a/tests/bugs/temporary.sh b/tests/bugs/temporary.sh index 8ffa8611..345ed852 100755 --- a/tests/bugs/temporary.sh +++ b/tests/bugs/temporary.sh @@ -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 diff --git a/tests/command/add-files.sh b/tests/command/add-files.sh index 48232f2e..f0542c0b 100755 --- a/tests/command/add-files.sh +++ b/tests/command/add-files.sh @@ -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 diff --git a/tests/command/aggregate.sh b/tests/command/aggregate.sh index 63abc6f3..48b0ef6e 100755 --- a/tests/command/aggregate.sh +++ b/tests/command/aggregate.sh @@ -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 diff --git a/tests/command/attributes.sh b/tests/command/attributes.sh index d4d36e60..f3a39e29 100755 --- a/tests/command/attributes.sh +++ b/tests/command/attributes.sh @@ -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 diff --git a/tests/command/autorecod.sh b/tests/command/autorecod.sh index e956fcfa..e4a86dfa 100755 --- a/tests/command/autorecod.sh +++ b/tests/command/autorecod.sh @@ -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 diff --git a/tests/command/beg-data.sh b/tests/command/beg-data.sh index 6d835015..e3933d8f 100755 --- a/tests/command/beg-data.sh +++ b/tests/command/beg-data.sh @@ -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 diff --git a/tests/command/correlation.sh b/tests/command/correlation.sh index d8fcb1cb..daec5de2 100755 --- a/tests/command/correlation.sh +++ b/tests/command/correlation.sh @@ -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; diff --git a/tests/command/count.sh b/tests/command/count.sh index 0813c428..d6fd1db8 100755 --- a/tests/command/count.sh +++ b/tests/command/count.sh @@ -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 diff --git a/tests/command/data-list.sh b/tests/command/data-list.sh index 3f821199..52b3dcb8 100755 --- a/tests/command/data-list.sh +++ b/tests/command/data-list.sh @@ -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 diff --git a/tests/command/do-if.sh b/tests/command/do-if.sh index 8cb6b824..b3bbed7f 100755 --- a/tests/command/do-if.sh +++ b/tests/command/do-if.sh @@ -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 diff --git a/tests/command/do-repeat.sh b/tests/command/do-repeat.sh index 0f395422..07d67ec9 100755 --- a/tests/command/do-repeat.sh +++ b/tests/command/do-repeat.sh @@ -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 diff --git a/tests/command/examine-extremes.sh b/tests/command/examine-extremes.sh index 67dfed0a..f771c66d 100755 --- a/tests/command/examine-extremes.sh +++ b/tests/command/examine-extremes.sh @@ -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 diff --git a/tests/command/examine-percentiles.sh b/tests/command/examine-percentiles.sh index 708e5342..bf2fcbf9 100755 --- a/tests/command/examine-percentiles.sh +++ b/tests/command/examine-percentiles.sh @@ -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 diff --git a/tests/command/examine.sh b/tests/command/examine.sh index 11534385..8fc9f7ca 100755 --- a/tests/command/examine.sh +++ b/tests/command/examine.sh @@ -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 diff --git a/tests/command/file-handle.sh b/tests/command/file-handle.sh index 3dfe52f1..ed44ba6b 100755 --- a/tests/command/file-handle.sh +++ b/tests/command/file-handle.sh @@ -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 diff --git a/tests/command/file-label.sh b/tests/command/file-label.sh index e6211b4d..4dc929ea 100755 --- a/tests/command/file-label.sh +++ b/tests/command/file-label.sh @@ -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 diff --git a/tests/command/filter.sh b/tests/command/filter.sh index ec76fba9..4c9c4161 100755 --- a/tests/command/filter.sh +++ b/tests/command/filter.sh @@ -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 diff --git a/tests/command/flip.sh b/tests/command/flip.sh index a3e5caf2..7766ad44 100755 --- a/tests/command/flip.sh +++ b/tests/command/flip.sh @@ -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 diff --git a/tests/command/get-data-gnm.sh b/tests/command/get-data-gnm.sh index c28c8dd4..cf4e6ca8 100755 --- a/tests/command/get-data-gnm.sh +++ b/tests/command/get-data-gnm.sh @@ -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 diff --git a/tests/command/get-data-psql.sh b/tests/command/get-data-psql.sh index 1d8afada..081c7974 100755 --- a/tests/command/get-data-psql.sh +++ b/tests/command/get-data-psql.sh @@ -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 diff --git a/tests/command/get-data-txt-examples.sh b/tests/command/get-data-txt-examples.sh index c601b506..1e1a5164 100755 --- a/tests/command/get-data-txt-examples.sh +++ b/tests/command/get-data-txt-examples.sh @@ -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 diff --git a/tests/command/get-data-txt-importcases.sh b/tests/command/get-data-txt-importcases.sh index 1c7b7cef..8293f290 100755 --- a/tests/command/get-data-txt-importcases.sh +++ b/tests/command/get-data-txt-importcases.sh @@ -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 diff --git a/tests/command/get-data-txt.sh b/tests/command/get-data-txt.sh index 2bd0346b..01bd196c 100755 --- a/tests/command/get-data-txt.sh +++ b/tests/command/get-data-txt.sh @@ -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 diff --git a/tests/command/import-export.sh b/tests/command/import-export.sh index 0cb178bf..9a581402 100755 --- a/tests/command/import-export.sh +++ b/tests/command/import-export.sh @@ -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 diff --git a/tests/command/input-program.sh b/tests/command/input-program.sh index a6e8c778..4aa5ebd2 100755 --- a/tests/command/input-program.sh +++ b/tests/command/input-program.sh @@ -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 diff --git a/tests/command/insert.sh b/tests/command/insert.sh index f80dc5fc..c6d1464c 100755 --- a/tests/command/insert.sh +++ b/tests/command/insert.sh @@ -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 diff --git a/tests/command/lag.sh b/tests/command/lag.sh index bc14e1a9..de197c97 100755 --- a/tests/command/lag.sh +++ b/tests/command/lag.sh @@ -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 diff --git a/tests/command/line-ends.sh b/tests/command/line-ends.sh index ca03b5ca..e78e4ae4 100755 --- a/tests/command/line-ends.sh +++ b/tests/command/line-ends.sh @@ -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 diff --git a/tests/command/list.sh b/tests/command/list.sh index 57eea9d4..dfeb29c3 100755 --- a/tests/command/list.sh +++ b/tests/command/list.sh @@ -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 diff --git a/tests/command/longvars.sh b/tests/command/longvars.sh index 3501f218..8040f44a 100755 --- a/tests/command/longvars.sh +++ b/tests/command/longvars.sh @@ -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 diff --git a/tests/command/loop.sh b/tests/command/loop.sh index 813777f4..21d883e8 100755 --- a/tests/command/loop.sh +++ b/tests/command/loop.sh @@ -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 diff --git a/tests/command/match-files.sh b/tests/command/match-files.sh index 9726b065..a125cbd6 100755 --- a/tests/command/match-files.sh +++ b/tests/command/match-files.sh @@ -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 diff --git a/tests/command/n_of_cases.sh b/tests/command/n_of_cases.sh index 54713555..24a3429b 100755 --- a/tests/command/n_of_cases.sh +++ b/tests/command/n_of_cases.sh @@ -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 diff --git a/tests/command/no_case_size.sh b/tests/command/no_case_size.sh index 0c94051a..4243f326 100755 --- a/tests/command/no_case_size.sh +++ b/tests/command/no_case_size.sh @@ -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 diff --git a/tests/command/npar-binomial.sh b/tests/command/npar-binomial.sh index 0db048dd..9a776f6b 100755 --- a/tests/command/npar-binomial.sh +++ b/tests/command/npar-binomial.sh @@ -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 diff --git a/tests/command/npar-chisquare.sh b/tests/command/npar-chisquare.sh index 33e2311b..0f4b3fac 100755 --- a/tests/command/npar-chisquare.sh +++ b/tests/command/npar-chisquare.sh @@ -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 diff --git a/tests/command/npar-sign.sh b/tests/command/npar-sign.sh index 17c0c239..1c7c629a 100755 --- a/tests/command/npar-sign.sh +++ b/tests/command/npar-sign.sh @@ -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 diff --git a/tests/command/npar-wilcoxon.sh b/tests/command/npar-wilcoxon.sh index 4242b55b..f4434a64 100755 --- a/tests/command/npar-wilcoxon.sh +++ b/tests/command/npar-wilcoxon.sh @@ -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 diff --git a/tests/command/oneway-missing.sh b/tests/command/oneway-missing.sh index 2b16368b..d4a4c6fd 100755 --- a/tests/command/oneway-missing.sh +++ b/tests/command/oneway-missing.sh @@ -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 diff --git a/tests/command/oneway-with-splits.sh b/tests/command/oneway-with-splits.sh index 7197a096..8466d8f5 100755 --- a/tests/command/oneway-with-splits.sh +++ b/tests/command/oneway-with-splits.sh @@ -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 diff --git a/tests/command/oneway.sh b/tests/command/oneway.sh index 339456d3..c55101d5 100755 --- a/tests/command/oneway.sh +++ b/tests/command/oneway.sh @@ -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 diff --git a/tests/command/print.sh b/tests/command/print.sh index 4a052758..f135b4c1 100755 --- a/tests/command/print.sh +++ b/tests/command/print.sh @@ -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 diff --git a/tests/command/rank.sh b/tests/command/rank.sh index d98c8e22..bc208af8 100755 --- a/tests/command/rank.sh +++ b/tests/command/rank.sh @@ -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 diff --git a/tests/command/regression-qr.sh b/tests/command/regression-qr.sh index 6f9b3490..1c4211ab 100755 --- a/tests/command/regression-qr.sh +++ b/tests/command/regression-qr.sh @@ -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 diff --git a/tests/command/regression.sh b/tests/command/regression.sh index 9f8da002..88b4cd83 100755 --- a/tests/command/regression.sh +++ b/tests/command/regression.sh @@ -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 diff --git a/tests/command/reliability.sh b/tests/command/reliability.sh index ed4b4b85..c9344042 100755 --- a/tests/command/reliability.sh +++ b/tests/command/reliability.sh @@ -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 diff --git a/tests/command/rename.sh b/tests/command/rename.sh index ff46486a..a4b9646e 100755 --- a/tests/command/rename.sh +++ b/tests/command/rename.sh @@ -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 diff --git a/tests/command/roc.sh b/tests/command/roc.sh index 0c24f0c5..081263ee 100755 --- a/tests/command/roc.sh +++ b/tests/command/roc.sh @@ -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 diff --git a/tests/command/roc2.sh b/tests/command/roc2.sh index a8020b0c..ffed0fe6 100755 --- a/tests/command/roc2.sh +++ b/tests/command/roc2.sh @@ -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 diff --git a/tests/command/sample.sh b/tests/command/sample.sh index 4f4515e2..4d001bf7 100755 --- a/tests/command/sample.sh +++ b/tests/command/sample.sh @@ -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 diff --git a/tests/command/split-file.sh b/tests/command/split-file.sh index eb2c991c..1452588d 100755 --- a/tests/command/split-file.sh +++ b/tests/command/split-file.sh @@ -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 diff --git a/tests/command/sysfile-info.sh b/tests/command/sysfile-info.sh index d8f8ae36..d5768252 100755 --- a/tests/command/sysfile-info.sh +++ b/tests/command/sysfile-info.sh @@ -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 diff --git a/tests/command/sysfiles-old.sh b/tests/command/sysfiles-old.sh index df16690b..e136a0b2 100755 --- a/tests/command/sysfiles-old.sh +++ b/tests/command/sysfiles-old.sh @@ -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 diff --git a/tests/command/sysfiles.sh b/tests/command/sysfiles.sh index d4f59052..a671dff5 100755 --- a/tests/command/sysfiles.sh +++ b/tests/command/sysfiles.sh @@ -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 diff --git a/tests/command/t-test-1-indep-val.sh b/tests/command/t-test-1-indep-val.sh index ba3e5c5f..0dcd194b 100755 --- a/tests/command/t-test-1-indep-val.sh +++ b/tests/command/t-test-1-indep-val.sh @@ -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 diff --git a/tests/command/t-test-1-sample-missing-anal.sh b/tests/command/t-test-1-sample-missing-anal.sh index 4cc2effa..cbac8dba 100755 --- a/tests/command/t-test-1-sample-missing-anal.sh +++ b/tests/command/t-test-1-sample-missing-anal.sh @@ -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 diff --git a/tests/command/t-test-1-sample-missing-list.sh b/tests/command/t-test-1-sample-missing-list.sh index 07471484..0db7e8e9 100755 --- a/tests/command/t-test-1-sample-missing-list.sh +++ b/tests/command/t-test-1-sample-missing-list.sh @@ -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 diff --git a/tests/command/t-test-1s.sh b/tests/command/t-test-1s.sh index 84369818..c20d0f66 100755 --- a/tests/command/t-test-1s.sh +++ b/tests/command/t-test-1s.sh @@ -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 diff --git a/tests/command/t-test-groups.sh b/tests/command/t-test-groups.sh index 53080bdf..57aef96a 100755 --- a/tests/command/t-test-groups.sh +++ b/tests/command/t-test-groups.sh @@ -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 diff --git a/tests/command/t-test-indep-missing-anal.sh b/tests/command/t-test-indep-missing-anal.sh index fd307ff3..aab5ecc4 100755 --- a/tests/command/t-test-indep-missing-anal.sh +++ b/tests/command/t-test-indep-missing-anal.sh @@ -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 diff --git a/tests/command/t-test-indep-missing-list.sh b/tests/command/t-test-indep-missing-list.sh index 2e62b4d2..026dba7f 100755 --- a/tests/command/t-test-indep-missing-list.sh +++ b/tests/command/t-test-indep-missing-list.sh @@ -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 diff --git a/tests/command/t-test-paired-missing-anal.sh b/tests/command/t-test-paired-missing-anal.sh index a747d4fd..b1e4157b 100755 --- a/tests/command/t-test-paired-missing-anal.sh +++ b/tests/command/t-test-paired-missing-anal.sh @@ -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 diff --git a/tests/command/t-test-paired-missing-list.sh b/tests/command/t-test-paired-missing-list.sh index 0f96be4e..f034651a 100755 --- a/tests/command/t-test-paired-missing-list.sh +++ b/tests/command/t-test-paired-missing-list.sh @@ -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 diff --git a/tests/command/t-test-pairs.sh b/tests/command/t-test-pairs.sh index 37f83304..eb23b6a1 100755 --- a/tests/command/t-test-pairs.sh +++ b/tests/command/t-test-pairs.sh @@ -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 diff --git a/tests/command/tabs.sh b/tests/command/tabs.sh index ad530f45..addb170b 100755 --- a/tests/command/tabs.sh +++ b/tests/command/tabs.sh @@ -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 diff --git a/tests/command/trimmed-mean.sh b/tests/command/trimmed-mean.sh index 660752dd..28843920 100755 --- a/tests/command/trimmed-mean.sh +++ b/tests/command/trimmed-mean.sh @@ -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 diff --git a/tests/command/update.sh b/tests/command/update.sh index 8bcb5671..92615a64 100755 --- a/tests/command/update.sh +++ b/tests/command/update.sh @@ -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. diff --git a/tests/command/use.sh b/tests/command/use.sh index 18b66f0f..b6bc661f 100755 --- a/tests/command/use.sh +++ b/tests/command/use.sh @@ -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 diff --git a/tests/command/variable-display.sh b/tests/command/variable-display.sh index d3f68024..1cf74f34 100755 --- a/tests/command/variable-display.sh +++ b/tests/command/variable-display.sh @@ -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 diff --git a/tests/command/vector.sh b/tests/command/vector.sh index e99fab4b..06a31867 100755 --- a/tests/command/vector.sh +++ b/tests/command/vector.sh @@ -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 diff --git a/tests/command/very-long-strings.sh b/tests/command/very-long-strings.sh index 7c09d8dc..e9d20e91 100755 --- a/tests/command/very-long-strings.sh +++ b/tests/command/very-long-strings.sh @@ -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 diff --git a/tests/command/weight.sh b/tests/command/weight.sh index 0d4fb397..40a3ca9c 100755 --- a/tests/command/weight.sh +++ b/tests/command/weight.sh @@ -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 diff --git a/tests/expressions/valuelabel.sh b/tests/expressions/valuelabel.sh index cb0f5ba3..1b5b92e2 100755 --- a/tests/expressions/valuelabel.sh +++ b/tests/expressions/valuelabel.sh @@ -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 diff --git a/tests/expressions/variables.sh b/tests/expressions/variables.sh index c4a2d5d4..b4f3ffca 100755 --- a/tests/expressions/variables.sh +++ b/tests/expressions/variables.sh @@ -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 diff --git a/tests/expressions/vectors.sh b/tests/expressions/vectors.sh index 57cef8b6..9205f7fc 100755 --- a/tests/expressions/vectors.sh +++ b/tests/expressions/vectors.sh @@ -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 diff --git a/tests/formats/360.sh b/tests/formats/360.sh index 5139dd30..9c156efd 100755 --- a/tests/formats/360.sh +++ b/tests/formats/360.sh @@ -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 index 00000000..5d8a2918 --- /dev/null +++ b/tests/output/render-test.c @@ -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 index 00000000..b9f8e623 --- /dev/null +++ b/tests/output/render.at @@ -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 +])]) + +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 + +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 + +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 + +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 diff --git a/tests/stats/descript-basic.sh b/tests/stats/descript-basic.sh index a1f4a64e..fedb73f6 100755 --- a/tests/stats/descript-basic.sh +++ b/tests/stats/descript-basic.sh @@ -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 diff --git a/tests/stats/descript-mean-bug.sh b/tests/stats/descript-mean-bug.sh index eb12331b..532abed0 100755 --- a/tests/stats/descript-mean-bug.sh +++ b/tests/stats/descript-mean-bug.sh @@ -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 diff --git a/tests/stats/descript-missing.sh b/tests/stats/descript-missing.sh index 9de2770e..fce8a17e 100755 --- a/tests/stats/descript-missing.sh +++ b/tests/stats/descript-missing.sh @@ -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 diff --git a/tests/stats/ntiles.sh b/tests/stats/ntiles.sh index dd00a361..4f4eedb1 100755 --- a/tests/stats/ntiles.sh +++ b/tests/stats/ntiles.sh @@ -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 diff --git a/tests/stats/percentiles-compatible.sh b/tests/stats/percentiles-compatible.sh index 9b170240..2d539a1d 100755 --- a/tests/stats/percentiles-compatible.sh +++ b/tests/stats/percentiles-compatible.sh @@ -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 diff --git a/tests/stats/percentiles-enhanced.sh b/tests/stats/percentiles-enhanced.sh index 9ee5a8c9..5c5accbb 100755 --- a/tests/stats/percentiles-enhanced.sh +++ b/tests/stats/percentiles-enhanced.sh @@ -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 index 00000000..fbf821a7 --- /dev/null +++ b/tests/testsuite.at @@ -0,0 +1,5 @@ +AT_INIT + +AT_TESTED([pspp]) + +m4_include([tests/output/render.at]) diff --git a/tests/xforms/recode.sh b/tests/xforms/recode.sh index ae29cc4d..f36e5f2a 100755 --- a/tests/xforms/recode.sh +++ b/tests/xforms/recode.sh @@ -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 -- 2.30.2