pspp-dev
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[datasets 16/18] Implement DATASET commands.


From: Ben Pfaff
Subject: [datasets 16/18] Implement DATASET commands.
Date: Sat, 30 Apr 2011 22:36:44 -0700

---
 NEWS                                            |    5 +
 doc/combining.texi                              |   10 +-
 doc/data-io.texi                                |   96 ++++++--
 doc/files.texi                                  |   32 ++--
 doc/language.texi                               |   62 ++---
 doc/transformation.texi                         |    7 +-
 src/data/any-reader.c                           |    6 +-
 src/data/any-writer.c                           |    8 +-
 src/data/automake.mk                            |   12 +-
 src/data/caseinit.c                             |   31 +++-
 src/data/caseinit.h                             |    3 +-
 src/data/{scratch-reader.c => dataset-reader.c} |   33 ++--
 src/data/{scratch-reader.h => dataset-reader.h} |   10 +-
 src/data/{scratch-writer.c => dataset-writer.c} |   60 ++---
 src/data/{scratch-writer.h => dataset-writer.h} |   10 +-
 src/data/dataset.c                              |  155 +++++++++++--
 src/data/dataset.h                              |   29 ++-
 src/data/dictionary.c                           |    4 +-
 src/data/file-handle-def.c                      |   59 ++---
 src/data/file-handle-def.h                      |   13 +-
 src/data/scratch-handle.c                       |   36 ---
 src/data/scratch-handle.h                       |   32 ---
 src/data/session.c                              |  180 ++++++++++++++
 src/data/session.h                              |   47 ++++
 src/language/command.c                          |    3 +
 src/language/command.def                        |   12 +-
 src/language/data-io/automake.mk                |    1 +
 src/language/data-io/combine-files.c            |    2 +-
 src/language/data-io/data-list.c                |    2 +-
 src/language/data-io/dataset.c                  |  279 +++++++++++++++++++++
 src/language/data-io/file-handle.h              |   17 +-
 src/language/data-io/file-handle.q              |  154 ++++++------
 src/language/data-io/get-data.c                 |    2 +-
 src/language/data-io/get.c                      |    2 +-
 src/language/data-io/inpt-pgm.c                 |    2 +-
 src/language/data-io/print-space.c              |    2 +-
 src/language/data-io/print.c                    |    2 +-
 src/language/data-io/save-translate.c           |    2 +-
 src/language/data-io/save.c                     |    2 +-
 src/language/dictionary/apply-dictionary.c      |    2 +-
 src/language/dictionary/sys-file-info.c         |    2 +-
 src/language/expressions/evaluate.c             |    2 +-
 src/language/stats/aggregate.c                  |    2 +-
 src/language/utilities/include.c                |    4 +-
 src/ui/gui/data-editor.ui                       |    7 +
 src/ui/gui/executor.c                           |  129 +++++++---
 src/ui/gui/psppire-data-window.c                |  193 ++++++++++++---
 src/ui/gui/psppire-data-window.h                |   20 ++-
 src/ui/gui/psppire-syntax-window.c              |   39 +--
 src/ui/gui/psppire-window.c                     |  288 ++++++++++++++-------
 src/ui/gui/psppire-window.h                     |   10 +-
 src/ui/gui/psppire.c                            |    3 +-
 src/ui/terminal/main.c                          |   13 +-
 tests/automake.mk                               |    1 +
 tests/language/data-io/dataset.at               |  302 +++++++++++++++++++++++
 tests/language/stats/aggregate.at               |   16 +-
 56 files changed, 1855 insertions(+), 602 deletions(-)
 rename src/data/{scratch-reader.c => dataset-reader.c} (61%)
 rename src/data/{scratch-reader.h => dataset-reader.h} (79%)
 rename src/data/{scratch-writer.c => dataset-writer.c} (65%)
 rename src/data/{scratch-writer.h => dataset-writer.h} (79%)
 delete mode 100644 src/data/scratch-handle.c
 delete mode 100644 src/data/scratch-handle.h
 create mode 100644 src/data/session.c
 create mode 100644 src/data/session.h
 create mode 100644 src/language/data-io/dataset.c
 create mode 100644 tests/language/data-io/dataset.at

diff --git a/NEWS b/NEWS
index 86c88fc..7002047 100644
--- a/NEWS
+++ b/NEWS
@@ -71,6 +71,11 @@ Changes from 0.7.3 to 0.7.7:
    PROMPT, CPROMPT, and DPROMPT subcommands.  The defaults are now the
    only supported values.
 
+ * The dataset commands DATASET ACTIVATE, DATASET DECLARE, DATASET
+   CLOSE, DATASET COPY, DATASET NAME, DATASET DISPLAY are now
+   implemented.  These commands replace the "scratch file" PSPP
+   extension, which is no longer supported.
+
 Changes from 0.7.2 to 0.7.3:
 
  * Charts are now produced with Cairo and Pango, instead of libplot.
diff --git a/doc/combining.texi b/doc/combining.texi
index 8fe7b20..a4f683d 100644
--- a/doc/combining.texi
+++ b/doc/combining.texi
@@ -2,7 +2,7 @@
 @chapter Combining Data Files
 
 This chapter describes commands that allow data from system files,
-portable files, scratch files, and the active dataset to be combined to
+portable files, and open datasets to be combined to
 form a new active dataset.  These commands can combine data files in the
 following ways:
 
@@ -63,10 +63,10 @@ them.  The command's output becomes the new active dataset. 
 The input
 files are not changed on disk.
 
 The syntax of each command begins with a specification of the files to
-be read as input.  For each input file, specify FILE with a system,
-portable, or scratch file's name as a string or a file handle
-(@pxref{File Handles}), or specify an asterisk (@samp{*}) to use the
-active dataset as input.  Use of portable or scratch files on FILE is a
+be read as input.  For each input file, specify FILE with a system
+file or portable file's name as a string, a dataset (@pxref{Datasets})
+or file handle name, (@pxref{File Handles}), or an asterisk (@samp{*})
+to use the active dataset as input.  Use of portable files on FILE is a
 PSPP extension.
 
 At least two FILE subcommands must be specified.  If the active dataset
diff --git a/doc/data-io.texi b/doc/data-io.texi
index 81bdd47..cfb99eb 100644
--- a/doc/data-io.texi
+++ b/doc/data-io.texi
@@ -26,6 +26,7 @@ actually be read until a procedure is executed.
 * BEGIN DATA::                  Embed data within a syntax file.
 * CLOSE FILE HANDLE::           Close a file handle.
 * DATAFILE ATTRIBUTE::          Set custom attributes on data files.
+* DATASET::                     Manage multiple datasets.
 * DATA LIST::                   Fundamental data reading command.
 * END CASE::                    Output the current case.
 * END FILE::                    Terminate the current input program.
@@ -78,12 +79,6 @@ given file.  The only specification is the name of the 
handle to close.
 Afterward
 @cmd{FILE HANDLE}.
 
-If the file handle name refers to a scratch file, then the storage
-associated with the scratch file in memory or on disk will be freed.
-If the scratch file is in use, e.g.@: it has been specified on a
address@hidden command whose execution has not completed, then freeing is
-delayed until it is no longer in use.
-
 The file named INLINE, which represents data entered between @cmd{BEGIN
 DATA} and @cmd{END DATA}, cannot be closed.  Attempts to close it with
 @cmd{CLOSE FILE HANDLE} have no effect.
@@ -137,6 +132,84 @@ with the entire active dataset, use @cmd{VARIABLE 
ATTRIBUTE}
 by conditional and looping structures such as @cmd{DO IF} or
 @cmd{LOOP}.
 
address@hidden DATASET
address@hidden DATASET commands
address@hidden DATASET
+
address@hidden
+DATASET NAME name address@hidden,address@hidden
+DATASET ACTIVATE name address@hidden,address@hidden
+DATASET COPY name address@hidden,HIDDEN,address@hidden
+DATASET DECLARE name address@hidden,HIDDEN,address@hidden
+DATASET CLOSE @{name,*,address@hidden
+DATASET DISPLAY.
address@hidden display
+
+The @cmd{DATASET} commands simplify use of multiple datasets within a
+PSPP session.  They allow datasets to be created and destroyed.  At
+any given time, most PSPP commands work with a single dataset, called
+the active dataset.
+
address@hidden DATASET NAME
+The DATASET NAME command gives the active dataset the specified name, or
+if it already had a name, it renames it.  If another dataset already
+had the given name, that dataset is deleted.
+
address@hidden DATASET ACTIVATE
+The DATASET ACTIVATE command selects the named dataset, which must
+already exist, as the active dataset.  Before switching the active
+dataset, any pending transformations are executed, as if @cmd{EXECUTE}
+had been specified.  If the active dataset is unnamed before
+switching, then it is deleted and becomes unavailable after switching.
+
address@hidden DATASET COPY
+The DATASET COPY command creates a new dataset with the specified
+name, whose contents are a copy of the active dataset.  Any pending
+transformations are executed, as if @cmd{EXECUTE} had been specified,
+before making the copy.  If a dataset with the given name already
+exists, it is replaced.  If the name is the name of the active
+dataset, then the active dataset becomes unnamed.
+
address@hidden DATASET DECLARE
+The DATASET DECLARE command creates a new dataset that is initially
+``empty,'' that is, it has no dictionary or data.  If a dataset with
+the given name already exists, this has no effect.  The new dataset
+can be used with commands that support output to a dataset,
+e.g. AGGREGATE (@pxref{AGGREGATE}).
+
address@hidden DATASET CLOSE
+The DATASET CLOSE command deletes a dataset.  If the active dataset is
+specified by name, or if @samp{*} is specified, then the active
+dataset becomes unnamed.  If a different dataset is specified by name,
+then it is deleted and becomes unavailable.  Specifying ALL deletes
+all datasets except for the active dataset, which becomes unnamed.
+
address@hidden DATASET DISPLAY
+The DATASET DISPLAY command lists all the currently defined datasets.
+
+Many DATASET commands accept an optional WINDOW subcommand.  In the
+PSPPIRE GUI, the value given for this subcommand influences how the
+dataset's window is displayed.  Outside the GUI, the WINDOW subcommand
+has no effect.  The valid values are:
+
address@hidden @asis
address@hidden ASIS
+Do not change how the window is displayed.  This is the default for
+DATASET NAME and DATASET ACTIVATE.
+
address@hidden FRONT
+Raise the dataset's window to the top.  Make it the default dataset
+for running syntax.
+
address@hidden MINIMIZED
+Display the window ``minimized'' to an icon.  Prefer other datasets
+for running syntax.  This is the default for DATASET COPY and DATASET
+DECLARE.
+
address@hidden HIDDEN
+Hide the dataset's window.  Prefer other datasets for running syntax.
address@hidden table
+
 @node DATA LIST
 @section DATA LIST
 @vindex DATA LIST
@@ -512,10 +585,6 @@ For binary files encoded in EBCDIC:
                 /MODE=360
                 /address@hidden,VARIABLE,address@hidden
                 [/LRECL=rec_len]
-
-To explicitly declare a scratch handle:
-        FILE HANDLE handle_name
-                /MODE=SCRATCH
 @end display
 
 Use @cmd{FILE HANDLE} to associate a file handle name with a file and
@@ -645,13 +714,6 @@ set, which are then translated from EBCDIC to the native 
character
 set.  Thus, when the host's native character set is based on ASCII,
 these fields are effectively padded with character @code{X'80'}.  This
 wart is implemented for compatibility.
-
address@hidden
-SCRATCH mode is a PSPP extension that designates the file handle as a
-scratch file handle.
-Its use is usually unnecessary because file handle names that begin with
address@hidden are assumed to refer to scratch files.  @pxref{File Handles},
-for more information.
 @end itemize
 
 The NAME subcommand specifies the name of the file associated with the
diff --git a/doc/files.texi b/doc/files.texi
index b4a2446..4fc92e1 100644
--- a/doc/files.texi
+++ b/doc/files.texi
@@ -30,10 +30,10 @@ and missing values taken from a file to corresponding
 variables in the active dataset.  In some cases it also updates the
 weighting variable.
 
-Specify a system file, portable file, or scratch file with a file name
-string or as a file handle (@pxref{File Handles}).  The dictionary in the
-file will be read, but it will not replace the active dataset dictionary.
-The file's data will not be read.
+Specify a system file or portable file's name, a data set name
+(@pxref{Datasets}), or a file handle name (@pxref{File Handles}).  The
+dictionary in the file will be read, but it will not replace the
+active dataset's dictionary.  The file's data will not be read.
 
 Only variables with names that exist in both the active dataset and the
 system file are considered.  Variables with the same name but different
@@ -100,7 +100,7 @@ EXPORT
 @end display
 
 The @cmd{EXPORT} procedure writes the active dataset's dictionary and
-data to a specified portable file or scratch file.
+data to a specified portable file.
 
 By default, cases excluded with FILTER are written to the
 file.  These can be excluded by specifying DELETE on the UNSELECTED
@@ -116,7 +116,7 @@ subcommand may be used to specify the number of decimal 
digits of
 precision to write.  DIGITS applies only to non-integers.
 
 The OUTFILE subcommand, which is the only required subcommand, specifies
-the portable file or scratch file to be written as a file name string or
+the portable file to be written as a file name string or
 a file handle (@pxref{File Handles}).
 
 DROP, KEEP, and RENAME follow the same format as the SAVE procedure
@@ -145,7 +145,7 @@ GET
 replaces them with the dictionary and data from a specified file.
 
 The FILE subcommand is the only required subcommand.  Specify the system
-file, portable file, or scratch file to be read as a string file name or
+file or portable file to be read as a string file name or
 a file handle (@pxref{File Handles}).
 
 By default, all the variables in a file are read.  The DROP
@@ -174,8 +174,7 @@ is affected by these subcommands.
 @cmd{GET} does not cause the data to be read, only the dictionary.  The data
 is read later, when a procedure is executed.
 
-Use of @cmd{GET} to read a portable file or scratch file is a PSPP
-extension.
+Use of @cmd{GET} to read a portable file is a PSPP extension.
 
 @node GET DATA
 @section GET DATA
@@ -633,8 +632,8 @@ IMPORT
 
 The @cmd{IMPORT} transformation clears the active dataset dictionary and
 data and
-replaces them with a dictionary and data from a system, portable file,
-or scratch file.
+replaces them with a dictionary and data from a system file or
+portable file.
 
 The FILE subcommand, which is the only required subcommand, specifies
 the portable file to be read as a file name string or a file handle
@@ -647,8 +646,7 @@ DROP, KEEP, and RENAME follow the syntax used by @cmd{GET} 
(@pxref{GET}).
 @cmd{IMPORT} does not cause the data to be read, only the dictionary.  The
 data is read later, when a procedure is executed.
 
-Use of @cmd{IMPORT} to read a system file or scratch file is a PSPP
-extension.
+Use of @cmd{IMPORT} to read a system file is a PSPP extension.
 
 @node SAVE
 @section SAVE
@@ -670,10 +668,10 @@ SAVE
 
 The @cmd{SAVE} procedure causes the dictionary and data in the active
 dataset to
-be written to a system file or scratch file.
+be written to a system file.
 
-OUTFILE is the only required subcommand.  Specify the system file or
-scratch file to be written as a string file name or a file handle
+OUTFILE is the only required subcommand.  Specify the system file
+to be written as a string file name or a file handle
 (@pxref{File Handles}).
 
 By default, cases excluded with FILTER are written to the system file.
@@ -921,7 +919,7 @@ XSAVE
 @end display
 
 The @cmd{XSAVE} transformation writes the active dataset's dictionary and
-data to a system file or scratch file.  It is similar to the @cmd{SAVE}
+data to a system file.  It is similar to the @cmd{SAVE}
 procedure, with two differences:
 
 @itemize
diff --git a/doc/language.texi b/doc/language.texi
index a81d4e2..a5ef4b6 100644
--- a/doc/language.texi
+++ b/doc/language.texi
@@ -13,7 +13,7 @@ Later chapters will describe individual commands in detail.
 * Types of Commands::           Commands come in several flavors.
 * Order of Commands::           Commands combine to form syntax files.
 * Missing Observations::        Handling missing observations.
-* Variables::                   The unit of data storage.
+* Datasets::                    Data organization.
 * Files::                       Files used by PSPP.
 * File Handles::                How files are named.
 * BNF::                         How command syntax is described.
@@ -380,19 +380,29 @@ for that variable.  However, most of the time 
user-missing values are
 treated in the same way as the system-missing value.
 
 For more information on missing values, see the following sections:
address@hidden, @ref{MISSING VALUES}, @ref{Expressions}.  See also the
address@hidden, @ref{MISSING VALUES}, @ref{Expressions}.  See also the
 documentation on individual procedures for information on how they
 handle missing values.
 
address@hidden Variables
address@hidden Variables
address@hidden variables
address@hidden Datasets
address@hidden Datasets
address@hidden dataset
address@hidden variable
 @cindex dictionary
 
-Variables are the basic unit of data storage in PSPP.  All the
-variables in a file taken together, apart from any associated data, are
-said to form a @dfn{dictionary}.  
-Some details of variables are described in the sections below.
+PSPP works with data organized into @dfn{datasets}.  A dataset
+consists of a set of @dfn{variables}, which taken together are said to
+form a @dfn{dictionary}, and one or more @dfn{cases}, each of which
+has one value for each variable.
+
+At any given time PSPP has exactly one distinguished dataset, called
+the @dfn{active dataset}.  Most PSPP commands work only with the
+active dataset.  In addition to the active dataset, PSPP also supports
+any number of additional open datasets.  The @cmd{DATASET} commands
+can choose a new active dataset from among those that are open, as
+well as create and destroy datasets (@pxref{DATASET}).
+
+The sections below describe variables in more detail.
 
 @menu
 * Attributes::                  Attributes of variables.
@@ -1307,14 +1317,6 @@ run.  The output files receive the tables and charts 
produced by
 statistical procedures.  The output files may be in any number of formats,
 depending on how PSPP is configured.
 
address@hidden active file
address@hidden file, active
address@hidden active file
-The active file is the ``file'' on which all PSPP procedures are
-performed.  The active file consists of a dictionary and a set of cases.
-The active file is not necessarily a disk file: it is stored in memory
-if there is room.
-
 @cindex system file
 @cindex file, system
 @item system file
@@ -1327,24 +1329,14 @@ cases.  @cmd{GET} and @cmd{SAVE} read and write system 
files.
 Portable files are files in a text-based format that store a dictionary
 and a set of cases.  @cmd{IMPORT} and @cmd{EXPORT} read and write
 portable files.
-
address@hidden scratch file
address@hidden file, scratch
address@hidden scratch file
-Scratch files consist of a dictionary and cases and may be stored in
-memory or on disk.  Most procedures that act on a system file or
-portable file can use a scratch file instead.  The contents of scratch
-files persist within a single PSPP session only.  @cmd{GET} and
address@hidden can be used to read and write scratch files.  Scratch files
-are a PSPP extension.
 @end table
 
 @node File Handles
 @section File Handles
 @cindex file handles
 
-A @dfn{file handle} is a reference to a data file, system file, portable
-file, or scratch file.  Most often, a file handle is specified as the
+A @dfn{file handle} is a reference to a data file, system file, or 
+portable file.  Most often, a file handle is specified as the
 name of a file as a string, that is, enclosed within @samp{'} or
 @samp{"}.
 
@@ -1365,15 +1357,6 @@ the syntax later to use a different file.  Use of 
@cmd{FILE HANDLE} is
 also required to read data files in binary formats.  @xref{FILE HANDLE},
 for more information.
 
-PSPP assumes that a file handle name that begins with @samp{#} refers to
-a scratch file, unless the name has already been declared on @cmd{FILE
-HANDLE} to refer to another kind of file.  A scratch file is similar to
-a system file, except that it persists only for the duration of a given
-PSPP session.  Most commands that read or write a system or portable
-file, such as @cmd{GET} and @cmd{SAVE}, also accept scratch file
-handles.  Scratch file handles may also be declared explicitly with
address@hidden HANDLE}.  Scratch files are a PSPP extension.
-
 In some circumstances, PSPP must distinguish whether a file handle
 refers to a system file or a portable file.  When this is necessary to
 read a file, e.g.@: as an input file for @cmd{GET} or @cmd{MATCH FILES},
@@ -1389,8 +1372,7 @@ file'' embedded into the syntax file between @cmd{BEGIN 
DATA} and
 
 The file to which a file handle refers may be reassigned on a later
 @cmd{FILE HANDLE} command if it is first closed using @cmd{CLOSE FILE
-HANDLE}.  The @cmd{CLOSE FILE HANDLE} command is also useful to free the
-storage associated with a scratch file.  @xref{CLOSE FILE HANDLE}, for
+HANDLE}.  @xref{CLOSE FILE HANDLE}, for
 more information.
 
 @node BNF
diff --git a/doc/transformation.texi b/doc/transformation.texi
index 57c9a49..f1d1cbf 100644
--- a/doc/transformation.texi
+++ b/doc/transformation.texi
@@ -37,11 +37,12 @@ variables called @dfn{break variables}.  Several functions 
are available
 for summarizing case contents.
 
 The OUTFILE subcommand is required and must appear first.  Specify a
-system file, portable file, or scratch file by file name or file
-handle (@pxref{File Handles}).
+system file or portable file by file name or file
+handle (@pxref{File Handles}), or a dataset by its name
+(@pxref{Datasets}).
 The aggregated cases are written to this file.  If @samp{*} is
 specified, then the aggregated cases replace the active dataset's data.
-Use of OUTFILE to write a portable file or scratch file is a PSPP extension.
+Use of OUTFILE to write a portable file is a PSPP extension.
 
 If address@hidden is given, then the subcommand MODE may also be
 specified.
diff --git a/src/data/any-reader.c b/src/data/any-reader.c
index 03a7b23..50feb68 100644
--- a/src/data/any-reader.c
+++ b/src/data/any-reader.c
@@ -24,10 +24,10 @@
 #include <stdio.h>
 #include <stdlib.h>
 
+#include "data/dataset-reader.h"
 #include "data/file-handle-def.h"
 #include "data/file-name.h"
 #include "data/por-file-reader.h"
-#include "data/scratch-reader.h"
 #include "data/sys-file-reader.h"
 #include "libpspp/assertion.h"
 #include "libpspp/message.h"
@@ -111,8 +111,8 @@ any_reader_open (struct file_handle *handle, struct 
dictionary **dict)
       msg (SE, _("The inline file is not allowed here."));
       return NULL;
 
-    case FH_REF_SCRATCH:
-      return scratch_reader_open (handle, dict);
+    case FH_REF_DATASET:
+      return dataset_reader_open (handle, dict);
     }
   NOT_REACHED ();
 }
diff --git a/src/data/any-writer.c b/src/data/any-writer.c
index b398c70..61d6ffc 100644
--- a/src/data/any-writer.c
+++ b/src/data/any-writer.c
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2006, 2011 Free Software Foundation, Inc.
+   Copyright (C) 2006, 2010, 2011 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
@@ -24,10 +24,10 @@
 #include <stdio.h>
 #include <stdlib.h>
 
+#include "data/dataset-writer.h"
 #include "data/file-handle-def.h"
 #include "data/file-name.h"
 #include "data/por-file-writer.h"
-#include "data/scratch-writer.h"
 #include "data/sys-file-writer.h"
 #include "libpspp/assertion.h"
 #include "libpspp/message.h"
@@ -67,8 +67,8 @@ any_writer_open (struct file_handle *handle, struct 
dictionary *dict)
       msg (ME, _("The inline file is not allowed here."));
       return NULL;
 
-    case FH_REF_SCRATCH:
-      return scratch_writer_open (handle, dict);
+    case FH_REF_DATASET:
+      return dataset_writer_open (handle, dict);
     }
 
   NOT_REACHED ();
diff --git a/src/data/automake.mk b/src/data/automake.mk
index f7ee43f..81a9d9c 100644
--- a/src/data/automake.mk
+++ b/src/data/automake.mk
@@ -50,6 +50,10 @@ src_data_libdata_la_SOURCES = \
        src/data/data-out.h \
        src/data/dataset.c \
        src/data/dataset.h \
+       src/data/dataset-reader.c \
+       src/data/dataset-reader.h \
+       src/data/dataset-writer.c \
+       src/data/dataset-writer.h \
        src/data/datasheet.c \
        src/data/datasheet.h \
        src/data/dict-class.c \
@@ -84,12 +88,8 @@ src_data_libdata_la_SOURCES = \
        src/data/por-file-writer.h \
        src/data/psql-reader.c \
        src/data/psql-reader.h \
-       src/data/scratch-handle.c \
-       src/data/scratch-handle.h \
-       src/data/scratch-reader.c \
-       src/data/scratch-reader.h \
-       src/data/scratch-writer.c \
-       src/data/scratch-writer.h \
+       src/data/session.c \
+       src/data/session.h \
        src/data/settings.c \
        src/data/settings.h \
        src/data/short-names.c \
diff --git a/src/data/caseinit.c b/src/data/caseinit.c
index 69a0f01..021db39 100644
--- a/src/data/caseinit.c
+++ b/src/data/caseinit.c
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2007, 2009, 2011 Free Software Foundation, Inc.
+   Copyright (C) 2007, 2009, 2010, 2011 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
@@ -53,7 +53,7 @@ struct init_list
 /* A bitmap of the "left" status of variables. */
 enum leave_class
   {
-    LEAVE_REINIT = 0x001,       /* Reinitalize for every case. */
+    LEAVE_REINIT = 0x001,       /* Reinitialize for every case. */
     LEAVE_LEFT = 0x002          /* Keep the value from one case to the next. */
   };
 
@@ -65,6 +65,22 @@ init_list_create (struct init_list *list)
   list->cnt = 0;
 }
 
+/* Initializes NEW as a copy of OLD. */
+static void
+init_list_clone (struct init_list *new, const struct init_list *old)
+{
+  size_t i;
+
+  new->values = xmemdup (old->values, old->cnt * sizeof *old->values);
+  new->cnt = old->cnt;
+
+  for (i = 0; i < new->cnt; i++)
+    {
+      struct init_value *iv = &new->values[i];
+      value_clone (&iv->value, &iv->value, iv->width);
+    }
+}
+
 /* Frees the storage associated with LIST. */
 static void
 init_list_destroy (struct init_list *list)
@@ -198,6 +214,17 @@ caseinit_create (void)
   return ci;
 }
 
+/* Creates and returns a copy of OLD. */
+struct caseinit *
+caseinit_clone (struct caseinit *old)
+{
+  struct caseinit *new = xmalloc (sizeof *new);
+  init_list_clone (&new->preinited_values, &old->preinited_values);
+  init_list_clone (&new->reinit_values, &old->reinit_values);
+  init_list_clone (&new->left_values, &old->left_values);
+  return new;
+}
+
 /* Clears the contents of case initializer CI. */
 void
 caseinit_clear (struct caseinit *ci)
diff --git a/src/data/caseinit.h b/src/data/caseinit.h
index dbff856..9f56621 100644
--- a/src/data/caseinit.h
+++ b/src/data/caseinit.h
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2007 Free Software Foundation, Inc.
+   Copyright (C) 2007, 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
@@ -36,6 +36,7 @@ struct ccase;
 
 /* Creation and destruction. */
 struct caseinit *caseinit_create (void);
+struct caseinit *caseinit_clone (struct caseinit *);
 void caseinit_clear (struct caseinit *);
 void caseinit_destroy (struct caseinit *);
 
diff --git a/src/data/scratch-reader.c b/src/data/dataset-reader.c
similarity index 61%
rename from src/data/scratch-reader.c
rename to src/data/dataset-reader.c
index 5def728..b679342 100644
--- a/src/data/scratch-reader.c
+++ b/src/data/dataset-reader.c
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2006, 2011 Free Software Foundation, Inc.
+   Copyright (C) 2006, 2010, 2011 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,15 +16,15 @@
 
 #include <config.h>
 
-#include "data/scratch-reader.h"
+#include "data/dataset-reader.h"
 
 #include <stdlib.h>
 
 #include "data/case.h"
 #include "data/casereader.h"
+#include "data/dataset.h"
 #include "data/dictionary.h"
 #include "data/file-handle-def.h"
-#include "data/scratch-handle.h"
 #include "libpspp/assertion.h"
 #include "libpspp/message.h"
 
@@ -33,31 +33,30 @@
 #include "gettext.h"
 #define _(msgid) gettext (msgid)
 
-/* Opens FH, which must have referent type FH_REF_SCRATCH, and
-   returns a scratch_reader for it, or a null pointer on
-   failure.  Stores the dictionary for the scratch file into
-   *DICT. */
+/* Opens FH, which must have referent type FH_REF_DATASET, and returns a
+   dataset_reader for it, or a null pointer on failure.  Stores a copy of the
+   dictionary for the dataset file into *DICT.  The caller takes ownership of
+   the casereader and the dictionary.  */
 struct casereader *
-scratch_reader_open (struct file_handle *fh, struct dictionary **dict)
+dataset_reader_open (struct file_handle *fh, struct dictionary **dict)
 {
-  struct scratch_handle *sh;
+  struct dataset *ds;
 
   /* We don't bother doing fh_lock or fh_ref on the file handle,
      as there's no advantage in this case, and doing these would
      require us to keep track of the "struct file_handle" and
      "struct fh_lock" and undo our work later. */
-  assert (fh_get_referent (fh) == FH_REF_SCRATCH);
+  assert (fh_get_referent (fh) == FH_REF_DATASET);
 
-  sh = fh_get_scratch_handle (fh);
-  if (sh == NULL || sh->casereader == NULL)
+  ds = fh_get_dataset (fh);
+  if (ds == NULL || !dataset_has_source (ds))
     {
-      msg (SE, _("Scratch file handle %s has not yet been written, "
-                 "using SAVE or another procedure, so it cannot yet "
-                 "be used for reading."),
+      msg (SE, _("Cannot read from dataset %s because no dictionary or data "
+                 "has been written to it yet."),
            fh_get_name (fh));
       return NULL;
     }
 
-  *dict = dict_clone (sh->dictionary);
-  return casereader_clone (sh->casereader);
+  *dict = dict_clone (dataset_dict (ds));
+  return casereader_clone (dataset_source (ds));
 }
diff --git a/src/data/scratch-reader.h b/src/data/dataset-reader.h
similarity index 79%
rename from src/data/scratch-reader.h
rename to src/data/dataset-reader.h
index a5ee005..420b6b1 100644
--- a/src/data/scratch-reader.h
+++ b/src/data/dataset-reader.h
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2006, 2009 Free Software Foundation, Inc.
+   Copyright (C) 2006, 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
@@ -14,14 +14,14 @@
    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 SCRATCH_READER_H
-#define SCRATCH_READER_H 1
+#ifndef DATASET_READER_H
+#define DATASET_READER_H 1
 
 #include <stdbool.h>
 
 struct dictionary;
 struct file_handle;
-struct casereader *scratch_reader_open (struct file_handle *,
+struct casereader *dataset_reader_open (struct file_handle *,
                                         struct dictionary **);
 
-#endif /* scratch-reader.h */
+#endif /* dataset-reader.h */
diff --git a/src/data/scratch-writer.c b/src/data/dataset-writer.c
similarity index 65%
rename from src/data/scratch-writer.c
rename to src/data/dataset-writer.c
index 2c545c7..c810cf2 100644
--- a/src/data/scratch-writer.c
+++ b/src/data/dataset-writer.c
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2006, 2009, 2011 Free Software Foundation, Inc.
+   Copyright (C) 2006, 2009-2011 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,7 +16,7 @@
 
 #include <config.h>
 
-#include "data/scratch-writer.h"
+#include "data/dataset-writer.h"
 
 #include <stdlib.h>
 
@@ -25,9 +25,9 @@
 #include "data/casereader.h"
 #include "data/casewriter-provider.h"
 #include "data/casewriter.h"
+#include "data/dataset.h"
 #include "data/dictionary.h"
 #include "data/file-handle-def.h"
-#include "data/scratch-handle.h"
 #include "data/variable.h"
 #include "libpspp/compiler.h"
 #include "libpspp/taint.h"
@@ -36,41 +36,41 @@
 
 #define N_(msgid) (msgid)
 
-/* A scratch file writer. */
-struct scratch_writer
+/* A dataset file writer. */
+struct dataset_writer
   {
-    struct file_handle *fh;             /* Underlying file handle. */
+    struct dataset *ds;                 /* Underlying dataset. */
     struct fh_lock *lock;               /* Exclusive access to file handle. */
     struct dictionary *dict;            /* Dictionary for subwriter. */
     struct case_map *compactor;         /* Compacts into dictionary. */
     struct casewriter *subwriter;       /* Data output. */
   };
 
-static const struct casewriter_class scratch_writer_casewriter_class;
+static const struct casewriter_class dataset_writer_casewriter_class;
 
-/* Opens FH, which must have referent type FH_REF_SCRATCH, and
-   returns a scratch_writer for it, or a null pointer on
-   failure.  Cases stored in the scratch_writer will be expected
+/* Opens FH, which must have referent type FH_REF_DATASET, and
+   returns a dataset_writer for it, or a null pointer on
+   failure.  Cases stored in the dataset_writer will be expected
    to be drawn from DICTIONARY. */
 struct casewriter *
-scratch_writer_open (struct file_handle *fh,
+dataset_writer_open (struct file_handle *fh,
                      const struct dictionary *dictionary)
 {
-  struct scratch_writer *writer;
+  struct dataset_writer *writer;
   struct casewriter *casewriter;
   struct fh_lock *lock;
 
   /* Get exclusive write access to handle. */
   /* TRANSLATORS: this fragment will be interpolated into
      messages in fh_lock() that identify types of files. */
-  lock = fh_lock (fh, FH_REF_SCRATCH, N_("scratch file"), FH_ACC_WRITE, true);
+  lock = fh_lock (fh, FH_REF_DATASET, N_("dataset"), FH_ACC_WRITE, true);
   if (lock == NULL)
     return NULL;
 
   /* Create writer. */
   writer = xmalloc (sizeof *writer);
   writer->lock = lock;
-  writer->fh = fh_ref (fh);
+  writer->ds = fh_get_dataset (fh);
 
   writer->dict = dict_clone (dictionary);
   dict_delete_scratch_vars (writer->dict);
@@ -85,7 +85,7 @@ scratch_writer_open (struct file_handle *fh,
   writer->subwriter = autopaging_writer_create (dict_get_proto (writer->dict));
 
   casewriter = casewriter_create (dict_get_proto (writer->dict),
-                                  &scratch_writer_casewriter_class, writer);
+                                  &dataset_writer_casewriter_class, writer);
   taint_propagate (casewriter_get_taint (writer->subwriter),
                    casewriter_get_taint (casewriter));
   return casewriter;
@@ -93,35 +93,24 @@ scratch_writer_open (struct file_handle *fh,
 
 /* Writes case C to WRITER. */
 static void
-scratch_writer_casewriter_write (struct casewriter *w UNUSED, void *writer_,
+dataset_writer_casewriter_write (struct casewriter *w UNUSED, void *writer_,
                                  struct ccase *c)
 {
-  struct scratch_writer *writer = writer_;
+  struct dataset_writer *writer = writer_;
   casewriter_write (writer->subwriter,
                     case_map_execute (writer->compactor, c));
 }
 
 /* Closes WRITER. */
 static void
-scratch_writer_casewriter_destroy (struct casewriter *w UNUSED, void *writer_)
+dataset_writer_casewriter_destroy (struct casewriter *w UNUSED, void *writer_)
 {
-  static unsigned int next_unique_id = 0x12345678;
-
-  struct scratch_writer *writer = writer_;
+  struct dataset_writer *writer = writer_;
   struct casereader *reader = casewriter_make_reader (writer->subwriter);
   if (!casereader_error (reader))
     {
-      /* Destroy previous contents of handle. */
-      struct scratch_handle *sh = fh_get_scratch_handle (writer->fh);
-      if (sh != NULL)
-        scratch_handle_destroy (sh);
-
-      /* Create new contents. */
-      sh = xmalloc (sizeof *sh);
-      sh->unique_id = ++next_unique_id;
-      sh->dictionary = writer->dict;
-      sh->casereader = reader;
-      fh_set_scratch_handle (writer->fh, sh);
+      dataset_set_dict (writer->ds, writer->dict);
+      dataset_set_source (writer->ds, reader);
     }
   else
     {
@@ -130,13 +119,12 @@ scratch_writer_casewriter_destroy (struct casewriter *w 
UNUSED, void *writer_)
     }
 
   fh_unlock (writer->lock);
-  fh_unref (writer->fh);
   free (writer);
 }
 
-static const struct casewriter_class scratch_writer_casewriter_class =
+static const struct casewriter_class dataset_writer_casewriter_class =
   {
-    scratch_writer_casewriter_write,
-    scratch_writer_casewriter_destroy,
+    dataset_writer_casewriter_write,
+    dataset_writer_casewriter_destroy,
     NULL,
   };
diff --git a/src/data/scratch-writer.h b/src/data/dataset-writer.h
similarity index 79%
rename from src/data/scratch-writer.h
rename to src/data/dataset-writer.h
index a9c7a4d..429d688 100644
--- a/src/data/scratch-writer.h
+++ b/src/data/dataset-writer.h
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2006, 2009 Free Software Foundation, Inc.
+   Copyright (C) 2006, 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
@@ -14,14 +14,14 @@
    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 SCRATCH_WRITER_H
-#define SCRATCH_WRITER_H 1
+#ifndef DATASET_WRITER_H
+#define DATASET_WRITER_H 1
 
 #include <stdbool.h>
 
 struct dictionary;
 struct file_handle;
-struct casewriter *scratch_writer_open (struct file_handle *,
+struct casewriter *dataset_writer_open (struct file_handle *,
                                         const struct dictionary *);
 
-#endif /* scratch-writer.h */
+#endif /* dataset-writer.h */
diff --git a/src/data/dataset.c b/src/data/dataset.c
index 466696c..10009f4 100644
--- a/src/data/dataset.c
+++ b/src/data/dataset.c
@@ -32,6 +32,7 @@
 #include "data/casewriter.h"
 #include "data/dictionary.h"
 #include "data/file-handle-def.h"
+#include "data/session.h"
 #include "data/transformations.h"
 #include "data/variable.h"
 #include "libpspp/deque.h"
@@ -44,6 +45,15 @@
 #include "gl/xalloc.h"
 
 struct dataset {
+  /* A dataset is usually part of a session.  Within a session its name must
+     unique.  The name must either be a valid PSPP identifier or the empty
+     string.  (It must be unique within the session even if it is the empty
+     string; that is, there may only be a single dataset within a session with
+     the empty string as its name.) */
+  struct session *session;
+  char *name;
+  enum dataset_display display;
+
   /* Cases are read from source,
      their transformation variables are initialized,
      pass through permanent_trns_chain (which transforms them into
@@ -96,8 +106,8 @@ struct dataset {
   const struct dataset_callbacks *callbacks;
   void *cb_data;
 
-  /* Default encoding for reading syntax files. */
-  char *syntax_encoding;
+  /* Uniquely distinguishes datasets. */
+  unsigned int seqno;
 };
 
 static void dataset_changed__ (struct dataset *);
@@ -116,35 +126,83 @@ dict_callback (struct dictionary *d UNUSED, void *ds_)
   dataset_changed__ (ds);
 }
 
-/* Creates and returns a new dataset.  The dataset initially has an empty
-   dictionary and no data source. */
+static void
+dataset_create_finish__ (struct dataset *ds, struct session *session)
+{
+  static unsigned int seqno;
+
+  dict_set_change_callback (ds->dict, dict_callback, ds);
+  proc_cancel_all_transformations (ds);
+  dataset_set_session (ds, session);
+  ds->seqno = ++seqno;
+}
+
+/* Creates a new dataset named NAME, adds it to SESSION, and returns it.  If
+   SESSION already contains a dataset named NAME, it is deleted and replaced.
+   The dataset initially has an empty dictionary and no data source. */
 struct dataset *
-dataset_create (void)
+dataset_create (struct session *session, const char *name)
 {
   struct dataset *ds;
 
   ds = xzalloc (sizeof *ds);
+  ds->name = xstrdup (name);
+  ds->display = DATASET_FRONT;
   ds->dict = dict_create (get_default_encoding ());
-  dict_set_change_callback (ds->dict, dict_callback, ds);
 
   ds->caseinit = caseinit_create ();
-  proc_cancel_all_transformations (ds);
-  ds->syntax_encoding = xstrdup ("Auto");
+
+  dataset_create_finish__ (ds, session);
+
   return ds;
 }
 
+/* Creates and returns a new dataset that has the same data and dictionary as
+   OLD named NAME, adds it to the same session as OLD, and returns the new
+   dataset.  If SESSION already contains a dataset named NAME, it is deleted
+   and replaced.
+
+   OLD must not have any active transformations or temporary state and must
+   not be in the middle of a procedure.
+
+   Callbacks are not cloned. */
+struct dataset *
+dataset_clone (struct dataset *old, const char *name)
+{
+  struct dataset *new;
+
+  assert (old->proc_state == PROC_COMMITTED);
+  assert (trns_chain_is_empty (old->permanent_trns_chain));
+  assert (old->permanent_dict == NULL);
+  assert (old->sink == NULL);
+  assert (old->temporary_trns_chain == NULL);
+
+  new = xzalloc (sizeof *new);
+  new->name = xstrdup (name);
+  new->display = DATASET_FRONT;
+  new->source = casereader_clone (old->source);
+  new->dict = dict_clone (old->dict);
+  new->caseinit = caseinit_clone (old->caseinit);
+  new->last_proc_invocation = old->last_proc_invocation;
+  new->ok = old->ok;
+
+  dataset_create_finish__ (new, old->session);
+
+  return new;
+}
+
 /* Destroys DS. */
 void
 dataset_destroy (struct dataset *ds)
 {
   if (ds != NULL)
     {
+      dataset_set_session (ds, NULL);
       dataset_clear (ds);
       dict_destroy (ds->dict);
       caseinit_destroy (ds->caseinit);
       trns_chain_destroy (ds->permanent_trns_chain);
       dataset_transformations_changed__ (ds, false);
-      free (ds->syntax_encoding);
       free (ds);
     }
 }
@@ -166,6 +224,55 @@ dataset_clear (struct dataset *ds)
   proc_cancel_all_transformations (ds);
 }
 
+const char *
+dataset_name (const struct dataset *ds)
+{
+  return ds->name;
+}
+
+void
+dataset_set_name (struct dataset *ds, const char *name)
+{
+  struct session *session = ds->session;
+  bool active = false;
+
+  if (session != NULL)
+    {
+      active = session_active_dataset (session) == ds;
+      if (active)
+        session_set_active_dataset (session, NULL);
+      dataset_set_session (ds, NULL);
+    }
+
+  free (ds->name);
+  ds->name = xstrdup (name);
+
+  if (session != NULL)
+    {
+      dataset_set_session (ds, session);
+      if (active)
+        session_set_active_dataset (session, ds);
+    }
+}
+
+struct session *
+dataset_session (const struct dataset *ds)
+{
+  return ds->session;
+}
+
+void
+dataset_set_session (struct dataset *ds, struct session *session)
+{
+  if (session != ds->session)
+    {
+      if (ds->session != NULL)
+        session_remove_dataset (ds->session, ds);
+      if (session != NULL)
+        session_add_dataset (session, ds);
+    }
+}
+
 /* Returns the dictionary within DS.  This is always nonnull, although it
    might not contain any variables. */
 struct dictionary *
@@ -229,6 +336,15 @@ dataset_steal_source (struct dataset *ds)
   return reader;
 }
 
+/* Returns a number unique to DS.  It can be used to distinguish one dataset
+   from any other within a given program run, even datasets that do not exist
+   at the same time. */
+unsigned int
+dataset_seqno (const struct dataset *ds)
+{
+  return ds->seqno;
+}
+
 void
 dataset_set_callbacks (struct dataset *ds,
                        const struct dataset_callbacks *callbacks,
@@ -238,17 +354,16 @@ dataset_set_callbacks (struct dataset *ds,
   ds->cb_data = cb_data;
 }
 
-void
-dataset_set_default_syntax_encoding (struct dataset *ds, const char *encoding)
+enum dataset_display
+dataset_get_display (const struct dataset *ds)
 {
-  free (ds->syntax_encoding);
-  ds->syntax_encoding = xstrdup (encoding);
+  return ds->display;
 }
 
-const char *
-dataset_get_default_syntax_encoding (const struct dataset *ds)
+void
+dataset_set_display (struct dataset *ds, enum dataset_display display)
 {
-  return ds->syntax_encoding;
+  ds->display = display;
 }
 
 /* Returns the last time the data was read. */
@@ -813,3 +928,11 @@ dataset_transformations_changed__ (struct dataset *ds, 
bool non_empty)
   if (ds->callbacks != NULL && ds->callbacks->transformations_changed != NULL)
     ds->callbacks->transformations_changed (non_empty, ds->cb_data);
 }
+
+/* Private interface for use by session code. */
+
+void
+dataset_set_session__ (struct dataset *ds, struct session *session)
+{
+  ds->session = session;
+}
diff --git a/src/data/dataset.h b/src/data/dataset.h
index b2aa8bc..8445094 100644
--- a/src/data/dataset.h
+++ b/src/data/dataset.h
@@ -21,17 +21,24 @@
 #include <stdbool.h>
 
 #include "data/transformations.h"
-#include "libpspp/compiler.h"
 
 struct casereader;
 struct dataset;
 struct dictionary;
+struct session;
 
-struct dataset *dataset_create (void);
+struct dataset *dataset_create (struct session *, const char *);
+struct dataset *dataset_clone (struct dataset *, const char *);
 void dataset_destroy (struct dataset *);
 
 void dataset_clear (struct dataset *);
 
+const char *dataset_name (const struct dataset *);
+void dataset_set_name (struct dataset *, const char *);
+
+struct session *dataset_session (const struct dataset *);
+void dataset_set_session (struct dataset *, struct session *);
+
 struct dictionary *dataset_dict (const struct dataset *);
 void dataset_set_dict (struct dataset *, struct dictionary *);
 
@@ -40,8 +47,7 @@ bool dataset_has_source (const struct dataset *ds);
 bool dataset_set_source (struct dataset *, struct casereader *);
 struct casereader *dataset_steal_source (struct dataset *);
 
-void dataset_set_default_syntax_encoding (struct dataset *, const char *);
-const char *dataset_get_default_syntax_encoding (const struct dataset *);
+unsigned int dataset_seqno (const struct dataset *);
 
 struct dataset_callbacks
   {
@@ -58,6 +64,17 @@ struct dataset_callbacks
 
 void dataset_set_callbacks (struct dataset *, const struct dataset_callbacks *,
                             void *aux);
+
+/* Dataset GUI window display status. */
+enum dataset_display
+  {
+    DATASET_ASIS,               /* Current state unchanged. */
+    DATASET_FRONT,              /* Display and raise to top. */
+    DATASET_MINIMIZED,          /* Display as icon. */
+    DATASET_HIDDEN              /* Do not display. */
+  };
+enum dataset_display dataset_get_display (const struct dataset *);
+void dataset_set_display (struct dataset *, enum dataset_display);
 
 /* Transformations. */
 
@@ -93,5 +110,9 @@ bool dataset_end_of_command (struct dataset *);
 
 const struct ccase *lagged_case (const struct dataset *ds, int n_before);
 void dataset_need_lag (struct dataset *ds, int n_before);
+
+/* Private interface for use by session code. */
+
+void dataset_set_session__(struct dataset *, struct session *);
 
 #endif /* dataset.h */
diff --git a/src/data/dictionary.c b/src/data/dictionary.c
index c8f5851..4a0afc7 100644
--- a/src/data/dictionary.c
+++ b/src/data/dictionary.c
@@ -181,7 +181,9 @@ dict_create (const char *encoding)
    dictionary.  If the new dictionary won't be used to access
    cases produced with the old dictionary, then the new
    dictionary's case indexes should be compacted with
-   dict_compact_values to save space. */
+   dict_compact_values to save space.
+
+   Callbacks are not cloned. */
 struct dictionary *
 dict_clone (const struct dictionary *s)
 {
diff --git a/src/data/file-handle-def.c b/src/data/file-handle-def.c
index 2b8e40c..6ca6977 100644
--- a/src/data/file-handle-def.c
+++ b/src/data/file-handle-def.c
@@ -23,15 +23,15 @@
 #include <stdlib.h>
 #include <string.h>
 
+#include "data/dataset.h"
+#include "data/file-name.h"
+#include "data/variable.h"
 #include "libpspp/compiler.h"
 #include "libpspp/hmap.h"
 #include "libpspp/i18n.h"
 #include "libpspp/message.h"
 #include "libpspp/str.h"
 #include "libpspp/hash-functions.h"
-#include "data/file-name.h"
-#include "data/variable.h"
-#include "data/scratch-handle.h"
 
 #include "gl/xalloc.h"
 
@@ -56,8 +56,8 @@ struct file_handle
     size_t record_width;        /* Length of fixed-format records. */
     size_t tab_width;           /* Tab width, 0=do not expand tabs. */
 
-    /* FH_REF_SCRATCH only. */
-    struct scratch_handle *sh;  /* Scratch file data. */
+    /* FH_REF_DATASET only. */
+    struct dataset *ds;         /* Dataset. */
   };
 
 /* All "struct file_handle"s with nonnull 'id' member. */
@@ -110,7 +110,6 @@ free_handle (struct file_handle *handle)
   free (handle->id);
   free (handle->name);
   free (handle->file_name);
-  scratch_handle_destroy (handle->sh);
   free (handle);
 }
 
@@ -243,13 +242,19 @@ fh_create_file (const char *id, const char *file_name,
 
 /* Creates a new file handle with the given ID, which must be
    unique among existing file identifiers.  The new handle is
-   associated with a scratch file (initially empty). */
+   associated with a dataset file (initially empty). */
 struct file_handle *
-fh_create_scratch (const char *id)
+fh_create_dataset (struct dataset *ds)
 {
+  const char *name;
   struct file_handle *handle;
-  handle = create_handle (id, xstrdup (id), FH_REF_SCRATCH);
-  handle->sh = NULL;
+
+  name = dataset_name (ds);
+  if (name[0] == '\0')
+    name = _("active dataset");
+
+  handle = create_handle (NULL, xstrdup (name), FH_REF_DATASET);
+  handle->ds = ds;
   return handle;
 }
 
@@ -334,22 +339,13 @@ fh_get_legacy_encoding (const struct file_handle *handle)
   return (handle->referent == FH_REF_FILE ? handle->encoding : C_ENCODING);
 }
 
-/* Returns the scratch file handle associated with HANDLE.
-   Applicable to only FH_REF_SCRATCH files. */
-struct scratch_handle *
-fh_get_scratch_handle (const struct file_handle *handle)
+/* Returns the dataset handle associated with HANDLE.
+   Applicable to only FH_REF_DATASET files. */
+struct dataset *
+fh_get_dataset (const struct file_handle *handle)
 {
-  assert (handle->referent == FH_REF_SCRATCH);
-  return handle->sh;
-}
-
-/* Sets SH to be the scratch file handle associated with HANDLE.
-   Applicable to only FH_REF_SCRATCH files. */
-void
-fh_set_scratch_handle (struct file_handle *handle, struct scratch_handle *sh)
-{
-  assert (handle->referent == FH_REF_SCRATCH);
-  handle->sh = sh;
+  assert (handle->referent == FH_REF_DATASET);
+  return handle->ds;
 }
 
 /* Returns the current default handle. */
@@ -382,7 +378,7 @@ struct fh_lock
     union
       {
         struct file_identity *file; /* FH_REF_FILE only. */
-        unsigned int unique_id;    /* FH_REF_SCRATCH only. */
+        unsigned int unique_id;    /* FH_REF_DATASET only. */
       }
     u;
     enum fh_access access;      /* Type of file access. */
@@ -590,11 +586,8 @@ make_key (struct fh_lock *lock, const struct file_handle 
*h,
   lock->access = access;
   if (lock->referent == FH_REF_FILE)
     lock->u.file = fn_get_identity (fh_get_file_name (h));
-  else if (lock->referent == FH_REF_SCRATCH)
-    {
-      struct scratch_handle *sh = fh_get_scratch_handle (h);
-      lock->u.unique_id = sh != NULL ? sh->unique_id : 0;
-    }
+  else if (lock->referent == FH_REF_DATASET)
+    lock->u.unique_id = dataset_seqno (fh_get_dataset (h));
 }
 
 /* Frees the key fields in LOCK. */
@@ -616,7 +609,7 @@ compare_fh_locks (const struct fh_lock *a, const struct 
fh_lock *b)
     return a->access < b->access ? -1 : 1;
   else if (a->referent == FH_REF_FILE)
     return fn_compare_file_identities (a->u.file, b->u.file);
-  else if (a->referent == FH_REF_SCRATCH)
+  else if (a->referent == FH_REF_DATASET)
     return (a->u.unique_id < b->u.unique_id ? -1
             : a->u.unique_id > b->u.unique_id);
   else
@@ -630,7 +623,7 @@ hash_fh_lock (const struct fh_lock *lock)
   unsigned int basis;
   if (lock->referent == FH_REF_FILE)
     basis = fn_hash_identity (lock->u.file);
-  else if (lock->referent == FH_REF_SCRATCH)
+  else if (lock->referent == FH_REF_DATASET)
     basis = lock->u.unique_id;
   else
     basis = 0;
diff --git a/src/data/file-handle-def.h b/src/data/file-handle-def.h
index 2df85f9..11898ef 100644
--- a/src/data/file-handle-def.h
+++ b/src/data/file-handle-def.h
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000, 2005, 2006, 2011 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 2005, 2006, 2010, 2011 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
@@ -20,6 +20,8 @@
 #include <stdbool.h>
 #include <stddef.h>
 
+struct dataset;
+
 /* What a file handle refers to.
    (Ordinarily only a single value is allowed, but fh_open()
    and fh_parse() take a mask.) */
@@ -27,7 +29,7 @@ enum fh_referent
   {
     FH_REF_FILE = 001,          /* Ordinary file (the most common case). */
     FH_REF_INLINE = 002,        /* The inline file. */
-    FH_REF_SCRATCH = 004        /* Temporary dataset. */
+    FH_REF_DATASET = 004        /* Dataset. */
   };
 
 /* File modes. */
@@ -63,7 +65,7 @@ void fh_done (void);
 struct file_handle *fh_create_file (const char *handle_name,
                                     const char *file_name,
                                     const struct fh_properties *);
-struct file_handle *fh_create_scratch (const char *handle_name);
+struct file_handle *fh_create_dataset (struct dataset *);
 const struct fh_properties *fh_default_properties (void);
 
 /* Reference management. */
@@ -90,9 +92,8 @@ size_t fh_get_record_width (const struct file_handle *);
 size_t fh_get_tab_width (const struct file_handle *);
 const char *fh_get_legacy_encoding (const struct file_handle *);
 
-/* Properties of FH_REF_SCRATCH file handles. */
-struct scratch_handle *fh_get_scratch_handle (const struct file_handle *);
-void fh_set_scratch_handle (struct file_handle *, struct scratch_handle *);
+/* Properties of FH_REF_DATASET file handles. */
+struct dataset *fh_get_dataset (const struct file_handle *);
 
 /* Mutual exclusion for access . */
 struct fh_lock *fh_lock (struct file_handle *, enum fh_referent mask,
diff --git a/src/data/scratch-handle.c b/src/data/scratch-handle.c
deleted file mode 100644
index a95f2cc..0000000
--- a/src/data/scratch-handle.c
+++ /dev/null
@@ -1,36 +0,0 @@
-/* PSPP - a program for statistical analysis.
-   Copyright (C) 2006, 2011 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 "data/scratch-handle.h"
-
-#include <stdlib.h>
-
-#include "data/casereader.h"
-#include "data/dictionary.h"
-
-/* Destroys HANDLE. */
-void
-scratch_handle_destroy (struct scratch_handle *handle)
-{
-  if (handle != NULL)
-    {
-      dict_destroy (handle->dictionary);
-      casereader_destroy (handle->casereader);
-      free (handle);
-    }
-}
diff --git a/src/data/scratch-handle.h b/src/data/scratch-handle.h
deleted file mode 100644
index c7775f4..0000000
--- a/src/data/scratch-handle.h
+++ /dev/null
@@ -1,32 +0,0 @@
-/* PSPP - a program for statistical analysis.
-   Copyright (C) 2006 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 SCRATCH_HANDLE_H
-#define SCRATCH_HANDLE_H 1
-
-#include <stdbool.h>
-
-/* A scratch file. */
-struct scratch_handle
-  {
-    unsigned int unique_id;             /* Identifies this scratch file. */
-    struct dictionary *dictionary;      /* Dictionary. */
-    struct casereader *casereader;      /* Cases. */
-  };
-
-void scratch_handle_destroy (struct scratch_handle *);
-
-#endif /* scratch-handle.h */
diff --git a/src/data/session.c b/src/data/session.c
new file mode 100644
index 0000000..24c22b2
--- /dev/null
+++ b/src/data/session.c
@@ -0,0 +1,180 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2010, 2011 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 "data/session.h"
+
+#include <assert.h>
+#include <stdlib.h>
+
+#include "data/dataset.h"
+#include "libpspp/assertion.h"
+#include "libpspp/cast.h"
+#include "libpspp/hash-functions.h"
+#include "libpspp/str.h"
+#include "libpspp/hmapx.h"
+
+#include "gl/xalloc.h"
+
+struct session
+  {
+    struct hmapx datasets;
+    struct dataset *active;
+    char *syntax_encoding;      /* Default encoding for syntax files. */
+  };
+
+static struct hmapx_node *session_lookup_dataset__ (const struct session *,
+                                                    const char *name);
+
+struct session *
+session_create (void)
+{
+  struct session *s;
+
+  s = xmalloc (sizeof *s);
+  hmapx_init (&s->datasets);
+  s->active = NULL;
+  s->syntax_encoding = xstrdup ("Auto");
+  return s;
+}
+
+void
+session_destroy (struct session *s)
+{
+  if (s != NULL)
+    {
+      struct hmapx_node *node, *next;
+      struct dataset *ds;
+
+      s->active = NULL;
+      HMAPX_FOR_EACH_SAFE (ds, node, next, &s->datasets)
+        dataset_destroy (ds);
+      free (s->syntax_encoding);
+      free (s);
+    }
+}
+
+struct dataset *
+session_active_dataset (struct session *s)
+{
+  return s->active;
+}
+
+void
+session_set_active_dataset (struct session *s, struct dataset *ds)
+{
+  assert (ds == NULL || dataset_session (ds) == s);
+  s->active = ds;
+}
+
+void
+session_add_dataset (struct session *s, struct dataset *ds)
+{
+  struct dataset *old;
+
+  old = session_lookup_dataset (s, dataset_name (ds));
+  if (old == s->active)
+    s->active = ds;
+  if (old != NULL)
+    session_remove_dataset (s, old);
+
+  hmapx_insert (&s->datasets, ds, hash_case_string (dataset_name (ds), 0));
+  if (s->active == NULL)
+    s->active = ds;
+
+  dataset_set_session__ (ds, s);
+}
+
+void
+session_remove_dataset (struct session *s, struct dataset *ds)
+{
+  assert (ds != s->active);
+  hmapx_delete (&s->datasets, session_lookup_dataset__ (s, dataset_name (ds)));
+  dataset_set_session__ (ds, NULL);
+}
+
+struct dataset *
+session_lookup_dataset (const struct session *s, const char *name)
+{
+  struct hmapx_node *node = session_lookup_dataset__ (s, name);
+  return node != NULL ? node->data : NULL;
+}
+
+struct dataset *
+session_lookup_dataset_assert (const struct session *s, const char *name)
+{
+  struct dataset *ds = session_lookup_dataset (s, name);
+  assert (ds != NULL);
+  return ds;
+}
+
+void
+session_set_default_syntax_encoding (struct session *s, const char *encoding)
+{
+  free (s->syntax_encoding);
+  s->syntax_encoding = xstrdup (encoding);
+}
+
+const char *
+session_get_default_syntax_encoding (const struct session *s)
+{
+  return s->syntax_encoding;
+}
+
+size_t
+session_n_datasets (const struct session *s)
+{
+  return hmapx_count (&s->datasets);
+}
+
+void
+session_for_each_dataset (const struct session *s,
+                          void (*cb) (struct dataset *, void *aux),
+                          void *aux)
+{
+  struct hmapx_node *node, *next;
+  struct dataset *ds;
+
+  HMAPX_FOR_EACH_SAFE (ds, node, next, &s->datasets)
+    cb (ds, aux);
+}
+
+struct dataset *
+session_get_dataset_by_seqno (const struct session *s, unsigned int seqno)
+{
+  struct hmapx_node *node;
+  struct dataset *ds;
+
+  HMAPX_FOR_EACH (ds, node, &s->datasets)
+    if (dataset_seqno (ds) == seqno)
+      return ds;
+  return NULL;
+}
+
+static struct hmapx_node *
+session_lookup_dataset__ (const struct session *s_, const char *name)
+{
+  struct session *s = CONST_CAST (struct session *, s_);
+  struct hmapx_node *node;
+  struct dataset *ds;
+
+  HMAPX_FOR_EACH_WITH_HASH (ds, node, hash_case_string (name, 0), &s->datasets)
+    if (!strcasecmp (dataset_name (ds), name))
+      return node;
+
+  return NULL;
+}
diff --git a/src/data/session.h b/src/data/session.h
new file mode 100644
index 0000000..f45cceb
--- /dev/null
+++ b/src/data/session.h
@@ -0,0 +1,47 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2010, 2011 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 SESSION_H
+#define SESSION_H 1
+
+#include <stddef.h>
+
+struct dataset;
+
+struct session *session_create (void);
+void session_destroy (struct session *);
+
+struct dataset *session_active_dataset (struct session *);
+void session_set_active_dataset (struct session *, struct dataset *);
+
+void session_add_dataset (struct session *, struct dataset *);
+void session_remove_dataset (struct session *, struct dataset *);
+struct dataset *session_lookup_dataset (const struct session *, const char *);
+struct dataset *session_lookup_dataset_assert (const struct session *,
+                                               const char *);
+
+void session_set_default_syntax_encoding (struct session *, const char *);
+const char *session_get_default_syntax_encoding (const struct session *);
+
+size_t session_n_datasets (const struct session *);
+void session_for_each_dataset (const struct session *,
+                               void (*cb) (struct dataset *, void *aux),
+                               void *aux);
+
+struct dataset *session_get_dataset_by_seqno (const struct session *,
+                                              unsigned int seqno);
+
+#endif /* session.h */
diff --git a/src/language/command.c b/src/language/command.c
index f5db973..ff2b030 100644
--- a/src/language/command.c
+++ b/src/language/command.c
@@ -26,6 +26,7 @@
 #include "data/casereader.h"
 #include "data/dataset.h"
 #include "data/dictionary.h"
+#include "data/session.h"
 #include "data/settings.h"
 #include "data/variable.h"
 #include "language/lexer/command-name.h"
@@ -129,10 +130,12 @@ enum cmd_result
 cmd_parse_in_state (struct lexer *lexer, struct dataset *ds,
                    enum cmd_state state)
 {
+  struct session *session = dataset_session (ds);
   int result;
 
   result = do_parse_command (lexer, ds, state);
 
+  ds = session_active_dataset (session);
   assert (!proc_is_open (ds));
   unset_cmd_algorithm ();
   dict_clear_aux (dataset_dict (ds));
diff --git a/src/language/command.def b/src/language/command.def
index 955a8ac..016afcb 100644
--- a/src/language/command.def
+++ b/src/language/command.def
@@ -18,6 +18,12 @@
 DEF_CMD (S_ANY, F_ENHANCED, "CLOSE FILE HANDLE", cmd_close_file_handle)
 DEF_CMD (S_ANY, 0, "CACHE", cmd_cache)
 DEF_CMD (S_ANY, 0, "CD", cmd_cd)
+DEF_CMD (S_ANY, 0, "DATASET ACTIVATE", cmd_dataset_activate)
+DEF_CMD (S_ANY, 0, "DATASET DECLARE", cmd_dataset_declare)
+DEF_CMD (S_ANY, 0, "DATASET CLOSE", cmd_dataset_close)
+DEF_CMD (S_ANY, 0, "DATASET COPY", cmd_dataset_copy)
+DEF_CMD (S_ANY, 0, "DATASET NAME", cmd_dataset_name)
+DEF_CMD (S_ANY, 0, "DATASET DISPLAY", cmd_dataset_display)
 DEF_CMD (S_ANY, 0, "DO REPEAT", cmd_do_repeat)
 DEF_CMD (S_ANY, 0, "END REPEAT", cmd_end_repeat)
 DEF_CMD (S_ANY, 0, "ECHO", cmd_echo)
@@ -172,12 +178,6 @@ UNIMPL_CMD ("CSSELECT", "Select complex samples")
 UNIMPL_CMD ("CSTABULATE", "Tabulate complex samples")
 UNIMPL_CMD ("CTABLES", "Display complex samples")
 UNIMPL_CMD ("CURVEFIT", "Fit curve to line plot")
-UNIMPL_CMD ("DATASET ACTIVATE", "Switch to alternate data set")
-UNIMPL_CMD ("DATASET CLOSE", "Delete alternate data set")
-UNIMPL_CMD ("DATASET COPY", "Duplicate alternate data set")
-UNIMPL_CMD ("DATASET DECLARE", "Start alternate data set")
-UNIMPL_CMD ("DATASET DISPLAY", "List alternate data sets")
-UNIMPL_CMD ("DATASET NAME", "Give the current data set a name")
 UNIMPL_CMD ("DATE", "Create time series data")
 UNIMPL_CMD ("DEFINE", "Syntax macros")
 UNIMPL_CMD ("DETECTANOMALY", "Find unusual cases")
diff --git a/src/language/data-io/automake.mk b/src/language/data-io/automake.mk
index 63c2c96..0695bd5 100644
--- a/src/language/data-io/automake.mk
+++ b/src/language/data-io/automake.mk
@@ -13,6 +13,7 @@ language_data_io_sources = \
        src/language/data-io/data-reader.h \
        src/language/data-io/data-writer.c \
        src/language/data-io/data-writer.h \
+       src/language/data-io/dataset.c \
        src/language/data-io/file-handle.h \
        src/language/data-io/get-data.c \
        src/language/data-io/get.c \
diff --git a/src/language/data-io/combine-files.c 
b/src/language/data-io/combine-files.c
index 5f82d15..e09b36e 100644
--- a/src/language/data-io/combine-files.c
+++ b/src/language/data-io/combine-files.c
@@ -223,7 +223,7 @@ combine_files (enum comb_command_type command,
         }
       else
         {
-          file->handle = fh_parse (lexer, FH_REF_FILE | FH_REF_SCRATCH);
+          file->handle = fh_parse (lexer, FH_REF_FILE, dataset_session (ds));
           if (file->handle == NULL)
             goto error;
 
diff --git a/src/language/data-io/data-list.c b/src/language/data-io/data-list.c
index 8ab7588..9beaea9 100644
--- a/src/language/data-io/data-list.c
+++ b/src/language/data-io/data-list.c
@@ -101,7 +101,7 @@ cmd_data_list (struct lexer *lexer, struct dataset *ds)
        {
          lex_match (lexer, T_EQUALS);
           fh_unref (fh);
-         fh = fh_parse (lexer, FH_REF_FILE | FH_REF_INLINE);
+         fh = fh_parse (lexer, FH_REF_FILE | FH_REF_INLINE, NULL);
          if (fh == NULL)
            goto error;
        }
diff --git a/src/language/data-io/dataset.c b/src/language/data-io/dataset.c
new file mode 100644
index 0000000..dbf0d35
--- /dev/null
+++ b/src/language/data-io/dataset.c
@@ -0,0 +1,279 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2010, 2011 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 "language/command.h"
+
+#include "data/dataset.h"
+#include "data/session.h"
+#include "language/lexer/lexer.h"
+#include "libpspp/message.h"
+#include "output/tab.h"
+
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
+
+static int
+parse_window (struct lexer *lexer, unsigned int allowed,
+              enum dataset_display def)
+{
+  if (!lex_match_id (lexer, "WINDOW"))
+    return def;
+  lex_match (lexer, T_EQUALS);
+
+  if (allowed & (1 << DATASET_MINIMIZED) && lex_match_id (lexer, "MINIMIZED"))
+    return DATASET_MINIMIZED;
+  else if (allowed & (1 << DATASET_ASIS) && lex_match_id (lexer, "ASIS"))
+    return DATASET_ASIS;
+  else if (allowed & (1 << DATASET_FRONT) && lex_match_id (lexer, "FRONT"))
+    return DATASET_FRONT;
+  else if (allowed & (1 << DATASET_HIDDEN) && lex_match_id (lexer, "HIDDEN"))
+    return DATASET_HIDDEN;
+
+  lex_error (lexer, NULL);
+  return -1;
+}
+
+static struct dataset *
+parse_dataset_name (struct lexer *lexer, struct session *session)
+{
+  struct dataset *ds;
+
+  if (!lex_force_id (lexer))
+    return NULL;
+
+  ds = session_lookup_dataset (session, lex_tokcstr (lexer));
+  if (ds != NULL)
+    lex_get (lexer);
+  else
+    msg (SE, _("There is no dataset named %s."), lex_tokcstr (lexer));
+  return ds;
+}
+
+int
+cmd_dataset_name (struct lexer *lexer, struct dataset *active)
+{
+  int display;
+
+  if (!lex_force_id (lexer))
+    return CMD_FAILURE;
+  dataset_set_name (active, lex_tokcstr (lexer));
+  lex_get (lexer);
+
+  display = parse_window (lexer, (1 << DATASET_ASIS) | (1 << DATASET_FRONT),
+                          DATASET_ASIS);
+  if (display < 0)
+    return CMD_FAILURE;
+  else if (display != DATASET_ASIS)
+    dataset_set_display (active, display);
+
+  return CMD_SUCCESS;
+}
+
+int
+cmd_dataset_activate (struct lexer *lexer, struct dataset *active)
+{
+  struct session *session = dataset_session (active);
+  struct dataset *ds;
+  int display;
+
+  ds = parse_dataset_name (lexer, session);
+  if (ds == NULL)
+    return CMD_FAILURE;
+
+  if (ds != active)
+    {
+      proc_execute (active);
+      session_set_active_dataset (session, ds);
+      if (dataset_name (active)[0] == '\0')
+        dataset_destroy (active);
+      return CMD_SUCCESS;
+    }
+
+  display = parse_window (lexer, (1 << DATASET_ASIS) | (1 << DATASET_FRONT),
+                          DATASET_ASIS);
+  if (display < 0)
+    return CMD_FAILURE;
+  else if (display != DATASET_ASIS)
+    dataset_set_display (ds, display);
+
+  return CMD_SUCCESS;
+}
+
+int
+cmd_dataset_copy (struct lexer *lexer, struct dataset *old)
+{
+  struct session *session = dataset_session (old);
+  struct dataset *new;
+  int display;
+  char *name;
+
+  /* Parse the entire command first.  proc_execute() can attempt to parse
+     BEGIN DATA...END DATA and it will fail confusingly if we are in the
+     middle of the command at the point.  */
+  if (!lex_force_id (lexer))
+    return CMD_FAILURE;
+  name = xstrdup (lex_tokcstr (lexer));
+  lex_get (lexer);
+
+  display = parse_window (lexer, ((1 << DATASET_MINIMIZED)
+                                  | (1 << DATASET_HIDDEN)
+                                  | (1 << DATASET_FRONT)),
+                          DATASET_MINIMIZED);
+  if (display < 0)
+    {
+      free (name);
+      return CMD_FAILURE;
+    }
+
+  if (session_lookup_dataset (session, name) == old)
+    {
+      new = old;
+      dataset_set_name (old, "");
+    }
+  else
+    {
+      proc_execute (old);
+      new = dataset_clone (old, name);
+    }
+  dataset_set_display (new, display);
+
+  free (name);
+  return CMD_SUCCESS;
+}
+
+int
+cmd_dataset_declare (struct lexer *lexer, struct dataset *ds)
+{
+  struct session *session = dataset_session (ds);
+  struct dataset *new;
+  int display;
+
+  if (!lex_force_id (lexer))
+    return CMD_FAILURE;
+
+  new = session_lookup_dataset (session, lex_tokcstr (lexer));
+  if (new == NULL)
+    new = dataset_create (session, lex_tokcstr (lexer));
+  lex_get (lexer);
+
+  display = parse_window (lexer, ((1 << DATASET_MINIMIZED)
+                                  | (1 << DATASET_HIDDEN)
+                                  | (1 << DATASET_FRONT)),
+                          DATASET_MINIMIZED);
+  if (display < 0)
+    return CMD_FAILURE;
+  dataset_set_display (new, display);
+
+  return CMD_SUCCESS;
+}
+
+static void
+dataset_close_cb (struct dataset *ds, void *session_)
+{
+  struct session *session = session_;
+
+  if (ds != session_active_dataset (session))
+    dataset_destroy (ds);
+}
+
+int
+cmd_dataset_close (struct lexer *lexer, struct dataset *ds)
+{
+  struct session *session = dataset_session (ds);
+
+  if (lex_match (lexer, T_ALL))
+    {
+      session_for_each_dataset (session, dataset_close_cb, session);
+      dataset_set_name (session_active_dataset (session), "");
+    }
+  else
+    {
+      if (!lex_match (lexer, T_ASTERISK))
+        {
+          ds = parse_dataset_name (lexer, session);
+          if (ds == NULL)
+            return CMD_FAILURE;
+        }
+
+      if (ds == session_active_dataset (session))
+        dataset_set_name (ds, "");
+      else
+        dataset_destroy (ds);
+    }
+
+  return CMD_SUCCESS;
+}
+
+static void
+dataset_display_cb (struct dataset *ds, void *p_)
+{
+  struct dataset ***p = p_;
+  **p = ds;
+  (*p)++;
+}
+
+static int
+sort_datasets (const void *a_, const void *b_)
+{
+  struct dataset *const *a = a_;
+  struct dataset *const *b = b_;
+
+  return strcmp (dataset_name (*a), dataset_name (*b));
+}
+
+int
+cmd_dataset_display (struct lexer *lexer UNUSED, struct dataset *ds)
+{
+  struct session *session = dataset_session (ds);
+  struct dataset **datasets, **p;
+  struct tab_table *t;
+  size_t i, n;
+
+  n = session_n_datasets (session);
+  datasets = xmalloc (n * sizeof *datasets);
+  p = datasets;
+  session_for_each_dataset (session, dataset_display_cb, &p);
+  qsort (datasets, n, sizeof *datasets, sort_datasets);
+
+  t = tab_create (1, n + 1);
+  tab_headers (t, 0, 0, 1, 0);
+  tab_box (t, TAL_1, TAL_1, -1, TAL_1, 0, 0, tab_nc (t) - 1, tab_nr (t) - 1);
+  tab_hline (t, TAL_2, 0, 0, 1);
+  tab_text (t, 0, 0, TAB_LEFT | TAT_TITLE, _("Dataset"));
+  for (i = 0; i < n; i++)
+    {
+      struct dataset *ds = datasets[i];
+      const char *name;
+
+      name = dataset_name (ds);
+      if (name[0] == '\0')
+        name = _("unnamed dataset");
+
+      if (ds == session_active_dataset (session))
+        tab_text_format (t, 0, i + 1, TAB_LEFT, "%s %s",
+                         name, _("(active dataset)"));
+      else
+        tab_text (t, 0, i + 1, TAB_LEFT, name);
+    }
+  tab_title (t, "Open datasets.");
+  tab_submit (t);
+
+  free (datasets);
+
+  return CMD_SUCCESS;
+}
diff --git a/src/language/data-io/file-handle.h 
b/src/language/data-io/file-handle.h
index 2dfb5e1..e2a2b56 100644
--- a/src/language/data-io/file-handle.h
+++ b/src/language/data-io/file-handle.h
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000, 2011 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 2010, 2011 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,19 @@
    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 !file_handle_h
-#define file_handle_h 1
+#ifndef LANGUAGE_DATA_IO_FILE_HANDLE_H
+#define LANGUAGE_DATA_IO_FILE_HANDLE_H 1
 
-/* File handles. */
+/* Parsing file handles. */
 
 #include <stdbool.h>
 #include <stddef.h>
 #include "data/file-handle-def.h"
 
-struct lexer ;
-struct file_handle *fh_parse (struct lexer *, enum fh_referent);
+struct lexer;
+struct session;
 
-#endif /* !file_handle.h */
+struct file_handle *fh_parse (struct lexer *, enum fh_referent,
+                              struct session *);
+
+#endif  /* language/data-io/file-handle.h */
diff --git a/src/language/data-io/file-handle.q 
b/src/language/data-io/file-handle.q
index 786955b..9bf6a6b 100644
--- a/src/language/data-io/file-handle.q
+++ b/src/language/data-io/file-handle.q
@@ -21,6 +21,7 @@
 #include <stdlib.h>
 
 #include "data/file-name.h"
+#include "data/session.h"
 #include "language/command.h"
 #include "language/data-io/file-handle.h"
 #include "language/lexer/lexer.h"
@@ -43,7 +44,7 @@
      name=string;
      lrecl=integer;
      tabwidth=integer "x>=0" "%s must be nonnegative";
-     mode=mode:!character/binary/image/360/scratch;
+     mode=mode:!character/binary/image/360;
      recform=recform:fixed/f/variable/v/spanned/vs.
 */
 /* (declarations) */
@@ -52,6 +53,7 @@
 int
 cmd_file_handle (struct lexer *lexer, struct dataset *ds)
 {
+  struct fh_properties properties;
   struct cmd_file_handle cmd;
   struct file_handle *handle;
   enum cmd_result result;
@@ -81,71 +83,65 @@ cmd_file_handle (struct lexer *lexer, struct dataset *ds)
   if (lex_end_of_command (lexer) != CMD_SUCCESS)
     goto exit_free_cmd;
 
-  if (cmd.mode != FH_SCRATCH)
+  properties = *fh_default_properties ();
+  if (cmd.s_name == NULL)
     {
-      struct fh_properties properties = *fh_default_properties ();
+      lex_sbc_missing (lexer, "NAME");
+      goto exit_free_cmd;
+    }
 
-      if (cmd.s_name == NULL)
+  switch (cmd.mode)
+    {
+    case FH_CHARACTER:
+      properties.mode = FH_MODE_TEXT;
+      if (cmd.sbc_tabwidth)
+        properties.tab_width = cmd.n_tabwidth[0];
+      break;
+    case FH_IMAGE:
+      properties.mode = FH_MODE_FIXED;
+      break;
+    case FH_BINARY:
+      properties.mode = FH_MODE_VARIABLE;
+      break;
+    case FH_360:
+      properties.encoding = "EBCDIC-US";
+      if (cmd.recform == FH_FIXED || cmd.recform == FH_F)
+        properties.mode = FH_MODE_FIXED;
+      else if (cmd.recform == FH_VARIABLE || cmd.recform == FH_V)
         {
-          lex_sbc_missing (lexer, "NAME");
-          goto exit_free_cmd;
+          properties.mode = FH_MODE_360_VARIABLE;
+          properties.record_width = 8192;
         }
-
-      switch (cmd.mode)
+      else if (cmd.recform == FH_SPANNED || cmd.recform == FH_VS)
         {
-        case FH_CHARACTER:
-          properties.mode = FH_MODE_TEXT;
-          if (cmd.sbc_tabwidth)
-            properties.tab_width = cmd.n_tabwidth[0];
-          break;
-        case FH_IMAGE:
-          properties.mode = FH_MODE_FIXED;
-          break;
-        case FH_BINARY:
-          properties.mode = FH_MODE_VARIABLE;
-          break;
-        case FH_360:
-          properties.encoding = "EBCDIC-US";
-          if (cmd.recform == FH_FIXED || cmd.recform == FH_F)
-            properties.mode = FH_MODE_FIXED;
-          else if (cmd.recform == FH_VARIABLE || cmd.recform == FH_V)
-            {
-              properties.mode = FH_MODE_360_VARIABLE;
-              properties.record_width = 8192;
-            }
-          else if (cmd.recform == FH_SPANNED || cmd.recform == FH_VS)
-            {
-              properties.mode = FH_MODE_360_SPANNED;
-              properties.record_width = 8192;
-            }
-          else
-            {
-              msg (SE, _("RECFORM must be specified with MODE=360."));
-              goto exit_free_cmd;
-            }
-          break;
-        default:
-          NOT_REACHED ();
+          properties.mode = FH_MODE_360_SPANNED;
+          properties.record_width = 8192;
         }
-
-      if (properties.mode == FH_MODE_FIXED || cmd.n_lrecl[0] != LONG_MIN)
+      else
         {
-          if (cmd.n_lrecl[0] == LONG_MIN)
-            msg (SE, _("The specified file mode requires LRECL.  "
-                       "Assuming %zu-character records."),
-                 properties.record_width);
-          else if (cmd.n_lrecl[0] < 1 || cmd.n_lrecl[0] >= (1UL << 31))
-            msg (SE, _("Record length (%ld) must be between 1 and %lu bytes.  "
-                       "Assuming %zu-character records."),
-                 cmd.n_lrecl[0], (1UL << 31) - 1, properties.record_width);
-          else
-            properties.record_width = cmd.n_lrecl[0];
+          msg (SE, _("RECFORM must be specified with MODE=360."));
+          goto exit_free_cmd;
         }
+      break;
+    default:
+      NOT_REACHED ();
+    }
 
-      fh_create_file (handle_name, cmd.s_name, &properties);
+  if (properties.mode == FH_MODE_FIXED || cmd.n_lrecl[0] != LONG_MIN)
+    {
+      if (cmd.n_lrecl[0] == LONG_MIN)
+        msg (SE, _("The specified file mode requires LRECL.  "
+                   "Assuming %zu-character records."),
+             properties.record_width);
+      else if (cmd.n_lrecl[0] < 1 || cmd.n_lrecl[0] >= (1UL << 31))
+        msg (SE, _("Record length (%ld) must be between 1 and %lu bytes.  "
+                   "Assuming %zu-character records."),
+             cmd.n_lrecl[0], (1UL << 31) - 1, properties.record_width);
+      else
+        properties.record_width = cmd.n_lrecl[0];
     }
-  else
-    fh_create_scratch (handle_name);
+
+  fh_create_file (handle_name, cmd.s_name, &properties);
 
   result = CMD_SUCCESS;
 
@@ -182,25 +178,47 @@ referent_name (enum fh_referent referent)
       return _("file");
     case FH_REF_INLINE:
       return _("inline file");
-    case FH_REF_SCRATCH:
-      return _("scratch file");
+    case FH_REF_DATASET:
+      return _("dataset");
     default:
       NOT_REACHED ();
     }
 }
 
-/* Parses a file handle name, which may be a file name as a string
-   or a file handle name as an identifier.  The allowed types of
-   file handle are restricted to those in REFERENT_MASK.  Returns
-   the file handle when successful, a null pointer on failure.
+/* Parses a file handle name:
 
-   The caller is responsible for fh_unref()'ing the returned
-   file handle when it is no longer needed. */
+      - If SESSION is nonnull, then the parsed syntax may be the name of a
+        dataset within SESSION.  Dataset names take precedence over file handle
+        names.
+
+      - If REFERENT_MASK includes FH_REF_FILE, the parsed syntax may be a file
+        name as a string or a file handle name as an identifier.
+
+      - If REFERENT_MASK includes FH_REF_INLINE, the parsed syntax may be the
+        identifier INLINE to represent inline data.
+
+   Returns the file handle when successful, a null pointer on failure.
+
+   The caller is responsible for fh_unref()'ing the returned file handle when
+   it is no longer needed. */
 struct file_handle *
-fh_parse (struct lexer *lexer, enum fh_referent referent_mask)
+fh_parse (struct lexer *lexer, enum fh_referent referent_mask,
+          struct session *session)
 {
   struct file_handle *handle;
 
+  if (session != NULL && lex_token (lexer) == T_ID)
+    {
+      struct dataset *ds;
+
+      ds = session_lookup_dataset (session, lex_tokcstr (lexer));
+      if (ds != NULL)
+        {
+          lex_get (lexer);
+          return fh_create_dataset (ds);
+        }
+    }
+
   if (lex_match_id (lexer, "INLINE"))
     handle = fh_inline_file ();
   else
@@ -215,14 +233,8 @@ fh_parse (struct lexer *lexer, enum fh_referent 
referent_mask)
       if (lex_token (lexer) == T_ID)
         handle = fh_from_id (lex_tokcstr (lexer));
       if (handle == NULL)
-        {
-          if (lex_token (lexer) != T_ID || lex_tokcstr (lexer)[0] != '#'
-              || settings_get_syntax () != ENHANCED)
             handle = fh_create_file (NULL, lex_tokcstr (lexer),
                                      fh_default_properties ());
-          else
-            handle = fh_create_scratch (lex_tokcstr (lexer));
-        }
       lex_get (lexer);
     }
 
diff --git a/src/language/data-io/get-data.c b/src/language/data-io/get-data.c
index d792752..e3e6c5d 100644
--- a/src/language/data-io/get-data.c
+++ b/src/language/data-io/get-data.c
@@ -293,7 +293,7 @@ parse_get_txt (struct lexer *lexer, struct dataset *ds)
   if (!lex_force_match_id (lexer, "FILE"))
     goto error;
   lex_force_match (lexer, T_EQUALS);
-  fh = fh_parse (lexer, FH_REF_FILE | FH_REF_INLINE);
+  fh = fh_parse (lexer, FH_REF_FILE | FH_REF_INLINE, NULL);
   if (fh == NULL)
     goto error;
 
diff --git a/src/language/data-io/get.c b/src/language/data-io/get.c
index 466d2a5..0e542ef 100644
--- a/src/language/data-io/get.c
+++ b/src/language/data-io/get.c
@@ -82,7 +82,7 @@ parse_read_command (struct lexer *lexer, struct dataset *ds, 
enum reader_command
          lex_match (lexer, T_EQUALS);
 
           fh_unref (fh);
-         fh = fh_parse (lexer, FH_REF_FILE | FH_REF_SCRATCH);
+         fh = fh_parse (lexer, FH_REF_FILE, NULL);
          if (fh == NULL)
             goto error;
        }
diff --git a/src/language/data-io/inpt-pgm.c b/src/language/data-io/inpt-pgm.c
index 99cc17a..eac1f06 100644
--- a/src/language/data-io/inpt-pgm.c
+++ b/src/language/data-io/inpt-pgm.c
@@ -290,7 +290,7 @@ cmd_reread (struct lexer *lexer, struct dataset *ds)
        {
          lex_match (lexer, T_EQUALS);
           fh_unref (fh);
-          fh = fh_parse (lexer, FH_REF_FILE | FH_REF_INLINE);
+          fh = fh_parse (lexer, FH_REF_FILE | FH_REF_INLINE, NULL);
          if (fh == NULL)
            {
              expr_free (e);
diff --git a/src/language/data-io/print-space.c 
b/src/language/data-io/print-space.c
index fd0d4cf..b7245e6 100644
--- a/src/language/data-io/print-space.c
+++ b/src/language/data-io/print-space.c
@@ -56,7 +56,7 @@ cmd_print_space (struct lexer *lexer, struct dataset *ds)
     {
       lex_match (lexer, T_EQUALS);
 
-      handle = fh_parse (lexer, FH_REF_FILE);
+      handle = fh_parse (lexer, FH_REF_FILE, NULL);
       if (handle == NULL)
        return CMD_FAILURE;
     }
diff --git a/src/language/data-io/print.c b/src/language/data-io/print.c
index 169b6a5..7b795f1 100644
--- a/src/language/data-io/print.c
+++ b/src/language/data-io/print.c
@@ -156,7 +156,7 @@ internal_cmd_print (struct lexer *lexer, struct dataset *ds,
        {
          lex_match (lexer, T_EQUALS);
 
-         fh = fh_parse (lexer, FH_REF_FILE);
+         fh = fh_parse (lexer, FH_REF_FILE, NULL);
          if (fh == NULL)
            goto error;
        }
diff --git a/src/language/data-io/save-translate.c 
b/src/language/data-io/save-translate.c
index 17293cf..f6487c5 100644
--- a/src/language/data-io/save-translate.c
+++ b/src/language/data-io/save-translate.c
@@ -96,7 +96,7 @@ cmd_save_translate (struct lexer *lexer, struct dataset *ds)
 
          lex_match (lexer, T_EQUALS);
 
-         handle = fh_parse (lexer, FH_REF_FILE);
+         handle = fh_parse (lexer, FH_REF_FILE, NULL);
          if (handle == NULL)
            goto error;
        }
diff --git a/src/language/data-io/save.c b/src/language/data-io/save.c
index 48645fc..cf84736 100644
--- a/src/language/data-io/save.c
+++ b/src/language/data-io/save.c
@@ -196,7 +196,7 @@ parse_write_command (struct lexer *lexer, struct dataset 
*ds,
 
          lex_match (lexer, T_EQUALS);
 
-         handle = fh_parse (lexer, FH_REF_FILE | FH_REF_SCRATCH);
+         handle = fh_parse (lexer, FH_REF_FILE, NULL);
          if (handle == NULL)
            goto error;
        }
diff --git a/src/language/dictionary/apply-dictionary.c 
b/src/language/dictionary/apply-dictionary.c
index 78824cf..c2de931 100644
--- a/src/language/dictionary/apply-dictionary.c
+++ b/src/language/dictionary/apply-dictionary.c
@@ -50,7 +50,7 @@ cmd_apply_dictionary (struct lexer *lexer, struct dataset *ds)
   lex_match_id (lexer, "FROM");
   lex_match (lexer, T_EQUALS);
 
-  handle = fh_parse (lexer, FH_REF_FILE | FH_REF_SCRATCH);
+  handle = fh_parse (lexer, FH_REF_FILE, dataset_session (ds));
   if (!handle)
     return CMD_FAILURE;
   reader = any_reader_open (handle, &dict);
diff --git a/src/language/dictionary/sys-file-info.c 
b/src/language/dictionary/sys-file-info.c
index 3f00106..27b2785 100644
--- a/src/language/dictionary/sys-file-info.c
+++ b/src/language/dictionary/sys-file-info.c
@@ -77,7 +77,7 @@ cmd_sysfile_info (struct lexer *lexer, struct dataset *ds 
UNUSED)
   lex_match_id (lexer, "FILE");
   lex_match (lexer, T_EQUALS);
 
-  h = fh_parse (lexer, FH_REF_FILE);
+  h = fh_parse (lexer, FH_REF_FILE, NULL);
   if (!h)
     return CMD_FAILURE;
 
diff --git a/src/language/expressions/evaluate.c 
b/src/language/expressions/evaluate.c
index 56deb9a..1736ab8 100644
--- a/src/language/expressions/evaluate.c
+++ b/src/language/expressions/evaluate.c
@@ -155,7 +155,7 @@ cmd_debug_evaluate (struct lexer *lexer, struct dataset 
*dsother UNUSED)
 
          if  ( ds == NULL )
            {
-             ds = dataset_create ();
+             ds = dataset_create (NULL, "");
              d = dataset_dict (ds);
            }
 
diff --git a/src/language/stats/aggregate.c b/src/language/stats/aggregate.c
index 0dee9b4..fed7656 100644
--- a/src/language/stats/aggregate.c
+++ b/src/language/stats/aggregate.c
@@ -182,7 +182,7 @@ cmd_aggregate (struct lexer *lexer, struct dataset *ds)
   lex_match (lexer, T_EQUALS);
   if (!lex_match (lexer, T_ASTERISK))
     {
-      out_file = fh_parse (lexer, FH_REF_FILE | FH_REF_SCRATCH);
+      out_file = fh_parse (lexer, FH_REF_FILE, dataset_session (ds));
       if (out_file == NULL)
         goto error;
     }
diff --git a/src/language/utilities/include.c b/src/language/utilities/include.c
index 81a064d..bcee162 100644
--- a/src/language/utilities/include.c
+++ b/src/language/utilities/include.c
@@ -24,6 +24,7 @@
 
 #include "data/dataset.h"
 #include "data/file-name.h"
+#include "data/session.h"
 #include "language/command.h"
 #include "language/lexer/include-path.h"
 #include "language/lexer/lexer.h"
@@ -81,7 +82,8 @@ do_insert (struct lexer *lexer, struct dataset *ds, enum 
variant variant)
   error_mode = LEX_ERROR_CONTINUE;
   cd = false;
   status = CMD_FAILURE;
-  encoding = xstrdup (dataset_get_default_syntax_encoding (ds));
+  encoding = xstrdup (session_get_default_syntax_encoding (
+                        dataset_session (ds)));
   while ( T_ENDCMD != lex_token (lexer))
     {
       if (lex_match_id (lexer, "ENCODING"))
diff --git a/src/ui/gui/data-editor.ui b/src/ui/gui/data-editor.ui
index 6a37b02..498f3fc 100644
--- a/src/ui/gui/data-editor.ui
+++ b/src/ui/gui/data-editor.ui
@@ -55,6 +55,12 @@
           </object>
         </child>
         <child>
+          <object class="GtkAction" id="rename_dataset">
+            <property name="name">rename_dataset</property>
+           <property name="label" translatable="yes">_Rename 
Dataset...</property>
+          </object>
+        </child>
+        <child>
           <object class="GtkAction" id="file_save">
             <property name="stock-id">gtk-save</property>
             <property name="name">file_save</property>
@@ -480,6 +486,7 @@
           <separator/>
           <menuitem action="file_save"/>
           <menuitem action="file_save_as"/>
+          <menuitem action="rename_dataset"/>
           <separator/>
           <menu action="file-information">
             <menuitem action="file_information_working-file"/>
diff --git a/src/ui/gui/executor.c b/src/ui/gui/executor.c
index 6a3f1e1..8fb4c26 100644
--- a/src/ui/gui/executor.c
+++ b/src/ui/gui/executor.c
@@ -20,6 +20,7 @@
 
 #include "data/dataset.h"
 #include "data/lazy-casereader.h"
+#include "data/session.h"
 #include "language/command.h"
 #include "language/lexer/lexer.h"
 #include "libpspp/cast.h"
@@ -35,38 +36,80 @@ create_casereader_from_data_store (void *data_store_)
   return psppire_data_store_get_reader (data_store);
 }
 
+static void
+new_pdw_cb (struct dataset *ds, void *aux UNUSED)
+{
+  PsppireDataWindow *pdw = psppire_data_window_for_dataset (ds);
+  if (pdw == NULL)
+    pdw = PSPPIRE_DATA_WINDOW (psppire_data_window_new (ds));
+
+  switch (dataset_get_display (ds))
+    {
+    case DATASET_ASIS:
+      break;
+
+    case DATASET_FRONT:
+      gtk_widget_show (GTK_WIDGET (pdw));
+      gtk_window_deiconify (GTK_WINDOW (pdw));
+      gdk_window_raise (gtk_widget_get_window (GTK_WIDGET (pdw)));
+      psppire_data_window_set_default (pdw);
+      break;
+
+    case DATASET_MINIMIZED:
+      gtk_window_iconify (GTK_WINDOW (pdw));
+      gtk_widget_show (GTK_WIDGET (pdw));
+      psppire_data_window_undefault (pdw);
+      break;
+
+    case DATASET_HIDDEN:
+      gtk_widget_hide (GTK_WIDGET (pdw));
+      psppire_data_window_undefault (pdw);
+      break;
+    }
+  dataset_set_display (ds, DATASET_ASIS);
+}
+
 gboolean
 execute_syntax (PsppireDataWindow *window, struct lex_reader *lex_reader)
 {
   struct lexer *lexer;
   gboolean retval = TRUE;
 
-  struct casereader *reader;
-  const struct caseproto *proto;
-  casenumber case_cnt;
-  unsigned long int lazy_serial;
-
-  /* When the user executes a number of snippets of syntax in a
-     row, none of which read from the active dataset, the GUI becomes
-     progressively less responsive.  The reason is that each syntax
-     execution encapsulates the active dataset data in another
-     datasheet layer.  The cumulative effect of having a number of
-     layers of datasheets wastes time and space.
-
-     To solve the problem, we use a "lazy casereader", a wrapper
-     around the casereader obtained from the data store, that
-     only actually instantiates that casereader when it is
-     needed.  If the data store casereader is never needed, then
-     it is reused the next time syntax is run, without wrapping
-     it in another layer. */
-  proto = psppire_data_store_get_proto (window->data_store);
-  case_cnt = psppire_data_store_get_case_count (window->data_store);
-  reader = lazy_casereader_create (proto, case_cnt,
-                                   create_casereader_from_data_store,
-                                   window->data_store, &lazy_serial);
-  dataset_set_source (window->dataset, reader);
-
-  g_return_val_if_fail (dataset_has_source (window->dataset), FALSE);
+  PsppireDataWindow *pdw, *next_pdw;
+
+  ll_for_each (pdw, PsppireDataWindow, ll, &all_data_windows)
+    {
+      const struct caseproto *proto;
+      struct casereader *reader;
+      casenumber case_cnt;
+
+      /* When the user executes a number of snippets of syntax in a
+         row, none of which read from the active dataset, the GUI becomes
+         progressively less responsive.  The reason is that each syntax
+         execution encapsulates the active dataset data in another
+         datasheet layer.  The cumulative effect of having a number of
+         layers of datasheets wastes time and space.
+
+         To solve the problem, we use a "lazy casereader", a wrapper
+         around the casereader obtained from the data store, that
+         only actually instantiates that casereader when it is
+         needed.  If the data store casereader is never needed, then
+         it is reused the next time syntax is run, without wrapping
+         it in another layer. */
+      proto = psppire_data_store_get_proto (pdw->data_store);
+      case_cnt = psppire_data_store_get_case_count (pdw->data_store);
+      reader = lazy_casereader_create (proto, case_cnt,
+                                       create_casereader_from_data_store,
+                                       pdw->data_store, &pdw->lazy_serial);
+      dataset_set_source (pdw->dataset, reader);
+
+      if (pdw == window)
+        session_set_active_dataset (the_session, pdw->dataset);
+
+      g_return_val_if_fail (dataset_has_source (pdw->dataset), FALSE);
+
+      pdw->dataset_seqno = dataset_seqno (pdw->dataset);
+    }
 
   lexer = lex_create ();
   psppire_set_lexer (lexer);
@@ -74,7 +117,8 @@ execute_syntax (PsppireDataWindow *window, struct lex_reader 
*lex_reader)
 
   for (;;)
     {
-      enum cmd_result result = cmd_parse (lexer, window->dataset);
+      struct dataset *ds = session_active_dataset (the_session);
+      enum cmd_result result = cmd_parse (lexer, ds);
 
       if ( cmd_result_is_failure (result))
        {
@@ -87,14 +131,33 @@ execute_syntax (PsppireDataWindow *window, struct 
lex_reader *lex_reader)
        break;
     }
 
-  proc_execute (window->dataset);
+  ll_for_each_safe (pdw, next_pdw, PsppireDataWindow, ll, &all_data_windows)
+    {
+      struct dataset *ds;
+
+      ds = session_get_dataset_by_seqno (the_session, pdw->dataset_seqno);
+      if (ds != NULL)
+        {
+          struct casereader *reader;
+
+          pdw->dataset = ds;
+          proc_execute (pdw->dataset);
 
-  psppire_dict_replace_dictionary (window->data_store->dict,
-                                  dataset_dict (window->dataset));
+          psppire_dict_replace_dictionary (pdw->data_store->dict,
+                                           dataset_dict (pdw->dataset));
+
+          reader = dataset_steal_source (pdw->dataset);
+          if (!lazy_casereader_destroy (reader, pdw->lazy_serial))
+            psppire_data_store_set_reader (pdw->data_store, reader);
+
+          g_object_set (G_OBJECT (pdw), "id", dataset_name (pdw->dataset),
+                        (void *) NULL);
+        }
+      else
+        gtk_widget_destroy (GTK_WIDGET (pdw));
+    }
 
-  reader = dataset_steal_source (window->dataset);
-  if (!lazy_casereader_destroy (reader, lazy_serial))
-    psppire_data_store_set_reader (window->data_store, reader);
+  session_for_each_dataset (the_session, new_pdw_cb, NULL);
 
   /* Destroy the lexer only after obtaining the dataset, because the dataset
      might depend on the lexer, if the casereader specifies inline data.  (In
diff --git a/src/ui/gui/psppire-data-window.c b/src/ui/gui/psppire-data-window.c
index a463ee4..ef06d83 100644
--- a/src/ui/gui/psppire-data-window.c
+++ b/src/ui/gui/psppire-data-window.c
@@ -20,6 +20,7 @@
 #include <stdlib.h>
 
 #include "data/dataset.h"
+#include "data/session.h"
 #include "language/lexer/lexer.h"
 #include "libpspp/message.h"
 #include "ui/gui/aggregate-dialog.h"
@@ -30,6 +31,7 @@
 #include "ui/gui/correlation-dialog.h"
 #include "ui/gui/crosstabs-dialog.h"
 #include "ui/gui/descriptives-dialog.h"
+#include "ui/gui/entry-dialog.h"
 #include "ui/gui/examine-dialog.h"
 #include "ui/gui/executor.h"
 #include "ui/gui/factor-dialog.h"
@@ -61,11 +63,14 @@
 #include "ui/gui/weight-cases-dialog.h"
 #include "ui/syntax-gen.h"
 
+#include "gl/xvasprintf.h"
+
 #include <gettext.h>
 #define _(msgid) gettext (msgid)
 #define N_(msgid) msgid
 
-static PsppireDataWindow *the_data_window;
+struct session *the_session;
+struct ll_list all_data_windows = LL_INITIALIZER (all_data_windows);
 
 static void psppire_data_window_class_init    (PsppireDataWindowClass *class);
 static void psppire_data_window_init          (PsppireDataWindow      
*data_editor);
@@ -466,10 +471,11 @@ sysfile_info (PsppireDataWindow *de)
 }
 
 
-/* Callback for data_save_as action. Prompt for a filename and save */
+/* PsppireWindow 'pick_filename' callback: prompt for a filename to save as. */
 static void
-data_save_as_dialog (PsppireDataWindow *de)
+data_pick_filename (PsppireWindow *window)
 {
+  PsppireDataWindow *de = PSPPIRE_DATA_WINDOW (window);
   GtkWidget *button_sys;
   GtkWidget *dialog =
     gtk_file_chooser_dialog_new (_("Save"),
@@ -515,6 +521,9 @@ data_save_as_dialog (PsppireDataWindow *de)
     gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER(dialog), vbox);
   }
 
+  gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog),
+                                                  TRUE);
+
   switch (gtk_dialog_run (GTK_DIALOG (dialog)))
     {
     case GTK_RESPONSE_ACCEPT:
@@ -538,8 +547,6 @@ data_save_as_dialog (PsppireDataWindow *de)
 
        psppire_window_set_filename (PSPPIRE_WINDOW (de), filename->str);
 
-       save_file (PSPPIRE_WINDOW (de));
-
        g_string_free (filename, TRUE);
       }
       break;
@@ -550,32 +557,68 @@ data_save_as_dialog (PsppireDataWindow *de)
   gtk_widget_destroy (dialog);
 }
 
-
-/* Callback for data_save action.
- */
-static void
-data_save (PsppireWindow *de)
+static bool
+confirm_delete_dataset (PsppireDataWindow *de,
+                        const char *old_dataset,
+                        const char *new_dataset,
+                        const char *existing_dataset)
 {
-  const gchar *fn = psppire_window_get_filename (de);
+  GtkWidget *dialog;
+  int result;
 
-  if ( NULL != fn)
-    psppire_window_save (de);
-  else
-    data_save_as_dialog (PSPPIRE_DATA_WINDOW (de));
-}
+  dialog = gtk_message_dialog_new (
+    GTK_WINDOW (de), 0, GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE, "%s",
+    _("Delete Existing Dataset?"));
 
+  gtk_message_dialog_format_secondary_text (
+    GTK_MESSAGE_DIALOG (dialog),
+    _("Renaming \"%s\" to \"%s\" will delete destroy the existing "
+      "dataset named \"%s\".  Are you sure that you want to do this?"),
+    old_dataset, new_dataset, existing_dataset);
+
+  gtk_dialog_add_buttons (GTK_DIALOG (dialog),
+                          GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+                          GTK_STOCK_DELETE, GTK_RESPONSE_OK,
+                          NULL);
+
+  g_object_set (dialog, "icon-name", "psppicon", NULL);
+
+  result = gtk_dialog_run (GTK_DIALOG (dialog));
+
+  gtk_widget_destroy (dialog);
+
+  return result == GTK_RESPONSE_OK;
+}
 
-/* Callback for data_new action.
-   Performs the NEW FILE command */
 static void
-new_file (PsppireDataWindow *de)
+on_rename_dataset (PsppireDataWindow *de)
 {
-  execute_const_syntax_string (de, "NEW FILE.");
-  psppire_window_set_filename (PSPPIRE_WINDOW (de), NULL);
+  struct dataset *ds = de->dataset;
+  struct session *session = dataset_session (ds);
+  const char *old_name = dataset_name (ds);
+  struct dataset *existing_dataset;
+  char *new_name;
+  char *prompt;
+
+  prompt = xasprintf (_("Please enter a new name for dataset \"%s\":"),
+                      old_name);
+  new_name = entry_dialog_run (GTK_WINDOW (de), _("Rename Dataset"), prompt,
+                               old_name);
+  free (prompt);
+
+  if (new_name == NULL)
+    return;
+
+  existing_dataset = session_lookup_dataset (session, new_name);
+  if (existing_dataset == NULL || existing_dataset == ds
+      || confirm_delete_dataset (de, old_name, new_name,
+                                 dataset_name (existing_dataset)))
+    g_free (execute_syntax_string (de, g_strdup_printf ("DATASET NAME %s.",
+                                                        new_name)));
+
+  free (new_name);
 }
 
-
-
 static void
 on_edit_paste (PsppireDataWindow  *de)
 {
@@ -688,8 +731,6 @@ file_quit (PsppireDataWindow *de)
   /* FIXME: Need to be more intelligent here.
      Give the user the opportunity to save any unsaved data.
   */
-  g_object_unref (de->data_store);
-
   psppire_quit ();
 }
 
@@ -919,15 +960,17 @@ psppire_data_window_finish_init (PsppireDataWindow *de,
 
   connect_action (de, "edit_cut", G_CALLBACK (on_edit_cut));
 
-  connect_action (de, "file_new_data", G_CALLBACK (new_file));
+  connect_action (de, "file_new_data", G_CALLBACK (create_data_window));
 
   connect_action (de, "file_import-text", G_CALLBACK 
(text_data_import_assistant));
 
-  connect_action (de, "file_save", G_CALLBACK (data_save));
+  connect_action (de, "file_save", G_CALLBACK (psppire_window_save));
  
   connect_action (de, "file_open", G_CALLBACK (psppire_window_open));
 
-  connect_action (de, "file_save_as", G_CALLBACK (data_save_as_dialog));
+  connect_action (de, "file_save_as", G_CALLBACK (psppire_window_save_as));
+
+  connect_action (de, "rename_dataset", G_CALLBACK (on_rename_dataset));
 
   connect_action (de, "file_information_working-file", G_CALLBACK 
(display_dict));
 
@@ -1145,7 +1188,7 @@ psppire_data_window_finish_init (PsppireDataWindow *de,
   gtk_widget_show (GTK_WIDGET (de->data_editor));
   gtk_widget_show (box);
 
-  the_data_window = de;
+  ll_push_head (&all_data_windows, &de->ll);
 }
 
 static void
@@ -1159,10 +1202,26 @@ psppire_data_window_dispose (GObject *object)
       dw->builder = NULL;
     }
 
-  if (the_data_window == dw)
-    the_data_window = NULL;
+  if (dw->var_store)
+    {
+      g_object_unref (dw->var_store);
+      dw->var_store = NULL;
+    }
 
-  G_OBJECT_CLASS (parent_class)->dispose (object);
+  if (dw->data_store)
+    {
+      g_object_unref (dw->data_store);
+      dw->data_store = NULL;
+    }
+
+  if (dw->ll.next != NULL)
+    {
+      ll_remove (&dw->ll);
+      dw->ll.next = NULL;
+    }
+
+  if (G_OBJECT_CLASS (parent_class)->dispose)
+    G_OBJECT_CLASS (parent_class)->dispose (object);
 }
 
 static void
@@ -1203,33 +1262,89 @@ psppire_data_window_get_property (GObject         
*object,
     };
 }
 
-
 GtkWidget*
 psppire_data_window_new (struct dataset *ds)
 {
-  return GTK_WIDGET (
+  GtkWidget *dw;
+
+  if (the_session == NULL)
+    the_session = session_create ();
+
+  if (ds == NULL)
+    {
+      static int n_datasets;
+      char *dataset_name;
+
+      dataset_name = xasprintf ("DataSet%d", ++n_datasets);
+      ds = dataset_create (the_session, dataset_name);
+      free (dataset_name);
+    }
+  assert (dataset_session (ds) == the_session);
+
+  dw = GTK_WIDGET (
     g_object_new (
       psppire_data_window_get_type (),
       /* TRANSLATORS: This will form a filename.  Please avoid whitespace. */
-      "filename", _("PSPP-data"),
       "description", _("Data Editor"),
       "dataset", ds,
       NULL));
+
+  if (dataset_name (ds) != NULL)
+    g_object_set (dw, "id", dataset_name (ds), (void *) NULL);
+
+  return dw;
 }
 
+bool
+psppire_data_window_is_empty (PsppireDataWindow *dw)
+{
+  return psppire_var_store_get_var_cnt (dw->var_store) == 0;
+}
 
 static void
 psppire_data_window_iface_init (PsppireWindowIface *iface)
 {
   iface->save = save_file;
+  iface->pick_filename = data_pick_filename;
   iface->load = load_file;
 }
-
 
 PsppireDataWindow *
 psppire_default_data_window (void)
 {
-  if (the_data_window == NULL)
-    gtk_widget_show (psppire_data_window_new (dataset_create ()));
-  return the_data_window;
+  if (ll_is_empty (&all_data_windows))
+    create_data_window ();
+  return ll_data (ll_head (&all_data_windows), PsppireDataWindow, ll);
+}
+
+void
+psppire_data_window_set_default (PsppireDataWindow *pdw)
+{
+  ll_remove (&pdw->ll);
+  ll_push_head (&all_data_windows, &pdw->ll);
+}
+
+void
+psppire_data_window_undefault (PsppireDataWindow *pdw)
+{
+  ll_remove (&pdw->ll);
+  ll_push_tail (&all_data_windows, &pdw->ll);
+}
+
+PsppireDataWindow *
+psppire_data_window_for_dataset (struct dataset *ds)
+{
+  PsppireDataWindow *pdw;
+
+  ll_for_each (pdw, PsppireDataWindow, ll, &all_data_windows)
+    if (pdw->dataset == ds)
+      return pdw;
+
+  return NULL;
+}
+
+void
+create_data_window (void)
+{
+  gtk_widget_show (psppire_data_window_new (NULL));
 }
diff --git a/src/ui/gui/psppire-data-window.h b/src/ui/gui/psppire-data-window.h
index 0100254..64fe076 100644
--- a/src/ui/gui/psppire-data-window.h
+++ b/src/ui/gui/psppire-data-window.h
@@ -1,5 +1,5 @@
 /* PSPPIRE - a graphical user interface for PSPP.
-   Copyright (C) 2008, 2010  Free Software Foundation
+   Copyright (C) 2008, 2010, 2011  Free Software Foundation
 
    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
@@ -23,11 +23,10 @@
 #include <glib-object.h>
 #include <gtk/gtk.h>
 
+#include "libpspp/ll.h"
 #include "ui/gui/psppire-window.h"
 #include "ui/gui/psppire-data-editor.h"
 
-struct dataset;
-
 G_BEGIN_DECLS
 
 #define PSPPIRE_DATA_WINDOW_TYPE            (psppire_data_window_get_type ())
@@ -65,6 +64,10 @@ struct _PsppireDataWindow
 
 
   gboolean save_as_portable;
+
+  struct ll ll;                 /* In global 'all_data_windows' list. */
+  unsigned long int lazy_serial;
+  unsigned int dataset_seqno;
 };
 
 struct _PsppireDataWindowClass
@@ -72,9 +75,20 @@ struct _PsppireDataWindowClass
   PsppireWindowClass parent_class;
 };
 
+extern struct session *the_session;
+extern struct ll_list all_data_windows;
+
 GType      psppire_data_window_get_type        (void);
 GtkWidget* psppire_data_window_new             (struct dataset *);
+
 PsppireDataWindow *psppire_default_data_window (void);
+void psppire_data_window_set_default (PsppireDataWindow *);
+void psppire_data_window_undefault (PsppireDataWindow *);
+
+PsppireDataWindow *psppire_data_window_for_dataset (struct dataset *);
+
+bool psppire_data_window_is_empty (PsppireDataWindow *);
+void create_data_window (void);
 
 G_END_DECLS
 
diff --git a/src/ui/gui/psppire-syntax-window.c 
b/src/ui/gui/psppire-syntax-window.c
index 2e90ba7..a61e56e 100644
--- a/src/ui/gui/psppire-syntax-window.c
+++ b/src/ui/gui/psppire-syntax-window.c
@@ -483,9 +483,9 @@ save_editor_to_file (PsppireSyntaxWindow *se,
 }
 
 
-/* Callback for the File->SaveAs menuitem */
+/* PsppireWindow 'pick_Filename' callback. */
 static void
-syntax_save_as (PsppireWindow *se)
+syntax_pick_filename (PsppireWindow *se)
 {
   GtkFileFilter *filter;
   gint response;
@@ -515,16 +515,9 @@ syntax_save_as (PsppireWindow *se)
 
   if ( response == GTK_RESPONSE_ACCEPT )
     {
-      GError *err = NULL;
       char *filename =
        gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog) );
-
-      if ( ! save_editor_to_file (PSPPIRE_SYNTAX_WINDOW (se), filename, &err) )
-       {
-         msg ( ME, "%s", err->message );
-         g_error_free (err);
-       }
-
+      psppire_window_set_filename (se, filename);
       free (filename);
     }
 
@@ -532,23 +525,17 @@ syntax_save_as (PsppireWindow *se)
 }
 
 
-/* Callback for the File->Save menuitem */
+/* PsppireWindow 'save' callback. */
 static void
 syntax_save (PsppireWindow *se)
 {
   const gchar *filename = psppire_window_get_filename (se);
-
-  if ( filename == NULL )
-    syntax_save_as (se);
-  else
+  GError *err = NULL;
+  save_editor_to_file (PSPPIRE_SYNTAX_WINDOW (se), filename, &err);
+  if ( err )
     {
-      GError *err = NULL;
-      save_editor_to_file (PSPPIRE_SYNTAX_WINDOW (se), filename, &err);
-      if ( err )
-       {
-         msg (ME, "%s", err->message);
-         g_error_free (err);
-       }
+      msg (ME, "%s", err->message);
+      g_error_free (err);
     }
 }
 
@@ -652,12 +639,10 @@ psppire_syntax_window_init (PsppireSyntaxWindow *window)
 
   g_signal_connect_swapped (get_action_assert (xml,"file_new_syntax"), 
"activate", G_CALLBACK (create_syntax_window), NULL);
 
-#if 0
   g_signal_connect (get_action_assert (xml,"file_new_data"),
                    "activate",
                    G_CALLBACK (create_data_window),
                    window);
-#endif
 
   g_signal_connect_swapped (get_action_assert (xml, "file_open"),
                    "activate",
@@ -666,12 +651,12 @@ psppire_syntax_window_init (PsppireSyntaxWindow *window)
 
   g_signal_connect_swapped (get_action_assert (xml, "file_save"),
                    "activate",
-                   G_CALLBACK (syntax_save),
+                   G_CALLBACK (psppire_window_save),
                    window);
 
   g_signal_connect_swapped (get_action_assert (xml, "file_save_as"),
                    "activate",
-                   G_CALLBACK (syntax_save_as),
+                   G_CALLBACK (psppire_window_save_as),
                    window);
 
   g_signal_connect (get_action_assert (xml,"file_quit"),
@@ -745,7 +730,6 @@ psppire_syntax_window_new (void)
 {
   return GTK_WIDGET (g_object_new (psppire_syntax_window_get_type (),
                                   /* TRANSLATORS: This will form a filename.  
Please avoid whitespace. */
-                                  "filename", _("Syntax"),
                                   "description", _("Syntax Editor"),
                                   NULL));
 }
@@ -825,6 +809,7 @@ static void
 psppire_syntax_window_iface_init (PsppireWindowIface *iface)
 {
   iface->save = syntax_save;
+  iface->pick_filename = syntax_pick_filename;
   iface->load = syntax_load;
 }
 
diff --git a/src/ui/gui/psppire-window.c b/src/ui/gui/psppire-window.c
index 332abe3..399b020 100644
--- a/src/ui/gui/psppire-window.c
+++ b/src/ui/gui/psppire-window.c
@@ -79,7 +79,8 @@ enum
 {
   PROP_0,
   PROP_FILENAME,
-  PROP_DESCRIPTION
+  PROP_DESCRIPTION,
+  PROP_ID
 };
 
 
@@ -94,12 +95,22 @@ psppire_window_set_title (PsppireWindow *window)
 {
   GString *title = g_string_sized_new (80);
 
-  g_string_printf (title, "%s ", window->basename ? window->basename : "");
-  g_string_append_unichar (title, 0x2014); /* em dash */
-  g_string_printf (title, " PSPPIRE %s", window->description);
-
   if (window->dirty)
-    g_string_prepend_c (title, '*');
+    g_string_append_c (title, '*');
+
+  if (window->basename || window->id)
+    {
+      if (window->basename)
+        g_string_append_printf (title, "%s ", window->basename);
+
+      if (window->id != '\0')
+        g_string_append_printf (title, "[%s] ", window->id);
+
+      g_string_append_unichar (title, 0x2014); /* em dash */
+      g_string_append_c (title, ' '); /* em dash */
+    }
+
+  g_string_append_printf (title, "PSPPIRE %s", window->description);
 
   gtk_window_set_title (GTK_WINDOW (window), title->str);
 
@@ -107,6 +118,60 @@ psppire_window_set_title (PsppireWindow *window)
 }
 
 static void
+psppire_window_update_list_name (PsppireWindow *window)
+{
+  PsppireWindowRegister *reg = psppire_window_register_new ();
+  GString *candidate = g_string_sized_new (80);
+  int n;
+
+  n = 1;
+  do
+    {
+      /* Compose a name. */
+      g_string_truncate (candidate, 0);
+      if (window->filename)
+        {
+          gchar *display_filename = g_filename_display_name (window->filename);
+          g_string_append (candidate, display_filename);
+          g_free (display_filename);
+
+          if (window->id)
+            g_string_append_printf (candidate, " [%s]", window->id);
+        }
+      else if (window->id)
+        g_string_append_printf (candidate, "[%s]", window->id);
+      else
+        g_string_append (candidate, window->description);
+
+      if (n++ > 1)
+        g_string_append_printf (candidate, " #%d", n);
+
+      if (window->list_name && !strcmp (candidate->str, window->list_name))
+        {
+          /* Keep the existing name. */
+          g_string_free (candidate, TRUE);
+          return;
+        }
+    }
+  while (psppire_window_register_lookup (reg, candidate->str));
+
+  if (window->list_name)
+    psppire_window_register_remove (reg, window->list_name);
+
+  g_free (window->list_name);
+  window->list_name = g_string_free (candidate, FALSE);
+
+  psppire_window_register_insert (reg, window, window->list_name);
+}
+
+static void
+psppire_window_name_changed (PsppireWindow *window)
+{
+  psppire_window_set_title (window);
+  psppire_window_update_list_name (window);
+}
+
+static void
 psppire_window_set_property (GObject         *object,
                             guint            prop_id,
                             const GValue    *value,
@@ -117,50 +182,24 @@ psppire_window_set_property (GObject         *object,
   switch (prop_id)
     {
     case PROP_DESCRIPTION:
+      g_free (window->description);
       window->description = g_value_dup_string (value);
       psppire_window_set_title (window);
       break;
     case PROP_FILENAME:
-      {
-       PsppireWindowRegister *reg = psppire_window_register_new ();
-
-       gchar *candidate_name ;
-
-       {
-         const gchar *name = g_value_get_string (value);
-         int x = 0;
-         GValue def = {0};
-         g_value_init (&def, pspec->value_type);
-
-         if ( NULL == name)
-           {
-             g_param_value_set_default (pspec, &def);
-             name = g_value_get_string (&def);
-           }
-
-         candidate_name = xstrdup (name);
-
-         while ( psppire_window_register_lookup (reg, candidate_name))
-           {
-             free (candidate_name);
-             candidate_name = uniquify (name, &x);
-           }
-
-         window->basename = g_filename_display_basename (candidate_name);
-
-         g_value_unset (&def);
-       }
-
-       psppire_window_set_title (window);
-
-       if ( window->name)
-         psppire_window_register_remove (reg, window->name);
-
-       free (window->name);
-       window->name = candidate_name;
-
-       psppire_window_register_insert (reg, window, window->name);
-      }
+      g_free (window->filename);
+      window->filename = g_value_dup_string (value);
+      g_free (window->basename);
+      window->basename = (window->filename
+                          ? g_filename_display_basename (window->filename)
+                          : NULL);
+      psppire_window_name_changed (window);
+      break;
+      break;
+    case PROP_ID:
+      g_free (window->id);
+      window->id = g_value_dup_string (value);
+      psppire_window_name_changed (window);
       break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -180,11 +219,14 @@ psppire_window_get_property (GObject         *object,
   switch (prop_id)
     {
     case PROP_FILENAME:
-      g_value_set_string (value, window->name);
+      g_value_set_string (value, window->filename);
       break;
     case PROP_DESCRIPTION:
       g_value_set_string (value, window->description);
       break;
+    case PROP_ID:
+      g_value_set_string (value, window->id);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -210,9 +252,12 @@ psppire_window_finalize (GObject *object)
 
   PsppireWindowRegister *reg = psppire_window_register_new ();
 
-  psppire_window_register_remove (reg, window->name);
-  free (window->name);
-  free (window->description);
+  psppire_window_register_remove (reg, window->list_name);
+  g_free (window->filename);
+  g_free (window->basename);
+  g_free (window->id);
+  g_free (window->description);
+  g_free (window->list_name);
 
   g_signal_handler_disconnect (psppire_window_register_new (),
                               window->remove_handler);
@@ -226,6 +271,17 @@ psppire_window_finalize (GObject *object)
     G_OBJECT_CLASS (parent_class)->finalize (object);
 }
 
+static GParamSpec *
+null_if_empty_param (const gchar *name, const gchar *nick,
+                     const gchar *blurb, const gchar *default_value,
+                     GParamFlags flags)
+{
+  GParamSpec *param;
+
+  param = g_param_spec_string (name, nick, blurb, default_value, flags);
+  ((GParamSpecString *) param)->null_fold_if_empty = TRUE;
+  return param;
+}
 
 static void
 psppire_window_class_init (PsppireWindowClass *class)
@@ -233,18 +289,25 @@ psppire_window_class_init (PsppireWindowClass *class)
   GObjectClass *object_class = G_OBJECT_CLASS (class);
 
   GParamSpec *description_spec =
-    g_param_spec_string ("description",
+    null_if_empty_param ("description",
                       "Description",
                       "A string describing the usage of the window",
-                        "??????", /*Should be overridden by derived classes */
+                        NULL, /*Should be overridden by derived classes */
                       G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
 
   GParamSpec *filename_spec =
-    g_param_spec_string ("filename",
+    null_if_empty_param ("filename",
                       "File name",
                       "The name of the file associated with this window, if 
any",
-                        /* TRANSLATORS: This will form a filename.  Please 
avoid whitespace. */
-                        _("Untitled"),
+                        NULL,
+                        G_PARAM_CONSTRUCT | G_PARAM_READWRITE);
+
+  GParamSpec *id_spec =
+    null_if_empty_param ("id",
+                         "Identifier",
+                         "The PSPP language identifier for the data associated 
"
+                         "with this window (e.g. dataset name)",
+                        NULL,
                         G_PARAM_CONSTRUCT | G_PARAM_READWRITE);
 
   object_class->set_property = psppire_window_set_property;
@@ -258,6 +321,10 @@ psppire_window_class_init (PsppireWindowClass *class)
                                    PROP_FILENAME,
                                    filename_spec);
 
+  g_object_class_install_property (object_class,
+                                   PROP_ID,
+                                   id_spec);
+
   parent_class = g_type_class_peek_parent (class);
 }
 
@@ -398,6 +465,11 @@ on_delete (PsppireWindow *w, GdkEvent *event, gpointer 
user_data)
          break;
        case GTK_RESPONSE_APPLY:
          psppire_window_save (w);
+          if (w->dirty)
+            {
+              /* Save failed, or user exited Save As dialog with Cancel. */
+              return TRUE;
+            }
          break;
        case GTK_RESPONSE_REJECT:
          break;
@@ -414,9 +486,12 @@ on_delete (PsppireWindow *w, GdkEvent *event, gpointer 
user_data)
 static void
 psppire_window_init (PsppireWindow *window)
 {
-  window->name = NULL;
   window->menu = NULL;
-  window->description = xstrdup ("");
+  window->filename = NULL;
+  window->basename = NULL;
+  window->id = NULL;
+  window->description = NULL;
+  window->list_name = NULL;
 
   window->menuitem_table  = g_hash_table_new (g_str_hash, g_str_equal);
 
@@ -450,33 +525,30 @@ psppire_window_init (PsppireWindow *window)
 gint
 psppire_window_query_save (PsppireWindow *se)
 {
-  gchar *fn;
   gint response;
   GtkWidget *dialog;
   GtkWidget *cancel_button;
 
-  const gchar *description;
-  const gchar *filename = psppire_window_get_filename (se);
+  gchar *description;
 
   GTimeVal time;
 
   g_get_current_time (&time);
 
-  g_object_get (se, "description", &description, NULL);
-
-  g_return_val_if_fail (filename != NULL, GTK_RESPONSE_NONE);
-
-
-  fn = g_filename_display_basename (filename);
-
+  if (se->filename)
+    description = g_filename_display_basename (se->filename);
+  else if (se->id)
+    description = g_strdup (se->id);
+  else
+    description = g_strdup (se->description);
   dialog =
     gtk_message_dialog_new (GTK_WINDOW (se),
                            GTK_DIALOG_MODAL,
                            GTK_MESSAGE_WARNING,
                            GTK_BUTTONS_NONE,
                            _("Save the changes to `%s' before closing?"),
-                           fn);
-  g_free (fn);
+                           description);
+  g_free (description);
 
   g_object_set (dialog, "icon-name", "psppicon", NULL);
 
@@ -506,7 +578,7 @@ psppire_window_query_save (PsppireWindow *se)
 }
 
 
-
+/* The return value is encoded in the glib filename encoding. */
 const gchar *
 psppire_window_get_filename (PsppireWindow *w)
 {
@@ -514,6 +586,7 @@ psppire_window_get_filename (PsppireWindow *w)
 }
 
 
+/* FILENAME must be encoded in the glib filename encoding. */
 void
 psppire_window_set_filename (PsppireWindow *w, const gchar *filename)
 {
@@ -595,16 +668,39 @@ psppire_window_save (PsppireWindow *w)
 {
   PsppireWindowIface *i = PSPPIRE_WINDOW_MODEL_GET_IFACE (w);
 
-  g_assert (PSPPIRE_IS_WINDOW_MODEL (w));
-
   g_assert (i);
-
   g_return_if_fail (i->save);
 
-  i->save (w);
+  if (w->filename == NULL)
+    psppire_window_save_as (w);
+  else
+    {
+      i->save (w);
+      w->dirty = FALSE;
+      psppire_window_set_title (w);
+    }
+}
 
-  w->dirty = FALSE;
-  psppire_window_set_title (w);
+void
+psppire_window_save_as (PsppireWindow *w)
+{
+  PsppireWindowIface *i = PSPPIRE_WINDOW_MODEL_GET_IFACE (w);
+  gchar *old_filename;
+
+  g_assert (i);
+  g_return_if_fail (i->pick_filename);
+
+  old_filename = w->filename;
+  w->filename = NULL;
+
+  i->pick_filename (w);
+  if (w->filename == NULL)
+    w->filename = old_filename;
+  else
+    {
+      g_free (old_filename);
+      psppire_window_save (w);
+    }
 }
 
 extern GtkRecentManager *the_recent_mgr;
@@ -686,26 +782,26 @@ psppire_window_file_chooser_dialog (PsppireWindow 
*toplevel)
   gtk_file_filter_add_pattern (filter, "*");
   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
 
-  {
-    gchar *dir_name;
-    gchar *filename = NULL;
-    g_object_get (toplevel, "filename", &filename, NULL);
-
-    if ( ! g_path_is_absolute (filename))
-      {
-       gchar *path =
-         g_build_filename (g_get_current_dir (), filename, NULL);
-       dir_name = g_path_get_dirname (path);
-       g_free (path);
-      }
-    else
-      {
-       dir_name = g_path_get_dirname (filename);
-      }
-    gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog),
-                                        dir_name);
-    free (dir_name);
-  }
+  if (toplevel->filename)
+    {
+      const gchar *filename = toplevel->filename;
+      gchar *dir_name;
+
+      if ( ! g_path_is_absolute (filename))
+        {
+          gchar *path =
+            g_build_filename (g_get_current_dir (), filename, NULL);
+          dir_name = g_path_get_dirname (path);
+          g_free (path);
+        }
+      else
+        {
+          dir_name = g_path_get_dirname (filename);
+        }
+      gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog),
+                                           dir_name);
+      free (dir_name);
+    }
 
   return dialog;
 }
diff --git a/src/ui/gui/psppire-window.h b/src/ui/gui/psppire-window.h
index f65e9e3..0aa913c 100644
--- a/src/ui/gui/psppire-window.h
+++ b/src/ui/gui/psppire-window.h
@@ -60,9 +60,11 @@ struct _PsppireWindow
   GtkWindow parent;
 
   /* <private> */
-  gchar *name;
-  gchar *description;
-  gchar *basename;
+  gchar *filename;             /* File name, in file name encoding, or NULL. */
+  gchar *basename;             /* Last component of filename, in UTF-8 */
+  gchar *id;                   /* Dataset name, or NULL.  */
+  gchar *description;          /* e.g. "Data Editor" */
+  gchar *list_name;            /* Name for "Windows" menu list. */
 
   GHashTable *menuitem_table;
   GtkMenuShell *menu;
@@ -86,6 +88,7 @@ struct _PsppireWindowIface
   GTypeInterface g_iface;
 
   void (*save) (PsppireWindow *w);
+  void (*pick_filename) (PsppireWindow *);
   gboolean (*load) (PsppireWindow *w, const gchar *);
 };
 
@@ -106,6 +109,7 @@ gboolean psppire_window_get_unsaved (PsppireWindow *);
 gint psppire_window_query_save (PsppireWindow *);
 
 void psppire_window_save (PsppireWindow *w);
+void psppire_window_save_as (PsppireWindow *w);
 gboolean psppire_window_load (PsppireWindow *w, const gchar *file);
 void psppire_window_open (PsppireWindow *de);
 GtkWidget *psppire_window_file_chooser_dialog (PsppireWindow *toplevel);
diff --git a/src/ui/gui/psppire.c b/src/ui/gui/psppire.c
index 80983e0..5963803 100644
--- a/src/ui/gui/psppire.c
+++ b/src/ui/gui/psppire.c
@@ -28,6 +28,7 @@
 #include "data/file-handle-def.h"
 #include "data/file-name.h"
 #include "data/por-file-reader.h"
+#include "data/session.h"
 #include "data/settings.h"
 #include "data/sys-file-reader.h"
 
@@ -45,12 +46,12 @@
 #include "ui/gui/psppire-data-store.h"
 #include "ui/gui/psppire-data-window.h"
 #include "ui/gui/psppire-dict.h"
+#include "ui/gui/psppire.h"
 #include "ui/gui/psppire-output-window.h"
 #include "ui/gui/psppire-selector.h"
 #include "ui/gui/psppire-var-store.h"
 #include "ui/gui/psppire-var-view.h"
 #include "ui/gui/psppire-window-register.h"
-#include "ui/gui/psppire.h"
 #include "ui/gui/widgets.h"
 #include "ui/source-init-opts.h"
 #include "ui/syntax-gen.h"
diff --git a/src/ui/terminal/main.c b/src/ui/terminal/main.c
index 805681d..c19a1c7 100644
--- a/src/ui/terminal/main.c
+++ b/src/ui/terminal/main.c
@@ -34,6 +34,7 @@
 #include "data/dictionary.h"
 #include "data/file-handle-def.h"
 #include "data/file-name.h"
+#include "data/session.h"
 #include "data/settings.h"
 #include "data/variable.h"
 #include "gsl/gsl_errno.h"
@@ -53,6 +54,7 @@
 #include "ui/terminal/terminal-opts.h"
 #include "ui/terminal/terminal-reader.h"
 #include "ui/terminal/terminal.h"
+#include "ui/terminal/terminal-opts.h"
 
 #include "gl/fatal-signal.h"
 #include "gl/progname.h"
@@ -61,7 +63,7 @@
 #include "gettext.h"
 #define _(msgid) gettext (msgid)
 
-static struct dataset *the_dataset;
+static struct session *the_session;
 
 static void add_syntax_reader (struct lexer *, const char *file_name,
                                const char *encoding, enum lex_syntax_mode);
@@ -96,7 +98,8 @@ main (int argc, char **argv)
   random_init ();
 
   lexer = lex_create ();
-  the_dataset = dataset_create ();
+  the_session = session_create ();
+  dataset_create (the_session, "");
 
   parser = argv_parser_create ();
   terminal_opts = terminal_opts_init (parser, &syntax_mode, &process_statrc,
@@ -108,7 +111,7 @@ main (int argc, char **argv)
   argv_parser_destroy (parser);
 
   msg_set_handler (output_msg, lexer);
-  dataset_set_default_syntax_encoding (the_dataset, syntax_encoding);
+  session_set_default_syntax_encoding (the_session, syntax_encoding);
 
   /* Add syntax files to source stream. */
   if (process_statrc)
@@ -134,7 +137,7 @@ main (int argc, char **argv)
   lex_get (lexer);
   for (;;)
     {
-      int result = cmd_parse (lexer, the_dataset);
+      int result = cmd_parse (lexer, session_active_dataset (the_session));
 
       if (result == CMD_EOF || result == CMD_FINISH)
        break;
@@ -159,7 +162,7 @@ main (int argc, char **argv)
     }
 
 
-  dataset_destroy (the_dataset);
+  session_destroy (the_session);
 
   random_done ();
   settings_done ();
diff --git a/tests/automake.mk b/tests/automake.mk
index 1145d64..d62ef63 100644
--- a/tests/automake.mk
+++ b/tests/automake.mk
@@ -293,6 +293,7 @@ TESTSUITE_AT = \
        tests/language/data-io/add-files.at \
        tests/language/data-io/data-list.at \
        tests/language/data-io/data-reader.at \
+       tests/language/data-io/dataset.at \
        tests/language/data-io/file-handle.at \
        tests/language/data-io/get-data-gnm.at \
        tests/language/data-io/get-data-psql.at \
diff --git a/tests/language/data-io/dataset.at 
b/tests/language/data-io/dataset.at
new file mode 100644
index 0000000..7d5e928
--- /dev/null
+++ b/tests/language/data-io/dataset.at
@@ -0,0 +1,302 @@
+AT_BANNER([DATASET commands])
+
+AT_SETUP([DATASET COPY])
+AT_DATA([dataset.pspp], [dnl
+DATASET NAME initial.
+DATA LIST NOTABLE /x 1.
+COMPUTE x = x + 1.
+DATASET COPY clone.
+BEGIN DATA.
+1
+2
+3
+4
+5
+END DATA.
+
+NEW FILE.
+DATA LIST NOTABLE /y 1.
+BEGIN DATA.
+6
+7
+8
+END DATA.
+LIST.
+DATASET DISPLAY.
+
+DATASET ACTIVATE clone.
+DATASET DISPLAY.
+LIST.
+
+DATASET ACTIVATE initial.
+DATASET DISPLAY.
+LIST.
+
+COMPUTE z=y.
+DATASET COPY clone.
+
+DATASET ACTIVATE clone.
+LIST.
+DATASET COPY clone.
+DATASET DISPLAY.
+
+DATASET CLOSE initial.
+DATASET DISPLAY.
+])
+AT_CHECK([pspp -O format=csv dataset.pspp], [0], [dnl
+Table: Data List
+y
+6
+7
+8
+
+Table: Open datasets.
+Dataset
+clone
+initial (active dataset)
+
+Table: Open datasets.
+Dataset
+clone (active dataset)
+initial
+
+Table: Data List
+x
+2
+3
+4
+5
+6
+
+Table: Open datasets.
+Dataset
+clone
+initial (active dataset)
+
+Table: Data List
+y
+6
+7
+8
+
+Table: Data List
+y,z
+6,6.00
+7,7.00
+8,8.00
+
+Table: Open datasets.
+Dataset
+unnamed dataset (active dataset)
+initial
+
+Table: Open datasets.
+Dataset
+unnamed dataset (active dataset)
+])
+AT_CLEANUP
+
+AT_SETUP([DATASET DECLARE])
+AT_DATA([dataset.pspp], [dnl
+DATASET DECLARE second.
+DATASET DISPLAY.
+DATA LIST NOTABLE/x 1.
+BEGIN DATA.
+1
+END DATA.
+LIST.
+DATASET ACTIVATE second.
+DATASET DISPLAY.
+LIST.
+])
+AT_CHECK([pspp -O format=csv dataset.pspp], [1], [dnl
+Table: Open datasets.
+Dataset
+unnamed dataset (active dataset)
+second
+
+Table: Data List
+x
+1
+
+Table: Open datasets.
+Dataset
+second (active dataset)
+
+dataset.pspp:10: error: LIST: LIST is allowed only after the active dataset 
has been defined.
+])
+AT_CLEANUP
+
+AT_SETUP([DATASET NAME deletes duplicate name])
+AT_DATA([dataset.pspp], [dnl
+DATASET NAME a.
+DATASET DECLARE b.
+DATASET DECLARE c.
+DATASET DISPLAY.
+
+DATASET NAME b.
+DATASET NAME c.
+DATASET DISPLAY.
+])
+AT_CHECK([pspp -O format=csv dataset.pspp], [0], [dnl
+Table: Open datasets.
+Dataset
+a (active dataset)
+b
+c
+
+Table: Open datasets.
+Dataset
+c (active dataset)
+])
+AT_CLEANUP
+
+AT_SETUP([DATASET ACTIVATE deletes unnamed dataset])
+AT_DATA([dataset.pspp], [dnl
+DATASET DECLARE x.
+DATASET DISPLAY.
+
+DATASET ACTIVATE x.
+DATASET DISPLAY.
+])
+AT_CHECK([pspp -O format=csv dataset.pspp], [0], [dnl
+Table: Open datasets.
+Dataset
+unnamed dataset (active dataset)
+x
+
+Table: Open datasets.
+Dataset
+x (active dataset)
+])
+AT_CLEANUP
+
+AT_SETUP([DATASET ACTIVATE executes pending transformations])
+AT_DATA([dataset.pspp], [dnl
+DATASET NAME one.
+DATASET DECLARE another.
+DATASET DISPLAY.
+
+DATA LIST NOTABLE /x 1.
+PRINT/x.
+DATASET ACTIVATE another.
+BEGIN DATA.
+1
+2
+3
+4
+5
+END DATA.
+
+LIST.
+
+DATASET ACTIVATE one.
+LIST.
+])
+AT_CHECK([pspp -O format=csv dataset.pspp], [1], [dnl
+Table: Open datasets.
+Dataset
+another
+one (active dataset)
+
+1 @&t@
+
+2 @&t@
+
+3 @&t@
+
+4 @&t@
+
+5 @&t@
+
+dataset.pspp:16: error: LIST: LIST is allowed only after the active dataset 
has been defined.
+
+Table: Data List
+x
+1
+2
+3
+4
+5
+])
+AT_CLEANUP
+
+AT_SETUP([DATASET CLOSE])
+AT_DATA([dataset.pspp], [dnl
+DATASET DISPLAY
+DATASET CLOSE *.
+DATASET DISPLAY.
+
+DATASET NAME this.
+DATASET DISPLAY.
+DATASET CLOSE this.
+DATASET DISPLAY.
+
+DATASET NAME this.
+DATASET DISPLAY.
+DATASET CLOSE *.
+DATASET DISPLAY.
+
+DATASET DECLARE that.
+DATASET DECLARE theother.
+DATASET DECLARE yetanother.
+DATASET DISPLAY.
+DATASET CLOSE ALL.
+DATASET DISPLAY.
+
+DATASET NAME this.
+DATASET DECLARE that.
+DATASET DECLARE theother.
+DATASET DECLARE yetanother.
+DATASET DISPLAY.
+DATASET CLOSE ALL.
+DATASET DISPLAY.
+])
+AT_CHECK([pspp -O format=csv dataset.pspp], [0], [dnl
+Table: Open datasets.
+Dataset
+unnamed dataset (active dataset)
+
+Table: Open datasets.
+Dataset
+unnamed dataset (active dataset)
+
+Table: Open datasets.
+Dataset
+this (active dataset)
+
+Table: Open datasets.
+Dataset
+unnamed dataset (active dataset)
+
+Table: Open datasets.
+Dataset
+this (active dataset)
+
+Table: Open datasets.
+Dataset
+unnamed dataset (active dataset)
+
+Table: Open datasets.
+Dataset
+unnamed dataset (active dataset)
+that
+theother
+yetanother
+
+Table: Open datasets.
+Dataset
+unnamed dataset (active dataset)
+
+Table: Open datasets.
+Dataset
+that
+theother
+this (active dataset)
+yetanother
+
+Table: Open datasets.
+Dataset
+unnamed dataset (active dataset)
+])
+AT_CLEANUP
diff --git a/tests/language/stats/aggregate.at 
b/tests/language/stats/aggregate.at
index 1c3a7e1..ae7a38a 100644
--- a/tests/language/stats/aggregate.at
+++ b/tests/language/stats/aggregate.at
@@ -4,7 +4,7 @@ dnl CHECK_AGGREGATE(OUTFILE, SORT, MISSING)
 dnl
 dnl Checks the AGGREGATE procedure with the specified combination of:
 dnl
-dnl - OUTFILE: One of "scratch", "active", or "external" according to
+dnl - OUTFILE: One of "dataset", "active", or "external" according to
 dnl   where AGGREGATE's output should be directed.
 dnl 
 dnl - SORT: Either "presorted" or "unsorted" according to whether
@@ -33,11 +33,12 @@ m4_define([CHECK_AGGREGATE], [
     [DATA LIST NOTABLE FILE='aggregate.data' /G N 1-2 S 3(a) W 4.
 WEIGHT BY w.
 MISSING VALUES n(4) s('4').
+m4_if([$1], [dataset], [DATASET DECLARE aggregate.])
 m4_if([$2], [presorted], [SORT CASES BY g.])
 AGGREGATE dnl
 m4_if([$1], [active], [OUTFILE=*],
       [$1], [external], [OUTFILE='aggregate.sys'],
-      address@hidden:@AGGREGATE]) dnl
+      [outfile=aggregate]) dnl
 m4_if([$2], [presorted], [/PRESORTED]) dnl
 m4_if([$3], [columnwise], [/MISSING=COLUMNWISE])
        /DOCUMENT
@@ -119,7 +120,7 @@ m4_if([$3], [columnwise], [/MISSING=COLUMNWISE])
        /NSUM = sum(n)
        /NSUMI = sum.(n).
 m4_if([$1], [external], [GET FILE='aggregate.sys'.],
-      [$1], [scratch], [GET address@hidden:@AGGREGATE.])
+      [$1], [dataset], [DATASET ACTIVATE aggregate.])
 LIST.
 ])
   AT_CHECK([pspp -O format=csv aggregate.sps], [0], [stdout])
@@ -157,10 +158,10 @@ 
G,N,NI,NU,NUI,NFGT2,NFGT2I,SFGT2,SFGT2I,NFIN23,NFIN23I,SFIN23,SFIN23I,NFLT2,NFLT
 ])])
   AT_CLEANUP])
 
-CHECK_AGGREGATE([scratch], [presorted], [itemwise])
-CHECK_AGGREGATE([scratch], [presorted], [columnwise])
-CHECK_AGGREGATE([scratch], [unsorted], [itemwise])
-CHECK_AGGREGATE([scratch], [unsorted], [columnwise])
+CHECK_AGGREGATE([dataset], [presorted], [itemwise])
+CHECK_AGGREGATE([dataset], [presorted], [columnwise])
+CHECK_AGGREGATE([dataset], [unsorted], [itemwise])
+CHECK_AGGREGATE([dataset], [unsorted], [columnwise])
 CHECK_AGGREGATE([active], [presorted], [itemwise])
 CHECK_AGGREGATE([active], [presorted], [columnwise])
 CHECK_AGGREGATE([active], [unsorted], [itemwise])
@@ -285,5 +286,4 @@ AT_CHECK([pspp -O format=csv dup-variables.sps], [1],
 ["dup-variables.sps:24: error: AGGREGATE: Variable name N_BREAK is not unique 
within the aggregate file dictionary, which contains the aggregate variables 
and the break variables."
 ])
 
-
 AT_CLEANUP
-- 
1.7.2.5




reply via email to

[Prev in Thread] Current Thread [Next in Thread]