Implement DATASET commands.
authorBen Pfaff <blp@cs.stanford.edu>
Sat, 30 Apr 2011 04:45:15 +0000 (21:45 -0700)
committerBen Pfaff <blp@cs.stanford.edu>
Wed, 4 May 2011 03:18:41 +0000 (20:18 -0700)
60 files changed:
NEWS
doc/combining.texi
doc/data-io.texi
doc/files.texi
doc/language.texi
doc/transformation.texi
src/data/any-reader.c
src/data/any-writer.c
src/data/automake.mk
src/data/caseinit.c
src/data/caseinit.h
src/data/dataset-reader.c [new file with mode: 0644]
src/data/dataset-reader.h [new file with mode: 0644]
src/data/dataset-writer.c [new file with mode: 0644]
src/data/dataset-writer.h [new file with mode: 0644]
src/data/dataset.c
src/data/dataset.h
src/data/dictionary.c
src/data/file-handle-def.c
src/data/file-handle-def.h
src/data/scratch-handle.c [deleted file]
src/data/scratch-handle.h [deleted file]
src/data/scratch-reader.c [deleted file]
src/data/scratch-reader.h [deleted file]
src/data/scratch-writer.c [deleted file]
src/data/scratch-writer.h [deleted file]
src/data/session.c [new file with mode: 0644]
src/data/session.h [new file with mode: 0644]
src/language/command.c
src/language/command.def
src/language/data-io/automake.mk
src/language/data-io/combine-files.c
src/language/data-io/data-list.c
src/language/data-io/dataset.c [new file with mode: 0644]
src/language/data-io/file-handle.h
src/language/data-io/file-handle.q
src/language/data-io/get-data.c
src/language/data-io/get.c
src/language/data-io/inpt-pgm.c
src/language/data-io/print-space.c
src/language/data-io/print.c
src/language/data-io/save-translate.c
src/language/data-io/save.c
src/language/dictionary/apply-dictionary.c
src/language/dictionary/sys-file-info.c
src/language/expressions/evaluate.c
src/language/stats/aggregate.c
src/language/utilities/include.c
src/ui/gui/data-editor.ui
src/ui/gui/executor.c
src/ui/gui/psppire-data-window.c
src/ui/gui/psppire-data-window.h
src/ui/gui/psppire-syntax-window.c
src/ui/gui/psppire-window.c
src/ui/gui/psppire-window.h
src/ui/gui/psppire.c
src/ui/terminal/main.c
tests/automake.mk
tests/language/data-io/dataset.at [new file with mode: 0644]
tests/language/stats/aggregate.at

diff --git a/NEWS b/NEWS
index 86c88fc879c71469e374eaf96e582739a77dcaab..70020475b02e0d099c6be8a7c3509f2b2cb568c7 100644 (file)
--- 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.
index 8fe7b2090099ff11989007edb455e69111ddce54..a4f683d56910601605a7c30d9ac173e3738582ba 100644 (file)
@@ -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
index 81bdd4770e44ea6f8ed8cec0afc7f1e6025b5ee2..cfb99eb7927d8b2f7a7940e32c81d9f2a4ef4e4b 100644 (file)
@@ -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
-@cmd{GET} 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}.
 
+@node DATASET
+@section DATASET commands
+@vindex DATASET
+
+@display
+DATASET NAME name [WINDOW=@{ASIS,FRONT@}].
+DATASET ACTIVATE name [WINDOW=@{ASIS,FRONT@}].
+DATASET COPY name [WINDOW=@{MINIMIZED,HIDDEN,FRONT@}].
+DATASET DECLARE name [WINDOW=@{MINIMIZED,HIDDEN,FRONT@}].
+DATASET CLOSE @{name,*,ALL@}.
+DATASET DISPLAY.
+@end 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.
+
+@vindex 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.
+
+@vindex 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.
+
+@vindex 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.
+
+@vindex 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}).
+
+@vindex 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.
+
+@vindex 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:
+
+@table @asis
+@item ASIS
+Do not change how the window is displayed.  This is the default for
+DATASET NAME and DATASET ACTIVATE.
+
+@item FRONT
+Raise the dataset's window to the top.  Make it the default dataset
+for running syntax.
+
+@item MINIMIZED
+Display the window ``minimized'' to an icon.  Prefer other datasets
+for running syntax.  This is the default for DATASET COPY and DATASET
+DECLARE.
+
+@item HIDDEN
+Hide the dataset's window.  Prefer other datasets for running syntax.
+@end table
+
 @node DATA LIST
 @section DATA LIST
 @vindex DATA LIST
@@ -512,10 +585,6 @@ For binary files encoded in EBCDIC:
                 /MODE=360
                 /RECFORM=@{FIXED,VARIABLE,SPANNED@}
                 [/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.
-
-@item
-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
-@samp{#} 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
index b4a2446960b3e944ec34ebabdcdfc16a77afc143..4fc92e1ba318cf2e62bcd11be2defb137e498750 100644 (file)
@@ -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
index a81d4e2e2a13c73db0c33ec557510bb7ec733b9c..a5ef4b69d6e338084847b97278a1357d8478ebaa 100644 (file)
@@ -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:
-@ref{Variables}, @ref{MISSING VALUES}, @ref{Expressions}.  See also the
+@ref{Datasets}, @ref{MISSING VALUES}, @ref{Expressions}.  See also the
 documentation on individual procedures for information on how they
 handle missing values.
 
-@node Variables
-@section Variables
-@cindex variables
+@node Datasets
+@section Datasets
+@cindex dataset
+@cindex 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.
 
-@cindex active file
-@cindex file, active
-@item 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.
-
-@cindex scratch file
-@cindex file, scratch
-@item 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
-@cmd{SAVE} 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
-@cmd{FILE 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
index 57c9a49aca741d8764d6115ee38cdf3afdfefaea..f1d1cbf2617b649c5393b10428325329c696447d 100644 (file)
@@ -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 OUTFILE=@samp{*} is given, then the subcommand MODE may also be
 specified.
index 03a7b23bce3f46aa68d6ef904eb8b43d2b1b02b1..50feb6892e936f4fbf9ea54d14f90707c11acf67 100644 (file)
 #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 ();
 }
index b398c70f8ece923455b9fa0b5d62f41e90814a10..61d6ffc8a26ec5c53501f0dc38aa20078cad8280 100644 (file)
@@ -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
 #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 ();
index f7ee43f66cbee25dd7d53efaaa89f98d413369af..81a9d9c5259bf87838cfe68260eb68eb28fa44d0 100644 (file)
@@ -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 \
index 69a0f01869fb7a66c97522d0a86243b9f523dfaf..021db396d15fe10b45cd74a0c9de3bf09f3143d1 100644 (file)
@@ -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)
index dbff8567e7bb2aa5cebb4d9fcc399d7f5feab488..9f566218428f45c354e13c09a86c083a2e83fabf 100644 (file)
@@ -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/dataset-reader.c b/src/data/dataset-reader.c
new file mode 100644 (file)
index 0000000..b679342
--- /dev/null
@@ -0,0 +1,62 @@
+/* PSPP - a program for statistical analysis.
+   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
+   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/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 "libpspp/assertion.h"
+#include "libpspp/message.h"
+
+#include "gl/xalloc.h"
+
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
+
+/* 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 *
+dataset_reader_open (struct file_handle *fh, struct dictionary **dict)
+{
+  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_DATASET);
+
+  ds = fh_get_dataset (fh);
+  if (ds == NULL || !dataset_has_source (ds))
+    {
+      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 (dataset_dict (ds));
+  return casereader_clone (dataset_source (ds));
+}
diff --git a/src/data/dataset-reader.h b/src/data/dataset-reader.h
new file mode 100644 (file)
index 0000000..420b6b1
--- /dev/null
@@ -0,0 +1,27 @@
+/* PSPP - a program for statistical analysis.
+   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
+   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 DATASET_READER_H
+#define DATASET_READER_H 1
+
+#include <stdbool.h>
+
+struct dictionary;
+struct file_handle;
+struct casereader *dataset_reader_open (struct file_handle *,
+                                        struct dictionary **);
+
+#endif /* dataset-reader.h */
diff --git a/src/data/dataset-writer.c b/src/data/dataset-writer.c
new file mode 100644 (file)
index 0000000..c810cf2
--- /dev/null
@@ -0,0 +1,130 @@
+/* PSPP - a program for statistical analysis.
+   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
+   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/dataset-writer.h"
+
+#include <stdlib.h>
+
+#include "data/case.h"
+#include "data/case-map.h"
+#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/variable.h"
+#include "libpspp/compiler.h"
+#include "libpspp/taint.h"
+
+#include "gl/xalloc.h"
+
+#define N_(msgid) (msgid)
+
+/* A dataset file writer. */
+struct dataset_writer
+  {
+    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 dataset_writer_casewriter_class;
+
+/* 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 *
+dataset_writer_open (struct file_handle *fh,
+                     const struct dictionary *dictionary)
+{
+  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_DATASET, N_("dataset"), FH_ACC_WRITE, true);
+  if (lock == NULL)
+    return NULL;
+
+  /* Create writer. */
+  writer = xmalloc (sizeof *writer);
+  writer->lock = lock;
+  writer->ds = fh_get_dataset (fh);
+
+  writer->dict = dict_clone (dictionary);
+  dict_delete_scratch_vars (writer->dict);
+  if (dict_count_values (writer->dict, 0)
+      < dict_get_next_value_idx (writer->dict))
+    {
+      writer->compactor = case_map_to_compact_dict (writer->dict, 0);
+      dict_compact_values (writer->dict);
+    }
+  else
+    writer->compactor = NULL;
+  writer->subwriter = autopaging_writer_create (dict_get_proto (writer->dict));
+
+  casewriter = casewriter_create (dict_get_proto (writer->dict),
+                                  &dataset_writer_casewriter_class, writer);
+  taint_propagate (casewriter_get_taint (writer->subwriter),
+                   casewriter_get_taint (casewriter));
+  return casewriter;
+}
+
+/* Writes case C to WRITER. */
+static void
+dataset_writer_casewriter_write (struct casewriter *w UNUSED, void *writer_,
+                                 struct ccase *c)
+{
+  struct dataset_writer *writer = writer_;
+  casewriter_write (writer->subwriter,
+                    case_map_execute (writer->compactor, c));
+}
+
+/* Closes WRITER. */
+static void
+dataset_writer_casewriter_destroy (struct casewriter *w UNUSED, void *writer_)
+{
+  struct dataset_writer *writer = writer_;
+  struct casereader *reader = casewriter_make_reader (writer->subwriter);
+  if (!casereader_error (reader))
+    {
+      dataset_set_dict (writer->ds, writer->dict);
+      dataset_set_source (writer->ds, reader);
+    }
+  else
+    {
+      casereader_destroy (reader);
+      dict_destroy (writer->dict);
+    }
+
+  fh_unlock (writer->lock);
+  free (writer);
+}
+
+static const struct casewriter_class dataset_writer_casewriter_class =
+  {
+    dataset_writer_casewriter_write,
+    dataset_writer_casewriter_destroy,
+    NULL,
+  };
diff --git a/src/data/dataset-writer.h b/src/data/dataset-writer.h
new file mode 100644 (file)
index 0000000..429d688
--- /dev/null
@@ -0,0 +1,27 @@
+/* PSPP - a program for statistical analysis.
+   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
+   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 DATASET_WRITER_H
+#define DATASET_WRITER_H 1
+
+#include <stdbool.h>
+
+struct dictionary;
+struct file_handle;
+struct casewriter *dataset_writer_open (struct file_handle *,
+                                        const struct dictionary *);
+
+#endif /* dataset-writer.h */
index 466696c7b9a34fb277d7120bfb1dfbb090434457..10009f4a5cc463e215a0373547cb19b6cadc8ce2 100644 (file)
@@ -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"
 #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);
 }
 \f
-/* 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;
 }
 \f
 /* 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);
 }
+\f
+/* Private interface for use by session code. */
+
+void
+dataset_set_session__ (struct dataset *ds, struct session *session)
+{
+  ds->session = session;
+}
index b2aa8bcf0e120a43c71fddf69295e5cb73d78c30..8445094713cdc38bb1eb9e9f72bf4309bf3e0013 100644 (file)
 #include <stdbool.h>
 
 #include "data/transformations.h"
-#include "libpspp/compiler.h"
 
 struct casereader;
 struct dataset;
 struct dictionary;
+struct session;
 \f
-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);
 \f
 /* Transformations. */
 
@@ -93,5 +110,9 @@ bool dataset_end_of_command (struct dataset *);
 \f
 const struct ccase *lagged_case (const struct dataset *ds, int n_before);
 void dataset_need_lag (struct dataset *ds, int n_before);
+\f
+/* Private interface for use by session code. */
+
+void dataset_set_session__(struct dataset *, struct session *);
 
 #endif /* dataset.h */
index c8f58516ecc79d1d6a7bc78a29353f838745634c..4a0afc73d312ab1fac54d061771a1e2cbf174170 100644 (file)
@@ -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)
 {
index 2b8e40ce347b7935b68589083dcda03030c07a74..6ca6977c87733d792fce4c7f31d48f8afab97e80 100644 (file)
 #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;
index 2df85f98b8c0c09387bee4d40144d8d034a6c2ef..11898ef578eca45d437c86c41dfe71bd09d29ffc 100644 (file)
@@ -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 (file)
index a95f2cc..0000000
+++ /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 (file)
index c7775f4..0000000
+++ /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/scratch-reader.c b/src/data/scratch-reader.c
deleted file mode 100644 (file)
index 5def728..0000000
+++ /dev/null
@@ -1,63 +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-reader.h"
-
-#include <stdlib.h>
-
-#include "data/case.h"
-#include "data/casereader.h"
-#include "data/dictionary.h"
-#include "data/file-handle-def.h"
-#include "data/scratch-handle.h"
-#include "libpspp/assertion.h"
-#include "libpspp/message.h"
-
-#include "gl/xalloc.h"
-
-#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. */
-struct casereader *
-scratch_reader_open (struct file_handle *fh, struct dictionary **dict)
-{
-  struct scratch_handle *sh;
-
-  /* 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);
-
-  sh = fh_get_scratch_handle (fh);
-  if (sh == NULL || sh->casereader == NULL)
-    {
-      msg (SE, _("Scratch file handle %s has not yet been written, "
-                 "using SAVE or another procedure, so it cannot yet "
-                 "be used for reading."),
-           fh_get_name (fh));
-      return NULL;
-    }
-
-  *dict = dict_clone (sh->dictionary);
-  return casereader_clone (sh->casereader);
-}
diff --git a/src/data/scratch-reader.h b/src/data/scratch-reader.h
deleted file mode 100644 (file)
index a5ee005..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-/* PSPP - a program for statistical analysis.
-   Copyright (C) 2006, 2009 Free Software Foundation, Inc.
-
-   This program is free software: you can redistribute it and/or modify
-   it under the terms of the GNU General Public License as published by
-   the Free Software Foundation, either version 3 of the License, or
-   (at your option) any later version.
-
-   This program is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-   GNU General Public License for more details.
-
-   You should have received a copy of the GNU General Public License
-   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
-
-#ifndef SCRATCH_READER_H
-#define SCRATCH_READER_H 1
-
-#include <stdbool.h>
-
-struct dictionary;
-struct file_handle;
-struct casereader *scratch_reader_open (struct file_handle *,
-                                        struct dictionary **);
-
-#endif /* scratch-reader.h */
diff --git a/src/data/scratch-writer.c b/src/data/scratch-writer.c
deleted file mode 100644 (file)
index 2c545c7..0000000
+++ /dev/null
@@ -1,142 +0,0 @@
-/* PSPP - a program for statistical analysis.
-   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
-   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-writer.h"
-
-#include <stdlib.h>
-
-#include "data/case.h"
-#include "data/case-map.h"
-#include "data/casereader.h"
-#include "data/casewriter-provider.h"
-#include "data/casewriter.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"
-
-#include "gl/xalloc.h"
-
-#define N_(msgid) (msgid)
-
-/* A scratch file writer. */
-struct scratch_writer
-  {
-    struct file_handle *fh;             /* Underlying file handle. */
-    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;
-
-/* 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
-   to be drawn from DICTIONARY. */
-struct casewriter *
-scratch_writer_open (struct file_handle *fh,
-                     const struct dictionary *dictionary)
-{
-  struct scratch_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);
-  if (lock == NULL)
-    return NULL;
-
-  /* Create writer. */
-  writer = xmalloc (sizeof *writer);
-  writer->lock = lock;
-  writer->fh = fh_ref (fh);
-
-  writer->dict = dict_clone (dictionary);
-  dict_delete_scratch_vars (writer->dict);
-  if (dict_count_values (writer->dict, 0)
-      < dict_get_next_value_idx (writer->dict))
-    {
-      writer->compactor = case_map_to_compact_dict (writer->dict, 0);
-      dict_compact_values (writer->dict);
-    }
-  else
-    writer->compactor = NULL;
-  writer->subwriter = autopaging_writer_create (dict_get_proto (writer->dict));
-
-  casewriter = casewriter_create (dict_get_proto (writer->dict),
-                                  &scratch_writer_casewriter_class, writer);
-  taint_propagate (casewriter_get_taint (writer->subwriter),
-                   casewriter_get_taint (casewriter));
-  return casewriter;
-}
-
-/* Writes case C to WRITER. */
-static void
-scratch_writer_casewriter_write (struct casewriter *w UNUSED, void *writer_,
-                                 struct ccase *c)
-{
-  struct scratch_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_)
-{
-  static unsigned int next_unique_id = 0x12345678;
-
-  struct scratch_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);
-    }
-  else
-    {
-      casereader_destroy (reader);
-      dict_destroy (writer->dict);
-    }
-
-  fh_unlock (writer->lock);
-  fh_unref (writer->fh);
-  free (writer);
-}
-
-static const struct casewriter_class scratch_writer_casewriter_class =
-  {
-    scratch_writer_casewriter_write,
-    scratch_writer_casewriter_destroy,
-    NULL,
-  };
diff --git a/src/data/scratch-writer.h b/src/data/scratch-writer.h
deleted file mode 100644 (file)
index a9c7a4d..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-/* PSPP - a program for statistical analysis.
-   Copyright (C) 2006, 2009 Free Software Foundation, Inc.
-
-   This program is free software: you can redistribute it and/or modify
-   it under the terms of the GNU General Public License as published by
-   the Free Software Foundation, either version 3 of the License, or
-   (at your option) any later version.
-
-   This program is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-   GNU General Public License for more details.
-
-   You should have received a copy of the GNU General Public License
-   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
-
-#ifndef SCRATCH_WRITER_H
-#define SCRATCH_WRITER_H 1
-
-#include <stdbool.h>
-
-struct dictionary;
-struct file_handle;
-struct casewriter *scratch_writer_open (struct file_handle *,
-                                        const struct dictionary *);
-
-#endif /* scratch-writer.h */
diff --git a/src/data/session.c b/src/data/session.c
new file mode 100644 (file)
index 0000000..24c22b2
--- /dev/null
@@ -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;
+}
+\f
+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 (file)
index 0000000..f45cceb
--- /dev/null
@@ -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 */
index f5db9731c1e6f6d30f8f77bf04d1a76f53c29bf7..ff2b030ce0e206f3af47c48f090368027dd60e4c 100644 (file)
@@ -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));
index 955a8ac5ebac28293ac37ef5976c6fb5ba6c3a26..016afcbbec1a0347fed92e42475372485b11bf96 100644 (file)
 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")
index 63c2c96a3402b7d418a698e0e88d9fe9b3dfed84..0695bd5d901f660431084878ad06a2e911291f33 100644 (file)
@@ -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 \
index 5f82d1512be3a821dd01783f73bdc2049c0f93a6..e09b36ecad9dde80b16e521ef21cfe22c9bbc01a 100644 (file)
@@ -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;
 
index 8ab758849e5b3d0126800741dcd92fe1897fc0c3..9beaea9c4cc831571c27e2a5ddb47dd2f0cdb546 100644 (file)
@@ -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 (file)
index 0000000..dbf0d35
--- /dev/null
@@ -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)
+\f
+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;
+}
index 2dfb5e16ff18b845b3b45f15670fe909fd23b503..e2a2b56ed3f7a22a3cf6259d76a6457ec84b1080 100644 (file)
@@ -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
    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 */
index 786955bc3e5265eafd55975b25c315581bb1a416..9bf6a6bf52ffaa664aaea7db9717fcefbc23aee9 100644 (file)
@@ -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);
     }
 
index d7927527eba98cba9fb43f34169f99a19e4d8d16..e3e6c5d43bd0d4a4b7818d88e2a81b2f02e3bd32 100644 (file)
@@ -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;
 
index 466d2a522c99b8be783c5502c7d37c2cff0929c0..0e542ef0d28dc50b08ea97bd780424da5aede1d1 100644 (file)
@@ -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;
        }
index 99cc17a0fb1a4927fddba0ee17138ee158c95057..eac1f06fca4789c751e6b229ef4aea116692d106 100644 (file)
@@ -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);
index fd0d4cf780fd5db41b63ba0f893781e4c2d5b732..b7245e629bdc2c551efe30950434cacee9877127 100644 (file)
@@ -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;
     }
index 169b6a5d7e5f935a2a91c3340e0fc40f56b02b5b..7b795f1ade5c8365e5a4bf68e7cd039c6a9a14c6 100644 (file)
@@ -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;
        }
index 17293cf6af361deb10a2846deab7d66162acec7f..f6487c57932e3faad8a64e45adcf570e9ab7b897 100644 (file)
@@ -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;
        }
index 48645fc29d68c2b8a803053109df8b77c0885f9e..cf847361264d618b1001677bd86c62661e73d3cb 100644 (file)
@@ -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;
        }
index 78824cf8d6e2c1dd5d403faf8394ea6517a9bbae..c2de9318ae81b13462c940aae839a580f886c2d4 100644 (file)
@@ -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);
index 3f00106ea2cf9c2b975d4dc7f0281389f783192c..27b27858b4ca698a8bfe6908e33001e9a7ef48c9 100644 (file)
@@ -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;
 
index 56deb9a5c0665c6f0cd50fb624d7815b20a9c21e..1736ab80e8f8b942832bf490875b2568279fb87f 100644 (file)
@@ -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);
            }
 
index 0dee9b4425b33415b4d83b80c61af20b954f45d7..fed765692f9a65cc30d20c3b1d17ff2bef81843b 100644 (file)
@@ -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;
     }
index 81a064d19bc087d19214b1a9a7881faa2e324a7a..bcee162c6a04f56e44a01164d0c7af87662cba62 100644 (file)
@@ -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"))
index 6a37b023543b1764629efb16998e53213b95b3f1..498f3fce8c5b5126fb18d90690744a5c0077cb9c 100644 (file)
            <property name="stock-id">gtk-convert</property>
           </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>
           <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"/>
index 6a3f1e154d68b9141acf83ed5a37f530c145bb56..8fb4c2603e37662c0753041b1221a131a0ee52cf 100644 (file)
@@ -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
index a463ee43d1a6c9effa5b9c6d879cc20f61e5ee34..ef06d83a7ca46b46c88071555dac96546b916b62 100644 (file)
@@ -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"
 #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;
 }
-
 \f
 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));
 }
index 0100254386adf3dd9085a0d9954555bbfe51fa76..64fe076dc7417a8a1dc78dda66085e7ffff3fd32 100644 (file)
@@ -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
 #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
 
index 2e90ba76535ac69733731a4207aaf69f38d23cc5..a61e56e583b7a045aabdc275c1ea9ab7813a2b05 100644 (file)
@@ -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;
 }
 
index 332abe37608df3477a1a8249451d99fddb909ed1..399b020b32cb3f767c6246c31538d5484143d493 100644 (file)
@@ -79,7 +79,8 @@ enum
 {
   PROP_0,
   PROP_FILENAME,
-  PROP_DESCRIPTION
+  PROP_DESCRIPTION,
+  PROP_ID
 };
 
 
@@ -94,18 +95,82 @@ 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);
 
   g_string_free (title, TRUE);
 }
 
+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,
@@ -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;
 }
index f65e9e3cf31a2fd6900e5827cc5d7ecf85bc7f39..0aa913c1f99edffcc23e51b59dbab1016dc574b9 100644 (file)
@@ -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);
index 80983e01b31099203f56d13cd58488cc9aae2395..5963803f45b9898ddd0d75e238afbe9c141f81b9 100644 (file)
@@ -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"
 
 #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"
index 805681d9ecc37f83bc40d0c00024391c9be7002f..c19a1c78eaade7c0c40e72cfe736d1ac7849b60a 100644 (file)
@@ -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 ();
index 1145d645af31ba0faf9412b409cf32f5cb4212ec..d62ef6379f071ff2df804d0df80acd0f17f45cb9 100644 (file)
@@ -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 (file)
index 0000000..7d5e928
--- /dev/null
@@ -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
index 1c3a7e17095ccd0613826486fa4958271c7153a4..ae7a38ad8376129af2cbb8d7ea392dd28c9ee91b 100644 (file)
@@ -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'],
-      [outfile=@%:@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 FILE=@%:@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