Add scratch file handles.
authorBen Pfaff <blp@gnu.org>
Sun, 29 Jan 2006 02:41:11 +0000 (02:41 +0000)
committerBen Pfaff <blp@gnu.org>
Sun, 29 Jan 2006 02:41:11 +0000 (02:41 +0000)
Now a file handle can refer to a disk file, to an in-memory
structure, or to the "inline" file, instead of just to a disk
file.  The introduction of new categories means that special cases
for the inline file in a few places could be eliminated, but it
also means that code that assumed that a handle refers to a file
has to check for that.

Also, now file handles can be freed, so code now must be sure not
to access a handle after closing it (with fh_close()).

Plus some cleanups.

96 files changed:
Smake
doc/data-io.texi
doc/files.texi
doc/language.texi
doc/transformation.texi
po/en_GB.po
po/pspp.pot
src/ChangeLog
src/Makefile.am
src/aggregate.c
src/any-reader.c [new file with mode: 0644]
src/any-reader.h [new file with mode: 0644]
src/any-writer.c [new file with mode: 0644]
src/any-writer.h [new file with mode: 0644]
src/apply-dict.c
src/cat.h
src/command.def
src/correlations.q
src/data-list.c
src/dfm-read.c
src/dfm-write.c
src/dictionary.c
src/dictionary.h
src/error.c
src/file-handle-def.c
src/file-handle-def.h
src/file-handle.h
src/file-handle.q
src/file-type.c
src/filename.c
src/filename.h
src/get.c
src/glob.c
src/inpt-pgm.c
src/matrix-data.c
src/pfm-read.c
src/pfm-read.h
src/pfm-write.c
src/pfm-write.h
src/print.c
src/regression.q
src/scratch-handle.c [new file with mode: 0644]
src/scratch-handle.h [new file with mode: 0644]
src/scratch-reader.c [new file with mode: 0644]
src/scratch-reader.h [new file with mode: 0644]
src/scratch-writer.c [new file with mode: 0644]
src/scratch-writer.h [new file with mode: 0644]
src/sfm-read.c
src/sfm-read.h
src/sfm-write.c
src/str.c
src/str.h
src/sysfile-info.c
src/var.h
src/vfm.c
src/vfm.h
tests/bugs/agg-crash-2.sh
tests/bugs/compute-lv.sh
tests/bugs/computebug.out
tests/bugs/crosstabs-crash.sh
tests/bugs/match-files-scratch.sh
tests/bugs/multipass.sh
tests/bugs/recode-copy-bug-1.out
tests/bugs/recode-copy-bug-2.out
tests/bugs/t-test-alpha.sh
tests/bugs/temp-freq.sh
tests/command/aggregate.sh
tests/command/autorecod.sh
tests/command/beg-data.sh
tests/command/count.sh
tests/command/data-list.sh
tests/command/examine-percentiles.sh
tests/command/examine.sh
tests/command/file-handle.sh
tests/command/file-label.sh
tests/command/flip.sh
tests/command/lag.sh
tests/command/list.sh
tests/command/longvars.sh
tests/command/loop.sh
tests/command/oneway-with-splits.sh
tests/command/oneway.sh
tests/command/print.sh
tests/command/rename.sh
tests/command/sysfile-info.sh
tests/command/t-test-1-indep-val.sh
tests/command/t-test-1s.sh
tests/command/t-test-groups.sh
tests/command/t-test-pairs.sh
tests/command/tabs.sh
tests/command/trimmed-mean.sh
tests/command/weight.sh
tests/expressions/variables.sh
tests/expressions/vectors.sh
tests/stats/descript-basic.sh
tests/stats/descript-missing.sh

diff --git a/Smake b/Smake
index 0100edff49317a660a0e99a52e755345bcb50ab0..c3309624dc07582b8719a083f811992bfcaf4f39 100644 (file)
--- a/Smake
+++ b/Smake
@@ -7,7 +7,7 @@ GNULIB_MODULES = alloca alloca-opt assert full-read full-write          \
 gethostname getline getlogin_r getopt gettext memchr memcmp memmem     \
 memmove memset progname readlink restrict snprintf stat-macros stdbool \
 stpcpy strcase strcspn strerror strftime strstr strtod strtok_r strtol \
-strtoul vsnprintf xalloc xalloc-die xreadlink
+strtoul vsnprintf xalloc xalloc-die xreadlink xvasprintf
 
 all: po/POTFILES.in
        test -d m4 || mkdir m4
index 0137ad29d89d8fff2cf7bab126d3dec361dd0a44..d4dab8af8ea564eb4a5fdcd4de04610f7f133d97 100644 (file)
@@ -8,26 +8,26 @@
 
 Data are the focus of the PSPP language.  
 Each datum  belongs to a @dfn{case} (also called an @dfn{observation}).
-Each case represents an individual or `experimental unit'.
+Each case represents an individual or ``experimental unit''.
 For example, in the results of a survey, the names of the respondents,
-their sex, age @i{etc}. and their responses are all data and the data
+their sex, age, etc.@: and their responses are all data and the data
 pertaining to single respondent is a case.
 This chapter examines
 the PSPP commands for defining variables and reading and writing data.
 
-@quotation
-@strong{Please note:} Data is not actually read until a procedure is
-executed.  These commands tell PSPP how to read data, but they
-do not @emph{cause} PSPP to read data.
+@quotation Note
+These commands tell PSPP how to read data, but the data will not
+actually be read until a procedure is executed.
 @end quotation
 
 @menu
 * BEGIN DATA::                  Embed data within a syntax file.
 * CLEAR TRANSFORMATIONS::       Clear pending transformations.
+* CLOSE FILE HANDLE::           Close a file handle.
 * DATA LIST::                   Fundamental data reading command.
 * END CASE::                    Output the current case.
 * END FILE::                    Terminate the current input program.
-* FILE HANDLE::                 Support for fixed-length records.
+* FILE HANDLE::                 Support for special file formats.
 * INPUT PROGRAM::               Support for complex input programs.
 * LIST::                        List cases in the active file.
 * MATRIX DATA::                 Read matrices in text format.
@@ -40,7 +40,7 @@ do not @emph{cause} PSPP to read data.
 * WRITE::                       Display values in write formats.
 @end menu
 
-@node BEGIN DATA, CLEAR TRANSFORMATIONS, Data Input and Output, Data Input and Output
+@node BEGIN DATA
 @section BEGIN DATA
 @vindex BEGIN DATA
 @vindex END DATA
@@ -65,7 +65,7 @@ white space and exactly one space between the words @code{END} and
 END DATA.
 @end example
 
-@node CLEAR TRANSFORMATIONS, DATA LIST, BEGIN DATA, Data Input and Output
+@node CLEAR TRANSFORMATIONS
 @section CLEAR TRANSFORMATIONS
 @vindex CLEAR TRANSFORMATIONS
 
@@ -77,7 +77,31 @@ CLEAR TRANSFORMATIONS.
 transformations.  It does not cancel the current input program.  It is
 valid only when PSPP is interactive, not in syntax files.
 
-@node DATA LIST, END CASE, CLEAR TRANSFORMATIONS, Data Input and Output
+@node CLOSE FILE HANDLE
+@section CLOSE FILE HANDLE
+
+@display
+CLOSE FILE HANDLE handle_name.
+@end display
+
+@cmd{CLOSE FILE HANDLE} disassociates the name of a file handle with a
+given file.  The only specification is the name of the handle to close.
+Afterward
+@cmd{FILE HANDLE}.
+
+If the file handle name refers to a scratch file, then the storage
+associated with the scratch file in memory or on disk will be freed.
+If the scratch file is in use, e.g.@: it has been specified on a
+@cmd{GET} command whose execution has not completed, then freeing is
+delayed until it is no longer in use.
+
+The file named INLINE, which represents data entered between @cmd{BEGIN
+DATA} and @cmd{END DATA}, cannot be closed.  Attempts to close it with
+@cmd{CLOSE FILE HANDLE} have no effect.
+
+@cmd{CLOSE FILE HANDLE} is a PSPP extension.
+
+@node DATA LIST
 @section DATA LIST
 @vindex DATA LIST
 @cindex reading data from a file
@@ -104,7 +128,7 @@ Each form of @cmd{DATA LIST} is described in detail below.
 * DATA LIST LIST::              Each case must be on a single line.
 @end menu
 
-@node DATA LIST FIXED, DATA LIST FREE, DATA LIST, DATA LIST
+@node DATA LIST FIXED
 @subsection DATA LIST FIXED
 @vindex DATA LIST FIXED
 @cindex reading fixed-format data
@@ -131,7 +155,7 @@ keyword FIXED is optional.
 
 The FILE subcommand must be used if input is to be taken from an
 external file.  It may be used to specify a filename as a string or a
-file handle (@pxref{FILE HANDLE}).  If the FILE subcommand is not used,
+file handle (@pxref{File Handles}).  If the FILE subcommand is not used,
 then input is assumed to be specified within the command file using
 @cmd{BEGIN DATA}@dots{}@cmd{END DATA} (@pxref{BEGIN DATA}).
 
@@ -229,7 +253,7 @@ applies to later FORTRAN and columnar specifiers.
 * DATA LIST FIXED Examples::    Examples of DATA LIST FIXED.
 @end menu
 
-@node DATA LIST FIXED Examples,  , DATA LIST FIXED, DATA LIST FIXED
+@node DATA LIST FIXED Examples
 @unnumberedsubsubsec Examples
 
 @enumerate
@@ -315,7 +339,7 @@ This example shows keywords abbreviated to their first 3 letters.
 
 @end enumerate
 
-@node DATA LIST FREE, DATA LIST LIST, DATA LIST FIXED, DATA LIST
+@node DATA LIST FREE
 @subsection DATA LIST FREE
 @vindex DATA LIST FREE
 
@@ -367,7 +391,7 @@ preceding it are to have input/output format @samp{F8.0}.
 Specified field widths are ignored on input, although all normal limits
 on field width apply, but they are honored on output.
 
-@node DATA LIST LIST,  , DATA LIST FREE, DATA LIST
+@node DATA LIST LIST
 @subsection DATA LIST LIST
 @vindex DATA LIST LIST
 
@@ -390,7 +414,7 @@ that each input line is expected to correspond to exactly one input
 record.  If more or fewer fields are found on an input line than
 expected, an appropriate diagnostic is issued.
 
-@node END CASE, END FILE, DATA LIST, Data Input and Output
+@node END CASE
 @section END CASE
 @vindex END CASE
 
@@ -401,7 +425,7 @@ END CASE.
 @cmd{END CASE} is used only within @cmd{INPUT PROGRAM} to output the
 current case.  @xref{INPUT PROGRAM}, for details.
 
-@node END FILE, FILE HANDLE, END CASE, Data Input and Output
+@node END FILE
 @section END FILE
 @vindex END FILE
 
@@ -412,50 +436,67 @@ END FILE.
 @cmd{END FILE} is used only within @cmd{INPUT PROGRAM} to terminate
 the current input program.  @xref{INPUT PROGRAM}.
 
-@node FILE HANDLE, INPUT PROGRAM, END FILE, Data Input and Output
+@node FILE HANDLE
 @section FILE HANDLE
 @vindex FILE HANDLE
 
 @display
-FILE HANDLE handle_name
-        /NAME='filename'
-        /MODE=@{CHARACTER,IMAGE@}
-        /LRECL=rec_len
-        /TABWIDTH=tab_width
+For text files:
+        FILE HANDLE handle_name
+                /NAME='filename'
+                [/MODE=CHARACTER]
+                /TABWIDTH=tab_width
+
+For binary files with fixed-length records:
+        FILE HANDLE handle_name
+                /NAME='filename'
+                /MODE=IMAGE
+                [/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
 its attributes, so that later commands can refer to the file by its
-handle name.  Because names of text files can be specified directly on
-commands that access files, @cmd{FILE HANDLE} is only needed when a
+handle name.  Names of text files can be specified directly on
+commands that access files, so that @cmd{FILE HANDLE} is only needed when a
 file is not an ordinary file containing lines of text.  However,
 @cmd{FILE HANDLE} may be used even for text files, and it may be
 easier to specify a file's name once and later refer to it by an
 abstract handle.
 
-Specify the file handle name as an identifier.  Any given identifier may
-only appear once in a PSPP run.  File handles may not be reassigned to a
-different file.  The file handle name must immediately follow the @cmd{FILE
-HANDLE} command name.
-
-The NAME subcommand specifies the name of the file associated with the
-handle.  It is the only required subcommand.
+Specify the file handle name as the identifier immediately following the
+@cmd{FILE HANDLE} command name.  The identifier INLINE is reserved for
+representing data embedded in the syntax file (@pxref{BEGIN DATA}) The
+file handle name must not already have been used in a previous
+invocation of @cmd{FILE HANDLE}, unless it has been closed by an
+intervening command (@pxref{CLOSE FILE HANDLE}).
 
 MODE specifies a file mode.  In CHARACTER mode, the default, the data
-file is opened in ANSI C text mode, so that local end of line
-conventions are followed, and each text line is read as one record.
+file is read as a text file, according to the local system's 
+conventions, and each text line is read as one record.
 In CHARACTER mode, most input programs will expand tabs to spaces
 (@cmd{DATA LIST FREE} with explicitly specified delimiters is an
 exception).  By default, each tab is 4 characters wide, but an
 alternate width may be specified on TABWIDTH.  A tab width of 0
 suppresses tab expansion entirely.
 
-By contrast, in IMAGE mode, the data file is opened in ANSI C binary
-mode and records are a fixed length.  In IMAGE mode, LRECL specifies
-the record length in bytes, with a default of 1024.  Tab characters
-are never expanded to spaces in binary mode.
+In IMAGE mode, the data file is opened in ANSI C binary mode and records
+are fixed in length.  In IMAGE mode, LRECL specifies the record length in
+bytes, with a default of 1024.  Tab characters are never expanded to
+spaces in binary mode.
+
+The NAME subcommand specifies the name of the file associated with the
+handle.  It is required in CHARACTER and IMAGE modes.
+
+The SCRATCH mode 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.
 
-@node INPUT PROGRAM, LIST, FILE HANDLE, Data Input and Output
+@node INPUT PROGRAM
 @section INPUT PROGRAM
 @vindex INPUT PROGRAM
 
@@ -606,7 +647,7 @@ LIST/FORMAT=NUMBERED.
 The above example causes an active file to be created consisting of 50
 random variates between 0 and 10.
 
-@node LIST, MATRIX DATA, INPUT PROGRAM, Data Input and Output
+@node LIST
 @section LIST
 @vindex LIST
 
@@ -647,7 +688,7 @@ cannot fit on a single line, then a multi-line format will be used.
 
 @cmd{LIST} is a procedure.  It causes the data to be read.
 
-@node MATRIX DATA, NEW FILE, LIST, Data Input and Output
+@node MATRIX DATA
 @section MATRIX DATA
 @vindex MATRIX DATA
 
@@ -674,7 +715,7 @@ the matrices.  You may not specify a variable named @code{VARNAME_}.  You
 should specify VARIABLES first.
 
 Specify the file to read on FILE, either as a file name string or a file
-handle (@pxref{FILE HANDLE}).  If FILE is not specified then matrix data
+handle (@pxref{File Handles}).  If FILE is not specified then matrix data
 must immediately follow @cmd{MATRIX DATA} with a @cmd{BEGIN
 DATA}@dots{}@cmd{END DATA}
 construct (@pxref{BEGIN DATA}).
@@ -749,7 +790,7 @@ Right now @cmd{MATRIX DATA} isn't too useful due to a lack of procedures
 accepting or producing related data, so these semantics aren't
 documented.  Later, they'll be described here in detail.
 
-@node NEW FILE, PRINT, MATRIX DATA, Data Input and Output
+@node NEW FILE
 @section NEW FILE
 @vindex NEW FILE
 
@@ -759,7 +800,7 @@ NEW FILE.
 
 @cmd{NEW FILE} command clears the current active file.
 
-@node PRINT, PRINT EJECT, NEW FILE, Data Input and Output
+@node PRINT
 @section PRINT
 @vindex PRINT
 
@@ -785,8 +826,8 @@ invoking a procedure (@pxref{EXECUTE}).
 All @cmd{PRINT} subcommands are optional.
 
 The OUTFILE subcommand specifies the file to receive the output.  The
-file may be a file name as a string or a file handle (@pxref{FILE
-HANDLE}).  If OUTFILE is not present then output will be sent to PSPP's
+file may be a file name as a string or a file handle (@pxref{File
+Handles}).  If OUTFILE is not present then output will be sent to PSPP's
 output listing file.
 
 The RECORDS subcommand specifies the number of lines to be output.  The
@@ -822,7 +863,7 @@ line, then text is written at that point on the line, the line will be
 truncated to that length, although additional text being added will
 again extend the line to that length.
 
-@node PRINT EJECT, PRINT SPACE, PRINT, Data Input and Output
+@node PRINT EJECT
 @section PRINT EJECT
 @vindex PRINT EJECT
 
@@ -845,7 +886,7 @@ written, the current page in the listing file is ejected.
 
 @xref{PRINT}, for more information on syntax and usage.
 
-@node PRINT SPACE, REREAD, PRINT EJECT, Data Input and Output
+@node PRINT SPACE
 @section PRINT SPACE
 @vindex PRINT SPACE
 
@@ -856,15 +897,15 @@ PRINT SPACE OUTFILE='filename' n_lines.
 @cmd{PRINT SPACE} prints one or more blank lines to an output file.
 
 The OUTFILE subcommand is optional.  It may be used to direct output to
-a file specified by file name as a string or file handle (@pxref{FILE
-HANDLE}).  If OUTFILE is not specified then output will be directed to
+a file specified by file name as a string or file handle (@pxref{File
+Handles}).  If OUTFILE is not specified then output will be directed to
 the listing file.
 
 n_lines is also optional.  If present, it is an expression
 (@pxref{Expressions}) specifying the number of blank lines to be
 printed.  The expression must evaluate to a nonnegative value.
 
-@node REREAD, REPEATING DATA, PRINT SPACE, Data Input and Output
+@node REREAD
 @section REREAD
 @vindex REREAD
 
@@ -878,8 +919,8 @@ already processed by @cmd{DATA LIST} or another input command to be re-read
 for further processing.
 
 The FILE subcommand, which is optional, is used to specify the file to
-have its line re-read.  The file must be specified in the form of a file
-handle (@pxref{FILE HANDLE}).  If FILE is not specified then the last
+have its line re-read.  The file must be specified as the name of a file
+handle (@pxref{File Handles}).  If FILE is not specified then the last
 file specified on @cmd{DATA LIST} will be assumed (last file specified
 lexically, not in terms of flow-of-control).
 
@@ -892,7 +933,7 @@ are numbered from 1 at the left margin.
 Issuing @code{REREAD} multiple times will not back up in the data
 file.  Instead, it will re-read the same line multiple times.
 
-@node REPEATING DATA, WRITE, REREAD, Data Input and Output
+@node REPEATING DATA
 @section REPEATING DATA
 @vindex REPEATING DATA
 
@@ -941,7 +982,7 @@ FIXED} (@pxref{DATA LIST FIXED}).
 All other subcommands are optional.
 
 FILE specifies the file to read, either a file name as a string or a
-file handle (@pxref{FILE HANDLE}).  If FILE is not present then the
+file handle (@pxref{File Handles}).  If FILE is not present then the
 default is the last file handle used on @cmd{DATA LIST} (lexically, not in
 terms of flow of control).
 
@@ -974,7 +1015,7 @@ have been declared with @cmd{NUMERIC} or another command.
 structure (@pxref{LOOP}).  Use @cmd{DATA LIST} before, not after,
 @cmd{REPEATING DATA}.
 
-@node WRITE,  , REPEATING DATA, Data Input and Output
+@node WRITE
 @section WRITE
 @vindex WRITE
 
index ea1ff885f471af869ba2aea5aea63601a26cd7da..816a61ed31932519b8421216d201a70480d17ecc 100644 (file)
@@ -21,18 +21,18 @@ portable files.
 @vindex APPLY DICTIONARY
 
 @display
-APPLY DICTIONARY FROM='filename'.
+APPLY DICTIONARY FROM=@{'filename',file_handle@}.
 @end display
 
 @cmd{APPLY DICTIONARY} applies the variable labels, value labels,
-and missing values from variables in a system file to corresponding
+and missing values taken from a file to corresponding
 variables in the active file.  In some cases it also updates the
 weighting variable.
 
-Specify a system file with a file name string or as a file handle
-(@pxref{FILE HANDLE}).  The dictionary in the system file will be read,
-but it will not replace the active file dictionary.  The system file's
-data will not be read.
+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 file dictionary.
+The file's data will not be read.
 
 Only variables with names that exist in both the active file and the
 system file are considered.  Variables with the same name but different
@@ -79,9 +79,9 @@ EXPORT
 @end display
 
 The @cmd{EXPORT} procedure writes the active file dictionary and data to a
-specified portable file.
+specified portable file or scratch file.
 
-By default, cases excluded with FILTER are written to the portable
+By default, cases excluded with FILTER are written to the
 file.  These can be excluded by specifying DELETE on the UNSELECTED
 subcommand.  Specifying RETAIN makes the default explicit.
 
@@ -95,8 +95,8 @@ subcommand may be used to specify the number of decimal digits of
 precision to write.  DIGITS applies only to non-integers.
 
 The OUTFILE subcommand, which is the only required subcommand, specifies
-the portable file to be written as a file name string or a file handle
-(@pxref{FILE HANDLE}).
+the portable file or scratch 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
 (@pxref{SAVE}).
@@ -114,25 +114,25 @@ The MAP subcommand is currently ignored.
 
 @display
 GET
-        /FILE='filename'
+        /FILE=@{'filename',file_handle@}
         /DROP=var_list
         /KEEP=var_list
         /RENAME=(src_names=target_names)@dots{}
 @end display
 
 @cmd{GET} clears the current dictionary and active file and
-replaces them with the dictionary and data from a specified system file.
+replaces them with the dictionary and data from a specified file.
 
 The FILE subcommand is the only required subcommand.  Specify the system
-file to be read as a string file name or a file handle (@pxref{FILE
-HANDLE}).
+file, portable file, or scratch file to be read as a string file name or
+a file handle (@pxref{File Handles}).
 
-By default, all the variables in a system file are read.  The DROP
+By default, all the variables in a file are read.  The DROP
 subcommand can be used to specify a list of variables that are not to be
 read.  By contrast, the KEEP subcommand can be used to specify variable
 that are to be read, with all other variables not read.
 
-Normally variables in a system file retain the names that they were
+Normally variables in a file retain the names that they were
 saved under.  Use the RENAME subcommand to change these names.  Specify,
 within parentheses, a list of variable names followed by an equals sign
 (@samp{=}) and the names that they should be renamed to.  Multiple
@@ -145,14 +145,17 @@ eliminated.  When this is done, only a single variable may be renamed at
 once.  For instance, @samp{/RENAME=A=B}.  This alternate syntax is
 deprecated.
 
-DROP, KEEP, and RENAME are performed in left-to-right order.  They
-each may be present any number of times.  @cmd{GET} never modifies a
-system file on disk.  Only the active file read from the system file
+DROP, KEEP, and RENAME are executed in left-to-right order.  
+Each may be present any number of times.  @cmd{GET} never modifies a
+file on disk.  Only the active file read from the file
 is affected by these subcommands.
 
 @cmd{GET} does not cause the data to be read, only the dictionary.  The data
 is read later, when a procedure is executed.
 
+Use of @cmd{GET} to read a portable file or scratch file is a PSPP
+extension.
+
 @node IMPORT
 @section IMPORT
 @vindex IMPORT
@@ -168,11 +171,12 @@ IMPORT
 
 The @cmd{IMPORT} transformation clears the active file dictionary and
 data and
-replaces them with a dictionary and data from a portable file on disk.
+replaces them with a dictionary and data from a system, portable file,
+or scratch 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
-(@pxref{FILE HANDLE}).
+(@pxref{File Handles}).
 
 The TYPE subcommand is currently not used.
 
@@ -181,6 +185,9 @@ DROP, KEEP, and RENAME follow the syntax used by @cmd{GET} (@pxref{GET}).
 @cmd{IMPORT} does not cause the data to be read, only the dictionary.  The
 data is read later, when a procedure is executed.
 
+Use of @cmd{IMPORT} to read a system file or scratch file is a PSPP
+extension.
+
 @node MATCH FILES
 @section MATCH FILES
 @vindex MATCH FILES
@@ -199,26 +206,28 @@ MATCH FILES
         /MAP
 @end display
 
-@cmd{MATCH FILES} merges one or more system files, optionally
+@cmd{MATCH FILES} merges one or more system, portable, or scratch files,
+optionally
 including the active file.  Records with the same values for BY
 variables are combined into a single record.  Records with different
-values are output in order.  Thus, multiple sorted system files are
-combined into a single sorted system file based on the value of the BY
+values are output in order.  Thus, multiple sorted files are
+combined into a single sorted file based on the value of the BY
 variables.  The results of the merge become the new active file.
 
 The BY subcommand specifies a list of variables that are used to match
-records from each of the system files.  Variables specified must exist
+records from each of the files.  Variables specified must exist
 in all the files specified on FILE and TABLE.  BY should usually be
 specified.  If TABLE or IN is used then BY is required.
 
-Specify FILE with a system file as a file name string or file handle
-(@pxref{FILE HANDLE}), or with an asterisk (@samp{*}) to
+Specify FILE with a system, portable, or scratch file as a file name
+string or file handle
+(@pxref{File Handles}), or with an asterisk (@samp{*}) to
 indicate the current active file.  The files specified on FILE are
 merged together based on the BY variables, or combined case-by-case if
 BY is not specified.  Normally at least two FILE subcommands should be
 specified.
 
-Specify TABLE with a system file to use it as a @dfn{table
+Specify TABLE with a file to use it as a @dfn{table
 lookup file}.  Records in table lookup files are not used up after
 they've been used once.  This means that data in table lookup files can
 correspond to any number of records in FILE files.  Table lookup files
@@ -247,13 +256,16 @@ FIRST, LAST, and MAP are currently ignored.
 @cmd{MATCH FILES} may not be specified following @cmd{TEMPORARY}
 (@pxref{TEMPORARY}) if the active file is used as an input source.
 
+Use of portable or scratch files on @cmd{MATCH FILES} is a PSPP
+extension.
+
 @node SAVE
 @section SAVE
 @vindex SAVE
 
 @display
 SAVE
-        /OUTFILE='filename'
+        /OUTFILE=@{'filename',file_handle@}
         /UNSELECTED=@{RETAIN,DELETE@}
         /@{COMPRESSED,UNCOMPRESSED@}
         /PERMISSIONS=@{WRITEABLE,READONLY@}
@@ -267,11 +279,11 @@ SAVE
 
 The @cmd{SAVE} procedure causes the dictionary and data in the active
 file to
-be written to a system file.
+be written to a system file or scratch file.
 
-OUTFILE is the only required subcommand.  Specify the system
-file to be written as a string file name or a file handle (@pxref{FILE
-HANDLE}).
+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
+(@pxref{File Handles}).
 
 By default, cases excluded with FILTER are written to the system file.
 These can be excluded by specifying DELETE on the UNSELECTED
@@ -384,7 +396,7 @@ XSAVE
 @end display
 
 The @cmd{XSAVE} transformation writes the active file dictionary and
-data to a system file stored on disk.  It is similar to the @cmd{SAVE}
+data to a system file or scratch file.  It is similar to the @cmd{SAVE}
 procedure, with two differences:
 
 @itemize
index e2565f96d1e8fc5a5218d0e05c8c7453b04783e3..36012f14093105583282564482d13e7f2aecc263 100644 (file)
@@ -20,6 +20,7 @@ Later chapters will describe individual commands in detail.
 * Missing Observations::        Handling missing observations.
 * Variables::                   The unit of data storage.
 * Files::                       Files used by PSPP.
+* File Handles::                How files are named.
 * BNF::                         How command syntax is described.
 @end menu
 
@@ -898,7 +899,7 @@ a scratch variable in an analysis, use @cmd{COMPUTE} (@pxref{COMPUTE})
 to copy its value into an ordinary variable, then use that ordinary
 variable in the analysis.
 
-@node Files, BNF, Variables, Language
+@node Files
 @section Files Used by PSPP
 
 PSPP makes use of many files each time it runs.  Some of these it
@@ -914,16 +915,14 @@ most important of these files:
 @itemx syntax file
 These names (synonyms) refer to the file that contains instructions
 that tell PSPP what to do.  The syntax file's name is specified on
-the PSPP command line.  Syntax files can also be pulled in with
+the PSPP command line.  Syntax files can also be read with
 @cmd{INCLUDE} (@pxref{INCLUDE}).
 
 @cindex file, data
 @cindex data file
 @item data file
-Data files contain raw data in ASCII format suitable for being read in
-by @cmd{DATA LIST}.  Data can be embedded in the syntax
-file with @cmd{BEGIN DATA} and @cmd{END DATA}: this makes the
-syntax file a data file too.
+Data files contain raw data in text or binary format.  Data can also
+be embedded in a syntax file with @cmd{BEGIN DATA} and @cmd{END DATA}.
 
 @cindex file, output
 @cindex output file
@@ -936,13 +935,82 @@ 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 contains variable definitions and
-cases.  The active file is not necessarily a disk file: it is stored
-in memory if there is room.
+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
+System files are binary files that store a dictionary and a set of
+cases.  @cmd{GET} and @cmd{SAVE} read and write system files.
+
+@cindex portable file
+@cindex file, portable
+@item portable 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 BNF,  , Files, Language
+@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
+name of a file as a string, that is, enclosed within @samp{'} or
+@samp{"}.
+
+PSPP also supports declaring named file handles with the @cmd{FILE
+HANDLE} command.  This command associates an identifier of your choice
+(the file handle's name) with a file.  Later, the file handle name can
+be substituted for the name of the file.  When PSPP syntax accesses a
+file multiple times, declaring a named file handle simplifies updating
+the syntax later to use a different file.  Use of @cmd{FILE HANDLE} is
+also required to read data files in binary formats.  @xref{FILE HANDLE},
+for more information.
+
+PSPP assumes that a file handle name that begins with @samp{#} refers to
+a scratch file, unless the name has already been declared on @cmd{FILE
+HANDLE} to refer to another kind of file.  A scratch file is similar to
+a system file, except that it persists only for the duration of a given
+PSPP session.  Most commands that read or write a system or portable
+file, such as @cmd{GET} and @cmd{SAVE}, also accept scratch file
+handles.  Scratch file handles may also be declared explicitly with
+@cmd{FILE HANDLE}.  Scratch files are a PSPP extension.
+
+In some circumstances, PSPP must distinguish whether a file handle
+refers to a system file or a portable file.  When this is necessary to
+read a file, e.g.@: as an input file for @cmd{GET} or @cmd{MATCH FILES},
+PSPP uses the file's contents to decide.  In the context of writing a
+file, e.g.@: as an output file for @cmd{SAVE} or @cmd{AGGREGATE}, PSPP
+decides based on the file's name: if it ends in @samp{.por} (with any
+capitalization), then PSPP writes a portable file; otherwise, PSPP
+writes a system file.
+
+INLINE is reserved as a file handle name.  It refers to the ``data
+file'' embedded into the syntax file between @cmd{BEGIN DATA} and
+@cmd{END DATA}.  @xref{BEGIN DATA}, for more information.
+
+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
+more information.
+
+@node BNF
 @section Backus-Naur Form
 @cindex BNF
 @cindex Backus-Naur Form
index e722b9c4c8620b19b1e473ce09389eda5ef2983b..0fff3bf942abc2168401d3d6e7acb39f98aef3a1 100644 (file)
@@ -23,7 +23,7 @@ as a rule.
 
 @display
 AGGREGATE 
-        OUTFILE=@{*,'filename'@}          
+        OUTFILE=@{*,'filename',file_handle@}          
         /PRESORTED
         /DOCUMENT
         /MISSING=COLUMNWISE
@@ -37,9 +37,11 @@ variables called @dfn{break variables}.  Several functions are available
 for summarizing case contents.
 
 The OUTFILE subcommand is required and must appear first.  Specify a
-system file by file name string or file handle (@pxref{FILE HANDLE}).
+system file, portable file, or scratch file by file name or file
+handle (@pxref{File Handles}).
 The aggregated cases are written to this file.  If @samp{*} is
-specified, then the aggregated cases replace the active file.
+specified, then the aggregated cases replace the active file.  Use of
+OUTFILE to write a portable file or scratch file is a PSPP extension.
 
 By default, the active file will be sorted based on the break variables
 before aggregation takes place.  If the active file is already sorted
index 0fc25bc532068467a7c3b3edb05adcfca4946175..037fcf32dce207d185fa43a1e5caeeee1936afe2 100644 (file)
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PSPP 0.3.1\n"
 "Report-Msgid-Bugs-To: pspp-dev@gnu.org\n"
-"POT-Creation-Date: 2006-01-24 17:40+0800\n"
+"POT-Creation-Date: 2006-01-28 17:20-0800\n"
 "PO-Revision-Date: 2004-01-23 13:04+0800\n"
 "Last-Translator: John Darrington <john@darrington.wattle.id.au>\n"
 "Language-Team: John Darrington <john@darrington.wattle.id.au>\n"
@@ -16,15 +16,15 @@ msgstr ""
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=2; plural=(n!=1);\n"
 
-#: src/aggregate.c:199
+#: src/aggregate.c:200
 msgid "while expecting COLUMNWISE"
 msgstr ""
 
-#: src/aggregate.c:228
+#: src/aggregate.c:229
 msgid "expecting BREAK"
 msgstr ""
 
-#: src/aggregate.c:233
+#: src/aggregate.c:234
 msgid ""
 "When PRESORTED is specified, specifying sorting directions with (A) or (D) "
 "has no effect.  Output data will be sorted the same way as the input data."
@@ -78,20 +78,34 @@ msgid ""
 "contains the aggregate variables and the break variables."
 msgstr ""
 
+#: src/any-reader.c:74
+#, c-format
+msgid "An error occurred while opening \"%s\": %s."
+msgstr ""
+
+#: src/any-reader.c:129
+#, c-format
+msgid "\"%s\" is not a system or portable file."
+msgstr ""
+
+#: src/any-reader.c:135 src/any-writer.c:80
+msgid "The inline file is not allowed here."
+msgstr ""
+
 #: src/apply-dict.c:71
 #, c-format
 msgid "Variable %s is %s in target file, but %s in source file."
 msgstr ""
 
 #: src/apply-dict.c:74 src/apply-dict.c:75 src/format.c:198 src/recode.c:464
-#: src/recode.c:465 src/sfm-read.c:1014 src/sfm-read.c:1159
-#: src/sfm-read.c:1160 src/vars-atr.c:40 src/vars-atr.c:48
+#: src/recode.c:465 src/sfm-read.c:1016 src/sfm-read.c:1161
+#: src/sfm-read.c:1162 src/vars-atr.c:40 src/vars-atr.c:48
 msgid "string"
 msgstr ""
 
 #: src/apply-dict.c:74 src/apply-dict.c:75 src/format.c:198 src/recode.c:464
-#: src/recode.c:465 src/sfm-read.c:1014 src/sfm-read.c:1159
-#: src/sfm-read.c:1160 src/vars-atr.c:40
+#: src/recode.c:465 src/sfm-read.c:1016 src/sfm-read.c:1161
+#: src/sfm-read.c:1162 src/vars-atr.c:40
 msgid "numeric"
 msgstr ""
 
@@ -234,8 +248,8 @@ msgstr ""
 msgid "Source variable count (%u) does not match target variable count (%u)."
 msgstr ""
 
-#: src/autorecode.c:142 src/command.c:797 src/file-handle.q:83 src/lexer.c:440
-#: src/matrix-data.c:532 src/print.c:335 src/print.c:1044 src/sel-if.c:57
+#: src/autorecode.c:142 src/command.c:797 src/lexer.c:440
+#: src/matrix-data.c:532 src/print.c:335 src/print.c:1046 src/sel-if.c:57
 #: src/sel-if.c:134 src/vector.c:197
 msgid "expecting end of command"
 msgstr ""
@@ -560,7 +574,7 @@ msgstr ""
 
 #: src/crosstabs.q:1109 src/crosstabs.q:1136 src/crosstabs.q:1156
 #: src/crosstabs.q:1178 src/examine.q:1131 src/frequencies.q:1142
-#: src/frequencies.q:1263 src/sysfile-info.c:518 src/vfm.c:840
+#: src/frequencies.q:1263 src/sysfile-info.c:518 src/vfm.c:845
 msgid "Value"
 msgstr ""
 
@@ -934,288 +948,275 @@ msgstr ""
 msgid "Field too long (%d characters).  Truncated after character %d."
 msgstr ""
 
-#: src/data-list.c:142
-msgid ""
-"DATA LIST may not use a different file from that specified on its "
-"surrounding FILE TYPE."
+#: src/data-list.c:144
+msgid "DATA LIST must use the same file as the enclosing FILE TYPE."
 msgstr ""
 
-#: src/data-list.c:161
+#: src/data-list.c:163
 msgid "The END subcommand may only be specified once."
 msgstr ""
 
-#: src/data-list.c:196
+#: src/data-list.c:198
 msgid "Only one of FIXED, FREE, or LIST may be specified."
 msgstr ""
 
-#: src/data-list.c:346 src/print.c:296
+#: src/data-list.c:348 src/print.c:296
 #, c-format
 msgid ""
 "The record number specified, %ld, is before the previous record, %d.  Data "
 "fields must be listed in order of increasing record number."
 msgstr ""
 
-#: src/data-list.c:375 src/data-list.c:1735
+#: src/data-list.c:377 src/data-list.c:1727
 msgid ""
 "SPSS-like or FORTRAN-like format specification expected after variable names."
 msgstr ""
 
-#: src/data-list.c:386
+#: src/data-list.c:388
 msgid "At least one variable must be specified."
 msgstr ""
 
-#: src/data-list.c:391 src/print.c:328
+#: src/data-list.c:393 src/print.c:328
 msgid ""
 "Variables are specified on records that should not exist according to "
 "RECORDS subcommand."
 msgstr ""
 
-#: src/data-list.c:424 src/data-list.c:438 src/print.c:520 src/print.c:533
+#: src/data-list.c:426 src/data-list.c:440 src/print.c:520 src/print.c:533
 msgid "Column positions for fields must be positive."
 msgstr ""
 
-#: src/data-list.c:443
+#: src/data-list.c:445
 msgid "The ending column for a field must be greater than the starting column."
 msgstr ""
 
-#: src/data-list.c:457
+#: src/data-list.c:459
 #, c-format
 msgid "The %d columns %d-%d can't be evenly divided into %d fields."
 msgstr ""
 
-#: src/data-list.c:477 src/print.c:561
+#: src/data-list.c:479 src/print.c:561
 msgid "A format specifier on this line has extra characters on the end."
 msgstr ""
 
-#: src/data-list.c:492 src/print.c:577
+#: src/data-list.c:494 src/print.c:577
 msgid "The value for number of decimal places must be at least 1."
 msgstr ""
 
-#: src/data-list.c:506 src/print.c:590
+#: src/data-list.c:508 src/print.c:590
 #, c-format
 msgid "Input format %s doesn't accept decimal places."
 msgstr ""
 
-#: src/data-list.c:553 src/data-list.c:649 src/data-list.c:863
+#: src/data-list.c:555 src/data-list.c:651 src/data-list.c:859
 #, c-format
 msgid "%s is a duplicate variable name."
 msgstr ""
 
-#: src/data-list.c:558
+#: src/data-list.c:560
 #, c-format
 msgid "There is already a variable %s of a different type."
 msgstr ""
 
-#: src/data-list.c:565
+#: src/data-list.c:567
 #, c-format
 msgid "There is already a string variable %s of a different width."
 msgstr ""
 
-#: src/data-list.c:640
+#: src/data-list.c:642
 msgid ""
 "The number of format specifications exceeds the given number of variable "
 "names."
 msgstr ""
 
-#: src/data-list.c:753 src/print.c:766
+#: src/data-list.c:755 src/print.c:766
 msgid ""
 "There aren't enough format specifications to match the number of variable "
 "names given."
 msgstr ""
 
-#: src/data-list.c:780 src/data-list.c:904 src/descript.c:882 src/print.c:797
-#: src/sysfile-info.c:139 src/sysfile-info.c:373 src/vfm.c:839
+#: src/data-list.c:782 src/data-list.c:900 src/descript.c:882 src/print.c:797
+#: src/sysfile-info.c:139 src/sysfile-info.c:373 src/vfm.c:844
 msgid "Variable"
 msgstr ""
 
-#: src/data-list.c:781 src/print.c:798
+#: src/data-list.c:783 src/print.c:798
 msgid "Record"
 msgstr ""
 
-#: src/data-list.c:782 src/print.c:799
+#: src/data-list.c:784 src/print.c:799
 msgid "Columns"
 msgstr ""
 
-#: src/data-list.c:783 src/data-list.c:905 src/print.c:800
+#: src/data-list.c:785 src/data-list.c:901 src/print.c:800
 msgid "Format"
 msgstr ""
 
-#: src/data-list.c:799
+#: src/data-list.c:800
 #, c-format
-msgid "Reading %d record from file %s."
-msgid_plural "Reading %d records from file %s."
+msgid "Reading %d record from %s."
+msgid_plural "Reading %d records from %s."
 msgstr[0] ""
 msgstr[1] ""
 
-#: src/data-list.c:803
+#: src/data-list.c:916
 #, c-format
-msgid "Reading %d record from the command file."
-msgid_plural "Reading %d records from the command file."
-msgstr[0] ""
-msgstr[1] ""
-
-#: src/data-list.c:921
-#, c-format
-msgid "Reading free-form data from file %s."
-msgstr ""
-
-#: src/data-list.c:924
-msgid "Reading free-form data from the command file."
+msgid "Reading free-form data from %s."
 msgstr ""
 
-#: src/data-list.c:975
+#: src/data-list.c:967
 #, c-format
 msgid "Quoted string missing terminating `%c'."
 msgstr ""
 
-#: src/data-list.c:1084
+#: src/data-list.c:1076
 #, c-format
 msgid "Partial case of %d of %d records discarded."
 msgstr ""
 
-#: src/data-list.c:1138
+#: src/data-list.c:1130
 #, c-format
 msgid "Partial case discarded.  The first variable missing was %s."
 msgstr ""
 
-#: src/data-list.c:1182
+#: src/data-list.c:1174
 #, c-format
 msgid ""
 "Missing value(s) for all variables from %s onward.  These will be filled "
 "with the system-missing value or blanks, as appropriate."
 msgstr ""
 
-#: src/data-list.c:1259
+#: src/data-list.c:1251
 msgid "Attempt to read past end of file."
 msgstr ""
 
-#: src/data-list.c:1398
+#: src/data-list.c:1390
 msgid ""
 "REPEATING DATA must use the same file as its corresponding DATA LIST or FILE "
 "TYPE."
 msgstr ""
 
-#: src/data-list.c:1408 src/data-list.c:1442 src/data-list.c:1455
-#: src/data-list.c:1468 src/data-list.c:1502
+#: src/data-list.c:1400 src/data-list.c:1434 src/data-list.c:1447
+#: src/data-list.c:1460 src/data-list.c:1494
 #, c-format
 msgid "%s subcommand given multiple times."
 msgstr ""
 
-#: src/data-list.c:1431
+#: src/data-list.c:1423
 #, c-format
 msgid "STARTS beginning column (%d) exceeds STARTS ending column (%d)."
 msgstr ""
 
-#: src/data-list.c:1488
+#: src/data-list.c:1480
 #, c-format
 msgid "CONTINUED beginning column (%d) exceeds CONTINUED ending column (%d)."
 msgstr ""
 
-#: src/data-list.c:1511
+#: src/data-list.c:1503
 #, c-format
 msgid "ID beginning column (%ld) must be positive."
 msgstr ""
 
-#: src/data-list.c:1526
+#: src/data-list.c:1518
 #, c-format
 msgid "ID ending column (%ld) must be positive."
 msgstr ""
 
-#: src/data-list.c:1532
+#: src/data-list.c:1524
 #, c-format
 msgid "ID ending column (%ld) cannot be less than ID beginning column (%d)."
 msgstr ""
 
-#: src/data-list.c:1572
+#: src/data-list.c:1564
 msgid "Missing required specification STARTS."
 msgstr ""
 
-#: src/data-list.c:1574
+#: src/data-list.c:1566
 msgid "Missing required specification OCCURS."
 msgstr ""
 
-#: src/data-list.c:1581
+#: src/data-list.c:1573
 msgid "ID specified without CONTINUED."
 msgstr ""
 
-#: src/data-list.c:1592
+#: src/data-list.c:1584
 #, c-format
 msgid ""
 "STARTS beginning column (%d) exceeds default STARTS ending column taken from "
 "file's record width (%d)."
 msgstr ""
 
-#: src/data-list.c:1605
+#: src/data-list.c:1597
 #, c-format
 msgid ""
 "CONTINUED beginning column (%d) exceeds default CONTINUED ending column "
 "taken from file's record width (%d)."
 msgstr ""
 
-#: src/data-list.c:1684
+#: src/data-list.c:1676
 msgid "String variable not allowed here."
 msgstr ""
 
-#: src/data-list.c:1694
+#: src/data-list.c:1686
 #, c-format
 msgid "%s (%d) must be at least 1."
 msgstr ""
 
-#: src/data-list.c:1700
+#: src/data-list.c:1692
 #, c-format
 msgid "Variable or integer expected for %s."
 msgstr ""
 
-#: src/data-list.c:1825
+#: src/data-list.c:1817
 #, c-format
 msgid "Encountered mismatched record ID \"%s\" expecting \"%s\"."
 msgstr ""
 
-#: src/data-list.c:1857
+#: src/data-list.c:1849
 #, c-format
 msgid ""
 "Variable %s starting in column %d extends beyond physical record length of %"
 "d."
 msgstr ""
 
-#: src/data-list.c:1924
+#: src/data-list.c:1916
 #, c-format
 msgid "Invalid value %d for OCCURS."
 msgstr ""
 
-#: src/data-list.c:1930
+#: src/data-list.c:1922
 #, c-format
 msgid "Beginning column for STARTS (%d) must be at least 1."
 msgstr ""
 
-#: src/data-list.c:1938
+#: src/data-list.c:1930
 #, c-format
 msgid "Ending column for STARTS (%d) is less than beginning column (%d)."
 msgstr ""
 
-#: src/data-list.c:1946
+#: src/data-list.c:1938
 #, c-format
 msgid "Invalid value %d for LENGTH."
 msgstr ""
 
-#: src/data-list.c:1953
+#: src/data-list.c:1945
 #, c-format
 msgid "Beginning column for CONTINUED (%d) must be at least 1."
 msgstr ""
 
-#: src/data-list.c:1961
+#: src/data-list.c:1953
 #, c-format
 msgid "Ending column for CONTINUED (%d) is less than beginning column (%d)."
 msgstr ""
 
-#: src/data-list.c:1993
+#: src/data-list.c:1985
 #, c-format
 msgid ""
 "Number of repetitions specified on OCCURS (%d) exceed number of repetitions "
 "available in space on STARTS (%d), and CONTINUED not specified."
 msgstr ""
 
-#: src/data-list.c:2011
+#: src/data-list.c:2003
 #, c-format
 msgid "Unexpected end of file with %d repetitions remaining out of %d."
 msgstr ""
@@ -1367,42 +1368,42 @@ msgstr ""
 msgid "Valid cases = %g; cases with missing value(s) = %g."
 msgstr ""
 
-#: src/dfm-read.c:158
+#: src/dfm-read.c:136
 #, c-format
 msgid "Could not open \"%s\" for reading as a data file: %s."
 msgstr ""
 
-#: src/dfm-read.c:191 src/dfm-read.c:209
+#: src/dfm-read.c:168 src/dfm-read.c:186
 msgid "BEGIN DATA expected."
 msgstr ""
 
-#: src/dfm-read.c:218
+#: src/dfm-read.c:195
 msgid ""
 "Unexpected end-of-file while reading data in BEGIN DATA.  This probably "
 "indicates a missing or misformatted END DATA command.  END DATA must appear "
 "by itself on a single line with exactly one space between words."
 msgstr ""
 
-#: src/dfm-read.c:251 src/dfm-read.c:271
+#: src/dfm-read.c:227 src/dfm-read.c:247
 #, c-format
 msgid "Error reading file %s: %s."
 msgstr ""
 
-#: src/dfm-read.c:274
+#: src/dfm-read.c:250
 #, c-format
 msgid "%s: Partial record at end of file."
 msgstr ""
 
-#: src/dfm-read.c:317
+#: src/dfm-read.c:299
 #, c-format
 msgid "Attempt to read beyond end-of-file on file %s."
 msgstr ""
 
-#: src/dfm-read.c:320
+#: src/dfm-read.c:302
 msgid "Attempt to read beyond END DATA."
 msgstr ""
 
-#: src/dfm-read.c:467
+#: src/dfm-read.c:446
 msgid ""
 "This command is not valid here since the current input program does not "
 "access the inline file."
@@ -1720,67 +1721,61 @@ msgstr ""
 msgid "%s is a PSPP extension."
 msgstr ""
 
-#: src/file-handle-def.c:229
+#: src/file-handle-def.c:303
 #, c-format
-msgid "Can't open %s as a %s because it is already open as a %s"
+msgid "Can't open %s as a %s because it is already open as a %s."
 msgstr ""
 
-#: src/file-handle-def.c:236
+#: src/file-handle-def.c:310
 #, c-format
-msgid "Can't open %s as a %s for %s because it is already open for %s"
+msgid "Can't open %s as a %s for %s because it is already open for %s."
 msgstr ""
 
-#: src/file-handle-def.c:244
+#: src/file-handle-def.c:318
 #, c-format
-msgid "Can't re-open %s as a %s for %s"
+msgid "Can't re-open %s as a %s for %s."
 msgstr ""
 
-#: src/file-handle.q:68
+#: src/file-handle.q:69
 #, c-format
 msgid ""
-"File handle %s already refers to file %s.  File handles cannot be redefined "
-"within a session."
+"File handle %s is already defined.  Use CLOSE FILE HANDLE before redefining "
+"a file handle."
 msgstr ""
 
-#: src/file-handle.q:89
-msgid "The FILE HANDLE required subcommand NAME is not present."
-msgstr ""
-
-#: src/file-handle.q:104
+#: src/file-handle.q:101
 #, c-format
 msgid ""
 "Fixed-length records were specified on /RECFORM, but record length was not "
 "specified on /LRECL.  Assuming %d-character records."
 msgstr ""
 
-#: src/file-handle.q:109
+#: src/file-handle.q:106
 #, c-format
 msgid ""
 "Record length (%ld) must be at least one byte.  Assuming %d-character "
 "records."
 msgstr ""
 
-#: src/file-handle.q:139
-msgid "expecting a file name or handle name"
+#: src/file-handle.q:152
+msgid "file"
 msgstr ""
 
-#: src/filename.c:227
-#, c-format
-msgid "Searching for `%s'..."
+#: src/file-handle.q:154
+msgid "inline file"
 msgstr ""
 
-#: src/filename.c:235 src/filename.c:267
-msgid "Search unsuccessful!"
+#: src/file-handle.q:156
+msgid "scratch file"
 msgstr ""
 
-#: src/filename.c:260
-#, c-format
-msgid "Found `%s'."
+#: src/file-handle.q:177
+msgid "expecting a file name or handle name"
 msgstr ""
 
-#: src/filename.c:665
+#: src/file-handle.q:204
 #, c-format
-msgid "Not opening pipe file `%s' because SAFER option set."
+msgid "Handle for %s not allowed here."
 msgstr ""
 
 #: src/file-type.c:133
@@ -1921,6 +1916,25 @@ msgstr ""
 msgid "Unknown record type %g."
 msgstr ""
 
+#: src/filename.c:227
+#, c-format
+msgid "Searching for `%s'..."
+msgstr ""
+
+#: src/filename.c:235 src/filename.c:267
+msgid "Search unsuccessful!"
+msgstr ""
+
+#: src/filename.c:260
+#, c-format
+msgid "Found `%s'."
+msgstr ""
+
+#: src/filename.c:677
+#, c-format
+msgid "Not opening pipe file `%s' because SAFER option set."
+msgstr ""
+
 #: src/flip.c:88
 msgid ""
 "FLIP ignores TEMPORARY.  Temporary transformations will be made permanent."
@@ -1982,6 +1996,29 @@ msgstr ""
 msgid "Unexpected end of file reading FLIP temporary file."
 msgstr ""
 
+#: src/format-prs.c:66
+msgid "X and T format specifiers not allowed here."
+msgstr ""
+
+#: src/format-prs.c:74
+#, c-format
+msgid "%.*s is not a valid data format."
+msgstr ""
+
+#: src/format-prs.c:115
+msgid "Format specifier expected."
+msgstr ""
+
+#: src/format-prs.c:127
+#, c-format
+msgid "Data format %s does not specify a width."
+msgstr ""
+
+#: src/format-prs.c:145
+#, c-format
+msgid "Data format %s is not valid."
+msgstr ""
+
 #: src/format.c:73
 #, c-format
 msgid "Format specifies a bad type (%d)"
@@ -2038,11 +2075,11 @@ msgstr ""
 msgid "%s variables are not compatible with %s format %s."
 msgstr ""
 
-#: src/format.c:197 src/pfm-read.c:476 src/sfm-read.c:1012 src/sfm-read.c:1021
+#: src/format.c:197 src/pfm-read.c:476 src/sfm-read.c:1014 src/sfm-read.c:1023
 msgid "String"
 msgstr ""
 
-#: src/format.c:197 src/pfm-read.c:476 src/sfm-read.c:1012 src/sfm-read.c:1021
+#: src/format.c:197 src/pfm-read.c:476 src/sfm-read.c:1014 src/sfm-read.c:1023
 msgid "Numeric"
 msgstr ""
 
@@ -2051,29 +2088,6 @@ msgstr ""
 msgid "String variable with width %d not compatible with format %s."
 msgstr ""
 
-#: src/format-prs.c:66
-msgid "X and T format specifiers not allowed here."
-msgstr ""
-
-#: src/format-prs.c:74
-#, c-format
-msgid "%.*s is not a valid data format."
-msgstr ""
-
-#: src/format-prs.c:115
-msgid "Format specifier expected."
-msgstr ""
-
-#: src/format-prs.c:127
-#, c-format
-msgid "Data format %s does not specify a width."
-msgstr ""
-
-#: src/format-prs.c:145
-#, c-format
-msgid "Data format %s is not valid."
-msgstr ""
-
 #: src/formats.c:89
 msgid "`(' expected after variable list"
 msgstr ""
@@ -2167,16 +2181,20 @@ msgstr ""
 msgid "No valid data for variable %s; statistics not displayed."
 msgstr ""
 
-#: src/get.c:306 src/get.c:320 src/get.c:345
+#: src/get.c:108
+msgid "expecting COMM or TAPE"
+msgstr ""
+
+#: src/get.c:343 src/get.c:357 src/get.c:382
 #, c-format
 msgid "expecting %s or %s"
 msgstr ""
 
-#: src/get.c:552 src/print.c:179
+#: src/get.c:586 src/print.c:179
 msgid "expecting a valid subcommand"
 msgstr ""
 
-#: src/get.c:585
+#: src/get.c:619
 #, c-format
 msgid ""
 "Cannot rename %s as %s because there already exists a variable named %s.  To "
@@ -2184,85 +2202,81 @@ msgid ""
 "as \"/RENAME (A=B)(B=C)(C=A)\", or equivalently, \"/RENAME (A B C=B C A)\"."
 msgstr ""
 
-#: src/get.c:610
+#: src/get.c:644
 msgid "`=' expected after variable list."
 msgstr ""
 
-#: src/get.c:617
+#: src/get.c:651
 #, c-format
 msgid ""
 "Number of variables on left side of `=' (%d) does not match number of "
 "variables on right side (%d), in parenthesized group %d of RENAME subcommand."
 msgstr ""
 
-#: src/get.c:630
+#: src/get.c:664
 #, c-format
 msgid "Requested renaming duplicates variable name %s."
 msgstr ""
 
-#: src/get.c:660
+#: src/get.c:694
 msgid "Cannot DROP all variables from dictionary."
 msgstr ""
 
-#: src/get.c:835
+#: src/get.c:869
 msgid "The active file may not be specified more than once."
 msgstr ""
 
-#: src/get.c:844
+#: src/get.c:878
 msgid "Cannot specify the active file since no active file has been defined."
 msgstr ""
 
-#: src/get.c:852
+#: src/get.c:886
 msgid ""
 "MATCH FILES may not be used after TEMPORARY when the active file is an input "
 "source.  Temporary transformations will be made permanent."
 msgstr ""
 
-#: src/get.c:890
+#: src/get.c:924
 msgid "Multiple IN subcommands for a single FILE or TABLE."
 msgstr ""
 
-#: src/get.c:910
+#: src/get.c:944
 msgid "BY may appear at most once."
 msgstr ""
 
-#: src/get.c:930
+#: src/get.c:964
 #, c-format
 msgid "File %s lacks BY variable %s."
 msgstr ""
 
-#: src/get.c:944
+#: src/get.c:978
 msgid "FIRST may appear at most once."
 msgstr ""
 
-#: src/get.c:958
+#: src/get.c:992
 msgid "LAST may appear at most once."
 msgstr ""
 
-#: src/get.c:999
+#: src/get.c:1033
 msgid "BY is required when TABLE is specified."
 msgstr ""
 
-#: src/get.c:1004
+#: src/get.c:1038
 msgid "BY is required when IN is specified."
 msgstr ""
 
-#: src/get.c:1032
+#: src/get.c:1066
 #, c-format
 msgid "IN variable name %s duplicates an existing variable name."
 msgstr ""
 
-#: src/get.c:1461
+#: src/get.c:1495
 #, c-format
 msgid ""
 "Variable %s in file %s (%s) has different type or width from the same "
 "variable in earlier file (%s)."
 msgstr ""
 
-#: src/get.c:1545
-msgid "expecting COMM or TAPE"
-msgstr ""
-
 #: src/getl.c:139
 #, c-format
 msgid "Can't find `%s' in include file search path."
@@ -3269,34 +3283,34 @@ msgstr ""
 msgid "Cannot change mode of %s: %s"
 msgstr ""
 
-#: src/pfm-read.c:87
+#: src/pfm-read.c:97
 #, c-format
 msgid "portable file %s corrupt at offset %ld: "
 msgstr ""
 
-#: src/pfm-read.c:114
+#: src/pfm-read.c:124
 msgid "unexpected end of file"
 msgstr ""
 
-#: src/pfm-read.c:172
+#: src/pfm-read.c:182
 #, c-format
 msgid ""
 "An error occurred while opening \"%s\" for reading as a portable file: %s."
 msgstr ""
 
-#: src/pfm-read.c:190
+#: src/pfm-read.c:200
 msgid "Data record expected."
 msgstr ""
 
-#: src/pfm-read.c:298
+#: src/pfm-read.c:308
 msgid "Missing numeric terminator."
 msgstr ""
 
-#: src/pfm-read.c:321
+#: src/pfm-read.c:331
 msgid "Invalid integer."
 msgstr ""
 
-#: src/pfm-read.c:332
+#: src/pfm-read.c:342
 #, c-format
 msgid "Bad string length %d."
 msgstr ""
@@ -3316,12 +3330,12 @@ msgstr ""
 msgid "Bad time string length %d."
 msgstr ""
 
-#: src/pfm-read.c:468 src/sfm-read.c:1004
+#: src/pfm-read.c:468 src/sfm-read.c:1006
 #, c-format
 msgid "%s: Bad format specifier byte (%d)."
 msgstr ""
 
-#: src/pfm-read.c:475 src/sfm-read.c:1020
+#: src/pfm-read.c:475 src/sfm-read.c:1022
 #, c-format
 msgid "%s variable %s has invalid format specifier %s."
 msgstr ""
@@ -3397,7 +3411,7 @@ msgstr ""
 msgid "%s: Writing portable file: %s."
 msgstr ""
 
-#: src/pfm-write.c:468
+#: src/pfm-write.c:466
 #, c-format
 msgid "%s: Closing portable file: %s."
 msgstr ""
@@ -3584,19 +3598,23 @@ msgstr ""
 
 #: src/print.c:839
 #, c-format
-msgid "Writing %d record(s) to file %s."
-msgstr ""
+msgid "Writing %d record to %s."
+msgid_plural "Writing %d records to %s."
+msgstr[0] ""
+msgstr[1] ""
 
-#: src/print.c:842
+#: src/print.c:843
 #, c-format
-msgid "Writing %d record(s) to the listing file."
-msgstr ""
+msgid "Writing %d record."
+msgid_plural "Writing %d records."
+msgstr[0] ""
+msgstr[1] ""
 
-#: src/print.c:1082
+#: src/print.c:1084
 msgid "The expression on PRINT SPACE evaluated to the system-missing value."
 msgstr ""
 
-#: src/print.c:1085
+#: src/print.c:1087
 #, c-format
 msgid "The expression on PRINT SPACE evaluated to %g."
 msgstr ""
@@ -3806,6 +3824,13 @@ msgstr ""
 msgid "Cannot sample %d observations from a population of %d."
 msgstr ""
 
+#: src/scratch-reader.c:59
+#, c-format
+msgid ""
+"Scratch file handle %s has not yet been written, using SAVE or another "
+"procedure, so it cannot yet be used for reading."
+msgstr ""
+
 #: src/sel-if.c:103
 msgid "The filter variable must be numeric."
 msgstr ""
@@ -3951,131 +3976,131 @@ msgstr ""
 msgid "corrupt system file: "
 msgstr ""
 
-#: src/sfm-read.c:151 src/sfm-write.c:931
+#: src/sfm-read.c:149 src/sfm-write.c:929
 #, c-format
 msgid "%s: Closing system file: %s."
 msgstr ""
 
-#: src/sfm-read.c:237
+#: src/sfm-read.c:239
 #, c-format
 msgid ""
 "An error occurred while opening \"%s\" for reading as a system file: %s."
 msgstr ""
 
-#: src/sfm-read.c:255
+#: src/sfm-read.c:257
 #, c-format
 msgid ""
 "%s: Index of weighting variable (%d) is not between 0 and number of elements "
 "per case (%d)."
 msgstr ""
 
-#: src/sfm-read.c:264
+#: src/sfm-read.c:266
 #, c-format
 msgid ""
 "%s: Weighting variable may not be a continuation of a long string variable."
 msgstr ""
 
-#: src/sfm-read.c:267
+#: src/sfm-read.c:269
 #, c-format
 msgid "%s: Weighting variable may not be a string variable."
 msgstr ""
 
-#: src/sfm-read.c:292
+#: src/sfm-read.c:294
 #, c-format
 msgid ""
 "%s: Orphaned variable index record (type 4).  Type 4 records must always "
 "immediately follow type 3 records."
 msgstr ""
 
-#: src/sfm-read.c:350
+#: src/sfm-read.c:352
 #, c-format
 msgid "%s: Invalid subrecord length. Record: 7; Subrecord: 11"
 msgstr ""
 
-#: src/sfm-read.c:404
+#: src/sfm-read.c:406
 #, c-format
 msgid "%s: Trailing garbage in long variable name map."
 msgstr ""
 
-#: src/sfm-read.c:411
+#: src/sfm-read.c:413
 #, c-format
 msgid "%s: Long variable mapping to invalid variable name `%s'."
 msgstr ""
 
-#: src/sfm-read.c:421
+#: src/sfm-read.c:423
 #, c-format
 msgid "%s: Long variable mapping for nonexistent variable %s."
 msgstr ""
 
-#: src/sfm-read.c:431
+#: src/sfm-read.c:433
 #, c-format
 msgid "%s: Duplicate long variable name `%s' within system file."
 msgstr ""
 
-#: src/sfm-read.c:459
+#: src/sfm-read.c:461
 #, c-format
 msgid "%s: Unrecognized record type 7, subtype %d encountered in system file."
 msgstr ""
 
-#: src/sfm-read.c:484
+#: src/sfm-read.c:486
 #, c-format
 msgid "%s: Unrecognized record type %d."
 msgstr ""
 
-#: src/sfm-read.c:516
+#: src/sfm-read.c:518
 #, c-format
 msgid ""
 "%s: Bad size (%d) or count (%d) field on record type 7, subtype 3.\tExpected "
 "size %d, count 8."
 msgstr ""
 
-#: src/sfm-read.c:527
+#: src/sfm-read.c:529
 #, c-format
 msgid ""
 "%s: Floating-point representation in system file is not IEEE-754.  PSPP "
 "cannot convert between floating-point formats."
 msgstr ""
 
-#: src/sfm-read.c:543
+#: src/sfm-read.c:545
 #, c-format
 msgid ""
 "%s: File-indicated endianness (%s) does not match endianness intuited from "
 "file header (%s)."
 msgstr ""
 
-#: src/sfm-read.c:546 src/sfm-read.c:547
+#: src/sfm-read.c:548 src/sfm-read.c:549
 msgid "big-endian"
 msgstr ""
 
-#: src/sfm-read.c:546 src/sfm-read.c:547
+#: src/sfm-read.c:548 src/sfm-read.c:549
 msgid "little-endian"
 msgstr ""
 
-#: src/sfm-read.c:548
+#: src/sfm-read.c:550
 msgid "unknown"
 msgstr ""
 
-#: src/sfm-read.c:552
+#: src/sfm-read.c:554
 #, c-format
 msgid "%s: File-indicated character representation code (%s) is not ASCII."
 msgstr ""
 
-#: src/sfm-read.c:556
+#: src/sfm-read.c:558
 msgid "DEC Kanji"
 msgstr ""
 
-#: src/sfm-read.c:556 src/sysfile-info.c:119
+#: src/sfm-read.c:558 src/sysfile-info.c:119
 msgid "Unknown"
 msgstr ""
 
-#: src/sfm-read.c:572
+#: src/sfm-read.c:574
 #, c-format
 msgid ""
 "%s: Bad size (%d) or count (%d) field on record type 7, subtype 4.\tExpected "
 "size %d, count 8."
 msgstr ""
 
-#: src/sfm-read.c:587
+#: src/sfm-read.c:589
 #, c-format
 msgid ""
 "%s: File-indicated value is different from internal value for at least one "
@@ -4083,220 +4108,220 @@ msgid ""
 "%g; LOWEST: %g, %g."
 msgstr ""
 
-#: src/sfm-read.c:614
+#: src/sfm-read.c:616
 #, c-format
 msgid ""
 "%s: Bad magic.  Proper system files begin with the four characters `$FL2'. "
 "This file will not be read."
 msgstr ""
 
-#: src/sfm-read.c:656
+#: src/sfm-read.c:658
 #, c-format
 msgid ""
 "%s: File layout code has unexpected value %d.  Value should be 2, in big-"
 "endian or little-endian format."
 msgstr ""
 
-#: src/sfm-read.c:684
+#: src/sfm-read.c:686
 #, c-format
 msgid "%s: Number of cases in file (%ld) is not between -1 and %d."
 msgstr ""
 
-#: src/sfm-read.c:689
+#: src/sfm-read.c:691
 #, c-format
 msgid "%s: Compression bias (%g) is not the usual value of 100."
 msgstr ""
 
-#: src/sfm-read.c:812
+#: src/sfm-read.c:814
 #, c-format
 msgid ""
 "%s: position %d: String variable does not have proper number of continuation "
 "records."
 msgstr ""
 
-#: src/sfm-read.c:823
+#: src/sfm-read.c:825
 #, c-format
 msgid "%s: position %d: Superfluous long string continuation record."
 msgstr ""
 
-#: src/sfm-read.c:829
+#: src/sfm-read.c:831
 #, c-format
 msgid "%s: position %d: Bad variable type code %d."
 msgstr ""
 
-#: src/sfm-read.c:832
+#: src/sfm-read.c:834
 #, c-format
 msgid "%s: position %d: Variable label indicator field is not 0 or 1."
 msgstr ""
 
-#: src/sfm-read.c:836
+#: src/sfm-read.c:838
 #, c-format
 msgid ""
 "%s: position %d: Missing value indicator field is not -3, -2, 0, 1, 2, or 3."
 msgstr ""
 
-#: src/sfm-read.c:842
+#: src/sfm-read.c:844
 #, c-format
 msgid "%s: position %d: Variable name begins with invalid character."
 msgstr ""
 
-#: src/sfm-read.c:846
+#: src/sfm-read.c:848
 #, c-format
 msgid "%s: position %d: Variable name begins with lowercase letter %c."
 msgstr ""
 
-#: src/sfm-read.c:850
+#: src/sfm-read.c:852
 #, c-format
 msgid ""
 "%s: position %d: Variable name begins with octothorpe (`#').  Scratch "
 "variables should not appear in system files."
 msgstr ""
 
-#: src/sfm-read.c:865
+#: src/sfm-read.c:867
 #, c-format
 msgid "%s: position %d: Variable name character %d is lowercase letter %c."
 msgstr ""
 
-#: src/sfm-read.c:874
+#: src/sfm-read.c:876
 #, c-format
 msgid ""
 "%s: position %d: character `\\%03o' (%c) is not valid in a variable name."
 msgstr ""
 
-#: src/sfm-read.c:881
+#: src/sfm-read.c:883
 #, c-format
 msgid "%s: Invalid variable name `%s' within system file."
 msgstr ""
 
-#: src/sfm-read.c:888
+#: src/sfm-read.c:890
 #, c-format
 msgid "%s: Duplicate variable name `%s' within system file."
 msgstr ""
 
-#: src/sfm-read.c:911
+#: src/sfm-read.c:913
 #, c-format
 msgid "%s: Variable %s indicates variable label of invalid length %d."
 msgstr ""
 
-#: src/sfm-read.c:932
+#: src/sfm-read.c:934
 #, c-format
 msgid "%s: Long string variable %s may not have missing values."
 msgstr ""
 
-#: src/sfm-read.c:953
+#: src/sfm-read.c:955
 #, c-format
 msgid ""
 "%s: String variable %s may not have missing values specified as a range."
 msgstr ""
 
-#: src/sfm-read.c:980
+#: src/sfm-read.c:982
 #, c-format
 msgid "%s: Long string continuation records omitted at end of dictionary."
 msgstr ""
 
-#: src/sfm-read.c:985
+#: src/sfm-read.c:987
 #, c-format
 msgid ""
 "%s: System file header indicates %d variable positions but %d were read from "
 "file."
 msgstr ""
 
-#: src/sfm-read.c:1010
+#: src/sfm-read.c:1012
 #, c-format
 msgid "%s: %s variable %s has %s format specifier %s."
 msgstr ""
 
-#: src/sfm-read.c:1063
+#: src/sfm-read.c:1065
 #, c-format
 msgid "%s: Invalid number of labels: %d.  Ignoring labels."
 msgstr ""
 
-#: src/sfm-read.c:1105
+#: src/sfm-read.c:1107
 #, c-format
 msgid ""
 "%s: Variable index record (type 4) does not immediately follow value label "
 "record (type 3) as it should."
 msgstr ""
 
-#: src/sfm-read.c:1116
+#: src/sfm-read.c:1118
 #, c-format
 msgid ""
 "%s: Number of variables associated with a value label (%d) is not between 1 "
 "and the number of variables (%d)."
 msgstr ""
 
-#: src/sfm-read.c:1132
+#: src/sfm-read.c:1134
 #, c-format
 msgid ""
 "%s: Variable index associated with value label (%d) is not between 1 and the "
 "number of values (%d)."
 msgstr ""
 
-#: src/sfm-read.c:1139
+#: src/sfm-read.c:1141
 #, c-format
 msgid ""
 "%s: Variable index associated with value label (%d) refers to a continuation "
 "of a string variable, not to an actual variable."
 msgstr ""
 
-#: src/sfm-read.c:1144
+#: src/sfm-read.c:1146
 #, c-format
 msgid "%s: Value labels are not allowed on long string variables (%s)."
 msgstr ""
 
-#: src/sfm-read.c:1155
+#: src/sfm-read.c:1157
 #, c-format
 msgid ""
 "%s: Variables associated with value label are not all of identical type.  "
 "Variable %s has %s type, but variable %s has %s type."
 msgstr ""
 
-#: src/sfm-read.c:1196
+#: src/sfm-read.c:1198
 #, c-format
 msgid "%s: File contains duplicate label for value %g for variable %s."
 msgstr ""
 
-#: src/sfm-read.c:1200
+#: src/sfm-read.c:1202
 #, c-format
 msgid "%s: File contains duplicate label for value `%.*s' for variable %s."
 msgstr ""
 
-#: src/sfm-read.c:1242 src/sfm-read.c:1519
+#: src/sfm-read.c:1244 src/sfm-read.c:1521
 #, c-format
 msgid "%s: Reading system file: %s."
 msgstr ""
 
-#: src/sfm-read.c:1245 src/sfm-read.c:1360 src/sfm-read.c:1402
+#: src/sfm-read.c:1247 src/sfm-read.c:1362 src/sfm-read.c:1404
 #, c-format
 msgid "%s: Unexpected end of file."
 msgstr ""
 
-#: src/sfm-read.c:1260
+#: src/sfm-read.c:1262
 #, c-format
 msgid "%s: Seeking system file: %s."
 msgstr ""
 
-#: src/sfm-read.c:1275
+#: src/sfm-read.c:1277
 #, c-format
 msgid "%s: System file contains multiple type 6 (document) records."
 msgstr ""
 
-#: src/sfm-read.c:1281
+#: src/sfm-read.c:1283
 #, c-format
 msgid "%s: Number of document lines (%ld) must be greater than 0."
 msgstr ""
 
-#: src/sfm-read.c:1313
+#: src/sfm-read.c:1315
 #, c-format
 msgid "%s: Error reading file: %s."
 msgstr ""
 
-#: src/sfm-read.c:1350
+#: src/sfm-read.c:1352
 #, c-format
 msgid "%s: Compressed data is corrupted.  Data ends in partial case."
 msgstr ""
 
-#: src/sfm-read.c:1522
+#: src/sfm-read.c:1524
 #, c-format
 msgid "%s: Partial record at end of system file."
 msgstr ""
@@ -4316,6 +4341,14 @@ msgstr ""
 msgid "%s: Writing system file: %s."
 msgstr ""
 
+#: src/sort-prs.c:94
+msgid "`A' or `D' expected inside parentheses."
+msgstr ""
+
+#: src/sort-prs.c:99
+msgid "`)' expected."
+msgstr ""
+
 #: src/sort.c:85
 msgid "Buffer limit must be at least 2."
 msgstr ""
@@ -4327,14 +4360,6 @@ msgid ""
 "each.  (PSPP workspace is currently restricted to a maximum of %d KB.)"
 msgstr ""
 
-#: src/sort-prs.c:94
-msgid "`A' or `D' expected inside parentheses."
-msgstr ""
-
-#: src/sort-prs.c:99
-msgid "`)' expected."
-msgstr ""
-
 #: src/sysfile-info.c:100
 msgid "File:"
 msgstr ""
@@ -4436,7 +4461,7 @@ msgstr ""
 msgid "Documents in the active file:"
 msgstr ""
 
-#: src/sysfile-info.c:380 src/sysfile-info.c:519 src/vfm.c:841
+#: src/sysfile-info.c:380 src/sysfile-info.c:519 src/vfm.c:846
 msgid "Label"
 msgstr ""
 
@@ -4467,51 +4492,6 @@ msgstr ""
 msgid "Vector"
 msgstr ""
 
-#: src/tab.c:258
-#, c-format
-msgid "bad vline: x=%d+%d=%d y=(%d+%d=%d,%d+%d=%d) in table size (%d,%d)\n"
-msgstr ""
-
-#: src/tab.c:333
-#, c-format
-msgid ""
-"bad box: (%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) in table size (%d,%d)\n"
-msgstr ""
-
-#: src/temporary.c:49
-msgid "This command is not valid inside DO IF or LOOP."
-msgstr ""
-
-#: src/temporary.c:56
-msgid ""
-"This command may only appear once between procedures and procedure-like "
-"commands."
-msgstr ""
-
-#: src/title.c:60
-#, c-format
-msgid "%s before: %s\n"
-msgstr ""
-
-#: src/title.c:60
-msgid "<none>"
-msgstr ""
-
-#: src/title.c:72
-#, c-format
-msgid "%s: `.' expected after string."
-msgstr ""
-
-#: src/title.c:88
-#, c-format
-msgid "%s after: %s\n"
-msgstr ""
-
-#: src/title.c:143
-#, c-format
-msgid "Document entered %s by %s:"
-msgstr ""
-
 #: src/t-test.q:270
 msgid "TESTVAL, GROUPS and PAIRS subcommands are mutually exclusive."
 msgstr ""
@@ -4637,6 +4617,51 @@ msgstr ""
 msgid "%s & %s"
 msgstr ""
 
+#: src/tab.c:258
+#, c-format
+msgid "bad vline: x=%d+%d=%d y=(%d+%d=%d,%d+%d=%d) in table size (%d,%d)\n"
+msgstr ""
+
+#: src/tab.c:333
+#, c-format
+msgid ""
+"bad box: (%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) in table size (%d,%d)\n"
+msgstr ""
+
+#: src/temporary.c:49
+msgid "This command is not valid inside DO IF or LOOP."
+msgstr ""
+
+#: src/temporary.c:56
+msgid ""
+"This command may only appear once between procedures and procedure-like "
+"commands."
+msgstr ""
+
+#: src/title.c:60
+#, c-format
+msgid "%s before: %s\n"
+msgstr ""
+
+#: src/title.c:60
+msgid "<none>"
+msgstr ""
+
+#: src/title.c:72
+#, c-format
+msgid "%s: `.' expected after string."
+msgstr ""
+
+#: src/title.c:88
+#, c-format
+msgid "%s after: %s\n"
+msgstr ""
+
+#: src/title.c:143
+#, c-format
+msgid "Document entered %s by %s:"
+msgstr ""
+
 #: src/val-labs.c:121
 #, c-format
 msgid ""
index 34e15e471b895b09b09c66a1e7d764ec1fcc3a11..83d719932c7c26283ebcc592f81a34354f39102c 100644 (file)
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: pspp-dev@gnu.org\n"
-"POT-Creation-Date: 2006-01-24 17:40+0800\n"
+"POT-Creation-Date: 2006-01-28 17:20-0800\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -17,15 +17,15 @@ msgstr ""
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
 
-#: src/aggregate.c:199
+#: src/aggregate.c:200
 msgid "while expecting COLUMNWISE"
 msgstr ""
 
-#: src/aggregate.c:228
+#: src/aggregate.c:229
 msgid "expecting BREAK"
 msgstr ""
 
-#: src/aggregate.c:233
+#: src/aggregate.c:234
 msgid ""
 "When PRESORTED is specified, specifying sorting directions with (A) or (D) "
 "has no effect.  Output data will be sorted the same way as the input data."
@@ -79,20 +79,34 @@ msgid ""
 "contains the aggregate variables and the break variables."
 msgstr ""
 
+#: src/any-reader.c:74
+#, c-format
+msgid "An error occurred while opening \"%s\": %s."
+msgstr ""
+
+#: src/any-reader.c:129
+#, c-format
+msgid "\"%s\" is not a system or portable file."
+msgstr ""
+
+#: src/any-reader.c:135 src/any-writer.c:80
+msgid "The inline file is not allowed here."
+msgstr ""
+
 #: src/apply-dict.c:71
 #, c-format
 msgid "Variable %s is %s in target file, but %s in source file."
 msgstr ""
 
 #: src/apply-dict.c:74 src/apply-dict.c:75 src/format.c:198 src/recode.c:464
-#: src/recode.c:465 src/sfm-read.c:1014 src/sfm-read.c:1159
-#: src/sfm-read.c:1160 src/vars-atr.c:40 src/vars-atr.c:48
+#: src/recode.c:465 src/sfm-read.c:1016 src/sfm-read.c:1161
+#: src/sfm-read.c:1162 src/vars-atr.c:40 src/vars-atr.c:48
 msgid "string"
 msgstr ""
 
 #: src/apply-dict.c:74 src/apply-dict.c:75 src/format.c:198 src/recode.c:464
-#: src/recode.c:465 src/sfm-read.c:1014 src/sfm-read.c:1159
-#: src/sfm-read.c:1160 src/vars-atr.c:40
+#: src/recode.c:465 src/sfm-read.c:1016 src/sfm-read.c:1161
+#: src/sfm-read.c:1162 src/vars-atr.c:40
 msgid "numeric"
 msgstr ""
 
@@ -235,8 +249,8 @@ msgstr ""
 msgid "Source variable count (%u) does not match target variable count (%u)."
 msgstr ""
 
-#: src/autorecode.c:142 src/command.c:797 src/file-handle.q:83 src/lexer.c:440
-#: src/matrix-data.c:532 src/print.c:335 src/print.c:1044 src/sel-if.c:57
+#: src/autorecode.c:142 src/command.c:797 src/lexer.c:440
+#: src/matrix-data.c:532 src/print.c:335 src/print.c:1046 src/sel-if.c:57
 #: src/sel-if.c:134 src/vector.c:197
 msgid "expecting end of command"
 msgstr ""
@@ -561,7 +575,7 @@ msgstr ""
 
 #: src/crosstabs.q:1109 src/crosstabs.q:1136 src/crosstabs.q:1156
 #: src/crosstabs.q:1178 src/examine.q:1131 src/frequencies.q:1142
-#: src/frequencies.q:1263 src/sysfile-info.c:518 src/vfm.c:840
+#: src/frequencies.q:1263 src/sysfile-info.c:518 src/vfm.c:845
 msgid "Value"
 msgstr ""
 
@@ -935,288 +949,275 @@ msgstr ""
 msgid "Field too long (%d characters).  Truncated after character %d."
 msgstr ""
 
-#: src/data-list.c:142
-msgid ""
-"DATA LIST may not use a different file from that specified on its "
-"surrounding FILE TYPE."
+#: src/data-list.c:144
+msgid "DATA LIST must use the same file as the enclosing FILE TYPE."
 msgstr ""
 
-#: src/data-list.c:161
+#: src/data-list.c:163
 msgid "The END subcommand may only be specified once."
 msgstr ""
 
-#: src/data-list.c:196
+#: src/data-list.c:198
 msgid "Only one of FIXED, FREE, or LIST may be specified."
 msgstr ""
 
-#: src/data-list.c:346 src/print.c:296
+#: src/data-list.c:348 src/print.c:296
 #, c-format
 msgid ""
 "The record number specified, %ld, is before the previous record, %d.  Data "
 "fields must be listed in order of increasing record number."
 msgstr ""
 
-#: src/data-list.c:375 src/data-list.c:1735
+#: src/data-list.c:377 src/data-list.c:1727
 msgid ""
 "SPSS-like or FORTRAN-like format specification expected after variable names."
 msgstr ""
 
-#: src/data-list.c:386
+#: src/data-list.c:388
 msgid "At least one variable must be specified."
 msgstr ""
 
-#: src/data-list.c:391 src/print.c:328
+#: src/data-list.c:393 src/print.c:328
 msgid ""
 "Variables are specified on records that should not exist according to "
 "RECORDS subcommand."
 msgstr ""
 
-#: src/data-list.c:424 src/data-list.c:438 src/print.c:520 src/print.c:533
+#: src/data-list.c:426 src/data-list.c:440 src/print.c:520 src/print.c:533
 msgid "Column positions for fields must be positive."
 msgstr ""
 
-#: src/data-list.c:443
+#: src/data-list.c:445
 msgid "The ending column for a field must be greater than the starting column."
 msgstr ""
 
-#: src/data-list.c:457
+#: src/data-list.c:459
 #, c-format
 msgid "The %d columns %d-%d can't be evenly divided into %d fields."
 msgstr ""
 
-#: src/data-list.c:477 src/print.c:561
+#: src/data-list.c:479 src/print.c:561
 msgid "A format specifier on this line has extra characters on the end."
 msgstr ""
 
-#: src/data-list.c:492 src/print.c:577
+#: src/data-list.c:494 src/print.c:577
 msgid "The value for number of decimal places must be at least 1."
 msgstr ""
 
-#: src/data-list.c:506 src/print.c:590
+#: src/data-list.c:508 src/print.c:590
 #, c-format
 msgid "Input format %s doesn't accept decimal places."
 msgstr ""
 
-#: src/data-list.c:553 src/data-list.c:649 src/data-list.c:863
+#: src/data-list.c:555 src/data-list.c:651 src/data-list.c:859
 #, c-format
 msgid "%s is a duplicate variable name."
 msgstr ""
 
-#: src/data-list.c:558
+#: src/data-list.c:560
 #, c-format
 msgid "There is already a variable %s of a different type."
 msgstr ""
 
-#: src/data-list.c:565
+#: src/data-list.c:567
 #, c-format
 msgid "There is already a string variable %s of a different width."
 msgstr ""
 
-#: src/data-list.c:640
+#: src/data-list.c:642
 msgid ""
 "The number of format specifications exceeds the given number of variable "
 "names."
 msgstr ""
 
-#: src/data-list.c:753 src/print.c:766
+#: src/data-list.c:755 src/print.c:766
 msgid ""
 "There aren't enough format specifications to match the number of variable "
 "names given."
 msgstr ""
 
-#: src/data-list.c:780 src/data-list.c:904 src/descript.c:882 src/print.c:797
-#: src/sysfile-info.c:139 src/sysfile-info.c:373 src/vfm.c:839
+#: src/data-list.c:782 src/data-list.c:900 src/descript.c:882 src/print.c:797
+#: src/sysfile-info.c:139 src/sysfile-info.c:373 src/vfm.c:844
 msgid "Variable"
 msgstr ""
 
-#: src/data-list.c:781 src/print.c:798
+#: src/data-list.c:783 src/print.c:798
 msgid "Record"
 msgstr ""
 
-#: src/data-list.c:782 src/print.c:799
+#: src/data-list.c:784 src/print.c:799
 msgid "Columns"
 msgstr ""
 
-#: src/data-list.c:783 src/data-list.c:905 src/print.c:800
+#: src/data-list.c:785 src/data-list.c:901 src/print.c:800
 msgid "Format"
 msgstr ""
 
-#: src/data-list.c:799
+#: src/data-list.c:800
 #, c-format
-msgid "Reading %d record from file %s."
-msgid_plural "Reading %d records from file %s."
+msgid "Reading %d record from %s."
+msgid_plural "Reading %d records from %s."
 msgstr[0] ""
 msgstr[1] ""
 
-#: src/data-list.c:803
+#: src/data-list.c:916
 #, c-format
-msgid "Reading %d record from the command file."
-msgid_plural "Reading %d records from the command file."
-msgstr[0] ""
-msgstr[1] ""
-
-#: src/data-list.c:921
-#, c-format
-msgid "Reading free-form data from file %s."
-msgstr ""
-
-#: src/data-list.c:924
-msgid "Reading free-form data from the command file."
+msgid "Reading free-form data from %s."
 msgstr ""
 
-#: src/data-list.c:975
+#: src/data-list.c:967
 #, c-format
 msgid "Quoted string missing terminating `%c'."
 msgstr ""
 
-#: src/data-list.c:1084
+#: src/data-list.c:1076
 #, c-format
 msgid "Partial case of %d of %d records discarded."
 msgstr ""
 
-#: src/data-list.c:1138
+#: src/data-list.c:1130
 #, c-format
 msgid "Partial case discarded.  The first variable missing was %s."
 msgstr ""
 
-#: src/data-list.c:1182
+#: src/data-list.c:1174
 #, c-format
 msgid ""
 "Missing value(s) for all variables from %s onward.  These will be filled "
 "with the system-missing value or blanks, as appropriate."
 msgstr ""
 
-#: src/data-list.c:1259
+#: src/data-list.c:1251
 msgid "Attempt to read past end of file."
 msgstr ""
 
-#: src/data-list.c:1398
+#: src/data-list.c:1390
 msgid ""
 "REPEATING DATA must use the same file as its corresponding DATA LIST or FILE "
 "TYPE."
 msgstr ""
 
-#: src/data-list.c:1408 src/data-list.c:1442 src/data-list.c:1455
-#: src/data-list.c:1468 src/data-list.c:1502
+#: src/data-list.c:1400 src/data-list.c:1434 src/data-list.c:1447
+#: src/data-list.c:1460 src/data-list.c:1494
 #, c-format
 msgid "%s subcommand given multiple times."
 msgstr ""
 
-#: src/data-list.c:1431
+#: src/data-list.c:1423
 #, c-format
 msgid "STARTS beginning column (%d) exceeds STARTS ending column (%d)."
 msgstr ""
 
-#: src/data-list.c:1488
+#: src/data-list.c:1480
 #, c-format
 msgid "CONTINUED beginning column (%d) exceeds CONTINUED ending column (%d)."
 msgstr ""
 
-#: src/data-list.c:1511
+#: src/data-list.c:1503
 #, c-format
 msgid "ID beginning column (%ld) must be positive."
 msgstr ""
 
-#: src/data-list.c:1526
+#: src/data-list.c:1518
 #, c-format
 msgid "ID ending column (%ld) must be positive."
 msgstr ""
 
-#: src/data-list.c:1532
+#: src/data-list.c:1524
 #, c-format
 msgid "ID ending column (%ld) cannot be less than ID beginning column (%d)."
 msgstr ""
 
-#: src/data-list.c:1572
+#: src/data-list.c:1564
 msgid "Missing required specification STARTS."
 msgstr ""
 
-#: src/data-list.c:1574
+#: src/data-list.c:1566
 msgid "Missing required specification OCCURS."
 msgstr ""
 
-#: src/data-list.c:1581
+#: src/data-list.c:1573
 msgid "ID specified without CONTINUED."
 msgstr ""
 
-#: src/data-list.c:1592
+#: src/data-list.c:1584
 #, c-format
 msgid ""
 "STARTS beginning column (%d) exceeds default STARTS ending column taken from "
 "file's record width (%d)."
 msgstr ""
 
-#: src/data-list.c:1605
+#: src/data-list.c:1597
 #, c-format
 msgid ""
 "CONTINUED beginning column (%d) exceeds default CONTINUED ending column "
 "taken from file's record width (%d)."
 msgstr ""
 
-#: src/data-list.c:1684
+#: src/data-list.c:1676
 msgid "String variable not allowed here."
 msgstr ""
 
-#: src/data-list.c:1694
+#: src/data-list.c:1686
 #, c-format
 msgid "%s (%d) must be at least 1."
 msgstr ""
 
-#: src/data-list.c:1700
+#: src/data-list.c:1692
 #, c-format
 msgid "Variable or integer expected for %s."
 msgstr ""
 
-#: src/data-list.c:1825
+#: src/data-list.c:1817
 #, c-format
 msgid "Encountered mismatched record ID \"%s\" expecting \"%s\"."
 msgstr ""
 
-#: src/data-list.c:1857
+#: src/data-list.c:1849
 #, c-format
 msgid ""
 "Variable %s starting in column %d extends beyond physical record length of %"
 "d."
 msgstr ""
 
-#: src/data-list.c:1924
+#: src/data-list.c:1916
 #, c-format
 msgid "Invalid value %d for OCCURS."
 msgstr ""
 
-#: src/data-list.c:1930
+#: src/data-list.c:1922
 #, c-format
 msgid "Beginning column for STARTS (%d) must be at least 1."
 msgstr ""
 
-#: src/data-list.c:1938
+#: src/data-list.c:1930
 #, c-format
 msgid "Ending column for STARTS (%d) is less than beginning column (%d)."
 msgstr ""
 
-#: src/data-list.c:1946
+#: src/data-list.c:1938
 #, c-format
 msgid "Invalid value %d for LENGTH."
 msgstr ""
 
-#: src/data-list.c:1953
+#: src/data-list.c:1945
 #, c-format
 msgid "Beginning column for CONTINUED (%d) must be at least 1."
 msgstr ""
 
-#: src/data-list.c:1961
+#: src/data-list.c:1953
 #, c-format
 msgid "Ending column for CONTINUED (%d) is less than beginning column (%d)."
 msgstr ""
 
-#: src/data-list.c:1993
+#: src/data-list.c:1985
 #, c-format
 msgid ""
 "Number of repetitions specified on OCCURS (%d) exceed number of repetitions "
 "available in space on STARTS (%d), and CONTINUED not specified."
 msgstr ""
 
-#: src/data-list.c:2011
+#: src/data-list.c:2003
 #, c-format
 msgid "Unexpected end of file with %d repetitions remaining out of %d."
 msgstr ""
@@ -1368,42 +1369,42 @@ msgstr ""
 msgid "Valid cases = %g; cases with missing value(s) = %g."
 msgstr ""
 
-#: src/dfm-read.c:158
+#: src/dfm-read.c:136
 #, c-format
 msgid "Could not open \"%s\" for reading as a data file: %s."
 msgstr ""
 
-#: src/dfm-read.c:191 src/dfm-read.c:209
+#: src/dfm-read.c:168 src/dfm-read.c:186
 msgid "BEGIN DATA expected."
 msgstr ""
 
-#: src/dfm-read.c:218
+#: src/dfm-read.c:195
 msgid ""
 "Unexpected end-of-file while reading data in BEGIN DATA.  This probably "
 "indicates a missing or misformatted END DATA command.  END DATA must appear "
 "by itself on a single line with exactly one space between words."
 msgstr ""
 
-#: src/dfm-read.c:251 src/dfm-read.c:271
+#: src/dfm-read.c:227 src/dfm-read.c:247
 #, c-format
 msgid "Error reading file %s: %s."
 msgstr ""
 
-#: src/dfm-read.c:274
+#: src/dfm-read.c:250
 #, c-format
 msgid "%s: Partial record at end of file."
 msgstr ""
 
-#: src/dfm-read.c:317
+#: src/dfm-read.c:299
 #, c-format
 msgid "Attempt to read beyond end-of-file on file %s."
 msgstr ""
 
-#: src/dfm-read.c:320
+#: src/dfm-read.c:302
 msgid "Attempt to read beyond END DATA."
 msgstr ""
 
-#: src/dfm-read.c:467
+#: src/dfm-read.c:446
 msgid ""
 "This command is not valid here since the current input program does not "
 "access the inline file."
@@ -1721,67 +1722,61 @@ msgstr ""
 msgid "%s is a PSPP extension."
 msgstr ""
 
-#: src/file-handle-def.c:229
+#: src/file-handle-def.c:303
 #, c-format
-msgid "Can't open %s as a %s because it is already open as a %s"
+msgid "Can't open %s as a %s because it is already open as a %s."
 msgstr ""
 
-#: src/file-handle-def.c:236
+#: src/file-handle-def.c:310
 #, c-format
-msgid "Can't open %s as a %s for %s because it is already open for %s"
+msgid "Can't open %s as a %s for %s because it is already open for %s."
 msgstr ""
 
-#: src/file-handle-def.c:244
+#: src/file-handle-def.c:318
 #, c-format
-msgid "Can't re-open %s as a %s for %s"
+msgid "Can't re-open %s as a %s for %s."
 msgstr ""
 
-#: src/file-handle.q:68
+#: src/file-handle.q:69
 #, c-format
 msgid ""
-"File handle %s already refers to file %s.  File handles cannot be redefined "
-"within a session."
+"File handle %s is already defined.  Use CLOSE FILE HANDLE before redefining "
+"a file handle."
 msgstr ""
 
-#: src/file-handle.q:89
-msgid "The FILE HANDLE required subcommand NAME is not present."
-msgstr ""
-
-#: src/file-handle.q:104
+#: src/file-handle.q:101
 #, c-format
 msgid ""
 "Fixed-length records were specified on /RECFORM, but record length was not "
 "specified on /LRECL.  Assuming %d-character records."
 msgstr ""
 
-#: src/file-handle.q:109
+#: src/file-handle.q:106
 #, c-format
 msgid ""
 "Record length (%ld) must be at least one byte.  Assuming %d-character "
 "records."
 msgstr ""
 
-#: src/file-handle.q:139
-msgid "expecting a file name or handle name"
+#: src/file-handle.q:152
+msgid "file"
 msgstr ""
 
-#: src/filename.c:227
-#, c-format
-msgid "Searching for `%s'..."
+#: src/file-handle.q:154
+msgid "inline file"
 msgstr ""
 
-#: src/filename.c:235 src/filename.c:267
-msgid "Search unsuccessful!"
+#: src/file-handle.q:156
+msgid "scratch file"
 msgstr ""
 
-#: src/filename.c:260
-#, c-format
-msgid "Found `%s'."
+#: src/file-handle.q:177
+msgid "expecting a file name or handle name"
 msgstr ""
 
-#: src/filename.c:665
+#: src/file-handle.q:204
 #, c-format
-msgid "Not opening pipe file `%s' because SAFER option set."
+msgid "Handle for %s not allowed here."
 msgstr ""
 
 #: src/file-type.c:133
@@ -1922,6 +1917,25 @@ msgstr ""
 msgid "Unknown record type %g."
 msgstr ""
 
+#: src/filename.c:227
+#, c-format
+msgid "Searching for `%s'..."
+msgstr ""
+
+#: src/filename.c:235 src/filename.c:267
+msgid "Search unsuccessful!"
+msgstr ""
+
+#: src/filename.c:260
+#, c-format
+msgid "Found `%s'."
+msgstr ""
+
+#: src/filename.c:677
+#, c-format
+msgid "Not opening pipe file `%s' because SAFER option set."
+msgstr ""
+
 #: src/flip.c:88
 msgid ""
 "FLIP ignores TEMPORARY.  Temporary transformations will be made permanent."
@@ -1983,6 +1997,29 @@ msgstr ""
 msgid "Unexpected end of file reading FLIP temporary file."
 msgstr ""
 
+#: src/format-prs.c:66
+msgid "X and T format specifiers not allowed here."
+msgstr ""
+
+#: src/format-prs.c:74
+#, c-format
+msgid "%.*s is not a valid data format."
+msgstr ""
+
+#: src/format-prs.c:115
+msgid "Format specifier expected."
+msgstr ""
+
+#: src/format-prs.c:127
+#, c-format
+msgid "Data format %s does not specify a width."
+msgstr ""
+
+#: src/format-prs.c:145
+#, c-format
+msgid "Data format %s is not valid."
+msgstr ""
+
 #: src/format.c:73
 #, c-format
 msgid "Format specifies a bad type (%d)"
@@ -2039,11 +2076,11 @@ msgstr ""
 msgid "%s variables are not compatible with %s format %s."
 msgstr ""
 
-#: src/format.c:197 src/pfm-read.c:476 src/sfm-read.c:1012 src/sfm-read.c:1021
+#: src/format.c:197 src/pfm-read.c:476 src/sfm-read.c:1014 src/sfm-read.c:1023
 msgid "String"
 msgstr ""
 
-#: src/format.c:197 src/pfm-read.c:476 src/sfm-read.c:1012 src/sfm-read.c:1021
+#: src/format.c:197 src/pfm-read.c:476 src/sfm-read.c:1014 src/sfm-read.c:1023
 msgid "Numeric"
 msgstr ""
 
@@ -2052,29 +2089,6 @@ msgstr ""
 msgid "String variable with width %d not compatible with format %s."
 msgstr ""
 
-#: src/format-prs.c:66
-msgid "X and T format specifiers not allowed here."
-msgstr ""
-
-#: src/format-prs.c:74
-#, c-format
-msgid "%.*s is not a valid data format."
-msgstr ""
-
-#: src/format-prs.c:115
-msgid "Format specifier expected."
-msgstr ""
-
-#: src/format-prs.c:127
-#, c-format
-msgid "Data format %s does not specify a width."
-msgstr ""
-
-#: src/format-prs.c:145
-#, c-format
-msgid "Data format %s is not valid."
-msgstr ""
-
 #: src/formats.c:89
 msgid "`(' expected after variable list"
 msgstr ""
@@ -2168,16 +2182,20 @@ msgstr ""
 msgid "No valid data for variable %s; statistics not displayed."
 msgstr ""
 
-#: src/get.c:306 src/get.c:320 src/get.c:345
+#: src/get.c:108
+msgid "expecting COMM or TAPE"
+msgstr ""
+
+#: src/get.c:343 src/get.c:357 src/get.c:382
 #, c-format
 msgid "expecting %s or %s"
 msgstr ""
 
-#: src/get.c:552 src/print.c:179
+#: src/get.c:586 src/print.c:179
 msgid "expecting a valid subcommand"
 msgstr ""
 
-#: src/get.c:585
+#: src/get.c:619
 #, c-format
 msgid ""
 "Cannot rename %s as %s because there already exists a variable named %s.  To "
@@ -2185,85 +2203,81 @@ msgid ""
 "as \"/RENAME (A=B)(B=C)(C=A)\", or equivalently, \"/RENAME (A B C=B C A)\"."
 msgstr ""
 
-#: src/get.c:610
+#: src/get.c:644
 msgid "`=' expected after variable list."
 msgstr ""
 
-#: src/get.c:617
+#: src/get.c:651
 #, c-format
 msgid ""
 "Number of variables on left side of `=' (%d) does not match number of "
 "variables on right side (%d), in parenthesized group %d of RENAME subcommand."
 msgstr ""
 
-#: src/get.c:630
+#: src/get.c:664
 #, c-format
 msgid "Requested renaming duplicates variable name %s."
 msgstr ""
 
-#: src/get.c:660
+#: src/get.c:694
 msgid "Cannot DROP all variables from dictionary."
 msgstr ""
 
-#: src/get.c:835
+#: src/get.c:869
 msgid "The active file may not be specified more than once."
 msgstr ""
 
-#: src/get.c:844
+#: src/get.c:878
 msgid "Cannot specify the active file since no active file has been defined."
 msgstr ""
 
-#: src/get.c:852
+#: src/get.c:886
 msgid ""
 "MATCH FILES may not be used after TEMPORARY when the active file is an input "
 "source.  Temporary transformations will be made permanent."
 msgstr ""
 
-#: src/get.c:890
+#: src/get.c:924
 msgid "Multiple IN subcommands for a single FILE or TABLE."
 msgstr ""
 
-#: src/get.c:910
+#: src/get.c:944
 msgid "BY may appear at most once."
 msgstr ""
 
-#: src/get.c:930
+#: src/get.c:964
 #, c-format
 msgid "File %s lacks BY variable %s."
 msgstr ""
 
-#: src/get.c:944
+#: src/get.c:978
 msgid "FIRST may appear at most once."
 msgstr ""
 
-#: src/get.c:958
+#: src/get.c:992
 msgid "LAST may appear at most once."
 msgstr ""
 
-#: src/get.c:999
+#: src/get.c:1033
 msgid "BY is required when TABLE is specified."
 msgstr ""
 
-#: src/get.c:1004
+#: src/get.c:1038
 msgid "BY is required when IN is specified."
 msgstr ""
 
-#: src/get.c:1032
+#: src/get.c:1066
 #, c-format
 msgid "IN variable name %s duplicates an existing variable name."
 msgstr ""
 
-#: src/get.c:1461
+#: src/get.c:1495
 #, c-format
 msgid ""
 "Variable %s in file %s (%s) has different type or width from the same "
 "variable in earlier file (%s)."
 msgstr ""
 
-#: src/get.c:1545
-msgid "expecting COMM or TAPE"
-msgstr ""
-
 #: src/getl.c:139
 #, c-format
 msgid "Can't find `%s' in include file search path."
@@ -3270,34 +3284,34 @@ msgstr ""
 msgid "Cannot change mode of %s: %s"
 msgstr ""
 
-#: src/pfm-read.c:87
+#: src/pfm-read.c:97
 #, c-format
 msgid "portable file %s corrupt at offset %ld: "
 msgstr ""
 
-#: src/pfm-read.c:114
+#: src/pfm-read.c:124
 msgid "unexpected end of file"
 msgstr ""
 
-#: src/pfm-read.c:172
+#: src/pfm-read.c:182
 #, c-format
 msgid ""
 "An error occurred while opening \"%s\" for reading as a portable file: %s."
 msgstr ""
 
-#: src/pfm-read.c:190
+#: src/pfm-read.c:200
 msgid "Data record expected."
 msgstr ""
 
-#: src/pfm-read.c:298
+#: src/pfm-read.c:308
 msgid "Missing numeric terminator."
 msgstr ""
 
-#: src/pfm-read.c:321
+#: src/pfm-read.c:331
 msgid "Invalid integer."
 msgstr ""
 
-#: src/pfm-read.c:332
+#: src/pfm-read.c:342
 #, c-format
 msgid "Bad string length %d."
 msgstr ""
@@ -3317,12 +3331,12 @@ msgstr ""
 msgid "Bad time string length %d."
 msgstr ""
 
-#: src/pfm-read.c:468 src/sfm-read.c:1004
+#: src/pfm-read.c:468 src/sfm-read.c:1006
 #, c-format
 msgid "%s: Bad format specifier byte (%d)."
 msgstr ""
 
-#: src/pfm-read.c:475 src/sfm-read.c:1020
+#: src/pfm-read.c:475 src/sfm-read.c:1022
 #, c-format
 msgid "%s variable %s has invalid format specifier %s."
 msgstr ""
@@ -3398,7 +3412,7 @@ msgstr ""
 msgid "%s: Writing portable file: %s."
 msgstr ""
 
-#: src/pfm-write.c:468
+#: src/pfm-write.c:466
 #, c-format
 msgid "%s: Closing portable file: %s."
 msgstr ""
@@ -3585,19 +3599,23 @@ msgstr ""
 
 #: src/print.c:839
 #, c-format
-msgid "Writing %d record(s) to file %s."
-msgstr ""
+msgid "Writing %d record to %s."
+msgid_plural "Writing %d records to %s."
+msgstr[0] ""
+msgstr[1] ""
 
-#: src/print.c:842
+#: src/print.c:843
 #, c-format
-msgid "Writing %d record(s) to the listing file."
-msgstr ""
+msgid "Writing %d record."
+msgid_plural "Writing %d records."
+msgstr[0] ""
+msgstr[1] ""
 
-#: src/print.c:1082
+#: src/print.c:1084
 msgid "The expression on PRINT SPACE evaluated to the system-missing value."
 msgstr ""
 
-#: src/print.c:1085
+#: src/print.c:1087
 #, c-format
 msgid "The expression on PRINT SPACE evaluated to %g."
 msgstr ""
@@ -3807,6 +3825,13 @@ msgstr ""
 msgid "Cannot sample %d observations from a population of %d."
 msgstr ""
 
+#: src/scratch-reader.c:59
+#, c-format
+msgid ""
+"Scratch file handle %s has not yet been written, using SAVE or another "
+"procedure, so it cannot yet be used for reading."
+msgstr ""
+
 #: src/sel-if.c:103
 msgid "The filter variable must be numeric."
 msgstr ""
@@ -3952,131 +3977,131 @@ msgstr ""
 msgid "corrupt system file: "
 msgstr ""
 
-#: src/sfm-read.c:151 src/sfm-write.c:931
+#: src/sfm-read.c:149 src/sfm-write.c:929
 #, c-format
 msgid "%s: Closing system file: %s."
 msgstr ""
 
-#: src/sfm-read.c:237
+#: src/sfm-read.c:239
 #, c-format
 msgid ""
 "An error occurred while opening \"%s\" for reading as a system file: %s."
 msgstr ""
 
-#: src/sfm-read.c:255
+#: src/sfm-read.c:257
 #, c-format
 msgid ""
 "%s: Index of weighting variable (%d) is not between 0 and number of elements "
 "per case (%d)."
 msgstr ""
 
-#: src/sfm-read.c:264
+#: src/sfm-read.c:266
 #, c-format
 msgid ""
 "%s: Weighting variable may not be a continuation of a long string variable."
 msgstr ""
 
-#: src/sfm-read.c:267
+#: src/sfm-read.c:269
 #, c-format
 msgid "%s: Weighting variable may not be a string variable."
 msgstr ""
 
-#: src/sfm-read.c:292
+#: src/sfm-read.c:294
 #, c-format
 msgid ""
 "%s: Orphaned variable index record (type 4).  Type 4 records must always "
 "immediately follow type 3 records."
 msgstr ""
 
-#: src/sfm-read.c:350
+#: src/sfm-read.c:352
 #, c-format
 msgid "%s: Invalid subrecord length. Record: 7; Subrecord: 11"
 msgstr ""
 
-#: src/sfm-read.c:404
+#: src/sfm-read.c:406
 #, c-format
 msgid "%s: Trailing garbage in long variable name map."
 msgstr ""
 
-#: src/sfm-read.c:411
+#: src/sfm-read.c:413
 #, c-format
 msgid "%s: Long variable mapping to invalid variable name `%s'."
 msgstr ""
 
-#: src/sfm-read.c:421
+#: src/sfm-read.c:423
 #, c-format
 msgid "%s: Long variable mapping for nonexistent variable %s."
 msgstr ""
 
-#: src/sfm-read.c:431
+#: src/sfm-read.c:433
 #, c-format
 msgid "%s: Duplicate long variable name `%s' within system file."
 msgstr ""
 
-#: src/sfm-read.c:459
+#: src/sfm-read.c:461
 #, c-format
 msgid "%s: Unrecognized record type 7, subtype %d encountered in system file."
 msgstr ""
 
-#: src/sfm-read.c:484
+#: src/sfm-read.c:486
 #, c-format
 msgid "%s: Unrecognized record type %d."
 msgstr ""
 
-#: src/sfm-read.c:516
+#: src/sfm-read.c:518
 #, c-format
 msgid ""
 "%s: Bad size (%d) or count (%d) field on record type 7, subtype 3.\tExpected "
 "size %d, count 8."
 msgstr ""
 
-#: src/sfm-read.c:527
+#: src/sfm-read.c:529
 #, c-format
 msgid ""
 "%s: Floating-point representation in system file is not IEEE-754.  PSPP "
 "cannot convert between floating-point formats."
 msgstr ""
 
-#: src/sfm-read.c:543
+#: src/sfm-read.c:545
 #, c-format
 msgid ""
 "%s: File-indicated endianness (%s) does not match endianness intuited from "
 "file header (%s)."
 msgstr ""
 
-#: src/sfm-read.c:546 src/sfm-read.c:547
+#: src/sfm-read.c:548 src/sfm-read.c:549
 msgid "big-endian"
 msgstr ""
 
-#: src/sfm-read.c:546 src/sfm-read.c:547
+#: src/sfm-read.c:548 src/sfm-read.c:549
 msgid "little-endian"
 msgstr ""
 
-#: src/sfm-read.c:548
+#: src/sfm-read.c:550
 msgid "unknown"
 msgstr ""
 
-#: src/sfm-read.c:552
+#: src/sfm-read.c:554
 #, c-format
 msgid "%s: File-indicated character representation code (%s) is not ASCII."
 msgstr ""
 
-#: src/sfm-read.c:556
+#: src/sfm-read.c:558
 msgid "DEC Kanji"
 msgstr ""
 
-#: src/sfm-read.c:556 src/sysfile-info.c:119
+#: src/sfm-read.c:558 src/sysfile-info.c:119
 msgid "Unknown"
 msgstr ""
 
-#: src/sfm-read.c:572
+#: src/sfm-read.c:574
 #, c-format
 msgid ""
 "%s: Bad size (%d) or count (%d) field on record type 7, subtype 4.\tExpected "
 "size %d, count 8."
 msgstr ""
 
-#: src/sfm-read.c:587
+#: src/sfm-read.c:589
 #, c-format
 msgid ""
 "%s: File-indicated value is different from internal value for at least one "
@@ -4084,220 +4109,220 @@ msgid ""
 "%g; LOWEST: %g, %g."
 msgstr ""
 
-#: src/sfm-read.c:614
+#: src/sfm-read.c:616
 #, c-format
 msgid ""
 "%s: Bad magic.  Proper system files begin with the four characters `$FL2'. "
 "This file will not be read."
 msgstr ""
 
-#: src/sfm-read.c:656
+#: src/sfm-read.c:658
 #, c-format
 msgid ""
 "%s: File layout code has unexpected value %d.  Value should be 2, in big-"
 "endian or little-endian format."
 msgstr ""
 
-#: src/sfm-read.c:684
+#: src/sfm-read.c:686
 #, c-format
 msgid "%s: Number of cases in file (%ld) is not between -1 and %d."
 msgstr ""
 
-#: src/sfm-read.c:689
+#: src/sfm-read.c:691
 #, c-format
 msgid "%s: Compression bias (%g) is not the usual value of 100."
 msgstr ""
 
-#: src/sfm-read.c:812
+#: src/sfm-read.c:814
 #, c-format
 msgid ""
 "%s: position %d: String variable does not have proper number of continuation "
 "records."
 msgstr ""
 
-#: src/sfm-read.c:823
+#: src/sfm-read.c:825
 #, c-format
 msgid "%s: position %d: Superfluous long string continuation record."
 msgstr ""
 
-#: src/sfm-read.c:829
+#: src/sfm-read.c:831
 #, c-format
 msgid "%s: position %d: Bad variable type code %d."
 msgstr ""
 
-#: src/sfm-read.c:832
+#: src/sfm-read.c:834
 #, c-format
 msgid "%s: position %d: Variable label indicator field is not 0 or 1."
 msgstr ""
 
-#: src/sfm-read.c:836
+#: src/sfm-read.c:838
 #, c-format
 msgid ""
 "%s: position %d: Missing value indicator field is not -3, -2, 0, 1, 2, or 3."
 msgstr ""
 
-#: src/sfm-read.c:842
+#: src/sfm-read.c:844
 #, c-format
 msgid "%s: position %d: Variable name begins with invalid character."
 msgstr ""
 
-#: src/sfm-read.c:846
+#: src/sfm-read.c:848
 #, c-format
 msgid "%s: position %d: Variable name begins with lowercase letter %c."
 msgstr ""
 
-#: src/sfm-read.c:850
+#: src/sfm-read.c:852
 #, c-format
 msgid ""
 "%s: position %d: Variable name begins with octothorpe (`#').  Scratch "
 "variables should not appear in system files."
 msgstr ""
 
-#: src/sfm-read.c:865
+#: src/sfm-read.c:867
 #, c-format
 msgid "%s: position %d: Variable name character %d is lowercase letter %c."
 msgstr ""
 
-#: src/sfm-read.c:874
+#: src/sfm-read.c:876
 #, c-format
 msgid ""
 "%s: position %d: character `\\%03o' (%c) is not valid in a variable name."
 msgstr ""
 
-#: src/sfm-read.c:881
+#: src/sfm-read.c:883
 #, c-format
 msgid "%s: Invalid variable name `%s' within system file."
 msgstr ""
 
-#: src/sfm-read.c:888
+#: src/sfm-read.c:890
 #, c-format
 msgid "%s: Duplicate variable name `%s' within system file."
 msgstr ""
 
-#: src/sfm-read.c:911
+#: src/sfm-read.c:913
 #, c-format
 msgid "%s: Variable %s indicates variable label of invalid length %d."
 msgstr ""
 
-#: src/sfm-read.c:932
+#: src/sfm-read.c:934
 #, c-format
 msgid "%s: Long string variable %s may not have missing values."
 msgstr ""
 
-#: src/sfm-read.c:953
+#: src/sfm-read.c:955
 #, c-format
 msgid ""
 "%s: String variable %s may not have missing values specified as a range."
 msgstr ""
 
-#: src/sfm-read.c:980
+#: src/sfm-read.c:982
 #, c-format
 msgid "%s: Long string continuation records omitted at end of dictionary."
 msgstr ""
 
-#: src/sfm-read.c:985
+#: src/sfm-read.c:987
 #, c-format
 msgid ""
 "%s: System file header indicates %d variable positions but %d were read from "
 "file."
 msgstr ""
 
-#: src/sfm-read.c:1010
+#: src/sfm-read.c:1012
 #, c-format
 msgid "%s: %s variable %s has %s format specifier %s."
 msgstr ""
 
-#: src/sfm-read.c:1063
+#: src/sfm-read.c:1065
 #, c-format
 msgid "%s: Invalid number of labels: %d.  Ignoring labels."
 msgstr ""
 
-#: src/sfm-read.c:1105
+#: src/sfm-read.c:1107
 #, c-format
 msgid ""
 "%s: Variable index record (type 4) does not immediately follow value label "
 "record (type 3) as it should."
 msgstr ""
 
-#: src/sfm-read.c:1116
+#: src/sfm-read.c:1118
 #, c-format
 msgid ""
 "%s: Number of variables associated with a value label (%d) is not between 1 "
 "and the number of variables (%d)."
 msgstr ""
 
-#: src/sfm-read.c:1132
+#: src/sfm-read.c:1134
 #, c-format
 msgid ""
 "%s: Variable index associated with value label (%d) is not between 1 and the "
 "number of values (%d)."
 msgstr ""
 
-#: src/sfm-read.c:1139
+#: src/sfm-read.c:1141
 #, c-format
 msgid ""
 "%s: Variable index associated with value label (%d) refers to a continuation "
 "of a string variable, not to an actual variable."
 msgstr ""
 
-#: src/sfm-read.c:1144
+#: src/sfm-read.c:1146
 #, c-format
 msgid "%s: Value labels are not allowed on long string variables (%s)."
 msgstr ""
 
-#: src/sfm-read.c:1155
+#: src/sfm-read.c:1157
 #, c-format
 msgid ""
 "%s: Variables associated with value label are not all of identical type.  "
 "Variable %s has %s type, but variable %s has %s type."
 msgstr ""
 
-#: src/sfm-read.c:1196
+#: src/sfm-read.c:1198
 #, c-format
 msgid "%s: File contains duplicate label for value %g for variable %s."
 msgstr ""
 
-#: src/sfm-read.c:1200
+#: src/sfm-read.c:1202
 #, c-format
 msgid "%s: File contains duplicate label for value `%.*s' for variable %s."
 msgstr ""
 
-#: src/sfm-read.c:1242 src/sfm-read.c:1519
+#: src/sfm-read.c:1244 src/sfm-read.c:1521
 #, c-format
 msgid "%s: Reading system file: %s."
 msgstr ""
 
-#: src/sfm-read.c:1245 src/sfm-read.c:1360 src/sfm-read.c:1402
+#: src/sfm-read.c:1247 src/sfm-read.c:1362 src/sfm-read.c:1404
 #, c-format
 msgid "%s: Unexpected end of file."
 msgstr ""
 
-#: src/sfm-read.c:1260
+#: src/sfm-read.c:1262
 #, c-format
 msgid "%s: Seeking system file: %s."
 msgstr ""
 
-#: src/sfm-read.c:1275
+#: src/sfm-read.c:1277
 #, c-format
 msgid "%s: System file contains multiple type 6 (document) records."
 msgstr ""
 
-#: src/sfm-read.c:1281
+#: src/sfm-read.c:1283
 #, c-format
 msgid "%s: Number of document lines (%ld) must be greater than 0."
 msgstr ""
 
-#: src/sfm-read.c:1313
+#: src/sfm-read.c:1315
 #, c-format
 msgid "%s: Error reading file: %s."
 msgstr ""
 
-#: src/sfm-read.c:1350
+#: src/sfm-read.c:1352
 #, c-format
 msgid "%s: Compressed data is corrupted.  Data ends in partial case."
 msgstr ""
 
-#: src/sfm-read.c:1522
+#: src/sfm-read.c:1524
 #, c-format
 msgid "%s: Partial record at end of system file."
 msgstr ""
@@ -4317,6 +4342,14 @@ msgstr ""
 msgid "%s: Writing system file: %s."
 msgstr ""
 
+#: src/sort-prs.c:94
+msgid "`A' or `D' expected inside parentheses."
+msgstr ""
+
+#: src/sort-prs.c:99
+msgid "`)' expected."
+msgstr ""
+
 #: src/sort.c:85
 msgid "Buffer limit must be at least 2."
 msgstr ""
@@ -4328,14 +4361,6 @@ msgid ""
 "each.  (PSPP workspace is currently restricted to a maximum of %d KB.)"
 msgstr ""
 
-#: src/sort-prs.c:94
-msgid "`A' or `D' expected inside parentheses."
-msgstr ""
-
-#: src/sort-prs.c:99
-msgid "`)' expected."
-msgstr ""
-
 #: src/sysfile-info.c:100
 msgid "File:"
 msgstr ""
@@ -4437,7 +4462,7 @@ msgstr ""
 msgid "Documents in the active file:"
 msgstr ""
 
-#: src/sysfile-info.c:380 src/sysfile-info.c:519 src/vfm.c:841
+#: src/sysfile-info.c:380 src/sysfile-info.c:519 src/vfm.c:846
 msgid "Label"
 msgstr ""
 
@@ -4468,51 +4493,6 @@ msgstr ""
 msgid "Vector"
 msgstr ""
 
-#: src/tab.c:258
-#, c-format
-msgid "bad vline: x=%d+%d=%d y=(%d+%d=%d,%d+%d=%d) in table size (%d,%d)\n"
-msgstr ""
-
-#: src/tab.c:333
-#, c-format
-msgid ""
-"bad box: (%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) in table size (%d,%d)\n"
-msgstr ""
-
-#: src/temporary.c:49
-msgid "This command is not valid inside DO IF or LOOP."
-msgstr ""
-
-#: src/temporary.c:56
-msgid ""
-"This command may only appear once between procedures and procedure-like "
-"commands."
-msgstr ""
-
-#: src/title.c:60
-#, c-format
-msgid "%s before: %s\n"
-msgstr ""
-
-#: src/title.c:60
-msgid "<none>"
-msgstr ""
-
-#: src/title.c:72
-#, c-format
-msgid "%s: `.' expected after string."
-msgstr ""
-
-#: src/title.c:88
-#, c-format
-msgid "%s after: %s\n"
-msgstr ""
-
-#: src/title.c:143
-#, c-format
-msgid "Document entered %s by %s:"
-msgstr ""
-
 #: src/t-test.q:270
 msgid "TESTVAL, GROUPS and PAIRS subcommands are mutually exclusive."
 msgstr ""
@@ -4638,6 +4618,51 @@ msgstr ""
 msgid "%s & %s"
 msgstr ""
 
+#: src/tab.c:258
+#, c-format
+msgid "bad vline: x=%d+%d=%d y=(%d+%d=%d,%d+%d=%d) in table size (%d,%d)\n"
+msgstr ""
+
+#: src/tab.c:333
+#, c-format
+msgid ""
+"bad box: (%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) in table size (%d,%d)\n"
+msgstr ""
+
+#: src/temporary.c:49
+msgid "This command is not valid inside DO IF or LOOP."
+msgstr ""
+
+#: src/temporary.c:56
+msgid ""
+"This command may only appear once between procedures and procedure-like "
+"commands."
+msgstr ""
+
+#: src/title.c:60
+#, c-format
+msgid "%s before: %s\n"
+msgstr ""
+
+#: src/title.c:60
+msgid "<none>"
+msgstr ""
+
+#: src/title.c:72
+#, c-format
+msgid "%s: `.' expected after string."
+msgstr ""
+
+#: src/title.c:88
+#, c-format
+msgid "%s after: %s\n"
+msgstr ""
+
+#: src/title.c:143
+#, c-format
+msgid "Document entered %s by %s:"
+msgstr ""
+
 #: src/val-labs.c:121
 #, c-format
 msgid ""
index bc4527fb9800d77a3ebdb52885514fff91a9ad47..a85914ac1598651525c559deceedd26bc280bc37 100644 (file)
@@ -1,6 +1,207 @@
+Sat Jan 28 17:45:36 2006  Ben Pfaff  <blp@gnu.org>
+
+       Cleaner (faster?) way to compact cases.
+
+       * dictionary.c: (dict_compact_case) Removed.
+       (dict_needs_compaction) New function.
+       (struct copy_map) New structure.
+       (struct dict_compactor) New structure.
+       (dict_make_compactor) New function.
+       (dict_compactor_compact) New function.
+       (dict_compactor_destroy) New function.
+       
+Sat Jan 28 17:24:22 2006  Ben Pfaff  <blp@gnu.org>
+
+       Cleanups.
+
+       * data-list.c: Make data_list_source_class static.
+       (dump_fixed_table) Use fh_get_name() to describe source of data.
+       (dump_free_table) Ditto.
+       (cmd_repeating_data) Eliminate special cases for inline file.
+
+       * dictionary.c: (dict_contains_var) Change return value from int to
+       bool.
+       (dict_rename_vars) Ditto.
+       (dict_create_vector) Ditto.
+
+Sat Jan 28 17:20:50 2006  Ben Pfaff  <blp@gnu.org>
+
+       Add scratch file handles.
+
+       Now a file handle can refer to a disk file, to an in-memory
+       structure, or to the "inline" file, instead of just to a disk
+       file.  The introduction of new categories means that special cases
+       for the inline file in a few places could be eliminated, but it
+       also means that code that assumed that a handle refers to a file
+       has to check for that.
+
+       Also, now file handles can be freed, so code now must be sure not
+       to access a handle after closing it (with fh_close()).
+
+       * Makefile.am: Add any-reader.c, any-reader.h, any-writer.c,
+       any-writer.h, scratch-handle.c, scratch-handle.h,
+       scratch-reader.c, scratch-reader.h, scratch-writer.c,
+       scratch-writer.h to pspp_SOURCES.
+
+       * any-reader.c: New file.
+       
+       * any-reader.h: New file.
+       
+       * any-writer.c: New file.
+       
+       * any-writer.h: New file.
+
+       * scratch-handle.c: New file.
+       
+       * scratch-handle.h: New file.
+
+       * scratch-reader.c: New file.
+       
+       * scratch-reader.h: New file.
+
+       * scratch-writer.c: New file.
+       
+       * scratch-writer.h: New file.
+
+       * aggregate.c: Use an any_writer instead of an sfm_writer, to add
+       flexibility.
+
+       * apply-dict.c: Use an any_reader instead of an sfm_reader, to add
+       flexibility.
+
+       * command.def: Add CLOSE FILE HANDLE command.
+
+       * dfm-reader.c: Now fewer special cases for inline file.
+       (static var inline_open_cnt) Removed.
+       (static var inline_file) Removed.
+       (dfm_close_reader) Eliminate a special case for inline file.
+       Reorganize to avoid access-after-free.
+       (dfm_open_reader) Eliminate a special case for inline file.
+       (read_inline_record) Use bool instead of int.  No need to
+       increment line number.
+       (read_file_record) Use bool instead of int.
+       (read_record) Check whether file handle is inline file, instead of
+       for null pointer.
+       (dfm_eof) Ditto.
+       (dfm_expand_tabs) Ditto.
+       (dfm_push) Ditto.
+       (dfm_pop) Ditto.
+       (cmd_begin_data) Fix inaccurate check for whether the inline file
+       is in use--now we can tell by checking whether the inline file's
+       open count is positive.
+       
+       * file-handle-def.c: (struct file_handle) Reorder members.  Add
+       `deleted' member.  Add `referent' member.  Add `sh' member.
+       (static var default_handle) New variable.
+       (static var inline_file) New variable.
+       (fh_init) Initialize inline file.
+       (free_handle) New function.
+       (fh_done) Rewrite.
+       (fh_from_name) Don't return deleted handles.
+       (fh_from_filename) Ditto.
+       (fh_create) Removed.
+       (create_handle) New function.
+       (fh_create_file) New function.
+       (fh_create_scratch) New function.
+       (fh_inline_file) New function.
+       (fh_free) Rewrite.
+       (fh_open) Now requires a referent type mask and verifies it.  All
+       references updated.
+       (fh_close) If open_cnt goes to 0 on a deleted handle, free it.
+       (fh_is_open) New function.
+       (fh_get_referent) New function.
+       (fh_get_filename) Limit to handles that refer to files.
+       (fh_get_mode) Ditto.
+       (fh_get_record_width) Limit to handles that refer to files or the
+       inline file.
+       (fh_get_tab_width) Ditto.
+       (fh_get_scratch_handle) New function.
+       (fh_set_scratch_handle) New function.
+       (fh_get_default_handle) New function.
+       (fh_set_default_handle) New function.
+
+       * file-handle.h: (enum fh_referent) New type.
+       (enum fh_mode) Rename MODE_TEXT to FH_MODE_TEXT, MODE_BINARY to
+       FH_MODE_BINARY, and update all usages.
+
+       * file-handle.q: Add "scratch" as a possible mode.
+       (cmd_file_handle) Mention CLOSE FILE HANDLE in error message.
+       Use lex_end_of_command(), lex_sbc_missing().  Support creating
+       scratch handles.
+       (cmd_close_file_handle) New function.
+       (referent_name) New function.
+       (fh_parse) Now takes a referent type mask to specify handles that
+       can be accepted.  Updated all references.
+       
+       * filename.c: (fn_extension) New function.
+
+       * get.c: Use any_reader and any_writer and thereby merge code that
+       has been duplicated for each kind of file.  Also, we had something
+       here called `any_writer' before, so its name had to be changed to
+       `case_writer'.
+       (enum operation) Removed, because unused.
+       (struct get_pgm) Removed.
+       (get_pgm_free) Removed.
+       (get_source_destroy) Removed.
+       (get_source_read) Removed.
+       (global var get_source_class) Removed.
+       (static var case_reader_source_class) Removed.
+       (enum reader_command) New enum.
+       (struct case_reader_pgm) New struct.
+       (parse_read_command) New function.
+       (case_reader_pgm_free) New function.
+       (case_reader_source_destroy) New function.
+       (case_Reader_source_Read) New function.
+       (cmd_get) Rewrote as a call to parse_read_command().
+       (cmd_import) Ditto.
+       (struct any_writer) Rename to case_writer.  Drop `writer_type',
+       `writer' members in favor of an `any_writer' member named
+       `writer'.
+       (any_writer_destroy) Rename case_writer_destroy.  Use
+       any_writer_close().
+       (parse_write_command) Allow scratch files.  Use any_writer.
+       (any_writer_write_case) Rename case_writer_write_case().  Use
+       any_writer_write().
+`      (struct mtf_file) Use any_reader.
+       (cmd_match_files) Allow scratch files.  Use any_reader.
+       (mtf_free_file) Use any_reader_close().
+       (mtf_read_nonactive_records) Use any_reader_read().
+       (mtf_processing) Use any_reader_read().
+       (struct import_pgm) Removed.
+       (import_pgm_free) Removed.
+       (import_source_destroy) Removed.
+       (import_source_read) Removed.
+       (global var import_source_class) Removed.
+
+       * glob.c: (global var default_handle) Removed.  Replaced all
+       references by fh_get_default_handle() or fh_set_default_handle().
+
+       * pfm-read.c: (static var portable_to_local) Moved from inside
+       read_header() to top level.
+       (pfm_detect) New function.
+
+       * pfm-write.c: (pfm_write_case) Make case argument const.
+       Reorganize to avoid access-after-free.
+
+       * print.c: (dump_table) Use fh_get_name() to describe source of
+       data.
+
+       * sfm-read.c: (sfm_close_reader) Reorganize to avoid
+       access-after-free.
+       (sfm_detect) New function.
+
+       * str.c: (str_lowercase) New function.
+
+       * vfm.c: Use new compaction interface.
+       (static var compaction_necessary) Removed.
+       (static var compactor) New variable.
+       (open_active_file) Initialize compactor.
+       (write_case) Use compactor.
+       (close_active_file) Free compactor.
+       
 Wed Jan 11 19:28:39 2006  Ben Pfaff  <blp@gnu.org>
 
-       Clean up file handle code in preparation to add temporary file
+       Clean up file handle code in preparation to add scratch file
        handles.
        
        * file-handle-def.c: Lots of formatting cleanup.  Added function
index 72731c98c7b2e5df7f3f9e2fff1165a0ecfac87b..84f7c881591b9d3c7420b6bf1dbfe748cd045028 100644 (file)
@@ -72,6 +72,10 @@ pspp_SOURCES =                                       \
        algorithm.h                             \
        alloc.c                                 \
        alloc.h                                 \
+       any-reader.c                            \
+       any-reader.h                            \
+       any-writer.c                            \
+       any-writer.h                            \
        apply-dict.c                            \
        ascii.c                                 \
        autorecode.c                            \
@@ -221,6 +225,12 @@ pspp_SOURCES =                                     \
        tab.c                                   \
        tab.h                                   \
        temporary.c                             \
+       scratch-handle.c                        \
+       scratch-handle.h                        \
+       scratch-reader.c                        \
+       scratch-reader.h                        \
+       scratch-writer.c                        \
+       scratch-writer.h                        \
        mkfile.c                                \
        mkfile.h                                \
        title.c                                 \
index 86f35287c01f7a9ade2982728e8f8ebfede67e97..67e8bb91b1c4e27d2006d1e8e7e08646dafb13e9 100644 (file)
@@ -21,6 +21,7 @@
 #include "error.h"
 #include <stdlib.h>
 #include "alloc.h"
+#include "any-writer.h"
 #include "case.h"
 #include "casefile.h"
 #include "command.h"
@@ -121,7 +122,7 @@ enum missing_treatment
 struct agr_proc 
   {
     /* We have either an output file or a sink. */
-    struct sfm_writer *writer;          /* Output file, or null if none. */
+    struct any_writer *writer;          /* Output file, or null if none. */
     struct case_sink *sink;             /* Sink, or null if none. */
 
     /* Break variables. */
@@ -181,7 +182,7 @@ cmd_aggregate (void)
   lex_match ('=');
   if (!lex_match ('*'))
     {
-      out_file = fh_parse ();
+      out_file = fh_parse (FH_REF_FILE | FH_REF_SCRATCH);
       if (out_file == NULL)
         goto error;
     }
@@ -278,8 +279,7 @@ cmd_aggregate (void)
     }
   else
     {
-      agr.writer = sfm_open_writer (out_file, agr.dict,
-                                    sfm_writer_default_options ());
+      agr.writer = any_writer_open (out_file, agr.dict);
       if (agr.writer == NULL)
         goto error;
       
@@ -297,7 +297,7 @@ cmd_aggregate (void)
           while (casereader_read_xfer (reader, &c)) 
             {
               if (aggregate_single_case (&agr, &c, &agr.agr_case)) 
-                sfm_write_case (agr.writer, &agr.agr_case);
+                any_writer_write (agr.writer, &agr.agr_case);
               case_destroy (&c);
             }
           casereader_destroy (reader);
@@ -312,7 +312,7 @@ cmd_aggregate (void)
       if (agr.case_cnt > 0) 
         {
           dump_aggregate_info (&agr, &agr.agr_case);
-          sfm_write_case (agr.writer, &agr.agr_case);
+          any_writer_write (agr.writer, &agr.agr_case);
         }
     }
   
@@ -652,7 +652,7 @@ agr_destroy (struct agr_proc *agr)
 {
   struct agr_var *iter, *next;
 
-  sfm_close_writer (agr->writer);
+  any_writer_close (agr->writer);
   if (agr->sort != NULL)
     sort_destroy_criteria (agr->sort);
   free (agr->break_vars);
@@ -1075,7 +1075,7 @@ presorted_agr_to_sysfile (struct ccase *c, void *agr_)
   struct agr_proc *agr = agr_;
 
   if (aggregate_single_case (agr, c, &agr->agr_case)) 
-    sfm_write_case (agr->writer, &agr->agr_case);
+    any_writer_write (agr->writer, &agr->agr_case);
 
   return 1;
 }
diff --git a/src/any-reader.c b/src/any-reader.c
new file mode 100644 (file)
index 0000000..0f6f610
--- /dev/null
@@ -0,0 +1,188 @@
+/* PSPP - computes sample statistics.
+   Copyright (C) 2006 Free Software Foundation, Inc.
+   Written by Ben Pfaff <blp@gnu.org>.
+
+   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 2 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, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301, USA. */
+
+#include <config.h>
+#include "any-reader.h"
+#include <assert.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "error.h"
+#include "file-handle-def.h"
+#include "filename.h"
+#include "pfm-read.h"
+#include "sfm-read.h"
+#include "str.h"
+#include "scratch-reader.h"
+#include "xalloc.h"
+
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
+
+/* Type of file backing an any_reader. */
+enum any_reader_type
+  {
+    SYSTEM_FILE,                /* System file. */
+    PORTABLE_FILE,              /* Portable file. */
+    SCRATCH_FILE                /* Scratch file. */
+  };
+
+/* Reader for any type of case-structured file. */
+struct any_reader 
+  {
+    enum any_reader_type type;  /* Type of file. */
+    void *private;              /* Private data. */
+  };
+
+/* Result of type detection. */
+enum detect_result 
+  {
+    YES,                        /* It is this type. */
+    NO,                         /* It is not this type. */
+    IO_ERROR                    /* File couldn't be opened. */
+  };
+
+/* Tries to detect whether HANDLE represents a given type of
+   file, by opening the file and passing it to DETECT, and
+   returns a detect_result. */
+static enum detect_result
+try_detect (struct file_handle *handle, bool (*detect) (FILE *))
+{
+  FILE *file;
+  bool is_type;
+
+  file = fn_open (fh_get_filename (handle), "rb");
+  if (file == NULL)
+    {
+      msg (ME, _("An error occurred while opening \"%s\": %s."),
+           fh_get_filename (handle), strerror (errno));
+      return IO_ERROR;
+    }
+    
+  is_type = detect (file);
+  
+  fn_close (fh_get_filename (handle), file);
+
+  return is_type ? YES : NO;
+}
+
+/* If PRIVATE is non-null, creates and returns a new any_reader,
+   initializing its fields to TYPE and PRIVATE.  If PRIVATE is a
+   null pointer, just returns a null pointer. */   
+static struct any_reader *
+make_any_reader (enum any_reader_type type, void *private) 
+{
+  if (private != NULL) 
+    {
+      struct any_reader *reader = xmalloc (sizeof *reader);
+      reader->type = type;
+      reader->private = private;
+      return reader;
+    }
+  else
+    return NULL;
+}
+
+/* Creates an any_reader for HANDLE.  On success, returns the new
+   any_reader and stores the file's dictionary into *DICT.  On
+   failure, returns a null pointer. */
+struct any_reader *
+any_reader_open (struct file_handle *handle, struct dictionary **dict)
+{
+  switch (fh_get_referent (handle)) 
+    {
+    case FH_REF_FILE:
+      {
+        enum detect_result result;
+
+        result = try_detect (handle, sfm_detect);
+        if (result == IO_ERROR)
+          return NULL;
+        else if (result == YES)
+          return make_any_reader (SYSTEM_FILE,
+                                  sfm_open_reader (handle, dict, NULL));
+
+        result = try_detect (handle, pfm_detect);
+        if (result == IO_ERROR)
+          return NULL;
+        else if (result == YES)
+          return make_any_reader (PORTABLE_FILE,
+                                  pfm_open_reader (handle, dict, NULL));
+
+        msg (SE, _("\"%s\" is not a system or portable file."),
+             fh_get_filename (handle));
+        return NULL;
+      }
+
+    case FH_REF_INLINE:
+      msg (SE, _("The inline file is not allowed here."));
+      return NULL;
+
+    case FH_REF_SCRATCH:
+      return make_any_reader (SCRATCH_FILE,
+                              scratch_reader_open (handle, dict));
+    }
+  abort ();
+}
+
+/* Reads a single case from READER into C.
+   Returns true if successful, false at end of file or on error. */
+bool
+any_reader_read (struct any_reader *reader, struct ccase *c) 
+{
+  switch (reader->type) 
+    {
+    case SYSTEM_FILE:
+      return sfm_read_case (reader->private, c);
+
+    case PORTABLE_FILE:
+      return pfm_read_case (reader->private, c);
+
+    case SCRATCH_FILE:
+      return scratch_reader_read_case (reader->private, c);
+    }
+  abort ();
+}
+
+/* Closes READER. */
+void
+any_reader_close (struct any_reader *reader) 
+{
+  if (reader == NULL)
+    return;
+
+  switch (reader->type) 
+    {
+    case SYSTEM_FILE:
+      sfm_close_reader (reader->private);
+      break;
+
+    case PORTABLE_FILE:
+      pfm_close_reader (reader->private);
+      break;
+
+    case SCRATCH_FILE:
+      scratch_reader_close (reader->private);
+      break;
+
+    default:
+      abort ();
+    }
+}
diff --git a/src/any-reader.h b/src/any-reader.h
new file mode 100644 (file)
index 0000000..d4f296e
--- /dev/null
@@ -0,0 +1,33 @@
+/* PSPP - computes sample statistics.
+   Copyright (C) 2006 Free Software Foundation, Inc.
+   Written by Ben Pfaff <blp@gnu.org>.
+
+   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 2 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, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301, USA. */
+
+#ifndef ANY_READER_H
+#define ANY_READER_H 1
+
+#include <stdbool.h>
+
+struct file_handle;
+struct dictionary;
+struct ccase;
+struct any_reader *any_reader_open (struct file_handle *,
+                                    struct dictionary **);
+bool any_reader_read (struct any_reader *, struct ccase *);
+void any_reader_close (struct any_reader *);
+
+#endif /* any-reader.h */
diff --git a/src/any-writer.c b/src/any-writer.c
new file mode 100644 (file)
index 0000000..048a720
--- /dev/null
@@ -0,0 +1,193 @@
+/* PSPP - computes sample statistics.
+   Copyright (C) 2006 Free Software Foundation, Inc.
+   Written by Ben Pfaff <blp@gnu.org>.
+
+   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 2 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, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301, USA. */
+
+#include <config.h>
+#include "any-writer.h"
+#include <assert.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "error.h"
+#include "file-handle-def.h"
+#include "filename.h"
+#include "pfm-write.h"
+#include "sfm-write.h"
+#include "str.h"
+#include "scratch-writer.h"
+#include "xalloc.h"
+
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
+
+/* Type of file backing an any_writer. */
+enum any_writer_type
+  {
+    SYSTEM_FILE,                /* System file. */
+    PORTABLE_FILE,              /* Portable file. */
+    SCRATCH_FILE                /* Scratch file. */
+  };
+
+/* Writer for any type of case-structured file. */
+struct any_writer 
+  {
+    enum any_writer_type type;  /* Type of file. */
+    void *private;              /* Private data. */
+  };
+
+/* Creates and returns a writer for HANDLE with the given DICT. */
+struct any_writer *
+any_writer_open (struct file_handle *handle, struct dictionary *dict)
+{
+  switch (fh_get_referent (handle)) 
+    {
+    case FH_REF_FILE:
+      {
+        struct any_writer *writer;
+        char *extension;
+
+        extension = fn_extension (fh_get_filename (handle));
+        str_lowercase (extension);
+
+        if (!strcmp (extension, ".por"))
+          writer = any_writer_from_pfm_writer (
+            pfm_open_writer (handle, dict, pfm_writer_default_options ()));
+        else
+          writer = any_writer_from_sfm_writer (
+            sfm_open_writer (handle, dict, sfm_writer_default_options ()));
+        free (extension);
+
+        return writer;
+      }
+
+    case FH_REF_INLINE:
+      msg (ME, _("The inline file is not allowed here."));
+      return NULL;
+
+    case FH_REF_SCRATCH:
+      return any_writer_from_scratch_writer (scratch_writer_open (handle,
+                                                                  dict));
+    }
+
+  abort ();
+}
+
+/* If PRIVATE is non-null, creates and returns a new any_writer,
+   initializing its fields to TYPE and PRIVATE.  If PRIVATE is a
+   null pointer, just returns a null pointer. */   
+static struct any_writer *
+make_any_writer (enum any_writer_type type, void *private) 
+{
+  if (private != NULL) 
+    {
+      struct any_writer *writer = xmalloc (sizeof *writer);
+      writer->type = type;
+      writer->private = private;
+      return writer; 
+    }
+  else
+    return NULL;
+}
+  
+/* If SFM_WRITER is non-null, encapsulates SFM_WRITER in an
+   any_writer and returns it.  If SFM_WRITER is null, just
+   returns a null pointer.
+
+   Useful when you need to pass options to sfm_open_writer().
+   Typical usage:
+        any_writer_from_sfm_writer (sfm_open_writer (fh, dict, opts))
+   If you don't need to pass options, then any_writer_open() by
+   itself is easier and more straightforward. */
+struct any_writer *
+any_writer_from_sfm_writer (struct sfm_writer *sfm_writer) 
+{
+  return make_any_writer (SYSTEM_FILE, sfm_writer);
+}
+
+/* If PFM_WRITER is non-null, encapsulates PFM_WRITER in an
+   any_writer and returns it.  If PFM_WRITER is null, just
+   returns a null pointer.
+
+   Useful when you need to pass options to pfm_open_writer().
+   Typical usage:
+        any_writer_from_pfm_writer (pfm_open_writer (fh, dict, opts))
+   If you don't need to pass options, then any_writer_open() by
+   itself is easier and more straightforward. */
+struct any_writer *
+any_writer_from_pfm_writer (struct pfm_writer *pfm_writer) 
+{
+  return make_any_writer (PORTABLE_FILE, pfm_writer);
+}
+
+/* If SCRATCH_WRITER is non-null, encapsulates SCRATCH_WRITER in
+   an any_writer and returns it.  If SCRATCH_WRITER is null, just
+   returns a null pointer.
+
+   Not particularly useful.  Included just for consistency. */
+struct any_writer *
+any_writer_from_scratch_writer (struct scratch_writer *scratch_writer) 
+{
+  return make_any_writer (SCRATCH_FILE, scratch_writer);
+}
+
+/* Writes cases C to WRITER.
+   Returns true if successful, false on failure. */
+bool
+any_writer_write (struct any_writer *writer, const struct ccase *c) 
+{
+  switch (writer->type) 
+    {
+    case SYSTEM_FILE:
+      return sfm_write_case (writer->private, c);
+
+    case PORTABLE_FILE:
+      return pfm_write_case (writer->private, c);
+
+    case SCRATCH_FILE:
+      scratch_writer_write_case (writer->private, c);
+      return true;
+    }
+  abort ();
+}
+
+/* Closes WRITER. */
+void
+any_writer_close (struct any_writer *writer) 
+{
+  if (writer == NULL)
+    return;
+
+  switch (writer->type) 
+    {
+    case SYSTEM_FILE:
+      sfm_close_writer (writer->private);
+      break;
+
+    case PORTABLE_FILE:
+      pfm_close_writer (writer->private);
+      break;
+
+    case SCRATCH_FILE:
+      scratch_writer_close (writer->private);
+      break;
+
+    default:
+      abort ();
+    }
+}
diff --git a/src/any-writer.h b/src/any-writer.h
new file mode 100644 (file)
index 0000000..9603b5e
--- /dev/null
@@ -0,0 +1,40 @@
+/* PSPP - computes sample statistics.
+   Copyright (C) 2006 Free Software Foundation, Inc.
+   Written by Ben Pfaff <blp@gnu.org>.
+
+   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 2 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, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301, USA. */
+
+#ifndef ANY_WRITER_H
+#define ANY_WRITER_H 1
+
+#include <stdbool.h>
+
+struct file_handle;
+struct dictionary;
+struct ccase;
+struct sfm_writer;
+struct pfm_writer;
+struct scratch_writer;
+
+struct any_writer *any_writer_open (struct file_handle *, struct dictionary *);
+struct any_writer *any_writer_from_sfm_writer (struct sfm_writer *);
+struct any_writer *any_writer_from_pfm_writer (struct pfm_writer *);
+struct any_writer *any_writer_from_scratch_writer (struct scratch_writer *);
+
+bool any_writer_write (struct any_writer *, const struct ccase *);
+void any_writer_close (struct any_writer *);
+
+#endif /* any-writer.h */
index 473daf0148981f3c8367b813071b1ad645063a94..ccfecb9eea5be63ef5617b09c52fcf3dac0d1da9 100644 (file)
 
 #include <config.h>
 #include <stdlib.h>
+#include "any-reader.h"
 #include "command.h"
 #include "dictionary.h"
 #include "error.h"
 #include "file-handle.h"
 #include "hash.h"
 #include "lexer.h"
-#include "sfm-read.h"
 #include "str.h"
 #include "value-labels.h"
 #include "var.h"
@@ -40,7 +40,7 @@ int
 cmd_apply_dictionary (void)
 {
   struct file_handle *handle;
-  struct sfm_reader *reader;
+  struct any_reader *reader;
   struct dictionary *dict;
 
   int n_matched = 0;
@@ -49,14 +49,14 @@ cmd_apply_dictionary (void)
   
   lex_match_id ("FROM");
   lex_match ('=');
-  handle = fh_parse ();
+  handle = fh_parse (FH_REF_FILE | FH_REF_SCRATCH);
   if (!handle)
     return CMD_FAILURE;
 
-  reader = sfm_open_reader (handle, &dict, NULL);
+  reader = any_reader_open (handle, &dict);
   if (dict == NULL)
     return CMD_FAILURE;
-  sfm_close_reader (reader);
+  any_reader_close (reader);
 
   for (i = 0; i < dict_get_var_cnt (dict); i++)
     {
@@ -163,7 +163,7 @@ cmd_apply_dictionary (void)
         dict_set_weight (default_dict, new_weight);
     }
   
-  sfm_close_reader (reader);
+  any_reader_close (reader);
 
   return lex_end_of_command ();
 }
index 47c4ba54bc8c8b3a795c2f09db7b8d6914796f8d..6912503537507f49f1c7b4aed9cbd630ae7ca717 100644 (file)
--- a/src/cat.h
+++ b/src/cat.h
@@ -53,4 +53,5 @@ struct cat_vals
                                           values stored.
                                         */
 };
+
 #endif
index a7f3cca8db5deec48f32e94ea6815fb2488e7b9f..737b7eedd09c35b7b9bb08a74f7960d8fa975272 100644 (file)
@@ -38,6 +38,7 @@ UNIMPL ("CASEPLOT",              ERRO, ERRO, ERRO, ERRO, "Plot time series")
 UNIMPL ("CASESTOVARS",           ERRO, ERRO, ERRO, ERRO, "Restructure complex data")
 UNIMPL ("CCF",                   ERRO, ERRO, ERRO, ERRO, "Time series cross correlation")
 DEFCMD ("CLEAR TRANSFORMATIONS",  ERRO, INPU, TRAN, TRAN, cmd_clear_transformations)
+DEFCMD ("CLOSE FILE HANDLE",      INIT, INPU, TRAN, PROC, cmd_close_file_handle)
 UNIMPL ("CLUSTER",               ERRO, ERRO, ERRO, ERRO, "Hierachial clustering")
 DEFCMD ("COMPUTE",                ERRO, INPU, TRAN, TRAN, cmd_compute)
 UNIMPL ("CONJOINT",              ERRO, ERRO, ERRO, ERRO, "Analyse full concept data")
index f190307d1e31c9ebbacb86d97c94733e5107a2fa..f5348748d50c95365e83c69fe2bb2ef00dd44c46 100644 (file)
@@ -137,7 +137,7 @@ cor_custom_matrix (struct cmd_correlations *cmd UNUSED)
     matrix_file = NULL;
   else 
     {
-      matrix_file = fh_parse ();
+      matrix_file = fh_parse (FH_REF_FILE);
       if (matrix_file == NULL)
         return 0; 
     }
index 7ac36a1a5b77a1f76ea63dc247bac95ea7f195d4..49fbf0d76a6a0df876bc7976a4b79b9581681c97 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - computes sample statistics.
-   Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc.
    Written by Ben Pfaff <blp@gnu.org>.
 
    This program is free software; you can redistribute it and/or
@@ -95,6 +95,8 @@ struct data_list_pgm
     size_t delim_cnt;           /* Number of delimiter, or 0 for spaces. */
   };
 
+static const struct case_source_class data_list_source_class;
+
 static int parse_fixed (struct data_list_pgm *);
 static int parse_free (struct dls_var_spec **, struct dls_var_spec **);
 static void dump_fixed_table (const struct dls_var_spec *,
@@ -111,9 +113,9 @@ static trns_proc_func data_list_trns_proc;
 int
 cmd_data_list (void)
 {
-  struct data_list_pgm *dls;     /* DATA LIST program under construction. */
+  struct data_list_pgm *dls;
   int table = -1;                /* Print table if nonzero, -1=undecided. */
-  struct file_handle *fh = NULL; /* File handle of source, NULL=inline file. */
+  struct file_handle *fh = fh_inline_file ();
 
   if (!case_source_is_complex (vfm_source))
     discard_variables ();
@@ -133,14 +135,14 @@ cmd_data_list (void)
       if (lex_match_id ("FILE"))
        {
          lex_match ('=');
-         fh = fh_parse ();
+         fh = fh_parse (FH_REF_FILE | FH_REF_INLINE);
          if (fh == NULL)
            goto error;
          if (case_source_is_class (vfm_source, &file_type_source_class)
-              && fh != default_handle)
+              && fh != fh_get_default_handle ())
            {
-             msg (SE, _("DATA LIST may not use a different file from "
-                        "that specified on its surrounding FILE TYPE."));
+             msg (SE, _("DATA LIST must use the same file "
+                        "as the enclosing FILE TYPE."));
              goto error;
            }
        }
@@ -235,7 +237,7 @@ cmd_data_list (void)
     }
 
   dls->case_size = dict_get_case_size (default_dict);
-  default_handle = fh;
+  fh_set_default_handle (fh);
 
   if (dls->type == -1)
     dls->type = DLS_FIXED;
@@ -795,15 +797,9 @@ dump_fixed_table (const struct dls_var_spec *specs,
                    fmt_to_string (&spec->input));
     }
 
-  if (fh != NULL)
-    tab_title (t, 1, ngettext ("Reading %d record from file %s.",
-                               "Reading %d records from file %s.", rec_cnt),
-               rec_cnt, fh_get_filename (fh));
-  else
-    tab_title (t, 1, ngettext ("Reading %d record from the command file.",
-                               "Reading %d records from the command file.",
-                               rec_cnt),
-               rec_cnt);
+  tab_title (t, 1, ngettext ("Reading %d record from %s.",
+                             "Reading %d records from %s.", rec_cnt),
+             rec_cnt, fh_get_name (fh));
   tab_submit (t);
 }
 \f
@@ -917,11 +913,7 @@ dump_free_table (const struct data_list_pgm *dls,
       }
   }
 
-  if (fh != NULL)
-    tab_title (t, 1, _("Reading free-form data from file %s."),
-               fh_get_filename (fh));
-  else
-    tab_title (t, 1, _("Reading free-form data from the command file."));
+  tab_title (t, 1, _("Reading free-form data from %s."), fh_get_name (fh));
   
   tab_submit (t);
 }
@@ -1309,7 +1301,7 @@ data_list_source_destroy (struct case_source *source)
   data_list_trns_free (source->aux);
 }
 
-const struct case_source_class data_list_source_class = 
+static const struct case_source_class data_list_source_class = 
   {
     "DATA LIST",
     NULL,
@@ -1367,12 +1359,12 @@ cmd_repeating_data (void)
   bool saw_length = false;      /* Saw LENGTH subcommand? */
   bool saw_continued = false;   /* Saw CONTINUED subcommand? */
   bool saw_id = false;          /* Saw ID subcommand? */
-  struct file_handle *const fh = default_handle;
+  struct file_handle *const fh = fh_get_default_handle ();
   
   assert (case_source_is_complex (vfm_source));
 
   rpd = xmalloc (sizeof *rpd);
-  rpd->reader = dfm_open_reader (default_handle);
+  rpd->reader = dfm_open_reader (fh);
   rpd->first = rpd->last = NULL;
   rpd->starts_beg.num = 0;
   rpd->starts_beg.var = NULL;
@@ -1390,7 +1382,7 @@ cmd_repeating_data (void)
        {
           struct file_handle *file;
          lex_match ('=');
-         file = fh_parse ();
+         file = fh_parse (FH_REF_FILE | FH_REF_INLINE);
          if (file == NULL)
            goto error;
          if (file != fh)
@@ -1585,7 +1577,7 @@ cmd_repeating_data (void)
   /* Calculate and check starts_end, cont_end if necessary. */
   if (rpd->starts_end.num == 0 && rpd->starts_end.var == NULL) 
     {
-      rpd->starts_end.num = fh != NULL ? fh_get_record_width (fh) : 80;
+      rpd->starts_end.num = fh_get_record_width (fh);
       if (rpd->starts_beg.num != 0 
           && rpd->starts_beg.num > rpd->starts_end.num)
         {
@@ -1598,7 +1590,7 @@ cmd_repeating_data (void)
     }
   if (rpd->cont_end.num == 0 && rpd->cont_end.var == NULL) 
     {
-      rpd->cont_end.num = fh != NULL ? fh_get_record_width (fh) : 80;
+      rpd->cont_end.num = fh_get_record_width (fh);
       if (rpd->cont_beg.num != 0
           && rpd->cont_beg.num > rpd->cont_end.num)
         {
index a739b5c32f1bd66d5cb531c1edf9a71738fd220d..6b3b3058c431338581968287f05bf770052aa986 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - computes sample statistics.
-   Copyright (C) 1997-2004 Free Software Foundation, Inc.
+   Copyright (C) 1997-2004, 2006 Free Software Foundation, Inc.
    Written by Ben Pfaff <blp@gnu.org>.
 
    This program is free software; you can redistribute it and/or
@@ -53,17 +53,14 @@ enum dfm_reader_flags
 struct dfm_reader
   {
     struct file_handle *fh;     /* File handle. */
-    struct file_ext file;      /* Associated file. */
     struct file_locator where;  /* Current location in data file. */
     struct string line;         /* Current line. */
-    size_t pos;                 /* Offset in line of current character. */
     struct string scratch;      /* Extra line buffer. */
     enum dfm_reader_flags flags; /* Zero or more of DFM_*. */
+    struct file_ext file;      /* Associated file. */
+    size_t pos;                 /* Offset in line of current character. */
   };
 
-static int inline_open_cnt;
-static struct dfm_reader *inline_file;
-
 static void read_record (struct dfm_reader *r);
 
 /* Closes reader R opened by dfm_open_reader(). */
@@ -71,81 +68,62 @@ void
 dfm_close_reader (struct dfm_reader *r)
 {
   int still_open;
+  bool is_inline;
 
   if (r == NULL)
     return;
 
-  if (r->fh != NULL) 
-    still_open = fh_close (r->fh, "data file", "rs");
-  else
-    {
-      assert (inline_open_cnt > 0);
-      still_open = --inline_open_cnt;
-
-      if (!still_open) 
-        {
-          /* Skip any remaining data on the inline file. */
-          if (r->flags & DFM_SAW_BEGIN_DATA)
-            while ((r->flags & DFM_EOF) == 0)
-              read_record (r);
-          inline_file = NULL;
-        }
-    }
+  is_inline = r->fh == fh_inline_file ();
+  still_open = fh_close (r->fh, "data file", "rs");
   if (still_open)
     return;
 
-  if (r->fh != NULL && r->file.file)
+  if (!is_inline)
     {
       fn_close_ext (&r->file);
       free (r->file.filename);
       r->file.filename = NULL;
     }
+  else
+    {
+      /* Skip any remaining data on the inline file. */
+      if (r->flags & DFM_SAW_BEGIN_DATA)
+        while ((r->flags & DFM_EOF) == 0)
+          read_record (r);
+    }
+
   ds_destroy (&r->line);
   ds_destroy (&r->scratch);
   free (r);
 }
 
 /* Opens the file designated by file handle FH for reading as a
-   data file.  Providing a null pointer for FH designates the
+   data file.  Providing fh_inline_file() for FH designates the
    "inline file", that is, data included inline in the command
-   file between BEGIN FILE and END FILE.  Returns nonzero only if
-   successful. */
+   file between BEGIN FILE and END FILE.  Returns a reader if
+   successful, or a null pointer otherwise. */
 struct dfm_reader *
 dfm_open_reader (struct file_handle *fh)
 {
   struct dfm_reader *r;
   void **rp;
 
-  if (fh != NULL) 
-    {
-      rp = fh_open (fh, "data file", "rs");
-      if (rp == NULL)
-        return NULL;
-      if (*rp != NULL)
-        return *rp; 
-    }
-  else 
-    {
-      assert (inline_open_cnt >= 0);
-      if (inline_open_cnt++ > 0)
-        return inline_file;
-      rp = NULL;
-    }
+  rp = fh_open (fh, FH_REF_FILE | FH_REF_INLINE, "data file", "rs");
+  if (rp == NULL)
+    return NULL;
+  if (*rp != NULL)
+    return *rp; 
   
   r = xmalloc (sizeof *r);
   r->fh = fh;
-  if (fh != NULL) 
-    {
-      r->where.filename = fh_get_filename (fh);
-      r->where.line_number = 0; 
-    }
-  r->file.file = NULL;
   ds_init (&r->line, 64);
   ds_init (&r->scratch, 0);
   r->flags = DFM_ADVANCE;
-
-  if (fh != NULL)
+  if (fh != fh_inline_file ()) 
     {
+      r->where.filename = fh_get_filename (fh);
+      r->where.line_number = 0; 
+      r->file.file = NULL;
       r->file.filename = xstrdup (fh_get_filename (r->fh));
       r->file.mode = "rb";
       r->file.file = NULL;
@@ -154,24 +132,23 @@ dfm_open_reader (struct file_handle *fh)
       r->file.postopen = NULL;
       r->file.preclose = NULL;
       if (!fn_open_ext (&r->file))
-       {
-         msg (ME, _("Could not open \"%s\" for reading "
-                     "as a data file: %s."),
+        {
+          msg (ME, _("Could not open \"%s\" for reading as a data file: %s."),
                fh_get_filename (r->fh), strerror (errno));
           err_cond_fail ();
           fh_close (fh,"data file", "rs");
           free (r);
           return NULL;
-       }
-      *rp = r;
+        }
     }
-  else
-    inline_file = r;
+  *rp = r;
 
   return r;
 }
 
-static int
+/* Reads a record from the inline file into R.
+   Returns true if successful, false on failure. */
+static bool
 read_inline_record (struct dfm_reader *r)
 {
   if ((r->flags & DFM_SAW_BEGIN_DATA) == 0)
@@ -208,7 +185,7 @@ read_inline_record (struct dfm_reader *r)
         {
           msg (SE, _("BEGIN DATA expected."));
           lex_preprocess_line ();
-          return 0;
+          return false;
         }
       getl_prompt = GETL_PRPT_DATA;
     }
@@ -223,25 +200,24 @@ read_inline_record (struct dfm_reader *r)
       err_failure ();
     }
 
-  if (r->fh != NULL)
-    r->where.line_number++;
-
   if (ds_length (&getl_buf) >= 8
       && !strncasecmp (ds_c_str (&getl_buf), "end data", 8))
     {
       lex_set_prog (ds_c_str (&getl_buf) + ds_length (&getl_buf));
-      return 0;
+      return false;
     }
 
   ds_replace (&r->line, ds_c_str (&getl_buf));
-  return 1;
+  return true;
 }
 
-static int
+/* Reads a record from a disk file into R.
+   Returns true if successful, false on failure. */
+static bool
 read_file_record (struct dfm_reader *r)
 {
-  assert (r->fh != NULL);
-  if (fh_get_mode (r->fh) == MODE_TEXT)
+  assert (r->fh != fh_inline_file ());
+  if (fh_get_mode (r->fh) == FH_MODE_TEXT)
     {
       ds_clear (&r->line);
       if (!ds_gets (&r->line, r->file.file)) 
@@ -252,10 +228,10 @@ read_file_record (struct dfm_reader *r)
                    fh_get_name (r->fh), strerror (errno));
               err_cond_fail ();
             }
-          return 0;
+          return false;
         }
     }
-  else if (fh_get_mode (r->fh) == MODE_BINARY)
+  else if (fh_get_mode (r->fh) == FH_MODE_BINARY)
     {
       size_t record_width = fh_get_record_width (r->fh);
       size_t amt;
@@ -274,10 +250,10 @@ read_file_record (struct dfm_reader *r)
             msg (ME, _("%s: Partial record at end of file."),
                  fh_get_name (r->fh));
           else
-            return 0;
+            return false;
 
           err_cond_fail ();
-          return 0;
+          return false;
         }
     }
   else
@@ -285,7 +261,7 @@ read_file_record (struct dfm_reader *r)
 
   r->where.line_number++;
 
-  return 1;
+  return true;
 }
 
 /* Reads a record from R, setting the current position to the
@@ -294,7 +270,13 @@ read_file_record (struct dfm_reader *r)
 static void
 read_record (struct dfm_reader *r)
 {
-  int success = r->fh != NULL ? read_file_record (r) : read_inline_record (r);
+  bool success;
+
+  if (fh_get_referent (r->fh) == FH_REF_FILE)
+    success = read_file_record (r);
+  else
+    success = read_inline_record (r);
+  
   if (success)
     r->pos = 0;
   else
@@ -313,7 +295,7 @@ dfm_eof (struct dfm_reader *r)
         read_record (r);
       else
         {
-          if (r->fh != NULL)
+          if (r->fh != fh_inline_file ())
             msg (SE, _("Attempt to read beyond end-of-file on file %s."),
                  fh_get_name (r->fh));
           else
@@ -359,15 +341,15 @@ dfm_expand_tabs (struct dfm_reader *r)
     return;
   r->flags |= DFM_TABS_EXPANDED;
 
-  if (r->fh != NULL
-      && (fh_get_mode (r->fh) == MODE_BINARY
+  if (r->fh != fh_inline_file ()
+      && (fh_get_mode (r->fh) == FH_MODE_BINARY
           || fh_get_tab_width (r->fh) == 0
           || memchr (ds_c_str (&r->line), '\t', ds_length (&r->line)) == NULL))
     return;
 
   /* Expand tabs from r->line into r->scratch, and figure out
      new value for r->pos. */
-  tab_width = r->fh != NULL ? fh_get_tab_width (r->fh) : 8;
+  tab_width = fh_get_tab_width (r->fh);
   ds_clear (&r->scratch);
   new_pos = 0;
   for (ofs = 0; ofs < ds_length (&r->line); ofs++)
@@ -439,7 +421,7 @@ dfm_column_start (struct dfm_reader *r)
 void
 dfm_push (struct dfm_reader *r)
 {
-  if (r->fh != NULL)
+  if (r->fh != fh_inline_file ())
     err_push_file_locator (&r->where);
 }
 
@@ -447,7 +429,7 @@ dfm_push (struct dfm_reader *r)
 void
 dfm_pop (struct dfm_reader *r)
 {
-  if (r->fh != NULL)
+  if (r->fh != fh_inline_file ())
     err_pop_file_locator (&r->where);
 }
 \f
@@ -459,10 +441,7 @@ cmd_begin_data (void)
 {
   struct dfm_reader *r;
 
-  /* FIXME: figure out the *exact* conditions, not these really
-     lenient conditions. */
-  if (vfm_source == NULL
-      || case_source_is_class (vfm_source, &storage_source_class))
+  if (!fh_is_open (fh_inline_file ()))
     {
       msg (SE, _("This command is not valid here since the current "
                  "input program does not access the inline file."));
@@ -471,7 +450,7 @@ cmd_begin_data (void)
     }
 
   /* Open inline file. */
-  r = dfm_open_reader (NULL);
+  r = dfm_open_reader (fh_inline_file ());
   r->flags |= DFM_SAW_BEGIN_DATA;
 
   /* Input procedure reads from inline file. */
index 465e04ebd2e69435c941232e08f8a11b8eecbd11..39aa7e387933b767bf4e13219e262f7ef0d0803b 100644 (file)
@@ -46,7 +46,7 @@ dfm_open_writer (struct file_handle *fh)
   struct dfm_writer *w;
   void **aux;
   
-  aux = fh_open (fh, "data file", "ws");
+  aux = fh_open (fh, FH_REF_FILE, "data file", "ws");
   if (aux == NULL)
     return NULL;
   if (*aux != NULL)
@@ -89,7 +89,7 @@ dfm_put_record (struct dfm_writer *w, const char *rec, size_t len)
 {
   assert (w != NULL);
 
-  if (fh_get_mode (w->fh) == MODE_BINARY
+  if (fh_get_mode (w->fh) == FH_MODE_BINARY
       && len < fh_get_record_width (w->fh))
     {
       size_t rec_width = fh_get_record_width (w->fh);
index f916fd0c3fa10219c2600279de622ac2ff721bce..c11f6f4124036b380b572390adf0b496c990ef60 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - computes sample statistics.
-   Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc.
    Written by Ben Pfaff <blp@gnu.org>.
 
    This program is free software; you can redistribute it and/or
@@ -25,6 +25,7 @@
 #include "alloc.h"
 #include "case.h"
 #include "cat.h"
+#include "cat-routines.h"
 #include "error.h"
 #include "hash.h"
 #include "misc.h"
@@ -406,8 +407,9 @@ dict_lookup_var_assert (const struct dictionary *d, const char *name)
   return v;
 }
 
-/* Returns nonzero if variable V is in dictionary D. */
-int
+/* Returns true if variable V is in dictionary D,
+   false otherwise. */
+bool
 dict_contains_var (const struct dictionary *d, const struct variable *v)
 {
   assert (d != NULL);
@@ -593,18 +595,18 @@ dict_rename_var (struct dictionary *d, struct variable *v,
 
 /* Renames COUNT variables specified in VARS to the names given
    in NEW_NAMES within dictionary D.  If the renaming would
-   result in a duplicate variable name, returns zero and stores a
+   result in a duplicate variable name, returns false and stores a
    name that would be duplicated into *ERR_NAME (if ERR_NAME is
-   non-null).  Otherwise, the renaming is successful, and nonzero
+   non-null).  Otherwise, the renaming is successful, and true
    is returned. */
-int
+bool
 dict_rename_vars (struct dictionary *d,
                   struct variable **vars, char **new_names,
                   size_t count, char **err_name) 
 {
   char **old_names;
   size_t i;
-  int success = 1;
+  bool success = true;
 
   assert (d != NULL);
   assert (count == 0 || vars != NULL);
@@ -649,7 +651,7 @@ dict_rename_vars (struct dictionary *d,
               hsh_force_insert (d->name_tab, vars[i]);
             }
 
-          success = 0;
+          success = false;
           goto done;
         }
     }
@@ -807,30 +809,6 @@ dict_compact_values (struct dictionary *d)
     }
 }
 
-/* Copies values from SRC, which represents a case arranged
-   according to dictionary D, to DST, which represents a case
-   arranged according to the dictionary that will be produced by
-   dict_compact_values(D). */
-void
-dict_compact_case (const struct dictionary *d,
-                   struct ccase *dst, const struct ccase *src)
-{
-  size_t i;
-  size_t value_idx;
-
-  value_idx = 0;
-  for (i = 0; i < d->var_cnt; i++) 
-    {
-      struct variable *v = d->var[i];
-
-      if (dict_class_from_id (v->name) != DC_SCRATCH)
-        {
-          case_copy (dst, value_idx, src, v->fv, v->nv);
-          value_idx += v->nv;
-        }
-    }
-}
-
 /* Returns the number of values that would be used by a case if
    dict_compact_values() were called. */
 size_t
@@ -874,6 +852,112 @@ dict_get_compacted_idx_to_fv (const struct dictionary *d)
   return idx_to_fv;
 }
 
+/* Returns true if a case for dictionary D would be smaller after
+   compaction, false otherwise.  Compacting a case eliminates
+   "holes" between values and after the last value.  Holes are
+   created by deleting variables (or by scratch variables).
+
+   The return value may differ from whether compacting a case
+   from dictionary D would *change* the case: compaction could
+   rearrange values even if it didn't reduce space
+   requirements. */
+bool
+dict_needs_compaction (const struct dictionary *d) 
+{
+  return dict_get_compacted_value_cnt (d) < dict_get_next_value_idx (d);
+}
+\f
+/* How to copy a contiguous range of values between cases. */
+struct copy_map
+  {
+    size_t src_idx;             /* Starting value index in source case. */
+    size_t dst_idx;             /* Starting value index in target case. */
+    size_t cnt;                 /* Number of values. */
+  };
+
+/* How to compact a case. */
+struct dict_compactor 
+  {
+    struct copy_map *maps;      /* Array of mappings. */
+    size_t map_cnt;             /* Number of mappings. */
+  };
+
+/* Creates and returns a dict_compactor that can be used to
+   compact cases for dictionary D.
+
+   Compacting a case eliminates "holes" between values and after
+   the last value.  Holes are created by deleting variables (or
+   by scratch variables). */
+struct dict_compactor *
+dict_make_compactor (const struct dictionary *d)
+{
+  struct dict_compactor *compactor;
+  struct copy_map *map;
+  size_t map_allocated;
+  size_t value_idx;
+  size_t i;
+
+  compactor = xmalloc (sizeof *compactor);
+  compactor->maps = NULL;
+  compactor->map_cnt = 0;
+  map_allocated = 0;
+
+  value_idx = 0;
+  map = NULL;
+  for (i = 0; i < d->var_cnt; i++) 
+    {
+      struct variable *v = d->var[i];
+
+      if (dict_class_from_id (v->name) == DC_SCRATCH)
+        continue;
+      if (map != NULL && map->src_idx + map->cnt == v->fv) 
+        map->cnt += v->nv;
+      else 
+        {
+          if (compactor->map_cnt == map_allocated)
+            compactor->maps = x2nrealloc (compactor->maps, &map_allocated,
+                                          sizeof *compactor->maps);
+          map = &compactor->maps[compactor->map_cnt++];
+          map->src_idx = v->fv;
+          map->dst_idx = value_idx;
+          map->cnt = v->nv;
+        }
+      value_idx += v->nv;
+    }
+
+  return compactor;
+}
+
+/* Compacts SRC by copying it to DST according to the scheme in
+   COMPACTOR.
+
+   Compacting a case eliminates "holes" between values and after
+   the last value.  Holes are created by deleting variables (or
+   by scratch variables). */
+void
+dict_compactor_compact (const struct dict_compactor *compactor,
+                        struct ccase *dst, const struct ccase *src) 
+{
+  size_t i;
+
+  for (i = 0; i < compactor->map_cnt; i++) 
+    {
+      const struct copy_map *map = &compactor->maps[i];
+      case_copy (dst, map->dst_idx, src, map->src_idx, map->cnt);
+    }
+}
+
+/* Destroys COMPACTOR. */
+void
+dict_compactor_destroy (struct dict_compactor *compactor) 
+{
+  if (compactor != NULL) 
+    {
+      free (compactor->maps);
+      free (compactor);
+    }
+}
+
 /* Returns the SPLIT FILE vars (see cmd_split_file()).  Call
    dict_get_split_cnt() to determine how many SPLIT FILE vars
    there are.  Returns a null pointer if and only if there are no
@@ -963,9 +1047,9 @@ dict_set_documents (struct dictionary *d, const char *documents)
 }
 
 /* Creates in D a vector named NAME that contains CNT variables
-   VAR (see cmd_vector()).  Returns nonzero if successful, or
-   zero if a vector named NAME already exists in D. */
-int
+   VAR (see cmd_vector()).  Returns true if successful, or
+   false if a vector named NAME already exists in D. */
+bool
 dict_create_vector (struct dictionary *d,
                     const char *name,
                     struct variable **var, size_t cnt) 
@@ -980,7 +1064,7 @@ dict_create_vector (struct dictionary *d,
   assert (cnt > 0);
   
   if (dict_lookup_vector (d, name) != NULL)
-    return 0;
+    return false;
 
   d->vector = xnrealloc (d->vector, d->vector_cnt + 1, sizeof *d->vector);
   vector = d->vector[d->vector_cnt] = xmalloc (sizeof *vector);
@@ -994,7 +1078,7 @@ dict_create_vector (struct dictionary *d,
     }
   vector->cnt = cnt;
   
-  return 1;
+  return true;
 }
 
 /* Returns the vector in D with index IDX, which must be less
index e923d52dc252d3a25debe74007e083a463c36f75..d02877141cab080ca53e87c48979cc5d72eda187 100644 (file)
@@ -20,6 +20,7 @@
 #ifndef DICTIONARY_H
 #define DICTIONARY_H
 
+#include <stdbool.h>
 #include <stddef.h>
 
 /* Dictionary. */ 
@@ -50,7 +51,7 @@ struct variable *dict_clone_var_assert (struct dictionary *,
 struct variable *dict_lookup_var (const struct dictionary *, const char *);
 struct variable *dict_lookup_var_assert (const struct dictionary *,
                                          const char *);
-int dict_contains_var (const struct dictionary *, const struct variable *);
+bool dict_contains_var (const struct dictionary *, const struct variable *);
 void dict_delete_var (struct dictionary *, struct variable *);
 void dict_delete_vars (struct dictionary *,
                        struct variable *const *, size_t count);
@@ -60,9 +61,9 @@ void dict_reorder_var (struct dictionary *d, struct variable *v,
 void dict_reorder_vars (struct dictionary *,
                         struct variable *const *, size_t count);
 void dict_rename_var (struct dictionary *, struct variable *, const char *);
-int dict_rename_vars (struct dictionary *,
-                      struct variable **, char **new_names,
-                      size_t count, char **err_name);
+bool dict_rename_vars (struct dictionary *,
+                       struct variable **, char **new_names,
+                       size_t count, char **err_name);
 
 struct ccase;
 struct variable *dict_get_weight (const struct dictionary *);
@@ -80,10 +81,14 @@ int dict_get_next_value_idx (const struct dictionary *);
 size_t dict_get_case_size (const struct dictionary *);
 
 void dict_compact_values (struct dictionary *);
-void dict_compact_case (const struct dictionary *,
-                        struct ccase *, const struct ccase *);
 size_t dict_get_compacted_value_cnt (const struct dictionary *);
 int *dict_get_compacted_idx_to_fv (const struct dictionary *);
+bool dict_needs_compaction (const struct dictionary *);
+
+struct dict_compactor *dict_make_compactor (const struct dictionary *);
+void dict_compactor_compact (const struct dict_compactor *,
+                             struct ccase *, const struct ccase *);
+void dict_compactor_destroy (struct dict_compactor *);
 
 struct variable *const *dict_get_split_vars (const struct dictionary *);
 size_t dict_get_split_cnt (const struct dictionary *);
@@ -96,9 +101,9 @@ void dict_set_label (struct dictionary *, const char *);
 const char *dict_get_documents (const struct dictionary *);
 void dict_set_documents (struct dictionary *, const char *);
 
-int dict_create_vector (struct dictionary *,
-                        const char *name,
-                        struct variable **, size_t cnt);
+bool dict_create_vector (struct dictionary *,
+                         const char *name,
+                         struct variable **, size_t cnt);
 const struct vector *dict_get_vector (const struct dictionary *,
                                       size_t idx);
 size_t dict_get_vector_cnt (const struct dictionary *);
index 1213b1a3c526369f545a009ea61467245667dc78..3a77c8722217868ac07e212df8c5aaaa83efd69e 100644 (file)
@@ -31,6 +31,7 @@
 #include "main.h"
 #include "output.h"
 #include "progname.h"
+#include "readln.h"
 #include "settings.h"
 #include "str.h"
 #include "var.h"
index 605d1dd942a248f0fdc3bdda53ade1228ed79c15..3be48250e85c3b35dfef883acc4b03a6443e7ba6 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - computes sample statistics.
-   Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc.
    Written by Ben Pfaff <blp@gnu.org>.
 
    This program is free software; you can redistribute it and/or
    02110-1301, USA. */
 
 #include <config.h>
-#include "file-handle.h"
 #include "file-handle-def.h"
 #include "error.h"
 #include <errno.h>
 #include <stdlib.h>
 #include <string.h>
 #include "alloc.h"
+#include "file-handle.h"
 #include "filename.h"
 #include "command.h"
 #include "getl.h"
 #include "error.h"
 #include "magic.h"
 #include "var.h"
-#include "file-handle-def.h"
+#include "scratch-handle.h"
 
 #include "gettext.h"
-
 #define _(msgid) gettext (msgid)
 
 /* (headers) */
 struct file_handle 
   {
     struct file_handle *next;   /* Next in global list. */
+    int open_cnt;               /* 0=not open, otherwise # of openers. */
+    bool deleted;               /* Destroy handle when open_cnt goes to 0? */
+
     char *name;                 /* File handle identifier. */
+    const char *type;           /* If open, type of file. */
+    char open_mode[3];          /* "[rw][se]". */
+    void *aux;                  /* Aux data pointer for owner if any. */
+    enum fh_referent referent;  /* What the file handle refers to. */
+
+    /* FH_REF_FILE only. */
     char *filename;            /* Filename as provided by user. */
     struct file_identity *identity; /* For checking file identity. */
-    struct file_locator where; /* Used for reporting error messages. */
     enum fh_mode mode;         /* File mode. */
+
+    /* FH_REF_FILE and FH_REF_INLINE only. */
     size_t record_width;        /* Length of fixed-format records. */
     size_t tab_width;           /* Tab width, 0=do not expand tabs. */
 
-    int open_cnt;               /* 0=not open, otherwise # of openers. */
-    const char *type;           /* If open, type of file. */
-    char open_mode[3];          /* "[rw][se]". */
-    void *aux;                  /* Aux data pointer for owner if any. */
+    /* FH_REF_SCRATCH only. */
+    struct scratch_handle *sh;  /* Scratch file data. */
   };
 
+/* List of all handles. */
 static struct file_handle *file_handles;
 
+/* Default file handle for DATA LIST, REREAD, REPEATING DATA
+   commands. */
+static struct file_handle *default_handle;
+
+/* The "file" that reads from BEGIN DATA...END DATA. */
+static struct file_handle *inline_file;
+
+static struct file_handle *create_handle (const char *name, enum fh_referent);
+
 /* File handle initialization routine. */
 void 
 fh_init (void)
 {
-  /* Currently nothing to do. */
+  inline_file = create_handle ("INLINE", FH_REF_INLINE);
+  inline_file->record_width = 80;
+  inline_file->tab_width = 8;
 }
 
-
-/* Destroy file handle.
-   Normally needed only if a file_handle needs to be re-assigned.
-   Otherwise, just let fh_done clean destroy the handle.
- */
-void 
-fh_free(struct file_handle *fh)
+/* Free HANDLE and remove it from the global list. */
+static void
+free_handle (struct file_handle *handle) 
 {
-  if ( !fh->name ) 
-    return ;
-
-  free (fh->name);
-  fh->name = 0;
-  free (fh->filename);
-  fn_free_identity (fh->identity);
-  free (fh);
-}
+  /* Remove handle from global list. */
+  if (file_handles == handle)
+    file_handles = handle->next;
+  else 
+    {
+      struct file_handle *iter = file_handles;
+      while (iter->next != handle)
+        iter = iter->next;
+      iter->next = handle->next;
+    }
 
+  /* Free data. */
+  free (handle->name);
+  free (handle->filename);
+  fn_free_identity (handle->identity);
+  scratch_handle_destroy (handle->sh);
+  free (handle);
+}
 
 /* Frees all the file handles. */
 void 
 fh_done (void)
 {
-  struct file_handle *fh, *next;
-  
-  for (fh = file_handles; fh != NULL; fh = next)
-    {
-      next = fh->next;
-      fh_free(fh);
-    }
-  file_handles = NULL;
+  while (file_handles != NULL) 
+    free_handle (file_handles);
 }
 
 /* Returns the handle named HANDLE_NAME, or a null pointer if
@@ -107,7 +124,7 @@ fh_from_name (const char *handle_name)
   struct file_handle *iter;
 
   for (iter = file_handles; iter != NULL; iter = iter->next)
-    if (!strcasecmp (handle_name, iter->name))
+    if (!iter->deleted && !strcasecmp (handle_name, iter->name))
       return iter;
   return NULL;
 }
@@ -127,7 +144,9 @@ fh_from_filename (const char *filename)
   if (identity != NULL) 
     {
       for (iter = file_handles; iter != NULL; iter = iter->next)
-        if (iter->identity != NULL
+        if (!iter->deleted
+            && iter->referent == FH_REF_FILE
+            && iter->identity != NULL
             && !fn_compare_file_identities (identity, iter->identity))
           {
             fn_free_identity (identity);
@@ -138,40 +157,69 @@ fh_from_filename (const char *filename)
 
   /* Then check for a file with the same name. */
   for (iter = file_handles; iter != NULL; iter = iter->next)
-    if (!strcmp (filename, iter->filename))
+    if (!iter->deleted
+        && iter->referent == FH_REF_FILE && !strcmp (filename, iter->filename))
       return iter; 
 
   return NULL;
 }
 
-/* Creates and returns a new file handle with the given values
-   and defaults for other values.  Adds the created file handle
-   to the global list. */
-struct file_handle *
-fh_create (const char *handle_name, const char *filename,
-           const struct fh_properties *properties)
-{
-  struct file_handle *handle;
-
-  assert(filename);
-  assert(handle_name);
+/* Creates a new handle with name HANDLE_NAME that refers to
+   REFERENT.  Links the new handle into the global list.  Returns
+   the new handle.
 
-  /* Create and initialize file handle. */
-  handle = xmalloc (sizeof *handle);
+   The new handle is not fully initialized.  The caller is
+   responsible for completing its initialization. */
+static struct file_handle *
+create_handle (const char *handle_name, enum fh_referent referent) 
+{
+  struct file_handle *handle = xzalloc (sizeof *handle);
   handle->next = file_handles;
+  handle->open_cnt = 0;
+  handle->deleted = false;
   handle->name = xstrdup (handle_name);
+  handle->type = NULL;
+  handle->aux = NULL;
+  handle->referent = referent;
+  file_handles = handle;
+  return handle;
+}
+
+/* Returns the unique handle of referent type FH_REF_INLINE,
+   which refers to the "inline file" that represents character
+   data in the command file between BEGIN DATA and END DATA. */
+struct file_handle *
+fh_inline_file (void) 
+{
+  return inline_file;
+}
+
+/* Creates a new file handle named HANDLE_NAME, which must not be
+   the name of an existing file handle.  The new handle is
+   associated with file FILENAME and the given PROPERTIES. */
+struct file_handle *
+fh_create_file (const char *handle_name, const char *filename,
+                const struct fh_properties *properties)
+{
+  struct file_handle *handle;
+  assert (fh_from_name (handle_name) == NULL);
+  handle = create_handle (handle_name, FH_REF_FILE);
   handle->filename = xstrdup (filename);
   handle->identity = fn_get_identity (filename);
-  handle->where.filename = handle->filename;
-  handle->where.line_number = 0;
   handle->mode = properties->mode;
   handle->record_width = properties->record_width;
   handle->tab_width = properties->tab_width;
-  handle->open_cnt = 0;
-  handle->type = NULL;
-  handle->aux = NULL;
-  file_handles = handle;
+  return handle;
+}
 
+/* Creates a new file handle named HANDLE_NAME, which must not be
+   the name of an existing file handle.  The new handle is
+   associated with a scratch file (initially empty). */
+struct file_handle *
+fh_create_scratch (const char *handle_name) 
+{
+  struct file_handle *handle = create_handle (handle_name, FH_REF_SCRATCH);
+  handle->sh = NULL;
   return handle;
 }
 
@@ -179,10 +227,31 @@ fh_create (const char *handle_name, const char *filename,
 const struct fh_properties *
 fh_default_properties (void)
 {
-  static const struct fh_properties default_properties = {MODE_TEXT, 1024, 4};
+  static const struct fh_properties default_properties
+    = {FH_MODE_TEXT, 1024, 4};
   return &default_properties;
 }
 
+/* Deletes FH from the global list of file handles.  Afterward,
+   attempts to search for it will fail.  Unless the file handle
+   is currently open, it will be destroyed; otherwise, it will be
+   destroyed later when it is closed.
+   Normally needed only if a file_handle needs to be re-assigned.
+   Otherwise, just let fh_done() destroy the handle. */
+void 
+fh_free (struct file_handle *handle)
+{
+  if (handle == fh_inline_file () || handle == NULL || handle->deleted)
+    return;
+  handle->deleted = true;
+
+  if (handle == default_handle)
+    default_handle = fh_inline_file ();
+
+  if (handle->open_cnt == 0)
+    free_handle (handle);
+}
+
 /* Returns an English description of MODE,
    which is in the format of the MODE argument to fh_open(). */
 static const char *
@@ -196,6 +265,9 @@ mode_name (const char *mode)
 
 /* Tries to open handle H with the given TYPE and MODE.
 
+   H's referent type must be one of the bits in MASK.  The caller
+   must verify this ahead of time; we simply assert it here.
+
    TYPE is the sort of file, e.g. "system file".  Only one given
    type of access is allowed on a given file handle at once.
    If successful, a reference to TYPE is retained, so it should
@@ -213,9 +285,11 @@ mode_name (const char *mode)
    modes the void * will necessarily be null only if no other
    sharers are active. */
 void **
-fh_open (struct file_handle *h, const char *type, const char *mode) 
+fh_open (struct file_handle *h, enum fh_referent mask UNUSED,
+         const char *type, const char *mode) 
 {
   assert (h != NULL);
+  assert ((fh_get_referent (h) & mask) != 0);
   assert (type != NULL);
   assert (mode != NULL);
   assert (mode[0] == 'r' || mode[0] == 'w');
@@ -227,21 +301,21 @@ fh_open (struct file_handle *h, const char *type, const char *mode)
       if (strcmp (h->type, type)) 
         {
           msg (SE, _("Can't open %s as a %s because it is "
-                     "already open as a %s"),
+                     "already open as a %s."),
                fh_get_name (h), type, h->type);
           return NULL; 
         }
       else if (strcmp (h->open_mode, mode)) 
         {
           msg (SE, _("Can't open %s as a %s for %s because it is "
-                     "already open for %s"),
+                     "already open for %s."),
                fh_get_name (h), type, mode_name (mode),
                mode_name (h->open_mode));
           return NULL;
         }
       else if (h->open_mode[1] == 'e')
         {
-          msg (SE, _("Can't re-open %s as a %s for %s"),
+          msg (SE, _("Can't re-open %s as a %s for %s."),
                fh_get_name (h), type, mode_name (mode));
           return NULL;
         }
@@ -260,7 +334,11 @@ fh_open (struct file_handle *h, const char *type, const char *mode)
 /* Closes file handle H, which must have been open for the
    specified TYPE and MODE of access provided to fh_open().
    Returns zero if the file is now closed, nonzero if it is still
-   open due to another reference. */
+   open due to another reference.
+
+   After fh_close() returns zero for a handle, it is unsafe to
+   reference that file handle again in any way, because its
+   storage may have been freed. */
 int
 fh_close (struct file_handle *h, const char *type, const char *mode)
 {
@@ -271,13 +349,23 @@ fh_close (struct file_handle *h, const char *type, const char *mode)
   assert (mode != NULL);
   assert (!strcmp (mode, h->open_mode));
 
-  h->open_cnt--;
-  if (h->open_cnt == 0) 
+  if (--h->open_cnt == 0) 
     {
       h->type = NULL;
       h->aux = NULL;
+      if (h->deleted)
+        free_handle (h);
+      return 0;
     }
-  return h->open_cnt;
+  return 1;
+}
+
+/* Is the file open?  BEGIN DATA...END DATA uses this to detect
+   whether the inline file is actually in use. */
+bool
+fh_is_open (const struct file_handle *handle) 
+{
+  return handle->open_cnt > 0;
 }
 
 /* Returns the identifier of file HANDLE.  If HANDLE was created
@@ -289,15 +377,21 @@ fh_close (struct file_handle *h, const char *type, const char *mode)
 const char *
 fh_get_name (const struct file_handle *handle)
 {
-  assert (handle != NULL);
   return handle->name;
 }
 
+/* Returns the type of object that HANDLE refers to. */
+enum fh_referent
+fh_get_referent (const struct file_handle *handle) 
+{
+  return handle->referent;
+}
+
 /* Returns the name of the file associated with HANDLE. */
 const char *
 fh_get_filename (const struct file_handle *handle) 
 {
-  assert (handle != NULL);
+  assert (handle->referent == FH_REF_FILE);
   return handle->filename;
 }
 
@@ -305,7 +399,7 @@ fh_get_filename (const struct file_handle *handle)
 enum fh_mode
 fh_get_mode (const struct file_handle *handle) 
 {
-  assert (handle != NULL);
+  assert (handle->referent == FH_REF_FILE);
   return handle->mode;
 }
 
@@ -313,16 +407,50 @@ fh_get_mode (const struct file_handle *handle)
 size_t
 fh_get_record_width (const struct file_handle *handle)
 {
-  assert (handle != NULL);
+  assert (handle->referent & (FH_REF_FILE | FH_REF_INLINE));
   return handle->record_width;
 }
 
 /* Returns the number of characters per tab stop for HANDLE, or
    zero if tabs are not to be expanded.  Applicable only to
-   MODE_TEXT files. */
+   FH_MODE_TEXT files. */
 size_t
 fh_get_tab_width (const struct file_handle *handle) 
 {
-  assert (handle != NULL);
+  assert (handle->referent & (FH_REF_FILE | FH_REF_INLINE));
   return handle->tab_width;
 }
+
+/* Returns the scratch file handle associated with HANDLE.
+   Applicable to only FH_REF_SCRATCH files. */
+struct scratch_handle *
+fh_get_scratch_handle (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;
+}
+
+/* Returns the current default handle. */
+struct file_handle *
+fh_get_default_handle (void) 
+{
+  return default_handle ? default_handle : fh_inline_file ();
+}
+
+/* Sets NEW_DEFAULT_HANDLE as the default handle. */
+void
+fh_set_default_handle (struct file_handle *new_default_handle) 
+{
+  assert (new_default_handle == NULL
+          || (new_default_handle->referent & (FH_REF_INLINE | FH_REF_FILE)));
+  default_handle = new_default_handle;
+}
index b4ca8e27d69bb32909b2521d2c069bc2c997212d..c5c61ea865a34cc67e8ea56aa473872d31f3fac0 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - computes sample statistics.
-   Copyright (C) 1997-9, 2000,2005 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 2005, 2006 Free Software Foundation, Inc.
    Written by Ben Pfaff <blp@gnu.org>.
 
    This program is free software; you can redistribute it and/or
 #ifndef FILE_HANDLE_DEF_H
 #define FILE_HANDLE_DEF_H
 
-#include <config.h>
+#include <stdbool.h>
+#include <stddef.h>
+
+/* What a file handle refers to.
+   (Ordinarily only a single value is allowed, but fh_open()
+   and fh_parse() take a mask.) */
+enum fh_referent
+  {
+    FH_REF_FILE = 001,          /* Ordinary file (the most common case). */
+    FH_REF_INLINE = 002,        /* The inline file. */
+    FH_REF_SCRATCH = 004        /* Temporary dataset. */
+  };
 
 /* File modes. */
 enum fh_mode
   {
-    MODE_TEXT,                  /* New-line delimited lines. */
-    MODE_BINARY                 /* Fixed-length records. */
+    FH_MODE_TEXT,               /* New-line delimited lines. */
+    FH_MODE_BINARY              /* Fixed-length records. */
   };
 
 /* Properties of a file handle. */
@@ -41,27 +52,45 @@ void fh_init (void);
 void fh_done (void);
 
 /* Creating file handles. */
-struct file_handle *fh_create (const char *handle_name, 
-                               const char *filename,
-                               const struct fh_properties *);
-/* Destroy file handle */
-void fh_free(struct file_handle *);
-
+struct file_handle *fh_create_file (const char *handle_name,
+                                    const char *filename,
+                                    const struct fh_properties *);
+struct file_handle *fh_create_scratch (const char *handle_name);
 const struct fh_properties *fh_default_properties (void);
 
-/* Finding file handles, based on handle name or filename. */
+/* Delete file handle from global list. */
+void fh_free (struct file_handle *);
+
+/* Finding file handles. */
 struct file_handle *fh_from_name (const char *handle_name);
 struct file_handle *fh_from_filename (const char *filename);
+struct file_handle *fh_inline_file (void);
 
-/* Querying properties of file handles. */
+/* Generic properties of file handles. */
 const char *fh_get_name (const struct file_handle *);
+enum fh_referent fh_get_referent (const struct file_handle *);
+
+/* Properties of FH_REF_FILE file handles. */
 const char *fh_get_filename (const struct file_handle *);
 enum fh_mode fh_get_mode (const struct file_handle *) ;
+
+/* Properties of FH_REF_FILE and FH_REF_INLINE file handles. */
 size_t fh_get_record_width (const struct file_handle *);
 size_t fh_get_tab_width (const struct file_handle *);
 
+/* Properties of FH_REF_SCRATCH file handles. */
+struct scratch_handle *fh_get_scratch_handle (struct file_handle *);
+void fh_set_scratch_handle (struct file_handle *, struct scratch_handle *);
+
 /* Opening and closing file handles. */
-void **fh_open (struct file_handle *, const char *type, const char *mode);
+void **fh_open (struct file_handle *, enum fh_referent mask,
+                const char *type, const char *mode);
 int fh_close (struct file_handle *, const char *type, const char *mode);
+bool fh_is_open (const struct file_handle *);
+
+/* Default file handle for DATA LIST, REREAD, REPEATING DATA
+   commands. */
+struct file_handle *fh_get_default_handle (void);
+void fh_set_default_handle (struct file_handle *);
 
 #endif
index 52cf8f121f6f395202458fdd6e96ac6b70033e29..2e8de05ab2f774c840b9ab09cd80a9f16449b5f3 100644 (file)
 
 /* File handles. */
 
+#include <stdbool.h>
 #include <stddef.h>
 #include "file-handle-def.h"
 
-struct file_handle *fh_parse (void);
+struct file_handle *fh_parse (enum fh_referent);
 
 #endif /* !file_handle.h */
index 0eb1373f7da832f699a42c4202d21ac0cdc4e06d..c13be76beda44a320979200adb2418a6f04a6044 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - computes sample statistics.
-   Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc.
    Written by Ben Pfaff <blp@gnu.org>.
 
    This program is free software; you can redistribute it and/or
@@ -29,6 +29,7 @@
 #include "getl.h"
 #include "error.h"
 #include "magic.h"
+#include "str.h"
 #include "var.h"
 #include "linked-list.h"
 #include "file-handle-def.h"
@@ -44,7 +45,7 @@
      name=string;
      lrecl=integer;
      tabwidth=integer "x>=0" "%s must be nonnegative";
-     mode=mode:!character/image.
+     mode=mode:!character/image/scratch.
 */
 /* (declarations) */
 /* (functions) */
@@ -65,9 +66,9 @@ cmd_file_handle (void)
   handle = fh_from_name (handle_name);
   if (handle != NULL)
     {
-      msg (SE, _("File handle %s already refers to file %s.  "
-                 "File handles cannot be redefined within a session."),
-          handle_name, fh_get_filename(handle));
+      msg (SE, _("File handle %s is already defined.  "
+                 "Use CLOSE FILE HANDLE before redefining a file handle."),
+          handle_name);
       return CMD_FAILURE;
     }
 
@@ -78,28 +79,24 @@ cmd_file_handle (void)
   if (!parse_file_handle (&cmd))
     return CMD_FAILURE;
 
-  if (token != '.')
-    {
-      lex_error (_("expecting end of command"));
-      goto lossage;
-    }
+  if (lex_end_of_command () != CMD_SUCCESS)
+    goto lossage;
 
-  if (cmd.s_name == NULL)
+  if (cmd.s_name == NULL && cmd.mode != FH_SCRATCH)
     {
-      msg (SE, _("The FILE HANDLE required subcommand NAME "
-                "is not present."));
+      lex_sbc_missing ("NAME");
       goto lossage;
     }
 
   switch (cmd.mode)
     {
     case FH_CHARACTER:
-      properties.mode = MODE_TEXT;
+      properties.mode = FH_MODE_TEXT;
       if (cmd.sbc_tabwidth)
         properties.tab_width = cmd.n_tabwidth[0];
       break;
     case FH_IMAGE:
-      properties.mode = MODE_BINARY;
+      properties.mode = FH_MODE_BINARY;
       if (cmd.n_lrecl[0] == NOT_LONG)
         msg (SE, _("Fixed-length records were specified on /RECFORM, but "
                    "record length was not specified on /LRECL.  "
@@ -116,7 +113,10 @@ cmd_file_handle (void)
       assert (0);
     }
 
-  handle = fh_create (handle_name, cmd.s_name, &properties);
+  if (cmd.mode != FH_SCRATCH)
+    fh_create_file (handle_name, cmd.s_name, &properties);
+  else
+    fh_create_scratch (handle_name);
 
   free_file_handle (&cmd);
   return CMD_SUCCESS;
@@ -126,36 +126,85 @@ cmd_file_handle (void)
   return CMD_FAILURE;
 }
 
-/* Parses a file handle name, which may be a filename as a string or
-   a file handle name as an identifier.  Returns the file handle or
-   NULL on failure. */
-struct file_handle *
-fh_parse (void)
+int
+cmd_close_file_handle (void) 
 {
   struct file_handle *handle;
 
-  if (token != T_ID && token != T_STRING)
+  if (!lex_force_id ())
+    return CMD_FAILURE;
+  handle = fh_from_name (tokid);
+  if (handle == NULL)
+    return CMD_FAILURE;
+
+  fh_free (handle);
+
+  return CMD_SUCCESS;
+}
+
+/* Returns the name for REFERENT. */
+static const char *
+referent_name (enum fh_referent referent) 
+{
+  switch (referent) 
     {
-      lex_error (_("expecting a file name or handle name"));
-      return NULL;
+    case FH_REF_FILE:
+      return _("file");
+    case FH_REF_INLINE:
+      return _("inline file");
+    case FH_REF_SCRATCH:
+      return _("scratch file");
+    default:
+      abort ();
     }
+}
 
-  /* Check for named handles first, then go by filename. */
-  handle = NULL;
-  if (token == T_ID) 
-    handle = fh_from_name (tokid);
-  if (handle == NULL)
-    handle = fh_from_filename (ds_c_str (&tokstr));
-  if (handle == NULL) 
+/* Parses a file handle name, which may be a filename 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. */
+struct file_handle *
+fh_parse (enum fh_referent referent_mask)
+{
+  struct file_handle *handle;
+
+  if (lex_match_id ("INLINE")) 
+    handle = fh_inline_file ();
+  else 
     {
-      char *filename = ds_c_str (&tokstr);
-      char *handle_name = xmalloc (strlen (filename) + 3);
-      sprintf (handle_name, "\"%s\"", filename);
-      handle = fh_create (handle_name, filename, fh_default_properties ());
-      free (handle_name);
+      if (token != T_ID && token != T_STRING)
+        {
+          lex_error (_("expecting a file name or handle name"));
+          return NULL;
+        }
+
+      handle = NULL;
+      if (token == T_ID) 
+        handle = fh_from_name (tokid);
+      if (handle == NULL) 
+        handle = fh_from_filename (ds_c_str (&tokstr)); 
+      if (handle == NULL)
+        {
+          if (token != T_ID || tokid[0] != '#' || get_syntax () != ENHANCED) 
+            {
+              char *filename = ds_c_str (&tokstr);
+              char *handle_name = xasprintf ("\"%s\"", filename);
+              handle = fh_create_file (handle_name, filename,
+                                       fh_default_properties ());
+              free (handle_name);
+            }
+          else
+            handle = fh_create_scratch (tokid);
+        }
+      lex_get ();
     }
 
-  lex_get ();
+  if (!(fh_get_referent (handle) & referent_mask)) 
+    {
+      msg (SE, _("Handle for %s not allowed here."),
+           referent_name (fh_get_referent (handle)));
+      return NULL;
+    }
 
   return handle;
 }
index ff97f8dbbfbfd868c7adcd72c10672fb3a32bf1b..4c7c4a03075931aaa0bcf25f7b2e0b4e88f8cf15 100644 (file)
@@ -103,7 +103,7 @@ int
 cmd_file_type (void)
 {
   static struct file_type_pgm *fty;     /* FIXME: static? WTF? */
-  struct file_handle *fh = NULL;
+  struct file_handle *fh = fh_inline_file ();
 
   /* Initialize. */
   discard_variables ();
@@ -139,7 +139,7 @@ cmd_file_type (void)
       if (lex_match_id ("FILE"))
        {
          lex_match ('=');
-         fh = fh_parse ();
+         fh = fh_parse (FH_REF_FILE | FH_REF_INLINE);
          if (fh == NULL)
            goto error;
        }
@@ -279,7 +279,7 @@ cmd_file_type (void)
   fty->reader = dfm_open_reader (fh);
   if (fty->reader == NULL)
     goto error;
-  default_handle = fh;
+  fh_set_default_handle (fh);
 
   create_col_var (&fty->record);
   if (fty->case_sbc.name[0])
index b78897e211bc2be19fec1e01e62e177bcce69e51..0c085776f5b889aefc23dfc9fb8174273bcc759e 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - computes sample statistics.
-   Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc.
    Written by Ben Pfaff <blp@gnu.org>.
 
    This program is free software; you can redistribute it and/or
@@ -530,6 +530,18 @@ fn_basename (const char *filename)
   abort ();
 }
 #endif
+
+/* Returns the extension part of FILENAME as a malloc()'d string.
+   If FILENAME does not have an extension, returns an empty
+   string. */
+char *
+fn_extension (const char *filename) 
+{
+  const char *extension = strrchr (filename, '.');
+  if (extension == NULL)
+    extension = "";
+  return xstrdup (extension);
+}
 \f
 #if unix
 /* Returns the current working directory, as a malloc()'d string.
index 3da1374d956304f611e064a7620d516afccf88c1..46cdd7c532ba82a67815f5fca4dddf9ea3e03db3 100644 (file)
@@ -35,6 +35,7 @@ char *fn_prepend_dir (const char *filename, const char *directory);
 char *fn_normalize (const char *fn);
 char *fn_dirname (const char *fn);
 char *fn_basename (const char *fn);
+char *fn_extension (const char *fn);
 
 char *fn_get_cwd (void);
 
index 0b3c131c8d8d777a0cc6a6309ef358ced53da1a6..deda2d57b6c6fd29de6e72a70d49575360178912 100644 (file)
--- a/src/get.c
+++ b/src/get.c
@@ -1,5 +1,5 @@
 /* PSPP - computes sample statistics.
-   Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc.
    Written by Ben Pfaff <blp@gnu.org>.
 
    This program is free software; you can redistribute it and/or
@@ -21,6 +21,8 @@
 #include "error.h"
 #include <stdlib.h>
 #include "alloc.h"
+#include "any-reader.h"
+#include "any-writer.h"
 #include "case.h"
 #include "command.h"
 #include "dictionary.h"
 #include "hash.h"
 #include "lexer.h"
 #include "misc.h"
-#include "pfm-read.h"
 #include "pfm-write.h"
 #include "settings.h"
-#include "sfm-read.h"
 #include "sfm-write.h"
 #include "str.h"
 #include "value-labels.h"
@@ -52,116 +52,147 @@ static void map_case (const struct case_map *,
                       const struct ccase *, struct ccase *);
 static void destroy_case_map (struct case_map *);
 
-/* Operation type. */
-enum operation 
+static bool parse_dict_trim (struct dictionary *);
+\f
+/* Reading system and portable files. */
+
+/* Type of command. */
+enum reader_command 
   {
-    OP_READ,    /* GET or IMPORT. */
-    OP_SAVE,    /* SAVE or XSAVE. */
-    OP_EXPORT   /* EXPORT. */
+    GET_CMD,
+    IMPORT_CMD
   };
 
-static bool parse_dict_trim (struct dictionary *);
-\f
-/* GET input program. */
-struct get_pgm 
+/* Case reader input program. */
+struct case_reader_pgm 
   {
-    struct sfm_reader *reader;  /* System file reader. */
-    struct case_map *map;       /* Map from system file to active file dict. */
+    struct any_reader *reader;  /* File reader. */
+    struct case_map *map;       /* Map from file dict to active file dict. */
     struct ccase bounce;        /* Bounce buffer. */
   };
 
-static void get_pgm_free (struct get_pgm *);
+static const struct case_source_class case_reader_source_class;
 
-/* Parses the GET command. */
-int
-cmd_get (void)
+static void case_reader_pgm_free (struct case_reader_pgm *);
+
+/* Parses a GET or IMPORT command. */
+static int
+parse_read_command (enum reader_command type)
 {
-  struct get_pgm *pgm = NULL;
-  struct file_handle *fh;
+  struct case_reader_pgm *pgm = NULL;
+  struct file_handle *fh = NULL;
   struct dictionary *dict = NULL;
 
-  pgm = xmalloc (sizeof *pgm);
-  pgm->reader = NULL;
-  pgm->map = NULL;
-  case_nullify (&pgm->bounce);
+  for (;;)
+    {
+      lex_match ('/');
 
-  discard_variables ();
+      if (lex_match_id ("FILE") || token == T_STRING)
+       {
+         lex_match ('=');
 
-  lex_match ('/');
-  if (lex_match_id ("FILE"))
-    lex_match ('=');
-  fh = fh_parse ();
-  if (fh == NULL)
-    goto error;
+         fh = fh_parse (FH_REF_FILE | FH_REF_SCRATCH);
+         if (fh == NULL)
+            goto error;
+       }
+      else if (type == IMPORT_CMD && lex_match_id ("TYPE"))
+       {
+         lex_match ('=');
 
-  pgm->reader = sfm_open_reader (fh, &dict, NULL);
+         if (lex_match_id ("COMM"))
+           type = PFM_COMM;
+         else if (lex_match_id ("TAPE"))
+           type = PFM_TAPE;
+         else
+           {
+             lex_error (_("expecting COMM or TAPE"));
+              goto error;
+           }
+       }
+      else
+        break; 
+    }
+  
+  if (fh == NULL) 
+    {
+      lex_sbc_missing ("FILE");
+      goto error;
+    }
+              
+  discard_variables ();
+
+  pgm = xmalloc (sizeof *pgm);
+  pgm->reader = any_reader_open (fh, &dict);
+  pgm->map = NULL;
+  case_nullify (&pgm->bounce);
   if (pgm->reader == NULL)
     goto error;
-  case_create (&pgm->bounce, dict_get_next_value_idx (dict));
 
+  case_create (&pgm->bounce, dict_get_next_value_idx (dict));
+  
   start_case_map (dict);
-  while (lex_match ('/'))
-    if (!parse_dict_trim (dict))
-      goto error;
 
-  if (!lex_end_of_command ())
-    return false;
+  while (token != '.')
+    {
+      lex_match ('/');
+      if (!parse_dict_trim (dict))
+        goto error;
+    }
 
-  dict_compact_values (dict);
   pgm->map = finish_case_map (dict);
-
+  
   dict_destroy (default_dict);
   default_dict = dict;
 
-  vfm_source = create_case_source (&get_source_class, pgm);
+  vfm_source = create_case_source (&case_reader_source_class, pgm);
 
   return CMD_SUCCESS;
 
  error:
-  get_pgm_free (pgm);
-  if (dict != NULL) 
+  case_reader_pgm_free (pgm);
+  if (dict != NULL)
     dict_destroy (dict);
   return CMD_FAILURE;
 }
 
-/* Frees a struct get_pgm. */
+/* Frees a struct case_reader_pgm. */
 static void
-get_pgm_free (struct get_pgm *pgm) 
+case_reader_pgm_free (struct case_reader_pgm *pgm) 
 {
   if (pgm != NULL) 
     {
-      sfm_close_reader (pgm->reader);
+      any_reader_close (pgm->reader);
       destroy_case_map (pgm->map);
       case_destroy (&pgm->bounce);
       free (pgm);
     }
 }
 
-/* Clears internal state related to GET input procedure. */
+/* Clears internal state related to case reader input procedure. */
 static void
-get_source_destroy (struct case_source *source)
+case_reader_source_destroy (struct case_source *source)
 {
-  struct get_pgm *pgm = source->aux;
-  get_pgm_free (pgm);
+  struct case_reader_pgm *pgm = source->aux;
+  case_reader_pgm_free (pgm);
 }
 
 /* Reads all the cases from the data file into C and passes them
    to WRITE_CASE one by one, passing WC_DATA. */
 static void
-get_source_read (struct case_source *source,
-                 struct ccase *c,
-                 write_case_func *write_case, write_case_data wc_data)
+case_reader_source_read (struct case_source *source,
+                    struct ccase *c,
+                    write_case_func *write_case, write_case_data wc_data)
 {
-  struct get_pgm *pgm = source->aux;
+  struct case_reader_pgm *pgm = source->aux;
   int ok;
 
   do
     {
       if (pgm->map == NULL)
-        ok = sfm_read_case (pgm->reader, c);
+        ok = any_reader_read (pgm->reader, c);
       else
         {
-          ok = sfm_read_case (pgm->reader, &pgm->bounce);
+          ok = any_reader_read (pgm->reader, &pgm->bounce);
           if (ok)
             map_case (pgm->map, &pgm->bounce, c);
         }
@@ -172,14 +203,30 @@ get_source_read (struct case_source *source,
   while (ok);
 }
 
-const struct case_source_class get_source_class =
+static const struct case_source_class case_reader_source_class =
   {
-    "GET",
+    "case reader",
     NULL,
-    get_source_read,
-    get_source_destroy,
+    case_reader_source_read,
+    case_reader_source_destroy,
   };
 \f
+/* GET. */
+int
+cmd_get (void) 
+{
+  return parse_read_command (GET_CMD);
+}
+
+/* IMPORT. */
+int
+cmd_import (void) 
+{
+  return parse_read_command (IMPORT_CMD);
+}
+\f
+/* Writing system and portable files. */ 
+
 /* Type of output file. */
 enum writer_type
   {
@@ -194,11 +241,10 @@ enum command_type
     PROC_CMD            /* Procedure. */
   };
 
-/* Portable or system file writer plus a case map. */
-struct any_writer
+/* File writer plus a case map. */
+struct case_writer
   {
-    enum writer_type writer_type;
-    void *writer;
+    struct any_writer *writer;  /* File writer. */
     struct case_map *map;       /* Map to output file dictionary
                                    (null pointer for identity mapping). */
     struct ccase bounce;        /* Bounce buffer for mapping (if needed). */
@@ -206,19 +252,11 @@ struct any_writer
 
 /* Destroys AW. */
 static void
-any_writer_destroy (struct any_writer *aw)
+case_writer_destroy (struct case_writer *aw)
 {
   if (aw != NULL) 
     {
-      switch (aw->writer_type) 
-        {
-        case PORFILE_WRITER:
-          pfm_close_writer (aw->writer);
-          break;
-        case SYSFILE_WRITER:
-          sfm_close_writer (aw->writer);
-          break;
-        }
+      any_writer_close (aw->writer);
       destroy_case_map (aw->map);
       case_destroy (&aw->bounce);
       free (aw);
@@ -235,7 +273,7 @@ any_writer_destroy (struct any_writer *aw)
    included.
 
    On failure, returns a null pointer. */
-static struct any_writer *
+static struct case_writer *
 parse_write_command (enum writer_type writer_type,
                      enum command_type command_type,
                      bool *retain_unselected)
@@ -243,7 +281,7 @@ parse_write_command (enum writer_type writer_type,
   /* Common data. */
   struct file_handle *handle; /* Output file. */
   struct dictionary *dict;    /* Dictionary for output file. */
-  struct any_writer *aw;      /* Writer. */  
+  struct case_writer *aw;      /* Writer. */  
 
   /* Common options. */
   bool print_map;             /* Print map?  TODO. */
@@ -261,7 +299,6 @@ parse_write_command (enum writer_type writer_type,
   handle = NULL;
   dict = dict_clone (default_dict);
   aw = xmalloc (sizeof *aw);
-  aw->writer_type = writer_type;
   aw->writer = NULL;
   aw->map = NULL;
   case_nullify (&aw->bounce);
@@ -286,7 +323,7 @@ parse_write_command (enum writer_type writer_type,
           
          lex_match ('=');
       
-         handle = fh_parse ();
+         handle = fh_parse (FH_REF_FILE | FH_REF_SCRATCH);
          if (handle == NULL)
            goto error;
        }
@@ -374,45 +411,42 @@ parse_write_command (enum writer_type writer_type,
   if (aw->map != NULL)
     case_create (&aw->bounce, dict_get_next_value_idx (dict));
 
-  switch (writer_type
+  if (fh_get_referent (handle) == FH_REF_FILE
     {
-    case SYSFILE_WRITER:
-      aw->writer = sfm_open_writer (handle, dict, sysfile_opts);
-      break;
-    case PORFILE_WRITER:
-      aw->writer = pfm_open_writer (handle, dict, porfile_opts);
-      break;
+      switch (writer_type) 
+        {
+        case SYSFILE_WRITER:
+          aw->writer = any_writer_from_sfm_writer (
+            sfm_open_writer (handle, dict, sysfile_opts));
+          break;
+        case PORFILE_WRITER:
+          aw->writer = any_writer_from_pfm_writer (
+            pfm_open_writer (handle, dict, porfile_opts));
+          break;
+        }
     }
-
+  else
+    aw->writer = any_writer_open (handle, dict);
   dict_destroy (dict);
   
   return aw;
 
  error:
-  any_writer_destroy (aw);
+  case_writer_destroy (aw);
   dict_destroy (dict);
   return NULL;
 }
 
 /* Writes case C to writer AW. */
 static void
-any_writer_write_case (struct any_writer *aw, struct ccase *c) 
+case_writer_write_case (struct case_writer *aw, struct ccase *c) 
 {
   if (aw->map != NULL) 
     {
       map_case (aw->map, c, &aw->bounce);
       c = &aw->bounce; 
     }
-  
-  switch (aw->writer_type) 
-    {
-    case SYSFILE_WRITER:
-      sfm_write_case (aw->writer, c);
-      break;
-    case PORFILE_WRITER:
-      pfm_write_case (aw->writer, c);
-      break;
-    }
+  any_writer_write (aw->writer, c);
 }
 \f
 /* SAVE and EXPORT. */
@@ -425,7 +459,7 @@ parse_output_proc (enum writer_type writer_type)
 {
   bool retain_unselected;
   struct variable *saved_filter_variable;
-  struct any_writer *aw;
+  struct case_writer *aw;
 
   aw = parse_write_command (writer_type, PROC_CMD, &retain_unselected);
   if (aw == NULL) 
@@ -437,7 +471,7 @@ parse_output_proc (enum writer_type writer_type)
   procedure (output_proc, aw);
   dict_set_filter (default_dict, saved_filter_variable);
 
-  any_writer_destroy (aw);
+  case_writer_destroy (aw);
   return CMD_SUCCESS;
 }
 
@@ -445,8 +479,8 @@ parse_output_proc (enum writer_type writer_type)
 static int
 output_proc (struct ccase *c, void *aw_) 
 {
-  struct any_writer *aw = aw_;
-  any_writer_write_case (aw, c);
+  struct case_writer *aw = aw_;
+  case_writer_write_case (aw, c);
   return 0;
 }
 
@@ -467,7 +501,7 @@ cmd_export (void)
 /* Transformation. */
 struct output_trns 
   {
-    struct any_writer *aw;      /* Writer. */
+    struct case_writer *aw;      /* Writer. */
   };
 
 static trns_proc_func output_trns_proc;
@@ -494,7 +528,7 @@ static int
 output_trns_proc (void *trns_, struct ccase *c, int case_num UNUSED)
 {
   struct output_trns *t = trns_;
-  any_writer_write_case (t->aw, c);
+  case_writer_write_case (t->aw, c);
   return -1;
 }
 
@@ -506,7 +540,7 @@ output_trns_free (void *trns_)
 
   if (t != NULL)
     {
-      any_writer_destroy (t->aw);
+      case_writer_destroy (t->aw);
       free (t);
     }
 }
@@ -709,7 +743,7 @@ struct mtf_file
     int type;                  /* One of MTF_*. */
     struct variable **by;      /* List of BY variables for this file. */
     struct file_handle *handle; /* File handle. */
-    struct sfm_reader *reader;  /* System file reader. */
+    struct any_reader *reader;  /* File reader. */
     struct dictionary *dict;   /* Dictionary from system file. */
 
     /* IN subcommand. */
@@ -859,11 +893,11 @@ cmd_match_files (void)
         }
       else
         {
-          file->handle = fh_parse ();
+          file->handle = fh_parse (FH_REF_FILE | FH_REF_SCRATCH);
           if (file->handle == NULL)
             goto error;
 
-          file->reader = sfm_open_reader (file->handle, &file->dict, NULL);
+          file->reader = any_reader_open (file->handle, &file->dict);
           if (file->reader == NULL)
             goto error;
 
@@ -1151,7 +1185,7 @@ static void
 mtf_free_file (struct mtf_file *file)
 {
   free (file->by);
-  sfm_close_reader (file->reader);
+  any_reader_close (file->reader);
   if (file->dict != default_dict)
     dict_destroy (file->dict);
   case_destroy (&file->input);
@@ -1225,7 +1259,7 @@ mtf_read_nonactive_records (void *mtf_)
   for (iter = mtf->head; iter != NULL; iter = next)
     {
       next = iter->next;
-      if (iter->handle && !sfm_read_case (iter->reader, &iter->input))
+      if (iter->handle && !any_reader_read (iter->reader, &iter->input))
         mtf_delete_file_in_place (mtf, &iter);
     }
 }
@@ -1323,7 +1357,7 @@ mtf_processing (struct ccase *c, void *mtf_)
                 {
                   if (iter->handle == NULL)
                     return 1;
-                  if (sfm_read_case (iter->reader, &iter->input))
+                  if (any_reader_read (iter->reader, &iter->input))
                     continue;
                   mtf_delete_file_in_place (mtf, &iter);
                 }
@@ -1405,7 +1439,7 @@ mtf_processing (struct ccase *c, void *mtf_)
        {
          next = iter->next_min;
          if (iter->reader != NULL
-              && !sfm_read_case (iter->reader, &iter->input))
+              && !any_reader_read (iter->reader, &iter->input))
             mtf_delete_file_in_place (mtf, &iter);
        }
     }
@@ -1500,162 +1534,6 @@ get_master (struct variable *v)
   return v->aux;
 }
 \f
-/* IMPORT command. */
-
-/* IMPORT input program. */
-struct import_pgm 
-  {
-    struct pfm_reader *reader;  /* Portable file reader. */
-    struct case_map *map;       /* Map from system file to active file dict. */
-    struct ccase bounce;        /* Bounce buffer. */
-  };
-
-static void import_pgm_free (struct import_pgm *);
-
-/* Parses the IMPORT command. */
-int
-cmd_import (void)
-{
-  struct import_pgm *pgm = NULL;
-  struct file_handle *fh = NULL;
-  struct dictionary *dict = NULL;
-  enum pfm_type type;
-
-  lex_match ('/');
-  for (;;)
-    {
-      if (pgm == NULL && (lex_match_id ("FILE") || token == T_STRING))
-       {
-         lex_match ('=');
-
-         fh = fh_parse ();
-         if (fh == NULL)
-            goto error;
-       }
-      else if (pgm == NULL && lex_match_id ("TYPE"))
-       {
-         lex_match ('=');
-
-         if (lex_match_id ("COMM"))
-           type = PFM_COMM;
-         else if (lex_match_id ("TAPE"))
-           type = PFM_TAPE;
-         else
-           {
-             lex_error (_("expecting COMM or TAPE"));
-              goto error;
-           }
-       }
-      else 
-        {
-          if (pgm == NULL) 
-            {
-              if (fh == NULL) 
-                {
-                  lex_sbc_missing ("FILE");
-                  goto error;
-                }
-              
-              discard_variables ();
-
-              pgm = xmalloc (sizeof *pgm);
-              pgm->reader = pfm_open_reader (fh, &dict, NULL);
-              pgm->map = NULL;
-              case_nullify (&pgm->bounce);
-              if (pgm->reader == NULL)
-                goto error;
-
-              case_create (&pgm->bounce, dict_get_next_value_idx (dict));
-  
-              start_case_map (dict);
-            }
-
-          if (token == '.')
-            break;
-          
-          if (!parse_dict_trim (dict))
-            goto error;
-        }
-
-      lex_match ('/');
-    }
-  if (pgm == NULL) 
-    {
-      lex_error (NULL);
-      goto error;
-    }
-
-  pgm->map = finish_case_map (dict);
-  
-  dict_destroy (default_dict);
-  default_dict = dict;
-
-  vfm_source = create_case_source (&import_source_class, pgm);
-
-  return CMD_SUCCESS;
-
- error:
-  import_pgm_free (pgm);
-  if (dict != NULL)
-    dict_destroy (dict);
-  return CMD_FAILURE;
-}
-
-/* Frees a struct import_pgm. */
-static void
-import_pgm_free (struct import_pgm *pgm) 
-{
-  if (pgm != NULL) 
-    {
-      pfm_close_reader (pgm->reader);
-      destroy_case_map (pgm->map);
-      case_destroy (&pgm->bounce);
-      free (pgm);
-    }
-}
-
-/* Clears internal state related to IMPORT input procedure. */
-static void
-import_source_destroy (struct case_source *source)
-{
-  struct import_pgm *pgm = source->aux;
-  import_pgm_free (pgm);
-}
-
-/* Reads all the cases from the data file into C and passes them
-   to WRITE_CASE one by one, passing WC_DATA. */
-static void
-import_source_read (struct case_source *source,
-                 struct ccase *c,
-                 write_case_func *write_case, write_case_data wc_data)
-{
-  struct import_pgm *pgm = source->aux;
-  int ok;
-
-  do
-    {
-      if (pgm->map == NULL)
-        ok = pfm_read_case (pgm->reader, c);
-      else
-        {
-          ok = pfm_read_case (pgm->reader, &pgm->bounce);
-          if (ok)
-            map_case (pgm->map, &pgm->bounce, c);
-        }
-
-      if (ok)
-        ok = write_case (wc_data);
-    }
-  while (ok);
-}
-
-const struct case_source_class import_source_class =
-  {
-    "IMPORT",
-    NULL,
-    import_source_read,
-    import_source_destroy,
-  };
 
 \f
 /* Case map.
index c0b3c905ad3e345f45e582beb97db4467907cdae..12e173d1feb0a533dcd42a5d15bc0c693314f706 100644 (file)
@@ -31,8 +31,6 @@ struct transformation *t_trns;
 size_t n_trns, m_trns, f_trns;
 
 int FILTER_before_TEMPORARY;
-
-struct file_handle *default_handle;
 \f
 /* Functions. */
 
index eaa19532eaef4b80ecf2921dafaaa3b0efde03e0..855b4ecaf82d62036fa6ac165c03c8d334418f59 100644 (file)
@@ -317,7 +317,7 @@ cmd_reread (void)
   struct expression *e;         /* Expression for column to set. */
   struct reread_trns *t;        /* Created transformation. */
 
-  fh = default_handle;
+  fh = fh_get_default_handle ();
   e = NULL;
   while (token != '.')
     {
@@ -339,7 +339,7 @@ cmd_reread (void)
       else if (lex_match_id ("FILE"))
        {
          lex_match ('=');
-          fh = fh_parse ();
+          fh = fh_parse (FH_REF_FILE | FH_REF_INLINE);
          if (fh == NULL)
            {
              expr_free (e);
index ee43d82f2ba2df94bc8cc94b9cd4119e5d8213ad..994285cd98d06d79c48603c7fed4dc30ebe1e3e8 100644 (file)
@@ -172,7 +172,7 @@ cmd_matrix_data (void)
 {
   struct pool *pool;
   struct matrix_data_pgm *mx;
-  struct file_handle *fh = NULL;
+  struct file_handle *fh = fh_inline_file ();
     
   unsigned seen = 0;
   
@@ -258,7 +258,7 @@ cmd_matrix_data (void)
       else if (lex_match_id ("FILE"))
        {
          lex_match ('=');
-         fh = fh_parse ();
+         fh = fh_parse (FH_REF_FILE | FH_REF_INLINE);
          if (fh == NULL)
            goto lossage;
        }
index 3dd939ddae25dc1564eefba3fb410e09bbeb8416..f398747584ae9b7e48c5ffeee25bebfb66581fc1 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - computes sample statistics.
-   Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc.
    Written by Ben Pfaff <blp@gnu.org>.
    Code for parsing floating-point numbers adapted from GNU C
    library.
 
 #include "debug-print.h"
 
+/* portable_to_local[PORTABLE] translates the given portable
+   character into the local character set. */
+static const char portable_to_local[256] =
+  {
+    "                                                                "
+    "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz ."
+    "<(+|&[]!$*);^-/|,%_>?`:$@'=\"      ~-   0123456789   -() {}\\     "
+    "                                                                "
+  };
+
 /* Portable file reader. */
 struct pfm_reader
   {
@@ -149,7 +159,7 @@ pfm_open_reader (struct file_handle *fh, struct dictionary **dict,
   struct pfm_reader *volatile r = NULL;
 
   *dict = dict_create ();
-  if (!fh_open (fh, "portable file", "rs"))
+  if (!fh_open (fh, FH_REF_FILE, "portable file", "rs"))
     goto error;
 
   /* Create and initialize reader. */
@@ -353,16 +363,6 @@ read_pool_string (struct pfm_reader *r)
 static void
 read_header (struct pfm_reader *r)
 {
-  /* portable_to_local[PORTABLE] translates the given portable
-     character into the local character set. */
-  static const char portable_to_local[256] =
-    {
-      "                                                                "
-      "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz ."
-      "<(+|&[]!$*);^-/|,%_>?`:$@'=\"      ~-   0123456789   -() {}\\     "
-      "                                                                "
-    };
-
   char *trans;
   int i;
 
@@ -687,3 +687,38 @@ pfm_read_case (struct pfm_reader *r, struct ccase *c)
   
   return true;
 }
+
+/* Returns true if FILE is an SPSS portable file,
+   false otherwise. */
+bool
+pfm_detect (FILE *file) 
+{
+  unsigned char header[464];
+  char trans[256];
+  int cooked_cnt, raw_cnt;
+  int i;
+
+  cooked_cnt = raw_cnt = 0;
+  while (cooked_cnt < sizeof header)
+    {
+      int c = getc (file);
+      if (c == EOF || raw_cnt++ > 512)
+        return false;
+      else if (c != '\n' && c != '\r') 
+        header[cooked_cnt++] = c;
+    }
+
+  memset (trans, 0, 256);
+  for (i = 64; i < 256; i++) 
+    {
+      unsigned char c = header[i + 200];
+      if (trans[c] == 0)
+        trans[c] = portable_to_local[i];
+    }
+
+  for (i = 0; i < 8; i++) 
+    if (trans[header[i + 456]] != "SPSSPORT"[i]) 
+      return false; 
+
+  return true;
+}
index 3f84e16023fd4f36ef6fd13667fec2a4eca7d01a..5639816a44f41d7a0e79f4b90a7e9a5cd846c4d4 100644 (file)
@@ -23,6 +23,7 @@
 /* Portable file reading. */
 
 #include <stdbool.h>
+#include <stdio.h>
 
 /* Information produced by pfm_read_dictionary() that doesn't fit into
    a dictionary struct. */
@@ -42,5 +43,6 @@ struct pfm_reader *pfm_open_reader (struct file_handle *,
                                     struct pfm_read_info *);
 bool pfm_read_case (struct pfm_reader *, struct ccase *);
 void pfm_close_reader (struct pfm_reader *);
+bool pfm_detect (FILE *);
 
 #endif /* pfm-read.h */
index ef94831e6ff45458a334abb0e64ea3ecc0474879..132216d5cbe6ff2a58fc4072d004c0d369eab706 100644 (file)
@@ -111,7 +111,7 @@ pfm_open_writer (struct file_handle *fh, struct dictionary *dict,
     goto open_error;
 
   /* Open file handle. */
-  if (!fh_open (fh, "portable file", "we"))
+  if (!fh_open (fh, FH_REF_FILE, "portable file", "we"))
     goto error;
 
   /* Initialize data structures. */
@@ -420,7 +420,7 @@ write_value_labels (struct pfm_writer *w, const struct dictionary *dict)
 /* Writes case ELEM to the portable file represented by H.  Returns
    success. */
 int 
-pfm_write_case (struct pfm_writer *w, struct ccase *c)
+pfm_write_case (struct pfm_writer *w, const struct ccase *c)
 {
   int i;
   
@@ -451,8 +451,6 @@ pfm_close_writer (struct pfm_writer *w)
   if (w == NULL)
     return;
 
-  fh_close (w->fh, "portable file", "we");
-  
   if (w->file != NULL)
     {
       char buf[80];
@@ -469,6 +467,8 @@ pfm_close_writer (struct pfm_writer *w)
              fh_get_filename (w->fh), strerror (errno));
     }
 
+  fh_close (w->fh, "portable file", "we");
+  
   free (w->vars);
   free (w);
 }
index e335098a846bc87e4f823d2a9b6e6e88822092b3..a1bb7ce90261f24f5fe9ac64b8dfc2b99422ddd5 100644 (file)
@@ -46,7 +46,7 @@ struct pfm_writer *pfm_open_writer (struct file_handle *, struct dictionary *,
                                     struct pfm_write_options);
 struct pfm_write_options pfm_writer_default_options (void);
 
-int pfm_write_case (struct pfm_writer *, struct ccase *);
+int pfm_write_case (struct pfm_writer *, const struct ccase *);
 void pfm_close_writer (struct pfm_writer *);
 
 #endif /* pfm-write.h */
index ce14d6618b467ca82be4cfcf91785895294a1fe9..966c481395d9c924342a8fef156110677b044130 100644 (file)
@@ -156,7 +156,7 @@ internal_cmd_print (int f)
        {
          lex_match ('=');
 
-         fh = fh_parse ();
+         fh = fh_parse (FH_REF_FILE);
          if (fh == NULL)
            goto error;
        }
@@ -191,7 +191,7 @@ internal_cmd_print (int f)
       if (prt.writer == NULL)
         goto error;
 
-      if (fh_get_mode (fh) == MODE_BINARY)
+      if (fh_get_mode (fh) == FH_MODE_BINARY)
         prt.options |= PRT_BINARY;
     }
 
@@ -836,10 +836,12 @@ dump_table (const struct file_handle *fh)
       }
 
   if (fh != NULL)
-    tab_title (t, 1, _("Writing %d record(s) to file %s."),
-               recno, fh_get_filename (fh));
+    tab_title (t, 1, ngettext ("Writing %d record to %s.",
+                               "Writing %d records to %s.", recno),
+               recno, fh_get_name (fh));
   else
-    tab_title (t, 1, _("Writing %d record(s) to the listing file."), recno);
+    tab_title (t, 1, ngettext ("Writing %d record.",
+                               "Writing %d records.", recno), recno);
   tab_submit (t);
 }
 
@@ -1027,7 +1029,7 @@ cmd_print_space (void)
     {
       lex_match ('=');
 
-      fh = fh_parse ();
+      fh = fh_parse (FH_REF_FILE);
       if (fh == NULL)
        return CMD_FAILURE;
       lex_get ();
index 29224be479565ea4344e209944d31cc8d2294832..23ba49a6e3e160036683d8038eb3cbb060b9f665 100644 (file)
@@ -687,7 +687,7 @@ regression_custom_export (struct cmd_regression *cmd)
     model_file = NULL;
   else
     {
-      model_file = fh_parse ();
+      model_file = fh_parse (FH_REF_FILE);
       if (model_file == NULL)
        return 0;
     }
diff --git a/src/scratch-handle.c b/src/scratch-handle.c
new file mode 100644 (file)
index 0000000..5e3b74a
--- /dev/null
@@ -0,0 +1,36 @@
+/* PSPP - computes sample statistics.
+   Copyright (C) 2006 Free Software Foundation, Inc.
+   Written by Ben Pfaff <blp@gnu.org>.
+
+   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 2 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, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301, USA. */
+
+#include <config.h>
+#include <stdlib.h>
+#include "scratch-handle.h"
+#include "casefile.h"
+#include "dictionary.h"
+
+/* Destroys HANDLE. */
+void
+scratch_handle_destroy (struct scratch_handle *handle) 
+{
+  if (handle != NULL) 
+    {
+      dict_destroy (handle->dictionary);
+      casefile_destroy (handle->casefile);
+      free (handle);
+    }
+}
diff --git a/src/scratch-handle.h b/src/scratch-handle.h
new file mode 100644 (file)
index 0000000..34739cf
--- /dev/null
@@ -0,0 +1,34 @@
+/* PSPP - computes sample statistics.
+   Copyright (C) 2006 Free Software Foundation, Inc.
+   Written by Ben Pfaff <blp@gnu.org>.
+
+   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 2 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, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301, USA. */
+
+#ifndef SCRATCH_HANDLE_H
+#define SCRATCH_HANDLE_H 1
+
+#include <stdbool.h>
+
+/* A scratch file. */
+struct scratch_handle 
+  {
+    struct dictionary *dictionary;      /* Dictionary. */
+    struct casefile *casefile;          /* Cases. */
+  };
+
+void scratch_handle_destroy (struct scratch_handle *);
+
+#endif /* scratch-handle.h */
diff --git a/src/scratch-reader.c b/src/scratch-reader.c
new file mode 100644 (file)
index 0000000..60355dc
--- /dev/null
@@ -0,0 +1,88 @@
+/* PSPP - computes sample statistics.
+   Copyright (C) 2006 Free Software Foundation, Inc.
+   Written by Ben Pfaff <blp@gnu.org>.
+
+   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 2 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, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301, USA. */
+
+#include <config.h>
+#include "scratch-reader.h"
+#include <stdlib.h>
+#include "casefile.h"
+#include "dictionary.h"
+#include "error.h"
+#include "file-handle-def.h"
+#include "scratch-handle.h"
+#include "xalloc.h"
+
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
+
+/* A reader for a scratch file. */
+struct scratch_reader 
+  {
+    struct file_handle *fh;             /* Underlying file handle. */
+    struct casereader *casereader;      /* Case reader. */
+  };
+
+/* 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.
+
+   If you use an any_reader instead, then your code can be more
+   flexible without being any harder to write. */
+struct scratch_reader *
+scratch_reader_open (struct file_handle *fh, struct dictionary **dict)
+{
+  struct scratch_handle *sh;
+  struct scratch_reader *reader;
+  
+  if (!fh_open (fh, FH_REF_SCRATCH, "scratch file", "rs"))
+    return NULL;
+  
+  sh = fh_get_scratch_handle (fh);
+  if (sh == 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);
+  reader = xmalloc (sizeof *reader);
+  reader->fh = fh;
+  reader->casereader = casefile_get_reader (sh->casefile);
+  return reader;
+}
+
+/* Reads a case from READER into C.
+   Returns true if successful, false on error or at end of file. */
+bool
+scratch_reader_read_case (struct scratch_reader *reader, struct ccase *c)
+{
+  return casereader_read (reader->casereader, c);
+}
+
+/* Closes READER. */
+void
+scratch_reader_close (struct scratch_reader *reader) 
+{
+  fh_close (reader->fh, "scratch file", "rs");
+  casereader_destroy (reader->casereader);
+  free (reader);
+}
diff --git a/src/scratch-reader.h b/src/scratch-reader.h
new file mode 100644 (file)
index 0000000..534ceb9
--- /dev/null
@@ -0,0 +1,33 @@
+/* PSPP - computes sample statistics.
+   Copyright (C) 2006 Free Software Foundation, Inc.
+   Written by Ben Pfaff <blp@gnu.org>.
+
+   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 2 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, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301, USA. */
+
+#ifndef SCRATCH_READER_H
+#define SCRATCH_READER_H 1
+
+#include <stdbool.h>
+
+struct dictionary;
+struct file_handle;
+struct ccase;
+struct scratch_reader *scratch_reader_open (struct file_handle *,
+                                            struct dictionary **);
+bool scratch_reader_read_case (struct scratch_reader *, struct ccase *);
+void scratch_reader_close (struct scratch_reader *);
+
+#endif /* scratch-reader.h */
diff --git a/src/scratch-writer.c b/src/scratch-writer.c
new file mode 100644 (file)
index 0000000..e5ac046
--- /dev/null
@@ -0,0 +1,112 @@
+/* PSPP - computes sample statistics.
+   Copyright (C) 2006 Free Software Foundation, Inc.
+   Written by Ben Pfaff <blp@gnu.org>.
+
+   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 2 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, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301, USA. */
+
+#include <config.h>
+#include "scratch-writer.h"
+#include <stdlib.h>
+#include "case.h"
+#include "casefile.h"
+#include "dictionary.h"
+#include "file-handle-def.h"
+#include "scratch-handle.h"
+#include "xalloc.h"
+
+/* A scratch file writer. */
+struct scratch_writer 
+  {
+    struct scratch_handle *handle;      /* Underlying scratch handle. */
+    struct file_handle *fh;             /* Underlying file handle. */
+    struct dict_compactor *compactor;   /* Compacts into handle->dictionary. */
+  };
+
+/* 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.
+
+   If you use an any_writer instead, then your code can be more
+   flexible without being any harder to write. */
+struct scratch_writer *
+scratch_writer_open (struct file_handle *fh,
+                     const struct dictionary *dictionary) 
+{
+  struct scratch_handle *sh;
+  struct scratch_writer *writer;
+  struct dictionary *scratch_dict;
+  struct dict_compactor *compactor;
+
+  if (!fh_open (fh, FH_REF_SCRATCH, "scratch file", "we"))
+    return NULL;
+
+  /* Destroy previous contents of handle. */
+  sh = fh_get_scratch_handle (fh);
+  if (sh != NULL) 
+    scratch_handle_destroy (sh);
+
+  /* Copy the dictionary and compact if needed. */
+  scratch_dict = dict_clone (dictionary);
+  if (dict_needs_compaction (scratch_dict)) 
+    {
+      compactor = dict_make_compactor (scratch_dict);
+      dict_compact_values (scratch_dict);
+    }
+  else
+    compactor = NULL;
+
+  /* Create new contents. */
+  sh = xmalloc (sizeof *sh);
+  sh->dictionary = scratch_dict;
+  sh->casefile = casefile_create (dict_get_next_value_idx (sh->dictionary));
+
+  /* Create writer. */
+  writer = xmalloc (sizeof *writer);
+  writer->handle = sh;
+  writer->fh = fh;
+  writer->compactor = compactor;
+
+  fh_set_scratch_handle (fh, sh);
+  return writer;
+}
+
+/* Writes case C to WRITER. */
+void
+scratch_writer_write_case (struct scratch_writer *writer,
+                           const struct ccase *c) 
+{
+  struct scratch_handle *handle = writer->handle;
+  if (writer->compactor) 
+    {
+      struct ccase tmp_case;
+      case_create (&tmp_case, dict_get_next_value_idx (handle->dictionary));
+      dict_compactor_compact (writer->compactor, &tmp_case, c);
+      casefile_append_xfer (handle->casefile, &tmp_case);
+    }
+  else 
+    casefile_append (handle->casefile, c);
+}
+
+/* Closes WRITER. */
+void
+scratch_writer_close (struct scratch_writer *writer) 
+{
+  struct scratch_handle *handle = writer->handle;
+  casefile_mode_reader (handle->casefile);
+  fh_close (writer->fh, "scratch file", "we");
+  free (writer);
+}
diff --git a/src/scratch-writer.h b/src/scratch-writer.h
new file mode 100644 (file)
index 0000000..33e3e14
--- /dev/null
@@ -0,0 +1,33 @@
+/* PSPP - computes sample statistics.
+   Copyright (C) 2006 Free Software Foundation, Inc.
+   Written by Ben Pfaff <blp@gnu.org>.
+
+   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 2 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, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301, USA. */
+
+#ifndef SCRATCH_WRITER_H
+#define SCRATCH_WRITER_H 1
+
+#include <stdbool.h>
+
+struct dictionary;
+struct file_handle;
+struct ccase;
+struct scratch_writer *scratch_writer_open (struct file_handle *,
+                                            const struct dictionary *);
+void scratch_writer_write_case (struct scratch_writer *, const struct ccase *);
+void scratch_writer_close (struct scratch_writer *);
+
+#endif /* scratch-writer.h */
index 9e25f7413507c936868c3944c4a4c927e5ec6e91..986dede94fd2947a62d8a944d1777cf319c688b3 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - computes sample statistics.
-   Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc.
    Written by Ben Pfaff <blp@gnu.org>.
 
    This program is free software; you can redistribute it and/or
@@ -143,15 +143,17 @@ sfm_close_reader (struct sfm_reader *r)
   if (r == NULL)
     return;
 
+  if (r->file)
+    {
+      if (fn_close (fh_get_filename (r->fh), r->file) == EOF)
+        msg (ME, _("%s: Closing system file: %s."),
+             fh_get_filename (r->fh), strerror (errno));
+      r->file = NULL;
+    }
+
   if (r->fh != NULL)
     fh_close (r->fh, "system file", "rs");
   
-  if ( r->file ) {
-    if (fn_close (fh_get_filename (r->fh), r->file) == EOF)
-      msg (ME, _("%s: Closing system file: %s."),
-          fh_get_filename (r->fh), strerror (errno));
-    r->file = NULL;
-  }
   free (r->vars);
   free (r->buf);
   free (r);
@@ -206,7 +208,7 @@ sfm_open_reader (struct file_handle *fh, struct dictionary **dict,
   struct variable **var_by_idx = NULL;
 
   *dict = dict_create ();
-  if (!fh_open (fh, "system file", "rs"))
+  if (!fh_open (fh, FH_REF_FILE, "system file", "rs"))
     goto error;
 
   /* Create and initialize reader. */
@@ -1524,3 +1526,17 @@ fread_ok (struct sfm_reader *r, void *buffer, size_t byte_cnt)
       return 0;
     }
 }
+\f
+/* Returns true if FILE is an SPSS system file,
+   false otherwise. */
+bool
+sfm_detect (FILE *file) 
+{
+  struct sysfile_header hdr;
+
+  if (fread (&hdr, sizeof hdr, 1, file) != 1)
+    return false;
+  if (strncmp ("$FL2", hdr.rec_type, 4))
+    return false;
+  return true; 
+}
index 3abb1e160b9bfa01fe1b4dea3aecd64a69a7a61d..d471ad7b9e0ab8c0700222e8ac7a2e2f67a13346 100644 (file)
@@ -20,6 +20,9 @@
 #ifndef SFM_READ_H
 #define SFM_READ_H 1
 
+#include <stdbool.h>
+#include <stdio.h>
+
 /* Reading system files. */
 
 /* System file info that doesn't fit in struct dictionary. */
@@ -41,5 +44,6 @@ struct sfm_reader *sfm_open_reader (struct file_handle *,
                                     struct sfm_read_info *);
 int sfm_read_case (struct sfm_reader *, struct ccase *);
 void sfm_close_reader (struct sfm_reader *);
+bool sfm_detect (FILE *);
 
 #endif /* sfm-read.h */
index 128d5e4b80513eb0e5ab1717d5d68f79cf625598..dcdab0b46d616dd6604adab4b33108fd69c80b5b 100644 (file)
@@ -156,7 +156,7 @@ sfm_open_writer (struct file_handle *fh, struct dictionary *d,
     goto open_error;
 
   /* Open file handle. */
-  if (!fh_open (fh, "system file", "we"))
+  if (!fh_open (fh, FH_REF_FILE, "system file", "we"))
     goto error;
 
   /* Create and initialize writer. */
@@ -904,8 +904,6 @@ sfm_close_writer (struct sfm_writer *w)
   if (w == NULL)
     return;
 
-  fh_close (w->fh, "system file", "we");
-  
   if (w->file != NULL) 
     {
       /* Flush buffer. */
@@ -932,6 +930,8 @@ sfm_close_writer (struct sfm_writer *w)
              fh_get_filename (w->fh), strerror (errno));
     }
 
+  fh_close (w->fh, "system file", "we");
+  
   free (w->buf);
   free (w->vars);
   free (w);
index 081ffea5a74d36290c05450b3d5e52e04cd8fa90..eaa9cdff9fd6f6eac8871ffaa964899900e67fee 100644 (file)
--- a/src/str.c
+++ b/src/str.c
@@ -1,5 +1,5 @@
 /* PSPP - computes sample statistics.
-   Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc.
    Written by Ben Pfaff <blp@gnu.org>.
 
    This program is free software; you can redistribute it and/or
@@ -270,6 +270,14 @@ str_uppercase (char *s)
   for (; *s != '\0'; s++)
     *s = toupper ((unsigned char) *s);
 }
+
+/* Converts each character in S to lowercase. */
+void
+str_lowercase (char *s) 
+{
+  for (; *s != '\0'; s++)
+    *s = tolower ((unsigned char) *s);
+}
 \f
 /* Initializes ST with initial contents S. */
 void
index db961959c45205b3a63da19ca773e01c544b121f..19fe779aa7131f39e7d8c3aa716bcdec9a71e102 100644 (file)
--- a/src/str.h
+++ b/src/str.h
@@ -34,6 +34,7 @@
 #include "strstr.h"
 #include "strtok_r.h"
 #include "vsnprintf.h"
+#include "xvasprintf.h"
 
 #ifndef HAVE_STRCHR
 #define strchr index
@@ -108,6 +109,7 @@ void str_copy_rpad (char *, size_t, const char *);
 void str_copy_trunc (char *, size_t, const char *);
 void str_copy_buf_trunc (char *, size_t, const char *, size_t);
 void str_uppercase (char *);
+void str_lowercase (char *);
 \f
 /* Fixed-length strings. */
 struct fixed_string 
index a9f074ab3be2ae6838f76ddd42afe1298fbf0d88..c12bd10081ef35b2941813ebaecc8f69dec4d8a1 100644 (file)
@@ -86,7 +86,7 @@ cmd_sysfile_info (void)
   lex_match_id ("FILE");
   lex_match ('=');
 
-  h = fh_parse ();
+  h = fh_parse (FH_REF_FILE);
   if (!h)
     return CMD_FAILURE;
 
index 3aeb9a638a63b33173d503cfbe7ac50baef18bf6..f4930eea0e65b61a279033d6ae4335b03fb6e7c5 100644 (file)
--- a/src/var.h
+++ b/src/var.h
@@ -129,10 +129,6 @@ extern struct dictionary *default_dict;
 \f
 /* Transformation state. */
 
-/* Default file handle for DATA LIST, REREAD, REPEATING DATA
-   commands. */
-extern struct file_handle *default_handle;
-
 /* PROCESS IF expression. */
 extern struct expression *process_if_expr;
 \f
index 3d8f03ae56b97f27ef2c01fb1c8cbf0d1c195d6b..06cf67ec1ebc8ca70221875b428b43e16e09e30e 100644 (file)
--- a/src/vfm.c
+++ b/src/vfm.c
@@ -1,5 +1,5 @@
 /* PSPP - computes sample statistics.
-   Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc.
    Written by Ben Pfaff <blp@gnu.org>.
 
    This program is free software; you can redistribute it and/or
@@ -35,6 +35,7 @@
 #include "ctl-stack.h"
 #include "error.h"
 #include "expressions/public.h"
+#include "file-handle-def.h"
 #include "misc.h"
 #include "settings.h"
 #include "som.h"
@@ -75,9 +76,9 @@ struct case_source *vfm_source;
 /* The replacement active file, to which cases are written. */
 struct case_sink *vfm_sink;
 
-/* Nonzero if the case needs to have values deleted before being
-   stored, zero otherwise. */
-static int compaction_necessary;
+/* The compactor used to compact a compact, if necessary;
+   otherwise a null pointer. */
+static struct dict_compactor *compactor;
 
 /* Time at which vfm was last invoked. */
 static time_t last_vfm_invocation;
@@ -229,8 +230,9 @@ open_active_file (void)
     }
 
   /* Figure out compaction. */
-  compaction_necessary = (dict_get_next_value_idx (temp_dict)
-                          != dict_get_compacted_value_cnt (temp_dict));
+  compactor = (dict_needs_compaction (temp_dict)
+               ? dict_make_compactor (temp_dict)
+               : NULL);
 
   /* Prepare sink. */
   if (vfm_sink == NULL)
@@ -279,10 +281,10 @@ write_case (struct write_case_data *wc_data)
   /* Write case to replacement active file. */
   if (vfm_sink->class->write != NULL) 
     {
-      if (compaction_necessary
+      if (compactor != NULL
         {
-          dict_compact_case (temp_dict, &wc_data->sink_case,
-                             &wc_data->trns_case);
+          dict_compactor_compact (compactor, &wc_data->sink_case,
+                                  &wc_data->trns_case);
           vfm_sink->class->write (vfm_sink, &wc_data->sink_case);
         }
       else
@@ -425,8 +427,11 @@ close_active_file (void)
     }
 
   /* Finish compaction. */
-  if (compaction_necessary)
-    dict_compact_values (default_dict);
+  if (compactor != NULL) 
+    {
+      dict_compactor_destroy (compactor);
+      dict_compact_values (default_dict); 
+    }
     
   /* Free data source. */
   free_case_source (vfm_source);
@@ -944,7 +949,7 @@ void
 discard_variables (void)
 {
   dict_clear (default_dict);
-  default_handle = NULL;
+  fh_set_default_handle (NULL);
 
   n_lag = 0;
   
index d72568ce8e1eaf3c33daccba7fd53bf86a1a6ca1..cfd639a925ead255085b610cc7f6bb49aca25637 100644 (file)
--- a/src/vfm.h
+++ b/src/vfm.h
@@ -56,12 +56,8 @@ struct case_source_class
   };
 
 extern const struct case_source_class storage_source_class;
-extern const struct case_source_class data_list_source_class;
 extern const struct case_source_class file_type_source_class;
 extern const struct case_source_class input_program_source_class;
-extern const struct case_source_class get_source_class;
-extern const struct case_source_class import_source_class;
-extern const struct case_source_class sort_source_class;
 
 struct dictionary;
 struct case_source *create_case_source (const struct case_source_class *,
index 39d0ac0a8b496c4331f959ca0a0fce581381094c..d4864f2c0dd172083a3441d7fcd2150af42c54b1 100755 (executable)
@@ -71,7 +71,7 @@ if [ $? -ne 0 ] ; then no_result ; fi
 
 perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
 diff -b  -w $TEMPDIR/pspp.list - << EOF
-1.1 DATA LIST.  Reading free-form data from the command file.
+1.1 DATA LIST.  Reading free-form data from INLINE.
 +--------+------+
 |Variable|Format|
 #========#======#
index 558ea854e8c3ce6dbd3d8fa8b7ce38dd3c9b3efb..941fcca66fabcc98bf287d02d1599b0fc69fc877 100755 (executable)
@@ -73,7 +73,7 @@ if [ $? -ne 0 ] ; then fail ; fi
 activity="compare output"
 perl -pi -e 's/^\s*$//g' pspp.list
 diff -b pspp.list - << EOF
-1.1 DATA LIST.  Reading free-form data from the command file.
+1.1 DATA LIST.  Reading free-form data from INLINE.
 +----------------+------+
 |    Variable    |Format|
 #================#======#
index 73640d4644b1aa3c6d400cd5a09a595ac9d94069..61fc7d0726ed3f6dd49b3b6b3c717f10c9032066 100644 (file)
@@ -1,4 +1,4 @@
-1.1 DATA LIST.  Reading free-form data from the command file.
+1.1 DATA LIST.  Reading free-form data from INLINE.
 +--------+------+
 |Variable|Format|
 #========#======#
index e50ed5f7ce6108760f7077443f04edbec64dfea0..b3cae373b8a5d7eab06e6d84d07a30228016b5ed 100755 (executable)
@@ -66,7 +66,7 @@ if [ $? -ne 0 ] ; then no_result ; fi
 
 perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
 diff -b  -w $TEMPDIR/pspp.list - << EOF
-1.1 DATA LIST.  Reading free-form data from the command file.
+1.1 DATA LIST.  Reading free-form data from INLINE.
 +--------+------+
 |Variable|Format|
 #========#======#
index c36bc9ad3ed106018dc68833a8c59e368e1c81da..a55a3246e04a626611e10f363ba69b3c0ea8d2b3 100755 (executable)
@@ -76,7 +76,7 @@ if [ $? -ne 0 ] ; then no_result ; fi
 
 perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
 diff -b  -w $TEMPDIR/pspp.list - << EOF
-1.1 DATA LIST.  Reading free-form data from the command file.
+1.1 DATA LIST.  Reading free-form data from INLINE.
 +--------+------+
 |Variable|Format|
 #========#======#
index 98ad523b60d32a5e08b08f9100d7e2ce58af7c68..8dfbacb590bd1bbd4f808edbf509c97efea556ed 100755 (executable)
@@ -76,7 +76,7 @@ if [ $? -ne 0 ] ; then no_result ; fi
 
 perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
 diff -b  -w $TEMPDIR/pspp.list - << EOF
-1.1 DATA LIST.  Reading free-form data from the command file.
+1.1 DATA LIST.  Reading free-form data from INLINE.
 +--------+------+
 |Variable|Format|
 #========#======#
index 47113b9b094873b7733818986e64cb7abbab6f20..659eba5f39ff2b3abea4a284f165dd341761dffa 100644 (file)
@@ -1,4 +1,4 @@
-1.1 DATA LIST.  Reading free-form data from the command file.
+1.1 DATA LIST.  Reading free-form data from INLINE.
 +--------+------+
 |Variable|Format|
 #========#======#
index 88674322a565ed08178eee6ae23d6ef35a935859..50899a81427730d85b58e8254987f5d298e480c7 100644 (file)
@@ -1,4 +1,4 @@
-1.1 DATA LIST.  Reading free-form data from the command file.
+1.1 DATA LIST.  Reading free-form data from INLINE.
 +--------+------+
 |Variable|Format|
 #========#======#
index 7d1c3ed05f083aa23ae64f41de92a324d430db2e..79a28e3576a2a9b7d931caecf1e5d22a7cede00c 100755 (executable)
@@ -80,7 +80,7 @@ if [ $? -ne 0 ] ; then no_result ; fi
 activity="compare output"
 perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
 diff  -b $TEMPDIR/pspp.list - <<EOF
-1.1 DATA LIST.  Reading free-form data from the command file.
+1.1 DATA LIST.  Reading free-form data from INLINE.
 +--------+------+
 |Variable|Format|
 #========#======#
index 4774193166ce529e4370620085ea276765ce268f..187d7ac2cad1fc4841ceb64a57de2ff53c15b3b8 100755 (executable)
@@ -77,7 +77,7 @@ if [ $? -ne 0 ] ; then no_result ; fi
 
 perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
 diff -b  -w $TEMPDIR/pspp.list - << EOF
-1.1 DATA LIST.  Reading free-form data from the command file.
+1.1 DATA LIST.  Reading free-form data from INLINE.
 +--------+------+
 |Variable|Format|
 #========#======#
index 534c93b4cc275b22bc6fdab0561b30a3bc8de753..a751f9dc6a26fd04276a1a2206b1a02dc0ae2664 100755 (executable)
@@ -166,7 +166,7 @@ G        N       NI      NU     NUI NFGT2 NFGT2I SFGT2 SFGT2I NFIN23 NFIN23I SFI
 4     1.00     1.00       1       1  .      .     .     1.000   .       .      .       .000  .      .     .      .000      .       .              4    .        .       .       1.000     .      .            4    .     .          4      .        .      .     .          4      .00      .00      .00     1.00     1.00     1.00     1.00      .00       0       0       0       1       1        1       1        0    .      .     .   100.0     .       .      .       .0    .      .     .      .0      .        .       .     100.0      .        .        .        .   
 EOF
 
-for outfile in active external; do
+for outfile in temporary active external; do
     for sort in presorted unsorted; do
        for missing in itemwise columnwise; do
            name=$outfile-$sort-$missing
@@ -182,8 +182,10 @@ for outfile in active external; do
                echo "aggregate"
                if [ "$outfile" = "active" ]; then
                    echo "      outfile=*"
-               else
+               elif [ "$outfile" = "external" ]; then
                    echo "      outfile='aggregate.sys'"
+               else
+                   echo "      outfile=#AGGREGATE"
                fi
                if [ "$sort" = "presorted" ]; then
                    echo "      /presorted"
@@ -194,6 +196,8 @@ for outfile in active external; do
                cat agg-skel.pspp
                if [ "$outfile" = "external" ]; then
                    echo "get file='aggregate.sys'."
+               elif [ "$outfile" = "temporary" ]; then
+                   echo "get file=#AGGREGATE."
                fi
                echo "list."
            } > $name.pspp
index b9f6e50d6ecd299a0fbf02b4dd29f6083fd92f3a..dc3043506c076d1847523ac339772189da0ae1ae 100755 (executable)
@@ -83,7 +83,7 @@ if [ $? -ne 0 ] ; then no_result ; fi
 activity="test output"
 perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
 diff -b $TEMPDIR/pspp.list - <<EOF
-1.1 DATA LIST.  Reading 1 record from the command file.
+1.1 DATA LIST.  Reading 1 record from INLINE.
 +--------+------+-------+------+
 |Variable|Record|Columns|Format|
 #========#======#=======#======#
index 2d328b76f7ee33ad43836b5424a828ec928d15da..ff26a4dae092f624ed1bc840d490ac76349ca2f1 100755 (executable)
@@ -82,7 +82,7 @@ if [ $? -ne 0 ] ; then no_result ; fi
 activity="compare data"
 perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
 diff -b $TEMPDIR/pspp.list - << foobar
-1.1 DATA LIST.  Reading 1 record from the command file.
+1.1 DATA LIST.  Reading 1 record from INLINE.
 +--------+------+-------+------+
 |Variable|Record|Columns|Format|
 #========#======#=======#======#
@@ -96,7 +96,7 @@ A B
 5 6 
 7 8 
 9 0 
-2.1 DATA LIST.  Reading 1 record from the command file.
+2.1 DATA LIST.  Reading 1 record from INLINE.
 +--------+------+-------+------+
 |Variable|Record|Columns|Format|
 #========#======#=======#======#
index 7c4d19a68952e4322a05d883d7b37fe317d54060..47ee7bcdefb6728ae62c1138350be7002ea893fb 100755 (executable)
@@ -75,7 +75,7 @@ if [ $? -ne 0 ] ; then no_result ; fi
 activity="compare results"
 perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
 diff -b  $TEMPDIR/pspp.list - <<EOF
-1.1 DATA LIST.  Reading 1 record from the command file.
+1.1 DATA LIST.  Reading 1 record from INLINE.
 +--------+------+-------+------+
 |Variable|Record|Columns|Format|
 #========#======#=======#======#
index de5281ec0e9074eafd893e7f2650f44752b59ec3..12f66377d26976fe6fd968bb760ea913ab90da78 100755 (executable)
@@ -108,7 +108,7 @@ if [ $? -ne 0 ] ; then fail ; fi
 activity="compare output"
 perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
 diff -b  $TEMPDIR/pspp.list - << EOF
-1.1 DATA LIST.  Reading free-form data from the command file.
+1.1 DATA LIST.  Reading free-form data from INLINE.
 +--------+------+
 |Variable|Format|
 #========#======#
index 7a0e773c1e6f1dca6f1229072b6511b69d2388e7..21e8cb078e01ef68d6ee6482a51722a3b22acffc 100755 (executable)
@@ -84,7 +84,7 @@ if [ $? -ne 0 ] ; then no_result ; fi
 activity="compare results"
 perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
 diff -b  $TEMPDIR/pspp.list - << EOF
-1.1 DATA LIST.  Reading free-form data from the command file.
+1.1 DATA LIST.  Reading free-form data from INLINE.
 +--------+------+
 |Variable|Format|
 #========#======#
index 079ee5994deeb026bcc702576e1c6b45d602b79c..4ce0257afffe9bbbf491d74cbfd02f74f2d7c83a 100755 (executable)
@@ -96,7 +96,7 @@ if [ $? -ne 0 ] ; then no_result ; fi
 activity="compare results"
 perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
 diff -b  $TEMPDIR/pspp.list - << EOF
-1.1 DATA LIST.  Reading free-form data from the command file.
+1.1 DATA LIST.  Reading free-form data from INLINE.
 +--------+------+
 |Variable|Format|
 #========#======#
index fecb7b33d3cd65a0e6986f97712f476509274195..12ae2e505c1d5deef5175086c85a441388edc1e8 100755 (executable)
@@ -74,7 +74,7 @@ if [ $? -ne 0 ] ; then no_result ; fi
 
 activity="compare output"
 diff  -b  $TEMPDIR/pspp.list - << EOF 
-1.1 DATA LIST.  Reading free-form data from file $TEMPDIR/wiggle.txt.
+1.1 DATA LIST.  Reading free-form data from myhandle.
 +--------+------+
 |Variable|Format|
 #========#======#
index be5a80473b08ce677141b95d3fe489c68c38b83b..37d3fb806c34d8b8e710f98abf5b7b014282e784 100755 (executable)
@@ -118,7 +118,7 @@ if [ $? -ne 0 ] ; then no_result ; fi
 activity="compare results"
 perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.filtered
 diff -b  $TEMPDIR/pspp.filtered - <<EOF
-1.1 DATA LIST.  Reading 1 record from the command file.
+1.1 DATA LIST.  Reading 1 record from INLINE.
 +--------+------+-------+------+
 |Variable|Record|Columns|Format|
 #========#======#=======#======#
index 9448e21f4b830bceb3f60bd65746af4a8f361434..db40d1abf3ebb2a82802080bb8c5672686665504 100755 (executable)
@@ -74,7 +74,7 @@ if [ $? -ne 0 ] ; then no_result ; fi
 activity="compare output"
 perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
 diff  -b  $TEMPDIR/pspp.list - << EOF
-1.1 DATA LIST.  Reading 1 record from the command file.
+1.1 DATA LIST.  Reading 1 record from INLINE.
 +--------+------+-------+------+
 |Variable|Record|Columns|Format|
 #========#======#=======#======#
index 01fbc59276292ce4a23e184386f407fb42b91cb1..ff1d459d0ad748959bc344eecec539a463d7701a 100755 (executable)
@@ -73,7 +73,7 @@ if [ $? -ne 0 ] ; then no_result ; fi
 activity="compare result"
 perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
 diff -b  $TEMPDIR/pspp.list - <<EOF
-1.1 DATA LIST.  Reading 1 record from the command file.
+1.1 DATA LIST.  Reading 1 record from INLINE.
 +--------+------+-------+------+
 |Variable|Record|Columns|Format|
 #========#======#=======#======#
index 80be418af34e83a7edef8a80240c483fec799e92..477ac8ba634909085cd1ad45b3fbc3e057015325 100755 (executable)
@@ -75,7 +75,7 @@ if [ $? -ne 0 ] ; then no_result ; fi
 activity="compare results"
 perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
 diff -b  $TEMPDIR/pspp.list - <<EOF
-1.1 DATA LIST.  Reading 1 record from file $top_srcdir/tests/weighting.data.
+1.1 DATA LIST.  Reading 1 record from "$top_srcdir/tests/weighting.data".
 +--------+------+-------+------+
 |Variable|Record|Columns|Format|
 #========#======#=======#======#
index 54dbcfa4fa5e65ce6d865a351c562bd4af6693b5..c579f40fb3bccb08858872232db8345c07b42bb3 100755 (executable)
@@ -73,7 +73,7 @@ if [ $? -ne 0 ] ; then no_result ; fi
 activity="compare output"
 perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
 diff  -b $TEMPDIR/pspp.list - <<EOF
-1.1 DATA LIST.  Reading free-form data from the command file.
+1.1 DATA LIST.  Reading free-form data from INLINE.
 +--------------+------+
 |   Variable   |Format|
 #==============#======#
index bcf4bfcd8c6429a24455be71c3542d1501ebf75a..b0943ed545da807f6c8381b9f22f4fc877974b62 100755 (executable)
@@ -78,7 +78,7 @@ if [ $? -ne 0 ] ; then fail ; fi
 activity="compare results"
 perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
 diff  -b $TEMPDIR/pspp.list  - <<EOF
-1.1 DATA LIST.  Reading 1 record from the command file.
+1.1 DATA LIST.  Reading 1 record from INLINE.
 +----------+------+-------+------+
 | Variable |Record|Columns|Format|
 #==========#======#=======#======#
index 4de772137164eeb28a4edb45bdcb55a37af2c160..da1d19cb38909d961b59b45e640f02269eec7b58 100755 (executable)
@@ -97,7 +97,7 @@ if [ $? -ne 0 ] ; then no_result ; fi
 
 perl -pi -e s/^\s*\$//g $TEMPDIR/pspp.list
 diff -b  $TEMPDIR/pspp.list - << EOF | perl -e 's/^\s*$//g'
-1.1 DATA LIST.  Reading free-form data from the command file.
+1.1 DATA LIST.  Reading free-form data from INLINE.
 +--------+------+
 |Variable|Format|
 #========#======#
index a15775b557f8ac38b20d53e65f85a3fc950941c4..54c74daa60aaf61c187c7dc878cffc437b35975d 100755 (executable)
@@ -94,7 +94,7 @@ if [ $? -ne 0 ] ; then no_result ; fi
 
 perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
 diff -b  $TEMPDIR/pspp.list - << EOF
-1.1 DATA LIST.  Reading free-form data from the command file.
+1.1 DATA LIST.  Reading free-form data from INLINE.
 +--------+------+
 |Variable|Format|
 #========#======#
index 78c6212fcfa5ae19f5b7d074e04191573667f391..a4783bbcb5ee2bcbedc44194532b41aadd078653 100755 (executable)
@@ -100,7 +100,7 @@ if [ $? -ne 0 ] ; then fail ; fi
 activity="compare output"
 perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
 diff -b  $TEMPDIR/pspp.list - << EOF
-1.1 DATA LIST.  Reading free-form data from file $TEMPDIR/data-list.data.
+1.1 DATA LIST.  Reading free-form data from "$TEMPDIR/data-list.data".
 +--------+------+
 |Variable|Format|
 #========#======#
@@ -109,7 +109,7 @@ diff -b  $TEMPDIR/pspp.list - << EOF
 |C       |F8.0  |
 |D       |F8.0  |
 +--------+------+
-2.1 PRINT.  Writing 1 record(s) to file foo.
+2.1 PRINT.  Writing 1 record to "foo".
 +--------+------+-------+------+
 |Variable|Record|Columns|Format|
 #========#======#=======#======#
@@ -125,7 +125,7 @@ diff -b  $TEMPDIR/pspp.list - << EOF
      .       2.00     3.00     4.00 
      .       6.00     7.00     8.00 
      .      10.00    11.00    12.00 
-3.1 DATA LIST.  Reading free-form data from file $TEMPDIR/data-list.data.
+3.1 DATA LIST.  Reading free-form data from "$TEMPDIR/data-list.data".
 +--------+------+
 |Variable|Format|
 #========#======#
@@ -134,7 +134,7 @@ diff -b  $TEMPDIR/pspp.list - << EOF
 |C       |F8.0  |
 |D       |F8.0  |
 +--------+------+
-4.1 PRINT.  Writing 1 record(s) to the listing file.
+4.1 PRINT.  Writing 1 record.
 +--------+------+-------+------+
 |Variable|Record|Columns|Format|
 #========#======#=======#======#
index e65e545bad96f49349b96ce9fbfe390f1734d4e0..9a241edebcfcac37109aede5049c33c9d4374d63 100755 (executable)
@@ -80,7 +80,7 @@ if [ $? -eq 0 ] ; then fail ; fi
 activity="compare output"
 perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
 diff  -b $TEMPDIR/pspp.list - <<EOF
-1.1 DATA LIST.  Reading free-form data from the command file.
+1.1 DATA LIST.  Reading free-form data from INLINE.
 +----------+------+
 | Variable |Format|
 #==========#======#
index 34269c586b8b3d56988f422c1b7e71974923d462..c847cefda733ffbdc9fb49b7314612901ea76a00 100755 (executable)
@@ -78,7 +78,7 @@ if [ $? -ne 0 ] ; then no_result ; fi
 activity="compare output"
 perl -pi -e 's/^\s*$//g' $TEMPDIR/out-filtered
 diff -b -w $TEMPDIR/out-filtered - << EOF
-1.1 DATA LIST.  Reading free-form data from the command file.
+1.1 DATA LIST.  Reading free-form data from INLINE.
 +--------+------+
 |Variable|Format|
 #========#======#
index db41de932e9b0b5196e4040c4e810d858cfedb43..87a26b5ca4a8411c94be0f23d4d861abc2ddad1e 100755 (executable)
@@ -89,7 +89,7 @@ if [ $? -ne 0 ] ; then no_result ; fi
 activity="compare output"
 perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
 diff  -b $TEMPDIR/pspp.list - <<EOF
-1.1 DATA LIST.  Reading free-form data from the command file.
+1.1 DATA LIST.  Reading free-form data from INLINE.
 +--------+------+
 |Variable|Format|
 #========#======#
index 77bf4bc6d16ddc6523fbb9334c26271960c7633f..4120e05551f6c90cf68db5d324a00725e6858f5d 100755 (executable)
@@ -72,7 +72,7 @@ if [ $? -ne 0 ] ; then no_result ; fi
 activity="compare output"
 perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
 diff  -b $TEMPDIR/pspp.list - <<EOF
-1.1 DATA LIST.  Reading free-form data from the command file.
+1.1 DATA LIST.  Reading free-form data from INLINE.
 +--------+------+
 |Variable|Format|
 #========#======#
index 8e388afa6ae8db82a18e310f157874bc2c42f24b..82288989d3bd946902a07dcb526f4adcfb55e27b 100755 (executable)
@@ -79,7 +79,7 @@ if [ $? -ne 0 ] ; then no_result ; fi
 activity="compare output"
 perl -pi -e s/^\s*\$//g $TEMPDIR/pspp.list
 diff  -b $TEMPDIR/pspp.list - <<EOF | perl -e 's/^\s*$//g'
-1.1 DATA LIST.  Reading free-form data from the command file.
+1.1 DATA LIST.  Reading free-form data from INLINE.
 +--------+------+
 |Variable|Format|
 #========#======#
index 34ac15e938377acc38af4f1482913b5f6dbc8f97..dd2d14ae00546d89e986f47b896af1b5a0316d68 100755 (executable)
@@ -71,7 +71,7 @@ if [ $? -ne 0 ] ; then no_result ; fi
 activity="compare output"
 perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
 diff  -b $TEMPDIR/pspp.list - <<EOF
-1.1 DATA LIST.  Reading free-form data from the command file.
+1.1 DATA LIST.  Reading free-form data from INLINE.
 +--------+------+
 |Variable|Format|
 #========#======#
index 93f4c01dd601330ec709eba031f4d3d46c0b0662..d5df591607bfdd9f41ee1ed28766de1399882f6d 100755 (executable)
@@ -76,7 +76,7 @@ if [ $? -ne 0 ] ; then no_result ; fi
 
 perl -pi -e s/^\s*\$//g $TEMPDIR/pspp.list
 diff  -b $TEMPDIR/pspp.list - << EOF | perl -e 's/^\s*$//g'
-1.1 DATA LIST.  Reading 1 record from the command file.
+1.1 DATA LIST.  Reading 1 record from INLINE.
 +--------+------+-------+------+
 |Variable|Record|Columns|Format|
 #========#======#=======#======#
index 0d65db5ef03e81dda3f76b984f8224868fdee23b..0e590e5d657f81f42c6cda37dbf5ded4b41b8ef7 100755 (executable)
@@ -77,7 +77,7 @@ if [ $? -ne 0 ] ; then no_result ; fi
 activity="compare results"
 perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
 diff -b  $TEMPDIR/pspp.list - << EOF
-1.1 DATA LIST.  Reading free-form data from the command file.
+1.1 DATA LIST.  Reading free-form data from INLINE.
 +--------+------+
 |Variable|Format|
 #========#======#
index 7dd31a372f80d13a84e99d00d2a3561ec9ca362a..3d04147c4135db59b682b17f162fb36ac2f043d7 100755 (executable)
@@ -68,7 +68,7 @@ if [ $? -ne 0 ] ; then no_result ; fi
 activity="compare results"
 perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
 diff  -b $TEMPDIR/pspp.list - <<EOF
-1.1 DATA LIST.  Reading 1 record from file $top_srcdir/tests/weighting.data.
+1.1 DATA LIST.  Reading 1 record from "$top_srcdir/tests/weighting.data".
 +--------+------+-------+------+
 |Variable|Record|Columns|Format|
 #========#======#=======#======#
index b4bfd4500d95f21a25d445f33fc491de48a65e26..fb6fde718f96876b5594e5d5be254b5bfa2ec72f 100755 (executable)
@@ -99,7 +99,7 @@ if [ $? -ne 0 ] ; then fail ; fi
 activity="compare results"
 perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
 diff -b  $TEMPDIR/pspp.list - <<EOF
-1.1 DATA LIST.  Reading 1 record from the command file.
+1.1 DATA LIST.  Reading 1 record from INLINE.
 +--------+------+-------+------+
 |Variable|Record|Columns|Format|
 #========#======#=======#======#
index c7419a56637aee57081ebdbcf3daf855c1109640..0c2239177e5f64eda18b05cf78aa3459ba8e7a97 100755 (executable)
@@ -75,7 +75,7 @@ if [ $? -ne 0 ] ; then fail ; fi
 activity="compare results"
 perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
 diff -b  $TEMPDIR/pspp.list - <<EOF
-1.1 DATA LIST.  Reading 1 record from the command file.
+1.1 DATA LIST.  Reading 1 record from INLINE.
 +--------+------+-------+------+
 |Variable|Record|Columns|Format|
 #========#======#=======#======#
index ff1877bc314ca6f999ba3194f1f4bcee21bec636..6b7eb90ab2a1b27dcfcc9f6185e1dd9ad60f6373 100755 (executable)
@@ -78,7 +78,7 @@ if [ $? -ne 0 ] ; then no_result ; fi
 activity="compare output"
 perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
 diff -b $TEMPDIR/pspp.list - <<EOF
-1.1 DATA LIST.  Reading 1 record from the command file.
+1.1 DATA LIST.  Reading 1 record from INLINE.
 +--------+------+-------+------+
 |Variable|Record|Columns|Format|
 #========#======#=======#======#
index afb77ea9f078bbb89d91531fab2e15af5596eb0a..8572266f2a07750d6c0d9aa5b7774141e75e668f 100755 (executable)
@@ -79,7 +79,7 @@ if [ $? -ne 0 ] ; then no_result ; fi
 activity="compare output"
 perl -pi -e 's/^\s*$//g' $TEMPDIR/pspp.list
 diff -b $TEMPDIR/pspp.list - <<EOF
-1.1 DATA LIST.  Reading 1 record from the command file.
+1.1 DATA LIST.  Reading 1 record from INLINE.
 +--------+------+-------+------+
 |Variable|Record|Columns|Format|
 #========#======#=======#======#