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.
@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:
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
* 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.
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.
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
/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
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
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
@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
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
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
@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
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
@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
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.
@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
* 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.
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.
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
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{"}.
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},
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
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.
#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"
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 ();
}
/* 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"
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 ();
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 \
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 \
/* 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
/* 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. */
};
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)
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)
/* 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
/* Creation and destruction. */
struct caseinit *caseinit_create (void);
+struct caseinit *caseinit_clone (struct caseinit *);
void caseinit_clear (struct caseinit *);
void caseinit_destroy (struct caseinit *);
--- /dev/null
+/* 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));
+}
--- /dev/null
+/* 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 */
--- /dev/null
+/* 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,
+ };
--- /dev/null
+/* 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 */
#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
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 *);
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);
}
}
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 *
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,
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. */
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;
+}
#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 *);
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
{
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. */
\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 */
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)
{
#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"
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. */
free (handle->id);
free (handle->name);
free (handle->file_name);
- scratch_handle_destroy (handle->sh);
free (handle);
}
/* 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;
}
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. */
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. */
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. */
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
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;
/* 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
#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.) */
{
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. */
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. */
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,
+++ /dev/null
-/* 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);
- }
-}
+++ /dev/null
-/* 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 */
+++ /dev/null
-/* 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);
-}
+++ /dev/null
-/* 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 */
+++ /dev/null
-/* 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,
- };
+++ /dev/null
-/* 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 */
--- /dev/null
+/* 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;
+}
--- /dev/null
+/* 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 */
#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"
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));
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)
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")
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 \
}
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;
{
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;
}
--- /dev/null
+/* 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;
+}
/* 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 */
#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"
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) */
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;
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;
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
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);
}
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;
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;
}
{
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);
{
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;
}
{
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;
}
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;
}
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;
}
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);
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;
if ( ds == NULL )
{
- ds = dataset_create ();
+ ds = dataset_create (NULL, "");
d = dataset_dict (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;
}
#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"
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"))
<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"/>
#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"
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);
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))
{
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
#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"
#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);
}
-/* 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"),
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:
psppire_window_set_filename (PSPPIRE_WINDOW (de), filename->str);
- save_file (PSPPIRE_WINDOW (de));
-
g_string_free (filename, TRUE);
}
break;
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)
{
/* 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 ();
}
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));
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
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
};
}
-
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));
}
/* 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 ())
gboolean save_as_portable;
+
+ struct ll ll; /* In global 'all_data_windows' list. */
+ unsigned long int lazy_serial;
+ unsigned int dataset_seqno;
};
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
}
-/* 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;
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);
}
}
-/* 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);
}
}
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",
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"),
{
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));
}
psppire_syntax_window_iface_init (PsppireWindowIface *iface)
{
iface->save = syntax_save;
+ iface->pick_filename = syntax_pick_filename;
iface->load = syntax_load;
}
{
PROP_0,
PROP_FILENAME,
- PROP_DESCRIPTION
+ PROP_DESCRIPTION,
+ PROP_ID
};
{
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,
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);
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;
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);
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)
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;
PROP_FILENAME,
filename_spec);
+ g_object_class_install_property (object_class,
+ PROP_ID,
+ id_spec);
+
parent_class = g_type_class_peek_parent (class);
}
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;
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);
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);
}
-
+/* The return value is encoded in the glib filename encoding. */
const gchar *
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)
{
{
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;
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;
}
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;
GTypeInterface g_iface;
void (*save) (PsppireWindow *w);
+ void (*pick_filename) (PsppireWindow *);
gboolean (*load) (PsppireWindow *w, const gchar *);
};
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);
#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"
#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"
#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"
#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);
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,
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)
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;
}
- dataset_destroy (the_dataset);
+ session_destroy (the_session);
random_done ();
settings_done ();
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 \
--- /dev/null
+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
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
[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
/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])
])])
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])
["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